Ejemplo n.º 1
0
    def __extract_names_multi_assign(self, elements):
        # Add self vars. in tuple assignments, e.g. self.x, self.y = 1, 2
        # Adds variables in tuple(s) in multiple assignments, e.g. a, (b, c) = 1, (2, 3)
        names: List[cst.Name] = []
        i = 0
        while i < len(elements):
            if match.matches(
                    elements[i],
                    match.Element(value=match.Name(value=match.DoNotCare()))):
                names.append(elements[i].value)
            elif match.matches(
                    elements[i],
                    match.Element(value=match.Attribute(attr=match.Name(
                        value=match.DoNotCare())))):
                names.append(elements[i].value)
            elif match.matches(
                    elements[i],
                    match.Element(value=match.Tuple(
                        elements=match.DoNotCare()))):
                elements.extend(
                    match.findall(
                        elements[i].value,
                        match.Element(value=match.OneOf(
                            match.Attribute(attr=match.Name(
                                value=match.DoNotCare())),
                            match.Name(value=match.DoNotCare())))))
            i += 1

        return names
Ejemplo n.º 2
0
class HttpRequestXReadLinesTransformer(BaseDjCodemodTransformer):
    """Replace `HttpRequest.xreadlines()` by iterating over the request."""

    deprecated_in = DJANGO_2_0
    removed_in = DJANGO_3_0

    # This should be conservative and only apply changes to:
    # - variables called `request`/`req`
    # - `request`/`req` attributes (e.g `self.request`/`view.req`...)
    matcher = m.Call(
        func=m.Attribute(
            value=m.OneOf(
                m.Name(value="request"),
                m.Name(value="req"),
                m.Attribute(attr=m.Name(value="request")),
                m.Attribute(attr=m.Name(value="req")),
            ),
            attr=m.Name(value="xreadlines"),
        )
    )

    def leave_Call(self, original_node: Call, updated_node: Call) -> BaseExpression:
        if not isinstance(updated_node.func, Attribute):
            # A bit redundant with matcher below,
            # but this is to make type checker happy
            return super().leave_Call(original_node, updated_node)
        if m.matches(updated_node, self.matcher):
            return updated_node.func.value
        return super().leave_Call(original_node, updated_node)
Ejemplo n.º 3
0
 def _test_import_from(self, node: ImportFrom) -> bool:
     return m.matches(
         node,
         m.ImportFrom(module=m.Attribute(
             value=m.Attribute(value=m.Name("django"),
                               attr=m.Name(value="utils")),
             attr=m.Name("translation"),
         ), ),
     )
Ejemplo n.º 4
0
    def test_extract_predicates(self) -> None:
        expression = cst.parse_expression("a + b[c], d(e, f * g)")
        nodes = m.extract(
            expression,
            m.Tuple(elements=[
                m.Element(
                    m.BinaryOperation(
                        left=m.SaveMatchedNode(m.Name(), "left"))),
                m.Element(
                    m.Call(func=m.SaveMatchedNode(m.Name(), "func")
                           | m.SaveMatchedNode(m.Attribute(), "attr"))),
            ]),
        )
        extracted_node_left = cst.ensure_type(
            cst.ensure_type(expression, cst.Tuple).elements[0].value,
            cst.BinaryOperation,
        ).left
        extracted_node_func = cst.ensure_type(
            cst.ensure_type(expression, cst.Tuple).elements[1].value,
            cst.Call).func
        self.assertEqual(nodes, {
            "left": extracted_node_left,
            "func": extracted_node_func
        })

        expression = cst.parse_expression("a + b[c], d.z(e, f * g)")
        nodes = m.extract(
            expression,
            m.Tuple(elements=[
                m.Element(
                    m.BinaryOperation(
                        left=m.SaveMatchedNode(m.Name(), "left"))),
                m.Element(
                    m.Call(func=m.SaveMatchedNode(m.Name(), "func")
                           | m.SaveMatchedNode(m.Attribute(), "attr"))),
            ]),
        )
        extracted_node_left = cst.ensure_type(
            cst.ensure_type(expression, cst.Tuple).elements[0].value,
            cst.BinaryOperation,
        ).left
        extracted_node_attr = cst.ensure_type(
            cst.ensure_type(expression, cst.Tuple).elements[1].value,
            cst.Call).func
        self.assertEqual(nodes, {
            "left": extracted_node_left,
            "attr": extracted_node_attr
        })
