# Path: django_migration_linter/cache.py
# class Cache(dict):
#     def __init__(self, django_folder, database, cache_path):
#         self.filename = os.path.join(
#             cache_path,
#             "{0}_{1}.pickle".format(django_folder.replace(os.sep, "_"), database),
#         )
# 
#         if not os.path.exists(os.path.dirname(self.filename)):
#             os.makedirs(os.path.dirname(self.filename))
# 
#         super(Cache, self).__init__()
# 
#     def load(self):
#         try:
#             with open(self.filename, "rb") as f:
#                 tmp_dict = pickle.load(f)
#                 self.update(tmp_dict)
#         except IOError:
#             pass
# 
#     def save(self):
#         with open(self.filename, "wb") as f:
#             pickle.dump(self, f, protocol=2)
# 
# Path: django_migration_linter/constants.py
# DEFAULT_CACHE_PATH = user_cache_dir("django-migration-linter", version=__version__)
# 
# DJANGO_APPS_WITH_MIGRATIONS = ("admin", "auth", "contenttypes", "sessions")
# 
# EXPECTED_DATA_MIGRATION_ARGS = ("apps", "schema_editor")
# 
# Path: django_migration_linter/operations.py
# class IgnoreMigration(Operation):
#     """
#     No-op migration operation that will enable the Django Migration Linter
#     to detect if the entire migration should be ignored (through code).
#     """
# 
#     reversible = True
#     reduces_to_sql = False
# 
#     def state_forwards(self, app_label, state):
#         pass
# 
#     def database_forwards(self, app_label, schema_editor, from_state, to_state):
#         pass
# 
#     def database_backwards(self, app_label, schema_editor, from_state, to_state):
#         pass
# 
#     def describe(self):
#         return "The Django migration linter will ignore this migration"
# 
# Path: django_migration_linter/sql_analyser/analyser.py
# def analyse_sql_statements(
#     sql_analyser_class, sql_statements, exclude_migration_tests=None
# ):
#     sql_analyser = sql_analyser_class(exclude_migration_tests)
#     sql_analyser.analyse(sql_statements)
#     return sql_analyser.errors, sql_analyser.ignored, sql_analyser.warnings
# 
# def get_sql_analyser_class(database_vendor, analyser_string=None):
#     if analyser_string:
#         return get_sql_analyser_from_string(analyser_string)
#     return get_sql_analyser_class_from_db_vendor(database_vendor)
# 
# Path: django_migration_linter/utils.py
# def clean_bytes_to_str(byte_input):
#     return byte_input.decode("utf-8").strip()
# 
# def get_migration_abspath(app_label, migration_name):
#     from django.db.migrations.loader import MigrationLoader
# 
#     module_name, _ = MigrationLoader.migrations_module(app_label)
#     migration_path = "{}.{}".format(module_name, migration_name)
#     migration_module = import_module(migration_path)
# 
#     migration_file = migration_module.__file__
#     if migration_file.endswith(".pyc"):
#         migration_file = migration_file[:-1]
#     return migration_file
# 
# def split_migration_path(migration_path):
#     from django.db.migrations.loader import MIGRATIONS_MODULE_NAME
# 
#     decomposed_path = split_path(migration_path)
#     for i, p in enumerate(decomposed_path):
#         if p == MIGRATIONS_MODULE_NAME:
#             return decomposed_path[i - 1], os.path.splitext(decomposed_path[i + 1])[0]


import hashlib
import inspect
import logging
import os
import re
from enum import Enum, unique
from subprocess import PIPE, Popen
from django.conf import settings
from django.core.management import call_command
from django.db import DEFAULT_DB_ALIAS, ProgrammingError, connections
from django.db.migrations import RunPython, RunSQL
from .cache import Cache
from .constants import (
    DEFAULT_CACHE_PATH,
    DJANGO_APPS_WITH_MIGRATIONS,
    EXPECTED_DATA_MIGRATION_ARGS,
)
from .operations import IgnoreMigration
from .sql_analyser import analyse_sql_statements, get_sql_analyser_class
from .utils import clean_bytes_to_str, get_migration_abspath, split_migration_path
        from django.db.migrations.loader import MigrationLoader
        from django.db.migrations.loader import MIGRATIONS_MODULE_NAME
            sql_errors, sql_ignored, sql_warnings = analyse_sql_statements(
                self.sql_analyser_class,
                sql_statements,
                self.exclude_migration_tests,
            )
            if sql_errors:
                error += sql_errors
            if sql_ignored:
                ignored += sql_ignored
            if sql_warnings:
                warning += sql_warnings

        # And analysse the reverse SQL
        if runsql.reversible and runsql.reverse_sql != RunSQL.noop:
            sql_statements = []
            if isinstance(runsql.reverse_sql, (list, tuple)):
                for sql in runsql.reverse_sql:
                    params = None
                    if isinstance(sql, (list, tuple)):
                        elements = len(sql)
                        if elements == 2:
                            sql, params = sql
                        else:
                            raise ValueError("Expected a 2-tuple but got %d" % elements)
                        sql_statements.append(sql % params)
                    else:
                        sql_statements.append(sql)
            else:
                sql_statements.append(runsql.reverse_sql)


