Skip to content

Commit cd5b7ff

Browse files
authored
Merge branch 'main' into arm-runners
2 parents 3b0f67e + 78cb377 commit cd5b7ff

File tree

8 files changed

+208
-142
lines changed

8 files changed

+208
-142
lines changed

Doc/library/contextlib.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -151,9 +151,9 @@ Functions and classes provided:
151151
created by :func:`asynccontextmanager` to meet the requirement that context
152152
managers support multiple invocations in order to be used as decorators.
153153

154-
.. versionchanged:: 3.10
155-
Async context managers created with :func:`asynccontextmanager` can
156-
be used as decorators.
154+
.. versionchanged:: 3.10
155+
Async context managers created with :func:`asynccontextmanager` can
156+
be used as decorators.
157157

158158

159159
.. function:: closing(thing)

InternalDocs/garbage_collector.md

Lines changed: 75 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -17,26 +17,26 @@ value returned by this function is always 1 more as the function also has a refe
1717
to the object when called):
1818

1919
```pycon
20-
>>> x = object()
21-
>>> sys.getrefcount(x)
22-
2
23-
>>> y = x
24-
>>> sys.getrefcount(x)
25-
3
26-
>>> del y
27-
>>> sys.getrefcount(x)
28-
2
20+
>>> x = object()
21+
>>> sys.getrefcount(x)
22+
2
23+
>>> y = x
24+
>>> sys.getrefcount(x)
25+
3
26+
>>> del y
27+
>>> sys.getrefcount(x)
28+
2
2929
```
3030

3131
The main problem with the reference counting scheme is that it does not handle reference
3232
cycles. For instance, consider this code:
3333

3434
```pycon
35-
>>> container = []
36-
>>> container.append(container)
37-
>>> sys.getrefcount(container)
38-
3
39-
>>> del container
35+
>>> container = []
36+
>>> container.append(container)
37+
>>> sys.getrefcount(container)
38+
3
39+
>>> del container
4040
```
4141

4242
In this example, `container` holds a reference to itself, so even when we remove
@@ -199,26 +199,26 @@ variable `A`, and one self-referencing object which is completely
199199
unreachable:
200200

201201
```pycon
202-
>>> import gc
203-
204-
>>> class Link:
205-
... def __init__(self, next_link=None):
206-
... self.next_link = next_link
207-
208-
>>> link_3 = Link()
209-
>>> link_2 = Link(link_3)
210-
>>> link_1 = Link(link_2)
211-
>>> link_3.next_link = link_1
212-
>>> A = link_1
213-
>>> del link_1, link_2, link_3
214-
215-
>>> link_4 = Link()
216-
>>> link_4.next_link = link_4
217-
>>> del link_4
218-
219-
# Collect the unreachable Link object (and its .__dict__ dict).
220-
>>> gc.collect()
221-
2
202+
>>> import gc
203+
>>>
204+
>>> class Link:
205+
... def __init__(self, next_link=None):
206+
... self.next_link = next_link
207+
...
208+
>>> link_3 = Link()
209+
>>> link_2 = Link(link_3)
210+
>>> link_1 = Link(link_2)
211+
>>> link_3.next_link = link_1
212+
>>> A = link_1
213+
>>> del link_1, link_2, link_3
214+
>>>
215+
>>> link_4 = Link()
216+
>>> link_4.next_link = link_4
217+
>>> del link_4
218+
>>>
219+
>>> # Collect the unreachable Link object (and its .__dict__ dict).
220+
>>> gc.collect()
221+
2
222222
```
223223

224224
The GC starts with a set of candidate objects it wants to scan. In the
@@ -439,48 +439,42 @@ These thresholds can be examined using the
439439
function:
440440

441441
```pycon
442-
>>> import gc
443-
>>> gc.get_threshold()
444-
(700, 10, 10)
442+
>>> import gc
443+
>>> gc.get_threshold()
444+
(700, 10, 10)
445445
```
446446

447447
The content of these generations can be examined using the
448448
`gc.get_objects(generation=NUM)` function and collections can be triggered
449449
specifically in a generation by calling `gc.collect(generation=NUM)`.
450450

