1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
|
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
from pathlib import Path
from types import MappingProxyType
from typing import (
Generic,
List,
Mapping,
MutableMapping,
Optional,
Protocol,
Type,
TYPE_CHECKING,
TypeVar,
Union,
)
from libcst._batched_visitor import BatchableCSTVisitor
from libcst._metadata_dependent import (
_T as _MetadataT,
_UNDEFINED_DEFAULT,
LazyValue,
MetadataDependent,
)
from libcst._visitors import CSTVisitor
if TYPE_CHECKING:
from libcst._nodes.base import CSTNode
from libcst._nodes.module import _ModuleSelfT as _ModuleT, Module
from libcst.metadata.wrapper import MetadataWrapper
ProviderT = Type["BaseMetadataProvider[object]"]
# BaseMetadataProvider[int] would be a subtype of BaseMetadataProvider[object], so the
# typevar is covariant.
_ProvidedMetadataT = TypeVar("_ProvidedMetadataT", covariant=True)
MaybeLazyMetadataT = Union[LazyValue[_ProvidedMetadataT], _ProvidedMetadataT]
class GenCacheMethod(Protocol):
def __call__(
self,
root_path: Path,
paths: List[str],
*,
timeout: Optional[int] = None,
use_pyproject_toml: bool = False,
) -> Mapping[str, object]: ...
# We can't use an ABCMeta here, because of metaclass conflicts
class BaseMetadataProvider(MetadataDependent, Generic[_ProvidedMetadataT]):
"""
The low-level base class for all metadata providers. This class should be
extended for metadata providers that are not visitor-based.
This class is generic. A subclass of ``BaseMetadataProvider[T]`` will
provider metadata of type ``T``.
"""
#: Cache of metadata computed by this provider
#
# N.B. This has some typing variance problems. See `set_metadata` for an
# explanation.
_computed: MutableMapping["CSTNode", MaybeLazyMetadataT]
#: Implement gen_cache to indicate the metadata provider depends on cache from external
#: system. This function will be called by :class:`~libcst.metadata.FullRepoManager`
#: to compute required cache object per file path.
gen_cache: Optional[GenCacheMethod] = None
def __init__(self, cache: object = None) -> None:
super().__init__()
self._computed: MutableMapping["CSTNode", MaybeLazyMetadataT] = {}
if self.gen_cache and cache is None:
# The metadata provider implementation is responsible to store and use cache.
raise ValueError(
f"Cache is required for initializing {self.__class__.__name__}."
)
self.cache = cache
def _gen(
self, wrapper: "MetadataWrapper"
) -> Mapping["CSTNode", MaybeLazyMetadataT]:
"""
Resolves and returns metadata mapping for the module in ``wrapper``.
This method is used by the metadata resolver and should not be called
directly.
"""
self._computed = {}
# Resolve metadata dependencies for this provider
with self.resolve(wrapper):
self._gen_impl(wrapper.module)
# Copy into a mapping proxy to ensure immutability
return MappingProxyType(dict(self._computed))
def _gen_impl(self, module: "Module") -> None:
"""
Override this method with a metadata computation implementation.
"""
...
def set_metadata(self, node: "CSTNode", value: MaybeLazyMetadataT) -> None:
"""
Record a metadata value ``value`` for ``node``.
"""
self._computed[node] = value
def get_metadata(
self,
key: Type["BaseMetadataProvider[_MetadataT]"],
node: "CSTNode",
default: Union[
MaybeLazyMetadataT, Type[_UNDEFINED_DEFAULT]
] = _UNDEFINED_DEFAULT,
) -> _MetadataT:
"""
The same method as :func:`~libcst.MetadataDependent.get_metadata` except
metadata is accessed from ``self._computed`` in addition to ``self.metadata``.
See :func:`~libcst.MetadataDependent.get_metadata`.
"""
if key is type(self):
if default is not _UNDEFINED_DEFAULT:
ret = self._computed.get(node, default)
else:
ret = self._computed[node]
if isinstance(ret, LazyValue):
return ret()
return ret
return super().get_metadata(key, node, default)
class VisitorMetadataProvider(CSTVisitor, BaseMetadataProvider[_ProvidedMetadataT]):
"""
The low-level base class for all non-batchable visitor-based metadata
providers. Inherits from :class:`~libcst.CSTVisitor`.
This class is generic. A subclass of ``VisitorMetadataProvider[T]`` will
provider metadata of type ``T``.
"""
def _gen_impl(self, module: "_ModuleT") -> None:
module.visit(self)
class BatchableMetadataProvider(
BatchableCSTVisitor, BaseMetadataProvider[_ProvidedMetadataT]
):
"""
The low-level base class for all batchable visitor-based metadata providers.
Batchable providers should be preferred when possible as they are more
efficient to run compared to non-batchable visitor-based providers.
Inherits from :class:`~libcst.BatchableCSTVisitor`.
This class is generic. A subclass of ``BatchableMetadataProvider[T]`` will
provider metadata of type ``T``.
"""
def _gen_impl(self, module: "Module") -> None:
"""
Batchables providers are resolved through _gen_batchable] so no
implementation should be provided in _gen_impl.
"""
pass
|