Ejemplo n.º 5
0
    def obf_function_name(self, func: cst.Call, updated_node):

        func_name = func.func

        # Обфускация имени функции
        if m.matches(
                func_name,
                m.Attribute()) and self.change_methods and self.can_rename(
                    func_name.attr.value, 'cf'):
            func_name = cst.ensure_type(func_name, cst.Attribute)
            func_name = func_name.with_changes(
                attr=self.get_new_cst_name(func_name.attr))
            updated_node = updated_node.with_changes(func=func_name)

        elif m.matches(func_name, m.Name()) and (
                self.change_functions
                and self.can_rename(func_name.value, 'f') or
                self.change_classes and self.can_rename(func_name.value, 'c')):
            func_name = cst.ensure_type(func_name, cst.Name)
            func_name = self.get_new_cst_name(func_name.value)
            updated_node = updated_node.with_changes(func=func_name)

        else:
            pass

        return updated_node
Ejemplo n.º 6
0
 def leave_AnnAssign(
     self, original_node: cst.AnnAssign, updated_node: cst.AnnAssign
 ) -> Union[cst.BaseSmallStatement, cst.RemovalSentinel]:
     # It handles a special case where a type-annotated variable has not initialized, e.g. foo: str
     # This case will be converted to foo = ... so that nodes traversal won't encounter exceptions later on
     if match.matches(
             original_node,
             match.AnnAssign(
                 target=match.Name(value=match.DoNotCare()),
                 annotation=match.Annotation(annotation=match.DoNotCare()),
                 value=None)):
         updated_node = cst.Assign(
             targets=[cst.AssignTarget(target=original_node.target)],
             value=cst.Ellipsis())
     # Handles type-annotated class attributes that has not been initialized, e.g. self.foo: str
     elif match.matches(
             original_node,
             match.AnnAssign(
                 target=match.Attribute(value=match.DoNotCare()),
                 annotation=match.Annotation(annotation=match.DoNotCare()),
                 value=None)):
         updated_node = cst.Assign(
             targets=[cst.AssignTarget(target=original_node.target)],
             value=cst.Ellipsis())
     else:
         updated_node = cst.Assign(
             targets=[cst.AssignTarget(target=original_node.target)],
             value=original_node.value)
     return updated_node
Ejemplo n.º 7
0
 def _get_async_expr_replacement(
         self, node: cst.CSTNode) -> Optional[cst.CSTNode]:
     if m.matches(node, m.Call()):
         node = cast(cst.Call, node)
         return self._get_async_call_replacement(node)
     elif m.matches(node, m.Attribute()):
         node = cast(cst.Attribute, node)
         return self._get_async_attr_replacement(node)
     elif m.matches(node, m.UnaryOperation(operator=m.Not())):
         node = cast(cst.UnaryOperation, node)
         replacement_expression = self._get_async_expr_replacement(
             node.expression)
         if replacement_expression is not None:
             return node.with_changes(expression=replacement_expression)
     elif m.matches(node, m.BooleanOperation()):
         node = cast(cst.BooleanOperation, node)
         maybe_left = self._get_async_expr_replacement(node.left)
         maybe_right = self._get_async_expr_replacement(node.right)
         if maybe_left is not None or maybe_right is not None:
             left_replacement = maybe_left if maybe_left is not None else node.left
             right_replacement = (maybe_right if maybe_right is not None
                                  else node.right)
             return node.with_changes(left=left_replacement,
                                      right=right_replacement)
     return None
