3
0
Fork 0

fix: add mechanism to discover external wutta subcommands

for sake of wuttasync, e.g. `wutta import-csv`
This commit is contained in:
Lance Edgar 2024-12-05 18:58:10 -06:00
parent 3a1ea22e9b
commit a9eebc682e
3 changed files with 53 additions and 2 deletions

View file

@ -33,5 +33,9 @@ This (``wuttjamaican.cli``) namespace exposes the following:
from .base import wutta_typer, make_typer
# TODO: is this the best we can do, to register available commands?
# nb. must bring in all modules for discovery to work
from . import make_uuid
# discover more commands, installed via other packages
from .base import typer_eager_imports
typer_eager_imports(wutta_typer)

View file

@ -41,6 +41,7 @@ import typer
from typing_extensions import Annotated
from wuttjamaican.conf import make_config
from wuttjamaican.util import load_entry_points
def make_cli_config(ctx: typer.Context):
@ -84,6 +85,43 @@ def typer_callback(
ctx.wutta_config = make_cli_config(ctx)
def typer_eager_imports(
group: [typer.Typer, str]):
"""
Eagerly import all modules which are registered as having
:term:`subcommands <subcommand>` belonging to the given group
(i.e. top-level :term:`command`).
This is used to locate subcommands which may be defined by
multiple different packages. It is mostly needed for the main
``wutta`` command, since e.g. various extension packages may
define additional subcommands for it.
Most custom apps will define their own top-level command and some
subcommands, but will have no need to "discover" additional
subcommands defined elsewhere. Hence you normally would not need
to call this function.
However if you wish to define a ``wutta`` subcommand(s), you
*would* need to register the entry point for your module(s)
containing the subcommand(s) like so (in ``pyproject.toml``):
.. code-block:: ini
[project.entry-points."wutta.typer_imports"]
poser = "poser.commands"
Note that the ``wutta.typer_imports`` above indicates you are
registering a module which defines ``wutta`` subcommands. The
``poser`` name is arbitrary but should match your package name.
:param group: Typer group command, or the name of one.
"""
if isinstance(group, typer.Typer):
group = group.info.name
load_entry_points(f'{group}.typer_imports')
def make_typer(**kwargs):
"""
Create a Typer command instance, per Wutta conventions.

View file

@ -2,7 +2,7 @@
import os
from unittest import TestCase
from unittest.mock import MagicMock
from unittest.mock import MagicMock, patch
import typer
@ -32,6 +32,15 @@ class TestTyperCallback(TestCase):
self.assertEqual(ctx.wutta_config.files_read, [example_conf])
class TestTyperEagerImports(TestCase):
def test_basic(self):
typr = mod.make_typer(name='foobreezy')
with patch.object(mod, 'load_entry_points') as load_entry_points:
mod.typer_eager_imports(typr)
load_entry_points.assert_called_once_with('foobreezy.typer_imports')
class TestMakeTyper(TestCase):
def test_basic(self):