Skip to content

Commit

Permalink
refactor: A Better API for Derived Settings
Browse files Browse the repository at this point in the history
  • Loading branch information
kdmccormick committed Jan 31, 2025
1 parent bda381c commit 7b56894
Show file tree
Hide file tree
Showing 7 changed files with 135 additions and 149 deletions.
55 changes: 14 additions & 41 deletions cms/envs/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@
get_theme_base_dirs_from_settings
)
from openedx.core.lib.license import LicenseMixin
from openedx.core.lib.derived import derived, derived_collection_entry
from openedx.core.lib.derived import Derived
from openedx.core.release import doc_version

# pylint: enable=useless-suppression
Expand Down Expand Up @@ -740,7 +740,7 @@
# Don't look for template source files inside installed applications.
'APP_DIRS': False,
# Instead, look for template source files in these dirs.
'DIRS': _make_mako_template_dirs,
'DIRS': Derived(_make_mako_template_dirs),
# Options specific to this backend.
'OPTIONS': {
'loaders': (
Expand All @@ -759,7 +759,7 @@
'NAME': 'mako',
'BACKEND': 'common.djangoapps.edxmako.backend.Mako',
'APP_DIRS': False,
'DIRS': _make_mako_template_dirs,
'DIRS': Derived(_make_mako_template_dirs),
'OPTIONS': {
'context_processors': CONTEXT_PROCESSORS,
'debug': False,
Expand All @@ -778,8 +778,6 @@
}
},
]
derived_collection_entry('TEMPLATES', 0, 'DIRS')
derived_collection_entry('TEMPLATES', 1, 'DIRS')
DEFAULT_TEMPLATE_ENGINE = TEMPLATES[0]

#################################### AWS #######################################
Expand Down Expand Up @@ -825,8 +823,7 @@
# Warning: Must have trailing slash to activate correct logout view
# (auth_backends, not LMS user_authn)
FRONTEND_LOGOUT_URL = '/logout/'
FRONTEND_REGISTER_URL = lambda settings: settings.LMS_ROOT_URL + '/register'
derived('FRONTEND_REGISTER_URL')
FRONTEND_REGISTER_URL = Derived(lambda settings: settings.LMS_ROOT_URL + '/register')

LMS_ENROLLMENT_API_PATH = "/api/enrollment/v1/"
ENTERPRISE_API_URL = LMS_INTERNAL_ROOT_URL + '/enterprise/api/v1/'
Expand Down Expand Up @@ -1316,8 +1313,7 @@
STATICI18N_FILENAME_FUNCTION = 'statici18n.utils.legacy_filename'
STATICI18N_ROOT = PROJECT_ROOT / "static"

LOCALE_PATHS = _make_locale_paths
derived('LOCALE_PATHS')
LOCALE_PATHS = Derived(_make_locale_paths)

# Messages
MESSAGE_STORAGE = 'django.contrib.messages.storage.session.SessionStorage'
Expand Down Expand Up @@ -2087,10 +2083,9 @@
# See annotations in lms/envs/common.py for details.
RETIRED_EMAIL_DOMAIN = 'retired.invalid'
# See annotations in lms/envs/common.py for details.
RETIRED_USERNAME_FMT = lambda settings: settings.RETIRED_USERNAME_PREFIX + '{}'
RETIRED_USERNAME_FMT = Derived(lambda settings: settings.RETIRED_USERNAME_PREFIX + '{}')
# See annotations in lms/envs/common.py for details.
RETIRED_EMAIL_FMT = lambda settings: settings.RETIRED_EMAIL_PREFIX + '{}@' + settings.RETIRED_EMAIL_DOMAIN
derived('RETIRED_USERNAME_FMT', 'RETIRED_EMAIL_FMT')
RETIRED_EMAIL_FMT = Derived(lambda settings: settings.RETIRED_EMAIL_PREFIX + '{}@' + settings.RETIRED_EMAIL_DOMAIN)
# See annotations in lms/envs/common.py for details.
RETIRED_USER_SALTS = ['abc', '123']
# See annotations in lms/envs/common.py for details.
Expand Down Expand Up @@ -2367,13 +2362,12 @@
############## Settings for Studio Context Sensitive Help ##############

HELP_TOKENS_INI_FILE = REPO_ROOT / "cms" / "envs" / "help_tokens.ini"
HELP_TOKENS_LANGUAGE_CODE = lambda settings: settings.LANGUAGE_CODE
HELP_TOKENS_VERSION = lambda settings: doc_version()
HELP_TOKENS_LANGUAGE_CODE = Derived(lambda settings: settings.LANGUAGE_CODE)
HELP_TOKENS_VERSION = Derived(lambda settings: doc_version())
HELP_TOKENS_BOOKS = {
'learner': 'https://edx.readthedocs.io/projects/open-edx-learner-guide',
'course_author': 'https://edx.readthedocs.io/projects/open-edx-building-and-running-a-course',
}
derived('HELP_TOKENS_LANGUAGE_CODE', 'HELP_TOKENS_VERSION')

