Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

imports.py 2.7KB

1 year ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. from __future__ import annotations
  2. import warnings
  3. from typing import Any, Dict, Iterable, Optional
  4. __all__ = ["lazy_import"]
  5. def import_name(name: str, source: str, namespace: Dict[str, Any]) -> Any:
  6. """
  7. Import ``name`` from ``source`` in ``namespace``.
  8. There are two use cases:
  9. - ``name`` is an object defined in ``source``;
  10. - ``name`` is a submodule of ``source``.
  11. Neither :func:`__import__` nor :func:`~importlib.import_module` does
  12. exactly this. :func:`__import__` is closer to the intended behavior.
  13. """
  14. level = 0
  15. while source[level] == ".":
  16. level += 1
  17. assert level < len(source), "importing from parent isn't supported"
  18. module = __import__(source[level:], namespace, None, [name], level)
  19. return getattr(module, name)
  20. def lazy_import(
  21. namespace: Dict[str, Any],
  22. aliases: Optional[Dict[str, str]] = None,
  23. deprecated_aliases: Optional[Dict[str, str]] = None,
  24. ) -> None:
  25. """
  26. Provide lazy, module-level imports.
  27. Typical use::
  28. __getattr__, __dir__ = lazy_import(
  29. globals(),
  30. aliases={
  31. "<name>": "<source module>",
  32. ...
  33. },
  34. deprecated_aliases={
  35. ...,
  36. }
  37. )
  38. This function defines ``__getattr__`` and ``__dir__`` per :pep:`562`.
  39. """
  40. if aliases is None:
  41. aliases = {}
  42. if deprecated_aliases is None:
  43. deprecated_aliases = {}
  44. namespace_set = set(namespace)
  45. aliases_set = set(aliases)
  46. deprecated_aliases_set = set(deprecated_aliases)
  47. assert not namespace_set & aliases_set, "namespace conflict"
  48. assert not namespace_set & deprecated_aliases_set, "namespace conflict"
  49. assert not aliases_set & deprecated_aliases_set, "namespace conflict"
  50. package = namespace["__name__"]
  51. def __getattr__(name: str) -> Any:
  52. assert aliases is not None # mypy cannot figure this out
  53. try:
  54. source = aliases[name]
  55. except KeyError:
  56. pass
  57. else:
  58. return import_name(name, source, namespace)
  59. assert deprecated_aliases is not None # mypy cannot figure this out
  60. try:
  61. source = deprecated_aliases[name]
  62. except KeyError:
  63. pass
  64. else:
  65. warnings.warn(
  66. f"{package}.{name} is deprecated",
  67. DeprecationWarning,
  68. stacklevel=2,
  69. )
  70. return import_name(name, source, namespace)
  71. raise AttributeError(f"module {package!r} has no attribute {name!r}")
  72. namespace["__getattr__"] = __getattr__
  73. def __dir__() -> Iterable[str]:
  74. return sorted(namespace_set | aliases_set | deprecated_aliases_set)
  75. namespace["__dir__"] = __dir__