Search code examples
pythonpython-importpython-modulepython-packagingpython-poetry

How to import module using path related to working directory in a python project that managed by poetry?


I'm using poetry to manage my python project, here's the project:

my_project/
├── pyproject.toml
├── module.py
└── scripts/
    └── main.py

And I want to know how to import function from module.py into my_scripts/main.py correctly.

My pyproject.toml:

[tool.poetry]
name = "my_project"
version = "0.1.0"
description = ""
authors = []

[tool.poetry.dependencies]
python = "^3.11"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

I have tried this:

# In my_scripts/main.py

from module import my_function

And run these commands:

poetry install
poetry shell
python my_scripts/main.py

then got this error:

ModuleNotFoundError: No module named 'module'

I also have put a __init__.py under my_project/ but didn't work out.


Solution

  • What made it work for me is to create a folder in your root with the same name of the package in the pyproject.toml file. If you don't do this, poetry will not find your project and will not install it in editable mode.

    You also need to let Python know that a folder is a package or sub-package by placing an __init__.py file in it.

    my_project/
    ├─ pyproject.toml
    ├─ my_project/
       ├─ __init__.py
       ├─ module.py
       ├─ scripts/
          ├─ __init__.py
          ├─ main.py
    

    When you initialize the folder you should see that the project is installed.

    ...> poetry install
    Installing dependencies from lock file
    
    Installing the current project: my_project (0.1.0)
    

    Last thing, you need to change the import in main.py.

    # main.py
    
    from my_project.module import my_function
    
    print("my_function imported")
    

    You can now activate the virtual environment (if it's not already active) and run your script.

    ...> poetry shell
    Spawning shell within: C:\...\my_project\.venv
    ...
    ...> python my_project\scripts\main.py
    my_function imported
    

    PS

    As @sinoroc pointed out in the comments, it is possible (and preferable according to him) to run the module with python -m my_project.scripts.main.

    I am new to the topic, but according to this SO answer:

    __package__ is set to the immediate parent package in <modulename> [when running python via command line with the -m option]

    This means that relative imports like the following magically work.

    # main.py
    
    from ..module import my_function
    
    # ..module tells python to look in the parent directory for module.py
    
    print("my_function imported")
    
    ...> python -m my_project.scripts.main
    my_function imported
    ...> python my_project\scripts\main.py
    Traceback (most recent call last):
    ...
    ImportError: attempted relative import with no known parent package