Subclass __init__ not seeing superclass conditonally-imported module - python

I have a conditional import in a self-initialized instance of a superclass, but subclass cannot see the module (python 2.7):
class A(object):
def __init__(self, arg1):
self.attr1 = self.method1(arg1)
def method1(self, arg1):
if arg1 == 'foo':
import amodule
return amodule.method1()
else:
return 'not a dependency on foo'
class B(A):
def __init__(self, arg1):
super(B, self).__init__(arg1)
if arg1 == 'foo':
self.attr2 = self.method2(self.attr1)
def method2(self, attr1):
return amodule.method2()
if __name__=='__main__':
b = B("foo")
print b.attr2
This throws NameError: global name 'amodule' is not defined. a = A("foo") works just fine
Shouldn't the super call have executed import amodule in this case? (And using import should have put the module into globals?)

Doesn't import add /amodule/ to the global namespace of the currently
executing module? (__main__)?
No, the module is added to sys.modules but if it was imported locally then you won't have any references to it anymore. i.e the name amodule is now gone.
You could still access the module using sys.modules:
def method2(self, attr1):
import sys
return sys.modules['amodule'].method2()
Or you could import it using import amodule again and it will be picked up from sys.modules.
# Here b.py contains
# print('Module b was imported')
def func1():
print('inside func1')
import b
def func2():
print('inside func2')
import sys
print(sys.modules['b'])
import b
def func3():
print('inside func3')
import b
import sys
print('Deleted b')
del sys.modules['b']
import b
func1()
print()
func2()
print()
func3()
Demo:
inside func1
Module b was imported
inside func2
<module 'b' from '/Users/ashwini/py/b.py'>
inside func3
Deleted b
Module b was imported

Try putting import amodule on the first line of the program.
The reason is that amodule is imported in method1, method2 does not have access.

If you follow your code, you would see you don't reach method1().
When you create the object
b = B(foo)
You go through A.init() becuase the call for super(). However, your init of class A() doesn't include any import statements. Then the A.__init__ part is done, and you continue with B.__init__(). The next command is a call to amodule object, which wasn't imported at all.
You can add an helper method, which checks if the arg equals 'Foo' and if so import the module. Then add a call to this function in A.__init__() function.
On another note, __init__() job is to initialize variables. It shouldn't return anything.

Related

Python pass variable to imported module at "compilation" time

I'm trying to create an object from a custom class with a decorator in it.
Then I need to use the decorator of that object to create a bunch of functions in a different file.
I found the solution of using builtins online but it feels a bit hacky and makes my lsp spit out a bunch of errors.
I'm wondering if anyone knows a better solution.
This is my file structure:
main.py
mylib
├── A.py
└── B.py
main.py
import builtins
from mylib.A import A
a = A("This is printing in the decorator")
builtins.FOO = a
import mylib.B as B
B.foo()
B.poo()
A.py
class A:
def __init__(self, _message):
self.message = _message
def our_decorator(self, func):
def function_wrapper():
print(self.message)
func()
return function_wrapper
B.py
import builtins
#builtins.FOO.our_decorator
def foo():
print("foo")
#builtins.FOO.our_decorator
def poo():
print("poo")
I don't want to change the file structure if it can be avoided.
Using builtins to have a magically created decorator is indeed hacky.
An alternative would be to patch the functions in B from main. It is slightly cleaner (and linters should not complain) because the B module has no longer to be aware of the decorator:
main.py:
from mylib.A import A
a = A()
import mylib.B as B
# decorate here the functions of the B module
B.foo = a.our_decorator(B.foo)
B.poo = a.our_decorator(B.poo)
B.foo()
B.poo()
A.py is unchanged...
B.py:
def foo():
print("foo")
def poo():
print("poo")
As only one version of a module exists in a process, you can even use the functions of B from a third module, provided:
either it is imported after the decoration
or it only imports the module name (import mylib.B as B) and not directly the functions (from mylib.B import foo)

How to import a class from another file which is importing first class for [duplicate]

