Imported Upstream version 3.3

This commit is contained in:
aviau 2014-10-16 14:40:34 -04:00
commit 83d25aaa27
123 changed files with 14216 additions and 0 deletions

184
CHANGES Normal file
View file

@ -0,0 +1,184 @@
Click Changelog
===============
This contains all major version changes between Click releases.
Version 3.3
-----------
(bugfix release, released on September 8th 2014)
- Fixed an issue with error reporting on Python 3 for invalid forwarding
of commands.
Version 3.2
-----------
(bugfix release, released on August 22nd 2014)
- Added missing `err` parameter forwarding to the `secho` function.
- Fixed default parameters not being handled properly by the context
invoke method. This is a backwards incompatible change if the function
was used improperly. See :ref:`upgrade-to-3.2` for more information.
- Removed the `invoked_subcommands` attribute largely. It is not possible
to provide it to work error free due to how the parsing works so this
API has been deprecated. See :ref:`upgrade-to-3.2` for more information.
- Restored the functionality of `invoked_subcommand` which was broken as
a regression in 3.1.
Version 3.1
-----------
(bugfix release, released on August 13th 2014)
- Fixed a regression that caused contexts of subcommands to be
created before the parent command was invoked which was a
regression from earlier Click versions.
Version 3.0
-----------
(codename "clonk clonk", released on August 12th 2014)
- formatter now no longer attempts to accomodate for terminals
smaller than 50 characters. If that happens it just assumes
a minimal width.
- added a way to not swallow exceptions in the test system.
- added better support for colors with pagers and ways to
override the autodetection.
- the CLI runner's result object now has a traceback attached.
- improved automatic short help detection to work better with
dots that do not terminate sentences.
- when definining options without actual valid option strings
now, Click will give an error message instead of silently
passing. This should catch situations where users wanted to
created arguments instead of options.
- Restructured Click internally to support vendoring.
- Added support for multi command chaining.
- Added support for defaults on options with `multiple` and
options and arguments with `nargs != 1`.
- label passed to `progressbar` is no longer rendered with
whitespace stripped.
- added a way to disable the standalone mode of the `main`
method on a Click command to be able to handle errors better.
- added support for returning values from command callbacks.
- added simplifications for printing to stderr from `echo`.
- added result callbacks for groups.
- entering a context multiple times defers the cleanup until
the last exit occurs.
- added `open_file`.
Version 2.6
-----------
(bugfix release, released on August 11th 2014)
- Fixed an issue where the wrapped streams on Python 3 would be reporting
incorrect values for seekable.
Version 2.5
-----------
(bugfix release, released on July 28th 2014)
- Fixed a bug with text wrapping on Python 3.
Version 2.4
-----------
(bugfix release, released on July 4th 2014)
- Corrected a bug in the change of the help option in 2.3.
Version 2.3
-----------
(bugfix release, released on July 3rd 2014)
- Fixed an incorrectly formatted help record for count options.'
- Add support for ansi code stripping on Windows if colorama
is not available.
- restored the Click 1.0 handling of the help parameter for certain
edge cases.
Version 2.2
-----------
(bugfix release, released on June 26th 2014)
- fixed tty detection on PyPy.
- fixed an issue that progress bars were not rendered when the
context manager was entered.
Version 2.1
-----------
(bugfix release, released on June 14th 2014)
- fixed the :func:`launch` function on windows.
- improved the colorama support on windows to try hard to not
screw up the console if the application is interrupted.
- fixed windows terminals incorrectly being reported to be 80
characters wide instead of 79
- use colorama win32 bindings if available to get the correct
dimensions of a windows terminal.
- fixed an issue with custom function types on Python 3.
- fixed an issue with unknown options being incorrectly reported
in error messages.
Version 2.0
-----------
(codename "tap tap tap", released on June 6th 2014)
- added support for opening stdin/stdout on Windows in
binary mode correctly.
- added support for atomic writes to files by going through
a temporary file.
- introduced :exc:`BadParameter` which can be used to easily perform
custom validation with the same error messages as in the type system.
- added :func:`progressbar`; a function to show progress bars.
- added :func:`get_app_dir`; a function to calculate the home folder
for configs.
- Added transparent handling for ANSI codes into the :func:`echo`
function through `colorama`.
- Added :func:`clear` function.
- Breaking change: parameter callbacks now get the parameter object
passed as second argument. There is legacy support for old callbacks
which will warn but still execute the script.
- Added :func:`style`, :func:`unstyle` and :func:`secho` for ANSI
styles.
- Added an :func:`edit` function that invokes the default editor.
- Added an :func:`launch` function that launches browsers and applications.
- nargs of -1 for arguments can now be forced to be a single item through
the required flag. It defaults to not required.
- setting a default for arguments now implicitly makes it non required.
- changed "yN" / "Yn" to "y/N" and "Y/n" in confirmation prompts.
- added basic support for bash completion.
- added :func:`getchar` to fetch a single character from the terminal.
- errors now go to stderr as intended.
- fixed various issues with more exotic parameter formats like DOS/Windows
style arguments.
- added :func:`pause` which works similar to the Windows ``pause`` cmd
built-in but becomes an automatic noop if the application is not run
through a terminal.
- added a bit of extra information about missing choice parameters.
- changed how the help function is implemented to allow global overriding
of the help option.
- added support for token normalization to implement case insensitive handling.
- added support for providing defaults for context settings.
Version 1.1
-----------
(bugfix release, released on May 23rd 2014)
- fixed a bug that caused text files in Python 2 to not accept
native strings.
Version 1.0
-----------
(no codename, released on May 21st 2014)
- Initial release.

38
LICENSE Normal file
View file

@ -0,0 +1,38 @@
Copyright (c) 2014 by Armin Ronacher.
Click uses parts of optparse written by Gregory P. Ward and maintained by the
Python software foundation. This is limited to code in the parser.py
module:
Copyright (c) 2001-2006 Gregory P. Ward. All rights reserved.
Copyright (c) 2002-2006 Python Software Foundation. All rights reserved.
Some rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* The names of the contributors may not be used to endorse or
promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

12
MANIFEST.in Normal file
View file

@ -0,0 +1,12 @@
include Makefile CHANGES LICENSE
recursive-include artwork *
recursive-include tests *
recursive-include examples *
recursive-include docs *
recursive-exclude docs *.pyc
recursive-exclude docs *.pyo
recursive-exclude tests *.pyc
recursive-exclude tests *.pyo
recursive-exclude examples *.pyc
recursive-exclude examples *.pyo
prune docs/_build

8
Makefile Normal file
View file

@ -0,0 +1,8 @@
test:
@cd tests; PYTHONPATH=.. py.test --tb=short
upload-docs:
$(MAKE) -C docs dirhtml
rsync -a docs/_build/dirhtml/* flow.srv.pocoo.org:/srv/websites/click.pocoo.org/static/
.PHONY: upload-docs

13
PKG-INFO Normal file
View file

@ -0,0 +1,13 @@
Metadata-Version: 1.1
Name: click
Version: 3.3
Summary: A simple wrapper around optparse for powerful command line utilities.
Home-page: http://github.com/mitsuhiko/click
Author: Armin Ronacher
Author-email: armin.ronacher@active-4.com
License: UNKNOWN
Description: UNKNOWN
Platform: UNKNOWN
Classifier: License :: OSI Approved :: BSD License
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3

20
README Normal file
View file

@ -0,0 +1,20 @@
$ click_
Click is a Python package for creating beautiful command line interfaces
in a composable way with as little code as necessary. It's the "Command
Line Interface Creation Kit". It's highly configurable but comes with
sensible defaults out of the box.
It aims to make the process of writing command line tools quick and fun
while also preventing any frustration caused by the inability to implement
an intended CLI API.
Click in three points:
- arbitrary nesting of commands
- automatic help page generation
- supports lazy loading of subcommands at runtime
Read the docs at http://click.pocoo.org/
This library is a work in progress. Please give feedback!

75
artwork/logo.svg Normal file
View file

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="237.36929"
height="110.7928"
id="svg4837"
version="1.1"
inkscape:version="0.48.2 r9819"
sodipodi:docname="New document 8">
<defs
id="defs4839" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.98994949"
inkscape:cx="259.76814"
inkscape:cy="40.769955"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
fit-margin-top="10"
fit-margin-left="10"
fit-margin-right="10"
fit-margin-bottom="10"
inkscape:window-width="1676"
inkscape:window-height="1006"
inkscape:window-x="4"
inkscape:window-y="0"
inkscape:window-maximized="1" />
<metadata
id="metadata4842">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-255.43458,-207.38101)">
<path
style="fill:#000000"
d="m 466.33424,306.48462 0,-1.6892 3.16724,0 3.16723,0 0,1.6892 0,1.68919 -3.16723,0 -3.16724,0 0,-1.68919 z m -3.37839,-5.06759 0,-3.37839 1.47804,0 1.47805,0 0,3.37839 0,3.37839 -1.47805,0 -1.47804,0 0,-3.37839 z m 10.13516,0 0,-3.37839 1.47804,0 1.47805,0 0,3.37839 0,3.37839 -1.47805,0 -1.47804,0 0,-3.37839 z m -30.82778,-28.92744 0,-28.92744 1.68919,0 c 1.68919,0 1.68919,0 1.68919,1.68919 0,1.6892 0,1.6892 1.6892,1.6892 1.68919,0 1.68919,0 1.68919,1.68919 0,1.6892 0,1.6892 1.6892,1.6892 1.68919,0 1.68919,0 1.68919,1.68919 0,1.6892 0,1.6892 -1.68919,1.6892 -1.6892,0 -1.6892,0 -1.6892,-1.6892 0,-1.68919 0,-1.68919 -1.68919,-1.68919 l -1.6892,0 0,23.85986 0,23.85985 1.6892,0 c 1.68919,0 1.68919,0 1.68919,1.6892 l 0,1.68919 -3.37839,0 -3.37838,0 0,-28.92744 z m 7.00029,25.22951 c -0.13394,-0.13393 -0.24352,-0.87696 -0.24352,-1.65118 0,-1.36423 0.0529,-1.40766 1.71469,-1.40766 l 1.71468,0 -0.13106,1.58362 c -0.12024,1.45279 -0.24178,1.5892 -1.47117,1.65118 -0.73706,0.0372 -1.44968,-0.042 -1.58362,-0.17596 z m 10.14204,-0.0392 c -0.13814,-0.22352 -0.20454,-1.7545 -0.14756,-3.40219 l 0.10361,-2.99581 1.60373,0 1.60374,0 -0.12334,3.27282 -0.12334,3.27281 -1.33284,0.12938 c -0.73305,0.0712 -1.44586,-0.0535 -1.584,-0.27702 z m 10.30707,-3.03247 0,-3.36553 1.47803,0 1.47805,0 0,3.30997 0,3.30996 -1.47805,0.0556 -1.47803,0.0556 0,-3.36553 z m -17.03271,-0.26872 c -0.15484,-0.15483 -0.28153,-0.91497 -0.28153,-1.68919 0,-1.36073 0.0563,-1.40766 1.68919,-1.40766 1.6892,0 1.6892,0 1.6892,1.6892 0,1.63289 -0.0469,1.68919 -1.40766,1.68919 -0.77422,0 -1.53436,-0.12669 -1.6892,-0.28154 z m 3.09686,-4.99719 c 0,-1.44286 0.0402,-1.47804 1.68919,-1.47804 1.64898,0 1.68919,0.0352 1.68919,1.47804 0,1.44285 -0.0402,1.47805 -1.68919,1.47805 -1.64898,0 -1.68919,-0.0352 -1.68919,-1.47805 z m 10.55746,-1.68919 0,-3.16724 6.75677,0 6.75678,0 0,-1.68919 c 0,-1.64898 0.0351,-1.6892 1.47804,-1.6892 l 1.47804,0 0,3.15652 0,3.15651 -6.6512,0.1163 -6.65119,0.1163 -0.13108,1.58361 c -0.12394,1.49763 -0.20994,1.58363 -1.58361,1.58363 l -1.45255,0 0,-3.16724 z m 10.13516,-8.44597 c 0,-1.40766 0.0704,-1.47804 1.47804,-1.47804 1.40766,0 1.47805,0.0704 1.47805,1.47804 0,1.40766 -0.0704,1.47804 -1.47805,1.47804 -1.40766,0 -1.47804,-0.0704 -1.47804,-1.47804 z m -3.37839,-3.37839 c 0,-1.40766 0.0704,-1.47804 1.47804,-1.47804 1.40767,0 1.47805,0.0704 1.47805,1.47804 0,1.40767 -0.0704,1.47805 -1.47805,1.47805 -1.40765,0 -1.47804,-0.0704 -1.47804,-1.47805 z m -3.37838,-3.37839 c 0,-1.40765 0.0704,-1.47804 1.47803,-1.47804 1.40767,0 1.47805,0.0704 1.47805,1.47804 0,1.40767 -0.0704,1.47805 -1.47805,1.47805 -1.40765,0 -1.47803,-0.0704 -1.47803,-1.47805 z m -3.37839,-3.48871 c 0,-1.52897 0.0553,-1.58931 1.47804,-1.61346 1.43567,-0.0244 1.47804,0.0211 1.47804,1.58838 0,1.56241 -0.0467,1.61345 -1.47804,1.61345 -1.42721,0 -1.47804,-0.0546 -1.47804,-1.58837 z m -3.37839,-3.47921 c 0,-1.64897 0.0351,-1.68919 1.47804,-1.68919 1.44285,0 1.47805,0.0402 1.47805,1.68919 0,1.64898 -0.0352,1.6892 -1.47805,1.6892 -1.44285,0 -1.47804,-0.0403 -1.47804,-1.6892 z m -3.45076,-3.37839 c 0.15328,-1.60738 0.23052,-1.68919 1.59478,-1.68919 1.3911,0 1.43368,0.0502 1.43368,1.68919 0,1.67253 -0.0157,1.6892 -1.59478,1.6892 l -1.59477,0 0.16109,-1.6892 z m -3.30601,-3.37838 c 0,-1.64898 0.0351,-1.6892 1.47804,-1.6892 1.44285,0 1.47804,0.0402 1.47804,1.6892 0,1.64897 -0.0352,1.68919 -1.47804,1.68919 -1.44286,0 -1.47804,-0.0402 -1.47804,-1.68919 z m -3.72061,-3.27282 c 0.12395,-1.49762 0.21706,-1.59073 1.71468,-1.71468 l 1.58363,-0.13107 0,1.71469 0,1.71468 -1.71469,0 -1.71468,0 0.13106,-1.58362 z"
id="path4856"
inkscape:connector-curvature="0" />
<path
style="fill:#000000"
d="m 271.34527,247.46047 c 0,-1.32826 -0.94153,-2.12734 -3.05297,-2.59109 -4.04056,-0.88746 -3.19986,-3.08429 1.01754,-2.65894 9.32239,0.94022 10.81407,-3.50307 2.60795,-7.76831 -2.71012,-1.40862 -5.36388,-3.27897 -5.89726,-4.15634 -1.47604,-2.42802 -0.0246,-6.42458 2.80076,-7.7119 1.48697,-0.67751 2.52398,-1.98054 2.52398,-3.17144 0,-1.13017 0.66136,-2.02144 1.5,-2.02144 0.825,0 1.5,0.85122 1.5,1.89159 0,1.19765 0.94321,2.12832 2.57104,2.53688 3.91591,0.98283 2.39946,3.10499 -1.95093,2.73019 -6.99788,-0.60291 -8.93527,3.8291 -2.87011,6.5657 9.0905,4.10163 11.015,9.73021 4.5,13.16112 -1.2375,0.65169 -2.25,2.06905 -2.25,3.1497 0,1.08065 -0.675,1.96482 -1.5,1.96482 -0.825,0 -1.5,-0.86425 -1.5,-1.92054 z m 38.357,-3.07104 c -4.57947,-1.84804 -6.77791,-8.48491 -4.32736,-13.06381 2.11183,-3.94599 10.30093,-6.28597 13.9414,-3.98367 2.2687,1.43477 0.36183,2.48777 -3.94232,2.177 -6.96682,-0.50302 -10.61631,6.27447 -5.91184,10.97894 1.71218,1.71218 2.95483,2.02243 6.5,1.62284 3.13626,-0.35351 4.38312,-0.13272 4.38312,0.77613 0,2.4062 -6.21813,3.27822 -10.643,1.49257 z m 21.1997,-1.23093 c -1.16379,-1.66155 -1.5567,-4.81653 -1.5567,-12.5 l 0,-10.27749 -2.5,0 c -1.55556,0 -2.5,-0.56667 -2.5,-1.5 0,-1.08333 1.11111,-1.5 4,-1.5 l 4,0 0.0153,11.25 c 0.008,6.1875 0.41056,11.87411 0.89363,12.63691 0.54214,0.85607 1.88505,1.19446 3.50856,0.88411 2.94677,-0.56331 4.58181,0.98192 2.46061,2.32547 -2.5829,1.63598 -6.70984,0.98184 -8.32141,-1.319 z m 20.4433,0.22251 c -1.55556,-1.55556 -2,-3.33333 -2,-8 0,-5.73333 -0.11111,-6 -2.5,-6 -1.55556,0 -2.5,-0.56667 -2.5,-1.5 0,-1.08333 1.11111,-1.5 4,-1.5 l 4,0 0.0153,6.75 c 0.0183,8.04891 0.82623,9.70461 4.40219,9.02102 2.94677,-0.56331 4.58181,0.98192 2.46061,2.32547 -2.3358,1.47948 -5.78176,0.99986 -7.87811,-1.09649 z m 18.357,1.00842 c -4.57947,-1.84804 -6.77791,-8.48491 -4.32736,-13.06381 2.11183,-3.94599 10.30093,-6.28597 13.9414,-3.98367 2.2687,1.43477 0.36183,2.48777 -3.94232,2.177 -6.96682,-0.50302 -10.61631,6.27447 -5.91184,10.97894 1.71218,1.71218 2.95483,2.02243 6.5,1.62284 3.13626,-0.35351 4.38312,-0.13272 4.38312,0.77613 0,2.4062 -6.21813,3.27822 -10.643,1.49257 z m 15.86478,-12.67646 c 0.20032,-9.84119 0.6282,-13.78431 1.52822,-14.08333 0.9358,-0.31091 1.25628,1.80502 1.275,8.41804 l 0.025,8.83333 4.01111,-4.25 c 2.32594,-2.46446 4.92367,-4.25 6.18321,-4.25 1.8968,0 1.6121,0.57059 -2.24643,4.50214 l -4.41851,4.50213 4.92271,4.99787 c 3.84161,3.90023 4.49929,4.99786 2.99467,4.99786 -1.06044,0 -4.08102,-2.12058 -6.71241,-4.71241 l -4.78435,-4.71241 0,4.71241 c 0,3.56082 -0.37346,4.71241 -1.52822,4.71241 -1.30999,0 -1.48849,-1.95179 -1.25,-13.66804 z m -37.85759,-9.82527 c -0.34636,-0.90262 -0.15025,-2.12063 0.43581,-2.70669 1.52361,-1.52361 4.41041,-0.13109 4.01242,1.93549 -0.4433,2.30188 -3.64766,2.85743 -4.44823,0.7712 z"
id="path4887"
inkscape:connector-curvature="0" />
<path
style="fill:#000000;fill-opacity:1;stroke:none"
d="m 411.32164,243.39982 c 7.38153,0.15924 14.76525,0.0821 22.14736,0.0136 -3.6814,3.83663 -9.42739,1.45689 -14.06576,2.07921 -3.69684,-0.007 -7.3939,0.0151 -11.09,0.0923 1.0029,-0.72825 2.00505,-1.45754 3.0084,-2.18514 z"
id="path4889"
inkscape:connector-curvature="0" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.5 KiB

13
click.egg-info/PKG-INFO Normal file
View file

@ -0,0 +1,13 @@
Metadata-Version: 1.1
Name: click
Version: 3.3
Summary: A simple wrapper around optparse for powerful command line utilities.
Home-page: http://github.com/mitsuhiko/click
Author: Armin Ronacher
Author-email: armin.ronacher@active-4.com
License: UNKNOWN
Description: UNKNOWN
Platform: UNKNOWN
Classifier: License :: OSI Approved :: BSD License
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3

122
click.egg-info/SOURCES.txt Normal file
View file

@ -0,0 +1,122 @@
CHANGES
LICENSE
MANIFEST.in
Makefile
README
setup.cfg
setup.py
artwork/logo.svg
click/__init__.py
click/_bashcomplete.py
click/_compat.py
click/_termui_impl.py
click/_textwrap.py
click/core.py
click/decorators.py
click/exceptions.py
click/formatting.py
click/parser.py
click/termui.py
click/testing.py
click/types.py
click/utils.py
click.egg-info/PKG-INFO
click.egg-info/SOURCES.txt
click.egg-info/dependency_links.txt
click.egg-info/top_level.txt
docs/Makefile
docs/advanced.rst
docs/api.rst
docs/arguments.rst
docs/bashcomplete.rst
docs/changelog.rst
docs/clickdoctools.py
docs/commands.rst
docs/complex.rst
docs/conf.py
docs/documentation.rst
docs/exceptions.rst
docs/index.rst
docs/license.rst
docs/make.bat
docs/options.rst
docs/parameters.rst
docs/prompts.rst
docs/python3.rst
docs/quickstart.rst
docs/setuptools.rst
docs/testing.rst
docs/upgrading.rst
docs/utils.rst
docs/why.rst
docs/_static/click-small.png
docs/_static/click-small@2x.png
docs/_static/click.png
docs/_static/click@2x.png
docs/_templates/sidebarintro.html
docs/_templates/sidebarlogo.html
docs/_themes/LICENSE
docs/_themes/README
docs/_themes/click/layout.html
docs/_themes/click/relations.html
docs/_themes/click/theme.conf
docs/_themes/click/static/click.css_t
examples/.DS_Store
examples/README
examples/aliases/README
examples/aliases/aliases.ini
examples/aliases/aliases.py
examples/aliases/setup.py
examples/colors/README
examples/colors/colors.py
examples/colors/setup.py
examples/complex/README
examples/complex/setup.py
examples/complex/complex/__init__.py
examples/complex/complex/cli.py
examples/complex/complex/commands/__init__.py
examples/complex/complex/commands/cmd_init.py
examples/complex/complex/commands/cmd_status.py
examples/imagepipe/.DS_Store
examples/imagepipe/.gitignore
examples/imagepipe/README
examples/imagepipe/example01.jpg
examples/imagepipe/example02.jpg
examples/imagepipe/imagepipe.py
examples/imagepipe/setup.py
examples/imagepipe/click_example_imagepipe.egg-info/PKG-INFO
examples/imagepipe/click_example_imagepipe.egg-info/SOURCES.txt
examples/imagepipe/click_example_imagepipe.egg-info/dependency_links.txt
examples/imagepipe/click_example_imagepipe.egg-info/entry_points.txt
examples/imagepipe/click_example_imagepipe.egg-info/requires.txt
examples/imagepipe/click_example_imagepipe.egg-info/top_level.txt
examples/inout/README
examples/inout/inout.py
examples/inout/setup.py
examples/naval/README
examples/naval/naval.py
examples/naval/setup.py
examples/repo/README
examples/repo/repo.py
examples/repo/setup.py
examples/termui/README
examples/termui/setup.py
examples/termui/termui.py
examples/validation/README
examples/validation/setup.py
examples/validation/validation.py
tests/conftest.py
tests/test_arguments.py
tests/test_basic.py
tests/test_chain.py
tests/test_commands.py
tests/test_compat.py
tests/test_context.py
tests/test_defaults.py
tests/test_formatting.py
tests/test_imports.py
tests/test_normalization.py
tests/test_options.py
tests/test_termui.py
tests/test_testing.py
tests/test_utils.py

View file

@ -0,0 +1 @@

View file

@ -0,0 +1 @@
click

85
click/__init__.py Normal file
View file

@ -0,0 +1,85 @@
# -*- coding: utf-8 -*-
"""
click
~~~~~
Click is a simple Python module that wraps the stdlib's optparse to make
writing command line scripts fun. Unlike other modules, it's based around
a simple API that does not come with too much magic and is composable.
In case optparse ever gets removed from the stdlib, it will be shipped by
this module.
:copyright: (c) 2014 by Armin Ronacher.
:license: BSD, see LICENSE for more details.
"""
# Core classes
from .core import Context, BaseCommand, Command, MultiCommand, Group, \
CommandCollection, Parameter, Option, Argument
# Decorators
from .decorators import pass_context, pass_obj, make_pass_decorator, \
command, group, argument, option, confirmation_option, \
password_option, version_option, help_option
# Types
from .types import ParamType, File, Path, Choice, IntRange, STRING, INT, \
FLOAT, BOOL, UUID
# Utilities
from .utils import echo, get_binary_stream, get_text_stream, open_file, \
format_filename, get_app_dir
# Terminal functions
from .termui import prompt, confirm, get_terminal_size, echo_via_pager, \
progressbar, clear, style, unstyle, secho, edit, launch, getchar, \
pause
# Exceptions
from .exceptions import ClickException, UsageError, BadParameter, \
FileError, Abort
# Formatting
from .formatting import HelpFormatter, wrap_text
# Parsing
from .parser import OptionParser
__all__ = [
# Core classes
'Context', 'BaseCommand', 'Command', 'MultiCommand', 'Group',
'CommandCollection', 'Parameter', 'Option', 'Argument',
# Decorators
'pass_context', 'pass_obj', 'make_pass_decorator', 'command', 'group',
'argument', 'option', 'confirmation_option', 'password_option',
'version_option', 'help_option',
# Types
'ParamType', 'File', 'Path', 'Choice', 'IntRange', 'STRING', 'INT',
'FLOAT', 'BOOL', 'UUID',
# Utilities
'echo', 'get_binary_stream', 'get_text_stream', 'open_file',
'format_filename', 'get_app_dir',
# Terminal functions
'prompt', 'confirm', 'get_terminal_size', 'echo_via_pager',
'progressbar', 'clear', 'style', 'unstyle', 'secho', 'edit', 'launch',
'getchar', 'pause',
# Exceptions
'ClickException', 'UsageError', 'BadParameter', 'FileError',
'Abort',
# Formatting
'HelpFormatter', 'wrap_text',
# Parsing
'OptionParser',
]
__version__ = '3.3'

74
click/_bashcomplete.py Normal file
View file

@ -0,0 +1,74 @@
import os
from .utils import echo
from .parser import split_arg_string
from .core import MultiCommand, Option
COMPLETION_SCRIPT = '''
%(complete_func)s() {
COMPREPLY=( $( COMP_WORDS="${COMP_WORDS[*]}" \\
COMP_CWORD=$COMP_CWORD \\
%(autocomplete_var)s=complete $1 ) )
return 0
}
complete -F %(complete_func)s -o default %(script_names)s
'''
def get_completion_script(prog_name, complete_var):
return (COMPLETION_SCRIPT % {
'complete_func': '_%s_completion' % prog_name,
'script_names': prog_name,
'autocomplete_var': complete_var,
}).strip() + ';'
def resolve_ctx(cli, prog_name, args):
ctx = cli.make_context(prog_name, args, resilient_parsing=True)
while ctx.args and isinstance(ctx.command, MultiCommand):
cmd = ctx.command.get_command(ctx, ctx.args[0])
if cmd is None:
return None
ctx = cmd.make_context(ctx.args[0], ctx.args[1:], parent=ctx,
resilient_parsing=True)
return ctx
def do_complete(cli, prog_name):
cwords = split_arg_string(os.environ['COMP_WORDS'])
cword = int(os.environ['COMP_CWORD'])
args = cwords[1:cword]
try:
incomplete = cwords[cword]
except IndexError:
incomplete = ''
ctx = resolve_ctx(cli, prog_name, args)
if ctx is None:
return True
choices = []
if incomplete and not incomplete[:1].isalnum():
for param in ctx.command.params:
if not isinstance(param, Option):
continue
choices.extend(param.opts)
choices.extend(param.secondary_opts)
elif isinstance(ctx.command, MultiCommand):
choices.extend(ctx.command.list_commands(ctx))
for item in choices:
if item.startswith(incomplete):
echo(item)
return True
def bashcomplete(cli, prog_name, complete_var, complete_instr):
if complete_instr == 'source':
echo(get_completion_script(prog_name, complete_var))
return True
elif complete_instr == 'complete':
return do_complete(cli, prog_name)
return False

560
click/_compat.py Normal file
View file

@ -0,0 +1,560 @@
import re
import io
import os
import sys
import codecs
from weakref import WeakKeyDictionary
PY2 = sys.version_info[0] == 2
WIN = sys.platform.startswith('win')
DEFAULT_COLUMNS = 80
_ansi_re = re.compile('\033\[((?:\d|;)*)([a-zA-Z])')
def _make_text_stream(stream, encoding, errors):
if encoding is None:
encoding = get_best_encoding(stream)
if errors is None:
errors = 'replace'
return _NonClosingTextIOWrapper(stream, encoding, errors,
line_buffering=True)
def is_ascii_encoding(encoding):
"""Checks if a given encoding is ascii."""
try:
return codecs.lookup(encoding).name == 'ascii'
except LookupError:
return False
def get_best_encoding(stream):
"""Returns the default stream encoding if not found."""
rv = getattr(stream, 'encoding', None) or sys.getdefaultencoding()
if is_ascii_encoding(rv):
return 'utf-8'
return rv
class _NonClosingTextIOWrapper(io.TextIOWrapper):
def __init__(self, stream, encoding, errors, **extra):
self._stream = stream = _FixupStream(stream)
io.TextIOWrapper.__init__(self, stream, encoding, errors, **extra)
# The io module is a place where the Python 3 text behavior
# was forced upon Python 2, so we need to unbreak
# it to look like Python 2.
if PY2:
def write(self, x):
if isinstance(x, str) or is_bytes(x):
try:
self.flush()
except Exception:
pass
return self.buffer.write(str(x))
return io.TextIOWrapper.write(self, x)
def writelines(self, lines):
for line in lines:
self.write(line)
def __del__(self):
try:
self.detach()
except Exception:
pass
def isatty(self):
# https://bitbucket.org/pypy/pypy/issue/1803
return self._stream.isatty()
class _FixupStream(object):
"""The new io interface needs more from streams than streams
traditionally implement. As such, this fix-up code is necessary in
some circumstances.
"""
def __init__(self, stream):
self._stream = stream
def __getattr__(self, name):
return getattr(self._stream, name)
def read1(self, size):
f = getattr(self._stream, 'read1', None)
if f is not None:
return f(size)
# We only dispatch to readline instead of read in Python 2 as we
# do not want cause problems with the different implementation
# of line buffering.
if PY2:
return self._stream.readline(size)
return self._stream.read(size)
def readable(self):
x = getattr(self._stream, 'readable', None)
if x is not None:
return x()
try:
self._stream.read(0)
except Exception:
return False
return True
def writable(self):
x = getattr(self._stream, 'writable', None)
if x is not None:
return x()
try:
self._stream.write('')
except Exception:
try:
self._stream.write(b'')
except Exception:
return False
return True
def seekable(self):
x = getattr(self._stream, 'seekable', None)
if x is not None:
return x()
try:
self._stream.seek(self._stream.tell())
except Exception:
return False
return True
if PY2:
text_type = unicode
bytes = str
raw_input = raw_input
string_types = (str, unicode)
iteritems = lambda x: x.iteritems()
range_type = xrange
def is_bytes(x):
return isinstance(x, (buffer, bytearray))
_identifier_re = re.compile(r'^[a-zA-Z_][a-zA-Z0-9_]*$')
# For Windows, we need to force stdout/stdin/stderr to binary if it's
# fetched for that. This obviously is not the most correct way to do
# it as it changes global state. Unfortunately, there does not seem to
# be a clear better way to do it as just reopening the file in binary
# mode does not change anything.
#
# An option would be to do what Python 3 does and to open the file as
# binary only, patch it back to the system, and then use a wrapper
# stream that converts newlines. It's not quite clear what's the
# correct option here.
if WIN:
import msvcrt
def set_binary_mode(f):
try:
fileno = f.fileno()
except Exception:
pass
else:
msvcrt.setmode(fileno, os.O_BINARY)
return f
else:
set_binary_mode = lambda x: x
def isidentifier(x):
return _identifier_re.search(x) is not None
def get_binary_stdin():
return set_binary_mode(sys.stdin)
def get_binary_stdout():
return set_binary_mode(sys.stdout)
def get_binary_stderr():
return set_binary_mode(sys.stderr)
def get_text_stdin(encoding=None, errors=None):
return _make_text_stream(sys.stdin, encoding, errors)
def get_text_stdout(encoding=None, errors=None):
return _make_text_stream(sys.stdout, encoding, errors)
def get_text_stderr(encoding=None, errors=None):
return _make_text_stream(sys.stderr, encoding, errors)
def filename_to_ui(value):
if isinstance(value, bytes):
value = value.decode(sys.getfilesystemencoding(), 'replace')
return value
else:
import io
text_type = str
raw_input = input
string_types = (str,)
range_type = range
isidentifier = lambda x: x.isidentifier()
iteritems = lambda x: iter(x.items())
def is_bytes(x):
return isinstance(x, (bytes, memoryview, bytearray))
def _is_binary_reader(stream, default=False):
try:
return isinstance(stream.read(0), bytes)
except Exception:
return default
# This happens in some cases where the stream was already
# closed. In this case, we assume the default.
def _is_binary_writer(stream, default=False):
try:
stream.write(b'')
except Exception:
try:
stream.write('')
return False
except Exception:
pass
return default
return True
def _find_binary_reader(stream):
# We need to figure out if the given stream is already binary.
# This can happen because the official docs recommend detaching
# the streams to get binary streams. Some code might do this, so
# we need to deal with this case explicitly.
if _is_binary_reader(stream, False):
return stream
buf = getattr(stream, 'buffer', None)
# Same situation here; this time we assume that the buffer is
# actually binary in case it's closed.
if buf is not None and _is_binary_reader(buf, True):
return buf
def _find_binary_writer(stream):
# We need to figure out if the given stream is already binary.
# This can happen because the official docs recommend detatching
# the streams to get binary streams. Some code might do this, so
# we need to deal with this case explicitly.
if _is_binary_writer(stream, False):
return stream
buf = getattr(stream, 'buffer', None)
# Same situation here; this time we assume that the buffer is
# actually binary in case it's closed.
if buf is not None and _is_binary_writer(buf, True):
return buf
def _stream_is_misconfigured(stream):
"""A stream is misconfigured if its encoding is ASCII."""
return is_ascii_encoding(getattr(stream, 'encoding', None))
def _is_compatible_text_stream(stream, encoding, errors):
stream_encoding = getattr(stream, 'encoding', None)
stream_errors = getattr(stream, 'errors', None)
# Perfect match.
if stream_encoding == encoding and stream_errors == errors:
return True
# Otherwise, it's only a compatible stream if we did not ask for
# an encoding.
if encoding is None:
return stream_encoding is not None
return False
def _force_correct_text_reader(text_reader, encoding, errors):
if _is_binary_reader(text_reader, False):
binary_reader = text_reader
else:
# If there is no target encoding set, we need to verify that the
# reader is not actually misconfigured.
if encoding is None and not _stream_is_misconfigured(text_reader):
return text_reader
if _is_compatible_text_stream(text_reader, encoding, errors):
return text_reader
# If the reader has no encoding, we try to find the underlying
# binary reader for it. If that fails because the environment is
# misconfigured, we silently go with the same reader because this
# is too common to happen. In that case, mojibake is better than
# exceptions.
binary_reader = _find_binary_reader(text_reader)
if binary_reader is None:
return text_reader
# At this point, we default the errors to replace instead of strict
# because nobody handles those errors anyways and at this point
# we're so fundamentally fucked that nothing can repair it.
if errors is None:
errors = 'replace'
return _make_text_stream(binary_reader, encoding, errors)
def _force_correct_text_writer(text_writer, encoding, errors):
if _is_binary_writer(text_writer, False):
binary_writer = text_writer
else:
# If there is no target encoding set, we need to verify that the
# writer is not actually misconfigured.
if encoding is None and not _stream_is_misconfigured(text_writer):
return text_writer
if _is_compatible_text_stream(text_writer, encoding, errors):
return text_writer
# If the writer has no encoding, we try to find the underlying
# binary writer for it. If that fails because the environment is
# misconfigured, we silently go with the same writer because this
# is too common to happen. In that case, mojibake is better than
# exceptions.
binary_writer = _find_binary_writer(text_writer)
if binary_writer is None:
return text_writer
# At this point, we default the errors to replace instead of strict
# because nobody handles those errors anyways and at this point
# we're so fundamentally fucked that nothing can repair it.
if errors is None:
errors = 'replace'
return _make_text_stream(binary_writer, encoding, errors)
def get_binary_stdin():
reader = _find_binary_reader(sys.stdin)
if reader is None:
raise RuntimeError('Was not able to determine binary '
'stream for sys.stdin.')
return reader
def get_binary_stdout():
writer = _find_binary_writer(sys.stdout)
if writer is None:
raise RuntimeError('Was not able to determine binary '
'stream for sys.stdout.')
return writer
def get_binary_stderr():
writer = _find_binary_writer(sys.stderr)
if writer is None:
raise RuntimeError('Was not able to determine binary '
'stream for sys.stderr.')
return writer
def get_text_stdin(encoding=None, errors=None):
return _force_correct_text_reader(sys.stdin, encoding, errors)
def get_text_stdout(encoding=None, errors=None):
return _force_correct_text_writer(sys.stdout, encoding, errors)
def get_text_stderr(encoding=None, errors=None):
return _force_correct_text_writer(sys.stderr, encoding, errors)
def filename_to_ui(value):
if isinstance(value, bytes):
value = value.decode(sys.getfilesystemencoding(), 'replace')
else:
value = value.encode('utf-8', 'surrogateescape') \
.decode('utf-8', 'replace')
return value
def get_streerror(e, default=None):
if hasattr(e, 'strerror'):
msg = e.strerror
else:
if default is not None:
msg = default
else:
msg = str(e)
if isinstance(msg, bytes):
msg = msg.decode('utf-8', 'replace')
return msg
def open_stream(filename, mode='r', encoding=None, errors='strict',
atomic=False):
# Standard streams first. These are simple because they don't need
# special handling for the atomic flag. It's entirely ignored.
if filename == '-':
if 'w' in mode:
if 'b' in mode:
return get_binary_stdout(), False
return get_text_stdout(encoding=encoding, errors=errors), False
if 'b' in mode:
return get_binary_stdin(), False
return get_text_stdin(encoding=encoding, errors=errors), False
# Non-atomic writes directly go out through the regular open functions.
if not atomic:
if encoding is None:
return open(filename, mode), True
return io.open(filename, mode, encoding=encoding, errors=errors), True
# Atomic writes are more complicated. They work by opening a file
# as a proxy in the same folder and then using the fdopen
# functionality to wrap it in a Python file. Then we wrap it in an
# atomic file that moves the file over on close.
import tempfile
fd, tmp_filename = tempfile.mkstemp(dir=os.path.dirname(filename),
prefix='.__atomic-write')
if encoding is not None:
f = io.open(fd, mode, encoding=encoding, errors=errors)
else:
f = os.fdopen(fd, mode)
return _AtomicFile(f, tmp_filename, filename), True
# Used in a destructor call, needs extra protection from interpreter cleanup.
_rename = os.rename
class _AtomicFile(object):
def __init__(self, f, tmp_filename, real_filename):
self._f = f
self._tmp_filename = tmp_filename
self._real_filename = real_filename
self.closed = False
@property
def name(self):
return self._real_filename
def close(self, delete=False):
if self.closed:
return
self._f.close()
_rename(self._tmp_filename, self._real_filename)
self.closed = True
def __getattr__(self, name):
return getattr(self._f, name)
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, tb):
self.close(delete=exc_type is not None)
def __repr__(self):
return repr(self._f)
auto_wrap_for_ansi = None
colorama = None
get_winterm_size = None
# If we're on Windows, we provide transparent integration through
# colorama. This will make ANSI colors through the echo function
# work automatically.
if WIN:
# Windows has a smaller terminal
DEFAULT_COLUMNS = 79
try:
import colorama
except ImportError:
pass
else:
_ansi_stream_wrappers = WeakKeyDictionary()
def auto_wrap_for_ansi(stream):
"""This function wraps a stream so that calls through colorama
are issued to the win32 console API to recolor on demand. It
also ensures to reset the colors if a write call is interrupted
to not destroy the console afterwards.
"""
try:
cached = _ansi_stream_wrappers.get(stream)
except Exception:
cached = None
if cached is not None:
return cached
strip = not isatty(stream)
ansi_wrapper = colorama.AnsiToWin32(stream, strip=strip)
rv = ansi_wrapper.stream
_write = rv.write
def _safe_write(s):
try:
return _write(s)
except:
ansi_wrapper.reset_all()
raise
rv.write = _safe_write
try:
_ansi_stream_wrappers[stream] = rv
except Exception:
pass
return rv
def get_winterm_size():
win = colorama.win32.GetConsoleScreenBufferInfo(
colorama.win32.STDOUT).srWindow
return win.Right - win.Left, win.Bottom - win.Top
def strip_ansi(value):
return _ansi_re.sub('', value)
def term_len(x):
return len(strip_ansi(x))
def isatty(stream):
try:
return stream.isatty()
except Exception:
return False
def _make_cached_stream_func(src_func, wrapper_func):
cache = WeakKeyDictionary()
def func():
stream = src_func()
try:
rv = cache.get(stream)
except Exception:
rv = None
if rv is not None:
return rv
rv = wrapper_func()
try:
cache[stream] = rv
except Exception:
pass
return rv
return func
_default_text_stdout = _make_cached_stream_func(
lambda: sys.stdout, get_text_stdout)
_default_text_stderr = _make_cached_stream_func(
lambda: sys.stderr, get_text_stderr)
binary_streams = {
'stdin': get_binary_stdin,
'stdout': get_binary_stdout,
'stderr': get_binary_stderr,
}
text_streams = {
'stdin': get_text_stdin,
'stdout': get_text_stdout,
'stderr': get_text_stderr,
}

508
click/_termui_impl.py Normal file
View file

@ -0,0 +1,508 @@
"""
click._termui_impl
~~~~~~~~~~~~~~~~~~
This module contains implementations for the termui module. To keep the
import time of Click down, some infrequently used functionality is placed
in this module and only imported as needed.
:copyright: (c) 2014 by Armin Ronacher.
:license: BSD, see LICENSE for more details.
"""
import os
import sys
import time
import math
from ._compat import _default_text_stdout, range_type, PY2, isatty, \
open_stream, strip_ansi, term_len, get_best_encoding, WIN
from .utils import echo
from .exceptions import ClickException
if os.name == 'nt':
BEFORE_BAR = '\r'
AFTER_BAR = '\n'
else:
BEFORE_BAR = '\r\033[?25l'
AFTER_BAR = '\033[?25h\n'
def _length_hint(obj):
"""Returns the length hint of an object."""
try:
return len(obj)
except TypeError:
try:
get_hint = type(obj).__length_hint__
except AttributeError:
return None
try:
hint = get_hint(obj)
except TypeError:
return None
if hint is NotImplemented or \
not isinstance(hint, (int, long)) or \
hint < 0:
return None
return hint
class ProgressBar(object):
def __init__(self, iterable, length=None, fill_char='#', empty_char=' ',
bar_template='%(bar)s', info_sep=' ', show_eta=True,
show_percent=None, show_pos=False, item_show_func=None,
label=None, file=None, width=30):
self.fill_char = fill_char
self.empty_char = empty_char
self.bar_template = bar_template
self.info_sep = info_sep
self.show_eta = show_eta
self.show_percent = show_percent
self.show_pos = show_pos
self.item_show_func = item_show_func
self.label = label or ''
if file is None:
file = _default_text_stdout()
self.file = file
self.width = width
self.autowidth = width == 0
if length is None:
length = _length_hint(iterable)
if iterable is None:
if length is None:
raise TypeError('iterable or length is required')
iterable = range_type(length)
self.iter = iter(iterable)
self.length = length
self.length_known = length is not None
self.pos = 0
self.avg = []
self.start = self.last_eta = time.time()
self.eta_known = False
self.finished = False
self.max_width = None
self.entered = False
self.current_item = None
self.is_hidden = not isatty(self.file)
def __enter__(self):
self.entered = True
self.render_progress()
return self
def __exit__(self, exc_type, exc_value, tb):
self.render_finish()
def __iter__(self):
if not self.entered:
raise RuntimeError('You need to use progress bars in a with block.')
self.render_progress()
return self
def render_finish(self):
if self.is_hidden:
return
self.file.write(AFTER_BAR)
self.file.flush()
@property
def pct(self):
if self.finished:
return 1.0
return min(self.pos / (float(self.length) or 1), 1.0)
@property
def time_per_iteration(self):
if not self.avg:
return 0.0
return sum(self.avg) / float(len(self.avg))
@property
def eta(self):
if self.length_known and not self.finished:
return self.time_per_iteration * (self.length - self.pos)
return 0.0
def format_eta(self):
if self.eta_known:
return time.strftime('%H:%M:%S', time.gmtime(self.eta + 1))
return ''
def format_pos(self):
pos = str(self.pos)
if self.length_known:
pos += '/%s' % self.length
return pos
def format_pct(self):
return ('% 4d%%' % int(self.pct * 100))[1:]
def format_progress_line(self):
show_percent = self.show_percent
info_bits = []
if self.length_known:
bar_length = int(self.pct * self.width)
bar = self.fill_char * bar_length
bar += self.empty_char * (self.width - bar_length)
if show_percent is None:
show_percent = not self.show_pos
else:
if self.finished:
bar = self.fill_char * self.width
else:
bar = list(self.empty_char * (self.width or 1))
if self.time_per_iteration != 0:
bar[int((math.cos(self.pos * self.time_per_iteration)
/ 2.0 + 0.5) * self.width)] = self.fill_char
bar = ''.join(bar)
if self.show_pos:
info_bits.append(self.format_pos())
if show_percent:
info_bits.append(self.format_pct())
if self.show_eta and self.eta_known and not self.finished:
info_bits.append(self.format_eta())
if self.item_show_func is not None:
item_info = self.item_show_func(self.current_item)
if item_info is not None:
info_bits.append(item_info)
return (self.bar_template % {
'label': self.label,
'bar': bar,
'info': self.info_sep.join(info_bits)
}).rstrip()
def render_progress(self):
from .termui import get_terminal_size
if self.is_hidden:
echo(self.label, file=self.file)
self.file.flush()
return
# Update width in case the terminal has been resized
if self.autowidth:
old_width = self.width
self.width = 0
clutter_length = term_len(self.format_progress_line())
new_width = max(0, get_terminal_size()[0] - clutter_length)
if new_width < old_width:
self.file.write(BEFORE_BAR)
self.file.write(' ' * self.max_width)
self.max_width = new_width
self.width = new_width
clear_width = self.width
if self.max_width is not None:
clear_width = self.max_width
self.file.write(BEFORE_BAR)
line = self.format_progress_line()
line_len = term_len(line)
if self.max_width is None or self.max_width < line_len:
self.max_width = line_len
# Use echo here so that we get colorama support.
echo(line, file=self.file, nl=False)
self.file.write(' ' * (clear_width - line_len))
self.file.flush()
def make_step(self):
self.pos += 1
if self.length_known and self.pos >= self.length:
self.finished = True
if (time.time() - self.last_eta) < 1.0:
return
self.last_eta = time.time()
self.avg = self.avg[-6:] + [-(self.start - time.time()) / (self.pos)]
self.eta_known = self.length_known
def finish(self):
self.eta_known = 0
self.current_item = None
self.finished = True
def next(self):
if self.is_hidden:
return next(self.iter)
try:
rv = next(self.iter)
self.current_item = rv
except StopIteration:
self.finish()
self.render_progress()
raise StopIteration()
else:
self.make_step()
self.render_progress()
return rv
if not PY2:
__next__ = next
del next
def pager(text, color=None):
"""Decide what method to use for paging through text."""
stdout = _default_text_stdout()
if not isatty(sys.stdin) or not isatty(stdout):
return _nullpager(stdout, text, color)
if 'PAGER' in os.environ:
if WIN:
return _tempfilepager(text, os.environ['PAGER'], color)
return _pipepager(text, os.environ['PAGER'], color)
if os.environ.get('TERM') in ('dumb', 'emacs'):
return _nullpager(stdout, text, color)
if WIN or sys.platform.startswith('os2'):
return _tempfilepager(text, 'more <', color)
if hasattr(os, 'system') and os.system('(less) 2>/dev/null') == 0:
return _pipepager(text, 'less', color)
import tempfile
fd, filename = tempfile.mkstemp()
os.close(fd)
try:
if hasattr(os, 'system') and os.system('more "%s"' % filename) == 0:
return _pipepager(text, 'more', color)
return _nullpager(stdout, text, color)
finally:
os.unlink(filename)
def _pipepager(text, cmd, color):
"""Page through text by feeding it to another program. Invoking a
pager through this might support colors.
"""
import subprocess
env = dict(os.environ)
# If we're piping to less we might support colors under the
# condition that
cmd_detail = cmd.rsplit('/', 1)[-1].split()
if color is None and cmd_detail[0] == 'less':
less_flags = os.environ.get('LESS', '') + ' '.join(cmd_detail[1:])
if not less_flags:
env['LESS'] = '-R'
color = True
elif 'r' in less_flags or 'R' in less_flags:
color = True
if not color:
text = strip_ansi(text)
c = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE,
env=env)
encoding = get_best_encoding(c.stdin)
try:
c.stdin.write(text.encode(encoding, 'replace'))
c.stdin.close()
except IOError:
pass
c.wait()
def _tempfilepager(text, cmd, color):
"""Page through text by invoking a program on a temporary file."""
import tempfile
filename = tempfile.mktemp()
if not color:
text = strip_ansi(text)
encoding = get_best_encoding(sys.stdout)
with open_stream(filename, 'wb')[0] as f:
f.write(text.encode(encoding))
try:
os.system(cmd + ' "' + filename + '"')
finally:
os.unlink(filename)
def _nullpager(stream, text, color):
"""Simply print unformatted text. This is the ultimate fallback."""
if not color:
text = strip_ansi(text)
stream.write(text)
class Editor(object):
def __init__(self, editor=None, env=None, require_save=True,
extension='.txt'):
self.editor = editor
self.env = env
self.require_save = require_save
self.extension = extension
def get_editor(self):
if self.editor is not None:
return self.editor
for key in 'VISUAL', 'EDITOR':
rv = os.environ.get(key)
if rv:
return rv
if WIN:
return 'notepad'
for editor in 'vim', 'nano':
if os.system('which %s &> /dev/null' % editor) == 0:
return editor
return 'vi'
def edit_file(self, filename):
import subprocess
editor = self.get_editor()
if self.env:
environ = os.environ.copy()
environ.update(self.env)
else:
environ = None
try:
c = subprocess.Popen('%s "%s"' % (editor, filename),
env=environ, shell=True)
exit_code = c.wait()
if exit_code != 0:
raise ClickException('%s: Editing failed!' % editor)
except OSError as e:
raise ClickException('%s: Editing failed: %s' % (editor, e))
def edit(self, text):
import tempfile
text = text or ''
if text and not text.endswith('\n'):
text += '\n'
fd, name = tempfile.mkstemp(prefix='editor-', suffix=self.extension)
try:
if WIN:
encoding = 'utf-8-sig'
text = text.replace('\n', '\r\n')
else:
encoding = 'utf-8'
text = text.encode(encoding)
f = os.fdopen(fd, 'wb')
f.write(text)
f.close()
timestamp = os.path.getmtime(name)
self.edit_file(name)
if self.require_save \
and os.path.getmtime(name) == timestamp:
return None
f = open(name, 'rb')
try:
rv = f.read()
finally:
f.close()
return rv.decode('utf-8-sig').replace('\r\n', '\n')
finally:
os.unlink(name)
def open_url(url, wait=False, locate=False):
import subprocess
def _unquote_file(url):
try:
import urllib
except ImportError:
import urllib
if url.startswith('file://'):
url = urllib.unquote(url[7:])
return url
if sys.platform == 'darwin':
args = ['open']
if wait:
args.append('-W')
if locate:
args.append('-R')
args.append(_unquote_file(url))
null = open('/dev/null', 'w')
try:
return subprocess.Popen(args, stderr=null).wait()
finally:
null.close()
elif WIN:
if locate:
url = _unquote_file(url)
args = 'explorer /select,"%s"' % _unquote_file(
url.replace('"', ''))
else:
args = 'start %s "" "%s"' % (
wait and '/WAIT' or '', url.replace('"', ''))
return os.system(args)
try:
if locate:
url = os.path.dirname(_unquote_file(url)) or '.'
else:
url = _unquote_file(url)
c = subprocess.Popen(['xdg-open', url])
if wait:
return c.wait()
return 0
except OSError:
if url.startswith(('http://', 'https://')) and not locate and not wait:
import webbrowser
webbrowser.open(url)
return 0
return 1
def _translate_ch_to_exc(ch):
if ch == '\x03':
raise KeyboardInterrupt()
if ch == '\x04':
raise EOFError()
if WIN:
import msvcrt
def getchar(echo):
rv = msvcrt.getch()
if echo:
msvcrt.putchar(rv)
_translate_ch_to_exc(rv)
if PY2:
enc = getattr(sys.stdin, 'encoding', None)
if enc is not None:
rv = rv.decode(enc, 'replace')
else:
rv = rv.decode('cp1252', 'replace')
return rv
else:
import tty
import termios
def getchar(echo):
if not isatty(sys.stdin):
f = open('/dev/tty')
fd = f.fileno()
else:
fd = sys.stdin.fileno()
f = None
try:
old_settings = termios.tcgetattr(fd)
try:
tty.setraw(fd)
ch = os.read(fd, 32)
if echo and isatty(sys.stdout):
sys.stdout.write(ch)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
sys.stdout.flush()
if f is not None:
f.close()
except termios.error:
pass
_translate_ch_to_exc(ch)
return ch.decode(get_best_encoding(sys.stdin), 'replace')

38
click/_textwrap.py Normal file
View file

@ -0,0 +1,38 @@
import textwrap
from contextlib import contextmanager
class TextWrapper(textwrap.TextWrapper):
def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
space_left = max(width - cur_len, 1)
if self.break_long_words:
last = reversed_chunks[-1]
cut = last[:space_left]
res = last[space_left:]
cur_line.append(cut)
reversed_chunks[-1] = res
elif not cur_line:
cur_line.append(reversed_chunks.pop())
@contextmanager
def extra_indent(self, indent):
old_initial_indent = self.initial_indent
old_subsequent_indent = self.subsequent_indent
self.initial_indent += indent
self.subsequent_indent += indent
try:
yield
finally:
self.initial_indent = old_initial_indent
self.subsequent_indent = old_subsequent_indent
def indent_only(self, text):
rv = []
for idx, line in enumerate(text.splitlines()):
indent = self.initial_indent
if idx > 0:
indent = self.subsequent_indent
rv.append(indent + line)
return '\n'.join(rv)

1575
click/core.py Normal file
View file

@ -0,0 +1,1575 @@
import os
import sys
import codecs
from contextlib import contextmanager
from itertools import chain, repeat
from functools import update_wrapper
from .types import convert_type, IntRange, BOOL
from .utils import make_str, make_default_short_help, echo
from .exceptions import ClickException, UsageError, BadParameter, Abort
from .termui import prompt, confirm
from .formatting import HelpFormatter, join_options
from .parser import OptionParser, split_opt
from ._compat import PY2, isidentifier, iteritems
_missing = object()
SUBCOMMAND_METAVAR = 'COMMAND [ARGS]...'
SUBCOMMANDS_METAVAR = 'COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]...'
def _bashcomplete(cmd, prog_name, complete_var=None):
"""Internal handler for the bash completion support."""
if complete_var is None:
complete_var = '_%s_COMPLETE' % (prog_name.replace('-', '_')).upper()
complete_instr = os.environ.get(complete_var)
if not complete_instr:
return
from ._bashcomplete import bashcomplete
if bashcomplete(cmd, prog_name, complete_var, complete_instr):
sys.exit(1)
def batch(iterable, batch_size):
return list(zip(*repeat(iter(iterable), batch_size)))
def invoke_param_callback(callback, ctx, param, value):
code = getattr(callback, '__code__', None)
args = getattr(code, 'co_argcount', 3)
if args < 3:
# This will become a warning in Click 3.0:
from warnings import warn
warn(Warning('Invoked legacy parameter callback "%s". The new '
'signature for such callbacks starting with '
'click 2.0 is (ctx, param, value).'
% callback), stacklevel=3)
return callback(ctx, value)
return callback(ctx, param, value)
@contextmanager
def augment_usage_errors(ctx, param=None):
"""Context manager that attaches extra information to exceptions that
fly.
"""
try:
yield
except BadParameter as e:
if e.ctx is None:
e.ctx = ctx
if param is not None and e.param is None:
e.param = param
raise
except UsageError as e:
if e.ctx is None:
e.ctx = ctx
raise
def iter_params_for_processing(invocation_order, declaration_order):
"""Given a sequence of parameters in the order as should be considered
for processing and an iterable of parameters that exist, this returns
a list in the correct order as they should be processed.
"""
def sort_key(item):
try:
idx = invocation_order.index(item)
except ValueError:
idx = float('inf')
return (not item.is_eager, idx)
return sorted(declaration_order, key=sort_key)
class Context(object):
"""The context is a special internal object that holds state relevant
for the script execution at every single level. It's normally invisible
to commands unless they opt-in to getting access to it.
The context is useful as it can pass internal objects around and can
control special execution features such as reading data from
environment variables.
A context can be used as context manager in which case it will call
:meth:`close` on teardown.
.. versionadded:: 2.0
Added the `resilient_parsing`, `help_option_names`,
`token_normalize_func` parameters.
.. versionadded:: 3.0
Added the `allow_extra_args` and `allow_interspersed_args`
parameters.
:param command: the command class for this context.
:param parent: the parent context.
:param info_name: the info name for this invokation. Generally this
is the most descriptive name for the script or
command. For the toplevel script is is usually
the name of the script, for commands below it it's
the name of the script.
:param obj: an arbitrary object of user data.
:param auto_envvar_prefix: the prefix to use for automatic environment
variables. If this is `None` then reading
from environment variables is disabled. This
does not affect manually set environment
variables which are always read.
:param default_map: a dictionary (like object) with default values
for parameters.
:param terminal_width: the width of the terminal. The default is
inherit from parent context. If no context
defines the terminal width then auto
detection will be applied.
:param resilient_parsing: if this flag is enabled then Click will
parse without any interactivity or callback
invocation. This is useful for implementing
things such as completion support.
:param allow_extra_args: if this is set to `True` then extra arguments
at the end will not raise an error and will be
kept on the context. The default is to inherit
from the command.
:param allow_interspersed_args: if this is set to `False` then options
and arguments cannot be mixed. The
default is to inherit from the command.
:param help_option_names: optionally a list of strings that define how
the default help parameter is named. The
default is ``['--help']``.
:param token_normalize_func: an optional function that is used to
normalize tokens (options, choices,
etc.). This for instance can be used to
implement case insensitive behavior.
"""
def __init__(self, command, parent=None, info_name=None, obj=None,
auto_envvar_prefix=None, default_map=None,
terminal_width=None, resilient_parsing=False,
allow_extra_args=None, allow_interspersed_args=None,
help_option_names=None, token_normalize_func=None):
#: the parent context or `None` if none exists.
self.parent = parent
#: the :class:`Command` for this context.
self.command = command
#: the descriptive information name
self.info_name = info_name
#: the parsed parameters except if the value is hidden in which
#: case it's not remembered.
self.params = {}
#: the leftover arguments.
self.args = []
if obj is None and parent is not None:
obj = parent.obj
#: the user object stored.
self.obj = obj
#: A dictionary (-like object) with defaults for parameters.
if default_map is None \
and parent is not None \
and parent.default_map is not None:
default_map = parent.default_map.get(info_name)
self.default_map = default_map
#: This flag indicates if a subcommand is going to be executed. A
#: group callback can use this information to figure out if it's
#: being executed directly or because the execution flow passes
#: onwards to a subcommand. By default it's None, but it can be
#: the name of the subcommand to execute.
#:
#: If chaining is enabled this will be set to ``'*'`` in case
#: any commands are executed. It is however not possible to
#: figure out which ones. If you require this knowledge you
#: should use a :func:`resultcallback`.
self.invoked_subcommand = None
if terminal_width is None and parent is not None:
terminal_width = parent.terminal_width
#: The width of the terminal (None is autodetection).
self.terminal_width = terminal_width
if allow_extra_args is None:
allow_extra_args = command.allow_extra_args
#: Indicates if the context allows extra args or if it should
#: fail on parsing.
#:
#: .. versionadded:: 3.0
self.allow_extra_args = allow_extra_args
if allow_interspersed_args is None:
allow_interspersed_args = command.allow_interspersed_args
#: Indicates if the context allows mixing of arguments and
#: options or not.
#:
#: .. versionadded:: 3.0
self.allow_interspersed_args = allow_interspersed_args
if help_option_names is None:
if parent is not None:
help_option_names = parent.help_option_names
else:
help_option_names = ['--help']
#: The names for the help options.
self.help_option_names = help_option_names
if token_normalize_func is None and parent is not None:
token_normalize_func = parent.token_normalize_func
#: An optional normalization function for tokens. This is
#: options, choices, commands etc.
self.token_normalize_func = token_normalize_func
#: Indicates if resilient parsing is enabled. In that case Click
#: will do its best to not cause any failures.
self.resilient_parsing = resilient_parsing
# If there is no envvar prefix yet, but the parent has one and
# the command on this level has a name, we can expand the envvar
# prefix automatically.
if auto_envvar_prefix is None:
if parent is not None \
and parent.auto_envvar_prefix is not None and \
self.info_name is not None:
auto_envvar_prefix = '%s_%s' % (parent.auto_envvar_prefix,
self.info_name.upper())
else:
self.auto_envvar_prefix = auto_envvar_prefix.upper()
self.auto_envvar_prefix = auto_envvar_prefix
self._close_callbacks = []
self._depth = 0
def __enter__(self):
self._depth += 1
return self
def __exit__(self, exc_type, exc_value, tb):
self._depth -= 1
if self._depth == 0:
self.close()
def _get_invoked_subcommands(self):
from warnings import warn
warn(Warning('This API does not work properly and has been largely '
'removed in Click 3.2 to fix a regression the '
'introduction of this API caused. Consult the '
'upgrade documentation for more information. For '
'more information about this see '
'http://click.pocoo.org/upgrading/#upgrading-to-3.2'),
stacklevel=2)
if self.invoked_subcommand is None:
return []
return [self.invoked_subcommand]
def _set_invoked_subcommands(self, value):
self.invoked_subcommand = \
len(value) > 1 and '*' or value and value[0] or None
invoked_subcommands = property(_get_invoked_subcommands,
_set_invoked_subcommands)
del _get_invoked_subcommands, _set_invoked_subcommands
def make_formatter(self):
"""Creates the formatter for the help and usage output."""
return HelpFormatter(width=self.terminal_width)
def call_on_close(self, f):
"""This decorator remembers a function as callback that should be
executed when the context tears down. This is most useful to bind
resource handling to the script execution. For instance, file objects
opened by the :class:`File` type will register their close callbacks
here.
:param f: the function to execute on teardown.
"""
self._close_callbacks.append(f)
return f
def close(self):
"""Invokes all close callbacks."""
for cb in self._close_callbacks:
cb()
self._close_callbacks = []
@property
def command_path(self):
"""The computed command path. This is used for the ``usage``
information on the help page. It's automatically created by
combining the info names of the chain of contexts to the root.
"""
rv = ''
if self.info_name is not None:
rv = self.info_name
if self.parent is not None:
rv = self.parent.command_path + ' ' + rv
return rv.lstrip()
def find_root(self):
"""Finds the outermost context."""
node = self
while node.parent is not None:
node = node.parent
return node
def find_object(self, object_type):
"""Finds the closest object of a given type."""
node = self
while node is not None:
if isinstance(node.obj, object_type):
return node.obj
node = node.parent
def ensure_object(self, object_type):
"""Like :meth:`find_object` but sets the innermost object to a
new instance of `object_type` if it does not exist.
"""
rv = self.find_object(object_type)
if rv is None:
self.obj = rv = object_type()
return rv
def lookup_default(self, name):
"""Looks up the default for a parameter name. This by default
looks into the :attr:`default_map` if available.
"""
if self.default_map is not None:
rv = self.default_map.get(name)
if callable(rv):
rv = rv()
return rv
def fail(self, message):
"""Aborts the execution of the program with a specific error
message.
:param message: the error message to fail with.
"""
raise UsageError(message, self)
def abort(self):
"""Aborts the script."""
raise Abort()
def exit(self, code=0):
"""Exits the application with a given exit code."""
sys.exit(code)
def get_usage(self):
"""Helper method to get formatted usage string for the current
context and command.
"""
return self.command.get_usage(self)
def get_help(self):
"""Helper method to get formatted help page for the current
context and command.
"""
return self.command.get_help(self)
def invoke(*args, **kwargs):
"""Invokes a command callback in exactly the way it expects. There
are two ways to invoke this method:
1. the first argument can be a callback and all other arguments and
keyword arguments are forwarded directly to the function.
2. the first argument is a click command object. In that case all
arguments are forwarded as well but proper click parameters
(options and click arguments) must be keyword arguments and Click
will fill in defaults.
Note that before Click 3.2 keyword arguments were not properly filled
in against the intention of this code and no context was created. For
more information about this change and why it was done in a bugfix
release see :ref:`upgrade-to-3.2`.
"""
self, callback = args[:2]
ctx = self
# This is just to improve the error message in cases where old
# code incorrectly invoked this method. This will eventually be
# removed.
injected_arguments = False
# It's also possible to invoke another command which might or
# might not have a callback. In that case we also fill
# in defaults and make a new context for this command.
if isinstance(callback, Command):
other_cmd = callback
callback = other_cmd.callback
ctx = Context(other_cmd, info_name=other_cmd.name, parent=self)
if callback is None:
raise TypeError('The given command does not have a '
'callback that can be invoked.')
for param in other_cmd.params:
if param.name not in kwargs and param.expose_value:
kwargs[param.name] = param.get_default(ctx)
injected_arguments = True
args = args[2:]
if getattr(callback, '__click_pass_context__', False):
args = (ctx,) + args
with augment_usage_errors(self):
try:
with ctx:
return callback(*args, **kwargs)
except TypeError as e:
if not injected_arguments:
raise
if 'got multiple values for' in str(e):
raise RuntimeError(
'You called .invoke() on the context with a command '
'but provided parameters as positional arguments. '
'This is not supported but sometimes worked by chance '
'in older versions of Click. To fix this see '
'http://click.pocoo.org/upgrading/#upgrading-to-3.2')
raise
def forward(*args, **kwargs):
"""Similar to :meth:`invoke` but fills in default keyword
arguments from the current context if the other command expects
it. This cannot invoke callbacks directly, only other commands.
"""
self, cmd = args[:2]
# It's also possible to invoke another command which might or
# might not have a callback.
if not isinstance(cmd, Command):
raise TypeError('Callback is not a command.')
for param in self.params:
if param not in kwargs:
kwargs[param] = self.params[param]
return self.invoke(cmd, **kwargs)
class BaseCommand(object):
"""The base command implements the minimal API contract of commands.
Most code will never use this as it does not implement a lot of useful
functionality but it can act as the direct subclass of alternative
parsing methods that do not depend on the Click parser.
For instance, this can be used to bridge Click and other systems like
argparse or docopt.
Because base commands do not implement a lot of the API that other
parts of Click take for granted, they are not supported for all
operations. For instance, they cannot be used with the decorators
usually and they have no built-in callback system.
.. versionchanged:: 2.0
Added the `context_settings` parameter.
:param name: the name of the command to use unless a group overrides it.
:param context_settings: an optional dictionary with defaults that are
passed to the context object.
"""
allow_extra_args = False
allow_interspersed_args = True
def __init__(self, name, context_settings=None):
#: the name the command thinks it has. Upon registering a command
#: on a :class:`Group` the group will default the command name
#: with this information. You should instead use the
#: :class:`Context`\'s :attr:`~Context.info_name` attribute.
self.name = name
#: an optional dictionary with defaults passed to the context.
self.context_settings = context_settings
def get_usage(self, ctx):
raise NotImplementedError('Base commands cannot get usage')
def get_help(self, ctx):
raise NotImplementedError('Base commands cannot get help')
def make_context(self, info_name, args, parent=None, **extra):
"""This function when given an info name and arguments will kick
off the parsing and create a new :class:`Context`. It does not
invoke the actual command callback though.
:param info_name: the info name for this invokation. Generally this
is the most descriptive name for the script or
command. For the toplevel script it's usually
the name of the script, for commands below it it's
the name of the script.
:param args: the arguments to parse as list of strings.
:param parent: the parent context if available.
:param extra: extra keyword arguments forwarded to the context
constructor.
"""
for key, value in iteritems(self.context_settings or {}):
if key not in extra:
extra[key] = value
ctx = Context(self, info_name=info_name, parent=parent, **extra)
self.parse_args(ctx, args)
return ctx
def parse_args(self, ctx, args):
"""Given a context and a list of arguments this creates the parser
and parses the arguments, then modifies the context as necessary.
This is automatically invoked by :meth:`make_context`.
"""
raise NotImplementedError('Base commands do not know how to parse '
'arguments.')
def invoke(self, ctx):
"""Given a context, this invokes the command. The default
implementation is raising a not implemented error.
"""
raise NotImplementedError('Base commands are not invokable by default')
def main(self, args=None, prog_name=None, complete_var=None,
standalone_mode=True, **extra):
"""This is the way to invoke a script with all the bells and
whistles as a command line application. This will always terminate
the application after a call. If this is not wanted, ``SystemExit``
needs to be caught.
This method is also available by directly calling the instance of
a :class:`Command`.
.. versionadded:: 3.0
Added the `standalone_mode` flag to control the standalone mode.
:param args: the arguments that should be used for parsing. If not
provided, ``sys.argv[1:]`` is used.
:param prog_name: the program name that should be used. By default
the program name is constructed by taking the file
name from ``sys.argv[0]``.
:param complete_var: the environment variable that controls the
bash completion support. The default is
``"_<prog_name>_COMPLETE"`` with prog name in
uppercase.
:param standalone_mode: the default behavior is to invoke the script
in standalone mode. Click will then
handle exceptions and convert them into
error messages and the function will never
return but shut down the interpreter. If
this is set to `False` they will be
propagated to the caller and the return
value of this function is the return value
of :meth:`invoke`.
:param extra: extra keyword arguments are forwarded to the context
constructor. See :class:`Context` for more information.
"""
# If we are in Python 3, we will verify that the environment is
# sane at this point of reject further execution to avoid a
# broken script.
if not PY2:
try:
import locale
fs_enc = codecs.lookup(locale.getpreferredencoding()).name
except Exception:
fs_enc = 'ascii'
if fs_enc == 'ascii':
raise RuntimeError('Click will abort further execution '
'because Python 3 was configured to use '
'ASCII as encoding for the environment. '
'Either switch to Python 2 or consult '
'http://click.pocoo.org/python3/ '
'for mitigation steps.')
if args is None:
args = sys.argv[1:]
else:
args = list(args)
if prog_name is None:
prog_name = make_str(os.path.basename(
sys.argv and sys.argv[0] or __file__))
# Hook for the Bash completion. This only activates if the Bash
# completion is actually enabled, otherwise this is quite a fast
# noop.
_bashcomplete(self, prog_name, complete_var)
try:
try:
with self.make_context(prog_name, args, **extra) as ctx:
rv = self.invoke(ctx)
if not standalone_mode:
return rv
ctx.exit()
except (EOFError, KeyboardInterrupt):
echo(file=sys.stderr)
raise Abort()
except ClickException as e:
if not standalone_mode:
raise
e.show()
sys.exit(e.exit_code)
except Abort:
if not standalone_mode:
raise
echo('Aborted!', file=sys.stderr)
sys.exit(1)
def __call__(self, *args, **kwargs):
"""Alias for :meth:`main`."""
return self.main(*args, **kwargs)
class Command(BaseCommand):
"""Commands are the basic building block of command line interfaces in
Click. A basic command handles command line parsing and might dispatch
more parsing to commands nested below it.
.. versionchanged:: 2.0
Added the `context_settings` parameter.
:param name: the name of the command to use unless a group overrides it.
:param context_settings: an optional dictionary with defaults that are
passed to the context object.
:param callback: the callback to invoke. This is optional.
:param params: the parameters to register with this command. This can
be either :class:`Option` or :class:`Argument` objects.
:param help: the help string to use for this command.
:param epilog: like the help string but it's printed at the end of the
help page after everything else.
:param short_help: the short help to use for this command. This is
shown on the command listing of the parent command.
:param add_help_option: by default each command registers a ``--help``
option. This can be disabled by this parameter.
"""
def __init__(self, name, context_settings=None, callback=None,
params=None, help=None, epilog=None, short_help=None,
options_metavar='[OPTIONS]', add_help_option=True):
BaseCommand.__init__(self, name, context_settings)
#: the callback to execute when the command fires. This might be
#: `None` in which case nothing happens.
self.callback = callback
#: the list of parameters for this command in the order they
#: should show up in the help page and execute. Eager parameters
#: will automatically be handled before non eager ones.
self.params = params or []
self.help = help
self.epilog = epilog
self.options_metavar = options_metavar
if short_help is None and help:
short_help = make_default_short_help(help)
self.short_help = short_help
self.add_help_option = add_help_option
def get_usage(self, ctx):
formatter = ctx.make_formatter()
self.format_usage(ctx, formatter)
return formatter.getvalue().rstrip('\n')
def get_params(self, ctx):
rv = self.params
help_option = self.get_help_option(ctx)
if help_option is not None:
rv = rv + [help_option]
return rv
def format_usage(self, ctx, formatter):
"""Writes the usage line into the formatter."""
pieces = self.collect_usage_pieces(ctx)
formatter.write_usage(ctx.command_path, ' '.join(pieces))
def collect_usage_pieces(self, ctx):
"""Returns all the pieces that go into the usage line and returns
it as a list of strings.
"""
rv = [self.options_metavar]
for param in self.get_params(ctx):
rv.extend(param.get_usage_pieces(ctx))
return rv
def get_help_option_names(self, ctx):
"""Returns the names for the help option."""
all_names = set(ctx.help_option_names)
for param in self.params:
all_names.difference_update(param.opts)
all_names.difference_update(param.secondary_opts)
return all_names
def get_help_option(self, ctx):
"""Returns the help option object."""
help_options = self.get_help_option_names(ctx)
if not help_options:
return
def show_help(ctx, param, value):
if value and not ctx.resilient_parsing:
echo(ctx.get_help())
ctx.exit()
return Option(help_options, is_flag=True,
is_eager=True, expose_value=False,
callback=show_help,
help='Show this message and exit.')
def make_parser(self, ctx):
"""Creates the underlying option parser for this command."""
parser = OptionParser(ctx)
parser.allow_interspersed_args = ctx.allow_interspersed_args
for param in self.get_params(ctx):
param.add_to_parser(parser, ctx)
return parser
def get_help(self, ctx):
"""Formats the help into a string and returns it. This creates a
formatter and will call into the following formatting methods:
"""
formatter = ctx.make_formatter()
self.format_help(ctx, formatter)
return formatter.getvalue().rstrip('\n')
def format_help(self, ctx, formatter):
"""Writes the help into the formatter if it exists.
This calls into the following methods:
- :meth:`format_usage`
- :meth:`format_help_text`
- :meth:`format_options`
- :meth:`format_epilog`
"""
self.format_usage(ctx, formatter)
self.format_help_text(ctx, formatter)
self.format_options(ctx, formatter)
self.format_epilog(ctx, formatter)
def format_help_text(self, ctx, formatter):
"""Writes the help text to the formatter if it exists."""
if self.help:
formatter.write_paragraph()
with formatter.indentation():
formatter.write_text(self.help)
def format_options(self, ctx, formatter):
"""Writes all the options into the formatter if they exist."""
opts = []
for param in self.get_params(ctx):
rv = param.get_help_record(ctx)
if rv is not None:
opts.append(rv)
if opts:
with formatter.section('Options'):
formatter.write_dl(opts)
def format_epilog(self, ctx, formatter):
"""Writes the epilog into the formatter if it exists."""
if self.epilog:
formatter.write_paragraph()
with formatter.indentation():
formatter.write_text(self.epilog)
def parse_args(self, ctx, args):
parser = self.make_parser(ctx)
opts, args, param_order = parser.parse_args(args=args)
for param in iter_params_for_processing(
param_order, self.get_params(ctx)):
value, args = param.handle_parse_result(ctx, opts, args)
if args and not ctx.allow_extra_args and not ctx.resilient_parsing:
ctx.fail('Got unexpected extra argument%s (%s)'
% (len(args) != 1 and 's' or '',
' '.join(map(make_str, args))))
ctx.args = args
return args
def invoke(self, ctx):
"""Given a context, this invokes the attached callback (if it exists)
in the right way.
"""
if self.callback is not None:
return ctx.invoke(self.callback, **ctx.params)
class MultiCommand(Command):
"""A multi command is the basic implementation of a command that
dispatches to subcommands. The most common version is the
:class:`Command`.
:param invoke_without_command: this controls how the multi command itself
is invoked. By default it's only invoked
if a subcommand is provided.
:param no_args_is_help: this controls what happens if no arguments are
provided. This option is enabled by default if
`invoke_without_command` is disabled or disabled
if it's enabled. If enabled this will add
``--help`` as argument if no arguments are
passed.
:param subcommand_metavar: the string that is used in the documentation
to indicate the subcommand place.
:param chain: if this is set to `True` chaining of multiple subcommands
is enabled. This restricts the form of commands in that
they cannot have optional arguments but it allows
multiple commands to be chained together.
:param result_callback: the result callback to attach to this multi
command.
"""
allow_extra_args = True
allow_interspersed_args = False
def __init__(self, name=None, invoke_without_command=False,
no_args_is_help=None, subcommand_metavar=None,
chain=False, result_callback=None, **attrs):
Command.__init__(self, name, **attrs)
if no_args_is_help is None:
no_args_is_help = not invoke_without_command
self.no_args_is_help = no_args_is_help
self.invoke_without_command = invoke_without_command
if subcommand_metavar is None:
if chain:
subcommand_metavar = SUBCOMMANDS_METAVAR
else:
subcommand_metavar = SUBCOMMAND_METAVAR
self.subcommand_metavar = subcommand_metavar
self.chain = chain
#: The result callback that is stored. This can be set or
#: overridden with the :func:`resultcallback` decorator.
self.result_callback = result_callback
def collect_usage_pieces(self, ctx):
rv = Command.collect_usage_pieces(self, ctx)
rv.append(self.subcommand_metavar)
return rv
def format_options(self, ctx, formatter):
Command.format_options(self, ctx, formatter)
self.format_commands(ctx, formatter)
def resultcallback(self, replace=False):
"""Adds a result callback to the chain command. By default if a
result callback is already registered this will chain them but
this can be disabled with the `replace` parameter. The result
callback is invoked with the return value of the subcommand
(or the list of return values from all subcommands if chaining
is enabled) as well as the parameters as they would be passed
to the main callback.
Example::
@click.group()
@click.option('-i', '--input', default=23)
def cli(input):
return 42
@cli.resultcallback()
def process_result(result, input):
return result + input
.. versionadded:: 3.0
:param replace: if set to `True` an already existing result
callback will be removed.
"""
def decorator(f):
old_callback = self.result_callback
if old_callback is None or replace:
self.result_callback = f
return f
def function(__value, *args, **kwargs):
return f(old_callback(__value, *args, **kwargs),
*args, **kwargs)
self.result_callback = rv = update_wrapper(function, f)
return rv
return decorator
def format_commands(self, ctx, formatter):
"""Extra format methods for multi methods that adds all the commands
after the options.
"""
rows = []
for subcommand in self.list_commands(ctx):
cmd = self.get_command(ctx, subcommand)
# What is this, the tool lied about a command. Ignore it
if cmd is None:
continue
help = cmd.short_help or ''
rows.append((subcommand, help))
if rows:
with formatter.section('Commands'):
formatter.write_dl(rows)
def parse_args(self, ctx, args):
if not args and self.no_args_is_help and not ctx.resilient_parsing:
echo(ctx.get_help())
ctx.exit()
return Command.parse_args(self, ctx, args)
def invoke(self, ctx):
def _process_result(value):
if self.result_callback is not None:
value = ctx.invoke(self.result_callback, value,
**ctx.params)
return value
if not ctx.args:
# If we are invoked without command the chain flag controls
# how this happens. If we are not in chain mode, the return
# value here is the return value of the command.
# If however we are in chain mode, the return value is the
# return value of the result processor invoked with an empty
# list (which means that no subcommand actually was executed).
if self.invoke_without_command:
if not self.chain:
return Command.invoke(self, ctx)
with ctx:
Command.invoke(self, ctx)
return _process_result([])
ctx.fail('Missing command.')
args = ctx.args
# If we're not in chain mode, we only allow the invocation of a
# single command but we also inform the current context about the
# name of the command to invoke.
if not self.chain:
# Make sure the context is entered so we do not clean up
# resources until the result processor has worked.
with ctx:
cmd_name, cmd, args = self.resolve_command(ctx, args)
ctx.invoked_subcommand = cmd_name
Command.invoke(self, ctx)
sub_ctx = cmd.make_context(cmd_name, args, parent=ctx)
with sub_ctx:
return _process_result(sub_ctx.command.invoke(sub_ctx))
# In chain mode we create the contexts step by step, but after the
# base command has been invoked. Because at that point we do not
# know the subcommands yet, the invoked subcommand attribute is
# set to ``*`` to inform the command that subcommands are executed
# but nothing else.
with ctx:
ctx.invoked_subcommand = args and '*' or None
Command.invoke(self, ctx)
# Otherwise we make every single context and invoke them in a
# chain. In that case the return value to the result processor
# is the list of all invoked subcommand's results.
contexts = []
while args:
cmd_name, cmd, args = self.resolve_command(ctx, args)
sub_ctx = cmd.make_context(cmd_name, args, parent=ctx,
allow_extra_args=True,
allow_interspersed_args=False)
contexts.append(sub_ctx)
args = sub_ctx.args
rv = []
for sub_ctx in contexts:
with sub_ctx:
rv.append(sub_ctx.command.invoke(sub_ctx))
return _process_result(rv)
def resolve_command(self, ctx, args):
cmd_name = make_str(args[0])
original_cmd_name = cmd_name
# Get the command
cmd = self.get_command(ctx, cmd_name)
# If we can't find the command but there is a normalization
# function available, we try with that one.
if cmd is None and ctx.token_normalize_func is not None:
cmd_name = ctx.token_normalize_func(cmd_name)
cmd = self.get_command(ctx, cmd_name)
# If we don't find the command we want to show an error message
# to the user that it was not provided. However, there is
# something else we should do: if the first argument looks like
# an option we want to kick off parsing again for arguments to
# resolve things like --help which now should go to the main
# place.
if cmd is None:
if split_opt(cmd_name)[0]:
self.parse_args(ctx, ctx.args)
ctx.fail('No such command "%s".' % original_cmd_name)
return cmd_name, cmd, args[1:]
def get_command(self, ctx, cmd_name):
"""Given a context and a command name, this returns a
:class:`Command` object if it exists or returns `None`.
"""
raise NotImplementedError()
def list_commands(self, ctx):
"""Returns a list of subcommand names in the order they should
appear.
"""
return []
class Group(MultiCommand):
"""A group allows a command to have subcommands attached. This is the
most common way to implement nesting in Click.
:param commands: a dictionary of commands.
"""
def __init__(self, name=None, commands=None, **attrs):
MultiCommand.__init__(self, name, **attrs)
#: the registered subcommands by their exported names.
self.commands = commands or {}
def add_command(self, cmd, name=None):
"""Registers another :class:`Command` with this group. If the name
is not provided, the name of the command is used.
"""
name = name or cmd.name
if name is None:
raise TypeError('Command has no name.')
self.commands[name] = cmd
def command(self, *args, **kwargs):
"""A shortcut decorator for declaring and attaching a command to
the group. This takes the same arguments as :func:`command` but
immediately registers the created command with this instance by
calling into :meth:`add_command`.
"""
def decorator(f):
cmd = command(*args, **kwargs)(f)
self.add_command(cmd)
return cmd
return decorator
def group(self, *args, **kwargs):
"""A shortcut decorator for declaring and attaching a group to
the group. This takes the same arguments as :func:`group` but
immediately registers the created command with this instance by
calling into :meth:`add_command`.
"""
def decorator(f):
cmd = group(*args, **kwargs)(f)
self.add_command(cmd)
return cmd
return decorator
def get_command(self, ctx, cmd_name):
return self.commands.get(cmd_name)
def list_commands(self, ctx):
return sorted(self.commands)
class CommandCollection(MultiCommand):
"""A command collection is a multi command that merges multiple multi
commands together into one. This is a straightforward implementation
that accepts a list of different multi commands as sources and
provides all the commands for each of them.
"""
def __init__(self, name=None, sources=None, **attrs):
MultiCommand.__init__(self, name, **attrs)
#: The list of registered multi commands.
self.sources = sources or []
def add_source(self, multi_cmd):
"""Adds a new multi command to the chain dispatcher."""
self.sources.append(multi_cmd)
def get_command(self, ctx, cmd_name):
for source in self.sources:
rv = source.get_command(ctx, cmd_name)
if rv is not None:
return rv
def list_commands(self, ctx):
rv = set()
for source in self.sources:
rv.update(source.list_commands(ctx))
return sorted(rv)
class Parameter(object):
"""A parameter to a command comes in two versions: they are either
:class:`Option`\s or :class:`Argument`\s. Other subclasses are currently
not supported by design as some of the internals for parsing are
intentionally not finalized.
Some settings are supported by both options and arguments.
.. versionchanged:: 2.0
Changed signature for parameter callback to also be passed the
parameter. In Click 2.0, the old callback format will still work,
but it will raise a warning to give you change to migrate the
code easier.
:param param_decls: the parameter declarations for this option or
argument. This is a list of flags or argument
names.
:param type: the type that should be used. Either a :class:`ParamType`
or a Python type. The later is converted into the former
automatically if supported.
:param required: controls if this is optional or not.
:param default: the default value if omitted. This can also be a callable,
in which case it's invoked when the default is needed
without any arguments.
:param callback: a callback that should be executed after the parameter
was matched. This is called as ``fn(ctx, param,
value)`` and needs to return the value. Before Click
2.0, the signature was ``(ctx, value)``.
:param nargs: the number of arguments to match. If not ``1`` the return
value is a tuple instead of single value.
:param metavar: how the value is represented in the help page.
:param expose_value: if this is `True` then the value is passed onwards
to the command callback and stored on the context,
otherwise it's skipped.
:param is_eager: eager values are processed before non eager ones. This
should not be set for arguments or it will inverse the
order of processing.
:param envvar: a string or list of strings that are environment variables
that should be checked.
"""
param_type_name = 'parameter'
def __init__(self, param_decls=None, type=None, required=False,
default=None, callback=None, nargs=1, metavar=None,
expose_value=True, is_eager=False, envvar=None):
self.name, self.opts, self.secondary_opts = \
self._parse_decls(param_decls or (), expose_value)
self.type = convert_type(type, default)
self.required = required
self.callback = callback
self.nargs = nargs
self.multiple = False
self.expose_value = expose_value
self.default = default
self.is_eager = is_eager
self.metavar = metavar
self.envvar = envvar
def make_metavar(self):
if self.metavar is not None:
return self.metavar
metavar = self.type.get_metavar(self)
if metavar is None:
metavar = self.type.name.upper()
if self.nargs != 1:
metavar += '...'
return metavar
def get_default(self, ctx):
"""Given a context variable this calculates the default value."""
# Otherwise go with the regular default.
if callable(self.default):
rv = self.default()
else:
rv = self.default
return self.type_cast_value(ctx, rv)
def add_to_parser(self, parser, ctx):
pass
def consume_value(self, ctx, opts):
value = opts.get(self.name)
if value is None:
value = ctx.lookup_default(self.name)
if value is None:
value = self.value_from_envvar(ctx)
return value
def type_cast_value(self, ctx, value):
"""Given a value this runs it properly through the type system.
This automatically handles things like `nargs` and `multiple`.
"""
def _convert(value, level):
if level == 0:
return self.type(value, self, ctx)
return tuple(_convert(x, level - 1) for x in value or ())
return _convert(value, (self.nargs != 1) + bool(self.multiple))
def process_value(self, ctx, value):
"""Given a value and context this runs the logic to convert the
value as necessary.
"""
# If the value we were given is None we do nothing. This way
# code that calls this can easily figure out if something was
# not provided. Otherwise it would be converted into an empty
# tuple for multiple invocations which is inconvenient.
if value is not None:
return self.type_cast_value(ctx, value)
def value_is_missing(self, value):
if value is None:
return True
if (self.nargs != 1 or self.multiple) and value == ():
return True
return False
def full_process_value(self, ctx, value):
value = self.process_value(ctx, value)
if value is None:
value = self.get_default(ctx)
if self.required and self.value_is_missing(value):
ctx.fail(self.get_missing_message(ctx))
return value
def get_missing_message(self, ctx):
rv = 'Missing %s %s.' % (
self.param_type_name,
' / '.join('"%s"' % x for x in chain(
self.opts, self.secondary_opts)),
)
extra = self.type.get_missing_message(self)
if extra:
rv += ' ' + extra
return rv
def resolve_envvar_value(self, ctx):
if self.envvar is None:
return
if isinstance(self.envvar, (tuple, list)):
for envvar in self.envvar:
rv = os.environ.get(envvar)
if rv is not None:
return rv
else:
return os.environ.get(self.envvar)
def value_from_envvar(self, ctx):
rv = self.resolve_envvar_value(ctx)
if rv is not None and self.nargs != 1:
rv = self.type.split_envvar_value(rv)
return rv
def handle_parse_result(self, ctx, opts, args):
with augment_usage_errors(ctx, param=self):
value = self.consume_value(ctx, opts)
try:
value = self.full_process_value(ctx, value)
except Exception:
if not ctx.resilient_parsing:
raise
value = None
if self.callback is not None:
try:
value = invoke_param_callback(
self.callback, ctx, self, value)
except Exception:
if not ctx.resilient_parsing:
raise
if self.expose_value:
ctx.params[self.name] = value
return value, args
def get_help_record(self, ctx):
pass
def get_usage_pieces(self, ctx):
return []
class Option(Parameter):
"""Options are usually optional values on the command line and
have some extra features that arguments don't have.
All other parameters are passed onwards to the parameter constructor.
:param show_default: controls if the default value should be shown on the
help page. Normally, defaults are not shown.
:param prompt: if set to `True` or a non empty string then the user will
be prompted for input if not set. If set to `True` the
prompt will be the option name capitalized.
:param confirmation_prompt: if set then the value will need to be confirmed
if it was prompted for.
:param hide_input: if this is `True` then the input on the prompt will be
hidden from the user. This is useful for password
input.
:param is_flag: forces this option to act as a flag. The default is
auto detection.
:param flag_value: which value should be used for this flag if it's
enabled. This is set to a boolean automatically if
the option string contains a slash to mark two options.
:param multiple: if this is set to `True` then the argument is accepted
multiple times and recorded. This is similar to ``nargs``
in how it works but supports arbitrary number of
arguments.
:param count: this flag makes an option increment an integer.
:param allow_from_autoenv: if this is enabled then the value of this
parameter will be pulled from an environment
variable in case a prefix is defined on the
context.
:param help: the help string.
"""
param_type_name = 'option'
def __init__(self, param_decls=None, show_default=False,
prompt=False, confirmation_prompt=False,
hide_input=False, is_flag=None, flag_value=None,
multiple=False, count=False, allow_from_autoenv=True,
type=None, help=None, **attrs):
default_is_missing = attrs.get('default', _missing) is _missing
Parameter.__init__(self, param_decls, type=type, **attrs)
if prompt is True:
prompt_text = self.name.replace('_', ' ').capitalize()
elif prompt is False:
prompt_text = None
else:
prompt_text = prompt
self.prompt = prompt_text
self.confirmation_prompt = confirmation_prompt
self.hide_input = hide_input
# Flags
if is_flag is None:
if flag_value is not None:
is_flag = True
else:
is_flag = bool(self.secondary_opts)
if is_flag and default_is_missing:
self.default = False
if flag_value is None:
flag_value = not self.default
self.is_flag = is_flag
self.flag_value = flag_value
if self.is_flag and isinstance(self.flag_value, bool) \
and type is None:
self.type = BOOL
self.is_bool_flag = True
else:
self.is_bool_flag = False
# Counting
self.count = count
if count:
if type is None:
self.type = IntRange(min=0)
if default_is_missing:
self.default = 0
self.multiple = multiple
self.allow_from_autoenv = allow_from_autoenv
self.help = help
self.show_default = show_default
# Sanity check for stuff we don't support
if __debug__:
if self.prompt and self.is_flag and not self.is_bool_flag:
raise TypeError('Cannot prompt for flags that are not bools.')
if not self.is_bool_flag and self.secondary_opts:
raise TypeError('Got secondary option for non boolean flag.')
if self.is_bool_flag and self.hide_input \
and self.prompt is not None:
raise TypeError('Hidden input does not work with boolean '
'flag prompts.')
if self.count:
if self.multiple:
raise TypeError('Options cannot be multiple and count '
'at the same time.')
elif self.is_flag:
raise TypeError('Options cannot be count and flags at '
'the same time.')
def _parse_decls(self, decls, expose_value):
opts = []
secondary_opts = []
name = None
possible_names = []
for decl in decls:
if isidentifier(decl):
if name is not None:
raise TypeError('Name defined twice')
name = decl
else:
split_char = decl[:1] == '/' and ';' or '/'
if split_char in decl:
first, second = decl.split(split_char, 1)
first = first.rstrip()
possible_names.append(split_opt(first))
opts.append(first)
secondary_opts.append(second.lstrip())
else:
possible_names.append(split_opt(decl))
opts.append(decl)
if name is None and possible_names:
possible_names.sort(key=lambda x: len(x[0]))
name = possible_names[-1][1].replace('-', '_').lower()
if not isidentifier(name):
name = None
if name is None:
if not expose_value:
return None, opts, secondary_opts
raise TypeError('Could not determine name for option')
if not opts and not secondary_opts:
raise TypeError('No options defined but a name was passed (%s). '
'Did you mean to declare an argument instead '
'of an option?' % name)
return name, opts, secondary_opts
def add_to_parser(self, parser, ctx):
kwargs = {
'dest': self.name,
'nargs': self.nargs,
'obj': self,
}
if self.multiple:
action = 'append'
elif self.count:
action = 'count'
else:
action = 'store'
if self.is_flag:
kwargs.pop('nargs', None)
if self.is_bool_flag and self.secondary_opts:
parser.add_option(self.opts, action=action + '_const',
const=True, **kwargs)
parser.add_option(self.secondary_opts, action=action +
'_const', const=False, **kwargs)
else:
parser.add_option(self.opts, action=action + '_const',
const=self.flag_value,
**kwargs)
else:
kwargs['action'] = action
parser.add_option(self.opts, **kwargs)
def get_help_record(self, ctx):
any_prefix_is_slash = []
def _write_opts(opts):
rv, any_slashes = join_options(opts)
if any_slashes:
any_prefix_is_slash[:] = [True]
if not self.is_flag and not self.count:
rv += ' ' + self.make_metavar()
return rv
rv = [_write_opts(self.opts)]
if self.secondary_opts:
rv.append(_write_opts(self.secondary_opts))
help = self.help or ''
extra = []
if self.default is not None and self.show_default:
extra.append('default: %s' % self.default)
if self.required:
extra.append('required')
if extra:
help = '%s[%s]' % (help and help + ' ' or '', '; '.join(extra))
return ((any_prefix_is_slash and '; ' or ' / ').join(rv), help)
def get_default(self, ctx):
# If we're a non boolean flag out default is more complex because
# we need to look at all flags in the same group to figure out
# if we're the the default one in which case we return the flag
# value as default.
if self.is_flag and not self.is_bool_flag:
for param in ctx.command.params:
if param.name == self.name and param.default:
return param.flag_value
return None
return Parameter.get_default(self, ctx)
def prompt_for_value(self, ctx):
"""This is an alternative flow that can be activated in the full
value processing if a value does not exist. It will prompt the
user until a valid value exists and then returns the processed
value as result.
"""
# Calculate the default before prompting anything to be stable.
default = self.get_default(ctx)
# If this is a prompt for a flag we need to handle this
# differently.
if self.is_bool_flag:
return confirm(self.prompt, default)
return prompt(self.prompt, default=default,
hide_input=self.hide_input,
confirmation_prompt=self.confirmation_prompt,
value_proc=lambda x: self.process_value(ctx, x))
def resolve_envvar_value(self, ctx):
rv = Parameter.resolve_envvar_value(self, ctx)
if rv is not None:
return rv
if self.allow_from_autoenv and \
ctx.auto_envvar_prefix is not None:
envvar = '%s_%s' % (ctx.auto_envvar_prefix, self.name.upper())
return os.environ.get(envvar)
def value_from_envvar(self, ctx):
rv = self.resolve_envvar_value(ctx)
if rv is None:
return None
value_depth = (self.nargs != 1) + bool(self.multiple)
if value_depth > 0 and rv is not None:
rv = self.type.split_envvar_value(rv)
if self.multiple and self.nargs != 1:
rv = batch(rv, self.nargs)
return rv
def full_process_value(self, ctx, value):
if value is None and self.prompt is not None \
and not ctx.resilient_parsing:
return self.prompt_for_value(ctx)
return Parameter.full_process_value(self, ctx, value)
class Argument(Parameter):
"""Arguments are positional parameters to a command. They generally
provide fewer features than options but can have infinite ``nargs``
and are required by default.
All parameters are passed onwards to the parameter constructor.
"""
param_type_name = 'argument'
def __init__(self, param_decls, required=None, **attrs):
if required is None:
if attrs.get('default') is not None:
required = False
else:
required = attrs.get('nargs', 1) > 0
Parameter.__init__(self, param_decls, required=required, **attrs)
def make_metavar(self):
if self.metavar is not None:
return self.metavar
var = self.name.upper()
if not self.required:
var = '[%s]' % var
if self.nargs != 1:
var += '...'
return var
def _parse_decls(self, decls, expose_value):
if not decls:
if not expose_value:
return None, [], []
raise TypeError('Could not determine name for argument')
if len(decls) == 1:
name = arg = decls[0]
name = name.replace('-', '_').lower()
elif len(decls) == 2:
name, arg = decls
else:
raise TypeError('Arguments take exactly one or two '
'parameter declarations, got %d' % len(decls))
return name, [arg], []
def get_usage_pieces(self, ctx):
return [self.make_metavar()]
def add_to_parser(self, parser, ctx):
parser.add_argument(dest=self.name, nargs=self.nargs,
obj=self)
# Circular dependency between decorators and core
from .decorators import command, group

293
click/decorators.py Normal file
View file

@ -0,0 +1,293 @@
import sys
import inspect
from functools import update_wrapper
from ._compat import iteritems
from .utils import echo
def pass_context(f):
"""Marks a callback as wanting to receive the current context
object as first argument.
"""
f.__click_pass_context__ = True
return f
def pass_obj(f):
"""Similar to :func:`pass_context`, but only pass the object on the
context onwards (:attr:`Context.obj`). This is useful if that object
represents the state of a nested system.
"""
@pass_context
def new_func(*args, **kwargs):
ctx = args[0]
return ctx.invoke(f, ctx.obj, *args[1:], **kwargs)
return update_wrapper(new_func, f)
def make_pass_decorator(object_type, ensure=False):
"""Given an object type this creates a decorator that will work
similar to :func:`pass_obj` but instead of passing the object of the
current context, it will find the innermost context of type
:func:`object_type`.
This generates a decorator that works roughly like this::
from functools import update_wrapper
def decorator(f):
@pass_context
def new_func(ctx, *args, **kwargs):
obj = ctx.find_object(object_type)
return ctx.invoke(f, obj, *args, **kwargs)
return update_wrapper(new_func, f)
return decorator
:param object_type: the type of the object to pass.
:param ensure: if set to `True`, a new object will be created and
remembered on the context if it's not there yet.
"""
def decorator(f):
@pass_context
def new_func(*args, **kwargs):
ctx = args[0]
if ensure:
obj = ctx.ensure_object(object_type)
else:
obj = ctx.find_object(object_type)
if obj is None:
raise RuntimeError('Managed to invoke callback without a '
'context object of type %r existing'
% object_type.__name__)
return ctx.invoke(f, obj, *args[1:], **kwargs)
return update_wrapper(new_func, f)
return decorator
def _make_command(f, name, attrs, cls):
if isinstance(f, Command):
raise TypeError('Attempted to convert a callback into a '
'command twice.')
try:
params = f.__click_params__
params.reverse()
del f.__click_params__
except AttributeError:
params = []
help = attrs.get('help')
if help is None:
help = inspect.getdoc(f)
if isinstance(help, bytes):
help = help.decode('utf-8')
else:
help = inspect.cleandoc(help)
attrs['help'] = help
return cls(name=name or f.__name__.lower(),
callback=f, params=params, **attrs)
def command(name=None, cls=None, **attrs):
"""Creates a new :class:`Command` and uses the decorated function as
callback. This will also automatically attach all decorated
:func:`option`\s and :func:`argument`\s as parameters to the command.
The name of the command defaults to the name of the function. If you
want to change that, you can pass the intended name as the first
argument.
All keyword arguments are forwarded to the underlying command class.
Once decorated the function turns into a :class:`Command` instance
that can be invoked as a command line utility or be attached to a
command :class:`Group`.
:param name: the name of the command. This defaults to the function
name.
:param cls: the command class to instantiate. This defaults to
:class:`Command`.
"""
if cls is None:
cls = Command
def decorator(f):
return _make_command(f, name, attrs, cls)
return decorator
def group(name=None, **attrs):
"""Creates a new :class:`Group` with a function as callback. This
works otherwise the same as :func:`command` just that the `cls`
parameter is set to :class:`Group`.
"""
attrs.setdefault('cls', Group)
return command(name, **attrs)
def _param_memo(f, param):
if isinstance(f, Command):
f.params.append(param)
else:
if not hasattr(f, '__click_params__'):
f.__click_params__ = []
f.__click_params__.append(param)
def argument(*param_decls, **attrs):
"""Attaches an option to the command. All positional arguments are
passed as parameter declarations to :class:`Argument`; all keyword
arguments are forwarded unchanged. This is equivalent to creating an
:class:`Option` instance manually and attaching it to the
:attr:`Command.params` list.
"""
def decorator(f):
_param_memo(f, Argument(param_decls, **attrs))
return f
return decorator
def option(*param_decls, **attrs):
"""Attaches an option to the command. All positional arguments are
passed as parameter declarations to :class:`Option`; all keyword
arguments are forwarded unchanged. This is equivalent to creating an
:class:`Option` instance manually and attaching it to the
:attr:`Command.params` list.
"""
def decorator(f):
if 'help' in attrs:
attrs['help'] = inspect.cleandoc(attrs['help'])
_param_memo(f, Option(param_decls, **attrs))
return f
return decorator
def confirmation_option(*param_decls, **attrs):
"""Shortcut for confirmation prompts that can be ignored by passing
``--yes`` as parameter.
This is equivalent to decorating a function with :func:`option` with
the following parameters::
def callback(ctx, param, value):
if not value:
ctx.abort()
@click.command()
@click.option('--yes', is_flag=True, callback=callback,
expose_value=False, prompt='Do you want to continue?')
def dropdb():
pass
"""
def decorator(f):
def callback(ctx, param, value):
if not value:
ctx.abort()
attrs.setdefault('is_flag', True)
attrs.setdefault('callback', callback)
attrs.setdefault('expose_value', False)
attrs.setdefault('prompt', 'Do you want to continue?')
attrs.setdefault('help', 'Confirm the action without prompting.')
return option(*(param_decls or ('--yes',)), **attrs)(f)
return decorator
def password_option(*param_decls, **attrs):
"""Shortcut for password prompts.
This is equivalent to decorating a function with :func:`option` with
the following parameters::
@click.command()
@click.option('--password', prompt=True, confirmation_prompt=True,
hide_input=True)
def changeadmin(password):
pass
"""
def decorator(f):
attrs.setdefault('prompt', True)
attrs.setdefault('confirmation_prompt', True)
attrs.setdefault('hide_input', True)
return option(*(param_decls or ('--password',)), **attrs)(f)
return decorator
def version_option(version=None, *param_decls, **attrs):
"""Adds a ``--version`` option which immediately ends the program
printing out the version number. This is implemented as an eager
option that prints the version and exits the program in the callback.
:param version: the version number to show. If not provided Click
attempts an auto discovery via setuptools.
:param prog_name: the name of the program (defaults to autodetection)
:param message: custom message to show instead of the default
(``'%(prog)s, version %(version)s'``)
:param others: everything else is forwarded to :func:`option`.
"""
if version is None:
module = sys._getframe(1).f_globals.get('__name__')
def decorator(f):
prog_name = attrs.pop('prog_name', None)
message = attrs.pop('message', '%(prog)s, version %(version)s')
def callback(ctx, param, value):
if not value or ctx.resilient_parsing:
return
prog = prog_name
if prog is None:
prog = ctx.find_root().info_name
ver = version
if ver is None:
try:
import pkg_resources
except ImportError:
pass
else:
for dist in pkg_resources.working_set:
scripts = dist.get_entry_map().get('console_scripts') or {}
for script_name, entry_point in iteritems(scripts):
if entry_point.module_name == module:
ver = dist.version
break
if ver is None:
raise RuntimeError('Could not determine version')
echo(message % {
'prog': prog,
'version': ver,
})
ctx.exit()
attrs.setdefault('is_flag', True)
attrs.setdefault('expose_value', False)
attrs.setdefault('is_eager', True)
attrs.setdefault('help', 'Show the version and exit.')
attrs['callback'] = callback
return option(*(param_decls or ('--version',)), **attrs)(f)
return decorator
def help_option(*param_decls, **attrs):
"""Adds a ``--help`` option which immediately ends the program
printing out the help page. This is usually unnecessary to add as
this is added by default to all commands unless suppressed.
Like :func:`version_option`, this is implemented as eager option that
prints in the callback and exits.
All arguments are forwarded to :func:`option`.
"""
def decorator(f):
def callback(ctx, param, value):
if value and not ctx.resilient_parsing:
echo(ctx.get_help())
ctx.exit()
attrs.setdefault('is_flag', True)
attrs.setdefault('expose_value', False)
attrs.setdefault('help', 'Show this message and exit.')
attrs.setdefault('is_eager', True)
attrs['callback'] = callback
return option(*(param_decls or ('--help',)), **attrs)(f)
return decorator
# Circular dependencies between core and decorators
from .core import Command, Group, Argument, Option

101
click/exceptions.py Normal file
View file

@ -0,0 +1,101 @@
from ._compat import PY2, filename_to_ui, get_text_stderr
from .utils import echo
class ClickException(Exception):
"""An exception that Click can handle and show to the user."""
#: The exit code for this exception
exit_code = 1
def __init__(self, message):
if PY2:
Exception.__init__(self, message.encode('utf-8'))
else:
Exception.__init__(self, message)
self.message = message
def format_message(self):
return self.message
def show(self, file=None):
if file is None:
file = get_text_stderr()
echo('Error: %s' % self.format_message(), file=file)
class UsageError(ClickException):
"""An internal exception that signals a usage error. This typically
aborts any further handling.
:param message: the error message to display.
:param ctx: optionally the context that caused this error. Click will
fill in the context automatically in some situations.
"""
exit_code = 2
def __init__(self, message, ctx=None):
ClickException.__init__(self, message)
self.ctx = ctx
def show(self, file=None):
if file is None:
file = get_text_stderr()
if self.ctx is not None:
echo(self.ctx.get_usage() + '\n', file=file)
echo('Error: %s' % self.format_message(), file=file)
class BadParameter(UsageError):
"""An exception that formats out a standardized error message for a
bad parameter. This is useful when thrown from a callback or type as
Click will attach contextual information to it (for instance, which
parameter it is).
.. versionadded:: 2.0
:param param: the parameter object that caused this error. This can
be left out, and Click will attach this info itself
if possible.
:param param_hint: a string that shows up as parameter name. This
can be used as alternative to `param` in cases
where custom validation should happen. If it is
a string it's used as such, if it's a list then
each item is quoted and separated.
"""
def __init__(self, message, ctx=None, param=None,
param_hint=None):
UsageError.__init__(self, message, ctx)
self.param = param
self.param_hint = param_hint
def format_message(self):
if self.param_hint is not None:
param_hint = self.param_hint
elif self.param is not None:
param_hint = self.param.opts or [self.param.name]
else:
return 'Invalid value: %s' % self.message
if isinstance(param_hint, (tuple, list)):
param_hint = ' / '.join('"%s"' % x for x in param_hint)
return 'Invalid value for %s: %s' % (param_hint, self.message)
class FileError(ClickException):
"""Raised if a file cannot be opened."""
def __init__(self, filename, hint=None):
ui_filename = filename_to_ui(filename)
if hint is None:
hint = 'unknown error'
ClickException.__init__(self, hint)
self.ui_filename = ui_filename
self.filename = filename
def format_message(self):
return 'Could not open file %s: %s' % (self.ui_filename, self.message)
class Abort(RuntimeError):
"""An internal signalling exception that signals Click to abort."""

239
click/formatting.py Normal file
View file

@ -0,0 +1,239 @@
from contextlib import contextmanager
from .termui import get_terminal_size
from .parser import split_opt
from ._compat import term_len
def measure_table(rows):
widths = {}
for row in rows:
for idx, col in enumerate(row):
widths[idx] = max(widths.get(idx, 0), term_len(col))
return tuple(y for x, y in sorted(widths.items()))
def iter_rows(rows, col_count):
for row in rows:
row = tuple(row)
yield row + ('',) * (col_count - len(row))
def wrap_text(text, width=78, initial_indent='', subsequent_indent='',
preserve_paragraphs=False):
"""A helper function that intelligently wraps text. By default, it
assumes that it operates on a single paragraph of text but if the
`preserve_paragraphs` parameter is provided it will intelligently
handle paragraphs (defined by two empty lines).
If paragraphs are handled, a paragraph can be prefixed with an empty
line containing the ``\\b`` character (``\\x08``) to indicate that
no rewrapping should happen in that block.
:param text: the text that should be rewrapped.
:param width: the maximum width for the text.
:param initial_indent: the initial indent that should be placed on the
first line as a string.
:param subsequent_indent: the indent string that should be placed on
each consecutive line.
:param preserve_paragraphs: if this flag is set then the wrapping will
intelligently handle paragraphs.
"""
from ._textwrap import TextWrapper
text = text.expandtabs()
wrapper = TextWrapper(width, initial_indent=initial_indent,
subsequent_indent=subsequent_indent,
replace_whitespace=False)
if not preserve_paragraphs:
return wrapper.fill(text)
p = []
buf = []
indent = None
def _flush_par():
if not buf:
return
if buf[0].strip() == '\b':
p.append((indent or 0, True, '\n'.join(buf[1:])))
else:
p.append((indent or 0, False, ' '.join(buf)))
del buf[:]
for line in text.splitlines():
if not line:
_flush_par()
indent = None
else:
if indent is None:
orig_len = term_len(line)
line = line.lstrip()
indent = orig_len - term_len(line)
buf.append(line)
_flush_par()
rv = []
for indent, raw, text in p:
with wrapper.extra_indent(' ' * indent):
if raw:
rv.append(wrapper.indent_only(text))
else:
rv.append(wrapper.fill(text))
return '\n\n'.join(rv)
class HelpFormatter(object):
"""This class helps with formatting text-based help pages. It's
usually just needed for very special internal cases, but it's also
exposed so that developers can write their own fancy outputs.
At present, it always writes into memory.
:param indent_increment: the additional increment for each level.
:param width: the width for the text. This defaults to the terminal
width clamped to a maximum of 78.
"""
def __init__(self, indent_increment=2, width=None):
self.indent_increment = indent_increment
if width is None:
width = max(min(get_terminal_size()[0], 80) - 2, 50)
self.width = width
self.current_indent = 0
self.buffer = []
def write(self, string):
"""Writes a unicode string into the internal buffer."""
self.buffer.append(string)
def indent(self):
"""Increases the indentation."""
self.current_indent += self.indent_increment
def dedent(self):
"""Decreases the indentation."""
self.current_indent -= self.indent_increment
def write_usage(self, prog, args='', prefix='Usage: '):
"""Writes a usage line into the buffer.
:param prog: the program name.
:param args: whitespace separated list of arguments.
:param prefix: the prefix for the first line.
"""
prefix = '%*s%s' % (self.current_indent, prefix, prog)
self.write(prefix)
text_width = max(self.width - self.current_indent - term_len(prefix), 10)
indent = ' ' * (term_len(prefix) + 1)
self.write(wrap_text(args, text_width,
initial_indent=' ',
subsequent_indent=indent))
self.write('\n')
def write_heading(self, heading):
"""Writes a heading into the buffer."""
self.write('%*s%s:\n' % (self.current_indent, '', heading))
def write_paragraph(self):
"""Writes a paragraph into the buffer."""
if self.buffer:
self.write('\n')
def write_text(self, text):
"""Writes re-indented text into the buffer. This rewraps and
preserves paragraphs.
"""
text_width = max(self.width - self.current_indent, 11)
indent = ' ' * self.current_indent
self.write(wrap_text(text, text_width,
initial_indent=indent,
subsequent_indent=indent,
preserve_paragraphs=True))
self.write('\n')
def write_dl(self, rows, col_max=30, col_spacing=2):
"""Writes a definition list into the buffer. This is how options
and commands are usually formatted.
:param rows: a list of two item tuples for the terms and values.
:param col_max: the maximum width of the first column.
:param col_spacing: the number of spaces between the first and
second column.
"""
rows = list(rows)
widths = measure_table(rows)
if len(widths) != 2:
raise TypeError('Expected two columns for definition list')
first_col = min(widths[0], col_max) + col_spacing
for first, second in iter_rows(rows, len(widths)):
self.write('%*s%s' % (self.current_indent, '', first))
if not second:
self.write('\n')
continue
if term_len(first) <= first_col - col_spacing:
self.write(' ' * (first_col - term_len(first)))
else:
self.write('\n')
self.write(' ' * (first_col + self.current_indent))
text_width = max(self.width - first_col - 2, 10)
lines = iter(wrap_text(second, text_width).splitlines())
if lines:
self.write(next(lines) + '\n')
for line in lines:
self.write('%*s%s\n' % (
first_col + self.current_indent, '', line))
else:
self.write('\n')
@contextmanager
def section(self, name):
"""Helpful context manager that writes a paragraph, a heading,
and the indents.
:param name: the section name that is written as heading.
"""
self.write_paragraph()
self.write_heading(name)
self.indent()
try:
yield
finally:
self.dedent()
@contextmanager
def indentation(self):
"""A context manager that increases the indentation."""
self.indent()
try:
yield
finally:
self.dedent()
def getvalue(self):
"""Returns the buffer contents."""
return ''.join(self.buffer)
def join_options(options):
"""Given a list of option strings this joins them in the most appropriate
way and returns them in the form ``(formatted_string,
any_prefix_is_slash)`` where the second item in the tuple is a flag that
indicates if any of the option prefixes was a slash.
"""
rv = []
any_prefix_is_slash = False
for opt in options:
prefix = split_opt(opt)[0]
if prefix == '/':
any_prefix_is_slash = True
rv.append((len(prefix), opt))
rv.sort(key=lambda x: x[0])
rv = ', '.join(x[1] for x in rv)
return rv, any_prefix_is_slash

347
click/parser.py Normal file
View file

@ -0,0 +1,347 @@
# -*- coding: utf-8 -*-
"""
click.parser
~~~~~~~~~~~~
This module started out as largely a copy paste from the stdlib's
optparse module with the features removed that we do not need from
optparse because we implement them in Click on a higher level (for
instance type handling, help formatting and a lot more).
The plan is to remove more and more from here over time.
The reason this is a different module and not optparse from the stdlib
is that there are differences in 2.x and 3.x about the error messages
generated and optparse in the stdlib uses gettext for no good reason
and might cause us issues.
"""
import re
from .exceptions import UsageError
from .utils import unpack_args
def split_opt(opt):
first = opt[:1]
if first.isalnum():
return '', opt
if opt[1:2] == first:
return opt[:2], opt[2:]
return first, opt[1:]
def normalize_opt(opt, ctx):
if ctx is None or ctx.token_normalize_func is None:
return opt
prefix, opt = split_opt(opt)
return prefix + ctx.token_normalize_func(opt)
def split_arg_string(string):
"""Given an argument string this attempts to split it into small parts."""
rv = []
for match in re.finditer(r"('([^'\\]*(?:\\.[^'\\]*)*)'"
r'|"([^"\\]*(?:\\.[^"\\]*)*)"'
r'|\S+)\s*', string, re.S):
arg = match.group().strip()
if arg[:1] == arg[-1:] and arg[:1] in '"\'':
arg = arg[1:-1].encode('ascii', 'backslashreplace') \
.decode('unicode-escape')
try:
arg = type(string)(arg)
except UnicodeError:
pass
rv.append(arg)
return rv
class Option(object):
def __init__(self, opts, dest, action=None, nargs=1, const=None, obj=None):
self._short_opts = []
self._long_opts = []
self.prefixes = set()
for opt in opts:
prefix, value = split_opt(opt)
if not prefix:
raise ValueError('Invalid start character for option (%s)'
% opt)
self.prefixes.add(prefix[0])
if len(prefix) == 1 and len(value) == 1:
self._short_opts.append(opt)
else:
self._long_opts.append(opt)
self.prefixes.add(prefix)
if action is None:
action = 'store'
self.dest = dest
self.action = action
self.nargs = nargs
self.const = const
self.obj = obj
@property
def takes_value(self):
return self.action in ('store', 'append')
def process(self, value, state):
if self.action == 'store':
state.opts[self.dest] = value
elif self.action == 'store_const':
state.opts[self.dest] = self.const
elif self.action == 'append':
state.opts.setdefault(self.dest, []).append(value)
elif self.action == 'append_const':
state.opts.setdefault(self.dest, []).append(self.const)
elif self.action == 'count':
state.opts[self.dest] = state.opts.get(self.dest, 0) + 1
else:
raise ValueError('unknown action %r' % self.action)
state.order.append(self.obj)
class Argument(object):
def __init__(self, dest, nargs=1, obj=None):
self.dest = dest
self.nargs = nargs
self.obj = obj
def process(self, value, state):
state.opts[self.dest] = value
state.order.append(self.obj)
class ParsingState(object):
def __init__(self, rargs):
self.opts = {}
self.largs = []
self.rargs = rargs
self.order = []
class OptionParser(object):
"""The option parser is an internal class that is ultimately used to
parse options and arguments. It's modelled after optparse and brings
a similar but vastly simplified API. It should generally not be used
directly as the high level Click classes wrap it for you.
It's not nearly as extensible as optparse or argparse as it does not
implement features that are implemented on a higher level (such as
types or defaults).
:param ctx: optionally the :class:`~click.Context` where this parser
should go with.
"""
def __init__(self, ctx=None):
#: The :class:`~click.Context` for this parser. This might be
#: `None` for some advanced use cases.
self.ctx = ctx
#: This controls how the parser deals with interspersed arguments.
#: If this is set to `False`, the parser will stop on the first
#: non-option. Click uses this to implement nested subcommands
#: safely.
self.allow_interspersed_args = True
self._short_opt = {}
self._long_opt = {}
self._opt_prefixes = set(['-', '--'])
self._args = []
def add_option(self, opts, dest, action=None, nargs=1, const=None,
obj=None):
"""Adds a new option named `dest` to the parser. The destination
is not inferred (unlike with optparse) and needs to be explicitly
provided. Action can be any of ``store``, ``store_const``,
``append``, ``appnd_const`` or ``count``.
The `obj` can be used to identify the option in the order list
that is returned from the parser.
"""
if obj is None:
obj = dest
opts = [normalize_opt(opt, self.ctx) for opt in opts]
option = Option(opts, dest, action=action, nargs=nargs,
const=const, obj=obj)
self._opt_prefixes.update(option.prefixes)
for opt in option._short_opts:
self._short_opt[opt] = option
for opt in option._long_opts:
self._long_opt[opt] = option
def add_argument(self, dest, nargs=1, obj=None):
"""Adds a positional argument named `dest` to the parser.
The `obj` can be used to identify the option in the order list
that is returned from the parser.
"""
if obj is None:
obj = dest
self._args.append(Argument(dest=dest, nargs=nargs, obj=obj))
def parse_args(self, args):
"""Parses positional arguments and returns ``(values, args, order)``
for the parsed options and arguments as well as the leftover
arguments if there are any. The order is a list of objects as they
appear on the command line. If arguments appear multiple times they
will be memorized multiple times as well.
"""
state = ParsingState(args)
try:
self._process_args_for_options(state)
self._process_args_for_args(state)
except UsageError:
if not self.ctx.resilient_parsing:
raise
return state.opts, state.largs, state.order
def _process_args_for_args(self, state):
pargs, args = unpack_args(state.largs + state.rargs,
[x.nargs for x in self._args])
for idx, arg in enumerate(self._args):
arg.process(pargs[idx], state)
state.largs = args
state.rargs = []
def _process_args_for_options(self, state):
while state.rargs:
arg = state.rargs.pop(0)
arglen = len(arg)
# Double dashes always handled explicitly regardless of what
# prefixes are valid.
if arg == '--':
return
elif arg[:1] in self._opt_prefixes and arglen > 1:
self._process_opts(arg, state)
elif self.allow_interspersed_args:
state.largs.append(arg)
else:
state.rargs.insert(0, arg)
return
# Say this is the original argument list:
# [arg0, arg1, ..., arg(i-1), arg(i), arg(i+1), ..., arg(N-1)]
# ^
# (we are about to process arg(i)).
#
# Then rargs is [arg(i), ..., arg(N-1)] and largs is a *subset* of
# [arg0, ..., arg(i-1)] (any options and their arguments will have
# been removed from largs).
#
# The while loop will usually consume 1 or more arguments per pass.
# If it consumes 1 (eg. arg is an option that takes no arguments),
# then after _process_arg() is done the situation is:
#
# largs = subset of [arg0, ..., arg(i)]
# rargs = [arg(i+1), ..., arg(N-1)]
#
# If allow_interspersed_args is false, largs will always be
# *empty* -- still a subset of [arg0, ..., arg(i-1)], but
# not a very interesting subset!
def _match_long_opt(self, opt):
# Is there an exact match?
if opt in self._long_opt:
return opt
# Isolate all words with s as a prefix.
possibilities = [word for word in self._long_opt
if word.startswith(opt)]
# No exact match, so there had better be just one possibility.
if not possibilities:
self._error('no such option: %s' % opt)
elif len(possibilities) == 1:
self._error('no such option: %s. Did you mean %s?' %
(opt, possibilities[0]))
return possibilities[0]
else:
# More than one possible completion: ambiguous prefix.
possibilities.sort()
self._error('no such option: %s. (Possible options: %s)'
% (opt, ', '.join(possibilities)))
def _process_long_opt(self, arg, state):
# Value explicitly attached to arg? Pretend it's the next argument.
if '=' in arg:
opt, next_arg = arg.split('=', 1)
state.rargs.insert(0, next_arg)
had_explicit_value = True
else:
opt = arg
had_explicit_value = False
opt = normalize_opt(opt, self.ctx)
opt = self._match_long_opt(opt)
option = self._long_opt[opt]
if option.takes_value:
nargs = option.nargs
if len(state.rargs) < nargs:
if nargs == 1:
self._error('%s option requires an argument' % opt)
else:
self._error('%s option requires %d arguments' % (opt, nargs))
elif nargs == 1:
value = state.rargs.pop(0)
else:
value = tuple(state.rargs[:nargs])
del state.rargs[:nargs]
elif had_explicit_value:
self._error('%s option does not take a value' % opt)
else:
value = None
option.process(value, state)
def _process_opts(self, arg, state):
if '=' in arg or normalize_opt(arg, self.ctx) in self._long_opt:
return self._process_long_opt(arg, state)
stop = False
i = 1
prefix = arg[0]
for ch in arg[1:]:
opt = normalize_opt(prefix + ch, self.ctx)
option = self._short_opt.get(opt)
i += 1
if not option:
self._error('no such option: %s' % (arg if arg.startswith('--') else opt))
if option.takes_value:
# Any characters left in arg? Pretend they're the
# next arg, and stop consuming characters of arg.
if i < len(arg):
state.rargs.insert(0, arg[i:])
stop = True
nargs = option.nargs
if len(state.rargs) < nargs:
if nargs == 1:
self._error('%s option requires an argument' % opt)
else:
self._error('%s option requires %d arguments' %
(opt, nargs))
elif nargs == 1:
value = state.rargs.pop(0)
else:
value = tuple(state.rargs[:nargs])
del state.rargs[:nargs]
else:
value = None
option.process(value, state)
if stop:
break
def _error(self, msg):
raise UsageError(msg, self.ctx)

496
click/termui.py Normal file
View file

@ -0,0 +1,496 @@
import os
import sys
import struct
from ._compat import raw_input, text_type, string_types, \
colorama, isatty, strip_ansi, get_winterm_size, \
DEFAULT_COLUMNS, WIN
from .utils import echo
from .exceptions import Abort, UsageError
from .types import convert_type
# The prompt functions to use. The doc tools currently override these
# functions to customize how they work.
visible_prompt_func = raw_input
_ansi_colors = ('black', 'red', 'green', 'yellow', 'blue', 'magenta',
'cyan', 'white', 'reset')
_ansi_reset_all = '\033[0m'
def hidden_prompt_func(prompt):
import getpass
return getpass.getpass(prompt)
def _build_prompt(text, suffix, show_default=False, default=None):
prompt = text
if default is not None and show_default:
prompt = '%s [%s]' % (prompt, default)
return prompt + suffix
def prompt(text, default=None, hide_input=False,
confirmation_prompt=False, type=None,
value_proc=None, prompt_suffix=': ', show_default=True):
"""Prompts a user for input. This is a convenience function that can
be used to prompt a user for input later.
If the user aborts the input by sending a interrupt signal, this
function will catch it and raise a :exc:`Abort` exception.
:param text: the text to show for the prompt.
:param default: the default value to use if no input happens. If this
is not given it will prompt until it's aborted.
:param hide_input: if this is set to true then the input value will
be hidden.
:param confirmation_prompt: asks for confirmation for the value.
:param type: the type to use to check the value against.
:param value_proc: if this parameter is provided it's a function that
is invoked instead of the type conversion to
convert a value.
:param prompt_suffix: a suffix that should be added to the prompt.
:param show_default: shows or hides the default value in the prompt.
"""
result = None
def prompt_func(text):
f = hide_input and hidden_prompt_func or visible_prompt_func
try:
# Write the prompt separately so that we get nice
# coloring through colorama on Windows
echo(text, nl=False)
return f('')
except (KeyboardInterrupt, EOFError):
raise Abort()
if value_proc is None:
value_proc = convert_type(type, default)
prompt = _build_prompt(text, prompt_suffix, show_default, default)
while 1:
while 1:
value = prompt_func(prompt)
if value:
break
# If a default is set and used, then the confirmation
# prompt is always skipped because that's the only thing
# that really makes sense.
elif default is not None:
return default
try:
result = value_proc(value)
except UsageError as e:
echo('Error: %s' % e.message)
continue
if not confirmation_prompt:
return result
while 1:
value2 = prompt_func('Repeat for confirmation: ')
if value2:
break
if value == value2:
return result
echo('Error: the two entered values do not match')
def confirm(text, default=False, abort=False, prompt_suffix=': ',
show_default=True):
"""Prompts for confirmation (yes/no question).
If the user aborts the input by sending a interrupt signal this
function will catch it and raise a :exc:`Abort` exception.
:param text: the question to ask.
:param default: the default for the prompt.
:param abort: if this is set to `True` a negative answer aborts the
exception by raising :exc:`Abort`.
:param prompt_suffix: a suffix that should be added to the prompt.
:param show_default: shows or hides the default value in the prompt.
"""
prompt = _build_prompt(text, prompt_suffix, show_default,
default and 'Y/n' or 'y/N')
while 1:
try:
# Write the prompt separately so that we get nice
# coloring through colorama on Windows
echo(prompt, nl=False)
value = visible_prompt_func('').lower().strip()
except (KeyboardInterrupt, EOFError):
raise Abort()
if value in ('y', 'yes'):
rv = True
elif value in ('n', 'no'):
rv = False
elif value == '':
rv = default
else:
echo('Error: invalid input')
continue
break
if abort and not rv:
raise Abort()
return rv
def get_terminal_size():
"""Returns the current size of the terminal as tuple in the form
``(width, height)`` in columns and rows.
"""
# If shutil has get_terminal_size() (Python 3.3 and later) use that
if sys.version_info >= (3, 3):
import shutil
shutil_get_terminal_size = getattr(shutil, 'get_terminal_size', None)
if shutil_get_terminal_size:
sz = shutil_get_terminal_size()
return sz.columns, sz.lines
if get_winterm_size is not None:
return get_winterm_size()
def ioctl_gwinsz(fd):
try:
import fcntl
import termios
cr = struct.unpack(
'hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234'))
except Exception:
return
return cr
cr = ioctl_gwinsz(0) or ioctl_gwinsz(1) or ioctl_gwinsz(2)
if not cr:
try:
fd = os.open(os.ctermid(), os.O_RDONLY)
try:
cr = ioctl_gwinsz(fd)
finally:
os.close(fd)
except Exception:
pass
if not cr or not cr[0] or not cr[1]:
cr = (os.environ.get('LINES', 25),
os.environ.get('COLUMNS', DEFAULT_COLUMNS))
return int(cr[1]), int(cr[0])
def echo_via_pager(text, color=None):
"""This function takes a text and shows it via an environment specific
pager on stdout.
.. versionchanged:: 3.0
Added the `color` flag.
:param text: the text to page.
:param color: controls if the pager supports ANSI colors or not. The
default is autodetection.
"""
if not isinstance(text, string_types):
text = text_type(text)
from ._termui_impl import pager
return pager(text + '\n', color)
def progressbar(iterable=None, length=None, label=None, show_eta=True,
show_percent=None, show_pos=False,
item_show_func=None, fill_char='#', empty_char='-',
bar_template='%(label)s [%(bar)s] %(info)s',
info_sep=' ', width=36, file=None):
"""This function creates an iterable context manager that can be used
to iterate over something while showing a progress bar. It will
either iterate over the `iterable` or `length` items (that are counted
up). While iteration happens, this function will print a rendered
progress bar to the given `file` (defaults to stdout) and will attempt
to calculate remaining time and more. By default, this progress bar
will not be rendered if the file is not a terminal.
The context manager creates the progress bar. When the context
manager is entered the progress bar is already displayed. With every
iteration over the progress bar, the iterable passed to the bar is
advanced and the bar is updated. When the context manager exits,
a newline is printed and the progress bar is finalized on screen.
No printing must happen or the progress bar will be unintentionally
destroyed.
Example usage::
with progressbar(items) as bar:
for item in bar:
do_something_with(item)
.. versionadded:: 2.0
:param iterable: an iterable to iterate over. If not provided the length
is required.
:param length: the number of items to iterate over. By default the
progressbar will attempt to ask the iterator about its
length, which might or might not work. If an iterable is
also provided this parameter can be used to override the
length. If an iterable is not provided the progress bar
will iterate over a range of that length.
:param label: the label to show next to the progress bar.
:param show_eta: enables or disables the estimated time display. This is
automatically disabled if the length cannot be
determined.
:param show_percent: enables or disables the percentage display. The
default is `True` if the iterable has a length or
`False` if not.
:param show_pos: enables or disables the absolute position display. The
default is `False`.
:param item_show_func: a function called with the current item which
can return a string to show the current item
next to the progress bar. Note that the current
item can be `None`!
:param fill_char: the character to use to show the filled part of the
progress bar.
:param empty_char: the character to use to show the non-filled part of
the progress bar.
:param bar_template: the format string to use as template for the bar.
The parameters in it are ``label`` for the label,
``bar`` for the progress bar and ``info`` for the
info section.
:param info_sep: the separator between multiple info items (eta etc.)
:param width: the width of the progress bar in characters, 0 means full
terminal width
:param file: the file to write to. If this is not a terminal then
only the label is printed.
"""
from ._termui_impl import ProgressBar
return ProgressBar(iterable=iterable, length=length, show_eta=show_eta,
show_percent=show_percent, show_pos=show_pos,
item_show_func=item_show_func, fill_char=fill_char,
empty_char=empty_char, bar_template=bar_template,
info_sep=info_sep, file=file, label=label,
width=width)
def clear():
"""Clears the terminal screen. This will have the effect of clearing
the whole visible space of the terminal and moving the cursor to the
top left. This does not do anything if not connected to a terminal.
.. versionadded:: 2.0
"""
if not isatty(sys.stdout):
return
# If we're on Windows and we don't have colorama available, then we
# clear the screen by shelling out. Otherwise we can use an escape
# sequence.
if WIN and colorama is None:
os.system('cls')
else:
sys.stdout.write('\033[2J\033[1;1H')
def style(text, fg=None, bg=None, bold=None, dim=None, underline=None,
blink=None, reverse=None, reset=True):
"""Styles a text with ANSI styles and returns the new string. By
default the styling is self contained which means that at the end
of the string a reset code is issued. This can be prevented by
passing ``reset=False``.
Examples::
click.echo(click.style('Hello World!', fg='green'))
click.echo(click.style('ATTENTION!', blink=True))
click.echo(click.style('Some things', reverse=True, fg='cyan'))
Supported color names:
* ``black`` (might be a gray)
* ``red``
* ``green``
* ``yellow`` (might be an orange)
* ``blue``
* ``magenta``
* ``cyan``
* ``white`` (might be light gray)
* ``reset`` (reset the color code only)
.. versionadded:: 2.0
:param text: the string to style with ansi codes.
:param fg: if provided this will become the foreground color.
:param bg: if provided this will become the background color.
:param bold: if provided this will enable or disable bold mode.
:param dim: if provided this will enable or disable dim mode. This is
badly supported.
:param underline: if provided this will enable or disable underline.
:param blink: if provided this will enable or disable blinking.
:param reverse: if provided this will enable or disable inverse
rendering (foreground becomes background and the
other way round).
:param reset: by default a reset-all code is added at the end of the
string which means that styles do not carry over. This
can be disabled to compose styles.
"""
bits = []
if fg:
try:
bits.append('\033[%dm' % (_ansi_colors.index(fg) + 30))
except ValueError:
raise TypeError('Unknown color %r' % fg)
if bg:
try:
bits.append('\033[%dm' % (_ansi_colors.index(bg) + 40))
except ValueError:
raise TypeError('Unknown color %r' % bg)
if bold is not None:
bits.append('\033[%dm' % (1 if bold else 22))
if dim is not None:
bits.append('\033[%dm' % (2 if dim else 22))
if underline is not None:
bits.append('\033[%dm' % (4 if underline else 24))
if blink is not None:
bits.append('\033[%dm' % (5 if blink else 25))
if reverse is not None:
bits.append('\033[%dm' % (7 if reverse else 27))
bits.append(text)
if reset:
bits.append(_ansi_reset_all)
return ''.join(bits)
def unstyle(text):
"""Removes ANSI styling information from a string. Usually it's not
necessary to use this function as Click's echo function will
automatically remove styling if necessary.
.. versionadded:: 2.0
:param text: the text to remove style information from.
"""
return strip_ansi(text)
def secho(text, file=None, nl=True, err=False, **styles):
"""This function combines :func:`echo` and :func:`style` into one
call. As such the following two calls are the same::
click.secho('Hello World!', fg='green')
click.echo(click.style('Hello World!', fg='green'))
All keyword arguments are forwarded to the underlying functions
depending on which one they go with.
.. versionadded:: 2.0
"""
text = style(text, **styles)
return echo(text, file=file, nl=nl, err=err)
def edit(text=None, editor=None, env=None, require_save=True,
extension='.txt', filename=None):
r"""Edits the given text in the defined editor. If an editor is given
(should be the full path to the executable but the regular operating
system search path is used for finding the executable) it overrides
the detected editor. Optionally, some environment variables can be
used. If the editor is closed without changes, `None` is returned. In
case a file is edited directly the return value is always `None` and
`require_save` and `extension` are ignored.
If the editor cannot be opened a :exc:`UsageError` is raised.
Note for Windows: to simplify cross-platform usage, the newlines are
automatically converted from POSIX to Windows and vice versa. As such,
the message here will have ``\n`` as newline markers.
:param text: the text to edit.
:param editor: optionally the editor to use. Defaults to automatic
detection.
:param env: environment variables to forward to the editor.
:param require_save: if this is true, then not saving in the editor
will make the return value become `None`.
:param extension: the extension to tell the editor about. This defaults
to `.txt` but changing this might change syntax
highlighting.
:param filename: if provided it will edit this file instead of the
provided text contents. It will not use a temporary
file as an indirection in that case.
"""
from ._termui_impl import Editor
editor = Editor(editor=editor, env=env, require_save=require_save,
extension=extension)
if filename is None:
return editor.edit(text)
editor.edit_file(filename)
def launch(url, wait=False, locate=False):
"""This function launches the given URL (or filename) in the default
viewer application for this file type. If this is an executable, it
might launch the executable in a new session. The return value is
the exit code of the launched application. Usually, ``0`` indicates
success.
Examples::
click.launch('http://click.pocoo.org/')
click.launch('/my/downloaded/file', locate=True)
.. versionadded:: 2.0
:param url: URL or filename of the thing to launch.
:param wait: waits for the program to stop.
:param locate: if this is set to `True` then instead of launching the
application associated with the URL it will attempt to
launch a file manager with the file located. This
might have weird effects if the URL does not point to
the filesystem.
"""
from ._termui_impl import open_url
return open_url(url, wait=wait, locate=locate)
# If this is provided, getchar() calls into this instead. This is used
# for unittesting purposes.
_getchar = None
def getchar(echo=False):
"""Fetches a single character from the terminal and returns it. This
will always return a unicode character and under certain rare
circumstances this might return more than one character. The
situations which more than one character is returned is when for
whatever reason multiple characters end up in the terminal buffer or
standard input was not actually a terminal.
Note that this will always read from the terminal, even if something
is piped into the standard input.
.. versionadded:: 2.0
:param echo: if set to `True`, the character read will also show up on
the terminal. The default is to not show it.
"""
f = _getchar
if f is None:
from ._termui_impl import getchar as f
return f(echo)
def pause(info='Press any key to continue ...'):
"""This command stops execution and waits for the user to press any
key to continue. This is similar to the Windows batch "pause"
command. If the program is not run through a terminal, this command
will instead do nothing.
.. versionadded:: 2.0
:param info: the info string to print before pausing.
"""
if not isatty(sys.stdin) or not isatty(sys.stdout):
return
try:
if info:
echo(info, nl=False)
try:
getchar()
except (KeyboardInterrupt, EOFError):
pass
finally:
if info:
echo()

294
click/testing.py Normal file
View file

@ -0,0 +1,294 @@
import os
import sys
import shutil
import tempfile
import contextlib
from ._compat import iteritems, PY2
# If someone wants to vendor click, we want to ensure the
# correct package is discovered. Ideally we could use a
# relative import here but unfortunately Python does not
# support that.
clickpkg = sys.modules[__name__.rsplit('.', 1)[0]]
if PY2:
from cStringIO import StringIO
else:
import io
from ._compat import _find_binary_reader
class EchoingStdin(object):
def __init__(self, input, output):
self._input = input
self._output = output
def __getattr__(self, x):
return getattr(self._input, x)
def _echo(self, rv):
self._output.write(rv)
return rv
def read(self, n=-1):
return self._echo(self._input.read(n))
def readline(self, n=-1):
return self._echo(self._input.readline(n))
def readlines(self):
return [self._echo(x) for x in self._input.readlines()]
def __iter__(self):
return iter(self._echo(x) for x in self._input)
def __repr__(self):
return repr(self._input)
def make_input_stream(input, charset):
# Is already an input stream.
if hasattr(input, 'read'):
if PY2:
return input
rv = _find_binary_reader(input)
if rv is not None:
return rv
raise TypeError('Could not find binary reader for input stream.')
if input is None:
input = b''
elif not isinstance(input, bytes):
input = input.encode(charset)
if PY2:
return StringIO(input)
return io.BytesIO(input)
class Result(object):
"""Holds the captured result of an invoked CLI script."""
def __init__(self, runner, output_bytes, exit_code, exception,
exc_info=None):
#: The runner that created the result
self.runner = runner
#: The output as bytes.
self.output_bytes = output_bytes
#: The exit code as integer.
self.exit_code = exit_code
#: The exception that happend if one did.
self.exception = exception
#: The traceback
self.exc_info = exc_info
@property
def output(self):
"""The output as unicode string."""
return self.output_bytes.decode(self.runner.charset, 'replace') \
.replace('\r\n', '\n')
def __repr__(self):
return '<Result %s>' % (
self.exception and repr(self.exception) or 'okay',
)
class CliRunner(object):
"""The CLI runner provides functionality to invoke a Click command line
script for unittesting purposes in a isolated environment. This only
works in single-threaded systems without any concurrency as it changes the
global interpreter state.
:param charset: the character set for the input and output data. This is
UTF-8 by default and should not be changed currently as
the reporting to Click only works in Python 2 properly.
:param env: a dictionary with environment variables for overriding.
:param echo_stdin: if this is set to `True`, then reading from stdin writes
to stdout. This is useful for showing examples in
some circumstances. Note that regular prompts
will automatically echo the input.
"""
def __init__(self, charset=None, env=None, echo_stdin=False):
if charset is None:
charset = 'utf-8'
self.charset = charset
self.env = env or {}
self.echo_stdin = echo_stdin
def get_default_prog_name(self, cli):
"""Given a command object it will return the default program name
for it. The default is the `name` attribute or ``"root"`` if not
set.
"""
return cli.name or 'root'
def make_env(self, overrides=None):
"""Returns the environment overrides for invoking a script."""
rv = dict(self.env)
if overrides:
rv.update(overrides)
return rv
@contextlib.contextmanager
def isolation(self, input=None, env=None):
"""A context manager that sets up the isolation for invoking of a
command line tool. This sets up stdin with the given input data
and `os.environ` with the overrides from the given dictionary.
This also rebinds some internals in Click to be mocked (like the
prompt functionality).
This is automatically done in the :meth:`invoke` method.
:param input: the input stream to put into sys.stdin.
:param env: the environment overrides as dictionary.
"""
input = make_input_stream(input, self.charset)
old_stdin = sys.stdin
old_stdout = sys.stdout
old_stderr = sys.stderr
env = self.make_env(env)
if PY2:
sys.stdout = sys.stderr = bytes_output = StringIO()
if self.echo_stdin:
input = EchoingStdin(input, bytes_output)
else:
bytes_output = io.BytesIO()
if self.echo_stdin:
input = EchoingStdin(input, bytes_output)
input = io.TextIOWrapper(input, encoding=self.charset)
sys.stdout = sys.stderr = io.TextIOWrapper(
bytes_output, encoding=self.charset)
sys.stdin = input
def visible_input(prompt=None):
sys.stdout.write(prompt or '')
val = input.readline().rstrip('\r\n')
sys.stdout.write(val + '\n')
sys.stdout.flush()
return val
def hidden_input(prompt=None):
sys.stdout.write((prompt or '') + '\n')
sys.stdout.flush()
return input.readline().rstrip('\r\n')
def _getchar(echo):
char = sys.stdin.read(1)
if echo:
sys.stdout.write(char)
sys.stdout.flush()
return char
old_visible_prompt_func = clickpkg.termui.visible_prompt_func
old_hidden_prompt_func = clickpkg.termui.hidden_prompt_func
old__getchar_func = clickpkg.termui._getchar
clickpkg.termui.visible_prompt_func = visible_input
clickpkg.termui.hidden_prompt_func = hidden_input
clickpkg.termui._getchar = _getchar
old_env = {}
try:
for key, value in iteritems(env):
old_env[key] = os.environ.get(value)
if value is None:
try:
del os.environ[key]
except Exception:
pass
else:
os.environ[key] = value
yield bytes_output
finally:
for key, value in iteritems(old_env):
if value is None:
try:
del os.environ[key]
except Exception:
pass
else:
os.environ[key] = value
sys.stdout = old_stdout
sys.stderr = old_stderr
sys.stdin = old_stdin
clickpkg.termui.visible_prompt_func = old_visible_prompt_func
clickpkg.termui.hidden_prompt_func = old_hidden_prompt_func
clickpkg.termui._getchar = old__getchar_func
def invoke(self, cli, args=None, input=None, env=None,
catch_exceptions=True, **extra):
"""Invokes a command in an isolated environment. The arguments are
forwarded directly to the command line script, the `extra` keyword
arguments are passed to the :meth:`~clickpkg.Command.main` function of
the command.
This returns a :class:`Result` object.
.. versionadded:: 3.0
The ``catch_exceptions`` parameter was added.
.. versionchanged:: 3.0
The result object now has an `exc_info` attribute with the
traceback if available.
:param cli: the command to invoke
:param args: the arguments to invoke
:param input: the input data for `sys.stdin`.
:param env: the environment overrides.
:param catch_exceptions: Whether to catch any other exceptions than
``SystemExit``.
:param extra: the keyword arguments to pass to :meth:`main`.
"""
exc_info = None
with self.isolation(input=input, env=env) as out:
exception = None
exit_code = 0
try:
cli.main(args=args or (),
prog_name=self.get_default_prog_name(cli), **extra)
except SystemExit as e:
if e.code != 0:
exception = e
exit_code = e.code
exc_info = sys.exc_info()
except Exception as e:
if not catch_exceptions:
raise
exception = e
exit_code = -1
exc_info = sys.exc_info()
finally:
sys.stdout.flush()
output = out.getvalue()
return Result(runner=self,
output_bytes=output,
exit_code=exit_code,
exception=exception,
exc_info=exc_info)
@contextlib.contextmanager
def isolated_filesystem(self):
"""A context manager that creates a temporary folder and changes
the current working directory to it for isolated filesystem tests.
"""
cwd = os.getcwd()
t = tempfile.mkdtemp()
os.chdir(t)
try:
yield t
finally:
os.chdir(cwd)
try:
shutil.rmtree(t)
except (OSError, IOError):
pass

451
click/types.py Normal file
View file

@ -0,0 +1,451 @@
import os
import sys
import stat
from ._compat import open_stream, text_type, filename_to_ui, get_streerror
from .exceptions import BadParameter
from .utils import safecall, LazyFile
class ParamType(object):
"""Helper for converting values through types. The following is
necessary for a valid type:
* it needs a name
* it needs to pass through None unchanged
* it needs to convert from a string
* it needs to convert its result type through unchanged
(eg: needs to be idempotent)
* it needs to be able to deal with param and context being `None`.
This can be the case when the object is used with prompt
inputs.
"""
#: the descriptive name of this type
name = None
#: if a list of this type is expected and the value is pulled from a
#: string environment variable, this is what splits it up. `None`
#: means any whitespace. For all parameters the general rule is that
#: whitespace splits them up. The exception are paths and files which
#: are split by ``os.path.pathsep`` by default (":" on Unix and ";" on
#: Windows).
envvar_list_splitter = None
def __call__(self, value, param=None, ctx=None):
if value is not None:
return self.convert(value, param, ctx)
def get_metavar(self, param):
"""Returns the metavar default for this param if it provides one."""
def get_missing_message(self, param):
"""Optionally might return extra information about a missing
parameter.
.. versionadded:: 2.0
"""
def convert(self, value, param, ctx):
"""Converts the value. This is not invoked for values that are
`None` (the missing value).
"""
return value
def split_envvar_value(self, rv):
"""Given a value from an environment variable this splits it up
into small chunks depending on the defined envvar list splitter.
If the splitter is set to `None`, which means that whitespace splits,
then leading and trailing whitespace is ignored. Otherwise, leading
and trailing splitters usually lead to empty items being included.
"""
return (rv or '').split(self.envvar_list_splitter)
def fail(self, message, param=None, ctx=None):
"""Helper method to fail with an invalid value message."""
raise BadParameter(message, ctx=ctx, param=param)
class FuncParamType(ParamType):
def __init__(self, func):
self.name = func.__name__
self.func = func
def convert(self, value, param, ctx):
try:
return self.func(value)
except ValueError:
try:
value = text_type(value)
except UnicodeError:
value = str(value).decode('utf-8', 'replace')
self.fail(value, param, ctx)
class StringParamType(ParamType):
name = 'text'
def convert(self, value, param, ctx):
if isinstance(value, bytes):
try:
enc = getattr(sys.stdin, 'encoding', None)
if enc is not None:
value = value.decode(enc)
except UnicodeError:
try:
value = value.decode(sys.getfilesystemencoding())
except UnicodeError:
value = value.decode('utf-8', 'replace')
return value
return value
def __repr__(self):
return 'STRING'
class Choice(ParamType):
"""The choice type allows a value to checked against a fixed set of
supported values. All of these values have to be strings.
See :ref:`choice-opts` for an example.
"""
name = 'choice'
def __init__(self, choices):
self.choices = choices
def get_metavar(self, param):
return '[%s]' % '|'.join(self.choices)
def get_missing_message(self, param):
return 'Choose from %s.' % ', '.join(self.choices)
def convert(self, value, param, ctx):
# Exact match
if value in self.choices:
return value
# Match through normalization
if ctx is not None and \
ctx.token_normalize_func is not None:
value = ctx.token_normalize_func(value)
for choice in self.choices:
if ctx.token_normalize_func(choice) == value:
return choice
self.fail('invalid choice: %s. (choose from %s)' %
(value, ', '.join(self.choices)), param, ctx)
def __repr__(self):
return 'Choice(%r)' % list(self.choices)
class IntParamType(ParamType):
name = 'integer'
def convert(self, value, param, ctx):
try:
return int(value)
except ValueError:
self.fail('%s is not a valid integer' % value, param, ctx)
def __repr__(self):
return 'INT'
class IntRange(IntParamType):
"""A parameter that works similar to :data:`click.INT` but restricts
the value to fit into a range. The default behavior is to fail if the
value falls outside the range, but it can also be silently clamped
between the two edges.
See :ref:`ranges` for an example.
"""
name = 'integer range'
def __init__(self, min=None, max=None, clamp=False):
self.min = min
self.max = max
self.clamp = clamp
def convert(self, value, param, ctx):
rv = IntParamType.convert(self, value, param, ctx)
if self.clamp:
if self.min is not None and rv < self.min:
return self.min
if self.max is not None and rv > self.max:
return self.max
if self.min is not None and rv < self.min or \
self.max is not None and rv > self.max:
if self.min is None:
self.fail('%s is bigger than the maximum valid value '
'%s.' % (rv, self.max), param, ctx)
elif self.max is None:
self.fail('%s is smaller than the minimum valid value '
'%s.' % (rv, self.min), param, ctx)
else:
self.fail('%s is not in the valid range of %s to %s.'
% (rv, self.min, self.max), param, ctx)
return rv
def __repr__(self):
return 'IntRange(%r, %r)' % (self.min, self.max)
class BoolParamType(ParamType):
name = 'boolean'
def convert(self, value, param, ctx):
if isinstance(value, bool):
return bool(value)
value = value.lower()
if value in ('true', '1', 'yes', 'y'):
return True
elif value in ('false', '0', 'no', 'n'):
return False
self.fail('%s is not a valid boolean' % value, param, ctx)
def __repr__(self):
return 'BOOL'
class FloatParamType(ParamType):
name = 'float'
def convert(self, value, param, ctx):
try:
return float(value)
except ValueError:
self.fail('%s is not a valid floating point value' %
value, param, ctx)
def __repr__(self):
return 'FLOAT'
class UUIDParameterType(ParamType):
name = 'uuid'
def convert(self, value, param, ctx):
import uuid
try:
return uuid.UUID(value)
except ValueError:
self.fail('%s is not a valid UUID value' % value, param, ctx)
def __repr__(self):
return 'UUID'
class File(ParamType):
"""Declares a parameter to be a file for reading or writing. The file
is automatically closed once the context tears down (after the command
finished working).
Files can be opened for reading or writing. The special value ``-``
indicates stdin or stdout depending on the mode.
By default, the file is opened for reading text data, but it can also be
opened in binary mode or for writing. The encoding parameter can be used
to force a specific encoding.
The `lazy` flag controls if the file should be opened immediately or
upon first IO. The default is to be non lazy for standard input and
output streams as well as files opened for reading, lazy otherwise.
Starting with Click 2.0, files can also be opened atomically in which
case all writes go into a separate file in the same folder and upon
completion the file will be moved over to the original location. This
is useful if a file regularly read by other users is modified.
See :ref:`file-args` for more information.
"""
name = 'filename'
envvar_list_splitter = os.path.pathsep
def __init__(self, mode='r', encoding=None, errors='strict', lazy=None,
atomic=False):
self.mode = mode
self.encoding = encoding
self.errors = errors
self.lazy = lazy
self.atomic = atomic
def resolve_lazy_flag(self, value):
if self.lazy is not None:
return self.lazy
if value == '-':
return False
elif 'w' in self.mode:
return True
return False
def convert(self, value, param, ctx):
try:
if hasattr(value, 'read') or hasattr(value, 'write'):
return value
lazy = self.resolve_lazy_flag(value)
if lazy:
f = LazyFile(value, self.mode, self.encoding, self.errors,
atomic=self.atomic)
if ctx is not None:
ctx.call_on_close(f.close_intelligently)
return f
f, should_close = open_stream(value, self.mode,
self.encoding, self.errors,
atomic=self.atomic)
# If a context is provided, we automatically close the file
# at the end of the context execution (or flush out). If a
# context does not exist, it's the caller's responsibility to
# properly close the file. This for instance happens when the
# type is used with prompts.
if ctx is not None:
if should_close:
ctx.call_on_close(safecall(f.close))
else:
ctx.call_on_close(safecall(f.flush))
return f
except (IOError, OSError) as e:
self.fail('Could not open file: %s: %s' % (
filename_to_ui(value),
get_streerror(e),
), param, ctx)
class Path(ParamType):
"""The path type is similar to the :class:`File` type but it performs
different checks. First of all, instead of returning a open file
handle it returns just the filename. Secondly, it can perform various
basic checks about what the file or directory should be.
:param exists: if set to true, the file or directory needs to exist for
this value to be valid. If this is not required and a
file does indeed not exist, then all further checks are
silently skipped.
:param file_okay: controls if a file is a possible value.
:param dir_okay: controls if a directory is a possible value.
:param writable: if true, a writable check is performed.
:param readable: if true, a readable check is performed.
:param resolve_path: if this is true, then the path is fully resolved
before the value is passed onwards. This means
that it's absolute and symlinks are resolved.
"""
envvar_list_splitter = os.path.pathsep
def __init__(self, exists=False, file_okay=True, dir_okay=True,
writable=False, readable=True, resolve_path=False):
self.exists = exists
self.file_okay = file_okay
self.dir_okay = dir_okay
self.writable = writable
self.readable = readable
self.resolve_path = resolve_path
if self.file_okay and not self.dir_okay:
self.name = 'file'
self.path_type = 'File'
if self.dir_okay and not self.file_okay:
self.name = 'directory'
self.path_type = 'Directory'
else:
self.name = 'path'
self.path_type = 'Path'
def convert(self, value, param, ctx):
rv = value
if self.resolve_path:
rv = os.path.realpath(rv)
try:
st = os.stat(rv)
except OSError:
if not self.exists:
return rv
self.fail('%s "%s" does not exist.' % (
self.path_type,
filename_to_ui(value)
), param, ctx)
if not self.file_okay and stat.S_ISREG(st.st_mode):
self.fail('%s "%s" is a file.' % (
self.path_type,
filename_to_ui(value)
), param, ctx)
if not self.dir_okay and stat.S_ISDIR(st.st_mode):
self.fail('%s "%s" is a directory.' % (
self.path_type,
filename_to_ui(value)
), param, ctx)
if self.writable and not os.access(value, os.W_OK):
self.fail('%s "%s" is not writable.' % (
self.path_type,
filename_to_ui(value)
), param, ctx)
if self.readable and not os.access(value, os.R_OK):
self.fail('%s "%s" is not readable.' % (
self.path_type,
filename_to_ui(value)
), param, ctx)
return rv
def convert_type(ty, default=None):
"""Converts a callable or python ty into the most appropriate param
ty.
"""
if isinstance(ty, ParamType):
return ty
guessed_type = False
if ty is None and default is not None:
ty = type(default)
guessed_type = True
if ty is text_type or ty is str or ty is None:
return STRING
if ty is int:
return INT
# Booleans are only okay if not guessed. This is done because for
# flags the default value is actually a bit of a lie in that it
# indicates which of the flags is the one we want. See get_default()
# for more information.
if ty is bool and not guessed_type:
return BOOL
if ty is float:
return FLOAT
if guessed_type:
return STRING
# Catch a common mistake
if __debug__:
try:
if issubclass(ty, ParamType):
raise AssertionError('Attempted to use an uninstantiated '
'parameter type (%s).' % ty)
except TypeError:
pass
return FuncParamType(ty)
#: A unicode string parameter type which is the implicit default. This
#: can also be selected by using ``str`` as type.
STRING = StringParamType()
#: An integer parameter. This can also be selected by using ``int`` as
#: type.
INT = IntParamType()
#: A floating point value parameter. This can also be selected by using
#: ``float`` as type.
FLOAT = FloatParamType()
#: A boolean parameter. This is the default for boolean flags. This can
#: also be selected by using ``bool`` as a type.
BOOL = BoolParamType()
#: A UUID parameter.
UUID = UUIDParameterType()

425
click/utils.py Normal file
View file

@ -0,0 +1,425 @@
import os
import sys
from collections import deque
from ._compat import text_type, open_stream, get_streerror, string_types, \
PY2, binary_streams, text_streams, filename_to_ui, \
auto_wrap_for_ansi, strip_ansi, isatty, _default_text_stdout, \
_default_text_stderr, is_bytes, WIN
if not PY2:
from ._compat import _find_binary_writer
echo_native_types = string_types + (bytes, bytearray)
def _posixify(name):
return '-'.join(name.split()).lower()
def unpack_args(args, nargs_spec):
"""Given an iterable of arguments and an iterable of nargs specifications,
it returns a tuple with all the unpacked arguments at the first index
and all remaining arguments as the second.
The nargs specification is the number of arguments that should be consumed
or `-1` to indicate that this position should eat up all the remainders.
Missing items are filled with `None`.
Examples:
>>> unpack_args(range(6), [1, 2, 1, -1])
((0, (1, 2), 3, (4, 5)), [])
>>> unpack_args(range(6), [1, 2, 1])
((0, (1, 2), 3), [4, 5])
>>> unpack_args(range(6), [-1])
(((0, 1, 2, 3, 4, 5),), [])
>>> unpack_args(range(6), [1, 1])
((0, 1), [2, 3, 4, 5])
"""
args = deque(args)
nargs_spec = deque(nargs_spec)
rv = []
spos = None
def _fetch(c):
try:
return (spos is not None and c.pop() or c.popleft())
except IndexError:
return None
while nargs_spec:
nargs = _fetch(nargs_spec)
if nargs == 1:
rv.append(_fetch(args))
elif nargs > 1:
x = [_fetch(args) for _ in range(nargs)]
# If we're reversed, we're pulling in the arguments in reverse,
# so we need to turn them around.
if spos is not None:
x.reverse()
rv.append(tuple(x))
elif nargs < 0:
if spos is not None:
raise TypeError('Cannot have two nargs < 0')
spos = len(rv)
rv.append(None)
# spos is the position of the wildcard (star). If it's not `None`,
# we fill it with the remainder.
if spos is not None:
rv[spos] = tuple(args)
args = []
return tuple(rv), list(args)
def safecall(func):
"""Wraps a function so that it swallows exceptions."""
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except Exception:
pass
return wrapper
def make_str(value):
"""Converts a value into a valid string."""
if isinstance(value, bytes):
try:
return value.decode(sys.getfilesystemencoding())
except UnicodeError:
return value.decode('utf-8', 'replace')
return text_type(value)
def make_default_short_help(help, max_length=45):
words = help.split()
total_length = 0
result = []
done = False
for word in words:
if word[-1:] == '.':
done = True
new_length = result and 1 + len(word) or len(word)
if total_length + new_length > max_length:
result.append('...')
done = True
else:
if result:
result.append(' ')
result.append(word)
if done:
break
total_length += new_length
return ''.join(result)
class LazyFile(object):
"""A lazy file works like a regular file but it does not fully open
the file but it does perform some basic checks early to see if the
filename parameter does make sense. This is useful for safely opening
files for writing.
"""
def __init__(self, filename, mode='r', encoding=None, errors='strict',
atomic=False):
self.name = filename
self.mode = mode
self.encoding = encoding
self.errors = errors
self.atomic = atomic
if filename == '-':
self._f, self.should_close = open_stream(filename, mode,
encoding, errors)
else:
if 'r' in mode:
# Open and close the file in case we're opening it for
# reading so that we can catch at least some errors in
# some cases early.
open(filename, mode).close()
self._f = None
self.should_close = True
def __getattr__(self, name):
return getattr(self.open(), name)
def __repr__(self):
if self._f is not None:
return repr(self._f)
return '<unopened file %r %s>' % (self.name, self.mode)
def open(self):
"""Opens the file if it's not yet open. This call might fail with
a :exc:`FileError`. Not handling this error will produce an error
that Click shows.
"""
if self._f is not None:
return self._f
try:
rv, self.should_close = open_stream(self.name, self.mode,
self.encoding,
self.errors,
atomic=self.atomic)
except (IOError, OSError) as e:
from .exceptions import FileError
raise FileError(self.name, hint=get_streerror(e))
self._f = rv
return rv
def close(self):
"""Closes the underlying file, no matter what."""
if self._f is not None:
self._f.close()
def close_intelligently(self):
"""This function only closes the file if it was opened by the lazy
file wrapper. For instance this will never close stdin.
"""
if self.should_close:
self.close()
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, tb):
self.close_intelligently()
class KeepOpenFile(object):
def __init__(self, file):
self._file = file
def __getattr__(self, name):
return getattr(self._file, name)
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, tb):
pass
def __repr__(self):
return repr(self._file)
def echo(message=None, file=None, nl=True, err=False):
"""Prints a message plus a newline to the given file or stdout. On
first sight, this looks like the print function, but it has improved
support for handling Unicode and binary data that does not fail no
matter how badly configured the system is.
Primarily it means that you can print binary data as well as Unicode
data on both 2.x and 3.x to the given file in the most appropriate way
possible. This is a very carefree function as in that it will try its
best to not fail.
In addition to that, if `colorama`_ is installed, the echo function will
also support clever handling of ANSI codes. Essentially it will then
do the following:
- add transparent handling of ANSI color codes on Windows.
- hide ANSI codes automatically if the destination file is not a
terminal.
.. _colorama: http://pypi.python.org/pypi/colorama
.. versionchanged:: 2.0
Starting with version 2.0 of Click, the echo function will work
with colorama if it's installed.
.. versionadded:: 3.0
The `err` parameter was added.
:param message: the message to print
:param file: the file to write to (defaults to ``stdout``)
:param err: if set to true the file defaults to ``stderr`` instead of
``stdout``. This is faster and easier than calling
:func:`get_text_stderr` yourself.
:param nl: if set to `True` (the default) a newline is printed afterwards.
"""
if file is None:
if err:
file = _default_text_stderr()
else:
file = _default_text_stdout()
# Convert non bytes/text into the native string type.
if message is not None and not isinstance(message, echo_native_types):
message = text_type(message)
# If there is a message, and we're in Python 3, and the value looks
# like bytes, we manually need to find the binary stream and write the
# message in there. This is done separately so that most stream
# types will work as you would expect. Eg: you can write to StringIO
# for other cases.
if message and not PY2 and is_bytes(message):
binary_file = _find_binary_writer(file)
if binary_file is not None:
file.flush()
binary_file.write(message)
if nl:
binary_file.write(b'\n')
binary_file.flush()
return
# ANSI-style support. If there is no message or we are dealing with
# bytes nothing is happening. If we are connected to a file we want
# to strip colors. If we are on windows we either wrap the stream
# to strip the color or we use the colorama support to translate the
# ansi codes to API calls.
if message and not is_bytes(message):
if not isatty(file):
message = strip_ansi(message)
elif WIN:
if auto_wrap_for_ansi is not None:
file = auto_wrap_for_ansi(file)
else:
message = strip_ansi(message)
if message:
file.write(message)
if nl:
file.write('\n')
file.flush()
def get_binary_stream(name):
"""Returns a system stream for byte processing. This essentially
returns the stream from the sys module with the given name but it
solves some compatibility issues between different Python versions.
Primarily this function is necessary for getting binary streams on
Python 3.
:param name: the name of the stream to open. Valid names are ``'stdin'``,
``'stdout'`` and ``'stderr'``
"""
opener = binary_streams.get(name)
if opener is None:
raise TypeError('Unknown standard stream %r' % name)
return opener()
def get_text_stream(name, encoding=None, errors='strict'):
"""Returns a system stream for text processing. This usually returns
a wrapped stream around a binary stream returned from
:func:`get_binary_stream` but it also can take shortcuts on Python 3
for already correctly configured streams.
:param name: the name of the stream to open. Valid names are ``'stdin'``,
``'stdout'`` and ``'stderr'``
:param encoding: overrides the detected default encoding.
:param errors: overrides the default error mode.
"""
opener = text_streams.get(name)
if opener is None:
raise TypeError('Unknown standard stream %r' % name)
return opener(encoding, errors)
def open_file(filename, mode='r', encoding=None, errors='strict',
lazy=False, atomic=False):
"""This is similar to how the :class:`File` works but for manual
usage. Files are opened non lazy by default. This can open regular
files as well as stdin/stdout if ``'-'`` is passed.
If stdin/stdout is returned the stream is wrapped so that the context
manager will not close the stream accidentally. This makes it possible
to always use the function like this without having to worry to
accidentally close a standard stream::
with open_file(filename) as f:
...
.. versionadded:: 3.0
:param filename: the name of the file to open (or ``'-'`` for stdin/stdout).
:param mode: the mode in which to open the file.
:param encoding: the encoding to use.
:param errors: the error handling for this file.
:param lazy: can be flipped to true to open the file lazily.
:param atomic: in atomic mode writes go into a temporary file and it's
moved on close.
"""
if lazy:
return LazyFile(filename, mode, encoding, errors, atomic=atomic)
f, should_close = open_stream(filename, mode, encoding, errors,
atomic=atomic)
if not should_close:
f = KeepOpenFile(f)
return f
def format_filename(filename, shorten=False):
"""Formats a filename for user display. The main purpose of this
function is to ensure that the filename can be displayed at all. This
will decode the filename to unicode if necessary in a way that it will
not fail. Optionally, it can shorten the filename to not include the
full path to the filename.
:param filename: formats a filename for UI display. This will also convert
the filename into unicode without failing.
:param shorten: this optionally shortens the filename to strip of the
path that leads up to it.
"""
if shorten:
filename = os.path.basename(filename)
return filename_to_ui(filename)
def get_app_dir(app_name, roaming=True, force_posix=False):
r"""Returns the config folder for the application. The default behavior
is to return whatever is most appropriate for the operating system.
To give you an idea, for an app called ``"Foo Bar"``, something like
the following folders could be returned:
Mac OS X:
``~/Library/Application Support/Foo Bar``
Mac OS X (POSIX):
``~/.foo-bar``
Unix:
``~/.config/foo-bar``
Unix (POSIX):
``~/.foo-bar``
Win XP (roaming):
``C:\Documents and Settings\<user>\Local Settings\Application Data\Foo Bar``
Win XP (not roaming):
``C:\Documents and Settings\<user>\Application Data\Foo Bar``
Win 7 (roaming):
``C:\Users\<user>\AppData\Roaming\Foo Bar``
Win 7 (not roaming):
``C:\Users\<user>\AppData\Local\Foo Bar``
.. versionadded:: 2.0
:param app_name: the application name. This should be properly capitalized
and can contain whitespace.
:param roaming: controls if the folder should be roaming or not on Windows.
Has no affect otherwise.
:param force_posix: if this is set to `True` then on any POSIX system the
folder will be stored in the home folder with a leading
dot instead of the XDG config home or darwin's
application support folder.
"""
if WIN:
key = roaming and 'APPDATA' or 'LOCALAPPDATA'
folder = os.environ.get(key)
if folder is None:
folder = os.path.expanduser('~')
return os.path.join(folder, app_name)
if force_posix:
return os.path.join(os.path.expanduser('~/.' + _posixify(app_name)))
if sys.platform == 'darwin':
return os.path.join(os.path.expanduser(
'~/Library/Application Support'), app_name)
return os.path.join(
os.environ.get('XDG_CONFIG_HOME', os.path.expanduser('~/.config')),
_posixify(app_name))

130
docs/Makefile Normal file
View file

@ -0,0 +1,130 @@
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = _build
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
clean:
-rm -rf $(BUILDDIR)/*
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
singlehtml:
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Classy.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Classy.qhc"
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/Classy"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Classy"
@echo "# devhelp"
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
"run these through (pdf)latex."
latexpdf: latex
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
make -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."

BIN
docs/_static/click-small.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
docs/_static/click-small@2x.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

BIN
docs/_static/click.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

BIN
docs/_static/click@2x.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

13
docs/_templates/sidebarintro.html vendored Normal file
View file

@ -0,0 +1,13 @@
<h3>About</h3>
<p>
Click is a Python package for creating beautiful command line interfaces in a
composable way with as little amount of code as necessary. Its the “Command
Line Interface Creation Kit”.
</p>
<h3>Useful Links</h3>
<ul>
<li><a href="http://click.pocoo.org/">The Click Website</a></li>
<li><a href="http://pypi.python.org/pypi/click">click @ PyPI</a></li>
<li><a href="http://github.com/mitsuhiko/click">click @ github</a></li>
<li><a href="http://github.com/mitsuhiko/click/issues">Issue Tracker</a></li>
</ul>

3
docs/_templates/sidebarlogo.html vendored Normal file
View file

@ -0,0 +1,3 @@
<p class="logo"><a href="{{ pathto(master_doc) }}">
<img class="logo" src="{{ pathto('_static/click-small@2x.png', 1) }}" width="120" height="50" alt="Logo">
</a></p>

37
docs/_themes/LICENSE vendored Normal file
View file

@ -0,0 +1,37 @@
Copyright (c) 2014 by Armin Ronacher.
Some rights reserved.
Redistribution and use in source and binary forms of the theme, with or
without modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* The names of the contributors may not be used to endorse or
promote products derived from this software without specific
prior written permission.
We kindly ask you to only use these themes in an unmodified manner just
for Click and Click-related products, not for unrelated projects. If you
like the visual style and want to use it for your own projects, please
consider making some larger changes to the themes (such as changing
font faces, sizes, colors or margins).
THIS THEME IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS THEME, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

31
docs/_themes/README vendored Normal file
View file

@ -0,0 +1,31 @@
Flask Sphinx Styles
===================
This repository contains sphinx styles for Flask and Flask related
projects. To use this style in your Sphinx documentation, follow
this guide:
1. put this folder as _themes into your docs folder. Alternatively
you can also use git submodules to check out the contents there.
2. add this to your conf.py:
sys.path.append(os.path.abspath('_themes'))
html_theme_path = ['_themes']
html_theme = 'flask'
The following themes exist:
- 'flask' - the standard flask documentation theme for large
projects
- 'flask_small' - small one-page theme. Intended to be used by
very small addon libraries for flask.
The following options exist for the flask_small theme:
[options]
index_logo = '' filename of a picture in _static
to be used as replacement for the
h1 in the index.rst file.
index_logo_height = 120px height of the index logo
github_fork = '' repository name on github for the
"fork me" badge

20
docs/_themes/click/layout.html vendored Normal file
View file

@ -0,0 +1,20 @@
{%- extends "basic/layout.html" %}
{%- block extrahead %}
{{ super() }}
{% endblock %}
{%- block relbar2 %}{% endblock %}
{% block header %}
{{ super() }}
{% if pagename == 'index' %}
<div class=indexwrapper>
{% endif %}
{% endblock %}
{%- block footer %}
<div class="footer">
&copy; Copyright {{ copyright }}.
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
</div>
{% if pagename == 'index' %}
</div>
{% endif %}
{%- endblock %}

19
docs/_themes/click/relations.html vendored Normal file
View file

@ -0,0 +1,19 @@
<h3>Related Topics</h3>
<ul>
<li><a href="{{ pathto(master_doc) }}">Documentation overview</a><ul>
{%- for parent in parents %}
<li><a href="{{ parent.link|e }}">{{ parent.title }}</a><ul>
{%- endfor %}
{%- if prev %}
<li>Previous: <a href="{{ prev.link|e }}" title="{{ _('previous chapter')
}}">{{ prev.title }}</a></li>
{%- endif %}
{%- if next %}
<li>Next: <a href="{{ next.link|e }}" title="{{ _('next chapter')
}}">{{ next.title }}</a></li>
{%- endif %}
{%- for parent in parents %}
</ul></li>
{%- endfor %}
</ul></li>
</ul>

403
docs/_themes/click/static/click.css_t vendored Normal file
View file

@ -0,0 +1,403 @@
/*
* click.css_t
* ~~~~~~~~~~~
*
* :copyright: Copyright 2014 by Armin Ronacher.
* :license: Flask Design License, see LICENSE for details.
*/
{% set page_width = '940px' %}
{% set sidebar_width = '220px' %}
@import url("basic.css");
@import url(http://fonts.googleapis.com/css?family=Ubuntu+Mono:400,400italic,700,700italic);
@import url(http://fonts.googleapis.com/css?family=Open+Sans:300,400);
/* -- page layout ----------------------------------------------------------- */
body {
font-family: 'Ubuntu Mono', 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono';
font-size: 15px;
background-color: white;
color: #000;
margin: 0;
padding: 0;
}
div.document {
width: {{ page_width }};
margin: 30px auto 0 auto;
}
div.documentwrapper {
float: left;
width: 100%;
}
div.bodywrapper {
margin: 0 0 0 {{ sidebar_width }};
}
div.sphinxsidebar {
width: {{ sidebar_width }};
}
div.sphinxsidebar h3,
div.sphinxsidebar h4 {
font-family: 'Open Sans', 'Helvetica', 'Arial', sans-serif;
font-weight: 300;
margin: 20px 0px 10px 0px;
padding: 0;
}
hr {
border: 1px solid #B1B4B6;
}
div.body {
background-color: #ffffff;
color: #3E4349;
padding: 0 30px 0 30px;
}
img.floatingflask {
padding: 0 0 10px 10px;
float: right;
}
div.footer {
width: {{ page_width }};
margin: 20px auto 30px auto;
font-size: 14px;
color: #888;
text-align: right;
}
div.footer a {
color: #888;
}
div.related {
display: none;
}
div.sphinxsidebar a {
color: #444;
text-decoration: none;
border-bottom: 1px dotted #999;
}
div.sphinxsidebar a:hover {
border-bottom: 1px solid #999;
}
div.sphinxsidebar {
font-size: 14px;
line-height: 1.5;
}
div.sphinxsidebarwrapper {
padding: 18px 10px;
}
div.sphinxsidebarwrapper p.logo {
padding: 0 0 20px 0;
margin: 0;
text-align: left;
}
div.sphinxsidebar h3,
div.sphinxsidebar h4 {
color: #444;
font-size: 18px;
font-weight: normal;
margin: 0 0 5px 0;
padding: 0;
}
div.sphinxsidebar h4 {
font-size: 15px;
}
div.sphinxsidebar h3 a {
color: #444;
}
div.sphinxsidebar p.logo a,
div.sphinxsidebar h3 a,
div.sphinxsidebar p.logo a:hover,
div.sphinxsidebar h3 a:hover {
border: none;
}
div.sphinxsidebar p {
color: #555;
margin: 10px 0;
}
div.sphinxsidebar ul {
margin: 10px 0;
padding: 0;
color: #000;
}
div.sphinxsidebar input {
border: 1px solid #ccc;
font-family: 'Ubuntu Mono', 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono';
font-size: 13px;
}
div.sphinxsidebar #searchbox input[type="text"] {
width: 120px;
}
/* -- body styles ----------------------------------------------------------- */
a {
color: #5D2CD1;
text-decoration: underline;
}
a:hover {
color: #7546E3;
text-decoration: underline;
}
div.body h1,
div.body h2,
div.body h3,
div.body h4,
div.body h5,
div.body h6 {
font-family: 'Open Sans', 'Helvetica', 'Arial', sans-serif;
font-weight: 400;
margin: 30px 0px 10px 0px;
padding: 0;
}
div.indexwrapper h1 {
text-indent: -999999px;
background: url(click.png) no-repeat center center;
height: 200px;
}
@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {
div.indexwrapper h1 {
background: url(click@2x.png) no-repeat center center;
background-size: 420px 175px;
}
}
div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; }
div.body h2 { font-size: 180%; }
div.body h3 { font-size: 150%; }
div.body h4 { font-size: 130%; }
div.body h5 { font-size: 100%; }
div.body h6 { font-size: 100%; }
a.headerlink {
color: #ddd;
padding: 0 4px;
text-decoration: none;
}
a.headerlink:hover {
color: #444;
background: #eaeaea;
}
div.body p, div.body dd, div.body li {
line-height: 1.4em;
}
div.admonition {
background: #fafafa;
margin: 20px -30px;
padding: 10px 30px;
border-top: 1px solid #ccc;
border-bottom: 1px solid #ccc;
}
div.admonition tt.xref, div.admonition a tt {
border-bottom: 1px solid #fafafa;
}
dd div.admonition {
margin-left: -60px;
padding-left: 60px;
}
div.admonition p.admonition-title {
font-family: 'Open Sans', 'Helvetica', 'Arial', sans-serif;
font-weight: 400;
font-size: 24px;
margin: 0 0 10px 0;
padding: 0;
line-height: 1;
}
div.admonition p.last {
margin-bottom: 0;
}
div.highlight {
background-color: white;
}
dt:target, .highlight {
background: #FAF3E8;
}
div.note {
background-color: #eee;
border: 1px solid #ccc;
}
div.seealso {
background-color: #ffc;
border: 1px solid #ff6;
}
div.topic {
background-color: #eee;
}
p.admonition-title {
display: inline;
}
p.admonition-title:after {
content: ":";
}
pre, tt {
font-family: 'Ubuntu Mono', 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono';
font-size: 15px;
}
img.screenshot {
}
tt.descname, tt.descclassname {
font-size: 0.95em;
}
tt.descname {
padding-right: 0.08em;
}
img.screenshot {
-moz-box-shadow: 2px 2px 4px #eee;
-webkit-box-shadow: 2px 2px 4px #eee;
box-shadow: 2px 2px 4px #eee;
}
table.docutils {
border: 1px solid #888;
-moz-box-shadow: 2px 2px 4px #eee;
-webkit-box-shadow: 2px 2px 4px #eee;
box-shadow: 2px 2px 4px #eee;
}
table.docutils td, table.docutils th {
border: 1px solid #888;
padding: 0.25em 0.7em;
}
table.field-list, table.footnote {
border: none;
-moz-box-shadow: none;
-webkit-box-shadow: none;
box-shadow: none;
}
table.footnote {
margin: 15px 0;
width: 100%;
border: 1px solid #eee;
background: #fdfdfd;
font-size: 0.9em;
}
table.footnote + table.footnote {
margin-top: -15px;
border-top: none;
}
table.field-list th {
padding: 0 0.8em 0 0;
}
table.field-list td {
padding: 0;
}
table.footnote td.label {
width: 0px;
padding: 0.3em 0 0.3em 0.5em;
}
table.footnote td {
padding: 0.3em 0.5em;
}
dl {
margin: 0;
padding: 0;
}
dl dd {
margin-left: 30px;
}
blockquote {
margin: 0 0 0 30px;
padding: 0;
}
ul, ol {
margin: 10px 0 10px 30px;
padding: 0;
}
pre {
padding: 7px 0 7px 30px;
margin: 15px 0;
line-height: 1.3em;
}
tt {
background-color: #ecf0f3;
color: #222;
/* padding: 1px 2px; */
}
tt.xref, a tt {
background-color: #FBFBFB;
border-bottom: 1px solid white;
}
a.reference {
text-decoration: none;
border-bottom: 1px dotted #5D2CD1;
}
a.reference:hover {
border-bottom: 1px solid #7546E3;
}
a.footnote-reference {
text-decoration: none;
font-size: 0.7em;
vertical-align: top;
border-bottom: 1px dotted #5D2CD1;
}
a.footnote-reference:hover {
border-bottom: 1px solid #7546E3;
}
a:hover tt {
background: #EEE;
}

4
docs/_themes/click/theme.conf vendored Normal file
View file

@ -0,0 +1,4 @@
[theme]
inherit = basic
stylesheet = click.css
pygments_style = tango

245
docs/advanced.rst Normal file
View file

@ -0,0 +1,245 @@
Advanced Patterns
=================
.. currentmodule:: click
In addition to common functionality that is implemented in the library
itself, there are countless patterns that can be implemented by extending
Click. This page should give some insight into what can be accomplished.
.. _aliases:
Command Aliases
---------------
Many tools support aliases for commands. For instance, you can configure
``git`` to accept ``git ci`` as alias for ``git commit``. Other tools
also support auto-discovery for aliases by automatically shortening them.
Click does not support this out of the box, but it's very easy to customize
the :class:`Group` or any other :class:`MultiCommand` to provide this
functionality.
As explained in :ref:`custom-multi-commands`, a multi command can provide
two methods: :meth:`~MultiCommand.list_commands` and
:meth:`~MultiCommand.get_command`. In this particular case, you only need
to override the latter as you generally don't want to enumerate the
aliases on the help page in order to avoid confusion.
This following example implements a subclass of :class:`Group` that
accepts a prefix for a command. If there were a command called ``push``,
it would accept ``pus`` as an alias (so long as it was unique):
.. click:example::
class AliasedGroup(click.Group):
def get_command(self, ctx, cmd_name):
rv = click.Group.get_command(self, ctx, cmd_name)
if rv is not None:
return rv
matches = [x for x in self.list_commands(ctx)
if x.startswith(cmd_name)]
if not matches:
return None
elif len(matches) == 1:
return click.Group.get_command(self, ctx, matches[0])
ctx.fail('Too many matches: %s' % ', '.join(sorted(matches)))
And it can then be used like this:
.. click:example::
@click.command(cls=AliasedGroup)
def cli():
pass
@cli.command()
def push():
pass
@cli.command()
def pop():
pass
Parameter Modifications
-----------------------
Parameters (options and arguments) are forwarded to the command callbacks
as you have seen. One common way to prevent a parameter from being passed
to the callback is the `expose_value` argument to a parameter which hides
the parameter entirely. The way this works is that the :class:`Context`
object has a :attr:`~Context.params` attribute which is a dictionary of
all parameters. Whatever is in that dictionary is being passed to the
callbacks.
This can be used to make up addition parameters. Generally this pattern
is not recommended but in some cases it can be useful. At the very least
it's good to know that the system works this way.
.. click:example::
import urllib
def open_url(ctx, param, value):
if value is not None:
ctx.params['fp'] = urllib.urlopen(value)
return value
@click.command()
@click.option('--url', callback=open_url)
def cli(url, fp=None):
if fp is not None:
click.echo('%s: %s' % (url, fp.code))
In this case the callback returns the URL unchanged but also passes a
second ``fp`` value to the callback. What's more recommended is to pass
the information in a wrapper however:
.. click:example::
import urllib
class URL(object):
def __init__(self, url, fp):
self.url = url
self.fp = fp
def open_url(ctx, param, value):
if value is not None:
return URL(value, urllib.urlopen(value))
@click.command()
@click.option('--url', callback=open_url)
def cli(url):
if url is not None:
click.echo('%s: %s' % (url.url, url.fp.code))
Token Normalization
-------------------
.. versionadded:: 2.0
Starting with Click 2.0, it's possible to provide a function that is used
for normalizing tokens. Tokens are option names, choice values, or command
values. This can be used to implement case insensitive options, for
instance.
In order to use this feature, the context needs to be passed a function that
performs the normalization of the token. For instance, you could have a
function that converts the token to lowercase:
.. click:example::
CONTEXT_SETTINGS = dict(token_normalize_func=lambda x: x.lower())
@click.command(context_settings=CONTEXT_SETTINGS)
@click.option('--name', default='Pete')
def cli(name):
click.echo('Name: %s' % name)
And how it works on the command line:
.. click:run::
invoke(cli, prog_name='cli', args=['--NAME=Pete'])
Invoking Other Commands
-----------------------
Sometimes, it might be interesting to invoke one command from another
command. This is a pattern that is generally discouraged with Click, but
possible nonetheless. For this, you can use the :func:`Context.invoke`
or :func:`Context.forward` methods.
They work similarly, but the difference is that :func:`Context.invoke` merely
invokes another command with the arguments you provide as a caller,
whereas :func:`Context.forward` fills in the arguments from the current
command. Both accept the command as the first argument and everything else
is passed onwards as you would expect.
Example:
.. click:example::
cli = click.Group()
@cli.command()
@click.option('--count', default=1)
def test(count):
click.echo('Count: %d' % count)
@cli.command()
@click.option('--count', default=1)
@click.pass_context
def dist(ctx, count):
ctx.forward(test)
ctx.invoke(test, count=42)
And what it looks like:
.. click:run::
invoke(cli, prog_name='cli', args=['dist'])
.. _callback-evaluation-order:
Callback Evaluation Order
-------------------------
Click works a bit differently than some other command line parsers in that
it attempts to reconcile the order of arguments as defined by the
programmer with the order of arguments as defined by the user before
invoking any callbacks.
This is an important concept to understand when porting complex
patterns to Click from optparse or other systems. A parameter
callback invocation in optparse happens as part of the parsing step,
whereas a callback invocation in Click happens after the parsing.
The main difference is that in optparse, callbacks are invoked with the raw
value as it happens, whereas a callback in Click is invoked after the
value has been fully converted.
Generally, the order of invocation is driven by the order in which the user
provides the arguments to the script; if there is an option called ``--foo``
and an option called ``--bar`` and the user calls it as ``--bar
--foo``, then the callback for ``bar`` will fire before the one for ``foo``.
There are three exceptions to this rule which are important to know:
Eagerness:
An option can be set to be "eager". All eager parameters are
evaluated before all non-eager parameters, but again in the order as
they were provided on the command line by the user.
This is important for parameters that execute and exit like ``--help``
and ``--version``. Both are eager parameters, but whatever parameter
comes first on the command line will win and exit the program.
Repeated parameters:
If an option or argument is split up on the command line into multiple
places because it is repeated -- for instance, ``--exclude foo --include
baz --exclude bar`` -- the callback will fire based on the position of
the first option. In this case, the callback will fire for
``exclude`` and it will be passed both options (``foo`` and
``bar``), then the callback for ``include`` will fire with ``baz``
only.
Note that even if a parameter does not allow multiple versions, Click
will still accept the position of the first, but it will ignore every
value except the last. The reason for this is to allow composability
through shell aliases that set defaults.
Missing parameters:
If a parameter is not defined on the command line, the callback will
still fire. This is different from how it works in optparse where
undefined values do not fire the callback. Missing parameters fire
their callbacks at the very end which makes it possible for them to
default to values from a parameter that came before.
Most of the time you do not need to be concerned about any of this,
but it is important to know how it works for some advanced cases.

