|  | 
| 1 |  | -from __future__ import annotations | 
| 2 |  | - | 
| 3 | 1 | from ctypes import ( | 
| 4 | 2 |     c_char, | 
| 5 | 3 |     c_char_p, | 
| 6 | 4 |     c_uint, | 
| 7 | 5 |     c_ulong, | 
| 8 | 6 |     pointer, | 
|  | 7 | +    Structure, | 
|  | 8 | +    _Pointer, | 
|  | 9 | +    PYFUNCTYPE, | 
|  | 10 | +    py_object, | 
|  | 11 | +    c_int, | 
|  | 12 | +    c_void_p, | 
|  | 13 | +    c_ssize_t, | 
|  | 14 | +    POINTER, | 
|  | 15 | +    cast, | 
| 9 | 16 | ) | 
| 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, | 
|  | 17 | +from typing import get_origin, _SpecialForm, _GenericAlias | 
|  | 18 | + | 
|  | 19 | +from _ctypes import sizeof | 
|  | 20 | + | 
|  | 21 | +Py_ssize_t = c_ssize_t | 
|  | 22 | + | 
|  | 23 | +_SelfPtr = object() | 
|  | 24 | + | 
|  | 25 | + | 
|  | 26 | +@_SpecialForm | 
|  | 27 | +def Self(self, parameters): | 
|  | 28 | +    raise TypeError(f"{self} is not subscriptable") | 
|  | 29 | + | 
|  | 30 | + | 
|  | 31 | +class _Ptr(_Pointer): | 
|  | 32 | +    def __new__(cls, *args, **kwargs): | 
|  | 33 | +        return pointer(*args, **kwargs) | 
|  | 34 | + | 
|  | 35 | +    def __class_getitem__(cls, item): | 
|  | 36 | +        """Return a `ctypes.POINTER` of the given type.""" | 
|  | 37 | +        # For ptr[Self], return a special object | 
|  | 38 | +        if item is Self: | 
|  | 39 | +            return _SelfPtr | 
|  | 40 | + | 
|  | 41 | +        # Get base of generic alias | 
|  | 42 | +        # noinspection PyUnresolvedReferences, PyProtectedMember | 
|  | 43 | +        if isinstance(item, _GenericAlias): | 
|  | 44 | +            item = get_origin(item) | 
|  | 45 | + | 
|  | 46 | +        try: | 
|  | 47 | +            return POINTER(item) | 
|  | 48 | +        except TypeError as e: | 
|  | 49 | +            raise TypeError(f"{e} (During POINTER({item}))") from e | 
|  | 50 | + | 
|  | 51 | + | 
|  | 52 | +def address(obj) -> int: | 
|  | 53 | +    source = py_object(obj) | 
|  | 54 | +    addr = c_void_p.from_buffer(source).value | 
|  | 55 | +    if addr is None: | 
|  | 56 | +        raise ValueError("address: NULL object")  # pragma: no cover | 
|  | 57 | +    return addr | 
|  | 58 | + | 
|  | 59 | + | 
|  | 60 | +# https://github.com/python/cpython/blob/3.11/Include/object.h#L196-L227 | 
|  | 61 | +unaryfunc = PYFUNCTYPE(py_object, py_object) | 
|  | 62 | +binaryfunc = PYFUNCTYPE(py_object, py_object, py_object) | 
|  | 63 | +ternaryfunc = PYFUNCTYPE(py_object, py_object, py_object, py_object) | 
|  | 64 | +inquiry = PYFUNCTYPE(c_int, py_object) | 
|  | 65 | +lenfunc = PYFUNCTYPE(Py_ssize_t, py_object) | 
|  | 66 | +ssizeargfunc = PYFUNCTYPE(py_object, py_object, Py_ssize_t) | 
|  | 67 | +ssizessizeargfunc = PYFUNCTYPE(py_object, py_object, Py_ssize_t, Py_ssize_t) | 
|  | 68 | +ssizeobjargproc = PYFUNCTYPE(c_int, py_object, Py_ssize_t, py_object) | 
|  | 69 | +ssizessizeobjargproc = PYFUNCTYPE(c_int, py_object, Py_ssize_t, Py_ssize_t, py_object) | 
|  | 70 | +objobjargproc = PYFUNCTYPE(c_int, py_object, py_object, py_object) | 
|  | 71 | + | 
|  | 72 | +objobjproc = PYFUNCTYPE(c_int, py_object, py_object) | 
|  | 73 | +visitproc = PYFUNCTYPE(c_int, py_object, c_void_p) | 
|  | 74 | +traverseproc = PYFUNCTYPE(c_int, py_object, visitproc, c_void_p) | 
|  | 75 | + | 
|  | 76 | +freefunc = PYFUNCTYPE(None, c_void_p) | 
|  | 77 | +destructor = PYFUNCTYPE(None, py_object) | 
|  | 78 | +getattrfunc = PYFUNCTYPE(py_object, py_object, c_char_p) | 
|  | 79 | +getattrofunc = PYFUNCTYPE(py_object, py_object, py_object) | 
|  | 80 | +setattrfunc = PYFUNCTYPE(c_int, py_object, c_char_p, py_object) | 
|  | 81 | +setattrofunc = PYFUNCTYPE(c_int, py_object, py_object, py_object) | 
|  | 82 | +reprfunc = PYFUNCTYPE(py_object, py_object) | 
|  | 83 | +hashfunc = PYFUNCTYPE(Py_ssize_t, py_object) | 
|  | 84 | +richcmpfunc = PYFUNCTYPE(py_object, py_object, py_object, c_int) | 
|  | 85 | +getiterfunc = PYFUNCTYPE(py_object, py_object) | 
|  | 86 | +iternextfunc = PYFUNCTYPE(py_object, py_object) | 
|  | 87 | + | 
|  | 88 | +descrgetfunc = PYFUNCTYPE(py_object, py_object, py_object, py_object) | 
|  | 89 | +descrsetfunc = PYFUNCTYPE(c_int, py_object, py_object, py_object) | 
|  | 90 | +initproc = PYFUNCTYPE(c_int, py_object, py_object, py_object) | 
|  | 91 | +newfunc = PYFUNCTYPE(py_object, py_object, py_object, py_object) | 
|  | 92 | +allocfunc = PYFUNCTYPE(py_object, py_object, Py_ssize_t) | 
|  | 93 | + | 
|  | 94 | +# PyObject *(*vectorcallfunc)(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwnames) | 
|  | 95 | +vectorcallfunc = PYFUNCTYPE( | 
|  | 96 | +    py_object, py_object, _Ptr[py_object], Py_ssize_t, py_object | 
| 40 | 97 | ) | 
| 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 | 
|  | 98 | +# PySendResult (*sendfunc)(PyObject *iter, PyObject *value, PyObject **result) | 
|  | 99 | +sendfunc = PYFUNCTYPE(c_int, py_object, py_object, _Ptr[py_object]) | 
|  | 100 | + | 
|  | 101 | + | 
|  | 102 | +class PyObject(Structure): | 
|  | 103 | +    _fields_ = [ | 
|  | 104 | +        ("ob_refcnt", c_ssize_t), | 
|  | 105 | +        ("ob_type", _Ptr[py_object]), | 
|  | 106 | +    ] | 
|  | 107 | + | 
|  | 108 | +    def as_ref(self) -> _Ptr[Self]: | 
|  | 109 | +        """Return a pointer to the Structure.""" | 
|  | 110 | +        return pointer(self)  # type: ignore | 
|  | 111 | + | 
|  | 112 | +    def into_object(self): | 
|  | 113 | +        """Cast the PyObject into a Python object.""" | 
|  | 114 | +        py_obj = cast(self.as_ref(), py_object) | 
|  | 115 | +        return py_obj.value | 
|  | 116 | + | 
|  | 117 | + | 
|  | 118 | +class PyTypeObject(Structure): | 
|  | 119 | +    _fields_ = [ | 
|  | 120 | +        ("ob_refcnt", c_ssize_t), | 
|  | 121 | +        # ("ob_type", LP_PyTypeObject), | 
|  | 122 | +        ("ob_type", _Ptr[PyObject]), | 
|  | 123 | +        ("ob_size", c_ssize_t), | 
|  | 124 | +        ("tp_name", c_char_p), | 
|  | 125 | +        ("tp_basicsize", c_ssize_t), | 
|  | 126 | +        ("tp_itemsize", c_ssize_t), | 
|  | 127 | +        ("tp_dealloc", destructor), | 
|  | 128 | +        ("tp_vectorcall_offset", c_ssize_t), | 
|  | 129 | +        ("tp_getattr", getattrfunc), | 
|  | 130 | +        ("tp_setattr", setattrfunc), | 
|  | 131 | +        # ("tp_as_async", _Ptr[PyAsyncMethods]), | 
|  | 132 | +        ("tp_as_async", _Ptr[PyObject]), | 
|  | 133 | +        ("tp_repr", reprfunc), | 
|  | 134 | +        # ("tp_as_number", _Ptr[PyNumberMethods]), | 
|  | 135 | +        ("tp_as_number", _Ptr[PyObject]), | 
|  | 136 | +        # ("tp_as_sequence", _Ptr[PySequenceMethods]), | 
|  | 137 | +        ("tp_as_sequence", _Ptr[PyObject]), | 
|  | 138 | +        # ("tp_as_mapping", _Ptr[PyMappingMethods]), | 
|  | 139 | +        ("tp_as_mapping", _Ptr[PyObject]), | 
|  | 140 | +        ("tp_hash", hashfunc), | 
|  | 141 | +        ("tp_call", ternaryfunc), | 
|  | 142 | +        ("tp_str", reprfunc), | 
|  | 143 | +        ("tp_getattro", getattrofunc), | 
|  | 144 | +        ("tp_setattro", setattrofunc), | 
|  | 145 | +        # ("tp_as_buffer", _Ptr[PyBufferProcs]), | 
|  | 146 | +        ("tp_as_buffer", _Ptr[PyObject]), | 
|  | 147 | +        ("tp_flags", c_ulong), | 
|  | 148 | +        ("tp_doc", c_char_p), | 
|  | 149 | +        ("tp_traverse", traverseproc), | 
|  | 150 | +        ("tp_clear", inquiry), | 
|  | 151 | +        ("tp_richcompare", richcmpfunc), | 
|  | 152 | +        ("tp_weaklistoffset", c_ssize_t), | 
|  | 153 | +        ("tp_iter", getiterfunc), | 
|  | 154 | +        ("tp_iternext", iternextfunc), | 
|  | 155 | +        ("tp_methods", _Ptr[PyObject]), | 
|  | 156 | +        ("tp_members", _Ptr[PyObject]), | 
|  | 157 | +        ("tp_getset", _Ptr[PyObject]), | 
|  | 158 | +        # ("tp_base", _Ptr[PyTypeObject]), | 
|  | 159 | +        ("tp_base", _Ptr[PyObject]), | 
|  | 160 | +        ("tp_dict", _Ptr[PyObject]), | 
|  | 161 | +        ("tp_descr_get", descrgetfunc), | 
|  | 162 | +        ("tp_descr_set", descrsetfunc), | 
|  | 163 | +        ("tp_dictoffset", c_ssize_t), | 
|  | 164 | +        ("tp_init", initproc), | 
|  | 165 | +        ("tp_alloc", allocfunc), | 
|  | 166 | +        ("tp_new", newfunc), | 
|  | 167 | +        ("tp_free", freefunc), | 
|  | 168 | +        ("tp_is_gc", inquiry), | 
|  | 169 | +        ("tp_bases", _Ptr[PyObject]), | 
|  | 170 | +        ("tp_mro", _Ptr[PyObject]), | 
|  | 171 | +        ("tp_cache", _Ptr[PyObject]), | 
|  | 172 | +        ("tp_subclasses", _Ptr[PyObject]), | 
|  | 173 | +        ("tp_weaklist", _Ptr[PyObject]), | 
|  | 174 | +        ("tp_del", destructor), | 
|  | 175 | +        ("tp_version_tag", c_uint), | 
|  | 176 | +        ("tp_finalize", descrsetfunc), | 
|  | 177 | +        ("tp_vectorcall", vectorcallfunc), | 
|  | 178 | +        ("tp_watched", c_char), | 
|  | 179 | +    ] | 
|  | 180 | + | 
|  | 181 | +    @classmethod | 
|  | 182 | +    def from_object(cls, obj) -> Self: | 
|  | 183 | +        """Create a PyObject from an object.""" | 
|  | 184 | +        return cls.from_address(address(obj)) | 
| 144 | 185 | 
 | 
