-
Notifications
You must be signed in to change notification settings - Fork 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
Centralized plugin loading that behaves just like click. #3
Conversation
@geowurster 👍 on this design. Yes, it would be good to prevent user plugin commands from colliding with the standard commands, so a new named entry point group makes sense. I'd prefer the CLI stay as flat as possible, ie, Yeah, "cligj" was maybe too cute. Oh, well. Originally, before I dove into Click, there was just a main.py. I left it there as I moved the Click-specific code to cli.py. I'm not aware of any other projects importing those modules (and nobody should). Let's consolidate or rename freely. I can't think of anything better than helpers.py if it's a module of functions only imported from main.py. |
@sgillies agreed. Here's the order of operations:
Sound good? I can tackle all these except for the release and I don't see any blocking issues or PR's. |
@geowurster Good plan. I'll create the issues for Fiona (milestone 1.6) and Rasterio (1.0). Ping me when it's merge ready and I'll do a last review and merge. I'll ask the same of you on my PRs. Okay? |
Yep, absolutely. |
…oken command icon from the name to the help message to prevent modifying the actual command name.
…a python module, which is required for manually registering and testing plugins.
@sgillies ready to go. |
Centralized plugin loading that behaves just like click.
👍 |
For #2
@sgillies I figured out how to simplify the API to the point where it behaves just like click normally does. I think this is actually something click should be able to handle natively so we should consider submitting a pull request, although their release cycle is kinda slow we will probably have to use the cligj implementation for a while. I'll add unittests after you review.
How does it work?
There's a full explanation of how this works in
cligj.plugins.__doc__
but in general it works by subclassing and intercepting click components, executing the entry point loading logic, attaching commands, broken commands, or groups, and then calling the equivalent click command. Here's a CLI interface that has a main group, a sub command, and loaded plugins:Extra gains
By doing this with decorators and subclassing
click.Group()
, commands and plugins are completely modular and can be moved from group to group in any click based application. Here's the same example from above but with all of therio
commands attached to aplugins
sub-group that also has its own command:fio & rio changes
I have a version of Fiona on a branch that uses this plugin architecture but I haven't fully looked around to see if there are additional changes that need to happen, but the tests pass, the few commands I tested worked, and breaking plugins works properly.
cli()
on setup causes circular imports so all commands must be decorated asclick.command()
.main.py
that is imported into one of the command files must be moved elsewhere. I dumped them in ahelper.py
in the branch but that may not be the best location.main.py
should exist only to provide an isolated entry point to the main cli group.cli.py
should be where all commands are aggregated for entry point access.Actions
rasterio.rio_commands
and users are instructed to register their plugins in the same place. We will likely be saved some headaches later ifrasterio.rio_commands
is reserved for the officialrio
commands and users are instructed to register their plugins atrasterio.rio_plugins
.cligj.plugins.group()
should be changed to take a string or tuple in that case.cligj.plugins
may help.cli.py
andmain.py
have an intended use? Since the individual click commands can't be attached to the main group maybe we want to have acommands.py
where we aggregate?main.py
? In the branch I just created ahelper.py
file to get them out of the way.