Skip to content

gh-100926: Move ctype's pointers cache to StgInfo #131282

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

Merged
merged 77 commits into from
May 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
87a974b
Move ctype's pointers cache to StgInfo
sergey-miryanov Mar 15, 2025
12600a6
Fix PyCStgInfo_clone for pointer_type
sergey-miryanov Mar 15, 2025
2b0d69f
Merge branch 'main' into gh-100926-ctypes-pointers-cache
sergey-miryanov Mar 15, 2025
df2167a
No need to use _ctypes_ptrtype_cache for create_pointer_inst
sergey-miryanov Mar 15, 2025
f96ae59
Remove unused st from create_pointer_inst
sergey-miryanov Mar 15, 2025
481cf59
It is better to check local cache first
sergey-miryanov Mar 16, 2025
96b4dd1
Update Modules/_ctypes/callproc.c
sergey-miryanov Mar 17, 2025
4fb5baa
Update Modules/_ctypes/callproc.c
sergey-miryanov Mar 17, 2025
758045f
Add news and whatsnew entries
sergey-miryanov Mar 17, 2025
2f9285f
Update Modules/_ctypes/_ctypes.c
sergey-miryanov Mar 18, 2025
b070ad5
Arrange pointer_type declaration
sergey-miryanov Mar 18, 2025
ef1e633
Use assertIs to check pointer types in tests
sergey-miryanov Mar 18, 2025
6ea410d
Merge branch 'main' into gh-100926-ctypes-pointers-cache
sergey-miryanov Mar 21, 2025
3754d3d
Implement pointer-type creation via __pointer_type__
sergey-miryanov Mar 21, 2025
10e36e4
Fix _CType_Type
sergey-miryanov Mar 22, 2025
29bfe9c
Update Lib/test/test_ctypes/test_c_simple_type_meta.py
sergey-miryanov Mar 22, 2025
f8139ff
Fix POINTER and test_creating_pointer_in_dunder_new_1
sergey-miryanov Mar 22, 2025
e1aaf45
Do not share pointer_type via StgInfo clone
sergey-miryanov Mar 23, 2025
82f74ec
Test if PyCStgInfo_clone not share pointer_type cache
sergey-miryanov Mar 23, 2025
eacc724
Simplify POINTER
sergey-miryanov Mar 23, 2025
0b373d5
Add some extra checks and tests
sergey-miryanov Mar 23, 2025
9c49abd
Fix tests for ct_meta with init
sergey-miryanov Mar 24, 2025
3c38aa5
Do not use Py_XSETREF if dst already NULL
sergey-miryanov Mar 24, 2025
efd4961
Merge branch 'main' into gh-100926-ctypes-pointers-cache
sergey-miryanov Mar 24, 2025
e251a7d
Add docstrings for POINTER and pointer
sergey-miryanov Mar 24, 2025
cdba1f6
Remove import of c-versions of POINTER and pointer
sergey-miryanov Mar 24, 2025
a241cd9
Remove c-versions of POINTER and pointer
sergey-miryanov Mar 24, 2025
8d85624
Remove c-versions of _pointer_type_cache/_ctypes_ptrtype_cache
sergey-miryanov Mar 24, 2025
aedc4b2
Add extra test for set_type/PyCPointerType_SetProto
sergey-miryanov Mar 24, 2025
6ac84c7
Add test for creating types with factory
sergey-miryanov Mar 24, 2025
2373c63
Fix docstrings
sergey-miryanov Mar 25, 2025
fc93bc8
Add some tests for pointer
sergey-miryanov Mar 25, 2025
5768347
Update news and whatsnew
sergey-miryanov Mar 25, 2025
a2cff24
Merge branch 'main' into gh-100926-ctypes-pointers-cache
sergey-miryanov Mar 26, 2025
87f8cf3
Fix tests
sergey-miryanov Mar 26, 2025
5b9891f
Merge branch 'main' into gh-100926-ctypes-pointers-cache
sergey-miryanov Mar 26, 2025
85ff1a0
Add few more tests
sergey-miryanov Mar 26, 2025
d11af80
Add some more tests
sergey-miryanov Mar 26, 2025
6dd1e1a
Update docs
sergey-miryanov Mar 26, 2025
33ae038
Update news
sergey-miryanov Mar 26, 2025
08bdada
Fix news
sergey-miryanov Mar 26, 2025
8505d4b
Add some more tests
sergey-miryanov Mar 27, 2025
c1bf7cc
Add more tests for metabases pointers
sergey-miryanov Mar 30, 2025
ea0bf20
Merge branch 'main' into gh-100926-ctypes-pointers-cache
sergey-miryanov Mar 30, 2025
fc3bc33
Merge branch 'main' into gh-100926-ctypes-pointers-cache
sergey-miryanov Mar 30, 2025
62d2deb
Try to add thread safety to pointer_type
sergey-miryanov Mar 30, 2025
360303f
Revert "Try to add thread safety to pointer_type"
sergey-miryanov Mar 31, 2025
a2cc961
Add set_non_ctypes_pointer_type
sergey-miryanov Apr 1, 2025
df56541
Add some new test and docstrings for tests
sergey-miryanov Apr 2, 2025
283bf96
Merge branch 'main' into gh-100926-ctypes-pointers-cache
sergey-miryanov Apr 2, 2025
7d42666
Address some neonene comments
sergey-miryanov Apr 3, 2025
0bb389e
Merge branch 'main' into gh-100926-ctypes-pointers-cache
sergey-miryanov Apr 26, 2025
e31c7e9
Move __pointer_type__ docs to common class variables section
sergey-miryanov Apr 26, 2025
56d41e2
Apply suggestions from code review
sergey-miryanov Apr 30, 2025
4316ad9
Remove Pointers for ctypes-like types section
sergey-miryanov Apr 30, 2025
9c99f9c
Update news entry
sergey-miryanov Apr 30, 2025
5753fc9
Update docstring for POINTER and add comments
sergey-miryanov Apr 30, 2025
58a1507
Implement _pointer_type_cache as PointerTypeCache proxy
sergey-miryanov Apr 30, 2025
7082009
Raise AttributeError if __pointer_type__ is not set
sergey-miryanov Apr 30, 2025
f89da96
Fixed POINTER if __pointer_type__ already initialized
sergey-miryanov May 1, 2025
1afa7b3
Fixed default value for PointerTypeCache.get
sergey-miryanov May 1, 2025
e99277c
Remove setting item in _pointer_type_cache from SetPointerType
sergey-miryanov May 1, 2025
ebed23e
tests: Remove teardown code that now does nothing
encukou Apr 30, 2025
c4ee75a
Remove _pointer_type_cache setting
encukou Apr 30, 2025
66c68b0
Handle old-style incomplete types
encukou May 1, 2025
0d2c75b
Document deprecations
encukou May 1, 2025
8597f6b
Allow setting/deleting __pointer_type__
encukou May 1, 2025
0b5de27
Allow arbitrary set_type as before
encukou May 1, 2025
78b6c15
Apply suggestions from code review
sergey-miryanov May 1, 2025
dfd529c
Merge encukou seggestions from PR
sergey-miryanov May 2, 2025
2fc600b
Fix typos in test_pointer_set_wrong_type test
sergey-miryanov May 2, 2025
cb78a03
set_pointer for type should reset type cache
sergey-miryanov May 2, 2025
4ef57e9
Merge branch 'main' into gh-100926-ctypes-pointers-cache
sergey-miryanov May 2, 2025
b33890d
get and getitem should consistent
sergey-miryanov May 2, 2025
0acc2b2
Merge branch 'main' into gh-100926-ctypes-pointers-cache
encukou May 2, 2025
3129ef5
Wording tweak
encukou May 2, 2025
8efa28a
Apply suggestions from code review
encukou May 2, 2025
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
22 changes: 21 additions & 1 deletion Doc/library/ctypes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2172,10 +2172,20 @@ Utility functions

