Skip to content

Merge updated fork #113

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 26 commits into from
Jan 18, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
fa083d0
Update test matrix and how tests are started on Travis
atodorov Jan 18, 2018
533b66c
Update the way we load tests
atodorov Jan 16, 2018
fbdddd6
Remove missing W5101 and W5103 pylint warnings
atodorov Jan 16, 2018
d8a55c3
Replace C0111 with missing-docstring for better readability
atodorov Jan 16, 2018
59257f9
Add required on_delete attribute, fixes no-value-for-parameter
atodorov Jan 16, 2018
10d612a
Remove missing IPAddressField b/c we test with Django 1.11+
atodorov Jan 16, 2018
fd68bc1
Disable augmentations for missing R0924 badly-implemeted-container
atodorov Jan 16, 2018
85bf1b7
Remove R0903 too-few-public-methods from test files
atodorov Jan 16, 2018
4b39e90
Use human readable name for suppressed message
atodorov Jan 16, 2018
8376e28
Remove unused tox configuration
atodorov Jan 18, 2018
cd1f67d
Add failure notifications for Travis CI
atodorov Jan 16, 2018
5bb7be2
Drop support for testing on Python 2
atodorov Jan 16, 2018
bec095f
Update comments in test
atodorov Jan 16, 2018
893d72b
Enable test case which is Django 1.8+
atodorov Jan 16, 2018
a8f668b
Update format of expected error messages for unicode test
atodorov Jan 16, 2018
18c8f73
Enable test for django-rest-framework serializer
atodorov Jan 16, 2018
527221c
Use PYTHONPATH for the shell script referenced in README
atodorov Jan 16, 2018
81e8294
Add common names to good_names instead of modifying const_rgx
vinaypai Dec 27, 2017
5f14ada
Add handler500 to good_names
atodorov Jan 17, 2018
0797e73
Support factory_boy's DjangoModelFactory Meta class
kouk Oct 17, 2017
ce00090
Create func_noerror_ugettext_lazy_format.py
canarduck Aug 14, 2017
e9975b5
ugettext_lazy is supposed to return six.text_type
canarduck Aug 14, 2017
d1e3663
Adding tests and transforms for DurationField
jmaroeder Aug 8, 2017
4241d9d
Add json field to WSGIRequest proxy
shawnjk Mar 9, 2017
e5767ed
add support for django.contrib.postgres.fields and also UUIDField
Sep 15, 2016
f6a5ac2
Update changelog
atodorov Jan 18, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 12 additions & 9 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
sudo: false
language: python
python:
- "2.7"
- "3.3"
- "3.4"
- "3.5"
- "3.6"
env:
- DJANGO='Django>=1.7,<1.8' SKIP='2.6 3.5'
- DJANGO='Django>=1.8,<1.9' SKIP='2.6'
- DJANGO='Django>=1.9,<1.10' SKIP='2.6 3.3'
- DJANGO='Django>=1.10,<1.11' SKIP='2.6 3.3'
- DJANGO="1.11,<2.0"
- DJANGO="2.0,<3.0"

install:
scripts/travis-install.sh
- pip install coverage coveralls djangorestframework psycopg2
- pip install "Django>=$DJANGO"
- pip install git+https://github.com/landscapeio/pylint-plugin-utils.git@master
script:
scripts/travis-build.sh
- PYTHONPATH=. coverage run test/test_func.py
after_success:
coveralls
notifications:
email:
on_failure: change
on_success: never
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,30 @@
# Changelog

## Version 0.8.0 (unreleased)

* [#109](http://github.com/landscapeio/pylint-django/pull/109),
adding 'urlpatterns', 'register', 'app_name' to good names. Obsoletes
[#111](http://github.com/landscapeio/pylint-django/pull/111), fixes
[#108](http://github.com/landscapeio/pylint-django/issues/108)
(Vinay Pai)
* Add 'handler500' to good names (Mr. Senko)
* [#103](http://github.com/landscapeio/pylint-django/pull/103):
Support factory_boy's DjangoModelFactory Meta class (Konstantinos Koukopoulos)
* [#100](https://github.com/landscapeio/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/landscapeio/pylint-django/issues/80) (canarduck)
* [#99](https://github.com/landscapeio/pylint-django/pull/99):
Add tests and transforms for DurationField, fixes
[#95](https://github.com/landscapeio/pylint-django/issues/95) (James M. Allen)
* [#92](https://github.com/landscapeio/pylint-django/pull/92):
Add json field to WSGIRequest proxy (sjk4sc)
* [#84](https://github.com/landscapeio/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 for latest version of pylint (>=1.8)

## Version 0.7.4
* [#88](https://github.com/landscapeio/pylint-django/pull/88) Fixed builds with Django 1.10 (thanks to [federicobond](https://github.com/federicobond))
* [#91](https://github.com/landscapeio/pylint-django/pull/91) Fixed race condition when running with pylint parallel execution mode (thanks to [jeremycarroll](https://github.com/jeremycarroll))
Expand Down
3 changes: 2 additions & 1 deletion CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@
* [jproffitt](https://github.com/jproffitt)
* [lhupfeldt](https://github.com/lhupfeldt)
* [smirolo](https://github.com/smirolo)
* [mbertolacci](https://github.com/mbertolacci)
* [mbertolacci](https://github.com/mbertolacci)
* [atodorov](https://github.com/atodorov)
33 changes: 21 additions & 12 deletions pylint_django/augmentations/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,16 @@ def is_model_meta_subclass(node):
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):
Expand Down Expand Up @@ -715,15 +725,16 @@ def apply_augmentations(linter):
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), 'R0901', is_class('django.views.generic.edit.FormView'))
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.
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'))
# 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)
Expand All @@ -739,14 +750,6 @@ def apply_augmentations(linter):
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)

# 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, _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)
# TODO: Count public methods of django.contrib.admin.options.ModelAdmin and increase
Expand All @@ -772,6 +775,12 @@ def apply_augmentations(linter):
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
Expand Down
9 changes: 2 additions & 7 deletions pylint_django/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,8 @@ def register(linter):
However, we will also use it to amend existing checker config.
"""
name_checker = get_checker(linter, NameChecker)
name_checker.config.good_names += ('qs',)

# Default pylint.checkers.base.CONST_NAME_RGX = re.compile('(([A-Z_][A-Z0-9_]*)|(__.*__))$').
start = name_checker.config.const_rgx.pattern[:-2]
end = name_checker.config.const_rgx.pattern[-2:]
const_rgx = '%s|(urls|urlpatterns|register)%s' % (start, end)
name_checker.config.const_rgx = re.compile(const_rgx)
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')
Expand Down
15 changes: 14 additions & 1 deletion pylint_django/transforms/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@
_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')
return cls.qname().startswith('django.db.models.fields') or \
cls.qname().startswith('django.contrib.postgres.fields')


def is_form_field(cls):
Expand Down Expand Up @@ -45,10 +48,20 @@ def apply_type_shim(cls, context=None): # noqa
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])

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from 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
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
class WSGIRequest(WSGIRequestOriginal):
status_code = None
content = ''
json = None
11 changes: 11 additions & 0 deletions pylint_django/transforms/transforms/django_db_models_fields.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from django.db.models import fields as django_fields
import datetime
from decimal import Decimal
from uuid import UUID


# --------
Expand Down Expand Up @@ -110,6 +111,12 @@ def __new__(cls, verbose_name=None, name=None, auto_now=False,
pass


class DurationField(datetime.timedelta, django_fields.DurationField):
if PY3:
def __new__(cls, verbose_name=None, name=None, **kwargs):
pass


# -------
# misc

Expand All @@ -119,3 +126,7 @@ class GenericIPAddressField(str, django_fields.GenericIPAddressField):

class IPAddressField(str, django_fields.IPAddressField):
pass


class UUIDField(UUID, django_fields.UUIDField):
pass
8 changes: 7 additions & 1 deletion pylint_django/transforms/transforms/django_forms_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,13 @@ 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):
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
ugettext_lazy = lambda x: None
ugettext_lazy = lambda x: ''
2 changes: 1 addition & 1 deletion scripts/test.sh
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
#!/bin/bash
python test/test_func.py
PYTHONPATH=. python test/test_func.py
9 changes: 0 additions & 9 deletions scripts/travis-build.sh

This file was deleted.

12 changes: 0 additions & 12 deletions scripts/travis-install.sh

This file was deleted.

8 changes: 0 additions & 8 deletions scripts/travis_skip.py

This file was deleted.

5 changes: 2 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,9 @@
'Intended Audience :: Developers',
'Operating System :: Unix',
'Topic :: Software Development :: Quality Assurance',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
)


Expand Down
Empty file added test/__init__.py
Empty file.
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
"""
Checks that Pylint does not complain about DRF serializers
"""
# pylint: disable=C0111,W5101,R0903
# pylint: disable=C0111,W5101

from rest_framework import serializers

class TestSerializerSubclass(serializers.ModelSerializer):
class Meta:
pass

2 changes: 2 additions & 0 deletions test/input/external_drf_noerror_serializer.rc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[testoptions]
requires = rest_framework
Loading