Imports inside package now that __init__.py is optional - python

I'm building a package that contains scripts to be run. They import modules contained in a subfolder directly under the script. Now that __init__ is not required after Python 3.3, what's the correct file structure and import statement to have? I'd like to not have to specify the import from the topmost folder down, only as a relative path, here sub/module.
This is the current state of the file structure:
Root\
src\
sub\
module.py
script.py
parent_module.py
setup.py
# Inside script.py
import sub.module # Doesn't work
from sub import module # Doesn't work
import src.sub.module # Does work!
import .sub.module # Doesn't work
import .parent_module # Does work!
I imagine I need to have some __init__ file, but what and where would that be? Any help is greatly appreciated, I don't know much about packaging.
Also, I'm certainly open to suggestions to changing the structure, if that makes things easier.

The missing __init__.py are not the problem - you are using outdated relative imports.
import sub.module # implicit relative import - py2 only
from . import sub.module # explicit relative import
Note that a . import always requires the from .<where> import <name> form. It would not produce a valid name otherwise. The following should work, assuming your run script.py via python3 -m src.script - an IDE will likely do the same.
from . import sub.module
from .sub import module
from .sub.module import *
from . import parent_module
If you are running as plain python3 script.py or python3 -m script, you cannot use relative imports. Only absolute imports will work in this case.
import sub.module
from sub import module
from sub.module import *
import parent_module
While you do not need __init__.py files, it is a good idea to add them if your package is not a namespace. Otherwise, other similarly constructed packages of the same name may be inserted into yours.

Related

Cannot find module when importing from project in python

My directory structure looks like this:
I have some utility functions in util/misc.py that I want to import in compose_dataset.py. However, I cannot get the import statement to work.
I'm working on Windows Python3.5.4, so from what I've read I don't need __init__.py files anymore. The project folder is a child of my PYTHONPATH that points solely to E:\Python. So far, I tried:
from misc import *
from util import *
from util.misc import *
from ..util.misc import *
and either received ImportError: No module named 'xyz' or ImportError: attempted relative import with no known parent package. I also tried adding init-files but I have no experience with those and simply added one to every directory, but (surprisingly) that didn't work either.
What am I missing here??
Try this:
import sys
sys.path.insert(0, '../')
from util.misc import *
You may also want to take a look at this post: How to access a module from outside your file folder in Python?
Set your PYTHONPATH to ., and add __init__.py files into each directory, where you want to import from, in your case add it into src, data, util directories. Assuming that you run your "entry point" script from the root directory of your project (same where your README.md file is), to import use this code:
# compose_dataset.py file
from src.util.misc import function_name

Relative Import Confusion in Python

I am having some trouble figuring out how to do relative imports in Python. I am currently working on my first major project so I want to do it right using unit tests. However, I am having trouble with my file structure and relative imports.
Here is my current structure:
App/
__init__.py
src/
__init__.py
person.py
tests/
__init__.py
person_tests.py
What I want to do is be able to import person.py into person_tests.py for the unit tests. I have attempted the following:
from . import person
from .. import person
from .App.src import person
from ..App.src import person
from ..src.person import *
from ..src import person
from .src import person
Every one of the above throws either a syntax error or
ValueError: Attempted relative import in non-package
Can someone please clarify this for me?
Edit: Python version is 2.7.
Edit: I would like to be able to use this with say unittest or nose.
My guess (and I'll delete this if I'm wrong) is that you're trying to use person_tests.py as a top-level script, rather than as a module inside a package, by doing something like this:
$ cd App/tests
$ python person_tests.py
In that case, person_tests does not end up as App.tests.person_tests, but as just __main__ (or, with minor variations, as the top-level person_tests, which has the same basic issues). So, .. does not refer to App, and therefore there is no way to get to person as a relative import.
More generally, nothing on PYTHONPATH, including ., should ever be in the middle of any package directory, or things will get broken.
The right answer is to not do that. Do something like this:
$ python -m App.tests.person_tests
Or write a top-level (outside the package) script that imports the module, and run that top-level script.
Just as abarnert says, you can not excute the script as main for the relative import based on the current name of the script. Quoted from the doc:
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.
. represents the current package and .. represents the parent package. So some of your import statement is wrong.
from . import person # wrong for no person module in package tests
from .. import person # wrong for no person module in package app
from .App.src import person # wrong for no app package in package tests
from ..App.src import person # wrong for no app package in package app
from ..src.person import * # right
from ..src import person # right
from .src import person # wrong
You can refer PEP328 for the standard of relative import.
you could append it to sys.path.
import sys
sys.path.append("../src")
import person

