When using MagicMock in Python, user can define spec parameter which indicates what class we are trying to mock.
Since __class__ attribute does not return the class being mocked, is it possible to get that class object some other way?
For example
mock = MagicMock(spec=MyClass)
mock.some_magic_method() # returns <class 'MyClass'>
Related
I understand __class__ can be used to get the class of an object, it also can be used to get current class in a class definition. My question is, in a python class definition, is it safe just use __class__, rather than self.__class__?
#!/usr/bin/python3
class foo:
def show_class():
print(__class__)
def show_class_self(self):
print(self.__class__)
if __name__ == '__main__':
x = foo()
x.show_class_self()
foo.show_class()
./foo.py
<class '__main__.foo'>
<class '__main__.foo'>
As the codes above demonstrated, at least in Python3, __class__ can be used to get the current class, in the method show_class, without the present of "self". Is it safe? Will it cause problems in some special situations? (I can think none of it right now).
__class__ is lexically scoped, whereas some_object.__class__ is dynamically dispatched. So the two can different when the lexical scope is different from the of the receiver, like if lambdas are involved:
#!/usr/bin/env python3
class ClassA:
def print_callback(self, callback):
print(callback(self))
class ClassB:
def test(self):
ClassA().print_callback(lambda o: o.__class__) # <class '__main__.ClassA'>
ClassA().print_callback(lambda _: __class__) # <class '__main__.ClassB'>
ClassB().test()
It depends on what you're trying to achieve. Do you want to know which class's source code region you find yourself in, or the class of a particular object?
And I think it goes without saying, but I'll mention it explicitly: don't rely on the attribute directly, use the type function. I.e. prefer type(o) over o.__class__.
That is documented in the datamodel, so I believe it is safe/reliable.
From 3.3.3.5. Executing the class body:
Class variables must be accessed through the first parameter of instance or class methods, or through the implicit lexically scoped __class__ reference described in the next section.
From 3.3.3.6. Creating the class object:
__class__ is an implicit closure reference created by the compiler if any methods in a class body refer to either __class__ or super
It is true that the docs mention any methods, your foo.show_class is a function but perhaps not convincingly a method. However PEP 3135, which added this reference, is worded differently:
Every function will have a cell named __class__ that contains the class object that the function is defined in.
...
For functions defined outside a class body, __class__ is not defined, and will result in runtime SystemError.
I have experimented a little. By checking __dict__ of a class or an instance, I can see some method has type function and some bound method. The experiment is messy, and I can't figure the following questions out.
In Python 3, what are the differences between methods of a class or instance, which are "function" and which are "bound method"?
How are they created respectively?
Can they both be called on a class and on an instance? Will they both be implicitly given an instance as their first argument?
Is "bound method" an attribute of a class or an instance of a class?
Thanks.
This answer will be really technical, I hope it's still understandable though. The problem is that it requires knowledge of the descriptor protocol to understand how methods in Python work.
All functions in Python 3 are descriptors, to be precise they are non-data descriptors. That means they implements a __get__ method - but no __set__ method.
That's interesting because descriptors can do (almost) anything if they are looked up on a class or an instance.
By the definition of the __get__ method in Pythons data model:
object.__get__(self, instance, owner)
Called to get the attribute of the owner class (class attribute access) or of an instance of that class (instance attribute access). owner is always the owner class, while instance is the instance that the attribute was accessed through, or None when the attribute is accessed through the owner. This method should return the (computed) attribute value or raise an AttributeError exception.
So what does this have to do with the difference between function and bound_method?
It's easy, a function accessed through __get__ with an instance=None will return itself:
>>> def func(x): return x
>>> func.__get__(None, object)
<function __main__.func>
>>> func.__get__(None, object) is func
True
While it will be a bound_method if accessed with an not-None instance:
>>> func.__get__(object())
<bound method func of <object object at 0x00000155614A0610>>
It's basically just a wrapper around func with the instance stored:
>>> m = func.__get__(object())
>>> m.__self__ # stored instance
<object at 0x155614a0650>
>>> m.__func__ # stored function
<function __main__.func>
However, when called, it will pass the instance as first argument to the wrapped function:
>>> m()
<object at 0x155614a0650>
So, bound methods will pass the instance as first argument, while functions do not (they requires all attributes).
So when you look at a class all normal methods will display as functions while all normal methods on an instance will be bound methods.
Why did I mention normal methods? Because you can define arbitrary descriptors. For example the Python built-ins already contain several exceptions:
classmethod
staticmethod
property (this is in fact a data-descriptor so I'll neglect it in the following discussion)
classmethods will display as bound method even when looked up on the class. That's because they are meant to be callable on the class and pass the class to the function, no matter if they are called on the class or the instance:
class Test(object):
#classmethod
def func(x):
return x
>>> Test.func
<bound method Test.func of <class '__main__.Test'>>
>>> Test().func
<bound method Test.func of <class '__main__.Test'>>
And staticmethods always display as functions because they never pass anything additional to the function:
class Test(object):
#staticmethod
def func(x):
return x
>>> Test().func
<function __main__.Test.func>
>>> Test.func
<function __main__.Test.func>
So it's easily possible to see also bound methods on the class (e.g. classmethods) and likewise one could also find normal functions on instances (e.g. staticmethods).
How can I override the metaclass of a Python class, with a unittest.mock.MagicMock instance instead?
I have a function whose job involves working with the metaclass of an argument:
# lorem.py
class Foo(object):
pass
def quux(existing_class):
…
metaclass = type(existing_class)
new_class = metaclass(…)
The unit tests for this function will need to assert that the calls to
the metaclass go as expected, without actually calling a real class
object.
Note: The test case does not care about the metaclass's behaviour; it cares that quux retrieves that metaclass (using type(existing_class)) and calls the metaclass with the correct arguments.
So to write a unit test for this function, I want to pass a class object whose metaclass is a mock object instead. This will allow, for example, making assertions about how the metaclass was called, and ensuring no unwanted side effects.
# test_lorem.py
import unittest
import unittest.mock
import lorem
class stub_metaclass(type):
def __new__(metaclass, name, bases, namespace):
return super().__new__(metaclass, name, bases, namespace)
class quux_TestCase(unittest.TestCase):
#unittest.mock.patch.object(
lorem.Foo, '__class__', side_effect=stub_metaclass)
def test_calls_expected_metaclass_with_class_name(
self,
mock_foo_metaclass,
):
expected_name = 'Foo'
expected_bases = …
expected_namespace = …
lorem.quux(lorem.Foo)
mock_foo_metaclass.assert_called_with(
expected_name, expected_bases, expected_namespace)
When I try to mock the __class__ attribute of an existing class, though, I get this error:
File "/usr/lib/python3/dist-packages/mock/mock.py", line 1500, in start
result = self.__enter__()
File "/usr/lib/python3/dist-packages/mock/mock.py", line 1460, in __enter__
setattr(self.target, self.attribute, new_attr)
TypeError: __class__ must be set to a class, not 'MagicMock' object
This is telling me that unittest.mock.patch is attempting to set the __class__ attribute temporarily to a MagicMock instance, as I want; but Python is refusing that with a TypeError.
But placing a mock object as the metaclass is exactly what I'm trying to do: put a unittest.mock.MagicMock instance in the __class__ attribute in order that the mock object will do all that it does: record calls, pretend valid behaviour, etc.
How can I set a mock object in place of the Foo class's __class__ attribute, in order to instrument Foo and test that my code uses Foo's metaclass correctly?
You can't do exactly what you want. As you can see an object's __class__ attribute is very special in Python, and even for ordinary instances there are checks in runtime to verify it is assigned to a proper type.
When you get down to a class's __class__, that is even more strict.
Possible approach:
One thing to do in there is not pass a class to your test - but an object that is an instance from a crafted ordinary class, which will have an artificial __class__ attribute. Even them, you will have to change your code from calling type(existing_class) to do existing_class.__class__ directly. For an instance object to "falsify" its __class__ anyway, you have to implement __class__ as a property on its class (or override __getattribute__; (the class itself will report its true metaclass, but an instance can return whatever is coded on the __class__ property.
class Foo:
#property
def __class__(self):
return stub_metaclass
Actual suggestion:
But then, since you are at it, maybe the simplest thing is to mock type instead on the target module where quux is defined.
class MockType:
def __init__(self):
self.mock = mock.Mock()
def __call__(self, *args):
return self.mock
...
class ...:
...
def test_calls_expected_metaclass_with_class_name(
self,
):
try:
new_type = MockType()
# This creates "type" on the module "lorem" namespace
# as a global variable. It will then override the built-in "type"
lorem.type = new_type
lorem.quux(lorem.Foo)
finally:
del lorem.type # un-shadows the built-in type on the module
new_type.mock.assert_called_with(
'Foo', unittest.mock.ANY, unittest.mock.ANY)
Still another approach
Another thing that can be done is to craft a full "MockMetaclass" in the "old fashion": without unittest.magicmock at all, instead, with intrumented __new__ and other relevant methods that will record the called parameters, and function as a true metaclass for a class you pass in as parameter.
Considerations on what is being done
People reaching here, please note that one should not test the class creation (and metaclass) mechanisms themselves. One can just assume the Python runtime have these working and tested already.
class Foo:
def __init__(self):
pass
#property
def get_features(self):
return (1,2,3,)
def execute(self):
print self.get_features()
f = Foo()
f.execute()
I get:
TypeError: 'tuple' object is not callable
What I am interested in is actually the length of that tuple.
You should not be calling properties. Instead, you access them like normal attributes:
def execute(self):
print self.get_features
The only difference between properties and attributes is that properties have getter and setter functions which get called implicitly when you access or set their values. For more information, see the documentation for property.
Also, your class should be inheriting from object:
class Foo(object):
You should always do this in Python 2.x so that your class becomes a new-style class, which has far more functionality than an old-style one.
The reason is that you have the #property decorator on your get_features() function, making it a property, hence it isn't callable.
This means that execute() simply has to print the attribute, and not call it as a function
Python (2.6) seems to be derping for no reason, can anyone see a problem with this code?
class DB ():
def doSomething (self, str):
print str
class A ():
__db = DB()
#staticmethod
def getDB ():
return A.__db
db = property(getDB)
A.db.doSomething("blah")
Fails with the exception:
AttributeError: 'property' object has no attribute 'doSomething'
It was my understanding that a property would automatically run its getter when accessed, so why is it complaining about a property object, and why isn't it finding my clearly available method?
In addition to needing to inherit from object, properties only work on instances.
a = A()
a.db.doSomething("blah")
To make a property work on the class, you can define a metaclass. (A class is an instance of a metaclass, so properties defined on the metaclass work on the class, just as properties defined on a class work on an instance of that class.)
You aren't using classes correctly. A class is (normally) two things:
A factory for creating a family of related objects
A definition of the common behaviour of those objects
These related objects are the instances of the class. Normal methods are invoked on instances of the class, not on the class itself. If you want methods that can be invoked from the class, without an instance, you need to label the methods with #classmethod (or #staticmethod).
However I don't actually know whether properties work when retrieved from a class object. I can't check right now, but I don't think so. The error you are getting is that A.db is retrieving the property object which defines the property itself, it isn't "evaluating" the property to get A.__db. Property objects have no doSomething attribute. Properties are designed to be created in classes as descriptions of how the instances of those classes work.
If you did intend to be working with an instance of A, then you'll need to create one:
my_a = A()
my_a.db.doSomething("blah")
However, this will also fail. You have not correctly written getDB as any kind of method. Normal methods need an argument to represent the instance it was invoked on (traditionally called self):
def getDB(self):
...
Static methods don't, but need a decorator to label them as static:
#staticmethod
def getDB():
...
Class methods need both an argument to receive the class they were invoked on, and a decorator:
#classmethod
def getDB(cls):
...
You don't need getters in Python:
class B(object):
def do_something(self, str):
print str
class A(object):
db = B()
A.db.do_something("blah")
(I also PEP8:ed the code)