# Django 5.2 documentation

# Source code for django.apps.config

    import inspect
    import os
    from importlib import import_module

    from django.core.exceptions import ImproperlyConfigured
    from django.utils.functional import cached_property
    from django.utils.module_loading import import_string, module_has_submodule

    APPS_MODULE_NAME = "apps"
    MODELS_MODULE_NAME = "models"

    class AppConfig:
        """Class representing a Django application and its configuration."""

        def __init__(self, app_name, app_module):
            # Full Python path to the application e.g. 'django.contrib.admin'.
            self.name = app_name

            # Root module for the application e.g. <module 'django.contrib.admin'
            # from 'django/contrib/admin/__init__.py'>.
            self.module = app_module

            # Reference to the Apps registry that holds this AppConfig. Set by the
            # registry when it registers the AppConfig instance.
            self.apps = None

            # The following attributes could be defined at the class level in a
            # subclass, hence the test-and-set pattern.

            # Last component of the Python path to the application e.g. 'admin'.
            # This value must be unique across a Django project.
            if not hasattr(self, "label"):
                self.label = app_name.rpartition(".")[2]
            if not self.label.isidentifier():
                raise ImproperlyConfigured(
                    "The app label '%s' is not a valid Python identifier." % self.label
                )

            # Human-readable name for the application e.g. "Admin".
            if not hasattr(self, "verbose_name"):
                self.verbose_name = self.label.title()

            # Filesystem path to the application directory e.g.
            # '/path/to/django/contrib/admin'.
            if not hasattr(self, "path"):
                self.path = self._path_from_module(app_module)

            # Module containing models e.g. <module 'django.contrib.admin.models'
            # from 'django/contrib/admin/models.py'>. Set by import_models().
            # None if the application doesn't have a models module.
            self.models_module = None

            # Mapping of lowercase model names to model classes. Initially set to
            # None to prevent accidental access before import_models() runs.
            self.models = None

        def __repr__(self):
            return "<%s: %s>" % (self.__class__.__name__, self.label)

        @cached_property
        def default_auto_field(self):
            from django.conf import settings

            return settings.DEFAULT_AUTO_FIELD

        @property
        def _is_default_auto_field_overridden(self):
            return self.__class__.default_auto_field is not AppConfig.default_auto_field

        def _path_from_module(self, module):
            """Attempt to determine app's filesystem path from its module."""
            # See #21874 for extended discussion of the behavior of this method in
            # various cases.
            # Convert to list because __path__ may not support indexing.
            paths = list(getattr(module, "__path__", []))
            if len(paths) != 1:
                filename = getattr(module, "__file__", None)
                if filename is not None:
                    paths = [os.path.dirname(filename)]
                else:
                    # For unknown reasons, sometimes the list returned by __path__
                    # contains duplicates that must be removed (#25246).
                    paths = list(set(paths))
            if len(paths) > 1:
                raise ImproperlyConfigured(
                    "The app module %r has multiple filesystem locations (%r); "
                    "you must configure this app with an AppConfig subclass "
                    "with a 'path' class attribute." % (module, paths)
                )
            elif not paths:
                raise ImproperlyConfigured(
                    "The app module %r has no filesystem location, "
                    "you must configure this app with an AppConfig subclass "
                    "with a 'path' class attribute." % module
                )
            return paths[0]

        @classmethod
        def create(cls, entry):
            """
            Factory that creates an app config from an entry in INSTALLED_APPS.
            """
            # create() eventually returns app_config_class(app_name, app_module).
            app_config_class = None
            app_name = None
            app_module = None

            # If import_module succeeds, entry points to the app module.
            try:
                app_module = import_module(entry)
            except Exception:
                pass
            else:
                # If app_module has an apps submodule that defines a single
                # AppConfig subclass, use it automatically.
                # To prevent this, an AppConfig subclass can declare a class
                # variable default = False.
                # If the apps module defines more than one AppConfig subclass,
                # the default one can declare default = True.
                if module_has_submodule(app_module, APPS_MODULE_NAME):
                    mod_path = "%s.%s" % (entry, APPS_MODULE_NAME)
                    mod = import_module(mod_path)
                    # Check if there's exactly one AppConfig candidate,
                    # excluding those that explicitly define default = False.
                    app_configs = [
                        (name, candidate)
                        for name, candidate in inspect.getmembers(mod, inspect.isclass)
                        if (
                            issubclass(candidate, cls)
                            and candidate is not cls
                            and getattr(candidate, "default", True)
                        )
                    ]
                    if len(app_configs) == 1:
                        app_config_class = app_configs[0][1]
                    else:
                        # Check if there's exactly one AppConfig subclass,
                        # among those that explicitly define default = True.
                        app_configs = [
                            (name, candidate)
                            for name, candidate in app_configs
                            if getattr(candidate, "default", False)
                        ]
                        if len(app_configs) > 1:
                            candidates = [repr(name) for name, _ in app_configs]
                            raise RuntimeError(
                                "%r declares more than one default AppConfig: "
                                "%s." % (mod_path, ", ".join(candidates))
                            )
                        elif len(app_configs) == 1:
                            app_config_class = app_configs[0][1]

                # Use the default app config class if we didn't find anything.
                if app_config_class is None:
                    app_config_class = cls
                    app_name = entry

            # If import_string succeeds, entry is an app config class.
            if app_config_class is None:
                try:
                    app_config_class = import_string(entry)
                except Exception:
                    pass
            # If both import_module and import_string failed, it means that entry
            # doesn't have a valid value.
            if app_module is None and app_config_class is None:
                # If the last component of entry starts with an uppercase letter,
                # then it was likely intended to be an app config class; if not,
                # an app module. Provide a nice error message in both cases.
                mod_path, _, cls_name = entry.rpartition(".")
                if mod_path and cls_name[0].isupper():
                    # We could simply re-trigger the string import exception, but
                    # we're going the extra mile and providing a better error
                    # message for typos in INSTALLED_APPS.
                    # This may raise ImportError, which is the best exception
                    # possible if the module at mod_path cannot be imported.
                    mod = import_module(mod_path)
                    candidates = [
                        repr(name)
                        for name, candidate in inspect.getmembers(mod, inspect.isclass)
                        if issubclass(candidate, cls) and candidate is not cls
                    ]
                    msg = "Module '%s' does not contain a '%s' class." % (
                        mod_path,
                        cls_name,
                    )
                    if candidates:
                        msg += " Choices are: %s." % ", ".join(candidates)
                    raise ImportError(msg)
                else:
                    # Re-trigger the module import exception.
                    import_module(entry)

            # Check for obvious errors. (This check prevents duck typing, but
            # it could be removed if it became a problem in practice.)
            if not issubclass(app_config_class, AppConfig):
                raise ImproperlyConfigured("'%s' isn't a subclass of AppConfig." % entry)

            # Obtain app name here rather than in AppClass.__init__ to keep
            # all error checking for entries in INSTALLED_APPS in one place.
            if app_name is None:
                try:
                    app_name = app_config_class.name
                except AttributeError:
                    raise ImproperlyConfigured("'%s' must supply a name attribute." % entry)

            # Ensure app_name points to a valid module.
            try:
                app_module = import_module(app_name)
            except ImportError:
                raise ImproperlyConfigured(
                    "Cannot import '%s'. Check that '%s.%s.name' is correct."
                    % (
                        app_name,
                        app_config_class.__module__,
                        app_config_class.__qualname__,
                    )
                )

            # Entry is a path to an app config class.
            return app_config_class(app_name, app_module)

        def get_model(self, model_name, require_ready=True):
            """
            Return the model with the given case-insensitive model_name.

            Raise LookupError if no model exists with this name.
            """
            if require_ready:
                self.apps.check_models_ready()
            else:
                self.apps.check_apps_ready()
            try:
                return self.models[model_name.lower()]
            except KeyError:
                raise LookupError(
                    "App '%s' doesn't have a '%s' model." % (self.label, model_name)
                )

        def get_models(self, include_auto_created=False, include_swapped=False):
            """
            Return an iterable of models.

            By default, the following models aren't included:

            - auto-created models for many-to-many relations without
              an explicit intermediate table,
            - models that have been swapped out.

            Set the corresponding keyword argument to True to include such models.
            Keyword arguments aren't documented; they're a private API.
            """
            self.apps.check_models_ready()
            for model in self.models.values():
                if model._meta.auto_created and not include_auto_created:
                    continue
                if model._meta.swapped and not include_swapped:
                    continue
                yield model

        def import_models(self):
            # Dictionary of models for this app, primarily maintained in the
            # 'all_models' attribute of the Apps this AppConfig is attached to.
            self.models = self.apps.all_models[self.label]

            if module_has_submodule(self.module, MODELS_MODULE_NAME):
                models_module_name = "%s.%s" % (self.name, MODELS_MODULE_NAME)
                self.models_module = import_module(models_module_name)

        def ready(self):
            """
            Override this method in subclasses to run code when Django starts.
            """