mirror of
https://github.com/django-components/django-components.git
synced 2025-09-26 15:39:08 +00:00
feat: registry.has(); helpers to get all components and registries; access component from ext class (#1030)
* feat: registry.has(); helpers to get all components and registries; access component from ext class * refactor: add missing import
This commit is contained in:
parent
944bef2d95
commit
107284f474
14 changed files with 217 additions and 15 deletions
|
@ -28,6 +28,13 @@
|
||||||
|
|
||||||
See the API reference for [`@djc_test`](https://django-components.github.io/django-components/0.131/reference/testing_api/#djc_test) for more details.
|
See the API reference for [`@djc_test`](https://django-components.github.io/django-components/0.131/reference/testing_api/#djc_test) for more details.
|
||||||
|
|
||||||
|
- `ComponentRegistry` now has a `has()` method to check if a component is registered
|
||||||
|
without raising an error.
|
||||||
|
|
||||||
|
- Get all created `Component` classes with `all_components()`.
|
||||||
|
|
||||||
|
- Get all created `ComponentRegistry` instances with `all_registries()`.
|
||||||
|
|
||||||
#### Refactor
|
#### Refactor
|
||||||
|
|
||||||
- The `startcomponent` and `upgradecomponent` commands are deprecated, and will be removed in v1.
|
- The `startcomponent` and `upgradecomponent` commands are deprecated, and will be removed in v1.
|
||||||
|
|
|
@ -80,6 +80,9 @@ registry.register("card", CardComponent)
|
||||||
registry.all() # {"button": ButtonComponent, "card": CardComponent}
|
registry.all() # {"button": ButtonComponent, "card": CardComponent}
|
||||||
registry.get("card") # CardComponent
|
registry.get("card") # CardComponent
|
||||||
|
|
||||||
|
# Check if component is registered
|
||||||
|
registry.has("button") # True
|
||||||
|
|
||||||
# Unregister single component
|
# Unregister single component
|
||||||
registry.unregister("card")
|
registry.unregister("card")
|
||||||
|
|
||||||
|
|
|
@ -330,6 +330,51 @@ class MyComponent(Component):
|
||||||
|
|
||||||
This will log the component name and color when the component is created, deleted, or rendered.
|
This will log the component name and color when the component is created, deleted, or rendered.
|
||||||
|
|
||||||
|
### Utility functions
|
||||||
|
|
||||||
|
django-components provides a few utility functions to help with writing extensions:
|
||||||
|
|
||||||
|
- [`all_components()`](../../../reference/api#django_components.all_components) - returns a list of all created component classes.
|
||||||
|
- [`all_registries()`](../../../reference/api#django_components.all_registries) - returns a list of all created registry instances.
|
||||||
|
|
||||||
|
### Accessing the component class from within an extension
|
||||||
|
|
||||||
|
When you are writing the extension class that will be nested inside a Component class, e.g.
|
||||||
|
|
||||||
|
```py
|
||||||
|
class MyTable(Component):
|
||||||
|
class MyExtension:
|
||||||
|
def some_method(self):
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
You can access the owner Component class (`MyTable`) from within methods of the extension class (`MyExtension`) by using the `component_class` attribute:
|
||||||
|
|
||||||
|
```py
|
||||||
|
class MyTable(Component):
|
||||||
|
class MyExtension:
|
||||||
|
def some_method(self):
|
||||||
|
print(self.component_class)
|
||||||
|
```
|
||||||
|
|
||||||
|
Here is how the `component_class` attribute may be used with our `ColorLogger`
|
||||||
|
extension shown above:
|
||||||
|
|
||||||
|
```python
|
||||||
|
class ColorLoggerExtensionClass(ComponentExtension.ExtensionClass):
|
||||||
|
color: str
|
||||||
|
|
||||||
|
def log(self, msg: str) -> None:
|
||||||
|
print(f"{self.component_class.name}: {msg}")
|
||||||
|
|
||||||
|
|
||||||
|
class ColorLoggerExtension(ComponentExtension):
|
||||||
|
name = "color_logger"
|
||||||
|
|
||||||
|
# All `Component.ColorLogger` classes will inherit from this class.
|
||||||
|
ExtensionClass = ColorLoggerExtensionClass
|
||||||
|
```
|
||||||
|
|
||||||
## Extension Commands
|
## Extension Commands
|
||||||
|
|
||||||
Extensions in django-components can define custom commands that can be executed via the Django management command interface. This allows for powerful automation and customization capabilities.
|
Extensions in django-components can define custom commands that can be executed via the Django management command interface. This allows for powerful automation and customization capabilities.
|
||||||
|
|
|
@ -155,6 +155,14 @@
|
||||||
options:
|
options:
|
||||||
show_if_no_docstring: true
|
show_if_no_docstring: true
|
||||||
|
|
||||||
|
::: django_components.all_components
|
||||||
|
options:
|
||||||
|
show_if_no_docstring: true
|
||||||
|
|
||||||
|
::: django_components.all_registries
|
||||||
|
options:
|
||||||
|
show_if_no_docstring: true
|
||||||
|
|
||||||
::: django_components.autodiscover
|
::: django_components.autodiscover
|
||||||
options:
|
options:
|
||||||
show_if_no_docstring: true
|
show_if_no_docstring: true
|
||||||
|
|
|
@ -15,7 +15,7 @@ from django_components.util.command import (
|
||||||
CommandSubcommand,
|
CommandSubcommand,
|
||||||
ComponentCommand,
|
ComponentCommand,
|
||||||
)
|
)
|
||||||
from django_components.component import Component, ComponentVars
|
from django_components.component import Component, ComponentVars, all_components
|
||||||
from django_components.component_media import ComponentMediaInput, ComponentMediaInputPath
|
from django_components.component_media import ComponentMediaInput, ComponentMediaInputPath
|
||||||
from django_components.component_registry import (
|
from django_components.component_registry import (
|
||||||
AlreadyRegistered,
|
AlreadyRegistered,
|
||||||
|
@ -24,6 +24,7 @@ from django_components.component_registry import (
|
||||||
RegistrySettings,
|
RegistrySettings,
|
||||||
register,
|
register,
|
||||||
registry,
|
registry,
|
||||||
|
all_registries,
|
||||||
)
|
)
|
||||||
from django_components.components import DynamicComponent
|
from django_components.components import DynamicComponent
|
||||||
from django_components.dependencies import render_dependencies
|
from django_components.dependencies import render_dependencies
|
||||||
|
@ -60,6 +61,8 @@ from django_components.util.types import EmptyTuple, EmptyDict
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
|
"all_components",
|
||||||
|
"all_registries",
|
||||||
"AlreadyRegistered",
|
"AlreadyRegistered",
|
||||||
"autodiscover",
|
"autodiscover",
|
||||||
"BaseNode",
|
"BaseNode",
|
||||||
|
|
|
@ -111,7 +111,7 @@ CssDataType = TypeVar("CssDataType", bound=Mapping[str, Any])
|
||||||
|
|
||||||
# NOTE: `ReferenceType` is NOT a generic pre-3.9
|
# NOTE: `ReferenceType` is NOT a generic pre-3.9
|
||||||
if sys.version_info >= (3, 9):
|
if sys.version_info >= (3, 9):
|
||||||
AllComponents = List[ReferenceType["Component"]]
|
AllComponents = List[ReferenceType[Type["Component"]]]
|
||||||
else:
|
else:
|
||||||
AllComponents = List[ReferenceType]
|
AllComponents = List[ReferenceType]
|
||||||
|
|
||||||
|
@ -120,6 +120,16 @@ else:
|
||||||
ALL_COMPONENTS: AllComponents = []
|
ALL_COMPONENTS: AllComponents = []
|
||||||
|
|
||||||
|
|
||||||
|
def all_components() -> List[Type["Component"]]:
|
||||||
|
"""Get a list of all created [`Component`](../api#django_components.Component) classes."""
|
||||||
|
components: List[Type["Component"]] = []
|
||||||
|
for comp_ref in ALL_COMPONENTS:
|
||||||
|
comp = comp_ref()
|
||||||
|
if comp is not None:
|
||||||
|
components.append(comp)
|
||||||
|
return components
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class RenderInput(Generic[ArgsType, KwargsType, SlotsType]):
|
class RenderInput(Generic[ArgsType, KwargsType, SlotsType]):
|
||||||
context: Context
|
context: Context
|
||||||
|
|
|
@ -5,6 +5,7 @@ from copy import copy
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Literal, Optional, Protocol, Tuple, Type, Union, cast
|
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Literal, Optional, Protocol, Tuple, Type, Union, cast
|
||||||
|
from weakref import WeakKeyDictionary
|
||||||
|
|
||||||
from django.contrib.staticfiles import finders
|
from django.contrib.staticfiles import finders
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
|
@ -398,7 +399,11 @@ def _get_comp_cls_attr(comp_cls: Type["Component"], attr: str) -> Any:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
media_cache: Dict[Type["Component"], MediaCls] = {}
|
# NOTE: We use weakref to avoid issues with lingering references.
|
||||||
|
if sys.version_info >= (3, 9):
|
||||||
|
media_cache: WeakKeyDictionary[Type["Component"], MediaCls] = WeakKeyDictionary()
|
||||||
|
else:
|
||||||
|
media_cache: WeakKeyDictionary = WeakKeyDictionary()
|
||||||
|
|
||||||
|
|
||||||
def _get_comp_cls_media(comp_cls: Type["Component"]) -> Any:
|
def _get_comp_cls_media(comp_cls: Type["Component"]) -> Any:
|
||||||
|
|
|
@ -150,6 +150,18 @@ class InternalRegistrySettings(NamedTuple):
|
||||||
ALL_REGISTRIES: AllRegistries = []
|
ALL_REGISTRIES: AllRegistries = []
|
||||||
|
|
||||||
|
|
||||||
|
def all_registries() -> List["ComponentRegistry"]:
|
||||||
|
"""
|
||||||
|
Get a list of all created [`ComponentRegistry`](../api#django_components.ComponentRegistry) instances.
|
||||||
|
"""
|
||||||
|
registries: List["ComponentRegistry"] = []
|
||||||
|
for reg_ref in ALL_REGISTRIES:
|
||||||
|
reg = reg_ref()
|
||||||
|
if reg is not None:
|
||||||
|
registries.append(reg)
|
||||||
|
return registries
|
||||||
|
|
||||||
|
|
||||||
class ComponentRegistry:
|
class ComponentRegistry:
|
||||||
"""
|
"""
|
||||||
Manages [components](../api#django_components.Component) and makes them available
|
Manages [components](../api#django_components.Component) and makes them available
|
||||||
|
@ -201,7 +213,8 @@ class ComponentRegistry:
|
||||||
registry.register("card", CardComponent)
|
registry.register("card", CardComponent)
|
||||||
registry.all()
|
registry.all()
|
||||||
registry.clear()
|
registry.clear()
|
||||||
registry.get()
|
registry.get("button")
|
||||||
|
registry.has("button")
|
||||||
```
|
```
|
||||||
|
|
||||||
# Using registry to share components
|
# Using registry to share components
|
||||||
|
@ -455,6 +468,29 @@ class ComponentRegistry:
|
||||||
|
|
||||||
return self._registry[name].cls
|
return self._registry[name].cls
|
||||||
|
|
||||||
|
def has(self, name: str) -> bool:
|
||||||
|
"""
|
||||||
|
Check if a [`Component`](../api#django_components.Component)
|
||||||
|
class is registered under the given name.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name (str): The name under which the component was registered. Required.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: `True` if the component is registered, `False` otherwise.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```python
|
||||||
|
# First register component
|
||||||
|
registry.register("button", ButtonComponent)
|
||||||
|
# Then check
|
||||||
|
registry.has("button")
|
||||||
|
# > True
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
return name in self._registry
|
||||||
|
|
||||||
def all(self) -> Dict[str, Type["Component"]]:
|
def all(self) -> Dict[str, Type["Component"]]:
|
||||||
"""
|
"""
|
||||||
Retrieve all registered [`Component`](../api#django_components.Component) classes.
|
Retrieve all registered [`Component`](../api#django_components.Component) classes.
|
||||||
|
@ -563,6 +599,9 @@ registry.get("button")
|
||||||
# Get all
|
# Get all
|
||||||
registry.all()
|
registry.all()
|
||||||
|
|
||||||
|
# Check if component is registered
|
||||||
|
registry.has("button")
|
||||||
|
|
||||||
# Unregister single
|
# Unregister single
|
||||||
registry.unregister("button")
|
registry.unregister("button")
|
||||||
|
|
||||||
|
|
|
@ -103,6 +103,11 @@ class OnComponentDataContext(NamedTuple):
|
||||||
|
|
||||||
|
|
||||||
class BaseExtensionClass:
|
class BaseExtensionClass:
|
||||||
|
"""Base class for all extension classes."""
|
||||||
|
|
||||||
|
component_class: Type["Component"]
|
||||||
|
"""The Component class that this extension is defined on."""
|
||||||
|
|
||||||
def __init__(self, component: "Component") -> None:
|
def __init__(self, component: "Component") -> None:
|
||||||
self.component = component
|
self.component = component
|
||||||
|
|
||||||
|
@ -116,6 +121,10 @@ class ComponentExtension:
|
||||||
Read more on [Extensions](../../concepts/advanced/extensions).
|
Read more on [Extensions](../../concepts/advanced/extensions).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
###########################
|
||||||
|
# USER INPUT
|
||||||
|
###########################
|
||||||
|
|
||||||
name: str
|
name: str
|
||||||
"""
|
"""
|
||||||
Name of the extension.
|
Name of the extension.
|
||||||
|
@ -255,6 +264,10 @@ class ComponentExtension:
|
||||||
|
|
||||||
urls: List[URLRoute] = []
|
urls: List[URLRoute] = []
|
||||||
|
|
||||||
|
###########################
|
||||||
|
# Misc
|
||||||
|
###########################
|
||||||
|
|
||||||
def __init_subclass__(cls) -> None:
|
def __init_subclass__(cls) -> None:
|
||||||
if not cls.name.isidentifier():
|
if not cls.name.isidentifier():
|
||||||
raise ValueError(f"Extension name must be a valid Python identifier, got {cls.name}")
|
raise ValueError(f"Extension name must be a valid Python identifier, got {cls.name}")
|
||||||
|
@ -532,7 +545,10 @@ class ExtensionManager:
|
||||||
bases: tuple[Type, ...] = (component_ext_subclass, ext_base_class)
|
bases: tuple[Type, ...] = (component_ext_subclass, ext_base_class)
|
||||||
else:
|
else:
|
||||||
bases = (ext_base_class,)
|
bases = (ext_base_class,)
|
||||||
component_ext_subclass = type(ext_class_name, bases, {})
|
|
||||||
|
# Allow to extension class to access the owner `Component` class that via
|
||||||
|
# `ExtensionClass.component_class`.
|
||||||
|
component_ext_subclass = type(ext_class_name, bases, {"component_class": component_cls})
|
||||||
|
|
||||||
# Finally, reassign the new class extension class on the component class.
|
# Finally, reassign the new class extension class on the component class.
|
||||||
setattr(component_cls, ext_class_name, component_ext_subclass)
|
setattr(component_cls, ext_class_name, component_ext_subclass)
|
||||||
|
|
|
@ -56,9 +56,6 @@ class SlotFunc(Protocol, Generic[TSlotData]):
|
||||||
def __call__(self, ctx: Context, slot_data: TSlotData, slot_ref: "SlotRef") -> SlotResult: ... # noqa E704
|
def __call__(self, ctx: Context, slot_data: TSlotData, slot_ref: "SlotRef") -> SlotResult: ... # noqa E704
|
||||||
|
|
||||||
|
|
||||||
SlotContent = Union[SlotResult, SlotFunc[TSlotData], "Slot[TSlotData]"]
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Slot(Generic[TSlotData]):
|
class Slot(Generic[TSlotData]):
|
||||||
"""This class holds the slot content function along with related metadata."""
|
"""This class holds the slot content function along with related metadata."""
|
||||||
|
@ -100,6 +97,11 @@ class Slot(Generic[TSlotData]):
|
||||||
return f"<{self.__class__.__name__} component_name={comp_name} slot_name={slot_name}>"
|
return f"<{self.__class__.__name__} component_name={comp_name} slot_name={slot_name}>"
|
||||||
|
|
||||||
|
|
||||||
|
# NOTE: This must be defined here, so we don't have any forward references
|
||||||
|
# otherwise Pydantic has problem resolving the types.
|
||||||
|
SlotContent = Union[SlotResult, SlotFunc[TSlotData], Slot[TSlotData]]
|
||||||
|
|
||||||
|
|
||||||
# Internal type aliases
|
# Internal type aliases
|
||||||
SlotName = str
|
SlotName = str
|
||||||
|
|
||||||
|
|
|
@ -265,7 +265,7 @@ def djc_test(
|
||||||
""" # noqa: E501
|
""" # noqa: E501
|
||||||
|
|
||||||
def decorator(func: Callable) -> Callable:
|
def decorator(func: Callable) -> Callable:
|
||||||
if isinstance(func, type):
|
if isinstance(func, type) and func.__name__.lower().startswith("test"):
|
||||||
# If `djc_test` is applied to a class, we need to apply it to each test method
|
# If `djc_test` is applied to a class, we need to apply it to each test method
|
||||||
# individually.
|
# individually.
|
||||||
# The rest of this function addresses `func` being a function
|
# The rest of this function addresses `func` being a function
|
||||||
|
|
|
@ -26,7 +26,7 @@ from django.urls import path
|
||||||
from django.utils.safestring import SafeString
|
from django.utils.safestring import SafeString
|
||||||
from pytest_django.asserts import assertHTMLEqual, assertInHTML
|
from pytest_django.asserts import assertHTMLEqual, assertInHTML
|
||||||
|
|
||||||
from django_components import Component, ComponentView, Slot, SlotFunc, register, types
|
from django_components import Component, ComponentView, Slot, SlotFunc, all_components, register, types
|
||||||
from django_components.slots import SlotRef
|
from django_components.slots import SlotRef
|
||||||
from django_components.urls import urlpatterns as dc_urlpatterns
|
from django_components.urls import urlpatterns as dc_urlpatterns
|
||||||
|
|
||||||
|
@ -1497,3 +1497,28 @@ class TestComponentHook:
|
||||||
assert context_in_before == context_in_after
|
assert context_in_before == context_in_after
|
||||||
assert "from_on_before" in context_in_before # type: ignore[operator]
|
assert "from_on_before" in context_in_before # type: ignore[operator]
|
||||||
assert "from_on_after" in context_in_after # type: ignore[operator]
|
assert "from_on_after" in context_in_after # type: ignore[operator]
|
||||||
|
|
||||||
|
|
||||||
|
@djc_test
|
||||||
|
class TestComponentHelpers:
|
||||||
|
def test_all_components(self):
|
||||||
|
# NOTE: When running all tests, this list may already have some components
|
||||||
|
# as some components in test files are defined on module level, outside of
|
||||||
|
# `djc_test` decorator.
|
||||||
|
all_comps_before = len(all_components())
|
||||||
|
|
||||||
|
# Components don't have to be registered to be included in the list
|
||||||
|
class TestComponent(Component):
|
||||||
|
template: types.django_html = """
|
||||||
|
Hello from test
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert len(all_components()) == all_comps_before + 1
|
||||||
|
|
||||||
|
@register("test2")
|
||||||
|
class Test2Component(Component):
|
||||||
|
template: types.django_html = """
|
||||||
|
Hello from test2
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert len(all_components()) == all_comps_before + 2
|
||||||
|
|
|
@ -106,7 +106,7 @@ class DummyNestedExtension(ComponentExtension):
|
||||||
|
|
||||||
|
|
||||||
def with_component_cls(on_created: Callable):
|
def with_component_cls(on_created: Callable):
|
||||||
class TestComponent(Component):
|
class TempComponent(Component):
|
||||||
template = "Hello {{ name }}!"
|
template = "Hello {{ name }}!"
|
||||||
|
|
||||||
def get_context_data(self, name="World"):
|
def get_context_data(self, name="World"):
|
||||||
|
@ -124,11 +124,27 @@ def with_registry(on_created: Callable):
|
||||||
@djc_test
|
@djc_test
|
||||||
class TestExtension:
|
class TestExtension:
|
||||||
@djc_test(components_settings={"extensions": [DummyExtension]})
|
@djc_test(components_settings={"extensions": [DummyExtension]})
|
||||||
def test_extensios_setting(self):
|
def test_extensions_setting(self):
|
||||||
assert len(app_settings.EXTENSIONS) == 2
|
assert len(app_settings.EXTENSIONS) == 2
|
||||||
assert isinstance(app_settings.EXTENSIONS[0], ViewExtension)
|
assert isinstance(app_settings.EXTENSIONS[0], ViewExtension)
|
||||||
assert isinstance(app_settings.EXTENSIONS[1], DummyExtension)
|
assert isinstance(app_settings.EXTENSIONS[1], DummyExtension)
|
||||||
|
|
||||||
|
@djc_test(components_settings={"extensions": [DummyExtension]})
|
||||||
|
def test_access_component_from_extension(self):
|
||||||
|
class TestAccessComp(Component):
|
||||||
|
template = "Hello {{ name }}!"
|
||||||
|
|
||||||
|
def get_context_data(self, arg1, arg2, name="World"):
|
||||||
|
return {"name": name}
|
||||||
|
|
||||||
|
ext_class = TestAccessComp.TestExtension # type: ignore[attr-defined]
|
||||||
|
assert issubclass(ext_class, ComponentExtension.ExtensionClass)
|
||||||
|
assert ext_class.component_class is TestAccessComp
|
||||||
|
|
||||||
|
# NOTE: Required for test_component_class_lifecycle_hooks to work
|
||||||
|
del TestAccessComp
|
||||||
|
gc.collect()
|
||||||
|
|
||||||
|
|
||||||
@djc_test
|
@djc_test
|
||||||
class TestExtensionHooks:
|
class TestExtensionHooks:
|
||||||
|
@ -147,7 +163,7 @@ class TestExtensionHooks:
|
||||||
|
|
||||||
# Verify on_component_class_created was called
|
# Verify on_component_class_created was called
|
||||||
assert len(extension.calls["on_component_class_created"]) == 1
|
assert len(extension.calls["on_component_class_created"]) == 1
|
||||||
assert extension.calls["on_component_class_created"][0] == "TestComponent"
|
assert extension.calls["on_component_class_created"][0] == "TempComponent"
|
||||||
|
|
||||||
# Create a component class in a separate scope, to avoid any references from within
|
# Create a component class in a separate scope, to avoid any references from within
|
||||||
# this test function, so we can garbage collect it after the function returns
|
# this test function, so we can garbage collect it after the function returns
|
||||||
|
@ -158,8 +174,11 @@ class TestExtensionHooks:
|
||||||
gc.collect()
|
gc.collect()
|
||||||
|
|
||||||
# Verify on_component_class_deleted was called
|
# Verify on_component_class_deleted was called
|
||||||
assert len(extension.calls["on_component_class_deleted"]) == 1
|
# NOTE: The previous test, `test_access_component_from_extension`, is sometimes
|
||||||
assert extension.calls["on_component_class_deleted"][0] == "TestComponent"
|
# garbage-collected too late, in which case it's included in `on_component_class_deleted`.
|
||||||
|
# So in the test we check only for the last call.
|
||||||
|
assert len(extension.calls["on_component_class_deleted"]) >= 1
|
||||||
|
assert extension.calls["on_component_class_deleted"][-1] == "TempComponent"
|
||||||
|
|
||||||
@djc_test(components_settings={"extensions": [DummyExtension]})
|
@djc_test(components_settings={"extensions": [DummyExtension]})
|
||||||
def test_registry_lifecycle_hooks(self):
|
def test_registry_lifecycle_hooks(self):
|
||||||
|
|
|
@ -9,6 +9,7 @@ from django_components import (
|
||||||
NotRegistered,
|
NotRegistered,
|
||||||
RegistrySettings,
|
RegistrySettings,
|
||||||
TagProtectedError,
|
TagProtectedError,
|
||||||
|
all_registries,
|
||||||
component_formatter,
|
component_formatter,
|
||||||
component_shorthand_formatter,
|
component_shorthand_formatter,
|
||||||
register,
|
register,
|
||||||
|
@ -39,14 +40,18 @@ class MockComponentView(Component):
|
||||||
@djc_test
|
@djc_test
|
||||||
class TestComponentRegistry:
|
class TestComponentRegistry:
|
||||||
def test_register_class_decorator(self):
|
def test_register_class_decorator(self):
|
||||||
|
assert not registry.has("decorated_component")
|
||||||
|
|
||||||
@register("decorated_component")
|
@register("decorated_component")
|
||||||
class TestComponent(Component):
|
class TestComponent(Component):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
assert registry.has("decorated_component")
|
||||||
assert registry.get("decorated_component") == TestComponent
|
assert registry.get("decorated_component") == TestComponent
|
||||||
|
|
||||||
# Cleanup
|
# Cleanup
|
||||||
registry.unregister("decorated_component")
|
registry.unregister("decorated_component")
|
||||||
|
assert not registry.has("decorated_component")
|
||||||
|
|
||||||
def test_register_class_decorator_custom_registry(self):
|
def test_register_class_decorator_custom_registry(self):
|
||||||
my_lib = Library()
|
my_lib = Library()
|
||||||
|
@ -245,3 +250,18 @@ class TestProtectedTags:
|
||||||
|
|
||||||
# Cleanup
|
# Cleanup
|
||||||
registry.unregister("sth_else")
|
registry.unregister("sth_else")
|
||||||
|
|
||||||
|
|
||||||
|
@djc_test
|
||||||
|
class TestRegistryHelpers:
|
||||||
|
def test_all_registries(self):
|
||||||
|
# Default registry
|
||||||
|
assert len(all_registries()) == 1
|
||||||
|
|
||||||
|
reg = ComponentRegistry()
|
||||||
|
|
||||||
|
assert len(all_registries()) == 2
|
||||||
|
|
||||||
|
del reg
|
||||||
|
|
||||||
|
assert len(all_registries()) == 1
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue