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)
Comment thread
serhiy-storchaka marked this conversation as resolved.
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)
Comment thread
serhiy-storchaka marked this conversation as resolved.
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()
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
1 change: 1 addition & 0 deletions Modules/_testcapi/parts.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,5 +67,6 @@ int _PyTestCapi_Init_Frame(PyObject *mod);
int _PyTestCapi_Init_Type(PyObject *mod);
int _PyTestCapi_Init_Function(PyObject *mod);
int _PyTestCapi_Init_Module(PyObject *mod);
int _PyTestCapi_Init_Weakref(PyObject *mod);

#endif // Py_TESTCAPI_PARTS_H
46 changes: 46 additions & 0 deletions Modules/_testcapi/weakref.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#include "parts.h"
#include "util.h"


static PyObject *
pyweakref_getref(PyObject *module, PyObject *ref)
{
NULLABLE(ref);
PyObject *obj = UNINITIALIZED_PTR;
int rc = PyWeakref_GetRef(ref, &obj);
if (rc == -1 && PyErr_Occurred()) {
assert(obj == NULL);
return NULL;
}
if (obj == NULL) {
return Py_BuildValue("i", rc);
}
else {
assert(obj != UNINITIALIZED_PTR);
return Py_BuildValue("iN", rc, obj);
}
}

static PyObject *
pyweakref_isdead(PyObject *module, PyObject *obj)
{
NULLABLE(obj);
int rc = PyWeakref_IsDead(obj);
if (rc == -1 && PyErr_Occurred()) {
return NULL;
}
return PyLong_FromLong(rc);
}


static PyMethodDef test_methods[] = {
{"pyweakref_getref", pyweakref_getref, METH_O},
{"pyweakref_isdead", pyweakref_isdead, METH_O},
{NULL},
};

int
_PyTestCapi_Init_Weakref(PyObject *m)
{
return PyModule_AddFunctions(m, test_methods);
}
3 changes: 3 additions & 0 deletions Modules/_testcapimodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -3909,6 +3909,9 @@ _testcapi_exec(PyObject *m)
if (_PyTestCapi_Init_Module(m) < 0) {
return -1;
}
if (_PyTestCapi_Init_Weakref(m) < 0) {
return -1;
}

return 0;
}
Expand Down
3 changes: 3 additions & 0 deletions Modules/_testlimitedcapi.c
Original file line number Diff line number Diff line change
Expand Up @@ -98,5 +98,8 @@ PyInit__testlimitedcapi(void)
if (_PyTestLimitedCAPI_Init_File(mod) < 0) {
return NULL;
}
if (_PyTestLimitedCAPI_Init_Weakref(mod) < 0) {
return NULL;
}
return mod;
}
1 change: 1 addition & 0 deletions Modules/_testlimitedcapi/parts.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,6 @@ int _PyTestLimitedCAPI_Init_Unicode(PyObject *module);
int _PyTestLimitedCAPI_Init_VectorcallLimited(PyObject *module);
int _PyTestLimitedCAPI_Init_Version(PyObject *module);
int _PyTestLimitedCAPI_Init_File(PyObject *module);
int _PyTestLimitedCAPI_Init_Weakref(PyObject *module);

#endif // Py_TESTLIMITEDCAPI_PARTS_H
78 changes: 78 additions & 0 deletions Modules/_testlimitedcapi/weakref.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#include "pyconfig.h" // Py_GIL_DISABLED
#ifndef Py_GIL_DISABLED
// Need limited C API 3.5 for PyModule_AddFunctions()
# define Py_LIMITED_API 0x03050000
#endif

#include "parts.h"
#include "util.h"


static PyObject *
pyweakref_check(PyObject *module, PyObject *obj)
{
NULLABLE(obj);
return PyLong_FromLong(PyWeakref_Check(obj));
}

static PyObject *
pyweakref_checkref(PyObject *module, PyObject *obj)
{
NULLABLE(obj);
return PyLong_FromLong(PyWeakref_CheckRef(obj));
}

static PyObject *
pyweakref_checkrefexact(PyObject *module, PyObject *obj)
{
NULLABLE(obj);
return PyLong_FromLong(PyWeakref_CheckRefExact(obj));
}

static PyObject *
pyweakref_checkproxy(PyObject *module, PyObject *obj)
{
NULLABLE(obj);
return PyLong_FromLong(PyWeakref_CheckProxy(obj));
}

static PyObject *
pyweakref_newref(PyObject *module, PyObject *args)
{
PyObject *obj;
PyObject *callback = NULL;
if (!PyArg_ParseTuple(args, "O|O", &obj, &callback)) {
return NULL;
}
NULLABLE(obj);
return PyWeakref_NewRef(obj, callback);
}

static PyObject *
pyweakref_newproxy(PyObject *module, PyObject *args)
{
PyObject *obj;
PyObject *callback = NULL;
if (!PyArg_ParseTuple(args, "O|O", &obj, &callback)) {
return NULL;
}
NULLABLE(obj);
return PyWeakref_NewProxy(obj, callback);
}


static PyMethodDef test_methods[] = {
{"pyweakref_check", pyweakref_check, METH_O},
{"pyweakref_checkref", pyweakref_checkref, METH_O},
{"pyweakref_checkrefexact", pyweakref_checkrefexact, METH_O},
{"pyweakref_checkproxy", pyweakref_checkproxy, METH_O},
{"pyweakref_newref", pyweakref_newref, METH_VARARGS},
{"pyweakref_newproxy", pyweakref_newproxy, METH_VARARGS},
{NULL},
};

int
_PyTestLimitedCAPI_Init_Weakref(PyObject *m)
{
return PyModule_AddFunctions(m, test_methods);
}
1 change: 1 addition & 0 deletions PCbuild/_testcapi.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@
<ClCompile Include="..\Modules\_testcapi\frame.c" />
<ClCompile Include="..\Modules\_testcapi\type.c" />
<ClCompile Include="..\Modules\_testcapi\function.c" />
<ClCompile Include="..\Modules\_testcapi\weakref.c" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\PC\python_nt.rc" />
Expand Down
3 changes: 3 additions & 0 deletions PCbuild/_testcapi.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,9 @@
<ClCompile Include="..\Modules\_testcapi\function.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\Modules\_testcapi\weakref.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\PC\python_nt.rc">
Expand Down
1 change: 1 addition & 0 deletions PCbuild/_testlimitedcapi.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@
<ClCompile Include="..\Modules\_testlimitedcapi\vectorcall_limited.c" />
<ClCompile Include="..\Modules\_testlimitedcapi\version.c" />
<ClCompile Include="..\Modules\_testlimitedcapi\file.c" />
<ClCompile Include="..\Modules\_testlimitedcapi\weakref.c" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\PC\python_nt.rc" />
Expand Down
1 change: 1 addition & 0 deletions PCbuild/_testlimitedcapi.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
<ClCompile Include="..\Modules\_testlimitedcapi\vectorcall_limited.c" />
<ClCompile Include="..\Modules\_testlimitedcapi\version.c" />
<ClCompile Include="..\Modules\_testlimitedcapi\file.c" />
<ClCompile Include="..\Modules\_testlimitedcapi\weakref.c" />
<ClCompile Include="..\Modules\_testlimitedcapi.c" />
</ItemGroup>
<ItemGroup>
Expand Down
Loading