refactor: fix wrongly initiated settings (#1250)

* refactor: fix wrongly initiated settings

* refacttor: remove `_load_settings()` from apps.py

* refactor: fix building of docs + update titles in API reference

* refactor: fix docs build error

* refactor: use EXTENSIONS_DEFAULTS

* refactor: update titles
This commit is contained in:
Juro Oravec 2025-06-10 10:12:48 +02:00 committed by GitHub
parent 2350a6b6c4
commit 458e1894db
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 65 additions and 41 deletions

View file

@ -34,6 +34,10 @@
Now, each subclass has it's own `Template` instance, and changes to the template of the subclass do not affect the template of the parent class. Now, each subclass has it's own `Template` instance, and changes to the template of the subclass do not affect the template of the parent class.
- Fix Django failing to restart due to "TypeError: 'Dynamic' object is not iterable" ([#1232](https://github.com/django-components/django-components/issues/1232))
- Fix bug when error formatting failed when error value was not a string.
## v0.140.1 ## v0.140.1
#### Fix #### Fix

View file

@ -1,16 +1,16 @@
# `.nav.yml` is provided by https://lukasgeiter.github.io/mkdocs-awesome-nav # `.nav.yml` is provided by https://lukasgeiter.github.io/mkdocs-awesome-nav
nav: nav:
- API: api.md - API: api.md
- Commands: commands.md - CLI commands: commands.md
- Components: components.md - Components: components.md
- Exceptions: exceptions.md - Exceptions: exceptions.md
- Extension commands: extension_commands.md
- Extension hooks: extension_hooks.md - Extension hooks: extension_hooks.md
- Extension URLs: extension_urls.md - Extension commands API: extension_commands.md
- Extension URLs API: extension_urls.md
- Settings: settings.md - Settings: settings.md
- Signals: signals.md - Signals: signals.md
- Tag formatters: tag_formatters.md - Tag formatters: tag_formatters.md
- Template tags: template_tags.md - Template tags: template_tags.md
- Template vars: template_vars.md - Template variables: template_vars.md
- URLs: urls.md - URLs: urls.md
- Testing API: testing_api.md - Testing API: testing_api.md

View file

@ -1,6 +1,6 @@
<!-- Autogenerated by reference.py --> <!-- Autogenerated by reference.py -->
# Extension commands # Extension commands API
Overview of all classes, functions, and other objects related to defining extension commands. Overview of all classes, functions, and other objects related to defining extension commands.

View file

@ -1,6 +1,6 @@
<!-- Autogenerated by reference.py --> <!-- Autogenerated by reference.py -->
# Extension URLs # Extension URLs API
Overview of all classes, functions, and other objects related to defining extension URLs. Overview of all classes, functions, and other objects related to defining extension URLs.

View file

@ -67,7 +67,7 @@ If you insert this tag multiple times, ALL JS scripts will be duplicately insert
<a href="https://github.com/django-components/django-components/tree/master/src/django_components/templatetags/component_tags.py#L3796" target="_blank">See source code</a> <a href="https://github.com/django-components/django-components/tree/master/src/django_components/templatetags/component_tags.py#L3799" target="_blank">See source code</a>

View file

@ -44,6 +44,7 @@ from pathlib import Path
from textwrap import dedent from textwrap import dedent
from typing import Any, Dict, List, NamedTuple, Optional, Sequence, Tuple, Type, Union from typing import Any, Dict, List, NamedTuple, Optional, Sequence, Tuple, Type, Union
from django.conf import settings
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from django.urls import URLPattern, URLResolver from django.urls import URLPattern, URLResolver
@ -465,7 +466,13 @@ def gen_reference_commands():
# Add link to source code # Add link to source code
module_abs_path = import_module(cmd_def_cls.__module__).__file__ module_abs_path = import_module(cmd_def_cls.__module__).__file__
module_rel_path = Path(module_abs_path).relative_to(Path.cwd()).as_posix() # type: ignore[arg-type] module_rel_path = Path(module_abs_path).relative_to(Path.cwd()).as_posix() # type: ignore[arg-type]
obj_lineno = inspect.findsource(cmd_def_cls)[1]
# NOTE: Raises `OSError` if the file is not found.
try:
obj_lineno = inspect.findsource(cmd_def_cls)[1]
except Exception:
obj_lineno = None
source_code_link = _format_source_code_html(module_rel_path, obj_lineno) source_code_link = _format_source_code_html(module_rel_path, obj_lineno)
# NOTE: For the commands we have to generate the markdown entries ourselves, # NOTE: For the commands we have to generate the markdown entries ourselves,
@ -524,7 +531,7 @@ def gen_reference_commands():
) )
def gen_reference_templatetags(): def gen_reference_template_tags():
""" """
Generate documentation for all Django template tags defined by django-components, Generate documentation for all Django template tags defined by django-components,
like `{% slot %}`, `{% component %}`, etc. like `{% slot %}`, `{% component %}`, etc.
@ -537,7 +544,7 @@ def gen_reference_templatetags():
] ]
preface = "<!-- Autogenerated by reference.py -->\n\n" preface = "<!-- Autogenerated by reference.py -->\n\n"
preface += (root / "docs/templates/reference_templatetags.md").read_text() preface += (root / "docs/templates/reference_template_tags.md").read_text()
out_file = root / "docs/reference/template_tags.md" out_file = root / "docs/reference/template_tags.md"
out_file.parent.mkdir(parents=True, exist_ok=True) out_file.parent.mkdir(parents=True, exist_ok=True)
@ -585,14 +592,14 @@ def gen_reference_templatetags():
) )
def gen_reference_templatevars(): def gen_reference_template_variables():
""" """
Generate documentation for all variables that are available inside the component templates Generate documentation for all variables that are available inside the component templates
under the `{{ component_vars }}` variable, as defined by `ComponentVars`. under the `{{ component_vars }}` variable, as defined by `ComponentVars`.
""" """
preface = "<!-- Autogenerated by reference.py -->\n\n" preface = "<!-- Autogenerated by reference.py -->\n\n"
preface += (root / "docs/templates/reference_templatevars.md").read_text() preface += (root / "docs/templates/reference_template_variables.md").read_text()
out_file = root / "docs/reference/template_vars.md" out_file = root / "docs/reference/template_variables.md"
out_file.parent.mkdir(parents=True, exist_ok=True) out_file.parent.mkdir(parents=True, exist_ok=True)
with out_file.open("w", encoding="utf-8") as f: with out_file.open("w", encoding="utf-8") as f:
@ -1099,6 +1106,13 @@ def _is_extension_url_api(obj: Any) -> bool:
def gen_reference(): def gen_reference():
"""The entrypoint to generate all the reference documentation.""" """The entrypoint to generate all the reference documentation."""
# Set up Django settings so we can import `extensions`
if not settings.configured:
settings.configure(
BASE_DIR=Path(__file__).parent.parent.parent,
)
gen_reference_api() gen_reference_api()
gen_reference_exceptions() gen_reference_exceptions()
gen_reference_components() gen_reference_components()
@ -1106,8 +1120,8 @@ def gen_reference():
gen_reference_tagformatters() gen_reference_tagformatters()
gen_reference_urls() gen_reference_urls()
gen_reference_commands() gen_reference_commands()
gen_reference_templatetags() gen_reference_template_tags()
gen_reference_templatevars() gen_reference_template_variables()
gen_reference_signals() gen_reference_signals()
gen_reference_testing_api() gen_reference_testing_api()
gen_reference_extension_hooks() gen_reference_extension_hooks()

