Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/libcst/codemod/_visitor.py: 51%
35 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#
6from typing import Mapping
8import libcst as cst
9from libcst import MetadataDependent
10from libcst.codemod._codemod import Codemod
11from libcst.codemod._context import CodemodContext
12from libcst.matchers import MatcherDecoratableTransformer, MatcherDecoratableVisitor
13from libcst.metadata import ProviderT
16class ContextAwareTransformer(Codemod, MatcherDecoratableTransformer):
17 """
18 A transformer which visits using LibCST. Allows visitor-based mutation of a tree.
19 Classes wishing to do arbitrary non-visitor-based mutation on a tree should
20 instead subclass from :class:`Codemod` and implement
21 :meth:`~Codemod.transform_module_impl`. This is a subclass of
22 :class:`~libcst.matchers.MatcherDecoratableTransformer` so all features of matchers
23 as well as :class:`~libcst.CSTTransformer` are available to subclasses of this
24 class.
25 """
27 def __init__(self, context: CodemodContext) -> None:
28 Codemod.__init__(self, context)
29 MatcherDecoratableTransformer.__init__(self)
31 def transform_module_impl(self, tree: cst.Module) -> cst.Module:
32 return tree.visit(self)
35class ContextAwareVisitor(MatcherDecoratableVisitor, MetadataDependent):
36 """
37 A visitor which visits using LibCST. Allows visitor-based collecting of info
38 on a tree. All codemods which wish to implement an information collector should
39 subclass from this instead of directly from
40 :class:`~libcst.matchers.MatcherDecoratableVisitor` or :class:`~libcst.CSTVisitor`
41 since this provides access to the current codemod context. As a result, this
42 class allows access to metadata which was calculated in a parent
43 :class:`~libcst.codemod.Codemod` through the
44 :meth:`~libcst.MetadataDependent.get_metadata` method.
46 Note that you cannot directly run a :class:`~libcst.codemod.ContextAwareVisitor`
47 using :func:`~libcst.codemod.transform_module` because visitors by definition
48 do not transform trees. However, you can instantiate a
49 :class:`~libcst.codemod.ContextAwareVisitor` inside a codemod and pass it to the
50 :class:`~libcst.CSTNode.visit` method on any node in order to run information
51 gathering with metadata and context support.
53 Remember that a :class:`~libcst.codemod.ContextAwareVisitor` is a subclass of
54 :class:`~libcst.MetadataDependent`, meaning that you still need to declare
55 your metadata dependencies with
56 :attr:`~libcst.MetadataDependent.METADATA_DEPENDENCIES` before you can retrieve
57 metadata using :meth:`~libcst.MetadataDependent.get_metadata`, even if the parent
58 codemod has listed its own metadata dependencies. Note also that the dependencies
59 listed on this class must be a strict subset of the dependencies listed in the
60 parent codemod.
61 """
63 def __init__(self, context: CodemodContext) -> None:
64 MetadataDependent.__init__(self)
65 MatcherDecoratableVisitor.__init__(self)
66 self.context = context
68 dependencies = self.get_inherited_dependencies()
69 if dependencies:
70 wrapper = self.context.wrapper
71 if wrapper is None:
72 raise Exception(
73 f"Attempting to instantiate {self.__class__.__name__} outside of "
74 + "an active transform. This means that metadata hasn't been "
75 + "calculated and we cannot successfully create this visitor."
76 )
77 for dep in dependencies:
78 if dep not in wrapper._metadata:
79 raise Exception(
80 f"Attempting to access metadata {dep.__name__} that was not a "
81 + "declared dependency of parent transform! This means it is "
82 + "not possible to compute this value. Please ensure that all "
83 + f"parent transforms of {self.__class__.__name__} declare "
84 + f"{dep.__name__} as a metadata dependency."
85 )
86 self.metadata: Mapping[ProviderT, Mapping[cst.CSTNode, object]] = {
87 dep: wrapper._metadata[dep] for dep in dependencies
88 }
90 def warn(self, warning: str) -> None:
91 """
92 Emit a warning that is displayed to the user who has invoked this codemod.
93 """
94 self.context.warnings.append(warning)
96 @property
97 def module(self) -> cst.Module:
98 """
99 Reference to the currently-traversed module. Note that this is only available
100 during a transform itself.
101 """
102 module = self.context.module
103 if module is None:
104 raise Exception(
105 f"Attempted access of {self.__class__.__name__}.module outside of "
106 + "transform_module()."
107 )
108 return module