Show docstrings on every function call - python-3.x

Let's say I have a code like this:
class NewTestCase(unittest.TestCase, CommonMethods):
def setUp(self):
self.shortDescription()
def test_01_sample test(self):
"""Testing something"""
self.create_account(self.arg['account'])
assert ...
...
class CommonMethods():
def create_account(self, account):
"""Creating account"""
...
if __name__ == '__main__':
unittest.main(verbosity=2, warnings='ignore')
I want to show the docstrings of all methods defined / created by me ('Testing something' and 'Creating account'), but the execution shows 'Testing something' only. Any tip?

Maybe there is an option for that in the unittest module, but I doubt it; otherwise, how would that module distinguish between your methods and functions and all sorts of library functions?
What you could do is to use another function to modify the existing functions to print their Docstring and/or other useful information whenever they are called. You could make this a decorator, or just call the function manually before running the tests.
This one should 'verbosify' all the methods of a given class (only slightly tested!), and you could make similar ones for individual functions or entire modules.
def verbosify(clazz):
for name in dir(clazz):
attr = getattr(clazz, name)
if not name.startswith("__") and callable(attr):
def attr_verbose(*args, **kwargs):
print("Calling", name, args, kwargs)
print(attr.__doc__)
return attr(*args, **kwargs)
setattr(clazz, name, attr_verbose)
Just call verbosify(CommonMethods) in your main block.

Related

Using singledispatch with custom class(CPython 3.8.2)

Let's say I want to set functions for each classes in module Named 'MacroMethods'. So I've set up singledispatch after seeing it in 'Fluent Python' like this:
#singledispatch
def addMethod(self, obj):
print(f'Wrong Object {str(obj)} supplied.')
return obj
...
#addMethod.register(MacroMethods.Wait)
def _(self, obj):
print('adding object wait')
obj.delay = self.waitSpin.value
obj.onFail = None
obj.onSuccess = None
return obj
Desired behavior is - when instance of class 'MacroMethods.Wait' is given as argument, singledispatch runs registered function with that class type.
Instead, it runs default function rather than registered one.
>>> Wrong Object <MacroMethods.Wait object at 0x0936D1A8> supplied.
However, type() clearly shows instance is class 'MacroMethods.Wait', and dict_keys property also contains it.
>>> dict_keys([<class 'object'>, ..., <class 'MacroMethods.Wait'>])
I suspect all custom classes I made count as 'object' type and don't run desired functions in result.
Any way to solve this problem? Entire codes are here.
Update
I've managed to mimic singledispatch's actions as following:
from functools import wraps
def state_deco(func_main):
"""
Decorator that mimics singledispatch for ease of interaction expansions.
"""
# assuming no args are needed for interaction functions.
func_main.dispatch_list = {} # collect decorated functions
#wraps(func_main)
def wrapper(target):
# dispatch target to destination interaction function.
nonlocal func_main
try:
# find and run callable for target
return func_main.dispatch_list[type(target)]()
except KeyError:
# If no matching case found, main decorated function will run instead.
func_main()
def register(target):
# A decorator that register decorated function to main decorated function.
def decorate(func_sub):
nonlocal func_main
func_main.dispatch_list[target] = func_sub
def register_wrapper(*args, **kwargs):
return func_sub(*args, **kwargs)
return register_wrapper
return decorate
wrapper.register = register
return wrapper
Used like:
#state_deco
def general():
return "A's reaction to undefined others."
#general.register(StateA)
def _():
return "A's reaction of another A"
#general.register(StateB)
def _():
return "A's reaction of B"
But still it's not singledispatch, so I find this might be inappropriate to post this as answer.
I wanted to do similar and had the same trouble. Looks like we have bumped into a python bug. Found a write-up that describes this situation.
Here is the link to the Python Bug Tracker.
Python 3.7 breaks on singledispatch_function.register(pseudo_type), which Python 3.6 accepted

Forwarding methods to a wrapped class

I'd like to attach some extra functionality (logging more or less) to methods of another class, but with the caveat that I can't subclass them, since I do not have control over the instantiation of the original class, but I can forward an object with the same duck typing. In other words, I'd like to prepend some code to a few methods while forwarding everything else to the original class instance.
An example of what I'm trying to accomplish would be something like the following:
class A(object):
def __init__(self, wrapped):
self._wrapped = wrapped
def __getattr__(self, name):
return getattr(self._wrapped, name)
class B(object):
def foo(self):
print("foo")
#classmethod
def bar():
print("bar")
b = B()
a = A(b)
a.foo() # prints "foo"
a.bar() # Fails (tries to pass the self parameter to bar())
Except that the method above fails for class methods, so it won't do it in my case. Is there a way to forward calls like this in a way that also works with class methods and static methods?

How to Call Multiple Methods in Python Class Without Calling Each on Individually?

I have a class that contains a number of methods:
class PersonalDetails(ManagedObjectABC):
def __init__(self, personal_details):
self.personal_details = personal_details
def set_gender(self):
self.gender='Male:
def set_age(self):
self.set_age=22
etc.
I have many such methods, all that begin with the word `set. I want to create a new method within this class that will execute all methods that begin with set, like this:
def execute_all_settings(self):
'''
wrapper for setting all variables that start with set.
Will skip anything not matching regex '^set'
'''
to_execute=[f'''self.{i}()''' for i in dir(self) if re.search('^set',i)
print(to_execute)
[exec(i) for i in to_execute]
However, this reports an error:
NameError: name 'self' is not defined
How can I go about doing this?
more info
The reason I want to do it this way, rather than simply call each method individually, is that new methods may be added in the future, so I want to execute all methods (that start with "set" no matter what they are)
Do not use either exec or eval. Instead use getattr.
Also note that set_age is both a method and an attribute, try to avoid that.
import re
class PersonalDetails:
def __init__(self, personal_details):
self.personal_details = personal_details
def set_gender(self):
self.gender = 'Male'
def set_age(self):
self.age = 22
def execute_all_settings(self):
'''
wrapper for setting all variables that start with set.
Will skip anything not matching regex '^set'
'''
to_execute = [i for i in dir(self) if re.search('^set', i)]
print(to_execute)
for func_name in to_execute:
getattr(self, func_name)()
pd = PersonalDetails('')
pd.execute_all_settings()
print(pd.gender)
# ['set_age', 'set_gender']
# Male
This solution will work as long as all the "set" methods either do not expect any arguments (which is the current use-case), or they all expect the same arguments.

Python - Wrapping object with a class and preserving its methods

I have a scenario where I can accept different objects (classes or functions) and I wrap them with a class to enhance their capabilities and I want to still be able to access their native methods (that I didn't write).
With the __call__ function, I can easily pass the arguments to the native __call__ function, but how can I still route the functions I don't know beforehand to their native functions?
For example:
import modules.i.didnt.write as some_classes
import modules.i.didnt.write2 as some_functions
class Wrapper:
def __init__(self, module, attr_name):
self.obj = getattr(module, attr_name)
self.extra_args = ....
def __call__(self, *args, **kwargs):
return self.obj(*args, **kwargs)
def added_functionality(self, ...):
....
wrapped_class = Wrapper(some_classes, 'class_a')
wrapped_function = Wrapper(some_functions, 'func_a')
wrapped_class(a=1, b=2)
wrapped_function(a=10, b=20)
wrapped_class.native_method(c=10) # <--------------
In this example, the last one will fail, because native_method does not exist in the Wrapper class, but it exists in the class_a original structure.
How can I support the native functionality while adding my own?
Am I taking the wrong approach? Is there a better way to do it? Is it even possible?

Python, mocking and wrapping methods without instantating objects

I want to mock a method of a class and use wraps, so that it is actually called, but I can inspect the arguments passed to it. I have seen at several places (here for example) that the usual way to do that is as follows (adapted to show my point):
from unittest import TestCase
from unittest.mock import patch
class Potato(object):
def foo(self, n):
return self.bar(n)
def bar(self, n):
return n + 2
class PotatoTest(TestCase):
spud = Potato()
#patch.object(Potato, 'foo', wraps=spud.foo)
def test_something(self, mock):
forty_two = self.spud.foo(n=40)
mock.assert_called_once_with(n=40)
self.assertEqual(forty_two, 42)
However, this instantiates the class Potato, in order to bind the mock to the instance method spud.foo.
What I need is to mock the method foo in all instances of Potato, and wrap them around the original methods. I.e, I need the following:
from unittest import TestCase
from unittest.mock import patch
class Potato(object):
def foo(self, n):
return self.bar(n)
def bar(self, n):
return n + 2
class PotatoTest(TestCase):
#patch.object(Potato, 'foo', wraps=Potato.foo)
def test_something(self, mock):
self.spud = Potato()
forty_two = self.spud.foo(n=40)
mock.assert_called_once_with(n=40)
self.assertEqual(forty_two, 42)
This of course doesn't work. I get the error:
TypeError: foo() missing 1 required positional argument: 'self'
It works however if wraps is not used, so the problem is not in the mock itself, but in the way it calls the wrapped function. For example, this works (but of course I had to "fake" the returned value, because now Potato.foo is never actually run):
from unittest import TestCase
from unittest.mock import patch
class Potato(object):
def foo(self, n):
return self.bar(n)
def bar(self, n):
return n + 2
class PotatoTest(TestCase):
#patch.object(Potato, 'foo', return_value=42)#, wraps=Potato.foo)
def test_something(self, mock):
self.spud = Potato()
forty_two = self.spud.foo(n=40)
mock.assert_called_once_with(n=40)
self.assertEqual(forty_two, 42)
This works, but it does not run the original function, which I need to run because the return value is used elsewhere (and I cannot fake it from the test).
Can it be done?
Note The actual reason behind my needs is that I'm testing a rest api with webtest. From the tests I perform some wsgi requests to some paths, and my framework instantiates some classes and uses their methods to fulfill the request. I want to capture the parameters sent to those methods to do some asserts about them in my tests.
In short, you can't do this using Mock instances alone.
patch.object creates Mock's for the specified instance (Potato), i.e. it replaces Potato.foo with a single Mock the moment it is called. Therefore, there is no way to pass instances to the Mock as the mock is created before any instances are. To my knowledge getting instance information to the Mock at runtime is also very difficult.
To illustrate:
from unittest.mock import MagicMock
class MyMock(MagicMock):
def __init__(self, *a, **kw):
super(MyMock, self).__init__(*a, **kw)
print('Created Mock instance a={}, kw={}'.format(a,kw))
with patch.object(Potato, 'foo', new_callable=MyMock, wrap=Potato.foo):
print('no instances created')
spud = Potato()
print('instance created')
The output is:
Created Mock instance a=(), kw={'name': 'foo', 'wrap': <function Potato.foo at 0x7f5d9bfddea0>}
no instances created
instance created
I would suggest monkey-patching your class in order to add the Mock to the correct location.
from unittest.mock import MagicMock
class PotatoTest(TestCase):
def test_something(self):
old_foo = Potato.foo
try:
mock = MagicMock(wraps=Potato.foo, return_value=42)
Potato.foo = lambda *a,**kw: mock(*a, **kw)
self.spud = Potato()
forty_two = self.spud.foo(n=40)
mock.assert_called_once_with(self.spud, n=40) # Now needs self instance
self.assertEqual(forty_two, 42)
finally:
Potato.foo = old_foo
Note that you using called_with is problematic as you are calling your functions with an instance.
Do you control creation of Potato instances, or at least have access to these instances after creating them? You should, else you'd not be able to check particular arg lists.
If so, you can wrap methods of individual instances using
spud = dig_out_a_potato()
with mock.patch.object(spud, "foo", wraps=spud.foo) as mock_spud:
# do your thing.
mock_spud.assert_called...
Your question looks identical to python mock - patching a method without obstructing implementation to me. https://stackoverflow.com/a/72446739/9230828 implements what you want (except that it uses a with statement instead of a decorator). wrap_object.py:
# Copyright (C) 2022, Benjamin Drung <bdrung#posteo.de>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import contextlib
import typing
import unittest.mock
#contextlib.contextmanager
def wrap_object(
target: object, attribute: str
) -> typing.Generator[unittest.mock.MagicMock, None, None]:
"""Wrap the named member on an object with a mock object.
wrap_object() can be used as a context manager. Inside the
body of the with statement, the attribute of the target is
wrapped with a :class:`unittest.mock.MagicMock` object. When
the with statement exits the patch is undone.
The instance argument 'self' of the wrapped attribute is
intentionally not logged in the MagicMock call. Therefore
wrap_object() can be used to check all calls to the object,
but not differentiate between different instances.
"""
mock = unittest.mock.MagicMock()
real_attribute = getattr(target, attribute)
def mocked_attribute(self, *args, **kwargs):
mock.__call__(*args, **kwargs)
return real_attribute(self, *args, **kwargs)
with unittest.mock.patch.object(target, attribute, mocked_attribute):
yield mock
Then you can write following unit test:
from unittest import TestCase
from wrap_object import wrap_object
class Potato:
def foo(self, n):
return self.bar(n)
def bar(self, n):
return n + 2
class PotatoTest(TestCase):
def test_something(self):
with wrap_object(Potato, 'foo') as mock:
self.spud = Potato()
forty_two = self.spud.foo(n=40)
mock.assert_called_once_with(n=40)
self.assertEqual(forty_two, 42)

Resources