NOTE: please, be sure that you really need such things in your project. It could be useful if you understand how it works. You can also spend hours or even days to understand what is going wrong if you don’t understand how method generation works in the project.
NOTE: this post is based on OpenStack Horizon integration tests.
We use method generation in our tests framework to generate methods like ‘go_to_some_page’. It’s pretty useful because we don’t need to implement such methods manually for each page.
The issue is that we need to generate methods with some default arguments. We used unbound methods for Python 2.7. Unfortunately, it doesn’t work at all in our case for Python 3.5+. What we need is something like: dynamically generated methods with some default arguments.
TBH, I’m pretty sure that it could be refactored to have less complex and cleaner code, so I’ll be happy to review such patches if somebody wants to do it.
There is a new function
functools.partialmethod
in Python 3.4 with is pretty similar to
functools.partial,
but leave self
as a first element, so it could be used for class methods:
def _go_to_page(self, path):
return Navigation._go_to_page(self, path)
wrapped_go_to = functools.partialmethod(_go_to_page, ‘/about’)
setattr(Navigation, ‘go_to_about_page’, wrapped_go_to)
The code above adds ‘go_to_about_page
method to class Navigation
which will
receive ‘/aboutvalue of
path` argument each time this method will be called.
I’m glad that this code is cleaner than we’ve got for Python 2.7:
class GoToMethodFactory(object):
def __init__(self, path):
self.path = path
self.name = self._create_name()
def __call__(self, *args, **kwargs):
return Navigation._go_to_page(args[0], self.path)
go_to_method = Navigation.GoToMethodFactory(‘/about’’)
inst_method = six.create_unbound_method(go_to_method, Navigation)
setattr(Navigation, inst_method.name, inst_method)
In both cases, the definition of the Navigation._go_to_page
is the same:
def _go_to_page(self, path):
…
It means, we need to path self
param everywhere (it’s easy because we use
code like: page.go_to_about_page()
) and pass path
param. That’s why we need
to store path
somewhere. For Python 2.7 we used a simple class for it. For
Python 3.5+ we use partialmethod
now.