# Used with Email sending
RETRY_ACTIVATION_EMAIL_MAX_ATTEMPTS = 5
Expand Down Expand Up @@ -2876,15 +2870,15 @@ def _should_send_learning_badge_events(settings):
},
'org.openedx.content_authoring.xblock.published.v1': {
'course-authoring-xblock-lifecycle':
{'event_key_field': 'xblock_info.usage_key', 'enabled': _should_send_xblock_events},
{'event_key_field': 'xblock_info.usage_key', 'enabled': Derived(_should_send_xblock_events)},
},
'org.openedx.content_authoring.xblock.deleted.v1': {
'course-authoring-xblock-lifecycle':
{'event_key_field': 'xblock_info.usage_key', 'enabled': _should_send_xblock_events},
{'event_key_field': 'xblock_info.usage_key', 'enabled': Derived(_should_send_xblock_events)},
},
'org.openedx.content_authoring.xblock.duplicated.v1': {
'course-authoring-xblock-lifecycle':
{'event_key_field': 'xblock_info.usage_key', 'enabled': _should_send_xblock_events},
{'event_key_field': 'xblock_info.usage_key', 'enabled': Derived(_should_send_xblock_events)},
},
# LMS events. These have to be copied over here because lms.common adds some derived entries as well,
# and the derivation fails if the keys are missing. If we ever remove the import of lms.common, we can remove these.
Expand All @@ -2899,38 +2893,17 @@ def _should_send_learning_badge_events(settings):
"org.openedx.learning.course.passing.status.updated.v1": {
"learning-badges-lifecycle": {
"event_key_field": "course_passing_status.course.course_key",
"enabled": _should_send_learning_badge_events,
"enabled": Derived(_should_send_learning_badge_events),
},
},
"org.openedx.learning.ccx.course.passing.status.updated.v1": {
"learning-badges-lifecycle": {
"event_key_field": "course_passing_status.course.ccx_course_key",
"enabled": _should_send_learning_badge_events,
"enabled": Derived(_should_send_learning_badge_events),
},
},
}


derived_collection_entry('EVENT_BUS_PRODUCER_CONFIG', 'org.openedx.content_authoring.xblock.published.v1',
'course-authoring-xblock-lifecycle', 'enabled')
derived_collection_entry('EVENT_BUS_PRODUCER_CONFIG', 'org.openedx.content_authoring.xblock.duplicated.v1',
'course-authoring-xblock-lifecycle', 'enabled')
derived_collection_entry('EVENT_BUS_PRODUCER_CONFIG', 'org.openedx.content_authoring.xblock.deleted.v1',
'course-authoring-xblock-lifecycle', 'enabled')

derived_collection_entry(
"EVENT_BUS_PRODUCER_CONFIG",
"org.openedx.learning.course.passing.status.updated.v1",
"learning-badges-lifecycle",
"enabled",
)
derived_collection_entry(
"EVENT_BUS_PRODUCER_CONFIG",
"org.openedx.learning.ccx.course.passing.status.updated.v1",
"learning-badges-lifecycle",
"enabled",
)

################### Authoring API ######################

# This affects the Authoring API swagger docs but not the legacy swagger docs under /api-docs/.
Expand Down
47 changes: 13 additions & 34 deletions lms/envs/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
get_themes_unchecked,
get_theme_base_dirs_from_settings
)
from openedx.core.lib.derived import derived, derived_collection_entry
from openedx.core.lib.derived import Derived
from openedx.core.release import doc_version
from lms.djangoapps.lms_xblock.mixin import LmsBlockMixin

Expand Down Expand Up @@ -1395,7 +1395,7 @@ def _make_mako_template_dirs(settings):
# Don't look for template source files inside installed applications.
'APP_DIRS': False,
# Instead, look for template source files in these dirs.
'DIRS': _make_mako_template_dirs,
'DIRS': Derived(_make_mako_template_dirs),
# Options specific to this backend.
'OPTIONS': {
'context_processors': CONTEXT_PROCESSORS,
Expand All @@ -1404,7 +1404,6 @@ def _make_mako_template_dirs(settings):
}
},
]
derived_collection_entry('TEMPLATES', 1, 'DIRS')
DEFAULT_TEMPLATE_ENGINE = TEMPLATES[0]
DEFAULT_TEMPLATE_ENGINE_DIRS = DEFAULT_TEMPLATE_ENGINE['DIRS'][:]

