(nettipäiväkirja 17.06.2025) I'm going to spend a little time explaining how Python importing works if no questions come in, so in this thread. This is complicated but could help you understand weird import / file not found errors.
Python looks for files two ways: # by filename # by module name.
Filenames are resolved relative to the current working directory. This is the directory that Python was started in. For instance, in this command: {{{ atehwa@kladakong:~/proj/skillio-teaching$ ./venv/bin/python Python 3.10.12 (main, Feb 4 2025, 14:57:36) [GCC 11.4.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import os >>> os.getcwd() '/home/atehwa/proj/skillio-teaching' }}}
You can see that my current working directory (cwd) is skillio-teaching, because I was in that directory when I started Python.
Module names, OTOH, are a bit more complicated. They are resolved into filenames depending on three things: # Python system path # the current working directory (because it is usually part of (1)) # the current package name (only for "relative" package names).
The Python system path is a list of directories that is used for looking up modules. You can easily see this list so: {{{ >>> import sys >>> sys.path ['', '/usr/lib/python310.zip', '/usr/lib/python3.10', '/usr/lib/python3.10/lib-dynload', '/home/atehwa/proj/skillio-teaching/venv/lib/python3.10/site-packages'] }}}
As you can see, there is a magical ' ' at the beginning of that list. That stands for the current working directory. It is because of this entry that commands such as this work: {{{ >>> import src.data.config >>> }}}
However, if I remove that entry, importing from my current
directory (which is my project directory) no longer works: {{{ >>> del
sys.path[0] >>> import src.data.queries Traceback (most recent call
last): File "
(note of warning: I restarted my Python in between because the previous import had already imported module src which still does have the data.queries submodule even if src itself is no longer possible to find via importing)
Absolute modules are basically that simple. There are a couple of complications more: # When import sees a name that already exists (such as src or src.data, it doesn't do anything for that name. # For a directory to be importable, it has to have an __init__.py file specifying the names in that module (i.e. what does the imported namespace have on the Python side?)
Then, relative modules are the ones starting with dots. Such as {{{ from .config import config }}}
Relative modules are resolved according to their current package (kinda akin to CWD, but on the module side).
To track the current package, Python uses a magical __package__ variable. Together with a magical __name__ variable, it tells Python "where it's at" at the moment.
To demonstrate how these magical variables work, I made a module test_module in the src package to print them: {{{ atehwa@kladakong:~/proj/skillio-teaching$ cat src/test_module.py
print(__package__) print(__name__) }}}
If I import this module in Python, both variables contain maximum information: {{{ atehwa@kladakong:~/proj/skillio-teaching$ python3 [...] >>> import src.test_module src src.test_module >>> }}}
If I run test_module.py as a script, it's not treated as a module at all, both vars will be garbage, and relative imports will not work: {{{ atehwa@kladakong:~/proj/skillio-teaching$ python3 src/test_module.py None __main__ }}}
If I ask Python to run the module by its module name, package is resolved properly but the module is told (via __name__ ) that it is being called directly from command line: {{{ atehwa@kladakong:~/proj/skillio-teaching$ python3 -m src.test_module src __main__ }}}
For interest, the Python "toplevel" (aka Python REPL aka Python command prompt) is treated the same way as scripts are: it doesn't have a package, and it is told that it is called directly from command line: {{{ atehwa@kladakong:~/proj/skillio-teaching$ python3 Python 3.10.12 (main, Feb 4 2025, 14:57:36) [GCC 11.4.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> print(__package__) None >>> print(__name__) __main__ }}}
Python modules (which are .py) files often provide functionality when they are being called directly from command line. For instance, json.tool module provides pretty-printing if called with -m: {{{ atehwa@kladakong:~/proj/skillio-teaching$ echo '{"foo":"bar"}' | python3 -m json.tool { "foo": "bar" } }}}
This functionality is activated by checking whether __name__ == '__main__', i.e. if the magical variable was set to indicate we were called directly from the command line.
----
* [merkintä: 2025-06] * [atehwa] * [kategoria: päiväkirjamerkintä] * [python] * [how to write functional python] * [sieve of erastothenes in clean python] * [teos: python]