77from types import MappingProxyType
88from typing import (
99 Callable ,
10- cast ,
1110 Generic ,
1211 List ,
1312 Mapping ,
1615 Type ,
1716 TYPE_CHECKING ,
1817 TypeVar ,
18+ Union ,
1919)
2020
2121from libcst ._batched_visitor import BatchableCSTVisitor
2222from libcst ._metadata_dependent import (
2323 _T as _MetadataT ,
2424 _UNDEFINED_DEFAULT ,
25+ LazyValue ,
2526 MetadataDependent ,
2627)
2728from libcst ._visitors import CSTVisitor
3637# BaseMetadataProvider[int] would be a subtype of BaseMetadataProvider[object], so the
3738# typevar is covariant.
3839_ProvidedMetadataT = TypeVar ("_ProvidedMetadataT" , covariant = True )
40+ MaybeLazyMetadataT = Union [LazyValue [_ProvidedMetadataT ], _ProvidedMetadataT ]
3941
4042
4143# We can't use an ABCMeta here, because of metaclass conflicts
@@ -52,16 +54,16 @@ class BaseMetadataProvider(MetadataDependent, Generic[_ProvidedMetadataT]):
5254 #
5355 # N.B. This has some typing variance problems. See `set_metadata` for an
5456 # explanation.
55- _computed : MutableMapping ["CSTNode" , _ProvidedMetadataT ]
57+ _computed : MutableMapping ["CSTNode" , MaybeLazyMetadataT ]
5658
57- #: Implement gen_cache to indicate the matadata provider depends on cache from external
59+ #: Implement gen_cache to indicate the metadata provider depends on cache from external
5860 #: system. This function will be called by :class:`~libcst.metadata.FullRepoManager`
5961 #: to compute required cache object per file path.
6062 gen_cache : Optional [Callable [[Path , List [str ], int ], Mapping [str , object ]]] = None
6163
6264 def __init__ (self , cache : object = None ) -> None :
6365 super ().__init__ ()
64- self ._computed = {}
66+ self ._computed : MutableMapping [ "CSTNode" , MaybeLazyMetadataT ] = {}
6567 if self .gen_cache and cache is None :
6668 # The metadata provider implementation is responsible to store and use cache.
6769 raise Exception (
@@ -71,7 +73,7 @@ def __init__(self, cache: object = None) -> None:
7173
7274 def _gen (
7375 self , wrapper : "MetadataWrapper"
74- ) -> Mapping ["CSTNode" , _ProvidedMetadataT ]:
76+ ) -> Mapping ["CSTNode" , MaybeLazyMetadataT ]:
7577 """
7678 Resolves and returns metadata mapping for the module in ``wrapper``.
7779
@@ -93,11 +95,7 @@ def _gen_impl(self, module: "Module") -> None:
9395 """
9496 ...
9597
96- # pyre-ignore[46]: The covariant `value` isn't type-safe because we write it to
97- # pyre: `self._computed`, however we assume that only one subclass in the MRO chain
98- # pyre: will ever call `set_metadata`, so it's okay for our purposes. There's no
99- # pyre: sane way to redesign this API so that it doesn't have this problem.
100- def set_metadata (self , node : "CSTNode" , value : _ProvidedMetadataT ) -> None :
98+ def set_metadata (self , node : "CSTNode" , value : MaybeLazyMetadataT ) -> None :
10199 """
102100 Record a metadata value ``value`` for ``node``.
103101 """
@@ -107,7 +105,9 @@ def get_metadata(
107105 self ,
108106 key : Type ["BaseMetadataProvider[_MetadataT]" ],
109107 node : "CSTNode" ,
110- default : _MetadataT = _UNDEFINED_DEFAULT ,
108+ default : Union [
109+ MaybeLazyMetadataT , Type [_UNDEFINED_DEFAULT ]
110+ ] = _UNDEFINED_DEFAULT ,
111111 ) -> _MetadataT :
112112 """
113113 The same method as :func:`~libcst.MetadataDependent.get_metadata` except
@@ -116,9 +116,12 @@ def get_metadata(
116116 """
117117 if key is type (self ):
118118 if default is not _UNDEFINED_DEFAULT :
119- return cast ( _MetadataT , self ._computed .get (node , default ) )
119+ ret = self ._computed .get (node , default )
120120 else :
121- return cast (_MetadataT , self ._computed [node ])
121+ ret = self ._computed [node ]
122+ if isinstance (ret , LazyValue ):
123+ return ret ()
124+ return ret
122125
123126 return super ().get_metadata (key , node , default )
124127
0 commit comments