Esempio n. 1
0
    def leave_ClassDef(self, original_node: cst.ClassDef, updated_node: cst.ClassDef) -> cst.ClassDef:
        new_bases: List[cst.Arg] = []
        namedtuple_base: Optional[cst.Arg] = None

        # Need to examine the original node's bases since they are directly tied to import metadata
        for base_class in original_node.bases:
            # Compare the base class's qualified name against the expected typing.NamedTuple
            if not QualifiedNameProvider.has_name(self, base_class.value, self.qualified_namedtuple):
                # Keep all bases that are not of type typing.NamedTuple
                new_bases.append(base_class)
            else:
                namedtuple_base = base_class

        # We still want to return the updated node in case some of its children have been modified
        if namedtuple_base is None:
            return updated_node

        AddImportsVisitor.add_needed_import(self.context, "attr", "dataclass")
        AddImportsVisitor.add_needed_import(self.context, "pydantic.dataclasses", "dataclass")
        RemoveImportsVisitor.remove_unused_import_by_node(self.context, namedtuple_base.value)

        call = cst.ensure_type(
            cst.parse_expression("dataclass(frozen=False)", config=self.module.config_for_parsing),
            cst.Call,
        )
        return updated_node.with_changes(
            lpar=cst.MaybeSentinel.DEFAULT,
            rpar=cst.MaybeSentinel.DEFAULT,
            bases=new_bases,
            decorators=[*original_node.decorators, cst.Decorator(decorator=call)],
        )
Esempio n. 2
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
Esempio n. 3
0
 def leave_ClassDef(self, original_node: cst.ClassDef,
                    updated_node: cst.ClassDef) -> cst.CSTNode:
     if isinstance(original_node, cst.ClassDef):
         print('changing')
         new_body = updated_node.body.with_changes(
             body=self._sort_nodes(updated_node.body.body))
         updated_node_2 = updated_node.with_changes(body=new_body)
         return updated_node_2
     return updated_node
Esempio n. 4
0
 def visit_ClassDef(self, node: cst.ClassDef) -> Optional[bool]:
     # found class def in the source
     # stack +=  [name]
     # annotations+=  [gramps.parent.name], base classes, None,  docstring
     self.stack.append(node.name.value)
     docstring = node.get_docstring(clean=False)
     ##todo: or possible use deep_clone op copy tree
     # classDef>body>identedblock>body>SimpleStatementLine>SimpleString
     bases = node.bases
     key: Tuple[str, ...] = tuple(self.stack)
     self.annotations[key] = (None, None, bases, docstring)
     print(tuple(self.stack))
Esempio n. 5
0
    def leave_ClassDef(self, original_node: cst.ClassDef) -> None:
        (namedtuple_base, new_bases) = self.partition_bases(original_node.bases)
        if namedtuple_base is not None:
            call = ensure_type(parse_expression("dataclass(frozen=True)"), cst.Call)

            replacement = original_node.with_changes(
                lpar=MaybeSentinel.DEFAULT,
                rpar=MaybeSentinel.DEFAULT,
                bases=new_bases,
                decorators=list(original_node.decorators) + [cst.Decorator(decorator=call)],
            )
            self.report(original_node, replacement=replacement)
Esempio n. 6
0
    def visit_ClassDef(self, node: cst.ClassDef) -> None:
        new_bases = tuple(base for base in node.bases
                          if not m.matches(base.value, m.Name("object")))

        if tuple(node.bases) != new_bases:
            # reconstruct classdef, removing parens if bases and keywords are empty
            new_classdef = node.with_changes(
                bases=new_bases,
                lpar=cst.MaybeSentinel.DEFAULT,
                rpar=cst.MaybeSentinel.DEFAULT,
            )

            # report warning and autofix
            self.report(node, replacement=new_classdef)
Esempio n. 7
0
 def leave_ClassDef(
     self, original: libcst.ClassDef, updated: libcst.ClassDef
 ) -> libcst.ClassDef:
     resulting_decorators = []
     for decorator_wrapper in updated.decorators:
         decorator = decorator_wrapper.decorator
         if not m.matches(
             decorator,
             m.Attribute(
                 value=m.Name("six"), attr=m.Name("python_2_unicode_compatible"),
             ),
         ):
             resulting_decorators.append(decorator_wrapper)
     return updated.with_changes(decorators=resulting_decorators)
Esempio n. 8
0
    def leave_ClassDef(self, original_node: cst.ClassDef,
                       updated_node: cst.ClassDef) -> cst.CSTNode:
        key = tuple(self.stack)
        self.stack.pop()
        if key in self.annotations:
            annotations = self.annotations[key]
            # # Todo: P1 add/update docstring
            # print(original_node.body.body[0].body[0].value.value)
            # docstring = cst.SimpleString('""" docstring """')
            return updated_node.with_changes(
                # add/update base class(es)
                bases=annotations[2])
            # Todo: add/update decorators

        return updated_node
    def visit_ClassDef(self, node: cst.ClassDef) -> None:
        doc_string = node.get_docstring()
        if not doc_string or "@sorted-attributes" not in doc_string:
            return

        found_any_assign: bool = False
        pre_assign_lines: List[LineType] = []
        assign_lines: List[LineType] = []
        post_assign_lines: List[LineType] = []

        def _add_unmatched_line(line: LineType) -> None:
            post_assign_lines.append(
                line) if found_any_assign else pre_assign_lines.append(line)

        for line in node.body.body:
            if m.matches(
                    line,
                    m.SimpleStatementLine(
                        body=[m.Assign(targets=[m.AssignTarget()])])):
                found_any_assign = True
                assign_lines.append(line)
            else:
                _add_unmatched_line(line)
                continue

        sorted_assign_lines = sorted(
            assign_lines,
            key=lambda line: line.body[0].targets[0].target.value)
        if sorted_assign_lines == assign_lines:
            return
        self.report(
            node,
            replacement=node.with_changes(body=node.body.with_changes(
                body=pre_assign_lines + sorted_assign_lines +
                post_assign_lines)),
        )
Esempio n. 10
0
 def leave_ClassDef(
     self,
     original_node: cst.ClassDef,
     updated_node: cst.ClassDef,
 ) -> cst.ClassDef:
     cls_name = ".".join(self.qualifier)
     self.qualifier.pop()
     definition = self.annotations.class_definitions.get(cls_name)
     if definition:
         b1 = _find_generic_base(definition)
         b2 = _find_generic_base(updated_node)
         if b1 and not b2:
             new_bases = list(updated_node.bases) + [b1]
             self.annotation_counts.typevars_and_generics_added += 1
             return updated_node.with_changes(bases=new_bases)
     return updated_node
Esempio n. 11
0
    def visit_ClassDef(self, node: cst.ClassDef) -> None:
        self.qualifier.append(node.name.value)
        new_bases = []
        for base in node.bases:
            value = base.value
            if isinstance(value, NAME_OR_ATTRIBUTE):
                new_value = self._handle_NameOrAttribute(value)
            elif isinstance(value, cst.Subscript):
                new_value = self._handle_Subscript(value)
            else:
                start = self.get_metadata(PositionProvider, node).start
                raise ValueError(
                    "Invalid type used as base class in stub file at " +
                    f"{start.line}:{start.column}. Only subscripts, names, and "
                    + "attributes are valid base classes for static typing.")
            new_bases.append(base.with_changes(value=new_value))

        self.class_definitions[node.name.value] = node.with_changes(
            bases=new_bases)