Skip to content

Commit 31b50f6

Browse files
authored
Merge pull request #50 from ionite34/docs-update
2 parents 932462e + 685861c commit 31b50f6

File tree

7 files changed

+172
-2
lines changed

7 files changed

+172
-2
lines changed

README.md

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ PyListObject(at 0x2833738):
4545
[py_doc_mutable_seq]: https://docs.python.org/3/library/stdtypes.html#mutable-sequence-types
4646
## Mutate tuples, strings, ints, or other immutable types
4747
> [TupleView][doc_tuple_view] and [StrView][doc_str_view] supports all [MutableSequence][py_doc_mutable_seq] methods (append, extend, insert, pop, remove, reverse, clear).
48+
49+
> ⚠️ A note on [safety.](#safety)
4850
```python
4951
from einspect import view
5052

@@ -111,6 +113,7 @@ custom: 2
111113
```
112114

113115
## Implement methods on built-in types
116+
> See the [Extending Types](https://docs.ionite.io/einspect/extending_types.html) docs page for more information.
114117
```python
115118
from einspect import impl, orig
116119

@@ -141,6 +144,70 @@ print("meaning of life" == 42) # True
141144
## Fully typed interface
142145
<img width="551" alt="image" src="https://user-images.githubusercontent.com/13956642/211129165-38a1c405-9d54-413c-962e-6917f1f3c2a1.png">
143146

147+
## Safety
148+
149+
This project is mainly for learning purposes or inspecting and debugging CPython internals for development and fun. You should not violate language conventions like mutability in production software and libraries.
150+
151+
The interpreter makes assumptions regarding types that are immutable, and changing them causes all those usages to be affected. While the intent of the project is to make a memory-correct mutation without further side effects, there can be very significant runtime implications of mutating interned strings with lots of shared references, including interpreter crashes.
152+
153+
For example, some strings like "abc" are interned and used by the interpreter. Changing them changes all usages of them, even attribute calls like `collections.abc`.
154+
155+
> The spirit of safety maintained by einspect is to do with memory layouts, not functional effects.
156+
157+
### For example, appending to tuple views (without an unsafe context) will check that the resize can fit within allocated memory
158+
```python
159+
from einspect import view
160+
161+
tup = (1, 2)
162+
v = view(tup)
163+
164+
v.append(3)
165+
print(tup) # (1, 2, 3)
166+
167+
v.append(4)
168+
# UnsafeError: insert required tuple to be resized beyond current memory allocation. Enter an unsafe context to allow this.
169+
```
170+
- Despite this, mutating shared references like empty tuples can cause issues in interpreter shutdown and other runtime operations.
171+
```python
172+
from einspect import view
173+
174+
tup = ()
175+
view(tup).append(1)
176+
```
177+
```
178+
Exception ignored in: <module 'threading' from '/lib/python3.11/threading.py'>
179+
Traceback (most recent call last):
180+
File "/lib/python3.11/threading.py", line 1563, in _shutdown
181+
_main_thread._stop()
182+
File "/lib/python3.11/threading.py", line 1067, in _stop
183+
with _shutdown_locks_lock:
184+
TypeError: 'str' object cannot be interpreted as an integer
185+
```
186+
187+
### Similarly, memory moves are also checked for GC-header compatibility and allocation sizes
188+
```python
189+
from einspect import view
190+
191+
v = view(101)
192+
v <<= 2
193+
194+
print(101) # 2
195+
196+
v <<= "hello"
197+
# UnsafeError: memory move of 54 bytes into allocated space of 32 bytes is out of bounds. Enter an unsafe context to allow this.
198+
```
199+
200+
- However, this will not check the fact that small integers between (-5, 256) are interned and used by the interpreter. Changing them may cause issues in any library or interpreter Python code.
201+
```python
202+
from einspect import view
203+
204+
view(0) << 100
205+
206+
exit()
207+
# sys:1: ImportWarning: can't resolve package from __spec__ or __package__, falling back on __name__ and __path__
208+
# IndexError: string index out of range
209+
```
210+
144211
## Table of Contents
145212
- [Views](#views)
146213
- [Using the `einspect.view` constructor](#using-the-einspectview-constructor)

docs/source/api/orig.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# orig
2+
3+
```{eval-rst}
4+
.. autoclass:: einspect.type_orig.orig
5+
:members:
6+
```

docs/source/api/views/view_type.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,5 @@ TypeView
44
.. autoclass:: einspect.views.view_type.TypeView
55
:members:
66
:inherited-members:
7+
8+
.. autodecorator:: einspect.views.view_type.impl

docs/source/extending_types.md

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# Extending Types
2+
3+
- {class}`~einspect.views.view_type.TypeView` subscripting can be used to set attributes on type objects, including built-in types.
4+
- The {meth}`~einspect.views.view_type.impl` decorator can implement the decorated method onto a type.
5+
6+
## Using the {meth}`~einspect.views.view_type.impl` decorator
7+
8+
> Works for normal methods, dunder methods, or decorated static / class / property methods.
9+
```python
10+
from einspect import impl
11+
12+
@impl(int)
13+
def is_even(self):
14+
return self % 2 == 0
15+
16+
print((2).is_even()) # True
17+
18+
@impl(int)
19+
def __matmul__(self, other):
20+
return self * other
21+
22+
print(5 @ 3) # 15
23+
24+
@impl(int)
25+
@property
26+
def real(self):
27+
return self + 1
28+
29+
print((2).real) # 3
30+
31+
@impl(int)
32+
@classmethod
33+
def try_from(cls, x):
34+
try:
35+
return cls(x)
36+
except ValueError:
37+
return None
38+
39+
print(int.try_from('2')) # 2
40+
print(int.try_from('a')) # None
41+
42+
@impl(list)
43+
@staticmethod
44+
def abc():
45+
return "abc"
46+
47+
print(list.abc()) # "abc"
48+
print([].abc()) # "abc"
49+
```
50+
## Using {class}`~einspect.type_orig.orig` to get original attributes
51+
Sometimes you may want to defer to the original implementation of a method or attribute before it was overriden by `impl` or `TypeView`, in this case calling `orig(<type>)` will return a proxy object of the type where attribute access will yield original attributes.
52+
53+
For example, we want to override the `__add__` of floats, by adding a print statement, but we want to still call the original `__add__` after our print.
54+
55+
Calling `orig(float)` will give us the float proxy object, where `orig(float).__add__` will give us the original `float.__add__` method before our override.
56+
57+
```python
58+
from einspect import impl, orig
59+
60+
@impl(float)
61+
def __add__(self, other):
62+
print(f"Adding {self} and {other}")
63+
return orig(float).__add__(self, other)
64+
```
65+
66+
## Using {meth}`~einspect.views.view_type.TypeView` subscripting
67+
Views of types can be subscripted to either get or set attributes on the type. This works in all cases where `@impl` can be used.
68+
69+
In addition, this form can also set static attributes like `__dict__` or `__name__`.
70+
```python
71+
from einspect import view
72+
73+
v = view(int)
74+
print(v["__name__"]) # int
75+
76+
v["is_even"] = lambda self: self % 2 == 0
77+
print((2).is_even()) # True
78+
79+
v["__name__"] = "MyInt"
80+
print(int.__name__) # MyInt
81+
print(int) # <class 'MyInt'>
82+
```
83+
84+
Multiple attributes can be set together by passing multiple attribute names.
85+
```python
86+
from einspect import view
87+
88+
v = view(str)
89+
v["__truediv__", "__floordiv__"] = str.split
90+
91+
print("Hello, world!" / ", ") # ['Hello', 'world!']
92+
print("abc-xyz" // "-") # ['abc', 'xyz']
93+
```

docs/source/index.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ Extended Inspections for CPython.
2121
intro
2222
views
2323
structs
24+
extending_types
2425
```
2526

2627
```{toctree}
@@ -29,6 +30,7 @@ structs
2930
3031
api/views/index
3132
api/structs/index
33+
api/orig
3234
api/types
3335
```
3436

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "einspect"
3-
version = "0.5.7"
3+
version = "0.5.7.post1"
44
packages = [{ include = "einspect", from = "src" }]
55
description = "Extended Inspect - view and modify memory structs of runtime objects."
66
authors = ["ionite34 <dev@ionite.io>"]

src/einspect/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,6 @@
1414

1515
__all__ = ("view", "unsafe", "impl", "orig", "ptr", "NULL")
1616

17-
__version__ = "0.5.7"
17+
__version__ = "0.5.7.post1"
1818

1919
unsafe: ContextManager[None] = global_unsafe

0 commit comments

Comments
 (0)