您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 

101 行
2.7 KiB

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