Get around circular import error when using Sphinx - python

I have a module robot.py with a similarly named class. I also have a cop.py module and a robber.py module, each with similarly named classes as well, each subclassing from Robot. I broke them up to keep the files shorter.
In creating a Robot object, the Robot has multiple Cop and Robber attributes (i.e., each Robot has some other cops and robbers that it keeps track of).
I've gotten around circular import issues with the following (simplified) code structure in Robot.py, where I put module import statements at the very bottom:
class Robot(object):
def __init__(self):
self.other_cop = cop_module.Cop()
self.other_robber = robber_module.Robber()
import cops_and_robots.robo_tools.cop as cop_module
import cops_and_robots.robo_tools.robber as robber_module
(Of course, each Cop.py and Robber.py both have from cops_and_robots.robo_tools.robot import Robot at the beginning of their files)
This works fine in practice. However, when I try to autodoc using Sphinx, I get the following issue:
/Users/nick/Dropbox/Syncs/Code/Github/cops_and_robots/src/cops_and_robots/docs/cops_and_robots.robo_tools.rst:41: WARNING: autodoc: failed to import module u'cops_and_robots.robo_tools.robber'; the following exception was raised:
Traceback (most recent call last):
File "/usr/local/lib/python2.7/site-packages/sphinx/ext/autodoc.py", line 335, in import_object
__import__(self.modname)
File "/Users/nick/Dropbox/Syncs/Code/Github/cops_and_robots/src/cops_and_robots/robo_tools/robber.py", line 22, in <module>
from cops_and_robots.robo_tools.robot import Robot
File "/Users/nick/Dropbox/Syncs/Code/Github/cops_and_robots/src/cops_and_robots/robo_tools/robot.py", line 520, in <module>
import cops_and_robots.robo_tools.robber as robber_module
AttributeError: 'module' object has no attribute 'robber'
Is there a good way for me to restructure my code, or to modify Sphinx, so that I don't fall into this circular dependency issue?

