8000 Introduced app registration system by monikasulik · Pull Request #6421 · django-cms/django-cms · GitHub
[go: up one dir, main page]

Skip to content

Introduced app registration system #6421

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 28 commits into from
Jun 26, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
4f4139e
App registry: autodiscover
monikasulik Jun 14, 2018
7db2fc9
App registration: Add cms_app attr to django app config
monikasulik Jun 14, 2018
0394871
App registration: run extension methods
monikasulik Jun 15, 2018
0ee728e
App registration: Some clean up
monikasulik Jun 15, 2018
f79f575
App registration: Edge cases
monikasulik Jun 15, 2018
f329f22
App registration: Configure CMS apps
monikasulik Jun 18, 2018
b5f1825
App registration: More clean up
monikasulik Jun 18, 2018
5b88aa1
Merge pull request #3 from monikasulik/app_registration_config
monikasulik Jun 19, 2018
c9f064d
App registration: Test comments
monikasulik Jun 19, 2018
e53eac3
App registration: Integrate into start up (WIP)
monikasulik Jun 21, 2018
f2299db
App registration: Clean up
monikasulik Jun 21, 2018
2416687
Merge pull request #4 from monikasulik/app_registration_start_up
monikasulik Jun 21, 2018
ec35c49
App registration: Remove import test (inconsistent across envs) and u…
monikasulik Jun 21, 2018
5968fc9
App registration: Clean up CMSAppConfig class
monikasulik Jun 21, 2018
c1b606e
Move app registration test apps to test_utils
monikasulik Jun 22, 2018
367e49f
App registration: CHANGELOG and AUTHORS changes
monikasulik Jun 22, 2018
31d767d
Merge pull request #5 from monikasulik/app_registration_improvements
monikasulik Jun 22, 2018
d908a66
App registration: Various minor changes after code review
monikasulik Jun 25, 2018
093408c
App registration: Split app config into 2 classes
monikasulik Jun 25, 2018
263c1c5
App registration: Polish up after separating config base classes
monikasulik Jun 25, 2018
741a803
Merge pull request #6 from monikasulik/app_registration_improvements
monikasulik Jun 25, 2018
bcc02f4
Fix python 2.7 compatibility issue
monikasulik Jun 26, 2018
e64a652
App registration: Improvements after code review
monikasulik Jun 26, 2018
6a55666
Clean up
monikasulik Jun 26, 2018
9f3f6f2
Clean up
monikasulik Jun 26, 2018
6e683d9
Fix abc import for python 2.7
monikasulik Jun 26, 2018
0850fd1
App registration: Clean up
monikasulik Jun 26, 2018
2055642
Minor cleanup
czpython Jun 26, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
8000
Diff view
Diff view
Prev Previous commit
Next Next commit
App registration: Clean up CMSAppConfig class
  • Loading branch information
monikasulik committed Jun 21, 2018
commit 5968fc99eb65d10cd73b57210fec8f9caf086d49
7 changes: 7 additions & 0 deletions cms/app_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,10 @@ def get_urls(self, page=None, language=None, **kwargs):
:return: list of urlconfs strings
"""
return self._urls


class CMSAppConfig():
"""Base class that all cms app configurations should inherit from"""

def configure_app(self, app):
raise NotImplementedError()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when not passing arguments, raise NotImplementedError, vs NotImplementedError()

31 changes: 10 additions & 21 deletions cms/app_registration.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
# -*- coding: utf-8 -*-
import inspect
from importlib import import_module

from django.apps import apps
from django.utils.module_loading import module_has_submodule
from django.core.exceptions import ImproperlyConfigured

from cms.app_base import CMSAppConfig


CMS_CONFIG_NAME = 'cms_config'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this can go into the constants.py


Expand Down Expand Up @@ -49,28 +52,19 @@ def autodiscover_cms_configs():
"class which inherits from CMSAppConfig")


def get_cms_apps_with_features():
def get_cms_apps():
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

decorate with lru_cache(max_size=None)

"""
Returns app configs of apps with cms features
Returns django app configs of apps with a cms config
"""
apps_with_features = []
cms_apps = []
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this can be a list comprehensio 8000 n


for app_config in apps.get_app_configs():
# The cms_app attr is added by the autodiscover_cms_configs
# function if a cms_config.py file with a suitable class is found.
is_cms_app = hasattr(app_config, 'cms_app')
if is_cms_app:
# The configure_app method is only present on the cms
# app class if the app provides a cms feature. For classes
# that only have config this method will not be present.
has_cms_extension = hasattr(
app_config.cms_app, 'configure_app')
else:
has_cms_extension = False
if is_cms_app and has_cms_extension:
apps_with_features.append(app_config)
if hasattr(app_config, 'cms_app'):
cms_apps.append(app_config)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove newline

return apps_with_features
return cms_apps


def configure_cms_apps(apps_with_features):
Expand All @@ -79,8 +73,7 @@ def configure_cms_apps(apps_with_features):
and run code to register them with their config
"""
for app_with_feature in apps_with_features:
enabled_property = "{app_label}_enabled".format(
app_label=app_with_feature.label)
enabled_property = "{}_enabled".format(app_with_feature.label)
configure_app = app_with_feature.cms_app.configure_app

for app_config in apps.get_app_configs():
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there should be a function that returns all apps with cms configs.
this can be wrapped with lru_cache for performance

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the spirit of YAGNI, wouldn't it be better to add that when we actually need it rather than before? It's a simple thing to add later if it is needed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We would use it here, and will be good because then we can compute it once at runtime

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You want one with all cms apps (both extensions and configs) or one with just the cms apps that define a config? Cause actually I think in this case it makes more sense if it's just the apps that have a cms_config attribute.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, just the ones with cms config

Expand All @@ -90,7 +83,3 @@ def configure_cms_apps(apps_with_features):
if getattr(app_config.cms_app, enabled_property, False):
# Feature enabled for this app so configure
configure_app(app_config)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should pass the cms_config directly here.
It will cover most cases, otherwise everyone would have to do app.cms_config all the time
in their configure_app method and I consider cms_config an internal that we shouldn't expose.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, that will mean that I'll also need to override the __init__ to take the django app config as in your original design. People might need things like app label, verbose_name etc. for things.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that I've just done this and looked at how the code is now, it has occurred to me that it would probably be neater to do def configure_app(cms_config, app_config) rather than override init and do cms_config.app_config 😉 Thoughts?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't like it because it implies that they're different while they're not, most cases will not need to use app_config.



class CMSAppConfig():
pass
34 changes: 19 additions & 15 deletions cms/tests/test_app_registration.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,34 +56,30 @@ def test_raises_exception_when_more_than_one_cms_app_class_found_in_cms_file(sel
app_registration.autodiscover_cms_configs()


class GetCmsAppsWithFeaturesTestCase(CMSTestCase):
class GetCmsAppsTestCase(CMSTestCase):

@patch.object(apps, 'get_app_configs')
def test_returns_only_apps_with_features(self, mocked_apps):
def test_returns_only_cms_apps(self, mocked_apps):
# apps with cms_app attr and a configure method
app_with_cms_ext1 = Mock(cms_app=Mock(spec=['configure_app']))
app_with_cms_ext2 = Mock(cms_app=Mock(spec=['configure_app']))
# app with cms_app attr without a configure method
# this throws an AttributeError if configure_app is accessed
app_with_cms_config = Mock(cms_app=Mock(spec=[]))
cms_app1 = Mock(cms_app=Mock(spec=['configure_app']))
cms_app2 = Mock(cms_app=Mock(spec=['configure_app']))
# app without cms_app attr
# this throws an AttributeError if cms_app is accessed
non_cms_app = Mock(spec=[])
# mock what apps have been installed
mocked_apps.return_value = [
app_with_cms_ext1,
app_with_cms_config,
app_with_cms_ext2,
cms_app1,
cms_app2,
non_cms_app,
]

apps_with_features = app_registration.get_cms_apps_with_features()
cms_apps = app_registration.get_cms_apps()

# Of the 4 installed apps only 2 have features (1 is a
# non-cms app and 1 is a cms app without a feature)
# Of the 3 installed apps only 2 have features (1 is a
# non-cms app)
self.assertListEqual(
apps_with_features,
[app_with_cms_ext1, app_with_cms_ext2])
cms_apps,
[cms_app1, cms_app2])


class ConfigureCmsAppsTestCase(CMSTestCase):
Expand Down Expand Up @@ -255,3 +251,11 @@ def test_cms_apps_setup_after_setup_function_run(self):
# whole app registration code to run through.
self.assertEqual(feature_app.cms_app.num_configured_apps, 1)
self.assertTrue(config_app.cms_app.configured)

@override_settings(INSTALLED_APPS=[
'cms.tests.test_app_registry.app_with_cms_config',
'cms.tests.test_app_registry.app_using_non_feature'
])
def test_raises_not_implemented_exception_when_feature_app_doesnt_implement_configure_method(self):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can be shortened to test_raises_not_implemented_exception and the rest explained in comment

with self.assertRaises(NotImplementedError):
setup.setup_cms_apps()
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
default_app_config = 'cms.tests.test_app_registry.app_using_non_feature.apps.NonFeatureCMSConfig'
Empty file.
6 changes: 6 additions & 0 deletions cms/tests/test_app_registry/app_using_non_feature/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.apps import AppConfig


class NonFeatureCMSConfig(AppConfig):
name = 'cms.tests.test_app_registry.app_using_non_feature'
label = 'app_using_non_feature'
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from cms.app_base import CMSAppConfig


class NonFeatureCMSConfig(CMSAppConfig):
# Attempting to use features from a cms app that doesnt define any
# features
app_with_cms_config_enabled = True
Empty file.
Empty file.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from cms.app_registration import CMSAppConfig
from cms.app_base import CMSAppConfig


class CMSConfigConfig(CMSAppConfig):
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from cms.app_registration import CMSAppConfig
from cms.app_base import CMSAppConfig


def some_function(*args, **kwargs):
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from cms.app_registration import CMSAppConfig
from cms.app_base import CMSAppConfig


class CMSFeatureConfig(CMSAppConfig):
Expand Down
6 changes: 3 additions & 3 deletions cms/utils/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from cms.utils.compat.dj import is_installed as app_is_installed
from cms.app_registration import (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

import order.
move this one before utils import

autodiscover_cms_configs, get_cms_apps_with_features,
autodiscover_cms_configs, get_cms_apps,
configure_cms_apps)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please change to:

from cms.app_registration import (
    autodiscover_cms_configs,
    configure_cms_apps,
    get_cms_apps,
)



Expand Down Expand Up @@ -50,5 +50,5 @@ def setup_cms_apps():
any of this functionality.
"""
autodiscover_cms_configs()
apps_with_cms_features = get_cms_apps_with_features()
configure_cms_apps(apps_with_cms_features)
cms_apps = get_cms_apps()
configure_cms_apps(cms_apps)
0