Example #1
0
    def _visit_classdef(self, node):
        """Class visitor."""
        if not node_is_subclass(node, 'django.db.models.base.Model', '.Model'):
            # we only care about models
            return

        for child in node.get_children():
            if _is_meta_with_abstract(child):
                return

            if isinstance(child, Assign):
                grandchildren = list(child.get_children())

                if not isinstance(grandchildren[0], AssignName):
                    continue

                name = grandchildren[0].name
                if name != '__unicode__':
                    continue

                grandchild = grandchildren[1]
                assigned = inferred(grandchild)()[0]

                if assigned.callable():
                    return

                self.add_message('E%s01' % BASE_ID, args=node.name, node=node)
                return

            if isinstance(child, FunctionDef) and child.name == '__unicode__':
                if PY3:
                    self.add_message('W%s02' % BASE_ID,
                                     args=node.name,
                                     node=node)
                return

        # if we get here, then we have no __unicode__ method directly on the class itself
        if PY3:
            return

        # a different warning is emitted if a parent declares __unicode__
        for method in node.methods():
            if method.name == '__unicode__':
                # this happens if a parent declares the unicode method but
                # this node does not
                self.add_message('W%s03' % BASE_ID, args=node.name, node=node)
                return

        # if the Django compatibility decorator is used then we don't emit a warning
        # see https://github.com/landscapeio/pylint-django/issues/10
        if node.decorators is not None:
            for decorator in node.decorators.nodes:
                if getattr(decorator, 'name',
                           None) == 'python_2_unicode_compatible':
                    return

        self.add_message('W%s01' % BASE_ID, args=node.name, node=node)
Example #2
0
def foreign_key_sets(chain, node):
    """
    When a Django model has a ForeignKey to another model, the target
    of the foreign key gets a '<modelname>_set' attribute for accessing
    a queryset of the model owning the foreign key - eg:

    class ModelA(models.Model):
        pass

    class ModelB(models.Model):
        a = models.ForeignKey(ModelA)

    Now, ModelA instances will have a modelb_set attribute.

    It's also possible to explicitly name the relationship using the related_name argument
    to the ForeignKey constructor. As it's impossible to know this without inspecting all
    models before processing, we'll instead do a "best guess" approach and see if the attribute
    being accessed goes on to be used as a queryset. This is via 'duck typing': if the method
    called on the attribute being accessed is something we might find in a queryset, we'll
    warn.
    """
    quack = False

    if node.attrname in MANAGER_ATTRS or node.attrname.endswith('_set'):
        # if this is a X_set method, that's a pretty strong signal that this is the default
        # Django name, rather than one set by related_name
        quack = True
    else:
        # we will
        if isinstance(node.parent, Attribute):
            func_name = getattr(node.parent, 'attrname', None)
            if func_name in MANAGER_ATTRS:
                quack = True

    if quack:
        children = list(node.get_children())
        for child in children:
            try:
                inferred_cls = inferred(child)()
            except InferenceError:
                pass
            else:
                for cls in inferred_cls:
                    if (node_is_subclass(cls,
                                         'django.db.models.manager.Manager',
                                         'django.db.models.base.Model',
                                         '.Model',
                                         'django.db.models.fields.related.ForeignObject')):
                        # This means that we are looking at a subclass of models.Model
                        # and something is trying to access a <something>_set attribute.
                        # Since this could exist, we will return so as not to raise an
                        # error.
                        return
    chain()
Example #3
0
    def _visit_classdef(self, node):
        """Class visitor."""
        if not node_is_subclass(node, 'django.db.models.base.Model', '.Model'):
            # we only care about models
            return

        for child in node.get_children():
            if _is_meta_with_abstract(child):
                return

            if isinstance(child, Assign):
                grandchildren = list(child.get_children())

                if not isinstance(grandchildren[0], AssignName):
                    continue

                name = grandchildren[0].name
                if name != '__unicode__':
                    continue

                grandchild = grandchildren[1]
                assigned = inferred(grandchild)()[0]

                if assigned.callable():
                    return

                self.add_message('E%s01' % BASE_ID, args=node.name, node=node)
                return

            if isinstance(child, FunctionDef) and child.name == '__unicode__':
                if PY3:
                    self.add_message('W%s02' % BASE_ID, args=node.name, node=node)
                return

        # if we get here, then we have no __unicode__ method directly on the class itself
        if PY3:
            return

        # a different warning is emitted if a parent declares __unicode__
        for method in node.methods():
            if method.name == '__unicode__':
                # this happens if a parent declares the unicode method but
                # this node does not
                self.add_message('W%s03' % BASE_ID, args=node.name, node=node)
                return

        # if the Django compatibility decorator is used then we don't emit a warning
        # see https://github.com/landscapeio/pylint-django/issues/10
        if node.decorators is not None:
            for decorator in node.decorators.nodes:
                if getattr(decorator, 'name', None) == 'python_2_unicode_compatible':
                    return

        self.add_message('W%s01' % BASE_ID, args=node.name, node=node)
def foreign_key_sets(chain, node):
    """
    When a Django model has a ForeignKey to another model, the target
    of the foreign key gets a '<modelname>_set' attribute for accessing
    a queryset of the model owning the foreign key - eg:

    class ModelA(models.Model):
        pass

    class ModelB(models.Model):
        a = models.ForeignKey(ModelA)

    Now, ModelA instances will have a modelb_set attribute.

    It's also possible to explicitly name the relationship using the related_name argument
    to the ForeignKey constructor. As it's impossible to know this without inspecting all
    models before processing, we'll instead do a "best guess" approach and see if the attribute
    being accessed goes on to be used as a queryset. This is via 'duck typing': if the method
    called on the attribute being accessed is something we might find in a queryset, we'll
    warn.
    """
    quack = False

    if node.attrname in MANAGER_ATTRS or node.attrname.endswith('_set'):
        # if this is a X_set method, that's a pretty strong signal that this is the default
        # Django name, rather than one set by related_name
        quack = True
    else:
        # we will
        if isinstance(node.parent, Attribute):
            func_name = getattr(node.parent, 'attrname', None)
            if func_name in MANAGER_ATTRS:
                quack = True

    if quack:
        children = list(node.get_children())
        for child in children:
            try:
                inferred_cls = inferred(child)()
            except InferenceError:
                pass
            else:
                for cls in inferred_cls:
                    if (node_is_subclass(cls,
                                         'django.db.models.manager.Manager',
                                         'django.db.models.base.Model',
                                         '.Model')):
                        # This means that we are looking at a subclass of models.Model
                        # and something is trying to access a <something>_set attribute.
                        # Since this could exist, we will return so as not to raise an
                        # error.
                        return
    chain()
def _attribute_is_magic(node, attrs, parents):
    """Checks that node is an attribute used inside one of allowed parents"""
    if node.attrname not in attrs:
        return False
    if not node.last_child():
        return False

    try:
        for cls in inferred(node.last_child())():
            if isinstance(cls, Super):
                cls = cls._self_class
            if node_is_subclass(cls, *parents) or cls.qname() in parents:
                return True
    except InferenceError:
        pass
    return False
Example #6
0
def _attribute_is_magic(node, attrs, parents):
    """Checks that node is an attribute used inside one of allowed parents"""
    if node.attrname not in attrs:
        return False
    if not node.last_child():
        return False

    try:
        for cls in inferred(node.last_child())():
            if isinstance(cls, Super):
                cls = cls._self_class
            if node_is_subclass(cls, *parents) or cls.qname() in parents:
                return True
    except InferenceError:
        pass
    return False
def is_model_field_display_method(node):
    """Accept model's fields with get_*_display names."""
    if not node.attrname.endswith('_display'):
        return
    if not node.attrname.startswith('get_'):
        return

    if node.last_child():
        # TODO: could validate the names of the fields on the model rather than
        # blindly accepting get_*_display
        try:
            for cls in inferred(node.last_child())():
                if node_is_subclass(cls, 'django.db.models.base.Model', '.Model'):
                    return True
        except InferenceError:
            return False
    return False
Example #8
0
def is_model_field_display_method(node):
    """Accept model's fields with get_*_display names."""
    if not node.attrname.endswith('_display'):
        return
    if not node.attrname.startswith('get_'):
        return

    if node.last_child():
        # TODO: could validate the names of the fields on the model rather than
        # blindly accepting get_*_display
        try:
            for cls in inferred(node.last_child())():
                if node_is_subclass(cls, 'django.db.models.base.Model', '.Model'):
                    return True
        except InferenceError:
            return False
    return False
def node_is_subclass(cls, *subclass_names):
    """Checks if cls node has parent with subclass_name."""
    if not isinstance(cls, (ClassDef, Instance)):
        return False

    if cls.bases == Uninferable:
        return False
    for base_cls in cls.bases:
        try:
            for inf in inferred(base_cls)():
                if inf.qname() in subclass_names:
                    return True
                if inf != cls and node_is_subclass(inf, *subclass_names):
                    # check up the hierarchy in case we are a subclass of
                    # a subclass of a subclass ...
                    return True
        except InferenceError:
            continue

    return False
Example #10
0
def node_is_subclass(cls, *subclass_names):
    """Checks if cls node has parent with subclass_name."""
    if not isinstance(cls, (ClassDef, Instance)):
        return False

    if cls.bases == Uninferable:
        return False
    for base_cls in cls.bases:
        try:
            for inf in inferred(base_cls)():
                if inf.qname() in subclass_names:
                    return True
                if inf != cls and node_is_subclass(inf, *subclass_names):
                    # check up the hierarchy in case we are a subclass of
                    # a subclass of a subclass ...
                    return True
        except InferenceError:
            continue

    return False
Example #11
0
def foreign_key_sets(chain, node):
    """
    When a Django model has a ForeignKey to another model, the target
    of the foreign key gets a '<modelname>_set' attribute for accessing
    a queryset of the model owning the foreign key - eg:

    class ModelA(models.Model):
        pass

    class ModelB(models.Model):
        a = models.ForeignKey(ModelA)

    Now, ModelA instances will have a modelb_set attribute.

    It's also possible to explicitly name the relationship using the related_name argument
    to the ForeignKey constructor. As it's impossible to know this without inspecting all
    models before processing, we'll instead do a "best guess" approach and see if the attribute
    being accessed goes on to be used as a queryset. This is via 'duck typing': if the method
    called on the attribute being accessed is something we might find in a queryset, we'll
    warn.
    """
    quack = False
    # Note: it would have been nice to import the Manager object from Django and
    # get its attributes that way - and this used to be the method - but unfortunately
    # there's no guarantee that Django is properly configured at that stage, and importing
    # anything from the django.db package causes an ImproperlyConfigured exception.
    # Therefore we'll fall back on a hard-coded list of attributes which won't be as accurate,
    # but this is not 100% accurate anyway.
    manager_attrs = (
        'none',
        'all',
        'count',
        'dates',
        'distinct',
        'extra',
        'get',
        'get_or_create',
        'create',
        'bulk_create',
        'filter',
        'aggregate',
        'annotate',
        'complex_filter',
        'exclude',
        'in_bulk',
        'iterator',
        'latest',
        'order_by',
        'select_for_update',
        'select_related',
        'prefetch_related',
        'values',
        'values_list',
        'update',
        'reverse',
        'defer',
        'only',
        'using',
        'exists',
    )

    if node.attrname in manager_attrs or node.attrname.endswith('_set'):
        # if this is a X_set method, that's a pretty strong signal that this is the default
        # Django name, rather than one set by related_name
        quack = True
    else:
        # we will
        if isinstance(node.parent, Attribute):
            func_name = getattr(node.parent, 'attrname', None)
            if func_name in manager_attrs:
                quack = True

    if quack:
        children = list(node.get_children())
        for child in children:
            try:
                inferred_cls = inferred(child)()
            except InferenceError:
                pass
            else:
                for cls in inferred_cls:
                    if (node_is_subclass(cls,
                                         'django.db.models.manager.Manager',
                                         'django.db.models.base.Model',
                                         '.Model')):
                        # This means that we are looking at a subclass of models.Model
                        # and something is trying to access a <something>_set attribute.
                        # Since this could exist, we will return so as not to raise an
                        # error.
                        return
    chain()
Example #12
0
def foreign_key_sets(chain, node):
    """
    When a Django model has a ForeignKey to another model, the target
    of the foreign key gets a '<modelname>_set' attribute for accessing
    a queryset of the model owning the foreign key - eg:

    class ModelA(models.Model):
        pass

    class ModelB(models.Model):
        a = models.ForeignKey(ModelA)

    Now, ModelA instances will have a modelb_set attribute.

    It's also possible to explicitly name the relationship using the related_name argument
    to the ForeignKey constructor. As it's impossible to know this without inspecting all
    models before processing, we'll instead do a "best guess" approach and see if the attribute
    being accessed goes on to be used as a queryset. This is via 'duck typing': if the method
    called on the attribute being accessed is something we might find in a queryset, we'll
    warn.
    """
    quack = False
    # Note: it would have been nice to import the Manager object from Django and
    # get its attributes that way - and this used to be the method - but unfortunately
    # there's no guarantee that Django is properly configured at that stage, and importing
    # anything from the django.db package causes an ImproperlyConfigured exception.
    # Therefore we'll fall back on a hard-coded list of attributes which won't be as accurate,
    # but this is not 100% accurate anyway.
    manager_attrs = (
        'none',
        'all',
        'count',
        'dates',
        'distinct',
        'extra',
        'get',
        'get_or_create',
        'create',
        'bulk_create',
        'filter',
        'aggregate',
        'annotate',
        'complex_filter',
        'exclude',
        'in_bulk',
        'iterator',
        'latest',
        'order_by',
        'select_for_update',
        'select_related',
        'prefetch_related',
        'values',
        'values_list',
        'update',
        'reverse',
        'defer',
        'only',
        'using',
        'exists',
    )

    if node.attrname in manager_attrs or node.attrname.endswith('_set'):
        # if this is a X_set method, that's a pretty strong signal that this is the default
        # Django name, rather than one set by related_name
        quack = True
    else:
        # we will
        if isinstance(node.parent, Attribute):
            func_name = getattr(node.parent, 'attrname', None)
            if func_name in manager_attrs:
                quack = True

    if quack:
        children = list(node.get_children())
        for child in children:
            try:
                inferred_cls = inferred(child)()
            except InferenceError:
                pass
            else:
                for cls in inferred_cls:
                    if (node_is_subclass(cls,
                                         'django.db.models.manager.Manager',
                                         'django.db.models.base.Model',
                                         '.Model')):
                        # This means that we are looking at a subclass of models.Model
                        # and something is trying to access a <something>_set attribute.
                        # Since this could exist, we will return so as not to raise an
                        # error.
                        return
    chain()