-
-
Notifications
You must be signed in to change notification settings - Fork 313
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
Example: How to create and register a subliminal provider extension. #1177
Comments
Thanks for bringing this up :) About the documentation, the Thish doc section explains how to write a provider but not what to do after:
Anyway, it would be great to add the information you give to the docs actually (if you have time for a PR :)) |
I'd be willing to submit a PR once I have my provider working. My own take on this is that adding information to the docs about extensions might be a touch premature. The cli module currently has no support for passing arguments to a provider. Getting args passed from the cli to my provider required some hackery: import click
from click import get_current_context
from subliminal.cli import (
providers_config,
)
from subliminal import cli
# pull out the function wrapped by the command and other decorators.
# Remember to unreverse the params before reattaching them to the
# replacement command.
# Fore sure breaking the original command group in some way here.
# There might be a way to reuse the existing group.
@click.group(
name=cli.subliminal.name,
params=list(reversed(cli.subliminal.params)),
context_settings=cli.subliminal.context_settings,
epilog=cli.subliminal.epilog,
)
# this ends up creating a whole new option group in the generated docs
# for some reason
@providers_config.option(
'--jimaku',
type=click.STRING,
nargs=2,
metavar='JIMAKU_APIKEY',
help='Jimaku API key.',
)
def root_cmd_wraper(*args, **kwargs):
restore_kwargs = {**kwargs}
jimaku_args = restore_kwargs.pop('jimaku', None)
if jimaku_args:
# setup provider config here
pass
ctx = get_current_context()
cli.subliminal.callback.__wrapped__(
ctx, *args, **restore_kwargs
)
# monkey patch subliminal
cli.subliminal = root_cmd_wraper Do we really want to add something like: "to pass args to your provider, reverse engineer click and monkey patch core" to the official docs? The extensions part of the docs being a stub is a stern, albeit indirect, indication that they're not fully developed to anyone thinking of writing one. Were there to be a guide on how to write an extension and then omit how to pass args with the cli, that feels like I'd be dead ending any hopeful developers. Maybe a new section of the docs like 'experimental', 'only if you dare' or a clear demarcation at the top of the extensions section would help? I hate to be clobbering github issues with non tickets like this. For the time being, this is the only place we have to collaborate on stuff. Would you mind keeping this one open? |
You cannot pass arguments to the providers directly from the CLI (I am still thinking what would be the best way to do it) but you can pass them through the configuration file: Create a section The documentation lacking clarity is enough reason to open an issue ;) We also opened a Discord channel: |
I've collected all the hacks I did to get my provider to actually run, check this link for details. There is one additional problem that is not addressed by #1180 making it impossible for providers from separate packages to work. Even though provider entry points are discovered by the extension manager, they are not passed to the AsyncProviderPool. Extensions have to be 'registered' yet again using by calling the register() method on the extension manager before they are actually used. I monkey patched click.option() to force the registration. Focus on this part: provider_manager.register('jimaku = sublimaku:JimakuProvider') Within this code: _option = click.option
def option(*args, **kwargs):
global provider_group
# force the download subcommand to use make the jimaku provider available
if '--provider' in args:
provider_manager = globals().get('provider_manager')
if provider_manager is None:
from subliminal import provider_manager
provider_manager.register('jimaku = sublimaku:JimakuProvider')
kwargs['type'] = click.Choice(sorted(provider_manager.names()))
# save a copy of the providers_config group so we can add our own
# options to it later
if provider_group is None and (group := kwargs.get('group')):
if isinstance(group, OptionGroup) and group.name == 'Providers configuration':
provider_group = group
return _option(*args, **kwargs)
click.option = option I'd argue that requiring a call on extension_manager.register() isn't adding any value, nor is it adding protection, as evidenced by the above code. Keeping core extensions separate from others would certainly come to have some merit in the future, but going through all the work of adding cross package discoverability and then barricading it off seems a little bit odd. |
I just tried creating a simple package with this entry-point in its
Then I install the package (in the same virtualenv as subliminal) and it is recognized by subliminal, it appears in the list of authorized providers (also with #1180, the arguments of mytestproviders are listed in the help):
I used https://pypi.org/project/entry-point-inspector/ to check that the entry-point was installed:
|
You don't need to use these hacks, see this example. To pass arguments, just add this section to your
Then with #1180 you will be able to pass arguments through the command line. |
If you are like me, you want to make your own provider but you don't want to fork or wrap subliminal.
For anyone wondering how to make their own provider 'extension', you may have noticed that documentation for this is a placeholder:
https://subliminal.readthedocs.io/en/latest/api/extensions.html
**Extensions Extension managers for the providers, refiners and language converters.
But don't fret, after many trials and tribulations I have figured out how to perform the magical incantations necessary to make an extension. Let my own suffering be your salvation and read on...
All you have to do is make an entry in your pyproject.toml like so:
In this example, your package name is
ihatestevedore
and it has a module nameddummyprovider
containing your provider class definition. The entry name that you assign (left side of the '=') is arbitrary and can be whatever you want. You can then create your package layout like so:Where you implement the
DummyProvider
class in the dummyprovider module:Here is what a complete project.toml might look like:
After installing your package, there should be an
entry_points.txt
within its home in the site-packages directory:It should look like:
Now your extension should be available for discovery by subliminal and you can follow the rest of the docs and examples concerning making your own provider.
More reading on how this works:
The text was updated successfully, but these errors were encountered: