Skip to content

Releases: ionite34/einspect

v0.5.6

01 Feb 16:27
df9338d
Compare
Choose a tag to compare

Adds

  • View.swap()

This is like View.move_from(), but swaps data from the target view from the supplied viewable object

from einspect import view

view("dog").swap("cat")

print("dog", "cat")
# cat dog

view(max).swap(min)

print(min(1, 2, 3), max(1, 2, 3))
# 3 1

Full Changelog: v0.5.5...v0.5.6

v0.5.5

30 Jan 18:14
686f611
Compare
Choose a tag to compare

Added

  • Implement sort() for TupleView and StrView

This sorts the tuple or string in-place, like list.sort, and accepts optional key and reverse arguments.

from einspect import view

s = "a1b2c3"
view(s).sort()

print(s)
>> 123abc
from einspect import view

t = (3, 1, 4, 2)
view(t).sort()

print(t)
>> (1, 2, 3, 4)

Fixes

  • Add 2 missing __all__ declarations in api and types by @ionite34 in #42
  • Change README heading phrasing, update lock file by @ionite34 in #43
  • Switch from yanked version of sphinx-autodoc-typehints by @ionite34 in #44

Full Changelog: v0.5.4...v0.5.5

v0.5.4

27 Jan 15:43
45165ea
Compare
Choose a tag to compare

Added

  • CFunctionView
  • PyCFunctionObject

These are essentially for support of builtin functions.

from einspect import view

v = view(abs)

print(v.ml.ml_name)
>> b'abs'

v <<= print

abs("hello")
>> hello

Fixes

  • FunctionView.version naming from func_version

Full Changelog: v0.5.3...v0.5.4

v0.5.3

26 Jan 22:07
01fbb5c
Compare
Choose a tag to compare

Added

  • PyFunctionObject and FunctionView
@struct
class PyFunctionObject(PyObject[FunctionType, None, None]):
    globals: ptr[PyObject]
    builtins: ptr[PyObject]
    name: ptr[PyObject]
    qualname: ptr[PyObject]
    code: ptr[PyObject]  # A code object, the __code__ attribute
    defaults: ptr[PyObject]  # NULL or a tuple
    kwdefaults: ptr[PyObject]  # NULL or a dict
    closure: ptr[PyObject]  # NULL or a tuple of cell objects

    func_doc: ptr[PyObject]  # The __doc__ attribute, can be anything
    func_dict: ptr[PyObject]  # The __dict__ attribute, a dict or NULL
    func_weakreflist: ptr[PyObject]  # List of weak references
    func_module: ptr[PyObject]  # The __module__ attribute, can be anything
    func_annotations: ptr[PyObject]  # Annotations, a dict or NULL
    vectorcall: vectorcallfunc

    func_version: Annotated[int, c_uint32]

So you can now do stuff like

from einspect import view

def func():
    assert "__builtins__" not in globals()
    return some_undefined_thing


v = view(func)
with v.unsafe():
    v.globals = {"some_undefined_thing": 123}
    
print(func())
>> 123

Full Changelog: v0.5.2...v0.5.3

v0.5.2.post1

25 Jan 23:59
371dc57
Compare
Choose a tag to compare

Metadata

  • Add 3.12 classifier to pyproject.toml

Full Changelog: v0.5.2...v0.5.2.post1

v0.5.2

25 Jan 23:42
5b18956
Compare
Choose a tag to compare

Added

  • py_unicode and instance dict compatibility for Python 3.12 definitions
  • bumped higher python bound in pyproject.toml to support Python 3.12

Full Changelog: v0.5.1...v0.5.2

v0.5.1

25 Jan 19:32
72767a4
Compare
Choose a tag to compare

Added

  • Formatting for SetView.info
from einspect import view

s = {1, 2, 3}
print(view(s).info())
PySetObject (at 0x7f70e22f5ac0):
   ob_refcnt: Py_ssize_t = 2
   ob_type: *PyTypeObject = &[set]
   fill: Py_ssize_t = 3
   used: Py_ssize_t = 3
   mask: Py_ssize_t = 7
   table: *SetEntry = &[{ key = &[NULL], hash = 0 }]
   hash: Py_hash_t = -1
   finger: Py_ssize_t = 0
   smalltable: Array[SetEntry] = [
      { key = &[NULL], hash = 0 },
      { key = &[1], hash = 1 },
      { key = &[2], hash = 2 },
      { key = &[3], hash = 3 },
      { key = &[NULL], hash = 0 },
      { key = &[NULL], hash = 0 },
      { key = &[NULL], hash = 0 },
      { key = &[NULL], hash = 0 }
   ]
   weakreflist: *PyObject = &[<weakref at 0x7f70e230dd10; to 'set' at 0x7f70e22f5ac0>]

Full Changelog: v0.5.0...v0.5.1

v0.5.0

25 Jan 19:01
c163d9b
Compare
Choose a tag to compare

New

  • einspect.view now supports subtypes. The additional instance dictionary is supported to be moved during View.move_from or View.move_to
from einspect import view

class Num(int):
    pass

