Example #1
0
def _is_exempt_from_public_methods(node: astroid.ClassDef) -> bool:
    """Check if a class is exempt from too-few-public-methods"""

    # If it's a typing.Namedtuple or an Enum
    for ancestor in node.ancestors():
        if ancestor.name == "Enum" and ancestor.root().name == "enum":
            return True
        if ancestor.qname() == TYPING_NAMEDTUPLE:
            return True

    # Or if it's a dataclass
    if not node.decorators:
        return False

    root_locals = set(node.root().locals)
    for decorator in node.decorators.nodes:
        if isinstance(decorator, astroid.Call):
            decorator = decorator.func
        if not isinstance(decorator, (astroid.Name, astroid.Attribute)):
            continue
        if isinstance(decorator, astroid.Name):
            name = decorator.name
        else:
            name = decorator.attrname
        if name in DATACLASSES_DECORATORS and (
                root_locals.intersection(DATACLASSES_DECORATORS)
                or DATACLASS_IMPORT in root_locals):
            return True
    return False
Example #2
0
 def instance_has_bool(class_def: astroid.ClassDef) -> bool:
     try:
         class_def.getattr("__bool__")
         return True
     except astroid.AttributeInferenceError:
         ...
     return False
    def _set_classdef_environment(self, node: astroid.ClassDef) -> None:
        """Method to set environment of a ClassDef node."""
        node.type_environment = Environment()
        for name in node.instance_attrs:
            node.type_environment.locals[
                name] = self.type_constraints.fresh_tvar(
                    node.instance_attrs[name][0])
            self.type_store.classes[node.name][name] = [
                (node.type_environment.locals[name], 'attribute')
            ]
        for name in node.locals:
            if name in ['__module__', '__qualname__']:
                node.type_environment.locals[name] = str
            else:
                node.type_environment.locals[
                    name] = self.type_constraints.fresh_tvar(
                        node.locals[name][0])

        self.type_store.classes[node.name]['__bases'] = [
            _node_to_type(base) for base in node.bases
        ]
        try:
            self.type_store.classes[node.name]['__mro'] = [
                cls.name for cls in node.mro()
            ]
        except astroid.exceptions.DuplicateBasesError:
            self.type_store.classes[node.name]['__mro'] = [node.name]
Example #4
0
def add_attribute_nodes(node: astroid.ClassDef, attr_names):
    for attr_name in attr_names:
        rhs_node = astroid.Unknown(
            lineno=node.lineno,
            parent=node,
        )
        node.locals[attr_name] = [rhs_node]
        node.instance_attrs[attr_name] = [rhs_node]
Example #5
0
def has_known_bases(klass: astroid.ClassDef, context=None) -> bool:
    """Return true if all base classes of a class could be inferred."""
    try:
        return klass._all_bases_known
    except AttributeError:
        pass
    for base in klass.bases:
        result = safe_infer(base, context=context)
        if (not isinstance(result, astroid.ClassDef) or result is klass
                or not has_known_bases(result, context=context)):
            klass._all_bases_known = False
            return False
    klass._all_bases_known = True
    return True
Example #6
0
def overrides_a_method(class_node: astroid.ClassDef, name: str) -> bool:
    """return True if <name> is a method overridden from an ancestor"""
    for ancestor in class_node.ancestors():
        if name in ancestor and isinstance(ancestor[name],
                                           astroid.FunctionDef):
            return True
    return False
def _is_dataclass(node: astroid.ClassDef) -> bool:
    """Check if a class definition defines a Python 3.7+ dataclass

    :param node: The class node to check.
    :type node: astroid.ClassDef

    :returns: True if the given node represents a dataclass class. False otherwise.
    :rtype: bool
    """
    if not node.decorators:
        return False

    root_locals = node.root().locals
    for decorator in node.decorators.nodes:
        if isinstance(decorator, astroid.Call):
            decorator = decorator.func
        if not isinstance(decorator, (astroid.Name, astroid.Attribute)):
            continue
        if isinstance(decorator, astroid.Name):
            name = decorator.name
        else:
            name = decorator.attrname
        if name == DATACLASS_DECORATOR and DATACLASS_DECORATOR in root_locals:
            return True
    return False
