Python Import errors and package static files (resources)
How to debug import errors (if they occur)
Let's talk about the difference between modules and packages.
- A
module
is just a file which contains some python code; - A
package
is just a folder which contains one or more modules plus a "special"__init__.py
file.
Sometimes you may get an ugly ImportError: cannot import ....
error. In this case, you can investigate that error by using the special (dunder) methods available at runtime.
Just add, at the top level of the modules that cause issues, this print (or log):
print(
f"PKG: {__package__}
MODULE: {__name__},
SPEC: {__spec__} DIR: {dir()}"
)
#Python stuff bellow
The above print
should look similar to this in the console:
PKG: htmgem
MODULE: htmgem.doc,
SPEC: ModuleSpec(name='htmgem.doc', loader=<_frozen_importlib_external.SourceFileLoader object at 0x7ff724f1ec70>, origin='/path/to/htmgem/doc.py')
DIR: ['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']
Python knows that the package's name is htmgem
, while the module's name is htmgem.doc
:
- PKG: htmgem
- MODULE: htmgem.doc
So, if in the module doc.py
you have an import like from doc import func
, you should be able to fix the import with from htmgem.doc import func
.
Try to avoid relative imports (the ones with from ..module import func
) and use only absolute imports.
The issue from above may not fit your case, but by printing to console the current package name (__package__
) and the module name (__name__
), you could find some clues on how to fix the import error issue.
Adding static files aka resources to a python package
In your package, create a resources
folder, mark it as a package by creating a __init__.py
file and add the static files you need. In my case the static file is index.html
.
.
├── htmgem
│ ├── doc.py
│ ├── __init__.py
│ ├── resources
│ │ ├── index.html
│ │ └── __init__.py
│ └── tags.py
├── README.md
└── tests
├── test_doc.py
└── test_tags.py
Now, to access that static file, you need to use importlib.resources
module.
In my case I need to read index.html
contents and generate some files, here is a snippet:
import importlib.resources as pkg_resources
from htmgem import resources
class Doc:
def __init__(self, base_html=None):
self.base_html = base_html or pkg_resources.read_text(resources, "index.html")
If the read_text
method is not what you need, you can use read_binary
instead. For more available methods check out python docs.