Source code for libcst.metadata.base_provider

# 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 (
    Callable,
    Generic,
    List,
    Mapping,
    MutableMapping,
    Optional,
    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]


# We can't use an ABCMeta here, because of metaclass conflicts
[docs] 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[Callable[[Path, List[str], int], Mapping[str, object]]] = 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 Exception( 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. """ ...
[docs] def set_metadata(self, node: "CSTNode", value: MaybeLazyMetadataT) -> None: """ Record a metadata value ``value`` for ``node``. """ self._computed[node] = value
[docs] 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)
[docs] 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)
[docs] 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