Skip to content

Instantly share code, notes, and snippets.

@sashadev-sky
Last active April 25, 2022 15:42
Show Gist options
  • Save sashadev-sky/d60a50dd2179d05bafcfa4a8d9c0d88c to your computer and use it in GitHub Desktop.
Save sashadev-sky/d60a50dd2179d05bafcfa4a8d9c0d88c to your computer and use it in GitHub Desktop.
"ImportError: attempted relative import with no known parent package" and its sibling "ValueError: attempted relative import beyond top-level package"
print('__file__={0:<35} | __name__={1:<20} | __package__={2:<20}'.format(
__file__, __name__, str(__package__)))

The method used by python interpreter to resolve the relative imports:

Important variables:

  • __name__: Relative imports use a module's name attribute to determine that module's position in the package hierarchy.

  • __package__: Package hierarchy, in other words, the algorithm to resolve the module is based on the values of __name__ and __package__ variables.

  • __file__: Helps us debug by showing us the pathname of the file from which the module was loaded

In cases where where you recieve the error in question, __package__ will be None and __name__ will be __main__.

Why?

If the module's name does not contain any package information (e.g., it is set to __main__), then relative imports are resolved as if the module were a top-level module, regardless of where the module is actually located on the file system.

In a file in app/jobs/takeover_schedule.py (/home/app/api/app/jobs/takeover_schedule.py in the Docker container) - when the file is being resolved correctly this is the output:

__file__=/home/app/api/app/jobs/takeover_schedule.py | __name__=app.jobs.takeover_schedule | __package__=app.jobs

It resolves fine when executed from another module within the flask application. But when running it from a crontab configured within the container, the output appeared as:

__file__=/home/app/api/app/jobs/takeover_schedule.py | __name__=__main__ | __package__=None

In other words, it does not have any information about the package the module belongs to.

In this solution, we needed to create a new script:

  1. Import it into the top level main.py. This will set the package information (__name__ and __package__ variables)

Alternatively, in this solution, we use the -m option without creating a new script:

  1. Make sure its ancestor directories are configured with an __init__.py. This will make them directory as package.

  2. Make sure your file's directory is configured with an __init__.py. This makes it a module.

  3. Call it with -m. The python -m option allows modules to be located using the Python module namespace for execution as scripts.

    • This will set the package information (just the __package__ variable, __name__ will remain __main__)

Quick way to resolve a path to another module in a flask application. More of a bandaid than a fix...

import sys
from pathlib import Path

file = Path(__file__).resolve()
package_root_directory = file.parents[1]
sys.path.append(str(package_root_directory))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment