-
-
Notifications
You must be signed in to change notification settings - Fork 32.1k
Reference cycle when importing ctypes #56351
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
When importing ctypes after gc.set_debug(gc.DEBUG_LEAK), the garbage collector finds a 'c_int_Array_3' class and some related objects. The class is created in ctypes/_endian.py: It seems that this could be avoided with: Of course, I realize this is not a bug because normally it will just get collected. It is just an extremely minor annoyance because this is currently the only thing still found by DEBUG_LEAK for my program ;) |
If you are able to rebuild Python, have you tried running the ctypes test after rebuilding with this change? And, does the test cover the internal uses of _array_type? |
Tests succeed with this change. There is only one use of _array_type, which is in the same module. This use is presumably tested, because the test fails if I change the line to _array_type = type(Structure). In fact, everything must behave exactly the same after this change, because the two values are identical: >>> from ctypes import *
>>> type(c_int * 3) is type(Array)
True |
Thank you for the test and explanation. There currently is no specific cytpes maintainer. But from what you have said, I might feel comfortable enough applying this, if no one else does, when I have the necessary setup on a new machine. |
I ran full test suit after making the _array_type = type(Array) change and everything passes. I also took a look at this and found additional leak. gc shows this as garbage: [(<class '_ctypes._SimpleCData'>,), <class 'ctypes.c_longdouble'>, <attribute '_ This is all caused by these lines in ctypes __init__.py: class c_longdouble(_SimpleCData):
_type_ = "g"
if sizeof(c_longdouble) == sizeof(c_double):
c_longdouble = c_double For me sizeof(c_longdouble) == sizeof(c_double) (I believe MS compiler always does this) but when we assign c_longdouble = c_double, there is a leak. I removed the alias lines: if sizeof(c_longdouble) == sizeof(c_double):
c_longdouble = c_double And the leak was gone. Looks like changing c_longdouble after declaring it causes a leak. Below for similar aliasing of longlong types, we have this: if _calcsize("l") == _calcsize("q"):
# if long and long long have the same size, make c_longlong an alias for c_long
c_longlong = c_long
c_ulonglong = c_ulong
else:
class c_longlong(_SimpleCData):
_type_ = "q"
_check_size(c_longlong)
class c_ulonglong(_SimpleCData):
_type_ = "Q" This avoids declaring c_longlong and c_ulonglong as class if not needed to. The problem is _calcsize("g") causes an error because "g" is used as long double througout ctypes but _calcsize is function from _struct.c, where "g" (long double) is not defined. Not sure why it isn't... So in short: |
New changeset 205da7a19a78 by Meador Inge in branch '3.2': New changeset b228d9da8bd3 by Meador Inge in branch 'default': New changeset 7cdbf627f958 by Meador Inge in branch '2.7': |
Meador, can we close this issue? |
I wanted to keep it open until the 'long double' problem is fixed as well. |
Looks like the long double issue is still there in 3.11 >>> import gc
>>> gc.set_debug(gc.DEBUG_LEAK)
>>> import ctypes
gc: collectable <function 0x0000026417BBE200>
gc: collectable <tuple 0x0000026417BE0040>
gc: collectable <dict 0x0000026417BC56C0>
gc: collectable <type 0x0000026417AD9840>
gc: collectable <tuple 0x0000026417BD6540>
gc: collectable <getset_descriptor 0x0000026417BD6580>
gc: collectable <getset_descriptor 0x0000026417BD65C0>
gc: collectable <tuple 0x0000026417BE05E0>
gc: collectable <_ctypes.PyCSimpleType 0x0000026417AE4E10>
gc: collectable <tuple 0x0000026417BD96C0>
gc: collectable <getset_descriptor 0x0000026417BE9080>
gc: collectable <getset_descriptor 0x0000026417BE90C0>
gc: collectable <StgDict 0x00000264178A5490>
>>> gc.garbage
[<function _C._m at 0x0000026417BBE200>, (<class 'object'>,), {'__module__': 'types', '_m': <function _C._m at 0x0000026417BBE200>, '__dict__': <attribute '__dict__' of '_C' objects>, '__weakref__': <attribute '__weakref__' of '_C' objects>, '__doc__': None}, <class 'types._C'>, (<class 'types._C'>, <class 'object'>), <attribute '__dict__' of '_C' objects>, <attribute '__weakref__' of '_C' objects>, (<class '_ctypes._SimpleCData'>,), <class 'ctypes.c_longdouble'>, (<class 'ctypes.c_longdouble'>, <class '_ctypes._SimpleCData'>, <class '_ctypes._CData'>, <class 'object'>), <attribute '__dict__' of 'c_longdouble' objects>, <attribute '__weakref__' of 'c_longdouble' objects>, {'__module__': 'ctypes', '_type_': 'g', '__dict__': <attribute '__dict__' of 'c_longdouble' objects>, '__weakref__': <attribute '__weakref__' of 'c_longdouble' objects>, '__doc__': None}] |
The _ctypes extension module could have a dict that maps each format code to its (size, alignment), based on |
The patch has been applied and the problem seems to be solved in 3.13. ╰─$ ./python
Python 3.13.0a0 (heads/main:2b7027d, Jun 2 2023, 23:42:56) [GCC 13.1.1 20230429] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import gc
>>> gc.set_debug(gc.DEBUG_LEAK)
>>> import ctypes
>>> gc.garbage
[]
>>> |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: