diff options
Diffstat (limited to 'meta/recipes-devtools/python/python3/valid-dists.patch')
| -rw-r--r-- | meta/recipes-devtools/python/python3/valid-dists.patch | 164 |
1 files changed, 0 insertions, 164 deletions
diff --git a/meta/recipes-devtools/python/python3/valid-dists.patch b/meta/recipes-devtools/python/python3/valid-dists.patch deleted file mode 100644 index 38b6ebc5cb..0000000000 --- a/meta/recipes-devtools/python/python3/valid-dists.patch +++ /dev/null | |||
| @@ -1,164 +0,0 @@ | |||
| 1 | From a65c29adc027b3615154cab73aaedd58a6aa23da Mon Sep 17 00:00:00 2001 | ||
| 2 | From: "Jason R. Coombs" <jaraco@jaraco.com> | ||
| 3 | Date: Tue, 23 Jul 2024 08:36:16 -0400 | ||
| 4 | Subject: [PATCH] Prioritize valid dists to invalid dists when retrieving by | ||
| 5 | name. | ||
| 6 | |||
| 7 | Closes python/importlib_metadata#489 | ||
| 8 | |||
| 9 | Upstream-Status: Backport [https://github.com/python/importlib_metadata/commit/a65c29adc027b3615154cab73aaedd58a6aa23da] | ||
| 10 | Signed-off-by: Ross Burton <ross.burton@arm.com> | ||
| 11 | --- | ||
| 12 | Lib/importlib/metadata/__init__.py | 14 +++- | ||
| 13 | Lib/importlib/metadata/_itertools.py | 98 ++++++++++++++++++++++++++++ | ||
| 14 | 2 files changed, 110 insertions(+), 2 deletions(-) | ||
| 15 | |||
| 16 | diff --git a/Lib/importlib/metadata/__init__.py b/Lib/importlib/metadata/__init__.py | ||
| 17 | index 8ce62dd..085378c 100644 | ||
| 18 | --- a/Lib/importlib/metadata/__init__.py | ||
| 19 | +++ b/Lib/importlib/metadata/__init__.py | ||
| 20 | @@ -21,7 +21,7 @@ import collections | ||
| 21 | from . import _meta | ||
| 22 | from ._collections import FreezableDefaultDict, Pair | ||
| 23 | from ._functools import method_cache, pass_none | ||
| 24 | -from ._itertools import always_iterable, unique_everseen | ||
| 25 | +from ._itertools import always_iterable, bucket, unique_everseen | ||
| 26 | from ._meta import PackageMetadata, SimplePath | ||
| 27 | |||
| 28 | from contextlib import suppress | ||
| 29 | @@ -404,7 +404,7 @@ class Distribution(DeprecatedNonAbstract): | ||
| 30 | if not name: | ||
| 31 | raise ValueError("A distribution name is required.") | ||
| 32 | try: | ||
| 33 | - return next(iter(cls.discover(name=name))) | ||
| 34 | + return next(iter(cls._prefer_valid(cls.discover(name=name)))) | ||
| 35 | except StopIteration: | ||
| 36 | raise PackageNotFoundError(name) | ||
| 37 | |||
| 38 | @@ -428,6 +428,16 @@ class Distribution(DeprecatedNonAbstract): | ||
| 39 | resolver(context) for resolver in cls._discover_resolvers() | ||
| 40 | ) | ||
| 41 | |||
| 42 | + @staticmethod | ||
| 43 | + def _prefer_valid(dists: Iterable[Distribution]) -> Iterable[Distribution]: | ||
| 44 | + """ | ||
| 45 | + Prefer (move to the front) distributions that have metadata. | ||
| 46 | + | ||
| 47 | + Ref python/importlib_resources#489. | ||
| 48 | + """ | ||
| 49 | + buckets = bucket(dists, lambda dist: bool(dist.metadata)) | ||
| 50 | + return itertools.chain(buckets[True], buckets[False]) | ||
| 51 | + | ||
| 52 | @staticmethod | ||
| 53 | def at(path: str | os.PathLike[str]) -> Distribution: | ||
| 54 | """Return a Distribution for the indicated metadata path. | ||
| 55 | diff --git a/Lib/importlib/metadata/_itertools.py b/Lib/importlib/metadata/_itertools.py | ||
| 56 | index d4ca9b9..79d3719 100644 | ||
| 57 | --- a/Lib/importlib/metadata/_itertools.py | ||
| 58 | +++ b/Lib/importlib/metadata/_itertools.py | ||
| 59 | @@ -1,3 +1,4 @@ | ||
| 60 | +from collections import defaultdict, deque | ||
| 61 | from itertools import filterfalse | ||
| 62 | |||
| 63 | |||
| 64 | @@ -71,3 +72,100 @@ def always_iterable(obj, base_type=(str, bytes)): | ||
| 65 | return iter(obj) | ||
| 66 | except TypeError: | ||
| 67 | return iter((obj,)) | ||
| 68 | + | ||
| 69 | + | ||
| 70 | +# Copied from more_itertools 10.3 | ||
| 71 | +class bucket: | ||
| 72 | + """Wrap *iterable* and return an object that buckets the iterable into | ||
| 73 | + child iterables based on a *key* function. | ||
| 74 | + | ||
| 75 | + >>> iterable = ['a1', 'b1', 'c1', 'a2', 'b2', 'c2', 'b3'] | ||
| 76 | + >>> s = bucket(iterable, key=lambda x: x[0]) # Bucket by 1st character | ||
| 77 | + >>> sorted(list(s)) # Get the keys | ||
| 78 | + ['a', 'b', 'c'] | ||
| 79 | + >>> a_iterable = s['a'] | ||
| 80 | + >>> next(a_iterable) | ||
| 81 | + 'a1' | ||
| 82 | + >>> next(a_iterable) | ||
| 83 | + 'a2' | ||
| 84 | + >>> list(s['b']) | ||
| 85 | + ['b1', 'b2', 'b3'] | ||
| 86 | + | ||
| 87 | + The original iterable will be advanced and its items will be cached until | ||
| 88 | + they are used by the child iterables. This may require significant storage. | ||
| 89 | + | ||
| 90 | + By default, attempting to select a bucket to which no items belong will | ||
| 91 | + exhaust the iterable and cache all values. | ||
| 92 | + If you specify a *validator* function, selected buckets will instead be | ||
| 93 | + checked against it. | ||
| 94 | + | ||
| 95 | + >>> from itertools import count | ||
| 96 | + >>> it = count(1, 2) # Infinite sequence of odd numbers | ||
| 97 | + >>> key = lambda x: x % 10 # Bucket by last digit | ||
| 98 | + >>> validator = lambda x: x in {1, 3, 5, 7, 9} # Odd digits only | ||
| 99 | + >>> s = bucket(it, key=key, validator=validator) | ||
| 100 | + >>> 2 in s | ||
| 101 | + False | ||
| 102 | + >>> list(s[2]) | ||
| 103 | + [] | ||
| 104 | + | ||
| 105 | + """ | ||
| 106 | + | ||
| 107 | + def __init__(self, iterable, key, validator=None): | ||
| 108 | + self._it = iter(iterable) | ||
| 109 | + self._key = key | ||
| 110 | + self._cache = defaultdict(deque) | ||
| 111 | + self._validator = validator or (lambda x: True) | ||
| 112 | + | ||
| 113 | + def __contains__(self, value): | ||
| 114 | + if not self._validator(value): | ||
| 115 | + return False | ||
| 116 | + | ||
| 117 | + try: | ||
| 118 | + item = next(self[value]) | ||
| 119 | + except StopIteration: | ||
| 120 | + return False | ||
| 121 | + else: | ||
| 122 | + self._cache[value].appendleft(item) | ||
| 123 | + | ||
| 124 | + return True | ||
| 125 | + | ||
| 126 | + def _get_values(self, value): | ||
| 127 | + """ | ||
| 128 | + Helper to yield items from the parent iterator that match *value*. | ||
| 129 | + Items that don't match are stored in the local cache as they | ||
| 130 | + are encountered. | ||
| 131 | + """ | ||
| 132 | + while True: | ||
| 133 | + # If we've cached some items that match the target value, emit | ||
| 134 | + # the first one and evict it from the cache. | ||
| 135 | + if self._cache[value]: | ||
| 136 | + yield self._cache[value].popleft() | ||
| 137 | + # Otherwise we need to advance the parent iterator to search for | ||
| 138 | + # a matching item, caching the rest. | ||
| 139 | + else: | ||
| 140 | + while True: | ||
| 141 | + try: | ||
| 142 | + item = next(self._it) | ||
| 143 | + except StopIteration: | ||
| 144 | + return | ||
| 145 | + item_value = self._key(item) | ||
| 146 | + if item_value == value: | ||
| 147 | + yield item | ||
| 148 | + break | ||
| 149 | + elif self._validator(item_value): | ||
| 150 | + self._cache[item_value].append(item) | ||
| 151 | + | ||
| 152 | + def __iter__(self): | ||
| 153 | + for item in self._it: | ||
| 154 | + item_value = self._key(item) | ||
| 155 | + if self._validator(item_value): | ||
| 156 | + self._cache[item_value].append(item) | ||
| 157 | + | ||
| 158 | + yield from self._cache.keys() | ||
| 159 | + | ||
| 160 | + def __getitem__(self, value): | ||
| 161 | + if not self._validator(value): | ||
| 162 | + return iter(()) | ||
| 163 | + | ||
| 164 | + return self._get_values(value) | ||
