Packages
A package is a folder of modules. Its __init__.py runs once, the first time anything from the package is imported, and the names it defines become the package's attributes. Technically a package is a module with a __path__: the list of folders its submodules are loaded from.
import importlib
import os
import sys
from pathlib import Path
Path("shop").mkdir(exist_ok=True)
Path("shop/prices.py").write_text(
"TAX = 0.2\n")
Path("shop/__init__.py").write_text(
"from .prices import TAX\n"
"__all__ = ['TAX']\n")
sys.path.insert(0, os.getcwd())
importlib.invalidate_caches()
import shop
print(shop.TAX, shop.__all__)
print(shop.prices.__name__)
print(shop.__path__)Inside a package
from .prices import TAXis a relative import:.is this package and..its parent. It works only in a module imported as part of a package.- A submodule loads only when something imports it. Here
import shoploadsshop.pricesbecause__init__.pyimports it. __all__lists the names thatfrom shop import *binds. Without it, every name not starting with_is bound.
Keep __init__.py short: re-export the public names and leave the real work in submodules.
Your task
Write is_package(module), true when the module has a __path__. Write public_names(module) returning what from module import * would bind: module.__all__ as a list when it exists, otherwise the sorted names not starting with _. Write submodule_names(package) returning the sorted names of every module in the package's folders, imported or not, using pkgutil.iter_modules.
The explanation opens here once every check passes.