Ejemplo n.º 8
0
 def visit_Call(self, node: cst.Call) -> None:
     if m.matches(
             node,
             m.Call(func=m.Attribute(value=m.SimpleString(),
                                     attr=m.Name(value="format"))),
     ):
         self.report(node)
Ejemplo n.º 9
0
 def _func_name(self, func):
     if m.matches(func, m.Name()):
         return func.value
     elif m.matches(func, m.Attribute()):
         return func.attr.value
     else:
         return 'func'
Ejemplo n.º 10
0
class ConstantsRenameCommand(VisitorBasedCodemodCommand):

    DESCRIPTION: str = "Rename constants"

    matchers_map = {
        matchers.Attribute(value=matchers.Name(value="wx"),
                           attr=matchers.Name(value=name)): renamed
        for name, renamed in [
            ("WXK_PRIOR", "WXK_PAGEUP"),
            ("WXK_NEXT", "WXK_PAGEDOWN"),
            ("WXK_NUMPAD_PRIOR", "WXK_NUMPAD_PAGEUP"),
            ("WXK_NUMPAD_NEXT", "WXK_NUMPAD_PAGEDOWN"),
            ("OPEN", "FD_OPEN"),
            ("FILE_MUST_EXIST", "FD_FILE_MUST_EXIST"),
            ("TE_LINEWRAP", "TE_BESTWRAP"),
        ]
    }

    def leave_Attribute(self, original_node: cst.Attribute,
                        updated_node: cst.Attribute) -> cst.Attribute:
        for matcher, renamed in self.matchers_map.items():
            if matchers.matches(updated_node, matcher):
                return updated_node.with_changes(attr=cst.Name(value=renamed))

        return updated_node
Ejemplo n.º 11
0
 def visit_Assign(self, node: Assign) -> Optional[bool]:
     """Record variable name the `Library()` call is assigned to."""
     if self.library_call_matcher and m.matches(
         node,
         m.Assign(value=self.library_call_matcher),
     ):
         # Visiting a `register = template.Library()` statement
         # Get all names on the left side of the assignment
         target_names = (
             assign_target.target.value for assign_target in node.targets
         )
         # Build the decorator matchers to look out for
         target_matchers = (
             m.Decorator(
                 decorator=m.Attribute(
                     value=m.Name(name),
                     attr=m.Name("assignment_tag"),
                 )
             )
             for name in target_names
         )
         # The final matcher should match if any of the decorator matchers matches
         self.context.scratch[self.ctx_key_decorator_matcher] = m.OneOf(
             *target_matchers
         )
     return super().visit_Assign(node)
Ejemplo n.º 12
0
    def process_variable(self, node: Union[cst.BaseExpression,
                                           cst.BaseAssignTargetExpression]):

        if m.matches(node, m.Name()):
            node = cst.ensure_type(node, cst.Name)

            if self.class_stack and not self.function_stack:
                self.class_stack[-1].variables.append(node.value)
            else:
                self.info.variables.append(node.value)

        elif m.matches(node, m.Attribute()):
            node = cst.ensure_type(node, cst.Attribute)

            splitted_attributes = split_attribute(node)

            if splitted_attributes[
                    0] == 'self' and self.class_stack and self.function_stack and len(
                        splitted_attributes) > 1:
                self.class_stack[-1].variables.append(splitted_attributes[1])
            else:
                self.info.variables.append(splitted_attributes[0])

        elif m.matches(node, m.Tuple()):
            node = cst.ensure_type(node, cst.Tuple)
            for el in node.elements:
                self.process_variable(el.value)

        else:
            pass
