Importing module from a directory above current working directory

巧了我就是萌 提交于 2021-02-04 06:31:06

问题


First of all, there are a bunch of solutions on stackoverflow regarding this but from the ones I tried none of them is working. I am working on a remote machine (linux). I am prototyping within the dir-2/module_2.py file using an ipython interpreter. Also I am trying to avoid using absolute paths as the absolute path in this remote machine is long and ugly, and I want my code to run on other machines upon download.

My directory structure is as follows:

/project-dir/
            -/dir-1/
                  -/__ init__.py
                  -/module_1.py
            -/dir-2/
                  -/__ init__.py
                  -/module_2.py
                  -/module_3.py

Now I want to import module_1 from module_2. However the solution mentioned in this stackoverflow post: link of using

sys.path.append('../..')
import module_2

Does not work. I get the error: ModuleNotFoundError: No module named 'module_1'

Moreover, within the ipython interpreter things like import .module_3 within module_2 throws error:

import .module_3
       ^ SyntaxError: invalid syntax

Isn't the dot operator supposed to work within the same directory as well. Overall I am quite confused by the importing mechanism. Any help with the initial problem is greatly appreciated! Thanks a lot!


回答1:


Why it didn't work?

If you run the module1.py file and you want to import module2 then you need something like

sys.path.append("../dir-2")

If you use sys.path.append("../..") then the folder you added to the path is the folder containing project-dirand there is notmodule2.py` file inside it.


The syntax import .module_3 is for relative imports. if you tried to execute module2.py and it contains import .module_3 it does not work because you are using module2.py as a script. To use relative imports you need to treat both module2.py and module_3.py as modules. That is, some other file imports module2 and module2 import something from module3 using this syntax.

Suggestion on how you can proceed

One possible solution that solves both problems is property organizing the project and (optionally, ut a good idea) packaging your library (that is, make your code "installable"). Then, once your library is installed (in the virtual environment you are working) you don't need hacky sys.path solutions. You will be able to import your library from any folder.

Furthermore, don't treat your modules as scripts (don't run your modules). Use a separate python file as your "executable" (or entry point) and import everything you need from there. With this, relative imports in your module*.py files will work correctly and you don't get confused.

A possible directory structure could be

/project-dir/
            - apps/
                  - main.py
            - yourlib/
                  -/__ init__.py
                  -/dir-1/
                        -/__ init__.py
                        -/module_1.py
                  -/dir-2/
                        -/__ init__.py
                        -/module_2.py
                        -/module_3.py

Notice that the the yourlib folder as well as subfolders contain an __init__.py file. With this structure, you only run main.py (the name does not need to be main.py).

Case 1: You don't want to package your library

If you don't want to package your library, then you can add sys.path.append("../") in main.py to add "the project-dir/ folder to the path. With that your yourlib library will be "importable" in main.py. You can do something like from yourlib import module_2 and it will work correctly (and module_2 can use relative imports). Alternatively, you can also directly put main.py in the project-dir/ folder and you don't need to change sys.path at all, since project-dir/ will be the "working directory" in that case.

Note that you can also have a tests folder inside project-dir and to run a test file you can do the same as you did to run main.py.

Case 2: You want to package your library

The previous solution already solves your problems, but going the extra mile adds some benefits, such as dependency management and no need to change sys.path no matter where you are. There are several options to package your library and I will show one option using poetry due to its simplicity.

After installing poetry, you can run the command below in a terminal to create a new project

poetry new mylib

This creates the following folder structure

mylib/
     - README.rst
     - mylib/
            - __init__.py
     - pyproject.toml
     - tests

You can then add the apps folder if you want, as well as subfolders inside mylib/ (each with a __init__.py file).

The pyproject.toml file specifies the dependencies and project metadata. You can edit it by hand and/or use poetry to add new dependencies, such as

poetry add pandas
poetry add --dev mypy

to add pandas as a dependency and mypy as a development dependency, for instance. After that, you can run

poetry build

to create a virtual environment and install your library in it. You can activate the virtual environment with poetry shell and you will be able to import your library from anywhere. Note that you can change your library files without the need to run poetry build again.

At last, if you want to publish your library in PyPi for everyone to see you can use

poetry publish --username your_pypi_username --password _passowrd_

TL; DR

Use an organized project structure with a clear place for the scripts you execute. Particularly, it is better if the script you execute is outside the folder with your modules. Also, don't run a module as a script (otherwise you can't use relative imports).



来源:https://stackoverflow.com/questions/64107888/importing-module-from-a-directory-above-current-working-directory

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!