If all occurring directories are Python packages, i.e. they all contain __init__.py, then you can use
from ..bar_dir import bar
If the directories aren't Python packages, you can do this by messing around with sys.path, but you shouldn't.
If all occurring directories are Python packages, i.e. they all contain __init__.py, then you can use
from ..bar_dir import bar
If the directories aren't Python packages, you can do this by messing around with sys.path, but you shouldn't.
You can use the sys and os modules for generalized imports. In foo.py start with the lines
import sys
import os
sys.path.append(os.path.abspath('../bar_dir'))
import bar
I am at a loss. For over two hours, I have been trying to figure out how to make a simple import from one directory to another, like this:
Parent Dir
Dir 1
script1.py
Dir2
script2.py
So, I'd like to import a function2 from script2 to script1. I tried relative imports, absolute imports - it doesn't work (resulting in various errors) . It only worked after I added script2 to the system path, but it seems like a hack and people advise against using it.
Can someone explain this to me like I am five?
python - Import from sibling directory - Stack Overflow
How to import module from sibling directory?
python - Sibling package imports - Stack Overflow
How to import a Python module from a sibling folder? - Stack Overflow
as a literal answer to the question 'Python Import from parent directory':
to import 'mymodule' that is in the parent directory of your current module:
import os
parentdir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
os.sys.path.insert(0,parentdir)
import mymodule
edit
Unfortunately, the __file__ attribute is not always set.
A more secure way to get the parentdir is through the inspect module:
import inspect
currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
parentdir = os.path.dirname(currentdir)
You can use relative import (example from link, current module - A.B.C):
from . import D # Imports A.B.D
from .. import E # Imports A.E
from ..F import G # Imports A.F.G
I have a directory structure like this:
project/
utils/
__init__.py
shell.py
tests/
__init__.py
test_shell.py
__init__.py
I want to import module shell.py from test_shell.py. How do I do this?
Note: All the __init__.py files are empty.
Edit - Solution Found:
After reading the comments, using Relative Imports has worked for me:
# in tests/test_shell.py from ..utils import shell
Then I can run the script from outside the project directory as:
python -m project.tests.test_shell
Here, project is the name of the containing folder.
Also, running py.test to run PyTest on all tests (inside tests folder) works correctly.
Tired of sys.path hacks?
There are plenty of sys.path.append -hacks available, but I found an alternative way of solving the problem in hand.
Summary
- Wrap the code into one folder (e.g.,
packaged_stuff) - Create
pyproject.tomlfile to describe your package (see minimalpyproject.tomlbelow) - Pip install the package in editable state with
pip install -e <myproject_folder> - Import using
from packaged_stuff.modulename import function_name
Setup
The starting point is the file structure you have provided, wrapped in a folder called myproject.
.
βββ myproject
βββ api
β βββ api_key.py
β βββ api.py
β βββ __init__.py
βββ examples
β βββ example_one.py
β βββ example_two.py
β βββ __init__.py
βββ LICENCE.md
βββ README.md
βββ tests
βββ __init__.py
βββ test_one.py
I will call the . the root folder, and in my example case it is located at C:\tmp\test_imports\.
api.py
As a test case, let's use the following ./api/api.py
def function_from_api():
return 'I am the return value from api.api!'
test_one.py
from api.api import function_from_api
def test_function():
print(function_from_api())
if __name__ == '__main__':
test_function()
Try to run test_one:
PS C:\tmp\test_imports> python .\myproject\tests\test_one.py
Traceback (most recent call last):
File ".\myproject\tests\test_one.py", line 1, in <module>
from api.api import function_from_api
ModuleNotFoundError: No module named 'api'
Also trying relative imports wont work:
Using from ..api.api import function_from_api would result into
PS C:\tmp\test_imports> python .\myproject\tests\test_one.py
Traceback (most recent call last):
File ".\tests\test_one.py", line 1, in <module>
from ..api.api import function_from_api
ValueError: attempted relative import beyond top-level package
Steps
1) Make a pyproject.toml file to the root level directory
(previously, people used a setup.py file)
The contents for a minimal pyproject.toml would be*
[project]
name = "myproject"
version = "0.1.0"
description = "My small project"
[build-system]
build-backend = "flit_core.buildapi"
requires = ["flit_core >=3.2,<4"]
2) Use a virtual environment
If you are familiar with virtual environments, activate one, and skip to the next step. Usage of virtual environments are not absolutely required, but they will really help you out in the long run (when you have more than one project ongoing...). The most basic steps are (run in the root folder):
- Create virtual env
python -m venv venv
- Activate virtual env
source ./venv/bin/activate(Linux, macOS) or./venv/Scripts/activate(Win)
To learn more about this, just Google out "python virtual env tutorial" or similar. You probably never need any other commands than creating, activating and deactivating.
Once you have made and activated a virtual environment, your console should give the name of the virtual environment in parenthesis
PS C:\tmp\test_imports> python -m venv venv
PS C:\tmp\test_imports> .\venv\Scripts\activate
(venv) PS C:\tmp\test_imports>
and your folder tree should look like this**
.
βββ myproject
β βββ api
β β βββ api_key.py
β β βββ api.py
β β βββ __init__.py
β βββ examples
β β βββ example_one.py
β β βββ example_two.py
β β βββ __init__.py
β βββ LICENCE.md
β βββ README.md
β βββ tests
β βββ __init__.py
β βββ test_one.py
βββ pyproject.toml
βββ venv
βββ Include
βββ Lib
βββ pyvenv.cfg
βββ Scripts [87 entries exceeds filelimit, not opening dir]
3) pip install your project in editable state
Install your top level package myproject using pip. The trick is to use the -e flag when doing the install. This way it is installed in an editable state, and all the edits made to the .py files will be automatically included in the installed package. Using pyproject.toml and -e flag requires pip >= 21.3
In the root directory, run
pip install -e . (note the dot, it stands for "current directory")
You can also see that it is installed by using pip freeze
Obtaining file:///home/user/projects/myproject
Installing build dependencies ... done
Checking if build backend supports build_editable ... done
Getting requirements to build editable ... done
Preparing editable metadata (pyproject.toml) ... done
Building wheels for collected packages: myproj
Building editable for myproj (pyproject.toml) ... done
Created wheel for myproj: filename=myproj-0.1.0-py2.py3-none-any.whl size=903 sha256=f19858b080d4e770c2a172b9a73afcad5f33f4c43c86e8eb9bdacbe50a627064
Stored in directory: /tmp/pip-ephem-wheel-cache-qohzx1u0/wheels/55/5f/e4/507fdeb40cdef333e3e0a8c50c740a430b8ce84cbe17ae5875
Successfully built myproject
Installing collected packages: myproject
Successfully installed myproject-0.1.0
(venv) PS C:\tmp\test_imports> pip freeze
myproject==0.1.0
4) Add myproject. into your imports
Note that you will have to add myproject. only into imports that would not work otherwise. Imports that worked without the pyproject.toml & pip install will work still work fine. See an example below.
Test the solution
Now, let's test the solution using api.py defined above, and test_one.py defined below.
test_one.py
from myproject.api.api import function_from_api
def test_function():
print(function_from_api())
if __name__ == '__main__':
test_function()
Running the test
(venv) PS C:\tmp\test_imports> python .\myproject\tests\test_one.py
I am the return value from api.api!
* Here using Flit as the build backend. Other alternatives exist.
** In reality, you could put your virtual environment anywhere on your hard disk.
Seven years after
Since I wrote the answer below, modifying sys.path is still a quick-and-dirty trick that works well for private scripts, but there has been several improvements
- Installing the package (in a virtualenv or not) will give you what you want, though I would suggest using pip to do it rather than using setuptools directly (and using
setup.cfgto store the metadata) - Using the
-mflag and running as a package works too (but will turn out a bit awkward if you want to convert your working directory into an installable package). - For the tests, specifically, pytest is able to find the api package in this situation and takes care of the
sys.pathhacks for you
So it really depends on what you want to do. In your case, though, since it seems that your goal is to make a proper package at some point, installing through pip -e is probably your best bet, even if it is not perfect yet.
Old answer
As already stated elsewhere, the awful truth is that you have to do ugly hacks to allow imports from siblings modules or parents package from a __main__ module. The issue is detailed in PEP 366. PEP 3122 attempted to handle imports in a more rational way but Guido has rejected it one the account of
The only use case seems to be running scripts that happen to be living inside a module's directory, which I've always seen as an antipattern.
(here)
Though, I use this pattern on a regular basis with
# Ugly hack to allow absolute import from the root folder
# whatever its name is. Please forgive the heresy.
if __name__ == "__main__" and __package__ is None:
from sys import path
from os.path import dirname as dir
path.append(dir(path[0]))
__package__ = "examples"
import api
Here path[0] is your running script's parent folder and dir(path[0]) your top level folder.
I have still not been able to use relative imports with this, though, but it does allow absolute imports from the top level (in your example api's parent folder).
This is happening because A and B are independent, unrelated, packages as far as Python is concerned.
Create a __init__.py in the same directory as Driver.py and everything should work as expected.
In my case adding __init__.py was not enough. You also have to append the path of the parent directory if you get module not found error.
root :
|
|__SiblingA:
| \__A.py
|
|__SiblingB:
| \_ __init__.py
| \__B.py
|
To import B.py from A.py, you have to do the following
import sys
# append the path of the parent directory
sys.path.append("..")
from SiblingB import B
print("B is successfully imported!")
You could remove the dots, and it should work:
# parsemessages.py
from helpers import parse
parse.execute("string to be parsed")
That's probably your best solution if you really don't want to make it a package. You could also nest the entire project one directory deeper, and call it like python3 foo/bot.py.
Explanation:
When you're not working with an actual installed package and just importing stuff relative to your current working directory, everything in that directory is considered a top-level package. In your case, bot, plugins, helpers, and commands are all top-level packages/modules. Your current working directory itself is not a package.
So when you do ...
from ..helpers import parse
... helpers is considered a top-level package, because it's in your current working directory, and you're trying to import from one level higher than that (from your current working directory itself, which is not a package).
When you do ...
from .helpers import parse
... you're importing relative to plugins. So .helpers resolves to plugins.helpers.
When you do ...
from helpers import parse
... it finds helpers as a top-level package because it's in your current working directory.
If you want to execute your code from the root, my best answer to this is adding to the Path your root folder with os.getcwd(). Be sure your sibling folder has a init.py file.
import os
os.sys.path.insert(0, os.getcwd())
from sibling import module