I want to separate code in different class and put them to different files. However those class are dependent on each other.
main.py:
from lib import A, B
def main():
a = A()
b = B()
a.hello()
b.hello()
if __name__ == '__main__':
main()
lib/_init_.py:
from a import A
from b import B
lib/a.py:
import lib.B
class A():
def __init__(self):
print "A"
def hello(self):
print "hello A"
b = B()
lib/b.py:
import lib.A
class B():
def __init__(self):
print "B"
def hello(self):
print "hello B"
a = A()
Is it possible to do that in Python?
EDIT:
I get this error message:
pydev debugger: starting
Traceback (most recent call last):
File "eclipse-python/plugins/org.python.pydev_2.7.1.2012100913/pysrc/pydevd.py", line 1397, in <module>
debugger.run(setup['file'], None, None)
File "eclipse-python/plugins/org.python.pydev_2.7.1.2012100913/pysrc/pydevd.py", line 1090, in run
pydev_imports.execfile(file, globals, locals) #execute the script
File "main.py", line 2, in <module>
from lib import A, B
File "lib/__init__.py", line 1, in <module>
from a import A
File "lib/a.py", line 1, in <module>
import lib.B
ImportError: No module named B
Instead of importing the modules on top, you could import the other module within the hello function.
class B():
def __init__(self):
print "B"
def hello(self):
from lib import A
print "hello B"
a = A()
When you have two classes depending on each other usually means that either they really belong to the same module or that you have a too tight coupling that should be resolved using dependency injection.
Now there are indeed a couple corner cases where importing from within the function is the "least worst" solution but that's still something you should avoid as much as possible.
Your main problem is that you're trying to import a class, but using syntax that only works to import a module. Specifically, import lib.A is never going to work if A is a class defined in module lib.a (and imported into the top-level namespace of lib).
What I suggest is that you avoid using the from _ import _ syntax unless you really need it. That makes the dependencies much easier to resolve:
lib/a.py:
import lib.b # note, we're not importing the class B, just the module b!
class A():
def foo(self):
return lib.b.B() # use the class later, with a qualified name
lib/b.py:
import lib.a # again, just import the module, not the class
class B():
def foo(self):
return lib.a.A() # use another qualified name reference
lib/__init__.py:
from a import A # these imports are fine, since the sub-modules don't rely on them
from b import B # they can be the public API for the A and B classes
You could also use relative module imports if you don't want a and b to be dependent on the name of their package lib.
This is sure to work because neither class A or B actually requires the other to exist yet in order to be defined. It's only after they're imported that instances of A need to know about the class B (and vise versa).
If one of the classes inherited from the other, or otherwise used an instance of the other at top level, you'd need to be more careful about which module was loaded up first, or it might still break.
If you want to import it only once you can import it in the constructor of the class and make the variable global:
class B():
def __init__(self):
global A
from lib import A
print "B"
def hello(self):
print "hello B"
a = A()
This would import A into a global variable and make it accessible form within the module.

How do I override imports of other files for unit testing

I am currently attempting to write unit tests for my Main.py's main() function
Here is a simplified version of my Main.py:
from Configuration import Configuration # Configuration.py is a file in the same dir
def main():
try:
Configuration('settings.ini')
except:
sys.exit(1) # Test path1
sys.exit(0) # Test path2
if __name__ == '__main__':
main()
In my Unit Tests\MainUnitTests.py I want to import ..\Main.py and fake the Configuration class in such a way that I can hit Test path1 and Test path2
I found that i can assert sys.exit() with the following:
with self.assertRaises(SystemExit) as cm:
main()
self.assertEqual(cm.exception.code, 1)
but I am having trouble overriding the from Configuration import Configuration
Thoughts?
So far I have tried the following within Unit Tests\MainUnitTests.py:
class FakeFactory(object):
def __init__(self, *a):
pass
sys.modules['Configuration'] = __import__('FakeFactory')
class Configuration(FakeFactory):
pass
Another example for demonstration:
foo.py:
from bar import a,b
x = a()
class a(object):
def __init__(self):
self.q = 2
y = a()
print x.q, y.q # prints '1 2' as intended
b() # I want this to print 2 without modifying bar.py
bar.py:
class a(object):
def __init__(self):
self.q = 1
def b():
t = a()
print t.q
when you use the import
from bar import a
it import the name directly into the module, so monkeypatching bar won't help, you need to override a directly in the main file:
def fake_a():
print("OVERRIDEN!")
main.a = fake_a
Do know that unittest has helper functions for this in the mock subpackage, I believe you could do something like:
from unittest.mock import patch
...
with patch("main.a", fake_a) as mock_obj: #there are additional things you can do with the mock_obj
do_stuff()
This would work in your first example with configuration since the class that needs to be patched is not used in the global scope although foo uses bar.a as soon as it is loaded so you would need to patch it before even loading foo:
from unittest.mock import patch
...
with patch("bar.a", fake_a) as mock_obj: #there are additional things you can do with the mock_obj
import foo #now when it loads it will be loaded with the patched name
However in this case foo.a would not be reverted at the end of the with block because it can't be caught by unittest... I really hope your actual use case doesn't use the stuff to be patched at module level.

Python: How to import package twice?

