def infer_key_classes(node, context=None): for arg in node.args: # typically the class of the foreign key will # be the first argument, so we'll go from left to right if isinstance(arg, (nodes.Name, nodes.Getattr)): try: key_cls = None for inferred in arg.infer(context=context): key_cls = inferred break except InferenceError: continue else: if key_cls is not None: break elif isinstance(arg, nodes.Const): try: model_name = arg.value.split('.')[-1] # can be 'Model' or 'app.Model' except AttributeError: break for module in MANAGER.astroid_cache.values(): if model_name in module.locals: class_defs = [ module_node for module_node in module.lookup(model_name)[1] if isinstance(module_node, nodes.ClassDef) and node_is_subclass(module_node, 'django.db.models.base.Model') ] if class_defs: return iter([instantiate_class(class_defs[0])()]) else: raise UseInferenceDefault return iter([instantiate_class(key_cls)()])
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. """ if node.attrname.endswith('_set'): children = list(node.get_children()) for child in children: try: inferred = child.infered() except InferenceError: pass else: for cls in inferred: if node_is_subclass(cls, 'django.db.models.base.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 is_model_test_case_subclass(node): """Checks that node is derivative of TestCase class.""" if not node.name.endswith('Test') and not isinstance( node.parent, ClassDef): return False return node_is_subclass(node, 'django.test.testcases.TestCase')
def visit_class(self, node): if not node_is_subclass(node, 'django.db.models.base.Model'): # we only care about models return for child in node.get_children(): if isinstance(child, Assign): grandchildren = list(child.get_children()) if not isinstance(grandchildren[0], AssName): continue name = grandchildren[0].name if name != '__unicode__': continue assigned = grandchildren[1].infered()[0] if assigned.callable(): return self.add_message('E%s01' % BASE_ID, args=node.name, node=node) return if isinstance(child, Function) and child.name == '__unicode__': if sys.version_info[0] >= 3: self.add_message('W%s02' % BASE_ID, args=node.name, node=node) return # if we get here, then we have no __unicode__ method if sys.version_info[0] >= 3: return self.add_message('W%s01' % BASE_ID, args=node.name, node=node)
def is_model_factory_meta_subclass(node): """Checks that node is derivative of DjangoModelFactory class.""" if node.name != 'Meta' or not isinstance(node.parent, ClassDef): return False parents = ('factory.django.DjangoModelFactory', '.DjangoModelFactory',) return node_is_subclass(node.parent, *parents)
def is_model_mpttmeta_subclass(node): """Checks that node is derivative of MPTTMeta class.""" if node.name != 'MPTTMeta' or not isinstance(node.parent, Class): return False parents = ('django.db.models.base.Model', 'django.forms.forms.Form', 'django.forms.models.ModelForm') return any([node_is_subclass(node.parent, parent) for parent in parents])
def is_model_meta_subclass(node): if node.name != 'Meta' or not isinstance(node.parent, Class): return False parents = ('django.db.models.base.Model', 'django.forms.forms.Form', 'django.forms.models.ModelForm') return any([node_is_subclass(node.parent, parent) for parent in parents])
def is_model_media_subclass(node): """Checks that node is derivative of Media class.""" if node.name != 'Media' or not isinstance(node.parent, Class): return False parents = ('django.contrib.admin.options.ModelAdmin', 'django.forms.widgets.Media', 'django.db.models.base.Model', 'django.forms.forms.Form', 'django.forms.models.ModelForm') return any([node_is_subclass(node.parent, parent) for parent in parents])
def is_model_meta_subclass(node): """Checks that node is derivative of Meta class.""" if node.name != 'Meta' or not isinstance(node.parent, Class): return False parents = ('django.db.models.base.Model', 'django.forms.forms.Form', 'django.forms.models.ModelForm') return any([node_is_subclass(node.parent, parent) for parent in parents])
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 infer_key_classes(node, context=None): keyword_args = [kw.value for kw in node.keywords] all_args = chain(node.args, keyword_args) for arg in all_args: # typically the class of the foreign key will # be the first argument, so we'll go from left to right if isinstance(arg, (nodes.Name, nodes.Attribute)): try: key_cls = None for inferred in arg.infer(context=context): key_cls = inferred break except InferenceError: continue else: if key_cls is not None: break elif isinstance(arg, nodes.Const): try: # can be 'Model' or 'app.Model' module_name, _, model_name = arg.value.rpartition('.') except AttributeError: break # when ForeignKey is specified only by class name we assume that # this class must be found in the current module if not module_name: current_module = node.frame() while not isinstance(current_module, nodes.Module): current_module = current_module.parent.frame() module_name = current_module.name elif not module_name.endswith('models'): # otherwise Django allows specifying an app name first, e.g. # ForeignKey('auth.User') so we try to convert that to # 'auth.models', 'User' which works nicely with the `endswith()` # comparison below module_name += '.models' for module in MANAGER.astroid_cache.values(): # only load model classes from modules which match the module in # which *we think* they are defined. This will prevent infering # other models of the same name which are found elsewhere! if model_name in module.locals and module.name.endswith(module_name): class_defs = [ module_node for module_node in module.lookup(model_name)[1] if isinstance(module_node, nodes.ClassDef) and node_is_subclass(module_node, 'django.db.models.base.Model') ] if class_defs: return iter([class_defs[0].instantiate_class()]) else: raise UseInferenceDefault return iter([key_cls.instantiate_class()])
def is_model_view_subclass_method_shouldnt_be_function(node): """Checks that node is get or post method of the View class.""" if node.name not in ('get', 'post'): return False parent = node.parent while parent and not isinstance(parent, ScopedClass): parent = parent.parent subclass = '.View' return parent is not None and parent.name.endswith('View') and node_is_subclass(parent, subclass)
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 = child.inferred() 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()
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 _get_model_class_defs_from_module(module, model_name, module_name): class_defs = [] for module_node in module.lookup(model_name)[1]: if (isinstance(module_node, nodes.ClassDef) and node_is_subclass( module_node, 'django.db.models.base.Model')): class_defs.append(module_node) elif isinstance(module_node, nodes.ImportFrom): imported_module = module_node.do_import_module() class_defs.extend( _get_model_class_defs_from_module(imported_module, model_name, module_name)) return class_defs
def is_model_mpttmeta_subclass(node): """Checks that node is derivative of MPTTMeta class.""" if node.name != 'MPTTMeta' or not isinstance(node.parent, ClassDef): return False parents = ('django.db.models.base.Model', '.Model', # for the transformed version used in this plugin 'django.forms.forms.Form', '.Form', 'django.forms.models.ModelForm', '.ModelForm') return node_is_subclass(node.parent, *parents)
def is_model_field_display_method(node): 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 for cls in node.last_child().infered(): if node_is_subclass(cls, 'django.db.models.base.Model'): return True return False
def is_model_media_subclass(node): """Checks that node is derivative of Media class.""" if node.name != 'Media' or not isinstance(node.parent, ClassDef): return False parents = ('django.contrib.admin.options.ModelAdmin', 'django.forms.widgets.Media', 'django.db.models.base.Model', '.Model', # for the transformed version used in this plugin 'django.forms.forms.Form', '.Form', 'django.forms.models.ModelForm', '.ModelForm') return node_is_subclass(node.parent, *parents)
def is_model_meta_subclass(node): """Checks that node is derivative of Meta class.""" if node.name != 'Meta' or not isinstance(node.parent, Class): return False parents = ('django.db.models.base.Model', 'django.forms.forms.Form', 'django.forms.models.ModelForm', 'rest_framework.serializers.ModelSerializer', 'rest_framework.generics.GenericAPIView', 'rest_framework.viewsets.ReadOnlyModelViewSet', 'rest_framework.viewsets.ModelViewSet', 'django_filters.filterset.FilterSet',) return any([node_is_subclass(node.parent, parent) for parent in parents])
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 for cls in node.last_child().infered(): if node_is_subclass(cls, 'django.db.models.base.Model'): return True return False
def is_model_view_subclass_method_shouldnt_be_function(node): """Checks that node is a default http method (i.e get, post, put, and more) of the View class.""" if node.name not in View.http_method_names: return False parent = node.parent while parent and not isinstance(parent, ScopedClass): parent = parent.parent subclass = ('django.views.View', 'django.views.generic.View', 'django.views.generic.base.View',) return parent is not None and node_is_subclass(parent, *subclass)
def is_model_view_subclass_method_shouldnt_be_function(node): """Checks that node is get or post method of the View class.""" if node.name not in ('get', 'post'): return False parent = node.parent while parent and not isinstance(parent, ScopedClass): parent = parent.parent subclass = ('django.views.View', 'django.views.generic.View', 'django.views.generic.base.View',) return parent is not None and node_is_subclass(parent, *subclass)
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 _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 node.last_child().inferred(): if isinstance(cls, Super): cls = cls._self_class # pylint: disable=protected-access if node_is_subclass(cls, *parents) or cls.qname() in parents: return True except InferenceError: pass return False
def is_model_meta_subclass(node): """Checks that node is derivative of Meta class.""" if node.name != 'Meta' or not isinstance(node.parent, Class): return False parents = ( 'django.db.models.base.Model', 'django.forms.forms.Form', 'django.forms.models.ModelForm', 'rest_framework.serializers.ModelSerializer', 'rest_framework.generics.GenericAPIView', 'rest_framework.viewsets.ReadOnlyModelViewSet', 'rest_framework.viewsets.ModelViewSet', 'django_filters.filterset.FilterSet', ) return any([node_is_subclass(node.parent, parent) for parent in parents])
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 is_model_meta_subclass(node): """Checks that node is derivative of Meta class.""" if node.name != 'Meta' or not isinstance(node.parent, ClassDef): return False parents = ('.Model', # for the transformed version used here 'django.db.models.base.Model', '.Form', 'django.forms.forms.Form', '.ModelForm', 'django.forms.models.ModelForm', 'rest_framework.serializers.ModelSerializer', 'rest_framework.generics.GenericAPIView', 'rest_framework.viewsets.ReadOnlyModelViewSet', 'rest_framework.viewsets.ModelViewSet', 'django_filters.filterset.FilterSet',) return node_is_subclass(node.parent, *parents)
def is_model_meta_subclass(node): """Checks that node is derivative of Meta class.""" if node.name != 'Meta' or not isinstance(node.parent, ClassDef): return False parents = ('.Model', # for the transformed version used here 'django.db.models.base.Model', '.Form', 'django.forms.forms.Form', '.ModelForm', 'django.forms.models.ModelForm', 'rest_framework.serializers.BaseSerializer', 'rest_framework.generics.GenericAPIView', 'rest_framework.viewsets.ReadOnlyModelViewSet', 'rest_framework.viewsets.ModelViewSet', 'django_filters.filterset.FilterSet',) return node_is_subclass(node.parent, *parents)
def visit_classdef(self, node): """Class visitor.""" if not node_is_subclass(node, 'django.forms.models.ModelForm', '.ModelForm'): # we only care about forms return meta = _get_child_meta(node) if not meta: return for child in meta.get_children(): if not isinstance(child, Assign): continue if child.targets[0].name == 'exclude': self.add_message('W%s04' % BASE_ID, node=child) break
def is_model_factory(node): """Checks that node is derivative of DjangoModelFactory or SubFactory class.""" try: parent_classes = node.expr.inferred() except: # noqa: E722, pylint: disable=bare-except return False parents = ('factory.declarations.SubFactory', 'factory.django.DjangoModelFactory') for parent_class in parent_classes: try: if parent_class.qname() in parents: return True if node_is_subclass(parent_class, *parents): return True except AttributeError: continue return False
def visit_class(self, node): """Class visitor.""" if not node_is_subclass(node, 'django.db.models.base.Model'): # we only care about models return for child in node.get_children(): if isinstance(child, Assign): grandchildren = list(child.get_children()) if not isinstance(grandchildren[0], AssName): continue name = grandchildren[0].name if name != '__unicode__': continue assigned = grandchildren[1].infered()[0] if assigned.callable(): return self.add_message('E%s01' % BASE_ID, args=node.name, node=node) return if isinstance(child, Function) and child.name == '__unicode__': if sys.version_info[0] >= 3: self.add_message('W%s02' % BASE_ID, args=node.name, node=node) return # if we get here, then we have no __unicode__ method if sys.version_info[0] >= 3: 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.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, Getattr): func_name = getattr(node.parent, 'attrname', None) # 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 func_name in manager_attrs: quack = True if quack: children = list(node.get_children()) for child in children: try: inferred = child.infered() except InferenceError: pass else: for cls in inferred: if node_is_subclass(cls, 'django.db.models.base.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 is_class(class_name): """Shortcut for node_is_subclass.""" return lambda node: node_is_subclass(node, class_name)
def is_class(class_name): return lambda node: node_is_subclass(node, class_name)
def visit_classdef(self, node): if node_is_subclass( node, "django.forms.forms.BaseForm", ".BaseForm") and not node_is_subclass( node, "django.forms.models.ModelForm", ".ModelForm"): self.add_message("must-inherit-from-model-form", node=node)
def is_model_admin_subclass(node): """Checks that node is derivative of ModelAdmin class.""" if node.name[-5:] != 'Admin' or isinstance(node.parent, ClassDef): return False return node_is_subclass(node, 'django.contrib.admin.options.ModelAdmin')
def visit_classdef(self, node): if node_is_subclass( node, 'django.forms.forms.BaseForm', '.BaseForm') and not node_is_subclass( node, 'django.forms.models.ModelForm', '.ModelForm'): self.add_message('must-inherit-from-model-form', node=node)
def is_model_test_case_subclass(node): """Checks that node is derivative of TestCase class.""" if not node.name.endswith('Test') and not isinstance(node.parent, ClassDef): return False return node_is_subclass(node, 'django.test.testcases.TestCase')