diff --git a/.appveyor.yml b/.appveyor.yml deleted file mode 100644 index 1201593..0000000 --- a/.appveyor.yml +++ /dev/null @@ -1,28 +0,0 @@ -environment: - global: - TOXENV: py,codecov - - matrix: - - PYTHON: C:\Python36-x64 - - PYTHON: C:\Python27-x64 - - PYTHON: C:\Python36 - - PYTHON: C:\Python27 - -init: - - SET PATH=%PYTHON%;%PATH% - -install: - - python -m pip install -U tox - -build: false - -test_script: - - python -m tox - -branches: - only: - - master - - /^.*-maintenance$/ - -cache: - - '%LOCALAPPDATA%\pip\Cache' diff --git a/.coveragerc b/.coveragerc deleted file mode 100644 index 2876fb5..0000000 --- a/.coveragerc +++ /dev/null @@ -1,3 +0,0 @@ -[run] -branch = true -source = click,tests diff --git a/.gitignore b/.gitignore deleted file mode 100644 index fe562f2..0000000 --- a/.gitignore +++ /dev/null @@ -1,15 +0,0 @@ -.DS_Store -*.pyc -*.pyo -*.egg-ignore -*.egg-info -.pytest_cache -dist -build -docs/_build -click.egg-info -venv/ -.tox -.cache -.ropeproject -.idea diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 55deb45..0000000 --- a/.travis.yml +++ /dev/null @@ -1,56 +0,0 @@ -os: linux -sudo: false -language: python - -matrix: - include: - - python: 3.6 - env: TOXENV=py,codecov - - python: 3.5 - env: TOXENV=py,codecov - - python: 3.4 - env: TOXENV=py,codecov - - python: 2.7 - env: TOXENV=py,codecov - - python: pypy3 - env: TOXENV=py,codecov - - python: nightly - env: TOXENV=py - - os: osx - language: generic - env: TOXENV=py3,py2,codecov - cache: - pip: false - directories: - - $HOME/Library/Caches/Homebrew - - $HOME/Library/Caches/pip - allow_failures: - - python: pypy3 - - python: nightly - - os: osx - fast_finish: true - -before_install: - - | - if [[ $TRAVIS_OS_NAME == 'osx' ]]; then - brew upgrade python - brew install python@2 - export PATH="/usr/local/opt/python/libexec/bin:${PATH}" - fi - -install: - - pip install tox - -script: - - tox - -cache: - - pip - -branches: - only: - - master - - /^.*-maintenance/ - -notifications: - email: false diff --git a/CHANGES.rst b/CHANGES.rst index 8eaee82..a98fabd 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,216 +1,293 @@ Click Changelog =============== -This contains all major version changes between Click releases. Version 7.0 ----------- -(upcoming release with new features, release date to be decided) +Released 2018-09-25 -- Non-standalone calls to Context.exit return the exit code, rather than - calling ``sys.exit`` (`#667`_)(`#533`_) -- Updated test env matrix. (`#1027`_) -- Fixes a ``ZeroDivisionError`` in ``ProgressBar.make_step``, - when the arg passed to the first call of ``ProgressBar.update`` is 0. (`#1012`_)(`#447`_) -- Document that options can be ``required=True``. (`#1022`_)(`#514`_) -- Fix path validation bug. (`#1020`_)(`#795`_) -- Document customizing option names. (`#1016`_)(`#725`_) -- Wrap ``click.Choice``'s missing message. (`#1000`_)(`#202`_) -- Don't add newlines by default for progressbars. (`#1013`_) -- Document how ``auto_envar_prefix`` works with command groups. (`#1011`_) -- Fix failing bash completion function test signature. -- Clarify how paramteres are named. (`#1009`_)(`#949`_) -- Document bytestripping behavior of ``CliRunner``. (`#1010`_)(`#334`_) -- Fix Google App Engine ``ImportError``. (`#995`_) -- Document that ANSI color info isn't parsedfrom bytearrays in Python 2. (`#334`_) -- Add note to documentation on how parameters are named. -- Fix formatting for short help. (`#1008`_) -- Extract bar formatting to its own method. (`#414`_) -- Move ``fcntl`` import. (`#965`_) -- Fixed issues where ``fd`` was undefined. (`#1007`_) -- Added deprecation flag to commands. (`#1005`_) -- Fix various Sphinx errors. (`#883`_) -- Add ``case_sensitive=False`` as an option to Choice types. (`#887`_) -- Add details about Python version support. (`#1004`_) -- Clarify documentation on command line options. (`#1003`_)(`#741`_) -- Add ``case_sensitive=False`` as an option to Choice. (`#569`_) -- Better handling of help text for dynamic default option values. (`#996`_) -- Allow short width to address cmd formatting. (`#1002`_) -- Add test case checking for custom param type. (`#1001`_) -- Make ``Argument.make_metavar()`` default to type metavar. (`#675`_) -- Show progressbar only if total execution time is visible. (`#487`_) -- Allow setting ``prog_name`` as extra in ``CliRunner.invoke`` (`#999`_)(`#616`_) -- Add support for Sphinx 1.7+ (`#991`_) -- Fix ``get_winter_size()`` so it correctly returns ``(0,0)``. (`#997`_) -- Update progress after iteration. (`#706`_)(`#651`_) -- Add ``show_envvar`` for showing environment variables in help. (`#710`_) -- Add support for bright colors. (`#809`_) -- Add documentation for ``ignore_unkown_options``. (`#684`_) -- Allow ``CliRunner`` to separate stdout and stderr. (`#868`_) -- Implement streaming pager. (`#889`_)(`#409`_) -- Progress bar now uses stderr by default. (`#863`_) -- Do not set options twice. (`#962`_) -- Add Py2/ unicode / str compatability for doc tools. (`#993`_)(`#719`_) -- Add copy option attrs so that custom classes can be re-used. (`#994`_)(`#926`_) -- ``param_hint`` in errors now derived from param itself. (`#709`_)(`#704`_)(`#598`_) -- Add a test that ensures that when an Argument is formatted into a usage error, - its metavar is used, not its name. (`#612`_) -- Fix variable precedence. (`#874`_)(`#873`_) -- Fix ``ResourceWarning`` that occurs during some tests. (`#878`_) -- Update README to match flask style and add ``long_description`` to setup.py. (`#990`_) -- Drop testing for 2.6 3.3 and 3.6. -- Make locale optional (`#880`_) -- Fix invalid escape sequences. (`#877`_) -- Added workaround for jupyter. (`#918`_) -- x and a filemodes now use stdout when file is ``'-'``. (`#929`_) -- ``_AtomicFile`` now uses the realpath of the original filename. (`#920`_) -- Fix missing comma in ``__all__`` list (`#935`_) -- Raw strings added so correct escaping occurs. (`#807`_) -- Add bool conversion for ``t`` and ``f``. (`#842`_) -- Update doc to match arg name for ``path_type``. (`#801`_) -- Add bright colors support for ``click.style`` - and fix the reset option for parameters ``fg`` and ``bg``. (`#703`_) -- Add test and documentation for ``Option`` naming: functionality. (`#799`_) -- Use deterministic option name; can't rely on list sort. (`#794`_)(`#793`_) -- Added support for bash completions containing spaces. (`#773`_) -- Added support for dynamic bash completion from a user-supplied callback. - (`#755`_) -- Added support for bash completion of ``type=click.Choice`` for ``Options`` and - ``Arguments``. (`#535`_) -- The user is now presented with the available choices if ``prompt=True`` and - ``type=click.Choice`` in a ``click.option``. The choices are listed within - parenthesis like ``'Choose fruit (apple, orange): '``. -- The exception objects now store unicode properly. -- Added the ability to hide commands and options from help. -- Added Float Range in Types. -- ``secho``'s first argument can now be ``None``, like in ``echo``. -- Usage errors now hint at the ``--help`` option. -- ``launch`` now works properly under Cygwin. (`#650`_) -- ``CliRunner.invoke`` now may receive ``args`` as a string representing - a Unix shell command. See (`#664`_). -- Fix bug that caused bashcompletion to give improper completions on - chained commands. (`#774`_) -- Add support for bright colors. -- ``'t'`` and ``'f'`` are now converted to ``True`` and ``False``. -- Fix bug that caused bashcompletion to give improper completions on - chained commands when a required option/argument was being completed. - (`#790`_)(`#806`_) -- Allow autocompletion function to determine whether or not to return - completions that start with the incomplete argument. -- Add native ZSH autocompletion support. (`#323`_)(`#865`_) -- Add support for auto-completion documentation. See (`#866`_)(`#869`_) -- Subcommands that are named by the function now automatically have the - underscore replaced with a dash. So if you register a function named - ``my_command`` it becomes ``my-command`` in the command line interface. -- Stdout is now automatically set to non blocking. -- Use realpath to convert atomic file internally into its full canonical - path so that changing working directories does not harm it. -- Force stdout/stderr writable. This works around issues with badly patched - standard streams like those from jupyter. +- Drop support for Python 2.6 and 3.3. (`#967`_, `#976`_) +- Wrap ``click.Choice``'s missing message. (`#202`_, `#1000`_) +- Add native ZSH autocompletion support. (`#323`_, `#865`_) +- Document that ANSI color info isn't parsed from bytearrays in + Python 2. (`#334`_) +- Document byte-stripping behavior of ``CliRunner``. (`#334`_, + `#1010`_) +- Usage errors now hint at the ``--help`` option. (`#393`_, `#557`_) +- Implement streaming pager. (`#409`_, `#889`_) +- Extract bar formatting to its own method. (`#414`_) +- Add ``DateTime`` type for converting input in given date time + formats. (`#423`_) +- ``secho``'s first argument can now be ``None``, like in ``echo``. + (`#424`_) +- Fixes a ``ZeroDivisionError`` in ``ProgressBar.make_step``, when the + arg passed to the first call of ``ProgressBar.update`` is 0. + (`#447`_, `#1012`_) +- Show progressbar only if total execution time is visible. (`#487`_) +- Added the ability to hide commands and options from help. (`#500`_) +- Document that options can be ``required=True``. (`#514`_, `#1022`_) +- Non-standalone calls to ``Context.exit`` return the exit code, + rather than calling ``sys.exit``. (`#533`_, `#667`_, `#1098`_) +- ``click.getchar()`` returns Unicode in Python 3 on Windows, + consistent with other platforms. (`#537`_, `#821`_, `#822`_, + `#1088`_, `#1108`_) +- Added ``FloatRange`` type. (`#538`_, `#553`_) +- Added support for bash completion of ``type=click.Choice`` for + ``Options`` and ``Arguments``. (`#535`_, `#681`_) +- Only allow one positional arg for ``Argument`` parameter + declaration. (`#568`_, `#574`_, `#1014`_) +- Add ``case_sensitive=False`` as an option to Choice. (`#569`_) +- ``click.getchar()`` correctly raises ``KeyboardInterrupt`` on "^C" + and ``EOFError`` on "^D" on Linux. (`#583`_, `#1115`_) +- Fix encoding issue with ``click.getchar(echo=True)`` on Linux. + (`#1115`_) +- ``param_hint`` in errors now derived from param itself. (`#598`_, + `#704`_, `#709`_) +- Add a test that ensures that when an argument is formatted into a + usage error, its metavar is used, not its name. (`#612`_) +- Allow setting ``prog_name`` as extra in ``CliRunner.invoke``. + (`#616`_, `#999`_) +- Help text taken from docstrings truncates at the ``\f`` form feed + character, useful for hiding Sphinx-style parameter documentation. + (`#629`_, `#1091`_) +- ``launch`` now works properly under Cygwin. (`#650`_) +- Update progress after iteration. (`#651`_, `#706`_) +- ``CliRunner.invoke`` now may receive ``args`` as a string + representing a Unix shell command. (`#664`_) +- Make ``Argument.make_metavar()`` default to type metavar. (`#675`_) +- Add documentation for ``ignore_unknown_options``. (`#684`_) +- Add bright colors support for ``click.style`` and fix the reset + option for parameters ``fg`` and ``bg``. (`#703`_, `#809`_) +- Add ``show_envvar`` for showing environment variables in help. + (`#710`_) +- Avoid ``BrokenPipeError`` during interpreter shutdown when stdout or + stderr is a closed pipe. (`#712`_, `#1106`_) +- Document customizing option names. (`#725`_, `#1016`_) +- Disable ``sys._getframes()`` on Python interpreters that don't + support it. (`#728`_) +- Fix bug in test runner when calling ``sys.exit`` with ``None``. + (`#739`_) +- Clarify documentation on command line options. (`#741`_, `#1003`_) +- Fix crash on Windows console. (`#744`_) +- Fix bug that caused bash completion to give improper completions on + chained commands. (`#754`_, `#774`_) +- Added support for dynamic bash completion from a user-supplied + callback. (`#755`_) +- Added support for bash completions containing spaces. (`#773`_) +- Allow autocompletion function to determine whether or not to return + completions that start with the incomplete argument. (`#790`_, + `#806`_) +- Fix option naming routine to match documentation and be + deterministic. (`#793`_, `#794`_) +- Fix path validation bug. (`#795`_, `#1020`_) +- Add test and documentation for ``Option`` naming: functionality. + (`#799`_) +- Update doc to match arg name for ``path_type``. (`#801`_) +- Raw strings added so correct escaping occurs. (`#807`_) +- Fix 16k character limit of ``click.echo`` on Windows. (`#816`_, + `#819`_) +- Overcome 64k character limit when writing to binary stream on + Windows 7. (`#825`_, `#830`_) +- Add bool conversion for "t" and "f". (`#842`_) +- ``NoSuchOption`` errors take ``ctx`` so that ``--help`` hint gets + printed in error output. (`#860`_) +- Fixed the behavior of Click error messages with regards to Unicode + on 2.x and 3.x. Message is now always Unicode and the str and + Unicode special methods work as you expect on that platform. + (`#862`_) +- Progress bar now uses stderr by default. (`#863`_) +- Add support for auto-completion documentation. (`#866`_, `#869`_) +- Allow ``CliRunner`` to separate stdout and stderr. (`#868`_) +- Fix variable precedence. (`#873`_, `#874`_) +- Fix invalid escape sequences. (`#877`_) +- Fix ``ResourceWarning`` that occurs during some tests. (`#878`_) +- When detecting a misconfigured locale, don't fail if the ``locale`` + command fails. (`#880`_) +- Add ``case_sensitive=False`` as an option to ``Choice`` types. + (`#887`_) +- Force stdout/stderr writable. This works around issues with badly + patched standard streams like those from Jupyter. (`#918`_) +- Fix completion of subcommand options after last argument (`#919`_, + `#930`_) +- ``_AtomicFile`` now uses the ``realpath`` of the original filename + so that changing the working directory does not affect it. + (`#920`_) +- Fix incorrect completions when defaults are present (`#925`_, + `#930`_) +- Add copy option attrs so that custom classes can be re-used. + (`#926`_, `#994`_) +- "x" and "a" file modes now use stdout when file is ``"-"``. + (`#929`_) +- Fix missing comma in ``__all__`` list. (`#935`_) +- Clarify how parameters are named. (`#949`_, `#1009`_) +- Stdout is now automatically set to non blocking. (`#954`_) +- Do not set options twice. (`#962`_) +- Move ``fcntl`` import. (`#965`_) +- Fix Google App Engine ``ImportError``. (`#995`_) +- Better handling of help text for dynamic default option values. + (`#996`_) +- Fix ``get_winter_size()`` so it correctly returns ``(0,0)``. + (`#997`_) +- Add test case checking for custom param type. (`#1001`_) +- Allow short width to address cmd formatting. (`#1002`_) +- Add details about Python version support. (`#1004`_) +- Added deprecation flag to commands. (`#1005`_) +- Fixed issues where ``fd`` was undefined. (`#1007`_) +- Fix formatting for short help. (`#1008`_) +- Document how ``auto_envvar_prefix`` works with command groups. + (`#1011`_) +- Don't add newlines by default for progress bars. (`#1013`_) +- Use Python sorting order for ZSH completions. (`#1047`_, `#1059`_) +- Document that parameter names are converted to lowercase by default. + (`#1055`_) +- Subcommands that are named by the function now automatically have + the underscore replaced with a dash. If you register a function + named ``my_command`` it becomes ``my-command`` in the command line + interface. +- Hide hidden commands and options from completion. (`#1058`_, + `#1061`_) +- Fix absolute import blocking Click from being vendored into a + project on Windows. (`#1068`_, `#1069`_) +- Fix issue where a lowercase ``auto_envvar_prefix`` would not be + converted to uppercase. (`#1105`_) -.. _#1027: https://github.com/pallets/click/pull/1027 -.. _#1012: https://github.com/pallets/click/pull/1012 -.. _#447: https://github.com/pallets/click/issues/447 -.. _#1022: https://github.com/pallets/click/pull/1022 -.. _#869: https://github.com/pallets/click/pull/869 -.. _#866: https://github.com/pallets/click/issues/866 -.. _#514: https://github.com/pallets/click/issues/514 -.. _#1020: https://github.com/pallets/click/pull/1020 -.. _#795: https://github.com/pallets/click/issues/795 -.. _#1016: https://github.com/pallets/click/pull/1016 -.. _#725: https://github.com/pallets/click/issues/725 -.. _#1000: https://github.com/pallets/click/pull/1000 .. _#202: https://github.com/pallets/click/issues/202 -.. _#1013: https://github.com/pallets/click/pull/1013 -.. _#1011: https://github.com/pallets/click/pull/1011 -.. _#865: https://github.com/pallets/click/pull/865 .. _#323: https://github.com/pallets/click/issues/323 -.. _#1009: https://github.com/pallets/click/pull/1009 -.. _#949: https://github.com/pallets/click/issues/949 -.. _#1010: https://github.com/pallets/click/pull/1010 .. _#334: https://github.com/pallets/click/issues/334 -.. _#995: https://github.com/pallets/click/pull/995 -.. _#1008: https://github.com/pallets/click/pull/1008 +.. _#393: https://github.com/pallets/click/issues/393 +.. _#409: https://github.com/pallets/click/issues/409 .. _#414: https://github.com/pallets/click/pull/414 -.. _#965: https://github.com/pallets/click/pull/965 -.. _#1005: https://github.com/pallets/click/pull/1005 +.. _#423: https://github.com/pallets/click/pull/423 +.. _#424: https://github.com/pallets/click/pull/424 +.. _#447: https://github.com/pallets/click/issues/447 +.. _#487: https://github.com/pallets/click/pull/487 +.. _#500: https://github.com/pallets/click/pull/500 +.. _#514: https://github.com/pallets/click/issues/514 +.. _#533: https://github.com/pallets/click/pull/533 +.. _#535: https://github.com/pallets/click/issues/535 +.. _#537: https://github.com/pallets/click/issues/537 +.. _#538: https://github.com/pallets/click/pull/538 +.. _#553: https://github.com/pallets/click/pull/553 +.. _#557: https://github.com/pallets/click/pull/557 +.. _#568: https://github.com/pallets/click/issues/568 +.. _#569: https://github.com/pallets/click/issues/569 +.. _#574: https://github.com/pallets/click/issues/574 +.. _#583: https://github.com/pallets/click/issues/583 +.. _#598: https://github.com/pallets/click/issues/598 +.. _#612: https://github.com/pallets/click/pull/612 +.. _#616: https://github.com/pallets/click/issues/616 +.. _#629: https://github.com/pallets/click/pull/629 +.. _#650: https://github.com/pallets/click/pull/650 +.. _#651: https://github.com/pallets/click/issues/651 +.. _#664: https://github.com/pallets/click/pull/664 +.. _#667: https://github.com/pallets/click/issues/667 +.. _#675: https://github.com/pallets/click/pull/675 +.. _#681: https://github.com/pallets/click/pull/681 +.. _#684: https://github.com/pallets/click/pull/684 +.. _#703: https://github.com/pallets/click/issues/703 +.. _#704: https://github.com/pallets/click/issues/704 +.. _#706: https://github.com/pallets/click/pull/706 +.. _#709: https://github.com/pallets/click/pull/709 +.. _#710: https://github.com/pallets/click/pull/710 +.. _#712: https://github.com/pallets/click/pull/712 +.. _#719: https://github.com/pallets/click/issues/719 +.. _#725: https://github.com/pallets/click/issues/725 +.. _#728: https://github.com/pallets/click/pull/728 +.. _#739: https://github.com/pallets/click/pull/739 +.. _#741: https://github.com/pallets/click/issues/741 +.. _#744: https://github.com/pallets/click/issues/744 +.. _#754: https://github.com/pallets/click/issues/754 +.. _#755: https://github.com/pallets/click/pull/755 +.. _#773: https://github.com/pallets/click/pull/773 +.. _#774: https://github.com/pallets/click/pull/774 +.. _#790: https://github.com/pallets/click/issues/790 +.. _#793: https://github.com/pallets/click/issues/793 +.. _#794: https://github.com/pallets/click/pull/794 +.. _#795: https://github.com/pallets/click/issues/795 +.. _#799: https://github.com/pallets/click/pull/799 +.. _#801: https://github.com/pallets/click/pull/801 +.. _#806: https://github.com/pallets/click/pull/806 +.. _#807: https://github.com/pallets/click/pull/807 +.. _#809: https://github.com/pallets/click/pull/809 +.. _#816: https://github.com/pallets/click/pull/816 +.. _#819: https://github.com/pallets/click/pull/819 +.. _#821: https://github.com/pallets/click/issues/821 +.. _#822: https://github.com/pallets/click/issues/822 +.. _#825: https://github.com/pallets/click/issues/825 +.. _#830: https://github.com/pallets/click/pull/830 +.. _#842: https://github.com/pallets/click/pull/842 +.. _#860: https://github.com/pallets/click/issues/860 +.. _#862: https://github.com/pallets/click/issues/862 +.. _#863: https://github.com/pallets/click/pull/863 +.. _#865: https://github.com/pallets/click/pull/865 +.. _#866: https://github.com/pallets/click/issues/866 +.. _#868: https://github.com/pallets/click/pull/868 +.. _#869: https://github.com/pallets/click/pull/869 +.. _#873: https://github.com/pallets/click/issues/873 +.. _#874: https://github.com/pallets/click/pull/874 +.. _#877: https://github.com/pallets/click/pull/877 +.. _#878: https://github.com/pallets/click/pull/878 +.. _#880: https://github.com/pallets/click/pull/880 .. _#883: https://github.com/pallets/click/pull/883 .. _#887: https://github.com/pallets/click/pull/887 -.. _#1004: https://github.com/pallets/click/pull/1004 -.. _#1003: https://github.com/pallets/click/pull/1003 -.. _#741: https://github.com/pallets/click/issues/741 -.. _#569: https://github.com/pallets/click/pull/569 -.. _#1007: https://github.com/pallets/click/pull/1007 -.. _#996: https://github.com/pallets/click/pull/996 -.. _#1002: https://github.com/pallets/click/pull/1002 -.. _#1001: https://github.com/pallets/click/pull/1001 -.. _#675: https://github.com/pallets/click/pull/675 -.. _#487: https://github.com/pallets/click/pull/487 -.. _#999: https://github.com/pallets/click/pull/999 -.. _#616: https://github.com/pallets/click/issues/616 -.. _#991: https://github.com/pallets/click/pull/991 -.. _#997: https://github.com/pallets/click/pull/997 -.. _#706: https://github.com/pallets/click/pull/706 -.. _#651: https://github.com/pallets/click/issues/651 -.. _#710: https://github.com/pallets/click/pull/710 -.. _#809: https://github.com/pallets/click/pull/809 -.. _#868: https://github.com/pallets/click/pull/868 .. _#889: https://github.com/pallets/click/pull/889 -.. _#409: https://github.com/pallets/click/issues/409 -.. _#863: https://github.com/pallets/click/pull/863 +.. _#918: https://github.com/pallets/click/pull/918 +.. _#919: https://github.com/pallets/click/issues/919 +.. _#920: https://github.com/pallets/click/pull/920 +.. _#925: https://github.com/pallets/click/issues/925 +.. _#926: https://github.com/pallets/click/issues/926 +.. _#929: https://github.com/pallets/click/pull/929 +.. _#930: https://github.com/pallets/click/pull/930 +.. _#935: https://github.com/pallets/click/pull/935 +.. _#949: https://github.com/pallets/click/issues/949 +.. _#954: https://github.com/pallets/click/pull/954 .. _#962: https://github.com/pallets/click/pull/962 +.. _#965: https://github.com/pallets/click/pull/965 +.. _#967: https://github.com/pallets/click/pull/967 +.. _#976: https://github.com/pallets/click/pull/976 +.. _#990: https://github.com/pallets/click/pull/990 +.. _#991: https://github.com/pallets/click/pull/991 .. _#993: https://github.com/pallets/click/pull/993 .. _#994: https://github.com/pallets/click/pull/994 -.. _#926: https://github.com/pallets/click/issues/926 -.. _#709: https://github.com/pallets/click/pull/709 -.. _#612: https://github.com/pallets/click/pull/612 -.. _#704: https://github.com/pallets/click/issues/704 -.. _#598: https://github.com/pallets/click/issues/598 -.. _#719: https://github.com/pallets/click/issues/719 -.. _#874: https://github.com/pallets/click/pull/874 -.. _#873: https://github.com/pallets/click/issues/873 -.. _#990: https://github.com/pallets/click/pull/990 -.. _#684: https://github.com/pallets/click/pull/684 -.. _#878: https://github.com/pallets/click/pull/878 -.. _#880: https://github.com/pallets/click/issues/880 -.. _#877: https://github.com/pallets/click/pull/877 -.. _#918: https://github.com/pallets/click/pull/918 -.. _#929: https://github.com/pallets/click/pull/929 -.. _#920: https://github.com/pallets/click/pull/920 -.. _#935: https://github.com/pallets/click/pull/935 -.. _#807: https://github.com/pallets/click/pull/807 -.. _#806: https://github.com/pallets/click/pull/806 -.. _#842: https://github.com/pallets/click/pull/842 -.. _#801: https://github.com/pallets/click/pull/801 -.. _#703: https://github.com/pallets/click/issues/703 -.. _#799: https://github.com/pallets/click/pull/799 -.. _#794: https://github.com/pallets/click/pull/794 -.. _#793: https://github.com/pallets/click/issues/793 -.. _#773: https://github.com/pallets/click/pull/773 -.. _#755: https://github.com/pallets/click/pull/755 -.. _#535: https://github.com/pallets/click/pull/535 -.. _#650: https://github.com/pallets/click/pull/650 -.. _#664: https://github.com/pallets/click/pull/664 -.. _#774: https://github.com/pallets/click/pull/774 -.. _#790: https://github.com/pallets/click/pull/790 +.. _#995: https://github.com/pallets/click/pull/995 +.. _#996: https://github.com/pallets/click/pull/996 +.. _#997: https://github.com/pallets/click/pull/997 +.. _#999: https://github.com/pallets/click/pull/999 +.. _#1000: https://github.com/pallets/click/pull/1000 +.. _#1001: https://github.com/pallets/click/pull/1001 +.. _#1002: https://github.com/pallets/click/pull/1002 +.. _#1003: https://github.com/pallets/click/pull/1003 +.. _#1004: https://github.com/pallets/click/pull/1004 +.. _#1005: https://github.com/pallets/click/pull/1005 +.. _#1007: https://github.com/pallets/click/pull/1007 +.. _#1008: https://github.com/pallets/click/pull/1008 +.. _#1009: https://github.com/pallets/click/pull/1009 +.. _#1010: https://github.com/pallets/click/pull/1010 +.. _#1011: https://github.com/pallets/click/pull/1011 +.. _#1012: https://github.com/pallets/click/pull/1012 +.. _#1013: https://github.com/pallets/click/pull/1013 +.. _#1014: https://github.com/pallets/click/pull/1014 +.. _#1016: https://github.com/pallets/click/pull/1016 +.. _#1020: https://github.com/pallets/click/pull/1020 +.. _#1022: https://github.com/pallets/click/pull/1022 +.. _#1027: https://github.com/pallets/click/pull/1027 +.. _#1047: https://github.com/pallets/click/pull/1047 +.. _#1055: https://github.com/pallets/click/pull/1055 +.. _#1058: https://github.com/pallets/click/pull/1058 +.. _#1059: https://github.com/pallets/click/pull/1059 +.. _#1061: https://github.com/pallets/click/pull/1061 +.. _#1068: https://github.com/pallets/click/issues/1068 +.. _#1069: https://github.com/pallets/click/pull/1069 +.. _#1088: https://github.com/pallets/click/issues/1088 +.. _#1091: https://github.com/pallets/click/pull/1091 +.. _#1098: https://github.com/pallets/click/pull/1098 +.. _#1105: https://github.com/pallets/click/pull/1105 +.. _#1106: https://github.com/pallets/click/pull/1106 +.. _#1108: https://github.com/pallets/click/pull/1108 +.. _#1115: https://github.com/pallets/click/pull/1115 -Version 6.8 ------------ - -(bugfix release; yet to be released) - -- Disabled ``sys._getframes()`` on Python interpreters that don't support it. See - #728. -- Fix bug in test runner when calling ``sys.exit`` with ``None``. See #739. -- Fix crash on Windows console, see #744. -- Fix bashcompletion on chained commands. See #754. -- Fix option naming routine to match documentation. See #793 -- Fixed the behavior of click error messages with regards to unicode on 2.x - and 3.x respectively. Message is now always unicode and the str and unicode - special methods work as you expect on that platform. - Version 6.7 ----------- diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 99b493a..4db9b65 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -7,8 +7,8 @@ Thanks for considering contributing to Click. Support questions ================= -Please, don't use the issue tracker for this. Check whether the `Pocoo IRC -channel `_ can help with your issue. If your problem +Please, don't use the issue tracker for this. Check whether the +``#pocoo`` IRC channel on Freenode can help with your issue. If your problem is not strictly Click-specific, ``#python`` on Freenode is generally more active. `StackOverflow `_ is also worth considering. @@ -33,9 +33,9 @@ Submitting patches may ignore the line-length-limit if following it would make the code uglier. - For features: Consider whether your feature would be a better fit for an - `external package `_ + `external package `_ -- For bugfixes: Submit against the latest maintenance branch instead of master! +- For docs and bug fixes: Submit against the latest maintenance branch instead of master! Running the testsuite --------------------- diff --git a/Click.egg-info/PKG-INFO b/Click.egg-info/PKG-INFO new file mode 100644 index 0000000..fa75a91 --- /dev/null +++ b/Click.egg-info/PKG-INFO @@ -0,0 +1,119 @@ +Metadata-Version: 1.2 +Name: Click +Version: 7.0 +Summary: Composable command line interface toolkit +Home-page: https://palletsprojects.com/p/click/ +Author: Armin Ronacher +Author-email: armin.ronacher@active-4.com +Maintainer: Pallets Team +Maintainer-email: contact@palletsprojects.com +License: BSD +Project-URL: Documentation, https://click.palletsprojects.com/ +Project-URL: Code, https://github.com/pallets/click +Project-URL: Issue tracker, https://github.com/pallets/click/issues +Description: \$ 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 + + + Installing + ---------- + + Install and update using `pip`_: + + .. code-block:: text + + $ pip install click + + Click supports Python 3.4 and newer, Python 2.7, and PyPy. + + .. _pip: https://pip.pypa.io/en/stable/quickstart/ + + + A Simple Example + ---------------- + + What does it look like? Here is an example of a simple Click program: + + .. code-block:: python + + 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 _ in range(count): + click.echo("Hello, %s!" % name) + + if __name__ == '__main__': + hello() + + And what it looks like when run: + + .. code-block:: text + + $ python hello.py --count=3 + Your name: Click + Hello, Click! + Hello, Click! + Hello, Click! + + + Donate + ------ + + The Pallets organization develops and supports Click and other popular + packages. In order to grow the community of contributors and users, and + allow the maintainers to devote more time to the projects, `please + donate today`_. + + .. _please donate today: https://palletsprojects.com/donate + + + Links + ----- + + * Website: https://palletsprojects.com/p/click/ + * Documentation: https://click.palletsprojects.com/ + * License: `BSD `_ + * Releases: https://pypi.org/project/click/ + * Code: https://github.com/pallets/click + * Issue tracker: https://github.com/pallets/click/issues + * Test status: + + * Linux, Mac: https://travis-ci.org/pallets/click + * Windows: https://ci.appveyor.com/project/pallets/click + + * Test coverage: https://codecov.io/gh/pallets/click + +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.* diff --git a/Click.egg-info/SOURCES.txt b/Click.egg-info/SOURCES.txt new file mode 100644 index 0000000..f67c6d1 --- /dev/null +++ b/Click.egg-info/SOURCES.txt @@ -0,0 +1,115 @@ +CHANGES.rst +CONTRIBUTING.rst +LICENSE.rst +MANIFEST.in +README.rst +setup.cfg +setup.py +tox.ini +Click.egg-info/PKG-INFO +Click.egg-info/SOURCES.txt +Click.egg-info/dependency_links.txt +Click.egg-info/top_level.txt +artwork/logo.svg +click/__init__.py +click/_bashcomplete.py +click/_compat.py +click/_termui_impl.py +click/_textwrap.py +click/_unicodefun.py +click/_winconsole.py +click/core.py +click/decorators.py +click/exceptions.py +click/formatting.py +click/globals.py +click/parser.py +click/termui.py +click/testing.py +click/types.py +click/utils.py +docs/Makefile +docs/advanced.rst +docs/api.rst +docs/arguments.rst +docs/bashcomplete.rst +docs/changelog.rst +docs/commands.rst +docs/complex.rst +docs/conf.py +docs/contrib.rst +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/requirements.txt +docs/setuptools.rst +docs/testing.rst +docs/upgrading.rst +docs/utils.rst +docs/why.rst +docs/wincmd.rst +docs/_static/click-icon.png +docs/_static/click-logo-sidebar.png +docs/_static/click-logo.png +examples/README +examples/aliases/README +examples/aliases/aliases.ini +examples/aliases/aliases.py +examples/aliases/setup.py +examples/bashcompletion/README +examples/bashcompletion/bashcompletion.py +examples/bashcompletion/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/.gitignore +examples/imagepipe/README +examples/imagepipe/example01.jpg +examples/imagepipe/example02.jpg +examples/imagepipe/imagepipe.py +examples/imagepipe/setup.py +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_bashcomplete.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 \ No newline at end of file diff --git a/Click.egg-info/dependency_links.txt b/Click.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/Click.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/Click.egg-info/top_level.txt b/Click.egg-info/top_level.txt new file mode 100644 index 0000000..dca9a90 --- /dev/null +++ b/Click.egg-info/top_level.txt @@ -0,0 +1 @@ +click diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 1704daa..0000000 --- a/LICENSE +++ /dev/null @@ -1,38 +0,0 @@ -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. diff --git a/LICENSE.rst b/LICENSE.rst new file mode 100644 index 0000000..87ce152 --- /dev/null +++ b/LICENSE.rst @@ -0,0 +1,39 @@ +Copyright © 2014 by the Pallets team. + +Some rights reserved. + +Redistribution and use in source and binary forms of the software as +well as documentation, 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. + +- Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE AND DOCUMENTATION 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 HOLDER 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 AND DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +---- + +Click uses parts of optparse written by Gregory P. Ward and maintained +by the Python Software Foundation. This is limited to code in parser.py. + +Copyright © 2001-2006 Gregory P. Ward. All rights reserved. +Copyright © 2002-2006 Python Software Foundation. All rights reserved. diff --git a/MANIFEST.in b/MANIFEST.in index 15458cf..33a336f 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,9 +1,11 @@ -include Makefile CHANGES LICENSE - +include CHANGES.rst +include CONTRIBUTING.rst +include LICENSE.rst +include README.rst +include tox.ini graft artwork -graft tests -graft examples graft docs prune docs/_build - +graft examples +graft tests global-exclude *.py[co] .DS_Store diff --git a/Makefile b/Makefile deleted file mode 100644 index 971e401..0000000 --- a/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -test: - @cd tests; PYTHONPATH=.. pytest --tb=short - -upload-docs: - $(MAKE) -C docs dirhtml - rsync -a docs/_build/dirhtml/* flow.srv.pocoo.org:/srv/websites/click.pocoo.org/static/ - -release: - python setup.py sdist bdist_wheel upload - -.PHONY: upload-docs diff --git a/PKG-INFO b/PKG-INFO new file mode 100644 index 0000000..fa75a91 --- /dev/null +++ b/PKG-INFO @@ -0,0 +1,119 @@ +Metadata-Version: 1.2 +Name: Click +Version: 7.0 +Summary: Composable command line interface toolkit +Home-page: https://palletsprojects.com/p/click/ +Author: Armin Ronacher +Author-email: armin.ronacher@active-4.com +Maintainer: Pallets Team +Maintainer-email: contact@palletsprojects.com +License: BSD +Project-URL: Documentation, https://click.palletsprojects.com/ +Project-URL: Code, https://github.com/pallets/click +Project-URL: Issue tracker, https://github.com/pallets/click/issues +Description: \$ 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 + + + Installing + ---------- + + Install and update using `pip`_: + + .. code-block:: text + + $ pip install click + + Click supports Python 3.4 and newer, Python 2.7, and PyPy. + + .. _pip: https://pip.pypa.io/en/stable/quickstart/ + + + A Simple Example + ---------------- + + What does it look like? Here is an example of a simple Click program: + + .. code-block:: python + + 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 _ in range(count): + click.echo("Hello, %s!" % name) + + if __name__ == '__main__': + hello() + + And what it looks like when run: + + .. code-block:: text + + $ python hello.py --count=3 + Your name: Click + Hello, Click! + Hello, Click! + Hello, Click! + + + Donate + ------ + + The Pallets organization develops and supports Click and other popular + packages. In order to grow the community of contributors and users, and + allow the maintainers to devote more time to the projects, `please + donate today`_. + + .. _please donate today: https://palletsprojects.com/donate + + + Links + ----- + + * Website: https://palletsprojects.com/p/click/ + * Documentation: https://click.palletsprojects.com/ + * License: `BSD `_ + * Releases: https://pypi.org/project/click/ + * Code: https://github.com/pallets/click + * Issue tracker: https://github.com/pallets/click/issues + * Test status: + + * Linux, Mac: https://travis-ci.org/pallets/click + * Windows: https://ci.appveyor.com/project/pallets/click + + * Test coverage: https://codecov.io/gh/pallets/click + +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.* diff --git a/README.rst b/README.rst index b6dd4b1..e4c0471 100644 --- a/README.rst +++ b/README.rst @@ -2,8 +2,8 @@ ========== 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 +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 @@ -12,9 +12,9 @@ implement an intended CLI API. Click in three points: -- arbitrary nesting of commands -- automatic help page generation -- supports lazy loading of subcommands at runtime +- Arbitrary nesting of commands +- Automatic help page generation +- Supports lazy loading of subcommands at runtime Installing @@ -41,13 +41,13 @@ What does it look like? Here is an example of a simple Click program: import click @click.command() - @click.option('--count', default=1, help='Number of greetings.') - @click.option('--name', prompt='Your name', - help='The person to greet.') + @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) + for _ in range(count): + click.echo("Hello, %s!" % name) if __name__ == '__main__': hello() @@ -57,17 +57,17 @@ And what it looks like when run: .. code-block:: text $ python hello.py --count=3 - Your name: John - Hello John! - Hello John! - Hello John! + Your name: Click + Hello, Click! + Hello, Click! + Hello, Click! Donate ------ -The Pallets organization develops and supports Flask and the libraries -it uses. In order to grow the community of contributors and users, and +The Pallets organization develops and supports Click and other popular +packages. In order to grow the community of contributors and users, and allow the maintainers to devote more time to the projects, `please donate today`_. @@ -79,7 +79,7 @@ Links * Website: https://palletsprojects.com/p/click/ * Documentation: https://click.palletsprojects.com/ -* License: `BSD `_ +* License: `BSD `_ * Releases: https://pypi.org/project/click/ * Code: https://github.com/pallets/click * Issue tracker: https://github.com/pallets/click/issues diff --git a/click/__init__.py b/click/__init__.py index 6de314b..d3c3366 100644 --- a/click/__init__.py +++ b/click/__init__.py @@ -1,17 +1,15 @@ # -*- coding: utf-8 -*- """ - click - ~~~~~ +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. +Click is a simple Python module inspired by the stdlib 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. +:copyright: © 2014 by the Pallets team. +:license: BSD, see LICENSE.rst for more details. """ # Core classes @@ -28,7 +26,7 @@ from .decorators import pass_context, pass_obj, make_pass_decorator, \ # Types from .types import ParamType, File, Path, Choice, IntRange, Tuple, \ - STRING, INT, FLOAT, BOOL, UUID, UNPROCESSED, FloatRange + DateTime, STRING, INT, FLOAT, BOOL, UUID, UNPROCESSED, FloatRange # Utilities from .utils import echo, get_binary_stream, get_text_stream, open_file, \ @@ -65,8 +63,9 @@ __all__ = [ 'version_option', 'help_option', # Types - 'ParamType', 'File', 'Path', 'Choice', 'IntRange', 'Tuple', 'STRING', - 'INT', 'FLOAT', 'BOOL', 'UUID', 'UNPROCESSED', 'FloatRange', + 'ParamType', 'File', 'Path', 'Choice', 'IntRange', 'Tuple', + 'DateTime', 'STRING', 'INT', 'FLOAT', 'BOOL', 'UUID', 'UNPROCESSED', + 'FloatRange', # Utilities 'echo', 'get_binary_stream', 'get_text_stream', 'open_file', @@ -95,4 +94,4 @@ __all__ = [ disable_unicode_literals_warning = False -__version__ = '7.0-dev' +__version__ = '7.0' diff --git a/click/_bashcomplete.py b/click/_bashcomplete.py index db42865..a5f1084 100644 --- a/click/_bashcomplete.py +++ b/click/_bashcomplete.py @@ -1,4 +1,3 @@ -import collections import copy import os import re @@ -8,8 +7,14 @@ from .parser import split_arg_string from .core import MultiCommand, Option, Argument from .types import Choice +try: + from collections import abc +except ImportError: + import collections as abc + WORDBREAK = '=' +# Note, only BASH version 4.4 and later have the nosort option. COMPLETION_SCRIPT_BASH = ''' %(complete_func)s() { local IFS=$'\n' @@ -19,7 +24,18 @@ COMPLETION_SCRIPT_BASH = ''' return 0 } -complete -F %(complete_func)s %(script_names)s +%(complete_func)setup() { + local COMPLETION_OPTIONS="" + local BASH_VERSION_ARR=(${BASH_VERSION//./ }) + # Only BASH version 4.4 and later have the nosort option. + if [ ${BASH_VERSION_ARR[0]} -gt 4 ] || ([ ${BASH_VERSION_ARR[0]} -eq 4 ] && [ ${BASH_VERSION_ARR[1]} -ge 4 ]); then + COMPLETION_OPTIONS="-o nosort" + fi + + complete $COMPLETION_OPTIONS -F %(complete_func)s %(script_names)s +} + +%(complete_func)setup ''' COMPLETION_SCRIPT_ZSH = ''' @@ -41,11 +57,13 @@ COMPLETION_SCRIPT_ZSH = ''' done if [ -n "$completions_with_descriptions" ]; then - _describe '' completions_with_descriptions + _describe -V unsorted completions_with_descriptions -U -Q fi + if [ -n "$completions" ]; then - compadd -M 'r:|=* l:|=* r:|=*' -a completions + compadd -U -V unsorted -Q -a completions fi + compstate[insert]="automenu" } compdef %(complete_func)s %(script_names)s @@ -73,18 +91,31 @@ def resolve_ctx(cli, prog_name, args): :return: the final context/command parsed """ ctx = cli.make_context(prog_name, args, resilient_parsing=True) - args_remaining = ctx.protected_args + ctx.args - while ctx is not None and args_remaining: + args = ctx.protected_args + ctx.args + while args: if isinstance(ctx.command, MultiCommand): - cmd = ctx.command.get_command(ctx, args_remaining[0]) - if cmd is None: - return None - ctx = cmd.make_context( - args_remaining[0], args_remaining[1:], parent=ctx, resilient_parsing=True) - args_remaining = ctx.protected_args + ctx.args + if not ctx.command.chain: + cmd_name, cmd, args = ctx.command.resolve_command(ctx, args) + if cmd is None: + return ctx + ctx = cmd.make_context(cmd_name, args, parent=ctx, + resilient_parsing=True) + args = ctx.protected_args + ctx.args + else: + # Walk chained subcommand contexts saving the last one. + while args: + cmd_name, cmd, args = ctx.command.resolve_command(ctx, args) + if cmd is None: + return ctx + sub_ctx = cmd.make_context(cmd_name, args, parent=ctx, + allow_extra_args=True, + allow_interspersed_args=False, + resilient_parsing=True) + args = sub_ctx.args + ctx = sub_ctx + args = sub_ctx.protected_args + sub_ctx.args else: - ctx = ctx.parent - + break return ctx @@ -132,7 +163,7 @@ def is_incomplete_argument(current_params, cmd_param): return True if cmd_param.nargs == -1: return True - if isinstance(current_param_values, collections.Iterable) \ + if isinstance(current_param_values, abc.Iterable) \ and cmd_param.nargs > 1 and len(current_param_values) < cmd_param.nargs: return True return False @@ -150,7 +181,7 @@ def get_user_autocompletions(ctx, args, incomplete, cmd_param): if isinstance(cmd_param.type, Choice): # Choices don't support descriptions. results = [(c, None) - for c in cmd_param.type.choices if c.startswith(incomplete)] + for c in cmd_param.type.choices if str(c).startswith(incomplete)] elif cmd_param.autocompletion is not None: dynamic_completions = cmd_param.autocompletion(ctx=ctx, args=args, @@ -160,20 +191,32 @@ def get_user_autocompletions(ctx, args, incomplete, cmd_param): return results +def get_visible_commands_starting_with(ctx, starts_with): + """ + :param ctx: context associated with the parsed command + :starts_with: string that visible commands must start with. + :return: all visible (not hidden) commands that start with starts_with. + """ + for c in ctx.command.list_commands(ctx): + if c.startswith(starts_with): + command = ctx.command.get_command(ctx, c) + if not command.hidden: + yield command + + def add_subcommand_completions(ctx, incomplete, completions_out): # Add subcommand completions. if isinstance(ctx.command, MultiCommand): completions_out.extend( - [(c, ctx.command.get_command(ctx, c).get_short_help_str()) for c in ctx.command.list_commands(ctx) if c.startswith(incomplete)]) + [(c.name, c.get_short_help_str()) for c in get_visible_commands_starting_with(ctx, incomplete)]) # Walk up the context list and add any other completion possibilities from chained commands while ctx.parent is not None: ctx = ctx.parent if isinstance(ctx.command, MultiCommand) and ctx.command.chain: - remaining_commands = sorted( - set(ctx.command.list_commands(ctx)) - set(ctx.protected_args)) - completions_out.extend( - [(c, ctx.command.get_command(ctx, c).get_short_help_str()) for c in remaining_commands if c.startswith(incomplete)]) + remaining_commands = [c for c in get_visible_commands_starting_with(ctx, incomplete) + if c.name not in ctx.protected_args] + completions_out.extend([(c.name, c.get_short_help_str()) for c in remaining_commands]) def get_choices(cli, prog_name, args, incomplete): @@ -203,11 +246,10 @@ def get_choices(cli, prog_name, args, incomplete): if start_of_option(incomplete): # completions for partial options for param in ctx.command.params: - if isinstance(param, Option): + if isinstance(param, Option) and not param.hidden: param_opts = [param_opt for param_opt in param.opts + param.secondary_opts if param_opt not in all_args or param.multiple] - completions.extend( - [(o, param.help) for o in param_opts if o.startswith(incomplete)]) + completions.extend([(o, param.help) for o in param_opts if o.startswith(incomplete)]) return completions # completion for option values from user supplied values for param in ctx.command.params: @@ -216,15 +258,11 @@ def get_choices(cli, prog_name, args, incomplete): # completion for argument values from user supplied values for param in ctx.command.params: if is_incomplete_argument(ctx.params, param): - completions.extend(get_user_autocompletions( - ctx, all_args, incomplete, param)) - # Stop looking for other completions only if this argument is required. - if param.required: - return completions - break + return get_user_autocompletions(ctx, all_args, incomplete, param) add_subcommand_completions(ctx, incomplete, completions) - return completions + # Sort before returning so that proper ordering can be enforced in custom types. + return sorted(completions) def do_complete(cli, prog_name, include_descriptions): diff --git a/click/_compat.py b/click/_compat.py index e8b06a4..937e230 100644 --- a/click/_compat.py +++ b/click/_compat.py @@ -224,9 +224,11 @@ if PY2: return set_binary_mode(sys.stdin) def get_binary_stdout(): + _wrap_std_stream('stdout') return set_binary_mode(sys.stdout) def get_binary_stderr(): + _wrap_std_stream('stderr') return set_binary_mode(sys.stderr) def get_text_stdin(encoding=None, errors=None): @@ -237,6 +239,7 @@ if PY2: force_readable=True) def get_text_stdout(encoding=None, errors=None): + _wrap_std_stream('stdout') rv = _get_windows_console_stream(sys.stdout, encoding, errors) if rv is not None: return rv @@ -244,6 +247,7 @@ if PY2: force_writable=True) def get_text_stderr(encoding=None, errors=None): + _wrap_std_stream('stderr') rv = _get_windows_console_stream(sys.stderr, encoding, errors) if rv is not None: return rv @@ -582,7 +586,7 @@ if WIN: # Windows has a smaller terminal DEFAULT_COLUMNS = 79 - from ._winconsole import _get_windows_console_stream + from ._winconsole import _get_windows_console_stream, _wrap_std_stream def _get_argv_encoding(): import locale @@ -644,6 +648,7 @@ else: return getattr(sys.stdin, 'encoding', None) or get_filesystem_encoding() _get_windows_console_stream = lambda *x: None + _wrap_std_stream = lambda *x: None def term_len(x): @@ -669,6 +674,7 @@ def _make_cached_stream_func(src_func, wrapper_func): return rv rv = wrapper_func() try: + stream = src_func() # In case wrapper_func() modified the stream cache[stream] = rv except Exception: pass diff --git a/click/_termui_impl.py b/click/_termui_impl.py index 0b39731..00a8e5e 100644 --- a/click/_termui_impl.py +++ b/click/_termui_impl.py @@ -1,14 +1,16 @@ +# -*- coding: utf-8 -*- """ - click._termui_impl - ~~~~~~~~~~~~~~~~~~ +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. +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. +:copyright: © 2014 by the Pallets team. +:license: BSD, see LICENSE.rst for more details. """ + import os import sys import time @@ -527,9 +529,11 @@ def open_url(url, wait=False, locate=False): def _translate_ch_to_exc(ch): - if ch == '\x03': + if ch == u'\x03': raise KeyboardInterrupt() - if ch == '\x04': + if ch == u'\x04' and not WIN: # Unix-like, Ctrl+D + raise EOFError() + if ch == u'\x1a' and WIN: # Windows, Ctrl+Z raise EOFError() @@ -541,16 +545,46 @@ if WIN: yield def getchar(echo): - rv = msvcrt.getch() + # The function `getch` will return a bytes object corresponding to + # the pressed character. Since Windows 10 build 1803, it will also + # return \x00 when called a second time after pressing a regular key. + # + # `getwch` does not share this probably-bugged behavior. Moreover, it + # returns a Unicode object by default, which is what we want. + # + # Either of these functions will return \x00 or \xe0 to indicate + # a special key, and you need to call the same function again to get + # the "rest" of the code. The fun part is that \u00e0 is + # "latin small letter a with grave", so if you type that on a French + # keyboard, you _also_ get a \xe0. + # E.g., consider the Up arrow. This returns \xe0 and then \x48. The + # resulting Unicode string reads as "a with grave" + "capital H". + # This is indistinguishable from when the user actually types + # "a with grave" and then "capital H". + # + # When \xe0 is returned, we assume it's part of a special-key sequence + # and call `getwch` again, but that means that when the user types + # the \u00e0 character, `getchar` doesn't return until a second + # character is typed. + # The alternative is returning immediately, but that would mess up + # cross-platform handling of arrow keys and others that start with + # \xe0. Another option is using `getch`, but then we can't reliably + # read non-ASCII characters, because return values of `getch` are + # limited to the current 8-bit codepage. + # + # Anyway, Click doesn't claim to do this Right(tm), and using `getwch` + # is doing the right thing in more situations than with `getch`. if echo: - msvcrt.putchar(rv) + func = msvcrt.getwche + else: + func = msvcrt.getwch + + rv = func() + if rv in (u'\x00', u'\xe0'): + # \x00 and \xe0 are control characters that indicate special key, + # see above. + rv += func() _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 @@ -580,7 +614,8 @@ else: def getchar(echo): with raw_terminal() as fd: ch = os.read(fd, 32) + ch = ch.decode(get_best_encoding(sys.stdin), 'replace') if echo and isatty(sys.stdout): sys.stdout.write(ch) _translate_ch_to_exc(ch) - return ch.decode(get_best_encoding(sys.stdin), 'replace') + return ch diff --git a/click/_unicodefun.py b/click/_unicodefun.py index 6383415..620edff 100644 --- a/click/_unicodefun.py +++ b/click/_unicodefun.py @@ -43,7 +43,7 @@ def _check_for_unicode_literals(): 'because it can introduce subtle bugs in your ' 'code. You should instead use explicit u"" literals ' 'for your unicode strings. For more information see ' - 'http://click.pocoo.org/python3/'), + 'https://click.palletsprojects.com/python3/'), stacklevel=bad_frame) @@ -117,7 +117,9 @@ def _verify_python3_env(): 'is not supported' ) % bad_locale - raise RuntimeError('Click will abort further execution because Python 3 ' - 'was configured to use ASCII as encoding for the ' - 'environment. Consult http://click.pocoo.org/python3/ ' - 'for mitigation steps.' + extra) + raise RuntimeError( + 'Click will abort further execution because Python 3 was' + ' configured to use ASCII as encoding for the environment.' + ' Consult https://click.palletsprojects.com/en/7.x/python3/ for' + ' mitigation steps.' + extra + ) diff --git a/click/_winconsole.py b/click/_winconsole.py index f4d95dd..bbb080d 100644 --- a/click/_winconsole.py +++ b/click/_winconsole.py @@ -201,6 +201,40 @@ class ConsoleStream(object): ) +class WindowsChunkedWriter(object): + """ + Wraps a stream (such as stdout), acting as a transparent proxy for all + attribute access apart from method 'write()' which we wrap to write in + limited chunks due to a Windows limitation on binary console streams. + """ + def __init__(self, wrapped): + # double-underscore everything to prevent clashes with names of + # attributes on the wrapped stream object. + self.__wrapped = wrapped + + def __getattr__(self, name): + return getattr(self.__wrapped, name) + + def write(self, text): + total_to_write = len(text) + written = 0 + + while written < total_to_write: + to_write = min(total_to_write - written, MAX_BYTES_WRITTEN) + self.__wrapped.write(text[written:written+to_write]) + written += to_write + + +_wrapped_std_streams = set() + + +def _wrap_std_stream(name): + # Python 2 & Windows 7 and below + if PY2 and sys.getwindowsversion()[:2] <= (6, 1) and name not in _wrapped_std_streams: + setattr(sys, name, WindowsChunkedWriter(getattr(sys, name))) + _wrapped_std_streams.add(name) + + def _get_text_stdin(buffer_stream): text_stream = _NonClosingTextIOWrapper( io.BufferedReader(_WindowsConsoleReader(STDIN_HANDLE)), @@ -210,14 +244,14 @@ def _get_text_stdin(buffer_stream): def _get_text_stdout(buffer_stream): text_stream = _NonClosingTextIOWrapper( - _WindowsConsoleWriter(STDOUT_HANDLE), + io.BufferedWriter(_WindowsConsoleWriter(STDOUT_HANDLE)), 'utf-16-le', 'strict', line_buffering=True) return ConsoleStream(text_stream, buffer_stream) def _get_text_stderr(buffer_stream): text_stream = _NonClosingTextIOWrapper( - _WindowsConsoleWriter(STDERR_HANDLE), + io.BufferedWriter(_WindowsConsoleWriter(STDERR_HANDLE)), 'utf-16-le', 'strict', line_buffering=True) return ConsoleStream(text_stream, buffer_stream) diff --git a/click/core.py b/click/core.py index 30aec62..7a1e342 100644 --- a/click/core.py +++ b/click/core.py @@ -7,7 +7,8 @@ from itertools import repeat from functools import update_wrapper from .types import convert_type, IntRange, BOOL -from .utils import make_str, make_default_short_help, echo, get_os_args +from .utils import PacifyFlushWrapper, make_str, make_default_short_help, \ + echo, get_os_args from .exceptions import ClickException, UsageError, BadParameter, Abort, \ MissingParameter, Exit from .termui import prompt, confirm, style @@ -182,7 +183,8 @@ class Context(object): add some safety mapping on the right. :param resilient_parsing: if this flag is enabled then Click will parse without any interactivity or callback - invocation. This is useful for implementing + invocation. Default values will also be + ignored. 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 @@ -312,7 +314,8 @@ class Context(object): 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. + #: will do its best to not cause any failures and default values + #: will be ignored. Useful for completion. self.resilient_parsing = resilient_parsing # If there is no envvar prefix yet, but the parent has one and @@ -325,7 +328,7 @@ class Context(object): auto_envvar_prefix = '%s_%s' % (parent.auto_envvar_prefix, self.info_name.upper()) else: - self.auto_envvar_prefix = auto_envvar_prefix.upper() + auto_envvar_prefix = auto_envvar_prefix.upper() self.auto_envvar_prefix = auto_envvar_prefix if color is None and parent is not None: @@ -732,6 +735,8 @@ class BaseCommand(object): sys.exit(e.exit_code) except IOError as e: if e.errno == errno.EPIPE: + sys.stdout = PacifyFlushWrapper(sys.stdout) + sys.stderr = PacifyFlushWrapper(sys.stderr) sys.exit(1) else: raise @@ -1177,7 +1182,7 @@ class MultiCommand(Command): # 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 cmd is None and not ctx.resilient_parsing: if split_opt(cmd_name)[0]: self.parse_args(ctx, ctx.args) ctx.fail('No such command "%s".' % original_cmd_name) @@ -1351,7 +1356,7 @@ class Parameter(object): self.is_eager = is_eager self.metavar = metavar self.envvar = envvar - self.autocompletion = autocompletion + self.autocompletion = autocompletion @property def human_readable_name(self): @@ -1382,7 +1387,6 @@ class Parameter(object): def add_to_parser(self, parser, ctx): pass - def consume_value(self, ctx, opts): value = opts.get(self.name) if value is None: @@ -1433,7 +1437,7 @@ class Parameter(object): def full_process_value(self, ctx, value): value = self.process_value(ctx, value) - if value is None: + if value is None and not ctx.resilient_parsing: value = self.get_default(ctx) if self.required and self.value_is_missing(value): @@ -1832,11 +1836,9 @@ class Argument(Parameter): 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)) + raise TypeError('Arguments take exactly one ' + 'parameter declaration, got %d' % len(decls)) return name, [arg], [] def get_usage_pieces(self, ctx): diff --git a/click/parser.py b/click/parser.py index 4fe3d2f..1c3ae9c 100644 --- a/click/parser.py +++ b/click/parser.py @@ -1,20 +1,21 @@ # -*- coding: utf-8 -*- """ - click.parser - ~~~~~~~~~~~~ +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). +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 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. +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 collections import deque from .exceptions import UsageError, NoSuchOption, BadOptionUsage, \ @@ -321,7 +322,7 @@ class OptionParser(object): if opt not in self._long_opt: possibilities = [word for word in self._long_opt if word.startswith(opt)] - raise NoSuchOption(opt, possibilities=possibilities) + raise NoSuchOption(opt, possibilities=possibilities, ctx=self.ctx) option = self._long_opt[opt] if option.takes_value: @@ -364,7 +365,7 @@ class OptionParser(object): if self.ignore_unknown_options: unknown_options.append(ch) continue - raise NoSuchOption(opt) + raise NoSuchOption(opt, ctx=self.ctx) if option.takes_value: # Any characters left in arg? Pretend they're the # next arg, and stop consuming characters of arg. diff --git a/click/termui.py b/click/termui.py index 70eaceb..bf9a3aa 100644 --- a/click/termui.py +++ b/click/termui.py @@ -524,7 +524,7 @@ def launch(url, wait=False, locate=False): Examples:: - click.launch('http://click.pocoo.org/') + click.launch('https://click.palletsprojects.com/') click.launch('/my/downloaded/file', locate=True) .. versionadded:: 2.0 @@ -557,6 +557,10 @@ def getchar(echo=False): Note that this will always read from the terminal, even if something is piped into the standard input. + Note for Windows: in rare cases when typing non-ASCII characters, this + function might wait for a second character and then return both at once. + This is because certain Unicode characters look like special-key markers. + .. versionadded:: 2.0 :param echo: if set to `True`, the character read will also show up on diff --git a/click/types.py b/click/types.py index 0d4b122..1f88032 100644 --- a/click/types.py +++ b/click/types.py @@ -1,5 +1,6 @@ import os import stat +from datetime import datetime from ._compat import open_stream, text_type, filename_to_ui, \ get_filesystem_encoding, get_streerror, _get_argv_encoding, PY2 @@ -182,6 +183,59 @@ class Choice(ParamType): return 'Choice(%r)' % list(self.choices) +class DateTime(ParamType): + """The DateTime type converts date strings into `datetime` objects. + + The format strings which are checked are configurable, but default to some + common (non-timezone aware) ISO 8601 formats. + + When specifying *DateTime* formats, you should only pass a list or a tuple. + Other iterables, like generators, may lead to surprising results. + + The format strings are processed using ``datetime.strptime``, and this + consequently defines the format strings which are allowed. + + Parsing is tried using each format, in order, and the first format which + parses successfully is used. + + :param formats: A list or tuple of date format strings, in the order in + which they should be tried. Defaults to + ``'%Y-%m-%d'``, ``'%Y-%m-%dT%H:%M:%S'``, + ``'%Y-%m-%d %H:%M:%S'``. + """ + name = 'datetime' + + def __init__(self, formats=None): + self.formats = formats or [ + '%Y-%m-%d', + '%Y-%m-%dT%H:%M:%S', + '%Y-%m-%d %H:%M:%S' + ] + + def get_metavar(self, param): + return '[{}]'.format('|'.join(self.formats)) + + def _try_to_convert_date(self, value, format): + try: + return datetime.strptime(value, format) + except ValueError: + return None + + def convert(self, value, param, ctx): + # Exact match + for format in self.formats: + dtime = self._try_to_convert_date(value, format) + if dtime: + return dtime + + self.fail( + 'invalid datetime format: {}. (choose from {})'.format( + value, ', '.join(self.formats))) + + def __repr__(self): + return 'DateTime' + + class IntParamType(ParamType): name = 'integer' diff --git a/click/utils.py b/click/utils.py index 9f175eb..fc84369 100644 --- a/click/utils.py +++ b/click/utils.py @@ -414,3 +414,27 @@ def get_app_dir(app_name, roaming=True, force_posix=False): return os.path.join( os.environ.get('XDG_CONFIG_HOME', os.path.expanduser('~/.config')), _posixify(app_name)) + + +class PacifyFlushWrapper(object): + """This wrapper is used to catch and suppress BrokenPipeErrors resulting + from ``.flush()`` being called on broken pipe during the shutdown/final-GC + of the Python interpreter. Notably ``.flush()`` is always called on + ``sys.stdout`` and ``sys.stderr``. So as to have minimal impact on any + other cleanup code, and the case where the underlying file is not a broken + pipe, all calls and attributes are proxied. + """ + + def __init__(self, wrapped): + self.wrapped = wrapped + + def flush(self): + try: + self.wrapped.flush() + except IOError as e: + import errno + if e.errno != errno.EPIPE: + raise + + def __getattr__(self, attr): + return getattr(self.wrapped, attr) diff --git a/docs/conf.py b/docs/conf.py index d64f8fa..39b6f46 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -5,7 +5,7 @@ from pallets_sphinx_themes import ProjectLink, get_version project = "Click" copyright = "2014 Pallets Team" author = "Pallets Team" -release, version = get_version("Click") +release, version = get_version("Click", version_length=1) # General -------------------------------------------------------------- @@ -16,9 +16,7 @@ intersphinx_mapping = {"python": ("https://docs.python.org/3/", None)} # HTML ----------------------------------------------------------------- html_theme = "click" -html_theme_options = { - "index_sidebar_logo": False, -} +html_theme_options = {"index_sidebar_logo": False} html_context = { "project_links": [ ProjectLink("Donate to Pallets", "https://palletsprojects.com/donate"), @@ -36,6 +34,7 @@ singlehtml_sidebars = {"index": ["project.html", "versions.html", "localtoc.html html_static_path = ["_static"] html_favicon = "_static/click-icon.png" html_logo = "_static/click-logo-sidebar.png" +html_title = "Click Documentation ({})".format(version) html_show_sourcelink = False html_domain_indices = False html_experimental_html5_writer = True @@ -43,5 +42,5 @@ html_experimental_html5_writer = True # LaTeX ---------------------------------------------------------------- latex_documents = [ - (master_doc, "Click.tex", "Click Documentation", "Pallets Team", "manual") + (master_doc, "Click-{}.tex".format(version), html_title, author, "manual") ] diff --git a/docs/license.rst b/docs/license.rst index 1f4654a..13eb726 100644 --- a/docs/license.rst +++ b/docs/license.rst @@ -10,4 +10,5 @@ of the software without written consent. License Text ------------ -.. include:: ../LICENSE +.. include:: ../LICENSE.rst + diff --git a/docs/parameters.rst b/docs/parameters.rst index 112e8fa..9ca3208 100644 --- a/docs/parameters.rst +++ b/docs/parameters.rst @@ -70,6 +70,9 @@ different behavior and some are supported out of the box: .. autoclass:: FloatRange :noindex: +.. autoclass:: DateTime + :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. diff --git a/docs/python3.rst b/docs/python3.rst index 65e1b50..0ec1014 100644 --- a/docs/python3.rst +++ b/docs/python3.rst @@ -120,7 +120,7 @@ If you see something like this error in Python 3:: ... 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 + to Python 2 or consult the Python 3 section of the docs for mitigation steps. You are dealing with an environment where Python 3 thinks you are diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 4824f66..4479d0e 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -227,6 +227,12 @@ script can instead be written like this: def dropdb(): click.echo('Dropped the database') +You would then invoke the :class:`Group` in your setuptools entry points or +other invocations:: + + if __name__ == '__main__': + cli() + Adding Parameters ----------------- diff --git a/docs/requirements.txt b/docs/requirements.txt index 6bb1491..295f450 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,2 +1,2 @@ -Sphinx -Pallets-Sphinx-Themes +Sphinx~=1.8.0 +Pallets-Sphinx-Themes~=1.1.0 diff --git a/docs/utils.rst b/docs/utils.rst index f16a57d..7250d84 100644 --- a/docs/utils.rst +++ b/docs/utils.rst @@ -245,8 +245,8 @@ select the provided file. Example usage:: - click.launch('http://click.pocoo.org/') - click.launch('/my/downloaded/file.txt', locate=True) + click.launch("https://click.palletsprojects.com/") + click.launch("/my/downloaded/file.txt", locate=True) Printing Filenames diff --git a/docs/wincmd.rst b/docs/wincmd.rst index b9c1161..901ee95 100644 --- a/docs/wincmd.rst +++ b/docs/wincmd.rst @@ -59,14 +59,21 @@ This hackery is used on both Python 2 and Python 3 as neither version of Python has native support for cmd.exe with unicode characters. There are some limitations you need to be aware of: -* this unicode support is limited to ``click.echo``, ``click.prompt`` as +* This unicode support is limited to ``click.echo``, ``click.prompt`` as well as ``click.get_text_stream``. -* depending on if unicode values or byte strings are passed the control +* Depending on if unicode values or byte strings are passed the control flow goes completely different places internally which can have some odd artifacts if data partially ends up being buffered. Click attempts to protect against that by manually always flushing but if you are mixing and matching different string types to ``stdout`` or ``stderr`` you will need to manually flush. +* The raw output stream is set to binary mode, which is a global + operation on Windows, so ``print`` calls will be affected. Prefer + ``click.echo`` over ``print``. +* On Windows 7 and below, there is a limitation where at most 64k + characters can be written in one call in binary mode. In this + situation, ``sys.stdout`` and ``sys.stderr`` are replaced with + wrappers that work around the limitation. Another important thing to note is that the Windows console's default fonts do not support a lot of characters which means that you are mostly diff --git a/examples/README b/examples/README index 2457507..6be3296 100644 --- a/examples/README +++ b/examples/README @@ -9,4 +9,4 @@ Click Examples through the wrong interpreter. For more information about this see the documentation: - http://click.pocoo.org/setuptools/ + https://click.palletsprojects.com/en/7.x/setuptools/ diff --git a/examples/bashcompletion/bashcompletion.py b/examples/bashcompletion/bashcompletion.py index 4d9fdde..1072840 100644 --- a/examples/bashcompletion/bashcompletion.py +++ b/examples/bashcompletion/bashcompletion.py @@ -8,6 +8,7 @@ def cli(): def get_env_vars(ctx, args, incomplete): + # Completions returned as strings do not have a description displayed. for key in os.environ.keys(): if incomplete in key: yield key @@ -26,11 +27,13 @@ def group(): def list_users(ctx, args, incomplete): - # Here you can generate completions dynamically - users = ['bob', 'alice'] - for user in users: - if user.startswith(incomplete): - yield user + # You can generate completions with descriptions by returning + # tuples in the form (completion, description). + users = [('bob', 'butcher'), + ('alice', 'baker'), + ('jerry', 'candlestick maker')] + # Ths will allow completion matches based on matches within the description string too! + return [user for user in users if incomplete in user[0] or incomplete in user[1]] @group.command(help='Choose a user') @@ -38,4 +41,5 @@ def list_users(ctx, args, incomplete): def subcmd(user): click.echo('Chosen user is %s' % user) + cli.add_command(group) diff --git a/setup.cfg b/setup.cfg index 3b0846a..525a9e7 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,8 +1,25 @@ -[bdist_wheel] -universal=1 - [metadata] -license_file = LICENSE +license_file = LICENSE.rst + +[bdist_wheel] +universal = 1 [tool:pytest] -addopts = -p no:warnings --tb=short +testpaths = tests + +[coverage:run] +branch = True +source = + click + tests + +[coverage:paths] +source = + click + .tox/*/lib/python*/site-packages/click + .tox/pypy/site-packages/click + +[egg_info] +tag_build = +tag_date = 0 + diff --git a/setup.py b/setup.py index 194d1d4..1c18dc7 100644 --- a/setup.py +++ b/setup.py @@ -1,57 +1,44 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- import io import re from setuptools import setup - -_version_re = re.compile(r'__version__\s+=\s+(.*)') - - -with io.open('README.rst', 'rt', encoding='utf8') as f: +with io.open("README.rst", "rt", encoding="utf8") as f: readme = f.read() -with io.open('click/__init__.py', 'rt', encoding='utf8') as f: - version = re.search(r'__version__ = \'(.*?)\'', f.read()).group(1) +with io.open("click/__init__.py", "rt", encoding="utf8") as f: + version = re.search(r"__version__ = \'(.*?)\'", f.read()).group(1) setup( - name='click', + name="Click", version=version, - url='https://palletsprojects.com/p/click/', - author='Armin Ronacher', - author_email='armin.ronacher@active-4.com', - maintainer='Pallets team', - maintainer_email='contact@palletsprojects.com', - long_description=readme, - packages=['click'], - description='A simple wrapper around optparse for ' - 'powerful command line utilities.', - license='BSD', - extras_require={ - 'dev': [ - 'pytest>=3', - 'coverage', - 'tox', - 'sphinx', - ], - 'docs': [ - 'sphinx', - 'Pallets-Sphinx-Themes', - ] + url="https://palletsprojects.com/p/click/", + project_urls={ + "Documentation": "https://click.palletsprojects.com/", + "Code": "https://github.com/pallets/click", + "Issue tracker": "https://github.com/pallets/click/issues", }, - classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: BSD License', - 'Operating System :: OS Independent', - 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - ], + license="BSD", + author="Armin Ronacher", + author_email="armin.ronacher@active-4.com", + maintainer="Pallets Team", + maintainer_email="contact@palletsprojects.com", + description="Composable command line interface toolkit", + long_description=readme, + packages=["click"], + include_package_data=True, python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*", + classifiers=[ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "License :: OSI Approved :: BSD License", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 2", + "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.4", + "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + ], ) diff --git a/tests/test_arguments.py b/tests/test_arguments.py index a6a3258..ca48fed 100644 --- a/tests/test_arguments.py +++ b/tests/test_arguments.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +import pytest import click from click._compat import PY2 @@ -287,3 +288,11 @@ def test_defaults_for_nargs(runner): result = runner.invoke(cmd, ['3']) assert result.exception is not None assert 'argument a takes 2 values' in result.output + + +def test_multiple_param_decls_not_allowed(runner): + with pytest.raises(TypeError): + @click.command() + @click.argument('x', click.Choice(['a', 'b'])) + def copy(x): + click.echo(x) diff --git a/tests/test_bashcomplete.py b/tests/test_bashcomplete.py index 0390841..7214d1a 100644 --- a/tests/test_bashcomplete.py +++ b/tests/test_bashcomplete.py @@ -132,7 +132,8 @@ def test_long_chain(): def test_chaining(): @click.group('cli', chain=True) @click.option('--cli-opt') - def cli(cli_opt): + @click.argument('arg', type=click.Choice(['cliarg1', 'cliarg2'])) + def cli(cli_opt, arg): pass @cli.command() @@ -142,33 +143,35 @@ def test_chaining(): @cli.command(help='bsub help') @click.option('--bsub-opt') - @click.argument('arg', type=click.Choice(['arg1', 'arg2']), required=True) + @click.argument('arg', type=click.Choice(['arg1', 'arg2'])) def bsub(bsub_opt, arg): pass @cli.command() @click.option('--csub-opt') - @click.argument('arg', type=click.Choice(['carg1', 'carg2']), required=False) + @click.argument('arg', type=click.Choice(['carg1', 'carg2']), default='carg1') def csub(csub_opt, arg): pass assert choices_without_help(cli, [], '-') == ['--cli-opt'] - assert choices_without_help(cli, [], '') == ['asub', 'bsub', 'csub'] - assert choices_without_help(cli, ['asub'], '-') == ['--asub-opt'] - assert choices_without_help(cli, ['asub'], '') == ['bsub', 'csub'] - assert choices_without_help(cli, ['bsub'], '') == ['arg1', 'arg2'] - assert choices_without_help(cli, ['asub', '--asub-opt'], '') == [] - assert choices_without_help(cli, ['asub', '--asub-opt', '5', 'bsub'], '-') == ['--bsub-opt'] - assert choices_without_help(cli, ['asub', 'bsub'], '-') == ['--bsub-opt'] - assert choices_with_help(cli, ['asub'], 'b') == [('bsub', 'bsub help')] - assert choices_without_help(cli, ['asub', 'csub'], '-') == ['--csub-opt'] + assert choices_without_help(cli, [], '') == ['cliarg1', 'cliarg2'] + assert choices_without_help(cli, ['cliarg1', 'asub'], '-') == ['--asub-opt'] + assert choices_without_help(cli, ['cliarg1', 'asub'], '') == ['bsub', 'csub'] + assert choices_without_help(cli, ['cliarg1', 'bsub'], '') == ['arg1', 'arg2'] + assert choices_without_help(cli, ['cliarg1', 'asub', '--asub-opt'], '') == [] + assert choices_without_help(cli, ['cliarg1', 'asub', '--asub-opt', '5', 'bsub'], '-') == ['--bsub-opt'] + assert choices_without_help(cli, ['cliarg1', 'asub', 'bsub'], '-') == ['--bsub-opt'] + assert choices_without_help(cli, ['cliarg1', 'asub', 'csub'], '') == ['carg1', 'carg2'] + assert choices_without_help(cli, ['cliarg1', 'bsub', 'arg1', 'csub'], '') == ['carg1', 'carg2'] + assert choices_without_help(cli, ['cliarg1', 'asub', 'csub'], '-') == ['--csub-opt'] + assert choices_with_help(cli, ['cliarg1', 'asub'], 'b') == [('bsub', 'bsub help')] def test_argument_choice(): @click.command() - @click.argument('arg1', required=False, type=click.Choice(['arg11', 'arg12'])) - @click.argument('arg2', required=False, type=click.Choice(['arg21', 'arg22'])) - @click.argument('arg3', required=False, type=click.Choice(['arg', 'argument'])) + @click.argument('arg1', required=True, type=click.Choice(['arg11', 'arg12'])) + @click.argument('arg2', type=click.Choice(['arg21', 'arg22']), default='arg21') + @click.argument('arg3', type=click.Choice(['arg', 'argument']), default='arg') def cli(): pass @@ -182,7 +185,7 @@ def test_argument_choice(): def test_option_choice(): @click.command() @click.option('--opt1', type=click.Choice(['opt11', 'opt12']), help='opt1 help') - @click.option('--opt2', type=click.Choice(['opt21', 'opt22'])) + @click.option('--opt2', type=click.Choice(['opt21', 'opt22']), default='opt21') @click.option('--opt3', type=click.Choice(['opt', 'option'])) def cli(): pass @@ -218,6 +221,8 @@ def test_option_and_arg_choice(): assert choices_without_help(cli, [''], '--opt1=') == ['opt11', 'opt12'] assert choices_without_help(cli, [], '') == ['arg11', 'arg12'] assert choices_without_help(cli, ['--opt2'], '') == ['opt21', 'opt22'] + assert choices_without_help(cli, ['arg11'], '--opt') == ['--opt1', '--opt2'] + assert choices_without_help(cli, [], '--opt') == ['--opt1', '--opt2'] def test_boolean_flag_choice(): @@ -258,11 +263,37 @@ def test_multi_option_choice(): def test_variadic_argument_choice(): @click.command() + @click.option('--opt', type=click.Choice(['opt1', 'opt2'])) @click.argument('src', nargs=-1, type=click.Choice(['src1', 'src2'])) def cli(local_opt): pass assert choices_without_help(cli, ['src1', 'src2'], '') == ['src1', 'src2'] + assert choices_without_help(cli, ['src1', 'src2'], '--o') == ['--opt'] + assert choices_without_help(cli, ['src1', 'src2', '--opt'], '') == ['opt1', 'opt2'] + assert choices_without_help(cli, ['src1', 'src2'], '') == ['src1', 'src2'] + + +def test_variadic_argument_complete(): + + def _complete(ctx, args, incomplete): + return ['abc', 'def', 'ghi', 'jkl', 'mno', 'pqr', 'stu', 'vwx', 'yz'] + + @click.group() + def entrypoint(): + pass + + @click.command() + @click.option('--opt', autocompletion=_complete) + @click.argument('arg', nargs=-1) + def subcommand(opt, arg): + pass + + entrypoint.add_command(subcommand) + + assert choices_without_help(entrypoint, ['subcommand', '--opt'], '') == _complete(0,0,0) + assert choices_without_help(entrypoint, ['subcommand', 'whatever', '--opt'], '') == _complete(0,0,0) + assert choices_without_help(entrypoint, ['subcommand', 'whatever', '--opt', 'abc'], '') == [] def test_long_chain_choice(): @@ -273,7 +304,7 @@ def test_long_chain_choice(): @cli.group() @click.option('--sub-opt', type=click.Choice(['subopt1', 'subopt2'])) @click.argument('sub-arg', required=False, type=click.Choice(['subarg1', 'subarg2'])) - def sub(sub_opt): + def sub(sub_opt, sub_arg): pass @sub.command(short_help='bsub help') @@ -283,15 +314,96 @@ def test_long_chain_choice(): def bsub(bsub_opt): pass - assert choices_with_help(cli, ['sub'], '') == [('subarg1', None), ('subarg2', None), ('bsub', 'bsub help')] + @sub.group('csub') + def csub(): + pass + + @csub.command() + def dsub(): + pass + + assert choices_with_help(cli, ['sub', 'subarg1'], '') == [('bsub', 'bsub help'), ('csub', '')] + assert choices_without_help(cli, ['sub'], '') == ['subarg1', 'subarg2'] assert choices_without_help(cli, ['sub', '--sub-opt'], '') == ['subopt1', 'subopt2'] assert choices_without_help(cli, ['sub', '--sub-opt', 'subopt1'], '') == \ - ['subarg1', 'subarg2', 'bsub'] + ['subarg1', 'subarg2'] assert choices_without_help(cli, - ['sub', '--sub-opt', 'subopt1', 'subarg1', 'bsub'], '-') == ['--bsub-opt'] + ['sub', '--sub-opt', 'subopt1', 'subarg1', 'bsub'], '-') == ['--bsub-opt'] assert choices_without_help(cli, - ['sub', '--sub-opt', 'subopt1', 'subarg1', 'bsub', '--bsub-opt'], '') == \ - ['bsubopt1', 'bsubopt2'] + ['sub', '--sub-opt', 'subopt1', 'subarg1', 'bsub'], '') == ['bsubarg1', 'bsubarg2'] assert choices_without_help(cli, - ['sub', '--sub-opt', 'subopt1', 'subarg1', 'bsub', '--bsub-opt', 'bsubopt1', 'bsubarg1'], - '') == ['bbsubarg1', 'bbsubarg2'] + ['sub', '--sub-opt', 'subopt1', 'subarg1', 'bsub', '--bsub-opt'], '') == \ + ['bsubopt1', 'bsubopt2'] + assert choices_without_help(cli, + ['sub', '--sub-opt', 'subopt1', 'subarg1', 'bsub', '--bsub-opt', 'bsubopt1', 'bsubarg1'], + '') == ['bbsubarg1', 'bbsubarg2'] + assert choices_without_help(cli, + ['sub', '--sub-opt', 'subopt1', 'subarg1', 'csub'], + '') == ['dsub'] + + +def test_chained_multi(): + @click.group() + def cli(): + pass + + @cli.group() + def sub(): + pass + + @sub.group() + def bsub(): + pass + + @sub.group(chain=True) + def csub(): + pass + + @csub.command() + def dsub(): + pass + + @csub.command() + def esub(): + pass + + assert choices_without_help(cli, ['sub'], '') == ['bsub', 'csub'] + assert choices_without_help(cli, ['sub'], 'c') == ['csub'] + assert choices_without_help(cli, ['sub', 'csub'], '') == ['dsub', 'esub'] + assert choices_without_help(cli, ['sub', 'csub', 'dsub'], '') == ['esub'] + + +def test_hidden(): + @click.group() + @click.option('--name', hidden=True) + @click.option('--choices', type=click.Choice([1, 2]), hidden=True) + def cli(name): + pass + + @cli.group(hidden=True) + def hgroup(): + pass + + @hgroup.group() + def hgroupsub(): + pass + + @cli.command() + def asub(): + pass + + @cli.command(hidden=True) + @click.option('--hname') + def hsub(): + pass + + assert choices_without_help(cli, [], '--n') == [] + assert choices_without_help(cli, [], '--c') == [] + # If the user exactly types out the hidden param, complete its options. + assert choices_without_help(cli, ['--choices'], '') == [1, 2] + assert choices_without_help(cli, [], '') == ['asub'] + assert choices_without_help(cli, [], '') == ['asub'] + assert choices_without_help(cli, [], 'h') == [] + # If the user exactly types out the hidden command, complete its subcommands. + assert choices_without_help(cli, ['hgroup'], '') == ['hgroupsub'] + assert choices_without_help(cli, ['hsub'], '--h') == ['--hname'] diff --git a/tests/test_basic.py b/tests/test_basic.py index 8ba251f..8de6301 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -332,6 +332,44 @@ def test_choice_option(runner): assert '--method [foo|bar|baz]' in result.output +def test_datetime_option_default(runner): + + @click.command() + @click.option('--start_date', type=click.DateTime()) + def cli(start_date): + click.echo(start_date.strftime('%Y-%m-%dT%H:%M:%S')) + + result = runner.invoke(cli, ['--start_date=2015-09-29']) + assert not result.exception + assert result.output == '2015-09-29T00:00:00\n' + + result = runner.invoke(cli, ['--start_date=2015-09-29T09:11:22']) + assert not result.exception + assert result.output == '2015-09-29T09:11:22\n' + + result = runner.invoke(cli, ['--start_date=2015-09']) + assert result.exit_code == 2 + assert ('Invalid value for "--start_date": ' + 'invalid datetime format: 2015-09. ' + '(choose from %Y-%m-%d, %Y-%m-%dT%H:%M:%S, %Y-%m-%d %H:%M:%S)' + ) in result.output + + result = runner.invoke(cli, ['--help']) + assert '--start_date [%Y-%m-%d|%Y-%m-%dT%H:%M:%S|%Y-%m-%d %H:%M:%S]' in result.output + + +def test_datetime_option_custom(runner): + @click.command() + @click.option('--start_date', + type=click.DateTime(formats=['%A %B %d, %Y'])) + def cli(start_date): + click.echo(start_date.strftime('%Y-%m-%dT%H:%M:%S')) + + result = runner.invoke(cli, ['--start_date=Wednesday June 05, 2010']) + assert not result.exception + assert result.output == '2010-06-05T00:00:00\n' + + def test_int_range_option(runner): @click.command() @click.option('--x', type=click.IntRange(0, 5)) diff --git a/tests/test_imports.py b/tests/test_imports.py index f400fa8..8e9a97d 100644 --- a/tests/test_imports.py +++ b/tests/test_imports.py @@ -32,7 +32,7 @@ click.echo(json.dumps(rv)) ALLOWED_IMPORTS = set([ 'weakref', 'os', 'struct', 'collections', 'sys', 'contextlib', 'functools', 'stat', 're', 'codecs', 'inspect', 'itertools', 'io', - 'threading', 'colorama', 'errno', 'fcntl' + 'threading', 'colorama', 'errno', 'fcntl', 'datetime' ]) if WIN: diff --git a/tests/test_options.py b/tests/test_options.py index 46a4adb..bd346c2 100644 --- a/tests/test_options.py +++ b/tests/test_options.py @@ -202,6 +202,19 @@ def test_dynamic_default_help_text(runner): assert 'lambda' not in result.output assert '(current user)' in result.output + +def test_toupper_envvar_prefix(runner): + @click.command() + @click.option('--arg') + def cmd(arg): + click.echo(arg) + + result = runner.invoke(cmd, [], auto_envvar_prefix='test', + env={'TEST_ARG': 'foo'}) + assert not result.exception + assert result.output == 'foo\n' + + def test_nargs_envvar(runner): @click.command() @click.option('--arg', nargs=2) diff --git a/tox.ini b/tox.ini index 1f68f4d..a44b110 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,9 @@ [tox] -envlist = py{36,35,34,27,py} +envlist = + py{37,36,35,34,27,py3,py} + docs-html + coverage-report +skip_missing_interpreters = true [testenv] passenv = LANG @@ -7,14 +11,14 @@ deps = pytest coverage colorama -commands = coverage run -p -m pytest {posargs:tests} +commands = coverage run -p -m pytest {posargs} [testenv:docs-html] -deps = sphinx +deps = -r docs/requirements.txt commands = sphinx-build -W -b html -d {envtmpdir}/doctrees docs {envtmpdir}/html [testenv:docs-linkcheck] -deps = sphinx +deps = -r docs/requirements.txt commands = sphinx-build -W -b linkcheck -d {envtmpdir}/doctrees docs {envtmpdir}/linkcheck [testenv:coverage-report]