Re: App-loading reloaded - custom app names in the admin
After the merge, some Selenium tests (which I don’t run locally in general) failed on Jenkins. That’s a test isolation issue: an AppDirectoriesFinder instance kept a cache of an incomplete list of applications, preventing later tests from finding static files correctly. I wanted to review the methods that add and remove apps to the app cache anyway — that’s the “part of the patch I don’t like much” I was talking about in my first email. I’ve sent a new pull request for this purpose: https://github.com/django/django/pull/2103. It introduces a new API called override_list_settings to alter settings that are lists of values. It allows simply using override_settings(INSTALLED_APPS=…) or override_list_settings(INSTALLED_APPS=…). django.test now contains some references to django.apps but that seems reasonable to be as the app cache is a core concept of Django. Overall the result seems much better, both in terms of API and implementation. I’ll merge that once I get a +1 from another core dev. -- Aymeric. On 22 déc. 2013, at 17:10, Aymeric Augustinwrote: > Merged: https://github.com/django/django/compare/7f2485b4d180...17c66e6fe77b > > -- > Aymeric. > > > > On 22 déc. 2013, at 12:14, Aymeric Augustin > wrote: > >> I’ve updated the pull request with these changes as well as a few minor >> comments made by reviewers. >> >> https://github.com/django/django/pull/2089 >> >> I think it’s worth merging in that state. I still have a list of about 20 >> things to check, but none of them is likely to change anything fundamental. >> >> -- >> Aymeric. >> >> >> >> On 21 déc. 2013, at 17:01, Aymeric Augustin >> wrote: >> >>> Hello, >>> >>> Based on the feedback I received through several channels (GitHub, IRC, >>> private email) I’m planning to make two API changes before merging this >>> pull request. >>> >>> >>> (1) Remove auto-discovery of AppConfig in application modules >>> >>> I implemented this shim to make it possible to take advantage of app >>> configs without changing the format of INSTALLED_APPS. I wanted to increase >>> backwards-compatibility and accelerate adoption in pluggable apps. I also >>> wanted to keep INSTALLED_APPS short and readable in the common case and >>> avoid this pattern: >>> >>> INSTALLED_APPS = ( >>> ‘django.contrib.admin.app.AdminConfig', >>> 'django.contrib.auth.app.AuthConfig', >>> 'django.contrib.contenttypes.app.ContentTypesConfig', >>> 'django.contrib.sessions.app.SessionsConfig', >>> 'django.contrib.messages.app.MessagesConfig', >>> 'django.contrib.staticfiles.app.StaticFilesConfig’, >>> # et caetera ad nauseam >>> # cold enterprisey thorny feelings >>> ) >>> >>> Astute readers will note that MIDDLEWARE_CLASSES looks even worse but I’m >>> not buying the “make it consistently ugly" argument :-) >>> >>> (Also I haven’t prepared any changes to Django’s contrib apps yet.) >>> >>> However, I can also list a few reasons not to provide this shim: >>> >>> - “There should be one-- and preferably only one --obvious way to do it.” >>> - “Explicit is better than implicit.” >>> - Django shouldn't encourage writing code in __init__.py files because it’s >>> a common cause of import loops eg. when a submodule attempts to import an >>> object defined in a parent package. Strictly speaking, importing the base >>> AppConfig class and implementing a subclass is safe: it doesn’t even load >>> the Django settings. But it’s still a code smell. >>> - Python ≥ 3.3 introduces support for namespace packages (PEP 420) by >>> making __init__.py files optional. I’m pretty sure someone will find a good >>> reason to implement a Django app as a namespace package; then they couldn’t >>> take advantage of this convention any more. Besides, skipping __init__.py >>> files entirely might become a good practice in the next years. >>> >>> One could also say that the explicit version makes it obvious which >>> applications use an application configuration and which don’t, but I don’t >>> find the difference between “using the application’s default AppConfig” and >>> “using Django’s default AppConfig” relevant. >>> >>> I’m ambivalent about this question. Considering that it can easily be added >>> later, I’m leaning towards not including it for now. The reverse would be >>> much more complicated. >>> >>> >>> (2) Move the code back into django.apps >>> >>> I originally wrote all the code in django.apps. >>> >>> Then I realized it could cause confusion because “apps” shipped with Django >>> are in django.contrib, not in django.apps, and I moved the code to >>> django.contrib.apps. One could argue that apps are a core concept. >>> >>> However, there are two strong arguments for moving the code back to its >>> original location. >>> >>> - Most APIs intended to be imported by user code live outside of core: >>> “django.forms”,
Re: App-loading reloaded - custom app names in the admin
I’ve updated the pull request with these changes as well as a few minor comments made by reviewers. https://github.com/django/django/pull/2089 I think it’s worth merging in that state. I still have a list of about 20 things to check, but none of them is likely to change anything fundamental. -- Aymeric. On 21 déc. 2013, at 17:01, Aymeric Augustinwrote: > Hello, > > Based on the feedback I received through several channels (GitHub, IRC, > private email) I’m planning to make two API changes before merging this pull > request. > > > (1) Remove auto-discovery of AppConfig in application modules > > I implemented this shim to make it possible to take advantage of app configs > without changing the format of INSTALLED_APPS. I wanted to increase > backwards-compatibility and accelerate adoption in pluggable apps. I also > wanted to keep INSTALLED_APPS short and readable in the common case and avoid > this pattern: > > INSTALLED_APPS = ( >‘django.contrib.admin.app.AdminConfig', >'django.contrib.auth.app.AuthConfig', >'django.contrib.contenttypes.app.ContentTypesConfig', >'django.contrib.sessions.app.SessionsConfig', >'django.contrib.messages.app.MessagesConfig', >'django.contrib.staticfiles.app.StaticFilesConfig’, ># et caetera ad nauseam ># cold enterprisey thorny feelings > ) > > Astute readers will note that MIDDLEWARE_CLASSES looks even worse but I’m not > buying the “make it consistently ugly" argument :-) > > (Also I haven’t prepared any changes to Django’s contrib apps yet.) > > However, I can also list a few reasons not to provide this shim: > > - “There should be one-- and preferably only one --obvious way to do it.” > - “Explicit is better than implicit.” > - Django shouldn't encourage writing code in __init__.py files because it’s a > common cause of import loops eg. when a submodule attempts to import an > object defined in a parent package. Strictly speaking, importing the base > AppConfig class and implementing a subclass is safe: it doesn’t even load the > Django settings. But it’s still a code smell. > - Python ≥ 3.3 introduces support for namespace packages (PEP 420) by making > __init__.py files optional. I’m pretty sure someone will find a good reason > to implement a Django app as a namespace package; then they couldn’t take > advantage of this convention any more. Besides, skipping __init__.py files > entirely might become a good practice in the next years. > > One could also say that the explicit version makes it obvious which > applications use an application configuration and which don’t, but I don’t > find the difference between “using the application’s default AppConfig” and > “using Django’s default AppConfig” relevant. > > I’m ambivalent about this question. Considering that it can easily be added > later, I’m leaning towards not including it for now. The reverse would be > much more complicated. > > > (2) Move the code back into django.apps > > I originally wrote all the code in django.apps. > > Then I realized it could cause confusion because “apps” shipped with Django > are in django.contrib, not in django.apps, and I moved the code to > django.contrib.apps. One could argue that apps are a core concept. > > However, there are two strong arguments for moving the code back to its > original location. > > - Most APIs intended to be imported by user code live outside of core: > “django.forms”, “django.db.models”, “django.templates”, “django.views”, etc. > - Most of the code in django.core would better live in a shallower hierarchy. > For instance “django.core.urlresolvers” doesn’t strike me a superior to > “django.urls”. Besides “flat is better than nested”. > > > Let me if you have additional arguments in favor of or against this changes. > I’ll try to implement them tomorrow or on Monday. > > -- > Aymeric. > > > > On 20 déc. 2013, at 16:37, Aymeric Augustin > wrote: > >> Merge request >> >> I sent a pull request implementing my second goal: >> https://github.com/django/django/pull/2089. It allows customizing >> application names in the admin. >> >> A handful of core developers were kind enough to oversee my efforts. Their >> feedback on the design has been positive. However, as far as I know, that >> part of the branch hasn’t received a full review yet. It would be helpful if >> someone had the time to read through the commits. >> >> Custom app names are implemented in a single commit that changes four lines >> and doesn’t have tests and docs yet. It doesn’t even deal with breadcrumbs. >> I’ve included it as a proof-of-concept but I can leave it aside for now if >> you find that too cavalier. >> >> What I reallly want to merge is the seventeen previous previous commits. >> Their sole purpose is to make the app cache the only piece of code that >> knows about the INSTALLED_APPS settings. That’s a
Re: App-loading reloaded - custom app names in the admin
Hello, Based on the feedback I received through several channels (GitHub, IRC, private email) I’m planning to make two API changes before merging this pull request. (1) Remove auto-discovery of AppConfig in application modules I implemented this shim to make it possible to take advantage of app configs without changing the format of INSTALLED_APPS. I wanted to increase backwards-compatibility and accelerate adoption in pluggable apps. I also wanted to keep INSTALLED_APPS short and readable in the common case and avoid this pattern: INSTALLED_APPS = ( ‘django.contrib.admin.app.AdminConfig', 'django.contrib.auth.app.AuthConfig', 'django.contrib.contenttypes.app.ContentTypesConfig', 'django.contrib.sessions.app.SessionsConfig', 'django.contrib.messages.app.MessagesConfig', 'django.contrib.staticfiles.app.StaticFilesConfig’, # et caetera ad nauseam # cold enterprisey thorny feelings ) Astute readers will note that MIDDLEWARE_CLASSES looks even worse but I’m not buying the “make it consistently ugly" argument :-) (Also I haven’t prepared any changes to Django’s contrib apps yet.) However, I can also list a few reasons not to provide this shim: - “There should be one-- and preferably only one --obvious way to do it.” - “Explicit is better than implicit.” - Django shouldn't encourage writing code in __init__.py files because it’s a common cause of import loops eg. when a submodule attempts to import an object defined in a parent package. Strictly speaking, importing the base AppConfig class and implementing a subclass is safe: it doesn’t even load the Django settings. But it’s still a code smell. - Python ≥ 3.3 introduces support for namespace packages (PEP 420) by making __init__.py files optional. I’m pretty sure someone will find a good reason to implement a Django app as a namespace package; then they couldn’t take advantage of this convention any more. Besides, skipping __init__.py files entirely might become a good practice in the next years. One could also say that the explicit version makes it obvious which applications use an application configuration and which don’t, but I don’t find the difference between “using the application’s default AppConfig” and “using Django’s default AppConfig” relevant. I’m ambivalent about this question. Considering that it can easily be added later, I’m leaning towards not including it for now. The reverse would be much more complicated. (2) Move the code back into django.apps I originally wrote all the code in django.apps. Then I realized it could cause confusion because “apps” shipped with Django are in django.contrib, not in django.apps, and I moved the code to django.contrib.apps. One could argue that apps are a core concept. However, there are two strong arguments for moving the code back to its original location. - Most APIs intended to be imported by user code live outside of core: “django.forms”, “django.db.models”, “django.templates”, “django.views”, etc. - Most of the code in django.core would better live in a shallower hierarchy. For instance “django.core.urlresolvers” doesn’t strike me a superior to “django.urls”. Besides “flat is better than nested”. Let me if you have additional arguments in favor of or against this changes. I’ll try to implement them tomorrow or on Monday. -- Aymeric. On 20 déc. 2013, at 16:37, Aymeric Augustinwrote: > Merge request > > I sent a pull request implementing my second goal: > https://github.com/django/django/pull/2089. It allows customizing application > names in the admin. > > A handful of core developers were kind enough to oversee my efforts. Their > feedback on the design has been positive. However, as far as I know, that > part of the branch hasn’t received a full review yet. It would be helpful if > someone had the time to read through the commits. > > Custom app names are implemented in a single commit that changes four lines > and doesn’t have tests and docs yet. It doesn’t even deal with breadcrumbs. > I’ve included it as a proof-of-concept but I can leave it aside for now if > you find that too cavalier. > > What I reallly want to merge is the seventeen previous previous commits. > Their sole purpose is to make the app cache the only piece of code that knows > about the INSTALLED_APPS settings. That’s a prerequisite for changing its > format and support custom application configurations. > > It’s a fairly large refactoring that, as far as I know, should be backwards > compatible. Of course, third-party apps that use INSTALLED_APPS will need to > be updated to support the new format. But as long as you aren’t using custom > application configurations, everything should still work as usual. Since I’ve > rewritten the app cache population code, the import sequence could be > different, and that could create import loops in some projects. Unfortunately > here’s no way to tell
App-loading reloaded - custom app names in the admin
Merge request I sent a pull request implementing my second goal: https://github.com/django/django/pull/2089. It allows customizing application names in the admin. A handful of core developers were kind enough to oversee my efforts. Their feedback on the design has been positive. However, as far as I know, that part of the branch hasn’t received a full review yet. It would be helpful if someone had the time to read through the commits. Custom app names are implemented in a single commit that changes four lines and doesn’t have tests and docs yet. It doesn’t even deal with breadcrumbs. I’ve included it as a proof-of-concept but I can leave it aside for now if you find that too cavalier. What I reallly want to merge is the seventeen previous previous commits. Their sole purpose is to make the app cache the only piece of code that knows about the INSTALLED_APPS settings. That’s a prerequisite for changing its format and support custom application configurations. It’s a fairly large refactoring that, as far as I know, should be backwards compatible. Of course, third-party apps that use INSTALLED_APPS will need to be updated to support the new format. But as long as you aren’t using custom application configurations, everything should still work as usual. Since I’ve rewritten the app cache population code, the import sequence could be different, and that could create import loops in some projects. Unfortunately here’s no way to tell for sure besides asking people to try it. It could just as well suppress import loops in other projects, except there aren’t many active projects with import loops :-) Again, if you see a good reason not to merge it, please tell me as soon as possible! The API for customizing an application’s configuration comes in two flavors. Currently the only attribute you can customize is “verbose_name”. 1) You’re the author of a pluggable app called Rock ’n’ roll and you’re fed up with seeing Rock_N_Roll in the admin: # rock_n_roll/__init__.py from django.core.apps import base class AppConfig(base.AppConfig): verbose_name = "Rock ’n’ roll" The class has to be called “AppConfig” for Django to discover it. 2) You’re an user of Rock ’n’ roll and you wish it had a more palatable name: # myproject/apps.py from django.core.apps import base class GypsyJazzConfig(base.AppConfig): name = "rock_n_roll" verbose_name = "Gypsy jazz" # myproject/settings.py INSTALLED_APPS = [ # … 'myproject.apps.GypsyJazzConfig', # … ] The class can live anywhere, but it must provide a “name” so that Django knows what application it relates to. Notes for reviewers There’s one part of the patch I don’t like much: the AppCache methods listed under ### DANGEROUS METHODS ###. I’ve made it very obvious that they’re private APIs and they’re only used in tests. At this time I don’t have any better ideas. It seems reasonable to decide that we’ll improve it later them if the need arises. Day 5 I thought about the API. The end result is described above. I’m skipping the details as I’m afraid they would add more confusion than clarity. Day 6 I began laying the ground for AppConfig classes in INSTALLED_APPS by searching a way to remove all direct references to INSTALLED_APPS to use app_cache.get_app_configs() instead. Note that app_cache.get_app_configs() calls app_cache.populate(). Therefore iterating on app_cache.get_app_configs() will trigger some imports, while iterating on settings.INSTALLED_APPS didn’t. This may result in different behavior, including import loops, but I’m afraid there’s no way around this. Hopefully the end result will be more deterministic than what we currently have, as Django will populate the app cache earlier. I don’t know how I’m going to deal with override_settings(INSTALLED_APPS=[…]). There’s a few dozen of these in the test suite. Until now I’ve refrained from implementing AppCache.reload() because it means https://code.djangoproject.com/attachment/ticket/3591/app-loading.2.diff#L165 and that scares me. I might end up reusing AppCache.set_available_apps(), although currently it only supports restricting the set of installed apps. Day 7 My plan to remove direct references to INSTALLED_APPS with get_app_configs() failed miserably. get_app_configs() calls populate() which imports every application. This triggers import loops. I also spent some time implementing support for override_settings(INSTALLED_APPS=…) at the level of the app cache but that got messy very quickly. In fact, we need to be able to iterate on applications to discover things (like translations and templates) without importing models. Currently the app cache’s API doesn’t provide that feature. My next idea is to split populate() in two stages: - First stage does everything that doesn’t require importing models modules. Application modules aren’t supposed to import much, if anything. - Second stage imports models modules and adds them to