Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
37 changes: 9 additions & 28 deletions pylint_django/augmentations/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -412,33 +412,27 @@ def is_model_meta_subclass(node):
'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',)
'django_filters.filterset.FilterSet',
'factory.django.DjangoModelFactory',)
return node_is_subclass(node.parent, *parents)


def is_model_factory(node):
"""Checks that node is derivative of SubFactory class."""
"""Checks that node is derivative of DjangoModelFactory or SubFactory class."""
try:
parent_classes = node.expr.inferred()
except: # noqa: E722, pylint: disable=bare-except
return False

parents = ('factory.declarations.SubFactory',)
parents = ('factory.declarations.SubFactory', 'factory.django.DjangoModelFactory')

for parent_class in parent_classes:
try:
if parent_class.qname() in parents:
return True

if node_is_subclass(parent_class, *parents):
return True
except AttributeError:
continue

Expand Down Expand Up @@ -714,17 +708,8 @@ def wrap_func(*args, **kwargs):

def is_wsgi_application(node):
frame = node.frame()

if node.name == 'application' and isinstance(frame, Module):
path_ends_with_wsgi = False
for path in frame.path:
if path.endswith('wsgi.py'):
path_ends_with_wsgi = True
break

return frame.name == 'wsgi' or path_ends_with_wsgi or frame.file.endswith('wsgi.py')

return False
return node.name == 'application' and isinstance(frame, Module) and \
(frame.name == 'wsgi' or frame.path[0].endswith('wsgi.py') or frame.file.endswith('wsgi.py'))


def apply_augmentations(linter):
Expand Down Expand Up @@ -809,10 +794,6 @@ def apply_augmentations(linter):
suppress_message(linter, MisdesignChecker.leave_classdef, 'too-few-public-methods', is_model_mpttmeta_subclass)

# factory_boy's DjangoModelFactory
suppress_message(linter, DocStringChecker.visit_classdef, 'missing-docstring', is_model_factory_meta_subclass)
suppress_message(linter, NewStyleConflictChecker.visit_classdef, 'old-style-class', is_model_factory_meta_subclass)
suppress_message(linter, ClassChecker.visit_classdef, 'W0232', is_model_factory_meta_subclass)
suppress_message(linter, MisdesignChecker.leave_classdef, 'too-few-public-methods', is_model_factory_meta_subclass)
suppress_message(linter, TypeChecker.visit_attribute, 'no-member', is_model_factory)

# ForeignKey and OneToOneField
Expand Down
4 changes: 2 additions & 2 deletions pylint_django/checkers/db_performance.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def _is_migrations_module(node):
if not isinstance(node, astroid.Module):
return False

return 'migrations' in node.path and not node.path.endswith('__init__.py')
return 'migrations' in node.path[0] and not node.path[0].endswith('__init__.py')


class NewDbFieldWithDefaultChecker(checkers.BaseChecker):
Expand Down Expand Up @@ -102,7 +102,7 @@ def _path(node):
last_name_space = ''
latest_migrations = []
for module in self._migration_modules:
name_space = module.path.split('migrations')[0]
name_space = module.path[0].split('migrations')[0]
if name_space != last_name_space:
last_name_space = name_space
latest_migrations.append(module)
Expand Down
57 changes: 19 additions & 38 deletions pylint_django/transforms/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,53 +2,34 @@
import os
import re

from astroid import MANAGER
from astroid.builder import AstroidBuilder
from astroid import nodes
import astroid

from pylint_django.transforms import foreignkey, fields


foreignkey.add_transform(MANAGER)
fields.add_transforms(MANAGER)
foreignkey.add_transform(astroid.MANAGER)
fields.add_transforms(astroid.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))
def _add_transform(package_name):
def fake_module_builder():
"""
Build a fake module to use within transformations.
@package_name is a parameter from the outher scope b/c according to
the docs this can't receive any parameters.
http://pylint.pycqa.org/projects/astroid/en/latest/extending.html?highlight=MANAGER#module-extender-transforms
"""
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()
with open(fake_module_path) as modulefile:
fake_module = modulefile.read()

fake = AstroidBuilder(MANAGER).string_build(fake_module)
return astroid.builder.AstroidBuilder(astroid.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])
astroid.register_module_extender(astroid.MANAGER, package_name, fake_module_builder)

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')
_add_transform('model_utils.managers',
'InheritanceManager',
'QueryManager',
'SoftDeletableManager')
_add_transform('django.utils.translation')
# register transform for FileField/ImageField, see #60
_add_transform('django.db.models.fields.files', 'FileField')
_add_transform('django.db.models.fields.files')
23 changes: 21 additions & 2 deletions pylint_django/transforms/foreignkey.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,31 @@ def infer_key_classes(node, context=None):
break
elif isinstance(arg, nodes.Const):
try:
model_name = arg.value.split('.')[-1] # can be 'Model' or 'app.Model'
# can be 'Model' or 'app.Model'
module_name, _, model_name = arg.value.rpartition('.')
except AttributeError:
break

# when ForeignKey is specified only by class name we assume that
# this class must be found in the current module
if not module_name:
current_module = node.frame()
while not isinstance(current_module, nodes.Module):
current_module = current_module.parent.frame()

module_name = current_module.name
elif not module_name.endswith('models'):
# otherwise Django allows specifying an app name first, e.g.
# ForeignKey('auth.User') so we try to convert that to
# 'auth.models', 'User' which works nicely with the `endswith()`
# comparison below
module_name += '.models'

for module in MANAGER.astroid_cache.values():
if model_name in module.locals:
# only load model classes from modules which match the module in
# which *we think* they are defined. This will prevent infering
# other models of the same name which are found elsewhere!
if model_name in module.locals and module.name.endswith(module_name):
class_defs = [
module_node for module_node in module.lookup(model_name)[1]
if isinstance(module_node, nodes.ClassDef)
Expand Down

This file was deleted.

This file was deleted.

62 changes: 0 additions & 62 deletions pylint_django/transforms/transforms/django_db_models.py

This file was deleted.

Loading