451451
```pycon
452-
>>> import gc
453-
>>> class MyObj:
454-
... pass
455-
...
456-
457-
# Move everything to the old generation so it's easier to inspect
458-
# the young generation.
459-
460-
>>> gc.collect()
461-
0
462-
463-
# Create a reference cycle.
464-
465-
>>> x = MyObj()
466-
>>> x.self = x
467-
468-
# Initially the object is in the young generation.
469-
470-
>>> gc.get_objects(generation=0)
471-
[..., <__main__.MyObj object at 0x7fbcc12a3400>, ...]
472-
473-
# After a collection of the youngest generation the object
474-
# moves to the old generation.
475-
476-
>>> gc.collect(generation=0)
477-
0
478-
>>> gc.get_objects(generation=0)
479-
[]
480-
>>> gc.get_objects(generation=1)
481-
[]
482-
>>> gc.get_objects(generation=2)
483-
[..., <__main__.MyObj object at 0x7fbcc12a3400>, ...]
452+
>>> import gc
453+
>>> class MyObj:
454+
... pass
455+
...
456+
>>> # Move everything to the old generation so it's easier to inspect
457+
>>> # the young generation.
458+
>>> gc.collect()
459+
0
460+
>>> # Create a reference cycle.
461+
>>> x = MyObj()
462+
>>> x.self = x
463+
>>>
464+
>>> # Initially the object is in the young generation.
465+
>>> gc.get_objects(generation=0)
466+
[..., <__main__.MyObj object at 0x7fbcc12a3400>, ...]
467+
>>>
468+
>>> # After a collection of the youngest generation the object
469+
>>> # moves to the old generation.
470+
>>> gc.collect(generation=0)
471+
0
472+
>>> gc.get_objects(generation=0)
473+
[]
474+
>>> gc.get_objects(generation=1)
475+
[]
476+
>>> gc.get_objects(generation=2)
477+
[..., <__main__.MyObj object at 0x7fbcc12a3400>, ...]
484478
```
485479

486480

@@ -563,18 +557,18 @@ the current tracking status of the object. Subsequent garbage collections may ch
563557
tracking status of the object.
564558

565559
```pycon
566-
>>> gc.is_tracked(0)
567-
False
568-
>>> gc.is_tracked("a")
569-
False
570-
>>> gc.is_tracked([])
571-
True
572-
>>> gc.is_tracked(())
573-
False
574-
>>> gc.is_tracked({})
575-
True
576-
>>> gc.is_tracked({"a": 1})
577-
True
560+
>>> gc.is_tracked(0)
561+
False
562+
>>> gc.is_tracked("a")
563+
False
564+
>>> gc.is_tracked([])
565+
True
566+
>>> gc.is_tracked(())
567+
False
568+
>>> gc.is_tracked({})
569+
True
570+
>>> gc.is_tracked({"a": 1})
571+
True
578572
```
579573

580574
Differences between GC implementations

Lib/fnmatch.py

Lines changed: 42 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -77,24 +77,30 @@ def translate(pat):
7777
There is no way to quote meta-characters.
7878
"""
7979

80-
STAR = object()
81-
parts = _translate(pat, STAR, '.')
82-
return _join_translated_parts(parts, STAR)
80+
parts, star_indices = _translate(pat, '*', '.')
81+
return _join_translated_parts(parts, star_indices)
8382

83+
_re_setops_sub = re.compile(r'([&~|])').sub
84+
_re_escape = functools.lru_cache(maxsize=512)(re.escape)
8485

85-
def _translate(pat, STAR, QUESTION_MARK):
86+
def _translate(pat, star, question_mark):
8687
res = []
8788
add = res.append
89+
star_indices = []
90+
8891
i, n = 0, len(pat)
8992
while i < n:
9093
c = pat[i]
9194
i = i+1
9295
if c == '*':
96+
# store the position of the wildcard
97+
star_indices.append(len(res))
98+
add(star)
9399
# compress consecutive `*` into one
94-
if (not res) or res[-1] is not STAR:
95-
add(STAR)
100+
while i < n and pat[i] == '*':
101+
i += 1
96102
elif c == '?':
97-
add(QUESTION_MARK)
103+
add(question_mark)
98104
elif c == '[':
99105
j = i
100106
if j < n and pat[j] == '!':
@@ -133,8 +139,6 @@ def _translate(pat, STAR, QUESTION_MARK):
133139
# Hyphens that create ranges shouldn't be escaped.
134140
stuff = '-'.join(s.replace('\\', r'\\').replace('-', r'\-')
135141
for s in chunks)
136-
# Escape set operations (&&, ~~ and ||).
137-
stuff = re.sub(r'([&~|])', r'\\\1', stuff)
138142
i = j+1
139143
if not stuff:
140144
# Empty range: never match.
@@ -143,50 +147,40 @@ def _translate(pat, STAR, QUESTION_MARK):
143147
# Negated empty range: match any character.
144148
add('.')
145149
else:
150+
# Escape set operations (&&, ~~ and ||).
151+
stuff = _re_setops_sub(r'\\\1', stuff)
146152
if stuff[0] == '!':
147153
stuff = '^' + stuff[1:]
148154
elif stuff[0] in ('^', '['):
149155
stuff = '\\' + stuff
150156
add(f'[{stuff}]')
151157
else:
152-
add(re.escape(c))
153-
assert i == n
154-
return res
155-
156-
157-
def _join_translated_parts(inp, STAR):
158-
# Deal with STARs.
159-
res = []
160-
add = res.append
161-
i, n = 0, len(inp)
162-
# Fixed pieces at the start?
163-
while i < n and inp[i] is not STAR:
164-
add(inp[i])
165-
i += 1
166-
# Now deal with STAR fixed STAR fixed ...
167-
# For an interior `STAR fixed` pairing, we want to do a minimal
168-
# .*? match followed by `fixed`, with no possibility of backtracking.
169-
# Atomic groups ("(?>...)") allow us to spell that directly.
170-
# Note: people rely on the undocumented ability to join multiple
171-
# translate() results together via "|" to build large regexps matching
172-
# "one of many" shell patterns.
173-
while i < n:
174-
assert inp[i] is STAR
175-
i += 1
176-
if i == n:
177-
add(".*")
178-
break
179-
assert inp[i] is not STAR
180-
fixed = []
181-
while i < n and inp[i] is not STAR:
182-
fixed.append(inp[i])
183-
i += 1
184-
fixed = "".join(fixed)
185-
if i == n:
186-
add(".*")
187-
add(fixed)
188-
else:
189-
add(f"(?>.*?{fixed})")
158+
add(_re_escape(c))
190159
assert i == n
191-
res = "".join(res)
160+
return res, star_indices
161+
162+
163+
def _join_translated_parts(parts, star_indices):
164+
if not star_indices:
165+
return fr'(?s:{"".join(parts)})\Z'
166+
iter_star_indices = iter(star_indices)
167+
j = next(iter_star_indices)
168+
buffer = parts[:j] # fixed pieces at the start
169+
append, extend = buffer.append, buffer.extend
170+
i = j + 1
171+
for j in iter_star_indices:
172+
# Now deal with STAR fixed STAR fixed ...
173+
# For an interior `STAR fixed` pairing, we want to do a minimal
174+
# .*? match followed by `fixed`, with no possibility of backtracking.
175+
# Atomic groups ("(?>...)") allow us to spell that directly.
176+
# Note: people rely on the undocumented ability to join multiple
177+
# translate() results together via "|" to build large regexps matching
178+
# "one of many" shell patterns.
179+
append('(?>.*?')
180+
extend(parts[i:j])
181+
append(')')
182+
i = j + 1
183+
append('.*')
184+
extend(parts[i:])
185+
res = ''.join(buffer)
192186
return fr'(?s:{res})\Z'

Lib/glob.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ def translate(pat, *, recursive=False, include_hidden=False, seps=None):
312312
if part:
313313
if not include_hidden and part[0] in '*?':
314314
results.append(r'(?!\.)')
315-
results.extend(fnmatch._translate(part, f'{not_sep}*', not_sep))
315+
results.extend(fnmatch._translate(part, f'{not_sep}*', not_sep)[0])
316316
if idx < last_part_idx:
317317
results.append(any_sep)
318318
res = ''.join(results)

0 commit comments

Comments
 (0)