tests recording migrations as applied or not tests marking migrations as appliedunapplied that should not affect records of another database the hastable method caches a positive result and not continually query for the existence of the migrations table tests the disk and database loader and running through migrations in memory unapply records on databases that don t roll back changes after each test method makes sure the loader can load the migrations for the test apps and then render them out to a new apps load and test the plan now render it out ensure we ve included unmigrated apps in there too generateplan doesn t readd migrations already in the plan 29180 the loader can load migrations with a dependency on an unmigrated app load and test the plan now render it out makes sure the loader uses migration runbefore load and test the plan makes sure the first migrations build correctly undefined migrationmodules implies default migration module migrationmodules allows disabling of migrations for a particular app if a migrationmodules override points to a missing module the error raised during the importation attempt should be propagated unless ignorenomigrationstrue loading with nothing applied should just give us the one node however fakeapply one migration and it should now use the old two empty database use squashed migration starting at 1 or 2 should use the squashed migration too however starting at 3 to 5 cannot use the squashed migration starting at 5 to 7 we are past the squashed migrations empty database use squashed migration starting at 1 or 2 should use the squashed migration too however starting at 3 or 4 nonexistent migrations would be needed starting at 5 to 7 we are passed the squashed migrations migrationloader checkconsistenthistory should ignore unapplied squashed migrations that have all of their replaces applied loader migrationloaderconnection recorder migrationrecorderconnection self addcleanuprecorder flush load with nothing applied both migrations squashed loader buildgraph plan setloader graph forwardsplanapp1 4auto plan loader appliedmigrations keys expectedplan app1 1auto app2 1squashed2 app1 2squashed3 app1 4auto self assertequalplan expectedplan load with nothing applied and migrate to a replaced migration not possible if loader replacemigrations is true default loader buildgraph msg node app1 3auto not a valid node with self assertraisesmessagenodenotfounderror msg loader graph forwardsplanapp1 3auto possible if loader replacemigrations is false loader replacemigrations false loader buildgraph plan setloader graph forwardsplanapp1 3auto plan loader appliedmigrations keys expectedplan app1 1auto app2 1auto app2 2auto app1 2auto app1 3auto self assertequalplan expectedplan loader replacemigrations true fakeapply a few from app1 unsquashes migration in app1 self recordappliedrecorder app1 1auto self recordappliedrecorder app1 2auto loader buildgraph plan setloader graph forwardsplanapp1 4auto plan loader appliedmigrations keys expectedplan app2 1squashed2 app1 3auto app1 4auto self assertequalplan expectedplan fakeapply one from app2 unsquashes migration in app2 too self recordappliedrecorder app2 1auto loader buildgraph plan setloader graph forwardsplanapp1 4auto plan loader appliedmigrations keys expectedplan app2 2auto app1 3auto app1 4auto self assertequalplan expectedplan overridesettings migrationmodulesmigrations migrations testmigrationsprivate def testignorefilesself migration directories without an init py file are ignored loader migrationloaderconnection loader loaddisk migrations name for app name in loader diskmigrations if app migrations self assertequalmigrations overridesettingsmigrationmodulesmigrations migrations testmigrations def testloadingpackagewithoutfileself testmodule importmodulemigrations testmigrations loader migrationloaderconnection file spec origin or the latter is none and former is undefined modulefile testmodule file moduleorigin testmodule spec origin modulehaslocation testmodule spec haslocation try del testmodule file testmodule spec origin none testmodule spec haslocation false loader loaddisk migrations name for app name in loader diskmigrations if app migrations self assertcountequalmigrations 0001initial 0002second finally testmodule file modulefile testmodule spec origin moduleorigin testmodule spec haslocation modulehaslocation class pycloadertestsmigrationtestbase def testvalidself with self temporarymigrationmodule modulemigrations testmigrations as migrationdir compile py files to pyc files and delete py files compileall compiledirmigrationdir forcetrue quiet1 legacytrue for name in os listdirmigrationdir if name endswith py os removeos path joinmigrationdir name loader migrationloaderconnection self assertinmigrations 0001initial loader diskmigrations def testinvalidself with self temporarymigrationmodule modulemigrations testmigrationsbadpyc as migrationdir the tpl suffix is to avoid the pyc exclusion in manifest in os rename os path joinmigrationdir 0001initial pyctpl os path joinmigrationdir 0001initial pyc msg rcouldn t import w migrations 0001initial as it appears to be a stale pyc file with self assertraisesregeximporterror msg migrationloaderconnection tests recording migrations as applied or not tests marking migrations as applied unapplied that should not affect records of another database the has_table method caches a positive result and not continually query for the existence of the migrations table tests the disk and database loader and running through migrations in memory unapply records on databases that don t roll back changes after each test method makes sure the loader can load the migrations for the test apps and then render them out to a new apps load and test the plan now render it out ensure we ve included unmigrated apps in there too _generate_plan doesn t readd migrations already in the plan 29180 the loader can load migrations with a dependency on an unmigrated app load and test the plan now render it out makes sure the loader uses migration run_before load and test the plan makes sure the __first__ migrations build correctly undefined migration_modules implies default migration module migration_modules allows disabling of migrations for a particular app if a migration_modules override points to a missing module the error raised during the importation attempt should be propagated unless ignore_no_migrations true loading with nothing applied should just give us the one node however fake apply one migration and it should now use the old two empty database use squashed migration starting at 1 or 2 should use the squashed migration too however starting at 3 to 5 cannot use the squashed migration starting at 5 to 7 we are past the squashed migrations empty database use squashed migration starting at 1 or 2 should use the squashed migration too however starting at 3 or 4 nonexistent migrations would be needed starting at 5 to 7 we are passed the squashed migrations migrationloader check_consistent_history should ignore unapplied squashed migrations that have all of their replaces applied the sample migrations are structured like this app_1 1 2 3 4 2_sq_3 app_2 1_sq_2 1 2 where 2_sq_3 is a replacing migration for 2 and 3 in app_1 as 1_sq_2 is a replacing migration for 1 and 2 in app_2 load with nothing applied both migrations squashed load with nothing applied and migrate to a replaced migration not possible if loader replace_migrations is true default possible if loader replace_migrations is false fake apply a few from app1 unsquashes migration in app1 fake apply one from app2 unsquashes migration in app2 too files prefixed with underscore tilde or dot aren t loaded migration directories without an __init__ py file are ignored to support frozen environments migrationloader loads migrations from regular packages with no __file__ attribute __file__ __spec__ origin or the latter is none and former is undefined to support frozen environments migrationloader loads pyc migrations compile py files to pyc files and delete py files migrationloader reraises importerrors caused by bad magic number pyc files with a more helpful message the tpl suffix is to avoid the pyc exclusion in manifest in
import compileall
import os
from importlib import import_module

