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
« 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)
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
33_T = TypeVar("_T")
36class _UNDEFINED_DEFAULT:
37 pass
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 """
49 def __init__(self, callable: Callable[[], _T]) -> None:
50 self.callable = callable
51 self.return_value: Union[_T, Type[_UNDEFINED_DEFAULT]] = _UNDEFINED_DEFAULT
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)
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 """
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]]
71 #: The set of metadata dependencies declared by this class.
72 METADATA_DEPENDENCIES: ClassVar[Collection["ProviderT"]] = ()
74 def __init__(self) -> None:
75 self.metadata = {}
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.
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
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`.
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 = {}
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 )
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 )
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)