Example #8
0
def make_node_create_uninferable_instance(node: astroid.ClassDef,
                                          context=None):
    def _instantiate_uninferable(*args, **kwargs):
        return astroid.Uninferable()

    node.instantiate_class = _instantiate_uninferable
    return node
Example #9
0
def has_conditional_instantiation(node: astroid.ClassDef, context=None):
    if 'pyomo' not in node.qname():
        return
    try:
        # check if the class defines a __new__()
        dunder_new_node: astroid.FunctionDef = node.local_attr('__new__')[0]
    except astroid.AttributeInferenceError:
        return False
    else:
        # _display(node)
        # find all return statements; if there's more than one, assume that instances are created conditionally,
        # and therefore the type of the instantiated object cannot be known with static analysis
        # to be more accurate, we should check for If nodes as well as maybe the presence of other __new__() calls
        return_statements = list(
            dunder_new_node.nodes_of_class(astroid.node_classes.Return))
        return len(return_statements) > 1
def _is_dataclass(node: astroid.ClassDef) -> bool:
    """Check if a class definition defines a Python 3.7+ dataclass

    :param node: The class node to check.
    :type node: astroid.ClassDef

    :returns: True if the given node represents a dataclass class. False otherwise.
    :rtype: bool
    """
    if not node.decorators:
        return False

    root_locals = node.root().locals
    for decorator in node.decorators.nodes:
        if isinstance(decorator, astroid.Call):
            decorator = decorator.func
        if not isinstance(decorator, (astroid.Name, astroid.Attribute)):
            continue
        if isinstance(decorator, astroid.Name):
            name = decorator.name
        else:
            name = decorator.attrname
        if name == DATACLASS_DECORATOR and DATACLASS_DECORATOR in root_locals:
            return True
    return False
Example #11
0
def transform(cls):
    """
    Mimics Flask-SQLAlchemy's _include_sqlalchemy
    """
    if cls.name == "SQLAlchemy":
        import sqlalchemy  # pylint: disable=import-outside-toplevel
        import sqlalchemy.orm  # pylint: disable=import-outside-toplevel

        for module in sqlalchemy, sqlalchemy.orm:
            for key in module.__all__:
                cls.locals[key] = [ClassDef(key, None)]
    if cls.name == "scoped_session":
        from sqlalchemy.orm import Session  # pylint: disable=import-outside-toplevel

        for key in Session.public_methods:
            cls.locals[key] = [ClassDef(key, None)]
Example #12
0
def _is_dataclass_like(node: astroid.ClassDef) -> bool:
    """Check if a class definition defines a Python data class

    A list of decorator names are introspected, such as the builtin
    `dataclass` decorator, as well as the popular `attrs` one from
    the `attrs` library.

    :param node: The class node to check.
    :type node: astroid.ClassDef

    :returns:
        `True` if the given node represents a dataclass class, `False` otherwise.
    :rtype: bool
    """
    if not node.decorators:
        return False

    root_locals = set(node.root().locals)
    for decorator in node.decorators.nodes:
        if isinstance(decorator, astroid.Call):
            decorator = decorator.func
        if not isinstance(decorator, (astroid.Name, astroid.Attribute)):
            continue
        if isinstance(decorator, astroid.Name):
            name = decorator.name
        else:
            name = decorator.attrname
        if name in DATACLASSES_DECORATORS and root_locals.intersection(
            DATACLASSES_DECORATORS
        ):
            return True
    return False
def _is_enum_subclass(cls: astroid.ClassDef) -> bool:
    """Return whether cls is a subclass of an Enum."""
    try:
        return any(klass.name in ENUM_BASE_NAMES
                   and getattr(klass.root(), "name", None) == "enum"
                   for klass in cls.mro())
    except MroError:
        return False
Example #14
0
def has_known_bases(klass: astroid.ClassDef,
                    context=None) -> bool:
    """Return true if all base classes of a class could be inferred."""
    try:
        return klass._all_bases_known
    except AttributeError:
        pass
    for base in klass.bases:
        result = safe_infer(base, context=context)
        # TODO: check for A->B->A->B pattern in class structure too?
        if (not isinstance(result, astroid.ClassDef) or
                result is klass or
                not has_known_bases(result, context=context)):
            klass._all_bases_known = False
            return False
    klass._all_bases_known = True
    return True
Example #15
0
def unimplemented_abstract_methods(
    node: astroid.ClassDef,
    is_abstract_cb: astroid.FunctionDef = None
) -> Dict[str, astroid.node_classes.NodeNG]:
    """
    Get the unimplemented abstract methods for the given *node*.

    A method can be considered abstract if the callback *is_abstract_cb*
    returns a ``True`` value. The check defaults to verifying that
    a method is decorated with abstract methods.
    The function will work only for new-style classes. For old-style
    classes, it will simply return an empty dictionary.
    For the rest of them, it will return a dictionary of abstract method
    names and their inferred objects.
    """
    if is_abstract_cb is None:
        is_abstract_cb = partial(decorated_with, qnames=ABC_METHODS)
    visited = {}  # type: Dict[str, astroid.node_classes.NodeNG]
    try:
        mro = reversed(node.mro())
    except NotImplementedError:
        # Old style class, it will not have a mro.
        return {}
    except astroid.ResolveError:
        # Probably inconsistent hierarchy, don'try
        # to figure this out here.
        return {}
    for ancestor in mro:
        for obj in ancestor.values():
            inferred = obj
            if isinstance(obj, astroid.AssignName):
                inferred = safe_infer(obj)
                if not inferred:
                    # Might be an abstract function,
                    # but since we don't have enough information
                    # in order to take this decision, we're taking
                    # the *safe* decision instead.
                    if obj.name in visited:
                        del visited[obj.name]
                    continue
                if not isinstance(inferred, astroid.FunctionDef):
                    if obj.name in visited:
                        del visited[obj.name]
            if isinstance(inferred, astroid.FunctionDef):
                # It's critical to use the original name,
                # since after inferring, an object can be something
                # else than expected, as in the case of the
                # following assignment.
                #
                # class A:
                #     def keys(self): pass
                #     __iter__ = keys
                abstract = is_abstract_cb(inferred)
                if abstract:
                    visited[obj.name] = inferred
                elif not abstract and obj.name in visited:
                    del visited[obj.name]
    return visited
Example #16
0
def class_is_abstract(node: astroid.ClassDef) -> bool:
    """return true if the given class node should be considered as an abstract
    class
    """
    for method in node.methods():
        if method.parent.frame() is node:
            if method.is_abstract(pass_is_abstract=False):
                return True
    return False
Example #17
0
def class_is_abstract(node: astroid.ClassDef) -> bool:
    """return true if the given class node should be considered as an abstract
    class
    """
    for method in node.methods():
        if method.parent.frame() is node:
            if method.is_abstract(pass_is_abstract=False):
                return True
    return False
Example #18
0
    def _set_classdef_environment(self, node: astroid.ClassDef) -> None:
        """Method to set environment of a ClassDef node."""
        node.type_environment = Environment()
        for name in node.instance_attrs:
            node.type_environment.locals[name] = self.type_constraints.fresh_tvar(node.instance_attrs[name][0])
            self.type_store.classes[node.name][name] = [(node.type_environment.locals[name], 'attribute')]
        for name in node.locals:
            if name in ['__module__', '__qualname__']:
                node.type_environment.locals[name] = str
            else:
                node.type_environment.locals[name] = self.type_constraints.fresh_tvar(node.locals[name][0])

        self.type_store.classes[node.name]['__bases'] = [_node_to_type(base)
                                                         for base in node.bases]
        try:
            self.type_store.classes[node.name]['__mro'] = [cls.name for cls in node.mro()]
        except astroid.exceptions.DuplicateBasesError:
            self.type_store.classes[node.name]['__mro'] = [node.name]