from django.db import connection, connections
from django.db.migrations.exceptions import (
    AmbiguityError,
    InconsistentMigrationHistory,
    NodeNotFoundError,
)
from django.db.migrations.loader import MigrationLoader
from django.db.migrations.recorder import MigrationRecorder
from django.test import TestCase, modify_settings, override_settings

from .test_base import MigrationTestBase


class RecorderTests(TestCase):
    

    databases = {"default", "other"}

    def test_apply(self):
        
        recorder = MigrationRecorder(connection)
        self.assertEqual(
            {(x, y) for (x, y) in recorder.applied_migrations() if x == "myapp"},
            set(),
        )
        recorder.record_applied("myapp", "0432_ponies")
        self.assertEqual(
            {(x, y) for (x, y) in recorder.applied_migrations() if x == "myapp"},
            {("myapp", "0432_ponies")},
        )
        
        recorder_other = MigrationRecorder(connections["other"])
        self.assertEqual(
            {(x, y) for (x, y) in recorder_other.applied_migrations() if x == "myapp"},
            set(),
        )
        recorder.record_unapplied("myapp", "0432_ponies")
        self.assertEqual(
            {(x, y) for (x, y) in recorder.applied_migrations() if x == "myapp"},
            set(),
        )

    def test_has_table_cached(self):
        
        recorder = MigrationRecorder(connection)
        self.assertIs(recorder.has_table(), True)
        with self.assertNumQueries(0):
            self.assertIs(recorder.has_table(), True)