How to fix "Attempted relative import in non-package" even with __init__.py

I'm trying to follow PEP 328, with the following directory structure:
pkg/
__init__.py
components/
core.py
__init__.py
tests/
core_test.py
__init__.py
In core_test.py I have the following import statement
from ..components.core import GameLoopEvents
However, when I run, I get the following error:
tests$ python core_test.py
Traceback (most recent call last):
File "core_test.py", line 3, in <module>
from ..components.core import GameLoopEvents
ValueError: Attempted relative import in non-package
Searching around I found "relative path not working even with __init__.py" and "Import a module from a relative path" but they didn't help.
Is there anything I'm missing here?
To elaborate on Ignacio Vazquez-Abrams's answer:
The Python import mechanism works relative to the __name__ of the current file. When you execute a file directly, it doesn't have its usual name, but has "__main__" as its name instead. So relative imports don't work.
You can, as Igancio suggested, execute it using the -m option. If you have a part of your package that is meant to be run as a script, you can also use the __package__ attribute to tell that file what name it's supposed to have in the package hierarchy.
See http://www.python.org/dev/peps/pep-0366/ for details.
Yes. You're not using it as a package.
python -m pkg.tests.core_test
It depends on how you want to launch your script.
If you want to launch your UnitTest from the command line in a classic way, that is:
python tests/core_test.py
Then, since in this case 'components' and 'tests' are siblings folders, you can import the relative module either using the insert or the append method of the sys.path module.
Something like:
import sys
from os import path
sys.path.append( path.dirname( path.dirname( path.abspath(__file__) ) ) )
from components.core import GameLoopEvents
Otherwise, you can launch your script with the '-m' argument (note that in this case, we are talking about a package, and thus you must not give the '.py' extension), that is:
python -m pkg.tests.core_test
In such a case, you can simply use the relative import as you were doing:
from ..components.core import GameLoopEvents
You can finally mix the two approaches, so that your script will work no matter how it is called.
For example:
if __name__ == '__main__':
if __package__ is None:
import sys
from os import path
sys.path.append( path.dirname( path.dirname( path.abspath(__file__) ) ) )
from components.core import GameLoopEvents
else:
from ..components.core import GameLoopEvents
You can use import components.core directly if you append the current directory to sys.path:
if __name__ == '__main__' and __package__ is None:
from os import sys, path
sys.path.append(path.dirname(path.dirname(path.abspath(__file__))))
In core_test.py, do the following:
import sys
sys.path.append('../components')
from core import GameLoopEvents
Issue is with your testing method,
you tried python core_test.py
then you will get this error
ValueError: Attempted relative import in non-package
Reason: you are testing your packaging from non-package source.
so test your module from package source.
if this is your project structure,
pkg/
__init__.py
components/
core.py
__init__.py
tests/
core_test.py
__init__.py
cd pkg
python -m tests.core_test # dont use .py
or from outside pkg/
python -m pkg.tests.core_test
single . if you want to import from folder in same directory .
for each step back add one more.
hi/
hello.py
how.py
in how.py
from .hi import hello
incase if you want to import how from hello.py
from .. import how
If your use case is for running tests, and it seams that it is, then you can do the following. Instead of running your test script as python core_test.py use a testing framework such as pytest. Then on the command line you can enter
$$ py.test
That will run the tests in your directory. This gets around the issue of __name__ being __main__ that was pointed out by #BrenBarn. Next, put an empty __init__.py file into your test directory, this will make the test directory part of your package. Then you will be able to do
from ..components.core import GameLoopEvents
However, if you run your test script as a main program then things will fail once again. So just use the test runner. Maybe this also works with other test runners such as nosetests but i haven't checked it. Hope this helps.
My quick-fix is to add the directory to the path:
import sys
sys.path.insert(0, '../components/')
As Paolo said, we have 2 invocation methods:
1) python -m tests.core_test
2) python tests/core_test.py
One difference between them is sys.path[0] string. Since the interpret will search sys.path when doing import, we can do with tests/core_test.py:
if __name__ == '__main__':
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from components import core
<other stuff>
And more after this, we can run core_test.py with other methods:
cd tests
python core_test.py
python -m core_test
...
Note, py36 tested only.
As you have already marked everything as a module, there's no need to use the relative reference if you launch as python module.
Instead of
from ..components.core import GameLoopEvents
simply
from pkg.components.core import GameLoopEvents
When you run from the parent of pkg, use the following
python -m pkg.tests.core_test
Old thread. I found out that adding an __all__= ['submodule', ...] to the
__init__.py file and then using the from <CURRENT_MODULE> import * in the target works fine.
You can use from pkg.components.core import GameLoopEvents, for example I use pycharm, the below is my project structure image, I just import from the root package, then it works:
This approach worked for me and is less cluttered than some solutions:
try:
from ..components.core import GameLoopEvents
except ValueError:
from components.core import GameLoopEvents
The parent directory is in my PYTHONPATH, and there are __init__.py files in the parent directory and this directory.
The above always worked in python 2, but python 3 sometimes hit an ImportError or ModuleNotFoundError (the latter is new in python 3.6 and a subclass of ImportError), so the following tweak works for me in both python 2 and 3:
try:
from ..components.core import GameLoopEvents
except ( ValueError, ImportError):
from components.core import GameLoopEvents
Try this
import components
from components import *
If someone is looking for a workaround, I stumbled upon one. Here's a bit of context. I wanted to test out one of the methods I've in a file. When I run it from within
if __name__ == "__main__":
it always complained of the relative imports. I tried to apply the above solutions, but failed to work, since there were many nested files, each with multiple imports.
Here's what I did. I just created a launcher, an external program that would import necessary methods and call them. Though, not a great solution, it works.
Here's one way which will piss off everyone but work pretty well. In tests run:
ln -s ../components components
Then just import components like you normally would.
For me only this worked: I had to explicitly set the value of package to the parent directory, and add the parent directory to sys.path
from os import path
import sys
if __package__ is None:
sys.path.append( path.dirname( path.dirname( path.abspath(__file__) ) ) )
__package__= "myparent"
from .subdir import something # the . can now be resolved
I can now directly run my script with python myscript.py.
python <main module>.py does not work with relative import
The problem is relative import does not work when you run a __main__ module from the command line
python <main_module>.py
It is clearly stated in PEP 338.
The release of 2.5b1 showed a surprising (although obvious in retrospect) interaction between this PEP and PEP 328 - explicit relative imports don't work from a main module. This is due to the fact that relative imports rely on __name__ to determine the current module's position in the package hierarchy. In a main module, the value of __name__ is always '__main__', so explicit relative imports will always fail (as they only work for a module inside a package).
Cause
Python Bug Tracker Issue1510172: Absolute/relative import not working?
The issue isn't actually unique to the -m switch. The problem is that relative imports are based on __name__, and in the main module, __name__ always has the value __main__. Hence, relative imports currently can't work properly from the main module of an application, because the main module doesn't know where it really fits in the Python module namespace (this is at least fixable in theory for the main modules executed through the -m switch, but directly executed files and the interactive interpreter are completely out of luck).
To understand further, see Relative imports in Python 3 for the detailed explanation and how to get it over.
I've had similar issues and as a software engineer, I think some of the suggested solutions here are not ideal. If you want relative imports, you should not have try/except and then sometimes do an absolute import. Also, to run a program, you should not have to change sys.path.
Furthermore, the program should always work, independent of your current working directory and independent of how you start it.
Thus, I've created a new, experimental import library: ultraimport
It allows file system based imports, no matter how you run your code.
From the original question, you would change your core_test.py to something like
import ultraimport
GameLoopEvents = ultraimport('__dir__/../components/core.py', 'GameLoopEvents')
print(GameLoopEvents)
and it would always find it, no matter how you run your tests.
$ python -m tests.core_test
<class 'core.GameLoopEvents'>
python ./tests/core_test.py
<class 'core.GameLoopEvents'>
I've also put this example into the examples folder in the git repo.
As the library is experimental, I am interested in feedback. It works for me but it not widely tested, yet.
If your project structure would look like this:
project
|
| --- module1
| |
| file1.py
|
|-----module2
| |
| file2.py
and you are going import file1.py from within file2.py,
you can do this in file2.py:
import sys
sys.path.append('.')
import file2
I still don't know why and how, but it worked for me.
This is very confusing and if you are using IDE like Pycharm, it's little more confusing.
What worked for me:
Make Pycharm project settings (if you are running python from a VE or from Python directory)
There is nothing wrong with the way you defined. Sometime it works with:
from folder1.file1 import class
if it does not work, use:
import folder1.file1
Your environment variable should be correctly mentioned in system or provide it in your command line argument.
Because your code contains if __name__ == "__main__", which doesn't be imported as a package, you'd better use sys.path.append() to solve the problem.