# Path: django_migration_linter/cache.py
# class Cache(dict):
#     def __init__(self, django_folder, database, cache_path):
#         self.filename = os.path.join(
#             cache_path,
#             "{0}_{1}.pickle".format(django_folder.replace(os.sep, "_"), database),
#         )
# 
#         if not os.path.exists(os.path.dirname(self.filename)):
#             os.makedirs(os.path.dirname(self.filename))
# 
#         super(Cache, self).__init__()
# 
#     def load(self):
#         try:
#             with open(self.filename, "rb") as f:
#                 tmp_dict = pickle.load(f)
#                 self.update(tmp_dict)
#         except IOError:
#             pass
# 
#     def save(self):
#         with open(self.filename, "wb") as f:
#             pickle.dump(self, f, protocol=2)
# 
# Path: django_migration_linter/constants.py
# DEFAULT_CACHE_PATH = user_cache_dir("django-migration-linter", version=__version__)
# 
# DJANGO_APPS_WITH_MIGRATIONS = ("admin", "auth", "contenttypes", "sessions")
# 
# EXPECTED_DATA_MIGRATION_ARGS = ("apps", "schema_editor")
# 
# Path: django_migration_linter/operations.py
# class IgnoreMigration(Operation):
#     """
#     No-op migration operation that will enable the Django Migration Linter
#     to detect if the entire migration should be ignored (through code).
#     """
# 
#     reversible = True
#     reduces_to_sql = False
# 
#     def state_forwards(self, app_label, state):
#         pass
# 
#     def database_forwards(self, app_label, schema_editor, from_state, to_state):
#         pass
# 
#     def database_backwards(self, app_label, schema_editor, from_state, to_state):
#         pass
# 
#     def describe(self):
#         return "The Django migration linter will ignore this migration"
# 
# Path: django_migration_linter/sql_analyser/analyser.py
# def analyse_sql_statements(
#     sql_analyser_class, sql_statements, exclude_migration_tests=None
# ):
#     sql_analyser = sql_analyser_class(exclude_migration_tests)
#     sql_analyser.analyse(sql_statements)
#     return sql_analyser.errors, sql_analyser.ignored, sql_analyser.warnings
# 
# def get_sql_analyser_class(database_vendor, analyser_string=None):
#     if analyser_string:
#         return get_sql_analyser_from_string(analyser_string)
#     return get_sql_analyser_class_from_db_vendor(database_vendor)
# 
# Path: django_migration_linter/utils.py
# def clean_bytes_to_str(byte_input):
#     return byte_input.decode("utf-8").strip()
# 
# def get_migration_abspath(app_label, migration_name):
#     from django.db.migrations.loader import MigrationLoader
# 
#     module_name, _ = MigrationLoader.migrations_module(app_label)
#     migration_path = "{}.{}".format(module_name, migration_name)
#     migration_module = import_module(migration_path)
# 
#     migration_file = migration_module.__file__
#     if migration_file.endswith(".pyc"):
#         migration_file = migration_file[:-1]
#     return migration_file
# 
# def split_migration_path(migration_path):
#     from django.db.migrations.loader import MIGRATIONS_MODULE_NAME
# 
#     decomposed_path = split_path(migration_path)
#     for i, p in enumerate(decomposed_path):
#         if p == MIGRATIONS_MODULE_NAME:
#             return decomposed_path[i - 1], os.path.splitext(decomposed_path[i + 1])[0]


# Path: django_migration_linter/migration_linter.py
import hashlib
import inspect
import logging
import os
import re
from enum import Enum, unique
from subprocess import PIPE, Popen
from django.conf import settings
from django.core.management import call_command
from django.db import DEFAULT_DB_ALIAS, ProgrammingError, connections
from django.db.migrations import RunPython, RunSQL
from .cache import Cache
from .constants import (
    DEFAULT_CACHE_PATH,
    DJANGO_APPS_WITH_MIGRATIONS,
    EXPECTED_DATA_MIGRATION_ARGS,
)
from .operations import IgnoreMigration
from .sql_analyser import analyse_sql_statements, get_sql_analyser_class
from .utils import clean_bytes_to_str, get_migration_abspath, split_migration_path
        from django.db.migrations.loader import MigrationLoader
        from django.db.migrations.loader import MIGRATIONS_MODULE_NAME

            sql_errors, sql_ignored, sql_warnings = analyse_sql_statements(
                self.sql_analyser_class,
                sql_statements,
                self.exclude_migration_tests,
            )
            if sql_errors:
                error += sql_errors
            if sql_ignored:
                ignored += sql_ignored
            if sql_warnings:
                warning += sql_warnings

        # And analysse the reverse SQL
        if runsql.reversible and runsql.reverse_sql != RunSQL.noop:
            sql_statements = []
            if isinstance(runsql.reverse_sql, (list, tuple)):
                for sql in runsql.reverse_sql:
                    params = None
                    if isinstance(sql, (list, tuple)):
                        elements = len(sql)
                        if elements == 2:
                            sql, params = sql
                        else:
                            raise ValueError("Expected a 2-tuple but got %d" % elements)
                        sql_statements.append(sql % params)
                    else:
                        sql_statements.append(sql)
            else:
                sql_statements.append(runsql.reverse_sql)


            sql_errors, sql_ignored, sql_warnings = analyse_sql_statements(