If you have some funky circular dependency in your python code (hey we've all been there) you may need to force Sphinx to import your dependencies in a particular order. I can't help you with which order you need to use, but you can add import statements into conf.py:
import os
import sys
sys.path.insert(0, os.path.abspath(YOUR_MODULE_PATH)
import YOUR_MODULE_DEPENDENCY_TO_RESOLVE_FIRST

Related

ModuleNotFoundError: No module named 'data_management' in PyCharm

In github there are four py Data which I put on my PyCharm. When I run main.py I get this message:
/Users/Armut/Desktop/High_D/Coursera/bin/python /Users/Armut/Desktop/High_D/main.py
Traceback (most recent call last):
File "/Users/Armut/Desktop/High_D/main.py", line 6, in <module>
from data_management.read_csv import *
ModuleNotFoundError: No module named 'data_management'
Here is a screenshots:
Can someone help, what I am doing wrong or how can I fix it?
EDIT (Put folders):
/Users/Armut/Desktop/High_D/Coursera/bin/python /Users/Armut/Desktop/High_D/main.py
WARNING:root:Failed to import geometry msgs in rigid_transformations.py.
WARNING:root:Failed to import ros dependencies in rigid_transforms.py
WARNING:root:autolab_core not installed as catkin package, RigidTransform ros methods will be unavailable
Traceback (most recent call last):
File "/Users/Armut/Desktop/High_D/main.py", line 7, in <module>
from visualization.visualize_frame import VisualizationPlot
ModuleNotFoundError: No module named 'visualization.visualize_frame'
EDIT:
/Users/Armut/Desktop/High_D/Coursera/bin/python /Users/Armut/Desktop/High_D/src/main.py
Traceback (most recent call last):
File "/Users/Armut/Desktop/High_D/src/main.py", line 7, in <module>
from src.visualization.visualize_frame import VisualizationPlot
File "/Users/Armut/Desktop/High_D/src/visualization/visualize_frame.py", line 10, in <module>
from utils.plot_utils import DiscreteSlider
ModuleNotFoundError: No module named 'utils.plot_utils'
Edit (No errors, but I just get a blank picture):
Edit (I installed matplotlib 3.0.3 and got this):
The issue here is, that it is just a picture. If you can see there are buttons like "next". I should be able to click it so I can track it. But how does it work?
Do the following
from read_csv import *
import visualize_frame as vf
The reason why it was not working for you is because you were importing files that dont exist on your system. When you do from data_management.read_csv import *, what you are telling the Python interpreter to do is to search for a folder called data_management inside you're Coursera folder and get everything from read_csv.py.
This is the same case with visualize_frame. Since you have a flat directory structure, you dont need the folder names. You can directly import the .py files as is.
Another thing to note here is that I personally wouldn't do from read_csv import * because I will be flooding my namespace with a lot of things I probably wont use. I would rather use import read_csv as any_alias_you_like. This way I only fill my namespace with what I want by doing the following
x = any_alias_you_like.function_call()
The reason why I didn't do this with the main code solution is because I am not sure where all you are using read_csv functions and classes in your code and if that is not accounted for by prefxing the alias name properly, you will run into a multiple errors. So my advice is to identify all the funcutions/classes that you are using in read_csv.py and prefix them properly with an alias.
I also used the import statement for the visualize_frame differently. This is because, when you do a from import..., you are only partially initializing the module. However, a proper import visualize_frame will ensure that your entire module is initialized in one call and you can use everything it offers by simply prefixing the alias.
Read about the difference between from import and import... here.
Read about how Python searches for libraries here.

Python: Circular Import error, eventhough no "from ... import ..." used

I am struggeling with resolving a circular import issue. My setup is as follows:
Module a.py:
import b
class A():
pass
Module b.py:
import a
class Bar(a.A):
pass
Notice the circular import in module b: is needs to access class A from module a, because B inherits from A.
Finally, the main module, main.py:
import a
# ...
When I run main.py, I get this error:
$ python3 main.py
Traceback (most recent call last):
File "/home/.../main.py", line 1, in <module>
import a
File "/home/.../a.py", line 1, in <module>
import b
File "/home/.../b.py", line 3, in <module>
class Bar(a.A):
AttributeError: partially initialized module 'a' has no attribute 'A' (most likely due to a circular import)
I thought, one way of resolving circular imports was to not use the from ... import ... statement, which I didn't do as you can see. But why am I still getting that error then?
EDIT: My current code structure requires that A and B are separate modules. Nevertheless, in this video, he is using a similar structure as mine, and he has no problems...
The ideal answer in such cases is usually to
combine the files or their contents (do you actually need more files?)
create a 3rd file and put shared contents into it (avoids breaking existing imports as you can simply import whatever you need into the existing libraries from the shared one)
create a 3rd file which imports both (cleaner with 3rd party libraries, but may break existing logic)
import a
import b
class Bar(a.A):
pass

"import fuzzywuzzy" causes error, while "from fuzzywuzzy import fuzz" does not

When running the following code, I get an error message.
import fuzzywuzzy
print(fuzzywuzzy.fuzz.ratio('about', 'doubt'))
Error message:
Traceback (most recent call last):
File "C:/Users/vincent/Documents/PythonScripts/test2.py", line 2, in <module>
print(fuzzywuzzy.fuzz.ratio('about', 'doubt'))
AttributeError: module 'fuzzywuzzy' has no attribute 'fuzz'
Howerer, the following runs perfectly.
from fuzzywuzzy import fuzz
print(fuzz.ratio('about', 'doubt'))
Could someone help me solve this? I really appreciate it.
The answer to this depends on how your packages are laid out. I'm assuming that your directory tree looks something like this:
fuzzywuzzy/
__init__.py
fuzz.py
In that case, it's likely that fuzz is not imported in the __init__.py for fuzzywuzzy. When importing the top level of a module python only allows access to those things are imported into that module. Sub-modules require additional imports, as you've seen in your example.
If you want the first import to work then you'll need to add import fuzz to the __init__.py file for fuzzywuzzy, otherwise you'll have to use a from ... import. You could also import fuzzywuzzy.fuzz.
I would recommend not adding those imports to __init__.py, however, because it makes your code less explicit an means that new people reading the code will have to look through another file to understand from where functions are being imported.

How to get Python relative imports right

I have a custom Python library ("common") that is being imported and used from several Python projects.
That central library has the following structure:
/common
/__init__.py
/wrapper.py
/util
/__init__.py
/misc.py
Our custom library resides in a central place /data/Development/Python, so in my Python projects I have an .env file in order to include our lib:
PYTHONPATH="/data/Development/Python"
That works fine, I can for example do something like:
from common.util import misc
However, now I want to make use of a class MyClass within common/wrapper.py from the code in common/util/misc.py. Thus I tried the following import in misc.py:
from ..wrapper import MyClass
But that leads to the following error:
Exception has occurred: ImportError
cannot import name 'MyClass'
Any ideas what I am doing wrong here?
PS: When I do an from .. import wrapper instead and then from code I use wrapper.MyClass, then it works fine. Does that make any sense?
It's finding wrapper, otherwise you'd get a different error:
>>> from wibble import myclass
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'wibble'
So it seems wrapper does not contain MyClass. Typo?
"However, now I want to make use of a class MyClass within common/wrapper.py from the code in common/util/misc.py. Thus I tried the following import in misc.py:"
If you do export PYTHONPATH=~/common
In order to import MyClass you will do:
from wrapper import MyClass
Because you have added common to your root folder, utils is already in your path since it is in you root folder.
Similarly, if you wanted to import from wrapper you would do from utils.misc import ThisClass since utils.misc is already in the root folder common

Import with files names conflict

I'm trying to develop a simulation class that replaces serial at specific apps(Win7, python 2.7).
I have a SerialHandle class that works in number of apps, It's job is add logic to the well known serial methods, the idea was to replace python serial.py with a dummy file with the same name so we won't have to change and imports at SerialHandle.
Now i have this file with Serial class just like the origin and it works fine:
serial.py
...Serial()
Since i want to really simulate the methods i need the SerialException from serialutil
so inside my serial.py i'm trying to import it using:
from serial import SerialException
But as expected i'll get this raise since from serial goes to the local file at first:
Traceback (most recent call last):
File "C:/CROW/ATE/DUTDrivers/DD_SimulatorExample/DD_SimulatorExample.py", line 18, in <module>
from Utilities.Serial.SerialHandle.trunk.SerialHandle import SerialHandle
File "C:\CROW\ATE\Utilities\Serial\SerialHandle\trunk\__init__.py", line 4, in <module>
from Utilities.Simulator import serial
File "C:\CROW\ATE\Utilities\Simulator\serial.py", line 11, in <module>
from serial import SerialException
ImportError: cannot import name SerialException
I understand the problem is the file name since at any other file it will work...
I've tried sys.append(site-packages....serial.py) no luck.
Questions:
Any way to tell the interpreter to ignore the local file at a specific from..import?
Is there any other way to import from an absolute path?
Notes:
the file naming as serial.py is not a decision it's a definition so changing the name is not relevant...
Overloading python serial is not an option also...
You must be using python 2.x, since absolute imports are the default in python 3.x. You can use absolute imports in your serial.py file by adding this at the top of the file:
from __future__ import absolute_import
Note that you will need to convert any implicit relative imports from your serial.py file into explicit relative imports. So if you were importing some_func from other_file.py, which is in the same directory, you would need to change that to:
from .other_file import some_func
Note that the "." indicates a relative import from the same package as the current file. See here for additional detail.

Categories