View file

@ -1,4 +1,4 @@
# Extension commands # Extension commands API
Overview of all classes, functions, and other objects related to defining extension commands. Overview of all classes, functions, and other objects related to defining extension commands.

View file

@ -1,4 +1,4 @@
# Extension URLs # Extension URLs API
Overview of all classes, functions, and other objects related to defining extension URLs. Overview of all classes, functions, and other objects related to defining extension URLs.

View file

@ -746,8 +746,8 @@ defaults = ComponentsSettings(
# #
# Settings are loaded from Django settings only once, at `apps.py` in `ready()`. # Settings are loaded from Django settings only once, at `apps.py` in `ready()`.
class InternalSettings: class InternalSettings:
def __init__(self, settings: Optional[Dict[str, Any]] = None): def __init__(self) -> None:
self._settings = ComponentsSettings(**settings) if settings else defaults self._settings: Optional[ComponentsSettings] = None
def _load_settings(self) -> None: def _load_settings(self) -> None:
data = getattr(settings, "COMPONENTS", {}) data = getattr(settings, "COMPONENTS", {})
@ -786,6 +786,11 @@ class InternalSettings:
tag_formatter=default(components_settings.tag_formatter, defaults.tag_formatter), # type: ignore[arg-type] tag_formatter=default(components_settings.tag_formatter, defaults.tag_formatter), # type: ignore[arg-type]
) )
def _get_settings(self) -> ComponentsSettings:
if self._settings is None:
self._load_settings()
return cast(ComponentsSettings, self._settings)
def _prepare_extensions(self, new_settings: ComponentsSettings) -> List["ComponentExtension"]: def _prepare_extensions(self, new_settings: ComponentsSettings) -> List["ComponentExtension"]:
extensions: Sequence[Union[Type["ComponentExtension"], str]] = default( extensions: Sequence[Union[Type["ComponentExtension"], str]] = default(
new_settings.extensions, cast(List[str], defaults.extensions) new_settings.extensions, cast(List[str], defaults.extensions)
@ -853,70 +858,73 @@ class InternalSettings:
return raw_value return raw_value
# TODO REMOVE THE PROPERTIES BELOW? THEY NO LONGER SERVE ANY PURPOSE
@property @property
def AUTODISCOVER(self) -> bool: def AUTODISCOVER(self) -> bool:
return self._settings.autodiscover # type: ignore[return-value] return self._get_settings().autodiscover # type: ignore[return-value]
@property @property
def CACHE(self) -> Optional[str]: def CACHE(self) -> Optional[str]:
return self._settings.cache return self._get_settings().cache
@property @property
def DIRS(self) -> Sequence[Union[str, PathLike, Tuple[str, str], Tuple[str, PathLike]]]: def DIRS(self) -> Sequence[Union[str, PathLike, Tuple[str, str], Tuple[str, PathLike]]]:
return self._settings.dirs # type: ignore[return-value] return self._get_settings().dirs # type: ignore[return-value]
@property @property
def APP_DIRS(self) -> Sequence[str]: def APP_DIRS(self) -> Sequence[str]:
return self._settings.app_dirs # type: ignore[return-value] return self._get_settings().app_dirs # type: ignore[return-value]
@property @property
def DEBUG_HIGHLIGHT_COMPONENTS(self) -> bool: def DEBUG_HIGHLIGHT_COMPONENTS(self) -> bool:
return self._settings.debug_highlight_components # type: ignore[return-value] return self._get_settings().debug_highlight_components # type: ignore[return-value]
@property @property
def DEBUG_HIGHLIGHT_SLOTS(self) -> bool: def DEBUG_HIGHLIGHT_SLOTS(self) -> bool:
return self._settings.debug_highlight_slots # type: ignore[return-value] return self._get_settings().debug_highlight_slots # type: ignore[return-value]
@property @property
def DYNAMIC_COMPONENT_NAME(self) -> str: def DYNAMIC_COMPONENT_NAME(self) -> str:
return self._settings.dynamic_component_name # type: ignore[return-value] return self._get_settings().dynamic_component_name # type: ignore[return-value]
@property @property
def LIBRARIES(self) -> List[str]: def LIBRARIES(self) -> List[str]:
return self._settings.libraries # type: ignore[return-value] return self._get_settings().libraries # type: ignore[return-value]
@property @property
def EXTENSIONS(self) -> List["ComponentExtension"]: def EXTENSIONS(self) -> List["ComponentExtension"]:
return self._settings.extensions # type: ignore[return-value] return self._get_settings().extensions # type: ignore[return-value]
@property
def EXTENSIONS_DEFAULTS(self) -> Dict[str, Any]:
return self._get_settings().extensions_defaults # type: ignore[return-value]
@property @property
def MULTILINE_TAGS(self) -> bool: def MULTILINE_TAGS(self) -> bool:
return self._settings.multiline_tags # type: ignore[return-value] return self._get_settings().multiline_tags # type: ignore[return-value]
@property @property
def RELOAD_ON_FILE_CHANGE(self) -> bool: def RELOAD_ON_FILE_CHANGE(self) -> bool:
return self._settings.reload_on_file_change # type: ignore[return-value] return self._get_settings().reload_on_file_change # type: ignore[return-value]
@property @property
def TEMPLATE_CACHE_SIZE(self) -> int: def TEMPLATE_CACHE_SIZE(self) -> int:
return self._settings.template_cache_size # type: ignore[return-value] return self._get_settings().template_cache_size # type: ignore[return-value]
@property @property
def STATIC_FILES_ALLOWED(self) -> Sequence[Union[str, re.Pattern]]: def STATIC_FILES_ALLOWED(self) -> Sequence[Union[str, re.Pattern]]:
return self._settings.static_files_allowed # type: ignore[return-value] return self._get_settings().static_files_allowed # type: ignore[return-value]
@property @property
def STATIC_FILES_FORBIDDEN(self) -> Sequence[Union[str, re.Pattern]]: def STATIC_FILES_FORBIDDEN(self) -> Sequence[Union[str, re.Pattern]]:
return self._settings.static_files_forbidden # type: ignore[return-value] return self._get_settings().static_files_forbidden # type: ignore[return-value]
@property @property
def CONTEXT_BEHAVIOR(self) -> ContextBehavior: def CONTEXT_BEHAVIOR(self) -> ContextBehavior:
return ContextBehavior(self._settings.context_behavior) return ContextBehavior(self._get_settings().context_behavior)
@property @property
def TAG_FORMATTER(self) -> Union["TagFormatterABC", str]: def TAG_FORMATTER(self) -> Union["TagFormatterABC", str]:
return self._settings.tag_formatter # type: ignore[return-value] return self._get_settings().tag_formatter # type: ignore[return-value]
app_settings = InternalSettings() app_settings = InternalSettings()

View file

@ -20,8 +20,6 @@ class ComponentsConfig(AppConfig):
from django_components.extension import extensions from django_components.extension import extensions
from django_components.util.django_monkeypatch import monkeypatch_template_cls from django_components.util.django_monkeypatch import monkeypatch_template_cls
app_settings._load_settings()
# NOTE: This monkeypatch is applied here, before Django processes any requests. # NOTE: This monkeypatch is applied here, before Django processes any requests.
# To make django-components work with django-debug-toolbar-template-profiler # To make django-components work with django-debug-toolbar-template-profiler
# See https://github.com/django-components/django-components/discussions/819 # See https://github.com/django-components/django-components/discussions/819

View file

@ -20,7 +20,7 @@ def _gen_subcommands() -> List[Type[ComponentCommand]]:
commands: List[Type[ComponentCommand]] = [] commands: List[Type[ComponentCommand]] = []
for extension in extensions.extensions: for extension in extensions.extensions:
ExtCommand = type( ExtCommand = type(
"ExtCommand", "ExtRunSubcommand_" + extension.name,
(ComponentCommand,), (ComponentCommand,),
{ {
"name": extension.name, "name": extension.name,

View file

@ -1047,7 +1047,7 @@ class ExtensionManager:
# - `MyExtensionBase` is the base class that the extension class inherits from. # - `MyExtensionBase` is the base class that the extension class inherits from.
bases_list = [ext_base_class] bases_list = [ext_base_class]
all_extensions_defaults = app_settings._settings.extensions_defaults or {} all_extensions_defaults = app_settings.EXTENSIONS_DEFAULTS or {}
extension_defaults = all_extensions_defaults.get(extension.name, None) extension_defaults = all_extensions_defaults.get(extension.name, None)
if extension_defaults: if extension_defaults:
# Create dummy class that holds the extension defaults # Create dummy class that holds the extension defaults

View file

@ -25,7 +25,7 @@ def component_error_message(component_path: List[str]) -> Generator[None, None,
if not components: if not components:
orig_msg = str(err.args[0]) orig_msg = str(err.args[0])
else: else:
orig_msg = err.args[0].split("\n", 1)[-1] orig_msg = str(err.args[0]).split("\n", 1)[-1]
else: else:
orig_msg = str(err) orig_msg = str(err)