chore: Push dev to master to release v0.110 (#767)

* feat: skeleton of dependency manager backend (#688)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* refactor: selectolax update and tests cleanup (#702)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* refactor: move release notes to own file (#704)

* chore: merge changes from master (#705)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Yassin Rakha <yaso2go@gmail.com>
Co-authored-by: Emil Stenström <emil@emilstenstrom.se>
fix for nested slots (#698) (#699)

* refactor: remove joint {% component_dependencies %} tag (#706)

Co-authored-by: Emil Stenström <emil@emilstenstrom.se>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* refactor: split up utils file and move utils to util dir (#707)

* docs: Move docs inside src/ to allow imports in python scripts (#708)

* refactor: Docs prep 1 (#715)

* refactor: Document template tags (#716)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* refactor: pass slot fills in template via slots param (#719)

* chore: Merge master to dev (#729)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Yassin Rakha <yaso2go@gmail.com>
Co-authored-by: Emil Stenström <emil@emilstenstrom.se>
Co-authored-by: Tom Larsen <larsent@gmail.com>
fix for nested slots (#698) (#699)

* fix: Do not raise error if multiple slots with same name are flagged as default (#727)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* refactor: tag formatter - allow fwd slash in end tag (#730)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* refactor: Use lowercase names for registry settings (#731)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* docs: add docstrings (#732)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* feat: define settings as a data class for type hints, intellisense, and docs (#733)

* refactor: fix reload-on-change logic, expose autodiscover's dirs-getting logic, rename settings (#734)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* docs: document settings (#743)

* docs: document settings

* refactor: fix linter errors

* feat: passthrough slots and more (#758)

* feat: passthrough slots and more

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* refactor: remove ComponentSlotContext.slots

* refactor: update comment

* docs: update changelog

* refactor: update docstrings

* refactor: document and test-cover more changes

* refactor: revert fill without name

* docs: update README

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* fix: apostrophes in tags (#765)

* refactor: fix merge error - duplicate code

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Emil Stenström <emil@emilstenstrom.se>
This commit is contained in:
Juro Oravec 2024-11-25 09:41:57 +01:00 committed by GitHub
parent 9f891453d5
commit 5fd45ab424
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
97 changed files with 8727 additions and 3011 deletions

View file

@ -1,24 +1,60 @@
import re
from dataclasses import dataclass
from enum import Enum
from os import PathLike
from pathlib import Path
from typing import TYPE_CHECKING, Dict, List, Tuple, Union
from typing import (
TYPE_CHECKING,
Callable,
Generic,
List,
Literal,
NamedTuple,
Optional,
Sequence,
Tuple,
TypeVar,
Union,
cast,
)
from django.conf import settings
from django_components.util.misc import default
if TYPE_CHECKING:
from django_components.tag_formatter import TagFormatterABC
T = TypeVar("T")
ContextBehaviorType = Literal["django", "isolated"]
class ContextBehavior(str, Enum):
"""
Configure how (and whether) the context is passed to the component fills
and what variables are available inside the [`{% fill %}`](../template_tags#fill) tags.
Also see [Component context and scope](../../concepts/fundamentals/component_context_scope#context-behavior).
**Options:**
- `django`: With this setting, component fills behave as usual Django tags.
- `isolated`: This setting makes the component fills behave similar to Vue or React.
"""
DJANGO = "django"
"""
With this setting, component fills behave as usual Django tags.
That is, they enrich the context, and pass it along.
1. Component fills use the context of the component they are within.
2. Variables from `get_context_data` are available to the component fill.
2. Variables from [`Component.get_context_data()`](../api#django_components.Component.get_context_data)
are available to the component fill.
Example:
**Example:**
Given this template
```django
@ -30,13 +66,13 @@ class ContextBehavior(str, Enum):
{% endwith %}
```
and this context returned from the `get_context_data()` method
```py
and this context returned from the `Component.get_context_data()` method
```python
{ "my_var": 123 }
```
Then if component "my_comp" defines context
```py
```python
{ "my_var": 456 }
```
@ -56,9 +92,10 @@ class ContextBehavior(str, Enum):
ISOLATED = "isolated"
"""
This setting makes the component fills behave similar to Vue or React, where
the fills use EXCLUSIVELY the context variables defined in `get_context_data`.
the fills use EXCLUSIVELY the context variables defined in
[`Component.get_context_data()`](../api#django_components.Component.get_context_data).
Example:
**Example:**
Given this template
```django
@ -71,12 +108,12 @@ class ContextBehavior(str, Enum):
```
and this context returned from the `get_context_data()` method
```py
```python
{ "my_var": 123 }
```
Then if component "my_comp" defines context
```py
```python
{ "my_var": 456 }
```
@ -91,95 +128,565 @@ class ContextBehavior(str, Enum):
"""
class AppSettings:
# This is the source of truth for the settings that are available. If the documentation
# or the defaults do NOT match this, they should be updated.
class ComponentsSettings(NamedTuple):
"""
Settings available for django_components.
**Example:**
```python
COMPONENTS = ComponentsSettings(
autodiscover=False,
dirs = [BASE_DIR / "components"],
)
```
"""
autodiscover: Optional[bool] = None
"""
Toggle whether to run [autodiscovery](../../concepts/fundamentals/autodiscovery) at the Django server startup.
Defaults to `True`
```python
COMPONENTS = ComponentsSettings(
autodiscover=False,
)
```
"""
dirs: Optional[Sequence[Union[str, PathLike, Tuple[str, str], Tuple[str, PathLike]]]] = None
"""
Specify the directories that contain your components.
Defaults to `[Path(settings.BASE_DIR) / "components"]`. That is, the root `components/` app.
Directories must be full paths, same as with
[STATICFILES_DIRS](https://docs.djangoproject.com/en/5.1/ref/settings/#std-setting-STATICFILES_DIRS).
These locations are searched during [autodiscovery](../../concepts/fundamentals/autodiscovery),
or when you [define HTML, JS, or CSS as separate files](../../concepts/fundamentals/defining_js_css_html_files).
```python
COMPONENTS = ComponentsSettings(
dirs=[BASE_DIR / "components"],
)
```
Set to empty list to disable global components directories:
```python
COMPONENTS = ComponentsSettings(
dirs=[],
)
```
"""
app_dirs: Optional[Sequence[str]] = None
"""
Specify the app-level directories that contain your components.
Defaults to `["components"]`. That is, for each Django app, we search `<app>/components/` for components.
The paths must be relative to app, e.g.:
```python
COMPONENTS = ComponentsSettings(
app_dirs=["my_comps"],
)
```
To search for `<app>/my_comps/`.
These locations are searched during [autodiscovery](../../concepts/fundamentals/autodiscovery),
or when you [define HTML, JS, or CSS as separate files](../../concepts/fundamentals/defining_js_css_html_files).
Set to empty list to disable app-level components:
```python
COMPONENTS = ComponentsSettings(
app_dirs=[],
)
```
"""
context_behavior: Optional[ContextBehaviorType] = None
"""
Configure whether, inside a component template, you can use variables from the outside
([`"django"`](../api#django_components.ContextBehavior.DJANGO))
or not ([`"isolated"`](../api#django_components.ContextBehavior.ISOLATED)).
This also affects what variables are available inside the [`{% fill %}`](../template_tags#fill)
tags.
Also see [Component context and scope](../../concepts/fundamentals/component_context_scope#context-behavior).
Defaults to `"django"`.
```python
COMPONENTS = ComponentsSettings(
context_behavior="isolated",
)
```
> NOTE: `context_behavior` and `slot_context_behavior` options were merged in v0.70.
>
> If you are migrating from BEFORE v0.67, set `context_behavior` to `"django"`.
> From v0.67 to v0.78 (incl) the default value was `"isolated"`.
>
> For v0.79 and later, the default is again `"django"`. See the rationale for change
> [here](https://github.com/EmilStenstrom/django-components/issues/498).
"""
dynamic_component_name: Optional[str] = None
"""
By default, the [dynamic component](../components#django_components.components.dynamic.DynamicComponent)
is registered under the name `"dynamic"`.
In case of a conflict, you can use this setting to change the component name used for
the dynamic components.
```python
# settings.py
COMPONENTS = ComponentsSettings(
dynamic_component_name="my_dynamic",
)
```
After which you will be able to use the dynamic component with the new name:
```django
{% component "my_dynamic" is=table_comp data=table_data headers=table_headers %}
{% fill "pagination" %}
{% component "pagination" / %}
{% endfill %}
{% endcomponent %}
```
"""
libraries: Optional[List[str]] = None
"""
Configure extra python modules that should be loaded.
This may be useful if you are not using the [autodiscovery feature](../../concepts/fundamentals/autodiscovery),
or you need to load components from non-standard locations. Thus you can have
a structure of components that is independent from your apps.
Expects a list of python module paths. Defaults to empty list.
**Example:**
```python
COMPONENTS = ComponentsSettings(
libraries=[
"mysite.components.forms",
"mysite.components.buttons",
"mysite.components.cards",
],
)
```
This would be the equivalent of importing these modules from within Django's
[`AppConfig.ready()`](https://docs.djangoproject.com/en/5.1/ref/applications/#django.apps.AppConfig.ready):
```python
class MyAppConfig(AppConfig):
def ready(self):
import "mysite.components.forms"
import "mysite.components.buttons"
import "mysite.components.cards"
```
# Manually loading libraries
In the rare case that you need to manually trigger the import of libraries, you can use
the [`import_libraries()`](../api/#django_components.import_libraries) function:
```python
from django_components import import_libraries
import_libraries()
```
"""
multiline_tags: Optional[bool] = None
"""
Enable / disable
[multiline support for template tags](../../concepts/fundamentals/template_tag_syntax#multiline-tags).
If `True`, template tags like `{% component %}` or `{{ my_var }}` can span multiple lines.
Defaults to `True`.
Disable this setting if you are making custom modifications to Django's
regular expression for parsing templates at `django.template.base.tag_re`.
```python
COMPONENTS = ComponentsSettings(
multiline_tags=False,
)
```
"""
# TODO_REMOVE_IN_V1
reload_on_template_change: Optional[bool] = None
"""Deprecated. Use
[`COMPONENTS.reload_on_file_change`](../settings/#django_components.app_settings.ComponentsSettings.reload_on_file_change)
instead.""" # noqa: E501
reload_on_file_change: Optional[bool] = None
"""
This is relevant if you are using the project structure where
HTML, JS, CSS and Python are in separate files and nested in a directory.
In this case you may notice that when you are running a development server,
the server sometimes does not reload when you change component files.
Django's native [live reload](https://stackoverflow.com/a/66023029/9788634) logic
handles only Python files and HTML template files. It does NOT reload when other
file types change or when template files are nested more than one level deep.
The setting `reload_on_file_change` fixes this, reloading the dev server even when your component's
HTML, JS, or CSS changes.
If `True`, django_components configures Django to reload when files inside
[`COMPONENTS.dirs`](../settings/#django_components.app_settings.ComponentsSettings.dirs)
or
[`COMPONENTS.app_dirs`](../settings/#django_components.app_settings.ComponentsSettings.app_dirs)
change.
See [Reload dev server on component file changes](../../guides/setup/dev_server_setup/#reload-dev-server-on-component-file-changes).
Defaults to `False`.
!!! warning
This setting should be enabled only for the dev environment!
""" # noqa: E501
static_files_allowed: Optional[List[Union[str, re.Pattern]]] = None
"""
A list of file extensions (including the leading dot) that define which files within
[`COMPONENTS.dirs`](../settings/#django_components.app_settings.ComponentsSettings.dirs)
or
[`COMPONENTS.app_dirs`](../settings/#django_components.app_settings.ComponentsSettings.app_dirs)
are treated as [static files](https://docs.djangoproject.com/en/5.1/howto/static-files/).
If a file is matched against any of the patterns, it's considered a static file. Such files are collected
when running [`collectstatic`](https://docs.djangoproject.com/en/5.1/ref/contrib/staticfiles/#collectstatic),
and can be accessed under the
[static file endpoint](https://docs.djangoproject.com/en/5.1/ref/settings/#static-url).
You can also pass in compiled regexes ([`re.Pattern`](https://docs.python.org/3/library/re.html#re.Pattern))
for more advanced patterns.
By default, JS, CSS, and common image and font file formats are considered static files:
```python
COMPONENTS = ComponentsSettings(
static_files_allowed=[
".css",
".js", ".jsx", ".ts", ".tsx",
# Images
".apng", ".png", ".avif", ".gif", ".jpg",
".jpeg", ".jfif", ".pjpeg", ".pjp", ".svg",
".webp", ".bmp", ".ico", ".cur", ".tif", ".tiff",
# Fonts
".eot", ".ttf", ".woff", ".otf", ".svg",
],
)
```
!!! warning
Exposing your Python files can be a security vulnerability.
See [Security notes](../../overview/security_notes).
"""
# TODO_REMOVE_IN_V1
forbidden_static_files: Optional[List[Union[str, re.Pattern]]] = None
"""Deprecated. Use
[`COMPONENTS.static_files_forbidden`](../settings/#django_components.app_settings.ComponentsSettings.static_files_forbidden)
instead.""" # noqa: E501
static_files_forbidden: Optional[List[Union[str, re.Pattern]]] = None
"""
A list of file extensions (including the leading dot) that define which files within
[`COMPONENTS.dirs`](../settings/#django_components.app_settings.ComponentsSettings.dirs)
or
[`COMPONENTS.app_dirs`](../settings/#django_components.app_settings.ComponentsSettings.app_dirs)
will NEVER be treated as [static files](https://docs.djangoproject.com/en/5.1/howto/static-files/).
If a file is matched against any of the patterns, it will never be considered a static file,
even if the file matches a pattern in
[`static_files_allowed`](../settings/#django_components.app_settings.ComponentsSettings.static_files_allowed).
Use this setting together with
[`static_files_allowed`](../settings/#django_components.app_settings.ComponentsSettings.static_files_allowed)
for a fine control over what file types will be exposed.
You can also pass in compiled regexes ([`re.Pattern`](https://docs.python.org/3/library/re.html#re.Pattern))
for more advanced patterns.
By default, any HTML and Python are considered NOT static files:
```python
COMPONENTS = ComponentsSettings(
static_files_forbidden=[
".html", ".django", ".dj", ".tpl",
# Python files
".py", ".pyc",
],
)
```
!!! warning
Exposing your Python files can be a security vulnerability.
See [Security notes](../../overview/security_notes).
"""
tag_formatter: Optional[Union["TagFormatterABC", str]] = None
"""
Configure what syntax is used inside Django templates to render components.
See the [available tag formatters](../tag_formatters).
Defaults to `"django_components.component_formatter"`.
Learn more about [Customizing component tags with TagFormatter](../../concepts/advanced/tag_formatter).
Can be set either as direct reference:
```python
from django_components import component_formatter
COMPONENTS = ComponentsSettings(
"tag_formatter": component_formatter
)
```
Or as an import string;
```python
COMPONENTS = ComponentsSettings(
"tag_formatter": "django_components.component_formatter"
)
```
**Examples:**
- `"django_components.component_formatter"`
Set
```python
COMPONENTS = ComponentsSettings(
"tag_formatter": "django_components.component_formatter"
)
```
To write components like this:
```django
{% component "button" href="..." %}
Click me!
{% endcomponent %}
```
- `django_components.component_shorthand_formatter`
Set
```python
COMPONENTS = ComponentsSettings(
"tag_formatter": "django_components.component_shorthand_formatter"
)
```
To write components like this:
```django
{% button href="..." %}
Click me!
{% endbutton %}
```
"""
template_cache_size: Optional[int] = None
"""
Configure the maximum amount of Django templates to be cached.
Defaults to `128`.
Each time a [Django template](https://docs.djangoproject.com/en/5.1/ref/templates/api/#django.template.Template)
is rendered, it is cached to a global in-memory cache (using Python's
[`lru_cache`](https://docs.python.org/3/library/functools.html#functools.lru_cache)
decorator). This speeds up the next render of the component.
As the same component is often used many times on the same page, these savings add up.
By default the cache holds 128 component templates in memory, which should be enough for most sites.
But if you have a lot of components, or if you are overriding
[`Component.get_template()`](../api#django_components.Component.get_template)
to render many dynamic templates, you can increase this number.
```python
COMPONENTS = ComponentsSettings(
template_cache_size=256,
)
```
To remove the cache limit altogether and cache everything, set `template_cache_size` to `None`.
```python
COMPONENTS = ComponentsSettings(
template_cache_size=None,
)
```
If you want to add templates to the cache yourself, you can use
[`cached_template()`](../api/#django_components.cached_template):
```python
from django_components import cached_template
cached_template("Variable: {{ variable }}")
# You can optionally specify Template class, and other Template inputs:
class MyTemplate(Template):
pass
cached_template(
"Variable: {{ variable }}",
template_cls=MyTemplate,
name=...
origin=...
engine=...
)
```
"""
# NOTE: Some defaults depend on the Django settings, which may not yet be
# initialized at the time that these settings are generated. For such cases
# we define the defaults as a factory function, and use the `Dynamic` class to
# mark such fields.
@dataclass(frozen=True)
class Dynamic(Generic[T]):
getter: Callable[[], T]
# This is the source of truth for the settings defaults. If the documentation
# does NOT match it, the documentation should be updated.
#
# NOTE: Because we need to access Django settings to generate default dirs
# for `COMPONENTS.dirs`, we do it lazily.
# NOTE 2: We show the defaults in the documentation, together with the comments
# (except for the `Dynamic` instances and comments like `type: ignore`).
# So `fmt: off` turns off Black formatting and `snippet:defaults` allows
# us to extract the snippet from the file.
#
# fmt: off
# --snippet:defaults--
defaults = ComponentsSettings(
autodiscover=True,
context_behavior=ContextBehavior.DJANGO.value, # "django" | "isolated"
# Root-level "components" dirs, e.g. `/path/to/proj/components/`
dirs=Dynamic(lambda: [Path(settings.BASE_DIR) / "components"]), # type: ignore[arg-type]
# App-level "components" dirs, e.g. `[app]/components/`
app_dirs=["components"],
dynamic_component_name="dynamic",
libraries=[], # E.g. ["mysite.components.forms", ...]
multiline_tags=True,
reload_on_file_change=False,
static_files_allowed=[
".css",
".js", ".jsx", ".ts", ".tsx",
# Images
".apng", ".png", ".avif", ".gif", ".jpg",
".jpeg", ".jfif", ".pjpeg", ".pjp", ".svg",
".webp", ".bmp", ".ico", ".cur", ".tif", ".tiff",
# Fonts
".eot", ".ttf", ".woff", ".otf", ".svg",
],
static_files_forbidden=[
# See https://marketplace.visualstudio.com/items?itemName=junstyle.vscode-django-support
".html", ".django", ".dj", ".tpl",
# Python files
".py", ".pyc",
],
tag_formatter="django_components.component_formatter",
template_cache_size=128,
)
# --endsnippet:defaults--
# fmt: on
class InternalSettings:
@property
def settings(self) -> Dict:
return getattr(settings, "COMPONENTS", {})
def _settings(self) -> ComponentsSettings:
data = getattr(settings, "COMPONENTS", {})
return ComponentsSettings(**data) if not isinstance(data, ComponentsSettings) else data
@property
def AUTODISCOVER(self) -> bool:
return self.settings.get("autodiscover", True)
return default(self._settings.autodiscover, cast(bool, defaults.autodiscover))
@property
def DIRS(self) -> List[Union[str, Tuple[str, str]]]:
base_dir_path = Path(settings.BASE_DIR)
return self.settings.get("dirs", [base_dir_path / "components"])
def DIRS(self) -> Sequence[Union[str, PathLike, Tuple[str, str], Tuple[str, PathLike]]]:
# For DIRS we use a getter, because default values uses Django settings,
# which may not yet be initialized at the time these settings are generated.
default_fn = cast(Dynamic[Sequence[Union[str, Tuple[str, str]]]], defaults.dirs)
default_dirs = default_fn.getter()
return default(self._settings.dirs, default_dirs)
@property
def APP_DIRS(self) -> List[str]:
return self.settings.get("app_dirs", ["components"])
def APP_DIRS(self) -> Sequence[str]:
return default(self._settings.app_dirs, cast(List[str], defaults.app_dirs))
@property
def DYNAMIC_COMPONENT_NAME(self) -> str:
return self.settings.get("dynamic_component_name", "dynamic")
return default(self._settings.dynamic_component_name, cast(str, defaults.dynamic_component_name))
@property
def LIBRARIES(self) -> List[str]:
return self.settings.get("libraries", [])
return default(self._settings.libraries, cast(List[str], defaults.libraries))
@property
def MULTILINE_TAGS(self) -> bool:
return self.settings.get("multiline_tags", True)
return default(self._settings.multiline_tags, cast(bool, defaults.multiline_tags))
@property
def RELOAD_ON_TEMPLATE_CHANGE(self) -> bool:
return self.settings.get("reload_on_template_change", False)
def RELOAD_ON_FILE_CHANGE(self) -> bool:
val = self._settings.reload_on_file_change
# TODO_REMOVE_IN_V1
if val is None:
val = self._settings.reload_on_template_change
return default(val, cast(bool, defaults.reload_on_file_change))
@property
def TEMPLATE_CACHE_SIZE(self) -> int:
return self.settings.get("template_cache_size", 128)
return default(self._settings.template_cache_size, cast(int, defaults.template_cache_size))
@property
def STATIC_FILES_ALLOWED(self) -> List[Union[str, re.Pattern]]:
default_static_files = [
".css",
".js",
# Images - See https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types#common_image_file_types # noqa: E501
".apng",
".png",
".avif",
".gif",
".jpg",
".jpeg",
".jfif",
".pjpeg",
".pjp",
".svg",
".webp",
".bmp",
".ico",
".cur",
".tif",
".tiff",
# Fonts - See https://stackoverflow.com/q/30572159/9788634
".eot",
".ttf",
".woff",
".otf",
".svg",
]
return self.settings.get("static_files_allowed", default_static_files)
def STATIC_FILES_ALLOWED(self) -> Sequence[Union[str, re.Pattern]]:
return default(self._settings.static_files_allowed, cast(List[str], defaults.static_files_allowed))
@property
def STATIC_FILES_FORBIDDEN(self) -> List[Union[str, re.Pattern]]:
default_forbidden_static_files = [
".html",
# See https://marketplace.visualstudio.com/items?itemName=junstyle.vscode-django-support
".django",
".dj",
".tpl",
# Python files
".py",
".pyc",
]
return self.settings.get("forbidden_static_files", default_forbidden_static_files)
def STATIC_FILES_FORBIDDEN(self) -> Sequence[Union[str, re.Pattern]]:
val = self._settings.static_files_forbidden
# TODO_REMOVE_IN_V1
if val is None:
val = self._settings.forbidden_static_files
return default(val, cast(List[str], defaults.static_files_forbidden))
@property
def CONTEXT_BEHAVIOR(self) -> ContextBehavior:
raw_value = self.settings.get("context_behavior", ContextBehavior.DJANGO.value)
raw_value = cast(str, default(self._settings.context_behavior, defaults.context_behavior))
return self._validate_context_behavior(raw_value)
def _validate_context_behavior(self, raw_value: ContextBehavior) -> ContextBehavior:
def _validate_context_behavior(self, raw_value: Union[ContextBehavior, str]) -> ContextBehavior:
try:
return ContextBehavior(raw_value)
except ValueError:
@ -188,7 +695,8 @@ class AppSettings:
@property
def TAG_FORMATTER(self) -> Union["TagFormatterABC", str]:
return self.settings.get("tag_formatter", "django_components.component_formatter")
tag_formatter = default(self._settings.tag_formatter, cast(str, defaults.tag_formatter))
return cast(Union["TagFormatterABC", str], tag_formatter)
app_settings = AppSettings()
app_settings = InternalSettings()