Skip to content

feat!: implement support for sub-plugins #13

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 8 commits into
base: master
Choose a base branch
from

Conversation

Sharp-Eyes
Copy link
Member

@Sharp-Eyes Sharp-Eyes commented Oct 11, 2023

This PR adds a multitude of things related to more conveniently splitting functionality over different files. Please refer to the sections below. To make all of this possible, plugins were first refactored by splitting typing and logical utilities into separate files, and extracting some behaviour into separate base classes. Therefore, this is technically a breaking change. However, the existing public API should remain unchanged.
Note that method/function names are not yet final, and may change before this PR is merged.

If you have any feedback or suggestions, please let me know!

Sub-plugins

SubPlugins add support for splitting a plugin across multiple files. On a very basic level, working with sub-plugins is done as follows:

# foo.py

sub_plugin = SubPlugin()

...  # Add commands etc.
# bar.py

import foo

plugin = Plugin()
plugin.register_sub_plugin(foo.sub_plugin)

...  # Add commands etc.

setup, teardown = plugin.create_extension_handlers()

Command Placeholders

Command placeholders allow you to separate a single command over multiple files. For this PR (and the foreseeable future), this feature only supports Slash Commands. If there is sufficient demand to do the same for context commands, I will look into implementing this, too.

For most purposes, using command placeholders is very simple. Say we wish to create a slash command ping in our main plugin file, and use a sub-plugin to define a subcommand pong. This is achieved as follows:

# foo.py

sub_plugin = SubPlugin()

# Register a subcommand to the external "ping" command.
# This returns a command placeholder.
@sub_plugin.external_sub_command("ping")
async def pong(inter: CommandInteraction):
    ...
# bar.py

import foo

plugin = Plugin()
plugin.register_sub_plugin(foo.sub_plugin)

@plugin.slash_command()
async def ping(inter: CommandInteraction):
    ...

setup, teardown = plugin.create_extension_handlers()

The order in which these are defined and loaded does not matter, as long as the sub plugin is loaded before the plugin is loaded into the bot. Command placeholders are merged into their target parent commands when Plugin.merge_placeholders is called, which is done automatically when the plugin is loaded.

To-do

A couple points remain to be done:

  • Implement group unloading.
    If a plugin that has sub-plugins is unloaded, the sub-plugins should be unloaded along with it. At the moment, this is not yet the case.
  • Add an example to the examples directory.
  • Somewhat rigorously test for unexpected behaviour.

Lastly, I am considering implementing a sort of magic function to automatically load all modules in a package, find the sub-plugins inside the module, and register them to the plugin. For example, given a file structure like

module
|- __init__.py
|- foo.py 
|- bar.py

where the main plugin is inside __init__.py and both foo.py and bar.py house a sub-plugin, calling plugin.gather_from_package() (name pending) would automatically look for sub-plugins inside foo.py and bar.py and register them.

@Sharp-Eyes Sharp-Eyes added enhancement New feature or request breaking This is a breaking change. labels Oct 11, 2023
@Sharp-Eyes Sharp-Eyes marked this pull request as draft October 11, 2023 21:22
@elenakrittik
Copy link
Contributor

As far as i understand, with current implementation it is not possible to create arbitrarily-nested sub-plugins (sub-sub-plugins, sub-sub-sub-plugins, etc.)? While this is not something that is absolutely necessary, it will be a good feature to have, for sub-sub-commands especially. I imagine such an implementation will remove the separate SubPlugin class and instead rework "main" Plugin so that it can act as either a "root" or a "sub-" one; due to that, it'll likely have to always use command placeholder classes.

Apart from that, good job overall (haven't done a "real" review yet)! Really looking forward for this to be merged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking This is a breaking change. enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants