Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions drf_spectacular/plumbing.py
Original file line number Diff line number Diff line change
Expand Up @@ -905,13 +905,20 @@ def deep_import_string(string: str) -> Any:


def load_enum_name_overrides():
return _load_enum_name_overrides(get_language())
# id() of the current ENUM_NAME_OVERRIDES dict is used (in addition to language) as part
# of the cache key, so a patched settings object (e.g. via SpectacularAPIView's
# custom_settings, serving multiple schemas with different overrides) gets its own cache
# entry instead of reusing whatever was cached for a different settings object. The dict
# itself can't be used directly as a cache key/argument since it (or its values, which may
# be raw choice lists) is not hashable.
return _load_enum_name_overrides(get_language(), id(spectacular_settings.ENUM_NAME_OVERRIDES))


@functools.lru_cache()
def _load_enum_name_overrides(language: str):
def _load_enum_name_overrides(language: str, enum_name_overrides_id: int):
enum_name_overrides = spectacular_settings.ENUM_NAME_OVERRIDES
overrides = {}
for name, choices in spectacular_settings.ENUM_NAME_OVERRIDES.items():
for name, choices in enum_name_overrides.items():
if isinstance(choices, str):
choices = deep_import_string(choices)
if not choices:
Expand Down Expand Up @@ -944,7 +951,7 @@ def _load_enum_name_overrides(language: str):
]
overrides[list_hash(hashable_values)] = name

if len(spectacular_settings.ENUM_NAME_OVERRIDES) != len(overrides):
if len(enum_name_overrides) != len(overrides):
error(
'ENUM_NAME_OVERRIDES has duplication issues. Encountered multiple names '
'for the same choice set. Enum naming might be unexpected.'
Expand Down
23 changes: 23 additions & 0 deletions tests/test_postprocessing.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,29 @@ class XView(generics.RetrieveAPIView):
assert len(schema['components']['schemas']) == 2


def test_global_enum_naming_override_not_stale_across_settings_objects(no_warnings, clear_caches):
"""
Regression test for #1244. load_enum_name_overrides() used to be cached only on
language, so loading overrides for one ENUM_NAME_OVERRIDES dict would poison the
cache for a *different* ENUM_NAME_OVERRIDES dict with the same language - exactly
what happens serving multiple schemas with per-schema custom_settings, since
SpectacularAPIView's custom_settings context manager doesn't clear this cache.
"""
with mock.patch(
'drf_spectacular.settings.spectacular_settings.ENUM_NAME_OVERRIDES',
{'LanguageEnum': 'tests.test_postprocessing.language_choices'},
):
overrides_a = load_enum_name_overrides()
assert overrides_a == {list_hash(list(language_choices)): 'LanguageEnum'}

with mock.patch(
'drf_spectacular.settings.spectacular_settings.ENUM_NAME_OVERRIDES',
{'VoteEnum': 'tests.test_postprocessing.vote_choices'},
):
overrides_b = load_enum_name_overrides()
assert overrides_b == {list_hash(list(vote_choices)): 'VoteEnum'}


@mock.patch('drf_spectacular.settings.spectacular_settings.ENUM_NAME_OVERRIDES', {
'LanguageEnum': 'tests.test_postprocessing.blank_null_language_choices'
})
Expand Down