Ejemplo n.º 13
0
    def new_obf_function_name(self, func: cst.Call):

        func_name = func.func

        # Обфускация имени функции
        if m.matches(func_name, m.Attribute()):
            func_name = cst.ensure_type(func_name, cst.Attribute)

            # Переименовывание имени
            if self.change_variables:
                func_name = func_name.with_changes(
                    value=self.obf_universal(func_name.value, 'v'))

            # Переименовывание метода
            if self.change_methods:
                func_name = func_name.with_changes(
                    attr=self.obf_universal(func_name.attr, 'cf'))

        elif m.matches(func_name, m.Name()):
            func_name = cst.ensure_type(func_name, cst.Name)
            if (self.change_functions
                    or self.change_classes) and self.can_rename(
                        func_name.value, 'c', 'f'):
                func_name = self.get_new_cst_name(func_name.value)

        else:
            pass

        func = func.with_changes(func=func_name)

        return func
Ejemplo n.º 14
0
 def handle_any_string(
         self, node: Union[cst.SimpleString,
                           cst.ConcatenatedString]) -> None:
     value = node.evaluated_value
     if value is None:
         return
     mod = cst.parse_module(value)
     extracted_nodes = m.extractall(
         mod,
         m.Name(
             value=m.SaveMatchedNode(m.DoNotCare(), "name"),
             metadata=m.MatchMetadataIfTrue(
                 cst.metadata.ParentNodeProvider,
                 lambda parent: not isinstance(parent, cst.Attribute),
             ),
         )
         | m.SaveMatchedNode(m.Attribute(), "attribute"),
         metadata_resolver=MetadataWrapper(mod, unsafe_skip_copy=True),
     )
     names = {
         cast(str, values["name"])
         for values in extracted_nodes if "name" in values
     } | {
         name
         for values in extracted_nodes if "attribute" in values
         for name, _ in cst.metadata.scope_provider._gen_dotted_names(
             cast(cst.Attribute, values["attribute"]))
     }
     self.names.update(names)
Ejemplo n.º 15
0
    def obf_function_args(self, func: cst.Call):

        new_args = []
        func_root = func.func
        func_name = ''

        if m.matches(func_root, m.Name()):
            func_name = cst.ensure_type(func_root, cst.Name).value
        elif m.matches(func_root, m.Attribute()):
            func_name = split_attribute(
                cst.ensure_type(func_root, cst.Attribute))[-1]

        if self.change_arguments or self.change_method_arguments:

            for arg in func.args:
                # Значения аргументов
                arg = arg.with_changes(value=self.obf_universal(arg.value))
                # Имена аргументов
                if arg.keyword is not None and self.can_rename_func_param(
                        arg.keyword.value, func_name):
                    new_keyword = self.get_new_cst_name(
                        arg.keyword) if arg.keyword is not None else None
                    arg = arg.with_changes(keyword=new_keyword)

                new_args.append(arg)

        func = func.with_changes(args=new_args)

        return func
Ejemplo n.º 16
0
 def __extract_variable_name_type(self, node: cst.AnnAssign):
     """
     Extracts a variable's identifier name and its type annotation
     """
     return match.extract(
         node,
         match.AnnAssign(  # Annotated Assignment
             target=match.OneOf(
                 match.Name(  # Variable name of assignment (only one)
                     value=match.SaveMatchedNode(  # Save result
                         match.MatchRegex(
                             r'(.)+'),  # Match any string literal
                         "name")),
                 # This extracts variables inside __init__ which typically starts with self (e.g. self.x:int=2)
                 match.Attribute(
                     value=match.Name(value=match.SaveMatchedNode(
                         match.MatchRegex(r'(.)+'),
                         "obj_name"  # Object name
                     )),
                     attr=match.Name(
                         match.SaveMatchedNode(match.MatchRegex(r'(.)+'),
                                               "name")),
                 )),
             annotation=match.SaveMatchedNode(  # Save result
                 match.DoNotCare(),  # Match any string literal
                 "type")))
Ejemplo n.º 17
0
    def leave_ClassDef(self, original_node: cst.ClassDef,
                       updated_node: cst.ClassDef):

        self.class_stack.pop()

        if not self.change_classes:
            return updated_node

        class_name = updated_node.name.value
        new_bases = []

        if self.can_rename(class_name, 'c'):
            updated_node = self.renamed(updated_node)

        for base in updated_node.bases:
            full_name = base.value

            if m.matches(full_name, m.Name()):
                full_name = cst.ensure_type(full_name, cst.Name)
                if self.can_rename(full_name.value, 'c'):
                    base = base.with_changes(
                        value=self.get_new_cst_name(full_name.value))
            elif m.matches(full_name, m.Attribute()):
                # TODO поддержка импортов
                pass
            else:
                pass

            new_bases.append(base)

        updated_node = updated_node.with_changes(bases=new_bases)

        return updated_node
Ejemplo n.º 18
0
def module_matcher(import_parts: Sequence) -> Union[m.BaseMatcherNode, m.DoNotCare]:
    """Build matcher for a module given sequence of import parts."""
    # If only one element, it is just a Name
    if len(import_parts) == 1:
        return m.Name(import_parts[0])
    *values, attr = import_parts
    value = module_matcher(values)
    return m.Attribute(value=value, attr=m.Name(attr))
Ejemplo n.º 19
0
 def visit_Attribute(self, node: cst.Attribute) -> None:
     # The attribute node can come through other context as well but we only care
     # about the ones coming from assignments.
     if self._assigntarget_counter > 0:
         # We only care about assignment attribute to *self*.
         if m.matches(node, m.Attribute(value=m.Name(value="self"))):
             self._validate_nodename(node, node.attr.value,
                                     NamingConvention.SNAKE_CASE)
Ejemplo n.º 20
0
 def _get_async_call_replacement(self,
                                 node: cst.Call) -> Optional[cst.CSTNode]:
     func = node.func
     if m.matches(func, m.Attribute()):
         func = cast(cst.Attribute, func)
         attr_func_replacement = self._get_async_attr_replacement(func)
         if attr_func_replacement is not None:
             return node.with_changes(func=attr_func_replacement)
     return self._get_awaitable_replacement(node)
Ejemplo n.º 21
0
def module_matcher(import_parts):
    *values, attr = import_parts
    if len(values) > 1:
        value = module_matcher(values)
    elif len(values) == 1:
        value = m.Name(values[0])
    else:
        value = None
    return m.Attribute(value=value, attr=m.Name(attr))
Ejemplo n.º 22
0
    def _make_name_or_attribute(
            parts: List[str]) -> Union[m.Name, m.Attribute]:
        if not parts:
            raise RuntimeError("Expected a non empty list of strings")

        # just a name, e.g. `coroutine`
        if len(parts) == 1:
            return m.Name(parts[0])

        # a name and attribute, e.g. `gen.coroutine`
        if len(parts) == 2:
            return m.Attribute(value=m.Name(parts[0]), attr=m.Name(parts[1]))

        # a complex attribute, e.g. `tornado.gen.coroutine`, we want to make
        # the attribute with value `tornado.gen` and attr `coroutine`
        value = _make_name_or_attribute(parts[:-1])
        attr = _make_name_or_attribute(parts[-1:])
        return m.Attribute(value=value, attr=attr)
Ejemplo n.º 23
0
    def leave_ImportFrom(
        self, original_node: ImportFrom, updated_node: ImportFrom
    ) -> Union[BaseSmallStatement, RemovalSentinel]:
        base_cls_matcher = []
        if m.matches(
            updated_node,
            m.ImportFrom(module=module_matcher(["django", "contrib", "admin"])),
        ):
            for imported_name in updated_node.names:
                if m.matches(
                    imported_name, m.ImportAlias(name=m.Name("TabularInline"))
                ):
                    base_cls_matcher.append(m.Arg(m.Name("TabularInline")))
                if m.matches(
                    imported_name, m.ImportAlias(name=m.Name("StackedInline"))
                ):
                    base_cls_matcher.append(m.Arg(m.Name("StackedInline")))
        if m.matches(
            updated_node,
            m.ImportFrom(module=module_matcher(["django", "contrib"])),
        ):
            for imported_name in updated_node.names:
                if m.matches(imported_name, m.ImportAlias(name=m.Name("admin"))):

                    base_cls_matcher.extend(
                        [
                            m.Arg(
                                m.Attribute(
                                    value=m.Name("admin"), attr=m.Name("TabularInline")
                                )
                            ),
                            m.Arg(
                                m.Attribute(
                                    value=m.Name("admin"), attr=m.Name("StackedInline")
                                )
                            ),
                        ]
                    )
        # Save valid matchers in the context
        if base_cls_matcher:
            self.context.scratch[self.ctx_key_base_cls_matcher] = m.OneOf(
                *base_cls_matcher
            )
        return super().leave_ImportFrom(original_node, updated_node)
