pylint-django-0.9.4/0000775000175200017520000000000013251563043014573 5ustar senkosenko00000000000000pylint-django-0.9.4/pylint_django/0000775000175200017520000000000013251563043017434 5ustar senkosenko00000000000000pylint-django-0.9.4/pylint_django/augmentations/0000775000175200017520000000000013251563043022312 5ustar senkosenko00000000000000pylint-django-0.9.4/pylint_django/augmentations/__init__.py0000664000175200017520000006410513245076665024445 0ustar senkosenko00000000000000"""Augmentations.""" # pylint: disable=invalid-name from pylint.checkers.base import DocStringChecker, NameChecker from pylint.checkers.design_analysis import MisdesignChecker from pylint.checkers.classes import ClassChecker from pylint.checkers.newstyle import NewStyleConflictChecker from pylint.checkers.variables import VariablesChecker from astroid import InferenceError from pylint_django.compat import ClassDef, ImportFrom, Attribute, django_version from astroid.objects import Super from astroid.scoped_nodes import Class as ScopedClass, Module from pylint.__pkginfo__ import numversion as PYLINT_VERSION from pylint.checkers.typecheck import TypeChecker from pylint.checkers.variables import ScopeConsumer from pylint_django.utils import node_is_subclass, PY3 from pylint_django.compat import inferred from pylint_plugin_utils import augment_visit, suppress_message from django.views.generic.base import View, RedirectView, ContextMixin from django.views.generic.dates import DateMixin, DayMixin, MonthMixin, WeekMixin, YearMixin from django.views.generic.detail import SingleObjectMixin, SingleObjectTemplateResponseMixin, TemplateResponseMixin from django.views.generic.edit import DeletionMixin, FormMixin, ModelFormMixin from django.views.generic.list import MultipleObjectMixin, MultipleObjectTemplateResponseMixin # 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', 'get_queryset', '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', } QS_ATTRS = { 'filter', 'exclude', 'annotate', 'order_by', 'reverse', 'distinct', 'values', 'values_list', 'dates', 'datetimes', 'none', 'all', 'select_related', 'prefetch_related', 'extra', 'defer', 'only', 'using', 'select_for_update', 'raw', 'get', 'create', 'get_or_create', 'update_or_create', 'bulk_create', 'count', 'in_bulk', 'iterator', 'latest', 'earliest', 'first', 'last', 'aggregate', 'exists', 'update', 'delete', 'as_manager', 'expression', 'output_field', } MODELADMIN_ATTRS = { # options 'actions', 'actions_on_top', 'actions_on_bottom', 'actions_selection_counter', 'date_hierarchy', 'empty_value_display', 'exclude', 'fields', 'fieldsets', 'filter_horizontal', 'filter_vertical', 'form', 'formfield_overrides', 'inlines', 'list_display', 'list_display_links', 'list_editable', 'list_filter', 'list_max_show_all', 'list_per_page', 'list_select_related', 'ordering', 'paginator', 'prepopulated_fields', 'preserve_filters', 'radio_fields', 'raw_id_fields', 'readonly_fields', 'save_as', 'save_on_top', 'search_fields', 'show_full_result_count', 'view_on_site', # template options 'add_form_template', 'change_form_template', 'change_list_template', 'delete_confirmation_template', 'delete_selected_confirmation_template', 'object_history_template', } MODEL_ATTRS = { 'DoesNotExist', 'MultipleObjectsReturned', '_base_manager', '_default_manager', '_meta', 'delete', 'get_next_by_date', 'get_previous_by_date', 'objects', 'save', } FIELD_ATTRS = { 'null', 'blank', 'choices', 'db_column', 'db_index', 'db_tablespace', 'default', 'editable', 'error_messages', 'help_text', 'primary_key', 'unique', 'unique_for_date', 'unique_for_month', 'unique_for_year', 'verbose_name', 'validators', } CHAR_FIELD_ATTRS = { 'max_length', } DATE_FIELD_ATTRS = { 'auto_now', 'auto_now_add', } DECIMAL_FIELD_ATTRS = { 'max_digits', 'decimal_places', } FILE_FIELD_ATTRS = { 'upload_to', 'storage', } IMAGE_FIELD_ATTRS = { 'height_field', 'width_field', } IP_FIELD_ATTRS = { 'protocol', 'unpack_ipv4', } SLUG_FIELD_ATTRS = { 'allow_unicode', } FOREIGNKEY_FIELD_ATTRS = { 'limit_choices_to', 'related_name', 'related_query_name', 'to_field', 'db_constraint', 'swappable', } MANYTOMANY_FIELD_ATTRS = { 'related_name', 'related_query_name', 'limit_choices_to', 'symmetrical', 'through', 'through_fields', 'db_table', 'db_constraint', 'swappable', } ONETOONE_FIELD_ATTRS = { 'parent_link', } VIEW_ATTRS = { ( ( '{}.{}'.format(cls.__module__, cls.__name__), '.{}'.format(cls.__name__) ), tuple(cls.__dict__.keys()) ) for cls in ( View, RedirectView, ContextMixin, DateMixin, DayMixin, MonthMixin, WeekMixin, YearMixin, SingleObjectMixin, SingleObjectTemplateResponseMixin, TemplateResponseMixin, DeletionMixin, FormMixin, ModelFormMixin, MultipleObjectMixin, MultipleObjectTemplateResponseMixin, ) } def ignore_import_warnings_for_related_fields(orig_method, self, node): """ Replaces the leave_module method on the VariablesChecker class to prevent unused-import warnings which are caused by the ForeignKey and OneToOneField transformations. By replacing the nodes in the AST with their type rather than the django field, imports of the form 'from django.db.models import OneToOneField' raise an unused-import warning """ consumer = self._to_consume[0] # pylint: disable=W0212 # we can disable this warning ('Access to a protected member _to_consume of a client class') # as it's not actually a client class, but rather, this method is being monkey patched # onto the class and so the access is valid new_things = {} iterat = consumer.to_consume.items if PY3 else consumer.to_consume.iteritems for name, stmts in iterat(): if isinstance(stmts[0], ImportFrom): if any([n[0] in ('ForeignKey', 'OneToOneField') for n in stmts[0].names]): continue new_things[name] = stmts consumer._atomic = ScopeConsumer(new_things, consumer.consumed, consumer.scope_type) # pylint: disable=W0212 self._to_consume = [consumer] # pylint: disable=W0212 return orig_method(self, 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 '_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 _set attribute. # Since this could exist, we will return so as not to raise an # error. return chain() def foreign_key_ids(chain, node): if node.attrname.endswith('_id'): return chain() 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 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.widgets.Widget', '.Widget', '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, 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 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, 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 _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_manager_attribute(node): """Checks that node is attribute of Manager or QuerySet class.""" parents = ('django.db.models.manager.Manager', '.Manager', 'django.db.models.query.QuerySet', '.QuerySet') return _attribute_is_magic(node, MANAGER_ATTRS.union(QS_ATTRS), parents) def is_admin_attribute(node): """Checks that node is attribute of BaseModelAdmin.""" parents = ('django.contrib.admin.options.BaseModelAdmin', '.BaseModelAdmin') return _attribute_is_magic(node, MODELADMIN_ATTRS, parents) def is_model_attribute(node): """Checks that node is attribute of Model.""" parents = ('django.db.models.base.Model', '.Model') return _attribute_is_magic(node, MODEL_ATTRS, parents) def is_field_attribute(node): """Checks that node is attribute of Field.""" parents = ('django.db.models.fields.Field', '.Field') return _attribute_is_magic(node, FIELD_ATTRS, parents) def is_charfield_attribute(node): """Checks that node is attribute of CharField.""" parents = ('django.db.models.fields.CharField', '.CharField') return _attribute_is_magic(node, CHAR_FIELD_ATTRS, parents) def is_datefield_attribute(node): """Checks that node is attribute of DateField.""" parents = ('django.db.models.fields.DateField', '.DateField') return _attribute_is_magic(node, DATE_FIELD_ATTRS, parents) def is_decimalfield_attribute(node): """Checks that node is attribute of DecimalField.""" parents = ('django.db.models.fields.DecimalField', '.DecimalField') return _attribute_is_magic(node, DECIMAL_FIELD_ATTRS, parents) def is_filefield_attribute(node): """Checks that node is attribute of FileField.""" parents = ('django.db.models.fields.files.FileField', '.FileField') return _attribute_is_magic(node, FILE_FIELD_ATTRS, parents) def is_imagefield_attribute(node): """Checks that node is attribute of ImageField.""" parents = ('django.db.models.fields.files.ImageField', '.ImageField') return _attribute_is_magic(node, IMAGE_FIELD_ATTRS, parents) def is_ipfield_attribute(node): """Checks that node is attribute of GenericIPAddressField.""" parents = ('django.db.models.fields.GenericIPAddressField', '.GenericIPAddressField') return _attribute_is_magic(node, IP_FIELD_ATTRS, parents) def is_slugfield_attribute(node): """Checks that node is attribute of SlugField.""" parents = ('django.db.models.fields.SlugField', '.SlugField') return _attribute_is_magic(node, SLUG_FIELD_ATTRS, parents) def is_foreignkeyfield_attribute(node): """Checks that node is attribute of ForeignKey.""" parents = ('django.db.models.fields.related.ForeignKey', '.ForeignKey') return _attribute_is_magic(node, FOREIGNKEY_FIELD_ATTRS, parents) def is_manytomanyfield_attribute(node): """Checks that node is attribute of ManyToManyField.""" parents = ('django.db.models.fields.related.ManyToManyField', '.ManyToManyField') return _attribute_is_magic(node, MANYTOMANY_FIELD_ATTRS, parents) def is_onetoonefield_attribute(node): """Checks that node is attribute of OneToOneField.""" parents = ('django.db.models.fields.related.OneToOneField', '.OneToOneField') return _attribute_is_magic(node, ONETOONE_FIELD_ATTRS, parents) 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 generic_is_view_attribute(parents, attrs): """Generates is_X_attribute function for given parents and attrs.""" def is_attribute(node): return _attribute_is_magic(node, attrs, parents) return is_attribute 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 is_model_view_subclass_unused_argument(node): """ Checks that node is get or post method of the View class and it has valid arguments. TODO: Bad checkings, need to be more smart. """ if not is_model_view_subclass_method_shouldnt_be_function(node): return False return 'request' in node.argnames() 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_media_valid_attributes(node): """Suppress warnings for valid attributes of Media class.""" if node.name not in ('js', ): return False parent = node.parent while parent and not isinstance(parent, ScopedClass): parent = parent.parent if parent is None or parent.name != "Media": return False return True def is_templatetags_module_valid_constant(node): """Suppress warnings for valid constants in templatetags module.""" if node.name not in ('register', ): return False parent = node.parent while not isinstance(parent, Module): parent = parent.parent if "templatetags." not in parent.name: return False return True def is_urls_module_valid_constant(node): """Suppress warnings for valid constants in urls module.""" if node.name not in ('urlpatterns', 'app_name'): return False parent = node.parent while not isinstance(parent, Module): parent = parent.parent if not parent.name.endswith('urls'): return False return True def allow_meta_protected_access(node): if django_version >= (1, 8): return node.attrname == '_meta' else: return False def is_class(class_name): """Shortcut for node_is_subclass.""" return lambda node: node_is_subclass(node, class_name) def wrap(orig_method, with_method): def wrap_func(*args, **kwargs): with_method(orig_method, *args, **kwargs) return wrap_func # The names of some visit functions changed in this commit: # https://bitbucket.org/logilab/pylint/commits/c94ee95abaa5737f13b91626fe321150c0ddd140 def _visit_class(checker): return getattr(checker, 'visit_classdef' if PYLINT_VERSION >= (1, 5) else 'visit_class') def _visit_attribute(checker): return getattr(checker, 'visit_attribute' if PYLINT_VERSION >= (1, 5) else 'visit_getattr') def _leave_class(checker): return getattr(checker, 'leave_classdef' if PYLINT_VERSION >= (1, 5) else 'leave_class') def _leave_function(checker): return getattr(checker, 'leave_functiondef' if PYLINT_VERSION >= (1, 5) else 'leave_function') def _visit_assignname(checker): return getattr(checker, 'visit_assignname' if PYLINT_VERSION >= (1, 5) else 'visit_assname') def _visit_assign(checker): return getattr(checker, 'visit_assign') def apply_augmentations(linter): """Apply augmentation and suppression rules.""" augment_visit(linter, _visit_attribute(TypeChecker), foreign_key_sets) augment_visit(linter, _visit_attribute(TypeChecker), foreign_key_ids) suppress_message(linter, _visit_attribute(TypeChecker), 'E1101', is_model_field_display_method) # supress errors when accessing magical class attributes suppress_message(linter, _visit_attribute(TypeChecker), 'E1101', is_manager_attribute) suppress_message(linter, _visit_attribute(TypeChecker), 'E1101', is_admin_attribute) suppress_message(linter, _visit_attribute(TypeChecker), 'E1101', is_model_attribute) suppress_message(linter, _visit_attribute(TypeChecker), 'E1101', is_field_attribute) suppress_message(linter, _visit_attribute(TypeChecker), 'E1101', is_charfield_attribute) suppress_message(linter, _visit_attribute(TypeChecker), 'E1101', is_datefield_attribute) suppress_message(linter, _visit_attribute(TypeChecker), 'E1101', is_decimalfield_attribute) suppress_message(linter, _visit_attribute(TypeChecker), 'E1101', is_filefield_attribute) suppress_message(linter, _visit_attribute(TypeChecker), 'E1101', is_imagefield_attribute) suppress_message(linter, _visit_attribute(TypeChecker), 'E1101', is_ipfield_attribute) suppress_message(linter, _visit_attribute(TypeChecker), 'E1101', is_slugfield_attribute) suppress_message(linter, _visit_attribute(TypeChecker), 'E1101', is_foreignkeyfield_attribute) suppress_message(linter, _visit_attribute(TypeChecker), 'E1101', is_manytomanyfield_attribute) suppress_message(linter, _visit_attribute(TypeChecker), 'E1101', is_onetoonefield_attribute) for parents, attrs in VIEW_ATTRS: suppress_message(linter, _visit_attribute(TypeChecker), 'E1101', generic_is_view_attribute(parents, attrs)) # formviews have too many ancestors, there's nothing the user of the library can do about that suppress_message(linter, _visit_class(MisdesignChecker), 'too-many-ancestors', is_class('django.views.generic.edit.FormView')) # model forms have no __init__ method anywhere in their bases suppress_message(linter, _visit_class(ClassChecker), 'W0232', is_class('django.forms.models.ModelForm')) # forms implement __getitem__ but not __len__, thus raising a "Badly implemented container" warning which # we will suppress. NOTE: removed from pylint, https://github.com/PyCQA/pylint/issues/112 # keeping here in case it gets re-implemented # suppress_message(linter, _leave_class(MisdesignChecker), 'R0924', is_class('django.forms.forms.Form')) # suppress_message(linter, _leave_class(MisdesignChecker), 'R0924', is_class('django.forms.models.ModelForm')) # Meta suppress_message(linter, _visit_class(DocStringChecker), 'missing-docstring', is_model_meta_subclass) suppress_message(linter, _visit_class(NewStyleConflictChecker), 'old-style-class', is_model_meta_subclass) suppress_message(linter, _visit_class(ClassChecker), 'no-init', is_model_meta_subclass) suppress_message(linter, _leave_class(MisdesignChecker), 'too-few-public-methods', is_model_meta_subclass) suppress_message(linter, _visit_attribute(ClassChecker), 'protected-access', allow_meta_protected_access) # Media suppress_message(linter, _visit_assignname(NameChecker), 'C0103', is_model_media_valid_attributes) suppress_message(linter, _visit_class(DocStringChecker), 'missing-docstring', is_model_media_subclass) suppress_message(linter, _visit_class(NewStyleConflictChecker), 'old-style-class', is_model_media_subclass) suppress_message(linter, _visit_class(ClassChecker), 'no-init', is_model_media_subclass) suppress_message(linter, _leave_class(MisdesignChecker), 'too-few-public-methods', is_model_media_subclass) # Admin # Too many public methods (40+/20) # TODO: Count public methods of django.contrib.admin.options.ModelAdmin and increase # MisdesignChecker.config.max_public_methods to this value to count only user' methods. # nb_public_methods = 0 # for method in node.methods(): # if not method.name.startswith('_'): # nb_public_methods += 1 suppress_message(linter, _leave_class(MisdesignChecker), 'R0904', is_model_admin_subclass) # Tests suppress_message(linter, _leave_class(MisdesignChecker), 'R0904', is_model_test_case_subclass) # View # Method could be a function (get, post) suppress_message(linter, _leave_function(ClassChecker), 'R0201', is_model_view_subclass_method_shouldnt_be_function) # Unused argument 'request' (get, post) suppress_message(linter, _leave_function(VariablesChecker), 'W0613', is_model_view_subclass_unused_argument) # django-mptt suppress_message(linter, _visit_class(DocStringChecker), 'missing-docstring', is_model_mpttmeta_subclass) suppress_message(linter, _visit_class(NewStyleConflictChecker), 'old-style-class', is_model_mpttmeta_subclass) suppress_message(linter, _visit_class(ClassChecker), 'W0232', is_model_mpttmeta_subclass) suppress_message(linter, _leave_class(MisdesignChecker), 'too-few-public-methods', is_model_mpttmeta_subclass) # factory_boy's DjangoModelFactory suppress_message(linter, _visit_class(DocStringChecker), 'missing-docstring', is_model_factory_meta_subclass) suppress_message(linter, _visit_class(NewStyleConflictChecker), 'old-style-class', is_model_factory_meta_subclass) suppress_message(linter, _visit_class(ClassChecker), 'W0232', is_model_factory_meta_subclass) suppress_message(linter, _leave_class(MisdesignChecker), 'too-few-public-methods', is_model_factory_meta_subclass) # ForeignKey and OneToOneField # Must update this in a thread safe way to support the parallel option on pylint (-j) current_leave_module = VariablesChecker.leave_module if current_leave_module.__name__ == 'leave_module': # current_leave_module is not wrapped # Two threads may hit the next assignment concurrently, but the result is the same VariablesChecker.leave_module = wrap(current_leave_module, ignore_import_warnings_for_related_fields) # VariablesChecker.leave_module is now wrapped # else VariablesChecker.leave_module is already wrapped pylint-django-0.9.4/pylint_django/checkers/0000775000175200017520000000000013251563043021223 5ustar senkosenko00000000000000pylint-django-0.9.4/pylint_django/checkers/__init__.py0000664000175200017520000000047613250467073023350 0ustar senkosenko00000000000000"""Checkers.""" from pylint_django.checkers.django_installed import DjangoInstalledChecker from pylint_django.checkers.models import ModelChecker def register_checkers(linter): """Register checkers.""" linter.register_checker(ModelChecker(linter)) linter.register_checker(DjangoInstalledChecker(linter)) pylint-django-0.9.4/pylint_django/checkers/django_installed.py0000664000175200017520000000210213230127102025056 0ustar senkosenko00000000000000from __future__ import absolute_import from pylint.checkers import BaseChecker from pylint.checkers.utils import check_messages from pylint_django.__pkginfo__ import BASE_ID class DjangoInstalledChecker(BaseChecker): name = 'django-installed-checker' msgs = { 'F%s01' % BASE_ID: ("Django is not available on the PYTHONPATH", 'django-not-available', "Django could not be imported by the pylint-django plugin, so most Django related " "improvements to pylint will fail."), 'W%s99' % BASE_ID: ('Placeholder message to prevent disabling of checker', 'django-not-available-placeholder', 'PyLint does not recognise checkers as being enabled unless they have at least' ' one message which is not fatal...') } @check_messages('django-not-available') def close(self): try: __import__('django') except ImportError: self.add_message('F%s01' % BASE_ID) pylint-django-0.9.4/pylint_django/checkers/models.py0000664000175200017520000001143213245076665023075 0ustar senkosenko00000000000000"""Models.""" from astroid import Const from pylint_django.compat import ClassDef, FunctionDef, inferred, AssignName from astroid.nodes import Assign from pylint.interfaces import IAstroidChecker from pylint.checkers.utils import check_messages from pylint.checkers import BaseChecker from pylint.__pkginfo__ import numversion as PYLINT_VERSION from pylint_django.__pkginfo__ import BASE_ID from pylint_django.utils import node_is_subclass, PY3 MESSAGES = { 'E%d01' % BASE_ID: ("__unicode__ on a model must be callable (%s)", 'model-unicode-not-callable', "Django models require a callable __unicode__ method"), 'W%d01' % BASE_ID: ("No __unicode__ method on model (%s)", 'model-missing-unicode', "Django models should implement a __unicode__ method for string representation"), 'W%d02' % BASE_ID: ("Found __unicode__ method on model (%s). Python3 uses __str__.", 'model-has-unicode', "Django models should not implement a __unicode__ " "method for string representation when using Python3"), 'W%d03' % BASE_ID: ("Model does not explicitly define __unicode__ (%s)", 'model-no-explicit-unicode', "Django models should implement a __unicode__ method for string representation. " "A parent class of this model does, but ideally all models should be explicit.") } def _is_meta_with_abstract(node): if isinstance(node, ClassDef) and node.name == 'Meta': for meta_child in node.get_children(): if not isinstance(meta_child, Assign): continue if not meta_child.targets[0].name == 'abstract': continue if not isinstance(meta_child.value, Const): continue # TODO: handle tuple assignment? # eg: # abstract, something_else = True, 1 if meta_child.value.value: # this class is abstract return True return False class ModelChecker(BaseChecker): """Django model checker.""" __implements__ = IAstroidChecker name = 'django-model-checker' msgs = MESSAGES # XXX: there's a bug in pylint's backwards compatability logic after changing # visit method names from visit_class to visit_classdef, see # https://bitbucket.org/logilab/pylint/issues/711/new-node-visit-methods-not-backwards if PYLINT_VERSION < (1, 5): @check_messages('model-missing-unicode') def visit_class(self, node): return self._visit_classdef(node) else: @check_messages('model-missing-unicode') def visit_classdef(self, node): return self._visit_classdef(node) 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/PyCQA/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) pylint-django-0.9.4/pylint_django/tests/0000775000175200017520000000000013251563043020576 5ustar senkosenko00000000000000pylint-django-0.9.4/pylint_django/tests/input/0000775000175200017520000000000013251563043021735 5ustar senkosenko00000000000000pylint-django-0.9.4/pylint_django/tests/input/__init__.py0000664000175200017520000000000013247332665024045 0ustar senkosenko00000000000000pylint-django-0.9.4/pylint_django/tests/input/external_django_tables2_noerror_meta_class.py0000664000175200017520000000065213247332665033044 0ustar senkosenko00000000000000# Check that Meta class definitions for django_tables2 classes # don't produce old-style-class warnings, see # https://github.com/PyCQA/pylint-django/issues/56 # pylint: disable=missing-docstring,too-few-public-methods from django.db import models import django_tables2 as tables class SimpleModel(models.Model): name = models.CharField() class SimpleTable(tables.Table): class Meta: model = SimpleModel pylint-django-0.9.4/pylint_django/tests/input/external_drf_noerror_serializer.py0000664000175200017520000000034313247332665030774 0ustar senkosenko00000000000000""" Checks that Pylint does not complain about DRF serializers """ # pylint: disable=C0111,W5101 from rest_framework import serializers class TestSerializerSubclass(serializers.ModelSerializer): class Meta: pass pylint-django-0.9.4/pylint_django/tests/input/external_drf_noerror_serializer.rc0000664000175200017520000000005013247332665030743 0ustar senkosenko00000000000000[testoptions] requires = rest_framework pylint-django-0.9.4/pylint_django/tests/input/external_psycopg2_noerror_postgres_fields.py0000664000175200017520000000313213247332665033011 0ustar senkosenko00000000000000""" Checks that Pylint does not complain Postgres model fields. """ # pylint: disable=C0111,W5101 from __future__ import print_function from django.contrib.postgres import fields from django.db import models class PostgresFieldsModel(models.Model): arrayfield = fields.ArrayField(models.CharField()) hstorefield = fields.HStoreField() jsonfield = fields.JSONField() rangefield = fields.RangeField() integerrangefield = fields.IntegerRangeField() bigintegerrangefield = fields.BigIntegerRangeField() floatrangefield = fields.FloatRangeField() datetimerangefield = fields.DateTimeRangeField() daterangefield = fields.DateRangeField() def arrayfield_tests(self): sorted_array = self.arrayfield.sort() print(sorted_array) def dictfield_tests(self): print(self.hstorefield.keys()) print(self.hstorefield.values()) print(self.hstorefield.update({'foo': 'bar'})) print(self.jsonfield.keys()) print(self.jsonfield.values()) print(self.jsonfield.update({'foo': 'bar'})) def rangefield_tests(self): print(self.rangefield.lower) print(self.rangefield.upper) print(self.integerrangefield.lower) print(self.integerrangefield.upper) print(self.bigintegerrangefield.lower) print(self.bigintegerrangefield.upper) print(self.floatrangefield.lower) print(self.floatrangefield.upper) print(self.datetimerangefield.lower) print(self.datetimerangefield.upper) print(self.daterangefield.lower) print(self.daterangefield.upper) pylint-django-0.9.4/pylint_django/tests/input/external_psycopg2_noerror_postgres_fields.rc0000664000175200017520000000004213247332665032762 0ustar senkosenko00000000000000[testoptions] requires = psycopg2 pylint-django-0.9.4/pylint_django/tests/input/func_model_does_not_use_unicode_py33.py0000664000175200017520000000062413247332665031567 0ustar senkosenko00000000000000""" Ensures that under PY3 django models with a __unicode__ method are flagged """ # pylint: disable=missing-docstring from django.db import models class SomeModel(models.Model): # [model-has-unicode] something = models.CharField(max_length=255) # no __str__ method something.something_else = 1 def lala(self): pass def __unicode__(self): return self.something pylint-django-0.9.4/pylint_django/tests/input/func_model_does_not_use_unicode_py33.txt0000664000175200017520000000014313247332665031752 0ustar senkosenko00000000000000model-has-unicode:9:SomeModel:Found __unicode__ method on model (SomeModel). Python3 uses __str__. pylint-django-0.9.4/pylint_django/tests/input/func_noerror_classviews.py0000664000175200017520000000071713247332665027271 0ustar senkosenko00000000000000""" Checks that Pylint does not complain about attributes and methods when using Class-based Views """ # pylint: disable=missing-docstring from django.views.generic import TemplateView class BoringView(TemplateView): # ensure that args, kwargs and request are not thrown up as errors def get_context_data(self, **kwargs): return { 'request': self.request, 'args': self.args, 'kwargs': self.kwargs } pylint-django-0.9.4/pylint_django/tests/input/func_noerror_duplicate_except_doesnotexist.py0000664000175200017520000000101313247332665033226 0ustar senkosenko00000000000000""" Checks that Pylint does not complain about duplicate except blocks catching DoesNotExist exceptions: https://github.com/PyCQA/pylint-django/issues/81 """ # pylint: disable=missing-docstring from django.db import models class Book(models.Model): name = models.CharField(max_length=100) class Author(models.Model): name = models.CharField(max_length=100) def dummy_func(): try: print("foo") except Book.DoesNotExist: print("bar") except Author.DoesNotExist: print("baz") pylint-django-0.9.4/pylint_django/tests/input/func_noerror_foreign_key_attributes.py0000664000175200017520000000114613247332665031652 0ustar senkosenko00000000000000""" Checks that Pylint does not complain about foreign key sets on models """ # pylint: disable=missing-docstring from django.db import models class SomeModel(models.Model): name = models.CharField(max_length=20) timestamp = models.DateTimeField() class OtherModel(models.Model): something = models.ForeignKey(SomeModel, on_delete=models.CASCADE) elsething = models.OneToOneField(SomeModel, on_delete=models.CASCADE) def something_doer(self): part_a = '%s - %s' % (self.something.name, self.something.timestamp) part_b = self.elsething.name return part_a, part_b pylint-django-0.9.4/pylint_django/tests/input/func_noerror_foreign_key_ids.py0000664000175200017520000000067313247332665030247 0ustar senkosenko00000000000000""" Checks that Pylint does not complain about foreign key id access """ # pylint: disable=missing-docstring,wrong-import-position from django.db import models class SomeModel(models.Model): count = models.IntegerField() class SomeOtherModel(models.Model): some_model = models.ForeignKey(SomeModel, on_delete=models.CASCADE) number = models.IntegerField() def do_something(self): self.number = self.some_model_id pylint-django-0.9.4/pylint_django/tests/input/func_noerror_foreign_key_sets.py0000664000175200017520000000141113247332665030435 0ustar senkosenko00000000000000""" Checks that Pylint does not complain about foreign key sets on models """ # pylint: disable=missing-docstring from django.db import models class SomeModel(models.Model): name = models.CharField(max_length=20) def get_others(self): return self.othermodel_set.all() def get_first(self): return self.othermodel_set.first() class OtherModel(models.Model): count = models.IntegerField() something = models.ForeignKey(SomeModel, on_delete=models.CASCADE) class ThirdModel(models.Model): whatever = models.ForeignKey(SomeModel, related_name='whatevs', on_delete=models.CASCADE) def count_whatevers(): if SomeModel().whatevs.exists(): return SomeModel().whatevs.count() return -1 pylint-django-0.9.4/pylint_django/tests/input/func_noerror_foreignkeys.py0000664000175200017520000000265113247332665027432 0ustar senkosenko00000000000000""" Checks that Pylint does not complain about various methods on Django model fields. """ # pylint: disable=missing-docstring,wrong-import-position from django.db import models from django.db.models import ForeignKey, OneToOneField class Author(models.Model): author_name = models.CharField(max_length=100) class ISBN(models.Model): value = models.CharField(max_length=100) class Book(models.Model): book_name = models.CharField(max_length=100) author = models.ForeignKey('Author', on_delete=models.CASCADE) isbn = models.OneToOneField(ISBN, on_delete=models.CASCADE) def get_isbn(self): return self.isbn.value def get_author_name(self): return self.author.author_name class Fruit(models.Model): fruit_name = models.CharField(max_length=20) class Seed(models.Model): fruit = ForeignKey(Fruit, on_delete=models.CASCADE) def get_fruit_name(self): return self.fruit.fruit_name class User(models.Model): username = models.CharField(max_length=32) class UserProfile(models.Model): user = OneToOneField(User, on_delete=models.CASCADE) def get_username(self): return self.user.username class UserPreferences(models.Model): """ Used for testing FK which refers to another model by string, not model class, see https://github.com/PyCQA/pylint-django/issues/35 """ user = ForeignKey('User', on_delete=models.CASCADE) pylint-django-0.9.4/pylint_django/tests/input/func_noerror_form_fields.py0000664000175200017520000000443313247332665027376 0ustar senkosenko00000000000000""" Checks that Pylint does not complain about various methods on Django form forms. """ # pylint: disable=missing-docstring,R0904 from __future__ import print_function from datetime import datetime, date from django import forms class ManyFieldsForm(forms.Form): booleanfield = forms.BooleanField() charfield = forms.CharField(max_length=40, null=True) datetimefield = forms.DateTimeField(auto_now_add=True) datefield = forms.DateField(auto_now_add=True) decimalfield = forms.DecimalField(max_digits=5, decimal_places=2) durationfield = forms.DurationField() emailfield = forms.EmailField() filefield = forms.FileField(name='test_file', upload_to='test') filepathfield = forms.FilePathField(path='/some/path') floatfield = forms.FloatField() genericipaddressfield = forms.GenericIPAddressField() imagefield = forms.ImageField(name='test_image', upload_to='test') intfield = forms.IntegerField(null=True) nullbooleanfield = forms.NullBooleanField() slugfield = forms.SlugField() timefield = forms.TimeField() urlfield = forms.URLField() def boolean_field_tests(self): print(self.booleanfield | True) print(self.nullbooleanfield | True) def string_field_tests(self): print(self.charfield.strip()) print(self.charfield.upper()) print(self.charfield.replace('x', 'y')) print(self.filepathfield.strip()) print(self.filepathfield.upper()) print(self.filepathfield.replace('x', 'y')) print(self.emailfield.strip()) print(self.emailfield.upper()) print(self.emailfield.replace('x', 'y')) def datetimefield_tests(self): now = datetime.now() print(now - self.datetimefield) print(self.datetimefield.ctime()) def datefield_tests(self): now = date.today() print(now - self.datefield) print(self.datefield.isoformat()) def decimalfield_tests(self): print(self.decimalfield.adjusted()) def durationfield_tests(self): now = datetime.now() print(now - self.durationfield) print(self.durationfield.total_seconds()) def filefield_tests(self): print(self.filefield) print(self.imagefield) def numberfield_tests(self): print(self.intfield + 5) pylint-django-0.9.4/pylint_django/tests/input/func_noerror_forms_py33.py0000664000175200017520000000107313247332665027106 0ustar senkosenko00000000000000""" Checks that Pylint does not complain about django Forms """ # pylint: disable=missing-docstring,wrong-import-position from django import forms class TestForm(forms.Form): class Meta: pass some_field = forms.CharField() def clean(self): print(self.cleaned_data) print(self.fields) print(self.error_class) class TestModelForm(forms.ModelForm): class Meta: pass class TestFormSubclass(forms.Form): class Meta: pass class TestModelFormSubclass(forms.ModelForm): class Meta: pass pylint-django-0.9.4/pylint_django/tests/input/func_noerror_formview_ancestors.py0000664000175200017520000000032213247332665031015 0ustar senkosenko00000000000000""" Checks that Pylint does not complain about django FormViews having too many ancestors """ # pylint: disable=missing-docstring from django.views.generic import FormView class SomeView(FormView): pass pylint-django-0.9.4/pylint_django/tests/input/func_noerror_ignore_meta_subclass.py0000664000175200017520000000044713247332665031276 0ustar senkosenko00000000000000""" This test ensures that a 'Meta' class defined on a Django model does not raise warnings such as 'old-style-class' and 'too-few-public-methods' """ # pylint: disable=missing-docstring from django.db import models class SomeModel(models.Model): class Meta: ordering = ('-id',) pylint-django-0.9.4/pylint_django/tests/input/func_noerror_import_q.py0000664000175200017520000000022213247332665026727 0ustar senkosenko00000000000000""" Checks that Pylint does not complain about import of Q. """ # pylint: disable=missing-docstring,unused-import from django.db.models import Q pylint-django-0.9.4/pylint_django/tests/input/func_noerror_issue_46.py0000664000175200017520000000032213247332665026537 0ustar senkosenko00000000000000""" Checks that Pylint does not complain about raising DoesNotExist """ # pylint: disable=missing-docstring from django.db import models class SomeModel(models.Model): pass raise SomeModel.DoesNotExist pylint-django-0.9.4/pylint_django/tests/input/func_noerror_manytomanyfield.py0000664000175200017520000000126713247332665030307 0ustar senkosenko00000000000000""" Checks that Pylint does not complain about various methods on many-to-many relationships """ # pylint: disable=missing-docstring from django.db import models class Book(models.Model): name = models.CharField(max_length=100) good = models.BooleanField(default=False) class Author(models.Model): name = models.CharField(max_length=100) wrote = models.ManyToManyField(Book, verbose_name="Book", related_name='books') def get_good_books(self): return self.wrote.filter(good=True) def is_author_of(self, book): return book in list(self.wrote.all()) def wrote_how_many(self): return self.wrote.count() pylint-django-0.9.4/pylint_django/tests/input/func_noerror_model_fields.py0000664000175200017520000000600013247332665027523 0ustar senkosenko00000000000000""" Checks that Pylint does not complain about various methods on Django model fields. """ # pylint: disable=missing-docstring from __future__ import print_function from datetime import datetime, date from decimal import Decimal from django.db import models class LotsOfFieldsModel(models.Model): bigintegerfield = models.BigIntegerField() booleanfield = models.BooleanField(default=True) charfield = models.CharField(max_length=40, null=True) commaseparatedintegerfield = models.CommaSeparatedIntegerField() datetimefield = models.DateTimeField(auto_now_add=True) datefield = models.DateField(auto_now_add=True) decimalfield = models.DecimalField(max_digits=5, decimal_places=2) durationfield = models.DurationField() emailfield = models.EmailField() filefield = models.FileField(name='test_file', upload_to='test') filepathfield = models.FilePathField() floatfield = models.FloatField() genericipaddressfield = models.GenericIPAddressField() imagefield = models.ImageField(name='test_image', upload_to='test') ipaddressfield = models.IPAddressField() intfield = models.IntegerField(null=True) nullbooleanfield = models.NullBooleanField() positiveintegerfield = models.PositiveIntegerField() positivesmallintegerfield = models.PositiveSmallIntegerField() slugfield = models.SlugField() smallintegerfield = models.SmallIntegerField() textfield = models.TextField() timefield = models.TimeField() urlfield = models.URLField() def boolean_field_tests(self): print(self.booleanfield | True) print(self.nullbooleanfield | True) def string_field_tests(self): print(self.charfield.strip()) print(self.charfield.upper()) print(self.charfield.replace('x', 'y')) print(self.filepathfield.strip()) print(self.filepathfield.upper()) print(self.filepathfield.replace('x', 'y')) print(self.emailfield.strip()) print(self.emailfield.upper()) print(self.emailfield.replace('x', 'y')) print(self.textfield.strip()) print(self.textfield.upper()) print(self.textfield.replace('x', 'y')) def datetimefield_tests(self): now = datetime.now() print(now - self.datetimefield) print(self.datetimefield.ctime()) def datefield_tests(self): now = date.today() print(now - self.datefield) print(self.datefield.isoformat()) def decimalfield_tests(self): print(self.decimalfield.compare(Decimal('1.4'))) def durationfield_tests(self): now = datetime.now() print(now - self.durationfield) print(self.durationfield.total_seconds()) def filefield_tests(self): print(self.filefield.file) print(self.imagefield.file) def numberfield_tests(self): print(self.intfield + 5) print(self.bigintegerfield + 4) print(self.smallintegerfield + 3) print(self.positiveintegerfield + 2) print(self.positivesmallintegerfield + 1) pylint-django-0.9.4/pylint_django/tests/input/func_noerror_model_methods.py0000664000175200017520000000064213247332665027726 0ustar senkosenko00000000000000""" Checks that Pylint does not complain about using Model and Manager methods """ # pylint: disable=missing-docstring from django.db import models class SomeModel(models.Model): pass if __name__ == '__main__': MODEL = SomeModel() MODEL.save() MODEL.delete() COUNT = SomeModel.objects.count() # added in django 1.6 FIRST = SomeModel.objects.first() LAST = SomeModel.objects.last() pylint-django-0.9.4/pylint_django/tests/input/func_noerror_model_unicode_callable.py0000664000175200017520000000053613247332665031532 0ustar senkosenko00000000000000""" Ensures that django models without a __unicode__ method are flagged """ # pylint: disable=missing-docstring,wrong-import-position from django.db import models def external_unicode_func(model): return model.something class SomeModel(models.Model): something = models.CharField(max_length=255) __unicode__ = external_unicode_func pylint-django-0.9.4/pylint_django/tests/input/func_noerror_model_unicode_lambda.py0000664000175200017520000000044413247332665031211 0ustar senkosenko00000000000000""" Ensures that django models without a __unicode__ method are flagged """ # pylint: disable=missing-docstring,wrong-import-position from django.db import models class SomeModel(models.Model): something = models.CharField(max_length=255) __unicode__ = lambda s: str(s.something) pylint-django-0.9.4/pylint_django/tests/input/func_noerror_models_py33.py0000664000175200017520000000125013247332665027240 0ustar senkosenko00000000000000""" Checks that Pylint does not complain about a fairly standard Django Model """ # pylint: disable=missing-docstring from django.db import models class SomeModel(models.Model): class Meta: pass some_field = models.CharField(max_length=20) other_fields = models.ManyToManyField('AnotherModel') def stuff(self): try: print(self._meta) print(self.other_fields.all()[0]) except self.DoesNotExist: print('does not exist') except self.MultipleObjectsReturned: print('lala') print(self.get_some_field_display()) class SubclassModel(SomeModel): class Meta: pass pylint-django-0.9.4/pylint_django/tests/input/func_noerror_protected_meta_access.py0000664000175200017520000000126213247332665031422 0ustar senkosenko00000000000000""" Tests to make sure that access to _meta on a model does not raise a protected-access warning, as it is part of the public API since Django 1.8 (see https://github.com/PyCQA/pylint-django/issues/66, and https://docs.djangoproject.com/en/1.9/ref/models/meta/) """ # pylint: disable=missing-docstring from __future__ import print_function from django.db import models class ModelWhichLikesMeta(models.Model): ursuary = models.BooleanField(default=False) def do_a_thing(self): return self._meta.get_field('ursuary') if __name__ == '__main__': MODEL = ModelWhichLikesMeta() MODEL.save() print(MODEL._meta.get_field('ursuary')) print(MODEL.do_a_thing()) pylint-django-0.9.4/pylint_django/tests/input/func_noerror_ugettext_lazy_format.py0000664000175200017520000000031013247332665031353 0ustar senkosenko00000000000000""" Checks that Pylint does not complain about django lazy proxy when using ugettext_lazy """ from django.utils.translation import ugettext_lazy ugettext_lazy('{something}').format(something='lala') pylint-django-0.9.4/pylint_django/tests/input/func_noerror_unicode_py2_compatible.py0000664000175200017520000000071313247332665031521 0ustar senkosenko00000000000000""" Ensures that no '__unicode__ missing' warning is emitted if the Django python3/2 compatability dectorator is used See https://github.com/PyCQA/pylint-django/issues/10 """ # pylint: disable=missing-docstring from django.utils.encoding import python_2_unicode_compatible from django.db import models @python_2_unicode_compatible class ModelName(models.Model): name = models.CharField(max_length=200) def __str__(self): return self.name pylint-django-0.9.4/pylint_django/tests/input/func_noerror_urls.py0000664000175200017520000000074113247332665026070 0ustar senkosenko00000000000000""" Checks that Pylint does not complain about attributes and methods when creating a typical urls.py """ # pylint: disable=missing-docstring # pylint: disable=C0103 # ^ eventually we should be able to override or update the # CONST_NAME_RGX value from django.views.generic import TemplateView from django.conf.urls import url class BoringView(TemplateView): pass urlpatterns = [ url(r'^something', BoringView.as_view(), name='something'), ] pylint-django-0.9.4/pylint_django/tests/input/func_noerror_uuid_field.py0000664000175200017520000000110013247332665027202 0ustar senkosenko00000000000000""" Checks that Pylint does not complain about UUID fields. """ # pylint: disable=C0111,W5101 from __future__ import print_function from django.db import models class LotsOfFieldsModel(models.Model): uuidfield = models.UUIDField() def uuidfield_tests(self): print(self.uuidfield.bytes) print(self.uuidfield.bytes_le) print(self.uuidfield.fields[2]) print(self.uuidfield.hex) # print(self.uuidfield.int) # Don't know how to properly check this one print(self.uuidfield.variant) print(self.uuidfield.version) pylint-django-0.9.4/pylint_django/tests/__init__.py0000664000175200017520000000000013247332665022706 0ustar senkosenko00000000000000pylint-django-0.9.4/pylint_django/tests/test_func.py0000664000175200017520000000262413247332665023157 0ustar senkosenko00000000000000 import os import sys import pytest import pylint # because there's no __init__ file in pylint/test/ sys.path.append(os.path.join(os.path.dirname(pylint.__file__), 'test')) import test_functional # alter sys.path again because the tests now live as a subdirectory # of pylint_django sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..')) from pylint_django.compat import django_version class PylintDjangoLintModuleTest(test_functional.LintModuleTest): """ Only used so that we can load this plugin into the linter! """ def __init__(self, test_file): super(PylintDjangoLintModuleTest, self).__init__(test_file) self._linter.load_plugin_modules(['pylint_django']) def get_tests(): HERE = os.path.dirname(os.path.abspath(__file__)) input_dir = os.path.join(HERE, 'input') suite = [] for fname in os.listdir(input_dir): if fname != '__init__.py' and fname.endswith('.py'): suite.append(test_functional.FunctionalTestFile(input_dir, fname)) return suite TESTS = get_tests() TESTS_NAMES = [t.base for t in TESTS] @pytest.mark.parametrize("test_file", TESTS, ids=TESTS_NAMES) def test_everything(test_file): # copied from pylint.tests.test_functional.test_functional LintTest = PylintDjangoLintModuleTest(test_file) LintTest.setUp() LintTest._runTest() if __name__ == '__main__': sys.exit(pytest.main(sys.argv)) pylint-django-0.9.4/pylint_django/transforms/0000775000175200017520000000000013251563043021632 5ustar senkosenko00000000000000pylint-django-0.9.4/pylint_django/transforms/transforms/0000775000175200017520000000000013251563043024030 5ustar senkosenko00000000000000pylint-django-0.9.4/pylint_django/transforms/transforms/django_contrib_postgres_fields.py0000664000175200017520000000151513230127102032627 0ustar senkosenko00000000000000from django.contrib.postgres import fields as django_fields from psycopg2 import extras # -------- # lists class ArrayField(list, django_fields.ArrayField): pass # -------- # dicts class HStoreField(dict, django_fields.HStoreField): pass class JSONField(dict, django_fields.JSONField): pass # -------- # ranges class RangeField(extras.Range, django_fields.RangeField): pass class IntegerRangeField(extras.NumericRange, django_fields.IntegerRangeField): pass class BigIntegerRangeField(extras.NumericRange, django_fields.BigIntegerRangeField): pass class FloatRangeField(extras.NumericRange, django_fields.FloatRangeField): pass class DateTimeRangeField(extras.DateTimeTZRange, django_fields.DateRangeField): pass class DateRangeField(extras.DateRange, django_fields.DateRangeField): pass pylint-django-0.9.4/pylint_django/transforms/transforms/django_core_handlers_wsgi.py0000664000175200017520000000025313230127102031552 0ustar senkosenko00000000000000from django.core.handlers.wsgi import WSGIRequest as WSGIRequestOriginal class WSGIRequest(WSGIRequestOriginal): status_code = None content = '' json = None pylint-django-0.9.4/pylint_django/transforms/transforms/django_db_models.py0000664000175200017520000000222113245076665027665 0ustar senkosenko00000000000000from django.core.exceptions import MultipleObjectsReturned \ as MultipleObjectsReturnedException def __noop(self, *args, **kwargs): """Just a dumb no-op function to make code a bit more DRY""" return None class Model(object): _meta = None objects = None id = None pk = None MultipleObjectsReturned = MultipleObjectsReturnedException save = __noop delete = __noop class Manager(object): """ Eliminate E1002 for Manager object """ get_queryset = __noop none = __noop all = __noop count = __noop dates = __noop distinct = __noop extra = __noop get = __noop get_or_create = __noop create = __noop bulk_create = __noop filter = __noop aggregate = __noop annotate = __noop complex_filter = __noop exclude = __noop in_bulk = __noop iterator = __noop latest = __noop order_by = __noop select_for_update = __noop select_related = __noop prefetch_related = __noop values = __noop values_list = __noop update = __noop reverse = __noop defer = __noop only = __noop using = __noop exists = __noop pylint-django-0.9.4/pylint_django/transforms/transforms/django_db_models_fields.py0000664000175200017520000000565613230127102031203 0ustar senkosenko00000000000000from django.db.models import fields as django_fields import datetime from decimal import Decimal from uuid import UUID # -------- # booleans from utils import PY3 class BooleanField(bool, django_fields.BooleanField): pass class NullBooleanField(bool, django_fields.NullBooleanField): pass # ------ # strings class CharField(str, django_fields.CharField): pass class SlugField(CharField, django_fields.SlugField): pass class URLField(CharField, django_fields.URLField): pass class TextField(str, django_fields.TextField): pass class EmailField(CharField, django_fields.EmailField): pass class CommaSeparatedIntegerField(CharField, django_fields.CommaSeparatedIntegerField): pass class FilePathField(CharField, django_fields.FilePathField): pass # ------- # numbers class IntegerField(int, django_fields.IntegerField): pass class BigIntegerField(IntegerField, django_fields.BigIntegerField): pass class SmallIntegerField(IntegerField, django_fields.SmallIntegerField): pass class PositiveIntegerField(IntegerField, django_fields.PositiveIntegerField): pass class PositiveSmallIntegerField(IntegerField, django_fields.PositiveSmallIntegerField): pass class FloatField(float, django_fields.FloatField): pass class DecimalField(Decimal, django_fields.DecimalField): # DecimalField is immutable and does not use __init__, but the Django DecimalField does. To # cheat pylint a little bit, we copy the definition of the DecimalField constructor parameters # into the __new__ method of Decimal so that Pylint believes we are constructing a Decimal with # the signature of DecimalField def __new__(cls, verbose_name=None, name=None, max_digits=None, decimal_places=None, **kwargs): pass # -------- # date/time # In Python3, the date and datetime objects are immutable, so we need to do # the same __new__ / __init__ fiddle as for Decimal class DateField(datetime.date, django_fields.DateField): if PY3: def __new__(cls, verbose_name=None, name=None, auto_now=False, auto_now_add=False, **kwargs): pass class DateTimeField(datetime.datetime, django_fields.DateTimeField): if PY3: def __new__(cls, verbose_name=None, name=None, auto_now=False, auto_now_add=False, **kwargs): pass class TimeField(datetime.time, django_fields.TimeField): if PY3: def __new__(cls, verbose_name=None, name=None, auto_now=False, auto_now_add=False, **kwargs): pass class DurationField(datetime.timedelta, django_fields.DurationField): if PY3: def __new__(cls, verbose_name=None, name=None, **kwargs): pass # ------- # misc class GenericIPAddressField(str, django_fields.GenericIPAddressField): pass class IPAddressField(str, django_fields.IPAddressField): pass class UUIDField(UUID, django_fields.UUIDField): pass pylint-django-0.9.4/pylint_django/transforms/transforms/django_db_models_fields_files.py0000664000175200017520000000111313245076665032374 0ustar senkosenko00000000000000from django.db.models.fields import files as django_fields class FileField(django_fields.FieldFile, django_fields.FileField): def __init__(self, verbose_name=None, name=None, upload_to='', storage=None, **kwargs): django_fields.FileField.__init__(verbose_name, name, upload_to, storage, **kwargs) class ImageField(django_fields.ImageFieldFile, django_fields.ImageField): def __init__(self, verbose_name=None, name=None, width_field=None, height_field=None, **kwargs): django_fields.ImageField.__init__(verbose_name, name, width_field, height_field, **kwargs) pylint-django-0.9.4/pylint_django/transforms/transforms/django_forms.py0000664000175200017520000000060713245076665027071 0ustar senkosenko00000000000000from django.forms import BaseForm, BaseModelForm class Form(BaseForm): cleaned_data = None fields = None instance = None data = None _errors = None base_fields = None class ModelForm(BaseModelForm): def save_m2m(self): return None cleaned_data = None fields = None instance = None data = None _errors = None base_fields = None pylint-django-0.9.4/pylint_django/transforms/transforms/django_forms_fields.py0000664000175200017520000000540313230127102030367 0ustar senkosenko00000000000000import datetime from decimal import Decimal from django.forms import fields as django_fields # -------- # booleans from utils import PY3 class BooleanField(bool, django_fields.BooleanField): pass class NullBooleanField(bool, django_fields.NullBooleanField): pass # ------- # strings class CharField(str, django_fields.CharField): pass class EmailField(CharField, django_fields.EmailField): pass class GenericIPAddressField(CharField, django_fields.GenericIPAddressField): pass class IPAddressField(CharField, django_fields.IPAddressField): pass class SlugField(CharField, django_fields.SlugField): pass class URLField(CharField, django_fields.URLField): pass class RegexField(CharField, django_fields.RegexField): pass class FilePathField(CharField, django_fields.FilePathField): pass # ------- # numbers class IntegerField(int, django_fields.IntegerField): pass class DecimalField(Decimal, django_fields.DecimalField): # DecimalField is immutable and does not use __init__, but the Django DecimalField does. To # cheat pylint a little bit, we copy the definition of the DecimalField constructor parameters # into the __new__ method of Decimal so that Pylint believes we are constructing a Decimal with # the signature of DecimalField def __new__(cls, max_value=None, min_value=None, max_digits=None, decimal_places=None, *args, **kwargs): pass class FloatField(float, django_fields.FloatField): pass # ------- # date/time # In Python3, the date and datetime objects are immutable, so we need to do # the same __new__ / __init__ fiddle as for Decimal class DateField(datetime.date, django_fields.DateField): if PY3: def __new__(cls, input_formats=None, *args, **kwargs): pass class DateTimeField(datetime.datetime, django_fields.DateTimeField): if PY3: def __new__(cls, input_formats=None, *args, **kwargs): pass class SplitDateTimeField(datetime.datetime, django_fields.SplitDateTimeField): if PY3: def __new__(cls, input_date_formats=None, input_time_formats=None, *args, **kwargs): pass class TimeField(datetime.time, django_fields.TimeField): if PY3: def __new__(cls, input_formats=None, *args, **kwargs): pass class DurationField(datetime.timedelta, django_fields.DurationField): if PY3: def __new__(cls, *args, **kwargs): pass # -------- # choice class ChoiceField(object, django_fields.ChoiceField): pass class MultipleChoiceField(ChoiceField, django_fields.MultipleChoiceField): pass class TypedChoiceField(ChoiceField, django_fields.TypedChoiceField): pass class TypedMultipleChoiceField(ChoiceField, django_fields.TypedMultipleChoiceField): pass pylint-django-0.9.4/pylint_django/transforms/transforms/django_utils_translation.py0000664000175200017520000000003513230127102031465 0ustar senkosenko00000000000000ugettext_lazy = lambda x: '' pylint-django-0.9.4/pylint_django/transforms/transforms/django_views_generic_base.py0000664000175200017520000000023313230127102031532 0ustar senkosenko00000000000000class View(object): request = None args = () kwargs = {} # as_view is marked as class-only def as_view(*args, **kwargs): pass pylint-django-0.9.4/pylint_django/transforms/transforms/mongoengine.py0000664000175200017520000000054613245076665026730 0ustar senkosenko00000000000000from mongoengine.errors import DoesNotExist, MultipleObjectsReturned from mongoengine.queryset.manager import QuerySetManager class Document(object): _meta = None objects = QuerySetManager() id = None pk = None MultipleObjectsReturned = MultipleObjectsReturned DoesNotExist = DoesNotExist def save(self): return None pylint-django-0.9.4/pylint_django/transforms/__init__.py0000664000175200017520000000311613230127102023731 0ustar senkosenko00000000000000"""Transforms.""" import os import re from pylint_django.transforms import foreignkey, fields from astroid import MANAGER from astroid.builder import AstroidBuilder from astroid import nodes foreignkey.add_transform(MANAGER) fields.add_transforms(MANAGER) def _add_transform(package_name, *class_names): """Transform package's classes.""" transforms_dir = os.path.join(os.path.dirname(__file__), 'transforms') fake_module_path = os.path.join(transforms_dir, '%s.py' % re.sub(r'\.', '_', package_name)) with open(fake_module_path) as modulefile: fake_module = modulefile.read() fake = AstroidBuilder(MANAGER).string_build(fake_module) def set_fake_locals(module): """Set fake locals for package.""" if module.name != package_name: return for class_name in class_names: # This changed from locals to _locals between astroid 1.3 and 1.4 if hasattr(module, '_locals'): module._locals[class_name].extend(fake._locals[class_name]) # pylint: disable=protected-access else: module.locals[class_name].extend(fake.locals[class_name]) MANAGER.register_transform(nodes.Module, set_fake_locals) _add_transform('django.core.handlers.wsgi', 'WSGIRequest') _add_transform('django.views.generic.base', 'View') _add_transform('django.forms', 'Form') _add_transform('django.forms', 'ModelForm') _add_transform('django.db.models', 'Model', 'Manager') _add_transform('django.utils.translation', 'ugettext_lazy') _add_transform('mongoengine', 'Document') pylint-django-0.9.4/pylint_django/transforms/fields.py0000664000175200017520000000667713230127102023457 0ustar senkosenko00000000000000from astroid import MANAGER, scoped_nodes, nodes, inference_tip import sys from pylint_django import utils _STR_FIELDS = ('CharField', 'SlugField', 'URLField', 'TextField', 'EmailField', 'CommaSeparatedIntegerField', 'FilePathField', 'GenericIPAddressField', 'IPAddressField', 'RegexField', 'SlugField') _INT_FIELDS = ('IntegerField', 'SmallIntegerField', 'BigIntegerField', 'PositiveIntegerField', 'PositiveSmallIntegerField') _BOOL_FIELDS = ('BooleanField', 'NullBooleanField') _RANGE_FIELDS = ('RangeField', 'IntegerRangeField', 'BigIntegerRangeField', 'FloatRangeField', 'DateTimeRangeField', 'DateRangeField') def is_model_field(cls): return cls.qname().startswith('django.db.models.fields') or \ cls.qname().startswith('django.contrib.postgres.fields') def is_form_field(cls): return cls.qname().startswith('django.forms.fields') def is_model_or_form_field(cls): return is_model_field(cls) or is_form_field(cls) def apply_type_shim(cls, context=None): # noqa if cls.name in _STR_FIELDS: base_nodes = scoped_nodes.builtin_lookup('str') elif cls.name in _INT_FIELDS: base_nodes = scoped_nodes.builtin_lookup('int') elif cls.name in _BOOL_FIELDS: base_nodes = scoped_nodes.builtin_lookup('bool') elif cls.name == 'FloatField': base_nodes = scoped_nodes.builtin_lookup('float') elif cls.name == 'DecimalField': if sys.version_info >= (3, 5): # I dunno, I'm tired and this works :( base_nodes = MANAGER.ast_from_module_name('_decimal').lookup('Decimal') else: base_nodes = MANAGER.ast_from_module_name('decimal').lookup('Decimal') elif cls.name in ('SplitDateTimeField', 'DateTimeField'): base_nodes = MANAGER.ast_from_module_name('datetime').lookup('datetime') elif cls.name == 'TimeField': base_nodes = MANAGER.ast_from_module_name('datetime').lookup('time') elif cls.name == 'DateField': base_nodes = MANAGER.ast_from_module_name('datetime').lookup('date') elif cls.name == 'DurationField': base_nodes = MANAGER.ast_from_module_name('datetime').lookup('timedelta') elif cls.name == 'UUIDField': base_nodes = MANAGER.ast_from_module_name('uuid').lookup('UUID') elif cls.name == 'ManyToManyField': base_nodes = MANAGER.ast_from_module_name('django.db.models.query').lookup('QuerySet') elif cls.name in ('ImageField', 'FileField'): base_nodes = MANAGER.ast_from_module_name('django.core.files.base').lookup('File') elif cls.name == 'ArrayField': base_nodes = scoped_nodes.builtin_lookup('list') elif cls.name in ('HStoreField', 'JSONField'): base_nodes = scoped_nodes.builtin_lookup('dict') elif cls.name in _RANGE_FIELDS: base_nodes = MANAGER.ast_from_module_name('psycopg2._range').lookup('Range') else: return iter([cls]) # XXX: for some reason, with python3, this particular line triggers a # check in the StdlibChecker for deprecated methods; one of these nodes # is an ImportFrom which has no qname() method, causing the checker # to die... if utils.PY3: base_nodes = [n for n in base_nodes[1] if not isinstance(n, nodes.ImportFrom)] else: base_nodes = list(base_nodes[1]) return iter([cls] + base_nodes) def add_transforms(manager): manager.register_transform(nodes.Class, inference_tip(apply_type_shim), is_model_or_form_field) pylint-django-0.9.4/pylint_django/transforms/foreignkey.py0000664000175200017520000000424413232211403024337 0ustar senkosenko00000000000000from astroid import MANAGER, nodes, InferenceError, inference_tip, UseInferenceDefault from pylint_django.utils import node_is_subclass from pylint_django.compat import ClassDef, instantiate_class, Attribute def is_foreignkey_in_class(node): # is this of the form field = models.ForeignKey if not isinstance(node.parent, nodes.Assign): return False if not isinstance(node.parent.parent, ClassDef): return False if isinstance(node.func, Attribute): attr = node.func.attrname elif isinstance(node.func, nodes.Name): attr = node.func.name else: return False return attr in ('OneToOneField', 'ForeignKey') 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 add_transform(manager): manager.register_transform(nodes.CallFunc, inference_tip(infer_key_classes), is_foreignkey_in_class) pylint-django-0.9.4/pylint_django/__init__.py0000664000175200017520000000050413230324773021546 0ustar senkosenko00000000000000"""pylint_django module.""" from __future__ import absolute_import import sys import warnings from pylint_django import plugin if sys.version_info < (3, ): warnings.warn("Version 0.8.0 is the last to support Python 2. " "Please migrate to Python 3!", DeprecationWarning) register = plugin.register pylint-django-0.9.4/pylint_django/__pkginfo__.py0000664000175200017520000000003413230127102022221 0ustar senkosenko00000000000000"""pkginfo.""" BASE_ID = 51 pylint-django-0.9.4/pylint_django/compat.py0000664000175200017520000000172013230127102021256 0ustar senkosenko00000000000000 try: from astroid.nodes import ClassDef, FunctionDef, ImportFrom, AssignName, Attribute except ImportError: from astroid.nodes import Class as ClassDef, \ Function as FunctionDef, \ From as ImportFrom, \ AssName as AssignName, \ Getattr as Attribute try: from astroid.bases import YES as Uninferable except ImportError: try: from astroid.util import YES as Uninferable except ImportError: from astroid.util import Uninferable try: django = __import__('django') django_version = django.VERSION except ImportError: # if not available, will be handled by the django_installed checker django_version = (1, 5) def inferred(node): if hasattr(node, 'inferred'): return node.inferred else: return node.infered def instantiate_class(node): if hasattr(node, 'instantiate_class'): return node.instantiate_class else: return node.instanciate_class pylint-django-0.9.4/pylint_django/plugin.py0000664000175200017520000000231113251562302021276 0ustar senkosenko00000000000000"""Common Django module.""" from pylint.checkers.base import NameChecker from pylint_plugin_utils import get_checker from pylint_django.checkers import register_checkers # we want to import the transforms to make sure they get added to the astroid manager, # however we don't actually access them directly, so we'll disable the warning from pylint_django import transforms # noqa, pylint: disable=unused-import def register(linter): """ Registering additional checkers. However, we will also use it to amend existing checker config. """ name_checker = get_checker(linter, NameChecker) name_checker.config.good_names += ('qs', 'urlpatterns', 'register', 'app_name', 'handler500') # we don't care about South migrations linter.config.black_list += ('migrations', 'south_migrations') # add all of the checkers register_checkers(linter) # register any checking fiddlers try: from pylint_django.augmentations import apply_augmentations apply_augmentations(linter) except ModuleNotFoundError: # probably trying to execute pylint_django when Django isn't installed # in this case the django-not-installed checker will kick-in pass pylint-django-0.9.4/pylint_django/utils.py0000664000175200017520000000161513230127102021136 0ustar senkosenko00000000000000"""Utils.""" from astroid.exceptions import InferenceError from astroid.bases import Instance from pylint_django.compat import Uninferable, inferred, ClassDef import sys PY3 = sys.version_info >= (3, 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 pylint-django-0.9.4/pylint_django.egg-info/0000775000175200017520000000000013251563043021126 5ustar senkosenko00000000000000pylint-django-0.9.4/pylint_django.egg-info/PKG-INFO0000664000175200017520000000126313251563042022224 0ustar senkosenko00000000000000Metadata-Version: 1.1 Name: pylint-django Version: 0.9.4 Summary: A Pylint plugin to help Pylint understand the Django web framework Home-page: https://github.com/PyCQA/pylint-django Author: landscape.io Author-email: code@landscape.io License: GPLv2 Description: UNKNOWN Keywords: pylint,django,plugin Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Environment :: Console Classifier: Intended Audience :: Developers Classifier: Operating System :: Unix Classifier: Topic :: Software Development :: Quality Assurance Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 pylint-django-0.9.4/pylint_django.egg-info/SOURCES.txt0000664000175200017520000000617613251563043023024 0ustar senkosenko00000000000000CHANGELOG.md CONTRIBUTORS.md LICENSE MANIFEST.in README.md setup.py pylint_django/__init__.py pylint_django/__pkginfo__.py pylint_django/compat.py pylint_django/plugin.py pylint_django/utils.py pylint_django.egg-info/PKG-INFO pylint_django.egg-info/SOURCES.txt pylint_django.egg-info/dependency_links.txt pylint_django.egg-info/not-zip-safe pylint_django.egg-info/requires.txt pylint_django.egg-info/top_level.txt pylint_django/augmentations/__init__.py pylint_django/checkers/__init__.py pylint_django/checkers/django_installed.py pylint_django/checkers/models.py pylint_django/tests/__init__.py pylint_django/tests/test_func.py pylint_django/tests/input/__init__.py pylint_django/tests/input/external_django_tables2_noerror_meta_class.py pylint_django/tests/input/external_drf_noerror_serializer.py pylint_django/tests/input/external_drf_noerror_serializer.rc pylint_django/tests/input/external_psycopg2_noerror_postgres_fields.py pylint_django/tests/input/external_psycopg2_noerror_postgres_fields.rc pylint_django/tests/input/func_model_does_not_use_unicode_py33.py pylint_django/tests/input/func_model_does_not_use_unicode_py33.txt pylint_django/tests/input/func_noerror_classviews.py pylint_django/tests/input/func_noerror_duplicate_except_doesnotexist.py pylint_django/tests/input/func_noerror_foreign_key_attributes.py pylint_django/tests/input/func_noerror_foreign_key_ids.py pylint_django/tests/input/func_noerror_foreign_key_sets.py pylint_django/tests/input/func_noerror_foreignkeys.py pylint_django/tests/input/func_noerror_form_fields.py pylint_django/tests/input/func_noerror_forms_py33.py pylint_django/tests/input/func_noerror_formview_ancestors.py pylint_django/tests/input/func_noerror_ignore_meta_subclass.py pylint_django/tests/input/func_noerror_import_q.py pylint_django/tests/input/func_noerror_issue_46.py pylint_django/tests/input/func_noerror_manytomanyfield.py pylint_django/tests/input/func_noerror_model_fields.py pylint_django/tests/input/func_noerror_model_methods.py pylint_django/tests/input/func_noerror_model_unicode_callable.py pylint_django/tests/input/func_noerror_model_unicode_lambda.py pylint_django/tests/input/func_noerror_models_py33.py pylint_django/tests/input/func_noerror_protected_meta_access.py pylint_django/tests/input/func_noerror_ugettext_lazy_format.py pylint_django/tests/input/func_noerror_unicode_py2_compatible.py pylint_django/tests/input/func_noerror_urls.py pylint_django/tests/input/func_noerror_uuid_field.py pylint_django/transforms/__init__.py pylint_django/transforms/fields.py pylint_django/transforms/foreignkey.py pylint_django/transforms/transforms/django_contrib_postgres_fields.py pylint_django/transforms/transforms/django_core_handlers_wsgi.py pylint_django/transforms/transforms/django_db_models.py pylint_django/transforms/transforms/django_db_models_fields.py pylint_django/transforms/transforms/django_db_models_fields_files.py pylint_django/transforms/transforms/django_forms.py pylint_django/transforms/transforms/django_forms_fields.py pylint_django/transforms/transforms/django_utils_translation.py pylint_django/transforms/transforms/django_views_generic_base.py pylint_django/transforms/transforms/mongoengine.pypylint-django-0.9.4/pylint_django.egg-info/dependency_links.txt0000664000175200017520000000000113251563042025173 0ustar senkosenko00000000000000 pylint-django-0.9.4/pylint_django.egg-info/not-zip-safe0000664000175200017520000000000113251563042023353 0ustar senkosenko00000000000000 pylint-django-0.9.4/pylint_django.egg-info/requires.txt0000664000175200017520000000007713251563042023531 0ustar senkosenko00000000000000pylint-plugin-utils>=0.2.1 pylint>=1.8.2 [with_django] Django pylint-django-0.9.4/pylint_django.egg-info/top_level.txt0000664000175200017520000000001613251563042023654 0ustar senkosenko00000000000000pylint_django pylint-django-0.9.4/CHANGELOG.md0000664000175200017520000002072213251562651016413 0ustar senkosenko00000000000000# Changelog ## Version 0.9.4 (12 March 2018) * Add an optional dependency on Django * Fix the `DjangoInstalledChecker` so it can actually warn when Django isn't available * Fix [#136](https://github.com/PyCQA/pylint-django/issues/136) by adding automated build and sanity test scripts ## Version 0.9.3 (removed from PyPI) * Fix [#133](https://github.com/PyCQA/pylint-django/issues/133) and [#134](https://github.com/PyCQA/pylint-django/issues/134) by including package data when building wheel and tar.gz packages for PyPI (Joseph Herlant) ## Version 0.9.2 (broken) * Fix [#129](https://github.com/PyCQA/pylint-django/issues/129) - Move tests under `site-packages/pylint_django` (Mr. Senko) * Fix [#96](https://github.com/PyCQA/pylint-django/issues/96) - List Django as a dependency (Mr. Senko) ## Version 0.9.1 (26 Feb 2018) * Fix [#123](https://github.com/PyCQA/pylint-django/issues/123) - Update links after the move to PyCQA (Mr. Senko) * Add test for Meta class from django_tables2 (Mr. Senko) * Fix flake8 complaints (Peter Bittner) * Add missing .txt and .rc test files to MANIFEST.in (Joseph Herlant) ## Version 0.9 (25 Jan 2018) * Fix [#120](https://github.com/PyCQA/pylint-django/issues/120) - TypeError: 'NamesConsumer' object does not support indexing (Simone Basso) * Fix [#110](https://github.com/PyCQA/pylint-django/issues/120) and [#35](https://github.com/PyCQA/pylint-django/issues/120) - resolve ForeignKey models specified as strings instead of class names (Mr. Senko) ## Version 0.8.0 (20 Jan 2018) * This is the last version to support Python 2. Issues a deprecation warning! * [#109](http://github.com/PyCQA/pylint-django/pull/109), adding 'urlpatterns', 'register', 'app_name' to good names. Obsoletes [#111](http://github.com/PyCQA/pylint-django/pull/111), fixes [#108](http://github.com/PyCQA/pylint-django/issues/108) (Vinay Pai) * Add 'handler500' to good names (Mr. Senko) * [#103](http://github.com/PyCQA/pylint-django/pull/103): Support factory_boy's DjangoModelFactory Meta class (Konstantinos Koukopoulos) * [#100](https://github.com/PyCQA/pylint-django/pull/100): Fix E1101:Instance of '__proxy__' has no 'format' member' when using .format() on a ugettext_lazy translation. Fixes [#80](https://github.com/PyCQA/pylint-django/issues/80) (canarduck) * [#99](https://github.com/PyCQA/pylint-django/pull/99): Add tests and transforms for DurationField, fixes [#95](https://github.com/PyCQA/pylint-django/issues/95) (James M. Allen) * [#92](https://github.com/PyCQA/pylint-django/pull/92): Add json field to WSGIRequest proxy (sjk4sc) * [#84](https://github.com/PyCQA/pylint-django/pull/84): Add support for django.contrib.postgres.fields and UUIDField (Villiers Strauss) * Stop testing with older Django versions. Currently testing with Django 1.11.x and 2.0 * Stop testing on Python 2, no functional changes in the source code though * Update tests and require latest version of pylint (>=1.8), fixes [#53](https://github.com/PyCQA/pylint-django/issues/53), [#97](https://github.com/PyCQA/pylint-django/issues/97) * [#81](https://github.com/PyCQA/pylint-django/issues/81) Fix 'duplicate-except' false negative for except blocks which catch the `DoesNotExist` exception. ## Version 0.7.4 * [#88](https://github.com/PyCQA/pylint-django/pull/88) Fixed builds with Django 1.10 (thanks to [federicobond](https://github.com/federicobond)) * [#91](https://github.com/PyCQA/pylint-django/pull/91) Fixed race condition when running with pylint parallel execution mode (thanks to [jeremycarroll](https://github.com/jeremycarroll)) * [#64](https://github.com/PyCQA/pylint-django/issues/64) "Meta is old style class" now suppressed on BaseSerializer too (thanks to [unklphil](https://github.com/unklphil)) * [#70](https://github.com/PyCQA/pylint-django/pull/70) Updating to handle newer pylint/astroid versions (thanks to [iXce](https://github.com/iXce)) ## Version 0.7.2 * [#76](https://github.com/PyCQA/pylint-django/pull/76) Better handling of mongoengine querysetmanager * [#73](https://github.com/PyCQA/pylint-django/pull/73) (#72)[https://github.com/PyCQA/pylint-django/issues/72] Make package zip safe to help fix some path problems * [#68](https://github.com/PyCQA/pylint-django/pull/68) Suppressed invalid constant warning for "app_name" in urls.py * [#67](https://github.com/PyCQA/pylint-django/pull/67) Fix view.args and view.kwargs * [#66](https://github.com/PyCQA/pylint-django/issues/66) accessing _meta no longer causes a protected-access warning as this is a public API as of Django 1.8 * [#65](https://github.com/PyCQA/pylint-django/pull/65) Add support of mongoengine module. * [#59](https://github.com/PyCQA/pylint-django/pull/59) Silence old-style-class for widget Meta ## Version 0.7.1 * [#52](https://github.com/PyCQA/pylint-django/issues/52) - Fixed stupid mistake when using versioninfo ## Version 0.7 * [#51](https://github.com/PyCQA/pylint-django/issues/51) - Fixed compatibility with pylint 1.5 / astroid 1.4.1 ## Version 0.6.1 * [#43](https://github.com/PyCQA/pylint-django/issues/43) - Foreign key ID access (`somefk_id`) does not raise an 'attribute not found' warning * [#31](https://github.com/PyCQA/pylint-django/issues/31) - Support for custom model managers (thanks [smirolo](https://github.com/smirolo)) * [#48](https://github.com/PyCQA/pylint-django/pull/48) - Added support for django-restframework (thanks [mbertolacci](https://github.com/mbertolacci)) ## Version 0.6 * Pylint 1.4 dropped support for Python 2.6, therefore a constraint is added that pylint-django will only work with Python2.6 if pylint<=1.3 is installed * [#40](https://github.com/PyCQA/pylint-django/issues/40) - pylint 1.4 warned about View and Model classes not having enough public methods; this is suppressed * [#37](https://github.com/PyCQA/pylint-django/issues/37) - fixed an infinite loop when using astroid 1.3.3+ * [#36](https://github.com/PyCQA/pylint-django/issues/36) - no longer warning about lack of `__unicode__` method on abstract model classes * [PR #34](https://github.com/PyCQA/pylint-django/pull/34) - prevent warning about use of `super()` on ModelManager classes ## Version 0.5.5 * [PR #27](https://github.com/PyCQA/pylint-django/pull/27) - better `ForeignKey` transforms, which now work when of the form `othermodule.ModelClass`. This also fixes a problem where an inferred type would be `_Yes` and pylint would fail * [PR #28](https://github.com/PyCQA/pylint-django/pull/28) - better knowledge of `ManyToManyField` classes ## Version 0.5.4 * Improved resiliance to inference failure when Django types cannot be inferred (which can happen if Django is not on the system path ## Version 0.5.3 * [Issue #25](https://github.com/PyCQA/pylint-django/issues/25) Fixing cases where a module defines `get` as a method ## Version 0.5.2 * Fixed a problem where type inference could get into an infinite loop ## Version 0.5.1 * Removed usage of a Django object, as importing it caused Django to try to configure itself and thus throw an ImproperlyConfigured exception. ## Version 0.5 * [Issue #7](https://github.com/PyCQA/pylint-django/issues/7) Improved handling of Django model fields * [Issue #10](https://github.com/PyCQA/pylint-django/issues/10) No warning about missing __unicode__ if the Django python3/2 compatability tools are used * [Issue #11](https://github.com/PyCQA/pylint-django/issues/11) Improved handling of Django form fields * [Issue #12](https://github.com/PyCQA/pylint-django/issues/12) Improved handling of Django ImageField and FileField objects * [Issue #14](https://github.com/PyCQA/pylint-django/issues/14) Models which do not define __unicode__ but whose parents do now have a new error (W5103) instead of incorrectly warning about no __unicode__ being present. * [Issue #21](https://github.com/PyCQA/pylint-django/issues/21) `ForeignKey` and `OneToOneField` fields on models are replaced with instance of the type they refer to in the AST, which allows pylint to generate correct warnings about attributes they may or may not have. ## Version 0.3 #### New Features * Python3 is now supported #### Bugfixes * `__unicode__` warning on models does not appear in Python3 ## Version 0.2 #### New Features * Pylint now recognises `BaseForm` as an ancestor of `Form` and subclasses * Improved `Form` support #### Bugfixes * [Issue #2](https://github.com/PyCQA/pylint-django/issues/2) - a subclass of a `Model` or `Form` also has warnings about a `Meta` class suppressed. * [Issue #3](https://github.com/PyCQA/pylint-django/issues/3) - `Form` and `ModelForm` subclasses no longer warn about `Meta` classes. pylint-django-0.9.4/CONTRIBUTORS.md0000664000175200017520000000064613245076665017074 0ustar senkosenko00000000000000* [carlio](https://github.com/carlio) * [mbarrien](https://github.com/mbarrien) * [frost-nzcr4](https://github.com/frost-nzcr4) * [ustun](https://github.com/ustun) * [jproffitt](https://github.com/jproffitt) * [lhupfeldt](https://github.com/lhupfeldt) * [smirolo](https://github.com/smirolo) * [mbertolacci](https://github.com/mbertolacci) * [atodorov](https://github.com/atodorov) * [bittner](https://github.com/bittner) pylint-django-0.9.4/LICENSE0000664000175200017520000004317613230127102015600 0ustar senkosenko00000000000000GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Pylint plugin for improving code analysis for when using Django Copyright (C) 2013 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. {signature of Ty Coon}, 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. pylint-django-0.9.4/MANIFEST.in0000664000175200017520000000020513247332665016337 0ustar senkosenko00000000000000include *.md include LICENSE include pylint_django/transforms/transforms/*.py recursive-include pylint_django/tests/ *.py *.rc *.txt pylint-django-0.9.4/README.md0000664000175200017520000000622113251562302016050 0ustar senkosenko00000000000000pylint-django ============= [![Build Status](https://travis-ci.org/PyCQA/pylint-django.svg?branch=master)](https://travis-ci.org/PyCQA/pylint-django) [![Code Quality](https://landscape.io/github/landscapeio/pylint-django/master/landscape.png)](https://landscape.io/github/landscapeio/pylint-django) [![Coverage Status](https://coveralls.io/repos/PyCQA/pylint-django/badge.svg)](https://coveralls.io/r/PyCQA/pylint-django) [![Latest Version](https://img.shields.io/pypi/v/pylint-django.svg)](https://pypi.python.org/pypi/pylint-django) # About `pylint-django` is a [Pylint](http://pylint.org) plugin for improving code analysis for when analysing code using Django. It is also used by the [Prospector](https://github.com/landscapeio/prospector) tool. # Installation ``` pip install pylint-django ``` **WARNING:** `pylint-django` will not install `Django` by default because this causes more trouble than good, [see discussion](https://github.com/PyCQA/pylint-django/pull/132). If you wish to automatically install the latest version of `Django` then ``` pip install pylint-django[with_django] ``` otherwise sort out your testing environment and please **DO NOT** report issues about missing Django! # Usage Ensure `pylint-django` is installed and on your path and then execute: ``` pylint --load-plugins pylint_django [..other options..] ``` ## Prospector If you have `prospector` installed, then `pylint-django` will already be installed as a dependency, and will be activated automatically if Django is detected. ``` prospector [..other options..] ``` # Features * Prevents warnings about Django-generated attributes such as `Model.objects` or `Views.request`. * Prevents warnings when using `ForeignKey` attributes ("Instance of ForeignKey has no member"). * Fixes pylint's knowledge of the types of Model and Form field attributes * Validates `Model.__unicode__` methods. * `Meta` informational classes on forms and models do not generate errors. # Contributing Please feel free to add your name to the `CONTRIBUTORS.md` file if you want to be credited when pull requests get merged. You can also add to the `CHANGELOG.md` file if you wish, although I'll also do that when merging if not. # Tests The structure of the test package follows that from pylint itself. It is fairly simple: create a module starting with `func_` followed by a test name, and insert into it some code. The tests will run pylint against these modules. If the idea is that no messages now occur, then that is fine, just check to see if it works by running `scripts/test.sh`. Ideally, add some pylint error suppression messages to the file to prevent spurious warnings, since these are all tiny little modules not designed to do anything so there's no need to be perfect. It is possible to make tests with expected error output, for example, if adding a new message or simply accepting that pylint is supposed to warn. the `messages` directory contains a list of files which should match the name of the test and contain error type, line number, class and expected text. These are useful to quickly add "expected messages". # License `pylint-django` is available under the GPLv2 license. pylint-django-0.9.4/setup.py0000664000175200017520000000205413251562324016307 0ustar senkosenko00000000000000# -*- coding: UTF-8 -*- """ Setup module for Pylint plugin for Django. """ from setuptools import setup, find_packages setup( name='pylint-django', url='https://github.com/PyCQA/pylint-django', author='landscape.io', author_email='code@landscape.io', description='A Pylint plugin to help Pylint understand the Django web framework', version='0.9.4', packages=find_packages(), include_package_data=True, install_requires=[ 'pylint-plugin-utils>=0.2.1', 'pylint>=1.8.2', ], extras_require={ 'with_django': ['Django'], }, license='GPLv2', classifiers=[ 'Development Status :: 4 - Beta', 'Environment :: Console', 'Intended Audience :: Developers', 'Operating System :: Unix', 'Topic :: Software Development :: Quality Assurance', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', ], keywords=['pylint', 'django', 'plugin'], zip_safe=False, ) pylint-django-0.9.4/PKG-INFO0000664000175200017520000000126313251563043015672 0ustar senkosenko00000000000000Metadata-Version: 1.1 Name: pylint-django Version: 0.9.4 Summary: A Pylint plugin to help Pylint understand the Django web framework Home-page: https://github.com/PyCQA/pylint-django Author: landscape.io Author-email: code@landscape.io License: GPLv2 Description: UNKNOWN Keywords: pylint,django,plugin Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Environment :: Console Classifier: Intended Audience :: Developers Classifier: Operating System :: Unix Classifier: Topic :: Software Development :: Quality Assurance Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 pylint-django-0.9.4/setup.cfg0000664000175200017520000000007313251563043016414 0ustar senkosenko00000000000000[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0