diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1bc1f41a..eb1b766f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,13 +2,37 @@
## v0.124
+#### Feat
+
+- Instead of inlining the JS and CSS under `Component.js` and `Component.css`, you can move
+ them to their own files, and link the JS/CSS files with `Component.js_file` and `Component.css_file`.
+
+ Even when you specify the JS/CSS with `Component.js_file` or `Component.css_file`, then you can still
+ access the content under `Component.js/css` - behind the scenes, the content of the JS/CSS files
+ will be set to `Component.js/css` upon first access.
+
+ With this change, the role of `Component.js/css` and the JS/CSS in `Component.Media` has changed:
+
+ - The JS/CSS defined in `Component.js/css` or `Component.js/css_file` is the "main" JS/CSS
+ - The JS/CSS defined in `Component.Media.js/css` are secondary or additional
+
+ See the updated ["Getting Started" tutorial](https://EmilStenstrom.github.io/django-components/0.124/concepts/getting_started/adding_js_and_css/)
+
#### Refactor
- The undocumented `Component.component_id` was removed. Instead, use `Component.id`. Changes:
- - While `component_id` was unique every time you instantiated `Component`, The new `id` is unique
+ - While `component_id` was unique every time you instantiated `Component`, the new `id` is unique
every time you render the component (e.g. with `Component.render()`)
- - The new `id` is available only during render, so e.g. from within `get_context_data()`
+ - The new `id` is available only during render, so e.g. from within `get_context_data()`
+
+- Component's HTML / CSS / JS are now resolved and loaded lazily. That is, if you specify `template_name`,
+ `js_file`, `css_file`, or `Media.js/css`, the file paths will be resolved only once you:
+
+ 1. Try to access component's HTML / CSS / JS, or
+ 2. Render the component.
+
+ Read more on [Accessing component's HTML / JS / CSS](https://EmilStenstrom.github.io/django-components/0.124/concepts/fundamentals/defining_js_css_html_files/#customize-how-paths-are-rendered-into-html-tags).
## v0.123
diff --git a/docs/concepts/fundamentals/defining_js_css_html_files.md b/docs/concepts/fundamentals/defining_js_css_html_files.md
index 09b067ac..2e84cc33 100644
--- a/docs/concepts/fundamentals/defining_js_css_html_files.md
+++ b/docs/concepts/fundamentals/defining_js_css_html_files.md
@@ -3,19 +3,60 @@ title: Defining HTML / JS / CSS files
weight: 8
---
-django_component's management of files is inspired by [Django's `Media` class](https://docs.djangoproject.com/en/5.0/topics/forms/media/).
+As you could have seen in [the tutorial](../../concepts/getting_started/adding_js_and_css.md), there's multiple ways how you can associate
+HTML / JS / CSS with a component:
-To be familiar with how Django handles static files, we recommend reading also:
+- You can set [`Component.template`](../../reference/api.md#django_components.Component.template),
+ [`Component.css`](../../reference/api.md#django_components.Component.css) and
+ [`Component.js`](../../reference/api.md#django_components.Component.js) to define the main HTML / CSS / JS for a component
+ as inlined code.
+- You can set [`Component.template_name`](../../reference/api.md#django_components.Component.template_name),
+ [`Component.css_file`](../../reference/api.md#django_components.Component.css_file) and
+ [`Component.js_file`](../../reference/api.md#django_components.Component.js_file) to define the main HTML / CSS / JS
+ for a component in separate files.
+- You can link additional CSS / JS files using
+ [`Component.Media.js`](../../reference/api.md#django_components.ComponentMediaInput.js)
+ and [`Component.Media.css`](../../reference/api.md#django_components.ComponentMediaInput.css).
-- [How to manage static files (e.g. images, JavaScript, CSS)](https://docs.djangoproject.com/en/5.0/howto/static-files/)
+!!! warning
-## Defining file paths relative to component or static dirs
+ You **cannot** use both inlined code **and** separate file for a single language type:
-As seen in the [getting started example](#create-your-first-component), to associate HTML/JS/CSS
-files with a component, you set them as `template_name`, `js_file` and `css_file` respectively:
+ - You can only either set `Component.template` or `Component.template_name`
+ - You can only either set `Component.css` or `Component.css_file`
+ - You can only either set `Component.js` or `Component.js_file`
-```py
-# In a file [project root]/components/calendar/calendar.py
+ However, you can freely mix these for different languages:
+
+ ```py
+ class MyTable(Component):
+ template: types.django_html = """
+
+ Hi there!
+
+ """
+ js_file = "my_table.js"
+ css_file = "my_table.css"
+ ```
+
+!!! note
+
+ django-component's management of files is inspired by [Django's `Media` class](https://docs.djangoproject.com/en/5.0/topics/forms/media/).
+
+ To be familiar with how Django handles static files, we recommend reading also:
+
+ - [How to manage static files (e.g. images, JavaScript, CSS)](https://docs.djangoproject.com/en/5.0/howto/static-files/)
+
+## Defining file paths relative to component
+
+As seen in the [getting started example](../getting_started/your_first_component.md), to associate HTML / JS / CSS
+files with a component, you can set them as
+[`Component.template_name`](../../reference/api.md#django_components.Component.template_name),
+[`Component.js_file`](../../reference/api.md#django_components.Component.js_file)
+and
+[`Component.css_file`](../../reference/api.md#django_components.Component.css_file) respectively:
+
+```py title="[project root]/components/calendar/calendar.py"
from django_components import Component, register
@register("calendar")
@@ -25,14 +66,21 @@ class Calendar(Component):
js_file = "script.js"
```
-In the example above, the files are defined relative to the directory where `component.py` is.
+In the example above, we defined the files relative to the directory where the component file is defined.
-Alternatively, you can specify the file paths relative to the directories set in `COMPONENTS.dirs` or `COMPONENTS.app_dirs`.
+Alternatively, you can specify the file paths relative to the directories set in
+[`COMPONENTS.dirs`](../../reference/settings.md#django_components.app_settings.ComponentsSettings.dirs)
+or
+[`COMPONENTS.app_dirs`](../../reference/settings.md#django_components.app_settings.ComponentsSettings.app_dirs).
-Assuming that `COMPONENTS.dirs` contains path `[project root]/components`, we can rewrite the example as:
+If you specify the paths relative to component's directory, django-componenents does the conversion automatically
+for you.
-```py
-# In a file [project root]/components/calendar/calendar.py
+Thus, assuming that
+[`COMPONENTS.dirs`](../../reference/settings.md#django_components.app_settings.ComponentsSettings.dirs)
+contains path `[project root]/components`, the example above is the same as writing:
+
+```py title="[project root]/components/calendar/calendar.py"
from django_components import Component, register
@register("calendar")
@@ -42,26 +90,78 @@ class Calendar(Component):
js_file = "calendar/script.js"
```
-NOTE: In case of conflict, the preference goes to resolving the files relative to the component's directory.
+!!! important
-## Defining multiple paths
+ **File path resolution in-depth**
+
+ At component class creation, django-components checks all file paths defined on the component (e.g. `Component.template_name`).
+
+ For each file path, it checks if the file path is relative to the component's directory.
+ And such file exists, the component's file path is re-written to be defined relative to a first matching directory
+ in [`COMPONENTS.dirs`](../../reference/settings.md#django_components.app_settings.ComponentsSettings.dirs)
+ or
+ [`COMPONENTS.app_dirs`](../../reference/settings.md#django_components.app_settings.ComponentsSettings.app_dirs).
+
+ **Example:**
+
+ ```py title="[root]/components/mytable/mytable.py"
+ class MyTable(Component):
+ template_name = "mytable.html"
+ ```
+
+ 1. Component `MyTable` is defined in file `[root]/components/mytable/mytable.py`.
+ 2. The component's directory is thus `[root]/components/mytable/`.
+ 3. Because `MyTable.template_name` is `mytable.html`, django-components tries to
+ resolve it as `[root]/components/mytable/mytable.html`.
+ 4. django-components checks the filesystem. If there's no such file, nothing happens.
+ 5. If there IS such file, django-components tries to rewrite the path.
+ 6. django-components searches `COMPONENTS.dirs` and `COMPONENTS.app_dirs` for a first
+ directory that contains `[root]/components/mytable/mytable.html`.
+ 7. It comes across `[root]/components/`, which DOES contain the path to `mytable.html`.
+ 8. Thus, it rewrites `template_name` from `mytable.html` to `mytable/mytable.html`.
+
+ NOTE: In case of ambiguity, the preference goes to resolving the files relative to the component's directory.
+
+## Defining additional JS and CSS files
Each component can have only a single template, and single main JS and CSS. However, you can define additional JS or CSS
-using the nested [`Media` class](../../../reference/api#django_components.Component.Media).
+using the nested [`Component.Media` class](../../../reference/api#django_components.Component.Media).
-This `Media` class behaves similarly to [Django's Media class](https://docs.djangoproject.com/en/5.1/topics/forms/media/#assets-as-a-static-definition),
-with a few differences:
+This `Media` class behaves similarly to
+[Django's Media class](https://docs.djangoproject.com/en/5.1/topics/forms/media/#assets-as-a-static-definition):
-1. Our Media class accepts various formats for the JS and CSS files: either a single file, a list, or (CSS-only) a dictonary (See below)
-2. Individual JS / CSS files can be any of `str`, `bytes`, `Path`, [`SafeString`](https://dev.to/doridoro/django-safestring-afj), or a function.
-3. Our Media class does NOT support [Django's `extend` keyword](https://docs.djangoproject.com/en/5.1/topics/forms/media/#extend)
+- Paths are generally handled as static file paths, and resolved URLs are rendered to HTML with
+ `media_class.render_js()` or `media_class.render_css()`.
+- A path that starts with `http`, `https`, or `/` is considered a URL, skipping the static file resolution.
+ This path is still rendered to HTML with `media_class.render_js()` or `media_class.render_css()`.
+- A [`SafeString`](https://docs.djangoproject.com/en/5.1/ref/utils/#django.utils.safestring.SafeString),
+ or a function (with `__html__` method) is considered an already-formatted HTML tag, skipping both static file
+ resolution and rendering with `media_class.render_js()` or `media_class.render_css()`.
+However, there's a few differences from Django's Media class:
+
+1. Our Media class accepts various formats for the JS and CSS files: either a single file, a list,
+ or (CSS-only) a dictonary (See [`ComponentMediaInput`](../../../reference/api#django_components.ComponentMediaInput)).
+2. Individual JS / CSS files can be any of `str`, `bytes`, `Path`,
+ [`SafeString`](https://docs.djangoproject.com/en/5.1/ref/utils/#django.utils.safestring.SafeString), or a function
+ (See [`ComponentMediaInputPath`](../../../reference/api#django_components.ComponentMediaInputPath)).
+3. Our Media class does NOT support
+ [Django's `extend` keyword](https://docs.djangoproject.com/en/5.1/topics/forms/media/#extend)
```py
-class MyComponent(Component):
+class MyTable(Component):
class Media:
- js = ["path/to/script1.js", "path/to/script2.js"]
- css = ["path/to/style1.css", "path/to/style2.css"]
+ js = [
+ "path/to/script.js",
+ "https://unpkg.com/alpinejs@3.14.7/dist/cdn.min.js", # AlpineJS
+ ]
+ css = {
+ "all": [
+ "path/to/style.css",
+ "https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css", # TailwindCSS
+ ],
+ "print": ["path/to/style2.css"],
+ }
```
## Configuring CSS Media Types
@@ -78,20 +178,29 @@ class MyComponent(Component):
class Media:
css = {
"all": "path/to/style1.css",
- "print": "path/to/style2.css",
+ "print": ["path/to/style2.css", "path/to/style3.css"],
}
```
-```py
-class MyComponent(Component):
- class Media:
- css = {
- "all": ["path/to/style1.css", "path/to/style2.css"],
- "print": ["path/to/style3.css", "path/to/style4.css"],
- }
-```
+!!! note
-NOTE: When you define CSS as a string or a list, the `all` media type is implied.
+ When you define CSS as a string or a list, the `all` media type is implied.
+
+ So these two examples are the same:
+
+ ```py
+ class MyComponent(Component):
+ class Media:
+ css = "path/to/style1.css"
+ ```
+
+ ```py
+ class MyComponent(Component):
+ class Media:
+ css = {
+ "all": ["path/to/style1.css"],
+ }
+ ```
## Supported types for file paths
@@ -103,6 +212,8 @@ File paths can be any of:
- `SafeData` (`__html__` method)
- `Callable` that returns any of the above, evaluated at class creation (`__new__`)
+See [`ComponentMediaInputPath`](../../../reference/api#django_components.ComponentMediaInputPath).
+
```py
from pathlib import Path
@@ -126,18 +237,26 @@ class SimpleComponent(Component):
]
```
-## Path as objects
+## Paths as objects
-In the example [above](#supported-types-for-file-paths), you could see that when we used `mark_safe` to mark a string as a `SafeString`, we had to define the full `
+#
+```
+
+If you want to modify the class that is instantiated for [`Component.media`](../../reference/api.md#django_components.Component.media),
+you can configure [`Component.media_class`](../../reference/api.md#django_components.Component.media_class)
+([See example](#customize-how-paths-are-rendered-into-html-tags)).
diff --git a/docs/css/style.css b/docs/css/style.css
index d776a117..4fa5b04b 100644
--- a/docs/css/style.css
+++ b/docs/css/style.css
@@ -11,6 +11,19 @@ h6 {
padding-top: 40px;
}
+.md-typeset h3 {
+ /* Original styling */
+ font-size: 1.25em;
+ font-weight: 400;
+ letter-spacing: -.01em;
+ line-height: 1.5;
+ margin: 1.6em 0 0.8em;
+
+ /* Custom */
+ border-top: 0.5px solid var(--md-typeset-color);
+ padding-top: 40px;
+}
+
.md-nav__item--section {
margin-top: 32px;
}
diff --git a/src/django_components/__init__.py b/src/django_components/__init__.py
index b076b6cf..20087a1c 100644
--- a/src/django_components/__init__.py
+++ b/src/django_components/__init__.py
@@ -7,6 +7,7 @@
from django_components.app_settings import ContextBehavior, ComponentsSettings
from django_components.autodiscovery import autodiscover, import_libraries
from django_components.component import Component, ComponentVars, ComponentView
+from django_components.component_media import ComponentMediaInput, ComponentMediaInputPath
from django_components.component_registry import (
AlreadyRegistered,
ComponentRegistry,
@@ -44,6 +45,8 @@ __all__ = [
"Component",
"ComponentFileEntry",
"ComponentFormatter",
+ "ComponentMediaInput",
+ "ComponentMediaInputPath",
"ComponentRegistry",
"ComponentVars",
"ComponentView",
diff --git a/src/django_components/component.py b/src/django_components/component.py
index 2907a3a3..aaa929d1 100644
--- a/src/django_components/component.py
+++ b/src/django_components/component.py
@@ -212,7 +212,20 @@ class Component(
The filepath must be relative to either the file where the component class was defined,
or one of the roots of `STATIFILES_DIRS`.
- Only one of `template_name`, `get_template_name`, `template` or `get_template` must be defined.
+ Only one of [`template_name`](../api#django_components.Component.template_name),
+ [`get_template_name`](../api#django_components.Component.get_template_name),
+ [`template`](../api#django_components.Component.template)
+ or [`get_template`](../api#django_components.Component.get_template) must be defined.
+
+ **Example:**
+
+ ```py
+ class MyComponent(Component):
+ template_name = "path/to/template.html"
+
+ def get_context_data(self):
+ return {"name": "World"}
+ ```
"""
def get_template_name(self, context: Context) -> Optional[str]:
@@ -222,7 +235,10 @@ class Component(
The filepath must be relative to either the file where the component class was defined,
or one of the roots of `STATIFILES_DIRS`.
- Only one of `template_name`, `get_template_name`, `template` or `get_template` must be defined.
+ Only one of [`template_name`](../api#django_components.Component.template_name),
+ [`get_template_name`](../api#django_components.Component.get_template_name),
+ [`template`](../api#django_components.Component.template)
+ or [`get_template`](../api#django_components.Component.get_template) must be defined.
"""
return None
@@ -230,14 +246,30 @@ class Component(
"""
Inlined Django template associated with this component. Can be a plain string or a Template instance.
- Only one of `template_name`, `get_template_name`, `template` or `get_template` must be defined.
+ Only one of [`template_name`](../api#django_components.Component.template_name),
+ [`get_template_name`](../api#django_components.Component.get_template_name),
+ [`template`](../api#django_components.Component.template)
+ or [`get_template`](../api#django_components.Component.get_template) must be defined.
+
+ **Example:**
+
+ ```py
+ class MyComponent(Component):
+ template = "Hello, {{ name }}!"
+
+ def get_context_data(self):
+ return {"name": "World"}
+ ```
"""
def get_template(self, context: Context) -> Optional[Union[str, Template]]:
"""
Inlined Django template associated with this component. Can be a plain string or a Template instance.
- Only one of `template_name`, `get_template_name`, `template` or `get_template` must be defined.
+ Only one of [`template_name`](../api#django_components.Component.template_name),
+ [`get_template_name`](../api#django_components.Component.get_template_name),
+ [`template`](../api#django_components.Component.template)
+ or [`get_template`](../api#django_components.Component.get_template) must be defined.
"""
return None
@@ -245,50 +277,167 @@ class Component(
return cast(DataType, {})
js: Optional[str] = None
- """Main JS associated with this component inlined as string."""
+ """
+ Main JS associated with this component inlined as string.
+
+ Only one of [`js`](../api#django_components.Component.js) or
+ [`js_file`](../api#django_components.Component.js_file) must be defined.
+
+ **Example:**
+
+ ```py
+ class MyComponent(Component):
+ js = "console.log('Hello, World!');"
+ ```
+ """
js_file: Optional[str] = None
"""
Main JS associated with this component as file path.
- When you create a Component subclass, these will happen:
+ When you create a Component class with `js_file`, these will happen:
- 1. The filepath is resolved, in case it is relative to the component Python file.
- 2. The file is read and its contents will be available under `MyComponent.js`.
+ 1. If the file path is relative to the directory where the component's Python file is,
+ the path is resolved.
+ 2. The file is read and its contents is set to [`Component.js`](../api#django_components.Component.js).
+
+ Only one of [`js`](../api#django_components.Component.js) or
+ [`js_file`](../api#django_components.Component.js_file) must be defined.
+
+ **Example:**
+
+ ```js title="path/to/script.js"
+ console.log('Hello, World!');
+ ```
+
+ ```py title="path/to/component.py"
+ class MyComponent(Component):
+ js_file = "path/to/script.js"
+
+ print(MyComponent.js)
+ # Output: console.log('Hello, World!');
+ ```
"""
css: Optional[str] = None
- """Main CSS associated with this component inlined as string."""
+ """
+ Main CSS associated with this component inlined as string.
+
+ Only one of [`css`](../api#django_components.Component.css) or
+ [`css_file`](../api#django_components.Component.css_file) must be defined.
+
+ **Example:**
+
+ ```py
+ class MyComponent(Component):
+ css = \"\"\"
+ .my-class {
+ color: red;
+ }
+ \"\"\"
+ ```
+ """
+
css_file: Optional[str] = None
"""
Main CSS associated with this component as file path.
- When you create a Component subclass, these will happen:
+ When you create a Component class with `css_file`, these will happen:
- 1. The filepath is resolved, in case it is relative to the component Python file.
- 2. The file is read and its contents will be available under `MyComponent.css`.
+ 1. If the file path is relative to the directory where the component's Python file is,
+ the path is resolved.
+ 2. The file is read and its contents is set to [`Component.css`](../api#django_components.Component.css).
+
+ Only one of [`css`](../api#django_components.Component.css) or
+ [`css_file`](../api#django_components.Component.css_file) must be defined.
+
+ **Example:**
+
+ ```css title="path/to/style.css"
+ .my-class {
+ color: red;
+ }
+ ```
+
+ ```py title="path/to/component.py"
+ class MyComponent(Component):
+ css_file = "path/to/style.css"
+
+ print(MyComponent.css)
+ # Output:
+ # .my-class {
+ # color: red;
+ # };
+ ```
"""
media: Optional[MediaCls] = None
"""
Normalized definition of JS and CSS media files associated with this component.
- `None` if `Media` is not defined.
+ `None` if [`Component.Media`](../api#django_components.Component.Media) is not defined.
+
+ This field is generated from [`Component.media_class`](../api#django_components.Component.media_class).
+
+ Read more on [Accessing component's HTML / JS / CSS](../../concepts/fundamentals/defining_js_css_html_files/#accessing-components-media-files).
+
+ **Example:**
+
+ ```py
+ class MyComponent(Component):
+ class Media:
+ js = "path/to/script.js"
+ css = "path/to/style.css"
+
+ print(MyComponent.media)
+ # Output:
+ #
+ #
+ ```
+ """ # noqa: E501
- NOTE: This field is generated from `Component.media_class`.
- """
media_class: Type[MediaCls] = MediaCls
+ """
+ Set the [Media class](https://docs.djangoproject.com/en/5.1/topics/forms/media/#assets-as-a-static-definition)
+ that will be instantiated with the JS and CSS media files from
+ [`Component.Media`](../api#django_components.Component.Media).
+
+ This is useful when you want to customize the behavior of the media files, like
+ customizing how the JS or CSS files are rendered into `"),
+ ]
+ css = [
+ Path("path/to/style.css"),
+ lambda: "path/to/style.css",
+ lambda: Path("path/to/style.css"),
+ ]
+```
+"""
+
+
# This is the interface of the class that user is expected to define on the component class, e.g.:
# ```py
# class MyComponent(Component):
@@ -30,7 +64,7 @@ COMP_MEDIA_LAZY_ATTRS = ("media", "template", "template_name", "js", "js_file",
# ```
class ComponentMediaInput(Protocol):
"""
- Defines JS and CSS media files associated with this component.
+ Defines JS and CSS media files associated with a [`Component`](../api#django_components.Component).
```py
class MyTable(Component):
@@ -49,8 +83,90 @@ class ComponentMediaInput(Protocol):
```
"""
- css: Optional[Union[str, List[str], Dict[str, str], Dict[str, List[str]]]] = None
- js: Optional[Union[str, List[str]]] = None
+ css: Optional[
+ Union[
+ ComponentMediaInputPath,
+ List[ComponentMediaInputPath],
+ Dict[str, ComponentMediaInputPath],
+ Dict[str, List[ComponentMediaInputPath]],
+ ]
+ ] = None
+ """
+ CSS files associated with a [`Component`](../api#django_components.Component).
+
+ - If a string, it's assumed to be a path to a CSS file.
+
+ - If a list, each entry is assumed to be a path to a CSS file.
+
+ - If a dict, the keys are media types (e.g. "all", "print", "screen", etc.), and the values are either:
+ - A string, assumed to be a path to a CSS file.
+ - A list, each entry is assumed to be a path to a CSS file.
+
+ Each entry can be a string, bytes, SafeString, PathLike, or a callable that returns one of the former
+ (see [`ComponentMediaInputPath`](../api#django_components.ComponentMediaInputPath)).
+
+ Examples:
+ ```py
+ class MyComponent(Component):
+ class Media:
+ css = "path/to/style.css"
+ ```
+
+ ```py
+ class MyComponent(Component):
+ class Media:
+ css = ["path/to/style1.css", "path/to/style2.css"]
+ ```
+
+ ```py
+ class MyComponent(Component):
+ class Media:
+ css = {
+ "all": "path/to/style.css",
+ "print": "path/to/print.css",
+ }
+ ```
+
+ ```py
+ class MyComponent(Component):
+ class Media:
+ css = {
+ "all": ["path/to/style1.css", "path/to/style2.css"],
+ "print": "path/to/print.css",
+ }
+ ```
+ """
+
+ js: Optional[Union[ComponentMediaInputPath, List[ComponentMediaInputPath]]] = None
+ """
+ JS files associated with a [`Component`](../api#django_components.Component).
+
+ - If a string, it's assumed to be a path to a JS file.
+
+ - If a list, each entry is assumed to be a path to a JS file.
+
+ Each entry can be a string, bytes, SafeString, PathLike, or a callable that returns one of the former
+ (see [`ComponentMediaInputPath`](../api#django_components.ComponentMediaInputPath)).
+
+ Examples:
+ ```py
+ class MyComponent(Component):
+ class Media:
+ js = "path/to/script.js"
+ ```
+
+ ```py
+ class MyComponent(Component):
+ class Media:
+ js = ["path/to/script1.js", "path/to/script2.js"]
+ ```
+
+ ```py
+ class MyComponent(Component):
+ class Media:
+ js = lambda: ["path/to/script1.js", "path/to/script2.js"]
+ ```
+ """
@dataclass
@@ -96,7 +212,7 @@ class ComponentMediaMeta(type):
def __new__(mcs, name: str, bases: Tuple[Type, ...], attrs: Dict[str, Any]) -> Type:
# Normalize the various forms of Media inputs we allow
if "Media" in attrs:
- normalize_media(attrs["Media"])
+ _normalize_media(attrs["Media"])
cls = super().__new__(mcs, name, bases, attrs)
comp_cls = cast(Type["Component"], cls)
@@ -155,7 +271,7 @@ def _setup_lazy_media_resolve(comp_cls: Type["Component"], attrs: Dict[str, Any]
if comp_media is None:
continue
if not comp_media.resolved:
- resolve_media(base, comp_media)
+ _resolve_media(base, comp_media)
value = getattr(comp_media, attr, None)
# For each of the pairs of inlined_content + file (e.g. `js` + `js_file`), if at least one of the two
@@ -204,7 +320,7 @@ def _setup_lazy_media_resolve(comp_cls: Type["Component"], attrs: Dict[str, Any]
setattr(comp_cls, attr, InterceptDescriptor(attr))
-def resolve_media(comp_cls: Type["Component"], comp_media: ComponentMedia) -> None:
+def _resolve_media(comp_cls: Type["Component"], comp_media: ComponentMedia) -> None:
"""
Resolve the media files associated with the component.
@@ -272,7 +388,7 @@ def resolve_media(comp_cls: Type["Component"], comp_media: ComponentMedia) -> No
comp_media.resolved = True
-def normalize_media(media: Type[ComponentMediaInput]) -> None:
+def _normalize_media(media: Type[ComponentMediaInput]) -> None:
"""
Resolve the `Media` class associated with the component.
@@ -394,7 +510,7 @@ def _is_media_filepath(filepath: Any) -> bool:
return False
-def _normalize_media_filepath(filepath: Any) -> Union[str, SafeData]:
+def _normalize_media_filepath(filepath: ComponentMediaInputPath) -> Union[str, SafeData]:
if callable(filepath):
filepath = filepath()