Python Can I limit where a function can be called from - python

I have a class and a normal constructor but I wish to preprocess the parameters and postprocess the result so I provide a mandated Factory constructor. Yes, I know that this is an unusual meaning for Factory and I also know that I could use memoization to do my processing but I had problems with extending a memoized class.
I wish to prevent myself from accidentally using the normal constructor and this is one way of doing it.
import inspect
class Foo():
def __init__(self):
actual_class_method = Foo.Factory
# [surely there's a way to do this without introspection?]
allowed_called_from = {name:method for name,method in inspect.getmembers(Foo, inspect.ismethod)}['Factory']
actual_called_from = inspect.currentframe().f_back.f_code # .co_name)
print("actual class method = ",actual_class_method," id = ",id(actual_class_method),",name = ",actual_class_method.__name__)
print("allowed called from = ",allowed_called_from,", id = ",id(allowed_called_from),", name =",allowed_called_from.__name__)
print()
print("actual called from = ",actual_called_from,", id = ",id(actual_called_from),", name =",actual_called_from.co_name)
#classmethod
def Factory(cls):
Foo()
Foo.Factory()
produces output
actual class method = <bound method Foo.Factory of <class '__main__.Foo'>> id = 3071817836 ,name = Factory
allowed called from = <bound method Foo.Factory of <class '__main__.Foo'>> , id = 3072138412 , name = Factory
actual called from = <code object Factory at 0xb7118f70, file "/home/david/Projects/Shapes/rebuild-v0/foo.py", line 15> , id = 3071381360 , name = Factory
Suppose I wished to check that the constructor to Foo() had been called from its Factory. I can find various things about the method whence Foo() was called such as its name and the filename where it was compiled, and this would be sufficient to stop me accidentally calling it directly, but I can't see a way of saying (the method that Foo() was called from) is (the method Factory() in the class Foo). Is there a way of doing this?

Alex Martelli posted an answer.
This might get you what you want:
class Foo:
def __init__(self):
print('Foo.__init__') # might consider throwing an exception
#classmethod
def makeit(cls):
self = cls.__new__(cls)
self.foo = 'foo'
return self
f = Foo() # accidentally create Foo in the usual way
g = Foo.makeit() # use the 'One True Way' of making a Foo
print(g.foo)
print(f.foo)
Output:
Foo.__init__
foo
Traceback (most recent call last):
File "D:\python\soMetaClassWorkAroundInit.py", line 19, in <module>
print(f.foo)
AttributeError: 'Foo' object has no attribute 'foo'

without inspect, you could provide a default argument to your constructor and check that the value passed is the one you're expecting (with default value at 0)
only the factory has a chance to pass the correct initialization value (or someone really wanting to call the constructor, but not by accident)
class Foo():
__MAGIC_INIT = 12345678
def __init__(self,magic=0):
if magic != self.__MAGIC_INIT:
raise Exception("Cannot call constructor, use the factory")
#classmethod
def Factory(cls):
return Foo(magic=Foo.__MAGIC_INIT)
f = Foo.Factory() # works
f = Foo() # exception
another variation would be to toggle a private "locking" boolean. If set to True, crash when entering constructor, else let it do its job, then reset to True.
Of course the factor has access to this boolean, and can set it to False before calling the constructor:
class Foo():
__FORBID_CONSTRUCTION = True
def __init__(self):
if self.__FORBID_CONSTRUCTION:
raise Exception("Cannot call constructor, use the factory")
Foo.__FORBID_CONSTRUCTION = True # reset to True
#classmethod
def Factory(cls):
Foo.__FORBID_CONSTRUCTION = False
return Foo()
f = Foo.Factory()
print("OK")
f = Foo()
not an issue even if multithreaded thanks to python GIL.

Related

unittest mock replace/reset mocked function when patching an object

I need to use unittest.mock.patch.object to mock an external method that may fail sometimes. In the test, the method shall raise some errors and then return to the original behaviour. Note that the behaviour I want to reproduce is by far more complicated than return 'bar' so I cannot just copy the code in Bar.some_method_that_may_fail:
import unittest
from unittest.mock import patch
class Bar(object):
def some_method_that_may_fail(self):
return "bar"
class Foo(object):
bar = None
def retry_method(self):
try:
self.__class__.bar = Bar().some_method_that_may_fail()
except KeyError:
self.retry_method()
class TestRetry(unittest.TestCase):
def setUp(self):
self.instance = Foo()
def test_retry(self):
# raise KeyError the first 5 calls to the function and then work normally
errors_list = [KeyError("")] * 5
def raise_errors(*_):
if errors_list:
errors_list.pop(0)
# TODO: return to original behaviour
with patch.object(Bar, 'some_method_that_may_fail', new=raise_errors) as mocked:
self.instance.retry_method()
self.assertEqual(self.instance.bar, 'bar')
if __name__ == '__main__':
unittest.main()
To return different values on subsequent invocations, you can use side_effect. Passing an array of values and/or exceptions will return these values/raise these exceptions in subsequent calls, in your case the exception instances and the result of an original call (if that is what you need).
So your test could look something like this:
class TestRetry(unittest.TestCase):
def setUp(self):
self.instance = Foo()
def test_retry(self):
original = Bar.some_method_that_may_fail # save the original
with patch(__name__ + '.Bar') as mocked:
bar = mocked.return_value
side_effect = ([KeyError()] * 5) + [original(bar)]
bar.some_method_that_may_fail.side_effect = side_effect
self.instance.retry_method()
self.assertEqual(6, mocked.call_count)
self.assertEqual('bar', self.instance.bar)
A few notes:
I replaced mock.patch.object with mock.patch, because you don't have an object to patch (Bar is instantiated inside the tested function, and you need to patch the instance, not the class)
using __name__ + '.Bar' for patching is because the tested function is in the same module as the test - in the real code this has to be replaced with the correct module path
I added a check for call_count to ensure that the method has indeed been called 6 times
Another thing: you have an error in your Foo, probably because you dumbed it down for the example. foo is a class variable, but you are setting an instance variable of the same name. You need instead:
class Foo:
bar = None
def retry_method(self):
try:
self.__class__.bar = Bar().some_method_that_may_fail()
except KeyError:
self.retry_method()
(note the self.__class__.bar)

Create an object from a python function returns a class

I have a function which returns a class:
def my_function():
# some logic
class AClass:
def __init__(self, argument):
init()
return AClass
And when I call this function, it returns a class, not an object of that class, right?
Value = my_function()
My question is how can I create an object from that class AClass?
Thank you.
my_class = my_function()
my_obj = my_class(arg)
Since the method returns a reference to a type you can simply use whatever constructor that is defined for the class directly on the return value.
Take this class for example:
class A:
def __init_(self, n = 0):
self.__n = n
Lets see what happens when reference the type directly when running the interpreter interactively:
>>> A
<class `__main__.A`>
Now lets return the type in a method:
>>> def f():
>>> return A
>>> f()
<class `__main__.A`>
Since the value of referencing the class directly and when returned from a method is the same, you can use that returned value the same you would normally. Therefore a = A() is the same as a = f()(). Even if the class takes parameter you can still reference it directly: a = f()(n = 10)

Storing reference to unbound method

I was trying to store reference to unbound method and noticed that it is being automatically bound. See example below. Is there more elegant way to store unbound method in the class without binding it?
def unbound_method():
print("YEAH!")
class A:
bound_method = unbound_method
unbound_methods = [unbound_method]
a = A()
a.unbound_methods[0]() # succeeds
a.bound_method() # fails
# TypeError: unbound_method() takes 0 positional arguments but 1 was given
This is not a standard "do you know about #staticmethod?" question.
What I'm trying to achieve is provide a way for children of the class provide another handler or certain situations. I do not control the unbound_method itself, it is provided from some library.
def unbound_method_a():
print("YEAH!")
def unbound_method_b():
print("WAY MORE YEAH!")
class A:
bound_method = unbound_method_a
class B(A):
bound_method = unbound_method_b
a = A()
a.bound_method() #fails
# print("YEAH!")
b = B()
b.bound_method() #fails
# print("WAY MORE YEAH!")
It can be achieved by wrapping the unbound method some dummy object like array, or in a bound method, just to drop self reference like this:
def unbound_method_a():
print("YEAH!")
def unbound_method_b():
print("WAY MORE YEAH!")
class A:
def call_unbound_method(self):
return unbound_method_a()
class B(A):
def call_unbound_method(self):
return unbound_method_b()
a = A()
a.call_unbound_method()
# print("YEAH!")
b = B()
b.call_unbound_method()
# print("WAY MORE YEAH!")
Not as far as I know. Would it be so bad if you just replace
a.bound_method()
with
A.bound_method()
?
I can't think of a situation in which the first one can't be replaced by the second one.

Assert that derived class methods are called in correct order

I'm trying to verify that the implementation of Base.run_this calls the methods of derived class (derived_method_[1st|2nd|3rd]) in correct order. As the output shows, the test is not working. How can I fix this?
class Base(object):
__metaclass__ = abc.ABCMeta
def __init__(self, parameters):
self.parameters = parameters;
#abc.abstractmethod
def must_implement_this(self):
return
def run_this(self):
self.must_implement_this()
if(self.parameters):
first = getattr(self, "derived_method_1st")
first()
second = getattr(self, "derived_method_2nd")
second()
third = getattr(self, "derived_method_3rd")
third()
class Derived(Base):
def must_implement_this(self):
pass
def derived_method_1st(self):
pass
def derived_method_2nd(self):
pass
def derived_method_3rd(self):
pass
mocked = MagicMock(wraps=Derived(True))
mocked.run_this()
mocked.assert_has_calls([call.derived_method_1st(), call.derived_method_2nd(), call.derived_method_3rd()])
Output
AssertionError: Calls not found.
Expected: [call.derived_method_1st(), call.derived_method_2nd(), call.derived_method_3rd()]
Actual: [call.run_this()]
wraps doesn't work well with instances. What happens here is that mocked.run_this returns a new mock object that 'wraps' Derived(True).run_this, where the latter is a bound method to the original Derived() instance.
As such, that method will call self.derived_method_* methods that are bound to that original instance, not to the mock.
You could patch in the run_this method on a spec mock instead:
mock = MagicMock(spec=Derived)
instance = mock()
instance.run_this = Derived.run_this.__get__(instance) # bind to mock instead
instance.parameters = True # adjust as needed for the test
instance.run_this()
Demo:
>>> mock = MagicMock(spec=Derived)
>>> instance = mock()
>>> instance.run_this = Derived.run_this.__get__(instance) # bind to mock instead
>>> instance.parameters = True # adjust as needed for the test
>>> instance.run_this()
>>> instance.mock_calls
[call.must_implement_this(),
call.derived_method_1st(),
call.derived_method_2nd(),
call.derived_method_3rd()]

How to test if an object is a function vs. an unbound method?

def is_unbound_method(func):
pass
def foo(): pass
class MyClass(object):
def bar(self): pass
What can I put in the body of is_unbound_method so that
is_unbound_method(foo) == False
is_unbound_method(MyClass().bar) == False
is_unbound_method(MyClass.bar) == True
??
An unbound method has __self__ set to None:
def is_unbound_method(func):
return getattr(func, '__self__', 'sentinel') is None
Demo:
>>> foo.__self__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'function' object has no attribute '__self__'
>>> is_unbound_method(foo)
False
>>> MyClass.bar.__self__
>>> is_unbound_method(MyClass.bar)
True
>>> MyClass().bar.__self__
<__main__.MyClass object at 0x106c64a50>
>>> is_unbound_method(MyClass().bar)
False
The attribute is also available as .im_self, but __self__ is forward compatible.
Note that in Python 3 unbound methods are gone; accessing MyClass.bar returns the function object. Thus the above function will always return False.
See the Datamodel documentation, in the User-defined methods section:
Special read-only attributes: im_self is the class instance object, im_func is the function object
[...]
Changed in version 2.6: For Python 3 forward-compatibility, im_func is also available as __func__, and im_self as __self__.
[...]
When a user-defined method object is created by retrieving a user-defined function object from a class, its im_self attribute is None and the method object is said to be unbound.
In Python 3 there's no reliable way to determine that just from the function object (since any function defined in a class is just a function like any other).
Perhaps a sub-optimal approach is to inspect the signature and check for self as the first parameter:
import inspect
def is_unbound_method(obj):
signature = inspect.signature(obj)
if not signature.parameters:
return False
return next(iter(signature.parameters), None) == "self"
But of course, this depends on the first parameter being named self (and on other functions not using self as the first parameter), which is just a convention.
If you already know the object is defined within a class, perhaps a better approach is to check whether it's a callable that's not a classmethod or a staticmethod:
import inspect
def is_unbound_method_from(cls, obj):
return bool(
callable(obj)
and not isinstance(obj, (classmethod, staticmethod))
and inspect.getmembers(cls, lambda m: m is obj)
)
You should reorder the clauses in the conjunction above from what you believe is least likely to most likely (to avoid unnecessary computation).
This is what I came up with... According to comments in correct answer, valid for 2.x only
def is_unbound_method(func):
"""Test if ``func`` is an unbound method.
>>> def f(): pass
>>> class MyClass(object):
... def f(self): pass
>>> is_unbound_method(f)
False
>>> is_unbound_method(MyClass().f)
False
>>> is_unbound_method(MyClass.f)
True
"""
return getattr(func, 'im_self', False) is None
I know that this is a very old question, but it shows up in Google, so I will add another way: you can take a look at functions __qualname__ attribute.
Here is my code:
class Test:
def method(self, x: int):
print(self, x)
def free_func(x: int):
print("I'm a func!", x)
def top_func():
def local_func(x: int):
print("I'm a func!", x)
show_contents(local_func)
def show_contents(obj):
print(getattr(obj, '__name__', ''))
print(getattr(obj, '__qualname__', ''))
t = Test()
print('--- Instance ---')
show_contents(t.method)
print('--- Class ---')
show_contents(Test.method)
print('--- Function ---')
show_contents(free_func)
print('--- Nested Function ---')
top_func()
And here is the output:
--- Instance ---
method
Test.method
--- Class ---
method
Test.method
--- Function ---
free_func
free_func
--- Nested Function ---
local_func
top_func.<locals>.local_func
It's more than a little hacky to use, but __qualname__ has been available since 3.3, so it should work, at least.
So you could use a function like this:
def is_method(func) -> bool:
if not callable(func):
raise ValueError(f"{func!r} is not a callable object")
qualname = func.__qualname__
name = func.__name__
if qualname == name: # it's a top-level function
return False
elif qualname.endswith('.'+name): # it's either a nested function or a method
prefix = qualname[:-len(name) - 1]
return not prefix.endswith('<locals>')
else: # what is it, even?
raise ValueError(f"Can't tell if {func!r} is a method")

Categories

Resources