summaryrefslogtreecommitdiffstats
path: root/meta/recipes-devtools/python/python3/valid-dists.patch
diff options
context:
space:
mode:
Diffstat (limited to 'meta/recipes-devtools/python/python3/valid-dists.patch')
-rw-r--r--meta/recipes-devtools/python/python3/valid-dists.patch164
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 @@
1From a65c29adc027b3615154cab73aaedd58a6aa23da Mon Sep 17 00:00:00 2001
2From: "Jason R. Coombs" <jaraco@jaraco.com>
3Date: Tue, 23 Jul 2024 08:36:16 -0400
4Subject: [PATCH] Prioritize valid dists to invalid dists when retrieving by
5 name.
6
7Closes python/importlib_metadata#489
8
9Upstream-Status: Backport [https://github.com/python/importlib_metadata/commit/a65c29adc027b3615154cab73aaedd58a6aa23da]
10Signed-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
16diff --git a/Lib/importlib/metadata/__init__.py b/Lib/importlib/metadata/__init__.py
17index 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.
55diff --git a/Lib/importlib/metadata/_itertools.py b/Lib/importlib/metadata/_itertools.py
56index 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)