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 using ComponentManager.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:

ComponentManager

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.

Parameters:

name (str) – The name to check.

Returns:

Whether a manager with the provided name exists.

Return type:

bool

Classes

class disnake_compass.impl.manager.ComponentManager(name, *, count=None, sep=None, client=None)[source]

Bases: ComponentManager

The 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 like logging.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.

  • count (bool | None) –

    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.

  • sep (str | None) –

    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 through add_to_client().

Attributes

property bot: Client[source]

An alias of client.

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().client is set, then any of its children get_manager("foo.bar").client will 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 using config().

Note

This is recursively checked for all the parents of this manager. For example, if get_manager("foo").count == True, then its child get_manager("foo.bar").count will also return True unless explicitly set to False.

Warning

As this takes 1 character, the effective maximum custom id length is reduced to 99 characters.

property is_root: bool[source]

Whether this manager is the root manager.

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 None in 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 child get_manager("foo.bar").sep will 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 the disnake.on_message_interaction and disnake.on_modal_submit events.

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 single yield-statement that yields None.

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 single yield statement that yields None.

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: - True if the error was successfully handled and should not be propagated further. - False or None if 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 False or None. 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 return True to indicate that the error was handled successfully, or either False or None to 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:

type[RichComponent]

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 to True so 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:
  • KeyError – The provided identifier does not belong to a registered component.

  • TypeError – The provided identifier belongs to a component that is not a button.

  • Exception – Any exception raised during button instantiation is propagated as-is.

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:

str

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:

str

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 to True so 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:
  • KeyError – The provided identifier does not belong to a registered component.

  • TypeError – The provided identifier belongs to a component that is not a string select.

  • Exception – Any exception raised during button instantiation is propagated as-is.

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 the components argument 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:
  • component_type (type[RichComponentT]) – The component class to register.

  • identifier (str | None) – The identifier under which to register this component class. This should be unique across all components.

Returns:

The component class that was just registered.

Return type:

type[ComponentT]

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_components argument. Consider using the root manager or similar if this is not something you can easily guarantee.

Parameters:
Returns:

A disnake-compatible structure of sendable components.

Return type:

disnake.ui.Components[disnake.ui.MessageUIComponent]