Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions fire/completion.py
Original file line number Diff line number Diff line change
Expand Up @@ -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('_', '-')
Expand Down
69 changes: 69 additions & 0 deletions repro_issue_382.py
Original file line number Diff line number Diff line change
@@ -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()