Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/libcst/_metadata_dependent.py: 65%

51 statements  

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

1# Copyright (c) Meta Platforms, Inc. and affiliates. 

2# 

3# This source code is licensed under the MIT license found in the 

4# LICENSE file in the root directory of this source tree. 

5# 

6import inspect 

7from abc import ABC 

8from contextlib import contextmanager 

9from typing import ( 

10 Callable, 

11 cast, 

12 ClassVar, 

13 Collection, 

14 Generic, 

15 Iterator, 

16 Mapping, 

17 Type, 

18 TYPE_CHECKING, 

19 TypeVar, 

20 Union, 

21) 

22 

23if TYPE_CHECKING: 

24 # Circular dependency for typing reasons only 

25 from libcst._nodes.base import CSTNode # noqa: F401 

26 from libcst.metadata.base_provider import ( # noqa: F401 

27 BaseMetadataProvider, 

28 ProviderT, 

29 ) 

30 from libcst.metadata.wrapper import MetadataWrapper # noqa: F401 

31 

32 

33_T = TypeVar("_T") 

34 

35 

36class _UNDEFINED_DEFAULT: 

37 pass 

38 

39 

40class LazyValue(Generic[_T]): 

41 """ 

42 The class for implementing a lazy metadata loading mechanism that improves the 

43 performance when retriving expensive metadata (e.g., qualified names). Providers 

44 including :class:`~libcst.metadata.QualifiedNameProvider` use this class to load 

45 the metadata of a certain node lazily when calling 

46 :func:`~libcst.MetadataDependent.get_metadata`. 

47 """ 

48 

49 def __init__(self, callable: Callable[[], _T]) -> None: 

50 self.callable = callable 

51 self.return_value: Union[_T, Type[_UNDEFINED_DEFAULT]] = _UNDEFINED_DEFAULT 

52 

53 def __call__(self) -> _T: 

54 if self.return_value is _UNDEFINED_DEFAULT: 

55 self.return_value = self.callable() 

56 return cast(_T, self.return_value) 

57 

58 

59class MetadataDependent(ABC): 

60 """ 

61 The low-level base class for all classes that declare required metadata 

62 dependencies. :class:`~libcst.CSTVisitor` and :class:`~libcst.CSTTransformer` 

63 extend this class. 

64 """ 

65 

66 #: A cached copy of metadata computed by :func:`~libcst.MetadataDependent.resolve`. 

67 #: Prefer using :func:`~libcst.MetadataDependent.get_metadata` over accessing 

68 #: this attribute directly. 

69 metadata: Mapping["ProviderT", Mapping["CSTNode", object]] 

70 

71 #: The set of metadata dependencies declared by this class. 

72 METADATA_DEPENDENCIES: ClassVar[Collection["ProviderT"]] = () 

73 

74 def __init__(self) -> None: 

75 self.metadata = {} 

76 

77 @classmethod 

78 def get_inherited_dependencies(cls) -> Collection["ProviderT"]: 

79 """ 

80 Returns all metadata dependencies declared by classes in the MRO of ``cls`` 

81 that subclass this class. 

82 

83 Recursively searches the MRO of the subclass for metadata dependencies. 

84 """ 

85 try: 

86 # pyre-fixme[16]: use a hidden attribute to cache the property 

87 return cls._INHERITED_METADATA_DEPENDENCIES_CACHE 

88 except AttributeError: 

89 dependencies = set() 

90 for c in inspect.getmro(cls): 

91 if issubclass(c, MetadataDependent): 

92 dependencies.update(c.METADATA_DEPENDENCIES) 

93 # pyre-fixme[16]: use a hidden attribute to cache the property 

94 cls._INHERITED_METADATA_DEPENDENCIES_CACHE = frozenset(dependencies) 

95 return cls._INHERITED_METADATA_DEPENDENCIES_CACHE 

96 

97 @contextmanager 

98 def resolve(self, wrapper: "MetadataWrapper") -> Iterator[None]: 

99 """ 

100 Context manager that resolves all metadata dependencies declared by 

101 ``self`` (using :func:`~libcst.MetadataDependent.get_inherited_dependencies`) 

102 on ``wrapper`` and caches it on ``self`` for use with 

103 :func:`~libcst.MetadataDependent.get_metadata`. 

104 

105 Upon exiting this context manager, the metadata cache on ``self`` is 

106 cleared. 

107 """ 

108 self.metadata = wrapper.resolve_many(self.get_inherited_dependencies()) 

109 yield 

110 self.metadata = {} 

111 

112 def get_metadata( 

113 self, 

114 key: Type["BaseMetadataProvider[_T]"], 

115 node: "CSTNode", 

116 default: _T = _UNDEFINED_DEFAULT, 

117 ) -> _T: 

118 """ 

119 Returns the metadata provided by the ``key`` if it is accessible from 

120 this visitor. Metadata is accessible in a subclass of this class if ``key`` 

121 is declared as a dependency by any class in the MRO of this class. 

122 """ 

123 if key not in self.get_inherited_dependencies(): 

124 raise KeyError( 

125 f"{key.__name__} is not declared as a dependency in {type(self).__name__}.METADATA_DEPENDENCIES." 

126 ) 

127 

128 if key not in self.metadata: 

129 raise KeyError( 

130 f"{key.__name__} is a dependency, but not set; did you forget a MetadataWrapper?" 

131 ) 

132 

133 if default is not _UNDEFINED_DEFAULT: 

134 value = self.metadata[key].get(node, default) 

135 else: 

136 value = self.metadata[key][node] 

137 if isinstance(value, LazyValue): 

138 value = value() 

139 return cast(_T, value)