Ejemplo n.º 24
0
class MakeModalCommand(VisitorBasedCodemodCommand):

    DESCRIPTION: str = "Replace built-in method MAkeModal with helper"

    method_matcher = matchers.FunctionDef(
        name=matchers.Name(value="MakeModal"),
        params=matchers.Parameters(params=[
            matchers.Param(name=matchers.Name(value="self")),
            matchers.ZeroOrMore()
        ]),
    )
    call_matcher = matchers.Call(
        func=matchers.Attribute(value=matchers.Name(value="self"),
                                attr=matchers.Name(value="MakeModal")))

    method_cst = cst.parse_statement(
        textwrap.dedent("""
            def MakeModal(self, modal=True):
                if modal and not hasattr(self, '_disabler'):
                    self._disabler = wx.WindowDisabler(self)
                if not modal and hasattr(self, '_disabler'):
                    del self._disabler
            """))

    def __init__(self, context: CodemodContext):
        super().__init__(context)

        self.stack: List[cst.ClassDef] = []

    def visit_ClassDef(self, node: cst.ClassDef) -> None:
        self.stack.append(node)

    def leave_ClassDef(self, original_node: cst.ClassDef,
                       updated_node: cst.ClassDef) -> cst.ClassDef:
        return self.stack.pop()

    def leave_Call(self, original_node: cst.Call,
                   updated_node: cst.Call) -> cst.Call:
        if matchers.matches(updated_node, self.call_matcher):
            # Search for MakeModal() method
            current_class = self.stack[-1]
            has_make_modal_method = False

            for method in current_class.body.body:
                if matchers.matches(method, self.method_matcher):
                    has_make_modal_method = True

            # If not, add it to the current class
            if not has_make_modal_method:
                current_class = current_class.with_changes(
                    body=current_class.body.with_changes(
                        body=[*current_class.body.body, self.method_cst]))

                self.stack[-1] = current_class

        return updated_node
    def _build_arg_class_matcher(self) -> Union[m.Attribute, m.Name]:
        matcher = m.Name(value=self.current_classes[0])

        # For nested classes, we need to match attributes, so we can target
        # `super(Foo.InnerFoo, self)` for example.
        if len(self.current_classes) > 1:
            for class_name in self.current_classes[1:]:
                matcher = m.Attribute(value=matcher, attr=m.Name(value=class_name))

        return matcher
Ejemplo n.º 26
0
class InlineHasAddPermissionsTransformer(ContextAwareTransformer):
    """Add the ``obj`` argument to ``InlineModelAdmin.has_add_permission()``."""

    context_key = "InlineHasAddPermissionsTransformer"
    base_cls_matcher = m.OneOf(
        m.Arg(m.Attribute(value=m.Name("admin"),
                          attr=m.Name("TabularInline"))),
        m.Arg(m.Name("TabularInline")),
        m.Arg(m.Attribute(value=m.Name("admin"),
                          attr=m.Name("StackedInline"))),
        m.Arg(m.Name("StackedInline")),
    )

    def visit_ClassDef_bases(self, node: ClassDef) -> None:
        for base_cls in node.bases:
            if m.matches(base_cls, self.base_cls_matcher):
                self.context.scratch[self.context_key] = True
        super().visit_ClassDef_bases(node)

    def leave_ClassDef(
            self, original_node: ClassDef,
            updated_node: ClassDef) -> Union[BaseStatement, RemovalSentinel]:
        self.context.scratch.pop(self.context_key, None)
        return super().leave_ClassDef(original_node, updated_node)

    @property
    def _is_context_right(self):
        return self.context.scratch.get(self.context_key, False)

    def leave_FunctionDef(
            self, original_node: FunctionDef, updated_node: FunctionDef
    ) -> Union[BaseStatement, RemovalSentinel]:
        if (m.matches(updated_node,
                      m.FunctionDef(name=m.Name("has_add_permission")))
                and self._is_context_right):
            if len(updated_node.params.params) == 2:
                old_params = updated_node.params
                updated_params = old_params.with_changes(params=(
                    *old_params.params,
                    Param(name=Name("obj"), default=Name("None")),
                ))
                return updated_node.with_changes(params=updated_params)
        return super().leave_FunctionDef(original_node, updated_node)
