Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/websockets/imports.py: 71%

42 statements  

« prev     ^ index     » next       coverage.py v7.3.1, created at 2023-09-25 06:20 +0000

1from __future__ import annotations 

2 

3import warnings 

4from typing import Any, Dict, Iterable, Optional 

5 

6 

7__all__ = ["lazy_import"] 

8 

9 

10def import_name(name: str, source: str, namespace: Dict[str, Any]) -> Any: 

11 """ 

12 Import ``name`` from ``source`` in ``namespace``. 

13 

14 There are two use cases: 

15 

16 - ``name`` is an object defined in ``source``; 

17 - ``name`` is a submodule of ``source``. 

18 

19 Neither :func:`__import__` nor :func:`~importlib.import_module` does 

20 exactly this. :func:`__import__` is closer to the intended behavior. 

21 

22 """ 

23 level = 0 

24 while source[level] == ".": 

25 level += 1 

26 assert level < len(source), "importing from parent isn't supported" 

27 module = __import__(source[level:], namespace, None, [name], level) 

28 return getattr(module, name) 

29 

30 

31def lazy_import( 

32 namespace: Dict[str, Any], 

33 aliases: Optional[Dict[str, str]] = None, 

34 deprecated_aliases: Optional[Dict[str, str]] = None, 

35) -> None: 

36 """ 

37 Provide lazy, module-level imports. 

38 

39 Typical use:: 

40 

41 __getattr__, __dir__ = lazy_import( 

42 globals(), 

43 aliases={ 

44 "<name>": "<source module>", 

45 ... 

46 }, 

47 deprecated_aliases={ 

48 ..., 

49 } 

50 ) 

51 

52 This function defines ``__getattr__`` and ``__dir__`` per :pep:`562`. 

53 

54 """ 

55 if aliases is None: 

56 aliases = {} 

57 if deprecated_aliases is None: 

58 deprecated_aliases = {} 

59 

60 namespace_set = set(namespace) 

61 aliases_set = set(aliases) 

62 deprecated_aliases_set = set(deprecated_aliases) 

63 

64 assert not namespace_set & aliases_set, "namespace conflict" 

65 assert not namespace_set & deprecated_aliases_set, "namespace conflict" 

66 assert not aliases_set & deprecated_aliases_set, "namespace conflict" 

67 

68 package = namespace["__name__"] 

69 

70 def __getattr__(name: str) -> Any: 

71 assert aliases is not None # mypy cannot figure this out 

72 try: 

73 source = aliases[name] 

74 except KeyError: 

75 pass 

76 else: 

77 return import_name(name, source, namespace) 

78 

79 assert deprecated_aliases is not None # mypy cannot figure this out 

80 try: 

81 source = deprecated_aliases[name] 

82 except KeyError: 

83 pass 

84 else: 

85 warnings.warn( 

86 f"{package}.{name} is deprecated", 

87 DeprecationWarning, 

88 stacklevel=2, 

89 ) 

90 return import_name(name, source, namespace) 

91 

92 raise AttributeError(f"module {package!r} has no attribute {name!r}") 

93 

94 namespace["__getattr__"] = __getattr__ 

95 

96 def __dir__() -> Iterable[str]: 

97 return sorted(namespace_set | aliases_set | deprecated_aliases_set) 

98 

99 namespace["__dir__"] = __dir__