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 4, 2024
1 parent b53f3f5 commit da175a9
Showing 1 changed file with 34 additions and 26 deletions.
60 changes: 34 additions & 26 deletions libcst/matchers/_visitors.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,20 +269,22 @@ def _check_types(
)


def _gather_matchers(obj: object) -> Set[BaseMatcherNode]:
visit_matchers: Set[BaseMatcherNode] = set()

for func in dir(obj):
try:
for matcher in getattr(getattr(obj, func), VISIT_POSITIVE_MATCHER_ATTR, []):
visit_matchers.add(cast(BaseMatcherNode, matcher))
for matcher in getattr(getattr(obj, func), VISIT_NEGATIVE_MATCHER_ATTR, []):
visit_matchers.add(cast(BaseMatcherNode, matcher))
except Exception:
# This could be a caculated property, and calling getattr() evaluates it.
# We have no control over the implementation detail, so if it raises, we
# should not crash.
pass
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 attr_name in dir(obj):
if not is_property(obj, attr_name):
func = getattr(obj, attr_name)
for matcher in getattr(func, VISIT_POSITIVE_MATCHER_ATTR, []):
visit_matchers[cast(BaseMatcherNode, matcher)] = None
for matcher in getattr(func, VISIT_NEGATIVE_MATCHER_ATTR, []):
visit_matchers[cast(BaseMatcherNode, matcher)] = None

return visit_matchers

Expand Down Expand Up @@ -444,12 +446,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: Optional[Dict[BaseMatcherNode, Optional[cst.CSTNode]]] = None
# 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 @@ -482,6 +479,14 @@ def __init__(self) -> None:
expected_none_return=False,
)

@property
def _matchers(self) -> Dict[BaseMatcherNode, Optional[cst.CSTNode]]:
return self.__matchers or _gather_matchers(self)

@_matchers.setter
def _matchers(self, value) -> None:
self.__matchers = value

def on_visit(self, node: cst.CSTNode) -> bool:
# First, evaluate any matchers that we have which we are not inside already.
self._matchers = _visit_matchers(self._matchers, node, self)
Expand Down Expand Up @@ -656,12 +661,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: Optional[Dict[BaseMatcherNode, Optional[cst.CSTNode]]] = None
# 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 @@ -689,6 +689,14 @@ def __init__(self) -> None:
expected_none_return=True,
)

@property
def _matchers(self) -> Dict[BaseMatcherNode, Optional[cst.CSTNode]]:
return self.__matchers or _gather_matchers(self)

@_matchers.setter
def _matchers(self, value) -> None:
self.__matchers = value

def on_visit(self, node: cst.CSTNode) -> bool:
# First, evaluate any matchers that we have which we are not inside already.
self._matchers = _visit_matchers(self._matchers, node, self)
Expand Down

0 comments on commit da175a9

Please sign in to comment.