einspect#

Extended Inspections for CPython.

Build codecov security

PyPI PyPI - Python Version

Check detailed states of built-in objects#

from einspect import view

ls = [1, 2, 3]
v = view(ls)
print(v.info())
PyListObject(at 0x2833738):
   ob_refcnt: Py_ssize_t = 5
   ob_type: *PyTypeObject = &[list]
   ob_item: **PyObject = &[&[1], &[2], &[3]]
   allocated: Py_ssize_t = 4

Mutate tuples, strings, ints, or other immutable types#

TupleView and StrView supports all MutableSequence methods (append, extend, insert, pop, remove, reverse, clear).

⚠️ A note on safety.

from einspect import view

tup = (1, 2)
v = view(tup)

v[1] = 500
print(tup)      # (1, 500)
v.append(3)
print(tup)      # (1, 500, 3)

del v[:2]
print(tup)      # (3,)
print(v.pop())  # 3

v.extend([1, 2])
print(tup)      # (1, 2)

v.clear()
print(tup)      # ()
from einspect import view

text = "hello"

v = view(text)
v[1] = "3"
v[4:] = "o~"
v.append("!")

print(text)  # h3llo~!
v.reverse()
print(text)  # !~oll3h
from einspect import view

n = 500
view(n).value = 10

print(500)        # 10
print(500 == 10)  # True

Modify attributes of built-in types, get original attributes with orig#

from einspect import view, orig

v = view(int)
v["__name__"] = "custom_int"
v["__iter__"] = lambda s: iter(range(s))
v["__repr__"] = lambda s: "custom: " + orig(int).__repr__(s)

print(int)
for i in 3:
    print(i)
<class 'custom_int'>
custom: 0
custom: 1
custom: 2

Implement methods on built-in types#

See the Extending Types docs page for more information.

from einspect import impl, orig

@impl(int)
def __add__(self, other):
    other = int(other)
    return orig(int).__add__(self, other)

print(50 + "25")  # 75

Move objects in memory#

from einspect import view

s = "meaning of life"

v = view(s)
with v.unsafe():
    v <<= 42

print("meaning of life")        # 42
print("meaning of life" == 42)  # True

CPython Struct bindings and API methods#

  • Easily make calls to CPython stable ABI (ctypes.pythonapi) as bound methods on PyObject instances.

from einspect.structs import PyDictObject

d = {"a": (1, 2), "b": (3, 4)}

res = PyDictObject(d).GetItem("a")

if res:
    print(res.contents.NewRef())

Equivalent to the following with ctypes:

from ctypes import pythonapi, py_object, c_void_p, cast

d = {"a": (1, 2), "b": (3, 4)}

PyDict_GetItem = pythonapi["PyDict_GetItem"]
# Can't use auto cast py_object for restype,
# since missing keys return NULL and causes segmentation fault with no set error
PyDict_GetItem.restype = c_void_p
PyDict_GetItem.argtypes = [py_object, py_object]

res = PyDict_GetItem(d, "a")
res = cast(res, py_object)

Py_NewRef = pythonapi["Py_NewRef"]
Py_NewRef.restype = py_object
Py_NewRef.argtypes = [py_object]

try:
    print(Py_NewRef(res.value))
except ValueError:
    pass
  • Create new instances of PyObject structs with field values, from existing objects, or from address.

from einspect.structs import PyLongObject, PyTypeObject

x = PyLongObject(
    ob_refcnt=1,
    ob_type=PyTypeObject(int).as_ref(),
    ob_size=1,
    ob_item=[15],
).into_object()

print(x)        # 15
print(x == 15)  # True
print(x is 15)  # False

Indices and tables#