print(view(10))
# IntView(<PyLongObject at 0x101786a60>)

print(view(Num(10)))
# IntView[Num](<PyLongObject at 0x1006e7440>)
  • Memory moves now no longer require an unsafe context if it can be performed safely.
  • If safe moving is not possible (target size exceeds allocated memory for the object) an UnsafeError is raised.
from einspect import view

x = 900
view(x) << 50

ls = [1, 2]
view(ls) << [3, 4, 5]

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

Changes

  • einspect.view DeprecationWarning for non-concrete types is removed.
  • View.move_from no longer makes a deep-copy of other, this was originally added to prevent member references from being dropped but hindered features like connected instance dictionary pointers from working.
  • Such, the target of a move will receive most heap-allocated pointers of the source. For example, moved lists will share the original ob_item array
from einspect import view, unsafe

ls = []
x = [3, 4]

with unsafe():
	view(ls) << x

print(ls)  # [3, 4]
# Updating x will now affect ls as well
x[0] = "hi"
print(ls)  # ['hi', 4]
  • However, this only applies to pointers referring to allocations off the object struct, other attributes are still statically moved. For example, calling x.clear() will deallocate the ob_item array of x and set it as a NULL pointer. But ls still has an ob_size of 2, and will try to access its ob_item pointer to get elements.
from einspect import view, unsafe

ls = []
x = [3, 4]

with unsafe():
	view(ls) << x

x.clear()
print(ls) # Segmentation fault (likely)
  • To revert to original behavior, deepcopy can be called explicitly
from copy import deepcopy
from einspect import view, unsafe

ls = []
x = [3, 4]

with unsafe():
	view(ls) << deepcopy(x)

x.clear()
print(ls) # [3, 4]
  • PyObject and subclasses can now be provided 1 positional argument to invoke from_object behavior:
from einspect.structs import PyObject, PyTypeObject

obj = PyObject("foo")
# equivalent to
obj = PyObject.from_object("foo")
  • Manual struct creation can still be done with kwargs. This mode will also now invoke the relevant _PyObject_New, _PyObject_NewVar, or _PyObject_GC_NewVar C APIs to correctly initialize GC linked lists and allocate the object using free-lists, matching the interpreter’s native handling of new objects.
from einspect.structs import PyObject, PyTypeObject

obj = PyObject(
    ob_refcnt=1,
    ob_type=PyTypeObject(object).as_ref(),
)

Full Changelog: v0.4.10...v0.5.0

v0.5.0a1

24 Jan 21:00
cd2422d
Compare
Choose a tag to compare
v0.5.0a1 Pre-release
Pre-release

New

  • einspect.view now supports subtypes. The additional instance dictionary is supported to be moved during View.move_from or View.move_to
from einspect import view

class Num(int):
    pass

print(view(10))
# IntView(<PyLongObject at 0x101786a60>)

print(view(Num(10)))
# IntView[Num](<PyLongObject at 0x1006e7440>)
  • Memory moves now no longer require an unsafe context if it can be performed safely.
  • If safe moving is not possible (target size exceeds allocated memory for the object) an UnsafeError is raised.
from einspect import view

x = 900
view(x) << 50

ls = [1, 2]
view(ls) << [3, 4, 5]

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

Changes

  • einspect.view DeprecationWarning for non-concrete types is removed.
  • View.move_from no longer makes a deep-copy of other, this was originally added to prevent member references from being dropped but hindered features like connected instance dictionary pointers from working.
  • Such, the target of a move will receive most heap-allocated pointers of the source. For example, moved lists will share the original ob_item array
from einspect import view, unsafe

ls = []
x = [3, 4]

with unsafe():
	view(ls) << x

print(ls)  # [3, 4]
# Updating x will now affect ls as well
x[0] = "hi"
print(ls)  # ['hi', 4]
  • However, this only applies to pointers referring to allocations off the object struct, other attributes are still statically moved. For example, calling x.clear() will deallocate the ob_item array of x and set it as a NULL pointer. But ls still has an ob_size of 2, and will try to access its ob_item pointer to get elements.
from einspect import view, unsafe

ls = []
x = [3, 4]

with unsafe():
	view(ls) << x

x.clear()
print(ls) # Segmentation fault (likely)
  • To revert to original behavior, deepcopy can be called explicitly
from copy import deepcopy
from einspect import view, unsafe

ls = []
x = [3, 4]

with unsafe():
	view(ls) << deepcopy(x)

x.clear()
print(ls) # [3, 4]

What's Changed

  • Implement formatting for MappingProxyView.info by @ionite34 in #30
  • Add tests for MappingProxyView info formatting by @ionite34 in #32
  • Add subclass support for Views, safe memory moves no longer requires unsafe context by @ionite34 in #33

Full Changelog: v0.4.10...v0.5.0a1

v0.4.10

23 Jan 21:38
5b4fdde
Compare
Choose a tag to compare

Adds

  • PyObject instance_dict method
  • View instance_dict property

Fixes

  • Formatting for StrView.info will now resolve additional values fields like wstr

Improvements

  • Test coverage

Full Changelog: v0.4.9...v0.4.10