Skip to content

Commit 2aecb2d

Browse files
authored
Make distinct sections for advanced patterns and extending click. (#2925)
sections added: - extending click - click concepts sections moved: - custom groups (advanced patterns -> extending click) - command aliases (advanced patterns -> extending click) - callback evaluation order (advanced patterns -> click concepts) - global context access (advanced patterns -> basic commands, groups and context) - detecting source parameters (advanced patterns -> basic commands, groups and context)
2 parents 937c68d + ab5cd75 commit 2aecb2d

File tree

6 files changed

+298
-269
lines changed

6 files changed

+298
-269
lines changed

docs/advanced.rst

Lines changed: 2 additions & 257 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,10 @@ Advanced Patterns
33

44
.. currentmodule:: click
55

6-
In addition to common functionality that is implemented in the library
7-
itself, there are countless patterns that can be implemented by extending
8-
Click. This page should give some insight into what can be accomplished.
6+
In addition to common functionality, Click offers some advanced features.
97

108
.. contents::
11-
:depth: 2
9+
:depth: 1
1210
:local:
1311

1412
Callbacks and Eager Options
@@ -110,133 +108,6 @@ also contain the parameter name.
110108
println()
111109
invoke(roll, input=["42", "2d12"])
112110

113-
.. _custom-groups:
114-
115-
Custom Groups
116-
-------------
117-
118-
You can customize the behavior of a group beyond the arguments it accepts by
119-
subclassing :class:`click.Group`.
120-
121-
The most common methods to override are :meth:`~click.Group.get_command` and
122-
:meth:`~click.Group.list_commands`.
123-
124-
The following example implements a basic plugin system that loads commands from
125-
Python files in a folder. The command is lazily loaded to avoid slow startup.
126-
127-
.. code-block:: python
128-
129-
import importlib.util
130-
import os
131-
import click
132-
133-
class PluginGroup(click.Group):
134-
def __init__(self, name=None, plugin_folder="commands", **kwargs):
135-
super().__init__(name=name, **kwargs)
136-
self.plugin_folder = plugin_folder
137-
138-
def list_commands(self, ctx):
139-
rv = []
140-
141-
for filename in os.listdir(self.plugin_folder):
142-
if filename.endswith(".py"):
143-
rv.append(filename[:-3])
144-
145-
rv.sort()
146-
return rv
147-
148-
def get_command(self, ctx, name):
149-
path = os.path.join(self.plugin_folder, f"{name}.py")
150-
spec = importlib.util.spec_from_file_location(name, path)
151-
module = importlib.util.module_from_spec(spec)
152-
spec.loader.exec_module(module)
153-
return module.cli
154-
155-
cli = PluginGroup(
156-
plugin_folder=os.path.join(os.path.dirname(__file__), "commands")
157-
)
158-
159-
if __name__ == "__main__":
160-
cli()
161-
162-
Custom classes can also be used with decorators:
163-
164-
.. code-block:: python
165-
166-
@click.group(
167-
cls=PluginGroup,
168-
plugin_folder=os.path.join(os.path.dirname(__file__), "commands")
169-
)
170-
def cli():
171-
pass
172-
173-
.. _aliases:
174-
175-
Command Aliases
176-
---------------
177-
178-
Many tools support aliases for commands. For example, you can configure
179-
``git`` to accept ``git ci`` as alias for ``git commit``. Other tools also
180-
support auto-discovery for aliases by automatically shortening them.
181-
182-
It's possible to customize :class:`Group` to provide this functionality. As
183-
explained in :ref:`custom-groups`, a group provides two methods:
184-
:meth:`~Group.list_commands` and :meth:`~Group.get_command`. In this particular
185-
case, you only need to override the latter as you generally don't want to
186-
enumerate the aliases on the help page in order to avoid confusion.
187-
188-
The following example implements a subclass of :class:`Group` that accepts a
189-
prefix for a command. If there was a command called ``push``, it would accept
190-
``pus`` as an alias (so long as it was unique):
191-
192-
.. click:example::
193-
194-
class AliasedGroup(click.Group):
195-
def get_command(self, ctx, cmd_name):
196-
rv = super().get_command(ctx, cmd_name)
197-
198-
if rv is not None:
199-
return rv
200-
201-
matches = [
202-
x for x in self.list_commands(ctx)
203-
if x.startswith(cmd_name)
204-
]
205-
206-
if not matches:
207-
return None
208-
209-
if len(matches) == 1:
210-
return click.Group.get_command(self, ctx, matches[0])
211-
212-
ctx.fail(f"Too many matches: {', '.join(sorted(matches))}")
213-
214-
def resolve_command(self, ctx, args):
215-
# always return the full command name
216-
_, cmd, args = super().resolve_command(ctx, args)
217-
return cmd.name, cmd, args
218-
219-
It can be used like this:
220-
221-
.. click:example::
222-
223-
@click.group(cls=AliasedGroup)
224-
def cli():
225-
pass
226-
227-
@cli.command
228-
def push():
229-
pass
230-
231-
@cli.command
232-
def pop():
233-
pass
234-
235-
See the `alias example`_ in Click's repository for another example.
236-
237-
.. _alias example: https://github.com/pallets/click/tree/main/examples/aliases
238-
239-
240111
Parameter Modifications
241112
-----------------------
242113

@@ -360,64 +231,6 @@ And what it looks like:
360231
invoke(cli, prog_name='cli', args=['dist'])
361232

362233

363-
.. _callback-evaluation-order:
364-
365-
Callback Evaluation Order
366-
-------------------------
367-
368-
Click works a bit differently than some other command line parsers in that
369-
it attempts to reconcile the order of arguments as defined by the
370-
programmer with the order of arguments as defined by the user before
371-
invoking any callbacks.
372-
373-
This is an important concept to understand when porting complex
374-
patterns to Click from optparse or other systems. A parameter
375-
callback invocation in optparse happens as part of the parsing step,
376-
whereas a callback invocation in Click happens after the parsing.
377-
378-
The main difference is that in optparse, callbacks are invoked with the raw
379-
value as it happens, whereas a callback in Click is invoked after the
380-
value has been fully converted.
381-
382-
Generally, the order of invocation is driven by the order in which the user
383-
provides the arguments to the script; if there is an option called ``--foo``
384-
and an option called ``--bar`` and the user calls it as ``--bar
385-
--foo``, then the callback for ``bar`` will fire before the one for ``foo``.
386-
387-
There are three exceptions to this rule which are important to know:
388-
389-
Eagerness:
390-
An option can be set to be "eager". All eager parameters are
391-
evaluated before all non-eager parameters, but again in the order as
392-
they were provided on the command line by the user.
393-
394-
This is important for parameters that execute and exit like ``--help``
395-
and ``--version``. Both are eager parameters, but whatever parameter
396-
comes first on the command line will win and exit the program.
397-
398-
Repeated parameters:
399-
If an option or argument is split up on the command line into multiple
400-
places because it is repeated -- for instance, ``--exclude foo --include
401-
baz --exclude bar`` -- the callback will fire based on the position of
402-
the first option. In this case, the callback will fire for
403-
``exclude`` and it will be passed both options (``foo`` and
404-
``bar``), then the callback for ``include`` will fire with ``baz``
405-
only.
406-
407-
Note that even if a parameter does not allow multiple versions, Click
408-
will still accept the position of the first, but it will ignore every
409-
value except the last. The reason for this is to allow composability
410-
through shell aliases that set defaults.
411-
412-
Missing parameters:
413-
If a parameter is not defined on the command line, the callback will
414-
still fire. This is different from how it works in optparse where
415-
undefined values do not fire the callback. Missing parameters fire
416-
their callbacks at the very end which makes it possible for them to
417-
default to values from a parameter that came before.
418-
419-
Most of the time you do not need to be concerned about any of this,
420-
but it is important to know how it works for some advanced cases.
421234

422235
.. _forwarding-unknown-options:
423236

@@ -515,74 +328,6 @@ everything below a subcommand be forwarded to another application than to
515328
handle some arguments yourself.
516329

517330

518-
Global Context Access
519-
---------------------
520-
521-
.. versionadded:: 5.0
522-
523-
Starting with Click 5.0 it is possible to access the current context from
524-
anywhere within the same thread through the use of the
525-
:func:`get_current_context` function which returns it. This is primarily
526-
useful for accessing the context bound object as well as some flags that
527-
are stored on it to customize the runtime behavior. For instance the
528-
:func:`echo` function does this to infer the default value of the `color`
529-
flag.
530-
531-
Example usage::
532-
533-
def get_current_command_name():
534-
return click.get_current_context().info_name
535-
536-
It should be noted that this only works within the current thread. If you
537-
spawn additional threads then those threads will not have the ability to
538-
refer to the current context. If you want to give another thread the
539-
ability to refer to this context you need to use the context within the
540-
thread as a context manager::
541-
542-
def spawn_thread(ctx, func):
543-
def wrapper():
544-
with ctx:
545-
func()
546-
t = threading.Thread(target=wrapper)
547-
t.start()
548-
return t
549-
550-
Now the thread function can access the context like the main thread would
551-
do. However if you do use this for threading you need to be very careful
552-
as the vast majority of the context is not thread safe! You are only
553-
allowed to read from the context, but not to perform any modifications on
554-
it.
555-
556-
557-
Detecting the Source of a Parameter
558-
-----------------------------------
559-
560-
In some situations it's helpful to understand whether or not an option
561-
or parameter came from the command line, the environment, the default
562-
value, or :attr:`Context.default_map`. The
563-
:meth:`Context.get_parameter_source` method can be used to find this
564-
out. It will return a member of the :class:`~click.core.ParameterSource`
565-
enum.
566-
567-
.. click:example::
568-
569-
@click.command()
570-
@click.argument('port', nargs=1, default=8080, envvar="PORT")
571-
@click.pass_context
572-
def cli(ctx, port):
573-
source = ctx.get_parameter_source("port")
574-
click.echo(f"Port came from {source.name}")
575-
576-
.. click:run::
577-
578-
invoke(cli, prog_name='cli', args=['8080'])
579-
println()
580-
invoke(cli, prog_name='cli', args=[], env={"PORT": "8080"})
581-
println()
582-
invoke(cli, prog_name='cli', args=[])
583-
println()
584-
585-
586331
Managing Resources
587332
------------------
588333

docs/click-concepts.rst

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
Click Concepts
2+
================
3+
4+
This section covers concepts about Click's design.
5+
6+
.. contents::
7+
:depth: 1
8+
:local:
9+
10+
.. _callback-evaluation-order:
11+
12+
Callback Evaluation Order
13+
-------------------------
14+
15+
Click works a bit differently than some other command line parsers in that
16+
it attempts to reconcile the order of arguments as defined by the
17+
programmer with the order of arguments as defined by the user before
18+
invoking any callbacks.
19+
20+
This is an important concept to understand when porting complex
21+
patterns to Click from optparse or other systems. A parameter
22+
callback invocation in optparse happens as part of the parsing step,
23+
whereas a callback invocation in Click happens after the parsing.
24+
25+
The main difference is that in optparse, callbacks are invoked with the raw
26+
value as it happens, whereas a callback in Click is invoked after the
27+
value has been fully converted.
28+
29+
Generally, the order of invocation is driven by the order in which the user
30+
provides the arguments to the script; if there is an option called ``--foo``
31+
and an option called ``--bar`` and the user calls it as ``--bar
32+
--foo``, then the callback for ``bar`` will fire before the one for ``foo``.
33+
34+
There are three exceptions to this rule which are important to know:
35+
36+
Eagerness:
37+
An option can be set to be "eager". All eager parameters are
38+
evaluated before all non-eager parameters, but again in the order as
39+
they were provided on the command line by the user.
40+
41+
This is important for parameters that execute and exit like ``--help``
42+
and ``--version``. Both are eager parameters, but whatever parameter
43+
comes first on the command line will win and exit the program.
44+
45+
Repeated parameters:
46+
If an option or argument is split up on the command line into multiple
47+
places because it is repeated -- for instance, ``--exclude foo --include
48+
baz --exclude bar`` -- the callback will fire based on the position of
49+
the first option. In this case, the callback will fire for
50+
``exclude`` and it will be passed both options (``foo`` and
51+
``bar``), then the callback for ``include`` will fire with ``baz``
52+
only.
53+
54+
Note that even if a parameter does not allow multiple versions, Click
55+
will still accept the position of the first, but it will ignore every
56+
value except the last. The reason for this is to allow composability
57+
through shell aliases that set defaults.
58+
59+
Missing parameters:
60+
If a parameter is not defined on the command line, the callback will
61+
still fire. This is different from how it works in optparse where
62+
undefined values do not fire the callback. Missing parameters fire
63+
their callbacks at the very end which makes it possible for them to
64+
default to values from a parameter that came before.
65+
66+
Most of the time you do not need to be concerned about any of this,
67+
but it is important to know how it works for some advanced cases.

0 commit comments

Comments
 (0)