Python module importing - python

I'm working on a GUI app. Let's say that I have a file main.py which is in the root directory and I want to import widgets like this:
from widgets import FancyWindow, ColorPicker
# ...
And my app is structured like so:
| main.py
+ widgets/
| __init__.py
| fancy_window.py
| color_picker.py
...
IIRC, I would have to import classes from the other modules like so:
from widgets.color_picker import ColorPicker
from widgets.fancy_window import FancyWindow
So my question is if there is something I can do in widgets/__init__.py to make it so that I can do imports in the way I want to?

You actually have it there already, just make your __init__.py have those two lines:
from widgets.color_picker import ColorPicker
from widgets.fancy_window import FancyWindow
Anything that you import (and any other symbols you define) in __init__.py will then be available.
E.g. if you put:
apple = 5
in __init__.py you could also do: from widgets import apple. No magic :)

In __init__.py you can import the modules, then export them:
from fancy_window import FancyWindow
from color_picker import ColorPicker
__all__ = [FancyWindow, ColorPicker]
Now, you can do the following:
from widgets import * # Only imports FancyWindow and ColorPicker
from widgets import FancyWindow
from widgets import ColorPicker
import widgets
color_picker = widgets.ColorPicker()

The best way would be to use __init__.py to perform setups needed for the package.
# __init__.py
from fancy_window import FancyWindow
from color_picker import ColorPicker
Then in main.py you can perform imports directly.
# main.py
from widgets import FancyWindow, ColorPicker
A more convenient way to package everything in __init__.py
# __init__.py
from fancy_window import *
from color_picker import *

yes, by adding the public variables of the other modules to __all__ in __init__.py
see https://docs.python.org/3/reference/simple_stmts.html#import

Related

Initialize module only once on imports from multiple files

I have multiple python scripts, like this:
utils.py
module1.py
module2.py
module3.py
main.py
...
In utils.py:
import some_lib
role_id = "some_key_1"
secret_id = "some_key_2"
def initialize():
real_key = some_web_request(role_id, secret_id)
some_lib.init(real_key)
# define some other functions using some_lib
In module1.py, module2.py, module3.py:
import utils
# define some other functions using functions in utils.py
In main.py:
import module1
import module2
import module3
# do something
I want to run utils.initialize() only once for initializing some_lib. However, due to how import works in python, if I put initialize() on global area in utils.py then it will run on every import statement run.
I can implement this like this, in utils.py
import some_lib
initialized = False
def initialize():
# same as above
if not initialized:
initialize()
initialized = True
Is this a good practice? Is there better or elegant way to implement this?

Import Modules vice versa

In MicroPython I created two modules 'mod_a' and 'mod_b'.
I am trying to grab functionality from one to the other and the other way around.
|mod_a
| | foo.py
| | __init__.py
|mod b
| | baa.py
| | __init__.py
foo.py
# necessary to grab module mod_b
import sys
sys.path.append('.')
from mod_b import Baa
class Foo:
b = Baa()
b.printer()
def drinker(self):
print('Drinking')
baa.py
import sys
# not working
# from mod_a import Foo
class Baa:
def printer(self):
print('Printer')
print('b.Baa', sys.path) => ['.' ...]
# ==> how to get this working
# a = Foo()
# a.drinker()
So far I tried
import sys
import os
if '/' not in sys.path:
sys.path.insert(0, os.getcwd())
sys.path.insert(1, '.')
sys.path.insert(2, '/mod_b')
sys.path.insert(2, '/mod_a')
sys.path.insert(2, '.mod_b')
sys.path.insert(2, '.mod_a')
and
sys.modules.get('.mod_b')
Please note that the following code only solves the import problem.
The code still has a circular import problem.
ImportError: cannot import name 'Baa' from partially initialized
module 'mod_b.baa' (most likely due to a circular import)
(C:\Users\Guest\test\.\mod_b\baa.py)
When initializing class Foo, it needs to call Baa.printer() which calls a = Foo() need the uninitialized class Foo, you need to fix that later.
foo.py
import sys
sys.path.insert(0,'.')
from mod_b.baa import Baa
class Foo:
b = Baa()
b.printer()
def drinker(self):
print('Drinking')
baa.py
import sys
sys.path.insert(0,'.')
from mod_a.foo import Foo
class Baa:
def printer(self):
print('Printer')
a = Foo()
a.drinker()
Circular imports are not supported in MicroPython up to v1.18.
MicroPython is largely based on Python 3.4 and differences to Python 3.5 are documented including circular imports.

How to mock in a python unittest a library not installed locally?

I am trying to test using unittest a python 3.6 script that starts with (my_app.py):
import sys
from awsglue.utils import getResolvedOptions
args = getResolvedOptions(sys.argv, ['opt1', 'opt2', 'opt3'])
opt1 = args['opt1']
opt2 = args['opt2']
opt3 = args['opt3']
....
so in my test I did something like:
import unittest
import datetime
from mock import patch
import my_app
class TestMyApp(unittest.TestCase):
#patch('awsglue.utils.getResolvedOptions')
def test_mock_stubs(self, patch_opts):
patch_opts.return_value = {}
....
but the test soon fails at import my_app with:
ModuleNotFoundError: No module named 'awsglue'
as there is not awsglue locally installed. How can I test a module that import a not locally installed library and also mock it in my test?
You'll need to mock the imported module before the import for my_app happens. patch won't work here, because patch imports the module in order to patch it. And in this case, that import itself would cause an error.
To do this the simplest way is to trick python into thinking that awsglue is already imported. You can do that by putting your mock directly in the sys.modules dictionary. After this you can do the my_app import.
import unittest
import sys
from unittest import mock
class TestMyApp(unittest.TestCase):
def test_mock_stubs(self):
# mock the module
mocked_awsglue = mock.MagicMock()
# mock the import by hacking sys.modules
sys.modules['awsglue.utils'] = mocked_awsglue
mocked_awsglue.getResolvedOptions.return_value = {}
# move the import here to make sure that the mocks are setup before it's imported
import my_app
You can move the import hack part to a setup fixture method.
However I would suggest just installing the package. The import hack can be very difficult to maintain.