how to import py file from different upper folder?

All,
File structure:
\
util
utils.py
modules
__init__.py
modules1.py
submodule
__init__.py
submodule.py
I want to know how to import utils.py in those
__init__.py
for example, now I run the python interpreter in \ level, I run import modules, I suppose the code from ..util.utils import * may works, but it is not.
may I know where is the mistake?
and may I know if there's a way i can import utils.py in a universal format?
something like
import \util\utils.py
I know I may use path.append(), but any alternative?
Thanks
============
got the answer from this post:
Import a module from a relative path
If you are developping a python package (what you are obviously doing, as you have init.py), then the most simple way to import your module is just via the package.
For example, if your package is called mypackage, then:
import mypackage.utils

Python: Importing a file from a parent folder

...Now I know this question has been asked many times & I have looked at these other threads. Nothing so far has worked, from using sys.path.append('.') to just import foo.
I have a python file that wishes to import a file (that is in its parent directory). Can you help me figure out how my child file can successfully import its a file in its parent directory. I am using python 2.7.
The structure is like so (each directory also has the __init__.py file in it):
StockTracker/
└─ Comp/
├─ a.py
└─ SubComp/
└─ b.py
Inside b.py, I would like to import a.py: So I have tried each of the following but I still get an error inside b.py saying "There is no such module a".
import a
import .a
import Comp.a
import StockTracker.Comp.a
import os
import sys
sys.path.append('.')
import a
sys.path.remove('.')
from .. import a
Should do it. This will only work on recent versions of Python--from 2.6, I believe [Edit: since 2.5].
Each level (Comp and Subcomp) must also be have an __init__.py file for this to work. You've said that they do.
When packages are structured into
subpackages (as with the sound package
in the example), you can use absolute
imports to refer to submodules of
siblings packages. For example, if the
module sound.filters.vocoder needs to
use the echo module in the
sound.effects package, it can use from
sound.effects import echo.
Starting with Python 2.5, in addition
to the implicit relative imports
described above, you can write
explicit relative imports with the
from module import name form of import
statement. These explicit relative
imports use leading dots to indicate
the current and parent packages
involved in the relative import. From
the surround module for example, you
might use:
from . import echo
from .. import formats
from ..filters import equalizer
Quote from here http://docs.python.org/tutorial/modules.html#intra-package-references
If the Comp directory is in your PYTHONPATH environment variable, plain old
import a
will work.
If you're using Linux or OS X, and launching your program from the bash shell, you can accomplish that by
export PYTHONPATH=$PYTHONPATH:/path/to/Comp
For Windows, take a look at these links:
http://docs.python.org/using/windows.html
http://www.itechtalk.com/thread3595.html
EDIT:
To modify the path programmatically, you were on the right track in your original question. You just need to add the parent directory instead of the current directory.
sys.path.append("..")
import a

Categories

Resources