I now see that my original post was too vague. I will more thoroughly explain myself below, including the actual patch (because it's that short!). *My proposal will not change Django's behavior at all for anyone not using "freezers" *such as PyOxidizer, cx_Freeze, Nuitka, PyInstaller, and the like. No changes to namespace package support. No changes to dependencies. In particular I mentioned #30950 <https://code.djangoproject.com/ticket/30950> only in its capacity as a tracking ticket, but my proposal supports a *very narrow* subset of that ticket's goals.
To filter out namespace packages, django.db.migrations.loader.MigrationLoader.load_disk <https://github.com/django/django/blob/8b2a30f6f16cb1f3538847954030d69da005bc7f/django/db/migrations/loader.py#L68-L128> currently rejects any module missing a __file__ attribute (including __file__ is None). Because __file__ is an *optional* <https://docs.python.org/3/reference/import.html#__file__> attribute of modules not provided by some freezers <https://pyoxidizer.readthedocs.io/en/stable/oxidized_importer_behavior_and_compliance.html#file-and-cached-module-attributes>, *just testing for __file__ casts too wide a net*. Fortunately, namespace packages have other differences with regular packages that can be detected at runtime. One of those differences is that their __loader__ is importlib._bootstrap_external._NamespaceLoader, but that is undocumented and specific to CPython. However, a difference that *is* documented in the Python language specification <https://docs.python.org/3/reference/import.html#namespace-packages> is the type of __path__: > Namespace packages do not use an ordinary list for their __path__ attribute. They instead use a custom iterable type.... So here is the patch that I propose. For brevity, I am excluding the test, but it's only another 17 lines. *diff --git a/django/db/migrations/loader.py b/django/db/migrations/loader.pyindex 95a5062ec9..fd3a92f779 100644--- a/django/db/migrations/loader.py+++ b/django/db/migrations/loader.py*@@ -88,15 +88,19 @@ *class* MigrationLoader: *continue* *raise* *else*: - # Empty directories are namespaces. - # getattr() needed on PY36 and older (replace w/attribute access). - *if* getattr(module, '__file__', None) *is* None: - self.unmigrated_apps.add(app_config.label) - *continue* # Module is not a package (e.g. migrations.py). *if* *not* hasattr(module, '__path__'): self.unmigrated_apps.add(app_config.label) *continue* + # Empty directories are namespaces. Namespace packages have no + # __file__ and don't use a list for __path__. See + # https://docs.python.org/3/reference/import.html#namespace-packages + *if* ( + getattr(module, '__file__', None) *is* None *and* + *not* isinstance(module.__path__, list) + ): + self.unmigrated_apps.add(app_config.label) + *continue* # Force a reload if it's already loaded (tests need this) *if* was_loaded: reload(module) I hope the narrow nature of this patch is more acceptable than what you initially thought I was proposing. I am happy to answer further questions. Thanks for taking the time to engage with me about this! On Tuesday, December 22, 2020 at 11:23:19 AM UTC-6 Mariusz Felisiak wrote: > Hi, > > I can only repeat my arguments > <https://code.djangoproject.com/ticket/30950#comment:3> from the ticket. > I don't see a strong reason to add an extra dependency for this. Django 4.0 > will drop support for Python 3.6 and Python 3.7, so we can reconsider this > ticket when developing Django 4.0+. > > Best, > Mariusz > -- You received this message because you are subscribed to the Google Groups "Django developers (Contributions to Django itself)" group. To unsubscribe from this group and stop receiving emails from it, send an email to django-developers+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/django-developers/1cc41b79-3bd2-4bbf-9882-0c8d9075b78bn%40googlegroups.com.