Example #19
0
def class_is_abstract(node: astroid.ClassDef) -> bool:
    """return true if the given class node should be considered as an abstract
    class
    """
    # Only check for explicit metaclass=ABCMeta on this specific class
    meta = node.declared_metaclass()
    if meta is not None:
        if meta.name == "ABCMeta" and meta.root().name in ABC_MODULES:
            return True

    for ancestor in node.ancestors():
        if ancestor.name == "ABC" and ancestor.root().name in ABC_MODULES:
            # abc.ABC inheritance
            return True

    for method in node.methods():
        if method.parent.frame() is node:
            if method.is_abstract(pass_is_abstract=False):
                return True
    return False
Example #20
0
    def visit_classdef(self, node: astroid.ClassDef) -> None:
        node.inf_type = NoType()

        # Update type_store for this class.
        # TODO: include node.instance_attrs as well?
        for attr in node.locals:
            attr_inf_type = self.type_constraints.resolve(node.type_environment.lookup_in_env(attr))
            attr_inf_type >> (
                lambda a: self.type_store.methods[attr].append((a, node.locals[attr][0].type)) if is_callable(a) else None)
            attr_inf_type >> (
                lambda a: self.type_store.classes[node.name][attr].append((a, node.locals[attr][0].type if is_callable(a) else 'attribute')))
def transform(node: NodeNG) -> None:
    """Make pylint understand FlaskSQLAlchemy proxies and wrappers.

    Note : it _looks_ like astroid transforms are run in some kind of try/except
    mechanism which makes some errors fail silently. For example, if you call:

    ```python
    from sqlalchemy.orm import Session
    Session.foo
    ```
    here, you would think it will raise an:
    `AttributeError: type object 'Session' has no attribute 'foo'`
    but... no. Instead it stops the transform and continue to the next node, so pylint
    does not raise an error, so you think your code (and ours) works, but it's not :-(
    So we need to write tests that fails to make sure our plugin works.
    """
    if node.name == "SQLAlchemy":
        import sqlalchemy
        import sqlalchemy.orm

        for module in sqlalchemy, sqlalchemy.orm:
            for key in sorted(module.__all__, key=sort_module_keys):
                if key not in FLASK_SQLALCHEMY_WRAPS:
                    node.locals[key] = [ClassDef(key, None)]
                else:
                    node.locals[key] = [
                        ClassDef(key, None), node.locals["Query"]
                    ]
    elif node.name == "scoped_session":
        from sqlalchemy.orm import Session

        for key in sorted(dir(Session), reverse=True):
            # `query` is in fact a proxy to `query_property`
            if key == "query":
                node.locals[key] = [
                    ClassDef(key, None), node.locals["query_property"]
                ]
            else:
                node.locals[key] = [ClassDef(key, None)]
Example #22
0
    def visit_classdef(self, node: astroid.ClassDef) -> None:
        node.inf_type = NoType()
        self.type_constraints.unify(self.lookup_inf_type(node.parent, node.name),
                                    Type[_ForwardRef(node.name)], node)

        # Update type_store for this class.
        # TODO: include node.instance_attrs as well?
        for attr in node.locals:
            attr_inf_type = self.type_constraints.resolve(node.type_environment.lookup_in_env(attr))
            attr_inf_type >> (
                lambda a: self.type_store.methods[attr].append((a, node.locals[attr][0].type)) if isinstance(a, CallableMeta) else None)
            attr_inf_type >> (
                lambda a: self.type_store.classes[node.name][attr].append((a, node.locals[attr][0].type if isinstance(a, CallableMeta) else 'attribute')))
Example #23
0
    def visit_classdef(self, node: astroid.ClassDef) -> None:
        node.inf_type = TypeInfo(NoType)
        self.type_constraints.unify(self.lookup_type(node.parent, node.name),
                                    _ForwardRef(node.name), node)

        # Update type_store for this class.
        # TODO: include node.instance_attrs as well?
        for attr in node.locals:
            attr_type = self.type_constraints.resolve(
                node.type_environment.lookup_in_env(attr)).getValue()
            self.type_store.classes[node.name][attr].append(attr_type)
            if isinstance(attr_type, CallableMeta):
                self.type_store.methods[attr].append(attr_type)
