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
136 changes: 136 additions & 0 deletions Lib/test/test_capi/test_weakref.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import weakref
import unittest
from test.support import import_helper

_testcapi = import_helper.import_module('_testcapi')
_testlimitedcapi = import_helper.import_module('_testlimitedcapi')
NULL = None

class Object:
pass

class Ref(weakref.ReferenceType):
pass


class CAPIWeakrefTest(unittest.TestCase):
def test_pyweakref_check(self):
# Test PyWeakref_Check()
check = _testlimitedcapi.pyweakref_check
obj = Object()
self.assertEqual(check(obj), 0)
self.assertEqual(check(weakref.ref(obj)), 1)
self.assertEqual(check(Ref(obj)), 1)
self.assertEqual(check(weakref.proxy(obj)), 1)

# CRASHES check(NULL)

def test_pyweakref_checkref(self):
# Test PyWeakref_CheckRef()
checkref = _testlimitedcapi.pyweakref_checkref
obj = Object()
self.assertEqual(checkref(obj), 0)
self.assertEqual(checkref(weakref.ref(obj)), 1)
self.assertEqual(checkref(Ref(obj)), 1)
self.assertEqual(checkref(weakref.proxy(obj)), 0)

# CRASHES checkref(NULL)

def test_pyweakref_checkrefexact(self):
# Test PyWeakref_CheckRefExact()
checkrefexact = _testlimitedcapi.pyweakref_checkrefexact
obj = Object()
self.assertEqual(checkrefexact(obj), 0)
self.assertEqual(checkrefexact(weakref.ref(obj)), 1)
self.assertEqual(checkrefexact(Ref(obj)), 0)
self.assertEqual(checkrefexact(weakref.proxy(obj)), 0)

# CRASHES checkrefexact(NULL)

def test_pyweakref_checkproxy(self):
# Test PyWeakref_CheckProxy()
checkproxy = _testlimitedcapi.pyweakref_checkproxy
obj = Object()
self.assertEqual(checkproxy(obj), 0)
self.assertEqual(checkproxy(weakref.ref(obj)), 0)
self.assertEqual(checkproxy(Ref(obj)), 0)
self.assertEqual(checkproxy(weakref.proxy(obj)), 1)

# CRASHES checkproxy(NULL)

def test_pyweakref_getref(self):
# Test PyWeakref_GetRef()
getref = _testcapi.pyweakref_getref
obj = Object()
wr = weakref.ref(obj)
wp = weakref.proxy(obj)
self.assertEqual(getref(wr), (1, obj))
self.assertEqual(getref(wp), (1, obj))
del obj
self.assertEqual(getref(wr), 0)
self.assertEqual(getref(wp), 0)

self.assertRaises(TypeError, getref, 42)
self.assertRaises(SystemError, getref, NULL)

def test_pyweakref_isdead(self):
# Test PyWeakref_IsDead()
isdead = _testcapi.pyweakref_isdead
obj = Object()
wr = weakref.ref(obj)
wp = weakref.proxy(obj)
self.assertEqual(isdead(wr), 0)
self.assertEqual(isdead(wp), 0)
del obj
self.assertEqual(isdead(wr), 1)
self.assertEqual(isdead(wp), 1)

self.assertRaises(TypeError, isdead, 42)
self.assertRaises(SystemError, isdead, NULL)

def test_pyweakref_newref(self):
# Test PyWeakref_NewRef()
newref = _testlimitedcapi.pyweakref_newref
obj = Object()
wr = newref(obj)
self.assertIs(type(wr), weakref.ReferenceType)
# PyWeakref_NewRef() handles None callback as NULL callback
wr = newref(obj, None)
self.assertIs(type(wr), weakref.ReferenceType)
log = []
wr = newref(obj, log.append)
self.assertIs(type(wr), weakref.ReferenceType)
self.assertEqual(log, [])
del obj
self.assertEqual(log, [wr])

self.assertRaises(TypeError, newref, [])
# CRASHES newref(NULL)

def test_pyweakref_newproxy(self):
# Test PyWeakref_NewProxy()
newproxy = _testlimitedcapi.pyweakref_newproxy
obj = Object()
wp = newproxy(obj)
self.assertIs(type(wp), weakref.ProxyType)
# PyWeakref_NewProxy() handles None callback as NULL callback
wp = newproxy(obj, None)
self.assertIs(type(wp), weakref.ProxyType)
log = []
wp = newproxy(obj, log.append)
self.assertIs(type(wp), weakref.ProxyType)
self.assertEqual(log, [])
del obj
self.assertEqual(log, [wp])

def func():
pass
wp = newproxy(func)
self.assertIs(type(wp), weakref.CallableProxyType)

self.assertRaises(TypeError, newproxy, [])
# CRASHES newproxy(NULL)


