Python 3 doesn't need __init__.py in this situation? - python

Suppose I have:
src/
__init__.py
a.py
b.py
Suppose __init__.py is an empty file, and a.py is just one line:
TESTVALUE = 5
Suppose b.py is:
from src import a
print(a.TESTVALUE)
Now in both Python 2.7 and Python 3.x, running b.py gives the result (5).
However, if I delete the file __init__.py, b.py still works in Python 3.x, but in Python 2.7, I get the error:
Traceback (most recent call last):
File "b.py", line 5, in <module>
from src import a
ImportError: No module named src
Why does Python 2.7 exhibit different behaviour in this situation?

Python 3 supports namespace packages that work without an __init__.py file.
Furthermore, these packages can be distribute over several directories. This means all directories on your sys.path that contain *.py files will be recognized as packages.
This breaks backwards compatibility in Python 3 in terms of imports. A typical problem is a directory in your current working directory that has a name like a library such as numpy and that contains Python files. While Python 2 ignores this directory, Python 3 will find it first and tries to import the library from there. This has bitten me several times.

Related

Trouble importing a module that imports a module

I'm having trouble with a python package that uses separate modules to structure code. The package itself is working, however when imported from another environment fails with a ModuleNotFound error.
Here's the structure:
Project-root
|
|--src/
| __init__.py
| module_a.py
| module_b.py
| module_c.py
| module_d.py
|--tests
etc.
In module_a.py I have:
from module_a import function_a1,...
from module_b import function_b1,...
from module_c import function_c1,...
In module_c I import module_d like:
from module_d import function_d1,...
As mentioned above, executing module_a or module_c directly from the CLI work as expected, the unit tests I've created in the test directory also work (with the help of sys.path.insert), however if I create a new environment and import the package I get the following error:
>>> import module_a
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/<abs_path>/.venv/lib/python3.9/site-packages/module_a.py", line 22, in <module>
from module_c import function_c1, function_c2
File /<abs_path>/.venv/lib/python3.9/site-packages/module_c.py", line 9, in <module>
import module_d
ModuleNotFoundError: No module named 'module_d'
>>>
I've exhausted all ideas how to overcome this, besides combining the code of modules c and d in one file, which I'd hate to do, or rethink the flow so that all modules are imported from module_a.
Any suggestions how to approach this would be greatly appreciated.
Update: It turned out to be a typing mistake in the name of module_d in setup.py. For whatever reason python setup.py install was failing silently or I wasn't reading the logs carefully.
The problem comes down to understanding the basics of the import system and the PYTHONPATH.
When you try to import a module (import module_a), Python will search in order in every directory listed in sys.path. If a directory matches the name (module_a)1, then it runs the __init__.py file is such exist.
When you get an [https://docs.python.org/3/library/exceptions.html#ImportError], it means that there is no directory in sys.path containing a directory with the name asked.
You said for your tests you did something like sys.path.insert(0, "some/path/"), but it is not a solution, just a broken fix.
What you should do is set your PYTHONPATH environment variable to contain the directory where your modules are located, Project-root/src in your case. That way, no need to ever use sys.path.insert, or fiddle with relative/absolute paths in import statements.
When you create your new environment, just set your environment variable PYTHONPATH to include Project-root/src and you are done. This is how installing regular Python modules (libraries) work : they are all put into a directory in site-packages.
1: this changed since old Python versions, it used to be required for the directory to contain an __init__.py file

Python3 relative imports failing in package

I've been reading tons of questions related to this matter but none of the has help me so far. I'm currently using the Python click library to execute scripts as commands.
The current command that I'm trying to execute is placed inside a Python Package which has a __main__.py file, like the parent dir has. The current project structure is the following one.
/myproject
/foo_one
__init__.py
foo_one.py
/foo_two
__init__.py
foo_two.py
/foo_three
__init__.py
foo_three.py
/foo_four
__init__.py
foo_four.py
/foo_five
__init__.py
foo_five.py
/foo_six
__init__.py
foo_six.py
__init__.py
__main__.py
foo_seven.py
Whenever I try to run the __main__.py script located in the project folder, the following error comes up.
ModuleNotFoundError: No module named '__main__.foo_two'; '__main__' is not a package
However, if I try to execute that same script from a folder above with the -m option like this python3 myproject -m, the following is shown up.
ImportError: attempted relative import with no known parent package
The __main__.py has 2 imports like this... The __init__.py is empty.
from .foo_two.foo_two import AClass, AnotherClass, OtherClass
from .foo_three.foo_three import AnotherClassMore
UPDATE: Correcting the syntax error in a previous command, while calling python -m myproject gives me a ModuleNotFoundError because of a module that isn't my responsibility, which is basically a library that is used in the project.
Hopefully, this will be of value to someone out there - I went through half a dozen stackoverflow posts trying to figure out relative imports similar to whats posted above here. I set up everything as suggested but I was still hitting ModuleNotFoundError: No module named 'my_module_name'
Since I was just developing locally and playing around, I hadn't created/run a setup.py file. I also hadn't apparently set my PYTHONPATH.
I realized that when I ran my code as I had been when the tests were in the same directory as the module, I couldn't find my module:
$ python3 test/my_module/module_test.py 2.4.0
Traceback (most recent call last):
File "test/my_module/module_test.py", line 6, in <module>
from my_module.module import *
ModuleNotFoundError: No module named 'my_module'
However, when I explicitly specified the path things started to work:
$ PYTHONPATH=. python3 test/my_module/module_test.py 2.4.0
...........
----------------------------------------------------------------------
Ran 11 tests in 0.001s
OK
So, in the event that anyone has tried a few suggestions, believes their code is structured correctly and still finds themselves in a similar situation as myself try either of the following if you don't just add your export the current directory to your PYTHONPATH:
Run your code and explicitly include the path like so:
$ PYTHONPATH=. python3 test/my_module/module_test.py
To avoid calling PYTHONPATH=., create a setup.py file with contents like the following and run python setup.py development to add packages to the path:
# setup.py
from setuptools import setup, find_packages
setup(
name='sample',
packages=find_packages()
)
The correct syntax would be
python -m myproject
This should execute __main__ in the top-level package.
You need to have __init__.py in each sub folder with python code to tell the interpreter to treat the folder as a module
/myproject
/foo_one
__init__.py # add this
foo_one.py
/foo_two
__init__.py # add this
foo_two.py
/foo_three
__init__.py # add this
foo_three.py
/foo_four
__init__.py # add this
foo_four.py
/foo_five
__init__.py # add this
foo_five.py
/foo_six
__init__.py # add this
foo_six.py
__init__.py
__main__.py
foo_seven.py
the __init__.py is telling the interpreter to treat sub folders as python modules / packages and you should be able to import
The __init__.py file can be empty but needs to be present in the sub folders to be able to import that module / package

Importing issue with python 3

I have a code that runs in Python 2.7 but not in 3.5 and I can't find the reason. It has to do with importing.
The root folder has a subfolder called s. From the root folder, I am running the script a.py which includes the line from s import *.
In the folder s, there is a file called b.py which has the import line: from c import c
which tries to import the class saved in c.py which is located in the subfolder s as well.
When I run the script a.py from the root folder, I get the ImportError saying "No module named c".
In Python 2.7 this runs without problems. Can someone please suggest what may be the issue and how this should be done differently in Python 3.5?
Implicit imports within packages are not available for Python 3, so to get this to work you will need to use an explicit relative import:
from .s import *
This should work for both Python 2 and Python 3. This also makes your intention more clear that you want to import from a relative package, not an installed package.

Import object declared inside __init__.py

I'm having trouble understanding how objects declared inside '__init__.py' are/should be imported to other files.
I have a directory structure like so
top/
|
|_lib/
|_ __init__.py
|_ one.py
File contents are as follows
lib/__init__.py
a=object()
lib/one.py
from lib import a
Here is the problem. If I fire a python shell from top directory, the following command runs well
>>> from lib.one import a
However if I change directory to top/lib and fire a similar command in a new python shell, I get error.
>>> from one import a
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "one.py", line 1, in <module>
from lib import a
ImportError: No module named lib
Ofcourse, I can change one.py like so, that will make everything work.
from __init__ import a
But I'm really trying to understand, why import command works from top directory and not from top/lib.
Thanks.
Generally speaking, I think it's best practice to have the data funnel up to __init__.py from the modules/subpackages rather than needing to rely on data from __init__.py in the surrounding modules. In other words, __init__.py can use one.py, but one.py shouldn't use data/functions in __init__.py.
Now, to your question...
It works in top because python does a relative import (which is gone in python3.x IIRC, so don't depend on it ;-). In other words, python looks in the current directory for a module or package name lib and it imports it. That's all fine so far. running from lib.one import a first imports lib (__init__.py) which works fine. Then it imports one -- lib still imports ok from one because it's relative to your current working directory -- Not relative to the source file.
When you move into the lib directory, python can no longer find lib in the current directory making it not importable. Note that with most packages, this is fixed by installing the package which puts it someplace that python can find it without it needing to be in the current directory.

Import from different directories in python

This is my folder structure:
src/
__init__py
Lowlevel/
__init__.py
ModuleToCheck.Py
Test/
__init__.py
ModuleToCheck_test.py
(__init__.py are empty files)
Now I want to import ModuleToCheck.py in ModuleToCheck_test.py
How am I able to do this without appending anything to sys.path?
Update:
from ..Lowlevel import ModuleToCheck leads to:
src$ python Test/ModuleToCheck_test.py
Traceback (most recent call last):
File "Test/ModuleToCheck_test.py", line 6, in <module>
from ..Lowlevel import ModuleToCheck
ValueError: Attempted relative import in non-package
The following is from http://docs.python.org/tutorial/modules.html#intra-package-references
Note that both explicit and implicit
relative imports are based on the name
of the current module. Since the name
of the main module is always
"__main__", modules intended for use
as the main module of a Python
application should always use absolute
imports.
You're running your module ModuleToCheck_test.py as the main module, hence the exception.
One solution is to create a test.py module in your src directory containing the following:
import Test.ModuleToCheck_test
You can then run that module using python test.py

Categories

Resources