Is there a way to import a package twice in the same python session, under the same name, but at different scope, in a multi-threaded environment ?
I would like to import the package, then override some of its functions to change its behavior only when used in specific class.
For instance, is it possible to achieve something like this ?
import mod
class MyClass:
mod = __import__('mod')
def __init__():
mod.function = new_function # override module function
def method():
mod.function() # call new_function
mod.function() # call original function
It might seem weird, but in this case the user deriving the class wouldn't have to change his code to use the improved package.
To import a module as a copy:
def freshimport(name):
import sys, importlib
if name in sys.modules:
del sys.modules[name]
mod = importlib.import_module(name)
sys.modules[name] = mod
return mod
Test:
import mymodule as m1
m2 = freshimport('mymodule')
assert m1.func is not m2.func
Note:
importlib.reload will not do the job, as it always "thoughtfully" updates the old module:
import importlib
import mymodule as m1
print(id(m1.func))
m2 = importlib.reload(m1)
print(id(m1.func))
print(id(m2.func))
Sample output:
139681606300944
139681606050680
139681606050680
It looks like a job for a context manager
import modul
def newfunc():
print('newfunc')
class MyClass:
def __enter__(self):
self._f = modul.func
modul.func = newfunc
return self
def __exit__(self, type, value, tb):
modul.func = self._f
def method(self):
modul.func()
modul.func()
with MyClass() as obj:
obj.method()
modul.func()
modul.func()
outputs
func
newfunc
newfunc
func
where modul.py contains
def func():
print('func')
NOTE: this solution suits single-threaded applications only (unspecified in the OP)

Simple cross import in python

I want to separate code in different class and put them to different files. However those class are dependent on each other.
main.py:
from lib import A, B
def main():
a = A()
b = B()
a.hello()
b.hello()
if __name__ == '__main__':
main()
lib/_init_.py:
from a import A
from b import B
lib/a.py:
import lib.B
class A():
def __init__(self):
print "A"
def hello(self):
print "hello A"
b = B()
lib/b.py:
import lib.A
class B():
def __init__(self):
print "B"
def hello(self):
print "hello B"
a = A()
Is it possible to do that in Python?
EDIT:
I get this error message:
pydev debugger: starting
Traceback (most recent call last):
File "eclipse-python/plugins/org.python.pydev_2.7.1.2012100913/pysrc/pydevd.py", line 1397, in <module>
debugger.run(setup['file'], None, None)
File "eclipse-python/plugins/org.python.pydev_2.7.1.2012100913/pysrc/pydevd.py", line 1090, in run
pydev_imports.execfile(file, globals, locals) #execute the script
File "main.py", line 2, in <module>
from lib import A, B
File "lib/__init__.py", line 1, in <module>
from a import A
File "lib/a.py", line 1, in <module>
import lib.B
ImportError: No module named B
Instead of importing the modules on top, you could import the other module within the hello function.
class B():
def __init__(self):
print "B"
def hello(self):
from lib import A
print "hello B"
a = A()
When you have two classes depending on each other usually means that either they really belong to the same module or that you have a too tight coupling that should be resolved using dependency injection.
Now there are indeed a couple corner cases where importing from within the function is the "least worst" solution but that's still something you should avoid as much as possible.
Your main problem is that you're trying to import a class, but using syntax that only works to import a module. Specifically, import lib.A is never going to work if A is a class defined in module lib.a (and imported into the top-level namespace of lib).
What I suggest is that you avoid using the from _ import _ syntax unless you really need it. That makes the dependencies much easier to resolve:
lib/a.py:
import lib.b # note, we're not importing the class B, just the module b!
class A():
def foo(self):
return lib.b.B() # use the class later, with a qualified name
lib/b.py:
import lib.a # again, just import the module, not the class
class B():
def foo(self):
return lib.a.A() # use another qualified name reference
lib/__init__.py:
from a import A # these imports are fine, since the sub-modules don't rely on them
from b import B # they can be the public API for the A and B classes
You could also use relative module imports if you don't want a and b to be dependent on the name of their package lib.
This is sure to work because neither class A or B actually requires the other to exist yet in order to be defined. It's only after they're imported that instances of A need to know about the class B (and vise versa).
If one of the classes inherited from the other, or otherwise used an instance of the other at top level, you'd need to be more careful about which module was loaded up first, or it might still break.
If you want to import it only once you can import it in the constructor of the class and make the variable global:
class B():
def __init__(self):
global A
from lib import A
print "B"
def hello(self):
print "hello B"
a = A()
This would import A into a global variable and make it accessible form within the module.

Categories

Resources