Home > database >  How can I set __package__ of a script from within an imported library?
How can I set __package__ of a script from within an imported library?

Time:11-30

The __package__ attribute can be set to allow directly executing a python script that is also part of a module (e.g. uses relative imports). Without __package__, one can only run it as python3 -m parent.child.script but with __package__ = 'child', one can also run more easily as python3 script.py.

From PEP 366:

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"

Note that this boilerplate is sufficient only if the top level package is already accessible via sys.path. Additional code that manipulates sys.path would be needed in order for direct execution to work without the top level package already being importable.

I'm trying to move this boilerplate into a library that each script can import. I already have code available to import the package, resulting in this boilerplate snippet:

import pathlib, imptools
if __name__ == '__main__' and __package__ == None:
  __package__ = pathlib.Path(__file__).parent.name
  imptools.import_from_path(__package__, '..')

Now I would like to simplify this to the following, which requires setting __package__ for the script from within the library rather than the script itself.

import relative_import
relative_import.enable()

I know how to find out what script file the library function is called from. This can be done via inspect:

import inspect
for info in inspect.stack():
  if info.filename == __file__:
    continue
  break
script = info.filename

The only problem left is, how can I set __package__ of the script module from within the library?

CodePudding user response:

You're trying to do something very finicky here, but this part, at least, is easy. The script module is always __main__. You can just import __main__ and set __main__.__package__.

CodePudding user response:

I've created the imptools package to easily enable relative imports for scripts inside modules:

import imptools  # pip3 install imptools

imptools.enable_relative()

# Relative imports...

The script can be run either as python3 -m module.script or as python3 script.py from any directory.

  • Related