class LoaderTests(TestCase):
    

    def setUp(self):
        self.applied_records = []

    def tearDown(self):
        
        
        if not connection.features.supports_transactions:
            for recorder, app, name in self.applied_records:
                recorder.record_unapplied(app, name)

    def record_applied(self, recorder, app, name):
        recorder.record_applied(app, name)
        self.applied_records.append((recorder, app, name))

    @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations"})
    @modify_settings(INSTALLED_APPS={"append": "basic"})
    def test_load(self):
        
        
        migration_loader = MigrationLoader(connection)
        self.assertEqual(
            migration_loader.graph.forwards_plan(("migrations", "0002_second")),
            [
                ("migrations", "0001_initial"),
                ("migrations", "0002_second"),
            ],
        )
        
        project_state = migration_loader.project_state(("migrations", "0002_second"))
        self.assertEqual(len(project_state.models), 2)

        author_state = project_state.models["migrations", "author"]
        self.assertEqual(
            list(author_state.fields), ["id", "name", "slug", "age", "rating"]
        )

        book_state = project_state.models["migrations", "book"]
        self.assertEqual(list(book_state.fields), ["id", "author"])

        
        self.assertIn("basic", project_state.real_apps)

    @override_settings(
        MIGRATION_MODULES={
            "migrations": "migrations.test_migrations",
            "migrations2": "migrations2.test_migrations_2",
        }
    )
    @modify_settings(INSTALLED_APPS={"append": "migrations2"})
    def test_plan_handles_repeated_migrations(self):
        
        migration_loader = MigrationLoader(connection)
        nodes = [("migrations", "0002_second"), ("migrations2", "0001_initial")]
        self.assertEqual(
            migration_loader.graph._generate_plan(nodes, at_end=True),
            [
                ("migrations", "0001_initial"),
                ("migrations", "0002_second"),
                ("migrations2", "0001_initial"),
            ],
        )

    @override_settings(
        MIGRATION_MODULES={"migrations": "migrations.test_migrations_unmigdep"}
    )
    def test_load_unmigrated_dependency(self):
        
        
        migration_loader = MigrationLoader(connection)
        self.assertEqual(
            migration_loader.graph.forwards_plan(("migrations", "0001_initial")),
            [
                ("contenttypes", "0001_initial"),
                ("auth", "0001_initial"),
                ("migrations", "0001_initial"),
            ],
        )
        
        project_state = migration_loader.project_state(("migrations", "0001_initial"))
        self.assertEqual(
            len([m for a, m in project_state.models if a == "migrations"]), 1
        )

        book_state = project_state.models["migrations", "book"]
        self.assertEqual(list(book_state.fields), ["id", "user"])

    @override_settings(
        MIGRATION_MODULES={"migrations": "migrations.test_migrations_run_before"}
    )
    def test_run_before(self):
        
        
        migration_loader = MigrationLoader(connection)
        self.assertEqual(
            migration_loader.graph.forwards_plan(("migrations", "0002_second")),
            [
                ("migrations", "0001_initial"),
                ("migrations", "0003_third"),
                ("migrations", "0002_second"),
            ],
        )

    @override_settings(
        MIGRATION_MODULES={
            "migrations": "migrations.test_migrations_first",
            "migrations2": "migrations2.test_migrations_2_first",
        }
    )
    @modify_settings(INSTALLED_APPS={"append": "migrations2"})
    def test_first(self):
        
        migration_loader = MigrationLoader(connection)
        self.assertEqual(
            migration_loader.graph.forwards_plan(("migrations", "second")),
            [
                ("migrations", "thefirst"),
                ("migrations2", "0001_initial"),
                ("migrations2", "0002_second"),
                ("migrations", "second"),
            ],
        )

    @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations"})
    def test_name_match(self):
        "Tests prefix name matching"
        migration_loader = MigrationLoader(connection)
        self.assertEqual(
            migration_loader.get_migration_by_prefix("migrations", "0001").name,
            "0001_initial",
        )
        msg = "There is more than one migration for 'migrations' with the prefix '0'"
        with self.assertRaisesMessage(AmbiguityError, msg):
            migration_loader.get_migration_by_prefix("migrations", "0")
        msg = "There is no migration for 'migrations' with the prefix 'blarg'"
        with self.assertRaisesMessage(KeyError, msg):
            migration_loader.get_migration_by_prefix("migrations", "blarg")

    def test_load_import_error(self):
        with override_settings(
            MIGRATION_MODULES={"migrations": "import_error_package"}
        ):
            with self.assertRaises(ImportError):
                MigrationLoader(connection)

    def test_load_module_file(self):
        with override_settings(
            MIGRATION_MODULES={"migrations": "migrations.faulty_migrations.file"}
        ):
            loader = MigrationLoader(connection)
            self.assertIn(
                "migrations",
                loader.unmigrated_apps,
                "App with migrations module file not in unmigrated apps.",
            )

    def test_load_empty_dir(self):
        with override_settings(
            MIGRATION_MODULES={"migrations": "migrations.faulty_migrations.namespace"}
        ):
            loader = MigrationLoader(connection)
            self.assertIn(
                "migrations",
                loader.unmigrated_apps,
                "App missing __init__.py in migrations module not in unmigrated apps.",
            )

    @override_settings(
        INSTALLED_APPS=["migrations.migrations_test_apps.migrated_app"],
    )
    def test_marked_as_migrated(self):
        
        migration_loader = MigrationLoader(connection)
        self.assertEqual(migration_loader.migrated_apps, {"migrated_app"})
        self.assertEqual(migration_loader.unmigrated_apps, set())

    @override_settings(
        INSTALLED_APPS=["migrations.migrations_test_apps.migrated_app"],
        MIGRATION_MODULES={"migrated_app": None},
    )
    def test_marked_as_unmigrated(self):
        
        migration_loader = MigrationLoader(connection)
        self.assertEqual(migration_loader.migrated_apps, set())
        self.assertEqual(migration_loader.unmigrated_apps, {"migrated_app"})

    @override_settings(
        INSTALLED_APPS=["migrations.migrations_test_apps.migrated_app"],
        MIGRATION_MODULES={"migrated_app": "missing-module"},
    )
    def test_explicit_missing_module(self):
        
        with self.assertRaisesMessage(ImportError, "missing-module"):
            migration_loader = MigrationLoader(connection)
        migration_loader = MigrationLoader(connection, ignore_no_migrations=True)
        self.assertEqual(migration_loader.migrated_apps, set())
        self.assertEqual(migration_loader.unmigrated_apps, {"migrated_app"})

    @override_settings(
        MIGRATION_MODULES={"migrations": "migrations.test_migrations_squashed"}
    )
    def test_loading_squashed(self):
        "Tests loading a squashed migration"
        migration_loader = MigrationLoader(connection)
        recorder = MigrationRecorder(connection)
        self.addCleanup(recorder.flush)
        
        self.assertEqual(
            len([x for x in migration_loader.graph.nodes if x[0] == "migrations"]),
            1,
        )
        
        self.record_applied(recorder, "migrations", "0001_initial")
        migration_loader.build_graph()
        self.assertEqual(
            len([x for x in migration_loader.graph.nodes if x[0] == "migrations"]),
            2,
        )

    @override_settings(
        MIGRATION_MODULES={"migrations": "migrations.test_migrations_squashed_complex"}
    )
    def test_loading_squashed_complex(self):
        "Tests loading a complex set of squashed migrations"

        loader = MigrationLoader(connection)
        recorder = MigrationRecorder(connection)
        self.addCleanup(recorder.flush)

        def num_nodes():
            plan = set(loader.graph.forwards_plan(("migrations", "7_auto")))
            return len(plan - loader.applied_migrations.keys())

        
        loader.build_graph()
        self.assertEqual(num_nodes(), 5)

        
        self.record_applied(recorder, "migrations", "1_auto")
        loader.build_graph()
        self.assertEqual(num_nodes(), 4)

        self.record_applied(recorder, "migrations", "2_auto")
        loader.build_graph()
        self.assertEqual(num_nodes(), 3)

        
        self.record_applied(recorder, "migrations", "3_auto")
        loader.build_graph()
        self.assertEqual(num_nodes(), 4)

        self.record_applied(recorder, "migrations", "4_auto")
        loader.build_graph()
        self.assertEqual(num_nodes(), 3)

        
        self.record_applied(recorder, "migrations", "5_auto")
        loader.build_graph()
        self.assertEqual(num_nodes(), 2)

        self.record_applied(recorder, "migrations", "6_auto")
        loader.build_graph()
        self.assertEqual(num_nodes(), 1)

        self.record_applied(recorder, "migrations", "7_auto")
        loader.build_graph()
        self.assertEqual(num_nodes(), 0)

    @override_settings(
        MIGRATION_MODULES={
            "app1": "migrations.test_migrations_squashed_complex_multi_apps.app1",
            "app2": "migrations.test_migrations_squashed_complex_multi_apps.app2",
        }
    )
    @modify_settings(
        INSTALLED_APPS={
            "append": [
                "migrations.test_migrations_squashed_complex_multi_apps.app1",
                "migrations.test_migrations_squashed_complex_multi_apps.app2",
            ]
        }
    )
    def test_loading_squashed_complex_multi_apps(self):
        loader = MigrationLoader(connection)
        loader.build_graph()

        plan = set(loader.graph.forwards_plan(("app1", "4_auto")))
        expected_plan = {
            ("app1", "1_auto"),
            ("app2", "1_squashed_2"),
            ("app1", "2_squashed_3"),
            ("app1", "4_auto"),
        }
        self.assertEqual(plan, expected_plan)

    @override_settings(
        MIGRATION_MODULES={
            "app1": "migrations.test_migrations_squashed_complex_multi_apps.app1",
            "app2": "migrations.test_migrations_squashed_complex_multi_apps.app2",
        }
    )
    @modify_settings(
        INSTALLED_APPS={
            "append": [
                "migrations.test_migrations_squashed_complex_multi_apps.app1",
                "migrations.test_migrations_squashed_complex_multi_apps.app2",
            ]
        }
    )
    def test_loading_squashed_complex_multi_apps_partially_applied(self):
        loader = MigrationLoader(connection)
        recorder = MigrationRecorder(connection)
        self.record_applied(recorder, "app1", "1_auto")
        self.record_applied(recorder, "app1", "2_auto")
        loader.build_graph()

        plan = set(loader.graph.forwards_plan(("app1", "4_auto")))
        plan -= loader.applied_migrations.keys()
        expected_plan = {
            ("app2", "1_squashed_2"),
            ("app1", "3_auto"),
            ("app1", "4_auto"),
        }

        self.assertEqual(plan, expected_plan)

    @override_settings(
        MIGRATION_MODULES={
            "migrations": "migrations.test_migrations_squashed_erroneous"
        }
    )
    def test_loading_squashed_erroneous(self):
        "Tests loading a complex but erroneous set of squashed migrations"

        loader = MigrationLoader(connection)
        recorder = MigrationRecorder(connection)
        self.addCleanup(recorder.flush)

        def num_nodes():
            plan = set(loader.graph.forwards_plan(("migrations", "7_auto")))
            return len(plan - loader.applied_migrations.keys())

        
        loader.build_graph()
        self.assertEqual(num_nodes(), 5)

        
        self.record_applied(recorder, "migrations", "1_auto")
        loader.build_graph()
        self.assertEqual(num_nodes(), 4)

        self.record_applied(recorder, "migrations", "2_auto")
        loader.build_graph()
        self.assertEqual(num_nodes(), 3)

        
        msg = (
            "Migration migrations.6_auto depends on nonexistent node "
            "('migrations', '5_auto'). Django tried to replace migration "
            "migrations.5_auto with any of [migrations.3_squashed_5] but wasn't able "
            "to because some of the replaced migrations are already applied."
        )

        self.record_applied(recorder, "migrations", "3_auto")
        with self.assertRaisesMessage(NodeNotFoundError, msg):
            loader.build_graph()

        self.record_applied(recorder, "migrations", "4_auto")
        with self.assertRaisesMessage(NodeNotFoundError, msg):
            loader.build_graph()

        
        self.record_applied(recorder, "migrations", "5_auto")
        loader.build_graph()
        self.assertEqual(num_nodes(), 2)

        self.record_applied(recorder, "migrations", "6_auto")
        loader.build_graph()
        self.assertEqual(num_nodes(), 1)

        self.record_applied(recorder, "migrations", "7_auto")
        loader.build_graph()
        self.assertEqual(num_nodes(), 0)

    @override_settings(
        MIGRATION_MODULES={"migrations": "migrations.test_migrations"},
        INSTALLED_APPS=["migrations"],
    )
    def test_check_consistent_history(self):
        loader = MigrationLoader(connection=None)
        loader.check_consistent_history(connection)
        recorder = MigrationRecorder(connection)
        self.record_applied(recorder, "migrations", "0002_second")
        msg = (
            "Migration migrations.0002_second is applied before its dependency "
            "migrations.0001_initial on database 'default'."
        )
        with self.assertRaisesMessage(InconsistentMigrationHistory, msg):
            loader.check_consistent_history(connection)

    @override_settings(
        MIGRATION_MODULES={"migrations": "migrations.test_migrations_squashed_extra"},
        INSTALLED_APPS=["migrations"],
    )
    def test_check_consistent_history_squashed(self):
        
        loader = MigrationLoader(connection=None)
        recorder = MigrationRecorder(connection)
        self.record_applied(recorder, "migrations", "0001_initial")
        self.record_applied(recorder, "migrations", "0002_second")
        loader.check_consistent_history(connection)
        self.record_applied(recorder, "migrations", "0003_third")
        loader.check_consistent_history(connection)

    @override_settings(
        MIGRATION_MODULES={
            "app1": "migrations.test_migrations_squashed_ref_squashed.app1",
            "app2": "migrations.test_migrations_squashed_ref_squashed.app2",
        }
    )
    @modify_settings(
        INSTALLED_APPS={
            "append": [
                "migrations.test_migrations_squashed_ref_squashed.app1",
                "migrations.test_migrations_squashed_ref_squashed.app2",
            ]
        }
    )
    def test_loading_squashed_ref_squashed(self):
        "Tests loading a squashed migration with a new migration referencing it"
        r

        loader = MigrationLoader(connection)
        recorder = MigrationRecorder(connection)
        self.addCleanup(recorder.flush)

        
        loader.build_graph()
        plan = set(loader.graph.forwards_plan(("app1", "4_auto")))
        plan -= loader.applied_migrations.keys()
        expected_plan = {
            ("app1", "1_auto"),
            ("app2", "1_squashed_2"),
            ("app1", "2_squashed_3"),
            ("app1", "4_auto"),
        }
        self.assertEqual(plan, expected_plan)

        
        
        loader.build_graph()
        msg = "Node ('app1', '3_auto') not a valid node"
        with self.assertRaisesMessage(NodeNotFoundError, msg):
            loader.graph.forwards_plan(("app1", "3_auto"))
        
        loader.replace_migrations = False
        loader.build_graph()
        plan = set(loader.graph.forwards_plan(("app1", "3_auto")))
        plan -= loader.applied_migrations.keys()
        expected_plan = {
            ("app1", "1_auto"),
            ("app2", "1_auto"),
            ("app2", "2_auto"),
            ("app1", "2_auto"),
            ("app1", "3_auto"),
        }
        self.assertEqual(plan, expected_plan)
        loader.replace_migrations = True

        
        self.record_applied(recorder, "app1", "1_auto")
        self.record_applied(recorder, "app1", "2_auto")
        loader.build_graph()
        plan = set(loader.graph.forwards_plan(("app1", "4_auto")))
        plan -= loader.applied_migrations.keys()
        expected_plan = {
            ("app2", "1_squashed_2"),
            ("app1", "3_auto"),
            ("app1", "4_auto"),
        }
        self.assertEqual(plan, expected_plan)

        
        self.record_applied(recorder, "app2", "1_auto")
        loader.build_graph()
        plan = set(loader.graph.forwards_plan(("app1", "4_auto")))
        plan -= loader.applied_migrations.keys()
        expected_plan = {
            ("app2", "2_auto"),
            ("app1", "3_auto"),
            ("app1", "4_auto"),
        }
        self.assertEqual(plan, expected_plan)

    @override_settings(
        MIGRATION_MODULES={"migrations": "migrations.test_migrations_private"}
    )
    def test_ignore_files(self):
        
        loader = MigrationLoader(connection)
        loader.load_disk()
        migrations = [
            name for app, name in loader.disk_migrations if app == "migrations"
        ]
        self.assertEqual(migrations, ["0001_initial"])

    @override_settings(
        MIGRATION_MODULES={
            "migrations": "migrations.test_migrations_namespace_package"
        },
    )
    def test_loading_namespace_package(self):
        
        loader = MigrationLoader(connection)
        loader.load_disk()
        migrations = [
            name for app, name in loader.disk_migrations if app == "migrations"
        ]
        self.assertEqual(migrations, [])

    @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations"})
    def test_loading_package_without__file__(self):
        
        test_module = import_module("migrations.test_migrations")
        loader = MigrationLoader(connection)
        
        
        module_file = test_module.__file__
        module_origin = test_module.__spec__.origin
        module_has_location = test_module.__spec__.has_location
        try:
            del test_module.__file__
            test_module.__spec__.origin = None
            test_module.__spec__.has_location = False
            loader.load_disk()
            migrations = [
                name for app, name in loader.disk_migrations if app == "migrations"
            ]
            self.assertCountEqual(migrations, ["0001_initial", "0002_second"])
        finally:
            test_module.__file__ = module_file
            test_module.__spec__.origin = module_origin
            test_module.__spec__.has_location = module_has_location


class PycLoaderTests(MigrationTestBase):
    def test_valid(self):
        
        with self.temporary_migration_module(
            module="migrations.test_migrations"
        ) as migration_dir:
            
            compileall.compile_dir(migration_dir, force=True, quiet=1, legacy=True)
            for name in os.listdir(migration_dir):
                if name.endswith(".py"):
                    os.remove(os.path.join(migration_dir, name))
            loader = MigrationLoader(connection)
            self.assertIn(("migrations", "0001_initial"), loader.disk_migrations)

    def test_invalid(self):
        
        with self.temporary_migration_module(
            module="migrations.test_migrations_bad_pyc"
        ) as migration_dir:
            
            os.rename(
                os.path.join(migration_dir, "0001_initial.pyc-tpl"),
                os.path.join(migration_dir, "0001_initial.pyc"),
            )
            msg = (
                r"Couldn't import '\w+.migrations.0001_initial' as it appears "
                "to be a stale .pyc file."
            )
            with self.assertRaisesRegex(ImportError, msg):
                MigrationLoader(connection)