Decorate class methods dynamically raises error in python - python

I am following this answer in order to decorate all methods of a class. This is my version of the code:
def decorate_object_methods(object_to_decorate, decorator):
for name in dir(object_to_decorate):
attr = getattr(object_to_decorate, name)
if callable(attr):
wrapped = decorator(attr)
setattr(object_to_decorate, name, wrapped)
The problem is that I am getting this exception in the last sentence of the method:
TypeError: __class__ must be set to a class, not 'function' object
I can't find what I'm doing wrong

Related

python child class method calling overridden classmethod with non-classmethod

I am trying to do the following in python3:
class Parent:
#classmethod
def show(cls, message):
print(f'{message}')
#classmethod
def ask(cls, message):
cls.show(f'{message}???')
class Child(Parent):
#property
def name(self):
return 'John'
def show(self, message):
print(f'{self.name}: {message}')
instance = Child()
instance.ask('what')
But it then complains
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 7, in ask
TypeError: Child.show() missing 1 required positional argument: 'message'
even so child.show works as expected. So it seems that child.ask is calling Parent.show... I tried to mark Child.show as classmethod too, but then the cls.name is not showing the expected output:
class Child2(Parent):
#property
def name(self):
return 'John'
#classmethod
def show(cls, message):
print(f'{cls.name}: {message}')
instance2 = Child2()
instance2.ask('what')
this shows
<property object at 0xfc7b90>: what???
Is there a way to override a parent classmethod with a non-classmethod, but keeping other parent classmethod to call the overridden one?
I found it hard to follow for the second half of the question but there was an issue I saw and it might help you solve your problem.
When you said even so child.show works as expected. So it seems that child.ask is calling Parent.show, thats not what is happening.
When you called instance.ask("what"), it called the #classmethod decorated method of the Child class (which is inherited from the parent). This ask method is passing the class Child as the first argument, (not the instance you created). This means the line
cls.show(f'{message}???')
is equivalent to
Child.show(f'{message}???') # because cls is the Class not the instance
The show method inside the Child class is an instance method and expects the first argument to be the actual instance (self) but the string f'{message}???' is being passed to it and it expects a second message string to be passed so that's why its is throwing an error.
Hope this helped

Attempt to patch a member function yields error AttributeError: 'module' object has no attribute 'object'

