HPI/my/core/legacy.py
seanbreckenridge 79eeab2128
cli completion doc updates, hide legacy import warning (#279)
* core/cli: hide warnings when autocompleting

* link to completion in setup/troubleshooting
* update completion docs to make source path clear
2023-03-06 21:36:36 +00:00

60 lines
2.7 KiB
Python

# I think 'compat' should be for python-specific compat stuff, whereas this for HPI specific backwards compatibility
import os
import inspect
import re
from typing import List
from my.core import warnings as W
def handle_legacy_import(
parent_module_name: str,
legacy_submodule_name: str,
parent_module_path: List[str],
) -> bool:
###
# this is to trick mypy into treating this as a proper namespace package
# should only be used for backwards compatibility on packages that are convernted into namespace & all.py pattern
# - https://www.python.org/dev/peps/pep-0382/#namespace-packages-today
# - https://github.com/karlicoss/hpi_namespace_experiment
# - discussion here https://memex.zulipchat.com/#narrow/stream/279601-hpi/topic/extending.20HPI/near/269946944
from pkgutil import extend_path
parent_module_path[:] = extend_path(parent_module_path, parent_module_name)
# 'this' source tree ends up first in the pythonpath when we extend_path()
# so we need to move 'this' source tree towards the end to make sure we prioritize overlays
parent_module_path[:] = parent_module_path[1:] + parent_module_path[:1]
###
# allow stuff like 'import my.module.submodule' and such
imported_as_parent = False
# allow stuff like 'from my.module import submodule'
importing_submodule = False
# some hacky traceback to inspect the current stack
# to see if the user is using the old style of importing
for f in inspect.stack():
# seems that when a submodule is imported, at some point it'll call some internal import machinery
# with 'parent' set to the parent module
# if parent module is imported first (i.e. in case of deprecated usage), it won't be the case
args = inspect.getargvalues(f.frame)
if args.locals.get('parent') == parent_module_name:
imported_as_parent = True
# this we can only detect from the code I guess
line = '\n'.join(f.code_context or [])
if re.match(rf'from\s+{parent_module_name}\s+import\s+{legacy_submodule_name}', line):
importing_submodule = True
# click sets '_HPI_COMPLETE' env var when it's doing autocompletion
# otherwise, the warning will be printed every time you try to tab complete
autocompleting_module_cli = "_HPI_COMPLETE" in os.environ
is_legacy_import = not (imported_as_parent or importing_submodule)
if is_legacy_import and not autocompleting_module_cli:
W.high(f'''\
importing {parent_module_name} is DEPRECATED! \
Instead, import from {parent_module_name}.{legacy_submodule_name} or {parent_module_name}.all \
See https://github.com/karlicoss/HPI/blob/master/doc/MODULE_DESIGN.org#allpy for more info.
''')
return is_legacy_import