Merge branch 'master' into do-not-check-git-folder

This commit is contained in:
Felix committed 2019-01-07 12:19:25 +01:00
commit 3ae4840e34
18 files changed
+107 -43

No files matched your search

+2 -3
View File
@@ -16,7 +16,7 @@ import hashlib
import os import os
import pickle import pickle
from django_migration_linter.utils import split_path, compose_migration_path from .utils import split_path
class Cache(dict): class Cache(dict):
@@ -42,8 +42,7 @@ class Cache(dict):
with open(self.filename, "wb") as f: with open(self.filename, "wb") as f:
pickle.dump(self, f, protocol=2) pickle.dump(self, f, protocol=2)
def md5(self, app_name, migration): def md5(self, path):
path = compose_migration_path(self.django_folder, app_name, migration)
hash_md5 = hashlib.md5() hash_md5 = hashlib.md5()
with open(path, "rb") as f: with open(path, "rb") as f:
for chunk in iter(lambda: f.read(4096), b""): for chunk in iter(lambda: f.read(4096), b""):
+12
View File
@@ -0,0 +1,12 @@
from django_migration_linter.utils import split_migration_path
class Migration(object):
def __init__(self, abs_path):
self.abs_path = abs_path
self.app_name, self.name = split_migration_path(self.abs_path)
def __lt__(self, other):
if not isinstance(other, Migration):
return NotImplemented
return self.abs_path < other.abs_path
+16 -18
View File
@@ -19,10 +19,10 @@ import re
from subprocess import Popen, PIPE from subprocess import Popen, PIPE
import sys import sys
from django_migration_linter.cache import Cache from .cache import Cache
from django_migration_linter.constants import DEFAULT_CACHE_PATH, MIGRATION_FOLDER_NAME from .constants import DEFAULT_CACHE_PATH, MIGRATION_FOLDER_NAME
from django_migration_linter.utils import split_migration_path from .migration import Migration
from . import utils from .utils import is_directory, is_django_project, is_git_project, clean_bytes_to_str
from .sql_analyser import analyse_sql_statements from .sql_analyser import analyse_sql_statements
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -31,13 +31,13 @@ logger = logging.getLogger(__name__)
class MigrationLinter(object): class MigrationLinter(object):
def __init__(self, project_path, **kwargs): def __init__(self, project_path, **kwargs):
# Verify correctness # Verify correctness
if not utils.is_directory(project_path): if not is_directory(project_path):
raise ValueError( raise ValueError(
"The given path {0} does not seem to be a directory.".format( "The given path {0} does not seem to be a directory.".format(
project_path project_path
) )
) )
if not utils.is_django_project(project_path): if not is_django_project(project_path):
raise ValueError( raise ValueError(
("The given path {0} does not " "seem to be a django project.").format( ("The given path {0} does not " "seem to be a django project.").format(
project_path project_path
@@ -72,11 +72,13 @@ class MigrationLinter(object):
if not self.no_cache: if not self.no_cache:
self.old_cache.load() self.old_cache.load()
def lint_migration(self, app_name, migration_name): def lint_migration(self, migration):
app_name = migration.app_name
migration_name = migration.name
print("({0}, {1})... ".format(app_name, migration_name), end="") print("({0}, {1})... ".format(app_name, migration_name), end="")
self.nb_total += 1 self.nb_total += 1
md5hash = self.old_cache.md5(app_name, migration_name) md5hash = self.old_cache.md5(migration.abs_path)
if md5hash in self.old_cache: if md5hash in self.old_cache:
if self.old_cache[md5hash]["result"] == "IGNORE": if self.old_cache[md5hash]["result"] == "IGNORE":
print("IGNORE (cached)") print("IGNORE (cached)")
@@ -140,7 +142,7 @@ class MigrationLinter(object):
# Lint those migrations # Lint those migrations
for m in migrations: for m in migrations:
self.lint_migration(*m) self.lint_migration(m)
if not self.no_cache: if not self.no_cache:
self.new_cache.save() self.new_cache.save()
@@ -182,9 +184,7 @@ class MigrationLinter(object):
) )
sql_statements = [] sql_statements = []
for line in map( for line in map(clean_bytes_to_str, sqlmigrate_process.stdout.readlines()):
utils.clean_bytes_to_str, sqlmigrate_process.stdout.readlines()
):
sql_statements.append(line) sql_statements.append(line)
sqlmigrate_process.wait() sqlmigrate_process.wait()
if sqlmigrate_process.returncode != 0: if sqlmigrate_process.returncode != 0:
@@ -203,19 +203,18 @@ class MigrationLinter(object):
).format(self.django_path, git_commit_id) ).format(self.django_path, git_commit_id)
logger.info("Executing {0}".format(git_diff_command)) logger.info("Executing {0}".format(git_diff_command))
diff_process = Popen(git_diff_command, shell=True, stdout=PIPE, stderr=PIPE) diff_process = Popen(git_diff_command, shell=True, stdout=PIPE, stderr=PIPE)
for line in map(utils.clean_bytes_to_str, diff_process.stdout.readlines()): for line in map(clean_bytes_to_str, diff_process.stdout.readlines()):
# Only gather lines that include added migrations # Only gather lines that include added migrations
if ( if (
re.search(r"\/{0}\/.*\.py".format(MIGRATION_FOLDER_NAME), line) re.search(r"\/{0}\/.*\.py".format(MIGRATION_FOLDER_NAME), line)
and "__init__" not in line and "__init__" not in line
): ):
app_name, migration_name = split_migration_path(line) migrations.append(Migration(os.path.join(self.django_path, line)))
migrations.append((app_name, migration_name))
diff_process.wait() diff_process.wait()
if diff_process.returncode != 0: if diff_process.returncode != 0:
output = [] output = []
for line in map(utils.clean_bytes_to_str, diff_process.stderr.readlines()): for line in map(clean_bytes_to_str, diff_process.stderr.readlines()):
output.append(line) output.append(line)
logger.error("Error while git diff command:\n{}".format("".join(output))) logger.error("Error while git diff command:\n{}".format("".join(output)))
raise Exception("Error while executing git diff command") raise Exception("Error while executing git diff command")
@@ -231,8 +230,7 @@ class MigrationLinter(object):
and file_name != "__init__.py" and file_name != "__init__.py"
): ):
full_migration_path = os.path.join(root, file_name) full_migration_path = os.path.join(root, file_name)
app_name, migration_name = split_migration_path(full_migration_path) migrations.append(Migration(full_migration_path))
migrations.append((app_name, migration_name))
return migrations return migrations
def should_ignore_migration(self, app_name, migration_name): def should_ignore_migration(self, app_name, migration_name):
+1 -1
View File
@@ -15,7 +15,7 @@
import re import re
import logging import logging
from django_migration_linter.operations import IGNORE_MIGRATION_SQL from .operations import IGNORE_MIGRATION_SQL
IGNORED_MIGRATION = "IGNORED_MIGRATION" IGNORED_MIGRATION = "IGNORED_MIGRATION"
+1 -1
View File
@@ -17,7 +17,7 @@ from __future__ import print_function
import os import os
import sys import sys
from django_migration_linter.constants import MIGRATION_FOLDER_NAME from .constants import MIGRATION_FOLDER_NAME
def is_django_project(path): def is_django_project(path):
+15 -10
View File
@@ -36,13 +36,14 @@ class CallLinterFromCommandLineTest(unittest.TestCase):
process.wait() process.wait()
self.assertEqual(process.returncode, 0) self.assertEqual(process.returncode, 0)
lines = list(map(utils.clean_bytes_to_str, process.stdout.readlines())) lines = list(map(utils.clean_bytes_to_str, process.stdout.readlines()))
self.assertEqual(len(lines), 5) self.assertEqual(len(lines), 6)
self.assertEqual( self.assertEqual(
sorted(lines[:3]), sorted(lines[:4]),
sorted([ sorted([
"(test_app1, 0001_initial)... OK", "(test_app1, 0001_initial)... OK",
"(test_app1, 0002_a_new_null_field)... OK", "(test_app1, 0002_a_new_null_field)... OK",
"(test_app2, 0001_foo)... OK", "(test_app2, 0001_foo)... OK",
"(test_app3, 0001_initial)... OK",
]) ])
) )
@@ -71,13 +72,14 @@ class CallLinterFromCommandLineTest(unittest.TestCase):
process.wait() process.wait()
self.assertEqual(process.returncode, 0) self.assertEqual(process.returncode, 0)
lines = list(map(utils.clean_bytes_to_str, process.stdout.readlines())) lines = list(map(utils.clean_bytes_to_str, process.stdout.readlines()))
self.assertEqual(len(lines), 5) self.assertEqual(len(lines), 6)
self.assertEqual( self.assertEqual(
sorted(lines[:3]), sorted(lines[:4]),
sorted([ sorted([
"(test_app1, 0001_initial)... OK", "(test_app1, 0001_initial)... OK",
"(test_app1, 0002_a_new_null_field)... OK", "(test_app1, 0002_a_new_null_field)... OK",
"(test_app2, 0001_foo)... IGNORE", "(test_app2, 0001_foo)... IGNORE",
"(test_app3, 0001_initial)... OK",
]) ])
) )
@@ -91,13 +93,14 @@ class CallLinterFromCommandLineTest(unittest.TestCase):
process.wait() process.wait()
self.assertEqual(process.returncode, 0) self.assertEqual(process.returncode, 0)
lines = list(map(utils.clean_bytes_to_str, process.stdout.readlines())) lines = list(map(utils.clean_bytes_to_str, process.stdout.readlines()))
self.assertEqual(len(lines), 5) self.assertEqual(len(lines), 6)
self.assertEqual( self.assertEqual(
sorted(lines[:3]), sorted(lines[:4]),
sorted([ sorted([
"(test_app1, 0001_initial)... IGNORE", "(test_app1, 0001_initial)... IGNORE",
"(test_app1, 0002_a_new_null_field)... IGNORE", "(test_app1, 0002_a_new_null_field)... IGNORE",
"(test_app2, 0001_foo)... OK", "(test_app2, 0001_foo)... OK",
"(test_app3, 0001_initial)... IGNORE",
]) ])
) )
@@ -111,13 +114,14 @@ class CallLinterFromCommandLineTest(unittest.TestCase):
process.wait() process.wait()
self.assertEqual(process.returncode, 0) self.assertEqual(process.returncode, 0)
lines = list(map(utils.clean_bytes_to_str, process.stdout.readlines())) lines = list(map(utils.clean_bytes_to_str, process.stdout.readlines()))
self.assertEqual(len(lines), 5) self.assertEqual(len(lines), 6)
self.assertEqual( self.assertEqual(
sorted(lines[:3]), sorted(lines[:4]),
sorted([ sorted([
"(test_app1, 0001_initial)... IGNORE", "(test_app1, 0001_initial)... IGNORE",
"(test_app1, 0002_a_new_null_field)... OK", "(test_app1, 0002_a_new_null_field)... OK",
"(test_app2, 0001_foo)... OK", "(test_app2, 0001_foo)... OK",
"(test_app3, 0001_initial)... IGNORE",
]) ])
) )
@@ -131,13 +135,14 @@ class CallLinterFromCommandLineTest(unittest.TestCase):
process.wait() process.wait()
self.assertEqual(process.returncode, 0) self.assertEqual(process.returncode, 0)
lines = list(map(utils.clean_bytes_to_str, process.stdout.readlines())) lines = list(map(utils.clean_bytes_to_str, process.stdout.readlines()))
self.assertEqual(len(lines), 5) self.assertEqual(len(lines), 6)
self.assertEqual( self.assertEqual(
sorted(lines[:3]), sorted(lines[:4]),
sorted([ sorted([
"(test_app1, 0001_initial)... IGNORE", "(test_app1, 0001_initial)... IGNORE",
"(test_app1, 0002_a_new_null_field)... OK", "(test_app1, 0002_a_new_null_field)... OK",
"(test_app2, 0001_foo)... IGNORE", "(test_app2, 0001_foo)... IGNORE",
"(test_app3, 0001_initial)... IGNORE",
]) ])
) )
+6 -4
View File
@@ -13,8 +13,10 @@
# limitations under the License. # limitations under the License.
import unittest import unittest
import os
from django_migration_linter import MigrationLinter from django_migration_linter import MigrationLinter
from django_migration_linter.migration import Migration
from tests import fixtures from tests import fixtures
@@ -24,10 +26,10 @@ class TestMultipleLinters(unittest.TestCase):
l2 = MigrationLinter(fixtures.RENAME_COLUMN_PROJECT) l2 = MigrationLinter(fixtures.RENAME_COLUMN_PROJECT)
l3 = MigrationLinter(fixtures.CORRECT_PROJECT) l3 = MigrationLinter(fixtures.CORRECT_PROJECT)
l1.lint_migration('test_app', '0002_add_new_not_null_field') l1.lint_migration(Migration(os.path.join(fixtures.ADD_NOT_NULL_COLUMN_PROJECT, 'test_app/migrations/0002_add_new_not_null_field.py')))
l2.lint_migration('test_app', '0002_rename_column') l2.lint_migration(Migration(os.path.join(fixtures.RENAME_COLUMN_PROJECT, 'test_app/migrations/0002_rename_column.py')))
l3.lint_migration('test_app1', '0001_initial') l3.lint_migration(Migration(os.path.join(fixtures.CORRECT_PROJECT, 'test_app1/migrations/0001_initial.py')))
l3.lint_migration('test_app1', '0002_a_new_null_field') l3.lint_migration(Migration(os.path.join(fixtures.CORRECT_PROJECT, 'test_app1/migrations/0002_a_new_null_field.py')))
self.assertTrue(l1.has_errors) self.assertTrue(l1.has_errors)
self.assertTrue(l2.has_errors) self.assertTrue(l2.has_errors)
Whitespace-only changes.
@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.
@@ -0,0 +1,5 @@
from django.apps import AppConfig
class TestApp3Config(AppConfig):
name = 'test_app3'
@@ -0,0 +1,21 @@
# Generated by Django 2.1.4 on 2019-01-04 20:55
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='C',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('not_null_field', models.IntegerField()),
],
),
]
@@ -0,0 +1,4 @@
from django.db import models
class C(models.Model):
not_null_field = models.IntegerField(null=False)
@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.
@@ -0,0 +1,3 @@
from django.shortcuts import render
# Create your views here.
@@ -11,10 +11,12 @@ https://docs.djangoproject.com/en/1.11/ref/settings/
""" """
import os import os
import sys
# Build paths inside the project like this: os.path.join(BASE_DIR, ...) # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))
# Quick-start development settings - unsuitable for production # Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/ # See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/
@@ -38,7 +40,8 @@ INSTALLED_APPS = [
'django.contrib.messages', 'django.contrib.messages',
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'test_app1', 'test_app1',
'test_app2' 'test_app2',
'apps.test_app3',
] ]
MIDDLEWARE = [ MIDDLEWARE = [
+11 -5
View File
@@ -13,8 +13,10 @@
# limitations under the License. # limitations under the License.
import unittest import unittest
import os
from django_migration_linter import MigrationLinter from django_migration_linter import MigrationLinter
from django_migration_linter.migration import Migration
from tests import fixtures from tests import fixtures
@@ -32,13 +34,16 @@ class TestLinterFunctions(unittest.TestCase):
linter = MigrationLinter(project_path) linter = MigrationLinter(project_path)
self.assertFalse(linter.has_errors) self.assertFalse(linter.has_errors)
linter.lint_migration('test_app', '0001_create_table') m = Migration(os.path.join(project_path, 'test_app/migrations/0001_create_table.py'))
linter.lint_migration(m)
self.assertFalse(linter.has_errors) self.assertFalse(linter.has_errors)
linter.lint_migration('test_app', '0002_add_new_not_null_field') m = Migration(os.path.join(project_path, 'test_app/migrations/0002_add_new_not_null_field.py'))
linter.lint_migration(m)
self.assertTrue(linter.has_errors) self.assertTrue(linter.has_errors)
linter.lint_migration('test_app', '0001_create_table') m = Migration(os.path.join(project_path, 'test_app/migrations/0001_create_table.py'))
linter.lint_migration(m)
self.assertTrue(linter.has_errors) self.assertTrue(linter.has_errors)
def test_linter_creation(self): def test_linter_creation(self):
@@ -80,12 +85,13 @@ class TestLinterFunctions(unittest.TestCase):
def test_gather_all_migrations(self): def test_gather_all_migrations(self):
linter = MigrationLinter(fixtures.CORRECT_PROJECT) linter = MigrationLinter(fixtures.CORRECT_PROJECT)
migrations = linter._gather_all_migrations() migrations = linter._gather_all_migrations()
self.assertEqual(len(migrations), 3) self.assertEqual(len(migrations), 4)
self.assertEqual( self.assertEqual(
sorted(migrations), sorted([(m.app_name, m.name) for m in migrations]),
sorted([ sorted([
("test_app1", "0001_initial"), ("test_app1", "0001_initial"),
("test_app1", "0002_a_new_null_field"), ("test_app1", "0002_a_new_null_field"),
("test_app2", "0001_foo"), ("test_app2", "0001_foo"),
("test_app3", "0001_initial"),
]) ])
) )