Ejemplo n.º 27
0
    def leave_Call(self, original_node, updated_node) -> cst.BaseExpression:
        if m.matches(updated_node.func,
                     m.Attribute(value=m.Call(m.Name('super')))):
            return updated_node \
                .with_deep_changes(
                    updated_node.func, value=cst.Name(self.cls.__name__)) \
                .with_changes(
                    args=[cst.Arg(cst.Name('self'))] + list(updated_node.args))

        return updated_node
Ejemplo n.º 28
0
 def leave_FunctionDef(self, original_node,
                       updated_node) -> cst.BaseStatement:
     fdef = updated_node
     ftool_pattern = m.Call(
         func=m.Attribute(value=m.Name("functools"), attr=m.Name("wraps")))
     if len(fdef.decorators) == 1:
         dec = fdef.decorators[0].decorator
         if m.matches(dec, ftool_pattern):
             return fdef.with_changes(decorators=[])
     return fdef
Ejemplo n.º 29
0
 def visit_Call(self, node: cst.Call) -> None:
     if m.matches(
         node,
         m.Call(func=m.Attribute(value=m.Name("self"), attr=m.Name("assertEquals"))),
     ):
         new_call = node.with_deep_changes(
             old_node=cst.ensure_type(node.func, cst.Attribute).attr,
             value="assertEqual",
         )
         self.report(node, replacement=new_call)
Ejemplo n.º 30
0
class MenuAppendCommand(VisitorBasedCodemodCommand):

    DESCRIPTION: str = "Migrate to wx.MenuAppend() method and update keywords"

    args_map = {"help": "helpString", "text": "item"}
    args_matchers_map = {
        matchers.Arg(keyword=matchers.Name(value=value)): renamed
        for value, renamed in args_map.items()
    }
    call_matcher = matchers.Call(
        func=matchers.Attribute(attr=matchers.Name(value="Append")),
        args=matchers.MatchIfTrue(lambda args: bool(
            set(arg.keyword.value for arg in args if arg and arg.keyword).
            intersection(MenuAppendCommand.args_map.keys()))),
    )
    deprecated_call_matcher = matchers.Call(
        func=matchers.Attribute(attr=matchers.Name(value="AppendItem")),
        args=[matchers.DoNotCare()],
    )

    def leave_Call(self, original_node: cst.Call,
                   updated_node: cst.Call) -> cst.Call:
        # Migrate form deprecated method AppendItem()
        if matchers.matches(updated_node, self.deprecated_call_matcher):
            updated_node = updated_node.with_changes(
                func=updated_node.func.with_changes(attr=cst.Name(
                    value="Append")))

        # Update keywords
        if matchers.matches(updated_node, self.call_matcher):
            updated_node_args = list(updated_node.args)

            for arg_matcher, renamed in self.args_matchers_map.items():
                for i, node_arg in enumerate(updated_node.args):
                    if matchers.matches(node_arg, arg_matcher):
                        updated_node_args[i] = node_arg.with_changes(
                            keyword=cst.Name(value=renamed))

                updated_node = updated_node.with_changes(
                    args=updated_node_args)

        return updated_node