From 2f3f686a00deb853e5fbd029872f0b10203e4aee Mon Sep 17 00:00:00 2001 From: Ivan Habunek Date: Thu, 14 Dec 2023 10:11:09 +0100 Subject: [PATCH] Rework how app, user are passed to context --- tests/integration/conftest.py | 17 +++++++------- toot/cli/base.py | 43 +++++++++++++++++++++++------------ toot/cli/lists.py | 14 ++++++++---- 3 files changed, 46 insertions(+), 28 deletions(-) diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index c0b14ee..e878b7b 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -24,6 +24,7 @@ from click.testing import CliRunner, Result from pathlib import Path from toot import api, App, User from toot.cli import Context +from toot.cli.base import TootObj def pytest_configure(config): @@ -116,24 +117,24 @@ def runner(): @pytest.fixture def run(app, user, runner): def _run(command, *params, input=None) -> Result: - ctx = Context(app, user) - return runner.invoke(command, params, obj=ctx, input=input) + obj = TootObj(test_ctx=Context(app, user)) + return runner.invoke(command, params, obj=obj, input=input) return _run @pytest.fixture def run_as(app, runner): def _run_as(user, command, *params, input=None) -> Result: - ctx = Context(app, user) - return runner.invoke(command, params, obj=ctx, input=input) + obj = TootObj(test_ctx=Context(app, user)) + return runner.invoke(command, params, obj=obj, input=input) return _run_as @pytest.fixture def run_json(app, user, runner): def _run_json(command, *params): - ctx = Context(app, user) - result = runner.invoke(command, params, obj=ctx) + obj = TootObj(test_ctx=Context(app, user)) + result = runner.invoke(command, params, obj=obj) assert result.exit_code == 0 return json.loads(result.stdout) return _run_json @@ -142,8 +143,8 @@ def run_json(app, user, runner): @pytest.fixture def run_anon(runner): def _run(command, *params) -> Result: - ctx = Context(None, None) - return runner.invoke(command, params, obj=ctx) + obj = TootObj(test_ctx=Context(None, None)) + return runner.invoke(command, params, obj=obj) return _run diff --git a/toot/cli/base.py b/toot/cli/base.py index 4a7362e..71df90f 100644 --- a/toot/cli/base.py +++ b/toot/cli/base.py @@ -64,7 +64,6 @@ CONTEXT = dict( ) -# Data object to add to Click context class Context(t.NamedTuple): app: t.Optional[App] user: t.Optional[User] = None @@ -73,16 +72,39 @@ class Context(t.NamedTuple): quiet: bool = False +class TootObj(t.NamedTuple): + """Data to add to Click context""" + color: bool = True + debug: bool = False + quiet: bool = False + # Pass a context for testing purposes + test_ctx: t.Optional[Context] = None + + def pass_context(f: "t.Callable[te.Concatenate[Context, P], R]") -> "t.Callable[P, R]": - """Pass `obj` from click context as first argument.""" + """Pass the toot Context as first argument.""" @wraps(f) def wrapped(*args: "P.args", **kwargs: "P.kwargs") -> R: - ctx = click.get_current_context() - return f(ctx.obj, *args, **kwargs) + return f(_get_context(), *args, **kwargs) return wrapped +def _get_context() -> Context: + click_context = click.get_current_context() + obj: TootObj = click_context.obj + + # This is used to pass a context for testing, not used in normal usage + if obj.test_ctx: + return obj.test_ctx + + user, app = config.get_active_user_app() + if not user or not app: + raise click.ClickException("This command requires you to be logged in.") + + return Context(app, user, obj.color, obj.debug, obj.quiet) + + json_option = click.option( "--json", is_flag=True, @@ -98,18 +120,9 @@ json_option = click.option( @click.option("--quiet/--no-quiet", default=False, help="Don't print anything to stdout") @click.version_option(__version__, message="%(prog)s v%(version)s") @click.pass_context -def cli( - ctx: click.Context, - max_width: int, - color: bool, - debug: bool, - quiet: bool, - app: t.Optional[App] = None, - user: t.Optional[User] = None, -): +def cli(ctx: click.Context, max_width: int, color: bool, debug: bool, quiet: bool): """Toot is a Mastodon CLI""" - user, app = config.get_active_user_app() - ctx.obj = Context(app, user, color, debug, quiet) + ctx.obj = TootObj(color, debug, quiet) ctx.color = color ctx.max_content_width = max_width diff --git a/toot/cli/lists.py b/toot/cli/lists.py index 0b485c6..7fe30df 100644 --- a/toot/cli/lists.py +++ b/toot/cli/lists.py @@ -1,6 +1,6 @@ import click -from toot import api +from toot import api, config from toot.cli.base import Context, cli, pass_context from toot.output import print_list_accounts, print_lists, print_warning @@ -11,8 +11,12 @@ def lists(ctx: click.Context): """Display and manage lists""" if ctx.invoked_subcommand is None: print_warning("`toot lists` is deprecated in favour of `toot lists list`") - lists = api.get_lists(ctx.obj.app, ctx.obj.user) + user, app = config.get_active_user_app() + if not user or not app: + raise click.ClickException("This command requires you to be logged in.") + + lists = api.get_lists(app, user) if lists: print_lists(lists) else: @@ -20,10 +24,10 @@ def lists(ctx: click.Context): @lists.command() -@click.pass_context -def list(ctx: click.Context): +@pass_context +def list(ctx: Context): """List all your lists""" - lists = api.get_lists(ctx.obj.app, ctx.obj.user) + lists = api.get_lists(ctx.app, ctx.user) if lists: print_lists(lists)