Expand Down Expand Up @@ -1734,7 +1733,7 @@ def _make_mako_template_dirs(settings):
'DOC_STORE_CONFIG': DOC_STORE_CONFIG,
'OPTIONS': {
'default_class': 'xmodule.hidden_block.HiddenBlock',
'fs_root': lambda settings: settings.DATA_DIR,
'fs_root': Derived(lambda settings: settings.DATA_DIR),
'render_template': 'common.djangoapps.edxmako.shortcuts.render_to_string',
}
},
Expand All @@ -1744,7 +1743,7 @@ def _make_mako_template_dirs(settings):
'DOC_STORE_CONFIG': DOC_STORE_CONFIG,
'OPTIONS': {
'default_class': 'xmodule.hidden_block.HiddenBlock',
'fs_root': lambda settings: settings.DATA_DIR,
'fs_root': Derived(lambda settings: settings.DATA_DIR),
'render_template': 'common.djangoapps.edxmako.shortcuts.render_to_string',
}
}
Expand Down Expand Up @@ -2054,8 +2053,7 @@ def _make_locale_paths(settings): # pylint: disable=missing-function-docstring
for locale_path in settings.COMPREHENSIVE_THEME_LOCALE_PATHS:
locale_paths += (path(locale_path), )
return locale_paths
LOCALE_PATHS = _make_locale_paths
derived('LOCALE_PATHS')
LOCALE_PATHS = Derived(_make_locale_paths)

# Messages
MESSAGE_STORAGE = 'django.contrib.messages.storage.session.SessionStorage'
Expand Down Expand Up @@ -4661,13 +4659,12 @@ def _make_locale_paths(settings): # pylint: disable=missing-function-docstring
############## Settings for LMS Context Sensitive Help ##############

HELP_TOKENS_INI_FILE = REPO_ROOT / "lms" / "envs" / "help_tokens.ini"
HELP_TOKENS_LANGUAGE_CODE = lambda settings: settings.LANGUAGE_CODE
HELP_TOKENS_VERSION = lambda settings: doc_version()
HELP_TOKENS_LANGUAGE_CODE = Derived(lambda settings: settings.LANGUAGE_CODE)
HELP_TOKENS_VERSION = Derived(lambda settings: doc_version())
HELP_TOKENS_BOOKS = {
'learner': 'https://edx.readthedocs.io/projects/open-edx-learner-guide',
'course_author': 'https://edx.readthedocs.io/projects/open-edx-building-and-running-a-course',
}
derived('HELP_TOKENS_LANGUAGE_CODE', 'HELP_TOKENS_VERSION')

