[Python-modules-commits] [pylint-django] 01/06: Import pylint-django_0.7.1.orig.tar.xz

Daniel Stender danstender-guest at moszumanska.debian.org
Sun Jan 24 12:52:47 UTC 2016


This is an automated email from the git hooks/post-receive script.

danstender-guest pushed a commit to branch master
in repository pylint-django.

commit 71a6acc8e41a8cb313121cfc79f7a0e88b083bf9
Author: Daniel Stender <debian at danielstender.com>
Date:   Tue Jan 19 15:55:41 2016 +0100

    Import pylint-django_0.7.1.orig.tar.xz
---
 .landscape.yaml                                    |   6 +-
 .travis.yml                                        |   8 +-
 CHANGELOG.md                                       |  11 +
 CONTRIBUTORS.md                                    |   2 +
 README.md                                          |   3 +-
 pylint_django/augmentations/__init__.py            | 227 +++++++++++++--------
 pylint_django/checkers/__init__.py                 |   2 +-
 pylint_django/checkers/models.py                   |  31 ++-
 pylint_django/compat.py                            |  31 +++
 pylint_django/transforms/__init__.py               |   2 +-
 pylint_django/transforms/fields.py                 |  37 +++-
 pylint_django/transforms/foreignkey.py             |   7 +-
 .../transforms/transforms/django_db_models.py      |   6 +-
 pylint_django/utils.py                             |  19 +-
 scripts/travis-build.sh                            |   2 +-
 scripts/travis-install.sh                          |  10 +-
 scripts/{travis-skip.py => travis_skip.py}         |   3 +-
 setup.py                                           |   5 +-
 test/external_drf/func_noerror_serializer.py       |  11 +
 test/input/func_noerror_foreign_key_ids.py         |  17 ++
 test/input/func_noerror_foreignkeys.py             |   2 +-
 test/input/func_noerror_forms_py33.py              |   2 +-
 test/input/func_noerror_forms_py_28.py             |   1 -
 test/input/func_noerror_issue_46.py                |  11 +
 test/input/func_noerror_model_fields.py            |   3 +-
 test/input/func_noerror_model_unicode_callable.py  |   2 +-
 test/input/func_noerror_model_unicode_lambda.py    |   2 +-
 test/test_func.py                                  |  27 ++-
 tox.ini                                            |   2 +-
 29 files changed, 335 insertions(+), 157 deletions(-)

diff --git a/.landscape.yaml b/.landscape.yaml
index f2b5591..4b8bab3 100644
--- a/.landscape.yaml
+++ b/.landscape.yaml
@@ -1,6 +1,8 @@
-uses: django
+uses:
+  - django
 strictness: high
 doc-warnings: no
 max-line-length: 120
 ignore-paths:
-  - ^scripts/
+  - scripts/
+  - pylint_django/compat.py
diff --git a/.travis.yml b/.travis.yml
index 814080f..096ab17 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,11 +5,13 @@ python:
   - "2.7"
   - "3.3"
   - "3.4"
+  - "3.5"
 env:
-  - DJANGO='Django>=1.4,<1.5' SKIP='3.3 3.4'
-  - DJANGO='Django>=1.5,<1.6' SKIP='3.3 3.4'
-  - DJANGO='Django>=1.6,<1.7' SKIP='3.4'
+  - DJANGO='Django>=1.4,<1.5' SKIP='3.3 3.4,3.5'
+  - DJANGO='Django>=1.5,<1.6' SKIP='3.3 3.4,3.5'
+  - DJANGO='Django>=1.6,<1.7' SKIP='3.4,3.5'
   - DJANGO='Django>=1.7,<1.8' SKIP='2.6'
+  - DJANGO='Django>=1.8,<1.9' SKIP='2.6'
 install:
   scripts/travis-install.sh
 script:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c4df9cb..2cfa0ad 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,16 @@
 # Changelog
 
+## Version 0.7.1
+* [#52](https://github.com/landscapeio/pylint-django/issues/52) - Fixed stupid mistake when using versioninfo
+
+## Version 0.7
+* [#51](https://github.com/landscapeio/pylint-django/issues/51) - Fixed compatibility with pylint 1.5 / astroid 1.4.1
+
+## Version 0.6.1
+* [#43](https://github.com/landscapeio/pylint-django/issues/43) - Foreign key ID access (`somefk_id`) does not raise an 'attribute not found' warning
+* [#31](https://github.com/landscapeio/pylint-django/issues/31) - Support for custom model managers (thanks [smirolo](https://github.com/smirolo))
+* [#48](https://github.com/landscapeio/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/landscapeio/pylint-django/issues/40) - pylint 1.4 warned about View and Model classes not having enough public methods; this is suppressed
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index bf44b92..e3515a8 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -4,3 +4,5 @@
 * [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)
\ No newline at end of file
diff --git a/README.md b/README.md
index 140550c..e1a5da6 100644
--- a/README.md
+++ b/README.md
@@ -4,8 +4,7 @@ pylint-django
 [![Build Status](https://travis-ci.org/landscapeio/pylint-django.png?branch=master)](https://travis-ci.org/landscapeio/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/landscapeio/pylint-django/badge.png)](https://coveralls.io/r/landscapeio/pylint-django)
-[![Latest Version](https://pypip.in/v/pylint-django/badge.png)](https://crate.io/packages/pylint-django)
-[![Downloads](https://pypip.in/d/pylint-django/badge.png)](https://crate.io/packages/pylint-django)
+[![Latest Version](https://img.shields.io/pypi/v/pylint-django.svg)](https://pypi.python.org/pypi/pylint-django)
 
 # About
 
diff --git a/pylint_django/augmentations/__init__.py b/pylint_django/augmentations/__init__.py
index 1150bd6..61600f2 100644
--- a/pylint_django/augmentations/__init__.py
+++ b/pylint_django/augmentations/__init__.py
@@ -1,14 +1,17 @@
 """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, Getattr
-from astroid.nodes import Class, From
+from astroid import InferenceError
+from pylint_django.compat import ClassDef, ImportFrom, Attribute
 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_django.utils import node_is_subclass, PY3
+from pylint_django.compat import inferred
 from pylint_plugin_utils import augment_visit, suppress_message
 
 
@@ -30,7 +33,7 @@ def ignore_import_warnings_for_related_fields(orig_method, self, node):
 
     iterat = to_consume[0].items if PY3 else to_consume[0].iteritems
     for name, stmts in iterat():
-        if isinstance(stmts[0], From):
+        if isinstance(stmts[0], ImportFrom):
             if any([n[0] in ('ForeignKey', 'OneToOneField') for n in stmts[0].names]):
                 continue
         new_things[name] = stmts
@@ -63,55 +66,53 @@ def foreign_key_sets(chain, node):
     warn.
     """
     quack = False
-
-    if node.attrname.endswith('_set'):
+    # Note: it would have been nice to import the Manager object from Django and
+    # get its attributes that way - and this used to be the method - but unfortunately
+    # there's no guarantee that Django is properly configured at that stage, and importing
+    # anything from the django.db package causes an ImproperlyConfigured exception.
+    # Therefore we'll fall back on a hard-coded list of attributes which won't be as accurate,
+    # but this is not 100% accurate anyway.
+    manager_attrs = (
+        'none',
+        'all',
+        'count',
+        'dates',
+        'distinct',
+        'extra',
+        'get',
+        'get_or_create',
+        'create',
+        'bulk_create',
+        'filter',
+        'aggregate',
+        'annotate',
+        'complex_filter',
+        'exclude',
+        'in_bulk',
+        'iterator',
+        'latest',
+        'order_by',
+        'select_for_update',
+        'select_related',
+        'prefetch_related',
+        'values',
+        'values_list',
+        'update',
+        'reverse',
+        'defer',
+        'only',
+        'using',
+        'exists',
+    )
+
+    if node.attrname in manager_attrs or node.attrname.endswith('_set'):
         # if this is a X_set method, that's a pretty strong signal that this is the default
         # Django name, rather than one set by related_name
         quack = True
     else:
         # we will
-        if isinstance(node.parent, Getattr):
+        if isinstance(node.parent, Attribute):
             func_name = getattr(node.parent, 'attrname', None)
-
-            # Note: it would have been nice to import the Manager object from Django and
-            # get its attributes that way - and this used to be the method - but unfortunately
-            # there's no guarantee that Django is properly configured at that stage, and importing
-            # anything from the django.db package causes an ImproperlyConfigured exception.
-            # Therefore we'll fall back on a hard-coded list of attributes which won't be as accurate,
-            # but this is not 100% accurate anyway.
-            manager_attrs = (
-                'none',
-                'all',
-                'count',
-                'dates',
-                'distinct',
-                'extra',
-                'get',
-                'get_or_create',
-                'create',
-                'bulk_create',
-                'filter',
-                'aggregate',
-                'annotate',
-                'complex_filter',
-                'exclude',
-                'in_bulk',
-                'iterator',
-                'latest',
-                'order_by',
-                'select_for_update',
-                'select_related',
-                'prefetch_related',
-                'values',
-                'values_list',
-                'update',
-                'reverse',
-                'defer',
-                'only',
-                'using',
-                'exists',
-            )
-
             if func_name in manager_attrs:
                 quack = True
 
@@ -119,12 +120,15 @@ def foreign_key_sets(chain, node):
         children = list(node.get_children())
         for child in children:
             try:
-                inferred = child.infered()
+                inferred_cls = inferred(child)()
             except InferenceError:
                 pass
             else:
-                for cls in inferred:
-                    if node_is_subclass(cls, 'django.db.models.base.Model'):
+                for cls in inferred_cls:
+                    if (node_is_subclass(cls,
+                                         'django.db.models.manager.Manager',
+                                         'django.db.models.base.Model',
+                                         '.Model')):
                         # This means that we are looking at a subclass of models.Model
                         # and something is trying to access a <something>_set attribute.
                         # Since this could exist, we will return so as not to raise an
@@ -133,9 +137,15 @@ def foreign_key_sets(chain, node):
     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, Class):
+    if node.name[-5:] != 'Admin' or isinstance(node.parent, ClassDef):
         return False
 
     return node_is_subclass(node, 'django.contrib.admin.options.ModelAdmin')
@@ -143,42 +153,56 @@ def is_model_admin_subclass(node):
 
 def is_model_media_subclass(node):
     """Checks that node is derivative of Media class."""
-    if node.name != 'Media' or not isinstance(node.parent, 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',
-               'django.forms.models.ModelForm')
-    return any([node_is_subclass(node.parent, parent) for parent in parents])
+               '.Form',
+               'django.forms.models.ModelForm',
+               '.ModelForm')
+    return node_is_subclass(node.parent, *parents)
 
 
 def is_model_meta_subclass(node):
     """Checks that node is derivative of Meta class."""
-    if node.name != 'Meta' or not isinstance(node.parent, Class):
+    if node.name != 'Meta' or not isinstance(node.parent, ClassDef):
         return False
 
-    parents = ('django.db.models.base.Model',
+    parents = ('.Model',  # for the transformed version used here
+               'django.db.models.base.Model',
+               '.Form',
                'django.forms.forms.Form',
-               'django.forms.models.ModelForm')
-    return any([node_is_subclass(node.parent, parent) for parent in parents])
+               '.ModelForm',
+               'django.forms.models.ModelForm',
+               'rest_framework.serializers.ModelSerializer',
+               'rest_framework.generics.GenericAPIView',
+               'rest_framework.viewsets.ReadOnlyModelViewSet',
+               'rest_framework.viewsets.ModelViewSet',
+               'django_filters.filterset.FilterSet',)
+    return node_is_subclass(node.parent, *parents)
 
 
 def is_model_mpttmeta_subclass(node):
     """Checks that node is derivative of MPTTMeta class."""
-    if node.name != 'MPTTMeta' or not isinstance(node.parent, 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',
-               'django.forms.models.ModelForm')
-    return any([node_is_subclass(node.parent, parent) for parent in parents])
+               '.Form',
+               'django.forms.models.ModelForm',
+               '.ModelForm')
+    return node_is_subclass(node.parent, *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, Class):
+    if not node.name.endswith('Test') and not isinstance(node.parent, ClassDef):
         return False
 
     return node_is_subclass(node, 'django.test.testcases.TestCase')
@@ -220,8 +244,8 @@ def is_model_field_display_method(node):
         # TODO: could validate the names of the fields on the model rather than
         # blindly accepting get_*_display
         try:
-            for cls in node.last_child().infered():
-                if node_is_subclass(cls, 'django.db.models.base.Model'):
+            for cls in inferred(node.last_child())():
+                if node_is_subclass(cls, 'django.db.models.base.Model', '.Model'):
                     return True
         except InferenceError:
             return False
@@ -284,41 +308,66 @@ def wrap(orig_method, with_method):
     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 apply_augmentations(linter):
     """Apply augmentation and suppression rules."""
-    augment_visit(linter, TypeChecker.visit_getattr, foreign_key_sets)
-    suppress_message(linter, TypeChecker.visit_getattr, 'E1101', is_model_field_display_method)
+    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)
 
     # formviews have too many ancestors, there's nothing the user of the library can do about that
-    suppress_message(linter, MisdesignChecker.visit_class, 'R0901', is_class('django.views.generic.edit.FormView'))
+    suppress_message(linter, _visit_class(MisdesignChecker), 'R0901', is_class('django.views.generic.edit.FormView'))
 
     # model forms have no __init__ method anywhere in their bases
-    suppress_message(linter, ClassChecker.visit_class, 'W0232', is_class('django.forms.models.ModelForm'))
+    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.
-    suppress_message(linter, MisdesignChecker.leave_class, 'R0924', is_class('django.forms.forms.Form'))
-    suppress_message(linter, MisdesignChecker.leave_class, 'R0924', is_class('django.forms.models.ModelForm'))
+    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, DocStringChecker.visit_class, 'C0111', is_model_meta_subclass)
-    suppress_message(linter, NewStyleConflictChecker.visit_class, 'C1001', is_model_meta_subclass)
-    suppress_message(linter, ClassChecker.visit_class, 'W0232', is_model_meta_subclass)
-    suppress_message(linter, MisdesignChecker.leave_class, 'R0903', is_model_meta_subclass)
+    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)
 
     # Media
-    suppress_message(linter, NameChecker.visit_assname, 'C0103', is_model_media_valid_attributes)
-    suppress_message(linter, DocStringChecker.visit_class, 'C0111', is_model_media_subclass)
-    suppress_message(linter, NewStyleConflictChecker.visit_class, 'C1001', is_model_media_subclass)
-    suppress_message(linter, ClassChecker.visit_class, 'W0232', is_model_media_subclass)
-    suppress_message(linter, MisdesignChecker.leave_class, 'R0903', is_model_media_subclass)
+    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), 'W0232', is_model_media_subclass)
+    suppress_message(linter, _leave_class(MisdesignChecker), 'too-few-public-methods', is_model_media_subclass)
 
     # Too few public methods started appearing for Views and Models as part of Pylint>=1.4 / astroid>=1.3.3
     # Not sure why, suspect this is a failure to get the parent classes somewhere
     # For now, just suppress it on models and views
-    suppress_message(linter, MisdesignChecker.leave_class, 'R0903', is_class('django.db.models.base.Model'))
-    # TODO: why does this not work with the fqn of 'View'? Must be something to do with the overriding and transforms
-    suppress_message(linter, MisdesignChecker.leave_class, 'R0903', is_class('.View'))
+    suppress_message(linter, _leave_class(MisdesignChecker), 'too-few-public-methods',
+                     is_class('.Model'))
+    suppress_message(linter, _leave_class(MisdesignChecker), 'too-few-public-methods',
+                     is_class('.View'))
 
     # Admin
     # Too many public methods (40+/20)
@@ -328,22 +377,22 @@ def apply_augmentations(linter):
     #for method in node.methods():
     #    if not method.name.startswith('_'):
     #        nb_public_methods += 1
-    suppress_message(linter, MisdesignChecker.leave_class, 'R0904', is_model_admin_subclass)
+    suppress_message(linter, _leave_class(MisdesignChecker), 'R0904', is_model_admin_subclass)
 
     # Tests
-    suppress_message(linter, MisdesignChecker.leave_class, 'R0904', is_model_test_case_subclass)
+    suppress_message(linter, _leave_class(MisdesignChecker), 'R0904', is_model_test_case_subclass)
 
     # View
     # Method could be a function (get, post)
-    suppress_message(linter, ClassChecker.leave_function, 'R0201', is_model_view_subclass_method_shouldnt_be_function)
+    suppress_message(linter, _leave_function(ClassChecker), 'R0201', is_model_view_subclass_method_shouldnt_be_function)
     # Unused argument 'request' (get, post)
-    suppress_message(linter, VariablesChecker.leave_function, 'W0613', is_model_view_subclass_unused_argument)
+    suppress_message(linter, _leave_function(VariablesChecker), 'W0613', is_model_view_subclass_unused_argument)
 
     # django-mptt
-    suppress_message(linter, DocStringChecker.visit_class, 'C0111', is_model_mpttmeta_subclass)
-    suppress_message(linter, NewStyleConflictChecker.visit_class, 'C1001', is_model_mpttmeta_subclass)
-    suppress_message(linter, ClassChecker.visit_class, 'W0232', is_model_mpttmeta_subclass)
-    suppress_message(linter, MisdesignChecker.leave_class, 'R0903', is_model_mpttmeta_subclass)
+    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)
 
     # ForeignKey and OneToOneField
     VariablesChecker.leave_module = wrap(VariablesChecker.leave_module, ignore_import_warnings_for_related_fields)
diff --git a/pylint_django/checkers/__init__.py b/pylint_django/checkers/__init__.py
index 3a81763..57698ce 100644
--- a/pylint_django/checkers/__init__.py
+++ b/pylint_django/checkers/__init__.py
@@ -6,4 +6,4 @@ from pylint_django.checkers.models import ModelChecker
 def register_checkers(linter):
     """Register checkers."""
     linter.register_checker(ModelChecker(linter))
-    linter.register_checker(DjangoInstalledChecker(linter))
+    linter.register_checker(DjangoInstalledChecker(linter))
\ No newline at end of file
diff --git a/pylint_django/checkers/models.py b/pylint_django/checkers/models.py
index b3100a2..ffb8350 100644
--- a/pylint_django/checkers/models.py
+++ b/pylint_django/checkers/models.py
@@ -1,9 +1,11 @@
 """Models."""
 from astroid import Const
-from astroid.nodes import Assign, Function, AssName, Class
+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
 
@@ -27,7 +29,7 @@ MESSAGES = {
 
 
 def _is_meta_with_abstract(node):
-    if isinstance(node, Class) and node.name == 'Meta':
+    if isinstance(node, ClassDef) and node.name == 'Meta':
         for meta_child in node.get_children():
             if not isinstance(meta_child, Assign):
                 continue
@@ -51,10 +53,21 @@ class ModelChecker(BaseChecker):
     name = 'django-model-checker'
     msgs = MESSAGES
 
-    @check_messages('model-missing-unicode')
-    def visit_class(self, node):
+    # 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'):
+        if not node_is_subclass(node, 'django.db.models.base.Model', '.Model'):
             # we only care about models
             return
 
@@ -65,21 +78,23 @@ class ModelChecker(BaseChecker):
             if isinstance(child, Assign):
                 grandchildren = list(child.get_children())
 
-                if not isinstance(grandchildren[0], AssName):
+                if not isinstance(grandchildren[0], AssignName):
                     continue
 
                 name = grandchildren[0].name
                 if name != '__unicode__':
                     continue
 
-                assigned = grandchildren[1].infered()[0]
+                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, Function) and child.name == '__unicode__':
+            if isinstance(child, FunctionDef) and child.name == '__unicode__':
                 if PY3:
                     self.add_message('W%s02' % BASE_ID, args=node.name, node=node)
                 return
diff --git a/pylint_django/compat.py b/pylint_django/compat.py
new file mode 100644
index 0000000..bb4f365
--- /dev/null
+++ b/pylint_django/compat.py
@@ -0,0 +1,31 @@
+
+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
+
+
+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
diff --git a/pylint_django/transforms/__init__.py b/pylint_django/transforms/__init__.py
index fad380b..25cae97 100644
--- a/pylint_django/transforms/__init__.py
+++ b/pylint_django/transforms/__init__.py
@@ -26,7 +26,7 @@ def _add_transform(package_name, *class_names):
         if module.name != package_name:
             return
         for class_name in class_names:
-            module.locals[class_name] = fake.locals[class_name]
+            module._locals[class_name] = fake._locals[class_name]  # pylint: disable=protected-access
 
     MANAGER.register_transform(nodes.Module, set_fake_locals)
 
diff --git a/pylint_django/transforms/fields.py b/pylint_django/transforms/fields.py
index ba1b1e8..568a767 100644
--- a/pylint_django/transforms/fields.py
+++ b/pylint_django/transforms/fields.py
@@ -1,4 +1,6 @@
 from astroid import MANAGER, scoped_nodes, nodes, inference_tip
+import sys
+from pylint_django import utils
 
 
 _STR_FIELDS = ('CharField', 'SlugField', 'URLField', 'TextField', 'EmailField',
@@ -24,29 +26,42 @@ def is_model_or_form_field(cls):
 def apply_type_shim(cls, context=None):
 
     if cls.name in _STR_FIELDS:
-        base_node = scoped_nodes.builtin_lookup('str')
+        base_nodes = scoped_nodes.builtin_lookup('str')
     elif cls.name in _INT_FIELDS:
-        base_node = scoped_nodes.builtin_lookup('int')
+        base_nodes = scoped_nodes.builtin_lookup('int')
     elif cls.name in _BOOL_FIELDS:
-        base_node = scoped_nodes.builtin_lookup('bool')
+        base_nodes = scoped_nodes.builtin_lookup('bool')
     elif cls.name == 'FloatField':
-        base_node = scoped_nodes.builtin_lookup('float')
+        base_nodes = scoped_nodes.builtin_lookup('float')
     elif cls.name == 'DecimalField':
-        base_node = MANAGER.ast_from_module_name('decimal').lookup('Decimal')
+        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_node = MANAGER.ast_from_module_name('datetime').lookup('datetime')
+        base_nodes = MANAGER.ast_from_module_name('datetime').lookup('datetime')
     elif cls.name == 'TimeField':
-        base_node = MANAGER.ast_from_module_name('datetime').lookup('time')
+        base_nodes = MANAGER.ast_from_module_name('datetime').lookup('time')
     elif cls.name == 'DateField':
-        base_node = MANAGER.ast_from_module_name('datetime').lookup('date')
+        base_nodes = MANAGER.ast_from_module_name('datetime').lookup('date')
     elif cls.name == 'ManyToManyField':
-        base_node = MANAGER.ast_from_module_name('django.db.models.query').lookup('QuerySet')
+        base_nodes = MANAGER.ast_from_module_name('django.db.models.query').lookup('QuerySet')
     elif cls.name in ('ImageField', 'FileField'):
-        base_node = MANAGER.ast_from_module_name('django.core.files.base').lookup('File')
+        base_nodes = MANAGER.ast_from_module_name('django.core.files.base').lookup('File')
     else:
         return iter([cls])
 
-    return iter([cls] + base_node[1])
+    # 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):
diff --git a/pylint_django/transforms/foreignkey.py b/pylint_django/transforms/foreignkey.py
index 206783f..cd5ab31 100644
--- a/pylint_django/transforms/foreignkey.py
+++ b/pylint_django/transforms/foreignkey.py
@@ -1,14 +1,15 @@
 from astroid import nodes, InferenceError, inference_tip, UseInferenceDefault
+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, nodes.Class):
+    if not isinstance(node.parent.parent, ClassDef):
         return False
 
-    if isinstance(node.func, nodes.Getattr):
+    if isinstance(node.func, Attribute):
         attr = node.func.attrname
     elif isinstance(node.func, nodes.Name):
         attr = node.func.name
@@ -34,7 +35,7 @@ def infer_key_classes(node, context=None):
                     break
     else:
         raise UseInferenceDefault
-    return iter([key_cls.instanciate_class()])
+    return iter([instantiate_class(key_cls)()])
 
 
 def add_transform(manager):
diff --git a/pylint_django/transforms/transforms/django_db_models.py b/pylint_django/transforms/transforms/django_db_models.py
index ec2f183..005a741 100644
--- a/pylint_django/transforms/transforms/django_db_models.py
+++ b/pylint_django/transforms/transforms/django_db_models.py
@@ -1,3 +1,5 @@
+from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist
+
 class Model(object):
     _meta = None
     objects = None
@@ -5,8 +7,8 @@ class Model(object):
     id = None
     pk = None
 
-    MultipleObjectsReturned = None
-    DoesNotExist = None
+    MultipleObjectsReturned = MultipleObjectsReturned
+    DoesNotExist = ObjectDoesNotExist
 
 
 # eliminate E1002 for Manager object
diff --git a/pylint_django/utils.py b/pylint_django/utils.py
index e977a24..70dcaf5 100644
--- a/pylint_django/utils.py
+++ b/pylint_django/utils.py
@@ -1,28 +1,27 @@
 """Utils."""
 from astroid.exceptions import InferenceError
-from astroid.bases import YES, Instance
-from astroid.nodes import Class
+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_name):
+def node_is_subclass(cls, *subclass_names):
     """Checks if cls node has parent with subclass_name."""
-    if not isinstance(cls, (Class, Instance)):
+    if not isinstance(cls, (ClassDef, Instance)):
         return False
 
-    if cls.bases == YES:
-        # ???
+    if cls.bases == Uninferable:
         return False
     for base_cls in cls.bases:
         try:
-            for inf in base_cls.infered():
-                if inf.qname() == subclass_name:
+            for inf in inferred(base_cls)():
+                if inf.qname() in subclass_names:
                     return True
-                if inf != cls and node_is_subclass(inf, subclass_name):
-                    # check up the hierachy in case we are a subclass of
+                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:
diff --git a/scripts/travis-build.sh b/scripts/travis-build.sh
index 7747d7e..02251ca 100755
--- a/scripts/travis-build.sh
+++ b/scripts/travis-build.sh
@@ -1,5 +1,5 @@
 #!/bin/bash
-python scripts/travis-skip.py
+python scripts/travis_skip.py
 
 if [ "$?" -eq "0" ]
 then
diff --git a/scripts/travis-install.sh b/scripts/travis-install.sh
index 9a7d029..a977c10 100755
--- a/scripts/travis-install.sh
+++ b/scripts/travis-install.sh
@@ -1,12 +1,12 @@
 #!/bin/bash
-python scripts/travis-skip.py
+python scripts/travis_skip.py
 
 if [ "$?" -eq "0" ]
 then
-    pip install --use-mirrors coverage coveralls
-    pip install --use-mirrors $DJANGO
-    pip install --use-mirrors git+https://github.com/landscapeio/pylint-plugin-utils.git@develop
-    pip install --use-mirrors --editable .
+    pip install coverage coveralls
+    pip install $DJANGO
+    pip install git+https://github.com/landscapeio/pylint-plugin-utils.git@develop
+    pip install --editable .
 else
     echo "Skipping"
 fi
diff --git a/scripts/travis-skip.py b/scripts/travis_skip.py
similarity index 90%
rename from scripts/travis-skip.py
rename to scripts/travis_skip.py
index f4675a3..8b0932b 100755
--- a/scripts/travis-skip.py
+++ b/scripts/travis_skip.py
@@ -1,5 +1,6 @@
 #!/usr/bin/env python
-import os, sys
+import os
+import sys
 for skip_ver in os.environ.get('SKIP', '').split():
     skip_ver = tuple(map(int, skip_ver.split('.')))
     if skip_ver == sys.version_info[:len(skip_ver)]:
diff --git a/setup.py b/setup.py
index 1eb982e..cdf4d35 100644
--- a/setup.py
+++ b/setup.py
@@ -5,7 +5,7 @@ import os
 import sys
 
 
-_version = '0.6'
+_version = '0.7.1'
 _packages = find_packages(exclude=["*.tests", "*.tests.*", "tests.*", "tests"])
 
 _short_description = "pylint-django is a Pylint plugin to aid Pylint in recognising and understanding" \
@@ -29,6 +29,7 @@ _classifiers = (
     'Programming Language :: Python :: 2.7',
     'Programming Language :: Python :: 3.3',
     'Programming Language :: Python :: 3.4',
+    'Programming Language :: Python :: 3.5',
 )
 
 
@@ -47,8 +48,6 @@ if sys.version_info < (2, 7):
 else:
     _install_requires += [
         'pylint>=1.0',
-        'astroid>=1.0',
-        'logilab-common>=0.60.0',
     ]
 
 setup(
diff --git a/test/external_drf/func_noerror_serializer.py b/test/external_drf/func_noerror_serializer.py
new file mode 100644
index 0000000..5b045a3
--- /dev/null
+++ b/test/external_drf/func_noerror_serializer.py
@@ -0,0 +1,11 @@
+"""
+Checks that Pylint does not complain about DRF serializers
+"""
+#  pylint: disable=C0111,W5101,R0903
+
+from rest_framework import serializers
+
+class TestSerializerSubclass(serializers.ModelSerializer):
+    class Meta:
+        pass
+
diff --git a/test/input/func_noerror_foreign_key_ids.py b/test/input/func_noerror_foreign_key_ids.py
new file mode 100644
index 0000000..201403b
--- /dev/null
+++ b/test/input/func_noerror_foreign_key_ids.py
@@ -0,0 +1,17 @@
+"""
+Checks that Pylint does not complain about foreign key id access
+"""
+#  pylint: disable=C0111,W5101,wrong-import-position
+from django.db import models
+
+
+class SomeModel(models.Model):
+    count = models.IntegerField()
+
+
+class SomeOtherModel(models.Model):
+    some_model = models.ForeignKey(SomeModel)
+    number = models.IntegerField()
+
+    def do_something(self):
+        self.number = self.some_model_id
diff --git a/test/input/func_noerror_foreignkeys.py b/test/input/func_noerror_foreignkeys.py
index 1ac4261..fa82df5 100644
--- a/test/input/func_noerror_foreignkeys.py
+++ b/test/input/func_noerror_foreignkeys.py
@@ -2,7 +2,7 @@
 Checks that Pylint does not complain about various
 methods on Django model fields.
 """
-#  pylint: disable=C0111,W5101
+#  pylint: disable=C0111,W5101,wrong-import-position
 from django.db import models
 from django.db.models import ForeignKey, OneToOneField
 
diff --git a/test/input/func_noerror_forms_py33.py b/test/input/func_noerror_forms_py33.py
index 3cd82c7..0520668 100644
--- a/test/input/func_noerror_forms_py33.py
+++ b/test/input/func_noerror_forms_py33.py
@@ -1,7 +1,7 @@
 """
 Checks that Pylint does not complain about django Forms
 """
-#  pylint: disable=C0111,W5101,R0903
+#  pylint: disable=C0111,W5101,R0903,wrong-import-position
 
 from django import forms
 
diff --git a/test/input/func_noerror_forms_py_28.py b/test/input/func_noerror_forms_py_28.py
index a506c73..b972f52 100644
--- a/test/input/func_noerror_forms_py_28.py
+++ b/test/input/func_noerror_forms_py_28.py
@@ -31,4 +31,3 @@ class TestFormSubclass(forms.Form):
 class TestModelFormSubclass(forms.ModelForm):
     class Meta:
         pass
-
diff --git a/test/input/func_noerror_issue_46.py b/test/input/func_noerror_issue_46.py
new file mode 100644
index 0000000..1adb597
--- /dev/null
+++ b/test/input/func_noerror_issue_46.py
@@ -0,0 +1,11 @@
+"""
+Checks that Pylint does not complain about raising DoesNotExist
+"""
+#  pylint: disable=C0111,W5101,W5103
+from django.db import models
+
+
+class SomeModel(models.Model):
+    pass
+
+raise SomeModel.DoesNotExist
diff --git a/test/input/func_noerror_model_fields.py b/test/input/func_noerror_model_fields.py
index 1390748..7009814 100644
--- a/test/input/func_noerror_model_fields.py
+++ b/test/input/func_noerror_model_fields.py
@@ -5,6 +5,7 @@ methods on Django model fields.
 #  pylint: disable=C0111,W5101
 from __future__ import print_function
 from datetime import datetime, date
+from decimal import Decimal
 from django.db import models
 
 
@@ -66,7 +67,7 @@ class LotsOfFieldsModel(models.Model):
         print(self.datefield.isoformat())
 
     def decimalfield_tests(self):
-        print(self.decimalfield.adjusted())
+        print(self.decimalfield.compare(Decimal('1.4')))
 
     def filefield_tests(self):
         print(self.filefield.file)
diff --git a/test/input/func_noerror_model_unicode_callable.py b/test/input/func_noerror_model_unicode_callable.py
index 94449dd..0ef1406 100644
--- a/test/input/func_noerror_model_unicode_callable.py
+++ b/test/input/func_noerror_model_unicode_callable.py
@@ -1,7 +1,7 @@
 """
 Ensures that django models without a __unicode__ method are flagged
 """
-#  pylint: disable=C0111
+#  pylint: disable=C0111,wrong-import-position
 
 from django.db import models
 
diff --git a/test/input/func_noerror_model_unicode_lambda.py b/test/input/func_noerror_model_unicode_lambda.py
index 51ef89c..22ad0e7 100644
--- a/test/input/func_noerror_model_unicode_lambda.py
+++ b/test/input/func_noerror_model_unicode_lambda.py
@@ -1,7 +1,7 @@
 """
 Ensures that django models without a __unicode__ method are flagged
 """
-#  pylint: disable=C0111
+#  pylint: disable=C0111,wrong-import-position
 
 from django.db import models
 
diff --git a/test/test_func.py b/test/test_func.py
index 9c4694f..9d628c0 100644
--- a/test/test_func.py
+++ b/test/test_func.py
@@ -2,7 +2,6 @@
... 60 lines suppressed ...

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/pylint-django.git



More information about the Python-modules-commits mailing list