Example #24
0
def is_subclass_of(child: astroid.ClassDef, parent: astroid.ClassDef) -> bool:
    """
    Check if first node is a subclass of second node.
    :param child: Node to check for subclass.
    :param parent: Node to check for superclass.
    :returns: True if child is derived from parent. False otherwise.
    """
    if not all(isinstance(node, astroid.ClassDef) for node in (child, parent)):
        return False

    for ancestor in child.ancestors():
        if astroid.helpers.is_subtype(ancestor, parent):
            return True
    return False
Example #25
0
def is_subclass_of(child: astroid.ClassDef, parent: astroid.ClassDef) -> bool:
    """
    Check if first node is a subclass of second node.
    :param child: Node to check for subclass.
    :param parent: Node to check for superclass.
    :returns: True if child is derived from parent. False otherwise.
    """
    if not all(isinstance(node, astroid.ClassDef) for node in (child, parent)):
        return False

    for ancestor in child.ancestors():
        if astroid.helpers.is_subtype(ancestor, parent):
            return True
    return False
Example #26
0
def cubicweb_transform(module):
    # handle objectify_predicate decorator (and its former name until bw compat
    # is kept). Only look at module level functions, should be enough.
    for assnodes in module.locals.values():
        for node in assnodes:
            if isinstance(node, FunctionDef) and node.decorators:
                for decorator in node.decorators.nodes:
                    try:
                        for infered in decorator.infer():
                            if infered.name in ('objectify_predicate',
                                                'objectify_selector'):
                                turn_function_to_class(node)
                                break
                        else:
                            continue
                        break
                    except InferenceError:
                        continue
    # add yams base types into 'yams.buildobjs', astng doesn't grasp globals()
    # magic in there
    if module.name == 'yams.buildobjs':
        from yams import BASE_TYPES
        for etype in BASE_TYPES:
            module.locals[etype] = [ClassDef(etype, None)]
    # add data() to uiprops module
    elif module.name.split('.')[-1] == 'uiprops':
        fake = AstroidBuilder(MANAGER).string_build('''
def data(string):
  return u''
''')
        module.locals['data'] = fake.locals['data']
    # handle lower case with underscores for relation names in schema.py
    if not module.qname().endswith('.schema'):
        return
    schema_locals = module.locals
    for assnodes in schema_locals.values():
        for node in assnodes:
            if not isinstance(node, ClassDef):
                continue
            # XXX can we infer ancestor classes? it would be better to know for sure that
            # one of the mother classes is yams.buildobjs.RelationDefinition for instance
            for base in node.basenames:
                if base in ('RelationDefinition', 'ComputedRelation',
                            'RelationType'):
                    new_name = node.name.replace('_', '').capitalize()
                    schema_locals[new_name] = schema_locals[node.name]
                    del schema_locals[node.name]
                    node.name = new_name
Example #27
0
def is_class_subscriptable_pep585_with_postponed_evaluation_enabled(
        value: astroid.ClassDef, node: astroid.node_classes.NodeNG) -> bool:
    """Check if class is subscriptable with PEP 585 and
    postponed evaluation enabled.
    """
    if not is_postponed_evaluation_enabled(node):
        return False

    parent_node = node.parent
    while True:
        # Check if any parent node matches condition
        if isinstance(
                parent_node,
            (astroid.AnnAssign, astroid.Arguments, astroid.FunctionDef)):
            break
        parent_node = parent_node.parent
        if isinstance(parent_node, astroid.Module):
            return False
    if value.qname() in SUBSCRIPTABLE_CLASSES_PEP585:
        return True
    return False
def _is_typing_namedtuple(node: astroid.ClassDef) -> bool:
    """Check if a class node is a typing.NamedTuple class"""
    for base in node.ancestors():
        if base.qname() == TYPING_NAMEDTUPLE:
            return True
    return False
def _is_typing_namedtuple(node: astroid.ClassDef) -> bool:
    """Check if a class node is a typing.NamedTuple class"""
    for base in node.ancestors():
        if base.qname() == TYPING_NAMEDTUPLE:
            return True
    return False