if __name__ == "__main__":
unittest.main()
50 changes: 50 additions & 0 deletions Lib/test/test_io/test_textio.py
Original file line number Diff line number Diff line change
Expand Up @@ -1560,6 +1560,56 @@ def closed(self):
wrapper = self.TextIOWrapper(raw)
wrapper.close() # should not crash

def test_reentrant_detach_during_flush(self):
# gh-143008: Reentrant detach() during flush should not crash.

class DetachOnce(self.BufferedRandom):
wrapper = None

def detach_once(self):
original = self.wrapper
self.wrapper = None
if original is not None:
original.detach()
original.flush()

class DetachOnFlush(DetachOnce):
def flush(self):
self.detach_once()

class DetachOnWrite(DetachOnce):
def write(self, b):
self.detach_once()
return len(b)

# Separate reference for after detach_once.
wrapper = None

def make_text(buffer):
nonlocal wrapper
buffer.wrapper = self.TextIOWrapper(buffer, encoding='utf-8')
wrapper = buffer.wrapper

# Many calls could result in the same null self->buffer crash.
tests = [
('truncate', lambda: wrapper.truncate(0)),
('close', lambda: wrapper.close()),
('detach', lambda: wrapper.detach()),
('seek', lambda: wrapper.seek(0)),
('tell', lambda: wrapper.tell()),
('reconfigure', lambda: wrapper.reconfigure(line_buffering=True)),
]
for name, method in tests:
with self.subTest(name):
make_text(DetachOnFlush(self.MockRawIO()))
self.assertRaisesRegex(ValueError, "detached", method)

# Should not crash.
with self.subTest('read via writeflush'):
make_text(DetachOnWrite(self.MockRawIO()))
wrapper.write('x')
self.assertRaisesRegex(ValueError, "detached", wrapper.read)


class PyTextIOWrapperTest(TextIOWrapperTest, PyTestCase):
shutdown_error = "LookupError: unknown encoding: ascii"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Fix a crash, when there's no memory left on a device,
which happened in code compilation.
Now it raises a proper :exc:`MemoryError`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix crash in :class:`io.TextIOWrapper` when reentrant
:meth:`io.TextIOBase.detach` is called reentrantly from the underlying buffer.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix race conditions when re-initializing a :class:`io.TextIOWrapper` object.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add more tests for ``PyWeakref_*`` C API.
4 changes: 2 additions & 2 deletions Modules/Setup.stdlib.in
Original file line number Diff line number Diff line change
Expand Up @@ -173,8 +173,8 @@
@MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c
@MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c
@MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c _testinternalcapi/test_lock.c _testinternalcapi/pytime.c _testinternalcapi/set.c _testinternalcapi/test_critical_sections.c _testinternalcapi/complex.c _testinternalcapi/interpreter.c _testinternalcapi/tuple.c
@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/run.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/gc.c _testcapi/hash.c _testcapi/time.c _testcapi/bytes.c _testcapi/object.c _testcapi/modsupport.c _testcapi/monitoring.c _testcapi/config.c _testcapi/import.c _testcapi/frame.c _testcapi/type.c _testcapi/function.c _testcapi/module.c
@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c _testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c _testlimitedcapi/bytes.c _testlimitedcapi/codec.c _testlimitedcapi/complex.c _testlimitedcapi/dict.c _testlimitedcapi/eval.c _testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c _testlimitedcapi/import.c _testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/object.c _testlimitedcapi/pyos.c _testlimitedcapi/set.c _testlimitedcapi/slots.c _testlimitedcapi/sys.c _testlimitedcapi/threadstate.c _testlimitedcapi/tuple.c _testlimitedcapi/unicode.c _testlimitedcapi/vectorcall_limited.c _testlimitedcapi/version.c _testlimitedcapi/file.c
@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/run.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/gc.c _testcapi/hash.c _testcapi/time.c _testcapi/bytes.c _testcapi/object.c _testcapi/modsupport.c _testcapi/monitoring.c _testcapi/config.c _testcapi/import.c _testcapi/frame.c _testcapi/type.c _testcapi/function.c _testcapi/module.c _testcapi/weakref.c
@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c _testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c _testlimitedcapi/bytes.c _testlimitedcapi/codec.c _testlimitedcapi/complex.c _testlimitedcapi/dict.c _testlimitedcapi/eval.c _testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c _testlimitedcapi/import.c _testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/object.c _testlimitedcapi/pyos.c _testlimitedcapi/set.c _testlimitedcapi/slots.c _testlimitedcapi/sys.c _testlimitedcapi/threadstate.c _testlimitedcapi/tuple.c _testlimitedcapi/unicode.c _testlimitedcapi/vectorcall_limited.c _testlimitedcapi/version.c _testlimitedcapi/file.c _testlimitedcapi/weakref.c
@MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c
@MODULE__TESTCLINIC_LIMITED_TRUE@_testclinic_limited _testclinic_limited.c

Expand Down
4 changes: 3 additions & 1 deletion Modules/_io/clinic/textio.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading