That setup should fail even without PyInstaller because mypkg.mymod is never imported.
# This initialises mypkg.__init__ only.
import mypkg
if __name__ == "__main__":
# So even if mymod has been collected by PyInstaller, it won't have been loaded
# and set as an attribute of `mypkg`.
mypkg.mymod.myfunc()
Possibly what your after is the following line in __init__.py?
from . import mymod
That way, initialising mypkg (using import mypkg) will implicitly load mymod and you can safely use mypkg.mymod.myfunc(). Alternatively you can just import mypkg.mymod.
Many hooks consist of only one statement, an assignment to hiddenimports. For example, the hook for the dnspython package, called hook-dns.rdata.py, has only this statement:
hiddenimports = [
"dns.rdtypes.*",
"dns.rdtypes.ANY.*"
]
When Analysis sees import dns.rdata or from dns import rdata it calls hook-dns.rdata.py and examines its value of hiddenimports. As a result, it is as if your source script also contained [my emphasis]:
import dns.rdtypes.*
import dsn.rdtypes.ANY.*
My question would then be: if I can solve it with "import mypkg.mymod" in my script, why can't I solve it with "--hidden-import=mypkg.mymod" in PyInstaller?
Thanks again. I do appreciate you taking the time to help me understand.
Because import mypkg.mymod initialises it but --hidden-import=mypkg.mymod just says include it. Python doesn’t automatically import modules just because they exist otherwise it would be incredibly slow.
If you really don’t want to use from . import mymod then you can put the following in your __init__.py (note Python>=3.6 only) but it’s terrible programming practice.
def __getattr__(submodule):
import importlib
try:
return importlib.import_module("mypkg." + submodule)
except ImportError:
raise AttributeError(submodule)