clarete / forbiddenfruit

Patch built-in python objects
https://clarete.li/forbiddenfruit/
GNU General Public License v3.0
827 stars 52 forks source link

[request] patch the `__bases__` of builtin types #31

Open alexchandel opened 5 years ago

alexchandel commented 5 years ago

I need to monkeypatch the base class of builtin types. No fancy magic methods, I just need to inherit normal method like this (contrived example):

class Iterator:
    def map(self, f):
        return map(f, self)

Naturally, this fails:

>>> list.__bases__ = (Iterator,)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't set attributes of built-in/extension type 'list'

But forbiddenfruit also fails:

>>> ff.curse(list, '__bases__', (Iterator,))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.7/site-packages/forbiddenfruit/__init__.py", line 412, in curse
    _curse_special(klass, attr, value)
  File "/usr/local/lib/python3.7/site-packages/forbiddenfruit/__init__.py", line 327, in _curse_special
    assert isinstance(func, FunctionType)
AssertionError

Could you please add this to forbiddenfruit?

alexchandel commented 4 years ago

For simple static classes, which aren't supposed to have multiple bases (but sometimes do, e.g. numpy dtypes), this means swapping out tp_base, ensuring to test for tp_bases to handle multi-parent static or dynamic classes and to (throw NotImplementedError or) patch those instead.

We'd also want checks like type_set_bases has for circular inheritance, valid assignments, etc.

On the other hand, adding Py_TPFLAGS_HEAPTYPE to the PyTypeObject's tp_flags simply disables the rejection in check_set_special_type_attr. But compatible_for_assignment still rejects assigning list.__bases__ with new types, as newto->tp_free != oldto->tp_free, since PyBaseObject_Type->tp_free == PyObject_Del instead of PyObject_GC_Del. But object's lack of explicit GC support may not be an issue…