Component Manager Implementation¶
Default implementation of the component manager api.
Functions¶
- disnake_compass.impl.manager.get_manager(name=None)[source]¶
Get a manager by name, or create one if it does not yet exist.
Calling
get_manager()without specifying a name returns the root manager. The root manager is – unless explicitly modified by the user – guaranteed to be the lowest-level manager, with no parents.Managers follow a parent-child hierarchy. For example, a manager “foo.bar” would be a child of “foo”. Any components registered to “foo.bar” would also be accessible to manager “foo”. This means that the root manager has access to all components.
To register a component to a manager, use
ComponentManager.register(). To ensure component callbacks are invoked, the manager must first be linked to a client. This is done usingComponentManager.add_to_client(). Since parents have access to the components of their children, it is often sufficient to bind only the root manager to a client.It is generally recommended to use a separate manager per extension, though you can share the same manager between files by using the same name, if desired.
Further configuration of managers can be done through
ComponentManager.config().- Parameters:
name (str) – The name of the component. If not provided, the root manager is returned.
- Returns:
A component manager with the desired name. If a component manager with this name already existed before calling this function, that same manager is returned. Otherwise, a new manager is created.
- Return type:
- disnake_compass.impl.manager.check_manager(name)[source]¶
Check if a manager with the provided name exists.
Note
Unlike
get_manager(), this function will not create a manager if the provided name does not exist.
Classes¶
- defadd_to_client
- defas_callback_wrapper
- defas_dependency_provider
- defas_exception_handler
- defconfig
- defderegister_component
- defget_identifier
- asyncinvoke_component
- deflookup_identifier
- defmake_button
- asyncmake_custom_id
- defmake_identifier
- defmake_select
- asyncparse_message_components
- asyncparse_raw_component
- defregister
- defregister_component
- defremove_from_client
- asyncupdate_layout
- class disnake_compass.impl.manager.ComponentManager(name, *, count=None, sep=None, client=None)[source]¶
Bases:
ComponentManagerThe standard implementation of a component manager.
Component managers keep track of disnake-compass’ special components and ensure they smoothly communicate with disnake’s clients.
To register a component to a component manager, use
register(). Without registering your components, they will remain unresponsive.To get an instance of a component manager, use
get_manager(). This will automatically create missing managers if needed, much likelogging.getLogger(). Similarly, managers feature a parent-child hierarchy in the same way loggers do. For example, a manager named “foo.bar” would be a child of the manager named “foo”.The topmost manager will always be the root manager, which can be acquired through calling
get_manager()without passing a name.When a component is invoked on - for example - a manager “foo.bar”, it will wrap the callback in the
as_callback_wrapper()wrappers bubbling up. That is, the callback is wrapped by the root manager, then “foo”, then “foo.bar”, and finally invoke the callback.If any exceptions occur during the wrapping or invocation of the callback, the managers’ exception handlers will be invoked starting from “foo.bar”, then “foo”, and finally the root manager. If any exception handler returns
True, the exception is considered handled and any remaining exception handlers are skipped.- Parameters:
name (
str) – The name of the component manager. This should be unique for all live component managers.Whether the component manager should insert one count character to resolve duplicates. Normally, sending two components with the same custom id would error. Enabling this ensures custom ids are unique by appending an incrementing character. This costs 1 character, effectively reducing the maximum custom id length to 99 characters.
If not set, the manager will use its parents’ settings. The default set on the root manager is
True.The character(s) to use as separator between custom id parts.
If not set, the manager will use its parents’ settings. The default set on the root manager is
"|".client (
Client|None) – The client to which to register this manager. This can be specified at any point throughadd_to_client().
Attributes¶
- property children: set[ComponentManager][source]¶
The children of this component manager.
- property client: Client[source]¶
The client to which this manager is registered.
If the manager has not yet been registered, this raises an exception.
Note
This is recursively accessed for all the parents of this manager. For example, if
get_manager().clientis set, then any of its childrenget_manager("foo.bar").clientwill also return that same client instance.It is therefore generally recommended to set the client on the root manager so that all other managers automatically have access to it.
- property components: Mapping[str, type[RichComponent]][source]¶
The components registered to this manager or any of its children.
In case a custom implementation is made, special care must be taken to ensure that these do not desync when a child’s components are updated.
- property count: bool[source]¶
Whether or not this manager should add a count character to custom ids.
This prevents an error when two components with otherwise equal custom ids are sent.
By default, this is set to
True. This can be changed usingconfig().Note
This is recursively checked for all the parents of this manager. For example, if
get_manager("foo").count == True, then its childget_manager("foo.bar").countwill also returnTrueunless explicitly set toFalse.Warning
As this takes 1 character, the effective maximum custom id length is reduced to 99 characters.
- property name: str[source]¶
The name of this manager.
Used in
get_manager(). This functions similar to loggers, where a parent-child relationship is denoted with a “.”. For example, a manager “foo.bar” has parent “foo”, which has the root manager as parent.
- property parent: ComponentManager | None[source]¶
The parent of this manager.
Returns
Nonein case this is the root manager.
- property sep: str[source]¶
The separator used to delimit parts of the custom ids of this manager.
By default, this is set to “|”. This can be changed using
config().Note
This is recursively accessed for all the parents of this manager. For example, if
get_manager("foo").sep == "|", then its childget_manager("foo.bar").sepwill also return"|"unless explicitly set to some other value.
Methods¶
- add_to_client(client, /)[source]¶
Register this manager to the provided client.
This is required to make components registered to this manager responsive.
This method registers the
invoke()callback as an event to the client for thedisnake.on_message_interactionanddisnake.on_modal_submitevents.Note
There is no need to separately register every manager you make. In general, it is sufficient to only register the root manager as the root manager will contain all components of its children. That is, the root manager contains all registered components, as every other manager is a child of the root manager.
- Parameters:
client (
Client) – The client to which to register this manager.- Raises:
RuntimeError – This manager has already been registered to the provided client.
- as_callback_wrapper(func, /)[source]¶
Register a callback as this managers’ callback wrapper.
By default, this is essentially a no-op.
A callback wrapper MUST be an async function with ONE yield statement. - Any code before the yield statement is run before the component callback is invoked, - The component is invoked at the yield statement, - Any code after the yield statement is run after the component callback is invoked. This can be used for cleanup.
It is therefore also possible to use context managers over the yield statement, to automatically handle resource management.
In case this manager has a parent manager, the parent’s callback wrapper will be used first, starting all the way at the root manager. For example, on a manager named “foo.bar”, the callback will first be wrapped by the root manager, then by “foo”, then by “foo.bar”, and only then will the component callback finally be invoked.
Note that any exceptions raised in any callback wrapper will cancel any other active callback wrappers and propagate the exception to the manager’s error handler.
Examples
manager = get_manager() @manager.as_callback_wrapper async def wrapper(component, interaction): print(f"User {inter.author.name} invoked {type(component).__name__}.) yield print(f"Successfully ran callback for {type(component).__name__}.)
- Parameters:
func (
CallbackWrapperFuncT) – The callback to register. This must be an async function that takes the component manager as the first argument, the component as the second argument, and the interaction as the last. The function must have a singleyield-statement that yieldsNone.- Returns:
The function that was just registered.
- Return type:
Callable[[
RichComponent,disnake.Interaction[disnake.Client]], AsyncGenerator[None, None]]
- as_dependency_provider(func, /)[source]¶
Register a callback as this manager’s dependency provider.
By default, this registers everything passed as a dependency.
A dependency provider MUST be an async function with ONE yield statement. - Any code before the yield statement is run before the interaction is parsed, - The interaction is parsed at the yield statement, - Any code after the yield statement is run after the interaction has been parsed. This can be used for cleanup.
It is therefore also possible to use context managers over the yield statement, to automatically handle resource management.
As this runs before we know which manager a component belongs to (if at all!), this is run only for the manager(s) registered to a client with
add_to_client().Examples
manager = get_manager() @manager.as_dependency_provider async def provider(manager, *dependencies): tokens = di.register_dependencies(*dependencies) yield di.reset_dependencies(tokens)
- Parameters:
func (
DependencyProviderFuncT) – The callback to register. This must be an async function that takes the component manager as the first argument, and any number of dependencies after. The function must have a singleyieldstatement that yieldsNone.- Returns:
The function that was just registered.
- Return type:
Callable[[
ComponentManager, …], AsyncGenerator[None, None]]
- as_exception_handler(func, /)[source]¶
Register a callback as this managers’ error handler.
By default, this simply logs the exception and keeps it from propagating.
An error handler should return a boolean or
None: -Trueif the error was successfully handled and should not be propagated further. -FalseorNoneif the error was not successfully handled and should be passed to the next error handler in line.Note that it is therefore also possible to use context managers over the yield statement.
In case this manager has a parent manager, the parent’s error handler will be used if this one returns
FalseorNone. For example, of a manager named “foo.bar”, any exceptions will first be handled by “foo.bar”, if that fails it will be handled by “foo”, and finally if that also fails it will be handled by the root handler.Examples
manager = get_manager() @manager.as_exception_handler async def wrapper(component, interaction, exception): if isinstance(exception, TypeError): return True # Silently ignore any TypeErrors return False # Propagate all other errors.
- Parameters:
func (
ExceptionHandlerFuncT) – The callback to register. This must be an async function that takes the component manager as the first argument, the component as the second, the interaction as the third, and the exception as the last. The function must returnTrueto indicate that the error was handled successfully, or eitherFalseorNoneto indicate the opposite.- Returns:
The function that was just registered.
- Return type:
Callable[[
RichComponent,disnake.Interaction[disnake.Client],Exception], None]
- config(*, count=OmittedType.Omitted, sep=OmittedType.Omitted)[source]¶
Set configuration options on this manager.
- deregister_component(identifier, /)[source]¶
Deregister a component from this component manager.
After deregistration, the component will no be tracked, and its callbacks can no longer fire until it is re-registered.
- Parameters:
identifier (
str) – The identifier of the component class to deregister.- Returns:
The component class that was just deregistered.
- Return type:
- get_identifier(custom_id, /)[source]¶
Extract the identifier and parameters from a custom id.
This is used to check whether the identifier is registered in
components.- Parameters:
custom_id (
str) – The custom id from which to extract the identifier.
- await invoke_component(interaction, /, *, with_di=True)[source]¶
Try to invoke a component with the given interaction.
If this manager has no registered component that matches the interaction, it is silently ignored. Otherwise, the interaction will be parsed into a fully fledged component, and its callback will then be invoked.
- Parameters:
interaction (
MessageInteraction[Client]) – The interaction with which to try to invoke a component callback.
- lookup_identifier(component_type, /)[source]¶
Look up the identifier of an already registered component type.
- Parameters:
component_type (
type[RichComponent]) – The component type for which to look up the registered identifier.
- make_button(identifier, *, as_root=True, label=OmittedType.Omitted, style=OmittedType.Omitted, emoji=OmittedType.Omitted, disabled=OmittedType.Omitted, **kwargs)[source]¶
Make an instance of the button class with the provided identifier.
- Parameters:
as_root (
bool) – Whether to use the root manager to get the component. This defaults toTrueso that any externally registered button can be built.identifier (
str) – The identifier of the button that is to be instantiated.label (
OmittedType|str|None) – The label to use. If not provided, uses the button class’ default.style (disnake.ButtonStyle) – The style to use. If not provided, uses the button class’ default.
emoji (
OmittedType|str|PartialEmoji|Emoji|None) – The emoji to use. If not provided, uses the button class’ default.disabled (
OmittedType|bool) – Whether or not to disable the button. If not provided, uses the button class’ default.**kwargs (
object) – Any remaining keyword arguments are passed to the button’s__init__.
- Returns:
The newly created button.
- Return type:
disnake_compass.api.RichButton- Raises:
- await make_custom_id(component, /)[source]¶
Make a custom id from the provided component.
This can then be used later to reconstruct the component without any state or data loss.
- Parameters:
component (
RichComponent) – The component for which to create a custom id.- Returns:
A custom id that fully represents the provided component.
- Return type:
- make_identifier(component_type, /)[source]¶
Make an identifier for the provided component class.
This is used to store the component in
components, and to determine which component’s callback should be fired when an interaction is received.- Parameters:
component_type (
type[RichComponent]) – The type of component for which to make an identifier.- Returns:
The component type’s identifier.
- Return type:
- make_select(identifier, *, as_root=True, placeholder=OmittedType.Omitted, min_values=OmittedType.Omitted, max_values=OmittedType.Omitted, disabled=OmittedType.Omitted, options=OmittedType.Omitted, **kwargs)[source]¶
Make an instance of the string select class with the provided identifier.
- Parameters:
as_root (
bool) – Whether to use the root manager to get the component. This defaults toTrueso that any externally registered select can be built.identifier (
str) – The identifier of the button that is to be instantiated.placeholder (
OmittedType|str|None) – The placeholder to use. If not provided, uses the select class’ default.min_values (
OmittedType|int) – The minimum number of values a user is allowed to select. If not provided, uses the select class’ default.max_values (
OmittedType|int) – The maximum number of values a user is allowed to select. If not provided, uses the select class’ default.disabled (
OmittedType|bool) – Whether or not to disable the button. If not provided, uses the select class’ default.options (
OmittedType|list[SelectOption]) – The options to use. If not provided, uses the select class’ default.**kwargs (
object) – Any remaining keyword arguments are passed to the select’s__init__.
- Returns:
The newly created string select.
- Return type:
disnake_compass.api.RichStringSelect- Raises:
- await parse_message_components(components)[source]¶
Parse all components on a message into a layout of ui components and a sequence of rich components.
This method takes a sequence of components such as that returned by
disnake.Message.components, and converts them into disnake UI components for re-sending, maintaining the same layout. It then also converts every possible component into a registered rich component so you have full power over editing them.Tip
This method is particularly useful if you wish to modify multiple components attached to a message. After your modifications, the output of this function can be passed into
update_layout()to update the layout with the changes you made to your rich components. This can then be passed to thecomponentsargument of any disnake methods.- Parameters:
components (typing.Sequence[disnake.components.MessageTopLevelComponent]) – The message components to parse.
- Returns:
A tuple containing:
The exact component layout that was passed in, except fully
converted into UI components.
A sequence containing only the rich components to make it easier
to modify them.
- Return type:
tuple`[:class:`Sequence`[:class:`Sequence`[:obj:`MessageComponents]],Sequence`[:class:`RichComponent]]
- await parse_raw_component(component, /)[source]¶
Parse a rich message component from a disnake raw component.
Note
This method only works for components registered to this manager.
- Parameters:
component (
Button|BaseSelectMenu) – The raw message component that is to be turned into a rich component.- Returns:
RichComponent– The newly created component.None– The provided component could not be parsed into a rich component that is registered to this manager.
- register(component_type=None, /, *, identifier=None)[source]¶
Register a component to this component manager.
This is the decorator interface to
register_component().
- register_component(component_type, /, *, identifier=None)[source]¶
Register a component to this component manager.
This returns the provided class, such that this method can serve as a decorator.
- Parameters:
- Returns:
The component class that was just registered.
- Return type:
- remove_from_client(client, /)[source]¶
Deregister this manager from the provided client.
This makes all components registered to this manager unresponsive.
- Parameters:
client (
Client) – The client from which to deregister this manager.- Raises:
RuntimeError – This manager is not registered to the provided client.
- await update_layout(layout, rich_components)[source]¶
Update a component layout in-place with a sequence of rich components.
A component layout can be obtained using
parse_message_components().Warning
Make sure that the manager you use to call this method is aware of all the components you pass through the
rich_componentsargument. Consider using the root manager or similar if this is not something you can easily guarantee.- Parameters:
layout (
Sequence[Section|TextDisplay|MediaGallery|File|Separator|Container]) – A sequence of components such that it is a valid input to disnake’s send and edit methods. This works for both v1 and v2 component layouts (v1 layouts are effectively a subset of v2 layouts).rich_components (
Sequence[RichComponent]) – The rich components to finalise and update the layout with.
- Returns:
A disnake-compatible structure of sendable components.
- Return type:
disnake.ui.Components[disnake.ui.MessageUIComponent]