Skip to content
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

Fix function inspection of functools.partials in migration files #247

Merged
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Features:

- Fixed `RunPython` model import check when using a `through` object like `MyModel.many_to_many.through.objects.filter(...)` (issue #218)
- Mark the `IgnoreMigration` operation as `elidable=True`
- Handle `functools.partial` functions in RunPython data migrations

Bug:

Expand Down
19 changes: 14 additions & 5 deletions django_migration_linter/migration_linter.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import functools
import hashlib
import inspect
import logging
Expand Down Expand Up @@ -473,10 +474,16 @@ def analyse_data_migration(

return errors, ignored, warnings

@staticmethod
def discover_function(function):
if isinstance(function, functools.partial):
return function.func
return function

def lint_runpython(
self, runpython: RunPython
) -> tuple[list[Issue], list[Issue], list[Issue]]:
function_name = runpython.code.__name__
function_name = self.discover_function(runpython.code).__name__
error = []
ignored = []
warning = []
Expand Down Expand Up @@ -551,8 +558,9 @@ def lint_runpython(
def get_runpython_model_import_issues(code: Callable) -> list[Issue]:
model_object_regex = re.compile(r"[^a-zA-Z0-9._]?([a-zA-Z0-9._]+?)\.objects")

function_name = code.__name__
source_code = inspect.getsource(code)
function = MigrationLinter.discover_function(code)
function_name = function.__name__
source_code = inspect.getsource(function)

called_models = model_object_regex.findall(source_code)
issues = []
Expand Down Expand Up @@ -582,8 +590,9 @@ def get_runpython_model_import_issues(code: Callable) -> list[Issue]:
def get_runpython_model_variable_naming_issues(code: Callable) -> list[Issue]:
model_object_regex = re.compile(r"[^a-zA-Z]?([a-zA-Z0-9]+?)\.objects")

function_name = code.__name__
source_code = inspect.getsource(code)
function = MigrationLinter.discover_function(code)
function_name = function.__name__
source_code = inspect.getsource(function)

called_models = model_object_regex.findall(source_code)
issues = []
Expand Down
9 changes: 9 additions & 0 deletions tests/functional/test_data_migrations.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,15 @@ def test_warnings_as_errors_tests_no_match(self):
self.assertEqual(0, self.linter.nb_erroneous)
self.assertFalse(self.linter.has_errors)

def test_partial_function(self):
reverse_migration = self.linter.migration_loader.disk_migrations[
("app_data_migrations", "0004_partial_function")
]
self.linter.lint_migration(reverse_migration)

self.assertEqual(1, self.linter.nb_warnings)
self.assertFalse(self.linter.has_errors)


class DataMigrationModelImportTestCase(unittest.TestCase):
def test_missing_get_model_import(self):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Generated by Django 2.2 on 2020-01-27 17:47

from __future__ import annotations

import functools

from django.db import migrations


def backward_op(c, david, test):
pass


def forward_op(const, arg1, arg2):
pass


class Migration(migrations.Migration):
dependencies = [("app_data_migrations", "0003_incorrect_arguments")]

operations = [
migrations.RunPython(
functools.partial(forward_op, "constant"),
functools.partial(backward_op, "constant"),
)
]