pax_global_header00006660000000000000000000000064136463623330014523gustar00rootroot0000000000000052 comment=48752720dc80c70c1058061a310fa6c117902ad5 cleo-0.8.1/000077500000000000000000000000001364636233300124535ustar00rootroot00000000000000cleo-0.8.1/.coveragerc000066400000000000000000000000421364636233300145700ustar00rootroot00000000000000[report] omit = cleo/_compat.py cleo-0.8.1/.github/000077500000000000000000000000001364636233300140135ustar00rootroot00000000000000cleo-0.8.1/.github/workflows/000077500000000000000000000000001364636233300160505ustar00rootroot00000000000000cleo-0.8.1/.github/workflows/release.yml000066400000000000000000000070211364636233300202130ustar00rootroot00000000000000name: Release on: push: tags: - '*.*.*' jobs: Linux: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Get tag id: tag run: | echo ::set-output name=tag::${GITHUB_REF#refs/tags/} - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: python-version: 3.8 - name: Install and set up Poetry run: | curl -fsS -o get-poetry.py https://raw.githubusercontent.com/sdispater/poetry/master/get-poetry.py python get-poetry.py --preview -y - name: Build distributions run: | source $HOME/.poetry/env poetry build -vvv - name: Upload distribution artifacts uses: actions/upload-artifact@v1 with: name: project-dist path: dist MacOS: runs-on: macos-latest steps: - uses: actions/checkout@v2 - name: Get tag id: tag run: | echo ::set-output name=tag::${GITHUB_REF#refs/tags/} - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: python-version: 3.8 - name: Install and set up Poetry run: | curl -fsS -o get-poetry.py https://raw.githubusercontent.com/sdispater/poetry/master/get-poetry.py python get-poetry.py --preview -y - name: Build distributions run: | source $HOME/.poetry/env poetry build -vvv - name: Upload distribution artifacts uses: actions/upload-artifact@v1 with: name: project-dist path: dist Windows: runs-on: windows-latest steps: - uses: actions/checkout@v2 - name: Get tag id: tag shell: bash run: | echo ::set-output name=tag::${GITHUB_REF#refs/tags/} - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: python-version: 3.8 - name: Install and setup Poetry run: | Invoke-WebRequest https://raw.githubusercontent.com/sdispater/poetry/master/get-poetry.py -O get-poetry.py python get-poetry.py --preview -y - name: Build distributions run: | $env:Path += ";$env:Userprofile\.poetry\bin" poetry build -vvv - name: Upload distribution artifact uses: actions/upload-artifact@v1 with: name: project-dist path: dist Release: needs: [Linux, MacOS, Windows] runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v2 - name: Get tag id: tag run: | echo ::set-output name=tag::${GITHUB_REF#refs/tags/} - name: Download distribution artifact uses: actions/download-artifact@master with: name: project-dist path: dist - name: Install and set up Poetry run: | curl -fsS -o get-poetry.py https://raw.githubusercontent.com/sdispater/poetry/master/get-poetry.py python get-poetry.py --preview -y - name: Check distributions run: | ls -la dist - name: Publish to PyPI env: POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_TOKEN }} run: | source $HOME/.poetry/env poetry publish - name: Create Release id: create_release uses: actions/create-release@v1 env: GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} with: tag_name: ${{ steps.tag.outputs.tag }} release_name: ${{ steps.tag.outputs.tag }} draft: false prerelease: false cleo-0.8.1/.github/workflows/tests.yml000066400000000000000000000100541364636233300177350ustar00rootroot00000000000000name: Tests on: [push, pull_request] jobs: Linting: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Set up Python 3.8 uses: actions/setup-python@v1 with: python-version: 3.8 - name: Linting run: | pip install pre-commit pre-commit run --all-files Linux: needs: Linting runs-on: ubuntu-latest strategy: matrix: python-version: [2.7, 3.5, 3.6, 3.7, 3.8, pypy2, pypy3] steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: python-version: ${{ matrix.python-version }} - name: Get full python version id: full-python-version run: | echo ::set-output name=version::$(python -c "import sys; print('-'.join(str(v) for v in sys.version_info))") - name: Install and set up Poetry run: | curl -fsS -o get-poetry.py https://raw.githubusercontent.com/sdispater/poetry/master/get-poetry.py python get-poetry.py --preview -y source $HOME/.poetry/env poetry config virtualenvs.in-project true - name: Set up cache uses: actions/cache@v1 with: path: .venv key: venv-${{ runner.os }}-${{ steps.full-python-version.outputs.version }}-${{ hashFiles('**/poetry.lock') }} - name: Install dependencies run: | source $HOME/.poetry/env poetry install - name: Test run: | source $HOME/.poetry/env poetry run pytest -q tests poetry install MacOS: needs: Linting runs-on: macos-latest strategy: matrix: python-version: [2.7, 3.5, 3.6, 3.7, 3.8, pypy2, pypy3] steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: python-version: ${{ matrix.python-version }} - name: Get full python version id: full-python-version run: | echo ::set-output name=version::$(python -c "import sys; print('-'.join(str(v) for v in sys.version_info))") - name: Install and set up Poetry run: | curl -fsS -o get-poetry.py https://raw.githubusercontent.com/sdispater/poetry/master/get-poetry.py python get-poetry.py --preview -y source $HOME/.poetry/env poetry config virtualenvs.in-project true - name: Set up cache uses: actions/cache@v1 with: path: .venv key: venv-${{ runner.os }}-${{ steps.full-python-version.outputs.version }}-${{ hashFiles('**/poetry.lock') }} - name: Install dependencies run: | source $HOME/.poetry/env poetry install - name: Test run: | source $HOME/.poetry/env poetry run pytest -q tests Windows: needs: Linting runs-on: windows-latest strategy: matrix: python-version: [2.7, 3.5, 3.6, 3.7, 3.8] steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: python-version: ${{ matrix.python-version }} - name: Get full python version id: full-python-version shell: bash run: | echo ::set-output name=version::$(python -c "import sys; print('-'.join(str(v) for v in sys.version_info))") - name: Install and setup Poetry run: | Invoke-WebRequest https://raw.githubusercontent.com/sdispater/poetry/master/get-poetry.py -O get-poetry.py python get-poetry.py --preview -y $env:Path += ";$env:Userprofile\.poetry\bin" poetry config virtualenvs.in-project true - name: Set up cache uses: actions/cache@v1 with: path: .venv key: venv-${{ runner.os }}-${{ steps.full-python-version.outputs.version }}-${{ hashFiles('**/poetry.lock') }} - name: Install dependencies run: | $env:Path += ";$env:Userprofile\.poetry\bin" poetry install - name: Test run: | $env:Path += ";$env:Userprofile\.poetry\bin" poetry run pytest -q tests cleo-0.8.1/.gitignore000066400000000000000000000003301364636233300144370ustar00rootroot00000000000000*.pyc # Packages *.egg *.egg-info dist build .cache # Installer logs pip-log.txt # Unit test / coverage reports .coverage .tox nosetests.xml .DS_Store .idea/* .python-version test.py /test .pytest_cache .vscode cleo-0.8.1/.pre-commit-config.yaml000066400000000000000000000001721364636233300167340ustar00rootroot00000000000000repos: - repo: https://github.com/ambv/black rev: stable hooks: - id: black python_version: python3.6 cleo-0.8.1/CHANGELOG.md000066400000000000000000000150261364636233300142700ustar00rootroot00000000000000# Change Log ## [0.8.1] - 2020-04-17 ### Changed - Upgraded `clikit` to version `^0.6.0`. ## [0.8.0] - 2020-03-26 ### Added - Errors are now rendered in a nicer way for Python 3.6+. ## [0.7.6] - 2019-10-25 ### Fixed - Upgraded `clikit` to fix issues in option parsing. ## [0.7.5] - 2019-06-28 ### Fixed - Upgraded dependency requirements for bug fixes. ## [0.7.4] - 2019-05-15 ### Fixed - Fixed command construction with the `argument` and `option` helpers. ## [0.7.3] - 2019-05-12 ### Added - Added the `argument` and `option` helpers. ### Fixed - Fixed the `decorated` option for the command tester. - Fixed tested applications being terminated after execution. ## [0.7.2] - 2018-12-08 ### Fixed - Fixed invalid combination of OPTIONAL_VALUE and MULTI_VALUED flags for options. ## [0.7.1] - 2018-12-07 ### Fixed - Fixed parser not setting proper flags. ## [0.7.0] - 2018-12-07 This version breaks backwards compatibility and caution is advised when updating. While the public API of the `Command` class is mostly the same, a lot of the internals has changed or has been removed. Cleo is now mostly a higher level wrapper for [CliKit](https://github.com/sdispater/clikit) which is more flexible. ### Added - Added a sub command system via CliKit. - Added an event system via CliKit. ### Changed - All helper classes have been removed. If you use the `Command` methods this should not affect you. - The testers `get_display()` method has been removed. Use `tester.io.fetch_output()`. - The testers `execute()` method no longer requires the command name and requires a string as arguments instead of a list. - The testers `execute()` method now accepts a `inputs` keyword argument to pass user inputs. - The `call()` method no longer requires the command name and requires a string as arguments instead of a list. - The tables now automatically wraps the cells based on the available width. - The table separators and table cells elements have been removed. - The look and feel of the `help` command has changed. - Namespace commands are no longer supported and will be treated as standard commands. - The `list` command has been removed and merged with `help`. ## [0.6.8] - 2018-06-25 ### Changed - Testers (application and command) now automatically sets `decorated` to `False`. ### Fixed - Fixed numeric values appearing when getting terminal size on Windows. ## [0.6.7] - 2018-06-25 ### Fixed - Fixed verbosity option behavior. ## [0.6.6] - 2018-05-21 ### Fixed - Fixed an error for choice questions with only one choice. ## [0.6.5] - 2018-04-04 ### Fixed - Fixed handling of KeyboardInterrupt. ## [0.6.4] - 2018-03-15 ### Fixed - Fixed bad python version requirements. ## [0.6.3] - 2018-03-15 ### Fixed - Fixed bad python version requirements. ## [0.6.2] - 2018-03-15 ### Changed - Removed the memory formatter in progress bars and indicators ### Fixed - Fixed an error in the `call()` method. ## [0.6.1] - 2017-08-07 ### Changed - `psutil` is now opt-in to avoid failed compilations. ## [0.6.0] - 2017-04-21 ### Added - Added a new `completions` command to generate autocompletion scripts. - Added support for command signature inheritance. - Added a new `spin()` helper to display a spinner. ### Changed - Removed the `_completion` command. - Removes ability to choose when a command name is ambiguous. ## [0.5.0] - 2016-09-21 ### Added - Improves terminal handling by adding the `Terminal` class. - Adds methods to write to stderr. - Adds `write()` and `overwrite()` method to commands. - Adds ability to regress progress bar. - Adds ability to choose when a command name is ambiguous. ### Changed - Removes support for decorators and dictionaries declarations. - Simplifies public API for creating arguments and options. - Improves string formatting. - Hides `_completion` command from list. - Improves aliases display. - Displays errors even in quiet mode - Changes console header format - Simplifies the way to create single command application. - Simplifies command testing with user inputs. ## [0.4.1] - 2016-02-09 ### Added - Adding support for Windows ## [0.4] - 2016-01-11 This is a major release with some API changes. ### Added - Commands definition can now be specified with the class docstring (support for string signature) - Two other levels of verbosity (`-vv` and `-vvv`) have been added - Commands description can now be output as json and markdown ### Changed - The `Command` class is now more high-level with a single `handle()` method to override and useful helper methods - The ``ProgressHelper`` has been removed and the ``ProgressBar`` class must be used - The `TableHelper` has largely been improved - `DialogHelper` has been replaced by a more robust `QuestionHelper` - `Command.set_code()` logic has changed to accept a `Command` instance to be able to use the new helper methods - Autocompletion has been improved ### Fixed - Values are now properly cast by validators - Fixing "flag" not being set properly - Progress bar now behaves properly (Fixes [#37](https://github.com/sdispater/cleo/issues/37)) - The `-n|--no-interaction` option behaves properly (Fixes [#38](https://github.com/sdispater/cleo/issues/39) and [#39](https://github.com/sdispater/cleo/issues/39)) [Unreleased]: https://github.com/sdispater/cleo/compare/0.8.1...master [0.8.1]: https://github.com/sdispater/cleo/releases/tag/0.8.1 [0.8.0]: https://github.com/sdispater/cleo/releases/tag/0.8.0 [0.7.6]: https://github.com/sdispater/cleo/releases/tag/0.7.6 [0.7.5]: https://github.com/sdispater/cleo/releases/tag/0.7.5 [0.7.4]: https://github.com/sdispater/cleo/releases/tag/0.7.4 [0.7.3]: https://github.com/sdispater/cleo/releases/tag/0.7.3 [0.7.2]: https://github.com/sdispater/cleo/releases/tag/0.7.2 [0.7.1]: https://github.com/sdispater/cleo/releases/tag/0.7.1 [0.7.0]: https://github.com/sdispater/cleo/releases/tag/0.7.0 [0.6.8]: https://github.com/sdispater/cleo/releases/tag/0.6.8 [0.6.7]: https://github.com/sdispater/cleo/releases/tag/0.6.7 [0.6.6]: https://github.com/sdispater/cleo/releases/tag/0.6.6 [0.6.5]: https://github.com/sdispater/cleo/releases/tag/0.6.5 [0.6.4]: https://github.com/sdispater/cleo/releases/tag/0.6.4 [0.6.3]: https://github.com/sdispater/cleo/releases/tag/0.6.3 [0.6.2]: https://github.com/sdispater/cleo/releases/tag/0.6.2 [0.6.1]: https://github.com/sdispater/cleo/releases/tag/0.6.1 [0.6.0]: https://github.com/sdispater/cleo/releases/tag/0.6.0 [0.5.0]: https://github.com/sdispater/cleo/releases/tag/0.5.0 [0.4.1]: https://github.com/sdispater/cleo/releases/tag/0.4.1 [0.4]: https://github.com/sdispater/cleo/releases/tag/0.4 cleo-0.8.1/LICENSE000066400000000000000000000020451364636233300134610ustar00rootroot00000000000000Copyright (c) 2013 Sébastien Eustace Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.cleo-0.8.1/README.rst000066400000000000000000000352421364636233300141500ustar00rootroot00000000000000Cleo #### .. image:: https://travis-ci.org/sdispater/cleo.png :alt: Cleo Build status :target: https://travis-ci.org/sdispater/cleo Create beautiful and testable command-line interfaces. Cleo is mostly a higher level wrapper for `CliKit `_, so a lot of the components and utilities comes from it. Refer to its documentation for more information. Resources ========= * `Documentation `_ * `Issue Tracker `_ Usage ===== To make a command that greets you from the command line, create ``greet_command.py`` and add the following to it: .. code-block:: python from cleo import Command class GreetCommand(Command): """ Greets someone greet {name? : Who do you want to greet?} {--y|yell : If set, the task will yell in uppercase letters} """ def handle(self): name = self.argument('name') if name: text = 'Hello {}'.format(name) else: text = 'Hello' if self.option('yell'): text = text.upper() self.line(text) You also need to create the file to run at the command line which creates an ``Application`` and adds commands to it: .. code-block:: python #!/usr/bin/env python from greet_command import GreetCommand from cleo import Application application = Application() application.add(GreetCommand()) if __name__ == '__main__': application.run() Test the new command by running the following .. code-block:: bash $ python application.py greet John This will print the following to the command line: .. code-block:: text Hello John You can also use the ``--yell`` option to make everything uppercase: .. code-block:: bash $ python application.py greet John --yell This prints: .. code-block:: text HELLO JOHN As you may have already seen, Cleo uses the command docstring to determine the command definition. The docstring must be in the following form : .. code-block:: python """ Command description Command signature """ The signature being in the following form: .. code-block:: python """ command:name {argument : Argument description} {--option : Option description} """ The signature can span multiple lines. .. code-block:: python """ command:name {argument : Argument description} {--option : Option description} """ Coloring the Output ------------------- Whenever you output text, you can surround the text with tags to color its output. For example: .. code-block:: python # green text self.line('foo') # yellow text self.line('foo') # black text on a cyan background self.line('foo') # white text on a red background self.line('foo') The closing tag can be replaced by ````, which revokes all formatting options established by the last opened tag. It is possible to define your own styles using the ``add_style()`` method: .. code-block:: python self.add_style('fire', fg='red', bg='yellow', options=['bold', 'blink']) self.line('foo') Available foreground and background colors are: ``black``, ``red``, ``green``, ``yellow``, ``blue``, ``magenta``, ``cyan`` and ``white``. And available options are: ``bold``, ``underscore``, ``blink``, ``reverse`` and ``conceal``. You can also set these colors and options inside the tag name: .. code-block:: python # green text self.line('foo') # black text on a cyan background self.line('foo') # bold text on a yellow background self.line('foo') Verbosity Levels ---------------- Cleo has four verbosity levels. These are defined in the ``Output`` class: ======================================= ================================== ====================== Mode Meaning Console option ======================================= ================================== ====================== ``NA`` Do not output any messages ``-q`` or ``--quiet`` ``clikit.VERBOSITY_NORMAL`` The default verbosity level (none) ``clikit.VERBOSITY_VERBOSE`` Increased verbosity of messages ``-v`` ``clikit.VERBOSITY_VERY_VERBOSE`` Informative non essential messages ``-vv`` ``clikit.VERBOSITY_DEBUG`` Debug messages ``-vvv`` ======================================= ================================== ====================== It is possible to print a message in a command for only a specific verbosity level. For example: .. code-block:: python if clikit.VERBOSITY_VERBOSE <= self.io.verbosity: self.line(...) There are also more semantic methods you can use to test for each of the verbosity levels: .. code-block:: python if self.output.is_quiet(): # ... if self.output.is_verbose(): # ... You can also pass the verbosity flag directly to `line()`. .. code-block:: python self.line("", verbosity=clikit.VERBOSITY_VERBOSE) When the quiet level is used, all output is suppressed. Using Arguments --------------- The most interesting part of the commands are the arguments and options that you can make available. Arguments are the strings - separated by spaces - that come after the command name itself. They are ordered, and can be optional or required. For example, add an optional ``last_name`` argument to the command and make the ``name`` argument required: .. code-block:: python class GreetCommand(Command): """ Greets someone greet {name : Who do you want to greet?} {last_name? : Your last name?} {--y|yell : If set, the task will yell in uppercase letters} """ You now have access to a ``last_name`` argument in your command: .. code-block:: python last_name = self.argument('last_name') if last_name: text += ' {}'.format(last_name) The command can now be used in either of the following ways: .. code-block:: bash $ python application.py greet John $ python application.py greet John Doe It is also possible to let an argument take a list of values (imagine you want to greet all your friends). For this it must be specified at the end of the argument list: .. code-block:: python class GreetCommand(Command): """ Greets someone greet {names* : Who do you want to greet?} {--y|yell : If set, the task will yell in uppercase letters} """ To use this, just specify as many names as you want: .. code-block:: bash $ python application.py demo:greet John Jane You can access the ``names`` argument as a list: .. code-block:: python names = self.argument('names') if names: text += ' {}'.format(', '.join(names)) There are 3 argument variants you can use: ================================ ==================================== =============================================================================================================== Mode Notation Value ================================ ==================================== =============================================================================================================== ``clikit.ARGUMENT_REQUIRED`` none (just write the argument name) The argument is required ``clikit.ARGUMENT_OPTIONAL`` ``argument?`` The argument is optional and therefore can be omitted ``clikit.ARGUMENT_MULTI_VALUED`` ``argument*`` The argument can contain an indefinite number of arguments and must be used at the end of the argument list ================================ ==================================== =============================================================================================================== You can combine them like this: .. code-block:: python class GreetCommand(Command): """ Greets someone greet {names?* : Who do you want to greet?} {--y|yell : If set, the task will yell in uppercase letters} """ If you want to set a default value, you can it like so: .. code-block:: text argument=default The argument will then be considered optional. Using Options ------------- Unlike arguments, options are not ordered (meaning you can specify them in any order) and are specified with two dashes (e.g. ``--yell`` - you can also declare a one-letter shortcut that you can call with a single dash like ``-y``). Options are *always* optional, and can be setup to accept a value (e.g. ``--dir=src``) or simply as a boolean flag without a value (e.g. ``--yell``). .. tip:: It is also possible to make an option *optionally* accept a value (so that ``--yell`` or ``--yell=loud`` work). Options can also be configured to accept a list of values. For example, add a new option to the command that can be used to specify how many times in a row the message should be printed: .. code-block:: python class GreetCommand(Command): """ Greets someone greet {name? : Who do you want to greet?} {--y|yell : If set, the task will yell in uppercase letters} {--iterations=1 : How many times should the message be printed?} """ Next, use this in the command to print the message multiple times: .. code-block:: python for _ in range(0, self.option('iterations')): self.line(text) Now, when you run the task, you can optionally specify a ``--iterations`` flag: .. code-block:: bash $ python application.py demo:greet John $ python application.py demo:greet John --iterations=5 The first example will only print once, since ``iterations`` is empty and defaults to ``1``. The second example will print five times. Recall that options don't care about their order. So, either of the following will work: .. code-block:: bash $ python application.py demo:greet John --iterations=5 --yell $ python application.py demo:greet John --yell --iterations=5 There are 4 option variants you can use: ================================ =================================== ====================================================================================== Option Notation Value ================================ =================================== ====================================================================================== ``clikit.OPTION_MULTI_VALUED`` ``--option=*`` This option accepts multiple values (e.g. ``--dir=/foo --dir=/bar``) ``clikit.OPTION_NO_VALUE`` ``--option`` Do not accept input for this option (e.g. ``--yell``) ``clikit.OPTION_REQUIRED_VALUE`` ``--option=`` This value is required (e.g. ``--iterations=5``), the option itself is still optional ``clikit.OPTION_OPTIONAL_VALUE`` ``--option=?`` This option may or may not have a value (e.g. ``--yell`` or ``--yell=loud``) ================================ =================================== ====================================================================================== You can combine them like this: .. code-block:: python class GreetCommand(Command): """ Greets someone greet {name? : Who do you want to greet?} {--y|yell : If set, the task will yell in uppercase letters} {--iterations=?*1 : How many times should the message be printed?} """ Testing Commands ---------------- Cleo provides several tools to help you test your commands. The most useful one is the ``CommandTester`` class. It uses a special IO class to ease testing without a real console: .. code-block:: python import pytest from cleo import Application from cleo import CommandTester def test_execute(self): application = Application() application.add(GreetCommand()) command = application.find('demo:greet') command_tester = CommandTester(command) command_tester.execute() assert "..." == tester.io.fetch_output() The ``CommandTester.io.fetch_output()`` method returns what would have been displayed during a normal call from the console. ``CommandTester.io.fetch_error()`` is also available to get what you have been written to the stderr. You can test sending arguments and options to the command by passing them as a string to the ``CommandTester.execute()`` method: .. code-block:: python import pytest from cleo import Application from cleo import CommandTester def test_execute(self): application = Application() application.add(GreetCommand()) command = application.find('demo:greet') command_tester = CommandTester(command) command_tester.execute("John") assert "John" in tester.io.fetch_output() You can also test a whole console application by using the ``ApplicationTester`` class. Calling an existing Command --------------------------- If a command depends on another one being run before it, instead of asking the user to remember the order of execution, you can call it directly yourself. This is also useful if you want to create a "meta" command that just runs a bunch of other commands. Calling a command from another one is straightforward: .. code-block:: python def handle(self): return_code = self.call('demo:greet', "John --yell") # ... If you want to suppress the output of the executed command, you can use the ``call_silent()`` method instead. Autocompletion -------------- Cleo supports automatic (tab) completion in ``bash``, ``zsh`` and ``fish``. To activate support for autocompletion, pass a ``complete`` keyword when initializing your application: .. code-block:: python application = Application('My Application', '0.1', complete=True) Now, register completion for your application by running one of the following in a terminal, replacing ``[program]`` with the command you use to run your application: .. code-block:: bash # BASH - Ubuntu / Debian [program] completions bash | sudo tee /etc/bash_completion.d/[program].bash-completion # BASH - Mac OSX (with Homebrew "bash-completion") [program] completions bash > $(brew --prefix)/etc/bash_completion.d/[program].bash-completion # ZSH - Config file mkdir ~/.zfunc echo "fpath+=~/.zfunc" >> ~/.zshrc [program] completions zsh > ~/.zfunc/_test # FISH [program] completions fish > ~/.config/fish/completions/[program].fish cleo-0.8.1/cleo/000077500000000000000000000000001364636233300133755ustar00rootroot00000000000000cleo-0.8.1/cleo/__init__.py000066400000000000000000000003201364636233300155010ustar00rootroot00000000000000# -*- coding: utf-8 -*- from .application import Application from .commands import Command from .helpers import argument, option from .testers import ApplicationTester, CommandTester __version__ = "0.8.1" cleo-0.8.1/cleo/_compat.py000066400000000000000000000021631364636233300153730ustar00rootroot00000000000000# -*- coding: utf-8 -*- import sys PY2 = sys.version_info[0] == 2 if PY2: long = long unicode = unicode basestring = basestring else: long = int unicode = str basestring = str def decode(string, encodings=None): if not PY2 and not isinstance(string, bytes): return string if PY2 and isinstance(string, unicode): return string if encodings is None: encodings = ["utf-8", "latin1", "ascii"] for encoding in encodings: try: return string.decode(encoding) except (UnicodeEncodeError, UnicodeDecodeError): pass return string.decode(encodings[0], errors="ignore") def encode(string, encodings=None): if not PY2 and isinstance(string, bytes): return string if PY2 and isinstance(string, str): return string if encodings is None: encodings = ["utf-8", "latin1", "ascii"] for encoding in encodings: try: return string.encode(encoding) except (UnicodeEncodeError, UnicodeDecodeError): pass return string.encode(encodings[0], errors="ignore") cleo-0.8.1/cleo/application.py000066400000000000000000000027611364636233300162600ustar00rootroot00000000000000from typing import Optional from typing import Tuple from clikit.console_application import ConsoleApplication from .commands import BaseCommand from .commands.completions_command import CompletionsCommand from .config import ApplicationConfig class Application(ConsoleApplication, object): """ An Application is the container for a collection of commands. This class is optimized for a standard CLI environment. Usage: >>> app = Application('myapp', '1.0 (stable)') >>> app.add(HelpCommand()) >>> app.run() """ def __init__( self, name=None, version=None, complete=True, config=None ): # type: (str, str, bool, Optional[ApplicationConfig]) -> None if config is None: config = ApplicationConfig(name, version) super(Application, self).__init__(config) if complete: self.add(CompletionsCommand()) def add_commands(self, *commands): # type: (Tuple[BaseCommand]) -> None for command in commands: self.add(command) def add(self, command): # type: (BaseCommand) -> Application """ Adds a command object. """ self.add_command(command.config) command.set_application(self) return self def find(self, name): # type: (str) -> BaseCommand names = name.split(" ") command = self.get_command(names[0]) for name in names[1:]: command = command.get_sub_command(name) return command.config.handler cleo-0.8.1/cleo/commands/000077500000000000000000000000001364636233300151765ustar00rootroot00000000000000cleo-0.8.1/cleo/commands/__init__.py000066400000000000000000000002021364636233300173010ustar00rootroot00000000000000# -*- coding: utf-8 -*- from .base_command import BaseCommand from .command import Command __all__ = ["BaseCommand", "Command"] cleo-0.8.1/cleo/commands/base_command.py000066400000000000000000000041101364636233300201540ustar00rootroot00000000000000from typing import Optional from clikit.api.args import Args from clikit.api.command import Command as CliKitCommand from clikit.api.config.command_config import CommandConfig from cleo.io import ConsoleIO class CommandError(Exception): pass class BaseCommand(object): name = None description = None help = None arguments = [] options = [] aliases = [] enabled = True hidden = False commands = [] def __init__(self): self._application = None self._config = CommandConfig(self.name) self._config.set_description(self.description) self._config.set_help(self.help) for argument in self.arguments: self._config._format_builder.add_argument(argument) for option in self.options: self._config._format_builder.add_option(option) for alias in self.aliases: self._config.add_alias(alias) if not self.enabled: self._config.disable() if self.hidden: self._config.hide() if self.commands: for command in self.commands: self.add_sub_command(command) self._config.set_handler(self) @property def config(self): # type: () -> CommandConfig return self._config @property def application(self): return self._application def handle( self, args, io, command ): # type: (Args, ConsoleIO, CliKitCommand) -> Optional[int] raise NotImplementedError() def set_application(self, application): self._application = application for command in self.commands: command.set_application(application) def add_sub_command(self, command): # type: (BaseCommand) -> None self._config.add_sub_command_config(command.config) command.set_application(self.application) def default(self, default=True): # type: (bool) -> BaseCommand self._config.default(default) return self def anonymous(self): # type: () -> BaseCommand self._config.anonymous() return self cleo-0.8.1/cleo/commands/command.py000066400000000000000000000207621364636233300171750ustar00rootroot00000000000000import re from typing import Optional from clikit.api.args import Args from clikit.api.args.format import ArgsFormat from clikit.api.formatter import Style from clikit.api.io import IO from clikit.args import StringArgs from clikit.io import NullIO from clikit.ui.components import ChoiceQuestion from clikit.ui.components import ConfirmationQuestion from clikit.ui.components import ProgressIndicator from clikit.ui.components import Question from clikit.ui.components import Table from clikit.ui.style import TableStyle from cleo.io import ConsoleIO from cleo.parser import Parser from .base_command import BaseCommand class Command(BaseCommand): signature = None validation = None TABLE_STYLES = { "ascii": TableStyle.ascii(), "borderless": TableStyle.borderless(), "solid": TableStyle.solid(), "compact": TableStyle.compact(), } def __init__(self): self._args = Args(ArgsFormat()) self._io = None self._command = None super(Command, self).__init__() doc = self.__doc__ or super(self.__class__, self).__doc__ if doc: self._parse_doc(doc) if not self.signature: parent = super(self.__class__, self) if hasattr(parent, "signature"): self.signature = parent.signature if self.signature: self._configure_using_fluent_definition() self._config.set_handler_method("wrap_handle") @property def io(self): # type: () -> ConsoleIO return self._io def _parse_doc(self, doc): doc = doc.strip().split("\n", 1) if len(doc) > 1: self._config.set_description(doc[0].strip()) self.signature = re.sub(r"\s{2,}", " ", doc[1].strip()) else: self._config.set_description(doc[0].strip()) def _configure_using_fluent_definition(self): """ Configure the console command using a fluent definition. """ definition = Parser.parse(self.signature) self._config.set_name(definition["name"]) for name, flags, description, default in definition["arguments"]: self._config.add_argument(name, flags, description, default) for long_name, short_name, flags, description, default in definition["options"]: self._config.add_option(long_name, short_name, flags, description, default) def wrap_handle( self, args, io, command ): # type: (Args, IO, CliKitCommand) -> Optional[int] self._args = args self._io = io self._command = command return self.handle() def handle(self): # type: () -> Optional[int] """ Executes the command. """ raise NotImplementedError() def call(self, name, args=None): # type: (str, Optional[str]) -> int """ Call another command. """ if args is None: args = "" args = StringArgs(args) command = self.application.get_command(name) return command.run(args, self.io) def call_silent(self, name, args=None): # type: (str, Optional[str]) -> int """ Call another command. """ if args is None: args = "" args = StringArgs(args) command = self.application.get_command(name) return command.run(args, NullIO()) def argument(self, key=None): """ Get the value of a command argument. """ if key is None: return self._args.arguments() return self._args.argument(key) def option(self, key=None): """ Get the value of a command option. """ if key is None: return self._args.options() return self._args.option(key) def confirm(self, question, default=False, true_answer_regex="(?i)^y"): """ Confirm a question with the user. """ return self._io.confirm(question, default, true_answer_regex) def ask(self, question, default=None): """ Prompt the user for input. """ if isinstance(question, Question): return self._io.ask_question(question) return self._io.ask(question, default) def secret(self, question): """ Prompt the user for input but hide the answer from the console. """ return self._io.ask_hidden(question) def choice(self, question, choices, default=None, attempts=None, multiple=False): """ Give the user a single choice from an list of answers. """ question = ChoiceQuestion(question, choices, default) question.set_max_attempts(attempts) question.set_multi_select(multiple) return self._io.ask_question(question) def create_question(self, question, type=None, **kwargs): """ Returns a Question of specified type. """ if not type: return Question(question, **kwargs) if type == "choice": return ChoiceQuestion(question, **kwargs) if type == "confirmation": return ConfirmationQuestion(question, **kwargs) def table(self, header=None, rows=None, style=None): """ Return a Table instance. """ if style is not None: style = self.TABLE_STYLES[style] table = Table(style) if header: table.set_header_row(header) if rows: table.set_rows(rows) return table def render_table(self, headers, rows, style=None): """ Format input to textual table. """ table = self.table(headers, rows, style) table.render(self._io) def write(self, text, style=None): """ Writes a string without a new line. Useful if you want to use overwrite(). """ if style: styled = "<%s>%s" % (style, text) else: styled = text self._io.write(styled) def line(self, text, style=None, verbosity=None): """ Write a string as information output. """ if style: styled = "<%s>%s" % (style, text) else: styled = text self._io.write_line(styled, verbosity) def line_error(self, text, style=None, verbosity=None): """ Write a string as information output to stderr. """ if style: styled = "<%s>%s" % (style, text) else: styled = text self._io.error_line(styled, verbosity) def info(self, text): """ Write a string as information output. :param text: The line to write :type text: str """ self.line(text, "info") def comment(self, text): """ Write a string as comment output. :param text: The line to write :type text: str """ self.line(text, "comment") def question(self, text): """ Write a string as question output. :param text: The line to write :type text: str """ self.line(text, "question") def progress_bar(self, max=0): """ Creates a new progress bar :param max: The maximum number of steps :type max: int :rtype: ProgressBar """ return self._io.progress_bar(max) def progress_indicator(self, fmt=None, interval=100, values=None): """ Creates a new progress indicator. """ return ProgressIndicator(self.io, fmt, interval, values) def spin(self, start_message, end_message, fmt=None, interval=100, values=None): """ Automatically spin a progress indicator. """ spinner = ProgressIndicator(self.io, fmt, interval, values) return spinner.auto(start_message, end_message) def add_style(self, name, fg=None, bg=None, options=None): """ Adds a new style """ style = Style(name) if fg is not None: style.fg(fg) if bg is not None: style.bg(bg) if options is not None: if "bold" in options: style.bold() if "underline" in options: style.underlined() self._io.output.formatter.add_style(style) self._io.error_output.formatter.add_style(style) def overwrite(self, text, size=None): """ Overwrites the current line. It will not add a new line so use line('') if necessary. """ self._io.overwrite(text, size=size) cleo-0.8.1/cleo/commands/completions/000077500000000000000000000000001364636233300175325ustar00rootroot00000000000000cleo-0.8.1/cleo/commands/completions/__init__.py000066400000000000000000000000301364636233300216340ustar00rootroot00000000000000# -*- coding: utf-8 -*- cleo-0.8.1/cleo/commands/completions/templates.py000066400000000000000000000042131364636233300221020ustar00rootroot00000000000000# -*- coding: utf-8 -*- BASH_TEMPLATE = """%(function)s() { local cur script coms opts com COMPREPLY=() _get_comp_words_by_ref -n : cur words # for an alias, get the real script behind it if [[ $(type -t ${words[0]}) == "alias" ]]; then script=$(alias ${words[0]} | sed -E "s/alias ${words[0]}='(.*)'/\\1/") else script=${words[0]} fi # lookup for command for word in ${words[@]:1}; do if [[ $word != -* ]]; then com=$word break fi done # completing for an option if [[ ${cur} == --* ]] ; then opts="%(opts)s" case "$com" in %(command_list)s esac COMPREPLY=($(compgen -W "${opts}" -- ${cur})) __ltrim_colon_completions "$cur" return 0; fi # completing for a command if [[ $cur == $com ]]; then coms="%(coms)s" COMPREPLY=($(compgen -W "${coms}" -- ${cur})) __ltrim_colon_completions "$cur" return 0 fi } %(compdefs)s""" ZSH_TEMPLATE = """#compdef %(script_name)s %(function)s() { local state com cur cur=${words[${#words[@]}]} # lookup for command for word in ${words[@]:1}; do if [[ $word != -* ]]; then com=$word break fi done if [[ ${cur} == --* ]]; then state="option" opts=(%(opts)s) elif [[ $cur == $com ]]; then state="command" coms=(%(coms)s) fi case $state in (command) _describe 'command' coms ;; (option) case "$com" in %(command_list)s esac _describe 'option' opts ;; *) # fallback to file completion _arguments '*:file:_files' esac } %(function)s "$@" %(compdefs)s""" FISH_TEMPLATE = """function __fish%(function)s_no_subcommand for i in (commandline -opc) if contains -- $i %(cmds_names)s return 1 end end return 0 end # global options %(opts)s # commands %(cmds)s # command options %(cmds_opts)s""" TEMPLATES = {"bash": BASH_TEMPLATE, "zsh": ZSH_TEMPLATE, "fish": FISH_TEMPLATE} cleo-0.8.1/cleo/commands/completions_command.py000066400000000000000000000333131364636233300216050ustar00rootroot00000000000000# -*- coding: utf-8 -*- import os import hashlib import posixpath import re import subprocess from .._compat import encode from ..helpers import argument from ..helpers import option from .command import Command from .completions.templates import TEMPLATES class CompletionsCommand(Command): name = "completions" description = "Generate completion scripts for your shell." arguments = [ argument("shell", "The shell to generate the scripts for.", optional=True) ] options = [ option( "alias", None, "Alias for the current command.", flag=False, multiple=True ) ] SUPPORTED_SHELLS = ("bash", "zsh", "fish") hidden = True help = """ One can generate a completion script for `{script_name}` that is compatible with \ a given shell. The script is output on `stdout` allowing one to re-direct \ the output to the file of their choosing. Where you place the file will \ depend on which shell, and which operating system you are using. Your \ particular configuration may also determine where these scripts need \ to be placed. Here are some common set ups for the three supported shells under \ Unix and similar operating systems (such as GNU/Linux). BASH: Completion files are commonly stored in `/etc/bash_completion.d/` Run the command: `{script_name} {command_name} bash > /etc/bash_completion.d/{script_name}.bash-completion` This installs the completion script. You may have to log out and log \ back in to your shell session for the changes to take effect. FISH: Fish completion files are commonly stored in\ `$HOME/.config/fish/completions` Run the command: `{script_name} {command_name} fish > ~/.config/fish/completions/{script_name}.fish` This installs the completion script. You may have to log out and log \ back in to your shell session for the changes to take effect. ZSH: ZSH completions are commonly stored in any directory listed in your \ `$fpath` variable. To use these completions, you must either add the \ generated script to one of those directories, or add your own \ to this list. Adding a custom directory is often the safest best if you're unsure \ of which directory to use. First create the directory, for this \ example we'll create a hidden directory inside our `$HOME` directory `mkdir ~/.zfunc` Then add the following lines to your `.zshrc` just before `compinit` `fpath+=~/.zfunc` Now you can install the completions script using the following command `{script_name} {command_name} zsh > ~/.zfunc/_{script_name}` You must then either log out and log back in, or simply run `exec zsh` For the new completions to take affect. CUSTOM LOCATIONS: Alternatively, you could save these files to the place of your choosing, \ such as a custom directory inside your $HOME. Doing so will require you \ to add the proper directives, such as `source`ing inside your login \ script. Consult your shells documentation for how to add such directives. """ def handle(self): # type: () -> int shell = self.argument("shell") if not shell: shell = self.get_shell_type() if shell not in self.SUPPORTED_SHELLS: raise ValueError( "[shell] argument must be one of {}".format( ", ".join(self.SUPPORTED_SHELLS) ) ) self.line(self.render(shell)) return 0 def render(self, shell): # type: (str) -> str return getattr(self, "render_{}".format(shell))() def render_bash(self): # type: () -> str template = TEMPLATES["bash"] script_path = posixpath.realpath(self._args.script_name) script_name = os.path.basename(script_path) aliases = [script_name, script_path] aliases += self.option("alias") function = self._generate_function_name(script_name, script_path) commands = [] global_options = set() options_descriptions = {} commands_options = {} for option in self.application.config.options.values(): options_descriptions["--" + option.long_name] = self.io.remove_format( option.description ) global_options.add("--" + option.long_name) for command in self.application.commands: command_config = command.config if not command_config.is_enabled() or command_config.is_hidden(): continue command_options = [] commands.append(command_config.name) options = command_config.options for name in sorted(options.keys()): option = options[name] name = "--" + option.long_name description = option.description command_options.append(name) options_descriptions[name] = description commands_options[command_config.name] = command_options compdefs = "\n".join( [ "complete -o default -F {} {}".format(function, alias) for alias in aliases ] ) commands = sorted(commands) command_list = [] for i, command in enumerate(commands): options = set(commands_options[command]).difference(global_options) options = sorted(options) options = [self._zsh_describe(opt, None).strip('"') for opt in options] desc = [ " ({})".format(command), ' opts="${{opts}} {}"'.format(" ".join(options)), " ;;", ] if i < len(commands) - 1: desc.append("") command_list.append("\n".join(desc)) output = template % { "script_name": script_name, "function": function, "opts": " ".join(sorted(global_options)), "coms": " ".join(commands), "command_list": "\n".join(command_list), "compdefs": compdefs, } return output def render_zsh(self): template = TEMPLATES["zsh"] script_path = posixpath.realpath(self._args.script_name) script_name = os.path.basename(script_path) aliases = [script_path] aliases += self.option("alias") function = self._generate_function_name(script_name, script_path) global_options = set() commands_descriptions = [] options_descriptions = {} commands_options_descriptions = {} commands_options = {} for option in self.application.config.options.values(): options_descriptions["--" + option.long_name] = self.io.remove_format( option.description ) global_options.add("--" + option.long_name) for command in self.application.commands: command_config = command.config if not command_config.is_enabled() or command_config.is_hidden(): continue command_options = [] commands_options_descriptions[command_config.name] = {} command_description = self._io.remove_format(command_config.description) commands_descriptions.append( self._zsh_describe(command_config.name, command_description) ) options = command_config.options for name in sorted(options.keys()): option = options[name] name = "--" + option.long_name description = self.io.remove_format(option.description) command_options.append(name) options_descriptions[name] = description commands_options_descriptions[command_config.name][name] = description commands_options[command_config.name] = command_options compdefs = "\n".join( ["compdef {} {}".format(function, alias) for alias in aliases] ) commands = sorted(list(commands_options.keys())) command_list = [] for i, command in enumerate(commands): options = set(commands_options[command]).difference(global_options) options = sorted(options) options = [ self._zsh_describe(opt, commands_options_descriptions[command][opt]) for opt in options ] desc = [ " ({})".format(command), " opts+=({})".format(" ".join(options)), " ;;", ] if i < len(commands) - 1: desc.append("") command_list.append("\n".join(desc)) opts = [] for opt in global_options: opts.append(self._zsh_describe(opt, options_descriptions[opt])) output = template % { "script_name": script_name, "function": function, "opts": " ".join(sorted(opts)), "coms": " ".join(sorted(commands_descriptions)), "command_list": "\n".join(command_list), "compdefs": compdefs, } return output def render_fish(self): template = TEMPLATES["fish"] script_path = posixpath.realpath(self._args.script_name) script_name = os.path.basename(script_path) aliases = [script_name] aliases += self.option("alias") function = self._generate_function_name(script_name, script_path) global_options = set() commands_descriptions = {} options_descriptions = {} commands_options_descriptions = {} commands_options = {} for option in self.application.config.options.values(): options_descriptions["--" + option.long_name] = self.io.remove_format( option.description ) global_options.add("--" + option.long_name) for command in self.application.commands: command_config = command.config if not command_config.is_enabled() or command_config.is_hidden(): continue command_options = [] commands_options_descriptions[command_config.name] = {} commands_descriptions[command_config.name] = self._io.remove_format( command_config.description ) options = command_config.options for name in sorted(options.keys()): option = options[name] name = "--" + option.long_name description = self._io.remove_format(option.description) command_options.append(name) options_descriptions[name] = description commands_options_descriptions[command_config.name][name] = description commands_options[command_config.name] = command_options opts = [] for opt in sorted(global_options): opts.append( "complete -c {} -n '__fish{}_no_subcommand' " "-l {} -d '{}'".format( script_name, function, opt[2:], options_descriptions[opt].replace("'", "\\'"), ) ) cmds_names = sorted(list(commands_options.keys())) cmds = [] cmds_opts = [] for i, cmd in enumerate(cmds_names): cmds.append( "complete -c {} -f -n '__fish{}_no_subcommand' " "-a {} -d '{}'".format( script_name, function, cmd, commands_descriptions[cmd].replace("'", "\\'"), ) ) cmds_opts += ["# {}".format(cmd)] options = set(commands_options[cmd]).difference(global_options) options = sorted(options) for opt in options: cmds_opts.append( "complete -c {} -A -n '__fish_seen_subcommand_from {}' " "-l {} -d '{}'".format( script_name, cmd, opt[2:], commands_options_descriptions[cmd][opt].replace("'", "\\'"), ) ) if i < len(cmds_names) - 1: cmds_opts.append("") output = template % { "script_name": script_name, "function": function, "cmds_names": " ".join(cmds_names), "opts": "\n".join(opts), "cmds": "\n".join(cmds), "cmds_opts": "\n".join(cmds_opts), } return output def get_shell_type(self): shell = os.getenv("SHELL") if not shell: raise RuntimeError( "Could not read SHELL environment variable. " "Please specify your shell type by passing it as the first argument." ) return os.path.basename(shell) def _generate_function_name(self, script_name, script_path): return "_{}_{}_complete".format( self._sanitize_for_function_name(script_name), hashlib.md5(encode(script_path)).hexdigest()[0:16], ) def _sanitize_for_function_name(self, name): name = name.replace("-", "_") return re.sub("[^A-Za-z0-9_]+", "", name) def _zsh_describe(self, value, description=None): value = '"' + value.replace(":", "\\:") if description: description = re.sub( r'(["\'#&;`|*?~<>^()\[\]{}$\\\x0A\xFF])', r"\\\1", description ) value += ":{}".format(subprocess.list2cmdline([description]).strip('"')) value += '"' return value cleo-0.8.1/cleo/config/000077500000000000000000000000001364636233300146425ustar00rootroot00000000000000cleo-0.8.1/cleo/config/__init__.py000066400000000000000000000000621364636233300167510ustar00rootroot00000000000000from .application_config import ApplicationConfig cleo-0.8.1/cleo/config/application_config.py000066400000000000000000000005461364636233300210510ustar00rootroot00000000000000from clikit.config import DefaultApplicationConfig from cleo.io import ConsoleIO class ApplicationConfig(DefaultApplicationConfig): """ Cleo's default application configuration. """ def configure(self): # type: () -> None super(ApplicationConfig, self).configure() @property def io_class(self): return ConsoleIO cleo-0.8.1/cleo/helpers.py000066400000000000000000000020231364636233300154060ustar00rootroot00000000000000from typing import Any from typing import Optional from clikit.api.args.format import Argument from clikit.api.args.format import Option def argument( name, description=None, optional=False, multiple=False, default=None ): # type: (str, Optional[str], bool, bool, Optional[Any]) -> Argument if optional: flags = Argument.OPTIONAL else: flags = Argument.REQUIRED if multiple: flags |= Argument.MULTI_VALUED return Argument(name, flags, description, default) def option( long_name, short_name=None, description=None, flag=True, value_required=True, multiple=False, default=None, ): # type: (str, Optional[str], Optional[str], bool, bool, bool, Optional[Any]) -> Option if flag: flags = Option.NO_VALUE elif value_required: flags = Option.REQUIRED_VALUE else: flags = Option.OPTIONAL_VALUE if multiple and not flag: flags |= Option.MULTI_VALUED return Option(long_name, short_name, flags, description, default) cleo-0.8.1/cleo/io/000077500000000000000000000000001364636233300140045ustar00rootroot00000000000000cleo-0.8.1/cleo/io/__init__.py000066400000000000000000000001061364636233300161120ustar00rootroot00000000000000from .buffered_io import BufferedIO from .console_io import ConsoleIO cleo-0.8.1/cleo/io/buffered_io.py000066400000000000000000000004461364636233300166330ustar00rootroot00000000000000from clikit.io import BufferedIO as BaseBufferedIO from .io_mixin import IOMixin class BufferedIO(IOMixin, BaseBufferedIO): """ An I/O that reads from and writes to a buffer. """ def __init__(self, *args, **kwargs): super(BufferedIO, self).__init__(*args, **kwargs) cleo-0.8.1/cleo/io/console_io.py000066400000000000000000000004271364636233300165120ustar00rootroot00000000000000from clikit.io import ConsoleIO as BaseConsoleIO from .io_mixin import IOMixin class ConsoleIO(IOMixin, BaseConsoleIO): """ A wrapper around CliKit's ConsoleIO. """ def __init__(self, *args, **kwargs): super(ConsoleIO, self).__init__(*args, **kwargs) cleo-0.8.1/cleo/io/io_mixin.py000066400000000000000000000061131364636233300161720ustar00rootroot00000000000000from clikit.ui.components import ChoiceQuestion from clikit.ui.components import ConfirmationQuestion from clikit.ui.components import ProgressBar from clikit.ui.components import Question class IOMixin(object): """ Helpers for IO classes """ def __init__(self, *args, **kwargs): super(IOMixin, self).__init__(*args, **kwargs) self._last_message = "" self._last_message_err = "" def progress_bar(self, max=0): # type: (int) -> ProgressBar """ Create a new progress bar """ return ProgressBar(self, max) def ask(self, question, default=None): question = Question(question, default) return self.ask_question(question) def ask_hidden(self, question): question = Question(question) question.hide() return self.ask_question(question) def confirm(self, question, default=True, true_answer_regex="(?i)^y"): return self.ask_question( ConfirmationQuestion(question, default, true_answer_regex) ) def choice(self, question, choices, default=None): if default is not None: default = choices[default] return self.ask_question(ChoiceQuestion(question, choices, default)) def ask_question(self, question): """ Asks a question. """ answer = question.ask(self) return answer def write(self, string, flags=0): super(IOMixin, self).write(string, flags) self._last_message = string def error(self, string, flags=0): super(IOMixin, self).error(string, flags) self._last_message = string def write_line(self, string, flags=0): super(IOMixin, self).write_line(string, flags) self._last_message = string def error_line(self, string, flags=0): super(IOMixin, self).error_line(string, flags) self._last_message = string def overwrite(self, message, size=None): self._do_overwrite(message, size) def overwrite_error(self, message, size=None): self._do_overwrite(message, size, True) def _do_overwrite(self, message, size=None, stderr=False): output = self.output if stderr: output = self.error_output # since overwrite is supposed to overwrite last message... if size is None: # removing possible formatting of lastMessage with strip_tags if stderr: last_message = self._last_message_err else: last_message = self._last_message size = len(output.remove_format(last_message)) # ...let's fill its length with backspaces output.write("\x08" * size) # write the new message output.write(message) fill = size - len(output.remove_format(message)) if fill > 0: # whitespace whatever has left output.write(" " * fill) # move the cursor back output.write("\x08" * fill) if stderr: self._last_message_err = message else: self._last_message = message cleo-0.8.1/cleo/parser.py000066400000000000000000000120611364636233300152430ustar00rootroot00000000000000import re import os from collections import namedtuple from clikit.api.args.format import Argument from clikit.api.args.format import Option _argument = namedtuple("argument", "name flags description default") _option = namedtuple("option", "long_name short_name flags description default") class Parser(object): @classmethod def parse(cls, expression): """ Parse the given console command definition into a dict. :param expression: The expression to parse :type expression: str :rtype: dict """ parsed = {"name": None, "arguments": [], "options": []} if not expression.strip(): raise ValueError("Console command signature is empty.") expression = expression.replace(os.linesep, "") matches = re.match(r"[^\s]+", expression) if not matches: raise ValueError("Unable to determine command name from signature.") name = matches.group(0) parsed["name"] = name tokens = re.findall(r"\{\s*(.*?)\s*\}", expression) if tokens: parsed.update(cls._parameters(tokens)) return parsed @classmethod def _parameters(cls, tokens): """ Extract all of the parameters from the tokens. :param tokens: The tokens to extract the parameters from :type tokens: list :rtype: dict """ arguments = [] options = [] for token in tokens: if not token.startswith("--"): arguments.append(cls._parse_argument(token)) else: options.append(cls._parse_option(token)) return {"arguments": arguments, "options": options} @classmethod def _parse_argument(cls, token): """ Parse an argument expression. :param token: The argument expression :type token: str :rtype: InputArgument """ description = "" validator = None if " : " in token: token, description = tuple(token.split(" : ", 2)) token = token.strip() description = description.strip() # Checking validator: matches = re.match(r"(.*)\((.*?)\)", token) if matches: token = matches.group(1).strip() validator = matches.group(2).strip() if token.endswith("?*"): return _argument( token.rstrip("?*"), Argument.MULTI_VALUED | Argument.OPTIONAL, description, None, ) elif token.endswith("*"): return _argument( token.rstrip("*"), Argument.MULTI_VALUED | Argument.REQUIRED, description, None, ) elif token.endswith("?"): return _argument(token.rstrip("?"), Argument.OPTIONAL, description, None) matches = re.match(r"(.+)=(.+)", token) if matches: return _argument( matches.group(1), Argument.OPTIONAL, description, matches.group(2) ) return _argument(token, Argument.REQUIRED, description, None) @classmethod def _parse_option(cls, token): """ Parse an option expression. :param token: The option expression :type token: str :rtype: InputOption """ description = "" validator = None if " : " in token: token, description = tuple(token.split(" : ", 2)) token = token.strip() description = description.strip() # Checking validator: matches = re.match(r"(.*)\((.*?)\)", token) if matches: token = matches.group(1).strip() validator = matches.group(2).strip() shortcut = None matches = re.split(r"\s*\|\s*", token, 2) if len(matches) > 1: shortcut = matches[0].lstrip("-") token = matches[1] else: token = token.lstrip("-") default = None mode = Option.NO_VALUE if token.endswith("=*"): mode = Option.MULTI_VALUED token = token.rstrip("=*") elif token.endswith("=?*"): mode = Option.MULTI_VALUED token = token.rstrip("=?*") elif token.endswith("=?"): mode = Option.OPTIONAL_VALUE token = token.rstrip("=?") elif token.endswith("="): mode = Option.REQUIRED_VALUE token = token.rstrip("=") matches = re.match(r"(.+)(=[?*]*)(.+)", token) if matches: token = matches.group(1) operator = matches.group(2) default = matches.group(3) if operator == "=*": mode = Option.REQUIRED_VALUE | Option.MULTI_VALUED elif operator == "=?*": mode = Option.MULTI_VALUED elif operator == "=?": mode = Option.OPTIONAL_VALUE elif operator == "=": mode = Option.REQUIRED_VALUE return _option(token, shortcut, mode, description, default) cleo-0.8.1/cleo/testers/000077500000000000000000000000001364636233300150665ustar00rootroot00000000000000cleo-0.8.1/cleo/testers/__init__.py000066400000000000000000000002471364636233300172020ustar00rootroot00000000000000# -*- coding: utf-8 -*- from .application_tester import ApplicationTester from .command_tester import CommandTester __all__ = ["ApplicationTester", "CommandTester"] cleo-0.8.1/cleo/testers/application_tester.py000066400000000000000000000025741364636233300213410ustar00rootroot00000000000000from clikit.args import StringArgs from cleo import Application from cleo.io import BufferedIO class ApplicationTester(object): """ Eases the testing of console applications. """ def __init__(self, application): # type: (Application) -> None self._application = application self._application.config.set_terminate_after_run(False) self._io = BufferedIO() self._status_code = 0 @property def io(self): # type: () -> BufferedIO return self._io @property def status_code(self): # type: () -> int return self._status_code def execute(self, args, **options): # type: (str, ...) -> int """ Executes the command Available options: * interactive: Sets the input interactive flag * verbosity: Sets the output verbosity flag """ args = StringArgs(args) if "inputs" in options: self._io.set_input(options["inputs"]) if "interactive" in options: self._io.set_interactive(options["interactive"]) if "verbosity" in options: self._io.set_verbosity(options["verbosity"]) self._status_code = self._application.run( args, self._io.input.stream, self._io.output.stream, self._io.error_output.stream, ) return self._status_code cleo-0.8.1/cleo/testers/command_tester.py000066400000000000000000000034771364636233300204570ustar00rootroot00000000000000from clikit.api.command import Command from clikit.args import StringArgs from clikit.formatter import AnsiFormatter from cleo.commands import BaseCommand from cleo.io import BufferedIO class CommandTester(object): """ Eases the testing of console commands. """ def __init__(self, command): # type: (BaseCommand) -> None """ Constructor """ self._command = command self._io = BufferedIO() self._inputs = [] self._status_code = None if self._command.application: for style in self._command.application.config.style_set.styles.values(): self._io.output.formatter.add_style(style) self._io.error_output.formatter.add_style(style) @property def io(self): # type: () -> BufferedIO return self._io @property def status_code(self): # type: () -> int return self._status_code def execute(self, args="", **options): # type: (str, ...) -> int """ Executes the command Available options: * interactive: Sets the input interactive flag * decorated: Sets the output decorated flag * verbosity: Sets the output verbosity flag """ args = StringArgs(args) if "inputs" in options: self._io.set_input(options["inputs"]) if "interactive" in options: self._io.set_interactive(options["interactive"]) if "verbosity" in options: self._io.set_verbosity(options["verbosity"]) if "decorated" in options and options["decorated"]: self._io.set_formatter(AnsiFormatter(forced=True)) command = Command(self._command.config, self._command.application) self._status_code = command.run(args, self._io) return self._status_code cleo-0.8.1/docs/000077500000000000000000000000001364636233300134035ustar00rootroot00000000000000cleo-0.8.1/docs/.gitignore000066400000000000000000000000071364636233300153700ustar00rootroot00000000000000_build cleo-0.8.1/docs/Makefile000066400000000000000000000151421364636233300150460ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Cleo.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Cleo.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/Cleo" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Cleo" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." cleo-0.8.1/docs/_static/000077500000000000000000000000001364636233300150315ustar00rootroot00000000000000cleo-0.8.1/docs/_static/progress.gif000066400000000000000000000370721364636233300173750ustar00rootroot00000000000000GIF89a@!1 HFHQOQRQSX\b^decefgjnknpprtrstu{uwyzz}~~Ā̀ӀӑƓeꖦߘ՝oƪ֪߫󮰸ǯͱҴ˸нþč˿ĩ˨˹̥ηϡϩϿŢɻٳٿ߳ȳ㣩㮰㹁㽊䡣朚ֿغͻϮ좢x캂ƜテѣmŔǶϭԲÊؾqڹוڰ֡! NETSCAPE2.0!,@! H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ }УHܶdFJʓ)G j‹q(2K~YKۦ?ra5دߵjծ;ӪDZ>TUkO1:3w`8TdAҞ$I[f11af `7JBaj7jĢe65qjS<(͟cM'M(QK\cm (S"DD)r-7ƬU 5é2"0-15$vpLT{l@Efbc"YFa?hY7PT%TX=XUh]Z2Fc8c/ b+bY\Ex8)PT:B5?BH$GFAD5P1;J& V(@[ ~,8-tq0  ލB!, m޸ -^ =Ex * z*hjOEZ(ˠ"&h`a]PJfS#;a|xU!#b>{` ~? m>MX1LAM\A/VDMԱM?,aRſW4Qw6r,ܱqXسJ 7?\pD!Q0iM(4Tpq ,D}01"S2'-̵3 9=?3L023B? jq BXH`WO9&^LakUe)ۏ&8C!ڃ*Aٝ0 T V1T\SXmlVT#LUt=+ 1EKVTU"uXG򝅁\@M8s!bڻ(&pKN/n[I 8/0U ZGҗ;aW8F}QXZhA!k"uG88iL# T81B*6`TDC*X<% A0P `j(KJT+\%DRRY?ǕrRC  [T=Mo١Cƈ`f<6 a2U0 !-(^;"80~*`?lj8 0+p9ч2@e ưGS졍 rT@Ir G=Ct#7pdʩzL8)z3+QQu^g6ҩ~ԞoYO ?!u“oy(@9Ї^e7bmسH>"݂ ($b>51PQKl>S5!m3,i O0砃9M378# MQ]L6I? @ D?Y2B?K7>FPe0 '`I#pcG+LB4AAphN&CB=W P4-6oS4hLD ? E@<Y6+9;W.(#H@W DjTEl}8&(zK tM5ӇXk@J 0(YQO`a)0"61m $hUS CǞ+Ds ^@T3FTj K)P0-KuhRT- JeH'=_SuPC!,? !&\s`$XB ;!,? !,? !,'HП?*\ȰÇ#JHŋ3jϡ={4ɓ(S\b~(mkI͛8sLs G֬m8@ҦIJ*F{P &Tƈ aZ,rPxkK^jJ B $-hoS߽G; :\=x> gԈ$z˷ #jӌ%0+Y #{kAf=qX͝pވ=:Tk74㿏SPa(*@a@5@E= SAנW2AO=ܔM0lCbO;L;LQ10SSO?Ө6 u dE n|=G i> _IXsM>ɘC98Λ#M'渉y4s9p357r#Pd&:= # f%~ υE~3Ё(]s@$ H&L2 %C,N pJ+w0#(<5?B%0>4@(5#P8(<|S/9ջZkr?h zb[e d LQp1 R6^h\|G"ȇ!#,smAp@@d](," *^O&p20@)˕5PE2ޖlU, Ң\i/pTdL%3[ $m I%Ї|xQбv?W#$V tP?ʪ4t?dD!, %H*\ȰÇ#JHŋȱǏ CIɓ(S\IrBm3͛8sꜨlgIx 糨QDQoyX'О 2pG5!,U0U\ EKhR_] j4n|Gt 2Pٔ*{ ,eҢeM@L[i/c@5ϲN`=n@ z^4D)NguntimV?`$0o/ !,? !,? !,'H?*\ȰÇكHŋ3jȱE籤ɓ(S\2Ȅ!3M۶yϟ7%7'EpG(ƙ0˦TsʵәGR/F(Qg@~^M DHZ˷ljn!\H0*T_m;µ9w WX(cӉC/ލP"M(Z^^<۞ ~ ̛;dΛ7^3:YsΚMQQk>81$"n t\ElDM@QUq#XJ!LcLXams3~b57,PH*8M6 3k16, ͐I-M?Eפf-edmx|/ TS߀R@!,V 38*\@k [&HAx $!,? !,? !,)H*\ȰÇ#JHŋȱǏ CI2F'WR+[,I͛7jt_?3L81h^ҧP9˂CڶCJlmŽչӠqƮ )$PS۬9Z5|]z\{&v0E か9m|+bC7n2 \qⴣ)W0#]Rup~D!?{{PyΜN~@uz (L/)5˷D•Aq5(PPAK8N= ӎO}tO8:9AOZ5Ss0FRp`#ָ(B W TcU@A>C>A %vqwq dw&ű6%Ao=qt@oD<y 4V>8,`#N4PHX 8_3 !=Dx A/! $H!(,1A[M?ĤØA(S5 1ܔM/㴃=S FP P8dEpLEP/#0J5BKnU@G (9 t9 O'/ e-8Q\(:x ͤc7LN1? BV@V6(aAg"(_,@hq k'Y$18֨1,-8XA8J#j6H |z'6K+0# 1 4eWzM?H55զo %$2L @ }-<,r SFP,l's@A5 {x$hJ 3,h%2c`pF1$`gaX4dQ7 d%n9 "TtP AX6%,QEڲ FP* GSSR "Ô&Sa#@i= TX{((D00àe/{#L3 -c?V_|)j@/M Lأ?kD K! d>O +Qb .*1E9* AT!IG-2yV"(XfOR<юz{B?JҒ:g&MJAz{Vt0iH)Ӛ4"!,? !,? !,? !,#*\ȰÇ#JHŋ3jѡmɓ!Dɲ˗ ͆2e yϞ2򜩐ΟH*]"щA 2ڴkD׮m R؅F]5!?pC7c C5l!Ohb!qKa^XAUQc MО}A5S_2+̴ ݷwn<9plfT #IvTYеj; QIh԰e 7ּٿf:fG5C iE߄X#Lm=30 q@N@pAA %\A[zS iO0;xc0Cܜ25GOeTx)*P , m(HL}e38\#O蘃D#.9砃;֤#':̘ 3}=$`h#X!"4TA´#80a]&DZB?0b9ՌuMB`2 %Č# &LrG:dI42ّ!4eZO#qx=p" C((v-Rs@<#3N8 **GS@-rL4c2&@@SxXr81 'R HOAijە#rp۴ :褓~砉N0S=P]r|q{4P)HAm5 0ÁY=~F5१d`b7v$&brG; Zs֦  À(h'PGpM=s"֨셣Q8ddFUPcnbD\?=iX4XZAFl0hl|ho1 c!:5C6D?pEX "TPF|@tkh1_ CB|+|L]SC; C{bfuK!TFG},S?xGC@`\I̱AUwHAo$S86[*߀? @QS'xP0?..(SngH !,H*\ȰÇ83jȱBmCIrd8Fsx˗0'p\E۪$3ϟ>+[ &УH7BF_wn u%Leb6iYϠ5i@ߣD\#`hqcp0  H)Fg[ћM!,? !,? !, ? \ȰÇ#JHŋ3jȱ;LУɓ(S\ɲeGPӥ͛8s܉dȉ.ɳѣk"]*a~ӦelXBqᶆ$ړզ=mQ}Mu߿c9Ȋ;8`߿ M0_N8"-Z`pWdӇJ0[LgvՃw7pXk?p߽ ɃKwTkA$J*"B4&8] rfcΛ8{5jfM#{_%ܸqS-HW O*u@!JlC 7 r5b0턣L5ZAR=سr)2PIq@>DSOPn@9s:cO:͜%7c?t2\VE [Hd@O"yP/&>,3V(E%$ 'r3I+`BG8C$أӃ=dԀ 90R@-44(0b6 ª,Dsn9 Nm7osC8"8L!-n tr*\Y !64}£=O ?O"L364@)oTcr\@XXlgPV?l# ǔXC2Y#TS":b[ltP?E1mK!, x H*\ȰC#PxLj wcƏ ?8P"_Hˮ l͛t Ǹj\V(ч47rt`2a  `$ף`0r˸bmGpݰx3;܂~Ԗhk6e=ؔg`Đ#+ٔi˘Vκ4 !,? !,? !,*H*\ȰÇ#JHŋsQ#Ə CIɏ<ϟ+Oʜ#͋ݜҠM*7| H*]ʴb˞J𩿟ABڴכX]ՠ0 v*}[mۮmSRlc_wxXg{zexeOc4T4gM.XffLҦQb6lxoCIG`Yv7b0F8j)WzQk7oFktH2ltfdwrpk j igi~cơoы=&k1=찂8WP8`0*)W /YPP5Ÿ@!0P@} W?D58F[PjySPo2E @u@ e7 JLnA9 q 5܄c7 Cl1 s b-!M":U2QU ԅNScWLP= bMZ1@&P 2?_zYSM{j5s@ n}A-9^-:d;AK6tl5=g 9 9kc9vl98:ČC?ӄ1ǫ$D`@@e] 9,AA_ D?I Tc^RCjz P54IB(,Ot?1޶"P:>&| ,cPD0V"b|#H@~؈`q@bILhE,Z;D,01Np %h ,$'9W8:A a>`N3B-0d฀Yc=iDVA.xX ^4q|Fk F!8q CcHACq )!Q}Nv'H9*,U-|$p"zJ@x}k]p߽Wۻv&DanCز eFrl}8Ś+w{̼y8bYOߚkl7"u˒6%p[}.`%,Tr1| 7 r4 rL8a43N4Ep,-]% $7S]$16L])C@0pL3栓NiMsN1рI5س '8X!P֤=@8 C,]"(5h1>t̐:P+d:v$ ' JG;Q$s?ml(6q+s?0G 8A(L@ }0l!6]p=Ai&T$aWmi8s9pO2¢s.L3Ħ^D(h lBB!s z!"A }|K\Qqb+p"7 &$,ؓ 'I#pc,L" a^5B4_/ӌ"!"@V0= d*5Fa@>ʢ68C9S8] Xg`S "#1. #Jp@ubim\X NGO?dWVa%XA=\aAI:B O=:(LS?0Ӌ(\}^2?k;5|A^02*ϕZAqIU.ltBT%Cy18F/(b ͉!@yPrp H (a Wm#S gHC*wH$ !,@ H*I'PbE 3j} I_0* LX U +!,? !,? !,? !,? !,/H*\ȰÇ#JHŋaȱǏ C9П? ?0cʜ%I6I$FT(Bɴӧ-_)URU*06MS*բZ] L.W܃nn/@~ V,M-̶IVdl^x3./@UlZ'k$!f۲c9'uM1_㶉8/s8(F`M Q# zLEr7n%rwU S8%M=LqA#P 1@#IJAN;K(Ί#9UψO8'r@jD~ oB A DK mES_]mЇ 1qܱAq!f4™B@&dA~JhBBai{oة@ooiF'/ i|y@wag@rB˒"1PƑFI@?Dm\,c]c@H RU DAh;۔@$8@ ̖Iã@@'1F%Ap#@B1 tp=6Ĩļ1Ab,8*,d)AC3&d < 7~Ƶ8 oMl'( F {bM,F3NU2P9CU{[ @ ?|ti C@9,~9(@0he2{(:,Q;4svy˼0b.~{2͆h=! #(0=m#J!I0фDs@@ۺep0A=0x@&AI+xAL+ȿA4kwQ68Nb a b`3gA&mH >dJE:I b30S$NAJ$;#ȢH0 }A3dBX vM$}R=6Q@ 5IlqqF',Ꮟ}a GFKx ) $0? #bY:$<>FC\L*Hôz$lE$@$PL Y6 2\D,.V , *#< G# Jr^T! 0Z$ l )@A(ְG/aC4◢7}5Гc  Azeե.wYkU4j([CkU(j ޺*|}I[:.AfJup]j֠vY9QTlپJn+KVxB(v66Dp{m.?$׎=j!O˖~{OhbR>׺2 ~!iIR.(?I &I(Cl 46AӏH%F $~Lx |;`8#yi'`{XΰՒFx 1]2(J@!, 8c%0D(Z"i&0F@{ۖE;cleo-0.8.1/docs/_static/theme_overrides.css000066400000000000000000000666561364636233300207520ustar00rootroot00000000000000@import url(http://fonts.googleapis.com/css?family=Montserrat:400,700); @import url(http://fonts.googleapis.com/css?family=Source+Code+Pro:300,400,500); body { font: 400 13px/26px "Open Sans",sans-serif; color: #687E95; text-rendering: optimizelegibility; background-color: #FFFFFF; } .wy-body-for-nav { background: #FFFFFF; } .wy-nav-content { background: #FFFFFF; } .wy-nav-content-wrap { background: #FFFFFF; margin-left: 300px; padding-left: 70px; padding-right: 70px; /*box-shadow: 0 0 1px rgba(0, 0, 0, 0.1);*/ max-width: 940px; } @media only screen and (max-width: 768px) { .wy-nav-content-wrap { margin-left: 0; padding-left: 0; padding-right: 0; } } .wy-nav-top { background: #1A1A1A; } .wy-plain-list-disc li, .rst-content .section ul li, .rst-content .toctree-wrapper ul li, article ul li { font: 400 13px/26px "Open Sans",sans-serif; color: #687E95; } .wy-nav-side { position: fixed; background: #1A1A1A; /*box-shadow: 0px 0 1px rgba(0, 0, 0, 0.1) inset; overflow: scroll; bottom: inherit;*/ } .wy-side-nav-search { font-family: "Open Sans", "Roboto", sans-serif; font-size: 15px; background-color: transparent; color: #7A7A7A; } .wy-menu-vertical ul { font-family: "Open Sans", "Roboto", sans-serif; margin-right: 1em; } .wy-menu-vertical > ul { padding-left: 20px; } .wy-menu-vertical li.current { background: transparent; } .wy-menu-vertical > ul > li { margin-bottom: 30px; } .wy-menu-vertical > ul > li > ul { display: block; } .wy-menu-vertical li.current > ul { background: transparent; padding: 0; } .wy-menu-vertical li.on a, .wy-menu-vertical li.current > a { background: transparent; border: 0; font-weight: 400; } .wy-menu-vertical li a { opacity: 1; } .wy-menu-vertical > ul > li > a, .wy-menu-vertical > ul > li > a:visited { color: #9A9A9A; opacity: 1; -webkit-transition: all 0.3s; -moz-transition: all 0.3s; -ms-transition: all 0.3s; -o-transition: all 0.3s; transition: all 0.3s; } .wy-menu-vertical > ul > li > a:hover { color: #FAC322; opacity: 1; } .wy-menu-vertical li a:hover { background: transparent; } .wy-menu-vertical li a, .wy-menu-vertical li.current > a { border-radius: 0 5px 5px 0; } .wy-menu-vertical li.current > ul > li > a { font-size: 13px; border-right: 0; opacity: 1; padding: 0.4045em 20px; } .wy-menu-vertical li.current > a { font-size: 13px; border-right: 0; } .wy-menu-vertical li.toctree-l2.current > a { background: none; font-size: 13px; color: #EF6155 !important; padding-left: 3.027em; } .wy-menu-vertical li.toctree-l1.current > ul > li > a::before { font-family: "FontAwesome"; content: ""; color: #EC7600; font-size: 8px; position: absolute; top: 4px; left: 3em; opacity: 0.3; display: none; } .wy-menu-vertical li.toctree-l1.current > ul > li.toctree-l2.current > a::before { content: "##"; color: #EF6155; font-size: 13px; position: absolute; top: 6px; left: 20px; opacity: 1; display: block; } .wy-menu-vertical li.toctree-l2.current ul { display: none !important; } .wy-menu-vertical li.toctree-l2.current li.toctree-l3 > a { background: none; padding: 0 4em; font-size: 12px; } .wy-menu-vertical li.toctree-l2.current li.toctree-l3.current ul { display: None; } .wy-menu-vertical a, .wy-menu-vertical a:visited { color: #7A7A7A; line-height: 20px; font-size: 13px; } .wy-menu-vertical a:hover { color: #5A5A5A; } .wy-menu-vertical > ul > li > ul > li > a, .wy-menu-vertical > ul > li > ul > li > a:visited, .wy-menu-vertical li.current a, .wy-menu-vertical li.current a:visited { color: #7A7A7A; background: transparent; -webkit-transition: all 0.3s; -moz-transition: all 0.3s; -ms-transition: all 0.3s; -o-transition: all 0.3s; transition: all 0.3s; } .wy-menu-vertical > ul > li > ul > li > a:hover, .wy-menu-vertical li.current a:hover { color: #EF6155; background: transparent; } .wy-menu-vertical li.current > a { color: #579ED1 !important; padding-left: 2.427em; } .wy-menu-vertical li.current > a:hover { } .wy-side-nav-search input[type="text"] { font-family: "Open Sans", "Roboto", sans-serif; border: 0; background: none; box-shadow: none; color: #7A7A7A; font-size: 13px; width: 90%; border-radius: 0; border-bottom: 1px solid rgba(129, 150, 154, 0.7); opacity: 1; padding: 6px 0; } .wy-side-nav-search > a, .wy-side-nav-search > a:visited { color: #9A9A9A; } .wy-side-nav-search > a:hover { background: none; } .wy-menu-vertical li { position: relative; } .wy-menu-vertical li span.toctree-expand::before, .wy-menu-vertical li.on a span.toctree-expand::before, .wy-menu-vertical li.current > a span.toctree-expand::before, .wy-menu-vertical li a span.toctree-expand, .wy-menu-vertical li a span.toctree-expand:before { display: none; } .wy-menu-vertical li.current ul li a span.toctree-expand, .wy-menu-vertical li.on a:hover span.toctree-expand, .wy-menu-vertical li.current a:hover span.toctree-expand{ display: none; } .wy-menu-vertical li.toctree-l1.current:before { content: "#"; color: #579ED1; font-size: 13px; position: absolute; top: 3px; left: 20px; z-index: 2; display: block; } .wy-menu-vertical li.toctree-l1:before { font-family: FontAwesome; content: ""; opacity: 1; color: #1999B3; font-size: 10px; top: 2px; position: absolute; left: 12px; z-index: 2; display: none; } .rst-content table.docutils, .rst-content table.field-list { border: medium none; width: 100%; } .rst-content table.docutils thead th, .rst-content table.field-list thead th { font-family: "Montserrat", serif; text-transform: uppercase; color: #7A7A7A; font-weight: 500; } .rst-content table.docutils thead th, .rst-content table.field-list thead th, .rst-content table.docutils tbody td, .rst-content table.field-list tbody td { border-top: 0; border-bottom: 1px solid rgba(230, 230, 230, 0.7); border-left: 0; border-right: 0; padding: 20px; white-space: pre-wrap; } .wy-table-odd td, .wy-table-striped tr:nth-child(2n-1) td, .rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td { background: none; } .wy-table td, .rst-content table.docutils td { font-size: 100% !important; } .rst-content table.docutils tbody td tt.code, .rst-content table.field-list tbody td tt.code, .rst-content table.docutils tbody td code.code, .rst-content table.field-list tbody td code.code { border: 0; background: none; padding: 0.3em 0; color: #26A6A6; } .rst-content .highlighted { border: 1px solid rgb(250, 195, 34); padding: 0 4px; border-radius: 3px; color: #BA8300; display: inline; font-weight: inherit; background: rgba(250, 195, 34, 0.6) none repeat scroll 0% 0%; } h1, h2, h3, h4, h5, h6 { color: #384E65; padding: 10px 0; font-family: "Open Sans", "Montserrat", serif; line-height: 1; margin: 40px 0 30px 0; font-weight: 300; position: relative; -moz-osx-font-smoothing: grayscale; -webkit-font-smoothing: antialiased; } .rst-content h1 .headerlink, .rst-content h2 .headerlink, .rst-content h3 .headerlink, .rst-content h4 .headerlink, .rst-content h5 .headerlink, .rst-content h6 .headerlink { visibility: hidden; color: inherit; font-size: inherit; margin: 0; } .rst-content h1 .headerlink:before, .rst-content h2 .headerlink:before, .rst-content h3 .headerlink:before, .rst-content h4 .headerlink:before, .rst-content h5 .headerlink:before, .rst-content h6 .headerlink:before { font-family: inherit; visibility: visible; position: absolute; left: -40px; width: 100%; content: "#"; opacity: 0; font-weight: inherit; -webkit-transition: all 0.3s; -moz-transition: all 0.3s; -ms-transition: all 0.3s; -o-transition: all 0.3s; transition: all 0.3s; } .rst-content h1 .headerlink:before { top: 20px; } .rst-content h1:hover .headerlink, .rst-content h2:hover .headerlink, .rst-content h3:hover .headerlink, .rst-content h4:hover .headerlink, .rst-content h5:hover .headerlink, .rst-content h6:hover .headerlink { display: inline; color: inherit; } .rst-content h1:hover .headerlink:before, .rst-content h2:hover .headerlink:before, .rst-content h3:hover .headerlink:before, .rst-content h4:hover .headerlink:before, .rst-content h5:hover .headerlink:before, .rst-content h6:hover .headerlink:before { opacity: 1; } .rst-content h1 .headerlink:after, .rst-content h2 .headerlink:after, .rst-content h3 .headerlink:after, .rst-content h4 .headerlink:after, .rst-content h5 .headerlink:after, .rst-content h6 .headerlink:after { visibility: hidden; content: ""; } h1 { font-size: 44px; } h2 { font-size: 38px; color: #579ED1; position: relative; } h2:before { display: none; font-family: "FontAwesome"; content: "#"; margin-left: -30px; font-size: 38px; top: 11px; color: #579ED1 !important; opacity: 0.6; position: absolute; } h3 { font-size: 24px; } h4 { font-size: 20px; } h1, h1 a, .header h1, .header h1 a { font: 300 44px/66px "Open Sans", "Roboto","Montserrat",sans-serif; margin-bottom: 30px; color: #485E75; } a, a:visited { color: #009CD4; } a:hover { color: #48B0F7; } a > em { font-style: normal; } p { font: 400 13px/26px "Open Sans",sans-serif; color: #687E95; } b, strong { font-weight: 600; } pre, pre code, .rst-content tt, code.docutils, .rst-content dl:not(.docutils) code { font-family: "Source Code Pro", "Consolas", "Menlo", "Monaco", "Courier New", Courier, monospace; color: #26A6A6; background-color: transparent; font-size: 13px; font-weight: 400; border: 0; padding: 0; } pre, code, .rst-content ttc { font-family: "Source Code Pro", "Consolas", "Menlo", "Monaco", "Courier New", Courier, monospace; font-weight: 400; border-radius: 3px; font-size: 13px; margin: 0px 2px; } div[class^="highlight"] { border: 0; background: transparent; } .higlight { background: transparent; } .highlight > pre, div[class^="highlight"] pre { font-family: "Source Code Pro", "Consolas", "Menlo", "Monaco", "Courier New", Courier, monospace; border-left: 1px solid #F2F2F2; font-size: 13px; padding: 2% 4%; margin-left: 1%; color: #788EA5; /*background-color: #FAFAFA;*/ } code, .rst-content tt, .rst-content code { font-family: "Source Code Pro", "Consolas", "Menlo", "Monaco", "Courier New", Courier, monospace; } code { color: #CB6077; background-color: transparent; font-size: 13px; font-weight: 500; } p > code, .rst-content tt, p > code.docutils { color: #26A6A6; padding: 0; display: inline; font-weight: 400; border: 0; background-color: transparent; } .rst-content tt.literal, .rst-content tt.literal, .rst-content code.literal, .rst-content dl:not(.docutils) code { color: #1976BF; } .rst-content .admonition-title { display: none; } .rst-content .note, .rst-content .warning, .rst-content .tip, .rst-content .caution, .rst-content .versionchanged, .rst-content .versionadded { padding: 4% 4%; margin: 30px 0; background: transparent; } .rst-content .note .highlight > pre, .rst-content .note div[class^="highlight"] pre, .rst-content .warning .highlight > pre, .rst-content .warning div[class^="highlight"] pre, .rst-content .versionadded .highlight > pre, .rst-content .versionadded div[class^="highlight"] pre, .rst-content .tip .highlight > pre, .rst-content .tip div[class^="highlight"] pre, .rst-content .caution .highlight > pre, .rst-content .caution div[class^="highlight"] pre { margin-left: 0; border-left: 0; } .rst-content .note { border-left: 1px dotted rgba(50, 154, 188, 0.3); border-right: 1px dotted rgba(50, 154, 188, 0.3); background: rgba(50, 154, 188, 0.02); color: #3399BB !important; box-shadow: 0 0 2px rgba(50, 154, 188, 0.1); } .rst-content .tip { border-left: 1px dotted rgba(73,182,127, 0.3); border-right: 1px dotted rgba(73,182,127, 0.3); background: rgba(73,182,127, 0.02); color: #49B67F !important; box-shadow: 0 0 2px rgba(73,182,127, 0.1); } .rst-content .tip > p { color: #49B67F !important; } .rst-content .tip > p code { color: #19864F !important; } .rst-content .warning, .rst-content .caution { border-left: 1px dotted rgba(249, 145, 87, 0.3); border-right: 1px dotted rgba(249, 145, 87, 0.3); background: rgba(249, 145, 87, 0.02); color: #F99157 !important; box-shadow: 0 0 2px rgba(249, 145, 87, 0.1); } .rst-content .note > p { color: #39B !important; } .rst-content .warning > p, .rst-content .caution > p { color: #F99157 !important; } .rst-content .versionadded, .rst-content .versionchanged { border-left: 1px dotted rgba(102, 119, 187, 0.3); border-right: 1px dotted rgba(102, 119, 187, 0.3); background: rgba(102, 119, 187, 0.02); color: #6677BB !important; margin-bottom: 20px; position: relative; box-shadow: 0 0 2px rgba(102, 119, 187, 0.1); } .rst-content .versionadded > p > span, .rst-content .versionchanged > p > span { font-family: "Montserrat", serif; display: block; font-size: 12px; margin-bottom: 20px; text-transform: uppercase; color: #36479B !important; } .rst-content .versionadded > p, .rst-content .versionchanged > p { color: #6677BB !important; } .rst-content .versionadded > p code, .rst-content .versionchanged > p code { color: #36479B !important; } .rst-content .versionadded > p, .rst-content .versionchanged > p { margin-bottom: 0; } .rst-content .note .versionchanged { padding-left: 4%; } .note code { border: 0; padding: 0; display: inline; } .note ul li, .warning ul li { color: inherit !important; } .warning code, .warning tt { border: 0; padding: 0; display: inline; color: #D9664F !important; } .btn-neutral, .btn-neutral:visited { color: #808080 !important; font: 400 12px/20px Montserrat,Arial,sans-serif; text-transform: uppercase; padding: 6px 14px; border-radius: 4px; box-shadow: none; border: 0; transition: all 0.3s; background-color: transparent !important; } .btn-neutral:hover { background-color: transparent !important; color: rgba(25, 153, 179, 0.8) !important; /*color: rgba(0, 168, 192, 1) !important;*/ } .btn-neutral:focus { background-color: transparent !important; color: rgba(25, 153, 179, 1) !important; box-shadow: none; padding: 6px 14px; } .btn-neutral span.fa { transition: all 0.3s; } .btn-neutral:hover span.fa-arrow-circle-left { padding-right: 5px; } .btn-neutral:hover span.fa-arrow-circle-right { padding-left: 5px; } .btn-neutral span.fa-arrow-circle-left:before { content: '' } .btn-neutral span.fa-arrow-circle-right:before { content: '' } /* Autodoc */ .rst-content table.field-list .field-name { text-align: left; white-space: nowrap; padding-right: 10px; width: 140px; font-weight: 600; } .rst-content table.field-list .field-body { padding: 8px 16px; text-align: left; vertical-align: top; border: 0; } .rst-content table.field-list .field-body ul { line-height: 0; } .rst-content table.field-list .field-body ul li { list-style: none; margin-left: 0; font-size: 100%; } .rst-content table.field-list td p { line-height: inherit; } .rst-content table.field-list .field-body strong { color: #F3888B; font-weight: 400; margin-top: 0; } .rst-content dl:not(.docutils).class > dt { border: 0; background: transparent; font-size: inherit; margin-bottom: 24px; color: #579ED1; font-weight: 600; } .rst-content dl:not(.docutils).class > dt > em, .rst-content dl:not(.docutils).class > dt > em.property { font-weight: 400; font-style: normal; } .rst-content dl:not(.docutils).class > dt > code { color: #F9A167; font-weight: 400; } .rst-content dl:not(.docutils) dl.classmethod > dt, .rst-content dl:not(.docutils) dl.method > dt { border-left: 1px solid #F2F2F2; background: transparent; font-size: inherit; } .rst-content dl:not(.docutils) dl.classmethod > dt > code, .rst-content dl:not(.docutils) dl.method > dt > code { color: #D9664F; font-weight: 400; } .rst-content dl:not(.docutils) dl.classmethod > dt > em, .rst-content dl:not(.docutils) dl.method > dt > em { font-weight: 400; } .rst-content dl:not(.docutils) dl.classmethod > dt > em.property, .rst-content dl:not(.docutils) dl.method > dt > em.property { font-weight: 400; color: #7AC; } /* Solarized Light For use with Jekyll and Pygments http://ethanschoonover.com/solarized SOLARIZED HEX ROLE --------- -------- ------------------------------------------ base01 #586e75 body text / default code / primary content base1 #93a1a1 comments / secondary content base3 #fdf6e3 background orange #cb4b16 constants red #dc322f regex, special keywords blue #268bd2 reserved keywords cyan #2aa198 strings, numbers green #859900 operators, other keywords */ .highlight { background-color: #fdf6e3; color: #586e75 } .highlight .c { color: #93a1a1 } /* Comment */ .highlight .err { color: #586e75 } /* Error */ .highlight .g { color: #586e75 } /* Generic */ .highlight .k { color: #859900 } /* Keyword */ .highlight .l { color: #586e75 } /* Literal */ .highlight .n { color: #586e75 } /* Name */ .highlight .o { color: #859900 } /* Operator */ .highlight .x { color: #cb4b16 } /* Other */ .highlight .p { color: #586e75 } /* Punctuation */ .highlight .cm { color: #93a1a1 } /* Comment.Multiline */ .highlight .cp { color: #859900 } /* Comment.Preproc */ .highlight .c1 { color: #93a1a1 } /* Comment.Single */ .highlight .cs { color: #859900 } /* Comment.Special */ .highlight .gd { color: #2aa198 } /* Generic.Deleted */ .highlight .ge { color: #586e75; font-style: italic } /* Generic.Emph */ .highlight .gr { color: #dc322f } /* Generic.Error */ .highlight .gh { color: #cb4b16 } /* Generic.Heading */ .highlight .gi { color: #859900 } /* Generic.Inserted */ .highlight .go { color: #586e75 } /* Generic.Output */ .highlight .gp { color: #586e75 } /* Generic.Prompt */ .highlight .gs { color: #586e75; font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #cb4b16 } /* Generic.Subheading */ .highlight .gt { color: #586e75 } /* Generic.Traceback */ .highlight .kc { color: #cb4b16 } /* Keyword.Constant */ .highlight .kd { color: #268bd2 } /* Keyword.Declaration */ .highlight .kn { color: #859900 } /* Keyword.Namespace */ .highlight .kp { color: #859900 } /* Keyword.Pseudo */ .highlight .kr { color: #268bd2 } /* Keyword.Reserved */ .highlight .kt { color: #dc322f } /* Keyword.Type */ .highlight .ld { color: #586e75 } /* Literal.Date */ .highlight .m { color: #2aa198 } /* Literal.Number */ .highlight .s { color: #2aa198 } /* Literal.String */ .highlight .na { color: #586e75 } /* Name.Attribute */ .highlight .nb { color: #B58900 } /* Name.Builtin */ .highlight .nc { color: #268bd2 } /* Name.Class */ .highlight .no { color: #cb4b16 } /* Name.Constant */ /*.highlight .nd { color: #268bd2 }*/ /* Name.Decorator */ .highlight .nd { color: #cb4b16 } .highlight .ni { color: #cb4b16 } /* Name.Entity */ .highlight .ne { color: #cb4b16 } /* Name.Exception */ .highlight .nf { color: #268bd2 } /* Name.Function */ .highlight .nl { color: #586e75 } /* Name.Label */ .highlight .nn { color: #586e75 } /* Name.Namespace */ .highlight .nx { color: #586e75 } /* Name.Other */ .highlight .py { color: #586e75 } /* Name.Property */ .highlight .nt { color: #268bd2 } /* Name.Tag */ .highlight .nv { color: #268bd2 } /* Name.Variable */ .highlight .ow { color: #859900 } /* Operator.Word */ .highlight .w { color: #586e75 } /* Text.Whitespace */ .highlight .mf { color: #ff9800 } /* Literal.Number.Float */ .highlight .mh { color: #ff9800 } /* Literal.Number.Hex */ .highlight .mi { color: #ff9800 } /* Literal.Number.Integer */ .highlight .mo { color: #ff9800 } /* Literal.Number.Oct */ .highlight .sb { color: #93a1a1 } /* Literal.String.Backtick */ .highlight .sc { color: #2aa198 } /* Literal.String.Char */ .highlight .sd { color: #586e75 } /* Literal.String.Doc */ .highlight .s2 { color: #2aa198 } /* Literal.String.Double */ .highlight .se { color: #cb4b16 } /* Literal.String.Escape */ .highlight .sh { color: #586e75 } /* Literal.String.Heredoc */ .highlight .si { color: #2aa198 } /* Literal.String.Interpol */ .highlight .sx { color: #2aa198 } /* Literal.String.Other */ .highlight .sr { color: #dc322f } /* Literal.String.Regex */ .highlight .s1 { color: #2aa198 } /* Literal.String.Single */ .highlight .ss { color: #2aa198 } /* Literal.String.Symbol */ .highlight .bp { color: #F2777A } /* Name.Builtin.Pseudo */ .highlight .vc { color: #F2777A } /* Name.Variable.Class */ .highlight .vg { color: #F2777A } /* Name.Variable.Global */ .highlight .vi { color: #F2777A } /* Name.Variable.Instance */ .highlight .il { color: #2aa198 } /* Literal.Number.Integer.Long */ /***** Mocha *****/ .highlight .s { color: #7BBDA4 } /* Literal.String */ .highlight .s1 { color: #7BBDA4 } /* Literal.String */ .highlight .si { color: #A89BB9 } /* Literal.String */ .highlight .m { color: #F4BC87 } /* Literal.Number */ .highlight .mf { color: #F4BC87 } /* Literal.Number */ .highlight .mh { color: #F4BC87 } /* Literal.Number */ .highlight .mi { color: #F4BC87 } /* Literal.Number */ .highlight .mo { color: #F4BC87 } /* Literal.Number */ .highlight .k { color: #BEB55B; font-weight: normal; } /* Keyword */ .highlight .kn { color: #BEB55B; font-weight: normal; } /* Keyword */ .highlight .ow { color: #BEB55B; font-weight: normal } /* Operator.Word */ .highlight .nc { color: #D28B71; font-weight: 500 } /* Name.Class */ .highlight .nf { color: #8AB3B5; font-weight: normal; } /* Name.Function */ .highlight .bp { color: #F3888B } /* Name.Builtin.Pseudo */ .highlight .vc { color: #F3888B } /* Name.Variable.Class */ .highlight .vg { color: #F3888B } /* Name.Variable.Global */ .highlight .vi { color: #F3888B } /* Name.Variable.Instance */ .highlight .nd { color: #BB9584 } /* Name.Decorator */ .highlight .ne { color: #CB6077; font-weight: 400 } /* Name.Exception */ .highlight .o { color: #8A8A8A } /* Operator */ .highlight .n { color: #8A8A8A } .highlight .c { color: #B8AFAD; font-style: normal; } .highlight .sd { color: #B8AFAD; font-style: normal; } .highlight .nl { color: #A89BB9 } /* Name.Label */ .highlight .nn { color: #A89BB9 } /* Name.Namespace */ .highlight .nx { color: #A89BB9 } /* Name.Other */ .highlight .py { color: #A89BB9 } /* Name.Property */ /* Inline code */ tt.code, code.code { border: 1px solid #F2F2F2; font-weight: normal; display: inline; padding: 0.3em 0.5em; background-color: #FAFAFA; } tt.code .name, code.code .name { color: #8A8A8A } tt.code .operator, code.code .operator { color: #8A8A8A } tt.code .punctuation, code.code .punctuation { color: #8A8A8A } tt.code .string, code.code .string { color: #7BBDA4 } tt.code .number, code.code .number { color: #F4BC87 } tt.code .integer, code.code .integer { color: #F4BC87 } /***** Eighties *****/ .highlight .s { color: #59B6CF } /* Literal.String */ .highlight .s1 { color: #59B6CF } /* Literal.String */ .highlight .si { color: #8986BF } /* Literal.String */ .highlight .m { color: #F9768F } /* Literal.Number */ .highlight .mf { color: #F9768F } /* Literal.Number */ .highlight .mh { color: #F9768F } /* Literal.Number */ .highlight .mi { color: #F9768F } /* Literal.Number */ .highlight .mo { color: #F9768F } /* Literal.Number */ .highlight .k { color: #579ED1; font-weight: normal; } /* Keyword */ .highlight .kn { color: #579ED1; font-weight: normal; } /* Keyword */ .highlight .ow { color: #579ED1; font-weight: normal } /* Operator.Word */ .highlight .nc { color: #69B69F; font-weight: 400 } /* Name.Class */ .highlight .nf { color: #8986BF; font-weight: normal; } /* Name.Function */ .highlight .bp { color: #69B69F } /* Name.Builtin.Pseudo */ .highlight .vc { color: #69B69F } /* Name.Variable.Class */ .highlight .vg { color: #69B69F } /* Name.Variable.Global */ .highlight .vi { color: #69B69F } /* Name.Variable.Instance */ .highlight .nd { color: #879ED1 } /* Name.Decorator */ .highlight .ne { color: #CB6077; font-weight: 400 } /* Name.Exception */ .highlight .o { color: #98AEC5 } /* Operator */ .highlight .n { color: #788EA5 } .highlight .c { color: #A8AFC2; font-style: normal; } .highlight .sd { color: #A8AFC2; font-style: normal; } .highlight .nl { color: #F9768F } /* Name.Label */ .highlight .nn { color: #8986BF } /* Name.Namespace */ .highlight .nx { color: #F9768F } /* Name.Other */ .highlight .py { color: #F9768F } /* Name.Property */ .highlight .nb { color: #63ABA3 } /* Name.Builtin */ .highlight .p { color: #98AEC5 } /* Punctuation */ /* Inline code */ tt.code, code.code { border: 1px solid #F2F2F2; font-weight: normal; display: inline; padding: 0.3em 0.5em; background-color: #FAFAFA; } tt.code .name, code.code .name { color: #788EA5 } tt.code .operator, code.code .operator { color: #98AEC5 } tt.code .punctuation, code.code .punctuation { color: #98AEC5 } tt.code .string, code.code .string { color: #59B6CF } tt.code .number, code.code .number { color: #F9768F } tt.code .integer, code.code .integer { color: #F9768F } tt.code .pseudo, code.code .pseudo { color: #69B69F } /* API */ .rst-content dl:not(.docutils) .property { font-family: "Source Code Pro", "Consolas", "Menlo", "Monaco", "Courier New", Courier, monospace; padding-right: 5px; } .rst-content dl:not(.docutils) em { font-family: "Source Code Pro", "Consolas", "Menlo", "Monaco", "Courier New", Courier, monospace; font-style: normal; color: #788EA5; } .rst-content dl.class:not(.docutils) > dt > code { font-family: "Source Code Pro", "Consolas", "Menlo", "Monaco", "Courier New", Courier, monospace; color: #788EA5; } .rst-content dl:not(.docutils) > dt > tt.descname, .rst-content dl:not(.docutils) > dt > tt.descname, .rst-content dl:not(.docutils) > dt > code.descname { color: #69B69F; } .rst-content dl.class:not(.docutils) > dt > .sig-paren, .rst-content dl:not(.docutils) dl.classmethod > dt > .sig-paren, .rst-content dl:not(.docutils) dl.method > dt > .sig-paren { font-family: "Source Code Pro", "Consolas", "Menlo", "Monaco", "Courier New", Courier, monospace; color: #98AEC5; font-weight: normal; font-style: normal; } .rst-content dl:not(.docutils) dl.classmethod > dt > code, .rst-content dl:not(.docutils) dl.method > dt > code { color: #8986BF; font-weight: 400; } .rst-content table.field-list .field-body strong { color: #F9768F; font-weight: 600; } .o { font-weight: normal; } /* Dark Orator */ div[class^="highlight"] { border: 0; background: #152B39; border-radius: 3px; } div[class^="highlight"] > pre { border: 0; color: #98AEC5; -webkit-border-radius: 3px; -moz-border-radius: 3px; border-radius: 3px; padding: 4% 4%; } .highlight .nn, .highlight .nc { color: #89C6BF; } .highlight .c, .highlight .c1 { color: #727995; font-style: normal; } .highlight .sd { color: #727995; font-style: normal; } .highlight .p, .highlight .o, code.code .operator { color: #687E95 } .highlight .n, code.code .name { color: #98AEC5 } .highlight .l { color: #98AEC5; } .note .last pre, .warning .last pre, .tip .last pre, .caution .last pre, .versionchanged .last pre, .versionadded .last pre { padding-bottom: 4%; margin-bottom: 0; } cleo-0.8.1/docs/api/000077500000000000000000000000001364636233300141545ustar00rootroot00000000000000cleo-0.8.1/docs/api/index.rst000066400000000000000000000117331364636233300160220ustar00rootroot00000000000000API Reference ############# Command ======= .. py:class:: Command(name=None) A ``Command`` represents a single CLI command. .. py:method:: argument(key=None) Get the value of a command argument. :param key: The argument name :type key: str :rtype: mixed .. py:method:: ask(question, default=None) Prompt the user for input. :param question: The question to ask :type question: str :param default: The default value :type default: str or None :rtype: str .. py:method:: call(name, options=None) Call another command. :param name: The command name :type name: str :param options: The options :type options: list or None .. py:method:: call_silent(name, options=None) Call another command silently. :param name: The command name :type name: str :param options: The options :type options: list or None .. py:method:: choice(question, choices, default=None, attempts=None, multiple=False) Give the user a single choice from an list of answers. :param question: The question to ask :type question: str :param choices: The available choices :type choices: list :param default: The default value :type default: str or None :param attempts: The max number of attempts :type attempts: int :param multiple: Multiselect :type multiple: int :rtype: str .. py:method:: comment(text) Write a string as comment output. :param text: The line to write :type text: str .. py:method:: confirm(self, question, default=False, true_answer_regex='(?i)^y') Confirm a question with the user. :param question: The question to ask :type question: str :param default: The default value :type default: bool :param true_answer_regex: A regex to match the "yes" answer :type true_answer_regex: str :rtype: bool .. py:method:: error(text) Write a string as error output. :param text: The line to write :type text: str .. py:method:: info(text) Write a string as information output. :param text: The line to write :type text: str .. py:method:: line(text, style=None, verbosity=None) Write a string as information output. :param text: The line to write :type text: str :param style: The style of the string :type style: str :param verbosity: The verbosity :type verbosity: None or int str .. py:method:: list(elements) Write a list of elements. :param elements: The elements to write a list for :type elements: list .. py:method:: option(key=None) Get the value of a command option. :param key: The option name :type key: str :rtype: mixed .. py:method:: progress_bar(max=0) Create a new progress bar :param max: The maximum number of steps :type max: int :rtype: ProgressBar .. py:method:: question(text) Write a string as question output. :param text: The line to write :type text: str .. py:method:: render_table(headers, rows, style='default') Format input to textual table.. :param headers: The table headers :type headers: list :param rows: The table rows :type rows: list :param style: The table style :type style: str .. py:method:: secret(question) Prompt the user for input but hide the answer from the console. :param question: The question to ask :type question: str :rtype: str .. py:method:: set_style(name, fg=None, bg=None, options=None) Set a new style :param name: The name of the style :type name: str :param fg: The foreground color :type fg: str :param bg: The background color :type bg: str :param options: The options :type options: list .. py:method:: table(headers=None, rows=None, style='default') Return a ``Table`` instance. :param headers: The table headers :type headers: list :param rows: The table rows :type rows: list :param style: The table style :type style: str .. py:method:: table_cell(value, **options) Return a ``TableCell`` instance :param value: The cell value :type value: str :param options: The cell options :type options: dict .. py:method:: table_separator() Return a ``TableSeparator`` instance :rtype: TableSeparator .. py:method:: table_style() Return a ``TableStyle`` instance :rtype: TableStyle .. py:method:: warning(text) Write a string as warning output. :param text: The line to write :type text: str cleo-0.8.1/docs/conf.py000066400000000000000000000210721364636233300147040ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # Cleo documentation build configuration file, created by # sphinx-quickstart on Sun Mar 9 21:01:54 2014. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys import os import sphinx_rtd_theme # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = ["sphinx.ext.autodoc"] # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] # The suffix of source filenames. source_suffix = ".rst" # The encoding of source files. # source_encoding = 'utf-8-sig' # The master toctree document. master_doc = "index" # General information about the project. project = u"Cleo" copyright = u"2014, Sébastien Eustace" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = "0.6" # The full version, including alpha/beta/rc tags. release = "0.6.1" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: # today = '' # Else, today_fmt is used as the format for a strftime call. # today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ["_build"] # The reST default role (used for this markup: `text`) to use for all # documents. # default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. # add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). # add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. # show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. # keep_warnings = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = "default" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. # html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". # html_title = None # A shorter title for the navigation bar. Default is the same as html_title. # html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. # html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. # html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ["_static"] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. # html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. # html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. # html_use_smartypants = True # Custom sidebar templates, maps document names to template names. # html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. # html_additional_pages = {} # If false, no module index is generated. # html_domain_indices = True # If false, no index is generated. # html_use_index = True # If true, the index is split into individual pages for each letter. # html_split_index = False # If true, links to the reST sources are added to the pages. # html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. # html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. # html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. # html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). # html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = "Cleodoc" # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ ("index", "Cleo.tex", u"Cleo Documentation", u"Sébastien Eustace", "manual") ] # The name of an image file (relative to this directory) to place at the top of # the title page. # latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. # latex_use_parts = False # If true, show page references after internal links. # latex_show_pagerefs = False # If true, show URL addresses after external links. # latex_show_urls = False # Documents to append as an appendix to all manuals. # latex_appendices = [] # If false, no module index is generated. # latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [("index", "cleo", u"Cleo Documentation", [u"Sébastien Eustace"], 1)] # If true, show URL addresses after external links. # man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ( "index", "Cleo", u"Cleo Documentation", u"Sébastien Eustace", "Cleo", "One line description of project.", "Miscellaneous", ) ] # Documents to append as an appendix to all manuals. # texinfo_appendices = [] # If false, no module index is generated. # texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. # texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. # texinfo_no_detailmenu = False on_rtd = os.environ.get("READTHEDOCS", None) == "True" if not on_rtd: html_theme = "sphinx_rtd_theme" def setup(app): app.add_stylesheet("theme_overrides.css") else: html_context = { "css_files": [ "https://media.readthedocs.org/css/sphinx_rtd_theme.css", "https://media.readthedocs.org/css/readthedocs-doc-embed.css", "_static/theme_overrides.css", ] } cleo-0.8.1/docs/helpers/000077500000000000000000000000001364636233300150455ustar00rootroot00000000000000cleo-0.8.1/docs/helpers/index.rst000066400000000000000000000003401364636233300167030ustar00rootroot00000000000000Helpers ------- Cleo also contains a set of "helpers" - different small tools capable of helping you with different tasks: .. toctree:: :maxdepth: 1 question_helper formatter_helper progress_bar table cleo-0.8.1/docs/helpers/progress_2.gif000066400000000000000000000420211364636233300176200ustar00rootroot00000000000000GIF89a@!ғḽ¿Ȓ鈚ﷺͷѱղȮ˲筲Րүవի竭ׂ鞩𣍟ؔv! NETSCAPE2.0! XMP DataXMP ~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#"!  !,@!~ƕοɜpӌԥ%̪ߋچ!!3 ~! ;QD)|8(Cȃ4HТ%E F.Nz%9!\(Ic ~`Hã  ` QSR5jS)⇀_rD"̹J/hHhР Frbƞ>0IiX 4~DXcrM(5 rMe•Eg94eAWCvm˔5\4בxoڣ1A%jLRJ*X`À'0` ?(<0< E OD64s(0 pN# " F%e (Z g~'{. PDj nXPY P#h@F 4= 0* $a@)_)h쀥t`YXk|i'8QVh ė[ҡj9ԙXlĤ)$G kf*H0ciBX : t`'ZXi(hB*)lhpkl A "U( (U0Du8`r 0V H@i>vTB$A 9TB (BZQPa6B'Ð8L ]'h#5 +>(@B# H샦WJ@%l#jz 2K< Ї  (MlP4kxp%)͆0a#Ba;, 5F :TP $ُ_sD3,KF_csn!D؅&b5fH_ gVr$ӃQ.TNSk3s oNI!5<5bsBAHÈM1 D2A|x9(6pG?.BqQ&<h# 1`x(#$R)S' 9qJliq@ *64~P(#/1t,aDGGP@v &ЁDDx(  (㌅$SCs r\5pGZGtDJQŌAE 4@h啃(1=x"!AhC=\@ G4cX*Kg5$H 5Ae29F ,v§z\2$ R-֘*X>I뭸6!,? D.01^m-'!,!,!,$~~~~%3I!..̓՘K3L3QĂIHbF ֜ǑH!b)4 0f# ϙi#F8!bD%HxX@;`ļG)PN`1 * E~\H ?Ib)+IBF + D2V AFX"NH ?lݻ'*4Af#AB*mP>80:Aʧ/bؼۑ42 فȀ9i(6h#o.B( B,0y/ބQE#mb򁄋d"J~h~pQ!xPQO| 4 R*ֵa-("%*q؂ qRu=S~`APA1 T` QIdB`ewҁ % r0'($J,41|'tTO@#W A! $w(}8'Pf)M8>X(HWP5 R(0bώ*iʕ ~~5QQ!M 4 nDl)ؕ"3#ٸ[SD>p@+ I !, " /A~ƪČ%*Ǒ+R+фD١MCS!ڃɒW܅I݅PlF"* "#&#B1#$FBp1dD, ͓?%dlI"!+ ɓgvD JGtE!կ,CQ#`F !,!,!,$~~&!$γL~AI;/ަЎM .5$)xID4͍?H"JgĈ?J1":t(ǕI.܉H;uv8%ʾ?W0SCX ҰȓQhcCƊ4Ad?N "1$4 `U<4)T}tb) w5Au82%`T񡃟ʕat A%d'E2$q0*rrR@ζAnRaz :b 51{#^FAJ,{Vn pD@{SDBQAF|U[  tq@ -p@ lA@C\(!Cd0@h-b*p ,1H6!8BZ@A ɩ$ -.Qb*TBH&DY DҔ 'h*N C444M!Fb8pǓ0ǎ.J,4H.p ( cgFz+ M̹對tbdEU.(gQkO0 @@ ao@4O(bo]!-bB!Ȼ-g{BF_ /dpjphDop \ 00 0x 0Lʌf3$8ᇘjuP@ T, !O!4p'ֆԷ Ho]fxi]׆ ĽD -YvwP ,0*SmٲhDqQbD!d*cX0X!"\cʯ/ ޴cHpkn0#q%<9 0 B Z.Kŵ (#O7SEa!??4L33Q=0 (C ]"jc@#DkL rh( 8>H80 aCboHL1&:xH*fV̢bq` (2!,!,!,!,#~~̐ē%I(ݑ!!؉$T/L/3/1rf wJQ>FW0PH`&̉H H1 ̟,gLDF91Bč#n "lX! !E`?PFH)* @Eh)U4Ƌ&CQ "+ +!DJ`"\ c~r#cDw4Cq@CeD4f, aN#8 c ? …'x23B 0 gC"x *xԥC.:b`1ő,x[q D!2!@ET0iI1\eFhnUR@"-a h@@-(!$q@JAY=Xk/hv 4&#qN '.jyđW2xGEЀ/H(gPQ5$"J8Ϭ#9l!d"L>фW%Q)13U z!z;6c,d )&kO !,!,!,$~~.o L!ȆȽی3L(p~b=`~N5~AZb kP@Oi@CF3 GpJ@?XgВdP bOh0Ōy'xHʲ)+ha &LHJ$tfppW'=^`4&A_xu扏 fܩVc@ٿhc &T@9y1$PZ ?|b=Hǟ8hÂő#Ycfc14U68ǃ$E 5Gn"șg@rB,.>liB "C W J$ = A"g2 IX0 el%A XB<<lG# `l1%`Ay\cSp0O5z'pDAl`敢dTc  b|! vQ R͉>!d9) 9 D `y(4J !`h3C= *ڔ#2>e k"뮵( k,1$!,~~p%%!#7;LˇD+YؾІ….oy3!,^K&\$JCAuN; #B !,!,!, ~~~ǑTTIQ(ِ.ڶpITIL~LQ gFSt Kh$L=F)h7A qDXH#Lpr Xѡ(GKvRÉ2O,0 % C 9Ru`Y?lX!ʼn2jW!a D$ Aǀ/KrJ  a3^ 2|a 06Q@JpQϟ Pa( $4LY#OZ0 Q^ĠF# @4i@t\C a" Aq9pxR$7AU)7G5XXށ@ p@ JtAq qŃ\p@ _K! 1 P%)@EqF`( %ܴSMEPd{QS$_$@b88;X@40C!囂pAcو@I3p2 h lOB K|HG {ni  R#O|Q Y!~lP=U5FY! G1WF*H0+@ ,k""%ǚ"dzɲjmVЊ"D : 'tø,A ~PU~\y :0H<2CFV 2@sp)h@Gr I?(Yw"736-r E;L@(]J@JЉ sŃP7 tlqխ&u[qJF hp Gx@hpD:`eQ$ 4%4LhW r9aadBrh.xʄXp;`d)B5 ƕP{cJ6b{u{ctLi%Y%t 4{ctBJ& ۫%=^B$ ?a CqTehj &IxBH $" `B-`vĊ"7d!G$=8C <""Bk\pE(2Ԡ#P  D *B!!(sD ї%$ja,u3M<0{`"q@diB@Nvnz38IN_L:OOs !.B஡f#uᆈ#8b 7FѡcKVڜ&T,(:.4`{H(&hcc "HiB`8%Bd;"+E|UЀA A&il@t1ʷo+ n@7?( @%HX'M|QъEvP6ˀ)9d! -IG?0|hF# Lje 5Y+Z\v(T4dJoPezYC ALxߨ5-(ApЁ * !!@ZKDX1g_tApEE$_DRBHP $u0 '$KR.0J;>\8 h/#Aa(v> CB@BKO wgDшl&?G@eXG M"fs W@NUj1j@l"kSn< ,%<*̱\VKg !, ( o!!!#6+ ?0p),A'9U>~W(ƃ~&ɒbV!~.Ӌꂁ!,2! H*\Ȱ@!,2!~ľǂчʣא!!Ћ~.mKS}jHAQ*D)8P@jv,DA?+iL\ڦSfA,sI('QA<)є0*ˆltH3j6 W AuA-Y6Ac6E\*An:[C:eΛ@1)xx'4 EQFNP(P SD$4C;a Q!B4,` \P/!,8b@xn#0^@Ȃtanj``Mv " , " 0& ̠` B@j0M>+ `/, BA(H!BÏ#? 1tT.hJ ,(0"hGʍv!ACs Zh075`"]G@:P@,Ѕ"ƆGJKpaq|IG^EDPhpBpPp@ hlj%df*AAxX98PAT ̀}$t3$zErA0P>I&r&t`j%j&zQyO$  tuE~ AG@ 9aC0*Go &,-F#;4%@ G 2|l!D ump/ D22*e`l+@ =LFX,\8&GW D B׫ANb̖' @x F(өHA 2`8j 0!z,1 VHy!"I0 =$@da&3Y/f! pJf$RR\A rx f@ @h]U!|P&P?Ot pD_Ǡp2ӝI詔R!'!.tЅ |p8x TBOb(MP)!Ǩ$a F;9Ia NjeDZ|UC\dz#@ȳ`!Z {L{H rKH|@춋ȳ%Ht^b˭K'R!0p쨸Щ;FQA )]bN)h`Z2H4aT㚆Lp JqCӃ\ - VO]J`m!TRm(&-dRd~,tI+:->K3zMP֦} -=U+C(J71nB9$ =A 0B C] "#]h0]tS'&H B`{c*NYsfeIWC^D4 0P+ jAQ_ B$$*XQ=,R0b"5;d(qȀ[.'`"RE)% 8x s$$ J!p1Ob(D X$@?hp T; AI) *ńHB!G9lߜ%$D&Ep"D$=bɐ2Xd(> IS*;RR(*l&BJ"El ?.~ FB:OɒU$niȚXNxDǸN`"W!FRWpJII(e: SͨF7юz if(MUҖ/ԨI_JӚ{MwS!,2! H*\Ȱ@;cleo-0.8.1/docs/helpers/progress_bar.rst000066400000000000000000000151651364636233300202770ustar00rootroot00000000000000Progress Bar ############ When executing longer-running commands, it may be helpful to show progress information, which updates as your command runs: .. image:: progress_2.gif To display progress details, use the ``progress_bar()`` method (which returns a ``ProgressBar`` instance), pass it a total number of units, and advance the progress as the command executes: .. code-block:: python def handle(self): # Create a new progress bar (50 units) progress = self.progress_bar(50) # Start and displays the progress bar for _ in range(50): # ... do some work # Advance the progress bar 1 unit progress.advance() # You can also advance the progress bar by more than 1 unit # progress.advance(3) # Ensure that the progress bar is at 100% progress.finish() Instead of advancing the bar by a number of steps (with the ``advance()`` method), you can also set the current progress by calling the ``set_progress()`` method. .. tip:: If your platform doesn't support ANSI codes, updates to the progress bar are added as new lines. To prevent the output from being flooded, adjust the ``set_redraw_frequency()`` accordingly. By default, when using a ``max``, the redraw frequency is set to *10%* of your ``max``. If you don't know the number of steps in advance, just omit the steps argument when using the ``progress_bar`` method: .. code-block:: python progress = self.progress_bar() The progress will then be displayed as a throbber: .. code-block:: text # no max steps (displays it like a throbber) 0 [>---------------------------] 5 [----->----------------------] 5 [============================] # max steps defined 0/3 [>---------------------------] 0% 1/3 [=========>------------------] 33% 3/3 [============================] 100% Whenever your task is finished, don't forget to call ``finish()`` to ensure that the progress bar display is refreshed with a 100% completion. .. note:: If you want to output something while the progress bar is running, call ``clear()`` first. After you're done, call ``display()`` to show the progress bar again. Customizing the Progress Bar ============================ Built-in Formats ---------------- By default, the information rendered on a progress bar depends on the current level of verbosity of the ``IO`` instance: .. code-block:: text # clikit.VERBOSITY_NORMAL (CLI with no verbosity flag) 0/3 [>---------------------------] 0% 1/3 [=========>------------------] 33% 3/3 [============================] 100% # clikit.VERBOSITY_VERBOSE (-v) 0/3 [>---------------------------] 0% 1 sec 1/3 [=========>------------------] 33% 1 sec 3/3 [============================] 100% 1 sec # clikit.VERBOSITY_VERY_VERBOSE (-vv) 0/3 [>---------------------------] 0% 1 sec 1/3 [=========>------------------] 33% 1 sec 3/3 [============================] 100% 1 sec # clikit.VERBOSITY_DEBUG (-vvv) 0/3 [>---------------------------] 0% 1 sec/1 sec 1.0 MB 1/3 [=========>------------------] 33% 1 sec/1 sec 1.0 MB 3/3 [============================] 100% 1 sec/1 sec 1.0 MB .. note:: If you call a command with the quiet flag (``-q``), the progress bar won't be displayed. Instead of relying on the verbosity mode of the current command, you can also force a format via ``set_format()``: .. code-block:: python progress.set_format('verbose') The built-in formats are the following: * ``normal`` * ``verbose`` * ``very_verbose`` * ``debug`` If you don't set the number of steps for your progress bar, use the ``_nomax`` variants: * ``normal_nomax`` * ``verbose_nomax`` * ``very_verbose_nomax`` * ``debug_nomax`` Custom Formats -------------- Instead of using the built-in formats, you can also set your own: .. code-block:: python progress.set_format('%bar%') This sets the format to only display the progress bar itself: .. code-block:: text >--------------------------- =========>------------------ ============================ A progress bar format is a string that contains specific placeholders (a name enclosed with the ``%`` character); the placeholders are replaced based on the current progress of the bar. Here is a list of the built-in placeholders: * ``current``: The current step * ``max``: The maximum number of steps (or 0 if no max is defined) * ``bar``: The bar itself * ``percent``: The percentage of completion (not available if no max is defined) * ``elapsed``: The time elapsed since the start of the progress bar * ``remaining``: The remaining time to complete the task (not available if no max is defined) * ``estimated``: The estimated time to complete the task (not available if no max is defined) * ``memory``: The current memory usage * ``message``: The current message attached to the progress bar For instance, here is how you could set the format to be the same as the ``debug`` one: .. code-block:: python progress.set_format(' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s% %memory:6s%') Notice the ``:6s`` part added to some placeholders? That's how you can tweak the appearance of the bar (formatting and alignment). The part after the colon (``:``) is used to set the format of the string. The ``message`` placeholder is a bit special as you must set the value yourself: .. code-block:: python progress.set_message('Task starts') progress.start() progress.set_message('Task in progress...') progress.advance() # ... progress.set_message('Task is finished') progress.finish() Bar Settings ------------ Amongst the placeholders, ``bar`` is a bit special as all the characters used to display it can be customized: .. code-block:: python # the finished part of the bar progress.set_bar_character('=') # the unfinished part of the bar progress.set_empty_bar_character(' ') # the progress character progress.set_progress_character('|') # the bar width progress.set_bar_width(50) .. warning:: For performance reasons, be careful if you set the total number of steps to a high number. For example, if you're iterating over a large number of items, consider setting the redraw frequency to a higher value by calling ``ProgressHelper.set_redraw_frequency()``, so it updates on only some iterations: .. code-block:: python progress.start(50000) # update every 100 iterations progress.set_redraw_frequency(100) for _ in range(50000) # ... do some work progress.advance() cleo-0.8.1/docs/helpers/question_helper.rst000066400000000000000000000130141364636233300210040ustar00rootroot00000000000000Question Helper ############### Asking the User for Confirmation ================================ Suppose you want to confirm an action before actually executing it. Add the following to your command: .. code-block:: python def handle(self): if not self.confirm('Continue with this action?', False): return In this case, the user will be asked "Continue with this action?". If the user answers with ``y`` it returns ``True`` or ``False`` if they answer with ``n``. The second argument to ``confirm()`` is the default value to return if the user doesn't enter any valid input. If the second argument is not provided, ``True`` is assumed. .. tip:: You can customize the regex used to check if the answer means "yes" in the third argument of the ``customize()`` method. For instance, to allow anything that starts with either ``y`` or ``j``, you would set it to: .. code-block:: python self.confirm('Continue with this action?', False, '(?i)^(y|j)') The regex defaults to ``(?i)^y``. Asking the User for Information =============================== You can also ask a question with more than a simple yes/no answer. For instance, if you want to know a user name, you can add this to your command: .. code-block:: python def handle(self): name = self.ask('Please enter your name', 'John Doe') The user will be asked "Please enter your name". They can type some name which will be returned by the ``ask()`` method. If they leave it empty, the default value (``John Doe`` here) is returned. Let the User Choose from a List of Answers ------------------------------------------ If you have a predefined set of answers the user can choose from, you could use a ``ChoiceQuestion`` or the ``choice()`` method which makes sure that the user can only enter a valid string from a predefined list: .. code-block:: python def handle(self): color = self.choice( 'Please select your favorite color (defaults to red)', ['red', 'blue', 'yellow'], 0 ) self.line('You have just selected: %s' % color) The option which should be selected by default is provided with the third argument. The default is ``None``, which means that no option is the default one. If the user enters an invalid string, an error message is shown and the user is asked to provide the answer another time, until they enter a valid string or reach the maximum number of attempts. The default value for the maximum number of attempts is ``None``, which means infinite number of attempts. Multiple Choices ---------------- Sometimes, multiple answers can be given. The ``ChoiceQuestion`` or ``choice()`` method provides this feature using comma separated values. This is disabled by default, to enable this use the ``multiple`` keyword if using the ``choice()`` method or the ``multiselect`` attribute if using the ``ChoiceQuestion`` directly: .. code-block:: python def handle(self): colors = self.choice( 'Please select your favorite color (defaults to red and blue), ['red', 'blue', 'yellow'], '0,1' multiple=True ) self.line('You have just selected: %s' % ', '.join(colors)) Now, when the user enters ``1,2``, the result will be: ``You have just selected: blue, yellow``. If the user does not enter anything, the result will be: ``You have just selected: red, blue``. Autocompletion -------------- You can also specify an array of potential answers for a given question. These will be autocompleted as the user types: .. code-block:: python def handle(self): names = ['John', 'Jane', 'Paul'] question = self.create_question('Please enter a name', default='John') question.set_autocomplete_values(names) name = self.ask(question) Hiding the User's Response -------------------------- You can also ask a question and hide the response. This is particularly convenient for passwords: .. code-block:: python def handle(self): password = self.secret('What is the database password?') Validating the Answer ===================== You can even validate the answer. For instance, you might only accept integers: .. code-block:: python def handle(self): question = self.create_question('Choose a number') question.set_validator(int) question.set_max_attempts(2) number = self.ask(question) The ``validator`` a callback which handles the validation. It should throw an exception if there is something wrong. The exception message is displayed in the console, so it is a good practice to put some useful information in it. The validator or the callback function should also return the value of the user's input if the validation was successful. You can set the max number of times to ask with the ``set_max_attempts()`` method. If you reach this max number it will use the default value. Using ``None`` means the amount of attempts is infinite. The user will be asked as long as they provide an invalid answer and will only be able to proceed if their input is valid. Testing a Command that Expects Input ==================================== If you want to write a unit test for a command which expects some kind of input from the command line, you need to set the helper input stream: .. code-block:: python def test_execute_command(self): command_tester = CommandTester(command) # Equals to a user inputting "Test" and hitting ENTER # If you need to enter a confirmation, "yes\n" will work command_tester.execute(inputs="Test\n") cleo-0.8.1/docs/helpers/table.rst000066400000000000000000000171661364636233300167010ustar00rootroot00000000000000Table ##### When building a console application it may be useful to display tabular data: .. code-block:: text +---------------+--------------------------+------------------+ | ISBN | Title | Author | +---------------+--------------------------+------------------+ | 99921-58-10-7 | Divine Comedy | Dante Alighieri | | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | | 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien | | 80-902734-1-6 | And Then There Were None | Agatha Christie | +---------------+--------------------------+------------------+ To display a table, use the ``table()`` method, set the headers, set the rows and then render the table: .. code-block:: python def handle(self): table = self.table() table.set_header_row(['ISBN', 'Title', 'Author']) table.set_rows([ ['99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'], ['9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'], ['960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'], ['80-902734-1-6', 'And Then There Were None', 'Agatha Christie'] ]) table.render(self.io) .. tip:: All these steps can be done in one go using the ``render_table`` method: .. code-block:: python self.render_table( ['ISBN', 'Title', 'Author'], [ ['99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'], ['9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'], ['960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'], ['80-902734-1-6', 'And Then There Were None', 'Agatha Christie'] ] ) You can add a table separator anywhere in the output by using ``table_seprator()``, which returns a ``TableSeparator``, as a row: .. code-block:: python table.set_rows([ ['99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'], ['9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'], self.table_separator(), ['960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'], ['80-902734-1-6', 'And Then There Were None', 'Agatha Christie'] ]) .. code-block:: text +---------------+--------------------------+------------------+ | ISBN | Title | Author | +---------------+--------------------------+------------------+ | 99921-58-10-7 | Divine Comedy | Dante Alighieri | | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | +---------------+--------------------------+------------------+ | 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien | | 80-902734-1-6 | And Then There Were None | Agatha Christie | +---------------+--------------------------+------------------+ The table style can be changed to any built-in styles via ``set_style()``: .. code-block:: python # same as calling nothing table.set_style('default') # changes the default style to compact table.set_style('compact') This code results in: .. code-block:: text ISBN Title Author 99921-58-10-7 Divine Comedy Dante Alighieri 9971-5-0210-0 A Tale of Two Cities Charles Dickens 960-425-059-0 The Lord of the Rings J. R. R. Tolkien 80-902734-1-6 And Then There Were None Agatha Christie You can also set the style to ``borderless``: .. code-block:: python table.set_style('borderless') which outputs: .. code-block:: text =============== ========================== ================== ISBN Title Author =============== ========================== ================== 99921-58-10-7 Divine Comedy Dante Alighieri 9971-5-0210-0 A Tale of Two Cities Charles Dickens 960-425-059-0 The Lord of the Rings J. R. R. Tolkien 80-902734-1-6 And Then There Were None Agatha Christie =============== ========================== ================== If the built-in styles do not fit your need, define your own: .. code-block:: python # by default, this is based on the default style style = self.table_style() # customize the style style.set_horizontal_border_char('|') style.set_vertical_border_char('-') style.set_crossing_char(' ') # use the style for this table table.set_style(style) Here is a full list of things you can customize: * ``set_adding_char()`` * ``set_horizontal_border_char()`` * ``set_vertical_border_char()`` * ``set_crossing_char()`` * ``set_cell_header_format()`` * ``set_cell_row_format()`` * ``set_border_format()`` * ``set_pad_type()`` .. tip:: The style can also be passed as a keyword argument to ``render_table()`` .. code-block:: python self.render_table( ['ISBN', 'Title', 'Author'], [ ['99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'], ['9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'], ['960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'], ['80-902734-1-6', 'And Then There Were None', 'Agatha Christie'] ] style='borderless' ) Spanning Multiple Columns and Rows ================================== To make a table cell that spans multiple columns you can use ``table_cell()``, which returns a ``TableCell`` instance: .. code-block:: python table = self.table() table.set_headers(['ISBN', 'Title', 'Author']) table.set_rows([ ['99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'], self.table_separator(), [self.table_cell('This value spans 3 columns.', colspan=3)] ]) table.render() This results in: .. code-block:: text +---------------+---------------+-----------------+ | ISBN | Title | Author | +---------------+---------------+-----------------+ | 99921-58-10-7 | Divine Comedy | Dante Alighieri | +---------------+---------------+-----------------+ | This value spans 3 columns. | +---------------+---------------+-----------------+ .. tip:: You can create a multiple-line page title using a header cell that spans the entire table width: .. code-block:: python table.set_headers([ [self.table_cell('Main table title', colspan=3)], ['ISBN', 'Title', 'Author'] ]) This generate: .. code-block:: text +-------+-------+--------+ | Main table title | +-------+-------+--------+ | ISBN | Title | Author | +-------+-------+--------+ | ... | +-------+-------+--------+ In a similar way you can span multiple rows: .. code-block:: python table = self.table() table.set_headers(['ISBN', 'Title', 'Author']) table.set_rows([ [ '978-0521567817', 'De Monarchia', self.table_cell('Dante Alighieri\nspans multiple rows', rowspan=2) ] ]) table.render() This outputs: .. code-block:: text +----------------+---------------+---------------------+ | ISBN | Title | Author | +----------------+---------------+---------------------+ | 978-0521567817 | De Monarchia | Dante Alighieri | | 978-0804169127 | Divine Comedy | spans multiple rows | +----------------+---------------+---------------------+ You can use the ``colspan`` and ``rowspan`` options at the same time which allows you to create any table layout you may wish. cleo-0.8.1/docs/index.rst000066400000000000000000000005261364636233300152470ustar00rootroot00000000000000Cleo ==== Cleo allows you to create beautiful and testable command-line commands. It is heavily inspired by the `Symfony Console Component `_, with some useful additions. .. toctree:: :maxdepth: 2 installation introduction helpers/index validators usage single_command_tool cleo-0.8.1/docs/installation.rst000066400000000000000000000004561364636233300166430ustar00rootroot00000000000000Installation ############ You can install Cleo in various ways: * Using [Poetry](https://poetry.eustace.io): .. code-block:: bash $ poetry add cleo * Using ``pip`` .. code-block:: bash $ pip install cleo * Use the official repository (https://github.com/sdispater/cleo) cleo-0.8.1/docs/introduction.rst000066400000000000000000000360171364636233300166650ustar00rootroot00000000000000Usage ##### To make a command that greets you from the command line, create ``greet_command.py`` and add the following to it: .. code-block:: python from cleo import Command class GreetCommand(Command): """ Greets someone greet {name? : Who do you want to greet?} {--y|yell : If set, the task will yell in uppercase letters} """ def handle(self): name = self.argument('name') if name: text = 'Hello {}'.format(name) else: text = 'Hello' if self.option('yell'): text = text.upper() self.line(text) You also need to create the file to run at the command line which creates an ``Application`` and adds commands to it: .. code-block:: python #!/usr/bin/env python from greet_command import GreetCommand from cleo import Application application = Application() application.add(GreetCommand()) if __name__ == '__main__': application.run() Test the new command by running the following .. code-block:: bash $ python application.py greet John This will print the following to the command line: .. code-block:: text Hello John You can also use the ``--yell`` option to make everything uppercase: .. code-block:: bash $ python application.py greet John --yell This prints: .. code-block:: text HELLO JOHN As you may have already seen, Cleo uses the command docstring to determine the command definition. The docstring must be in the following form : .. code-block:: python """ Command description Command signature """ The signature being in the following form: .. code-block:: python """ command:name {argument : Argument description} {--option : Option description} """ The signature can span multiple lines. .. code-block:: python """ command:name {argument : Argument description} {--option : Option description} """ Coloring the Output =================== Whenever you output text, you can surround the text with tags to color its output. For example: .. code-block:: python # green text self.line('foo') # yellow text self.line('foo') # black text on a cyan background self.line('foo') # white text on a red background self.line('foo') The closing tag can be replaced by ````, which revokes all formatting options established by the last opened tag. It is possible to define your own styles using the ``add_style()`` method: .. code-block:: python self.add_style('fire', fg='red', bg='yellow', options=['bold', 'blink']) self.line('foo') Available foreground and background colors are: ``black``, ``red``, ``green``, ``yellow``, ``blue``, ``magenta``, ``cyan`` and ``white``. And available options are: ``bold``, ``underscore``, ``blink``, ``reverse`` and ``conceal``. You can also set these colors and options inside the tag name: .. code-block:: python # green text self.line('foo') # black text on a cyan background self.line('foo') # bold text on a yellow background self.line('foo') Verbosity Levels ================ Cleo has four verbosity levels. These are defined in the ``Output`` class: ======================================= ================================== ====================== Mode Meaning Console option ======================================= ================================== ====================== ``NA`` Do not output any messages ``-q`` or ``--quiet`` ``clikit.VERBOSITY_NORMAL`` The default verbosity level (none) ``clikit.VERBOSITY_VERBOSE`` Increased verbosity of messages ``-v`` ``clikit.VERBOSITY_VERY_VERBOSE`` Informative non essential messages ``-vv`` ``clikit.VERBOSITY_DEBUG`` Debug messages ``-vvv`` ======================================= ================================== ====================== It is possible to print a message in a command for only a specific verbosity level. For example: .. code-block:: python if clikit.VERBOSITY_VERBOSE <= self.io.verbosity: self.line(...) There are also more semantic methods you can use to test for each of the verbosity levels: .. code-block:: python if self.output.is_quiet(): # ... if self.output.is_verbose(): # ... You can also pass the verbosity flag directly to `line()`. .. code-block:: python self.line("", verbosity=clikit.VERBOSITY_VERBOSE) When the quiet level is used, all output is suppressed. Using Arguments =============== The most interesting part of the commands are the arguments and options that you can make available. Arguments are the strings - separated by spaces - that come after the command name itself. They are ordered, and can be optional or required. For example, add an optional ``last_name`` argument to the command and make the ``name`` argument required: .. code-block:: python class GreetCommand(Command): """ Greets someone greet {name : Who do you want to greet?} {last_name? : Your last name?} {--y|yell : If set, the task will yell in uppercase letters} """ You now have access to a ``last_name`` argument in your command: .. code-block:: python last_name = self.argument('last_name') if last_name: text += ' {}'.format(last_name) The command can now be used in either of the following ways: .. code-block:: bash $ python application.py greet John $ python application.py greet John Doe It is also possible to let an argument take a list of values (imagine you want to greet all your friends). For this it must be specified at the end of the argument list: .. code-block:: python class GreetCommand(Command): """ Greets someone greet {names* : Who do you want to greet?} {--y|yell : If set, the task will yell in uppercase letters} """ To use this, just specify as many names as you want: .. code-block:: bash $ python application.py demo:greet John Jane You can access the ``names`` argument as a list: .. code-block:: python names = self.argument('names') if names: text += ' {}'.format(', '.join(names)) There are 3 argument variants you can use: ================================ ==================================== =============================================================================================================== Mode Notation Value ================================ ==================================== =============================================================================================================== ``clikit.ARGUMENT_REQUIRED`` none (just write the argument name) The argument is required ``clikit.ARGUMENT_OPTIONAL`` ``argument?`` The argument is optional and therefore can be omitted ``clikit.ARGUMENT_MULTI_VALUED`` ``argument*`` The argument can contain an indefinite number of arguments and must be used at the end of the argument list ================================ ==================================== =============================================================================================================== You can combine them like this: .. code-block:: python class GreetCommand(Command): """ Greets someone greet {names?* : Who do you want to greet?} {--y|yell : If set, the task will yell in uppercase letters} """ If you want to set a default value, you can it like so: .. code-block:: text argument=default The argument will then be considered optional. Using Options ============= Unlike arguments, options are not ordered (meaning you can specify them in any order) and are specified with two dashes (e.g. ``--yell`` - you can also declare a one-letter shortcut that you can call with a single dash like ``-y``). Options are *always* optional, and can be setup to accept a value (e.g. ``--dir=src``) or simply as a boolean flag without a value (e.g. ``--yell``). .. tip:: It is also possible to make an option *optionally* accept a value (so that ``--yell`` or ``--yell=loud`` work). Options can also be configured to accept a list of values. For example, add a new option to the command that can be used to specify how many times in a row the message should be printed: .. code-block:: python class GreetCommand(Command): """ Greets someone greet {name? : Who do you want to greet?} {--y|yell : If set, the task will yell in uppercase letters} {--iterations=1 : How many times should the message be printed?} """ Next, use this in the command to print the message multiple times: .. code-block:: python for _ in range(0, self.option('iterations')): self.line(text) Now, when you run the task, you can optionally specify a ``--iterations`` flag: .. code-block:: bash $ python application.py demo:greet John $ python application.py demo:greet John --iterations=5 The first example will only print once, since ``iterations`` is empty and defaults to ``1``. The second example will print five times. Recall that options don't care about their order. So, either of the following will work: .. code-block:: bash $ python application.py demo:greet John --iterations=5 --yell $ python application.py demo:greet John --yell --iterations=5 There are 4 option variants you can use: ================================ =================================== ====================================================================================== Option Notation Value ================================ =================================== ====================================================================================== ``clikit.OPTION_MULTI_VALUED`` ``--option=*`` This option accepts multiple values (e.g. ``--dir=/foo --dir=/bar``) ``clikit.OPTION_NO_VALUE`` ``--option`` Do not accept input for this option (e.g. ``--yell``) ``clikit.OPTION_REQUIRED_VALUE`` ``--option=`` This value is required (e.g. ``--iterations=5``), the option itself is still optional ``clikit.OPTION_OPTIONAL_VALUE`` ``--option=?`` This option may or may not have a value (e.g. ``--yell`` or ``--yell=loud``) ================================ =================================== ====================================================================================== You can combine them like this: .. code-block:: python class GreetCommand(Command): """ Greets someone greet {name? : Who do you want to greet?} {--y|yell : If set, the task will yell in uppercase letters} {--iterations=?*1 : How many times should the message be printed?} """ Helpers ======= Cleo also contains a set of "helpers" - different small tools capable of helping you with different tasks: * :doc:`helpers/question_helper`: interactively ask the user for information * :doc:`helpers/progress_bar`: shows a progress bar * :doc:`helpers/table`: displays tabular data as a table Testing Commands ================ Cleo provides several tools to help you test your commands. The most useful one is the ``CommandTester`` class. It uses a special IO class to ease testing without a real console: .. code-block:: python import pytest from cleo import Application from cleo import CommandTester def test_execute(self): application = Application() application.add(GreetCommand()) command = application.find('demo:greet') command_tester = CommandTester(command) command_tester.execute() assert "..." == tester.io.fetch_output() The ``CommandTester.io.fetch_output()`` method returns what would have been displayed during a normal call from the console. ``CommandTester.io.fetch_error()`` is also available to get what you have been written to the stderr. You can test sending arguments and options to the command by passing them as a string to the ``CommandTester.execute()`` method: .. code-block:: python import pytest from cleo import Application from cleo import CommandTester def test_execute(self): application = Application() application.add(GreetCommand()) command = application.find('demo:greet') command_tester = CommandTester(command) command_tester.execute("John") assert "John" in tester.io.fetch_output() Testing with user inputs ------------------------ To test user inputs, you pass it to ``execute()``. .. code-block:: python command_tester = CommandTester(command) command_tester.execute(inputs="123\nfoo\nbar") .. tip:: You can also test a whole console application by using the ``ApplicationTester`` class. Calling an existing Command =========================== If a command depends on another one being run before it, instead of asking the user to remember the order of execution, you can call it directly yourself. This is also useful if you want to create a "meta" command that just runs a bunch of other commands. Calling a command from another one is straightforward: .. code-block:: python def handle(self): return_code = self.call('demo:greet', "John --yell") # ... .. tip:: If you want to suppress the output of the executed command, you can use the ``call_silent()`` method instead. Overwrite the current line ========================== If you want to overwrite the current line, you can use the ``overwrite()`` method. .. code-block:: python def handle(self): self.write('Processing...') # do some work self.overwrite('Done!') .. warning:: ``overwrite()`` will only work in combination with the ``write()`` method which does not add a new line. .. note:: ``overwrite()`` does not automatically add a new line so you must call ``line('')`` if necessary. Autocompletion ============== Cleo supports automatic (tab) completion in ``bash``, ``zsh`` and ``fish``. You can register completion for your application by running one of the following in a terminal, replacing ``[program]`` with the command you use to run your application: .. code-block:: bash # BASH - Ubuntu / Debian [program] completions bash | sudo tee /etc/bash_completion.d/[program].bash-completion # BASH - Mac OSX (with Homebrew "bash-completion") [program] completions bash > $(brew --prefix)/etc/bash_completion.d/[program].bash-completion # ZSH - Config file mkdir ~/.zfunc echo "fpath+=~/.zfunc" >> ~/.zshrc [program] completions zsh > ~/.zfunc/_test # FISH [program] completions fish > ~/.config/fish/completions/[program].fish cleo-0.8.1/docs/make.bat000066400000000000000000000144671364636233300150240ustar00rootroot00000000000000@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=_build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . set I18NSPHINXOPTS=%SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. xml to make Docutils-native XML files echo. pseudoxml to make pseudoxml-XML files for display purposes echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) %SPHINXBUILD% 2> nul if errorlevel 9009 ( echo. echo.The 'sphinx-build' command was not found. Make sure you have Sphinx echo.installed, then set the SPHINXBUILD environment variable to point echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.http://sphinx-doc.org/ exit /b 1 ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Cleo.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Cleo.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdf" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf cd %BUILDDIR%/.. echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdfja" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf-ja cd %BUILDDIR%/.. echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) if "%1" == "xml" ( %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml if errorlevel 1 exit /b 1 echo. echo.Build finished. The XML files are in %BUILDDIR%/xml. goto end ) if "%1" == "pseudoxml" ( %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml if errorlevel 1 exit /b 1 echo. echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. goto end ) :end cleo-0.8.1/docs/single_command_tool.rst000066400000000000000000000010411364636233300201450ustar00rootroot00000000000000Building a Single Command Application ##################################### When building a command line tool, you may not need to provide several commands. In such case, having to pass the command name each time is tedious. Fortunately, it is possible to remove this need by using `default()` when adding a command: .. code-block:: python from cleo import Application command = GreetCommand() app = Application() app.add(command.default()) # this now executes the 'GreetCommand' without passing its name app.run() cleo-0.8.1/docs/usage.rst000066400000000000000000000053721364636233300152500ustar00rootroot00000000000000Using Console Commands, Shortcuts and Built-in Commands ####################################################### In addition to the options you specify for your commands, there are some built-in options as well as a couple of built-in commands for Cleo. .. note:: These examples assume you have added a file ``application.py`` to run at the cli: .. code-block:: python #!/usr/bin/env python # application.py from cleo import Application application = Application() # ... if __name__ == '__main__': application.run() Built-in Commands ================= The help command lists the help information for the specified command. For example, to get the help for the ``list`` command: .. code-block:: bash $ python application.py help list Running ``help`` without specifying a command will list the global options: .. code-block:: bash $ python application.py help Global Options ============== You can get help information for any command with the ``--help`` option. To get help for the ``greet`` command: .. code-block:: bash $ python application.py greet --help $ python application.py greet -h You can suppress output with: .. code-block:: bash $ python application.py greet --quiet $ python application.py greet -q You can get more verbose messages (if this is supported for a command) with: .. code-block:: bash $ python application.py greet --verbose $ python application.py greet -v If you need more verbose output, use `-vv` or `-vvv` .. code-block:: bash $ python application.py greet -vv $ python application.py greet -vvv If you set the optional arguments to give your application a name and version: .. code-block:: python application = Application('console', '1.2') then you can use: .. code-block:: bash $ python application.py --version $ python application.py -V to get this information output: .. code-block:: text Console version 1.2 If you do not provide both arguments then it will just output: .. code-block:: text console tool You can force turning on ANSI output coloring with: .. code-block:: bash $ python application.py greet --ansi or turn it off with: .. code-block:: bash $ python application.py greet --no-ansi You can suppress any interactive questions from the command you are running with: .. code-block:: bash $ python application.py greet --no-interaction $ python application.py greet -n Shortcut Syntax =============== You do not have to type out the full command names. You can just type the shortest unambiguous name to run a command. So if there are non-clashing commands, then you can run ``help`` like this: .. code-block:: bash $ python application.py h cleo-0.8.1/poetry.lock000066400000000000000000001504131364636233300146530ustar00rootroot00000000000000[[package]] category = "dev" description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." name = "appdirs" optional = false python-versions = "*" version = "1.4.3" [[package]] category = "dev" description = "A few extensions to pyyaml." name = "aspy.yaml" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "1.3.0" [package.dependencies] pyyaml = "*" [[package]] category = "dev" description = "Atomic file writes." name = "atomicwrites" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "1.3.0" [[package]] category = "dev" description = "Classes Without Boilerplate" name = "attrs" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "19.3.0" [package.extras] azure-pipelines = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "pytest-azurepipelines"] dev = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "sphinx", "pre-commit"] docs = ["sphinx", "zope.interface"] tests = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] [[package]] category = "dev" description = "The uncompromising code formatter." marker = "python_version >= \"3.6\" and implementation_name != \"pypy\"" name = "black" optional = false python-versions = ">=3.6" version = "19.10b0" [package.dependencies] appdirs = "*" attrs = ">=18.1.0" click = ">=6.5" pathspec = ">=0.6,<1" regex = "*" toml = ">=0.9.4" typed-ast = ">=1.4.0" [package.extras] d = ["aiohttp (>=3.3.2)", "aiohttp-cors"] [[package]] category = "dev" description = "Validate configuration and produce human readable error messages." name = "cfgv" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "2.0.1" [package.dependencies] six = "*" [[package]] category = "dev" description = "Composable command line interface toolkit" marker = "python_version >= \"3.6\" and implementation_name != \"pypy\"" name = "click" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" version = "7.1.1" [[package]] category = "main" description = "CliKit is a group of utilities to build beautiful and testable command line interfaces." name = "clikit" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "0.6.0" [package.dependencies] pastel = ">=0.2.0,<0.3.0" pylev = ">=1.3,<2.0" [package.dependencies.crashtest] python = ">=3.6,<4.0" version = ">=0.3.0,<0.4.0" [package.dependencies.enum34] python = ">=2.7,<2.8" version = ">=1.1,<2.0" [package.dependencies.typing] python = ">=2.7,<2.8 || >=3.4,<3.5" version = ">=3.6,<4.0" [package.dependencies.typing-extensions] python = ">=3.5.0,<3.5.4" version = ">=3.6,<4.0" [[package]] category = "dev" description = "Cross-platform colored terminal text." marker = "sys_platform == \"win32\" and python_version == \"3.4\"" name = "colorama" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "0.4.1" [[package]] category = "dev" description = "Cross-platform colored terminal text." marker = "sys_platform == \"win32\" and python_version != \"3.4\" or platform_system == \"Windows\"" name = "colorama" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" version = "0.4.3" [[package]] category = "dev" description = "Updated configparser from Python 3.7 for Python 2.6+." marker = "python_version < \"3\"" name = "configparser" optional = false python-versions = ">=2.6" version = "4.0.2" [package.extras] docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] testing = ["pytest (>=3.5,<3.7.3 || >3.7.3)", "pytest-checkdocs (>=1.2)", "pytest-flake8", "pytest-black-multipy"] [[package]] category = "dev" description = "Backports and enhancements for the contextlib module" marker = "python_version < \"3.4\"" name = "contextlib2" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "0.6.0.post1" [[package]] category = "dev" description = "Code coverage measurement for Python" name = "coverage" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, <4" version = "4.5.4" [[package]] category = "dev" description = "Code coverage measurement for Python" name = "coverage" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" version = "5.1" [package.extras] toml = ["toml"] [[package]] category = "main" description = "Manage Python errors with ease" marker = "python_version >= \"3.6\" and python_version < \"4.0\"" name = "crashtest" optional = false python-versions = ">=3.6,<4.0" version = "0.3.0" [[package]] category = "dev" description = "Distribution utilities" name = "distlib" optional = false python-versions = "*" version = "0.3.0" [[package]] category = "main" description = "Python 3.4 Enum backported to 3.3, 3.2, 3.1, 2.7, 2.6, 2.5, and 2.4" marker = "python_version >= \"2.7\" and python_version < \"2.8\"" name = "enum34" optional = false python-versions = "*" version = "1.1.10" [[package]] category = "dev" description = "A platform independent file lock." name = "filelock" optional = false python-versions = "*" version = "3.0.12" [[package]] category = "dev" description = "Python function signatures from PEP362 for Python 2.6, 2.7 and 3.2+" marker = "python_version < \"3.0\"" name = "funcsigs" optional = false python-versions = "*" version = "1.0.2" [[package]] category = "dev" description = "Backport of the concurrent.futures package from Python 3" marker = "python_version < \"3.2\"" name = "futures" optional = false python-versions = ">=2.6, <3" version = "3.3.0" [[package]] category = "dev" description = "File identification library for Python" name = "identify" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" version = "1.4.14" [package.extras] license = ["editdistance"] [[package]] category = "dev" description = "Read metadata from Python packages" name = "importlib-metadata" optional = false python-versions = ">=2.7,!=3.0,!=3.1,!=3.2,!=3.3" version = "0.23" [package.dependencies] zipp = ">=0.5" [package.extras] docs = ["sphinx", "rst.linker"] testing = ["packaging", "importlib-resources"] [[package]] category = "dev" description = "Read metadata from Python packages" marker = "python_version < \"3.8\"" name = "importlib-metadata" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" version = "1.6.0" [package.dependencies] zipp = ">=0.5" [package.dependencies.configparser] python = "<3" version = ">=3.5" [package.dependencies.contextlib2] python = "<3" version = "*" [package.dependencies.pathlib2] python = "<3" version = "*" [package.extras] docs = ["sphinx", "rst.linker"] testing = ["packaging", "importlib-resources"] [[package]] category = "dev" description = "Read resources from Python packages" marker = "python_version < \"3.7\"" name = "importlib-resources" optional = false python-versions = ">=2.7,!=3.0,!=3.1,!=3.2,!=3.3" version = "1.0.2" [package.dependencies] [package.dependencies.typing] python = "<3.5" version = "*" [[package]] category = "dev" description = "Read resources from Python packages" marker = "python_version < \"3.7\"" name = "importlib-resources" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" version = "1.4.0" [package.dependencies] [package.dependencies.contextlib2] python = "<3" version = "*" [package.dependencies.importlib-metadata] python = "<3.8" version = "*" [package.dependencies.pathlib2] python = "<3" version = "*" [package.dependencies.singledispatch] python = "<3.4" version = "*" [package.dependencies.typing] python = "<3.5" version = "*" [package.dependencies.zipp] python = "<3.8" version = ">=0.4" [package.extras] docs = ["sphinx", "rst.linker", "jaraco.packaging"] [[package]] category = "dev" description = "Rolling backport of unittest.mock for all Pythons" marker = "python_version < \"3.0\"" name = "mock" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "3.0.5" [package.dependencies] six = "*" [package.dependencies.funcsigs] python = "<3.3" version = ">=1" [package.extras] build = ["twine", "wheel", "blurb"] docs = ["sphinx"] test = ["pytest", "pytest-cov"] [[package]] category = "dev" description = "More routines for operating on iterables, beyond itertools" marker = "python_version <= \"2.7\"" name = "more-itertools" optional = false python-versions = "*" version = "5.0.0" [package.dependencies] six = ">=1.0.0,<2.0.0" [[package]] category = "dev" description = "More routines for operating on iterables, beyond itertools" marker = "python_version > \"2.7\"" name = "more-itertools" optional = false python-versions = ">=3.4" version = "7.2.0" [[package]] category = "dev" description = "More routines for operating on iterables, beyond itertools" marker = "python_version > \"2.7\"" name = "more-itertools" optional = false python-versions = ">=3.5" version = "8.2.0" [[package]] category = "dev" description = "Node.js virtual environment builder" name = "nodeenv" optional = false python-versions = "*" version = "1.3.5" [[package]] category = "dev" description = "Core utilities for Python packages" name = "packaging" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "20.3" [package.dependencies] pyparsing = ">=2.0.2" six = "*" [[package]] category = "main" description = "Bring colors to your terminal." name = "pastel" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "0.2.0" [[package]] category = "dev" description = "Object-oriented filesystem paths" marker = "python_version < \"3.6\" or python_version < \"3.4\" and sys_platform != \"win32\"" name = "pathlib2" optional = false python-versions = "*" version = "2.3.5" [package.dependencies] six = "*" [package.dependencies.scandir] python = "<3.5" version = "*" [[package]] category = "dev" description = "Utility library for gitignore style pattern matching of file paths." marker = "python_version >= \"3.6\" and implementation_name != \"pypy\"" name = "pathspec" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" version = "0.8.0" [[package]] category = "dev" description = "plugin and hook calling mechanisms for python" name = "pluggy" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "0.13.1" [package.dependencies] [package.dependencies.importlib-metadata] python = "<3.8" version = ">=0.12" [package.extras] dev = ["pre-commit", "tox"] [[package]] category = "dev" description = "A framework for managing and maintaining multi-language pre-commit hooks." name = "pre-commit" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "1.18.3" [package.dependencies] "aspy.yaml" = "*" cfgv = ">=2.0.0" identify = ">=1.0.0" importlib-metadata = "*" nodeenv = ">=0.11.1" pyyaml = "*" six = "*" toml = "*" virtualenv = ">=15.2" [package.dependencies.importlib-resources] python = "<3.7" version = "*" [[package]] category = "dev" description = "A framework for managing and maintaining multi-language pre-commit hooks." name = "pre-commit" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" version = "1.21.0" [package.dependencies] "aspy.yaml" = "*" cfgv = ">=2.0.0" identify = ">=1.0.0" nodeenv = ">=0.11.1" pyyaml = "*" six = "*" toml = "*" virtualenv = ">=15.2" [package.dependencies.futures] python = "<3.2" version = "*" [package.dependencies.importlib-metadata] python = "<3.8" version = "*" [package.dependencies.importlib-resources] python = "<3.7" version = "*" [[package]] category = "dev" description = "library with cross-python path, ini-parsing, io, code, log facilities" name = "py" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "1.8.1" [[package]] category = "main" description = "A pure Python Levenshtein implementation that's not freaking GPL'd." name = "pylev" optional = false python-versions = "*" version = "1.3.0" [[package]] category = "dev" description = "Python parsing module" name = "pyparsing" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" version = "2.4.7" [[package]] category = "dev" description = "pytest: simple powerful testing with Python" name = "pytest" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" version = "4.6.9" [package.dependencies] atomicwrites = ">=1.0" attrs = ">=17.4.0" packaging = "*" pluggy = ">=0.12,<1.0" py = ">=1.5.0" six = ">=1.10.0" wcwidth = "*" [[package.dependencies.colorama]] python = "<3.4.0 || >=3.5.0" version = "*" [[package.dependencies.colorama]] python = ">=3.4,<3.5" version = "<=0.4.1" [[package.dependencies.more-itertools]] python = "<2.8" version = ">=4.0.0,<6.0.0" [[package.dependencies.more-itertools]] python = ">=2.8" version = ">=4.0.0" [package.dependencies.funcsigs] python = "<3.0" version = ">=1.0" [package.dependencies.importlib-metadata] python = "<3.8" version = ">=0.12" [package.dependencies.pathlib2] python = "<3.6" version = ">=2.2.0" [package.extras] testing = ["argcomplete", "hypothesis (>=3.56)", "nose", "requests", "mock"] [[package]] category = "dev" description = "Pytest plugin for measuring coverage." name = "pytest-cov" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "2.8.1" [package.dependencies] coverage = ">=4.4" pytest = ">=3.6" [package.extras] testing = ["fields", "hunter", "process-tests (2.0.2)", "six", "virtualenv"] [[package]] category = "dev" description = "Thin-wrapper around the mock package for easier use with py.test" name = "pytest-mock" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "1.13.0" [package.dependencies] pytest = ">=2.7" [package.dependencies.mock] python = "<3.0" version = "*" [package.extras] dev = ["pre-commit", "tox"] [[package]] category = "dev" description = "YAML parser and emitter for Python" name = "pyyaml" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "5.2" [[package]] category = "dev" description = "YAML parser and emitter for Python" name = "pyyaml" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" version = "5.3.1" [[package]] category = "dev" description = "Alternative regular expression module, to replace re." marker = "python_version >= \"3.6\" and implementation_name != \"pypy\"" name = "regex" optional = false python-versions = "*" version = "2020.4.4" [[package]] category = "dev" description = "scandir, a better directory iterator and faster os.walk()" marker = "python_version < \"3.5\" or python_version < \"3.4\" and sys_platform != \"win32\"" name = "scandir" optional = false python-versions = "*" version = "1.10.0" [[package]] category = "dev" description = "This library brings functools.singledispatch from Python 3.4 to Python 2.6-3.3." marker = "python_version < \"3.4\"" name = "singledispatch" optional = false python-versions = "*" version = "3.4.0.3" [package.dependencies] six = "*" [[package]] category = "dev" description = "Python 2 and 3 compatibility utilities" name = "six" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" version = "1.14.0" [[package]] category = "dev" description = "Python Library for Tom's Obvious, Minimal Language" name = "toml" optional = false python-versions = "*" version = "0.10.0" [[package]] category = "dev" description = "tox is a generic virtualenv management and test command line tool" name = "tox" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "3.14.0" [package.dependencies] filelock = ">=3.0.0,<4" packaging = ">=14" pluggy = ">=0.12.0,<1" py = ">=1.4.17,<2" six = ">=1.0.0,<2" toml = ">=0.9.4" virtualenv = ">=14.0.0" [package.dependencies.importlib-metadata] python = "<3.8" version = ">=0.12,<1" [package.extras] docs = ["sphinx (>=2.0.0,<3)", "towncrier (>=18.5.0)", "pygments-github-lexers (>=0.0.5)", "sphinxcontrib-autoprogram (>=0.1.5)"] testing = ["freezegun (>=0.3.11,<1)", "pathlib2 (>=2.3.3,<3)", "pytest (>=4.0.0,<6)", "pytest-cov (>=2.5.1,<3)", "pytest-mock (>=1.10.0,<2)", "pytest-xdist (>=1.22.2,<2)", "pytest-randomly (>=1.2.3,<2)", "flaky (>=3.4.0,<4)", "psutil (>=5.6.1,<6)"] [[package]] category = "dev" description = "tox is a generic virtualenv management and test command line tool" name = "tox" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" version = "3.14.6" [package.dependencies] colorama = ">=0.4.1" filelock = ">=3.0.0,<4" packaging = ">=14" pluggy = ">=0.12.0,<1" py = ">=1.4.17,<2" six = ">=1.14.0,<2" toml = ">=0.9.4" virtualenv = ">=16.0.0,<20.0.0 || >20.0.0,<20.0.1 || >20.0.1,<20.0.2 || >20.0.2,<20.0.3 || >20.0.3,<20.0.4 || >20.0.4,<20.0.5 || >20.0.5,<20.0.6 || >20.0.6,<20.0.7 || >20.0.7" [package.dependencies.importlib-metadata] python = "<3.8" version = ">=0.12,<2" [package.extras] docs = ["sphinx (>=2.0.0,<3)", "towncrier (>=18.5.0)", "pygments-github-lexers (>=0.0.5)", "sphinxcontrib-autoprogram (>=0.1.5)"] testing = ["freezegun (>=0.3.11,<1)", "pathlib2 (>=2.3.3,<3)", "pytest (>=4.0.0,<6)", "pytest-cov (>=2.5.1,<3)", "pytest-mock (>=1.10.0,<2)", "pytest-xdist (>=1.22.2,<2)", "pytest-randomly (>=1.0.0,<4)", "flaky (>=3.4.0,<4)", "psutil (>=5.6.1,<6)"] [[package]] category = "dev" description = "a fork of Python 2 and 3 ast modules with type comment support" marker = "python_version >= \"3.6\" and implementation_name != \"pypy\"" name = "typed-ast" optional = false python-versions = "*" version = "1.4.1" [[package]] category = "main" description = "Type Hints for Python" marker = "python_version >= \"2.7\" and python_version < \"2.8\" or python_version >= \"3.4\" and python_version < \"3.5\" or python_version < \"3.5\"" name = "typing" optional = false python-versions = "*" version = "3.7.4.1" [[package]] category = "main" description = "Backported and Experimental Type Hints for Python 3.5+" marker = "python_version >= \"3.5.0\" and python_version < \"3.5.4\"" name = "typing-extensions" optional = false python-versions = "*" version = "3.7.4.2" [[package]] category = "dev" description = "Virtual Python Environment builder" name = "virtualenv" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" version = "20.0.18" [package.dependencies] appdirs = ">=1.4.3,<2" distlib = ">=0.3.0,<1" filelock = ">=3.0.0,<4" six = ">=1.9.0,<2" [package.dependencies.contextlib2] python = "<3.3" version = ">=0.6.0,<1" [package.dependencies.importlib-metadata] python = "<3.8" version = ">=0.12,<2" [package.dependencies.importlib-resources] python = "<3.7" version = ">=1.0,<2" [package.dependencies.pathlib2] python = "<3.4" version = ">=2.3.3,<3" [package.extras] docs = ["sphinx (>=2.0.0,<3)", "sphinx-argparse (>=0.2.5,<1)", "sphinx-rtd-theme (>=0.4.3,<1)", "towncrier (>=19.9.0rc1)", "proselint (>=0.10.2,<1)"] testing = ["pytest (>=4.0.0,<6)", "coverage (>=4.5.1,<6)", "pytest-mock (>=2.0.0,<3)", "pytest-env (>=0.6.2,<1)", "pytest-timeout (>=1.3.4,<2)", "packaging (>=20.0)", "xonsh (>=0.9.16,<1)"] [[package]] category = "dev" description = "Measures number of Terminal column cells of wide-character codes" name = "wcwidth" optional = false python-versions = "*" version = "0.1.9" [[package]] category = "dev" description = "Backport of pathlib-compatible object wrapper for zip files" marker = "python_version < \"3.8\"" name = "zipp" optional = false python-versions = ">=2.7" version = "1.2.0" [package.dependencies] [package.dependencies.contextlib2] python = "<3.4" version = "*" [package.extras] docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] testing = ["pathlib2", "unittest2", "jaraco.itertools", "func-timeout"] [metadata] content-hash = "fadd2f6181c1c391b9218340b4add46a8edd03b47d656aa4a015bdade63c72d5" python-versions = "~2.7 || ^3.4" [metadata.files] appdirs = [ {file = "appdirs-1.4.3-py2.py3-none-any.whl", hash = "sha256:d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e"}, {file = "appdirs-1.4.3.tar.gz", hash = "sha256:9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92"}, ] "aspy.yaml" = [ {file = "aspy.yaml-1.3.0-py2.py3-none-any.whl", hash = "sha256:463372c043f70160a9ec950c3f1e4c3a82db5fca01d334b6bc89c7164d744bdc"}, {file = "aspy.yaml-1.3.0.tar.gz", hash = "sha256:e7c742382eff2caed61f87a39d13f99109088e5e93f04d76eb8d4b28aa143f45"}, ] atomicwrites = [ {file = "atomicwrites-1.3.0-py2.py3-none-any.whl", hash = "sha256:03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4"}, {file = "atomicwrites-1.3.0.tar.gz", hash = "sha256:75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6"}, ] attrs = [ {file = "attrs-19.3.0-py2.py3-none-any.whl", hash = "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c"}, {file = "attrs-19.3.0.tar.gz", hash = "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"}, ] black = [ {file = "black-19.10b0-py36-none-any.whl", hash = "sha256:1b30e59be925fafc1ee4565e5e08abef6b03fe455102883820fe5ee2e4734e0b"}, {file = "black-19.10b0.tar.gz", hash = "sha256:c2edb73a08e9e0e6f65a0e6af18b059b8b1cdd5bef997d7a0b181df93dc81539"}, ] cfgv = [ {file = "cfgv-2.0.1-py2.py3-none-any.whl", hash = "sha256:fbd93c9ab0a523bf7daec408f3be2ed99a980e20b2d19b50fc184ca6b820d289"}, {file = "cfgv-2.0.1.tar.gz", hash = "sha256:edb387943b665bf9c434f717bf630fa78aecd53d5900d2e05da6ad6048553144"}, ] click = [ {file = "click-7.1.1-py2.py3-none-any.whl", hash = "sha256:e345d143d80bf5ee7534056164e5e112ea5e22716bbb1ce727941f4c8b471b9a"}, {file = "click-7.1.1.tar.gz", hash = "sha256:8a18b4ea89d8820c5d0c7da8a64b2c324b4dabb695804dbfea19b9be9d88c0cc"}, ] clikit = [ {file = "clikit-0.6.0-py2.py3-none-any.whl", hash = "sha256:a666d0727b1ddbfdef9ed2bfbda00ad6f1dd1fa19255ff79b8ca05df77809b7b"}, {file = "clikit-0.6.0.tar.gz", hash = "sha256:16b8aa2703413eae1138b50bdf825cb1465e0ee456efab4e8effe3bf03b7c2a4"}, ] colorama = [ {file = "colorama-0.4.1-py2.py3-none-any.whl", hash = "sha256:f8ac84de7840f5b9c4e3347b3c1eaa50f7e49c2b07596221daec5edaabbd7c48"}, {file = "colorama-0.4.1.tar.gz", hash = "sha256:05eed71e2e327246ad6b38c540c4a3117230b19679b875190486ddd2d721422d"}, {file = "colorama-0.4.3-py2.py3-none-any.whl", hash = "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff"}, {file = "colorama-0.4.3.tar.gz", hash = "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1"}, ] configparser = [ {file = "configparser-4.0.2-py2.py3-none-any.whl", hash = "sha256:254c1d9c79f60c45dfde850850883d5aaa7f19a23f13561243a050d5a7c3fe4c"}, {file = "configparser-4.0.2.tar.gz", hash = "sha256:c7d282687a5308319bf3d2e7706e575c635b0a470342641c93bea0ea3b5331df"}, ] contextlib2 = [ {file = "contextlib2-0.6.0.post1-py2.py3-none-any.whl", hash = "sha256:3355078a159fbb44ee60ea80abd0d87b80b78c248643b49aa6d94673b413609b"}, {file = "contextlib2-0.6.0.post1.tar.gz", hash = "sha256:01f490098c18b19d2bd5bb5dc445b2054d2fa97f09a4280ba2c5f3c394c8162e"}, ] coverage = [ {file = "coverage-4.5.4-cp26-cp26m-macosx_10_12_x86_64.whl", hash = "sha256:eee64c616adeff7db37cc37da4180a3a5b6177f5c46b187894e633f088fb5b28"}, {file = "coverage-4.5.4-cp27-cp27m-macosx_10_12_x86_64.whl", hash = "sha256:ef824cad1f980d27f26166f86856efe11eff9912c4fed97d3804820d43fa550c"}, {file = "coverage-4.5.4-cp27-cp27m-macosx_10_13_intel.whl", hash = "sha256:9a334d6c83dfeadae576b4d633a71620d40d1c379129d587faa42ee3e2a85cce"}, {file = "coverage-4.5.4-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:7494b0b0274c5072bddbfd5b4a6c6f18fbbe1ab1d22a41e99cd2d00c8f96ecfe"}, {file = "coverage-4.5.4-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:826f32b9547c8091679ff292a82aca9c7b9650f9fda3e2ca6bf2ac905b7ce888"}, {file = "coverage-4.5.4-cp27-cp27m-win32.whl", hash = "sha256:63a9a5fc43b58735f65ed63d2cf43508f462dc49857da70b8980ad78d41d52fc"}, {file = "coverage-4.5.4-cp27-cp27m-win_amd64.whl", hash = "sha256:e2ede7c1d45e65e209d6093b762e98e8318ddeff95317d07a27a2140b80cfd24"}, {file = "coverage-4.5.4-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:dd579709a87092c6dbee09d1b7cfa81831040705ffa12a1b248935274aee0437"}, {file = "coverage-4.5.4-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:08907593569fe59baca0bf152c43f3863201efb6113ecb38ce7e97ce339805a6"}, {file = "coverage-4.5.4-cp33-cp33m-macosx_10_10_x86_64.whl", hash = "sha256:6b62544bb68106e3f00b21c8930e83e584fdca005d4fffd29bb39fb3ffa03cb5"}, {file = "coverage-4.5.4-cp34-cp34m-macosx_10_12_x86_64.whl", hash = "sha256:331cb5115673a20fb131dadd22f5bcaf7677ef758741312bee4937d71a14b2ef"}, {file = "coverage-4.5.4-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:bf1ef9eb901113a9805287e090452c05547578eaab1b62e4ad456fcc049a9b7e"}, {file = "coverage-4.5.4-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:386e2e4090f0bc5df274e720105c342263423e77ee8826002dcffe0c9533dbca"}, {file = "coverage-4.5.4-cp34-cp34m-win32.whl", hash = "sha256:fa964bae817babece5aa2e8c1af841bebb6d0b9add8e637548809d040443fee0"}, {file = "coverage-4.5.4-cp34-cp34m-win_amd64.whl", hash = "sha256:df6712284b2e44a065097846488f66840445eb987eb81b3cc6e4149e7b6982e1"}, {file = "coverage-4.5.4-cp35-cp35m-macosx_10_12_x86_64.whl", hash = "sha256:efc89291bd5a08855829a3c522df16d856455297cf35ae827a37edac45f466a7"}, {file = "coverage-4.5.4-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:e4ef9c164eb55123c62411f5936b5c2e521b12356037b6e1c2617cef45523d47"}, {file = "coverage-4.5.4-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:ff37757e068ae606659c28c3bd0d923f9d29a85de79bf25b2b34b148473b5025"}, {file = "coverage-4.5.4-cp35-cp35m-win32.whl", hash = "sha256:bf0a7aed7f5521c7ca67febd57db473af4762b9622254291fbcbb8cd0ba5e33e"}, {file = "coverage-4.5.4-cp35-cp35m-win_amd64.whl", hash = "sha256:19e4df788a0581238e9390c85a7a09af39c7b539b29f25c89209e6c3e371270d"}, {file = "coverage-4.5.4-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:60851187677b24c6085248f0a0b9b98d49cba7ecc7ec60ba6b9d2e5574ac1ee9"}, {file = "coverage-4.5.4-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:245388cda02af78276b479f299bbf3783ef0a6a6273037d7c60dc73b8d8d7755"}, {file = "coverage-4.5.4-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:c0afd27bc0e307a1ffc04ca5ec010a290e49e3afbe841c5cafc5c5a80ecd81c9"}, {file = "coverage-4.5.4-cp36-cp36m-win32.whl", hash = "sha256:6ba744056423ef8d450cf627289166da65903885272055fb4b5e113137cfa14f"}, {file = "coverage-4.5.4-cp36-cp36m-win_amd64.whl", hash = "sha256:af7ed8a8aa6957aac47b4268631fa1df984643f07ef00acd374e456364b373f5"}, {file = "coverage-4.5.4-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:3a794ce50daee01c74a494919d5ebdc23d58873747fa0e288318728533a3e1ca"}, {file = "coverage-4.5.4-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:0be0f1ed45fc0c185cfd4ecc19a1d6532d72f86a2bac9de7e24541febad72650"}, {file = "coverage-4.5.4-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:eca2b7343524e7ba246cab8ff00cab47a2d6d54ada3b02772e908a45675722e2"}, {file = "coverage-4.5.4-cp37-cp37m-win32.whl", hash = "sha256:93715dffbcd0678057f947f496484e906bf9509f5c1c38fc9ba3922893cda5f5"}, {file = "coverage-4.5.4-cp37-cp37m-win_amd64.whl", hash = "sha256:23cc09ed395b03424d1ae30dcc292615c1372bfba7141eb85e11e50efaa6b351"}, {file = "coverage-4.5.4-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:141f08ed3c4b1847015e2cd62ec06d35e67a3ac185c26f7635f4406b90afa9c5"}, {file = "coverage-4.5.4.tar.gz", hash = "sha256:e07d9f1a23e9e93ab5c62902833bf3e4b1f65502927379148b6622686223125c"}, {file = "coverage-5.1-cp27-cp27m-macosx_10_12_x86_64.whl", hash = "sha256:0cb4be7e784dcdc050fc58ef05b71aa8e89b7e6636b99967fadbdba694cf2b65"}, {file = "coverage-5.1-cp27-cp27m-macosx_10_13_intel.whl", hash = "sha256:c317eaf5ff46a34305b202e73404f55f7389ef834b8dbf4da09b9b9b37f76dd2"}, {file = "coverage-5.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:b83835506dfc185a319031cf853fa4bb1b3974b1f913f5bb1a0f3d98bdcded04"}, {file = "coverage-5.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5f2294dbf7875b991c381e3d5af2bcc3494d836affa52b809c91697449d0eda6"}, {file = "coverage-5.1-cp27-cp27m-win32.whl", hash = "sha256:de807ae933cfb7f0c7d9d981a053772452217df2bf38e7e6267c9cbf9545a796"}, {file = "coverage-5.1-cp27-cp27m-win_amd64.whl", hash = "sha256:bf9cb9a9fd8891e7efd2d44deb24b86d647394b9705b744ff6f8261e6f29a730"}, {file = "coverage-5.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:acf3763ed01af8410fc36afea23707d4ea58ba7e86a8ee915dfb9ceff9ef69d0"}, {file = "coverage-5.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:dec5202bfe6f672d4511086e125db035a52b00f1648d6407cc8e526912c0353a"}, {file = "coverage-5.1-cp35-cp35m-macosx_10_12_x86_64.whl", hash = "sha256:7a5bdad4edec57b5fb8dae7d3ee58622d626fd3a0be0dfceda162a7035885ecf"}, {file = "coverage-5.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:1601e480b9b99697a570cea7ef749e88123c04b92d84cedaa01e117436b4a0a9"}, {file = "coverage-5.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:dbe8c6ae7534b5b024296464f387d57c13caa942f6d8e6e0346f27e509f0f768"}, {file = "coverage-5.1-cp35-cp35m-win32.whl", hash = "sha256:a027ef0492ede1e03a8054e3c37b8def89a1e3c471482e9f046906ba4f2aafd2"}, {file = "coverage-5.1-cp35-cp35m-win_amd64.whl", hash = "sha256:0e61d9803d5851849c24f78227939c701ced6704f337cad0a91e0972c51c1ee7"}, {file = "coverage-5.1-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:2d27a3f742c98e5c6b461ee6ef7287400a1956c11421eb574d843d9ec1f772f0"}, {file = "coverage-5.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:66460ab1599d3cf894bb6baee8c684788819b71a5dc1e8fa2ecc152e5d752019"}, {file = "coverage-5.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:5c542d1e62eece33c306d66fe0a5c4f7f7b3c08fecc46ead86d7916684b36d6c"}, {file = "coverage-5.1-cp36-cp36m-win32.whl", hash = "sha256:2742c7515b9eb368718cd091bad1a1b44135cc72468c731302b3d641895b83d1"}, {file = "coverage-5.1-cp36-cp36m-win_amd64.whl", hash = "sha256:dead2ddede4c7ba6cb3a721870f5141c97dc7d85a079edb4bd8d88c3ad5b20c7"}, {file = "coverage-5.1-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:01333e1bd22c59713ba8a79f088b3955946e293114479bbfc2e37d522be03355"}, {file = "coverage-5.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:e1ea316102ea1e1770724db01998d1603ed921c54a86a2efcb03428d5417e489"}, {file = "coverage-5.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:adeb4c5b608574a3d647011af36f7586811a2c1197c861aedb548dd2453b41cd"}, {file = "coverage-5.1-cp37-cp37m-win32.whl", hash = "sha256:782caea581a6e9ff75eccda79287daefd1d2631cc09d642b6ee2d6da21fc0a4e"}, {file = "coverage-5.1-cp37-cp37m-win_amd64.whl", hash = "sha256:00f1d23f4336efc3b311ed0d807feb45098fc86dee1ca13b3d6768cdab187c8a"}, {file = "coverage-5.1-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:402e1744733df483b93abbf209283898e9f0d67470707e3c7516d84f48524f55"}, {file = "coverage-5.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:a3f3654d5734a3ece152636aad89f58afc9213c6520062db3978239db122f03c"}, {file = "coverage-5.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:6402bd2fdedabbdb63a316308142597534ea8e1895f4e7d8bf7476c5e8751fef"}, {file = "coverage-5.1-cp38-cp38-win32.whl", hash = "sha256:8fa0cbc7ecad630e5b0f4f35b0f6ad419246b02bc750de7ac66db92667996d24"}, {file = "coverage-5.1-cp38-cp38-win_amd64.whl", hash = "sha256:79a3cfd6346ce6c13145731d39db47b7a7b859c0272f02cdb89a3bdcbae233a0"}, {file = "coverage-5.1-cp39-cp39-win32.whl", hash = "sha256:a82b92b04a23d3c8a581fc049228bafde988abacba397d57ce95fe95e0338ab4"}, {file = "coverage-5.1-cp39-cp39-win_amd64.whl", hash = "sha256:bb28a7245de68bf29f6fb199545d072d1036a1917dca17a1e75bbb919e14ee8e"}, {file = "coverage-5.1.tar.gz", hash = "sha256:f90bfc4ad18450c80b024036eaf91e4a246ae287701aaa88eaebebf150868052"}, ] crashtest = [ {file = "crashtest-0.3.0-py3-none-any.whl", hash = "sha256:06069a9267c54be31c42b03574b72407bf780e13c82cb0238f24ea69cf25b6dd"}, {file = "crashtest-0.3.0.tar.gz", hash = "sha256:e9c06cc96400939ab5327123a3f699078eaad8a6283247d7b2ae0f6afffadf14"}, ] distlib = [ {file = "distlib-0.3.0.zip", hash = "sha256:2e166e231a26b36d6dfe35a48c4464346620f8645ed0ace01ee31822b288de21"}, ] enum34 = [ {file = "enum34-1.1.10-py2-none-any.whl", hash = "sha256:a98a201d6de3f2ab3db284e70a33b0f896fbf35f8086594e8c9e74b909058d53"}, {file = "enum34-1.1.10-py3-none-any.whl", hash = "sha256:c3858660960c984d6ab0ebad691265180da2b43f07e061c0f8dca9ef3cffd328"}, {file = "enum34-1.1.10.tar.gz", hash = "sha256:cce6a7477ed816bd2542d03d53db9f0db935dd013b70f336a95c73979289f248"}, ] filelock = [ {file = "filelock-3.0.12-py3-none-any.whl", hash = "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836"}, {file = "filelock-3.0.12.tar.gz", hash = "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59"}, ] funcsigs = [ {file = "funcsigs-1.0.2-py2.py3-none-any.whl", hash = "sha256:330cc27ccbf7f1e992e69fef78261dc7c6569012cf397db8d3de0234e6c937ca"}, {file = "funcsigs-1.0.2.tar.gz", hash = "sha256:a7bb0f2cf3a3fd1ab2732cb49eba4252c2af4240442415b4abce3b87022a8f50"}, ] futures = [ {file = "futures-3.3.0-py2-none-any.whl", hash = "sha256:49b3f5b064b6e3afc3316421a3f25f66c137ae88f068abbf72830170033c5e16"}, {file = "futures-3.3.0.tar.gz", hash = "sha256:7e033af76a5e35f58e56da7a91e687706faf4e7bdfb2cbc3f2cca6b9bcda9794"}, ] identify = [ {file = "identify-1.4.14-py2.py3-none-any.whl", hash = "sha256:2bb8760d97d8df4408f4e805883dad26a2d076f04be92a10a3e43f09c6060742"}, {file = "identify-1.4.14.tar.gz", hash = "sha256:faffea0fd8ec86bb146ac538ac350ed0c73908326426d387eded0bcc9d077522"}, ] importlib-metadata = [ {file = "importlib_metadata-0.23-py2.py3-none-any.whl", hash = "sha256:d5f18a79777f3aa179c145737780282e27b508fc8fd688cb17c7a813e8bd39af"}, {file = "importlib_metadata-0.23.tar.gz", hash = "sha256:aa18d7378b00b40847790e7c27e11673d7fed219354109d0e7b9e5b25dc3ad26"}, {file = "importlib_metadata-1.6.0-py2.py3-none-any.whl", hash = "sha256:2a688cbaa90e0cc587f1df48bdc97a6eadccdcd9c35fb3f976a09e3b5016d90f"}, {file = "importlib_metadata-1.6.0.tar.gz", hash = "sha256:34513a8a0c4962bc66d35b359558fd8a5e10cd472d37aec5f66858addef32c1e"}, ] importlib-resources = [ {file = "importlib_resources-1.0.2-py2.py3-none-any.whl", hash = "sha256:6e2783b2538bd5a14678284a3962b0660c715e5a0f10243fd5e00a4b5974f50b"}, {file = "importlib_resources-1.0.2.tar.gz", hash = "sha256:d3279fd0f6f847cced9f7acc19bd3e5df54d34f93a2e7bb5f238f81545787078"}, {file = "importlib_resources-1.4.0-py2.py3-none-any.whl", hash = "sha256:dd98ceeef3f5ad2ef4cc287b8586da4ebad15877f351e9688987ad663a0a29b8"}, {file = "importlib_resources-1.4.0.tar.gz", hash = "sha256:4019b6a9082d8ada9def02bece4a76b131518866790d58fdda0b5f8c603b36c2"}, ] mock = [ {file = "mock-3.0.5-py2.py3-none-any.whl", hash = "sha256:d157e52d4e5b938c550f39eb2fd15610db062441a9c2747d3dbfa9298211d0f8"}, {file = "mock-3.0.5.tar.gz", hash = "sha256:83657d894c90d5681d62155c82bda9c1187827525880eda8ff5df4ec813437c3"}, ] more-itertools = [ {file = "more-itertools-5.0.0.tar.gz", hash = "sha256:38a936c0a6d98a38bcc2d03fdaaedaba9f412879461dd2ceff8d37564d6522e4"}, {file = "more_itertools-5.0.0-py2-none-any.whl", hash = "sha256:c0a5785b1109a6bd7fac76d6837fd1feca158e54e521ccd2ae8bfe393cc9d4fc"}, {file = "more_itertools-5.0.0-py3-none-any.whl", hash = "sha256:fe7a7cae1ccb57d33952113ff4fa1bc5f879963600ed74918f1236e212ee50b9"}, {file = "more-itertools-7.2.0.tar.gz", hash = "sha256:409cd48d4db7052af495b09dec721011634af3753ae1ef92d2b32f73a745f832"}, {file = "more_itertools-7.2.0-py3-none-any.whl", hash = "sha256:92b8c4b06dac4f0611c0729b2f2ede52b2e1bac1ab48f089c7ddc12e26bb60c4"}, {file = "more-itertools-8.2.0.tar.gz", hash = "sha256:b1ddb932186d8a6ac451e1d95844b382f55e12686d51ca0c68b6f61f2ab7a507"}, {file = "more_itertools-8.2.0-py3-none-any.whl", hash = "sha256:5dd8bcf33e5f9513ffa06d5ad33d78f31e1931ac9a18f33d37e77a180d393a7c"}, ] nodeenv = [ {file = "nodeenv-1.3.5-py2.py3-none-any.whl", hash = "sha256:5b2438f2e42af54ca968dd1b374d14a1194848955187b0e5e4be1f73813a5212"}, ] packaging = [ {file = "packaging-20.3-py2.py3-none-any.whl", hash = "sha256:82f77b9bee21c1bafbf35a84905d604d5d1223801d639cf3ed140bd651c08752"}, {file = "packaging-20.3.tar.gz", hash = "sha256:3c292b474fda1671ec57d46d739d072bfd495a4f51ad01a055121d81e952b7a3"}, ] pastel = [ {file = "pastel-0.2.0-py2.py3-none-any.whl", hash = "sha256:18b559dc3ad4ba9b8bd5baebe6503f25f36d21460f021cf27a8d889cb5d17840"}, {file = "pastel-0.2.0.tar.gz", hash = "sha256:46155fc523bdd4efcd450bbcb3f2b94a6e3b25edc0eb493e081104ad09e1ca36"}, ] pathlib2 = [ {file = "pathlib2-2.3.5-py2.py3-none-any.whl", hash = "sha256:0ec8205a157c80d7acc301c0b18fbd5d44fe655968f5d947b6ecef5290fc35db"}, {file = "pathlib2-2.3.5.tar.gz", hash = "sha256:6cd9a47b597b37cc57de1c05e56fb1a1c9cc9fab04fe78c29acd090418529868"}, ] pathspec = [ {file = "pathspec-0.8.0-py2.py3-none-any.whl", hash = "sha256:7d91249d21749788d07a2d0f94147accd8f845507400749ea19c1ec9054a12b0"}, {file = "pathspec-0.8.0.tar.gz", hash = "sha256:da45173eb3a6f2a5a487efba21f050af2b41948be6ab52b6a1e3ff22bb8b7061"}, ] pluggy = [ {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, ] pre-commit = [ {file = "pre_commit-1.18.3-py2.py3-none-any.whl", hash = "sha256:fa78ff96e8e9ac94c748388597693f18b041a181c94a4f039ad20f45287ba44a"}, {file = "pre_commit-1.18.3.tar.gz", hash = "sha256:1d3c0587bda7c4e537a46c27f2c84aa006acc18facf9970bf947df596ce91f3f"}, {file = "pre_commit-1.21.0-py2.py3-none-any.whl", hash = "sha256:f92a359477f3252452ae2e8d3029de77aec59415c16ae4189bcfba40b757e029"}, {file = "pre_commit-1.21.0.tar.gz", hash = "sha256:8f48d8637bdae6fa70cc97db9c1dd5aa7c5c8bf71968932a380628c25978b850"}, ] py = [ {file = "py-1.8.1-py2.py3-none-any.whl", hash = "sha256:c20fdd83a5dbc0af9efd622bee9a5564e278f6380fffcacc43ba6f43db2813b0"}, {file = "py-1.8.1.tar.gz", hash = "sha256:5e27081401262157467ad6e7f851b7aa402c5852dbcb3dae06768434de5752aa"}, ] pylev = [ {file = "pylev-1.3.0-py2.py3-none-any.whl", hash = "sha256:1d29a87beb45ebe1e821e7a3b10da2b6b2f4c79b43f482c2df1a1f748a6e114e"}, {file = "pylev-1.3.0.tar.gz", hash = "sha256:063910098161199b81e453025653ec53556c1be7165a9b7c50be2f4d57eae1c3"}, ] pyparsing = [ {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, ] pytest = [ {file = "pytest-4.6.9-py2.py3-none-any.whl", hash = "sha256:c77a5f30a90e0ce24db9eaa14ddfd38d4afb5ea159309bdd2dae55b931bc9324"}, {file = "pytest-4.6.9.tar.gz", hash = "sha256:19e8f75eac01dd3f211edd465b39efbcbdc8fc5f7866d7dd49fedb30d8adf339"}, ] pytest-cov = [ {file = "pytest-cov-2.8.1.tar.gz", hash = "sha256:cc6742d8bac45070217169f5f72ceee1e0e55b0221f54bcf24845972d3a47f2b"}, {file = "pytest_cov-2.8.1-py2.py3-none-any.whl", hash = "sha256:cdbdef4f870408ebdbfeb44e63e07eb18bb4619fae852f6e760645fa36172626"}, ] pytest-mock = [ {file = "pytest-mock-1.13.0.tar.gz", hash = "sha256:e24a911ec96773022ebcc7030059b57cd3480b56d4f5d19b7c370ec635e6aed5"}, {file = "pytest_mock-1.13.0-py2.py3-none-any.whl", hash = "sha256:67e414b3caef7bff6fc6bd83b22b5bc39147e4493f483c2679bc9d4dc485a94d"}, ] pyyaml = [ {file = "PyYAML-5.2-cp27-cp27m-win32.whl", hash = "sha256:35ace9b4147848cafac3db142795ee42deebe9d0dad885ce643928e88daebdcc"}, {file = "PyYAML-5.2-cp27-cp27m-win_amd64.whl", hash = "sha256:ebc4ed52dcc93eeebeae5cf5deb2ae4347b3a81c3fa12b0b8c976544829396a4"}, {file = "PyYAML-5.2-cp35-cp35m-win32.whl", hash = "sha256:38a4f0d114101c58c0f3a88aeaa44d63efd588845c5a2df5290b73db8f246d15"}, {file = "PyYAML-5.2-cp35-cp35m-win_amd64.whl", hash = "sha256:483eb6a33b671408c8529106df3707270bfacb2447bf8ad856a4b4f57f6e3075"}, {file = "PyYAML-5.2-cp36-cp36m-win32.whl", hash = "sha256:7f38e35c00e160db592091751d385cd7b3046d6d51f578b29943225178257b31"}, {file = "PyYAML-5.2-cp36-cp36m-win_amd64.whl", hash = "sha256:0e7f69397d53155e55d10ff68fdfb2cf630a35e6daf65cf0bdeaf04f127c09dc"}, {file = "PyYAML-5.2-cp37-cp37m-win32.whl", hash = "sha256:e4c015484ff0ff197564917b4b4246ca03f411b9bd7f16e02a2f586eb48b6d04"}, {file = "PyYAML-5.2-cp37-cp37m-win_amd64.whl", hash = "sha256:4b6be5edb9f6bb73680f5bf4ee08ff25416d1400fbd4535fe0069b2994da07cd"}, {file = "PyYAML-5.2-cp38-cp38-win32.whl", hash = "sha256:8100c896ecb361794d8bfdb9c11fce618c7cf83d624d73d5ab38aef3bc82d43f"}, {file = "PyYAML-5.2-cp38-cp38-win_amd64.whl", hash = "sha256:2e9f0b7c5914367b0916c3c104a024bb68f269a486b9d04a2e8ac6f6597b7803"}, {file = "PyYAML-5.2.tar.gz", hash = "sha256:c0ee8eca2c582d29c3c2ec6e2c4f703d1b7f1fb10bc72317355a746057e7346c"}, {file = "PyYAML-5.3.1-cp27-cp27m-win32.whl", hash = "sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f"}, {file = "PyYAML-5.3.1-cp27-cp27m-win_amd64.whl", hash = "sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76"}, {file = "PyYAML-5.3.1-cp35-cp35m-win32.whl", hash = "sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2"}, {file = "PyYAML-5.3.1-cp35-cp35m-win_amd64.whl", hash = "sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c"}, {file = "PyYAML-5.3.1-cp36-cp36m-win32.whl", hash = "sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2"}, {file = "PyYAML-5.3.1-cp36-cp36m-win_amd64.whl", hash = "sha256:69f00dca373f240f842b2931fb2c7e14ddbacd1397d57157a9b005a6a9942648"}, {file = "PyYAML-5.3.1-cp37-cp37m-win32.whl", hash = "sha256:d13155f591e6fcc1ec3b30685d50bf0711574e2c0dfffd7644babf8b5102ca1a"}, {file = "PyYAML-5.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf"}, {file = "PyYAML-5.3.1-cp38-cp38-win32.whl", hash = "sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97"}, {file = "PyYAML-5.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee"}, {file = "PyYAML-5.3.1.tar.gz", hash = "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d"}, ] regex = [ {file = "regex-2020.4.4-cp27-cp27m-win32.whl", hash = "sha256:90742c6ff121a9c5b261b9b215cb476eea97df98ea82037ec8ac95d1be7a034f"}, {file = "regex-2020.4.4-cp27-cp27m-win_amd64.whl", hash = "sha256:24f4f4062eb16c5bbfff6a22312e8eab92c2c99c51a02e39b4eae54ce8255cd1"}, {file = "regex-2020.4.4-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:08119f707f0ebf2da60d2f24c2f39ca616277bb67ef6c92b72cbf90cbe3a556b"}, {file = "regex-2020.4.4-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:c9423a150d3a4fc0f3f2aae897a59919acd293f4cb397429b120a5fcd96ea3db"}, {file = "regex-2020.4.4-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:c087bff162158536387c53647411db09b6ee3f9603c334c90943e97b1052a156"}, {file = "regex-2020.4.4-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:1cbe0fa0b7f673400eb29e9ef41d4f53638f65f9a2143854de6b1ce2899185c3"}, {file = "regex-2020.4.4-cp36-cp36m-win32.whl", hash = "sha256:0ce9537396d8f556bcfc317c65b6a0705320701e5ce511f05fc04421ba05b8a8"}, {file = "regex-2020.4.4-cp36-cp36m-win_amd64.whl", hash = "sha256:7e1037073b1b7053ee74c3c6c0ada80f3501ec29d5f46e42669378eae6d4405a"}, {file = "regex-2020.4.4-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:4385f12aa289d79419fede43f979e372f527892ac44a541b5446617e4406c468"}, {file = "regex-2020.4.4-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:a58dd45cb865be0ce1d5ecc4cfc85cd8c6867bea66733623e54bd95131f473b6"}, {file = "regex-2020.4.4-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:ccccdd84912875e34c5ad2d06e1989d890d43af6c2242c6fcfa51556997af6cd"}, {file = "regex-2020.4.4-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:ea4adf02d23b437684cd388d557bf76e3afa72f7fed5bbc013482cc00c816948"}, {file = "regex-2020.4.4-cp37-cp37m-win32.whl", hash = "sha256:2294f8b70e058a2553cd009df003a20802ef75b3c629506be20687df0908177e"}, {file = "regex-2020.4.4-cp37-cp37m-win_amd64.whl", hash = "sha256:e91ba11da11cf770f389e47c3f5c30473e6d85e06d7fd9dcba0017d2867aab4a"}, {file = "regex-2020.4.4-cp38-cp38-manylinux1_i686.whl", hash = "sha256:5635cd1ed0a12b4c42cce18a8d2fb53ff13ff537f09de5fd791e97de27b6400e"}, {file = "regex-2020.4.4-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:23069d9c07e115537f37270d1d5faea3e0bdded8279081c4d4d607a2ad393683"}, {file = "regex-2020.4.4-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:c162a21e0da33eb3d31a3ac17a51db5e634fc347f650d271f0305d96601dc15b"}, {file = "regex-2020.4.4-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:fb95debbd1a824b2c4376932f2216cc186912e389bdb0e27147778cf6acb3f89"}, {file = "regex-2020.4.4-cp38-cp38-win32.whl", hash = "sha256:2a3bf8b48f8e37c3a40bb3f854bf0121c194e69a650b209628d951190b862de3"}, {file = "regex-2020.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:5bfed051dbff32fd8945eccca70f5e22b55e4148d2a8a45141a3b053d6455ae3"}, {file = "regex-2020.4.4.tar.gz", hash = "sha256:295badf61a51add2d428a46b8580309c520d8b26e769868b922750cf3ce67142"}, ] scandir = [ {file = "scandir-1.10.0-cp27-cp27m-win32.whl", hash = "sha256:92c85ac42f41ffdc35b6da57ed991575bdbe69db895507af88b9f499b701c188"}, {file = "scandir-1.10.0-cp27-cp27m-win_amd64.whl", hash = "sha256:cb925555f43060a1745d0a321cca94bcea927c50114b623d73179189a4e100ac"}, {file = "scandir-1.10.0-cp34-cp34m-win32.whl", hash = "sha256:2c712840c2e2ee8dfaf36034080108d30060d759c7b73a01a52251cc8989f11f"}, {file = "scandir-1.10.0-cp34-cp34m-win_amd64.whl", hash = "sha256:2586c94e907d99617887daed6c1d102b5ca28f1085f90446554abf1faf73123e"}, {file = "scandir-1.10.0-cp35-cp35m-win32.whl", hash = "sha256:2b8e3888b11abb2217a32af0766bc06b65cc4a928d8727828ee68af5a967fa6f"}, {file = "scandir-1.10.0-cp35-cp35m-win_amd64.whl", hash = "sha256:8c5922863e44ffc00c5c693190648daa6d15e7c1207ed02d6f46a8dcc2869d32"}, {file = "scandir-1.10.0-cp36-cp36m-win32.whl", hash = "sha256:2ae41f43797ca0c11591c0c35f2f5875fa99f8797cb1a1fd440497ec0ae4b022"}, {file = "scandir-1.10.0-cp36-cp36m-win_amd64.whl", hash = "sha256:7d2d7a06a252764061a020407b997dd036f7bd6a175a5ba2b345f0a357f0b3f4"}, {file = "scandir-1.10.0-cp37-cp37m-win32.whl", hash = "sha256:67f15b6f83e6507fdc6fca22fedf6ef8b334b399ca27c6b568cbfaa82a364173"}, {file = "scandir-1.10.0-cp37-cp37m-win_amd64.whl", hash = "sha256:b24086f2375c4a094a6b51e78b4cf7ca16c721dcee2eddd7aa6494b42d6d519d"}, {file = "scandir-1.10.0.tar.gz", hash = "sha256:4d4631f6062e658e9007ab3149a9b914f3548cb38bfb021c64f39a025ce578ae"}, ] singledispatch = [ {file = "singledispatch-3.4.0.3-py2.py3-none-any.whl", hash = "sha256:833b46966687b3de7f438c761ac475213e53b306740f1abfaa86e1d1aae56aa8"}, {file = "singledispatch-3.4.0.3.tar.gz", hash = "sha256:5b06af87df13818d14f08a028e42f566640aef80805c3b50c5056b086e3c2b9c"}, ] six = [ {file = "six-1.14.0-py2.py3-none-any.whl", hash = "sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"}, {file = "six-1.14.0.tar.gz", hash = "sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a"}, ] toml = [ {file = "toml-0.10.0-py2.7.egg", hash = "sha256:f1db651f9657708513243e61e6cc67d101a39bad662eaa9b5546f789338e07a3"}, {file = "toml-0.10.0-py2.py3-none-any.whl", hash = "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e"}, {file = "toml-0.10.0.tar.gz", hash = "sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c"}, ] tox = [ {file = "tox-3.14.0-py2.py3-none-any.whl", hash = "sha256:0bc216b6a2e6afe764476b4a07edf2c1dab99ed82bb146a1130b2e828f5bff5e"}, {file = "tox-3.14.0.tar.gz", hash = "sha256:c4f6b319c20ba4913dbfe71ebfd14ff95d1853c4231493608182f66e566ecfe1"}, {file = "tox-3.14.6-py2.py3-none-any.whl", hash = "sha256:b2c4b91c975ea5c11463d9ca00bebf82654439c5df0f614807b9bdec62cc9471"}, {file = "tox-3.14.6.tar.gz", hash = "sha256:a4a6689045d93c208d77230853b28058b7513f5123647b67bf012f82fa168303"}, ] typed-ast = [ {file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:73d785a950fc82dd2a25897d525d003f6378d1cb23ab305578394694202a58c3"}, {file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:aaee9905aee35ba5905cfb3c62f3e83b3bec7b39413f0a7f19be4e547ea01ebb"}, {file = "typed_ast-1.4.1-cp35-cp35m-win32.whl", hash = "sha256:0c2c07682d61a629b68433afb159376e24e5b2fd4641d35424e462169c0a7919"}, {file = "typed_ast-1.4.1-cp35-cp35m-win_amd64.whl", hash = "sha256:4083861b0aa07990b619bd7ddc365eb7fa4b817e99cf5f8d9cf21a42780f6e01"}, {file = "typed_ast-1.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:269151951236b0f9a6f04015a9004084a5ab0d5f19b57de779f908621e7d8b75"}, {file = "typed_ast-1.4.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:24995c843eb0ad11a4527b026b4dde3da70e1f2d8806c99b7b4a7cf491612652"}, {file = "typed_ast-1.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:fe460b922ec15dd205595c9b5b99e2f056fd98ae8f9f56b888e7a17dc2b757e7"}, {file = "typed_ast-1.4.1-cp36-cp36m-win32.whl", hash = "sha256:4e3e5da80ccbebfff202a67bf900d081906c358ccc3d5e3c8aea42fdfdfd51c1"}, {file = "typed_ast-1.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:249862707802d40f7f29f6e1aad8d84b5aa9e44552d2cc17384b209f091276aa"}, {file = "typed_ast-1.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8ce678dbaf790dbdb3eba24056d5364fb45944f33553dd5869b7580cdbb83614"}, {file = "typed_ast-1.4.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:c9e348e02e4d2b4a8b2eedb48210430658df6951fa484e59de33ff773fbd4b41"}, {file = "typed_ast-1.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:bcd3b13b56ea479b3650b82cabd6b5343a625b0ced5429e4ccad28a8973f301b"}, {file = "typed_ast-1.4.1-cp37-cp37m-win32.whl", hash = "sha256:d5d33e9e7af3b34a40dc05f498939f0ebf187f07c385fd58d591c533ad8562fe"}, {file = "typed_ast-1.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:0666aa36131496aed8f7be0410ff974562ab7eeac11ef351def9ea6fa28f6355"}, {file = "typed_ast-1.4.1-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:d205b1b46085271b4e15f670058ce182bd1199e56b317bf2ec004b6a44f911f6"}, {file = "typed_ast-1.4.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:6daac9731f172c2a22ade6ed0c00197ee7cc1221aa84cfdf9c31defeb059a907"}, {file = "typed_ast-1.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:498b0f36cc7054c1fead3d7fc59d2150f4d5c6c56ba7fb150c013fbc683a8d2d"}, {file = "typed_ast-1.4.1-cp38-cp38-win32.whl", hash = "sha256:715ff2f2df46121071622063fc7543d9b1fd19ebfc4f5c8895af64a77a8c852c"}, {file = "typed_ast-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc0fea399acb12edbf8a628ba8d2312f583bdbdb3335635db062fa98cf71fca4"}, {file = "typed_ast-1.4.1-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:d43943ef777f9a1c42bf4e552ba23ac77a6351de620aa9acf64ad54933ad4d34"}, {file = "typed_ast-1.4.1.tar.gz", hash = "sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b"}, ] typing = [ {file = "typing-3.7.4.1-py2-none-any.whl", hash = "sha256:c8cabb5ab8945cd2f54917be357d134db9cc1eb039e59d1606dc1e60cb1d9d36"}, {file = "typing-3.7.4.1-py3-none-any.whl", hash = "sha256:f38d83c5a7a7086543a0f649564d661859c5146a85775ab90c0d2f93ffaa9714"}, {file = "typing-3.7.4.1.tar.gz", hash = "sha256:91dfe6f3f706ee8cc32d38edbbf304e9b7583fb37108fef38229617f8b3eba23"}, ] typing-extensions = [ {file = "typing_extensions-3.7.4.2-py2-none-any.whl", hash = "sha256:f8d2bd89d25bc39dabe7d23df520442fa1d8969b82544370e03d88b5a591c392"}, {file = "typing_extensions-3.7.4.2-py3-none-any.whl", hash = "sha256:6e95524d8a547a91e08f404ae485bbb71962de46967e1b71a0cb89af24e761c5"}, {file = "typing_extensions-3.7.4.2.tar.gz", hash = "sha256:79ee589a3caca649a9bfd2a8de4709837400dfa00b6cc81962a1e6a1815969ae"}, ] virtualenv = [ {file = "virtualenv-20.0.18-py2.py3-none-any.whl", hash = "sha256:5021396e8f03d0d002a770da90e31e61159684db2859d0ba4850fbea752aa675"}, {file = "virtualenv-20.0.18.tar.gz", hash = "sha256:ac53ade75ca189bc97b6c1d9ec0f1a50efe33cbf178ae09452dcd9fd309013c1"}, ] wcwidth = [ {file = "wcwidth-0.1.9-py2.py3-none-any.whl", hash = "sha256:cafe2186b3c009a04067022ce1dcd79cb38d8d65ee4f4791b8888d6599d1bbe1"}, {file = "wcwidth-0.1.9.tar.gz", hash = "sha256:ee73862862a156bf77ff92b09034fc4825dd3af9cf81bc5b360668d425f3c5f1"}, ] zipp = [ {file = "zipp-1.2.0-py2.py3-none-any.whl", hash = "sha256:e0d9e63797e483a30d27e09fffd308c59a700d365ec34e93cc100844168bf921"}, {file = "zipp-1.2.0.tar.gz", hash = "sha256:c70410551488251b0fee67b460fb9a536af8d6f9f008ad10ac51f615b6a521b1"}, ] cleo-0.8.1/pyproject.toml000066400000000000000000000013211364636233300153640ustar00rootroot00000000000000[tool.poetry] name = "cleo" version = "0.8.1" description = "Cleo allows you to create beautiful and testable command-line interfaces." authors = [ "Sébastien Eustace " ] license = "MIT" readme = "README.rst" homepage = "https://github.com/sdispater/cleo" keywords = ["cli", "commands"] # Requirements [tool.poetry.dependencies] python = "~2.7 || ^3.4" clikit = "^0.6.0" [tool.poetry.dev-dependencies] pytest = "^4.4" pytest-mock = "^1.7" pre-commit = "^1.12" pytest-cov = "^2.6" tox = "^3.5" black = {version = "^19.3b0", markers= "python_version >= '3.6' and implementation_name != 'pypy'"} [build-system] requires = ["poetry-core>=1.0.0a5"] build-backend = "poetry.core.masonry.api" cleo-0.8.1/tests/000077500000000000000000000000001364636233300136155ustar00rootroot00000000000000cleo-0.8.1/tests/__init__.py000066400000000000000000000000001364636233300157140ustar00rootroot00000000000000cleo-0.8.1/tests/commands/000077500000000000000000000000001364636233300154165ustar00rootroot00000000000000cleo-0.8.1/tests/commands/__init__.py000066400000000000000000000000301364636233300175200ustar00rootroot00000000000000# -*- coding: utf-8 -*- cleo-0.8.1/tests/commands/completion/000077500000000000000000000000001364636233300175675ustar00rootroot00000000000000cleo-0.8.1/tests/commands/completion/__init__.py000066400000000000000000000000301364636233300216710ustar00rootroot00000000000000# -*- coding: utf-8 -*- cleo-0.8.1/tests/commands/completion/fixtures/000077500000000000000000000000001364636233300214405ustar00rootroot00000000000000cleo-0.8.1/tests/commands/completion/fixtures/__init__.py000066400000000000000000000000301364636233300235420ustar00rootroot00000000000000# -*- coding: utf-8 -*- cleo-0.8.1/tests/commands/completion/fixtures/bash.txt000066400000000000000000000025621364636233300231230ustar00rootroot00000000000000_my_function() { local cur script coms opts com COMPREPLY=() _get_comp_words_by_ref -n : cur words # for an alias, get the real script behind it if [[ $(type -t ${words[0]}) == "alias" ]]; then script=$(alias ${words[0]} | sed -E "s/alias ${words[0]}='(.*)'/\1/") else script=${words[0]} fi # lookup for command for word in ${words[@]:1}; do if [[ $word != -* ]]; then com=$word break fi done # completing for an option if [[ ${cur} == --* ]] ; then opts="--ansi --help --no-ansi --no-interaction --quiet --verbose --version" case "$com" in (command:with:colons) opts="${opts} --goodbye" ;; (hello) opts="${opts} --dangerous-option --option-without-description" ;; (help) opts="${opts} " ;; esac COMPREPLY=($(compgen -W "${opts}" -- ${cur})) __ltrim_colon_completions "$cur" return 0; fi # completing for a command if [[ $cur == $com ]]; then coms="command:with:colons hello help" COMPREPLY=($(compgen -W "${coms}" -- ${cur})) __ltrim_colon_completions "$cur" return 0 fi } complete -o default -F _my_function script complete -o default -F _my_function /path/to/my/script cleo-0.8.1/tests/commands/completion/fixtures/command_with_colons.py000066400000000000000000000002431364636233300260370ustar00rootroot00000000000000# -*- coding: utf-8 -*- from cleo import Command class CommandWithColons(Command): """ Test. command:with:colons { --goodbye } """ cleo-0.8.1/tests/commands/completion/fixtures/fish.txt000066400000000000000000000031641364636233300231360ustar00rootroot00000000000000function __fish_my_function_no_subcommand for i in (commandline -opc) if contains -- $i command:with:colons hello help return 1 end end return 0 end # global options complete -c script -n '__fish_my_function_no_subcommand' -l ansi -d 'Force ANSI output' complete -c script -n '__fish_my_function_no_subcommand' -l help -d 'Display this help message' complete -c script -n '__fish_my_function_no_subcommand' -l no-ansi -d 'Disable ANSI output' complete -c script -n '__fish_my_function_no_subcommand' -l no-interaction -d 'Do not ask any interactive question' complete -c script -n '__fish_my_function_no_subcommand' -l quiet -d 'Do not output any message' complete -c script -n '__fish_my_function_no_subcommand' -l verbose -d 'Increase the verbosity of messages: "-v" for normal output, "-vv" for more verbose output and "-vvv" for debug' complete -c script -n '__fish_my_function_no_subcommand' -l version -d 'Display this application version' # commands complete -c script -f -n '__fish_my_function_no_subcommand' -a command:with:colons -d 'Test.' complete -c script -f -n '__fish_my_function_no_subcommand' -a hello -d 'Complete me please.' complete -c script -f -n '__fish_my_function_no_subcommand' -a help -d 'Display the manual of a command' # command options # command:with:colons complete -c script -A -n '__fish_seen_subcommand_from command:with:colons' -l goodbye -d '' # hello complete -c script -A -n '__fish_seen_subcommand_from hello' -l dangerous-option -d 'This $hould be `escaped`.' complete -c script -A -n '__fish_seen_subcommand_from hello' -l option-without-description -d '' # help cleo-0.8.1/tests/commands/completion/fixtures/hello_command.py000066400000000000000000000003511364636233300246120ustar00rootroot00000000000000# -*- coding: utf-8 -*- from cleo import Command class HelloCommand(Command): """ Complete me please. hello { --dangerous-option= : This $hould be `escaped`. } { --option-without-description } """ cleo-0.8.1/tests/commands/completion/fixtures/zsh.txt000066400000000000000000000027321364636233300230110ustar00rootroot00000000000000#compdef script _my_function() { local state com cur cur=${words[${#words[@]}]} # lookup for command for word in ${words[@]:1}; do if [[ $word != -* ]]; then com=$word break fi done if [[ ${cur} == --* ]]; then state="option" opts=("--ansi:Force ANSI output" "--help:Display this help message" "--no-ansi:Disable ANSI output" "--no-interaction:Do not ask any interactive question" "--quiet:Do not output any message" "--verbose:Increase the verbosity of messages: \\\"-v\\\" for normal output, \\\"-vv\\\" for more verbose output and \\\"-vvv\\\" for debug" "--version:Display this application version") elif [[ $cur == $com ]]; then state="command" coms=("command\:with\:colons:Test." "hello:Complete me please." "help:Display the manual of a command") fi case $state in (command) _describe 'command' coms ;; (option) case "$com" in (command:with:colons) opts+=("--goodbye") ;; (hello) opts+=("--dangerous-option:This \$hould be \`escaped\`." "--option-without-description") ;; (help) opts+=() ;; esac _describe 'option' opts ;; *) # fallback to file completion _arguments '*:file:_files' esac } _my_function "$@" compdef _my_function /path/to/my/script cleo-0.8.1/tests/commands/completion/test_completions_command.py000066400000000000000000000044221364636233300252340ustar00rootroot00000000000000# -*- coding: utf-8 -*- import os import pytest from cleo import Application from cleo import CommandTester from .fixtures.hello_command import HelloCommand from .fixtures.command_with_colons import CommandWithColons app = Application() app.add(HelloCommand()) app.add(CommandWithColons()) def test_invalid_shell(): command = app.find("completions") tester = CommandTester(command) with pytest.raises(ValueError): tester.execute("pomodoro") def test_bash(mocker): mocker.patch( "clikit.api.args.args.Args.script_name", new_callable=mocker.PropertyMock, return_value="/path/to/my/script", ) mocker.patch( "cleo.commands.completions_command.CompletionsCommand._generate_function_name", return_value="_my_function", ) command = app.find("completions") tester = CommandTester(command) tester.execute("bash") with open(os.path.join(os.path.dirname(__file__), "fixtures", "bash.txt")) as f: expected = f.read() assert expected == tester.io.fetch_output().replace("\r\n", "\n") def test_zsh(mocker): mocker.patch( "clikit.api.args.args.Args.script_name", new_callable=mocker.PropertyMock, return_value="/path/to/my/script", ) mocker.patch( "cleo.commands.completions_command.CompletionsCommand._generate_function_name", return_value="_my_function", ) command = app.find("completions") tester = CommandTester(command) tester.execute("zsh") with open(os.path.join(os.path.dirname(__file__), "fixtures", "zsh.txt")) as f: expected = f.read() assert expected == tester.io.fetch_output().replace("\r\n", "\n") def test_fish(mocker): mocker.patch( "clikit.api.args.args.Args.script_name", new_callable=mocker.PropertyMock, return_value="/path/to/my/script", ) mocker.patch( "cleo.commands.completions_command.CompletionsCommand._generate_function_name", return_value="_my_function", ) command = app.find("completions") tester = CommandTester(command) tester.execute("fish") with open(os.path.join(os.path.dirname(__file__), "fixtures", "fish.txt")) as f: expected = f.read() assert expected == tester.io.fetch_output().replace("\r\n", "\n") cleo-0.8.1/tests/commands/test_command.py000066400000000000000000000036321364636233300204510ustar00rootroot00000000000000# -*- coding: utf-8 -*- from cleo.application import Application from cleo.commands.command import Command from cleo.helpers import argument from cleo.testers import CommandTester from ..fixtures.signature_command import SignatureCommand from ..fixtures.inherited_command import ChildCommand class MyCommand(Command): """ Command testing. test {action : The action to execute.} """ def handle(self): action = self.argument("action") getattr(self, "_" + action)() def _overwrite(self): self.write("Processing...") self.overwrite("Done!") class MySecondCommand(Command): name = "test2" description = "Command testing" arguments = [argument("foo", "Bar", multiple=True)] def handle(self): foos = self.argument("foo") self.line(",".join(foos)) def test_set_application(): application = Application() command = Command() command.set_application(application) assert application == command.application def test_with_signature(): command = SignatureCommand() config = command.config assert "signature:command" == config.name assert "description" == config.description assert "help" == config.help assert 2 == len(config.arguments) assert 2 == len(config.options) def test_signature_inheritance(): command = ChildCommand() config = command.config assert "parent" == config.name assert "Parent Command." == config.description def test_overwrite(): command = MyCommand() tester = CommandTester(command) tester.execute("overwrite", decorated=True) expected = "Processing...{}Done! {}".format("\x08" * 13, "\x08" * 8) assert expected == tester.io.fetch_output() def test_explicit_multiple_argument(): command = MySecondCommand() tester = CommandTester(command) tester.execute("1 2 3") assert "1,2,3\n" == tester.io.fetch_output() cleo-0.8.1/tests/fixtures/000077500000000000000000000000001364636233300154665ustar00rootroot00000000000000cleo-0.8.1/tests/fixtures/__init__.py000066400000000000000000000000001364636233300175650ustar00rootroot00000000000000cleo-0.8.1/tests/fixtures/inherited_command.py000066400000000000000000000002641364636233300215130ustar00rootroot00000000000000# -*- coding: utf-8 -*- from cleo import Command class ParentCommand(Command): """ Parent Command. parent """ class ChildCommand(ParentCommand): pass cleo-0.8.1/tests/fixtures/signature_command.py000066400000000000000000000005051364636233300215370ustar00rootroot00000000000000from cleo.commands.command import Command class SignatureCommand(Command): name = "no:configure" signature = ( "signature:command {foo : Foo} {bar? : Bar} {--z|baz : Baz} {--Z|bazz : Bazz}" ) description = "description" help = "help" def handle(self): self.line("handle called") cleo-0.8.1/tests/test_helpers.py000066400000000000000000000042751364636233300167000ustar00rootroot00000000000000from clikit.api.args.format import Argument from clikit.api.args.format import Option from cleo import argument from cleo import option def test_argument(): arg = argument("foo", "Foo") assert "Foo" == arg.description assert arg.is_required() assert not arg.is_optional() assert not arg.is_multi_valued() assert arg.default is None arg = argument("foo", "Foo", optional=True, default="bar") assert not arg.is_required() assert arg.is_optional() assert not arg.is_multi_valued() assert "bar" == arg.default arg = argument("foo", "Foo", multiple=True) assert arg.is_required() assert not arg.is_optional() assert arg.is_multi_valued() assert [] == arg.default arg = argument("foo", "Foo", optional=True, multiple=True, default=["bar"]) assert not arg.is_required() assert arg.is_optional() assert arg.is_multi_valued() assert ["bar"] == arg.default def test_option(): opt = option("foo", "f", "Foo") assert "Foo" == opt.description assert not opt.accepts_value() assert not opt.is_value_optional() assert not opt.is_value_required() assert not opt.is_multi_valued() assert opt.default is None opt = option("foo", "f", "Foo", flag=False) assert "Foo" == opt.description assert opt.accepts_value() assert not opt.is_value_optional() assert opt.is_value_required() assert not opt.is_multi_valued() opt = option("foo", "f", "Foo", flag=False, value_required=False) assert "Foo" == opt.description assert opt.accepts_value() assert opt.is_value_optional() assert not opt.is_value_required() assert not opt.is_multi_valued() opt = option("foo", "f", "Foo", flag=False, multiple=True) assert "Foo" == opt.description assert opt.accepts_value() assert not opt.is_value_optional() assert opt.is_value_required() assert opt.is_multi_valued() assert [] == opt.default opt = option("foo", "f", "Foo", flag=False, default="bar") assert "Foo" == opt.description assert opt.accepts_value() assert not opt.is_value_optional() assert opt.is_value_required() assert not opt.is_multi_valued() assert "bar" == opt.default cleo-0.8.1/tests/test_parser.py000066400000000000000000000077621364636233300165360ustar00rootroot00000000000000from clikit.api.args.format import Argument from clikit.api.args.format import Option from cleo.parser import Parser def test_basic_parameter_parsing(): results = Parser.parse("command:name") assert "command:name" == results["name"] results = Parser.parse("command:name {argument} {--option}") assert "command:name" == results["name"] assert "argument" == results["arguments"][0].name assert "option" == results["options"][0].long_name assert Option.NO_VALUE == results["options"][0].flags results = Parser.parse("command:name {argument*} {--option=}") assert "command:name" == results["name"] assert "argument" == results["arguments"][0].name assert Argument.MULTI_VALUED | Argument.REQUIRED == results["arguments"][0].flags assert "option" == results["options"][0].long_name assert Option.REQUIRED_VALUE == results["options"][0].flags results = Parser.parse("command:name {argument?*} {--option=*}") assert "command:name" == results["name"] assert "argument" == results["arguments"][0].name assert Argument.MULTI_VALUED | Argument.OPTIONAL == results["arguments"][0].flags assert "option" == results["options"][0].long_name assert Option.MULTI_VALUED == results["options"][0].flags results = Parser.parse( "command:name {argument?* : The argument description.} {--option=* : The option description.}" ) assert "command:name" == results["name"] assert "argument" == results["arguments"][0].name assert "The argument description." == results["arguments"][0].description assert Argument.MULTI_VALUED | Argument.OPTIONAL == results["arguments"][0].flags assert "option" == results["options"][0].long_name assert "The option description." == results["options"][0].description assert Option.MULTI_VALUED == results["options"][0].flags results = Parser.parse( "command:name " "{argument?* : The argument description.} " "{--option=* : The option description.}" ) assert "command:name" == results["name"] assert "argument" == results["arguments"][0].name assert "The argument description." == results["arguments"][0].description assert Argument.MULTI_VALUED | Argument.OPTIONAL == results["arguments"][0].flags assert "option" == results["options"][0].long_name assert "The option description." == results["options"][0].description assert Option.MULTI_VALUED == results["options"][0].flags def test_shortcut_name_parsing(): results = Parser.parse("command:name {--o|option}") assert "command:name" == results["name"] assert "option" == results["options"][0].long_name assert "o" == results["options"][0].short_name assert Option.NO_VALUE == results["options"][0].flags results = Parser.parse("command:name {--o|option=}") assert "command:name" == results["name"] assert "option" == results["options"][0].long_name assert "o" == results["options"][0].short_name assert Option.REQUIRED_VALUE == results["options"][0].flags results = Parser.parse("command:name {--o|option=*}") assert "command:name" == results["name"] assert "option" == results["options"][0].long_name assert "o" == results["options"][0].short_name assert Option.MULTI_VALUED == results["options"][0].flags results = Parser.parse("command:name {--o|option=* : The option description.}") assert "command:name" == results["name"] assert "option" == results["options"][0].long_name assert "o" == results["options"][0].short_name assert "The option description." == results["options"][0].description assert Option.MULTI_VALUED == results["options"][0].flags results = Parser.parse("command:name " "{--o|option=* : The option description.}") assert "command:name" == results["name"] assert "option" == results["options"][0].long_name assert "o" == results["options"][0].short_name assert "The option description." == results["options"][0].description assert Option.MULTI_VALUED == results["options"][0].flags cleo-0.8.1/tests/testers/000077500000000000000000000000001364636233300153065ustar00rootroot00000000000000cleo-0.8.1/tests/testers/__init__.py000066400000000000000000000000301364636233300174100ustar00rootroot00000000000000# -*- coding: utf-8 -*- cleo-0.8.1/tests/testers/test_application_tester.py000066400000000000000000000012621364636233300226110ustar00rootroot00000000000000import os import pytest from cleo.commands import Command from cleo.application import Application from cleo.testers.application_tester import ApplicationTester class FooCommand(Command): """ Foo command foo {foo : Foo argument} """ def handle(self): self.line(self.argument("foo")) @pytest.fixture() def app(): app = Application() app.config.set_terminate_after_run(False) app.add(FooCommand()) return app @pytest.fixture() def tester(app): return ApplicationTester(app) def test_execute(tester): assert 0 == tester.execute("foo bar") assert 0 == tester.status_code assert "bar\n" == tester.io.fetch_output() cleo-0.8.1/tests/testers/test_command_tester.py000066400000000000000000000007711364636233300217300ustar00rootroot00000000000000import os import pytest from cleo.commands.command import Command from cleo.testers.command_tester import CommandTester class FooCommand(Command): """ Foo command foo {foo : Foo argument} """ def handle(self): self.line(self.argument("foo")) @pytest.fixture() def tester(): return CommandTester(FooCommand()) def test_execute(tester): assert 0 == tester.execute("bar") assert 0 == tester.status_code assert "bar\n" == tester.io.fetch_output() cleo-0.8.1/tox.ini000066400000000000000000000002631364636233300137670ustar00rootroot00000000000000[tox] isolated_build = true envlist = py27, py35, py36, py37, py38 [testenv] whitelist_externals = poetry commands = poetry install --no-root -v poetry run pytest tests/