############## OPEN EDX ENTERPRISE SERVICE CONFIGURATION ######################
# The Open edX Enterprise service is currently hosted via the LMS container/process.
Expand Down Expand Up @@ -4955,14 +4952,13 @@ def _make_locale_paths(settings): # pylint: disable=missing-function-docstring
# .. setting_description: Set the format a retired user username field gets transformed into, where {}
# is replaced with the hash of the original username. This is a derived setting that depends on
# RETIRED_USERNAME_PREFIX value.
RETIRED_USERNAME_FMT = lambda settings: settings.RETIRED_USERNAME_PREFIX + '{}'
RETIRED_USERNAME_FMT = Derived(lambda settings: settings.RETIRED_USERNAME_PREFIX + '{}')
# .. setting_name: RETIRED_EMAIL_FMT
# .. setting_default: retired__user_{}@retired.invalid
# .. setting_description: Set the format a retired user email field gets transformed into, where {} is
# replaced with the hash of the original email. This is a derived setting that depends on
# RETIRED_EMAIL_PREFIX and RETIRED_EMAIL_DOMAIN values.
RETIRED_EMAIL_FMT = lambda settings: settings.RETIRED_EMAIL_PREFIX + '{}@' + settings.RETIRED_EMAIL_DOMAIN
derived('RETIRED_USERNAME_FMT', 'RETIRED_EMAIL_FMT')
RETIRED_EMAIL_FMT = Derived(lambda settings: settings.RETIRED_EMAIL_PREFIX + '{}@' + settings.RETIRED_EMAIL_DOMAIN)
# .. setting_name: RETIRED_USER_SALTS
# .. setting_default: ['abc', '123']
# .. setting_description: Set a list of salts used for hashing usernames and emails on users retirement.
Expand Down Expand Up @@ -5450,11 +5446,11 @@ def _should_send_learning_badge_events(settings):
EVENT_BUS_PRODUCER_CONFIG = {
'org.openedx.learning.certificate.created.v1': {
'learning-certificate-lifecycle':
{'event_key_field': 'certificate.course.course_key', 'enabled': _should_send_certificate_events},
{'event_key_field': 'certificate.course.course_key', 'enabled': Derived(_should_send_certificate_events)},
},
'org.openedx.learning.certificate.revoked.v1': {
'learning-certificate-lifecycle':
{'event_key_field': 'certificate.course.course_key', 'enabled': _should_send_certificate_events},
{'event_key_field': 'certificate.course.course_key', 'enabled': Derived(_should_send_certificate_events)},
},
'org.openedx.learning.course.unenrollment.completed.v1': {
'course-unenrollment-lifecycle':
Expand Down Expand Up @@ -5516,33 +5512,16 @@ def _should_send_learning_badge_events(settings):
"org.openedx.learning.course.passing.status.updated.v1": {
"learning-badges-lifecycle": {
"event_key_field": "course_passing_status.course.course_key",
"enabled": _should_send_learning_badge_events,
"enabled": Derived(_should_send_learning_badge_events),
},
},
"org.openedx.learning.ccx.course.passing.status.updated.v1": {
"learning-badges-lifecycle": {
"event_key_field": "course_passing_status.course.ccx_course_key",
"enabled": _should_send_learning_badge_events,
"enabled": Derived(_should_send_learning_badge_events),
},
},
}
derived_collection_entry('EVENT_BUS_PRODUCER_CONFIG', 'org.openedx.learning.certificate.created.v1',
'learning-certificate-lifecycle', 'enabled')
derived_collection_entry('EVENT_BUS_PRODUCER_CONFIG', 'org.openedx.learning.certificate.revoked.v1',
'learning-certificate-lifecycle', 'enabled')

derived_collection_entry(
"EVENT_BUS_PRODUCER_CONFIG",
"org.openedx.learning.course.passing.status.updated.v1",
"learning-badges-lifecycle",
"enabled",
)
derived_collection_entry(
"EVENT_BUS_PRODUCER_CONFIG",
"org.openedx.learning.ccx.course.passing.status.updated.v1",
"learning-badges-lifecycle",
"enabled",
)

BEAMER_PRODUCT_ID = ""

Expand Down
6 changes: 2 additions & 4 deletions lms/envs/docs/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,7 @@ For example:
for locale_path in settings.COMPREHENSIVE_THEME_LOCALE_PATHS:
locale_paths += (path(locale_path), )
return locale_paths
LOCALE_PATHS = _make_locale_paths
derived('LOCALE_PATHS')
LOCALE_PATHS = Derived(_make_locale_paths)
In this case, ``LOCALE_PATHS`` will be defined correctly at the end of the
settings module parsing no matter what ``REPO_ROOT``,
Expand Down Expand Up @@ -92,7 +91,6 @@ when nested within each other:
'NAME': 'mako',
'BACKEND': 'common.djangoapps.edxmako.backend.Mako',
'APP_DIRS': False,
'DIRS': _make_mako_template_dirs,
'DIRS': Derived(_make_mako_template_dirs),
},
]
derived_collection_entry('TEMPLATES', 1, 'DIRS')
11 changes: 0 additions & 11 deletions lms/envs/production.py
Original file line number Diff line number Diff line change
Expand Up @@ -349,17 +349,6 @@ def get_env_setting(setting):
# use the one from common.py
MODULESTORE = convert_module_store_setting_if_needed(_YAML_TOKENS.get('MODULESTORE', MODULESTORE))

# After conversion above, the modulestore will have a "stores" list with all defined stores, for all stores, add the
# fs_root entry to derived collection so that if it's a callable it can be resolved. We need to do this because the
# `derived_collection_entry` takes an exact index value but the config file might have overridden the number of stores
# and so we can't be sure that the 2 we define in common.py will be there when we try to derive settings. This could
# lead to exceptions being thrown when the `derive_settings` call later in this file tries to update settings. We call
# the derived_collection_entry function here to ensure that we update the fs_root for any callables that remain after
# we've updated the MODULESTORE setting from our config file.
for idx, store in enumerate(MODULESTORE['default']['OPTIONS']['stores']):
if 'OPTIONS' in store and 'fs_root' in store["OPTIONS"]:
derived_collection_entry('MODULESTORE', 'default', 'OPTIONS', 'stores', idx, 'OPTIONS', 'fs_root')

BROKER_URL = "{}://{}:{}@{}/{}".format(CELERY_BROKER_TRANSPORT,
CELERY_BROKER_USER,
CELERY_BROKER_PASSWORD,
Expand Down
1 change: 1 addition & 0 deletions mypy.ini
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ files =
openedx/core/djangoapps/content_staging,
openedx/core/djangoapps/content_libraries,
openedx/core/djangoapps/xblock,
openedx/core/lib/derived.py,
openedx/core/types,
openedx/core/djangoapps/content_tagging,
xmodule/util/keys.py,
Expand Down
Loading

0 comments on commit 7b56894

Please sign in to comment.