Relative import from .py file to other .py file in vscode

my folder structure is
(.spark-env) chaitanya@debian:~/GIT/Pytest$ tree

.
├── hi
│   ├── hi.py
│   └── __init__.py
├── __init__.py
├── source
│   ├── __init__.py
│   └── my_function.py
└── tests
    ├── __init__.py
    └── test_my_function.py

i am using vscode and in my launch.json contain

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Python Debugger: Module",
            "type": "debugpy",
            "request": "launch",
            "module": "hi.py",
            "env": {
                "PYTHONPATH": "${workspaceFolder}/"
            }
        }
    ]
}

in my_function.py contain

source/my_function.py


def add(number1: int, number2: int) -> int:
    result = number1 + number2
    return result

def div(number1: int, number2: int) -> float:
    result = number1 / number2
    return result

in hi.py contain

hi/hi.py


from source.my_function import add
if __name__ == "__main__":
    x = add(1, 2)
    print(x)

when i try to run the hi.py i am getting the error of (.spark-env) chaitanya@debian:~/GIT/Pytest$ /home/chaitanya/GIT/.spark-env/bin/python3 /home/chaitanya/GIT/Pytest/hi/hi.py
Traceback (most recent call last):
File “/home/chaitanya/GIT/Pytest/hi/hi.py”, line 3, in
from source.my_function import add
ModuleNotFoundError: No module named ‘source’

also i am using debian 12 and vscode 1.88 i am not able to do any relative import i have try this in windows vscode also but nothing work if the same code i run in pycharm it run without getting any error above code work perfectly in the pycharm but in vscode i am not able to import it please help me

Related background:

1 Like

In order for this import to work:

from source.my_function import add

the directory containing source needs to be on the import path. There’s a bunch of ways that can happen, including:

  1. Configuring the import path in your IDE settings.

    Not sure about VS Code, but in PyCharm, you can add directories to the import path that PyCharm uses in its run configurations by right clicking on a directory and selecting “Mark Directory As” > “Sources Root”.

  2. Relying on how you run your program to insert the right directory.

    • Running python ./a/b/c.py puts the directory a/b on the import path.
    • Running python -m a.b.c puts the current working directory on the import path.

    This is possibly the most popular option (often by accident), since its what makes things “just work” most of the time. Relying on this can be a bit precarious though, since you need to be mindful of what directory you’re running your script/program from, and changes to your project layout can break things in subtle ways.

  3. Installing your project as a distribution package. This is most robust option, but requires becoming familiar with python packaging and learning how to use some additional tools. If you install your project as a package, you’ll be able to import your modules consistently from anywhere. This also has the nice added benefit that it will make it easy for static analysis tools (e.g. your IDE, type checkers, linters, etc) to understand your project’s layout. This can be as simple as creating a pyproject.toml file with a few boilerplate lines and then running python -m pip install . inside your virtual environment. The exact steps will vary depending on which build backend you use (I personally like poetry, which will also manage your virtual environments and dependencies for you). You can find a more complete tutorial in the official Python Packaging Guide.

  4. Setting the PYTHONPATH environment variable.

  5. Hacking with sys.path at runtime. Very versatile, but sensitive to changes in your project layout and how you run your code. Also difficult for IDEs and static analysis tools to understand.

2 Likes

… But OP is attempting an absolute import, so the first two links are not applicable. Apparently it is not intended for hi and source to have a common package; hi is just holding a driver script.

Right, this is where the problem lies. OP runs from the root folder, expecting it to be on the sys.path; but must run a top-level script from a subfolder. Doing this by name puts its folder on the sys.path instead, causing the absolute import to fail. Running it instead as /home/chaitanya/GIT/.spark-env/bin/python3 -m hi.hi should solve the problem. This makes Python recognize hi as a top-level package which is found in the project root, and also makes it recognize the project root as a place to search for absolute imports. Of course, since the module is now specified as a full-qualified Python name, rather than a file name, there is no longer any absolute vs relative path distinction (it seems like OP hoped that using absolute paths would avoid the problem; it does not).

If you want to use relative imports, the rules are a bit different. But relative import does not make sense here, because the hi, source and test packages are not related to each other in a suitable way. (I assume, for example, that if you were to build a wheel for PyPI, you would not include the tests.)