.. function:: POINTER(type, /)

Create and return a new ctypes pointer type. Pointer types are cached and
Create or return a ctypes pointer type. Pointer types are cached and
reused internally, so calling this function repeatedly is cheap.
*type* must be a ctypes type.

.. impl-detail::

The resulting pointer type is cached in the ``__pointer_type__``
attribute of *type*.
It is possible to set this attribute before the first call to
``POINTER`` in order to set a custom pointer type.
However, doing this is discouraged: manually creating a suitable
pointer type is difficult without relying on implementation
details that may change in future Python versions.


.. function:: pointer(obj, /)

Expand Down Expand Up @@ -2340,6 +2350,16 @@ Data types
library. *name* is the name of the symbol that exports the data, *library*
is the loaded shared library.

Common class variables of ctypes data types:

.. attribute:: __pointer_type__

The pointer type that was created by calling
:func:`POINTER` for corresponding ctypes data type. If a pointer type
was not yet created, the attribute is missing.

.. versionadded:: next

Common instance variables of ctypes data types:

.. attribute:: _b_base_
Expand Down
14 changes: 13 additions & 1 deletion Doc/whatsnew/3.14.rst
Original file line number Diff line number Diff line change
Expand Up @@ -803,11 +803,16 @@ ctypes
loaded by the current process.
(Contributed by Brian Ward in :gh:`119349`.)

* Move :func:`ctypes.POINTER` types cache from a global internal cache
(``_pointer_type_cache``) to the :attr:`ctypes._CData.__pointer_type__`
attribute of the corresponding :mod:`ctypes` types.
This will stop the cache from growing without limits in some situations.
(Contributed by Sergey Miryanov in :gh:`100926`).

* The :class:`ctypes.py_object` type now supports subscription,
making it a :term:`generic type`.
(Contributed by Brian Schubert in :gh:`132168`.)


datetime
--------

Expand Down Expand Up @@ -1675,6 +1680,13 @@ Deprecated
:func:`codecs.open` is now deprecated. Use :func:`open` instead.
(Contributed by Inada Naoki in :gh:`133036`.)