169
docs/api.rst Normal file
View file

@ -0,0 +1,169 @@
API
===
.. module:: click
This part of the documentation lists the full API reference of all public
classes and functions.
Decorators
----------
.. autofunction:: command
.. autofunction:: group
.. autofunction:: argument
.. autofunction:: option
.. autofunction:: password_option
.. autofunction:: confirmation_option
.. autofunction:: version_option
.. autofunction:: help_option
.. autofunction:: pass_context
.. autofunction:: pass_obj
.. autofunction:: make_pass_decorator
Utilities
---------
.. autofunction:: echo
.. autofunction:: echo_via_pager
.. autofunction:: prompt
.. autofunction:: confirm
.. autofunction:: progressbar
.. autofunction:: clear
.. autofunction:: style
.. autofunction:: unstyle
.. autofunction:: secho
.. autofunction:: edit
.. autofunction:: launch
.. autofunction:: getchar
.. autofunction:: pause
.. autofunction:: get_terminal_size
.. autofunction:: get_binary_stream
.. autofunction:: get_text_stream
.. autofunction:: open_file
.. autofunction:: get_app_dir
.. autofunction:: format_filename
Commands
--------
.. autoclass:: BaseCommand
:members:
.. autoclass:: Command
:members:
.. autoclass:: MultiCommand
:members:
.. autoclass:: Group
:members:
.. autoclass:: CommandCollection
:members:
Parameters
----------
.. autoclass:: Parameter
:members:
.. autoclass:: Option
.. autoclass:: Argument
Context
-------
.. autoclass:: Context
:members:
Types
-----
.. autodata:: STRING
.. autodata:: INT
.. autodata:: FLOAT
.. autodata:: BOOL
.. autodata:: UUID
.. autoclass:: File
.. autoclass:: Path
.. autoclass:: Choice
.. autoclass:: IntRange
.. autoclass:: ParamType
:members:
Exceptions
----------
.. autoexception:: ClickException
.. autoexception:: Abort
.. autoexception:: UsageError
.. autoexception:: BadParameter
.. autoexception:: FileError
Formatting
----------
.. autoclass:: HelpFormatter
:members:
.. autofunction:: wrap_text
Parsing
-------
.. autoclass:: OptionParser
:members:
Testing
-------
.. currentmodule:: click.testing
.. autoclass:: CliRunner
:members:
.. autoclass:: Result
:members:

