how to make __main__ a package -OR- script and a package submodule at the same time
Question:
I have this structure:
app.py
sub/
__init__.py
lib.py
utils.py
In app.py
, I want to be able to do sth like this:
from sub.lib import some_func
In lib.py
, I want to be able to import utils.py
.
And I also want that it’s possible to execute lib.py
as a script directly. (I.e. cd sub; python3 lib.py
.)
The problem is, if I start lib.py
as a script, it will not be a package, and thus, it cannot use relative imports, so sth like from .utils import some_util_func
would not work. However, if I import lib.py
as part of the sub
package, only the relative import will work.
How do I solve this? (In an easy way. E.g. without creating another small wrapper script just to call lib.py
.)
E.g. if there is a way to mark the __main__
module as a package, it would solve that. But how? Is this possible? E.g. by defining __path__
or so?
Answers:
Ok, actually, my last comment really worked. If I add this in the beginning of lib.py
, it seems to work:
import os
if __name__ == '__main__':
__path__ = [os.path.dirname(os.path.abspath(__file__))]
(About __path__
, see e.g. here.)
Edit With newer Python versions (3.9), this does not seem to be enough anymore. See below for an extended version.
Also, in PEP 366 it is mentioned:
When the main module is specified by its filename, then the __package__
attribute will be set to None
. To allow relative imports when the module is executed directly, boilerplate similar to the following would be needed before the first relative import statement:
if __name__ == "__main__" and __package__ is None:
__package__ = "expected.package.name"
I didn’t tried this yet.
For Python 3.9, I needed to extend my solution:
if __name__ == '__main__':
# Make relative imports work here. https://stackoverflow.com/questions/54576879/
__path__ = [os.path.dirname(os.path.abspath(__file__))]
__package__ = "YOUR_CUSTOM_PACKAGE_NAME"
pkg_mod = types.ModuleType(__package__)
sys.modules[__package__] = pkg_mod
pkg_mod.__package__ = __package__
pkg_mod.__file__ = os.path.abspath("__init__.py")
pkg_mod.__path__ = __path__
You can set PYTHONPATH environment variable when starting your program.
Just type from lib directory:
PYTHNONPATH="path/to/root/directory" python lib.py
I have this structure:
app.py
sub/
__init__.py
lib.py
utils.py
In app.py
, I want to be able to do sth like this:
from sub.lib import some_func
In lib.py
, I want to be able to import utils.py
.
And I also want that it’s possible to execute lib.py
as a script directly. (I.e. cd sub; python3 lib.py
.)
The problem is, if I start lib.py
as a script, it will not be a package, and thus, it cannot use relative imports, so sth like from .utils import some_util_func
would not work. However, if I import lib.py
as part of the sub
package, only the relative import will work.
How do I solve this? (In an easy way. E.g. without creating another small wrapper script just to call lib.py
.)
E.g. if there is a way to mark the __main__
module as a package, it would solve that. But how? Is this possible? E.g. by defining __path__
or so?
Ok, actually, my last comment really worked. If I add this in the beginning of lib.py
, it seems to work:
import os
if __name__ == '__main__':
__path__ = [os.path.dirname(os.path.abspath(__file__))]
(About __path__
, see e.g. here.)
Edit With newer Python versions (3.9), this does not seem to be enough anymore. See below for an extended version.
Also, in PEP 366 it is mentioned:
When the main module is specified by its filename, then the
__package__
attribute will be set toNone
. To allow relative imports when the module is executed directly, boilerplate similar to the following would be needed before the first relative import statement:
if __name__ == "__main__" and __package__ is None:
__package__ = "expected.package.name"
I didn’t tried this yet.
For Python 3.9, I needed to extend my solution:
if __name__ == '__main__':
# Make relative imports work here. https://stackoverflow.com/questions/54576879/
__path__ = [os.path.dirname(os.path.abspath(__file__))]
__package__ = "YOUR_CUSTOM_PACKAGE_NAME"
pkg_mod = types.ModuleType(__package__)
sys.modules[__package__] = pkg_mod
pkg_mod.__package__ = __package__
pkg_mod.__file__ = os.path.abspath("__init__.py")
pkg_mod.__path__ = __path__
You can set PYTHONPATH environment variable when starting your program.
Just type from lib directory:
PYTHNONPATH="path/to/root/directory" python lib.py