* :mod:`ctypes`:
Calling :func:`ctypes.POINTER` on a string is deprecated.
Use :ref:`ctypes-incomplete-types` for self-referential structures.
Also, the internal ``ctypes._pointer_type_cache`` is deprecated.
See :func:`ctypes.POINTER` for updated implementation details.
(Contributed by Sergey Myrianov in :gh:`100926`.)

* :mod:`functools`:
Calling the Python implementation of :func:`functools.reduce` with *function*
or *sequence* as keyword arguments is now deprecated.
Expand Down
76 changes: 67 additions & 9 deletions Lib/ctypes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,72 @@ class c_void_p(_SimpleCData):
class c_bool(_SimpleCData):
_type_ = "?"

from _ctypes import POINTER, pointer, _pointer_type_cache
def POINTER(cls):
"""Create and return a new ctypes pointer type.

Pointer types are cached and reused internally,
so calling this function repeatedly is cheap.
"""
if cls is None:
return c_void_p
try:
return cls.__pointer_type__
except AttributeError:
pass
if isinstance(cls, str):
# handle old-style incomplete types (see test_ctypes.test_incomplete)
import warnings
warnings._deprecated("ctypes.POINTER with string", remove=(3, 19))
try:
return _pointer_type_cache_fallback[cls]
except KeyError:
result = type(f'LP_{cls}', (_Pointer,), {})
_pointer_type_cache_fallback[cls] = result
return result

# create pointer type and set __pointer_type__ for cls
return type(f'LP_{cls.__name__}', (_Pointer,), {'_type_': cls})

def pointer(obj):
"""Create a new pointer instance, pointing to 'obj'.

The returned object is of the type POINTER(type(obj)). Note that if you
just want to pass a pointer to an object to a foreign function call, you
should use byref(obj) which is much faster.
"""
typ = POINTER(type(obj))
return typ(obj)

class _PointerTypeCache:
def __setitem__(self, cls, pointer_type):
import warnings
warnings._deprecated("ctypes._pointer_type_cache", remove=(3, 19))
try:
cls.__pointer_type__ = pointer_type
except AttributeError:
_pointer_type_cache_fallback[cls] = pointer_type

def __getitem__(self, cls):
import warnings
warnings._deprecated("ctypes._pointer_type_cache", remove=(3, 19))
try:
return cls.__pointer_type__
except AttributeError:
return _pointer_type_cache_fallback[cls]

def get(self, cls, default=None):
import warnings
warnings._deprecated("ctypes._pointer_type_cache", remove=(3, 19))
try:
return cls.__pointer_type__
except AttributeError:
return _pointer_type_cache_fallback.get(cls, default)

def __contains__(self, cls):
return hasattr(cls, '__pointer_type__')

_pointer_type_cache_fallback = {}
_pointer_type_cache = _PointerTypeCache()

class c_wchar_p(_SimpleCData):
_type_ = "Z"
Expand All @@ -277,15 +342,14 @@ class c_wchar(_SimpleCData):
_type_ = "u"

def _reset_cache():
_pointer_type_cache.clear()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not worth _pointer_type_cache_fallback.clear()?

_pointer_type_cache_fallback.clear()
_c_functype_cache.clear()
if _os.name == "nt":
_win_functype_cache.clear()
# _SimpleCData.c_wchar_p_from_param
POINTER(c_wchar).from_param = c_wchar_p.from_param
# _SimpleCData.c_char_p_from_param
POINTER(c_char).from_param = c_char_p.from_param
_pointer_type_cache[None] = c_void_p

def create_unicode_buffer(init, size=None):
"""create_unicode_buffer(aString) -> character array
Expand Down Expand Up @@ -319,13 +383,7 @@ def create_unicode_buffer(init, size=None):
def SetPointerType(pointer, cls):
import warnings
warnings._deprecated("ctypes.SetPointerType", remove=(3, 15))
if _pointer_type_cache.get(cls, None) is not None:
raise RuntimeError("This type already exists in the cache")
if id(pointer) not in _pointer_type_cache:
raise RuntimeError("What's this???")
pointer.set_type(cls)
_pointer_type_cache[cls] = pointer
del _pointer_type_cache[id(pointer)]

def ARRAY(typ, len):
return typ * len
Expand Down
2 changes: 0 additions & 2 deletions Lib/test/test_ctypes/test_byteswap.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,6 @@ class TestStructure(parent):
self.assertEqual(len(data), sizeof(TestStructure))
ptr = POINTER(TestStructure)
s = cast(data, ptr)[0]
del ctypes._pointer_type_cache[TestStructure]
self.assertEqual(s.point.x, 1)
self.assertEqual(s.point.y, 2)

Expand Down Expand Up @@ -371,7 +370,6 @@ class TestUnion(parent):
self.assertEqual(len(data), sizeof(TestUnion))
ptr = POINTER(TestUnion)
s = cast(data, ptr)[0]
del ctypes._pointer_type_cache[TestUnion]
self.assertEqual(s.point.x, 1)
self.assertEqual(s.point.y, 2)

Expand Down
Loading
Loading