Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Doc/library/contextlib.rst
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,9 @@ Functions and classes provided:
created by :func:`asynccontextmanager` to meet the requirement that context
managers support multiple invocations in order to be used as decorators.

.. versionchanged:: 3.10
Async context managers created with :func:`asynccontextmanager` can
be used as decorators.
.. versionchanged:: 3.10
Async context managers created with :func:`asynccontextmanager` can
be used as decorators.


.. function:: closing(thing)
Expand Down
156 changes: 75 additions & 81 deletions InternalDocs/garbage_collector.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,26 @@ value returned by this function is always 1 more as the function also has a refe
to the object when called):

```pycon
>>> x = object()
>>> sys.getrefcount(x)
2
>>> y = x
>>> sys.getrefcount(x)
3
>>> del y
>>> sys.getrefcount(x)
2
>>> x = object()
>>> sys.getrefcount(x)
2
>>> y = x
>>> sys.getrefcount(x)
3
>>> del y
>>> sys.getrefcount(x)
2
```

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

```pycon
>>> container = []
>>> container.append(container)
>>> sys.getrefcount(container)
3
>>> del container
>>> container = []
>>> container.append(container)
>>> sys.getrefcount(container)
3
>>> del container
```

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

```pycon
>>> import gc

>>> class Link:
... def __init__(self, next_link=None):
... self.next_link = next_link

>>> link_3 = Link()
>>> link_2 = Link(link_3)
>>> link_1 = Link(link_2)
>>> link_3.next_link = link_1
>>> A = link_1
>>> del link_1, link_2, link_3

>>> link_4 = Link()
>>> link_4.next_link = link_4
>>> del link_4

# Collect the unreachable Link object (and its .__dict__ dict).
>>> gc.collect()
2
>>> import gc
>>>
>>> class Link:
... def __init__(self, next_link=None):
... self.next_link = next_link
...
>>> link_3 = Link()
>>> link_2 = Link(link_3)
>>> link_1 = Link(link_2)
>>> link_3.next_link = link_1
>>> A = link_1
>>> del link_1, link_2, link_3
>>>
>>> link_4 = Link()
>>> link_4.next_link = link_4
>>> del link_4
>>>
>>> # Collect the unreachable Link object (and its .__dict__ dict).
>>> gc.collect()
2
```

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

```pycon
>>> import gc
>>> gc.get_threshold()
(700, 10, 10)
>>> import gc
>>> gc.get_threshold()
(700, 10, 10)
```

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

```pycon
>>> import gc
>>> class MyObj:
... pass
...

# Move everything to the old generation so it's easier to inspect
# the young generation.

>>> gc.collect()
0

# Create a reference cycle.

>>> x = MyObj()
>>> x.self = x

# Initially the object is in the young generation.

>>> gc.get_objects(generation=0)
[..., <__main__.MyObj object at 0x7fbcc12a3400>, ...]

# After a collection of the youngest generation the object
# moves to the old generation.

>>> gc.collect(generation=0)
0
>>> gc.get_objects(generation=0)
[]
>>> gc.get_objects(generation=1)
[]
>>> gc.get_objects(generation=2)
[..., <__main__.MyObj object at 0x7fbcc12a3400>, ...]
>>> import gc
>>> class MyObj:
... pass
...
>>> # Move everything to the old generation so it's easier to inspect
>>> # the young generation.
>>> gc.collect()
0
>>> # Create a reference cycle.
>>> x = MyObj()
>>> x.self = x
>>>
>>> # Initially the object is in the young generation.
>>> gc.get_objects(generation=0)
[..., <__main__.MyObj object at 0x7fbcc12a3400>, ...]
>>>
>>> # After a collection of the youngest generation the object
>>> # moves to the old generation.
>>> gc.collect(generation=0)
0
>>> gc.get_objects(generation=0)
[]
>>> gc.get_objects(generation=1)
[]
>>> gc.get_objects(generation=2)
[..., <__main__.MyObj object at 0x7fbcc12a3400>, ...]
```


Expand Down Expand Up @@ -563,18 +557,18 @@ the current tracking status of the object. Subsequent garbage collections may ch
tracking status of the object.

```pycon
>>> gc.is_tracked(0)
False
>>> gc.is_tracked("a")
False
>>> gc.is_tracked([])
True
>>> gc.is_tracked(())
False
>>> gc.is_tracked({})
True
>>> gc.is_tracked({"a": 1})
True
>>> gc.is_tracked(0)
False
>>> gc.is_tracked("a")
False
>>> gc.is_tracked([])
True
>>> gc.is_tracked(())
False
>>> gc.is_tracked({})
True
>>> gc.is_tracked({"a": 1})
True
```

Differences between GC implementations
Expand Down
4 changes: 2 additions & 2 deletions Lib/test/test_threading.py
Original file line number Diff line number Diff line change
Expand Up @@ -1196,11 +1196,11 @@ def f():
except RuntimeError:
print('ok')
else:
print('skip')
print('!skip!')
"""
_, out, err = assert_python_ok("-u", "-c", code)
out = out.strip()
if out == b'skip':
if b'!skip!' in out:
self.skipTest('RLIMIT_NPROC had no effect; probably superuser')
self.assertEqual(out, b'ok')
self.assertEqual(err, b'')
Expand Down
23 changes: 23 additions & 0 deletions Lib/test/test_winconsoleio.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,29 @@ def test_write_empty_data(self):
with ConIO('CONOUT$', 'w') as f:
self.assertEqual(f.write(b''), 0)

@requires_resource('console')
def test_write(self):
testcases = []
with ConIO('CONOUT$', 'w') as f:
for a in [
b'',
b'abc',
b'\xc2\xa7\xe2\x98\x83\xf0\x9f\x90\x8d',
b'\xff'*10,
]:
for b in b'\xc2\xa7', b'\xe2\x98\x83', b'\xf0\x9f\x90\x8d':
testcases.append(a + b)
for i in range(1, len(b)):
data = a + b[:i]
testcases.append(data + b'z')
testcases.append(data + b'\xff')
# incomplete multibyte sequence
with self.subTest(data=data):
self.assertEqual(f.write(data), len(a))
for data in testcases:
with self.subTest(data=data):
self.assertEqual(f.write(data), len(data))

def assertStdinRoundTrip(self, text):
stdin = open('CONIN$', 'r')
old_stdin = sys.stdin
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix possible crash (in debug build), incorrect output or returning incorrect
value from raw binary ``write()`` when writing to console on Windows.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix faulthandler for trampoline frames. If the top-most frame is a
trampoline frame, skip it. Patch by Victor Stinner.
Loading
Loading