Skip to content

Commit 89577a3

Browse files
authored
Automatic --print_shtab option when shtab is installed, providing completions for many type hints without the need to modify code (#528)
1 parent 890cd45 commit 89577a3

15 files changed

+761
-103
lines changed

CHANGELOG.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ Added
2222
- Allow providing a function with return type a class in ``class_path``
2323
(`lightning#13613
2424
<https://github.com/Lightning-AI/pytorch-lightning/discussions/13613>`__)
25+
- Automatic ``--print_shtab`` option when ``shtab`` is installed, providing
26+
completions for many type hints without the need to modify code (`#528
27+
<https://github.com/omni-us/jsonargparse/pull/528>`__).
2528

2629
Fixed
2730
^^^^^

DOCUMENTATION.rst

Lines changed: 83 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2544,17 +2544,41 @@ function to provide an instance of the parser to :class:`.ActionParser`.
25442544
Tab completion
25452545
==============
25462546

2547-
Tab completion is available for jsonargparse parsers by using the `argcomplete
2548-
<https://pypi.org/project/argcomplete/>`__ package. There is no need to
2549-
implement completer functions or to call :func:`argcomplete.autocomplete` since
2550-
this is done automatically by :py:meth:`.ArgumentParser.parse_args`. The only
2551-
requirement to enable tab completion is to install argcomplete either directly
2552-
or by installing jsonargparse with the ``argcomplete`` extras require as
2553-
explained in section :ref:`installation`. Then the tab completion can be enabled
2554-
`globally <https://kislyuk.github.io/argcomplete/#global-completion>`__ for all
2555-
argcomplete compatible tools or for each `individual
2556-
<https://kislyuk.github.io/argcomplete/#synopsis>`__ tool. A simple
2557-
``example.py`` tool would be:
2547+
Tab completion is available for jsonargparse parsers by using either the `shtab
2548+
<https://pypi.org/project/shtab/>`__ package or the `argcomplete
2549+
<https://pypi.org/project/argcomplete/>`__ package.
2550+
2551+
shtab
2552+
-----
2553+
2554+
For ``shtab`` to work, there is no need to set ``complete``/``choices`` to the
2555+
parser actions, and no need to call :func:`shtab.add_argument_to`. This is done
2556+
automatically by :py:meth:`.ArgumentParser.parse_args`. The only requirement is
2557+
to install shtab either directly or by installing jsonargparse with the
2558+
``shtab`` extras require as explained in section :ref:`installation`.
2559+
2560+
.. note::
2561+
2562+
Automatic shtab support is currently experimental and subject to change.
2563+
2564+
Once ``shtab`` is installed, parsers will automatically have the
2565+
``--print_shtab`` option that can be used to print the completion script for the
2566+
supported shells. For example in linux to enable bash completions for all users,
2567+
as root it would be used as:
2568+
2569+
.. code-block:: bash
2570+
2571+
# example.py --print_shtab=bash > /etc/bash_completion.d/example
2572+
2573+
Without installing, completion scripts can be tested by sourcing or evaluating
2574+
them, for instance:
2575+
2576+
.. code-block:: bash
2577+
2578+
$ eval "$(example.py --print_shtab=bash)"
2579+
2580+
The scripts work both to complete when there are choices, but also gives
2581+
instructions to the user for guidance. Take for example the parser:
25582582

25592583
.. testsetup:: tab_completion
25602584

@@ -2572,18 +2596,60 @@ argcomplete compatible tools or for each `individual
25722596

25732597
parser.parse_args()
25742598

2575-
Then in a bash shell you can add the executable bit to the script, activate tab
2576-
completion and use it as follows:
2599+
The completions print the type of the argument, how many options are matched,
2600+
and afterward the list of choices matched up to that point. If only one option
2601+
matches, then the value is completed without printing guidance. For example:
2602+
2603+
.. code-block:: bash
2604+
2605+
$ example.py --bool <TAB><TAB>
2606+
Expected type: Optional[bool]; 3/3 matched choices
2607+
true false null
2608+
$ example.py --bool f<TAB>
2609+
$ example.py --bool false
2610+
2611+
For the case of subclass types, the import class paths for known subclasses are
2612+
completed, both for the switch to select the class and for the corresponding
2613+
``--*.help`` switch. The ``init_args`` for known subclasses are also completed,
2614+
giving as guidance which of the subclasses accepts it. An example would be:
2615+
2616+
.. code-block:: bash
2617+
2618+
$ example.py --cls <TAB><TAB>
2619+
Expected type: BaseClass; 3/3 matched choices
2620+
some.module.BaseClass other.module.SubclassA
2621+
other.module.SubclassB
2622+
$ example.py --cls other.module.SubclassA --cls.<TAB><TAB>
2623+
--cls.param1 --cls.param2
2624+
$ example.py --cls other.module.SubclassA --cls.param2 <TAB><TAB>
2625+
Expected type: int; Accepted by subclasses: SubclassA
2626+
2627+
argcomplete
2628+
-----------
2629+
2630+
For ``argcompete`` to work, there is no need to implement completer functions or
2631+
to call :func:`argcomplete.autocomplete` since this is done automatically by
2632+
:py:meth:`.ArgumentParser.parse_args`. The only requirement to enable tab
2633+
completion is to install argcomplete either directly or by installing
2634+
jsonargparse with the ``argcomplete`` extras require as explained in section
2635+
:ref:`installation`.
2636+
2637+
The tab completion can be enabled `globally
2638+
<https://kislyuk.github.io/argcomplete/#global-completion>`__ for all
2639+
argcomplete compatible tools or for each `individual
2640+
<https://kislyuk.github.io/argcomplete/#synopsis>`__ tool.
2641+
2642+
Using the same ``bool`` example as shown above, activate tab completion and use
2643+
it as follows:
25772644

25782645
.. code-block:: bash
25792646
2580-
$ chmod +x example.py
25812647
$ eval "$(register-python-argcomplete example.py)"
25822648
2583-
$ ./example.py --bool <TAB><TAB>
2649+
$ example.py --bool <TAB><TAB>
25842650
false null true
2585-
$ ./example.py --bool f<TAB>
2586-
$ ./example.py --bool false
2651+
$ example.py --bool f<TAB>
2652+
$ example.py --bool false
25872653
25882654
25892655
.. _logging:

jsonargparse/_actions.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from ._common import Action, get_class_instantiator, is_subclass, parser_context
1313
from ._loaders_dumpers import get_loader_exceptions, load_value
1414
from ._namespace import Namespace, NSKeyError, split_key, split_key_root
15-
from ._optionals import FilesCompleterMethod, get_config_read_mode
15+
from ._optionals import get_config_read_mode
1616
from ._type_checking import ArgumentParser
1717
from ._util import (
1818
NoneType,
@@ -145,7 +145,7 @@ def filter_default_actions(actions):
145145
return {k: a for k, a in actions.items() if not isinstance(a, default)}
146146

147147

148-
class ActionConfigFile(Action, FilesCompleterMethod):
148+
class ActionConfigFile(Action):
149149
"""Action to indicate that an argument is a configuration file or a configuration string."""
150150

151151
def __init__(self, **kwargs):
@@ -208,6 +208,12 @@ def apply_config(parser, cfg, dest, value) -> None:
208208
cfg[dest] = []
209209
cfg[dest].append(cfg_path)
210210

211+
def completer(self, prefix, **kwargs):
212+
from ._completions import get_files_completer
213+
214+
files_completer = get_files_completer()
215+
return sorted(files_completer(prefix, **kwargs))
216+
211217

212218
previous_config: ContextVar = ContextVar("previous_config", default=None)
213219

0 commit comments

Comments
 (0)