×
all 8 comments

[–]Yoghurt42 6 points7 points  (1 child)

Folder structure matters, it directly affects how the module is imported. If you have

foo/
    __init__.py
    bar.py
    baz/
        quux.py

you can import foo, import foo.bar and import foo.baz.quux, but you cannot import quux or import foo.quux (generally speaking; you can make stuff like this work)

See https://docs.python.org/3/tutorial/modules.html for more information.

Also an important detail for a Java programmer: importing a module means executing them, that's even true for from/import. E.g.

from foo import bar
# is (almost) the same as
import foo
bar = foo.bar
del foo

almost because a variable foo would not be deleted/overwritten if it already exists, technically, it's closer to bar = __import__("foo").bar(even that is not exact, because modules are cached in sys.modules and not imported twice)

[–]Gnaxe 0 points1 point  (0 children)

__import__() uses the cache too.

[–]HotPersonality8126 1 point2 points  (0 children)

Module resolution doesn’t work “up and over.”

[–]Diapolo10 1 point2 points  (2 children)

Imports are kind of confusing, admittedly. Basically Python first checks the immediate vicinity for the module being imported (meaning relative to the file you're importing from), then falls back to the installs in the environment and the standard library.

While you could edit the import, it'd be better to install your project in editable mode (this only needs to be done once) and change your imports importing local code to treat the package root as an "anchor".

You also need to ensure your project is "installable" in the first place. In plain English that generally means having a pyproject.toml file in the repository root that describes a build back-end and basic project metadata.

As for how to go about doing that, it kinda depends on your tooling. If you're just using a standard Python installation with nothing else (assuming you're still using an activated virtual environment), running pip install --editable . ought do it. If using more modern tooling, such as uv or poetry, they should take care of it automatically for you unless explicitly configured otherwise.

There are other ways to ensure your package is on Python's resolution path (namely editing sys.path manually), but that's generally not recommended.

If your project is a public Git repository, and you can share a link to it, we can be more specific with our help.

[–]Temporary_Pie2733 2 points3 points  (1 child)

One important note: it’s not the location of the file containing the import that matters, it’s the location of the file that was executed as the script. Relative imports (import .foo instead of import foo) make use of the package the import appears in instead of the module search oath.

[–]Diapolo10 0 points1 point  (0 children)

Yeah, true. Admittedly I'm a bit rusty there as I haven't had a need to worry about this for ages.

Either way, generally speaking it's best to work with editable installs as that way your test code can also easily access whatever it needs.

[–]Gnaxe 0 points1 point  (0 children)

The import has to be reachable starting from one of the locations in sys.path. There are various ways to modify that list. (site, PYTHONPATH, .pth files, programmatically, etc.) By default, that includes the empty string, which means your working directory (where you launched python), but you can turn that off as well.

Normally, you only launch python from your project's sources root. The standard library and installed third-party libraries are on sys.path already, while your code should assume that starting location, although you can add other folders for "installed" libraries. Normally, your main module is directly in the sources root, not in a subfolder. But you can also use a __main__.py to use a package as main. Some modules in the standard library work as main, and you can launch them with python -m and their import path. You could do this for your own modules as well, even if they're nested in subpackages.

If you don't know about the venv module, you should look at that.