|
1 | | -from __future__ import annotations |
2 | | - |
| 1 | +import sys |
3 | 2 | from ctypes import ( |
| 3 | + POINTER, |
| 4 | + PYFUNCTYPE, |
| 5 | + Structure, |
| 6 | + _Pointer, |
4 | 7 | c_char, |
5 | 8 | c_char_p, |
| 9 | + c_int, |
| 10 | + c_ssize_t, |
6 | 11 | c_uint, |
| 12 | + c_uint16, |
| 13 | + c_uint32, |
| 14 | + c_uint8, |
7 | 15 | c_ulong, |
| 16 | + c_void_p, |
| 17 | + cast, |
8 | 18 | pointer, |
| 19 | + py_object, |
9 | 20 | ) |
10 | | -from typing import TypeVar |
11 | | - |
12 | | -from einspect.structs.include.descrobject_h import PyGetSetDef, PyMemberDef |
13 | | -from einspect.structs.include.methodobject_h import PyMethodDef |
14 | | -from einspect.structs.include.object_h import ( |
15 | | - destructor, |
16 | | - getattrfunc, |
17 | | - setattrfunc, |
18 | | - PyAsyncMethods, |
19 | | - reprfunc, |
20 | | - PyNumberMethods, |
21 | | - PySequenceMethods, |
22 | | - PyMappingMethods, |
23 | | - hashfunc, |
24 | | - ternaryfunc, |
25 | | - getattrofunc, |
26 | | - setattrofunc, |
27 | | - PyBufferProcs, |
28 | | - traverseproc, |
29 | | - inquiry, |
30 | | - richcmpfunc, |
31 | | - getiterfunc, |
32 | | - iternextfunc, |
33 | | - descrgetfunc, |
34 | | - descrsetfunc, |
35 | | - initproc, |
36 | | - allocfunc, |
37 | | - newfunc, |
38 | | - freefunc, |
39 | | - vectorcallfunc, |
40 | | -) |
41 | | -from einspect.structs.py_object import PyObject, PyVarObject |
42 | | -from einspect.types import ptr |
43 | | -from typing_extensions import Annotated, Self |
44 | | - |
45 | | -__all__ = ("PyTypeObject",) |
46 | | - |
47 | | -_T = TypeVar("_T") |
48 | | - |
49 | | -DEFAULT = object() |
50 | | - |
51 | | - |
52 | | -# noinspection PyPep8Naming |
53 | | -class PyTypeObject(PyVarObject[_T, None, None]): |
54 | | - """ |
55 | | - Defines a PyTypeObject Structure. |
56 | | -
|
57 | | - https://github.com/python/cpython/blob/3.11/Doc/includes/typestruct.h |
58 | | -
|
59 | | - .. |
60 | | - source: Include/cpython/object.h (struct _typeobject) |
61 | | - """ |
62 | | - |
63 | | - tp_name: Annotated[bytes, c_char_p] |
64 | | - # For allocation |
65 | | - tp_basicsize: int |
66 | | - tp_itemsize: int |
67 | | - # Methods to implement standard operations |
68 | | - tp_dealloc: destructor |
69 | | - tp_vectorcall_offset: int |
70 | | - tp_getattr: getattrfunc |
71 | | - tp_setattr: setattrfunc |
72 | | - # formerly known as tp_compare (Python 2) or tp_reserved (Python 3) |
73 | | - tp_as_async: ptr[PyAsyncMethods] |
74 | | - |
75 | | - tp_repr: reprfunc |
76 | | - |
77 | | - # Method suites for standard classes |
78 | | - tp_as_number: ptr[PyNumberMethods] |
79 | | - tp_as_sequence: ptr[PySequenceMethods] |
80 | | - tp_as_mapping: ptr[PyMappingMethods] |
81 | | - |
82 | | - # More standard operations (here for binary compatibility) |
83 | | - tp_hash: hashfunc |
84 | | - tp_call: ternaryfunc |
85 | | - tp_str: reprfunc |
86 | | - tp_getattro: getattrofunc |
87 | | - tp_setattro: setattrofunc |
88 | | - |
89 | | - # Functions to access object as input/output buffer |
90 | | - tp_as_buffer: ptr[PyBufferProcs] |
91 | | - |
92 | | - # Flags to define presence of optional/expanded features |
93 | | - tp_flags: Annotated[int, c_ulong] |
94 | | - |
95 | | - tp_doc: Annotated[bytes, c_char_p] # Documentation string |
96 | | - |
97 | | - # Assigned meaning in release 2.0 |
98 | | - # call function for all accessible objects |
99 | | - tp_traverse: traverseproc |
100 | | - |
101 | | - tp_clear: inquiry # delete references to contained objects |
102 | | - |
103 | | - # Assigned meaning in release 2.1 |
104 | | - # rich comparisons |
105 | | - tp_richcompare: richcmpfunc |
106 | | - |
107 | | - tp_weaklistoffset: int # weak reference enabler |
108 | | - |
109 | | - # Iterators |
110 | | - tp_iter: getiterfunc |
111 | | - tp_iternext: iternextfunc |
112 | | - |
113 | | - # Attribute descriptor and subclassing stuff |
114 | | - tp_methods: ptr[PyMethodDef] |
115 | | - tp_members: ptr[PyMemberDef] |
116 | | - tp_getset: ptr[PyGetSetDef] |
117 | | - |
118 | | - # Strong reference on a heap type, borrowed reference on a static type |
119 | | - tp_base: pointer[Self] |
120 | | - tp_dict: ptr[PyObject] |
121 | | - tp_descr_get: descrgetfunc |
122 | | - tp_descr_set: descrsetfunc |
123 | | - tp_dictoffset: int |
124 | | - tp_init: initproc |
125 | | - tp_alloc: allocfunc |
126 | | - tp_new: newfunc |
127 | | - tp_free: freefunc # Low-level free-memory routine |
128 | | - tp_is_gc: inquiry # For PyObject_IS_GC |
129 | | - tp_bases: ptr[PyObject] |
130 | | - tp_mro: ptr[PyObject] # method resolution order |
131 | | - tp_cache: ptr[PyObject] |
132 | | - tp_subclasses: ptr[PyObject] # for static builtin types this is an index |
133 | | - tp_weaklist: ptr[PyObject] |
134 | | - tp_del: destructor |
135 | | - |
136 | | - # Type attribute cache version tag. Added in version 2.6 |
137 | | - tp_version_tag: Annotated[int, c_uint] |
138 | | - |
139 | | - tp_finalize: destructor |
140 | | - tp_vectorcall: vectorcallfunc |
141 | | - |
142 | | - # bitset of which type-watchers care about this type |
143 | | - tp_watched: c_char |
144 | | - |
145 | | - |
146 | | -# https://github.com/python/cpython/blob/809aa9a682fc865f7502e7421da0a74d204aab6d/Objects/typevarobject.c#L29 |
147 | | -class PyTypeVarObject(PyVarObject[_T, None, None]): |
148 | | - name: ptr[PyObject] |
149 | | - # not sure why but this is the only thing that works but that's fine because it's the only thing we need |
150 | | - bound: ptr[PyObject] |
| 21 | +from typing import get_origin, _SpecialForm, _GenericAlias |
| 22 | + |
| 23 | + |
| 24 | +_SelfPtr = object() |
| 25 | + |
| 26 | + |
| 27 | +@_SpecialForm |
| 28 | +def Self(self, parameters): |
| 29 | + raise TypeError(f"{self} is not subscriptable") |
| 30 | + |
| 31 | + |
| 32 | +class _Ptr(_Pointer): |
| 33 | + def __new__(cls, *args, **kwargs): |
| 34 | + return pointer(*args, **kwargs) |
| 35 | + |
| 36 | + def __class_getitem__(cls, item): |
| 37 | + """Return a `ctypes.POINTER` of the given type.""" |
| 38 | + # For ptr[Self], return a special object |
| 39 | + if item is Self: |
| 40 | + return _SelfPtr |
| 41 | + |
| 42 | + # Get base of generic alias |
| 43 | + # noinspection PyUnresolvedReferences, PyProtectedMember |
| 44 | + if isinstance(item, _GenericAlias): |
| 45 | + item = get_origin(item) |
| 46 | + |
| 47 | + try: |
| 48 | + return POINTER(item) |
| 49 | + except TypeError as e: |
| 50 | + raise TypeError(f"{e} (During POINTER({item}))") from e |
| 51 | + |
| 52 | + |
| 53 | +def address(obj) -> int: |
| 54 | + source = py_object(obj) |
| 55 | + addr = c_void_p.from_buffer(source).value |
| 56 | + if addr is None: |
| 57 | + raise ValueError("address: NULL object") # pragma: no cover |
| 58 | + return addr |
| 59 | + |
| 60 | + |
| 61 | +# https://github.com/python/cpython/blob/3.11/Include/object.h#L196-L227 |
| 62 | +unaryfunc = PYFUNCTYPE(py_object, py_object) |
| 63 | +binaryfunc = PYFUNCTYPE(py_object, py_object, py_object) |
| 64 | +ternaryfunc = PYFUNCTYPE(py_object, py_object, py_object, py_object) |
| 65 | +inquiry = PYFUNCTYPE(c_int, py_object) |
| 66 | +lenfunc = PYFUNCTYPE(c_ssize_t, py_object) |
| 67 | +ssizeargfunc = PYFUNCTYPE(py_object, py_object, c_ssize_t) |
| 68 | +ssizessizeargfunc = PYFUNCTYPE(py_object, py_object, c_ssize_t, c_ssize_t) |
| 69 | +ssizeobjargproc = PYFUNCTYPE(c_int, py_object, c_ssize_t, py_object) |
| 70 | +ssizessizeobjargproc = PYFUNCTYPE(c_int, py_object, c_ssize_t, c_ssize_t, py_object) |
| 71 | +objobjargproc = PYFUNCTYPE(c_int, py_object, py_object, py_object) |
| 72 | + |
| 73 | +objobjproc = PYFUNCTYPE(c_int, py_object, py_object) |
| 74 | +visitproc = PYFUNCTYPE(c_int, py_object, c_void_p) |
| 75 | +traverseproc = PYFUNCTYPE(c_int, py_object, visitproc, c_void_p) |
| 76 | + |
| 77 | +freefunc = PYFUNCTYPE(None, c_void_p) |
| 78 | +destructor = PYFUNCTYPE(None, py_object) |
| 79 | +getattrfunc = PYFUNCTYPE(py_object, py_object, c_char_p) |
| 80 | +getattrofunc = PYFUNCTYPE(py_object, py_object, py_object) |
| 81 | +setattrfunc = PYFUNCTYPE(c_int, py_object, c_char_p, py_object) |
| 82 | +setattrofunc = PYFUNCTYPE(c_int, py_object, py_object, py_object) |
| 83 | +reprfunc = PYFUNCTYPE(py_object, py_object) |
| 84 | +hashfunc = PYFUNCTYPE(c_ssize_t, py_object) |
| 85 | +richcmpfunc = PYFUNCTYPE(py_object, py_object, py_object, c_int) |
| 86 | +getiterfunc = PYFUNCTYPE(py_object, py_object) |
| 87 | +iternextfunc = PYFUNCTYPE(py_object, py_object) |
| 88 | + |
| 89 | +descrgetfunc = PYFUNCTYPE(py_object, py_object, py_object, py_object) |
| 90 | +descrsetfunc = PYFUNCTYPE(c_int, py_object, py_object, py_object) |
| 91 | +initproc = PYFUNCTYPE(c_int, py_object, py_object, py_object) |
| 92 | +newfunc = PYFUNCTYPE(py_object, py_object, py_object, py_object) |
| 93 | +allocfunc = PYFUNCTYPE(py_object, py_object, c_ssize_t) |
| 94 | + |
| 95 | +# PyObject *(*vectorcallfunc)(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwnames) |
| 96 | +vectorcallfunc = PYFUNCTYPE(py_object, py_object, _Ptr[py_object], c_ssize_t, py_object) |
| 97 | +# PySendResult (*sendfunc)(PyObject *iter, PyObject *value, PyObject **result) |
| 98 | +sendfunc = PYFUNCTYPE(c_int, py_object, py_object, _Ptr[py_object]) |
| 99 | + |
| 100 | + |
| 101 | +def _is_gil_enabled(): |
| 102 | + try: |
| 103 | + return sys._is_gil_enabled() |
| 104 | + except: |
| 105 | + return True |
| 106 | + |
| 107 | + |
| 108 | +if _is_gil_enabled(): |
| 109 | + _py_object_fields = [ |
| 110 | + ("ob_refcnt", c_ssize_t), |
| 111 | + ("ob_type", _Ptr[py_object]), |
| 112 | + ] |
| 113 | +else: |
| 114 | + # https://github.com/python/cpython/blob/main/Include/object.h#L168 |
| 115 | + _py_object_fields = [ |
| 116 | + ("ob_tid", c_ssize_t), |
| 117 | + ("ob_flags", c_uint16), |
| 118 | + # https://github.com/python/cpython/blob/main/Include/cpython/pylock.h#L29 |
| 119 | + ("ob_mutex", c_uint8), |
| 120 | + ("ob_gc_bits", c_uint8), |
| 121 | + ("ob_ref_local", c_uint32), |
| 122 | + ("ob_ref_shared", c_ssize_t), |
| 123 | + ("ob_type", _Ptr[py_object]), |
| 124 | + ] |
| 125 | + |
| 126 | + |
| 127 | +class PyObject(Structure): |
| 128 | + _fields_ = _py_object_fields |
| 129 | + |
| 130 | + def as_ref(self) -> _Ptr[Self]: |
| 131 | + """Return a pointer to the Structure.""" |
| 132 | + return pointer(self) # type: ignore |
| 133 | + |
| 134 | + def into_object(self): |
| 135 | + """Cast the PyObject into a Python object.""" |
| 136 | + py_obj = cast(self.as_ref(), py_object) |
| 137 | + return py_obj.value |
| 138 | + |
| 139 | + |
| 140 | +# https://github.com/python/cpython/blob/3.11/Doc/includes/typestruct.h |
| 141 | +class PyTypeObject(Structure): |
| 142 | + _fields_ = _py_object_fields + [ |
| 143 | + ("ob_size", c_ssize_t), |
| 144 | + ("tp_name", c_char_p), |
| 145 | + ("tp_basicsize", c_ssize_t), |
| 146 | + ("tp_itemsize", c_ssize_t), |
| 147 | + ("tp_dealloc", destructor), |
| 148 | + ("tp_vectorcall_offset", c_ssize_t), |
| 149 | + ("tp_getattr", getattrfunc), |
| 150 | + ("tp_setattr", setattrfunc), |
| 151 | + # ("tp_as_async", _Ptr[PyAsyncMethods]), |
| 152 | + ("tp_as_async", _Ptr[PyObject]), |
| 153 | + ("tp_repr", reprfunc), |
| 154 | + # ("tp_as_number", _Ptr[PyNumberMethods]), |
| 155 | + ("tp_as_number", _Ptr[PyObject]), |
| 156 | + # ("tp_as_sequence", _Ptr[PySequenceMethods]), |
| 157 | + ("tp_as_sequence", _Ptr[PyObject]), |
| 158 | + # ("tp_as_mapping", _Ptr[PyMappingMethods]), |
| 159 | + ("tp_as_mapping", _Ptr[PyObject]), |
| 160 | + ("tp_hash", hashfunc), |
| 161 | + ("tp_call", ternaryfunc), |
| 162 | + ("tp_str", reprfunc), |
| 163 | + ("tp_getattro", getattrofunc), |
| 164 | + ("tp_setattro", setattrofunc), |
| 165 | + # ("tp_as_buffer", _Ptr[PyBufferProcs]), |
| 166 | + ("tp_as_buffer", _Ptr[PyObject]), |
| 167 | + ("tp_flags", c_ulong), |
| 168 | + ("tp_doc", c_char_p), |
| 169 | + ("tp_traverse", traverseproc), |
| 170 | + ("tp_clear", inquiry), |
| 171 | + ("tp_richcompare", richcmpfunc), |
| 172 | + ("tp_weaklistoffset", c_ssize_t), |
| 173 | + ("tp_iter", getiterfunc), |
| 174 | + ("tp_iternext", iternextfunc), |
| 175 | + ("tp_methods", _Ptr[PyObject]), |
| 176 | + ("tp_members", _Ptr[PyObject]), |
| 177 | + ("tp_getset", _Ptr[PyObject]), |
| 178 | + # ("tp_base", _Ptr[PyTypeObject]), |
| 179 | + ("tp_base", _Ptr[PyObject]), |
| 180 | + ("tp_dict", _Ptr[PyObject]), |
| 181 | + ("tp_descr_get", descrgetfunc), |
| 182 | + ("tp_descr_set", descrsetfunc), |
| 183 | + ("tp_dictoffset", c_ssize_t), |
| 184 | + ("tp_init", initproc), |
| 185 | + ("tp_alloc", allocfunc), |
| 186 | + ("tp_new", newfunc), |
| 187 | + ("tp_free", freefunc), |
| 188 | + ("tp_is_gc", inquiry), |
| 189 | + ("tp_bases", _Ptr[PyObject]), |
| 190 | + ("tp_mro", _Ptr[PyObject]), |
| 191 | + ("tp_cache", _Ptr[PyObject]), |
| 192 | + ("tp_subclasses", _Ptr[PyObject]), |
| 193 | + ("tp_weaklist", _Ptr[PyObject]), |
| 194 | + ("tp_del", destructor), |
| 195 | + ("tp_version_tag", c_uint), |
| 196 | + ("tp_finalize", descrsetfunc), |
| 197 | + ("tp_vectorcall", vectorcallfunc), |
| 198 | + ("tp_watched", c_char), |
| 199 | + ] |
| 200 | + |
| 201 | + @classmethod |
| 202 | + def from_object(cls, obj) -> Self: |
| 203 | + """Create a PyObject from an object.""" |
| 204 | + return cls.from_address(address(obj)) |
| 205 | + |
| 206 | + |
| 207 | +class PyTypeVarObject(Structure): |
| 208 | + _fields_ = _py_object_fields + [ |
| 209 | + ("ob_size", c_ssize_t), |
| 210 | + ("name", _Ptr[PyObject]), |
| 211 | + # not sure why but this is the only thing that works but that's fine because it's the only thing we need |
| 212 | + ("bound", _Ptr[PyObject]), |
| 213 | + ] |
| 214 | + |
| 215 | + @classmethod |
| 216 | + def from_object(cls, obj) -> Self: |
| 217 | + """Create a PyObject from an object.""" |
| 218 | + return cls.from_address(address(obj)) |
151 | 219 |
|
152 | 220 |
|
153 | 221 | if __name__ == "__main__": |
154 | | - print(PyTypeObject.from_object(type(3.14))) |
| 222 | + # for k, v in PyObject._fields_: |
| 223 | + # print(f"('{k}', {v.__name__}),") |
| 224 | + # for k, v in PyVarObject._fields_: |
| 225 | + # print(f"('{k}', {v.__name__}),") |
| 226 | + # for k, v in PyTypeObject._fields_: |
| 227 | + # print(f"('{k}', {v.__name__}),") |
| 228 | + print(PyTypeObject.from_object(type(3.14)).tp_name) |
| 229 | + print(PyTypeObject.from_object(type(3.14)).tp_base) |
| 230 | + print(PyTypeObject.from_object(type(3.14)).ob_type) |
| 231 | + for k, v in PyTypeVarObject._fields_: |
| 232 | + print(f"('{k}', {v.__name__}),") |
0 commit comments