1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
|
import itertools
import typing
from collections.abc import Iterable
import pytest
from mypy.nodes import Node
from refurb.settings import Settings
from refurb.types import Checks
from refurb.visitor import METHOD_NODE_MAPPINGS, RefurbVisitor
from refurb.visitor.mapping import VisitorNodeTypeMap
from .mypy_visitor import get_mypy_visitor_mapping
@pytest.fixture()
def dummy_visitor() -> RefurbVisitor:
"""
This fixture provides a RefurbVisitor instance with a visit method for each
possible node, but no checks to run.
This forces method generation but calling the methods does nothing.
"""
checks = Checks(list, {ty: [] for ty in METHOD_NODE_MAPPINGS.values()})
return RefurbVisitor(checks, Settings())
def get_visit_methods(
visitor: RefurbVisitor,
) -> Iterable[tuple[str, type[Node]]]:
"""
Find visitor methods in the instance (those that have been generated in
__init__) and in the class' __dict__ (the ones that are overridden
directly in the class).
Not using inspect.getmembers because that goes too deep into the parents
and that would deafeat the purpose of this, which is testing that the
methods are defined in the RefurbVisitor.
"""
method_sources = itertools.chain(
[
(method_name, getattr(visitor, method_name))
for method_name in dir(visitor)
if hasattr(visitor, method_name)
],
visitor.__class__.__dict__.items(),
)
for method_name, method in method_sources:
if callable(method) and method_name.startswith("visit_"):
yield method_name, method
def test_visitor_generation(dummy_visitor: RefurbVisitor) -> None:
"""
Ensure the visitor creates all expected methods with the right types (The
ones listed in refurb.visitor.METHOD_NODE_MAPPINGS).
"""
visitor_mappings: VisitorNodeTypeMap = {}
for method_name, method in get_visit_methods(dummy_visitor):
method_types = typing.get_type_hints(method)
assert "o" in method_types, f"No 'o' parameter in method {method_name}"
node_type = method_types["o"]
visitor_mappings[method_name] = node_type
assert visitor_mappings == METHOD_NODE_MAPPINGS
def test_mypy_consistence() -> None:
"""
Ensure the visitor method name to node type mappings used in refurb are
in sync with the ones of mypy.
This is meant as a failsafe, especially when the mypy dependency is
upgraded.
If this fails, review the mappings in refurb.visitor.METHOD_NODE_MAPPINGS.
"""
mypy_visitor_mapping = get_mypy_visitor_mapping()
assert mypy_visitor_mapping == METHOD_NODE_MAPPINGS
|