Replace Python class definition at runtime - python

This question may sound similar to the following, but I'm not sure how to apply their solutions to my use-case:
How to import a module given the full path?
Can you use a string to instantiate a class?
I have a class Foo defined and imported in my project. However, at runtime I may have a string containing a different definition of Foo (along with lots of other classes and import statements). I'd like to be able to replace the already loaded Foo with the one in my string, in a way that after the operation, anybody who instantiates f = Foo() would instantiate the definition from my string. At the same time, I'd like to ignore any other definitions/imports in my string. How to do this?
Assume the following project structure and use-case:
project/
__init__.py
mytypes/
__init__.py
foo.py # contains the definition 'class Foo'
another_package/
bar.py
main.py
Inside main.py and bar.py I have from mytypes.foo import Foo. After the replace operation detailed above I want both to use the new definition of Foo from the replacement string, but no other definition from my string.

The short answer is: don't do this. You will run into all kinds of strange errors that you will not expect
You can use exec to run some arbitrary code, if you pass a dictionary as the second argument the resulting "globals" from the executed string will be stored in the dictionary
namespace = {}
exec('class Foo:\n x = 10', namespace)
namespace['Foo'] # This will be a class named Foo
You could then assign this to the module
import your_module
your_module.Foo = namespace['Foo']
Now anywhere that your_module.Foo is accessed you will get the class from your string.
However, your module may have been imported at some time before you have patched it, it's very difficult to be able to say for certain when your module will be imported. Someone may have bound Foo using from your_module import Foo, if this has run before your patch then you will not change this Foo class. Even if you only ever access your_module.Foo, if an instance has been initialised before your patch then any subsequent instances will not even have the same type!
f = your_module.Foo()
# run your patch
isinstance(f, your_module.Foo) # False
In the case above f is an instance of a completely different type to the current your_module.Foo

Related

How do I mock a class inside a module called from another module in Python?

I have a structure like this:
dir_a/
foo.py
bar.py
dir_b/
parameters.py
I am trying to mock the Parameters class in parameters.py when testing. Both foo.py and bar.py import and use Parameters in the same way. Class Foo creates instances of class Bar.
My problem is that Foo gets the mocked version of Parameters but not Bar. I (thought I) understand the concept of where to patch, but still it seems like the issue is there. This is the test code:
#mock.patch("path.to.foo.Parameters")
#mock.patch("path.to.bar.Parameters")
def test_something(self, bar_mock, foo_mock):
foo_mock().get.return_value = 'val1'
bar_mock().get.return_value = 'val2'
Foo().load()
load() creates instances of Bar. Within foo I have the mocked Parameters and within bar I have the normal Parameters. What am I missing here?
If I directly from the test call Bar(), then it gets the mocked version.
And then a follow-up question. How can I mock the return values for different inputs to get? If I add parenthesis after get, like bar_mock().get('param.a').return_value, the return value becomes a MagicMock instead of a value.
Small update
What I didn't mention is that Parameters uses the Borg singleton pattern. It shouldn't matter because when I tried to make a reproducible example, it worked with the Borg pattern. My original problem (why I wanted to mock it from the beginning) was that if I initiate Parameters in the test, the production code still has its own (uninitiated) instance.

Problem with importing files.py in python

I think that this is a quite basic question, but I wasn't able to find anything. Sorry if this happen to be a duplicate.
I have a file with some functions defined, let's call this file main_functions.py.
In this file I rely on a function, which we can call foo(). For instance, in the file main_functions.py we can have something like this:
def bar():
return foo()
foo() is definend in another file, called secondary_functions.py
def foo():
return 1
Now, in my main script, I would like to import a file where I can define foo(), and then do something like:
from secondary_functions import * # Here I define foo()
from main_functions import *
bar()
If I do so, the function inside main_functions is not able to find the definitions that are present in secondary_functions, and I will get an error like:
NameError: name 'foo' is not defined
It is very important for me to solve this problem.
My aim is to be able to have different files called secondary_functions1.py, secondary_functions2.py, eccetera, definitions of foo().
And, to solve the problem, I don't want to change everytime the file that depend on these definitions, for instance inserting everytime something like import secondary_functionsX.py, which would solve the problem. I would like to change only the main script.
The foo name is imported in main.py. foo is not available in the main_functions.py module, because you have not imported it in that module. See Namespaces with Module Imports for more on why it works this way.
One way to do what you want is to supply foo as an argument to bar(). Example:
main.py:
from secondary_functions import foo
from main_functions import bar
bar(foo)
main_functions.py:
def bar(foo):
return foo()
secondary_functions.py:
def foo():
return 1
After the import statements, variables like pippo have become global variables in the scope of the main program. But global variables are not inherited by modules that get imported. Modules are supposed to be able to stand on their own as self-contained units, that can be imported by any program; imagine what could go wrong if they started using variables from whatever imports them...
Thus, the only way to do this is explicitly ‘giving’ the values to your module, for instance as additional function arguments. You could put everything that’s in main_functions.py in a Class, and then have your main script give it the desired global variables as arguments of its init construction function, so it can store them for usage by bar() and other methods.
It seems that the problem isn't calling the files correctly it's that you're not calling pippo correctly, if pippo is a global variable then i don't see why it's not working. the only way i can think of solving this is by saying file.pippo
and if you're going to have multiple files with a variable called pippo then it's best not to make them global and call then individually like i just showed you.
another thing that could be the problem is if you are defining pippo inside a function, which then makes it a local variable to that function only.
And the last problem i can think of is if you're using them in main_functions and they haven't been defined in main_functions and you're not importing the files into main_functions i.e
# in main_functions.py
import secondary_functions
then i don't think main_functions will be able to find the function and variable without making them arguments for the function you're using them in. or again you can do something like file.pippo

Importing in Python for multiple files

I am struggling to figure out how to handle importing dependencies that are used in multiple files.
Let's say I want to import an external API for example, and two classes depend on this import. Putting the import into the 'index' file, as an attempt to make it global does not work. I can import it in each class file fine, but that seems to be a violation of DRY, as well as setting myself up for failure later on.
So is there a way to import once, in a single file that is globally accessible? What I experimented with was creating an index.py, foo.py (for the foo class) and bar.py (for the bar class):
Index:
from example import API
import foo
import bar
foo()
bar()
foo.py:
class foo:
... (try to put the example API to use)
bar.py: (same as foo.py really, just here to make the case for using the same dependency in two different places)
This failed to work, as the classes appeared to not be able to access exampleAPI. What is the correct way to do this, or am I looking at it wrong? Thanks!
In general, you should import each module you need in each of your own modules that needs to use it. You don't need to worry about duplication, since each module has its own global namespace. Furthermore, modules are cached (in the sys.modules dictionary) so you don't need to worry about extra work being done to load the module multiple times.
That said, there can be some exceptions. For instance, if the specific source of an API is considered "private" information (e.g. because it's an implementation detail or because it might be configurable and not always come from the same place all the time), it might make sense to import it into some namespace where all other users will look for it.
On the other hand, your example suggests you may be splitting up your code more than you should. Unlike some other languages (such as Java), in Python it's neither required nor recommended for each class to live in its own file. Instead, you should divide your code up into modules dictated by how closely they interact with each other. Closely related classes should be part of the same module, while pieces that don't interact at all might make more sense in separate modules (especially if some other code might need one part but not the other). It may not be inappropriate for your whole program to be in a single module! Obviously, some of this is a matter of style and taste, so there's not a single best option for every programmer in every situation.
For your example code, if you want to keep separate modules, I'd suggest something like this:
index.py:
from foo import Foo # no need to import API here if you're not using it directly
from bar import Bar
foo = Foo() # create an instance of the foo class
result = foo.some_method() # call methods on it
bar = Bar(foo) # you can also pass your instances around to other classes
foo.py:
from example import API
class Foo:
def some_method(self):
return API.whatever() # use the API in some way
bar.py:
from example import API # don't worry about importing the API more than ocne
class Bar:
def __init__(self, foo):
self.foo = foo
def blah(self):
API.something_else(self.foo.some_method())
Note that I changed some names around. Python's convention is to use CapitalizedNames for classes, and lowercase_names_with_underscores (sometimes known as "snake case") for modules, variables and methods. Your original code seemed to have some confusion between the modules name foo and bar and the classes within them with the same names. Using different styles for the different names can help avoiding that confusion.

Does Python import instantiate a mystery class?

I thought about this for a while and can't think of a better title, sorry.
I'm new'ish to Python, and (like many other's it seems) I just can't get my head around import.
I think I understand 'modules' and 'packages', classes and attributes and all that. It's one specific behavior I need clarified.
Say I have a file, foo.py. It has one line it:
x = 1
If, in another file, I `import foo", I can reference x. And, wonderfully, in another file I can import foo and now those two files can share x. Leaving classes out of the discussion for simplicity, I believe this is the pythonic way to share attributes between files.
Here's the question: Is is fair to say, when I import foo, that foo.py itself is, (for lack of a better metaphor), secretly instantiated by the interpreter?
I realize if I define a class in a module, it follow traditional rules and only become instantiated if I explicitly do so. But, the python interpreter (via the import statement) instantiating an instance of my module in the global namespace is the only way to explain the attribute sharing behavior.
Is this true? Semi-true? Or am I wandering with the Sleestaks in the Land of the Lost?
When you import a module:
if the module has not been previously imported, the file is parsed in to a module object which is added to sys.modules with a key that is the import path from the pythonpath to your module
that module object (or some member thereof) is aliased in the importing namespace, the alias and object being referenced being determined by the specific form of import you used
So when you import foo, the interpreter checks sys.modules for something registered with the name foo. If it finds it, it provides a label foo in the local namespace for the foo module. If it doesn't, it searches down the pythonpath until it finds a foo module, parses that to a module object, adds that object to sys.modules, and adds a label in the local namespace for that module object.
import foo as foof does the same thing, only the local namespace label created is foof. from foo import x follows the same process up to the point of creating a label and reference in the local namespace, instead providing a label x in the namespace for the attribute x from the foo module. from foo import x as foox just combines the 2 ideas.
With classes, you can actually poke around this whole system by crawling up and down the tree using the __module__ attribute.
The import creates an instance of a "module" object. It is worth knowing that this is created only the first time the module is imported. The following times it is imported you are getting a reference to the original. You can create your own module objects on the fly with a bit of instrospection.
import glob # Import any python module
moduleType = type(glob)
onTheFly = moduleType("OnTheFly", "Docstring for this module")
Although there isn't much benefit to creating these.
Yes, indeed its true. If you execute import foo a module object foo is instatiated and the contents of your file e.g a class bar is added as a member of that object.

Masquerading real module of a class

Suppose you have the following layout for a python package
./a
./a/__init__.py
./a/_b.py
inside __init__.py you have
from _b import *
and inside _b.py you have
class B(object): pass
If you import from interactive prompt
>>> import a
>>> a.B
<class 'a._b.B'>
>>>
How can I completely hide the existence of _b ?
The problem I am trying to solve is the following: I want a facade package importing "hidden" modules and classes. The classes available from the facade (in my case a) are kept stable and guaranteed for the future. I want, however, freedom to relocate classes "under the hood", hence the hidden modules. This is all nice, but if some client code pickles an object provided by the facade, this pickled data will refer to the hidden module nesting, not to the facade nesting. In other words, if I reposition the B class in a module _c.py, client codes will not be able to unpickle because the pickled classes are referring to a._b.B, which has been moved. If they referred to a.B, I could relocate the B class as much as I want under the hood, without ruining pickled data.
try:
B.__module__= 'a'
Incidentally you probably want an absolute import:
from a._b import *
as relative imports without the new explicit dot syntax are going away (see PEP 328).
ETA re comment:
I would have to set the module explicitly for every class
Yes, I don't think there's a way around that but you could at least automate it, at the end of __init__:
for value in globals().values():
if inspect.isclass(value) and value.__module__.startswith('a.'):
value.__module__= 'a'
(For new-style classes only you could get away with isinstance(value, type) instead of inspect. If the module doesn't have to run as __main__ you could use __name__ instead of hard-coding 'a'.)
You could set the __module__ variable for Class B
class B(object): pass
B.__module__ = 'a'
For classes, functions, and methods, this attribute contains the name of the module in which the object was defined.
Or define it once in your __init__.py:
from a._b import B # change this line, when required, e.g. from a._c import B
B.__module__ = 'a'

Categories