diff --git a/forbiddenfruit/__init__.py b/forbiddenfruit/__init__.py index afcd926..5b3e095 100644 --- a/forbiddenfruit/__init__.py +++ b/forbiddenfruit/__init__.py @@ -203,6 +203,7 @@ class PyAsyncMethods(ctypes.Structure): ('tp_hash', ctypes.CFUNCTYPE(ctypes.c_int64, PyObject_p)), ('tp_call', ctypes.CFUNCTYPE(PyObject_p, PyObject_p, PyObject_p, PyObject_p)), ('tp_str', ctypes.CFUNCTYPE(PyObject_p, PyObject_p)), + ('tp_getattr', ctypes.CFUNCTYPE(PyObject_p, PyObject_p, PyObject_p)) # ... ] @@ -317,6 +318,7 @@ def __filtered_dir__(obj=None): # divmod isn't a dunder, still make it overridable override_dict['divmod()'] = ('tp_as_number', "nb_divmod") override_dict['__str__'] = ('tp_str', "tp_str") +override_dict['__getattr__'] = ('tp_getattr', "tp_getattr") def _is_dunder(func_name): diff --git a/tests/unit/test_forbidden_fruit.py b/tests/unit/test_forbidden_fruit.py index 5830337..be5dbe7 100644 --- a/tests/unit/test_forbidden_fruit.py +++ b/tests/unit/test_forbidden_fruit.py @@ -330,3 +330,16 @@ def type_error_str(self): reverse(TypeError, '__str__') assert str(te) == "testing" + +@skip_legacy +def test_overriding_getattr(): + """Overload __getattr__ for dicts to lookup a key""" + def getter(self, x): + try: + return object.__getattribute__(self, x) + except AttributeError: + return self[x] + curse(dict, "__getattr__", getter) + + my_dict = {"abc": "xyz"} + assert(my_dict.abc == "xyz")