Import in a module fails because __name__ is __main__

Here is my project structure:
Project
main.py
myPackage/
subfolder1/
__init__.py
script11.py
script12.py
subfolder2/
__init__.py
script2.py
__init__.py
in main.pyI import script2.py the following way :
from myPackage.subfolder2 import script2 as script2
then, I call a function from script2.py in main.py with :
bar = script2.foo()
and in script2 I need to import a function from script1 :
from ..subfolder1.script11 import my_function
and it breaks with the error :
attempted relative import with no known parent package
I have inspected the __name__ variable and indeed it has the value __main__. How can I manage that properly ?
All you should have to do is change your import in main.py to from myPackage.subfolder2 import script2. I set up a directory and some files in this way, using that import, and the script runs as expected:
main.py
myPackage/
subfolder1/
script11.py
subfolder2/
script2.py
script11.py
def bar():
return 10
script2.py
from ..subfolder1.script11 import bar
def foo():
x = bar()
print('foo is', x)
main.py
from myPackage.subfolder2 import script2 as s2
s2.foo()
Running:
>>> py .\main.py
foo is 10
Some notes:
I'm assuming Python 3, since Python 2 was deprecated beginning of this year
In Python 3, the __init__.py files aren't necessary to make a package, but having them doesn't hurt anything. You can leave them out if you want.
The as script2 part in from subfolder2 import script2 as script2 is redundant. It will already be imported as script2.

Python - Intra-package importing when package modules are sometimes used as standalone?

Sorry if this has already been answered using terminology I don't know to search for.
I have one project:
project1/
class1.py
class2.py
Where class2 imports some things from class1, but each has its own if __name__ == '__main__' that uses their respective classes I run frequently. But then, I have a second project which creates a subclass of each of the classes from project1. So I would like project1 to be a package, so that I can import it into project2 nicely:
project2/
project1/
__init__.py
class1.py
class2.py
subclass1.py
subclass2.py
However, I'm having trouble with the importing with this. If I make project1 a package then inside class2.py I would want to import class1.py code using from project1.class1 import class1. This makes project2 code run correctly. But now when I'm trying to use project1 not as a package, but just running code from directly within that directory, the project1 code fails (since it doesn't know what project1 is). If I set it up for project1 to work directly within that directory (i.e. the import in class2 is from class1 import Class1), then this import fails when trying to use project1 as a package from project2.
Is there a way to have it both ways (use project1 both as a package and not as a package)? If there is a way, is it a discouraged way and I should be restructuring my code anyway? Other suggestions on how I should be handling this? Thanks!
EDIT
Just to clarify, the problem arrises because subclass2 imports class2 which in turn imports class1. Depending on which way class2 imports class1 the import will fail from project2 or from project1 because one sees project1 as a package while the other sees it as the working directory.
EDIT 2
I'm using Python 3.5. Apparently this works in Python 2, but not in my current version of python.
EDIT 2: Added code to class2.py to attach the parent directory to the PYTHONPATH to comply with how Python3 module imports work.
import sys
import os
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
Removed relative import of Class1.
Folder structure:
project2
- class3.py
- project1
- __init__.py
- class1.py
- class2.py
project2/project1/class1.py
class Class1(object):
def __init__(self):
super(Class1, self).__init__()
self.name = "DAVE!"
def printname(self):
print(self.name)
def run():
thingamy = Class1()
thingamy.printname()
if __name__ == "__main__":
run()
project2/project1/class2.py
import sys
import os
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from class1 import Class1
class Class2(Class1):
def childMethod(self):
print('Calling child method')
def run():
thingamy = Class2()
thingamy.printname()
thingamy.childMethod()
if __name__ == "__main__":
run()
project2/class3.py
from project1.class2 import Class2
from project1.class1 import Class1
class Class3(Class2):
def anotherChildMethod(self):
print('Calling another child method')
def run():
thingamy = Class3()
thingamy.printname()
thingamy.anotherChildMethod()
if __name__ == "__main__":
run()
With this setup each of class1, 2 and 3 can be run as standalone scripts.
You could run class2.py from inside the project2 folder, i.e. with the current working directory set to the project2 folder:
user#host:.../project2$ python project1/class2.py
On windows that would look like this:
C:\...project2> python project1/class2.py
Alternatively you could modify the python path inside of class2.py:
import sys
sys.path.append(".../project2")
from project1.class1 import class1
Or modify the PYTHONPATH environment variable similarly.
To be able to extend your project and import for example something in subclass1.py from subclass2.py you should consider starting the import paths always with project2, for example in class2.py:
from project2.project1.class1 import class1
Ofcourse you would need to adjust the methods I just showed to match the new path.

Categories

Resources