| 145 | 186 | 
 | 
| 146 | 187 | # 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] | 
|  | 188 | +class PyTypeVarObject(Structure): | 
|  | 189 | +    _fields_ = [ | 
|  | 190 | +        ("ob_refcnt", c_ssize_t), | 
|  | 191 | +        # ("ob_type", LP_PyTypeObject), | 
|  | 192 | +        ("ob_type", _Ptr[PyObject]), | 
|  | 193 | +        ("ob_size", c_ssize_t), | 
|  | 194 | +        ("name", _Ptr[PyObject]), | 
|  | 195 | +        # not sure why but this is the only thing that works but that's fine because it's the only thing we need | 
|  | 196 | +        ("bound", _Ptr[PyObject]), | 
|  | 197 | +    ] | 
|  | 198 | + | 
|  | 199 | +    @classmethod | 
|  | 200 | +    def from_object(cls, obj) -> Self: | 
|  | 201 | +        """Create a PyObject from an object.""" | 
|  | 202 | +        return cls.from_address(address(obj)) | 
| 151 | 203 | 
 | 
| 152 | 204 | 
 | 
| 153 | 205 | if __name__ == "__main__": | 
| 154 |  | -    print(PyTypeObject.from_object(type(3.14))) | 
|  | 206 | +    # for k, v in PyObject._fields_: | 
|  | 207 | +    #     print(f"('{k}', {v.__name__}),") | 
|  | 208 | +    # for k, v in PyVarObject._fields_: | 
|  | 209 | +    #     print(f"('{k}', {v.__name__}),") | 
|  | 210 | +    # for k, v in PyTypeObject._fields_: | 
|  | 211 | +    #     print(f"('{k}', {v.__name__}),") | 
|  | 212 | +    print(PyTypeObject.from_object(type(3.14)).tp_name) | 
|  | 213 | +    print(PyTypeObject.from_object(type(3.14)).tp_base) | 
|  | 214 | +    print(PyTypeObject.from_object(type(3.14)).ob_type) | 
|  | 215 | +    for k, v in PyTypeVarObject._fields_: | 
|  | 216 | +        print(f"('{k}', {v.__name__}),") | 
0 commit comments