245
docs/arguments.rst Normal file
View file

@ -0,0 +1,245 @@
.. _arguments:
Arguments
=========
.. currentmodule:: click
Arguments work similarly to :ref:`options <options>` but are positional.
They also only support a subset of the features of options due to their
syntactical nature. Click will also not attempt to document arguments for
you and wants you to document them manually in order to avoid ugly help
pages.
Basic Arguments
---------------
The most basic option is a simple string argument of one value. If no
type is provided, the type of the default value is used, and if no default
value is provided, the type is assumed to be :data:`STRING`.
Example:
.. click:example::
@click.command()
@click.argument('filename')
def touch(filename):
click.echo(filename)
And what it looks like:
.. click:run::
invoke(touch, args=['foo.txt'])
Variadic Arguments
------------------
The second most common version is variadic arguments where a specific (or
unlimited) number of arguments is accepted. This can be controlled with
the ``nargs`` parameter. If it is set to ``-1``, then an unlimited number
of arguments is accepted.
The value is then passed as a tuple. Note that only one argument can be
set to ``nargs=-1``, as it will eat up all arguments.
Example:
.. click:example::
@click.command()
@click.argument('src', nargs=-1)
@click.argument('dst', nargs=1)
def copy(src, dst):
for fn in src:
click.echo('move %s to folder %s' % (fn, dst))
And what it looks like:
.. click:run::
invoke(copy, args=['foo.txt', 'bar.txt', 'my_folder'])
Note that this is not how you would write this application. The reason
for this is that in this particular example the arguments are defined as
strings. Filenames, however, are not strings! They might be on certain
operating systems, but not necessarily on all. For better ways to write
this, see the next sections.
.. admonition:: Note on Non-Empty Variadic Arguments
If you come from ``argparse``, you might be missing support for setting
``nargs`` to ``+`` to indicate that at least one argument is required.
This is supported by setting ``required=True``. However, this should
not be used if you can avoid it as we believe scripts should gracefully
degrade into becoming noops if a variadic argument is empty. The
reason for this is that very often, scripts are invoked with wildcard
inputs from the command line and they should not error out if the
wildcard is empty.
.. _file-args:
File Arguments
--------------
Since all the examples have already worked with filenames, it makes sense
to explain how to deal with files properly. Command line tools are more
fun if they work with files the Unix way, which is to accept ``-`` as a
special file that refers to stdin/stdout.
Click supports this through the :class:`click.File` type which
intelligently handles files for you. It also deals with Unicode and bytes
correctly for all versions of Python so your script stays very portable.
Example:
.. click:example::
@click.command()
@click.argument('input', type=click.File('rb'))
@click.argument('output', type=click.File('wb'))
def inout(input, output):
while True:
chunk = input.read(1024)
if not chunk:
break
output.write(chunk)
And what it does:
.. click:run::
with isolated_filesystem():
invoke(inout, args=['-', 'hello.txt'], input=['hello'],
terminate_input=True)
invoke(inout, args=['hello.txt', '-'])
File Path Arguments
-------------------
In the previous example, the files were opened immediately. But what if
we just want the filename? The naïve way is to use the default string
argument type. However, remember that Click is Unicode-based, so the string
will always be a Unicode value. Unfortunately, filenames can be Unicode or
bytes depending on which operating system is being used. As such, the type
is insufficient.
Instead, you should be using the :class:`Path` type, which automatically
handles this ambiguity. Not only will it return either bytes or Unicode
depending on what makes more sense, but it will also be able to do some
basic checks for you such as existence checks.
Example:
.. click:example::
@click.command()
@click.argument('f', type=click.Path(exists=True))
def touch(f):
click.echo(click.format_filename(f))
And what it does:
.. click:run::
with isolated_filesystem():
with open('hello.txt', 'w') as f:
f.write('Hello World!\n')
invoke(touch, args=['hello.txt'])
println()
invoke(touch, args=['missing.txt'])
File Opening Safety
-------------------
The :class:`FileType` type has one problem it needs to deal with, and that
is to decide when to open a file. The default behavior is to be
"intelligent" about it. What this means is that it will open stdin/stdout
and files opened for reading immediately. This will give the user direct
feedback when a file cannot be opened, but it will only open files
for writing the first time an IO operation is performed by automatically
wrapping the file in a special wrapper.
This behavior can be forced by passing ``lazy=True`` or ``lazy=False`` to
the constructor. If the file is opened lazily, it will fail its first IO
operation by raising an :exc:`FileError`.
Since files opened for writing will typically immediately empty the file,
the lazy mode should only be disabled if the developer is absolutely sure
that this is intended behavior.
Forcing lazy mode is also very useful to avoid resource handling
confusion. If a file is opened in lazy mode, it will receive a
``close_intelligently`` method that can help figure out if the file
needs closing or not. This is not needed for parameters, but is
necessary for manually prompting with the :func:`prompt` function as you
do not know if a stream like stdout was opened (which was already open
before) or a real file that needs closing.
Starting with Click 2.0, it is also possible to open files in atomic mode by
passing ``atomic=True``. In atomic mode, all writes go into a separate
file in the same folder, and upon completion, the file will be moved over to
the original location. This is useful if a file regularly read by other
users is modified.
Environment Variables
---------------------
Like options, arguments can also grab values from an environment variable.
Unlike options, however, this is only supported for explicitly named
environment variables.
Example usage:
.. click:example::
@click.command()
@click.argument('src', envvar='SRC', type=click.File('r'))
def echo(src):
click.echo(src.read())
And from the command line:
.. click:run::
with isolated_filesystem():
with open('hello.txt', 'w') as f:
f.write('Hello World!')
invoke(echo, env={'SRC': 'hello.txt'})
In that case, it can also be a list of different environment variables
where the first one is picked.
Generally, this feature is not recommended because it can cause the user
a lot of confusion.
Argument-Like Options
---------------------
Sometimes, you want to process arguments that look like options. For
instance, imagine you have a file named ``-foo.txt``. If you pass this as
an argument in this manner, Click will treat it as an option.
To solve this, Click does what any POSIX style command line script does,
and that is to accept the string ``--`` as a separator for options and
arguments. After the ``--`` marker, all further parameters are accepted as
arguments.
Example usage:
.. click:example::
@click.command()
@click.argument('files', nargs=-1, type=click.Path())
def touch(files):
for filename in files:
click.echo(filename)
And from the command line:
.. click:run::
invoke(touch, ['--', '-foo.txt', 'bar.txt'])

69
docs/bashcomplete.rst Normal file
View file

@ -0,0 +1,69 @@
Bash Complete
=============
.. versionadded:: 2.0
As of Click 2.0, there is built-in support for Bash completion for
any Click script. There are certain restrictions on when this completion
is available, but for the most part it should just work.
Limitations
-----------
Bash completion is only available if a script has been installed properly,
and not executed through the ``python`` command. For information about
how to do that, see :ref:`setuptools-integration`. Also, Click currently
only supports completion for Bash.
Currently, Bash completion is an internal feature that is not customizable.
This might be relaxed in future versions.
What it Completes
-----------------
Generally, the Bash completion support will complete subcommands and
parameters. Subcommands are always listed whereas parameters only if at
least a dash has been provided. Example::
$ repo <TAB><TAB>
clone commit copy delete setuser
$ repo clone -<TAB><TAB>
--deep --help --rev --shallow -r
Activation
----------
In order to activate Bash completion, you need to inform Bash that
completion is available for your script, and how. Any Click application
automatically provides support for that. The general way this works is
through a magic environment variable called ``_<PROG_NAME>_COMPLETE``,
where ``<PROG_NAME>`` is your application executable name in uppercase
with dashes replaced by underscores.
If your tool is called ``foo-bar``, then the magic variable is called
``_FOO_BAR_COMPLETE``. By exporting it with the ``source`` value it will
spit out the activation script which can be trivally activated.
For instance, to enable Bash completion for your ``foo-bar`` script, this
is what you would need to put into your ``.bashrc``::
eval "$(_FOO_BAR_COMPLETE=source foo-bar)"
From this point onwards, your script will have Bash completion enabled.
Activation Script
-----------------
The above activation example will always invoke your application on
startup. This might be slowing down the shell activation time
significantly if you have many applications. Alternatively, you could also
ship a file with the contents of that, which is what Git and other systems
are doing.
This can be easily accomplished::
_FOO_BAR_COMPLETE=source foo-bar > foo-bar-complete.sh
And then you would put this into your bashrc instead::
. /path/to/foo-bar-complete.sh

3
docs/changelog.rst Normal file
View file

@ -0,0 +1,3 @@
.. currentmodule:: click
.. include:: ../CHANGES

255
docs/clickdoctools.py Normal file
View file

@ -0,0 +1,255 @@
import os
import sys
import click
import shutil
import tempfile
import contextlib
try:
from StringIO import StringIO
except ImportError:
from io import StringIO
from docutils import nodes
from docutils.statemachine import ViewList
from sphinx.domains import Domain
from sphinx.util.compat import Directive
class EchoingStdin(object):
def __init__(self, input, output):
self._input = input
self._output = output
def __getattr__(self, x):
return getattr(self._input, x)
def _echo(self, rv):
mark = False
if rv.endswith('\xff'):
rv = rv[:-1]
mark = True
self._output.write(rv)
if mark:
self._output.write('^D\n')
return rv
def read(self, n=-1):
return self._echo(self._input.read(n))
def readline(self, n=-1):
return self._echo(self._input.readline(n))
def readlines(self):
return [self._echo(x) for x in self._input.readlines()]
def __iter__(self):
return iter(self._echo(x) for x in self._input)
@contextlib.contextmanager
def isolation(input=None, env=None):
if isinstance(input, unicode):
input = input.encode('utf-8')
input = StringIO(input or '')
output = StringIO()
sys.stdin = EchoingStdin(input, output)
sys.stdin.encoding = 'utf-8'
def visible_input(prompt=None):
sys.stdout.write(prompt or '')
val = input.readline().rstrip('\r\n')
sys.stdout.write(val + '\n')
sys.stdout.flush()
return val
def hidden_input(prompt=None):
sys.stdout.write((prompt or '') + '\n')
sys.stdout.flush()
return input.readline().rstrip('\r\n')
sys.stdout = output
sys.stderr = output
old_visible_prompt_func = click.termui.visible_prompt_func
old_hidden_prompt_func = click.termui.hidden_prompt_func
click.termui.visible_prompt_func = visible_input
click.termui.hidden_prompt_func = hidden_input
old_env = {}
try:
if env:
for key, value in env.iteritems():
old_env[key] = os.environ.get(value)
os.environ[key] = value
yield output
finally:
for key, value in old_env.iteritems():
if value is None:
try:
del os.environ[key]
except Exception:
pass
else:
os.environ[key] = value
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__
click.termui.visible_prompt_func = old_visible_prompt_func
click.termui.hidden_prompt_func = old_hidden_prompt_func
@contextlib.contextmanager
def isolated_filesystem():
cwd = os.getcwd()
t = tempfile.mkdtemp()
os.chdir(t)
try:
yield
finally:
os.chdir(cwd)
try:
shutil.rmtree(t)
except (OSError, IOError):
pass
class ExampleRunner(object):
def __init__(self):
self.namespace = {
'click': click,
'__file__': 'dummy.py',
}
def declare(self, source):
code = compile(source, '<docs>', 'exec')
eval(code, self.namespace)
def run(self, source):
code = compile(source, '<docs>', 'exec')
buffer = []
def invoke(cmd, args=None, prog_name=None,
input=None, terminate_input=False, env=None,
**extra):
if env:
for key, value in sorted(env.items()):
if ' ' in value:
value = '"%s"' % value
buffer.append('$ export %s=%s' % (key, value))
args = args or []
if prog_name is None:
prog_name = cmd.name.replace('_', '-')
buffer.append(('$ %s %s' % (
prog_name,
' '.join(('"%s"' % x) if ' ' in x else x for x in args)
)).rstrip())
if isinstance(input, (tuple, list)):
input = '\n'.join(input) + '\n'
if terminate_input:
input += '\xff'
with isolation(input=input, env=env) as output:
try:
cmd.main(args=args, prog_name=prog_name.split()[-1],
**extra)
except SystemExit:
pass
buffer.extend(output.getvalue().splitlines())
def println(text=''):
buffer.append(text)
eval(code, self.namespace, {
'invoke': invoke,
'println': println,
'isolated_filesystem': isolated_filesystem,
})
return buffer
def close(self):
pass
def parse_rst(state, content_offset, doc):
node = nodes.section()
# hack around title style bookkeeping
surrounding_title_styles = state.memo.title_styles
surrounding_section_level = state.memo.section_level
state.memo.title_styles = []
state.memo.section_level = 0
state.nested_parse(doc, content_offset, node, match_titles=1)
state.memo.title_styles = surrounding_title_styles
state.memo.section_level = surrounding_section_level
return node.children
def get_example_runner(document):
runner = getattr(document, 'click_example_runner', None)
if runner is None:
runner = document.click_example_runner = ExampleRunner()
return runner
class ExampleDirective(Directive):
has_content = True
required_arguments = 0
optional_arguments = 0
final_argument_whitespace = False
def run(self):
doc = ViewList()
runner = get_example_runner(self.state.document)
try:
runner.declare('\n'.join(self.content))
except:
runner.close()
raise
doc.append('.. sourcecode:: python', '')
doc.append('', '')
for line in self.content:
doc.append(' ' + line, '')
return parse_rst(self.state, self.content_offset, doc)
class RunExampleDirective(Directive):
has_content = True
required_arguments = 0
optional_arguments = 0
final_argument_whitespace = False
def run(self):
doc = ViewList()
runner = get_example_runner(self.state.document)
try:
rv = runner.run('\n'.join(self.content))
except:
runner.close()
raise
doc.append('.. sourcecode:: text', '')
doc.append('', '')
for line in rv:
doc.append(' ' + line, '')
return parse_rst(self.state, self.content_offset, doc)
class ClickDomain(Domain):
name = 'click'
label = 'Click'
directives = {
'example': ExampleDirective,
'run': RunExampleDirective,
}
def delete_example_runner_state(app, doctree):
runner = getattr(doctree, 'click_example_runner', None)
if runner is not None:
runner.close()
del doctree.click_example_runner
def setup(app):
app.add_domain(ClickDomain)
app.connect('doctree-read', delete_example_runner_state)

503
docs/commands.rst Normal file
View file

@ -0,0 +1,503 @@
Commands and Groups
===================
.. currentmodule:: click
The most important feature of Click is the concept of arbitrarily nesting
command line utilities. This is implemented through the :class:`Command`
and :class:`Group` (actually :class:`MultiCommand`).
Callback Invocation
-------------------
For a regular command, the callback is executed whenever the command runs.
If the script is the only command, it will always fire (unless a parameter
callback prevents it. This for instance happens if someone passes
``--help`` to the script).
For groups and multi commands, the situation looks different. In this case,
the callback fires whenever a subcommand fires (unless this behavior is
changed). What this means in practice is that an outer command runs
when an inner command runs:
.. click:example::
@click.group()
@click.option('--debug/--no-debug', default=False)
def cli(debug):
click.echo('Debug mode is %s' % ('on' if debug else 'off'))
@cli.command()
def sync():
click.echo('Synching')
Here is what this looks like:
.. click:run::
invoke(cli, prog_name='tool.py')
println()
invoke(cli, prog_name='tool.py', args=['--debug', 'sync'])
Passing Parameters
------------------
Click strictly separates parameters between commands and subcommands. What this
means is that options and arguments for a specific command have to be specified
*after* the command name itself, but *before* any other command names.
This behavior is already observable with the predefined ``--help`` option.
Suppose we have a program called ``tool.py``, containing a subcommand called
``sub``.
- ``tool.py --help`` will return the help for the whole program (listing
subcommands).
- ``tool.py sub --help`` will return the help for the ``sub`` subcommand.
- But ``tool.py --help sub`` will treat ``--help`` as an argument for the main
program. Click then invokes the callback for ``--help``, which prints the
help and aborts the program before click can process the subcommand.
Nested Handling and Contexts
----------------------------
As you can see from the earlier example, the basic command group accepts a
debug argument which is passed to its callback, but not to the sync
command itself. The sync command only accepts its own arguments.
This allows tools to act completely independent of each other, but how
does one command talk to a nested one? The answer to this is the
:class:`Context`.
Each time a command is invoked, a new context is created and linked with the
parent context. Normally, you can't see these contexts, but they are
there. Contexts are passed to parameter callbacks together with the
value automatically. Commands can also ask for the context to be passed
by marking themselves with the :func:`pass_context` decorator. In that
case, the context is passed as first argument.
The context can also carry a program specified object that can be
used for the program's purposes. What this means is that you can build a
script like this:
.. click:example::
@click.group()
@click.option('--debug/--no-debug', default=False)
@click.pass_context
def cli(ctx, debug):
ctx.obj['DEBUG'] = debug
@cli.command()
@click.pass_context
def sync(ctx):
click.echo('Debug is %s' % (ctx.obj['DEBUG'] and 'on' or 'off'))
if __name__ == '__main__':
cli(obj={})
If the object is provided, each context will pass the object onwards to
its children, but at any level a context's object can be overridden. To
reach to a parent, ``context.parent`` can be used.
In addition to that, instead of passing an object down, nothing stops the
application from modifying global state. For instance, you could just flip
a global ``DEBUG`` variable and be done with it.
Decorating Commands
-------------------
As you have seen in the earlier example, a decorator can change how a
command is invoked. What actually happens behind the scenes is that
callbacks are always invoked through the :meth:`Context.invoke` method
which automatically invokes a command correctly (by either passing the
context or not).
This is very useful when you want to write custom decorators. For
instance, a common pattern would be to configure an object representing
state and then storing it on the context and then to use a custom
decorator to find the most recent object of this sort and pass it as first
argument.
For instance, the :func:`pass_obj` decorator can be implemented like this:
.. click:example::
from functools import update_wrapper
def pass_obj(f):
@click.pass_context
def new_func(ctx, *args, **kwargs):
return ctx.invoke(f, ctx.obj, *args, **kwargs)
return update_wrapper(new_func, f)
The :meth:`Context.invoke` command will automatically invoke the function
in the correct way, so the function will either be called with ``f(ctx,
obj)`` or ``f(obj)`` depending on whether or not it itself is decorated with
:func:`with_context`.
This is a very powerful context that can be used to build very complex
nested applications; see :ref:`complex-guide` for more information.
Group Invocation Without Command
--------------------------------
By default, a group or multi command is not invoked unless a subcommand is
passed. In fact, not providing a command automatically passes ``--help``
by default. This behavior can be changed by passing
``invoke_without_command=True`` to a group. In that case, the callback is
always invoked instead of showing the help page. The context object also
includes information about whether or not the invocation would go to a
subcommand.
Example:
.. click:example::
@click.group(invoke_without_command=True)
@click.pass_context
def cli(ctx):
if ctx.invoked_subcommand is None:
click.echo('I was invoked without subcommand')
else:
click.echo('I am about to invoke %s' % ctx.invoked_subcommand)
@cli.command()
def sync():
click.echo('The subcommand')
And how it works in practice:
.. click:run::
invoke(cli, prog_name='tool', args=[])
invoke(cli, prog_name='tool', args=['sync'])
.. _custom-multi-commands:
Custom Multi Commands
---------------------
In addition to using :func:`click.group`, you can also build your own
custom multi commands. This is useful when you want to support commands
being loaded lazily from plugins.
A custom multi command just needs to implement a list and load method:
.. click:example::
import click
import os
plugin_folder = os.path.join(os.path.dirname(__file__), 'commands')
class MyCLI(click.MultiCommand):
def list_commands(self, ctx):
rv = []
for filename in os.listdir(plugin_folder):
if filename.endswith('.py'):
rv.append(filename[:-3])
rv.sort()
return rv
def get_command(self, ctx, name):
ns = {}
fn = os.path.join(plugin_folder, name + '.py')
with open(fn) as f:
code = compile(f.read(), fn, 'exec')
eval(code, ns, ns)
return ns['cli']
cli = MyCLI(help='This tool\'s subcommands are loaded from a '
'plugin folder dynamically.')
if __name__ == '__main__':
cli()
These custom classes can also be used with decorators:
.. click:example::
@click.command(cls=MyCLI)
def cli():
pass
Merging Multi Commands
----------------------
In addition to implementing custom multi commands, it can also be
interesting to merge multiple together into one script. While this is
generally not as recommended as it nests one below the other, the merging
approach can be useful in some circumstances for a nicer shell experience.
The default implementation for such a merging system is the
:class:`CommandCollection` class. It accepts a list of other multi
commands and makes the commands available on the same level.
Example usage:
.. click:example::
import click
@click.group()
def cli1():
pass
@cli1.command()
def cmd1():
"""Command on cli1"""
@click.group()
def cli2():
pass
@cli2.command()
def cmd2():
"""Command on cli2"""
cli = click.CommandCollection(sources=[cli1, cli2])
if __name__ == '__main__':
cli()
And what it looks like:
.. click:run::
invoke(cli, prog_name='cli', args=['--help'])
In case a command exists in more than one source, the first source wins.
Multi Command Chaining
----------------------
.. versionadded:: 3.0
Sometimes it is useful to be allowed to invoke more than one subcommand in
one go. For instance if you have installed a setuptools package before
ouy might be familiar with the ``setup.py sdist bdist_wheel upload``
command chain which invokes ``dist`` before ``bdist_wheel`` before
``upload``. Starting with Click 3.0 this is very simple to implement.
All you have to do is to pass ``chain=True`` to your multicommand:
.. click:example::
@click.group(chain=True)
def cli():
pass
@cli.command('sdist')
def sdist():
click.echo('sdist called')
@cli.command('bdist_wheel')
def bdist_wheel():
click.echo('bdist_wheel called')
Now you can invoke it like this:
.. click:run::
invoke(cli, prog_name='setup.py', args=['sdist', 'bdist_wheel'])
When using multi command chaining you can only have one command (the last)
use ``nargs=-1`` on an argument. Other than that there are no
restrictions on how they work. They can accept options and arguments as
normal.
Another note: the :attr:`Context.invoked_subcommand` attribute is a bit
useless for multi commands as it will give ``'*'`` as value if more than
one command is invoked. This is necessary because the handling of
subcommands happens one after another so the exact subcommands that will
be handled are not yet available when the callback fires.
Multi Command Pipelines
-----------------------
.. versionadded:: 3.0
A very common usecase of multi command chaining is to have one command
process the result of the previous command. There are various ways in
which this can be facilitated. The most obvious way is to store a value
on the context object and process it from function to function. This
works by decorating a function with :func:`pass_context` after which the
context object is provided and a subcommand can store it's data there.
Another way to accomplish this is to setup pipelines by returning
processing functions. Think of it like this: when a subcommand gets
invoked it processes all of it's parameters and comes up with a plan of
how to do it's processing. At that point it then returns a processing
function and returns.
Where do the returned functions go? The chained multicommand can register
a callback with :meth:`MultiCommand.resultcallback` that goes over all
these functions and then invoke them.
To make this a bit more concrete consider this example:
.. click:example::
@click.group(chain=True, invoke_without_command=True)
@click.option('-i', '--input', type=click.File('r'))
def cli(input):
pass
@cli.resultcallback()
def process_pipeline(processors, input):
iterator = (x.rstrip('\r\n') for x in input)
for processor in processors:
iterator = processor(iterator)
for item in iterator:
click.echo(item)
@cli.command('uppercase')
def make_uppercase():
def processor(iterator):
for line in iterator:
yield line.upper()
return processor
@cli.command('lowercase')
def make_lowercase():
def processor(iterator):
for line in iterator:
yield line.lower()
return processor
@cli.command('strip')
def make_strip():
def processor(iterator):
for line in iterator:
yield line.strip()
return processor
That's a lot in one go, so let's go through it step by step.
1. The first thing is to make a :func:`group` that is chainable. In
addition to that we also instruct Click to invoke even if no
subcommand is defined. If this would not be done, then invoking an
empty pipeline would produce the help page instead of running the
result callbacks.
2. The next thing we do is to register a result callback on our group.
This callback will be invoked with an argument which is the list of
all return values of all subcommands and then the same keyword
parameters as our group itself. This means we can access the input
file easily there without having to use the context object.
3. In this result callback we create an iterator of all the lines in the
input file and then pass this iterator through all the returned
callbacks from all subcommands and finally we print all lines to
stdout.
After that point we can register as many subcommands as we want and each
subcommand can return a processor function to modify the stream of lines.
One important thing of note is that Click shuts down the context after
each callback has been run. This means that for instance file types
cannot be accessed in the `processor` functions as the files will already
be closed there. This limitation is unlikely to change because it would
make resource handling much more complicated. For such it's recommended
to not use the file type and manually open the file through
:func:`open_file`.
For a more complex example that also improves upon handling of the
pipelines have a look at the `imagepipe multi command chaining demo
<https://github.com/mitsuhiko/click/tree/master/examples/imagepipe>`__ in
the Click repository. It implements a pipeline based image editing tool
that has a nice internal structure for the pipelines.
Overriding Defaults
-------------------
By default, the default value for a parameter is pulled from the
``default`` flag that is provided when it's defined, but that's not the
only place defaults can be loaded from. The other place is the
:attr:`Context.default_map` (a dictionary) on the context. This allows
defaults to be loaded from a configuration file to override the regular
defaults.
This is useful if you plug in some commands from another package but
you're not satisfied with the defaults.
The default map can be nested arbitrarily for each subcommand and
provided when the script is invoked. Alternatively, it can also be
overriden at any point by commands. For instance, a top-level command could
load the defaults from a configuration file.
Example usage:
.. click:example::
import click
@click.group()
def cli():
pass
@cli.command()
@click.option('--port', default=8000)
def runserver(port):
click.echo('Serving on http://127.0.0.1:%d/' % port)
if __name__ == '__main__':
cli(default_map={
'runserver': {
'port': 5000
}
})
And in action:
.. click:run::
invoke(cli, prog_name='cli', args=['runserver'], default_map={
'runserver': {
'port': 5000
}
})
Context Defaults
----------------
.. versionadded:: 2.0
Starting with Click 2.0 you can override defaults for contexts not just
when calling your script, but also in the decorator that declares a
command. For instance given the previous example which defines a custom
``default_map`` this can also be accomplished in the decorator now.
This example does the same as the previous example:
.. click:example::
import click
CONTEXT_SETTINGS = dict(
default_map={'runserver': {'port': 5000}}
)
@click.group(context_settings=CONTEXT_SETTINGS)
def cli():
pass
@cli.command()
@click.option('--port', default=8000)
def runserver(port):
click.echo('Serving on http://127.0.0.1:%d/' % port)
if __name__ == '__main__':
cli()
And again the example in action:
.. click:run::
invoke(cli, prog_name='cli', args=['runserver'])

219
docs/complex.rst Normal file
View file

