From 99981527a2450b52d1df84809bb73db8a3f311e0 Mon Sep 17 00:00:00 2001 From: ShivamTiwari3 Date: Mon, 9 Mar 2026 00:31:59 +0530 Subject: [PATCH] Fix completion suggesting --self for class-grouped commands --- fire/completion.py | 8 ++++++ repro_issue_382.py | 69 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 repro_issue_382.py diff --git a/fire/completion.py b/fire/completion.py index 1597d464..87cbcd86 100644 --- a/fire/completion.py +++ b/fire/completion.py @@ -379,6 +379,14 @@ def _CompletionsFromArgs(fn_args): Returns: A list of possible completion strings for that function. """ + # Completion should not suggest implicit bound args. + # + # When generating completions, Fire may inspect unbound methods (functions + # retrieved from a class). These will include "self" or "cls" in the argspec, + # even though users can never pass those via the CLI. + if fn_args and fn_args[0] in ('self', 'cls'): + fn_args = fn_args[1:] + completions = [] for arg in fn_args: arg = arg.replace('_', '-') diff --git a/repro_issue_382.py b/repro_issue_382.py new file mode 100644 index 00000000..e7f930dc --- /dev/null +++ b/repro_issue_382.py @@ -0,0 +1,69 @@ +import os +import sys + +# Use local Fire without installing. +sys.path.insert(0, os.path.dirname(__file__)) + +# Fire depends on termcolor for pretty output; to keep this repro standalone +# in minimal environments, provide a tiny stub. +try: + import termcolor # type: ignore +except ModuleNotFoundError: # pragma: no cover + import types + + termcolor = types.SimpleNamespace(colored=lambda s, *args, **kwargs: s) + sys.modules["termcolor"] = termcolor + +import fire + + +class Foo: + def bar(self): + return "bar" + + def do_stuff(self, arg1): + return f"stuff {arg1}" + + +def main() -> None: + # Enable completion output. + # In Fire, `-- --completion` is passed after the Fire arguments separator. + # We'll call Fire in a way that simulates CLI invocation. + # + # The bug report says that completion output includes a `--self` flag, which should + # never be suggested to the user. + argv = [ + "prog", + "--", + "--completion", + ] + + # Fire uses sys.argv. + old_argv = sys.argv + try: + sys.argv = argv + # Fire writes completion script to stdout. Capture by redirecting. + from io import StringIO + import contextlib + + buf = StringIO() + with contextlib.redirect_stdout(buf): + try: + fire.Fire(Foo) + except SystemExit as e: + # Fire may exit after printing completion. + if e.code not in (0, None): + raise + + out = buf.getvalue() + finally: + sys.argv = old_argv + + assert "--self" not in out, ( + "Completion output must not include the implicit 'self' parameter as a flag.\n" + f"Output was:\n{out[:2000]}" + ) + + +if __name__ == "__main__": + main()