Skip to content

Commit

Permalink
Delayed initialization of matchers
Browse files Browse the repository at this point in the history
To support multiprocessing on Windows/macOS
Issue #1181
  • Loading branch information
kiri11 committed Sep 3, 2024
1 parent 9834694 commit 3053edf
Showing 1 changed file with 15 additions and 17 deletions.
32 changes: 15 additions & 17 deletions libcst/matchers/_visitors.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,17 +265,23 @@ def _check_types(
)


def _gather_matchers(obj: object) -> Set[BaseMatcherNode]:
visit_matchers: Set[BaseMatcherNode] = set()
def _gather_matchers(obj: object) -> Dict[BaseMatcherNode, Optional[cst.CSTNode]]:
"""
Set of gating matchers that we need to track and evaluate. We use these
in conjunction with the call_if_inside and call_if_not_inside decorators
to determine whether to call a visit/leave function.
"""

visit_matchers: Dict[BaseMatcherNode, Optional[cst.CSTNode]] = {}

for func in dir(obj):
try:
for matcher in getattr(getattr(obj, func), VISIT_POSITIVE_MATCHER_ATTR, []):
visit_matchers.add(cast(BaseMatcherNode, matcher))
visit_matchers[cast(BaseMatcherNode, matcher)] = None
for matcher in getattr(getattr(obj, func), VISIT_NEGATIVE_MATCHER_ATTR, []):
visit_matchers.add(cast(BaseMatcherNode, matcher))
visit_matchers[cast(BaseMatcherNode, matcher)] = None
except Exception:
# This could be a caculated property, and calling getattr() evaluates it.
# This could be a calculated property, and calling getattr() evaluates it.
# We have no control over the implementation detail, so if it raises, we
# should not crash.
pass
Expand Down Expand Up @@ -366,6 +372,8 @@ def _visit_matchers(
node: cst.CSTNode,
metadata_resolver: cst.MetadataDependent,
) -> Dict[BaseMatcherNode, Optional[cst.CSTNode]]:
# Populate matchers dict if it's empty
matchers = matchers or _gather_matchers(metadata_resolver)
new_matchers: Dict[BaseMatcherNode, Optional[cst.CSTNode]] = {}
for matcher, existing_node in matchers.items():
# We don't care about visiting matchers that are already true.
Expand Down Expand Up @@ -448,12 +456,7 @@ class MatcherDecoratableTransformer(CSTTransformer):

def __init__(self) -> None:
CSTTransformer.__init__(self)
# List of gating matchers that we need to track and evaluate. We use these
# in conjuction with the call_if_inside and call_if_not_inside decorators
# to determine whether or not to call a visit/leave function.
self._matchers: Dict[BaseMatcherNode, Optional[cst.CSTNode]] = {
m: None for m in _gather_matchers(self)
}
self._matchers: Dict[BaseMatcherNode, Optional[cst.CSTNode]] = {}
# Mapping of matchers to functions. If in the course of visiting the tree,
# a node matches one of these matchers, the corresponding function will be
# called as if it was a visit_* method.
Expand Down Expand Up @@ -660,12 +663,7 @@ class MatcherDecoratableVisitor(CSTVisitor):

def __init__(self) -> None:
CSTVisitor.__init__(self)
# List of gating matchers that we need to track and evaluate. We use these
# in conjuction with the call_if_inside and call_if_not_inside decorators
# to determine whether or not to call a visit/leave function.
self._matchers: Dict[BaseMatcherNode, Optional[cst.CSTNode]] = {
m: None for m in _gather_matchers(self)
}
self._matchers: Dict[BaseMatcherNode, Optional[cst.CSTNode]] = {}
# Mapping of matchers to functions. If in the course of visiting the tree,
# a node matches one of these matchers, the corresponding function will be
# called as if it was a visit_* method.
Expand Down

0 comments on commit 3053edf

Please sign in to comment.