Importing classes contained in a module - python

I have the following files in my directory:
foo/
foo.py
foolib/
__init__.py
bar.py
Within __init__.py:
__all__ = ["bar"]
Within bar.py:
class Bar:
def __init__(self):
None
def hello(self):
print("Hello World")
return
def hi():
print("Hi World")
Now if I have the following code within foo.py:
from foolib import *
bar.hi()
foobar = Bar()
foobar.hello()
"Hi World" prints, but I get a NameError for Bar(). If I explicitly import the module:
from foolib.bar import *
I get the expected output "Hello World".
Is there a way for me to import classes from the modules, without explicitly calling them? I feel like I am missing something in the __init__ file. Either that or I am flagrantly violating some Python best practice.

To import the class you must import the class, somewhere. When you do from foolib import *, because of your __init__.py this imports the module bar. It doesn't allow you to access anything inside that module.
If you want to automatically access everything in bar from the foolib package without having to import bar, you could put this in __init__.py:
from bar import *
This makes everything in bar available directly in foolib.

Related

"from module import class" importing other classes from same module

Given the following files:
a.py
-----
class CommonClass(object):
def do_thing(self):
pass
b.py
-----
from a import CommonClass
class SubClassA(CommonClass):
def do_thing(self):
print("I am A")
class SubClassB(CommonClass):
def do_thing(self):
print("I am B")
c.py
-----
from a import CommonClass
from b import SubClassA
if __name__ == "__main__":
for member in CommonClass.__subclasses__():
member().do_thing()
I would expect only SubClassA is imported, and visible when looping through subclasses of CommonClass, but it seems SubClassB is imported as well.
I am running Python 3.8.5 and this is the output of python3 c.py:
$ python3 c.py
I am A
I am B
How can I only import the classes I want?
You did import only SubClassA to c.py. This can be tested by doing
x = SubClassB()
or
x = b.SubClassB()
Both will result in a NameError.
The thing is, that when you import a file, it is actually being ran, even when using from x import y!
This can be easily seen by adding a print("I'm from b.py") at the end of b.py and then running c.py.
This makes both SubClassA and SubClassB be subclasses of CommonClass, which you imported. So, while you don't have access to the SubClassB name, it is still a subclass of CommonClass and you can access it from there.
In general you don't have to import a module to be able to use its objects. Importing expands your namespace to include that module so you can create an object directly. You can still use this module's objects even without importing it if you acquired them in some other way (like importing a third module which returns objects from the second one).
Anyway right now, you are not really using even the imported SubClassA. If you want to "allow" certain classes to be considered only from an external source, you can create an allowed set of classes:
from a import CommonClass
from b import SubClassA
allowed_classes = {SubClassA}
if __name__ == "__main__":
for member in CommonClass.__subclasses__():
if member in allowed_classes:
member().do_thing()
Which only prints I am A
from a import CommonClass
from b import SubClassA
if __name__ == "__main__":
h = CommonClass.__subclasses__()[0]()
h.do_thing()
You can do this.

Python 2.6 unittest - how to set a value to use for a global variable in a function that you're testing

I'm having trouble setting the value of a global variable in a function that I'm writing for unit tests.
The function is probably not ready to be used in a test. Or at least to be used to test in an easy manner, but I'm trying to work around that.
Here is an example of the function I'm trying to test:
def my_func_with_globals(filepath):
spos=filepath.find(__my_global_var1)
new_path = filepath[0:spos] + __my_global_var2
return new_path
def some_function():
...
my_func_with_globals(filepath)
...
if __name__ = '__main__':
global __my_global_var1
__my_global_var1='value1'
global __my_global_var2
__my_global_var2='value2'
...
some_function()
And here is an example of my test:
import unittest
from my_module import *
class UnitTestMyModule(unittest.TestCase):
def test_my_func_with_globals(self):
self.assertEqual(my_func_with_globals('arbitrary/file/path'), 'valid output')
Another example of my test using #kdopen's suggestion (gives me the same error):
import unittest
import my_module
class UnitTestMyModule(unittest.TestCase):
def test_my_func_with_globals(self):
my_module.__my_global_var1='some/value'
my_module.__my_global_var2='second_val'
self.assertEqual(my_module.my_func_with_globals('arbitrary/file/path'), 'valid output')
I keep getting the error:
NameError: global name '__my_global_var1' is not defined.
I've tried a few different things, but I can't get anything to work. Using unittest.mock.patch looks like it would work perfectly, but I'm stuck with what I currently have with v2.6.4.
The globals are defined with a double leading underscore, so they are not imported by the from my_module import * statement.
You can make them accessible with the following:
from my_module import __my_global_var1, __my_global_var2
Alternatively, if you used import my_module you can access them as my_module.__my_global_var1 etc.
But I don't see any reference to the global variables in your sample test case
Here's a simple example
a.py
__global1 = 1
def foo():
return __global1
b.py:
import a
print "global1: %d" % a.__global1
print "foo: %d" % a.foo()
a.__global1 = 2
print "foo: %d" % a.foo()
And running b.py
$ python2.6 b.py
global1: 1
foo: 1
foo: 2
UPDATE:
Dang it, missed the obvious
You declare the variables within the if test. That code doesn't run on import - only when you execute python my_module from the command line.
During importing, __name__ will be set to my_module, not __main__
So, yes - they are undefined when you call your unit test.

import class defined in same module file?

I have a module file called mymodule.py, which contains the following code:
class foo:
def __init__(self):
self.foo = 1
class bar:
import foo
def __init__(self):
self.bar = foo().foo
The __init__.py file in the same directory has
from mymodule import foo
From a script in the same directory, I have the following code:
from mymodule import bar
When I try to run bar(), I get the error that No module named foo. How can I create an instance of foo in bar when they are defined within the same module file?
Classes are imported with module name first. However, you don't need to import classes in mymodule from within mymodule, just use it.
Meaning: remove the import foo line
You do not need to import an object defined in the same module:
class foo:
def __init__(self):
self.foo = 1
class bar:
def __init__(self):
self.bar = foo().foo
The import statement is intended for objects defined in other files only; you import the names defined in another python file into the current module.

Override module method where from...import is used

I have a problem overriding the method where from...import statement is used. Some example to illustrate the problem:
# a.py module
def print_message(msg):
print(msg)
# b.py module
from a import print_message
def execute():
print_message("Hello")
# c.py module which will be executed
import b
b.execute()
I'd like to override print_message(msg) method without changing code in a or b module. I tried in many ways but from...import imports the original method. When I changed the code to
import a
a.print_message
then I see my change.
Could you suggest how to solve this problem?
------------------ Update ------------------
I tried to do that like below e.g.:
# c.py module
import b
import a
import sys
def new_print_message(msg):
print("New content")
module = sys.modules["a"]
module.print_message = new_print_message
sys.module["a"] = module
But this is not working where I'm using for...import statement. Is working only for import a but as I wrote I don't want change code in b.py and a.py modules.
With your a and b modules untouched you could try implementing c as follows:
import a
def _new_print_message(message):
print "NEW:", message
a.print_message = _new_print_message
import b
b.execute()
You have to first import a, then override the function and then import b so that it would use the a module that is already imported (and changed).
module1.py
def function1():
print("module1 function1")
function2()
def function2():
print("module1 function2")
module2.py
import module1
test = module1.function1()
print(test)
""" output
module1 function1
module1 function2
"""
def myfunction():
print("module2 myfunction")
module1.function2 = lambda: myfunction()
test = module1.function1()
print(test)
"""output
module1 function1
module2 myfunction
"""

python: I need to understand better imports and packages

My application has a structure similar to this one:
myapp.py
basemod.py
[pkg1]
__init__.py
mod1.py
[pkg2]
__init__.py
mod2.py
myapp.py:
import pkg1
import pkg2
if __name__ == '__main__':
pkg1.main()
pkg2.main()
basemod.py:
import pkg1
def get_msg():
return pkg1.msg
pkg1/__init__.py:
import mod1
msg = None
def main():
global msg
mod1.set_bar()
msg = mod1.bar
pkg1/mod1.py:
bar = None
def set_bar():
global bar
bar = 'Hello World'
pkg2/__init__.py:
import mod2
def main():
mod2.print_foo()
pkg2/mod2.py:
import basemod
foo = basemod.get_msg()
def print_foo():
print(foo)
If I run myapp.py I get:
None
While in my mind I'd expect:
Hello World
My goal is to keep the two packages completely independent from each other, and only communicating through basemod.py, which is a sort of API to pkg1.
I'm starting to think that I have not completely understood how imports among packages work, what am I doing wrong?
Thank you!
Took me a while to read through all that code, but it looks like your problem is in pkg2/mod2.py. The line foo = basemod.get_msg() is executed the first time that file is imported, and never again. So by the time you change the value of mod1.bar, this has already executed, and foo is None.
The solution should simply be to move that line into the print_foo function, so it is only executed when that function is called - which is after the code that sets the relevant value.

Categories