ModuleNotFoundError running Python 3.8.x
I'm building a python package containing classes and functions each with verbose tests. Inside the package I'm trying to use these building blocks to provide end-to-end uses and examples by using absolute imports at the top of these files (like in my tests).
The project structure is as follows:
.
├── __init__.py
├── setup.py
├── examples
│ ├── __init__.py
│ └── end_to_end_1.py
├── tests
│ ├── __init__.py
│ └── utils
│ ├── __init__.py
│ ├── test_useful_one.py
│ └── test_useful_two.py
└── utils
├── __init__.py
├── useful_one.py
└── useful_two.py
I'm running all tests from the package root using python -m unittest tests/**/*.py, and both test files contain absolute package imports from utils like so,
from utils.useful_one import UsefulClass
this approach is succesful in running the tests and importing the UsefulClass class into the test files.
My issue arises when trying to use the same import statement inside the examples/end_to_end_1.py module and executing the file (again from the root of the package) using
python examples/end_to_end_1.py
Now I get a runtime ModuleNotFoundError: No module named 'utils'.
In trying to follow the python language guidelines I'm trying to use absolute imports where possible but to no avail.
I have definitely misunderstood how the __init__.py files are supposed to tell the runtime where to resolve packages from. I don't think this use case is abnormal, since I see this same pattern inside node packages and ruby gems all the time.
Makeshift (Temporary) Solution
At the moment to solve this I have applied the solution from Absolute import results in ModuleNotFoundError which, despite working, seems not too scalable when the repository is publically available and I feel like the Python ecosystem will have a solution for this issue. As Raymond Hettinger says, it feels like..
There must be a better way!
Related
I'd like to apply the Packaging Python Projects process to a package with only one module/.py file.
I'm new to Python so I'm probably misunderstanding something here (but coding for decades), but many packaging tutorials I've seen lead to names that must be accessed like mypackage.mymodule.xyz. The trivial examples they show are often of the form samelongname.samelongname. For single module projects, that seems overkill.
My main objective is to just have one level of scoping for users i.e. modulename.
Packaging Python Projects suggests the structure:
packaging_tutorial/
├── LICENSE
├── pyproject.toml
├── README.md
├── setup.cfg
├── src/
│ └── example_package/
│ ├── __init__.py
│ └── example.py
└── tests/
I specifically like the idea of putting the .py file under src to clearly distinguish it.
For one module and no package, I was thinking that would be (excerpted):
packaging_tutorial/
├── src/
│ └── example.py
The configuration they offer is (excerpted):
[options]
package_dir =
= src
packages = find:
python_requires = >=3.6
[options.packages.find]
where = src
However, when I do python3 -m build, the .py file does not end up in the built dist.
Before finding the linked docs, I was keeping the .py in the package root (i.e. not under src) and using a setup.py instead of a setup.cfg like this:
from setuptools import setup
setup(
py_modules=['mpvsilence']
)
That worked, but when I tried to make it more like the docs:
py_modules doesn't seem to work in a setup.cfg.
py_modules=['src/mpvsilence'] didn't seem to work in the setup.py
I'm really just flopping around here. Any guidance would be appreciated.
I'm a beginner and I'm not understanding something in the project folder structure.
I have this project:
.
└── convertersProject/
├── conftest.py -----------------> best practice if you're not using src structure
├── converters -----------------> module I'm trying to import/
│ ├── __init__.py
│ ├── binconverter.py
│ └── importester1.py --------> import binconverter OR from converters import */
│ └── submodule/
│ ├── __init__.py -----> not needed if I don't want to use it as package
│ └── importester2.py -> from converter import binconverter
├── outsidemodule /
│ ├── importester3.py
│ └── __init__.py
└── test /
└── test_converters.py
I'm receiving always ModuleNotErrorFound when trying to execute importester1/2/3.py directly from project folder. I'm using a virtual enviroment, setting up with python -m venv 'name' from a pyenv python 3.8.5 set with pyenv shell 3.8.5
What I think I'm understanding:
I have to use absolute paths as from converters.binconverter import bin2dec being bin2dec fucntion in binconverter. If I'd want to use relative, I should be inside the folder tree as trying to execute importertest2.py, because submodule is inside converters. So, I coudn't use relatives for outsidemodule
PYTHONPATH is the current folder you're executing, so if I executing from project folder as python converters/submodule/importester2.py, I don't have to append any value to the PYTHONPATH of the virtual enviroment (indeed, I've read it's not a good practice)
__init__.py allows you to use module without appending values to PYTHONPATH virtualenv, so I could import the converter module into outsidemodule using absolute paths.
tests are working using this logic. In fact if I change something the VSCode debugger detect import problems.
What the hell am I missing?
I am currently developing a package simultaneously with a few projects that use the package, and I'm struggling to figure out how to structure my directory tree and imports.
Ideally, I want something like this:
main_directory
├── shared_package
│ ├── __init__.py
│ ├── package_file1.py
│ └── package_file2.py
├── project1
│ ├── main.py
│ ├── module1.py
│ └── other_package
│ ├── __init__.py
│ └── other_package_file.py
└── project2
└── ...
I can't figure out how to make the imports work cleanly for importing shared_package from python files in project1. Is there a preferred way to do this?
Any help would be appreciated!
shared_package will eventually be standalone. Other people will import and install it the normal way, and it'll be stored with the rest of the python modules in site-packages or wherever.
To replicate this, I recommend just updating your PYTHONPATH to point to main_directory (or wherever you put shared_package anyway) - that way,
import shared_package
will still work fine for the code if shared_package was installed normally, because it's on the pythonpath either way.
Note that PYTHONPATH is an environment variable, so the means for doing this will vary based on your operating system. Regardless, a quick search for how to modify the variable permanently on your OS should be easy.
I am trying to run tests in radish, a Behaviour Driven Development environment for Python, but I am failing to do even the easiest of things.
I have this structure:
.
├── features
│ └── my.feature
└── radish
├── __init__.py
├── harness
│ ├── __init__.py
│ └── main.py
└── steps.py
When I do
python -c "import radish.harness"
from my working dir ".", things are fine.
When I do the same ("import radish.harness" or "import harness") in the file steps.py, I'm getting this when calling the command "radish features" from the same directory:
ModuleNotFoundError: No module named 'radish.harness'
or
ModuleNotFoundError: No module named 'harness'
The radish-bdd quick start guide quick start guide says about this:
How does radish find my python modules? radish imports all python
modules inside the basedir. Per default the basedir points to
$PWD/radish which in our case is perfectly fine.
Indeed a file placed in the radish directory will be imported automatically, but I am unable to import anything from within these files (apart from system libraries).
Can anyone advise me on how to import modules? I'm lost. It seems that my python knowledge on module import isn't helping.
I suggest you to move the 'harness' directory at the same level as 'features' and 'radish' directory.
.
├── features
│ └── my.feature
├── radish
│ ├── __init__.py
│ └── steps.py
└── harness
├── __init__.py
└── main.py
If you call radish from your working dir (".") like this:
radish -b radish features/my.feature
Then you can import your "harness" module from steps.py like this
import harness
That will work because in this case Python will find your "harness" module as it is in the current directory.
I would like to include a third party library into my Python script folder to distribute it all togehter (I am awary of the distribution license, and this library is fine to distribute). This is in order to avoid installing the library on another machine.
Saying I have a script (my_script.py), which calls this external library. I tried to copy this library from the site-packages subdirectory of Python directory into the directory where I have my files, but it seems not to be enough (I think th reason is in the __init__.py of this library which probably needs the folder to be in the PYTHONPATH).
Would it be reasonable to insert some lines of code in my_script.py to temporary append its folder to sys.path in order to make the all things working?
For instance, if I have a structure similar to this:
Main_folder
my_script.py
/external_lib_folder
__init__.py
external_lib.py
and external_lib_folder is the external library I copied from site-packages and inserted in my Main_folder, would it be fine if I write these lines (e.g.) in my_script.py?
import os,sys
main_dir = os.path.dirname(os.path.abspath(__file__))
sys.path.append(main_dir)
EDIT
I ended up choosing the sys.path.append solution. I added these lines to my my_script.py:
import os, sys
# temporarily appends the folder containing this file into sys.path
main_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)),'functions')
sys.path.append(main_dir)
Anyway, I chose to insert this as an edit in my question and accept the answer of Torxed because of the time he spent in helping me (and of course because his solution works as well).
Python3
import importlib.machinery, imp
namespace = 'external_lib'
loader = importlib.machinery.SourceFileLoader(namespace, '/home/user/external_lib_folder/external_lib.py')
external_lib = loader.load_module(namespace)
# How to use it:
external_lib.function(data_or_something)
This would be an ideal way to load custom paths in Python 3.
Not entirely sure this is what you wanted but It's relevant enough to post an alternative to adding to sys.path.
Python2
In python 2 you could just do (if i'm not mistaken, been a while since i used an older version of Python):
external_lib = __import__('external_lib_folder')
This does however require you to keep the __init__.py and a proper declaration of functions in sad script, otherwise it will fail.
**It's also important that the folder you're trying to import from is of the same name that the __init__.py script in sad folder is trying to import it's sub-libraries from, for instance geopy would be:
./myscript.py
./geopy/
./geopy/__init__.py
./geopy/compat.py
...
And the code of myscript.py would look like this:
handle = __import__('geopy')
print(handle)
Which would produce the following output:
[user#machine project]$ python2 myscript.py
<module 'geopy' from '/home/user/project/geopy/__init__.pyc'>
[user#machine project]$ tree -L 2
.
├── geopy
│ ├── compat.py
│ ├── compat.pyc
│ ├── distance.py
│ ├── distance.pyc
│ ├── exc.py
│ ├── exc.pyc
│ ├── format.py
│ ├── format.pyc
│ ├── geocoders
│ ├── __init__.py
│ ├── __init__.pyc
│ ├── location.py
│ ├── location.pyc
│ ├── point.py
│ ├── point.pyc
│ ├── units.py
│ ├── units.pyc
│ ├── util.py
│ ├── util.pyc
│ └── version.pyc
└── myscript.py
2 directories, 20 files
Because in __init__.py of geopy, it's defined imports such as from geopy.point import Point which requires a namespace or a folder of geopy to be present.
There for you can't rename the folder to functions and place a folder called geopy in there because that won't work, nor will placing the contents of geopy in a folder called functions because that's not what geopy will look for.
Adding the path to sys.path (Py2 + 3)
As discussed in the comments, you can also add the folder to your sys.path variable prior to imports.
import sys
sys.path.insert(0, './functions')
import geopy
print(geopy)
>>> <module 'geopy' from './functions/geopy/__init__.pyc'>
Why this is a bad idea: It will work, and is used by many. The problems that can occur is that you might replace system functions or other modules might get loaded from other folders if you're not careful where you import stuff from. There for use .insert(0, ...) for most and be sure you actually want to risk replacing system built-ins with "shady" path names.
What you suggest is bad practice, it is a weak arrangement. The best solution (which is also easy to do) is to package it properly and add an explicit dependency, like this:
from setuptools import setup
setup(name='funniest',
version='0.1',
description='The funniest joke in the world',
url='http://github.com/storborg/funniest',
author='Flying Circus',
author_email='flyingcircus#example.com',
license='MIT',
packages=['funniest'],
install_requires=[
'markdown',
],
zip_safe=False)
This will work if the third party library is on pipy. If it's not, use this:
setup(
...
dependency_links=['http://github.com/user/repo/tarball/master#egg=package-1.0']
...
)
(See this explanation for packaging).