Metadata-Version: 2.1
Name: einspect
Version: 0.4.6
Summary: Extended Inspect - view and modify memory structs of runtime objects.
Home-page: https://github.com/ionite34/einspect
License: MIT
Keywords: einspect,inspect,inspections,extensions,inspect-extensions,ctypes,ctypes-extensions,pointers,structs,memory,mutate,setattr,modify
Author: ionite34
Author-email: dev@ionite.io
Requires-Python: >=3.8,<3.12
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Topic :: Software Development :: Debuggers
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Dist: typing-extensions (>=4.4.0,<5.0.0)
Project-URL: Documentation, https://docs.ionite.io/einspect
Project-URL: Repository, https://github.com/ionite34/einspect
Description-Content-Type: text/markdown

# einspect

[![Build](https://github.com/ionite34/einspect/actions/workflows/build.yml/badge.svg)](https://github.com/ionite34/einspect/actions/workflows/build.yml)
[![codecov](https://codecov.io/gh/ionite34/einspect/branch/main/graph/badge.svg?token=v71SdG5Bo6)](https://codecov.io/gh/ionite34/einspect)
[![security](https://snyk-widget.herokuapp.com/badge/pip/einspect/badge.svg)](https://security.snyk.io/package/pip/einspect)

[![PyPI](https://img.shields.io/pypi/v/einspect)][pypi]
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/einspect)][pypi]

[pypi]: https://pypi.org/project/einspect/

> Extended Inspections for CPython

### [Documentation](https://docs.ionite.io/einspect)

- [View and modify memory structures of live objects.](#check-detailed-states-of-built-in-objects)
- [Able to mutate immutable objects like tuples and ints.](#mutate-tuples-strings-ints-or-other-immutable-types)
- [Modify slot functions or attributes of built-in types.](#modify-attributes-of-built-in-types-get-original-attributes-with-orig)
- [Fully typed, extensible framework in pure Python.](#move-objects-in-memory)

### Check detailed states of built-in objects
```python
from einspect import view

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

[doc_tuple_view]: https://docs.ionite.io/einspect/api/views/view_tuple.html#einspect.views.view_tuple
[py_doc_mutable_seq]: https://docs.python.org/3/library/stdtypes.html#mutable-sequence-types
### Mutate tuples, strings, ints, or other immutable types
> [TupleView][doc_tuple_view] supports all [MutableSequence][py_doc_mutable_seq] methods (append, extend, insert, pop, remove, reverse, clear).
```python
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)      # ()
```

```python
from einspect import view

text = "hello"
num = 100

view(text).buffer[:] = b"world"
view(num).value = 5

print(text)  # world
print(num)   # 5
```

### Modify attributes of built-in types, get original attributes with `orig`
```python
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)
```
```python
<class 'custom_int'>
custom: 0
custom: 1
custom: 2
```

### Implement methods on built-in types with `@impl`
```python
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
```python
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
```

### Fully typed interface
<img width="551" alt="image" src="https://user-images.githubusercontent.com/13956642/211129165-38a1c405-9d54-413c-962e-6917f1f3c2a1.png">

## Table of Contents
- [Views](#views)
  - [Using the `einspect.view` constructor](#using-the-einspectview-constructor)
  - [Inspecting struct attributes](#inspecting-struct-attributes)

## Views

### Using the `einspect.view` constructor

This is the recommended and simplest way to create a `View` onto an object. Equivalent to constructing a specific `View` subtype from `einspect.views`, except the choice of subtype is automatic based on object type.

```python
from einspect import view

print(view(1))
print(view("hello"))
print(view([1, 2]))
print(view((1, 2)))
```
> ```
> IntView(<PyLongObject at 0x102058920>)
> StrView(<PyUnicodeObject at 0x100f12ab0>)
> ListView(<PyListObject at 0x10124f800>)
> TupleView(<PyTupleObject at 0x100f19a00>)
> ```

### Inspecting struct attributes

Attributes of the underlying C Struct of objects can be accessed through the view's properties.
```python
from einspect import view

ls = [1, 2]
v = view(ls)

# Inherited from PyObject
print(v.ref_count)  # ob_refcnt
print(v.type)       # ob_type
# Inherited from PyVarObject
print(v.size)       # ob_size
# From PyListObject
print(v.item)       # ob_item
print(v.allocated)  # allocated
```
> ```
> 4
> <class 'tuple'>
> 3
> <einspect.structs.c_long_Array_3 object at 0x105038ed0>
> ```

## 2. Writing to view attributes

Writing to these attributes will affect the underlying object of the view.

Note that most memory-unsafe attribute modifications require entering an unsafe context manager with `View.unsafe()`
```python
with v.unsafe():
    v.size -= 1

print(obj)
```
> `(1, 2)`

Since `items` is an array of integer pointers to python objects, they can be replaced by `id()` addresses to modify
index items in the tuple.
```python
from einspect import view

tup = (100, 200)

with view(tup).unsafe() as v:
    s = "dog"
    v.item[0] = id(s)

print(tup)
```
> ```
> ('dog', 200)
>
> >> Process finished with exit code 139 (interrupted by signal 11: SIGSEGV)
> ```

So here we did set the item at index 0 with our new item, the string `"dog"`, but this also caused a segmentation fault.
Note that the act of setting an item in containers like tuples and lists "steals" a reference to the object, even
if we only supplied the address pointer.

To make this safe, we will have to manually increment a ref-count before the new item is assigned. To do this we can
either create a `view` of our new item, and increment its `ref_count += 1`, or use the apis from `einspect.api`, which
are pre-typed implementations of `ctypes.pythonapi` methods.
```python
from einspect import view
from einspect.api import Py

tup = (100, 200)

with view(tup).unsafe() as v:
    a = "bird"
    Py.IncRef(a)
    v.item[0] = id(a)

    b = "kitten"
    Py.IncRef(b)
    v.item[1] = id(b)

print(tup)
```
> `('bird', 'kitten')`

🎉 No more seg-faults, and we just successfully set both items in an otherwise immutable tuple.

To make the above routine easier, you can access an abstraction by simply indexing the view.

```python
from einspect import view

tup = ("a", "b", "c")

v = view(tup)
v[0] = 123
v[1] = "hm"
v[2] = "🤔"

print(tup)
```
> `(123, 'hm', '🤔')`