I would like to assert from a UT, TestRunner.test_run that some deeply nested function Prompt.run_cmd is called with the string argument "unique cmd". My setup besically resembles the following:
# Module application/engine/prompt.py
class Prompt:
def run_cmd(self, input):
pass
# Module: application/scheduler/runner.py
class Runner:
def __init__(self):
self.prompt = application.engine.prompt.Prompt()
def run(self):
self.prompt.run_cmd("unique cmd")
# Module tests/application/scheduler/runner_test.py
class TestRunner(unittest.TestCase):
...
def test_run(self):
# calls Runner.run
# Objective assert that Prompt.run is called with the argument "unique cmd"
# Current attempt below:
with mock.patch(application.engine.prompt, "run_cmd") as mock_run_cmd:
pass
Unfortunately my attempts to mock the Prompt.run_cmd fail with the error message
AttributeError: 'module' object has no attribute 'object'
If you wanted to patch a concrete instance, you could easily do this using mock.patch.object and wraps (see for example this question.
If you want to patch your function for all instances instead, you indeed have to use mock.patch. In this case you could only mock the class itself, as mocking the method would not work (because it is used on instances, not classes), so you cannot use wraps here (at least I don't know a way to do this).
What you could do instead is derive your own class from Prompt and overwrite the method to collect the calls yourself. You could then patch Prompt by your own implementation. Here is a simple example:
class MyPrompt(Prompt):
calls = []
def run_cmd(self, input):
super().run_cmd(input)
# we just add a string in the call list - this could be more sophisticated
self.__class__.calls.append(input)
class TestRunner(unittest.TestCase):
def test_run(self):
with mock.patch("application.engine.prompt.Prompt", MyPrompt) as mock_prompt:
runner = Runner()
runner.run()
self.assertEqual(["unique cmd"], mock_prompt.calls)

Accessing `super()` inside python class property

I'm learning about python's inheritance, and have come across a behaviour I don't quite understand. Here is a minimal working example:
class Test():
def meth1(self):
print('accessing meth1')
return super().a #calling random nonexisting attribute; error (as expected)
#property
def prop1(self):
print('accessing prop1')
return super().a #calling random nonexisting attribute; no error?
def __getattr__(self, name):
print('getattr ' + name)
test = Test()
Calling .meth1() fails as expected...
In [1]: test.meth1()
accessing meth1
Traceback (most recent call last):
File "<ipython-input-160-4a0675c95211>", line 1, in <module>
test.meth1()
File "<ipython-input-159-1401fb9a0e13>", line 5, in meth1
return super().a #calling random nonexisting attribute; error (as expected)
AttributeError: 'super' object has no attribute 'a'
...as super() is object which does indeed not have this attribute.
But .prop1 does not...
In [2]: test.prop1
accessing prop1
getattr prop1
...which I don't understand. It seems the property is called twice, once 'normally' and once via __getattr__.
Some observations:
I assume it's got something to do with the property decorator.
The attribute .a seems to never be accessed.
If I replace the return super().a line in prop1 with something like return 5, the __getattr__ method is never called.
If I actually make Test inherit from a class having an attribute a, its value is returned from test.meth1(), but not from test.prop1.
Could someone explain what's going on here? I've not been able to find any useful information addressing the combination of attribute decorators and super().
Many thanks,
TLDR: meth1 raises AttributeError after lookup, when __getattr__ is not involved. prop1 raises AttributeError during lookup, triggering a fallback to __getattr__ which succeeds to return None.
>>> test.prop1 # AttributeError happens here during lookup
accessing prop1
getattr prop1
>>> meth = test.meth1 # AttributeError happens *not* here during lookup
>>> meth() # AttributeError happens here *after* lookup
...
AttributeError: 'super' object has no attribute 'a'
The __getattr__ method is only called when an "attribute is not found" – in other words that AttributeError is raised on access. The same behaviour occurs when the property raises the error directly:
class Test():
#property
def prop1(self):
print('accessing prop1')
raise AttributeError # replaces `super().a`
def __getattr__(self, name):
print('getattr ' + name)
test = Test()
test.prop1 # < explicitly raises AttributeError
# accessing prop1
# getattr prop1
test.prop2 # < implicitly raises AttributeError
# getattr prop2
The AttributeError does not reveal whether it comes from a missing prop1 attribute or some nested internal attribute (say, super().a). Thus, both trigger the fallback to __getattr__.
This is intended behaviour of __getattr__.
object.__getattr__(self, name)
Called when the default attribute access fails with an AttributeError (either __getattribute__() raises an AttributeError because name is not an instance attribute or an attribute in the class tree for self; or __get__() of a name property raises AttributeError).
It allows properties to fallback to the regular lookup mechanism when they cannot produce a value.

Unable to patch an object's attribute correctly

I have a Python module as follows:
# src/exec.py
class A:
def run(self, stuff):
b = B(stuff.x)
class B:
def __init__(self, x):
self.obj = self.create_some_obj()
I'm trying to test a part of class A independently, for which I need to replace the obj in B with a fake object. I'm doing this as follows:
# test/test_execs.py
import exec as ex
class FakeObjForB:
def __init__(self):
# some init
class TestClass:
#patch.object(ex.B, 'obj', FakeObjForB())
def test_with_fake_obj(self):
a = ex.A()
a.run()
# assert something about the state of a that depends on the b inside its run method
Running this test gives me the error: AttributeError: <class 'B'> does not have the attribute 'obj'. I tried replacing the line with the #patch decorator with #patch.object(ex.B, 'obj', FakeObjForB(), create=True). This, however, results in b.obj using the actual definition, and not FakeObjForB, which in turn leads to a false-failure in the assertion in test_with_fake_obj. Any clues about what I'm doing incorrectly here?
In your example you're patching the B class, that's the object passed as the first argument. That class doesn't declare obj attribute on the class level and so AttributeError is raised. When you provide create=True it won't complain as that argument allows the obj attribute to be dynamically created when needed/accessed. But, that won't ever happen as the very first "access" of that attribute is its actual creation - no dynamic mocking ever happened.
A solution is to actually patch the method whose returned value would be assigned to the obj attribute, like:
#patch.object(ex.B, 'create_some_obj', FakeObjForB())

Property and __getattr__ compatibility issue with AttributeError

I just encountered an unexpected behavior. This is a simple class with a __getattr__ method and a property attribute with a typo inside:
class A(object):
def __getattr__(self, attr):
if not attr.startswith("ignore_"):
raise AttributeError(attr)
#property
def prop(self):
return self.some_typo
a = A() # Instantiating
a.ignore_this # This is ignored
a.prop # This raises an Attribute Error
This is the expected outcome (the one I get if __getattr__ is commented):
AttributeError: 'A' object has no attribute 'some_typo'
And this is what I get:
AttributeError: prop
I know this has to do with__getattr__ catching the AttributeError but is there a nice and clean workaround for this issue? Because I can assure you, this is a debug nightmare...
You can just raise a better exception message:
class A(object):
def __getattr__(self, attr):
if not attr.startswith("ignore_"):
raise AttributeError("%r object has not attribute %r" % (self.__class__.__name__, attr))
#property
def prop(self):
return self.some_typo
a=A()
a.ignore_this
a.prop
EDIT: calling __getattribute__ from object base class solves the problem
class A(object):
def __getattr__(self, attr):
if not attr.startswith("ignore_"):
return self.__getattribute__(attr)
#property
def prop(self):
return self.some_typo
As mentioned by #asmeurer, the solution by #mguijarr calls prop twice. When prop first runs, it raises an AttributeError which triggers __getattr__. Then self.__getattribute__(attr) triggers prop again, finally resulting in the desired exception.
BETTER ANSWER:
Here we are better off replacing __getattribute__ instead of __getattr__. It gives us more control since __getattribute__ is invoked on all attribute access. In contrast, __getattr__ is only called when there has already been an AttributeError, and it doesn't give us access to that original error.
class A(object):
def __getattribute__(self, attr):
try:
return super().__getattribute__(attr)
except AttributeError as e:
if not attr.startswith("ignore_"):
raise e
#property
def prop(self):
print("hi")
return self.some_typo
To explain, since A subclasses object in this case, super().__getattribute__(attr) is equivalent to object.__getattribute__(self, attr). That reads a's underlying object attribute, avoiding the infinite recursion had we instead used self.__getattribute__(attr).
In case of AttributeError, we have full control to either fail or reraise, and reraising gives a sensible error message.

Categories