@ -0,0 +1,219 @@
.. _complex-guide:
Complex Applications
====================
.. currentmodule:: click
Click is designed to assist with the creation of complex and simple CLI tools
alike. However, the power of its design is the ability to arbitrarily nest
systems together. For instance, if you have ever used Django, you will
have realized that it provides a command line utility, but so does Celery.
When using Celery with Django, there are two tools that need to interact with
each other and be cross-configured.
In a theoretical world of two separate Click command line utilities, they
could solve this problem by nesting one inside the other. For instance, the
web framework could also load the commands for the message queue framework.
Basic Concepts
--------------
To understand how this works, you need to understand two concepts: contexts
and the calling convention.
Contexts
````````
Whenever a Click command is executed, a :class:`Context` object is created
which holds state for this particular invocation. It remembers parsed
parameters, what command created it, which resources need to be cleaned up
at the end of the function, and so forth. It can also optionally hold an
application-defined object.
Context objects build a linked list until they hit the top one. Each context
is linked to a parent context. This allows a command to work below
another command and store its own information there without having to be
afraid of altering up the state of the parent command.
Because the parent data is available, however, it is possible to navigate to
it if needed.
Most of the time, you do not see the context object, but when writing more
complex applications it comes in handy. This brings us to the next point.
Calling Convention
``````````````````
When a Click command callback is executed, it's passed all the non-hidden
parameters as keyword arguments. Notably absent is the context. However,
a callback can opt into being passed to the context object by marking itself
with :func:`pass_context`.
So how do you invoke a command callback if you don't know if it should
receive the context or not? The answer is that the context itself
provides a helper function (:meth:`Context.invoke`) which can do this for
you. It accepts the callback as first argument and then invokes the
function correctly.
Building a Git Clone
--------------------
In this example, we want to build a command line tool that resembles a
version control system. Systems like Git usually provide one
over-arching command that already accepts some parameters and
configuration, and then have extra subcommands that do other things.
The Root Command
````````````````
At the top level, we need a group that can hold all our commands. In this
case, we use the basic :func:`click.group` which allows us to register
other Click commands below it.
For this command, we also want to accept some parameters that configure the
state of our tool:
.. click:example::
import os
import click
class Repo(object):
def __init__(self, home=None, debug=False):
self.home = os.path.abspath(home or '.')
self.debug = debug
@click.group()
@click.option('--repo-home', envvar='REPO_HOME', default='.repo')
@click.option('--debug/--no-debug', default=False,
envvar='REPO_DEBUG')
@click.pass_context
def cli(ctx, repo_home, debug):
ctx.obj = Repo(repo_home, debug)
Let's understand what this does. We create a group command which can
have subcommands. When it is invoked, it will create an instance of a
``Repo`` class. This holds the state for our command line tool. In this
case, it just remembers some parameters, but at this point it could also
start loading configuration files and so on.
This state object is then remembered by the context as :attr:`~Context.obj`.
This is a special attribute where commands are supposed to remember what
they need to pass on to their children.
In order for this to work, we need to mark our function with
:func:`pass_context`, because otherwise, the context object would be
entirely hidden from us.
The First Child Command
```````````````````````
Let's add our first child command to it, the clone command:
.. click:example::
@cli.command()
@click.argument('src')
@click.argument('dest', required=False)
def clone(src, dest):
pass
So now we have a clone command, but how do we get access to the repo? As
you can imagine, one way is to use the :func:`pass_context` function which
again will make our callback also get the context passed on which we
memorized the repo. However, there is a second version of this decorator
called :func:`pass_obj` which will just pass the stored object, (in our case
the repo):
.. click:example::
@cli.command()
@click.argument('src')
@click.argument('dest', required=False)
@click.pass_obj
def clone(repo, src, dest):
pass
Interleaved Commands
````````````````````
While not relevant for the particular program we want to build, there is
also quite good support for interleaving systems. Imagine for instance that
there was a super cool plugin for our version control system that needed a
lot of configuration and wanted to store its own configuration as
:attr:`~Context.obj`. If we would then attach another command below that,
we would all of a sudden get the plugin configuration instead of our repo
object.
One obvious way to remedy this is to store a reference to the repo in the
plugin, but then a command needs to be aware that it's attached below such a
plugin.
There is a much better system that can built by taking advantage of the linked
nature of contexts. We know that the plugin context is linked to the context
that created our repo. Because of that, we can start a search for the last
level where the object stored by the context was a repo.
Built-in support for this is provided by the :func:`make_pass_decorator`
factory, which will create decorators for us that find objects (it
internally calls into :meth:`Context.find_object`). In our case, we
know that we want to find the closest ``Repo`` object, so let's make a
decorator for this:
.. click:example::
pass_repo = click.make_pass_decorator(Repo)
If we now use ``pass_repo`` instead of ``pass_obj``, we will always get a
repo instead of something else:
.. click:example::
@cli.command()
@click.argument('src')
@click.argument('dest', required=False)
@pass_repo
def clone(repo, src, dest):
pass
Ensuring Object Creation
````````````````````````
The above example only works if there was an outer command that created a
``Repo`` object and stored it in the context. For some more advanced use
cases, this might become a problem. The default behavior of
:func:`make_pass_decorator` is to call :meth:`Context.find_object`
which will find the object. If it can't find the object, it will raise an
error. The alternative behavior is to use :meth:`Context.ensure_object`
which will find the object, and if it cannot find it, will create one and
store it in the innermost context. This behavior can also be enabled for
:func:`make_pass_decorator` by passing ``ensure=True``:
.. click:example::
pass_repo = click.make_pass_decorator(Repo, ensure=True)
In this case, the innermost context gets an object created if it is
missing. This might replace objects being placed there earlier. In this
case, the command stays executable, even if the outer command does not run.
For this to work, the object type needs to have a constructor that accepts
no arguments.
As such it runs standalone:
.. click:example::
@click.command()
@pass_repo
def cp(repo):
click.echo(repo)
As you can see:
.. click:run::
invoke(cp, [])

219
docs/conf.py Normal file
View file

@ -0,0 +1,219 @@
# -*- coding: utf-8 -*-
#
# click documentation build configuration file, created by
# sphinx-quickstart on Mon Apr 26 19:53:01 2010.
#
# This file is execfile()d with the current directory set to its containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys, os
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.append(os.path.abspath('_themes'))
sys.path.append(os.path.abspath('..'))
sys.path.append(os.path.abspath('.'))
# -- General configuration -----------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx',
'clickdoctools']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'click'
copyright = u'2014, Armin Ronacher'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '1.0'
# The full version, including alpha/beta/rc tags.
release = '1.0'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# -- Options for HTML output ---------------------------------------------------
# The theme to use for HTML and HTML Help pages. Major themes that come with
# Sphinx are currently 'default' and 'sphinxdoc'.
html_theme = 'click'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
html_theme_options = {
}
# Add any paths that contain custom themes here, relative to this directory.
html_theme_path = ['_themes']
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
html_title = 'click'
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
html_sidebars = {
'index': ['sidebarintro.html', 'sourcelink.html', 'searchbox.html'],
'**': ['sidebarlogo.html', 'localtoc.html', 'relations.html',
'sourcelink.html', 'searchbox.html']
}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_domain_indices = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = ''
# Output file base name for HTML help builder.
htmlhelp_basename = 'clickdoc'
# -- Options for LaTeX output --------------------------------------------------
# The paper size ('letter' or 'a4').
#latex_paper_size = 'letter'
# The font size ('10pt', '11pt' or '12pt').
#latex_font_size = '10pt'
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'click.tex', u'click documentation',
u'Armin Ronacher', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# Additional stuff for the LaTeX preamble.
#latex_preamble = ''
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_domain_indices = True
# -- Options for manual page output --------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'click', u'click documentation',
[u'Armin Ronacher'], 1)
]
intersphinx_mapping = {
'http://docs.python.org/dev': None
}

162
docs/documentation.rst Normal file
View file

@ -0,0 +1,162 @@
Documenting Scripts
===================
.. currentmodule:: click
Click makes it very easy to document your command line tools. First of
all, it automatically generates help pages for you. While these are
currently not customizable in terms of their layout, all of the text
can be changed.
Help Texts
----------
Commands and options accept help arguments. In the case of commands, the
docstring of the function is automatically used if provided.
Simple example:
.. click:example::
@click.command()
@click.option('--count', default=1, help='number of greetings')
@click.argument('name')
def hello(count, name):
"""This script prints hello NAME COUNT times."""
for x in range(count):
click.echo('Hello %s!' % name)
And what it looks like:
.. click:run::
invoke(hello, args=['--help'])
Arguments cannot be documented this way. This is to follow the general
convention of Unix tools of using arguments for only the most necessary
things and to document them in the introduction text by referring to them
by name.
Preventing Rewrapping
---------------------
The default behavior of Click is to rewrap text based on the width of the
terminal. In some circumstances, this can become a problem. The main issue
is when showing code examples, where newlines are significant.
Rewrapping can be disabled on a per-paragraph basis by adding a line with
solely the ``\b`` escape marker in it. This line will be removed from the
help text and rewrapping will be disabled.
Example:
.. click:example::
@click.command()
def cli():
"""First paragraph.
This is a very long second paragraph and as you
can see wrapped very early in the source text
but will be rewrapped to the terminal width in
the final output.
\b
This is
a paragraph
without rewrapping.
And this is a paragraph
that will be rewrapped again.
"""
And what it looks like:
.. click:run::
invoke(cli, args=['--help'])
Meta Variables
--------------
Options and parameters accept a ``metavar`` argument that can change the
meta variable in the help page. The default version is the parameter name
in uppercase with underscores, but can be annotated differently if
desired. This can be customized at all levels:
.. click:example::
@click.command(options_metavar='<options>')
@click.option('--count', default=1, help='number of greetings',
metavar='<int>')
@click.argument('name', metavar='<name>')
def hello(count, name):
"""This script prints hello <name> <int> times."""
for x in range(count):
click.echo('Hello %s!' % name)
Example:
.. click:run::
invoke(hello, args=['--help'])
Command Short Help
------------------
For commands, a short help snippet is generated. By default, it's the first
sentence of the help message of the command, unless it's too long. This can
also be overridden:
.. click:example::
@click.group()
def cli():
"""A simple command line tool."""
@cli.command('init', short_help='init the repo')
def init():
"""Initializes the repository."""
@cli.command('delete', short_help='delete the repo')
def delete():
"""Deletes the repository."""
And what it looks like:
.. click:run::
invoke(cli, prog_name='repo.py')
Help Parameter Customization
----------------------------
.. versionadded:: 2.0
The help parameter is implemented in Click in a very special manner.
Unlike regular parameters it's automatically added by Click for any
command and it performs automatic conflict resolution. By default it's
called ``--help``, but this can be changed. If a command itself implements
a parameter with the same name, the default help parameter stops accepting
it. There is a context setting that can be used to override the names of
the help parameters called :attr:`~Context.help_option_names`.
This example changes the default parameters to ``-h`` and ``--help``
instead of just ``--help``:
.. click:example::
CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help'])
@click.command(context_settings=CONTEXT_SETTINGS)
def cli():
pass
And what it looks like:
.. click:run::
invoke(cli, ['-h'])

73
docs/exceptions.rst Normal file
View file

@ -0,0 +1,73 @@
Exception Handling
==================
.. currentmodule:: click
Click internally uses exceptions to signal various error conditions that
the user of the application might have caused. Primarily this is things
like incorrect usage.
Where are Errors Handled?
-------------------------
Click's main error handling is happening in :meth:`BaseCommand.main`. In
there it handles all subclasses of :exc:`ClickException` as well as the
standard :exc:`EOFError` and :exc:`KeyboardInterrupt` exceptions. The
latter are internally translated into a :exc:`Abort`.
The logic applied is the following:
1. If an :exc:`EOFError` or :exc:`KeyboardInterrupt` happens, reraise it
as :exc:`Abort`.
2. If an :exc:`ClickException` is raised, invoke the
:meth:`ClickException.show` method on it to display it and then exit
the program with :attr:`ClickException.exit_code`.
3. If an :exc:`Abort` exception is raised print the string ``Aborted!``
to standard error and exit the program with exit code ``1``.
4. if it goes through well, exit the program with exit code ``0``.
What if I don't want that?
--------------------------
Generally you always have the option to invoke the :meth:`invoke` method
yourself. For instance if you have a :class:`Command` you can invoke it
manually like this::
ctx = command.make_context('command-name', ['args', 'go', 'here'])
with ctx:
result = command.invoke(ctx)
In this case exceptions will not be handled at all and bubbled up as you
would expect.
Starting with Click 3.0 you can also use the :meth:`Command.main` method
but disable the standalone mode which will do two things: disable
exception handling and disable the implicit :meth:`sys.exit` at the end.
So you can do something like this::
command.main(['command-name', 'args', 'go', 'here'],
standalone_mode=False)
Which Exceptions Exist?
-----------------------
Click has two exception bases: :exc:`ClickException` which is raised for
all exceptions that Click wants to signal to the user and :exc:`Abort`
which is used to instruct Click to abort the execution.
A :exc:`ClickException` has a :meth:`~ClickException.show` method which
can render an error message to stderr or the given file object. If you
want to use the exception yourself for doing something check the API docs
about what else they provide.
The following common subclasses exist:
* :exc:`UsageError` to inform the user that something went wrong.
* :exc:`BadParameter` to inform the user that something went wrong with
a specific parameter. These are often handled internally in Click and
augmented with extra information if possible. For instance if those
are raised from a callback Click will automatically augment it with
the parameter name if possible.
* :exc:`FileError` this is an error that is raised by the
:exc:`FileType` if Click encounters issues opening the file.

98
docs/index.rst Normal file
View file

@ -0,0 +1,98 @@
Welcome to the Click Documentation
==================================
Click is a Python package for creating beautiful command line interfaces
in a composable way with as little code as necessary. It's the "Command
Line Interface Creation Kit". It's highly configurable but comes with
sensible defaults out of the box.
It aims to make the process of writing command line tools quick and fun
while also preventing any frustration caused by the inability to implement
an intended CLI API.
Click in three points:
- arbitrary nesting of commands
- automatic help page generation
- supports lazy loading of subcommands at runtime
What does it look like? Here is an example of a simple Click program:
.. click:example::
import click
@click.command()
@click.option('--count', default=1, help='Number of greetings.')
@click.option('--name', prompt='Your name',
help='The person to greet.')
def hello(count, name):
"""Simple program that greets NAME for a total of COUNT times."""
for x in range(count):
click.echo('Hello %s!' % name)
if __name__ == '__main__':
hello()
And what it looks like when run:
.. click:run::
invoke(hello, ['--count=3'], prog_name='python hello.py', input='John\n')
It automatically generates nicely formatted help pages:
.. click:run::
invoke(hello, ['--help'], prog_name='python hello.py')
You can get the library directly from PyPI::
pip install click
Documentation Contents
----------------------
This part of the documentation guides you through all of the library's
usage patterns.
.. toctree::
:maxdepth: 2
why
quickstart
setuptools
parameters
options
arguments
commands
prompts
documentation
complex
advanced
testing
utils
bashcomplete
exceptions
python3
API Reference
-------------
If you are looking for information on a specific function, class, or
method, this part of the documentation is for you.
.. toctree::
:maxdepth: 2
api
Miscellaneous Pages
-------------------
.. toctree::
:maxdepth: 2
changelog
upgrading
license

13
docs/license.rst Normal file
View file

@ -0,0 +1,13 @@
License
=======
Click is licensed under a three-clause BSD License. It basically means:
do whatever you want with it as long as the copyright in Click sticks
around, the conditions are not modified and the disclaimer is present.
Furthermore, you must not use the names of the authors to promote derivatives
of the software without written consent.
License Text
------------
.. include:: ../LICENSE

155
docs/make.bat Normal file
View file

@ -0,0 +1,155 @@
@ECHO OFF
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set BUILDDIR=_build
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
if NOT "%PAPER%" == "" (
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
)
if "%1" == "" goto help
if "%1" == "help" (
:help
echo.Please use `make ^<target^>` where ^<target^> is one of
echo. html to make standalone HTML files
echo. dirhtml to make HTML files named index.html in directories
echo. singlehtml to make a single large HTML file
echo. pickle to make pickle files
echo. json to make JSON files
echo. htmlhelp to make HTML files and a HTML help project
echo. qthelp to make HTML files and a qthelp project
echo. devhelp to make HTML files and a Devhelp project
echo. epub to make an epub
echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
echo. text to make text files
echo. man to make manual pages
echo. changes to make an overview over all changed/added/deprecated items
echo. linkcheck to check all external links for integrity
echo. doctest to run all doctests embedded in the documentation if enabled
goto end
)
if "%1" == "clean" (
for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
del /q /s %BUILDDIR%\*
goto end
)
if "%1" == "html" (
%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/html.
goto end
)
if "%1" == "dirhtml" (
%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
goto end
)
if "%1" == "singlehtml" (
%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
goto end
)
if "%1" == "pickle" (
%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
echo.
echo.Build finished; now you can process the pickle files.
goto end
)
if "%1" == "json" (
%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
echo.
echo.Build finished; now you can process the JSON files.
goto end
)
if "%1" == "htmlhelp" (
%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
echo.
echo.Build finished; now you can run HTML Help Workshop with the ^
.hhp project file in %BUILDDIR%/htmlhelp.
goto end
)
if "%1" == "qthelp" (
%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
echo.
echo.Build finished; now you can run "qcollectiongenerator" with the ^
.qhcp project file in %BUILDDIR%/qthelp, like this:
echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Classy.qhcp
echo.To view the help file:
echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Classy.ghc
goto end
)
if "%1" == "devhelp" (
%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
echo.
echo.Build finished.
goto end
)
if "%1" == "epub" (
%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
echo.
echo.Build finished. The epub file is in %BUILDDIR%/epub.
goto end
)
if "%1" == "latex" (
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
echo.
echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
goto end
)
if "%1" == "text" (
%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
echo.
echo.Build finished. The text files are in %BUILDDIR%/text.
goto end
)
if "%1" == "man" (
%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
echo.
echo.Build finished. The manual pages are in %BUILDDIR%/man.
goto end
)
if "%1" == "changes" (
%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
echo.
echo.The overview file is in %BUILDDIR%/changes.
goto end
)
if "%1" == "linkcheck" (
%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
echo.
echo.Link check complete; look for any errors in the above output ^
or in %BUILDDIR%/linkcheck/output.txt.
goto end
)
if "%1" == "doctest" (
%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
echo.
echo.Testing of doctests in the sources finished, look at the ^
results in %BUILDDIR%/doctest/output.txt.
goto end
)
:end

611
docs/options.rst Normal file
View file

@ -0,0 +1,611 @@
.. _options:
Options
=======
.. currentmodule:: click
Adding options to commands can be accomplished by the :func:`option`
decorator. Since options can come in various different versions, there
are a ton of parameters to configure their behavior. Options in click are
distinct from :ref:`positional arguments <arguments>`.
Basic Value Options
-------------------
The most basic option is a value option. These options accept one
argument which is a value. If no type is provided, the type of the default
value is used. If no default value is provided, the type is assumed to be
:data:`STRING`. By default, the name of the parameter is the first long
option defined; otherwise the first short one is used.
.. click:example::
@click.command()
@click.option('--n', default=1)
def dots(n):
click.echo('.' * n)
And on the command line:
.. click:run::
invoke(dots, args=['--n=2'])
In this case the option is of type :data:`INT` because the default value
is an integer.
Multi Value Options
-------------------
Sometimes, you have options that take more than one argument. For options,
only a fixed number of arguments is supported. This can be configured by
the ``nargs`` parameter. The values are then stored as a tuple.
.. click:example::
@click.command()
@click.option('--pos', nargs=2, type=float)
def findme(pos):
click.echo('%s / %s' % pos)
And on the command line:
.. click:run::
invoke(findme, args=['--pos', '2.0', '3.0'])
Multiple Options
----------------
Similarly to ``nargs``, there is also the case of wanting to support a
parameter being provided multiple times to and have all values recorded --
not just the last one. For instance, ``git commit -m foo -m bar`` would
record two lines for the commit message: ``foo`` and ``bar``. This can be
accomplished with the ``multiple`` flag:
Example:
.. click:example::
@click.command()
@click.option('--message', '-m', multiple=True)
def commit(message):
click.echo('\n'.join(message))
And on the command line:
.. click:run::
invoke(commit, args=['-m', 'foo', '-m', 'bar'])
Counting
--------
In some very rare circumstances, it is interesting to use the repetition
of options to count an integer up. This can be used for verbosity flags,
for instance:
.. click:example::
@click.command()
@click.option('-v', '--verbose', count=True)
def log(verbose):
click.echo('Verbosity: %s' % verbose)
And on the command line:
.. click:run::
invoke(log, args=['-vvv'])
Boolean Flags
-------------
Boolean flags are options that can be enabled or disabled. This can be
accomplished by defining two flags in one go separated by a slash (``/``)
for enabling or disabling the option. (If a slash is in an option string,
Click automatically knows that it's a boolean flag and will pass
``is_flag=True`` implicitly.) Click always wants you to provide an enable
and disable flag so that you can change the default later.
Example:
.. click:example::
import os
@click.command()
@click.option('--shout/--no-shout', default=False)
def info(shout):
rv = os.uname()[0]
if shout:
rv = rv.upper() + '!!!!111'
click.echo(rv)
And on the command line:
.. click:run::
invoke(info, args=['--shout'])
invoke(info, args=['--no-shout'])
If you really don't want an off-switch, you can just define one and
manually inform Click that something is a flag:
.. click:example::
import os
@click.command()
@click.option('--shout', is_flag=True)
def info(shout):
rv = os.uname()[0]
if shout:
rv = rv.upper() + '!!!!111'
click.echo(rv)
And on the command line:
.. click:run::
invoke(info, args=['--shout'])
Note that if a slash is contained in your option already (for instance, if
you use Windows-style parameters where ``/`` is the prefix character), you
can alternatively split the parameters through ``;`` instead:
.. click:example::
@click.command()
@click.option('/debug;/no-debug')
def log(debug):
click.echo('debug=%s' % debug)
if __name__ == '__main__':
log()
Feature Switches
----------------
In addition to boolean flags, there are also feature switches. These are
implemented by setting multiple options to the same parameter name and
defining a flag value. Note that by providing the ``flag_value`` parameter,
Click will implicitly set ``is_flag=True``.
To set a default flag, assign a value of `True` to the flag that should be
the default.
.. click:example::
import os
@click.command()
@click.option('--upper', 'transformation', flag_value='upper',
default=True)
@click.option('--lower', 'transformation', flag_value='lower')
def info(transformation):
click.echo(getattr(os.uname()[0], transformation)())
And on the command line:
.. click:run::
invoke(info, args=['--upper'])
invoke(info, args=['--lower'])
invoke(info)
.. _choice-opts:
Choice Options
--------------
Sometimes, you want to have a parameter be a choice of a list of values.
In that case you can use :class:`Choice` type. It can be instantiated
with a list of valid values.
Example:
.. click:example::
@click.command()
@click.option('--hash-type', type=click.Choice(['md5', 'sha1']))
def digest(hash_type):
click.echo(hash_type)
What it looks like:
.. click:run::
invoke(digest, args=['--hash-type=md5'])
println()
invoke(digest, args=['--hash-type=foo'])
println()
invoke(digest, args=['--help'])
.. _option-prompting:
Prompting
---------
In some cases, you want parameters that can be provided from the command line,
but if not provided, ask for user input instead. This can be implemented with
Click by defining a prompt string.
Example:
.. click:example::
@click.command()
@click.option('--name', prompt=True)
def hello(name):
click.echo('Hello %s!' % name)
And what it looks like:
.. click:run::
invoke(hello, args=['--name=John'])
invoke(hello, input=['John'])
If you are not happy with the default prompt string, you can ask for
a different one:
.. click:example::
@click.command()
@click.option('--name', prompt='Your name please')
def hello(name):
click.echo('Hello %s!' % name)
What it looks like:
.. click:run::
invoke(hello, input=['John'])
Password Prompts
----------------
Click also supports hidden prompts and asking for confirmation. This is
useful for password input:
.. click:example::
@click.command()
@click.option('--password', prompt=True, hide_input=True,
confirmation_prompt=True)
def encrypt(password):
click.echo('Encrypting password to %s' % password.encode('rot13'))
What it looks like:
.. click:run::
invoke(encrypt, input=['secret', 'secret'])
Because this combination of parameters is quite common, this can also be
replaced with the :func:`password_option` decorator:
.. click:example::
@click.command()
@click.password_option()
def encrypt(password):
click.echo('Encrypting password to %s' % password.encode('rot13'))
Callbacks and Eager Options
---------------------------
Sometimes, you want a parameter to completely change the execution flow.
For instance, this is the case when you want to have a ``--version``
parameter that prints out the version and then exits the application.
In such cases, you need two concepts: eager parameters and a callback. An
eager parameter is a parameter that is handled before others, and a
callback is what executes after the parameter is handled. The eagerness
is necessary so that an earlier required parameter does not produce an
error message. For instance, if ``--version`` was not eager and a
parameter ``--foo`` was required and defined before, you would need to
specify it for ``--version`` to work. For more information, see
:ref:`callback-evaluation-order`.
A callback is a function that is invoked with two parameters: the current
:class:`Context` and the value. The context provides some useful features
such as quitting the application and gives access to other already
processed parameters.
Here an example for a ``--version`` flag:
.. click:example::
def print_version(ctx, param, value):
if not value or ctx.resilient_parsing:
return
click.echo('Version 1.0')
ctx.exit()
@click.command()
@click.option('--version', is_flag=True, callback=print_version,
expose_value=False, is_eager=True)
def hello():
click.echo('Hello World!')
The `expose_value` parameter prevents the pretty pointless ``version``
parameter from being passed to the callback. If that was not specified, a
boolean would be passed to the `hello` script. The `resilient_parsing`
flag is applied to the context if Click wants to parse the command line
without any destructive behavior that would change the execution flow. In
this case, because we would exit the program, we instead do nothing.
What it looks like:
.. click:run::
invoke(hello)
invoke(hello, args=['--version'])
.. admonition:: Callback Signature Changes
In Click 2.0 the signature for callbacks changed. For more
information about these changes see :ref:`upgrade-to-2.0`.
Yes Parameters
--------------
For dangerous operations, it's very useful to be able to ask a user for
confirmation. This can be done by adding a boolean ``--yes`` flag and
asking for confirmation if the user did not provide it and to fail in a
callback:
.. click:example::
def abort_if_false(ctx, param, value):
if not value:
ctx.abort()
@click.command()
@click.option('--yes', is_flag=True, callback=abort_if_false,
expose_value=False,
prompt='Are you sure you want to drop the db?')
def dropdb():
click.echo('Dropped all tables!')
And what it looks like on the command line:
.. click:run::
invoke(dropdb, input=['n'])
invoke(dropdb, args=['--yes'])
Because this combination of parameters is quite common, this can also be
replaced with the :func:`confirmation_option` decorator:
.. click:example::
@click.command()
@click.confirmation_option(help='Are you sure you want to drop the db?')
def dropdb():
click.echo('Dropped all tables!')
.. admonition:: Callback Signature Changes
In Click 2.0 the signature for callbacks changed. For more
information about these changes see :ref:`upgrade-to-2.0`.
Values from Environment Variables
---------------------------------
A very useful feature of Click is the ability to accept parameters from
environment variables in addition to regular parameters. This allows
tools to be automated much easier. For instance, you might want to pass
a configuration file with a ``--config`` parameter but also support exporting
a ``TOOL_CONFIG=hello.cfg`` key-value pair for a nicer development
experience.
This is supported by Click in two ways. One is to automatically build
environment variables which is supported for options only. To enable this
feature, the ``auto_envvar_prefix`` parameter needs to be passed to the
script that is invoked. Each command and parameter is then added as an
uppercase underscore-separated variable. If you have a subcommand
called ``foo`` taking an option called ``bar`` and the prefix is
``MY_TOOL``, then the variable is ``MY_TOOL_FOO_BAR``.
Example usage:
.. click:example::
@click.command()
@click.option('--username')
def greet(username):
click.echo('Hello %s!' % username)
if __name__ == '__main__':
greet(auto_envvar_prefix='GREETER')
And from the command line:
.. click:run::
invoke(greet, env={'GREETER_USERNAME': 'john'},
auto_envvar_prefix='GREETER')
The second option is to manually pull values in from specific environment
variables by defining the name of the environment variable on the option.
Example usage:
.. click:example::
@click.command()
@click.option('--username', envvar='USERNAME')
def greet(username):
click.echo('Hello %s!' % username)
if __name__ == '__main__':
greet()
And from the command line:
.. click:run::
invoke(greet, env={'USERNAME': 'john'})
In that case it can also be a list of different environment variables
where the first one is picked.
Multiple Values from Environment Values
---------------------------------------
As options can accept multiple values, pulling in such values from
environment variables (which are strings) is a bit more complex. The way
Click solves this is by leaving it up to the type to customize this
behavior. For both ``multiple`` and ``nargs`` with values other than
``1``, Click will invoke the :meth:`ParamType.split_envvar_value` method to
perform the splitting.
The default implementation for all types is to split on whitespace. The
exceptions to this rule are the :class:`File` and :class:`Path` types
which both split according to the operating system's path splitting rules.
On Unix systems like Linux and OS X, the splitting happens for those on
every colon (``:``), and for Windows, on every semicolon (``;``).
Example usage:
.. click:example::
@click.command()
@click.option('paths', '--path', envvar='PATHS', multiple=True,
type=click.Path())
def perform(paths):
for path in paths:
click.echo(path)
if __name__ == '__main__':
perform()
And from the command line:
.. click:run::
import os
invoke(perform, env={'PATHS': './foo/bar%s./test' % os.path.pathsep})
Other Prefix Characters
-----------------------
Click can deal with alternative prefix characters other than ``-`` for
options. This is for instance useful if you want to handle slashes as
parameters ``/`` or something similar. Note that this is strongly
discouraged in general because Click wants developers to stay close to
POSIX semantics. However in certain situations this can be useful:
.. click:example::
@click.command()
@click.option('+w/-w')
def chmod(w):
click.echo('writable=%s' % w)
if __name__ == '__main__':
chmod()
And from the command line:
.. click:run::
invoke(chmod, args=['+w'])
invoke(chmod, args=['-w'])
Note that if you are using ``/`` as prefix character and you want to use a
boolean flag you need to separate it with ``;`` instead of ``/``:
.. click:example::
@click.command()
@click.option('/debug;/no-debug')
def log(debug):
click.echo('debug=%s' % debug)
if __name__ == '__main__':
log()
.. _ranges:
Range Options
-------------
A special mention should go to the :class:`IntRange` type, which works very
similarly to the :data:`INT` type, but restricts the value to fall into a
specific range (inclusive on both edges). It has two modes:
- the default mode (non-clamping mode) where a value that falls outside
of the range will cause an error.
- an optional clamping mode where a value that falls outside of the
range will be clamped. This means that a range of ``0-5`` would
return ``5`` for the value ``10`` or ``0`` for the value ``-1`` (for
example).
Example:
.. click:example::
@click.command()
@click.option('--count', type=click.IntRange(0, 20, clamp=True))
@click.option('--digit', type=click.IntRange(0, 10))
def repeat(count, digit):
click.echo(str(digit) * count)
if __name__ == '__main__':
repeat()
And from the command line:
.. click:run::
invoke(repeat, args=['--count=1000', '--digit=5'])
invoke(repeat, args=['--count=1000', '--digit=12'])
If you pass ``None`` for any of the edges, it means that the range is open
at that side.
Callbacks for Validation
------------------------
.. versionchanged:: 2.0
If you want to apply custom validation logic, you can do this in the
parameter callbacks. These callbacks can both modify values as well as
raise errors if the validation does not work.
In Click 1.0, you can only raise the :exc:`UsageError` but starting with
Click 2.0, you can also raise the :exc:`BadParameter` error, which has the
added advantage that it will automatically format the error message to
also contain the parameter name.
Example:
.. click:example::
def validate_rolls(ctx, param, value):
try:
rolls, dice = map(int, value.split('d', 2))
return (dice, rolls)
except ValueError:
raise click.BadParameter('rolls need to be in format NdM')
@click.command()
@click.option('--rolls', callback=validate_rolls, default='1d6')
def roll(rolls):
click.echo('Rolling a %d-sided dice %d time(s)' % rolls)
if __name__ == '__main__':
roll()
And what it looks like:
.. click:run::
invoke(roll, args=['--rolls=42'])
println()
invoke(roll, args=['--rolls=2d12'])

120
docs/parameters.rst Normal file
View file

@ -0,0 +1,120 @@
Parameters
==========
.. currentmodule:: click
Click supports two types of parameters for scripts: options and arguments.
There is generally some confusion among authors of command line scripts of
when to use which, so here is a quick overview of the differences. As its
name indicates, an option is optional. While arguments can be optional
within reason, they are much more restricted in how optional they can be.
To help you decide between options and arguments, the recommendation is
to use arguments exclusively for things like going to subcommands or input
filenames / URLs, and have everything else be an option instead.
Differences
-----------
Arguments can do less than options. The following features are only
available for options:
* automatic prompting for missing input
* act as flags (boolean or otherwise)
* option values can be pulled from environment variables, arguments can not
* options are fully documented in the help page, arguments are not
(this is intentional as arguments might be too specific to be
automatically documented)
On the other hand arguments unlike options can accept an arbitrary number
of arguments. Options can strictly ever only accept a fixed number of
arguments (defaults to 1).
Parameter Types
---------------
Parameters can be of different types. Types can be implemented with
different behavior and some are supported out of the box:
``str`` / :data:`click.STRING`:
The default parameter type which indicates unicode strings.
``int`` / :data:`click.INT`:
A parameter that only accepts integers.
``float`` / :data:`click.FLOAT`:
A parameter that only accepts floating point values.
``bool`` / :data:`click.BOOL`:
A parameter that accepts boolean values. This is automatically used
for boolean flags. If used with string values ``1``, ``yes``, ``y``
and ``true`` convert to `True` and ``0``, ``no``, ``n`` and ``false``
convert to `False`.
:data:`click.UUID`:
A parameter that accepts UUID values. This is not automatically
guessed but represented as :class:`uuid.UUID`.
.. autoclass:: File
:noindex:
.. autoclass:: Path
:noindex:
.. autoclass:: Choice
:noindex:
.. autoclass:: IntRange
:noindex:
Custom parameter types can be implemented by subclassing
:class:`click.ParamType`. For simple cases, passing a Python function that
fails with a `ValueError` is also supported, though discouraged.
Parameter Names
---------------
Parameters (both options and arguments) accept a number of positional
arguments which are the parameter declarations. Each string with a
single dash is added as short argument; each string starting with a double
dash as long one. If a string is added without any dashes, it becomes the
internal parameter name which is also used as variable name.
If a parameter is not given a name without dashes, a name is generated
automatically by taking the longest argument and converting all dashes to
underscores. For an option with ``('-f', '--foo-bar')``, the parameter
name is `foo_bar`. For an option with ``('-x',)``, the parameter is `x`.
For an option with ``('-f', '--filename', 'dest')``, the parameter is
called `dest`.
Implementing Custom Types
-------------------------
To implement a custom type, you need to subclass the :class:`ParamType`
class. Types can be invoked with or without context and parameter object,
which is why they need to be able to deal with this.
The following code implements an integer type that accepts hex and octal
numbers in addition to normal integers, and converts them into regular
integers::
import click
class BasedIntParamType(click.ParamType):
name = 'integer'
def convert(self, value, param, ctx):
try:
if value[:2].lower() == '0x':
return int(value[2:], 16)
elif value[:1] == '0':
return int(value, 8)
return int(value, 10)
except ValueError:
self.fail('%s is not a valid integer' % value, param, ctx)
BASED_INT = BasedIntParamType()
As you can see, a subclass needs to implement the :meth:`ParamType.convert`
method and optionally provide the :attr:`ParamType.name` attribute. The
latter can be used for documentation purposes.

48
docs/prompts.rst Normal file
View file

@ -0,0 +1,48 @@
User Input Prompts
==================
.. currentmodule:: click
Click supports prompts in two different places. The first is automated
prompts when the parameter handling happens, and the second is to ask for
prompts at a later point independently.
This can be accomplished with the :func:`prompt` function, which asks for
valid input according to a type, or the :func:`confirm` function, which asks
for confirmation (yes/no).
Option Prompts
--------------
Option prompts are integrated into the option interface. See
:ref:`option-prompting` for more information. Internally, it
automatically calls either :func:`prompt` or :func:`confirm` as necessary.
Input Prompts
-------------
To manually ask for user input, you can use the :func:`prompt` function.
By default, it accepts any Unicode string, but you can ask for any other
type. For instance, you can ask for a valid integer::
value = click.prompt('Please enter a valid integer', type=int)
Additionally, the type will be determined automatically if a default value is
provided. For instance, the following will only accept floats::
value = click.prompt('Please enter a number', default=42.0)
Confirmation Prompts
--------------------
To ask if a user wants to continue with an action, the :func:`confirm`
function comes in handy. By default, it returns the result of the prompt
as a boolean value::
if click.confirm('Do you want to continue?'):
click.echo('Well done!')
There is also the option to make the function automatically abort the
execution of the program if it does not return ``True``::
click.confirm('Do you want to continue?', abort=True)

154
docs/python3.rst Normal file
View file

@ -0,0 +1,154 @@
Python 3 Support
================
.. currentmodule:: click
Click supports Python 3, but like all other command line utility libraries,
it suffers from the Unicode text model in Python 3. All examples in the
documentation were written so that they could run on both Python 2.x and
Python 3.3 or higher.
At the moment, it is strongly recommended is to use Python 2 for Click
utilities unless Python 3 is a hard requirement.
.. _python3-limitations:
Python 3 Limitations
--------------------
At the moment, Click suffers from a few problems with Python 3:
* The command line in Unix traditionally is in bytes, and not Unicode.
While there are encoding hints for all of this, there are generally
some situations where this can break. The most common one is SSH
connections to machines with different locales.
Misconfigured environments can currently cause a wide range of Unicode
problems in Python 3 due to the lack of support for roundtripping
surrogate escapes. This will not be fixed in Click itself!
For more information see :ref:`python3-surrogates`.
* Standard input and output in Python 3 is opened in Unicode mode by
default. Click has to reopen the stream in binary mode in certain
situations. Because there is no standardized way to do this, this
might not always work. Primarily this can become a problem when
testing command-line applications.
This is not supported::
sys.stdin = io.StringIO('Input here')
sys.stdout = io.StringIO()
Instead you need to do this::
input = 'Input here'
in_stream = io.BytesIO(input.encode('utf-8'))
sys.stdin = io.TextIOWrapper(in_stream, encoding='utf-8')
out_stream = io.BytesIO()
sys.stdout = io.TextIOWrapper(out_stream, encoding='utf-8')
Remember that in that case, you need to use ``out_stream.getvalue()``
and not ``sys.stdout.getvalue()`` if you want to access the buffer
contents as the wrapper will not forward that method.
Python 2 and 3 Differences
--------------------------
Click attempts to minimize the differences between Python 2 and Python 3
by following the best practices for both languages.
In Python 2, the following is true:
* ``sys.stdin``, ``sys.stdout``, and ``sys.stderr`` are opened in binary
mode, but under some circumstances they support Unicode output. Click
attempts to not subvert this but provides support for forcing streams
to be Unicode-based.
* ``sys.argv`` is always byte-based. Click will pass bytes to all
input types and convert as necessary. The :class:`STRING` type
automatically will decode properly the input value into a string by
trying the most appropriate encodings.
* When dealing with files, Click will never go through the Unicode APIs
and will instead use the operating system's byte APIs to open the
files.
In Python 3, the following is true:
* ``sys.stdin``, ``sys.stdout`` and ``sys.stderr`` are by default
text-based. When Click needs a binary stream, it attempts to discover
the underlying binary stream. See :ref:`python3-limitations` for how
this works.
* ``sys.argv`` is always Unicode-based. This also means that the native
type for input values to the types in Click is Unicode, and not bytes.
This causes problems when the terminal is incorrectly set and Python
does not figure out the encoding. In that case, the Unicode string
will contain error bytes encoded as surrogate escapes.
* When dealing with files, Click will always use the Unicode file system
API calls by using the operating system's reported or guessed
filesystem encoding. Surrogates are supported for filenames, so it
should be possible to open files through the :class:`File` type even
if the environment is misconfigured.
.. _python3-surrogates:
Python 3 Surrogate Handling
---------------------------
Click in Python 3 does all the Unicode handling in the standard library
and is subject to its behavior. In Python 2, Click does all the Unicode
handling itself, which means there are differences in error behavior.
The most glaring difference is that in Python 2, Unicode will "just work",
while in Python 3, it requires extra care. The reason for this is that on
Python 3, the encoding detection is done in the interpreter and on Linux
and certain other operating systems its encoding handling is problematic.
The biggest source of frustration is that Click scripts invoked by
init systems (sysvinit, upstart, systemd, etc.), deployment tools (salt,
puppet), or cron jobs (cron) will refuse to work unless a Unicode locale is
exported.
If Click encounters such an environment it will prevent further execution
to force you to set a locale. This is done because Click cannot know
about the state of the system once it's invoked and restore the values
before Python's Unicode handling kicked in.
If you see something like this error in Python 3::
Traceback (most recent call last):
...
RuntimeError: Click will abort further execution because Python 3 was
configured to use ASCII as encoding for the environment. Either switch
to Python 2 or consult for http://click.pocoo.org/python3/
mitigation steps.
You are dealing with an environment where Python 3 thinks you are
restricted to ASCII data. The solution to these problems is different
depending on which locale your computer is running in.
For instance, if you have a German Linux machine, you can fix the problem
by exporting the locale to ``de_DE.utf-8``::
export LC_ALL=de_DE.utf-8
export LANG=de_DE.utf-8
If you are on a US machine, ``en_EN.utf-8`` is the encoding of choice. On
some newer Linux systems, you could also try ``C.UTF-8`` as the locale::
export LC_ALL=C.UTF-8
export LANG=C.UTF-8
You need to do this before you invoke your Python script. If you are
curious about the reasons for this, you can join the discussions in the
Python 3 bug tracker:
* `ASCII is a bad filesystem default encoding
<http://bugs.python.org/issue13643#msg149941>`_
* `Use surrogateescape as default error handler
<http://bugs.python.org/issue19977>`_
* `Python 3 raises Unicode errors in the C locale
<http://bugs.python.org/issue19846>`_
* `LC_CTYPE=C: pydoc leaves terminal in an unusable state
<http://bugs.python.org/issue21398>`_ (this is relevant to Click
because the pager support is provided by the stdlib pydoc module)

274
docs/quickstart.rst Normal file
View file

@ -0,0 +1,274 @@
Quickstart
==========
.. currentmodule:: click
You can get the library directly from PyPI::
pip install click
The installation into a :ref:`virtualenv` is heavily recommended.
.. _virtualenv:
virtualenv
----------
Virtualenv is probably what you want to use for developing Click
applications.
What problem does virtualenv solve? Chances are that you want to use it
for other projects besides your Click script. But the more projects you
have, the more likely it is that you will be working with different
versions of Python itself, or at least different versions of Python
libraries. Let's face it: quite often libraries break backwards
compatibility, and it's unlikely that any serious application will have
zero dependencies. So what do you do if two or more of your projects have
conflicting dependencies?
Virtualenv to the rescue! Virtualenv enables multiple side-by-side
installations of Python, one for each project. It doesn't actually
install separate copies of Python, but it does provide a clever way to
keep different project environments isolated. Let's see how virtualenv
works.
If you are on Mac OS X or Linux, chances are that one of the following two
commands will work for you::
$ sudo easy_install virtualenv
or even better::
$ sudo pip install virtualenv
One of these will probably install virtualenv on your system. Maybe it's even
in your package manager. If you use Ubuntu, try::
$ sudo apt-get install python-virtualenv
If you are on Windows (or none of the above methods worked) you must install
``pip`` first. For more information about this, see `installing pip`_.
Once you have it installed, run the ``pip`` command from above, but without
the `sudo` prefix.
.. _installing pip: http://pip.readthedocs.org/en/latest/installing.html
Once you have virtualenv installed, just fire up a shell and create
your own environment. I usually create a project folder and a `venv`
folder within::
$ mkdir myproject
$ cd myproject
$ virtualenv venv
New python executable in venv/bin/python
Installing setuptools, pip............done.
Now, whenever you want to work on a project, you only have to activate the
corresponding environment. On OS X and Linux, do the following::
$ . venv/bin/activate
If you are a Windows user, the following command is for you::
$ venv\scripts\activate
Either way, you should now be using your virtualenv (notice how the prompt of
your shell has changed to show the active environment).
And if you want to go back to the real world, use the following command::
$ deactivate
After doing this, the prompt of your shell should be as familar as before.
Now, let's move on. Enter the following command to get Flask activated in your
virtualenv::
$ pip install Click
A few seconds later and you are good to go.
Screencast and Examples
-----------------------
There is a screencast available which shows the basic API of Click and
how to build simple applications with it. It also explores how to build
commands with subcommands.
* `Building Command Line Applications with Click
<https://www.youtube.com/watch?v=kNke39OZ2k0>`_
Examples of Click applications can be found in the documentation as well
as in the GitHub repository together with readme files:
* ``inout``: `File input and output
<https://github.com/mitsuhiko/click/tree/master/examples/inout>`_
* ``naval``: `Port of docopt naval example
<https://github.com/mitsuhiko/click/tree/master/examples/naval>`_
* ``aliases``: `Command alias example
<https://github.com/mitsuhiko/click/tree/master/examples/aliases>`_
* ``repo``: `Git-/Mercurial-like command line interface
<https://github.com/mitsuhiko/click/tree/master/examples/repo>`_
* ``complex``: `Complex example with plugin loading
<https://github.com/mitsuhiko/click/tree/master/examples/complex>`_
* ``validation``: `Custom parameter validation example
<https://github.com/mitsuhiko/click/tree/master/examples/validation>`_
* ``colors``: `Colorama ANSI color support
<https://github.com/mitsuhiko/click/tree/master/examples/colors>`_
* ``termui``: `Terminal UI functions demo
<https://github.com/mitsuhiko/click/tree/master/examples/termui>`_
* ``imagepipe``: `Multi command chaining demo
<https://github.com/mitsuhiko/click/tree/master/examples/imagepipe>`_
Basic Concepts
--------------
Click is based on declaring commands through decorators. Internally, there
is a non-decorator interface for advanced use cases, but it's discouraged
for high-level usage.
A function becomes a Click command line tool by decorating it through
:func:`click.command`. At its simplest, just decorating a function
with this decorator will make it into a callable script:
.. click:example::
import click
@click.command()
def hello():
click.echo('Hello World!')
What's happening is that the decorator converts the function into a
:class:`Command` which then can be invoked::
if __name__ == '__main__':
hello()
And what it looks like:
.. click:run::
invoke(hello, args=[], prog_name='python hello.py')
And the corresponding help page:
.. click:run::
invoke(hello, args=['--help'], prog_name='python hello.py')
Echoing
-------
Why does this example use :func:`echo` instead of the regular
:func:`print` function? The answer to this question is that Click
attempts to support both Python 2 and Python 3 the same way and to be very
robust even when the environment is misconfigured. Click wants to be
functional at least on a basic level even if everything is completely
broken.
What this means is that the :func:`echo` function applies some error
correction in case the terminal is misconfigured instead of dying with an
:exc:`UnicodeError`.
As an added benefit, starting with Click 2.0, the echo function also
has good support for ANSI colors. It will automatically strip ANSI codes
if the output stream is a file and if colorama is supported, ANSI colors
will also work on Windows. See :ref:`ansi-colors` for more information.
If you don't need this, you can also use the `print()` construct /
function.
Nesting Commands
----------------
Commands can be attached to other commands of type :class:`Group`. This
allows arbitrary nesting of scripts. As an example here is a script that
implements two commands for managing databases:
.. click:example::
@click.group()
def cli():
pass
@click.command()
def initdb():
click.echo('Initialized the database')
@click.command()
def dropdb():
click.echo('Dropped the database')
cli.add_command(initdb)
cli.add_command(dropdb)
As you can see, the :func:`group` decorator works like the :func:`command`
decorator, but creates a :class:`Group` object instead which can be given
multiple subcommands that can be attached with :meth:`Group.add_command`.
For simple scripts, it's also possible to automatically attach and create a
command by using the :meth:`Group.command` decorator instead. The above
script can instead be written like this:
.. click:example::
@click.group()
def cli():
pass
@cli.command()
def initdb():
click.echo('Initialized the database')
@cli.command()
def dropdb():
click.echo('Dropped the database')
Adding Parameters
-----------------
To add parameters, use the :func:`option` and :func:`argument` decorators:
.. click:example::
@click.command()
@click.option('--count', default=1, help='number of greetings')
@click.argument('name')
def hello(count, name):
for x in range(count):
click.echo('Hello %s!' % name)
What it looks like:
.. click:run::
invoke(hello, args=['--help'], prog_name='python hello.py')
.. _switching-to-setuptools:
Switching to Setuptools
-----------------------
In the code you wrote so far there is a block at the end of the file which
looks like this: ``if __name__ == '__main__':``. This is traditionally
how a standalone Python file looks like. With Click you can continue
doing that, but there are better ways through setuptools.
There are two main (and many more) reasons for this:
The first one is that setuptools automatically generates executable
wrappers for Windows so your command line utilities work on Windows too.
The second reason is that setuptools scripts work with virtualenv on Unix
without the virtualenv having to be activated. This is a very useful
concept which allows you to bundle your scripts with all requirements into
a virtualenv.
Click is perfectly equipped to work with that and in fact the rest of the
documentation will assume that you are writing applications through
setuptools.
I strongly recommend to have a look at the :ref:`setuptools-integration`
chapter before reading the rest as the examples assume that you will
be using setuptools.

137
docs/setuptools.rst Normal file
View file

@ -0,0 +1,137 @@
.. _setuptools-integration:
Setuptools Integration
======================
When writing command line utilities, it's recommended to write them as
modules that are distributed with setuptools instead of using Unix
shebangs.
Why would you want to do that? There are a bunch of reasons:
1. One of the problems with the traditional approach is that the first
module the Python interpreter loads has an incorrect name. This might
sound like a small issue but it has quite significant implications.
The first module is not called by it's actual name, but the
interpreter renames it to ``__main__``. While that is a perfectly
valid name it means that if another piece of code wants to import from
that module it will trigger the import a second time under it's real
name and all the sudden your code is imported twice.
2. Not on all platforms are things that easy to execute. On Linux and OS
X you can add a comment to the beginning of the file (``#/usr/bin/env
python``) and your script works like an executable (assuming it has
the executable bit set). This however does not work on Windows.
While on Windows you can associate interpreters with file extensions
(like having everything ending in ``.py`` execute through the Python
interpreter) you will then run into issues if you want to use the
script in a virtualenv.
In fact running a script in a virtualenv is an issue with OS X and
Linux as well. With the traditional approach you need to have the
whole virtualenv activated so that the correct Python interpreter is
used. Not very user friendly.
3. The main trick only works if the script is a Python module. If your
application grows too large and you want to start using a package you
will run into issues.
Introduction
------------
To bundle your script with setuptools, all you need is the script in a
Python package and a ``setup.py`` file.
Imagine this directory structure::
yourscript.py
setup.py
Contents of ``yourscript.py``:
.. click:example::
import click
@click.command()
def cli():
"""Example script."""
click.echo('Hello World!')
Contents of ``setup.py``::
from setuptools import setup
setup(
name='yourscript',
version='0.1',
py_modules=['yourscript'],
install_requires=[
'Click',
],
entry_points='''
[console_scripts]
yourscript=yourscript:cli
''',
)
The magic is in the ``entry_points`` parameter. Below
``console_scripts``, each line identifies one console script. The first
part before the equals sign (``=``) is the name of the script that should
be generated, the second part is the import path followed by a colon
(``:``) with the Click command.
That's it.
Testing The Script
------------------
To test the script, you can make a new virtualenv and then install your
package::
$ virtualenv venv
$ . venv/bin/activate
$ pip install --editable .
Afterwards, your command should be available:
.. click:run::
invoke(cli, prog_name='yourscript')
Scripts in Packages
-------------------
If your script is growing and you want to switch over to your script being
contained in a Python package the changes necessary are minimal. Let's
assume your directory structure changed to this::
yourpackage/
main.py
utils.py
scripts/
yourscript.py
In this case instead of using ``py_modules`` in your ``setup.py`` file you
can use ``packages`` and the automatic package finding support of
setuptools. In addition to that it's also recommended to include other
package data.
These would be the modified contents of ``setup.py``::
from setuptools import setup, find_packages
setup(
name='yourpackage',
version='0.1',
packages=find_packages(),
include_package_data=True,
install_requires=[
'Click',
],
entry_points='''
[console_scripts]
yourscript=yourpackage.scripts.yourscript:cli
''',
)

90
docs/testing.rst Normal file
View file

@ -0,0 +1,90 @@
Testing Click Applications
==========================
.. currentmodule:: click.testing
For basic testing, Click provides the :mod:`click.testing` module which
provides test functionality that helps you invoke command line
applications and check their behavior.
These tools should really only be used for testing as they change
the entire interpreter state for simplicity and are not in any way
thread-safe!
Basic Testing
-------------
The basic functionality for testing Click applications is the
:class:`CliRunner` which can invoke commands as command line scripts. The
:meth:`CliRunner.invoke` method runs the command line script in isolation
and captures the output as both bytes and binary data.
The return value is a :class:`Result` object, which has the captured output
data, exit code, and optional exception attached.
Example::
import click
from click.testing import CliRunner
def test_hello_world():
@click.command()
@click.argument('name')
def hello(name):
click.echo('Hello %s!' % name)
runner = CliRunner()
result = runner.invoke(hello, ['Peter'])
assert result.exit_code == 0
assert result.output == 'Hello Peter!\n'
File System Isolation
---------------------
For basic command line tools that want to operate with the file system, the
:meth:`CliRunner.isolated_filesystem` method comes in useful which sets up
an empty folder and changes the current working directory to.
Example::
import click
from click.testing import CliRunner
def test_cat():
@click.command()
@click.argument('f', type=click.File())
def cat(f):
click.echo(f.read())
runner = CliRunner()
with runner.isolated_filesystem():
with open('hello.txt') as f:
f.write('Hello World!')
result = runner.invoke(cat, ['hello.txt'])
assert result.exit_code == 0
assert result.output == 'Hello World!\n'
Input Streams
-------------
The test wrapper can also be used to provide input data for the input
stream (stdin). This is very useful for testing prompts, for instance::
import click
from click.testing import CliRunner
def test_prompts():
@click.command()
@click.option('--foo', prompt=True)
def test(foo):
click.echo('foo=%s' % foo)
runner = CliRunner()
result = runner.invoke(test, input='wau wau\n')
assert not result.exception
assert result.output == 'Foo: wau wau\nfoo=wau wau\n'
Note that prompts will be emulated so that they write the input data to
the output stream as well. If hidden input is expected then this
obviously does not happen.

100
docs/upgrading.rst Normal file
View file

@ -0,0 +1,100 @@
Upgrading To Newer Releases
===========================
Click attempts the highest level of backwards compatibility but sometimes
this is not entirely possible. In case we need to break backwards
compatibility this document gives you information about how to upgrade or
handle backwards compatibility properly.
.. _upgrade-to-3.2:
Upgrading to 3.2
----------------
Click 3.2 had to perform two changes to multi commands which were
triggered by a change between Click 2 and Click 3 that had bigger
consequences than anticipated.
Context Invokes
```````````````
Click 3.2 contains a fix for the :meth:`Context.invoke` function when used
with other commands. The original intention of this function was to
invoke the other command as as if it came from the command line when it
was passed a context object instead of a function. This use was only
documented in a single place in the documentation before and there was no
proper explanation for the method in the API documentation.
The core issue is that before 3.2 this call worked against intentions::
ctx.invoke(other_command, 'arg1', 'arg2')
This was never intended to work as it does not allow Click to operate on
the parameters. Given that this pattern was never documented and ill
intended the decision was made to change this behavior in a bugfix release
before it spreads by accident and developers depend on it.
The correct invocation for the above command is the following::
ctx.invoke(other_command, name_of_arg1='arg1', name_of_arg2='arg2')
This also allowed us to fix the issue that defaults were not handled
properly by this function.
Multicommand Chaining API
`````````````````````````
Click 3 introduced multicommand chaning. This required a change in how
Click internally dispatches. Unfortunately this change was not correctly
implemented and it appeared that it was possible to provide an API that
can inform the super command about all the subcommands that will be
invoked.
This assumption however does not work with one of the API guarantees that
have been given in the past. As such this functionality has been removed
in 3.2 as it was already broken. Instead the accidentally broken
functionality of the :attr:`Context.invoked_subcommand` attribute was
restored.
If you do require the know which exact commands will be invoked there are
different ways to cope with this. The first one is to let the subcommands
all return functions and then to invoke the functions in a
:meth:`Context.resultcallback`.
.. _upgrade-to-2.0:
Upgrading to 2.0
----------------
Click 2.0 has one breaking change which is the signature for parameter
callbacks. Before 2.0, the callback was invoked with ``(ctx, value)``
whereas now it's ``(ctx, param, value)``. This change was necessary as it
otherwise made reusing callbacks too complicated.
To ease the transition Click will still accept old callbacks. Starting
with Click 3.0 it will start to issue a warning to stderr to encourage you
to upgrade.
In case you want to support both Click 1.0 and Click 2.0, you can make a
simple decorator that adjusts the signatures::
import click
from functools import update_wrapper
def compatcallback(f):
# Click 1.0 does not have a version string stored, so we need to
# use getattr here to be safe.
if getattr(click, '__version__', '0.0') >= '2.0':
return f
return update_wrapper(lambda ctx, value: f(ctx, None, value), f)
With that helper you can then write something like this::
@compatcallback
def callback(ctx, param, value):
return value.upper()
Note that because Click 1.0 did not pass a parameter, the `param` argument
here would be `None`, so a compatibility callback could not use that
argument.

368
docs/utils.rst Normal file
View file

@ -0,0 +1,368 @@
Utilities
=========
.. currentmodule:: click
Besides the functionality that Click provides to interface with argument
parsing and handling, it also provides a bunch of addon functionality that
is useful for writing command line utilities.
Printing to Stdout
------------------
The most obvious helper is the :func:`echo` function, which in many ways
works like the Python ``print`` statement or function. The main difference is
that it works the same in Python 2 and 3, it intelligently detects
misconfigured output streams, and it will never fail (except in Python 3; for
more information see :ref:`python3-limitations`).
Example::
import click
click.echo('Hello World!')
Most importantly, it can print both Unicode and binary data, unlike the
built-in ``print`` function in Python 3, which cannot output any bytes. It
will, however, emit a trailing newline by default, which needs to be
suppressed by passing ``nl=False``::
click.echo(b'\xe2\x98\x83', nl=False)
.. versionadded:: 3.0
Starting with Click 3.0 you can also easily print to standard error by
passing ``err=True``::
click.echo('Hello World!', err=True)
.. _ansi-colors:
ANSI Colors
-----------
.. versionadded:: 2.0
Starting with Click 2.0, the :func:`echo` function gained extra
functionality to deal with ANSI colors and styles. Note that on Windows,
this functionality is only available if `colorama`_ is installed. If it
is installed, then ANSI codes are intelligently handled.
Primarily this means that:
- Click's :func:`echo` function will automatically strip ANSI color codes
if the stream is not connected to a terminal.
- the :func:`echo` function will transparently connect to the terminal on
Windows and translate ANSI codes to terminal API calls. This means
that colors will work on Windows the same way they do on other
operating systems.
Note for `colorama` support: Click will automatically detect when `colorama`
is available and use it. Do *not* call ``colorama.init()``!
To install `colorama`, run this command::
$ pip install colorama
For styling a string, the :func:`style` function can be used::
import click
click.echo(click.style('Hello World!', fg='green'))
click.echo(click.style('Some more text', bg='blue', fg='white'))
click.echo(click.style('ATTENTION', blink=True, bold=True))
The combination of :func:`echo` and :func:`style` is also available in
a single function called :func:`secho`::
click.secho('Hello World!', fg='green')
click.secho('Some more text', bg='blue', fg='white')
click.secho('ATTENTION', blink=True, bold=True)
.. _colorama: https://pypi.python.org/pypi/colorama
Pager Support
-------------
In some situations, you might want to show long texts on the terminal and
let a user scroll through it. This can be achieved by using the
:func:`echo_via_pager` function which works similarly to the :func:`echo`
function, but always writes to stdout and, if possible, through a pager.
Example:
.. click:example::
@click.command()
def less():
click.echo_via_pager('\n'.join('Line %d' % idx
for idx in range(200)))
Screen Clearing
---------------
.. versionadded:: 2.0
To clear the terminal screen, you can use the :func:`clear` function that
is provided starting with Click 2.0. It does what the name suggests: it
clears the entire visible screen in a platform-agnostic way:
::
import click
click.clear()
Getting Characters from Terminal
--------------------------------
.. versionadded:: 2.0
Normally, when reading input from the terminal, you would read from
standard input. However, this is buffered input and will not show up until
the line has been terminated. In certain circumstances, you might not want
to do that and instead read individual characters as they are being written.
For this, Click provides the :func:`getchar` function which reads a single
character from the terminal buffer and returns it as a Unicode character.
Note that this function will always read from the terminal, even if stdin
is instead a pipe.
Example::
import click
click.echo('Continue? [yn] ', nl=False)
c = click.getchar()
click.echo()
if c == 'y':
click.echo('We will go on')
elif c == 'n':
click.echo('Abort!')
else:
click.echo('Invalid input :(')
Note that this reads raw input, which means that things like arrow keys
will show up in the platform's native escape format. The only characters
translated are ``^C`` and ``^D`` which are converted into keyboard
interrupts and end of file exceptions respectively. This is done because
otherwise, it's too easy to forget about that and to create scripts that
cannot be properly exited.
Waiting for Key Press
---------------------
.. versionadded:: 2.0
Sometimes, it's useful to pause until the user presses any key on the
keyboard. This is especially useful on Windows where ``cmd.exe`` will
close the window at the end of the command execution by default, instead
of waiting.
In click, this can be accomplished with the :func:`pause` function. This
function will print a quick message to the terminal (which can be
customized) and wait for the user to press a key. In addition to that,
it will also become a NOP (no operation instruction) if the script is not
run interactively.
Example::
import click
click.pause()
Launching Editors
-----------------
.. versionadded:: 2.0
Click supports launching editors automatically through :func:`edit`. This
is very useful for asking users for multi-line input. It will
automatically open the user's defined editor or fall back to a sensible
default. If the user closes the editor without saving, the return value
will be `None` otherwise the entered text.
Example usage::
import click
def get_commit_message():
MARKER = '# Everything below is ignored\n'
message = click.edit('\n\n' + MARKER)
if message is not None:
return message.split(MARKER, 1)[0].rstrip('\n')
Alternatively, the function can also be used to launch editors for files by
a specific filename. In this case, the return value is always `None`.
Example usage::
import click
click.edit(filename='/etc/passwd')
Launching Applications
----------------------
.. versionadded:: 2.0
Click supports launching applications through :func:`launch`. This can be
used to open the default application assocated with a URL or filetype.
This can be used to launch web browsers or picture viewers, for instance.
In addition to this, it can also launch the file manager and automatically
select the provided file.
Example usage::
click.launch('http://click.pocoo.org/')
click.launch('/my/downloaded/file.txt', locate=True)
Printing Filenames
------------------
Because filenames might not be Unicode, formatting them can be a bit
tricky. Generally, this is easier in Python 2 than on 3, as you can just
write the bytes to stdout with the ``print`` function, but in Python 3, you
will always need to operate in Unicode.
The way this works with click is through the :func:`format_filename`
function. It does a best-effort conversion of the filename to Unicode and
will never fail. This makes it possible to use these filenames in the
context of a full Unicode string.
Example::
click.echo('Path: %s' % click.format_filename(b'foo.txt'))
Standard Streams
----------------
For command line utilities, it's very important to get access to input and
output streams reliably. Python generally provides access to these
streams through ``sys.stdout`` and friends, but unfortunately, there are
API differences between 2.x and 3.x, especially with regards to how these
streams respond to Unicode and binary data.
Because of this, click provides the :func:`get_binary_stream` and
:func:`get_text_stream` functions, which produce consistent results with
different Python versions and for a wide variety pf terminal configurations.
The end result is that these functions will always return a functional
stream object (except in very odd cases in Python 3; see
:ref:`python3-limitations`).
Example::
import click
stdin_text = click.get_text_stream('stdin')
stdout_binary = click.get_binary_stream('stdout')
Intelligent File Opening
------------------------
.. versionadded:: 3.0
Starting with Click 3.0 the logic for opening files from the :class:`File`
type is exposed through the :func:`open_file` function. It can
intelligently open stdin/stdout as well as any other file.
Example::
import click
stdout = click.open_file('-', 'w')
test_file = click.open_file('test.txt', 'w')
If stdin or stdout are returned, the return value is wrapped in a special
file where the context manager will prevent the closing of the file. This
makes the handling of standard streams transparent and you can always use
it like this::
with click.open_file(filename, 'w') as f:
f.write('Hello World!\n')
Finding Application Folders
---------------------------
.. versionadded:: 2.0
Very often, you want to open a configuration file that belongs to your
application. However, different operating systems store these configuration
files in different locations depending on their standards. Click provides
a :func:`get_app_dir` function which returns the most appropriate location
for per-user config files for your application depending on the OS.
Example usage::
import os
import click
import ConfigParser
APP_NAME = 'My Application'
def read_config():
cfg = os.path.join(click.get_app_dir(APP_NAME), 'config.ini')
parser = ConfigParser.RawConfigParser()
parser.read([cfg])
rv = {}
for section in parser.sections():
for key, value in parser.items(section):
rv['%s.%s' % (section, key)] = value
return rv
Showing Progress Bars
---------------------
.. versionadded:: 2.0
Sometimes, you have command line scripts that need to process a lot of data,
but you want to quickly show the user some progress about how long that
will take. Click supports simple progress bar rendering for that through
the :func:`progressbar` function.
The basic usage is very simple: the idea is that you have an iterable that
you want to operate on. For each item in the iterable it might take some
time to do processing. So say you have a loop like this::
for user in all_the_users_to_process:
modify_the_user(user)
To hook this up with an automatically updating progress bar, all you need
to do is to change the code to this::
import click
with click.progressbar(all_the_users_to_process) as bar:
for user in bar:
modify_the_user(user)
Click will then automatically print a progress bar to the terminal and
calculate the remaining time for you. The calculation of remaining time
requires that the iterable has a length. If it does not have a length
but you know the length, you can explicitly provide it::
with click.progressbar(all_the_users_to_process,
length=number_of_users) as bar:
for user in bar:
modify_the_user(user)
Another useful feature is to associate a label with the progress bar which
will be shown preceding the progress bar::
with click.progressbar(all_the_users_to_process,
label='Modifying user accounts',
length=number_of_users) as bar:
for user in bar:
modify_the_user(user)

152
docs/why.rst Normal file
View file

@ -0,0 +1,152 @@
Why Click?
==========
There are so many libraries out there for writing command line utilities;
why does Click exist?
This question is easy to answer: because there is not a single command
line utility for Python out there which ticks the following boxes:
* is lazily composable without restrictions
* fully follows the Unix command line conventions
* supports loading values from environment variables out of the box
* supports for prompting of custom values
* is fully nestable and composable
* works the same in Python 2 and 3
* supports file handling out of the box
* comes with useful common helpers (getting terminal dimensions,
ANSI colors, fetching direct keyboard input, screen clearing,
finding config paths, launching apps and editors, etc.)
There are many alternatives to Click and you can have a look at them if
you enjoy them better. The obvious ones are ``optparse`` and ``argparse``
from the standard library.
Click is actually implemented as a wrapper around a mild fork of
``optparse`` and does not implement any parsing itself. The reason it's
not based on ``argparse`` is that ``argparse`` does not allow proper
nesting of commands by design and has some deficiencies when it comes to
POSIX compliant argument handling.
Click is designed to be fun to work with and at the same time not stand in
your way. It's not overly flexible either. Currently, for instance, it
does not allow you to customize the help pages too much. This is intentional
because Click is designed to allow you to nest command line utilities. The
idea is that you can have a system that works together with another system by
tacking two Click instances together and they will continue working as they
should.
Too much customizability would break this promise.
Click was written to support the `Flask <http://flask.pocoo.org/>`_
microframework ecosystem because no tool could provide it with the
functionality it needed.
To get an understanding of what Click is all about, I strongly recommend
looking at the :ref:`complex-guide` chapter to see what it's useful for.
Why not Argparse?
-----------------
Click is internally based on optparse instead of argparse. This however
is an implementation detail that a user does not have to be concerned
with. The reason however Click is not using argparse is that it has some
problematic behaviors that make handling arbitrary command line interfaces
hard:
* argparse has built-in magic behavior to guess if something is an
argument or an option. This becomes a problem when dealing with
incomplete command lines as it's not possible to know without having a
full understanding of the command line how the parser is going to
behave. This goes against Click's ambitions of dispatching to
subparsers.
* argparse currently does not support disabling of interspearsed
arguments. Without this feature it's not possible to safely implement
Click's nested parsing nature.
Why not Docopt etc.?
--------------------
Docopt and many tools like it are cool in how they work, but very few of
these tools deal with nesting of commands and composability in a way like
Click. To the best of the developer's knowledge, Click is the first
Python library that aims to create a level of composability of applications
that goes beyond what the system itself supports.
Docopt, for instance, acts by parsing your help pages and then parsing
according to those rules. The side effect of this is that docopt is quite
rigid in how it handles the command line interface. The upside of docopt
is that it gives you strong control over your help page; the downside is
that due to this it cannot rewrap your output for the current terminal
width and it makes translations hard. On top of that docopt is restricted
to basic parsing. It does not handle argument dispatching and callback
invocation or types. This means there is a lot of code that needs to be
written in addition to the basic help page to handle the parsing results.
Most of all, however, it makes composability hard. While docopt does
support dispatching to subcommands, it for instance does not directly
support any kind of automatic subcommand enumeration based on what's
available or it does not enforce subcommands to work in a consistent way.
This is fine, but it's different from how Click wants to work. Click aims
to support fully composable command line user interfaces by doing the
following:
- Click does not just parse, it also dispatches to the appropriate code.
- Click has a strong concept of an invocation context that allows
subcommands to respond to data from the parent command.
- Click has strong information available for all parameters and commands
so that it can generate unified help pages for the full CLI and to
assist the user in converting the input data as necessary.
- Click has a strong understanding of what types are and can give the user
consistent error messages if something goes wrong. A subcommand
written by a different developer will not suddenly die with a
different error messsage because it's manually handled.
- Click has enough meta information available for its whole program
that it can evolve over time to improve the user experience without
forcing developers to adjust their programs. For instance, if Click
decides to change how help pages are formatted, all Click programs
will automatically benefit from this.
The aim of Click is to make composable systems, whereas the aim of docopt
is to build the most beautiful and hand-crafted command line interfaces.
These two goals conflict with one another in subtle ways. Click
actively prevents people from implementing certain patterns in order to
achieve unified command line interfaces. You have very little input on
reformatting your help pages for instance.
Why Hardcoded Behaviors?
------------------------
The other question is why Click goes away from optparse and hardcodes
certain behaviors instead of staying configurable. There are multiple
reasons for this. The biggest one is that too much configurability makes
it hard to achieve a consistent command line experience.
The best example for this is optparse's ``callback`` functionality for
accepting arbitrary number of arguments. Due to syntactical ambiguities
on the command line, there is no way to implement fully variadic arguments.
There are always tradeoffs that need to be made and in case of
``argparse`` these tradeoffs have been critical enough, that a system like
Click cannot even be implemented on top of it.
In this particular case, Click attempts to stay with a handful of accepted
paradigms for building command line interfaces that can be well documented
and tested.
Why No Auto Correction?
-----------------------
The question came up why Click does not auto correct parameters given that
even optparse and argparse support automatic expansion of long arguments.
The reason for this is that it's a liability for backwards compatibility.
If people start relying on automatically modified parameters and someone
adds a new parameter in the future, the script might stop working. These
kinds of problems are hard to find so Click does not attempt to be magical
about this.
This sort of behavior however can be implemented on a higher level to
support things such as explicit aliases. For more information see
:ref:`aliases`.

BIN
examples/.DS_Store vendored Normal file

Binary file not shown.

12
examples/README Normal file
View file

@ -0,0 +1,12 @@
Click Examples
This folder contains various Click examples. Note that
all of these are not runnable by themselves but should be
installed into a virtualenv.
This is done this way so that scripts also properly work
on Windows and in virtualenvs without accidentally executing
through the wrong interpreter.
For more information about this see the documentation:
http://click.pocoo.org/setuptools/

17
examples/aliases/README Normal file
View file

@ -0,0 +1,17 @@
$ aliases_
aliases is a fairly advanced example that shows how
to implement command aliases with Click. It uses a
subclass of the default group to customize how commands
are located.
It supports both aliases read from a config file as well
as automatic abbreviations.
The aliases from the config are read from the aliases.ini
file. Try `aliases st` and `aliases ci`!
Usage:
$ pip install --editable .
$ aliases --help

View file

@ -0,0 +1,2 @@
[aliases]
ci=commit

111
examples/aliases/aliases.py Normal file
View file

@ -0,0 +1,111 @@
import os
import click
try:
import ConfigParser as configparser
except ImportError:
import configparser
class Config(object):
"""The config in this example only holds aliases."""
def __init__(self):
self.path = os.getcwd()
self.aliases = {}
def read_config(self, filename):
parser = configparser.RawConfigParser()
parser.read([filename])
try:
self.aliases.update(parser.items('aliases'))
except configparser.NoSectionError:
pass
pass_config = click.make_pass_decorator(Config, ensure=True)
class AliasedGroup(click.Group):
"""This subclass of a group supports looking up aliases in a config
file and with a bit of magic.
"""
def get_command(self, ctx, cmd_name):
# Step one: bulitin commands as normal
rv = click.Group.get_command(self, ctx, cmd_name)
if rv is not None:
return rv
# Step two: find the config object and ensure it's there. This
# will create the config object is missing.
cfg = ctx.ensure_object(Config)
# Step three: lookup an explicit command aliase in the config
if cmd_name in cfg.aliases:
actual_cmd = cfg.aliases[cmd_name]
return click.Group.get_command(self, ctx, actual_cmd)
# Alternative option: if we did not find an explicit alias we
# allow automatic abbreviation of the command. "status" for
# instance will match "st". We only allow that however if
# there is only one command.
matches = [x for x in self.list_commands(ctx)
if x.lower().startswith(cmd_name.lower())]
if not matches:
return None
elif len(matches) == 1:
return click.Group.get_command(self, ctx, matches[0])
ctx.fail('Too many matches: %s' % ', '.join(sorted(matches)))
def read_config(ctx, param, value):
"""Callback that is used whenever --config is passed. We use this to
always load the correct config. This means that the config is loaded
even if the group itself never executes so our aliases stay always
available.
"""
cfg = ctx.ensure_object(Config)
if value is None:
value = os.path.join(os.path.dirname(__file__), 'aliases.ini')
cfg.read_config(value)
return value
@click.command(cls=AliasedGroup)
@click.option('--config', type=click.Path(exists=True, dir_okay=False),
callback=read_config, expose_value=False,
help='The config file to use instead of the default.')
def cli():
"""An example application that supports aliases."""
@cli.command()
def push():
"""Pushes changes."""
click.echo('Push')
@cli.command()
def pull():
"""Pulls changes."""
click.echo('Pull')
@cli.command()
def clone():
"""Clones a repository."""
click.echo('Clone')
@cli.command()
def commit():
"""Commits pending changes."""
click.echo('Commit')
@cli.command()
@pass_config
def status(config):
"""Shows the status."""
click.echo('Status for %s' % config.path)

15
examples/aliases/setup.py Normal file
View file

@ -0,0 +1,15 @@
from setuptools import setup
setup(
name='click-example-aliases',
version='1.0',
py_modules=['aliases'],
include_package_data=True,
install_requires=[
'Click',
],
entry_points='''
[console_scripts]
aliases=aliases:cli
''',
)

11
examples/colors/README Normal file
View file

@ -0,0 +1,11 @@
$ colors_
colors is a simple example that shows how you can
colorize text.
For this to work on Windows, colorama is required.
Usage:
$ pip install --editable .
$ colors

26
examples/colors/colors.py Normal file
View file

@ -0,0 +1,26 @@
import click
all_colors = 'black', 'red', 'green', 'yellow', 'blue', 'magenta', \
'cyan', 'white'
@click.command()
def cli():
"""This script prints some colors. If colorama is installed this will
also work on Windows. It will also automatically remove all ANSI
styles if data is piped into a file.
Give it a try!
"""
for color in all_colors:
click.echo(click.style('I am colored %s' % color, fg=color))
for color in all_colors:
click.echo(click.style('I am colored %s and bold' % color,
fg=color, bold=True))
for color in all_colors:
click.echo(click.style('I am reverse colored %s' % color, fg=color,
reverse=True))
click.echo(click.style('I am blinking', blink=True))
click.echo(click.style('I am underlined', underline=True))

17
examples/colors/setup.py Normal file
View file

@ -0,0 +1,17 @@
from setuptools import setup
setup(
name='click-example-colors',
version='1.0',
py_modules=['colors'],
include_package_data=True,
install_requires=[
'Click',
# Colorama is only required for Windows.
'colorama',
],
entry_points='''
[console_scripts]
colors=colors:cli
''',
)

16
examples/complex/README Normal file
View file

@ -0,0 +1,16 @@
$ complex_
complex is an example of building very complex cli
applications that load subcommands dynamically from
a plugin folder and other things.
All the commands are implemented as plugins in the
`complex.commands` package. If a python module is
placed named "cmd_foo" it will show up as "foo"
command and the `cli` object within it will be
loaded as nested Click command.
Usage:
$ pip install --editable .
$ complex --help

View file

View file

@ -0,0 +1,65 @@
import os
import sys
import click
CONTEXT_SETTINGS = dict(auto_envvar_prefix='COMPLEX')
class Context(object):
def __init__(self):
self.verbose = False
self.home = os.getcwd()
def log(self, msg, *args):
"""Logs a message to stderr."""
if args:
msg %= args
click.echo(msg, file=sys.stderr)
def vlog(self, msg, *args):
"""Logs a message to stderr only if verbose is enabled."""
if self.verbose:
self.log(msg, *args)
pass_context = click.make_pass_decorator(Context, ensure=True)
cmd_folder = os.path.abspath(os.path.join(os.path.dirname(__file__),
'commands'))
class ComplexCLI(click.MultiCommand):
def list_commands(self, ctx):
rv = []
for filename in os.listdir(cmd_folder):
if filename.endswith('.py') and \
filename.startswith('cmd_'):
rv.append(filename[4:-3])
rv.sort()
return rv
def get_command(self, ctx, name):
try:
if sys.version_info[0] == 2:
name = name.encode('ascii', 'replace')
mod = __import__('complex.commands.cmd_' + name,
None, None, ['cli'])
except ImportError:
return
return mod.cli
@click.command(cls=ComplexCLI, context_settings=CONTEXT_SETTINGS)
@click.option('--home', type=click.Path(exists=True, file_okay=False,
resolve_path=True),
help='Changes the folder to operate on.')
@click.option('-v', '--verbose', is_flag=True,
help='Enables verbose mode.')
@pass_context
def cli(ctx, verbose, home):
"""A complex command line interface."""
ctx.verbose = verbose
if home is not None:
ctx.home = home

View file

@ -0,0 +1,13 @@
import click
from complex.cli import pass_context
@click.command('init', short_help='Initializes a repo.')
@click.argument('path', required=False, type=click.Path(resolve_path=True))
@pass_context
def cli(ctx, path):
"""Initializes a repository."""
if path is None:
path = ctx.home
ctx.log('Initialized the repository in %s',
click.format_filename(path))

View file

@ -0,0 +1,10 @@
import click
from complex.cli import pass_context
@click.command('status', short_help='Shows file changes.')
@pass_context
def cli(ctx):
"""Shows file changes in the current working directory."""
ctx.log('Changed files: none')
ctx.vlog('bla bla bla, debug info')

15
examples/complex/setup.py Normal file
View file

@ -0,0 +1,15 @@
from setuptools import setup
setup(
name='click-example-complex',
version='1.0',
packages=['complex', 'complex.commands'],
include_package_data=True,
install_requires=[
'Click',
],
entry_points='''
[console_scripts]
complex=complex.cli:cli
''',
)

BIN
examples/imagepipe/.DS_Store vendored Normal file

Binary file not shown.

1
examples/imagepipe/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
processed-*

13
examples/imagepipe/README Normal file
View file

@ -0,0 +1,13 @@
$ imagepipe_
imagepipe is an example application that implements some
multi commands that chain image processing instructions
together.
This requires pillow.
Usage:
$ pip install --editable .
$ imagepipe open -i example01.jpg resize -w 128 display
$ imagepipe open -i example02.jpg blur save

View file

@ -0,0 +1,10 @@
Metadata-Version: 1.0
Name: click-example-imagepipe
Version: 1.0
Summary: UNKNOWN
Home-page: UNKNOWN
Author: UNKNOWN
Author-email: UNKNOWN
License: UNKNOWN
Description: UNKNOWN
Platform: UNKNOWN

View file

@ -0,0 +1,8 @@
README
imagepipe.py
click_example_imagepipe.egg-info/PKG-INFO
click_example_imagepipe.egg-info/SOURCES.txt
click_example_imagepipe.egg-info/dependency_links.txt
click_example_imagepipe.egg-info/entry_points.txt
click_example_imagepipe.egg-info/requires.txt
click_example_imagepipe.egg-info/top_level.txt

View file

@ -0,0 +1,4 @@
[console_scripts]
imagepipe=imagepipe:cli

View file

@ -0,0 +1 @@
Click

View file

@ -0,0 +1 @@
imagepipe

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

View file

@ -0,0 +1,266 @@
import click
from functools import update_wrapper
from PIL import Image, ImageFilter, ImageEnhance
@click.group(chain=True)
def cli():
"""This script processes a bunch of images through pillow in a unix
pipe. One commands feeds into the next.
Example:
\b
imagepipe open -i example01.jpg resize -w 128 display
imagepipe open -i example02.jpg blur save
"""
@cli.resultcallback()
def process_commands(processors):
"""This result callback is invoked with an iterable of all the chained
subcommands. As in this example each subcommand returns a function
we can chain them together to feed one into the other, similar to how
a pipe on unix works.
"""
# Start with an empty iterable.
stream = ()
# Pipe it through all stream processors.
for processor in processors:
stream = processor(stream)
# Evaluate the stream and throw away the items.
for _ in stream:
pass
def processor(f):
"""Helper decorator to rewrite a function so that it returns another
function from it.
"""
def new_func(*args, **kwargs):
def processor(stream):
return f(stream, *args, **kwargs)
return processor
return update_wrapper(new_func, f)
def generator(f):
"""Similar to the :func:`processor` but passes through old values
unchanged and does not pass through the values as parameter.
"""
@processor
def new_func(stream, *args, **kwargs):
for item in stream:
yield item
for item in f(*args, **kwargs):
yield item
return update_wrapper(new_func, f)
def copy_filename(new, old):
new.filename = old.filename
return new
@cli.command('open')
@click.option('-i', '--image', 'images', type=click.Path(),
multiple=True, help='The image file to open.')
@generator
def open_cmd(images):
"""Loads one or multiple images for processing. The input parameter
can be specified multiple times to load more than one image.
"""
for image in images:
try:
click.echo('Opening "%s"' % image)
if image == '-':
img = Image.open(click.get_binary_stdin())
img.filename = '-'
else:
img = Image.open(image)
yield img
except Exception as e:
click.echo('Could not open image "%s": %s' % (image, e), err=True)
@cli.command('save')
@click.option('--filename', default='processed-%04d.png', type=click.Path(),
help='The format for the filename.',
show_default=True)
@processor
def save_cmd(images, filename):
"""Saves all processed images to a series of files."""
for idx, image in enumerate(images):
try:
fn = filename % (idx + 1)
click.echo('Saving "%s" as "%s"' % (image.filename, fn))
yield image.save(fn)
except Exception as e:
click.echo('Could not save image "%s": %s' %
(image.filename, e), err=True)
@cli.command('display')
@processor
def display_cmd(images):
"""Opens all images in an image viewer."""
for image in images:
click.echo('Displaying "%s"' % image.filename)
image.show()
yield image
@cli.command('resize')
@click.option('-w', '--width', type=int, help='The new width of the image.')
@click.option('-h', '--height', type=int, help='The new height of the image.')
@processor
def resize_cmd(images, width, height):
"""Resizes an image by fitting it into the box without changing
the aspect ratio.
"""
for image in images:
w, h = (width or image.size[0], height or image.size[1])
click.echo('Resizing "%s" to %dx%d' % (image.filename, w, h))
image.thumbnail((w, h))
yield image
@cli.command('crop')
@click.option('-b', '--border', type=int, help='Crop the image from all '
'sides by this amount.')
@processor
def crop_cmd(images, border):
"""Crops an image from all edges."""
for image in images:
box = [0, 0, image.size[0], image.size[1]]
if border is not None:
for idx, val in enumerate(box):
box[idx] = max(0, val - border)
click.echo('Cropping "%s" by %dpx' % (image.filename, border))
yield copy_filename(image.crop(box), image)
else:
yield image
def convert_rotation(ctx, param, value):
if value is None:
return
value = value.lower()
if value in ('90', 'r', 'right'):
return (Image.ROTATE_90, 90)
if value in ('180', '-180'):
return (Image.ROTATE_180, 180)
if value in ('-90', '270', 'l', 'left'):
return (Image.ROTATE_270, 270)
raise click.BadParameter('invalid rotation "%s"' % value)
def convert_flip(ctx, param, value):
if value is None:
return
value = value.lower()
if value in ('lr', 'leftright'):
return (Image.FLIP_LEFT_RIGHT, 'left to right')
if value in ('tb', 'topbottom', 'upsidedown', 'ud'):
return (Image.FLIP_LEFT_RIGHT, 'top to bottom')
raise click.BadParameter('invalid flip "%s"' % value)
@cli.command('transpose')
@click.option('-r', '--rotate', callback=convert_rotation,
help='Rotates the image (in degrees)')
@click.option('-f', '--flip', callback=convert_flip,
help='Flips the image [LR / TB]')
@processor
def transpose_cmd(images, rotate, flip):
"""Transposes an image by either rotating or flipping it."""
for image in images:
if rotate is not None:
mode, degrees = rotate
click.echo('Rotate "%s" by %ddeg' % (image.filename, degrees))
image = copy_filename(image.transpose(mode), image)
if flip is not None:
mode, direction = flip
click.echo('Flip "%s" %s' % (image.filename, direction))
image = copy_filename(image.transpose(mode), image)
yield image
@cli.command('blur')
@click.option('-r', '--radius', default=2, show_default=True,
help='The blur radius.')
@processor
def blur_cmd(images, radius):
"""Applies gaussian blur."""
blur = ImageFilter.GaussianBlur(radius)
for image in images:
click.echo('Blurring "%s" by %dpx' % (image.filename, radius))
yield copy_filename(image.filter(blur), image)
@cli.command('smoothen')
@click.option('-i', '--iterations', default=1, show_default=True,
help='How many iterations of the smoothen filter to run.')
@processor
def smoothen_cmd(images, iterations):
"""Applies a smoothening filter."""
for image in images:
click.echo('Smoothening "%s" %d time%s' %
(image.filename, iterations, iterations != 1 and 's' or '',))
for x in xrange(iterations):
image = copy_filename(image.filter(ImageFilter.BLUR), image)
yield image
@cli.command('emboss')
@processor
def emboss_cmd(images):
"""Embosses an image."""
for image in images:
click.echo('Embossing "%s"' % image.filename)
yield copy_filename(image.filter(ImageFilter.EMBOSS), image)
@cli.command('sharpen')
@click.option('-f', '--factor', default=2.0,
help='Sharpens the image.', show_default=True)
@processor
def sharpen_cmd(images, factor):
"""Sharpens an image."""
for image in images:
click.echo('Sharpen "%s" by %f' % (image.filename, factor))
enhancer = ImageEnhance.Sharpness(image)
yield copy_filename(enhancer.enhance(max(1.0, factor)), image)
@cli.command('paste')
@click.option('-l', '--left', default=0, help='Offset from left.')
@click.option('-r', '--right', default=0, help='Offset from right.')
@processor
def paste_cmd(images, left, right):
"""Pastes the second image on the first image and leaves the rest
unchanged.
"""
imageiter = iter(images)
image = next(imageiter, None)
to_paste = next(imageiter, None)
if to_paste is None:
if image is not None:
yield image
return
click.echo('Paste "%s" on "%s"' %
(to_paste.filename, image.filename))
mask = None
if to_paste.mode == 'RGBA' or 'transparency' in to_paste.info:
mask = to_paste
image.paste(to_paste, (left, right), mask)
image.filename += '+' + to_paste.filename
yield image
for image in imageiter:
yield image

View file

@ -0,0 +1,16 @@
from setuptools import setup
setup(
name='click-example-imagepipe',
version='1.0',
py_modules=['imagepipe'],
include_package_data=True,
install_requires=[
'Click',
'pillow',
],
entry_points='''
[console_scripts]
imagepipe=imagepipe:cli
''',
)

10
examples/inout/README Normal file
View file

@ -0,0 +1,10 @@
$ inout_
inout is a simple example of an application that
can read from files and write to files but also
accept input from stdin or write to stdout.
Usage:
$ pip install --editable .
$ inout input_file.txt output_file.txt

30
examples/inout/inout.py Normal file
View file

@ -0,0 +1,30 @@
import click
@click.command()
@click.argument('input', type=click.File('rb'), nargs=-1)
@click.argument('output', type=click.File('wb'))
def cli(input, output):
"""This script works similar to the Unix `cat` command but it writes
into a specific file (which could be the standard output as denoted by
the ``-`` sign).
\b
Copy stdin to stdout:
inout - -
\b
Copy foo.txt and bar.txt to stdout:
inout foo.txt bar.txt -
\b
Write stdin into the file foo.txt
inout - foo.txt
"""
for f in input:
while True:
chunk = f.read(1024)
if not chunk:
break
output.write(chunk)
output.flush()

15
examples/inout/setup.py Normal file
View file

@ -0,0 +1,15 @@
from setuptools import setup
setup(
name='click-example-inout',
version='0.1',
py_modules=['inout'],
include_package_data=True,
install_requires=[
'Click',
],
entry_points='''
[console_scripts]
inout=inout:cli
''',
)

14
examples/naval/README Normal file
View file

@ -0,0 +1,14 @@
$ naval_
naval is a simple example of an application that
is ported from the docopt example of the same name.
Unlike the original this one also runs some code and
prints messages and it's command line interface was
changed slightly to make more sense with established
POSIX semantics.
Usage:
$ pip install --editable .
$ naval --help

70
examples/naval/naval.py Normal file
View file

@ -0,0 +1,70 @@
import click
@click.group()
@click.version_option()
def cli():
"""Naval Fate.
This is the docopt example adopted to Click but with some actual
commands implemented and not just the empty parsing which really
is not all that interesting.
"""
@cli.group()
def ship():
"""Manages ships."""
@ship.command('new')
@click.argument('name')
def ship_new(name):
"""Creates a new ship."""
click.echo('Created ship %s' % name)
@ship.command('move')
@click.argument('ship')
@click.argument('x', type=float)
@click.argument('y', type=float)
@click.option('--speed', metavar='KN', default=10,
help='Speed in knots.')
def ship_move(ship, x, y, speed):
"""Moves SHIP to the new location X,Y."""
click.echo('Moving ship %s to %s,%s with speed %s' % (ship, x, y, speed))
@ship.command('shoot')
@click.argument('ship')
@click.argument('x', type=float)
@click.argument('y', type=float)
def ship_shoot(ship, x, y):
"""Makes SHIP fire to X,Y."""
click.echo('Ship %s fires to %s,%s' % (ship, x, y))
@cli.group('mine')
def mine():
"""Manages mines."""
@mine.command('set')
@click.argument('x', type=float)
@click.argument('y', type=float)
@click.option('ty', '--moored', flag_value='moored',
default=True,
help='Moored (anchored) mine. Default.')
@click.option('ty', '--drifting', flag_value='drifting',
help='Drifting mine.')
def mine_set(x, y, ty):
"""Sets a mine at a specific coordinate."""
click.echo('Set %s mine at %s,%s' % (ty, x, y))
@mine.command('remove')
@click.argument('x', type=float)
@click.argument('y', type=float)
def mine_remove(x, y):
"""Removes a mine at a specific coordinate."""
click.echo('Removed mine at %s,%s' % (x, y))

15
examples/naval/setup.py Normal file
View file

@ -0,0 +1,15 @@
from setuptools import setup
setup(
name='click-example-naval',
version='2.0',
py_modules=['naval'],
include_package_data=True,
install_requires=[
'Click',
],
entry_points='''
[console_scripts]
naval=naval:cli
''',
)

9
examples/repo/README Normal file
View file

@ -0,0 +1,9 @@
$ repo_
repo is a simple example of an application that looks
and works similar to hg or git.
Usage:
$ pip install --editable .
$ repo --help

151
examples/repo/repo.py Normal file
View file

@ -0,0 +1,151 @@
import os
import sys
import posixpath
import click
class Repo(object):
def __init__(self, home):
self.home = home
self.config = {}
self.verbose = False
def set_config(self, key, value):
self.config[key] = value
if self.verbose:
click.echo(' config[%s] = %s' % (key, value), file=sys.stderr)
def __repr__(self):
return '<Repo %r>' % self.home
pass_repo = click.make_pass_decorator(Repo)
@click.group()
@click.option('--repo-home', envvar='REPO_HOME', default='.repo',
metavar='PATH', help='Changes the repository folder location.')
@click.option('--config', nargs=2, multiple=True,
metavar='KEY VALUE', help='Overrides a config key/value pair.')
@click.option('--verbose', '-v', is_flag=True,
help='Enables verbose mode.')
@click.version_option('1.0')
@click.pass_context
def cli(ctx, repo_home, config, verbose):
"""Repo is a command line tool that showcases how to build complex
command line interfaces with Click.
This tool is supposed to look like a distributed version control
system to show how something like this can be structured.
"""
# Create a repo object and remember it as as the context object. From
# this point onwards other commands can refer to it by using the
# @pass_repo decorator.
ctx.obj = Repo(os.path.abspath(repo_home))
ctx.obj.verbose = verbose
for key, value in config:
ctx.obj.set_config(key, value)
@cli.command()
@click.argument('src')
@click.argument('dest', required=False)
@click.option('--shallow/--deep', default=False,
help='Makes a checkout shallow or deep. Deep by default.')
@click.option('--rev', '-r', default='HEAD',
help='Clone a specific revision instead of HEAD.')
@pass_repo
def clone(repo, src, dest, shallow, rev):
"""Clones a repository.
This will clone the repository at SRC into the folder DEST. If DEST
is not provided this will automatically use the last path component
of SRC and create that folder.
"""
if dest is None:
dest = posixpath.split(src)[-1] or '.'
click.echo('Cloning repo %s to %s' % (src, os.path.abspath(dest)))
repo.home = dest
if shallow:
click.echo('Making shallow checkout')
click.echo('Checking out revision %s' % rev)
@cli.command()
@click.confirmation_option()
@pass_repo
def delete(repo):
"""Deletes a repository.
This will throw away the current repository.
"""
click.echo('Destroying repo %s' % repo.home)
click.echo('Deleted!')
@cli.command()
@click.option('--username', prompt=True,
help='The developer\'s shown username.')
@click.option('--email', prompt='E-Mail',
help='The developer\'s email address')
@click.password_option(help='The login password.')
@pass_repo
def setuser(repo, username, email, password):
"""Sets the user credentials.
This will override the current user config.
"""
repo.set_config('username', username)
repo.set_config('email', email)
repo.set_config('password', '*' * len(password))
click.echo('Changed credentials.')
@cli.command()
@click.option('--message', '-m', multiple=True,
help='The commit message. If provided multiple times each '
'argument gets converted into a new line.')
@click.argument('files', nargs=-1, type=click.Path())
@pass_repo
def commit(repo, files, message):
"""Commits outstanding changes.
Commit changes to the given files into the repository. You will need to
"repo push" to push up your changes to other repositories.
If a list of files is omitted, all changes reported by "repo status"
will be committed.
"""
if not message:
marker = '# Files to be committed:'
hint = ['', '', marker, '#']
for file in files:
hint.append('# U %s' % file)
message = click.edit('\n'.join(hint))
if message is None:
click.echo('Aborted!')
return
msg = message.split(marker)[0].rstrip()
if not msg:
click.echo('Aborted! Empty commit message')
return
else:
msg = '\n'.join(message)
click.echo('Files to be committed: %s' % (files,))
click.echo('Commit message:\n' + msg)
@cli.command(short_help='Copies files.')
@click.option('--force', is_flag=True,
help='forcibly copy over an existing managed file')
@click.argument('src', nargs=-1, type=click.Path())
@click.argument('dst', type=click.Path())
@pass_repo
def copy(repo, src, dst, force):
"""Copies one or multiple files to a new location. This copies all
files from SRC to DST.
"""
for fn in src:
click.echo('Copy from %s -> %s' % (fn, dst))

15
examples/repo/setup.py Normal file
View file

@ -0,0 +1,15 @@
from setuptools import setup
setup(
name='click-example-repo',
version='0.1',
py_modules=['repo'],
include_package_data=True,
install_requires=[
'Click',
],
entry_points='''
[console_scripts]
repo=repo:cli
''',
)

Some files were not shown because too many files have changed in this diff Show more