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

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

43 statements  

1from __future__ import annotations 

2 

3import warnings 

4from collections.abc import Iterable 

5from typing import Any 

6 

7 

8__all__ = ["lazy_import"] 

9 

10 

11def import_name(name: str, source: str, namespace: dict[str, Any]) -> Any: 

12 """ 

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

14 

15 There are two use cases: 

16 

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

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

19 

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

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

22 

23 """ 

24 level = 0 

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

26 level += 1 

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

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

29 return getattr(module, name) 

30 

31 

32def lazy_import( 

33 namespace: dict[str, Any], 

34 aliases: dict[str, str] | None = None, 

35 deprecated_aliases: dict[str, str] | None = None, 

36) -> None: 

37 """ 

38 Provide lazy, module-level imports. 

39 

40 Typical use:: 

41 

42 __getattr__, __dir__ = lazy_import( 

43 globals(), 

44 aliases={ 

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

46 ... 

47 }, 

48 deprecated_aliases={ 

49 ..., 

50 } 

51 ) 

52 

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

54 

55 """ 

56 if aliases is None: 

57 aliases = {} 

58 if deprecated_aliases is None: 

59 deprecated_aliases = {} 

60 

61 namespace_set = set(namespace) 

62 aliases_set = set(aliases) 

63 deprecated_aliases_set = set(deprecated_aliases) 

64 

65 assert not namespace_set & aliases_set, "namespace conflict" 

66 assert not namespace_set & deprecated_aliases_set, "namespace conflict" 

67 assert not aliases_set & deprecated_aliases_set, "namespace conflict" 

68 

69 package = namespace["__name__"] 

70 

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

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

73 try: 

74 source = aliases[name] 

75 except KeyError: 

76 pass 

77 else: 

78 return import_name(name, source, namespace) 

79 

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

81 try: 

82 source = deprecated_aliases[name] 

83 except KeyError: 

84 pass 

85 else: 

86 warnings.warn( 

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

88 DeprecationWarning, 

89 stacklevel=2, 

90 ) 

91 return import_name(name, source, namespace) 

92 

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

94 

95 namespace["__getattr__"] = __getattr__ 

96 

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

98 return sorted(namespace_set | aliases_set | deprecated_aliases_set) 

99 

100 namespace["__dir__"] = __dir__