pax_global_header00006660000000000000000000000064144766076430014533gustar00rootroot0000000000000052 comment=4ebe8d8bbec02a4be6227829b1762fa656370bd4 questionary-2.0.1/000077500000000000000000000000001447660764300141165ustar00rootroot00000000000000questionary-2.0.1/.devcontainer/000077500000000000000000000000001447660764300166555ustar00rootroot00000000000000questionary-2.0.1/.devcontainer/Dockerfile000066400000000000000000000027201447660764300206500ustar00rootroot00000000000000# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.208.0/containers/python-3/.devcontainer/base.Dockerfile # [Choice] Python version (use -bullseye variants on local arm64/Apple Silicon): 3, 3.10, 3.9, 3.8, 3.7, 3.6, 3-bullseye, 3.10-bullseye, 3.9-bullseye, 3.8-bullseye, 3.7-bullseye, 3.6-bullseye, 3-buster, 3.10-buster, 3.9-buster, 3.8-buster, 3.7-buster, 3.6-buster ARG VARIANT="3.9-bullseye" FROM mcr.microsoft.com/vscode/devcontainers/python:0-${VARIANT} ENV PYTHONFAULTHANDLER=1 \ PYTHONUNBUFFERED=1 \ PYTHONHASHSEED=random \ PIP_NO_CACHE_DIR=off \ PIP_DISABLE_PIP_VERSION_CHECK=on \ PIP_DEFAULT_TIMEOUT=100 # [Optional] If your pip requirements rarely change, uncomment this section to add them to the image. # COPY requirements.txt /tmp/pip-tmp/ # RUN pip3 --disable-pip-version-check --no-cache-dir install -r /tmp/pip-tmp/requirements.txt \ # && rm -rf /tmp/pip-tmp RUN pip install 'poetry==1.1.8' COPY poetry.lock pyproject.toml ./ RUN poetry config virtualenvs.create false \ && poetry install --no-interaction --no-ansi --extras "docs" # [Optional] Uncomment this section to install additional OS packages. # RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ # && apt-get -y install --no-install-recommends # [Optional] Uncomment this line to install global node packages. # RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g " 2>&1 questionary-2.0.1/.devcontainer/devcontainer.json000066400000000000000000000037471447660764300222440ustar00rootroot00000000000000// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: // https://github.com/microsoft/vscode-dev-containers/tree/v0.208.0/containers/python-3 { "name": "Python 3", "build": { "dockerfile": "Dockerfile", "context": "..", "args": { // Update 'VARIANT' to pick a Python version: 3, 3.10, 3.9, 3.8, 3.7, 3.6 // Append -bullseye or -buster to pin to an OS version. // Use -bullseye variants on local on arm64/Apple Silicon. "VARIANT": "3.8" } }, // Set *default* container specific settings.json values on container create. "settings": { "python.defaultInterpreterPath": "/usr/local/bin/python", "python.linting.enabled": true, "python.linting.pylintEnabled": true, "python.formatting.autopep8Path": "/usr/local/py-utils/bin/autopep8", "python.formatting.blackPath": "/usr/local/py-utils/bin/black", "python.formatting.yapfPath": "/usr/local/py-utils/bin/yapf", "python.linting.banditPath": "/usr/local/py-utils/bin/bandit", "python.linting.flake8Path": "/usr/local/py-utils/bin/flake8", "python.linting.mypyPath": "/usr/local/py-utils/bin/mypy", "python.linting.pydocstylePath": "/usr/local/py-utils/bin/pydocstyle", "python.linting.pylintPath": "/usr/local/py-utils/bin/pylint" }, // Add the IDs of extensions you want installed when the container is created. "extensions": [ "ms-python.python", "ms-python.vscode-pylance" ], // Use 'forwardPorts' to make a list of ports inside the container available locally. // "forwardPorts": [], // Use 'postCreateCommand' to run commands after the container is created. // "postCreateCommand": "pip3 install --user -r requirements.txt", // Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. "remoteUser": "vscode", "features": { "docker-in-docker": "20.10", "docker-from-docker": "20.10", "kubectl-helm-minikube": "1.22", "terraform": "1.0", "git": "os-provided", "github-cli": "latest", "sshd": "latest" } } questionary-2.0.1/.github/000077500000000000000000000000001447660764300154565ustar00rootroot00000000000000questionary-2.0.1/.github/ISSUE_TEMPLATE/000077500000000000000000000000001447660764300176415ustar00rootroot00000000000000questionary-2.0.1/.github/ISSUE_TEMPLATE/bug-report.yml000066400000000000000000000045311447660764300224550ustar00rootroot00000000000000name: Bug report description: File a bug report to help us improve labels: [Bug] body: - type: textarea id: describe-bug attributes: label: Describe the bug description: > Please give us a clear and concise description of what the bug is. Please include a screenshot if you are able to. validations: required: true - type: textarea id: example attributes: label: Example description: > Please provide a small and concise example to reproduce the issue. This will be automatically formatted into code, so no need for backticks. render: python validations: required: true - type: textarea id: steps attributes: label: Steps to reproduce description: Please provide any further steps to reproduce the issue. placeholder: | 1. ... 2. ... 3. ... - type: textarea id: expected-outcome attributes: label: Expected behaviour description: Please tell us what the expected behaviour should be been. validations: required: true - type: checkboxes id: checked-latest attributes: label: Latest version description: > Please ensure that you have checked that this issue occurs on the latest version of questionary. You can upgrade by running `pip install -U questionary`. options: - label: > I have checked that this issue occurs on the latest version of questionary. required: true - type: input id: questionary-version attributes: label: Questionary version description: > You can run `pip show questionary` to see the version of questionary that is installed. validations: required: true - type: input id: prompt-toolkit-version attributes: label: Prompt Toolkit version description: > You can run `pip show prompt_toolkit` to see the version of Prompt Toolkit that is installed. validations: required: true - type: dropdown id: operating-system attributes: label: Operating System description: Which operating system are you using? options: - Windows - macOS - Linux - Other (please specify in description) validations: required: true questionary-2.0.1/.github/ISSUE_TEMPLATE/config.yml000066400000000000000000000002511447660764300216270ustar00rootroot00000000000000blank_issues_enabled: false contact_links: - name: Documentation url: https://questionary.readthedocs.io/ about: Please find answers to common questions here questionary-2.0.1/.github/ISSUE_TEMPLATE/feature-request.yml000066400000000000000000000016631447660764300235130ustar00rootroot00000000000000name: Feature request description: Suggest an idea for this project labels: [Enhancement] body: - type: textarea id: problem-description attributes: label: Describe the problem description: > Is your feature request related to a problem? Please provide a clear and concise description of what the problem is. placeholder: I'm always frustrated when... validations: required: true - type: textarea id: solution-description attributes: label: Describe the solution description: > Please provide a clear and concise description of the solution that you would like to see. validations: required: true - type: textarea id: alternatives attributes: label: Alternatives considered description: > Please provide a clear and concise description of any alternative solutions or features you have considered. questionary-2.0.1/.github/ISSUE_TEMPLATE/question.yml000066400000000000000000000022431447660764300222340ustar00rootroot00000000000000name: Need help description: Ask a question if you need help labels: [Question] body: - type: textarea id: question attributes: label: Question description: > Please describe the problem that you are having. validations: required: true - type: textarea id: previous-attempts attributes: label: What have you already tried? description: > Please describe any attempts that you have made to solve the problem. This might include code examples. This will help us get a better understanding of what you are trying to do. validations: required: true - type: checkboxes id: read-the-docs attributes: label: Read the documentation description: > Please check if your question is answered by the [documentation](https://questionary.readthedocs.io/). Don't worry if you ask a question that is already answered by the documentation - we'll just point you to the right place. options: - label: > I have checked to ensure that my question is not answered by the documentation. required: true questionary-2.0.1/.github/PULL_REQUEST_TEMPLATE.md000066400000000000000000000012661447660764300212640ustar00rootroot00000000000000**What is the problem that this PR addresses?** ... **How did you solve it?** ... **Checklist** - [ ] I have read the [Contributor's Guide](https://questionary.readthedocs.io/en/stable/pages/contributors.html#steps-for-submitting-code). - [ ] I will check that all automated PR checks pass before the PR gets reviewed. questionary-2.0.1/.github/dependabot.yml000066400000000000000000000003151447660764300203050ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: "daily" - package-ecosystem: "pip" directory: "/" schedule: interval: "daily" questionary-2.0.1/.github/workflows/000077500000000000000000000000001447660764300175135ustar00rootroot00000000000000questionary-2.0.1/.github/workflows/continuous-integration.yml000066400000000000000000000103561447660764300247720ustar00rootroot00000000000000name: Continuous Integration on: push: branches: - master tags: - "*" pull_request: # SECRETS # - PYPI_TOKEN: publishing token for tmbo account, needs to be maintainer of # tmbo/questionary on pypi jobs: quality: name: Code Quality runs-on: ubuntu-latest steps: - name: Checkout git repository 🕝 uses: actions/checkout@v4 - name: Set up Python 3.11 🐍 uses: actions/setup-python@v4 with: python-version: "3.11" - name: Install poetry 🦄 uses: Gr1N/setup-poetry@v8 - name: Load Poetry Cached Libraries ⬇ uses: actions/cache@v3 with: path: ~/.cache/pypoetry key: ${{ runner.os }}-poetry-${{ hashFiles('**/poetry.lock') }} restore-keys: ${{ runner.os }}-poetry- - name: Install dependencies 🖥 run: poetry install --no-interaction - name: Lint Code 🎎 run: make lint - name: Check Types 📚 run: make types - name: Check Version Numbers 🕸 run: poetry run python scripts/validate_version.py test: name: Run Tests runs-on: ${{ matrix.os }} timeout-minutes: 10 strategy: matrix: os: [ubuntu-latest, windows-latest, macos-latest] python-version: ["3.8", "3.9", "3.10", "3.11"] promttoolkit: [3.*, 2.*] include: - promttoolkit: 3.0.29 os: ubuntu-latest - promttoolkit: 3.0.19 os: ubuntu-latest steps: - name: Checkout git repository 🕝 uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} 🐍 uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Install poetry 🦄 uses: Gr1N/setup-poetry@v8 - name: Load Poetry Cached Libraries ⬇ uses: actions/cache@v3 with: path: ~/.cache/pypoetry key: ${{ runner.os }}-poetry-${{ hashFiles('**/poetry.lock') }} restore-keys: ${{ runner.os }}-poetry- - name: Install dependencies 🖥 run: | poetry install --no-interaction poetry run pip install prompt_toolkit==${{ matrix.promttoolkit }} - name: Test Code 🔍 run: make test - name: Send Coverage Report 📊 env: COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} COVERALLS_SERVICE_NAME: github COVERALLS_PARALLEL: true run: poetry run coveralls post-test: name: Signal test completion needs: test runs-on: ubuntu-latest container: python:3-slim steps: - name: Finished run: | pip3 install --upgrade coveralls coveralls --service=github --finish env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} docs: name: Test Docs runs-on: ubuntu-latest steps: - name: Checkout git repository 🕝 uses: actions/checkout@v4 - name: Set up Python 3.11 🐍 uses: actions/setup-python@v4 with: python-version: "3.11" - name: Install poetry 🦄 uses: Gr1N/setup-poetry@v8 - name: Load Poetry Cached Libraries ⬇ uses: actions/cache@v3 with: path: ~/.cache/pypoetry key: ${{ runner.os }}-poetry-${{ hashFiles('**/poetry.lock') }} restore-keys: ${{ runner.os }}-poetry - name: Install dependencies 🖥 run: poetry install --no-interaction --with=docs - name: Build docs ⚒️ run: make docs deploy: name: Deploy to PyPI runs-on: ubuntu-latest # deploy will only be run when there is a tag available if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') needs: [quality, test, docs] # only run after all other stages succeeded steps: - name: Checkout git repository 🕝 uses: actions/checkout@v4 - name: Set up Python 3.11 🐍 uses: actions/setup-python@v4 with: python-version: "3.11" - name: Install poetry 🦄 uses: Gr1N/setup-poetry@v8 - name: Build ⚒️ Distributions run: | poetry build poetry publish -u __token__ -p ${{ secrets.PYPI_TOKEN }} questionary-2.0.1/.gitignore000066400000000000000000000004751447660764300161140ustar00rootroot00000000000000*# *.DS_Store *.egg *.eggs *.egg-info *.egg-info/ *.iml *.log *.pyc *.sass-cache *.sqlite *build/ *dat *npy *pyc *~ .env .cache/ .pytest_cache/ .coverage .idea/ .vscode/ .ipynb_checkpoints .ruby-version .tox bower_components/ build/ build/lib/ dist/ docs/_build jnk/ logs/ profile.* server/ tmp/ .python-version .venvquestionary-2.0.1/.pre-commit-config.yaml000066400000000000000000000014351447660764300204020ustar00rootroot00000000000000repos: - repo: https://github.com/humitos/mirrors-autoflake rev: v1.1 hooks: - id: autoflake args: ["-i", "--remove-all-unused-imports"] - repo: https://github.com/psf/black rev: 23.3.0 hooks: - id: black args: - "--target-version=py38" - "--target-version=py39" - "--target-version=py310" - "--target-version=py311" - "--line-length=88" - repo: https://github.com/pycqa/isort rev: 5.12.0 hooks: - id: isort name: isort (python) args: - "--force-single-line-imports" - "--profile=black" - repo: https://github.com/pycqa/flake8 rev: 6.0.0 hooks: - id: flake8 args: ["--max-line-length", "120", "--max-doc-length", "140"] questionary-2.0.1/.readthedocs.yml000066400000000000000000000004531447660764300172060ustar00rootroot00000000000000version: 2 formats: - pdf sphinx: configuration: docs/conf.py fail_on_warning: true build: os: "ubuntu-22.04" tools: python: "3.11" jobs: post_create_environment: - pip install poetry - poetry config virtualenvs.create false post_install: - make install questionary-2.0.1/LICENSE000066400000000000000000000020561447660764300151260ustar00rootroot00000000000000Copyright 2020 Tom Bocklisch and contributors 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. questionary-2.0.1/MANIFEST.in000066400000000000000000000003351447660764300156550ustar00rootroot00000000000000include LICENSE NOTICE README.md requirements.txt include pyproject.toml include questionary/py.typed recursive-include docs *.gif recursive-include docs *.png recursive-include examples *.py recursive-include tests *.py questionary-2.0.1/Makefile000066400000000000000000000023721447660764300155620ustar00rootroot00000000000000.PHONY: clean install develop lint test types docs livedocs JOBS ?= 1 help: @echo "make" @echo " clean" @echo " Remove Python/build artifacts." @echo " develop" @echo " Configure development environment for questionary." @echo " install" @echo " Install questionary." @echo " lint" @echo " Check the code style and apply black formatting." @echo " test" @echo " Run the unit tests." @echo " types" @echo " Check for type errors using pytype." @echo " docs" @echo " Build the documentation." @echo " livedocs" @echo " Build the documentation with a live preview for quick iteration." clean: find . -name '*.pyc' -exec rm -f {} + find . -name '*.pyo' -exec rm -f {} + find . -name '*~' -exec rm -f {} + rm -rf build/ rm -rf questionary.egg-info/ rm -rf .mypy_cache/ rm -rf .pytest_cache/ rm -rf dist/ poetry run make -C docs clean install: poetry install --with="docs" develop: install poetry run pre-commit install lint: poetry run pre-commit run -a test: poetry run pytest --cov questionary -v types: poetry run mypy --version poetry run mypy questionary docs: poetry run make -C docs html livedocs: poetry run sphinx-autobuild docs docs/build/html questionary-2.0.1/NOTICE000066400000000000000000000045631447660764300150320ustar00rootroot00000000000000Tom Bocklisch Copyright 2019 Tom Bocklisch ---- This product includes software from PyInquirer (https://github.com/CITGuru/PyInquirer), under the MIT License. Copyright 2018 Oyetoke Toby and contributors 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. ---- This product includes software from whaaaaat (https://github.com/finklabs/whaaaaat), under the MIT License. Copyright 2016 Fink Labs GmbH and inquirerpy contributors 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 questionary-2.0.1/README.md000066400000000000000000000102611447660764300153750ustar00rootroot00000000000000# Questionary [![Version](https://img.shields.io/pypi/v/questionary.svg)](https://pypi.org/project/questionary/) [![License](https://img.shields.io/pypi/l/questionary.svg)](#) [![Continuous Integration](https://github.com/tmbo/questionary/workflows/Continuous%20Integration/badge.svg)](#) [![Coverage](https://coveralls.io/repos/github/tmbo/questionary/badge.svg?branch=master)](https://coveralls.io/github/tmbo/questionary?branch=master) [![Supported Python Versions](https://img.shields.io/pypi/pyversions/questionary.svg)](https://pypi.python.org/pypi/questionary) [![Documentation](https://readthedocs.org/projects/questionary/badge/?version=latest)](https://questionary.readthedocs.io/en/latest/?badge=latest) ✨ Questionary is a Python library for effortlessly building pretty command line interfaces ✨ * [Features](#features) * [Installation](#installation) * [Usage](#usage) * [Documentation](#documentation) * [Support](#support) ![Example](https://raw.githubusercontent.com/tmbo/questionary/master/docs/images/example.gif) ```python3 import questionary questionary.text("What's your first name").ask() questionary.password("What's your secret?").ask() questionary.confirm("Are you amazed?").ask() questionary.select( "What do you want to do?", choices=["Order a pizza", "Make a reservation", "Ask for opening hours"], ).ask() questionary.rawselect( "What do you want to do?", choices=["Order a pizza", "Make a reservation", "Ask for opening hours"], ).ask() questionary.checkbox( "Select toppings", choices=["foo", "bar", "bazz"] ).ask() questionary.path("Path to the projects version file").ask() ``` Used and supported by [](https://github.com/RasaHQ/rasa) ## Features Questionary supports the following input prompts: * [Text](https://questionary.readthedocs.io/en/stable/pages/types.html#text) * [Password](https://questionary.readthedocs.io/en/stable/pages/types.html#password) * [File Path](https://questionary.readthedocs.io/en/stable/pages/types.html#file-path) * [Confirmation](https://questionary.readthedocs.io/en/stable/pages/types.html#confirmation) * [Select](https://questionary.readthedocs.io/en/stable/pages/types.html#select) * [Raw select](https://questionary.readthedocs.io/en/stable/pages/types.html#raw-select) * [Checkbox](https://questionary.readthedocs.io/en/stable/pages/types.html#checkbox) * [Autocomplete](https://questionary.readthedocs.io/en/stable/pages/types.html#autocomplete) There is also a helper to [print formatted text](https://questionary.readthedocs.io/en/stable/pages/types.html#printing-formatted-text) for when you want to spice up your printed messages a bit. ## Installation Use the package manager [pip](https://pip.pypa.io/en/stable/) to install Questionary: ```bash $ pip install questionary ✨🎂✨ ``` ## Usage ```python import questionary questionary.select( "What do you want to do?", choices=[ 'Order a pizza', 'Make a reservation', 'Ask for opening hours' ]).ask() # returns value of selection ``` That's all it takes to create a prompt! Have a [look at the documentation](https://questionary.readthedocs.io/) for some more examples. ## Documentation Documentation for Questionary is available [here](https://questionary.readthedocs.io/). ## Support Please [open an issue](https://github.com/tmbo/questionary/issues/new) with enough information for us to reproduce your problem. A [minimal, reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) would be very helpful. ## Contributing Contributions are very much welcomed and appreciated. Head over to the documentation on [how to contribute](https://questionary.readthedocs.io/en/stable/pages/contributors.html#steps-for-submitting-code). ## Authors and Acknowledgment Questionary is written and maintained by Tom Bocklisch and Kian Cross. It is based on the great work by [Oyetoke Toby](https://github.com/CITGuru/PyInquirer) and [Mark Fink](https://github.com/finklabs/whaaaaat). ## License Licensed under the [MIT License](https://github.com/tmbo/questionary/blob/master/LICENSE). Copyright 2021 Tom Bocklisch. questionary-2.0.1/docs/000077500000000000000000000000001447660764300150465ustar00rootroot00000000000000questionary-2.0.1/docs/Makefile000066400000000000000000000011331447660764300165040ustar00rootroot00000000000000# Minimal makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = -W --keep-going -n SPHINXBUILD = python -m sphinx SOURCEDIR = . BUILDDIR = build # Put it first so that "make" without argument is like "make help". help: @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) .PHONY: help Makefile # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) questionary-2.0.1/docs/conf.py000066400000000000000000000015011447660764300163420ustar00rootroot00000000000000import os import sys sys.path.insert(0, os.path.abspath("../")) from questionary import __version__ # noqa: E402 project = "Questionary" copyright = "2021, Questionary" author = "Questionary" version = __version__ release = __version__ extensions = [ "sphinx.ext.autodoc", "sphinx.ext.intersphinx", "sphinx.ext.viewcode", "sphinx.ext.napoleon", "sphinx_copybutton", "sphinx_autodoc_typehints", ] autodoc_typehints = "description" copybutton_prompt_text = r">>> |\.\.\. |\$ " copybutton_prompt_is_regexp = True html_theme = "sphinx_rtd_theme" html_theme_options = { "navigation_depth": 2, } intersphinx_mapping = { "python": ("https://docs.python.org/3", None), "prompt_toolkit": ("https://python-prompt-toolkit.readthedocs.io/en/3.0.36/", None), } autodoc_member_order = "alphabetical" questionary-2.0.1/docs/images/000077500000000000000000000000001447660764300163135ustar00rootroot00000000000000questionary-2.0.1/docs/images/autocomplete.gif000066400000000000000000001157661447660764300215230ustar00rootroot00000000000000GIF89aG*^P[\\oopƸ{{óŮʶllР__ލ}}̺`O#Ҽϼ__ttiiOѬH%6m?Û7ը:8a4Y3-K #ezTu'=p,xgČ;&ɔF9fy7{ z[ФK6լ_n ;lOg۾;ݼ{ <ċ?<̛;=ԫ[=ܻ{>˛?>ۻ?ۿ?`H`` .`>aNHa^ana~b"Hb&b*b.c2Hc6ވc:c>dBIdFdJ.dN> eRNIeV^eZne^~ fbIfffjfn grIgvމgzg~ hJhh.h> iNJi^ini~ jJjjj kJkފkk lKll.l> mNKm^mnm~ nKn枋nn oKoދoo pLpp /p? qOLq_qoq r"Lr&r*r. s2Ls6ߌs:s> tBMtFtJ/tN? uROMuV_uZou^ vbMvfvjvn wrMwvߍwzw~ xNxx/x?yONy>_yoyz袏Nz馟zꪯz뮿{N{ߎ{{|O|!d,@^!,D!. ,  @pH,Ȥrl:ШtJZجvzxh$&{+߃N`+-ߜgy:م(8D1r!9͸bH2(lGL9Mb#W4~=D]#욤 x9`l24l 7gkɠ r.QX)4|};{ SD8/E_oQ鉒*#!R񛩧%Ɖ:d$kNBɜUU2PwR1i3ft־Y//)&Kc8TrU_B#*f ĈBMib(l,j荆p406CEsS'$1RCG)>7y1>V>sn|tGā Au9V^9L)?'Q&=lEW<%

Z֬TADӚ 1=[6rMCJxQ,#tÀP|j3Gi;9xjZ3.F!CҠh/˕]yRATc;풗Zh&mO F-:@%V*-A1a)Ցa "P8T89=D A}/|z҉0c?. ,4Ca􆖶\FdƜ|E4<e:!q2ЁJƊGQgxS_c}8/"=X?rA.ptPQ@k)A`qkBM-"WZOWu\9KȐ%X3$#e ýڍnb+ko| x)hg4ݗxĆV0"aEZ@ ChJ1&[q1]!?l/"`XLVpnCАY|1xW0 b@ZOL-! mLYP5&r3cN1yW NR%&c Z:xS6nLg\xγ =πHMB !! ,  ` dihlp,tmx|pH,Ȥrl:ШtJZجvzDKqy];p a~z~$s$t s3][|gswb4^\'sy2@ #&# c #> /֎D8pNY+B{LC@%x Ei欙1bg0`O@ dRb" h9Mn2 Ń!(rQE)ҴūGxK9Ka/,%ͱJmsДV-"a;\Um&.!Y[b\v56k.]#t.۪Wb,--yp׾گ%8FCzЫEG{ t."E<Ѿ3zL[k`swL u[ ڱ m`hɆBrg_0!i$L0'"z0 xƗLգ"j 9#>`'qx!Wx ]E/FFa5nTSa`, 83nx2((-#Ў"Wn~RyՐfilgŬ)¸m 7~U$p^._P`Eb~ͅUI哏7#sc.3mX7*Q@uxy7P$E-k*I+0e NHL I( j`(GB"Br6䳥Io1h QvW"Yi /(䨫nŚy(Ag=KZa^qyrt.y/P ʍPBjh*s"8͂ j5/{D/B`r&ht%GBq8ޙÆ{"m+8ѥ҃7Kfgβ7 /@ k7$6vex0ueUrwNodL.Qffan׵ F7 k㝂,%)sR7_iң0xyjsrσE7.Cy[:R%b; S \!Z) |♿(E4 ϝz%E.~w4@̑6-vb .( N%p2*Kg[Wz]ҕfxo aUľX,Jˆ O5L.. L_@Zj/:#KV 'ؾ7$izH3[$i i8} X"< Q4I= EN(SC*d؃*YEʒ,r u𣓟$e(G $4@j&BQ8D:V8A~,^nXHT'HL`#!FLO~ ӕo<A29/Yh!q>Yjfo'@+yHx퇢"}E?6<FSQt(Wql2"J8Z=GzIJJ@g5Kin*@~/MCC(9Fg4kD6]]3׾iPhʵ`8ق W#_xVqBK9vU#-#F/ 0lg 5ly[k+ݺW0 ¾b6<\,, y^sKZͮvz xKMoB!, @ dihlp,tmx|pH,Ȥrl:ШtJZجvzxL.zn|N~xFI ^Z<}>( [j k5^A=&]i n3ͼ烝*\(gqFj=2Ï CNp(h-A8 sŔ \T&N: iFI4Qs'0U2SPL3iXd.M^պj[L4%Pĺ L'u+5s)S|WL˘QuoĩTvfAVļԻl`MPzck[ļOPt/oumV hğvV7@_7 ]}/g hÍWKтlm>bqؠv(G]y#nH# =/!c)!vmnNYGㅣ?;ݑ"g'֢ /~teYa>BCtiŬ͂{i$Б9xapga);W@% KYcU$h^V'sㅠq MY$ pi|cn n>(bi!=e`9w! i҈":f\mym xvff} )A>HRs-5%*E<;n.¨V gC箩Vkl۫ʪL{.X+.b]*hm,ԥ +4k8,Ϻ0e݇8񘗝3I Zn+"P~i Ӝ2͠p2bB\t vBGC8gzyj\55M2ɐuՀt ͐W~%Yg Dܳi_Sw 5߄ hn{-Cc&xkjioF3<|M.XHgbfsZn+9ު/Ѣ"z t:(-Lk1zf,=-onHAEqmnճE n݂<0bz{`v*M$dÐ@H g'CI-HHcGSq2X8ȯzg~( K0`Z ڬtgcAsf1% SJ,8? {n!*!ɃXk[O= *yA'IJ\clDGkn:a7R.Ɣ/r~ݨ)pft"1lMq J*51qZC74}egTL52UkߦɂIƒ 8^4'p+Ov0mR*N>t$4-?͕e:"U9*RJb ~Ɋ[MX Npp<:J=}P+IP׽cOљ^Dʄ(DdutJ,8Kq:,PU}G8 ɡ1::rY'pw f. 83qo+\)ŽuT7IRQLIm(9QkBj3 nX+{ؗ5C,p9n!" \4 "?ԤЀuC0pG&;PL*[Xβ.{`L2hN6y L:S3d>πΗBЈN F;ѐ~#MJ[47N{Z GMRC!ԦNWjհ[Yָts^ϻMl3N|e;ЦpMj[;Ӿm4dq6Bv~Mη~[z= [␮87q@k ?.؃>Wg0r!I, , @pH,Ȥrl:ШtJZvzxL.Wynisc|[C>==>fjf d]BUZ B WDRsgYeE>QY zR ıHQFB GIDPC׏J>zWD6%H^=xBWxRږEZi1R`"0tI^d ~ĉ4re9n<s$žriQ\@A5搓&&n9N*UK\ ;tHSf2P΢lOx[Gt-0! .PR%IA82.MY!!5@1ڳ;JQ(DM_-7&hW%SvRSUjnQgxTx'=eT+L VDѷTf[ $.Eai@wQQ1J3#*JvMdc WU}]#Y5`hM=YBE[VKҊi>er3UgP|R8P1=:: ^cH9ES)eRi ~(M2B˔|^QkM*焄Cpx 7bh%d? .=@p;fډ?ˈ:dm#Ldm|ID$ٽ-hjkGd啱SCNBa'"15MjYQ:Er_$,XR\ZtD Ja'T^H]C()C1^eAfFH5=Ⱦ<ش2r׽v#X| ws yMބ'ljk*GWn_Rdb褗.D1A!, ,/`@pH,Ȥrl:ШtJZvbxL.zn|N~P]Zn]\M[ H*\ȰÇ#JH4h8!XIɓ(S\ɲ˗0cʬ@BBɳ@ JѣH>f իXjʵׯ`aQHC:ê]˶۷pʝKe@K߿ L%7T#KL˘3[$aCMӨgYc˞M۸]kO Nnwq/УKNËOj$i˫_ϾIyz(h =%A߁ 6FEhfau (∕uX!(,je"h-(4hҋ٨<<8z̐DiH&L6:=)TViXfTF`)dihRlp)tex|矾 蠄j衈ҲѢ6裐F*餔Vj饘f馜vjjꩨꪬ꫰*무j뭸뮼+k&6F+~jfvkԆ+k覫+~[,l' 7,lG,wgw ,S`(/56:Ϻs7ͱѧ4"4tOCG=SkJ3u]SMȒ lR 2|mG/hĝt s08%?8(.8OᘧZ+B▛]P~X~&y.,^:7;5;!j{\xܘF)Cj|c)ox犧ᑏ7ߩgqnU h}Â߫X ' m ϘG- ZpXUpY@tU*t0EM|٣@6I*|@ԫ@@$X)s̃ U@lvBsFRAр^Lȫ3 و1P[IuQk8@8:`Z4@KhGW({ Pod@> jP#Q]>)TBT5AІ*te!E#F4U9g~0UfbSR2u"+d Oi"e%R TZuSB-Yb̋'gݚUݱvU[J1V;-_kiMWDB&F=j$xUԕVm:[Ԏukl]I6S*^7 YB7KP XʚwҜΨް 0\%on[Dv%jp#wRH(2Q Nw.z0)aMFCNYPC#b>!EO,'@{t[T* l~uVuL&r,cS+?RMr~ #wُXOm@Z{2,^9lRErٌ3I7eBЈ.Լfɯr? HJ.c90QϊS fȂ:8X:Ѯ4S}r4:Wc\٫I [stm;XNːvs/-V+ԏ^b8+3ڻ#m6YTl kQzƶT]L 0\||fqtS|c8 h.%5X5~p/5wMm_׶ln˼ zcFLv]kq6yoorttsknU;cﭑ Azy׏}ކn~)gwt_l'+yw/֙l`wB/x7`xw{G蠇9$^7=>k4Z:if}g/+-޶ _g910R/`|e淀7r(EﯷW#r>tS2Fdvg@.DY)dY \@Qk'^ݕLAsg_ʕeeA 0_S>U%@nŧZYy(l# HtAU3lX'vѦ6sVbvz7l$m&xjFwmI,^(y DiWD"(g9dMhNOe>ׂMi_g6ZFG;#n%XttWGWd3hbȇe7gS9so+uwC@uro8cw?DXgN؂b?;T͓IFkv؆\X.k8ㅀ6ue^ti8,}*'bgvpBq q(=c Gq4vq҈b;qa(~+spϨqq)2g狱biU]`؆d\BKXs;i.8XF؋v87Xkz85HȊIA{:Fȁ.o9Logty)yHŔI> x[D^;i@ xj8|Gn#d|vY|4q3vSXmF 3\DCYzv]j^YNxp2/$GZUdyIjɜI $ }yj2e3#@ttd0\yh3;h j%*ٕqeU/vGW.ً*<;9)~(~aX`ɦ}a))ݗ~ RǍg/i_$f ?ũfmx\J"xdY|yw։L(DvX7[zn8͓BOڡnw *u8no)dEHDy_S?_Ǡvy@؉2׎@9VzxIvQz8nyъ@N{yGzʓd IwIQjpW=d[ ~QaI kRX} 1SrycDgyiIX'sQF\N3EʯzJ7Y ‰D:ktL~֞Ff{2+Xۣ :\AWgk)CJʬgkHgu*j$ ~kHI{{+M:a멊 (5Z {s꺳ɩ={jC[ ZwN}KvK[^۶8 9o%+(~~8-{Obɰ {G48z*~ȋh.˕,)ED ɂ73k:kwzj[3&uL9脗V[雜S|0zTBG[]:jnz+jɣOʸYcy:˗ۿ3l)j;B۠G- Bð*Kj,|jC5YIj*LÏfڸ6:TZћ{itÚ8׌}[Q8k૚nsrǰr/w- tŘA*N,턝ǢՊKLJB 2 yÅɝ}gJ8|9|9+V*= x[] @*$ɋ.y %\| 3ּ̻szUS'; ;ĥ+\ AEi'l[]_l܃l?`|hec=dPnO"'5Py\(ǪP&sPrv2iP84Y7$EGpfe.mD8Vt%EUTXtпyɾͭErğE`ٗL К&TvTTe|H'T$PS\AI|O`wKX8igǵGgE{xvxyBYg=o܂amyhzM.|Q>Lu*YK^Fr>N 㲊.؈LcHs{֬ZIx26g) ) h(%^7%967e)7=l|>D~u_:'^$Zopivn аn|\mٍwO:WC7vnn^Fܞ˹mެNds&*Үs]SIf7wDs?+tv~kS}\G{{񤺭/뮾*чT#\%O7ΪfD֮1/\X;BVnJuPl->J@}QRR>brg۾iS6֓4[xv fBoUvOQadIo/FuDXmuOoIO-_0{--{9d5 1 =2M?=.M"Ѫ$@Dj8udG!@1 2?7%/޿HB}?KI49؃CJJ/.֝"OL/-#=b%>-=RCAWb|.]/c)=J-2M<$ 00[)-&KZ˟cMuT/0 1K&#L&3F'2MV)e3unT&K -&2hs[|` G::B*( $3'-+1'ML8$EHGC ?FGC$ &:KO7.sA t}55(-aGM]Q{3o3YU'g+yS˗E7B!'>iwě+6/8;aC!F8bE1fԸcGA9dI')!PuLxiEX^1ae$h9$ K4y")&C"4ʕFuj->]4Tj'#K>1j$ eoe  kb \a}!GU]#\q_k,4_nM"æ1-pnE|q _CV'm^ʁG&h ;ousd|yѧW}{Ǘ/n؜Ra%Gʓ?,&+2kéjA"* mjq±Cb*D "i; {H21,Q:BѺNHÌ3'/q(c3 Ǽl!%}}̎1"ˑL#/7:lc7+LkrKTEmG!TIsµrZ? Z0.!Jj/©%EKUTrpPU,Yiu(4f (S,Sf@%, nO; 2r5Sh3تGZ(Г,IK.ň\N+(ZxnNur_x䓡&6^+ PnQ;svH ֞R1nėFkG+(Yiq9RKTU խ4E"*z4_n[M70 ~vzDUielYQW=Q˱d [94dk5kKia&|pLm L0$~6fF߿ {\Vʼ7hgwّu߁^_E]U{3z6щ| /uik[l m zeT,${` [IuO*e|517+ N5x+mJ.doeʜCv380 kt0a,)ιoїB%1yXMu]ؘq}X:$#y`n޵1Ʉ'-MtŘ@]!RN'UA[heh{!ml<+`q(#Q"6h@ŸV,5a#4 jTCqOy1nܡS8m R' Q[b-@])n,Od|h&i瑰d'KH)Q41Le.Tx5 :W*c" qQ_7ǣd-=ilc<!$N)dŗt'! j@̑$E$}I-DRC1J\qVYFE𠣔\w$a Prxc,Te*ld.9:W6H0MuSP~t4P,\ TjGٯծU|fzhWɉ=uj<9.ph!1Bt ݫ.߆ piuSPP&!ڴ`ad%'ˁz2-y'PPl0DڙNRmq[&h+Vޘ{T*p۩M͚ZNSScש3^7&0[b?%>=O2f!E}?5v:bWjDCn+{yu0slqznAbčnY'PTrj8*TyfjA:O%6"}~K*dh2^Ѷ+~SBqͲ:)#P^F`wt6ZVڬ4oYhV=a#'~8-=ągyy$ͩz= 8R8t`J ρ+ QKPQm .qk]Zd=>}~)s)UWt69lfP+⤞z29u|kR~v-+"r\wQzէ4O ؐ~i2x޶ }$-"ݚArb#]6vKbEeZiS06w\{Q{5IrbBkwi0P&Hc`" 뛴I=Bb{m EFnT 1;Rם^wp33>}䕷1LmuPvI;7ٝWI7z^ 'MW%HV[8^{ d >!a.?շOd;(Hg<7џge?3ϟ2 d(_ʑo v>+ L:0%p)PRPjMl.=D0xJJ0UpYR@*" Ώki_0upYg|Љ̬0 -0 S _P p  p 0 p ɰ + 0 P Ͱ 0p 0pC1 1(! ,%,[ dihlp,tmApH,xȜql:PZZ,lz0kzn|N|Ev|}r}mdz% $#H" ; :Lj 9#Õ %Jݰ% 9$<HlTi#VC6޹ 1;|31 I^ 5$8b2v \0幜湙IlꬆΟzvCѣ ]ʴiUNJz*իXWKٳhӪ]`pʝKݻx7o\}۽/gX1a#K<1 // (9Ө^ m!5՟~̄ ]euw;;mܜˎ( Écm\4 6tu;?;to*'Bᒱ:))yijWdҨ{:뎏Ɇ+V*Xӆ^ u2ࠚj;њxIhIe'kr駷>Gu!{2ІRH uf{݊)tc<؍smNlKj*"l+)gyoc hې,hW@[QaqJk ;GO7@l%b )gbPEen$jlz?9~҅WXт[p!޽2gFfgIq^ ʕ3y^-Њ 7{E8`[0kʆaW'mXߨkyC*r}uN57=/oM ^H@ԨYkKHhU2Hq&G]pIJѢ6;\Xgw/NbTx"kc=i,!c,cjNs_: c*bw>L D:ȵoi ec `xEhpقCGyNӨ49i|]!戆YH#a,\e T6A6Qd* [W>u]"I+_Qx iA{%:zQ9^ RED,KIjbc24 _f(NJ:.٥e$gUSPL!Q>a.E6"QK(0uօ)F.P &DGdFP &P/rrDO.5lQF`3I(hڪUKj3r"U0̤b|YQ r*Y: t?1':Je7k)S_F,T`zVjb*CI4k@^BZR[fPQjbkuw.=9A{ফښw^ FZ.EÞq؄*EYv-*e_-k#)$Y:m-uu~﹫ic-n{RUEvrӔ6?-sほ$ơ"@ qK}wjByVL*`]+0 2~,C63N "!,D!, ,/@@pH,Ȥrl:ШtJZجvzx,zn|N~SdaPd‹cB ddYɁZǰÇ#JHŋ3jȱǏ0 0 (@Ea0cʜI͛8sɳOEV'`adePJJիXj]DcFKpqڵ۷pʝKݻ!J-}={$0~*X0B !q?<ƒPVaЏ+eװc˞Mm|_Ä޼@Ы"B<;jQC Z=HrQ׉-$ַӫ_Ͼcĝȴ剡sDy}w5`taD' %oQ'xW@i G|(,ugD!%Dy)6P٧ ]?~RBU5"V2w1z0v`)d%$_$hJ䗝@2iVx8 .^ͽ A^ GpY棐F*餔VיC:d1hB(Tb<#{Ehxk 僆sĪp%R j樥k&,3υDy Oٳy'AiܺA{k_\iO 3.F YۺtXV]cOHX_^q&;BKKclN4gf{(>5 <>'@r|C)JrI|;I K8j:'2Y& `l{7zp"Q()IHWp84'sH B@ 1894ȯ`;|B&:E;Jf(0UA-z` H2h4S6pH:bṿ> *(BL"HC$'IJZx$&7Nzk8(GIRL*WV򕰌,gIZ'.w^ 0IbL2f:Ќ4IjZ̦6nz 8IrL:IM\~ @JЂMBM{:D'zOZͨF7юz HGJlR(MJWj˒0LgJӚ8iHU@=OQҨA?%@?NfTuΩ*XHS^&Vkrg='X*ձUkfYրwMg^jά8E)JE8& :XÞe?CЁ@Ǹ09Pu*^C{VV$%mM3+ ֲegMlvm__گVti{[ۂS>sKܪV4.:?q ״dM_9X xWewR4}b!V*u1y;[}f2D*]&5nu: i h./]mrr}mu-<`d+o7}"EqKþDƊW clDz YHN2+oؙ$FgTh`cG-R4n&m{ZU2VfUK٬dlY;uqvfIh΃8\/ϸ&W4S {Q ҲOrDT2ʪNC[o V3Zq a =4vC?}[u׵vI:ߊ%Ǯm=&7>d9S/i v}G. _vvv[>uCh9۳^x6O~K|{y[s=vK~DSoy֫\g9qxCv?=\Ukl b/hzˠxτn$8YIY5kSaI'h8F[Wm d8bǃo V18"z2Ll'(] fl6me\}Vh:T8\i(U!(;`)['bg8xvl #tkE(KgZ(! +gO(~ĀGnj'8X?~Ә^`~䵍~҈^ո8d4Ȃ4pvZ+(h)VS؁wQ8[$ GH@SXhxXWfUE萃 " gHO4}^V? ȑ6TfZ lGia0b[Q QgZxHxe4(u(xHb}xp  Ȍَ 茲~iHMz7v֐Ƶg|e壘5GZUGre&lj9ayv5?'q%KYّnW7mņwh9srhyYV&WM6OɗᩞDhyG#`mz5ީ,ZZ1(=əI4/ݖ!LzxxY`VA-zXأ8}$~٣ơ[ g9egЙRsXT%ɤb*\ZLcJn֧(^e9Ŝ)9䛒کxqBZJRkyф\wi L砪*3[V4ڡDʗHg`a68%29ڂou#b̷`ʫ֫6*溥JeJyxyfsTz:_jv :axciúa"ڪ \`( Jl:m k˪ji7Lp_v1YV튉 *"J㚳 s[Jy 'qJK~Ʃj(ٍH댘wp؜Llr*eZLz`5[dU*{lgss o,gsM=VJ:U緃dס &yʥvfk{v*6_ɮ{( {Jaۗ믐ɳeZr9k|` pI[*te)‹[jhƷ`:K{g:P5魄 wtzǸ V֦oeb˴G*pv p%IaTڵh~,QlIMx싧1i-_tZEۗU9f\Ɨg#XdqsmyHڮy+] `&,Y]8ƇL쒠裨Y=@ MFfLnxLR\ΫNߋIr [ }TPhFze*L y{L}DܴN+ \~ۘhj꩹Tc Jz!,̰Q'D|{}yƈVD-Mȱ̈Ґ,EǏȈ+V1jD IZmk_ی8a*[xnֶ,+2M/Ť駩 $LWᅍfXjxF,%hI8,l ݰSQi-*V/n̿b9|eެʨ{ɞ|bjӜy1՚巔6L:޺͋ol'ۊŸ f#}s+-U k}iں}1 ڷ}|\= յ,|9۱lع ؁Va}˧ZyM|oZj- |װ逺Ą͊mk*vܳڼ4c&:kkNb97_kXNjJ0v]DO^?9zK (W~{4n!Ѿy]Qʌ, 8nW LI:Ld'zL<-8sܹ옕ɑo~mG\ NWi9Zr 㾿v =S8\ ?ߕvψ/߿Ĵ_&9Ƅ֪^ۯX q,+*,&?yYO趞ZǴϴOL[?՘G함O%nmz~R{ug97z폍;yVoݶKg pt[jn:QXͥOɵ؋=]%?ۜƾWV طO]_n_~yB;h册nkE4՝P_%h J $n;k.h` _hOB8ޚ"O'6ɡȭEfc6fc6fc6f0_dױk5fi/>fLUc6fc6fc6fcVRj|k5f 5_nN5ο:f+c6fc6fc6fc6f4kkTc6fc6fc6fc6fc6fc6fkkTc6fc6fc6fc6fc6fc6fkkTc6fc6fc6fc6fc6fc6fkkTc6fc6fc6fc6fc6fc6fkkTc6fc6fc6fc6fc6fc6f!!,, ,/`@pH,Ȥrl:ШtJZجvzҀxL&zn|N~wecOe,_ed:F*\ȰÇ#JHŋ3jD"Hɓ(S\ɲ˗0cʜ9.1 (2@ JѣH*]0 19;ɴիXjʵׯG Щ@m ,!Pv,e"wl4D \BLÈ+^y d$eI[cc@1Ac˞M۸s3l5Ax1_C}\LrEi"uOӫw:A1N9D/@g]QE~F(VQgDxgMqPBXd jFXb"f ~:vFf8m&6!';n`@[.g *_(P ]m ' 7̂Ekn"AV,`DqV1l(\jnYi0'ȴ,Dmh,L7P4QWmXg]k`-U\:h}0p-tmxv߀.n=U7G.Gxgwqy褗n騧꽌.if/o'wyG/Wogw/o觯/o Hl:'H Z̠7z0| GH0 W0 gH8 wKaH"HL&:>*nTaE+/@n!JhX5/p$c8G*>>^ύC#ƨ=BPf<F@[d %)?Jϒp$yh xғ/IOF`@( x@:0&+,w)e4= r/dNhLͲޓ%-gc:ӎԜ%&]~Sל 9X"'ӹȓ~&y#dVIT~b)1pd%D* h aqt|˰aFsyvF9Mn/!L)=Ԗ,`KڴhIo>j/S)m>sD_SuZ>2iԦ9[^5 H%#?*e`lh(V =)GxP(T]R=WMb#=Y17 C>X'YZkMF6CLujMyG|B)J;:[kK2p4n%cVV}m+ Dr%^] XN2TN{SGtmO:9]pT kTp%k>Vm: V>pW>g iF/ݰ J=4w@x׻rscAֱ&Oq'P#9Jf e#똼Rk"gyݣNM.&),(Cg+gGx,1}pQ7 yX[DWޞ iBzvh>xz^W,RtkjJk,a7uM_yaV'[lm]?:NjMhze?kq?{$e+8wޛsm]ߗ׶_Sf c:p4m]sϾ5Pzvܿ67pwFƧX}s]k!@xhMΚŜnmw] ܹo|Lޮ]:\]~p$-"=.uoWB[ׅF[t:t4soĿڦvz-a͘rnf<> MJӖyz-]J[~;V[}N=iXw.=Me'{H$ >gG>>w8~ 2hGٻ>yBuq>VyD~2g`fxW~7~M}&NM}xGxqԁL{8qIo7tj]Kv=jf|% :M_# k{LϰTy'I\W {ȇ Y4NDVT_3=RV~ף["hzW W} \NwOs i4<6hNfh(ΕGMv׌6s*FKHUpFhHp F e9E_mX'{ clxK(XyyUV7X-Hj{IMؘzM 8[x8a"苟XА7A;@8fviRS:vڧEMˏf*=bWdzA gnkiwʶxO zNASj6Ho9'G#\xkNb:6&i\̕M*{ kxbsfU y9HZZ:ck ]ˣM:;*E"Z; գU){ڽ6 +۰=uتSJ&[TZva ;ۉh\=a7ꬬm I{lU ~iJi~zl8ʊ)k=m1F 8Im=`+o?\ji9TB\xȾ@\YK=,ŷz䗷bn+zZy5eN٣ů+8Yeڄ*;[Ktǽ ɍ.;+JLԱ'廝ymMMΥ` :YMܝmuō^ z|3ǧ ÛgbU`ʟ[:nW^e٬ (}y_7>۬';X=; xzqs(&ӧ&T 9Q6eu]vaKMwӻDWd* dd^w:P{@$]6F;!sT{qVqmrIjffo[׆ގUM/RODtaxFhwH1_KK^i7"KH H|YʪԽӃ0=vڻȫL Wu۫NXrvgp Hp]N=(p$I\zva~K^Y=Ο<¿&)oPLL<@iQ X:Igr> VNe&'/LB,QqAxL?IJY F LLIJ $ /پ63K (B&VP1 sd]I:,;H焏=] qEC?/ J300(3#/.,Yg_~b0sDp!,%4O*O)m8 +ToK&{[#Yr<rL)$*kL֌mM94N4sI* A L8 =-P#t@5TF%mJĴK5 #H-FĉNr(WaUYi='[q5W(N@UNAXemgVikVY)mg\sMlm7b*`"<t O.NXVar݊18Ն9? od&%aYiEmUtm)qZhm.ڌ@LgAe_^媱Z뭹z[찻.N[n[{c[O /pO\!\rB3\9wUjWZ\Ya]iP{3@. 4 !A N1A nAB|4 QB$t aCΐ5 qC=@=aXD!ElD%.MtE)S SE#nZX=qz0>#@F9ΑuX?!j#EA"< HG/QI `L%Gў#IMn`=I| iBt Tҁp60”Uos, !IВ% ?PP.t38XR#e+iMUQm ]nA`#w ke%yb2y%4O}Ӄ$* J]ӛ4yv1 H3\Rh:) d.LeջgIQR^ϟ%ZwMuE;iLӛ.>%uTz‹)ZPCթQg:W' >44ѓU1ZJCVs@#+E'*rm+[P ֬5,#W*Vz+] KQNϰ*ٶ‘[!^v"^ Ēu -j2q]mq[2?oy4mzMjO4npGir}yP@F7MVU^{(O澈`|F5 ޯnY w[[ӐF2@ *Xc}' 2Z$bM+vMWo& _N\% lV8p\ ?h\6Qޞr+ pV'k{gب Jl5s{֭'mmn}m~nVvio*pp)_U_gEuS7:-wt#r-ríqAWwP17>WK7uUwu;ȕ6ׄFuCwvOht}uWKwyw}7\͸yҬvy wywz|zvw{{{K({w| ||6}#|}}w~ַ~c~wY8 H8xo+!O, ,/`@pH,Ȥrl:ШtJZجvzxL.zn|N~XN H*$oÇ#JHŋ3jث!Ǐ CIɓ(Sq˗0cʜI͛8eɳ@ J(F*]ʴӧP^C*իXj$ծ`ÊKٳƾ]˶۷pT+ݻxˎ.߿ Lp*+^̸cKL#c̹ϠiMӨANͺװ㮎M۸ͻ3N OμГ-NOiνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<>)DI6|4(0Xf\ѤL& f a:)R^9tiEdi$HM* ęI%65"p@@M.*h&B*ꨤ'i@&1&J!j+,ګ*h,&KĪJ^몶2K"&i覫 &I  fiۯRBmʋj eH,ea+0*)1*ڦ20!$/0,ip{ pp,JLtA-diJ#/nB൞x5H*mT`5M8'U#I|y6* wFD-#loH/.4&C)up[NjR@ł \w๞Kӽx뚧@w;(p"*@n>{Gs|C@/zNϹK'lזAZƇ3@ 8zӊ!lg^J3yj ~(֥7#e\8R gHC4NsCu;xw;]\x!B &!| %6|'(:e3b H2 Ͼ';:`uȈD!`I/X KnvcFKd7X ̤&gH=1c70ODx8${\H6z\Ud+VJ^b%uuXnL&:yokQLǎEQRAl!6,ЌFMIOܢ1vQ-cp *MjcC+ {5`ͻ0N&ЇN.VBPK*@* c]@>Ui;F4v,d7z6!Kx8~MjlL Njfc= jfS*q:ЍtKZͮvz xKw:Z6KEU}R(|nHrTT, L K!mc@^ fO pl,%Xb8L0q@ŷV eP6Ӛb+X HMmA d@ /aܪǫ ˱Q-Q6ԏqq19` Ġ Gf0'DyxP(IL3d2WkÑA7 `!x0s~iZzcLlɫXz!i<:rYѢ߳2]n@^ߊg` z^Z\  H`KliӦ]JgwHs<@B8}r#qy>½ӝw,F2& rcZo~=(ܠ]g4baoEz- t{aD&G"C@y?Y6y)ms#9fW>yE˅0RPtFn u a\ϺNvDP3R¥1iYnx/()R6=9|+NaMƂM++Wq^!-:W}*x{z SsL(':;aێu"}ɍF؉Uځ!XU;y{߼"w8LY){Ht4:c)?)>gz=GbrGbwzBrffd&{0gf{ty0B`Qp,`WgulWrFy'|b`gr'}\nwW}ܧpJS$Z'qM9ce'6P5{V6c=44WKnE0}nv WzHFJЀFPsEghlba(vkH@o2؂Ijn|Hjf:ppE@28AU~,E~M{Id8p&CKtHZ|;|ZLJt&tI`fL.{1wwjhlwHzsu_'vv춍'XuGhoۨv^G4BhkFwT]@ԉe^Whj<0~KBc=XhJX0}oxȌ{D sF(z ygz7scozquo$v*G0iGKpS}׃U@]Ok?@>DTDyCYEXCU@r ɐG)`s63ȀȘHבиtHd.p8K{Mf-[8|&8inn2Hȓ>AY%$MyeUAgu7BbSS@oY{(/nyOzqGrtzt]*wRȗ˧k수V4 JvPQ0w=iW>h\ᗙ8Ale҄h{4tYiCyψ#ItMsv }iimnH!I"}{ڈ`W~oޘkɡ Vg0w9٢%GAsg?%++4QBDEDBxEn~.[No._oH70 <0… :|1ĉ+Z1ƍ52$Ȏ$K<@ʏ([|9qJ4k(S͝<{ҧСDSY4ҥd 5TNLhլ\5ؒ`ǚ=[,ڵlm 7nrA޽3 ب k8b ;~̠1ɐ%S2̀5s|3pE>}4^U~}5LeӾM6xgČ;&ɔF9fy7{ z[ФK6լ_n ;lOg۾;ݼ{ <ċ?<̛;=ԫ[=ܻ{>˛?>ۻ?ۿ?`H`` .`>aNHa^ana~b"Hb&b*b.c2Hc6ވc:c>dBIdFdJ.dN> eRNIeV^eZne^~ fbIfffjfn grIgvމgzg~ hJhh.h> iNJi^ini~ jJjjj kJkފkk lKll.l> mNKm^mnm~ nKn枋nn oKoދoo pLpp /p? qOLq_qoq r"Lr&r*r. s2Ls6ߌs:s> tBMtFtJ/tN? uROMuV_uZou^ vbMvfvjvn wrMwvߍwzw~ xNxx/x?yONy>_yoyz袏Nz馟zꪯz뮿{N{ߎ{{|O|!],@U!(,  pH,Ȥrl:ШtJZجvzx#Gԥ\Xuh#@5\n@\QDSn }<Ƹ DnH:0WX5hB>H΋ sG0R6ȘZ!h =VidQ`Nh)]E|)&cqfCfJ HxYE cPWO$Sa'6rY&Na$ 7.z+$`郖&`ˣ1Wd4"bUZ69G Y2,eTLY*$1kN1O֬Dfb IjQs8ndY8CEcKB4Z4ᢗU]>e\(EC)؈6V@6 )t -S/\04W[Y(;TJmBr (tC&9JiLH=3i>!Ѓ}44pP2i[_WշbyF\U-r@Vnj=T8 e)!h 'o:qMT` Qi%~¸09"DQ"^Qj[R*Cp[ =K1#1I2 MDC u'afGfMr-#;6 Qo|მ\^I>DzxDuW{. E:ܦ6 nXh휄9i)qJx44HP"p280:H-A%ڷnR׻4l>8dW Zj}KT߸B8#4N"kǚE6{HDVƋThch:Ph5r|&:C`fe`drpe 6HS˵F#d۾%fT26> gHD1/d[\uИqC c:wUL̂ p]Uʕ22ÅB#U?"t&pB, 8z H^‘Z 6P_sgPXMHק>,7* (1o[/@(.rK]ƌLaڎZ 4\&Ǐ2AKUM ;jƊX 2, G*(RVP@&&F;gux]p %D'!AE)\ZhU=JqY1+6  B+ fQfa}jæT,J$y-[UjĦ25Wyw;P;X*ګOۇT_b<5krE%aamZF Nj1@(9*hJFU\|&Mڀ[ CF܀ >rkJ-A"=){6`U (R!@jD[[2cb fI7rdr?Mu"h>Yԅ >K i6}#j*Pf&w ڑ( }"k`KĠhOWv{H }\;VAk_QقN g A˶}X6~qo+.8hc,f a"PP^U8QS$\_ѷɌZkY|ְT@y+d 2?<;CsN:җ; JԧN8VϺַ~ s`{(!!,   dihlp,tmx|pH,Ȥrl:ШtJ>ج6kzxl_eD2I/v&m a{}C*F0  (|. QQ-'(f )P(ɚ#^@ƵOPe #t%Ӝu&$й;u\7v܌@OĻp ,L$gN DƐ5LA".F#d}X1(G񙧴Ō$iT+Iur>=X P-Um3l(YADM) ÁC=D;C%ΡW*u`,B fz|C Cµnס4!62w |-OI`Kƙ vz0 Azu n dww τʼn0!tq8]l! \4 1]w)؝ca2\V1ց"8Bd4c 9߀Dug[ Ieo$`}p@P5veP/Q]|=8Q6e9+sg1Kr05Dې]ZQ `ʝ# \ fuuhCPE #pE)?u,Dw_hZ 2FbfHD0ͤYOM>PC8EƇvT<㌆Ub K~8&.w : ~؇Y",oږ`Md1̉JzGY"Nv)ꮇc2ןD& |ev}WGe;B7%[ٔ J$HAS,5J؂"O(IvqDۺȠ Z˰oc&L5Gޢ+q/; 4Q_ 54i҇ѽ%$jRS܎u KݹZ2Ҡv6Gx awG]RZ``Gvi^)D/ [kvp]ZYNS .y&<3@]:z'fȸfADAMvuˈ3ГNxXXTS#! &xQ2Tna ) Lf0pBXP2=ՃAC Q'ɂXf&<$/3R+f?ҠP* ȶȂfpM WMB4h´ )7_ɱ`@d^5Lf&Bm" I)[fD py\FWeX\BG*]u%m07&xDSSʙ 8x\e_cR+4VPPRkњRE`r1]h<swIP.&cLK~` @́PE.q:Ag yb@vqkNJwu>aAj JEJ^&Q@[Ԓ@G®X;WH*, (ymE k>1RloC[y{k!sdEt7q\[jAv()v¥f`eRD r&=f*#MG[xV4d!F 82P}*lkuV*E~5(06lT%0c~rЊV )^q^вؙ66L핀`WU\X2$lYQ (!녞T>("#yb2r(h֊TX`U`,[2itȃH93W%lkF pb kV4d1i:o`Acd*%wkfNԨjmpe3bÆ j+ymLc,RCnaMݪvKD4h{Q5xx-IK1eoa?^ITPa >7 S[yŁUL~ T<C_Rj89" FW:iЧN[XϺַ{`NhOpNxϻމ@/Ox5ި'O7wGOOWӳϽjO/=^Ώ/zS~⭯{r>O~Oןu$/`??A8_>x-` ؀ 8x~؁ݷ"| 8&xW(,{*؂02X6Hy4x:<@xw!!5, ,wpH,Ȥrl:ШtJZجvzxL.zn2`N~kvsuDxS fzNÅғHB$""'#WSvKs s^`vuo9#ygDn""@@QICg` ,뗄,yNČ4$o4 d!H# bh%F8JGRxY+TҢsRS ,O$6qzk< mpf:~ 7Y(KnHWF E P2VDW6P_"{dmH`Ci 5҃U82 lE:nj͓}4oܣwx4 +i wDLBWāH !(Ą1A0!  .@; ,\Hhf!H}W-G蠁HE #@cI'#OXepM f^ah*PVVhsUQ_ХAt~uI0|U.o&TFgd4Bzu D҉~7FY2GĪڊIN%oV ,ŒmHH<_]F-8 a,Fb;Eי׊HbU}G LP4R.@*5hÒp&[PdǏ17m",hP^Xw0CN23S<1}MVyL'2 Ѱ+'3Ҏ<ܱNXD 98`TEכHB^#Hc r Dh^eM-Xv[y@|RtA#E: ]n96VKv筅^ţ1SNW,/Qs1 6/,Npvaֵ G–>KMD(5x_9uE+#/ot{|Y\=Ui,qEFK^T$Mm^ƦA!ptUw} [sh,Oc8c d UDCO{uPVcXY!jV-A3Caު?Ή'bGazu1+$/}ߓ Cc 8l_@c: ~wD#J# ͨHQ+r fvBUb!I@ - , ÕB풠5*ХSS|pb VI@@zD [h|\EAWghsiI۱-HY >90Z-!f#1+ӆ#nF`ǟ*akLh UCj\s{}Ч/ØY岅\X i*'"ZZ ћfyldE TIb 3ydZ XWB`6x7L::W%hZmH )s!$vڎRJ! t`vBf6 &ن޾Qܑwi )rjX-ȣlGJy@v/og6"Ƞk8zEMZ1XM3/1 m4$T -daW\8'XCQp0lUBù%ɞ.x@B(G۬ H7Ft 8Ȇ)֦F f5fUvy]X WV'Ȭz&]~P ^ oU=y/ݘVfZ FۭG_R}ǵ;zOpAJj2ŇM^Ue.v%Ĭz>& egoo#\՛>/c[K6ƁI7kR:I8@{%tag{sweԆ7ZWu$y3畈~atW/+fk&@=GtvvU ks\xU!U]S׉3uH<@`# ,'D,.oRT$]Gr|u)%"iK0mF'"o7(a R'%2Le.B(d 7$IVy0ÛT%x`qHxY"h^AG5[CvǦN j$&TFwyٔ=C-ylՔQ4& Lh40gC>SY;ٜ鑉s51'/ ɠR@s7h3PG5;B^HI ?)â&!u%)C|!"29LxE1y)n7 'ڣҞrW*(wnu9>:n^7 Ф9700X$H7zMIFvcMgx0C0{qrԱ# skH+[T 6<@Q?.rs7nXx6:Hj 10@ЈͲuU%7[In&{! [ `Fi#АHa@  |ћ[żTΰCv5S{E{啻 +}1KR%]_RK"V $ӛ=jة1Jh@{hgPLb W$^oy–ݨ A˵U0x< } /:<5Y;MtiС?lSeNLS\/ziUb\W"|sRclft 1AH+i;<5N<|Ȝʼ< \؜ڼթJ<\|<\|p' 8< =ѷQl} HM$]&}Ҕ`r`|,i(2=4-a1%(HB5`jKԄ35}H]oL-pZw6SHc @yd[MJ`<#|Ds)Hƛ3Z1gb=t]ްXx;U40c`_JfDav؎cJP#Bk)m6٠ڢ}R E_P܃,_ل=۴]fa֖mF? gg]}4X::}6\%Q؝ `ڥ/4=}k) B۽0ˀP=`b3£],SF,-gw[b|Cqc (*,.02>4^6~8:<>@B>D^F~HJLNPR>T^V~XZ\^ν_bw톰|^foNp|S} hp^w\hkh;I꾵^nM I 2w_*!46SJh uqڣd"._4 0ɯ@B?D_FHJLNPR?T_VXZ\Ro^b_fhjG^'pn_C~;q<~Q.4'(udb/R8L{j |ܛn3$*>aTn.SZRbգtZ\}::v._^sˮe͎j~3jmٞ>f..n<ȵ_4 ##:K}6یo?ӥ_kOQAY2 ? BB.O#QoS9HF3,vǸF4M~Z! ,P]3pH,ȤX0:tJZVzgbpдܸk+ 8ÝvdZ_Z I  zG BZp^qF[DsvyDK qwC|[~H S wCBkrIS Pʢ}ifGUBEPDU19c`!9d_ @AK"]-KdaXmK19N"Sͣ>*\@!6ʁ{T '9T"]O$ :)̵ΖIpbKP%Tx3W+p PZ"5@}(ar&Pv@§8i%ǐ#KL˘3k̹ϠCMiьOWJj-]VfCU%1L$: m>!%R @;᪰.uU`M2䨄ͣWs_S| v6`<Y1FKFLenΔt_Fy' 0VA0Qo=Z/isAm6 1yϥ"~&~oEhEGwEcH]Q$$"(DN2QP5S(C(tIaOGtHE'b2>`y&LWEEt)qWF!,3lD gH,ȤrШZ*v-j1 Kv'>vwC |}Yoy H D! b CBzkh !o^hbud{ N| CnD |ýŏJɿC |ٷF}a 嗓ⶥN¢wT (0 >$+X<)mi:7dDshы30$" |F$M9|-m5% nnP?z?].ڑ#*OXez+V^ e,Y,fR+(!,P]1pH,ȤQ(8tJZ `^pR-w׳,6 z~wҥ|em^Wc H zC%#C)_\p[U[HlB%""WdER Be\Dxez&g'Vl[PR˃[g B 'VISæ.1@1RT8A=6O2k7O%Ub!(n-zHB@FS$"LEbμ@QS$Hh"| I񞙸 @ & xV֧td8RaQatnN!.,3LD gH,ȤrШZ*v-jːl:cMoo!߉! t|\kqeCiunVs J! r pPHuE CQ  ^nPkNČP {h_ǰSG vEQFG F#de*6#lS 5_ο*'.H @QF)GCP!}0EށUXنFV rO"7oX1ig[MOʼ6m{\LPv|w"X>Σ -WbR< `,OgLk3nz:Id-ZiI1]vY@Bܾ lbV'jX.R` i6M?+^@d'F:o~.SzR?O ^zȤ09]l81W:X ƾ ZMy CmiVM74G׸kD(ؿ~riGzİ,HmDaLlRLȄ :q@T$N|]8w:c*vq/Tc3&L#c'8γFxXA4@R{HB򍏈 @E5,tW"'AQ Y$7Nz (GIRL*WV򕰌,gIZ̥.w^ 0IbӐ_<2fB=溜IMO/{X^:P01p|ut%)!p>P9wVl<iFI"lb g@}3'P(P( sLE03(9ˇϖ =` @-ʄ4lQHB%PV 9*T@)FyDhH,̀) j`9UK.b?k'M:@Uq~J +JkvO (VP $ȬfW@t+*TJ?铟 @Jv "%^1R!-djN!Mj7 U}gY+ˁ̞<0A VfTpw[ m.@ x`@k.f=D7q|雼cIlM^{ a!f/xTB<N36'N_zeT[1|_ /d8x" l\H6 x0zr % n^$0JbкvSwVuF+j![}V [л6 #m @ jh~ЙR@ VڴT]{bZFrQh)64+u@0i]KaH@u@Ӏa[HHBZz/vvD`Sv WggҽE LP5ҸlY+YOӮ;D /M7 ꥞nBti>樥\'o(CV O8&m~>'žo|Z-y?AӜ~ʹSoxzDzoh)Dyfp<3|FzvlVf^x.1~}E`[C'h|`'~WAl-nWwzPp%Tzs ufy^7Wh&ȁ 'iu88\mq|2}HzDV(1uZY' 0r1Vp8\0s]m;q@Q6K*`{+PV%;'THssŀyspsrD  WpP ;W2}n'wRlUւTm>HߗXNVXG u8^qHTvv`z%cdFx5?B&{6Y؃ PJh)w Tpz8ksqXX%^(gIHӸv(Z M%^ y(mfi=)rn GNjj(yp(g%srh|hوGSyfqGi]Ԓ>!j6g8~ yUZoxaq_ELphXh&(ՏFGi%a*'nkcvLhE}vhygt؏$|Wrzv3wny nR:PLhteȷ}` ybۦyhPn/ iP;CE:T<ߗ|nnp*fRtP'TnUPnǙjVgjֵq|5r ;n}0ZmzKM6s٫˛.Kus7⻴XPzNYQdINkUiL e3iJ;^ [;uNI w {rO!i# fr.02<4\6|8:<>@B7- \謱npӂE ҖSޭ߽Bh @4HD~H.&5NrUpWe`M}<_Pt |UhOB~ nۏ n~膞n-㺍}nB Ϛ1PT?_C/iO6fI̟/EPUQ \^NH: Jb/X?^23.&RCmPNQds/~ϭOȋS@}RSYG۶)yjpʾ4?ߣQ@~ۀڢBp-0kXÂQH<^Vg~n'rh4N6t:aXD`^t!Ff(o*"69;;3ACEGIKMOQSUWY[]_acegikmoqsu#6׾2|$6:8;.76%z&<!6S_0_kleE;0SWy#xGF>zuױg׮ذ0  H3Kp~+2XSVLuKr: 狼#5:@-h-8P:bB:GBhJMn^3&xE\z*DbX ;@6p%ReJ68bDyΨl7S9DϨ 992!#BPO$ܫ3P-LeOmp1@ %LhR7Z55BST?(G˚Gvhn!@8:0VwzSv$e=Pxh(A5=o Wq-7,@4Ju =|ā-OHwzwtRnțMq籅'1h]0 Pe3W+"+l\qXTU7$dHP~| 6bcRfg2Z#J)TܧZꩩON0&DEY:D3C2{\4.׼G;3 ZR.in!\rƎ\L{rSNsynßcךZ|Yoa 68+:cÅ)b˝5HTo to/v;7sutxUO_}V< **Md|&nŚz1%y3@. ZA N1eAB #4 QB` aCΐ5t] mC=D!E4D%.MtE)NUE-n]F1e4јF5mtG9ΑuG=}HA4G!HE.t#!IIN%1IMn'AJQ4)QJUt+aKYΒ-!d, l@0I8ͻ`(dihlp,tmx|pH,Ȥrl:ШtJZجvbxL.zn#-|N<\|cTnQÖaw_ճym? H*\ȰÇ#JHŋ3jȱȣǏ Cɓ&G\ɲ 0KIM1aɳǜ(} J!ГE*]hJPJeTԫXr @ׯ`p KԱfӪv۷3KܺxV߅| 00ÈmN̸1ժ#K^x*cΠ9MzҨNzְM{ڸ;N<ȓOg;questionary-2.0.1/docs/images/confirm.gif000066400000000000000000000337531447660764300204520ustar00rootroot00000000000000GIF89aG3U345+,-"#$WWX789GGHϊKLMkllccd[\\stt{{|v,nPpK&S߸7^*DNͩͦ6E6m%W|d rK \ Xq ! NETSCAPE2.0!5,Gڋ޼H扦ʶ L ĢL*̦ JԪjܮ N (8HXhx)9IYiy *:JZjz +;K[k{ ,xgČ;&ɔF9fy7{ z[ФK6լ_n ;lOg۾;ݼ{ <ċ?<̛;=ԫ[=ܻ{>˛?>ۻ?ۿ?`H`` .`>aNHa^ana~b"Hb&b*b.c2Hc6ވc:c>dBIdFdJ.dN> eRNIeV^eZne^~ fbIfffjfn grIgvމgzg~ hJhh.h> iNJi^ini~ jJjjj kJkފkk lKll.l> mNKm^mnm~ nKn枋nn oKoދoo pLpp /p? qOLq_qoq r"Lr&r*r. s2Ls6ߌs:s> tBMtFtJ/tN? uROMuV_uZou^ vbMvfvjvn wrMwvߍwzw~ xNxx/x?yONy>_yoyz袏Nz馟zꪯz뮿{N{ߎ{{|O|!],W!(,  pH,Ȥrl:ШtJZجvzxLzlG8ZЫ 3ң89U@{ځMEDwW~: 'd*KD^1i!p/̄Ct%:n"D-E$ f(쀄LS0)B !l"b1#H- ɕ8"<@x'≱8 (.iTYaLIZUS#D`:xS~xdvQp]&W_WT`Dj5BY<)'~ZF:iAChA :,TXGDh"D8!Bc@GŖ$䀫Yī)5@d2/zh蚨P(=R9ehXISt)HSf4T( 'Xd hmMgjYU_~VN'XAn-eB`٤0`e_WOc S}Q|^N L0ANbByb`jE㡚qȊagcT5;G9P, P)$p#RAFpBꞰBlENj -5#em,o_J+)"`2`RpsVjZJ5 @8WMnhh9c)xgb)VSr`Wl(9.~*v; L)yd0/?$MCi{ z-N!pN:&EůLpx_lF]#,hjtIxv)zLfJ:>.j[ &9xA:+W܎׫՞ؤeJ#tr=ʴ t.IF %H0Hh`DI$ ȏ/,(`(WUu>{$4X'-qe]\[tۈz𨮝ʩ`TĢE4Mbvf/{U'¥|]Z{cy*p6L\} 9衐r H6Zu$b2°PWʩ\#:&HLd_-tmlW(Ijuf`kh 6t#ܱ`"jb i娫lS ;ay–Q(zޚB t4kIjc~SkbUfZ(bµ!툣$džWry p}2,ƴnlbi'ÈPGe=\ ̰\Pc` ld% *\090%q&q*'TƝm(-YT@3`UapM1rgse `!a&CSŶl Hw4s=UcIBZ(=s5;ۚ' @T e2JAh|0%DwxS?p~K~p8Cf%_# X1=,')QfUb1>& M #)g1ް ܴ 4Nc" rFf3!TCb) !PF9B+ ʑ>J3:傦l~\ dʔaF '9 ɠ2BL-sphbSҡO{i,x/8D)$8&P+ސ$vيOJKEt2N\4͟n:RyTإ~x3Lu-4| PYCpA0.E"u@)\Q\ʂ y 2S "VjN$U%ꬢpظOh̺4 b%W~T?EgVJe@Vb5E &´Ѡl9ZGO@jdZCMCܖMSp*}nZKՕ! #B8^6 JM)[ 4@Gͯ~LN,!, @` dihlp,tmx|pH,Ȥrl:ШtJZجvzxL.Zi|N~k`tJ *  &F /pIh&% Bе=  (d#@-VٹˆS*e9h%BA yc!Ǐ C"Fi2IFWNRț8sܹH 4$sVBYtC B1:ZaÅjD jujI`uFXYe6hZP^0\zZEk% UV%,gj+ޭQ@r]e>+-`suM5,y-f̋bpjQ.@]; יB"Cj2 AI3KN$yEpg+m._!)iVQtT$P$Dh駠:Hl̊Sqshz!,z!M`3u`@w]ꉆvF_$J!)EQQHMF++'Z(U$1Y[ֳd: _b0>Bm Wl!-N^2ͪp#j6|NJq0>Yj$~- Dғ ? r 2p$'eܔ.7r6#zNF4(xt'S$ch hbCcy(ʬ ` 7`ҐK*k,MyLb&$~kBff]֏c'C@!ݲF"eT`Ǩu7L:iq'TJ<xĖ)ٲj^N'PnRu2ڦ.I 2لVĽ)jt&ɶy}P&?ׂ (Ld2Pٍ(A)1v8u&'f*fUkC$d(0YhL)TK҃.e`0& &p#zQ9g6 ӏ|j4] R.<"LTWtu8'D$ꃘ*.ŠJ rJj^KWl]Si ‘=## G%(%y0? ֡ZR;J>yX: p~b47`a $40M4iV_(H447q* #[~:՟u R4U CXs0z֒GwHosdu+/ ų9#u4I1n34 ( -ZJ]i9m{LdQMe* ˜5809r8DGWL Ĝ(%3(\#w]3ACLg3x։ ss"B:yN;g6W_4P!zј7jejJ֤`rQ(sxE հgMZָεw^MbNf;ЎMj~n{{%Mr:vV7Mzۻ򾷾~:NGO^3w[x*{8GN[&OWl0y].搦9wB>ЇHOl;>`:ԧN@VϺ֫{G:NhO{Ϯ=lxyC~ .<d+/[!!,D!5 , ,J ` dihlp,tmx,|pH,HoK:ШtJM1լvf>xL.)bQh",ʀIM#<P;8/(wxz{ 4 t=nG;ms% 'sr'q1̀H;;f%;2#&3<[҆|vqmi!,AG,HTq̮w9, ^CBc{{]Hn}GŗSJA'_0hiL'E"R-aTZS(2R Ta|XmtAg}TWBottt-4 (Q`TAa$+aO:1\`_=F$s dăӒ`Xl4ߵp,Wby t-g2x DZay,X^ݹ bɠ[qU%#m B$$WB))aH DHWU2ngj)'(eyLYűybc, ׃ՠgj$Ĉ-r'e2fX**̻u {+@twmh=-H}µK-?UObn6Mъ7+Ħes-1A~+ɝ{ p$4pF1X*hD&2q3ӷMxEڈ%BENjWV<<1 f| 0]o~q@-_T ƈТ5@ڔnĵ{JEq E0@HHtcb.Pr )p 9`/s+a!!isG frK ˎ\)r ΰ+sD(Zɏ{J8NڜX-ptB^PCUWUpbſ<Mb WE:#G6&o0RA7IR2~<*WiJ=ܣ+cIZ b̥.!j?, ,`pH,Ȥrl:ШtJZجvzxL.zn|N~/}~JL >F#C,;;#E ;Bڂ #&C>D &&DҰHs H dCL(M< 9ȱǏ$w>X`\8$< I͛(t%3•7H*I2I!,X6OHrojO2@Àݢz۷pkWʌ ęq !3B0S! |e{˘W}KxܘPbʧc5df>F$P`o-|cAzZڶW!VGN:HH$~ZOlŦ%O~5G h& 6F(Vhfv ($h(,by488 S4P 50C RрL>Yo6^рP6BK=^\LrT=`VQgds< a Q(?AhЃ1B8#ţ<YsضIu}x w7 e͕[1eY»{e FwoӀ:@ Ph@ |vf79 v)J$i5q [1Ζ{<1FSD`K:~ԗ=)Έ61u=8R4Q.oeML}RH)6 .A`KMY^hU뚪 diG!|gu8X` @d CZU,^u?0 bM̪\Rj<"Kӊ{<O?׽ҵ`pZxfve+kU}ӫЋVZҚf`^5X˂zRɄ`^bLY jS|B(%cfWՠz Uf T eQo%EkĄM5\Ὑ3]^{Gǖr/w|\ߏ)6&AFlIXE*ɸN0_OP<s#fR1tnd.spEOZ L@#;hn:(. c%]OZ 4C6_2b.R}rN4hN@ӞN:ɷ`WMk +ƾsw8\`ӣbNf;ЎMj[ڪޞmck7 _q;+脨R h_p*PJ(Z 醭g{\ 0`|'8A 2R@ @y!F;l) I8RҲ+XB Bq w0ܼn9Z^ LN336-y̕OB8p / x܌.4)#3x>ARg \pmob1 L-;YK%+Ie&ch$PUdufgGx:s{Ϥ ;E׮e/e6~dOv|u%Y=b7x?{iOsb{U~~uǹF„YB`KpJ6P#tcIBuFRN&u$zXsVq|&a7qCV$|xmr4AQH u +'!؁&B`:(`qF8 WOHV@Gz=GGxT`w&>uUzCZ2>ߓ=4> __C9C9RehRNXUX(H@(2l7R?huwuǃEЉ7&u:(qZԊ'0q9 VmYHT(xUM'TcQh7qRo7Zw,vRubhWqc~%rXkir0DttJ&(Jhu28ȁ85'002J|؃;+X$H 0{ȂD81KH u f xquHB 4aqHw x*[d#YM G9uRwxSzq@\ՎY,keLȌ.(by i&@qɏȊ'@GЖl uauRps?MH=bczCPK xH{B Dq[7ddG5#o8KnaEp3A f=J25)'>.%w h2vw~)ȏeY{sD-ؐNv*c$`_I7;MT~G…D[Fq%QIyC6hhoUdHZa)4iZ0B0fi'H1"EI-dIيsQ՞ؕP;F`EV::O@rWSf]n=w1URw/zJ(^PGGy(*ڡ &tJU|EHOâ / I)ُH0ئOФhd^5xS8xC`0(+ȣNp )QxtotFY֪ $GpHtTgU8٢ !` {}D@p) if:V+sIQ-ERJٱKS3iת/rUEi*SE@rbhxG~^.H&9h*+Js}0 ٜbT7S<:jٮJJf}Rf8B#w'T$$eH_ZdQSjץ3 fd83^ūej wq ؜.f٨$g_o$:{ 7%k} 3$7#M7캹#7GIlPi4k47pbi6;\얔eKOXhx ,jf;rʪv?z 1`Wbbú4,ɟMYv=ʪ]Cg9evRi<Z^tDnk2J3+%jLAЛVKsȸԧ;;5*雤S( KׇAwQ&?/wYЎĻVtť%AvW@V[^6Q+ha^\/̊ZF)&BNX$/ǥeqb(!a8kAvL"'w7wEsj\ al`%4 "t~/re q2\7sp RAi ,*PI|n\<Ƭ@|ʬ+ɼά$"!d, l@0I8ͻ`(dihlp,tmx|pH,Ȥrl:ШtJZجvbxL.zn#-|N<\|cTnQÖaw_ճym? H*\ȰÇ#JHŋ3jȱȣǏ C ɓ&G\ɲ 0KIM1aɳǜ(} J!ГE*]hJPJeTԫXr*@ׯ`p KԱfӪv۷3KܺxV߅| 00ÈmN̸1ժ#K^x*cΠ9MzҨNzְM{ڸ;N<ȓOg;questionary-2.0.1/docs/images/example.gif000066400000000000000000001220151447660764300204360ustar00rootroot00000000000000GIF89al##$ccd[\\345789KLLkllģsst{{|678+,,?@AWWXGGH"#!ez_Y~Qr=Tkc#6n` nP ؕʌ $!8Ō;~ 9ɔ+[9͜;{ :ѤK>:լ[~ ;ٴk۾;ݼ{ <ċ?<̛;=ԫ[=ܻ{>˛?>ۻ?ۿ?`H`` .`>aNHa^ana~b"Hb&b*b.c2Hc6ވc:c>dBIdFdJ.dN> eRNIeV^eZne^~ fbIfffjfn grIgvމgzg~ hJhh.h> iNJi^ini~ jJjjj kJkފkk lKll.l> mNKm^mnm~ nKn枋nn oKoދoo pLpp /p? qOLq_qoq r"Lr&r*r. s2Ls6ߌs:s> tBMtFtJ/tN? uROMuV_uZou^ vbMvfvjvn wrMwvߍwzw~ xNxx/x?yONy_yoyz袏Nz馟zꪯz뮿{N{ߎ{{|O||/|?}OO}_}o}~O~柏~~Oߏ p,*p lJp/ jp?p$, Op,l _p4 op< qD,$*qLl(JqT,-jq\0qd,ψ4qll8qtx!],@_!I,  @pH,Ȥrl:ШtJZجvr8-zn ~}[cHQmu Nf//V2.TbG `Q cjcnD433:Z/66"Q̬tE`U Ksp {;5=K83̧w791jSC@$opQq,,@N c82N m0rYfÃEv%RLE) &9נ`ٴhdcG%4h"uP\# j0C |VG2 [&X]6߆-4 d'YAd{=#ؚyeDi*$!b&2Ǝ^5xbμaeʣ :-!n@ڶp"JL#Qi ꜧ_V;N):k!CDГLCj& ͡,E@i"\BB-BQD `Z1 Z ԰1<N5,ƴ!Him4# s #G1&Xc, clCNـ g)IÆS`eJe%)4Acv(xg~QD}U~s$vcȅ!q9\`@6zc84!vAVG !iL%֠cB£;3Sh  ?,3b"qcƶ>STo-h>"& \3T:9~'p* (BMC'`W&E WLf,G𩑭z z rfb;A MaBo!NiehiDw7)e iʶkGx#dIN2H;`R2aTh췹3Mx*rXrٍZXz;+ cE𝳪TkuHxDA 7(ik6*<1y{uFs$ABbY_.+Xv]$ Yzu [_Qfx+T*L<iӝj)%G$^5'qlXh"Qz= 6IR<9CmB :ҸHԪvK"WVt^t#"SQ0h YVATA%.12t[;a״Bayl'!,`_$F|)J)}@T?Ԁujd]瞠J)=NIGWBbW +М9~l;,\=R)ajX, 2&MHP'2@M=]4Mt)jF5K]Ft 2h q!<$O$R97TpjT`db+MUU{oʢ h JUTZՠG$!`fuc5 10 af""@4X˳(eq孮q7!R$5v3.L϶IW6af+ @kU>Ƅo{'T m0 tX )cQЙr@D92Vb);ڐ4"! fa4*uh(ɯ|iɡ!NZ$FBɧ?de<=ߤ@dR=~=*\l_CL`<w4q0X UqC9!]Qh_eSVivj"d!rP=/ej&3HృC48]ELB&z>͙Pvau(<7Y;Fju13,[{i6pxz*^Kj9m > b\b^x+[;aHwXnLuL$XybZQnq<m2g7Cz3+/(O02cN\,wO@뀕 !., V ` dihlp,tmx|pH,Ȥrl:ШtJAl4sjoXH `֠k;A.My^m}>q% kx'dn8]{y47i  $&m" 'V 3˩ $#"ӾΚt&1 쭾)`U|^D@nH0# E #8-w*Ñ8L) !E@ j&",;sІ51INT4yo\,#U=6*N('CԚT 6) Hlw]x!񄒛8W!"]d|༊5`k$Άo-! F׎^cYx{T 8kZO+)Ȝ(3&Oitt%~O0Xp4E*SatZ%}*pFOMT5~'BEƀ\ ԍPH6Ԋs Tb%/\bjT"HrȚc )TF"XQPR;HPd aVu-L]晛m]r" IbyHW٦ 6Ismy].<*^h͘y(u^j *_&7K[ uI.h50(X|jfhPf94-vYyBX59[}rc[c-ǧsm5g ?!mQX ,b @t)zl D G)a;T1fR&-K!bZ.k̂m+AG& qKiprf"e@^׳!;j"c;-yyWi[hKiAb[/FeY-#Ͳ Xd^^86k^CŪ paM{jXYʕ8C8)pE/ kc|R2R}k (>uJ,KFpΦlsT3 q1\2(8PYR,7T,LnGuyEJ"Hhc51U)zL*OA P!#$+&E1UoX%Cɧ# d$BH&JREbLb.bc}##*Rc]:J (aO!T֣n2p(EɂY>ÖA]^ f & Q /2"\ˌ(NGryL, W 8IrL:v<!, &@ dihlp,tmx|pH,Ȥrl:ШtJZجvzxL.zn|N~g_K@(2JK " .Kɂ= (,<ٝ =u&pz*\Ȱa0$((vI8 ;XALG 4 DP''M#SM pPNscԟ&~2Pډ\+p #IW4 AӮ`:'@P[r\Kÿ @:x(U"_( C"ea2pl]B,@ĶF<Yv, 0jrY0jBc+فJk]y޸e]wcb^_S)7gYڴtW  Ai,K"F7wf  pƟhbj^m<("stnPXl @Q.l\2ƈcj"_jQZ%=Ц\IBr PwIR&yEqd%gÉmaVOfMM)v 9A:I¦bs<馜7 $P@i<=di@XӍ~g'<B vqGKМ~<:9xU{*jhqp*D52xB+/>^m.f/C[MZW0vm#f0ٹOT%l{&RaR6Rf0 jZl⮉*C*D3=\oZ|LnXs+̫LS/˜~:?%G wnu+iC ,ra. -ms+wW@oR.0Kz-@r]yiӔWn9$Okk]?~"hE@\: w]\[3N$;~hf 3{ /CCשa!pG&S>֢'C0b3C J.y܉,)9g[#-lC lE7Ԣ@m4P WB6X'Am*֨l78llT%6[&  , U (ww4J? յh4ez=H*`{qOuRls_ H||ق*u@{%db 8(:``İ1bnU:AnYW0cLw6αNj1L HN2n&;}\[&SXެ. d^13,p,:9 !!O#, ,W @pH,Ȥrl:ШtJZl1zڰxL.t|N;l~x_Djs FQ^C!"e\q \|_EK`B!OGW Kv lDfanB" GnVJq] Cha'O.K%DhJ2{X ] X h8eʕC0LQE1H1Yp3gQo-S %F0p2>(!`ӀIE5|:Abz'L]4˴e-P@Tת \ p69VTxX/!RW$ImU t%X_HJ/mpHBB/oK4TEF@:VȀ kS20"Yt\28 xeBSeN p}w,@SFFN}X-'yuA\s$vTp%Z6xN<&C Y[TCVיyѴډZ !&-?!eU_}%bLf#<}$l] aM8H~&*a56TtD-ARf'_XeRi, ѩnB楀H ői,YZ׃fg'"5_Ac!=($CEt@C央7~D ޭPݣ"ΪM-~1&)cWޱ}NA(@60sZpJ"Tꙸ*[mrE\&$+bR\}Pʞ2AArwFqSr.siB2DϹiFj2x;+M 8.k˹"R j, >uZ'-36~]b]t;+[NJ6f+dИ,* h;Zpƴ EtBi4 1^*SAvÍ]Z3"썽~TC(3 >$ԑ*+*ISJ*f@jSv)ϭ tbJ|\ÞkyLX43eg3!Z"` .\"ƜUP8Y̐GucՄÊaaz#(Vz!QM@( |i!#Mi5uAAT(ōz=PxId/,qq ýEL@l`+B A_]J TXhILV7ʺ܃vZF+)iRqK\|86K`<&De:|&KؼD4nz3t8m9vb<@ !,V, oIg8ʥWǁXIj4sM|-0w2#)/سqrRPu6, FY*+&ڵ o;Fr&n*z"#0! ,c,  hg¯\ҪMn1!0D ("(^RH5P%qH'> ɑȁQrOzD}ysj$qspy#r1s(] k8o04USl$nZTBs[F::6D!!,p,  hg¯\ҪMn1!QyLɁ2Z(.jXW@G3z|l'}r{psuYRKBGIF:@B6D!!<#,,{@@pH,Ȥrl:ШtJZجvzxL.zn|Nۿ~M{z̐{ܱ zL|Hp_J;W0|H' G΢Ǐ6J ɓ&\ɲʖ0cb(C/oy@Ih'FʴӧPJJիXȵkW`ÊlƱhӪEcڷpbiu,c&LHwܿCV5B@Fgd/ 0H!UaĈ!?@8֘%B K$\4#ؕg O/:YpCBmu.|2Ԇ@br"^-%_~_ oazi40_!B` aIhCI` О}A-E,^RHa#f=X`pW4b>(^`!H< 5R`ʙZz]E@ee&g8@ 1WǚGPXV熁Ŝ1 -RlCW WVIhEq<]ܙ n@W{p(fk:|'kexj٪栾fMg^F:ۙBەz&6;ٺlͪzZFbX ]ݵ:@Czުv^MLٷmh>\ku ̊ ɘ c"^1thfc DbZyOh\I᧫[HTtgFm"Kb M 5 +sW_m1kw~nX:hjPi8b13]pԘ uckmyn{ -gOƜxewy`Cvm+:)vs6SV(83l;Ke~z~n]Z,| .oj`.V'ۥq_C4mE[ܳP? aT&&HA@lPαݴ V0q:[>ݱKy_]u-k%+YsB @AjL Ȗ,ܐGSFXD W5 gTH4:v&~x Y՚H!nkbkH?Vl#&!<"t]r** ExPP@A8ĢuESlT"&j$ F݉g>VGTFY 1j% p@(/t‹/+SDI٬@2p j.tBٖl)Mo4ƱNdiL%fRc)R'ħfO[H huД铜j&gΈQacXๆsn}Y^&O;Al"#iE%y9N@ӁKi09~NFuZLRZ:^L*VBX[UHJֲAfMZ2ֵ^@+\J,xͫZ ! ,.L  hh°\/;7;**' @h1[!EYMH"@-j)R0xrBs_>%$[%D6q84aj62-!!,;L(  diA#+pάx^N֣ i!E JLS&VM !4oxL)y=c@ux7,$}+\{" +%zO8 P3qr)d)[oqXIv#OG/$?/N=;:5/'%ټ83گ֞!!,D! ,VL  hg¯\j9M3 .!cR"@¡*>䊲HLO@@Z!C4fNJ0DDZ$ =659`iq62E!! ,cL  hg¯\j9M3 .!cR"@¡*>䊲HLO@@Z!C4fNJ0DDZ$ =659`iq62E!!V$,Ln@@pH,Ȥrl:ШtJZجvzxL.zn|NI~xM$~}yı˿L ٦x#{xONzR Hು* pÇƜAH1⒊3jE?($ɓ(?ILɲe.cC͛89s JѣH*]ʴӧ4I:իXPݪ.ׯ`pٳORECڷY&L("Wԅ2!$jC*B R-,0@ݹ+?R.\ $`kuZ :ж 0oL@ HAjY_o b=$CyṄ,&DS LjuF{`HşuYQwA[O$8ka0DpY hZס  A@u7_E"xbn.h@B䒉X$HH"F}c @>xWXM`%Hd^Zq\=p6)qm z# :\)eYH`U )m]~rkRƨ: 3BO7f꩚T: fhqJhyϲqǖjm.[]iix`.]ʕj 0Dr٘߮Dyqmm0 <@q怆Ƚ6268b6^6A=ZhY*x"7'dᒺff@G~Q7ٷ%F@"Okv,B,޺%ʭ2jrLJDR̅vI(lb:"g\}h(;f`.|붽ևNct]n [DhZ/>S>yמygVz3n=2eMž=utNǷK3[doC`t3 .!Jsq=#$ĭ,5+@m qgy`d8 |9=Z l]YyYvz4 1 (6+n0@^3JbݭAoiUC3CnEywat 1GqSQ$*HK^CXǤ>#yCAUŰymAA9vq4h4N-hc ZL+Fa@OcB#xOz+KKZ LZ2 fzV/ t4#(ЉJG aa[q* ْ#7SA˂5h3נ Ƥ.k4|3mB>j V"B; Rq 45S'\|jGReԐf<|;8đN8>zAkZ{wҬt i 4' *;cEMKH.e)5\#:M\ql٥ ,9kYJli6L@ryjgK4 mnv pێ+@!V-,lA@pH,Ȥrl:ШtJZجvzxL.zn|N~j-I)F,(ͨ*''$E%߬D+&',C#D(&&H#K5NfmD(8HǏ C~8ƒCH0gVA@*Wzs"0{⧺No3\z- h7K34q\mLLO^)\6:yE7T_.<02v ,؀#4K3y7D5NgUj Sσ`H7 ;W!=!:!Tb oوZ]dQ(:QW!߈TlxF aW©]55}lWZ \ݩmR2: fu+4<: WHip_:sx9Ԁz4`U}n*_e m]&" x]Θ {RgR% k[:k[mnlf_5ӊVCxp 184yw1vIuo{h\V:'ո}BT !>.o" 9uC? |~^E(4o* Z"5Ec so $j-U_E{mpS#?BtU'"Hܥ#TD$:Ms0-U@E<;V.Rx4vSGQqr{ՇrD u:iU7 w6H8}pN{Fe6dB'6tb:X2`cDT<|bpsi^\EF^r~J2T&uѣ1jp%YQhTX40r$H'8*>4m)H(X[q)8Mx]&'Ec8!!VlA'PhȌ r*fSEj+jh!4"BՒ~^eLy h#؏9Xx ِ# 9'yّ "9ɋ r$BAZ*8>xqS1 i'<)"Gq2IؓF!e2;؉Ln RsR!A39E6%3X9syUX29`9A5'S5`c%`o4o9:SsLSy2+EVp##EA~d]4XE9飅}~'bGExyYlfFoG$jcg~rȀ?GYXY=UuAGXrQX<UD(S.tWX8htCX(TE6Lsw䌺K \ PGd5Hkn3+aBxe)lsf `q]e}CO޵XQpv$1`g4e9ot,ye }8sȏdE`7>] twoy7jTb&fP&e2760NAjS&?XW-.DHp*egM4ǗdzaztnKN٥;Htyai?T*()D'Z[d`u?0ǥxVFpzBw$zu||{ؤT@ǃ {A44fv334swxRz@t'vJGןaj%>=:JwzU< P] `XWg(vG5bJzES*0d>,$U`x $6ZK ۰zs[{۱ ";$[&{(*,۲Ȓ.Kēt:%+i8&3DKYgCQerԒmÝOk)Guyq[87Zr^fiYqAa8i2m5/#Tw7Ys8[5eC3qw34J0H|83aKAG8{k$9x|1@ PO#<췞~LR;zE{vc_dJC9(H?&HL Z$a{yXyii!.d,X/D`ɹPw4ޔplQK[ټcځ8F(V먃WOdO !.gN4iA[6hfcU)?r@޹qvĴ|Vcˬ!@xCӷLU\h. **z@]<'wYU3Hs@y-!@(ģucb2w GƣY+XTsNT|+l`oeqZ*shIn:>{ KXGLܡ%;hiSUǙZ2A3}R[uU+²hbʋ'6_AmO!wɱsRU:C{\녍˧;))|.rQ<3$8F-(6Y`:%d3CٿqJ$":bppak\pr=t]v}xz|~׀؂=؄]؆}؈؊M^Я-e֋m"5qXHn 8DDZ0@Lj R|;ZONRRK] )xubw2Òr7K-ESt>7ԭ2gۭ8WS:s3ť683ݽ4,c6OV EDCci\Y:ܘW'uў۲]<A ~)Fu;P}zɷWVނfYdRUO,~T>炻7,6&N/{E{C^/ .4FÊҏ&#t wux&ǫ$ ç>- ͞u(AI7jkKKfQ UJϊϣD½ԏ'3Կ_~M14/S>o\kl5-ԑy1@<>ZGTIh=%pZ@IDys-Q }Tn=)cM#LH>*HbǑv#fIܠǟٟ'}踉# Fp|SE4d&x f^~f i1>DZwZ+NJ&3݊%w9.=Vԅr\E>oI5)N˜D9ZzBE.+s>Lx>}Iy3lDUϨn-7~wz؄@g# YXҌ.}K`.AǰZW~!߫m,}b-.~Cx%aͳKTIEL$r3gb_6Y[߅~wǍI(m`i]q)ֹWo0m@z Hxz@c&/A!., +Q dihlp,tmxK4b0@g@+fQD pYM(.f%FW I3"E!!V-,M@pH,Ȥrl:ШtJZجvzxL.zn|N~-UcRV,'%^%Qɱ)BOK&K#'&$CҹH+'*K( ۈv')$"/&Gŋ Q CwC@,HTT B 8hD'##f)r"#`N( ͟C6uV`VB+N^iӐ$`\˶N<@£i׉90/H'5+8W:"[8PdGB~{02`uK^qTSO6B=xKm XAnN7*>;iQ˒%jL>x)-i~M^7T%9;yF>}mH}2&MH)xSyNȀP@:%8tM3farCX(i\C{1Vty(Do(cüxi߆8" aR !7HB,9cFYz(׆ZWLa4{!W*ۑf>˖$Mn*^nhf^y(Lqɕrs^h";JnXj\ ) 딌W 0m#Tcf1lDi:@4ڱ~q-HZ4[lUT-\mD6y"]SkLOvxuW ,M@y$ty0(A=9]K:*\a#`f| Ʉo1xogBA!wvBm_0a4ѕCd#3pX 6y 4@G`@2hR&9p@3y% H ZY]&zr0':qGgļD T ƄYhbA"e/ Z<0`&9ÕAvjPJF ;ͩ0J-dx2JSW JR"ի2(P0   Ԅ5 ZQ0S ՚^&)V!X TM# f l4@` c; 6>MM L&8CYKXYpGP䱓Pmj@Xu,V;vlb+͞u:{WTjA\M؊&Vp>B0nwC6@}n+okZbJef&MZKVA#,5wDs' #64 Udv H)^ ŁN9&?0 G\Sg2*`,'fa;.2H x o`f `.u7rh HnDp@LtͩȖ]IQ&a;;Đ2]'}J'fWsM.kPjS= V{Y]2+w :q 8`v-s(+;V /^ϚrDT>A8 Q xC\~Θht z6_e9L ٜphX!Ka ䷿ ]EqfQ9aϧjp"]jMIxcJ]Gf8I=# ss6@b洝9̄i> ~ˌK#Zecb omU&Y/O;LPn`Q]y|˗eu=Q|8 ^-'3acl)Z%W[-m>t`Wfc\3E{'UxTG~ZUHF05I'h4M ~Z@vVӷ~Їgdg|XJfL*U~`%uV@uUQ>O.ՀHi 8(oagN4wC{b(pK_P~#pBvx(k?\zg&t̖SVlGb[z&f@N%6dA`Oxy<QGKNW81JȂ*z\ek`gHkg &Ņb&h}-w1>G^uTD(L]dl}U!oPPRhb^rIyliWr{ S(Q"pK$KlfX XS1SxLM]7lVh71EXowh؋=ۘ|WmMUvNJayphXq)zՀȇօbzȋxw+8>t:TicPw^fGV|ՕTuVVwAvsOiDgpK fd@ .v(RoWfۗZO|x[r^hf*NNU0iXN2 5 {yIZdQddFm]$sȤ[:L|X!fdg~yrOFv5ނ^I[~hŘ O^ߵUw^ Fx'g,TTH5^gsxkXYXiYe6c9oy:WYM>cMdNŖY zm HcRU8)T)jnc֊FUqaHrrڠj hӈ{UNh`'Fq'Kq9den<'zCq†vN' PtL=3Qg{`UPjX |[4m`PSd`6JPUEjϠ $ |6O&b֓ek>O4v`CWG l ʩEa47d5Nq:*nJB;!X_nLV fHSg^@̪h ZY ښIJEbƢ$C@ȖL:E_0JjHĮG[q4 {F]԰;[{۱JaK(nJlhD2 hO0S~Mt3YlVLoB|@ʐLfx6NGPn˙q'[tŭ[N!|ЫesRDe-Xj[v;Zf5Y% E[X&~i fZe6Jo}VUk؟iPSvLDvNn eZԟgPvJTdOX]G; Vn(wY}zYY\%5}*n ,gn~G|OP^z5\6+m|2F4XT+ҪΞ{;~ ΐ4wWΡ^DO$t&wl@)d|Lx˲xoJa ޔ:;ƵO0˛||qA="iP~ !腧֨f8 5X䀁e,[fRA`AM$ 00|[1`@mzf҄/ZP\nQSᬁI:gcʌ>L"|]WzcZq;@.J iEG^fw0 O#Hӷ'C ҝQzq H>7b/O)u/|zb/.+,8JA# AA`,κD`.:NZ p4ƏA3`vT@ W(AC«dl' -< (ń-̠ L,Ϥ1XtNx+>Lʣ2|DJ4q̇\ ӱ̅BFK>]/\# ^IiMlTcl#D-!=kogp) n"RX*u6R 6>529$*IqSu[VNu@VSVMn^7OE3`N}s_#y@=ce1`/>Ad> RY$3L%K^"M\fp+eH2ol<[٫9UW;׊h$-H\; &2~ܑI2Ũ Z꿙޸oJ Mix}LyH)BhV 2͉Rg^1U_֭<乹"yl˶B(ImЊ0!Ÿe\τbR#H~k[;4FWZaًr'c=u자V A&N]P*r@l7DF#ތ- 3whsdIN*i$,tJ~x@^B=mIm' OJ )Wor/Pgu)I"2E2|$%PnԢZ+ o\LGy қEupa^wByA\S6x_#7lxRA6HaC5Fϝ-uF`@a!$ ̖-왌I栩 $4SES+@F )dPt%@㹢YjN}@I#}t,pI`KHQNE1QnGAR%5IQR-uKaSΔ5h66) ?5(TYiGu)xBpF1֜L#ㄢX4 "bRg2T8_d!&]sV{,JwEuw2c'iSn0P-nv"pĐGTE,XM]Qָ/4G0d o1RKEuΒ`Ѷ=DZBK@"h;ZEo`qA5o!l&y1%4F }Eh{^n0e=DA1r~!lrj*,ʄ|=P .QCµ3};ī><'ƒIc6 țfXA)~1>/S8Rd,>(j$ɿgCf, M4޶Ŕ̹/a"ˆZF6Zͬl`hŬv4!(,D!,l cehlp,ixk߽pHȤe-dZuӫvr)Lz1n|N~3 #Q POOJI D C B ΤC# H*\ȰÇ#JHŋ3jȱǏ CC\BS2H-KcP`2 +  =NCYZ Sԭ\ x%*Y ĺ\뒂Zk#!(,l D@]lp,tmHGpH,a HШt2@za5@8n4{zkj;nVwYg%¦ÓƖf_%]TSQFP H*\ȰÇ#JHŋ3jȱiM4@Ο ;JGN@#B+QP%LgL4ѐu $T`E0ǃOE}I dˆ!, 7@pH,Ȥrl:ШtJZجvzxL.zn|N~y\SPZ&''M',N˞+%*&U$M*KѷVJ*J,&'H\in ǰÇ(NM@A"Mls7d?D@!ĤB@ F'k܉+ Sf4ӧP;MPG4+ȭFm%q~Ԙغ%צ;5t #gvͻ+[ݢJFZrlJh' AVh,w\^ְwz`ĸs6]°r8am%jۜ{ $Qq0&b@iv_ϾV;Dy ]˩7K. _vbETYMĠJaLjM `NvDӈT,G'G+ѫm@-Qmv=tr MpW #H#) (\m`0-6p~+ X:Z++i˱HڐL6m5'EZpEVl1č4\*l+W@h|iveRvF47gu+G (B8b &N]ōGav[6' emSmݔmz߀/|D'^P83G.Wngw砇.褗n騧밿׾y츻nw.|'p;G/=>gDks槯;篿H@oL: |'R 3^a  >C(\SB0 cHC ΰ8L sC@H H4D-PH*ZX̢.z` H2hL6p# !:x̣x1 IBL"F:򑐌$'EY򒘄%7Nz (GIRL*WV򕰌,gIZ̥.w^ 0IbL2f:Ќ4IjZ̦6nz 8IrL:v~ @JЂB!I, S@pH,Ȥrl:ШtJZجvzxL.zn|N~|qUi}S~{+'&WQ M  ai Dџ#S `pG÷>xt/U@`I,lCm또 t#zZO )d;> DE5AV,dۅۂ>)ͬNK/GA\:-'#o0l~ M1TlAJK"A'Y|n:1\0خ?M]&:sVj O14fl03}0CL&sMl=J"۫ 7#6+,KhU5DHO2t ӵ/걱5JL%J5fjx>] ;L(OB+g['V4-d0L2n{s?9?DU/Q #N >8΋9,\w+#'XOwA_g%mq\s\ Ҁ|5oeG)'-8&LAe F|qwzeˏ f X@ՠ"# ]´X-p2Qh d*!Ԁ&1mhS(YG~ڬULvVSLxQ׎/SA패Poo1SL Kd=H΍\7p7 ;BIWI5ڤ*đSn@N.M+w BݬeDA`xiA\W MZ{ ~p| 4+Pr[ kaunwY/N_ڈBfZtm ! ̀ cZK.dj֗L% 'veĄ=k,k3LcR'^Cg i#ٻzưTy4!jYŘPF̜E2C&,YUT\+Jʟi%4hOzqg g0s?5˾FnXCwDŻ3LMc[m\v"m M:ոٌ| H+J\el+<2<u[nׅkgЇg+WNы{{][DSd\J넶TݞW^̥"EtyV3 e#/h+TMf[!Y&Iv&Ia~[%Y!}@Tgͅl!42(PWֵhcN}IA@T'<Qg~Qrm5BZ8$W]6qWB6~GG@r'ZG_V9|~3Ddw(( h%(\Z0(?ʦwAr!HDSCv$p qN(gIXEfW`ag@!^Ni3F}Y$SYzzR-5u-X:?mL7"&,ٶlwkGjiy_[<,"rl&@I931UCM 際0,zOp9+AÑ9V nhGa7 .)F6f<7̩(ꜺF~y ~Fv)8?4Z@ oEoʑ g5[(75*'IPwu[^j: ;p8nU$h,M w+ I: ];bj-{"Fv+6 r '"˰DK9{epE0rWHmS*0Vz _ɵl4Ew^~뺾>^~Ȟʾ왰>헽~풽ؾ킭5=Myɯ2%%p"$_|^$f zrz+cF?/g<dcvL^! 9 |t"4~u/_:3J>iv"%IU8 in LiD(jq.hv&YEc#75?P jW=f?yKIxNr?#!#z#s Cl/cZ,{ؑ?_?_?h߫?KZO~%Q%*!)Q aolΡ>۟@X–>@F~%C6*O\voT&~<.q뢎@qjK/;`c4/Gߊ&ζsv5~ FE]NS\K+aFK, ƔNyH90 0"Hlzϭ7q?*=s-ٲڻP$zSʿb` FsDBxJo Ru }D{} Cu} IW EvOu~C|h|QuBuEVI`pCG WMBCHŽ kE qS"P+"a vj^G.K/*30[#V19$gA(@2V搒 E+&!{ ѐ@iʔD(2^ K&v x c@y-!)v;op,iEt!VsL˘3k̹ϠCMӨS^ͺG&;}8ڵc{Rg K/P z ׬_0޵3YL)nR%1سx'N_? muS} 6TI Ō. r)ׄ NaK"}<NZ  V! +83GyeUY&GD%0 GBQDYMB=s!ZU4.BWXz qIBxc. 0gPL0S*c&A\gtK[({(1^:a#M"yr%0U'7Y ?f!֖y[ ]z8oE ݅fXqB1rQ<'ћV".JXZΕ7cآCPѶ2FC1Y\yVu4rhXf\v}/£ym]ƅ8d>O\4/_jΈwuK |) QIwS'qZF*餔奘zQl${F_ YHiS.!"(uA:ι䝅"H; ~ ;y)$2k6쳩d*uv0ݎ0ݚjsN`:x7L.kĦ*^"~ , ")߭1©p`>O4~W9Ŝnjll8Ck!.8ՆJЯvoIrP3*hҙu;9m6h25 t\w\gL62ᵝP8&De|Fh(ڐG.fkWԉ`c"1ocU_W!PE'~56b\_qF9z4{"yc幝3AV煟>WدD,[t}Mf̑'6X҅[3olYЭJgȞ Bd*zOB' JB_@OQzWs]cM[M:r07!8!)ÂHuQ'4!_)"ʼnO,JX̢.FM'1X vYHfx@TG*魏 $HB!  Q(F:򑐌$'IJZ̤&7Nz (GIRL*WV2+,cZ򖸜,w \ ffxKaLf89Ke:Ќ&))KiZfIKmz<&7{rgvs|qx}-[?Oe@І:t; }D'JѓD4m̨F7юz@JҒ(MJ Ҋ0FKcJӚ43NwS+䴧@ Pӡ6-*RT*P*@*ժZUTJծz`QVJֲ~sfMZֵl+\JUʵxk(׾|`KMb:d'Kʢ ͬf7z hGKҚMjWDebKͭnw pK;rzmt˅Zͮvz xKMݯz|Kͯ~LN;'L [ΰ7{ GL(NW0gL8αw@L"HN&;PL*[Xβ.{`L2hN6pL:xγ>πMB !V,  @pH,Ȥrl:ШtJZجvr8-zn ~}[cHQmu Nf//V2.TbG `Q cjcnD433:Z/66"Q̬tE`U Ksp {;5=K83̧w791jSC@$opQq,,@N c82N m0rYfÃEv%RLE) &9נ`ٴhdcG%4h"uP\# j0C |VG2 [&X]6߆-4 d'YAd{=#ؚyeDi*$!b&2Ǝ^5xbμaeʣ :-!n@ڶp"JL#Qi ꜧ_V;N):k!CDГLCj& ͡,E@i"\BB-BQD `Z1 Z ԰1<N5,ƴ!Him4# s #G1&Xc, clCNـ g)IÆS`eJe%)4Acv(xg~QD}U~s$vcȅ!q9\`@6zc84!vAVG !iL%֠cB£;3Sh  ?,3b"qcƶ>STo-h>"& \3T:9~'p* (BMC'`W&E WLf,G𩑭z z rfb;A MaBo!NiehiDw7)e iʶkGx#dIN2H;`R2aTh췹3Mx*rXrٍZXz;+ cE𝳪TkuHxDA 7(ik6*<1y{uFs$ABbY_.+Xv]$ Yzu [_Qfx+T*L<iӝj)%G$^5'qlXh"Qz= 6IR<9CmB :ҸHԪvK"WVt^t#"SQ0h YVATA%.12t[;a״Bayl'!,`_$F|)J)}@T?Ԁujd]瞠J)=NIGWBbW +М9~l;,\=R)ajX, 2&MHP'2@M=]4Mt)jF5K]Ft 2h q!<$O$R97TpjT`db+MUU{oʢ h JUTZՠG$!`fuc5 10 af""@4X˳(eq孮q7!R$5v3.L϶IW6af+ @kU>Ƅo{'T m0 tX )cQЙr@D92Vb);ڐ4"! fa4*uh(ɯ|iɡ!NZ$FBɧ?de<=ߤ@dR=~=*\l_CL`<w4q0X UqC9!]Qh_eSVivj"d!rP=/ej&3HృC48]ELB&z>͙Pvau(<7Y;Fju13,[{i6pxz*^Kj9m > b\b^x+[;aHwXnLuL$XybZQnq<m2g7Cz3+/(O02cN\,wO@뀕 !d, @@pH,Ȥrl:ШtJZجvzxL.zn|N ~MƭLͼ֦^ޤ-.#筁QLȰ@ xH"{&Z=cI"E<ɲ帕.cʤs͛jӖ@CѣH*]ʴӧPJJիXjʵׯ`ÊKKhӢ-˶۷R= ݻm˷߫zLi +^8 bB#Kn˘3c@ϠpL)ΡS֌zׄGM{ڸs?j o 68ȭͼy!ɣKgܹ{cn];ﹽoOϾVϟ(& 6F\;questionary-2.0.1/docs/images/password.gif000066400000000000000000000425321447660764300206520ustar00rootroot00000000000000GIF89aG{{|++,[\\789?@Accd"#$KKL345WWXkllGGHsst$# թ:0Q$·2p+ԉNn})A T?5! դ'06Ij>`C@ M|ϡ`%\~q&%J{>{rO_&=7Pp5yTz$v4Y%ZFЅruh@5[ \1CL^I(pE1T(n21,b%7['㎾ȖGޱcpB8#xj-71ELH$g(% 晎٣eCԃjĦY&Eb畝J]`s5RK%'N!^FPi>C(j) " ` e\tԎ'"H a *&r>iNw$)p1$m,ND#p_Z5h Qsim22Sa'35h:] mB Ko3OtMBt0 d a _#14ު-YkhȬlKꬑKl@6۲3#2pG*bfk0[~Y-BugUuUHx zBۂ+nΉ}VY}%qM '(iRޕe (GRTGԋiZk9l)yI,ͿRI(n$2GP,$HYՖ$MG]v"'6 lwr=VxƖԆC)ԧ 0b ick r!'ܔ%fɤT)uEz!5M_]eW0Jm!ox) 㝄7[ZO!iDGk%+sWi6i H<Ϧ?i~g%v[MVu=m,Gbh^4`$2˴Xuj0e` 5Y@.t\ ˆqX̍MkHDjVY#IȚLQU#lH$0!V%ڲ~v8)itb%m5^_g֎AN#aHR)Rr Q_2Q3`JXb?"EC; "|bV br0@q-39ӈ'2,Xg1(18]^Q52"Sz<"$2pkL즳Z Ah1{h/BJn /"(&l?vZfDήHPlJ"Aq"""܎sE$jc$+SfC $۝߽p4 U9|靐x;gt'EZ \N8o.dʡ?. LV0GcNg9w`ЇNtHO҇ !,   dihlp,tmx|pH,Ȥrl:ШtJZجv˵"p-30z &83 R (o*vwz}(P u'nx"ek~#g|5 #&# '%ѻ>1 $ͨ" # ڹ62.;Yh?}L5 ƛ0W/j'^CB DƊ#@n1_N4Lٝf\$tPGP4Ңv<{CUd`*UX°BN vP9 7@(ly-A{5vQXANTXTġ6RiLl:72_ :7 Б rTvl ɥQhocuz b˚{,.+ nU\ k{]YAg.?,*~sx* <`qn+2;֕oȴ#x)N2w;30Nff;$0CAD]%t# 9XF4CKD 5+\ )}`\%_h!Ë{8 80@y􌒍krk]Av1n9؁p4?(d: ʒ \3e2`\hʞ)ȥ\CkPv@I)3':͓|e@(/|>#$΄Xea@tʆX=sǺDYR-0P/&t\0Bn%Ic|GpKHK7dYNkAE/B@RJTv1=u,DӃoVRf[ÖC@dEE+xQӁQ$"!cG3ҤG(x48hج ӓuV|%))*,UGH5,}l*-sAa\Qe=Cfn=`x!x&S`Gu:F]#߱XSD&@V ۩& k`%"<:JV"Gg-! 6-55G=z"lGsVjd#rJ4% r8`ߜ {}rW*ܦ C˵t;t (PH[KEb+wk4 p6)߀NaPgk-`+tI/$|rcyO@src^lK nS#Xskm3-VH.`w*x5AN2@D--2.P)@ocsg,Έ$oeߒ7.%~'܆ܤ2I /`uvJ|-I1׻eH9PY! b5h ND $6t&W5u@R!!cV{,NZf>I29Ljz̧>Pϑtqzc 0j\ nYhqI4Xnf3s 40Z\Ņb "ߥss=Cj$PHZi D *|dnjHMR@ E7N vS+ǁr:XDC%t#0'=M@Q-ʋJٺ,Y%51YV?S`S.&W*cC0{d;(΄%Ih8y5N+jI@DUKͭnM ,4-+EYXPp!V}nL$j@(F*tgXq|R+ެbʬkB4 kw3Uק?~9P@RISߩRS^('A ~W3o.,;] iHj eYAmo,  lWBa,uDA_κ2&^kuϕ>*LfO. /`Ьiflv>3h}=h>6tF7:xv4~}2[.JM]y1Q YRZ$n R: cfMԸARW BQ XNYNrͯP U վv)nNjMrNvMzη~N7C;/\[ϸƕ@n G(O#n09[.8s瞦8ЇN ;?ҶqNԧSX˭{:N\fOnp.;[~kO;u6<'Oqϼ!{pz!, ,/ pH,Ȥrl:ШtJZ΁vbxL.ŮZn𸼼V~?w|nU~\C$0eZ qZ`[Q^BP GXKDG] PlC0N lW ֺZJ՚G$OE_YzlTGY X `!-1 )g!(B$G#.J@Get,)tVDӢLaŚI\3OENB^ {&5@v dU=^fB ؚWBJ BH"cG+ͼ vlb7A=R2^Q-xr\\q^H*{B' `6@^l+8e]%Ɇ8Z|wp!\{+o{[zH-%I V xJ6@rد|O]# 6uSs02Q`zGn8XyQ[8U7حC9Jd]_1B{ mq@^͒ ]1 }HExVS)"hIXr]-cL"b0']5U+[ H-Wm*Z2^2v%KE:e )`E<*7hW\aQ3 0`b֋)JéSHU%AGn JGD#)%s%mޓhB%K$[%a6&8p462-[!!,;, ` $9h쨾g¯*kH7<* hY!*PQ%оBӊAPbq K_8%K%[%a6&4p623E!! ,I, ` 8hg¯\jII32[\62O!! ,V, ` 8hg¯\jiP3&.aaHC>N_Bdq"UPP4lE1FietJ07JDO$=659uZp62E!!.,c, ` 8hg¯\jiP3&.aaHC>N_Bdq"UPP4lE1FietJ07JDO$=659uZp62E!! ,p, ` 8hg¯\jiP3&.aaHC>N_Bdq"UPP4lE1FietJ07JDO$=659uZp62E!! ,}, ` 8hh°\/kH7;<* hY!*PQ%ŀBӊAPbq K_>%K$[%a6&8p462-[!! ,, ` $9h쨾g¯*kH7<* hY!*PQ%оBӊAPbq K_8%K%[%a6&4p623E!!',, ` 8hg¯\jiP3&.aaHC>N_Bdq"UPP4lE1FietJ07JDO$=659uZp62E!!~0, ,`pH,Ȥrl:ШtJZجvzxL.zn|N~V0M͸K H*$6pÇ#J!ŋ3jŎ CI䤏&S\ɲ<.cʜI&26sɳg(> Jt%ТH*](ӧPJMtիX.ׯ`_q KٳƢ]˶mTnʝ.ݻx󎴫߿! LÈ+N BLrŘ3k̹ϠCMӨS^ͺװc˞M۸sͻ n!Fx! ģKN:#|Ё>F8΅sBcO/~no^B}f6^ 0% aIhANDv!auHl'RȀ08ٰw gaئA`1AZ`xAhēZ2""eCp9@(0dk`+A$hdc@S(#6޸p {\ ELYbmti1J)]*AjnzסYoɇen:!Ewn%] ڐô9@>PBz1:H' {ELRgjF"' AD `ʁ/ i_a!G<\5d\C%X1 E aeJPnCT&T,R+AA=WKDvop3hUL)s4$J6@m2"? z$5N FCdS6)L16q=^2@oҁOKF[EZԥfIoYYAʧS 6\ǵd&O2ys2#<`2d.RzJQ.V0JC]–(xS9;)MjU3G@5HH67%uhpJJ}럄W'8(+vIS8@)5%o&ʄ%g΃T8ƕ b\{ p(Aְo^;̞vL,0XEg4b#}%[7+tmh-Q,ش©BP>Ul8-K},'uF,L8QU"qU de྾сWU v1VkW7+SC}EBi1i&f,RMmlY* Ϟ+&몆I!e)l;Vn׵mkYaC|Ld֨&ԣZ|LJ Y3'8$._xN2A>a+f"C [;dWt4/hEB;<_s]@ 3shBMc&5 Ɩgy%>.8 MlRhOw]d1cB}}B`ZÞ=kuiҕ\iˁ$ zF ~6F]E; ؉F7@>а8EBJ4{HX 7@>>&u=mbO!u #JwU6{\OpηO (%w`):ͶTnu@L{O`sXVRXcE;"r+yeq|NuoIv|??gi UP5ޮљ7Z7O]5qKȡs *CWv**{^"X#W'*ۜgtC\Q.O(h?%hcHORY-)yѳ"C}fQ+W{BF#zAW`4gw^4P\LWemF*dBO/sGrve{H;Ct>CHI@?&{B7ff_vtK-'#kL.qmF?k&y'UsDlug~gcTPqBVVvȧ|QU'ncȁ|ts&_IVeF?bpm Q  g?^WIB}z_rq>XqwX|>c2rXS}dGS)X\Lf&A8Z2.JJ4HWA8ZbƊjN~DFTHt}G@LFH(y4T7XKO(\;sHw4VBX#%Pd%ሆ%xA gs^?U(_/VPIg@`yUX`^5ܔ.^sdB p9Ƒ2"Y.b9!)IX[;#xa3TgR}s" PИ_u+BIGy2ƒ(y%BbS[- o/9tBq6Mғv}lgJ`5I 30Df4X92BYUɒF^%#eyvh2O@ Qs0F̃& 6X3i sr#KI#[ {b>?9I%1 8IwsGXGf8}w6R@(M7 5k[QF􉱉89Lpic睴61tY™ B8qHv.duequ҈}8j@XjDmZ7sQJ/מxXR剡kT sɠ;M09aS6Ѐ4_CRh)Q@G)=F75hY&T\.L"GqE DYzY2"SKC2<@ŕBsIEe;ɒ*)uO57^nҦ9x]f`ǟO7GV+hbL7Z@9R[*#iDbCS%G%]ʃ9 JyE)X%0f{:::_AxMcU06S1It40Y`Q%a:eq0^ 60z4­jh*8J7ad%ﱋ<Ψz6H3zA 8:6P% Xf8Jf8㱍`1*K MO!#zIM@92hNTA?FjK*[%:zVPN+iTLwBM J$iW;t[v{xz|۷~;[{۸{F6 [۷#۹VC鹢;KL\yQ_`5;&; `*$@»nMR'p*&+ `op۽WpJ*kvX}ڂѫ*'%-@ Loˬ,i5۾Iۿi(d0+@ !к$|R7 nl9JePUZL)ԫ_'<>\5J\NLRF58jF_S.g|C:r#}S3ҧ9aX  ) #2,:l;)px<=+6VK]y 5})8~7c :T)@)a6[T\3e(ʮaనOFKļ&¬gx!JA*Ut%ʧJGDɧgf` 9܌:D|Osܝ,44K4,+\ElK0kʭ|7@0 Еs(}GUPH,<+:O5N|P9ӤO|t"kZ`BD0Ca' ]yY}^ .L3['fm 3R <3q|$E" –Ջ%)]g0\nm$0csCB%(@wudgHl _[0E{@  CPyln}K@L,lO-<3ܬ[mԛM-Ҍʅ  H- GsUKlK܅vyKx_< %>cĬI' >5Ȃ<-͡:I`GN!-xN3s~*rO,+rL3C,|ݲ i`˓# "Xe|_\&_bUfuB/u꟒3V t^np1SJJD,큮!>2؝.-ߡ^N֫~-}^j]OzWz2槉qg^/ÈN._"Չ<:~}~3>kLM)n-+E͆D~A 3}FLLJe-Iiȝ;~ofm(`Z-.π%U]yO=/Nn*N1/:ۂB0GػDGcFN2#86]?wC:oFZ9]ZHmoV)(.?ኯ.kI2`+A/BSGNEEft|߽_ 4_ْa۔ $G ZkOh@:Tp TFd-)RHQ kru%Y(v¤b? pj )c 鄣nό01`eŤ CsIS#EEt飄+eάUAuT3++vSrV8RCsIDq@z;[{<\|=Az+#~^Z ".Ho%Ib0߿ 1K&yDL4Ba?' (NhivQI M< *NEa`Ov PV!ܾxٶun\sfAZC3-`IE^e(Âj` Hm=,DZM51YI%f(t9")S䙹Gvvw M .[SKjSui:k)*VCV ~|קnX]檻ob8 %qcz 'B GogBCm*Kqڳ]|eP7, zlRNF%J++8RL.Jf딣(Ҁṣt9T㌔XeZm; ^}`6]=d]f2hjkݖn֮l\t!d, l@0I8ͻ`(dihlp,tmx|pH,Ȥrl:ШtJZجvbxL.zn#-|N<\|cTnQÖaw_ճym? H*\ȰÇ#JHŋ3jȱȣǏ C0ɓ&G\ɲ 0KIM1aɳǜ(} J!ГE*]hJPJeTԫXr:@ׯ`p KԱfӪv۷3KܺxV߅| 00ÈmN̸1ժ#K^x*cΠ9MzҨNzְM{ڸ;N<ȓOg;questionary-2.0.1/docs/images/path.gif000066400000000000000000002646131447660764300177520ustar00rootroot00000000000000GIF89aG ļDDDKLLWXYtttccdkll{||WWX+++##$333Û7ը:{X mhZg^T3yf.K2U&F7`?$xgČ;&ɔF9fy7{ z[ФK6լ_n ;lOg۾;ݼ{ <ċ?<̛;=ԫ[=ܻ{=˛?>ۻ?ۿ?`H`` .`>aNHa^ana~b"Hb&b*b.c2Hc6ވc:c>dBIdFdJ.dN> eRNIeV^eZne^~ fbIfffjfn grIgvމgzg~ hJhh.h> iNJi^ini~ jJjjj kJkފkk lKll.l> mNKm^mnm~ nKn枋nn oKoދoo pLpp /p? qOLq_qoq r"Lr&r*r. s2Ls6ߌs:s> tBMtFtJ/tN? uROMuV_uZou^ vbMvfvjvn wrMwvߍwzw~ xNxx/x?yONy?_yoyz袏Nz馟zꪯz뮿{N{ߎ{{|O|^! ,P!5,  @pH,Ȥrl:ШtJZجv- pK.znCT~s~Mw]^b i22igT20~)((-e,)g.11e+P,{ch)3J-(f1fO0_g3K/V 1LVCKLGe)㗦[|T>l pA-k l:65B!DA7 f SK4`@QI<䙰;#YAQ(*2b̫ J-:$6VV"6ɚ ^Fm{W٦ɖ-<$4+74%0j2Y:']He3 FWԍW֍5+"\겘M `ZMƐ瀟P. >]It uc,9)sl\sp(& ! +bdj-v1R' Xznфj&N+>acQ-BlGVՒ]7ьMԜu5"j iA7i6H|祗ϡňě(dIg&i9Px'YU{y%pJR(qE ըUDD (d:^VE ^uCiyFzW ' !%ڪ$}ң$" L[dLn!g8Ċ'q"ag2QvgJJ7b'(pu84*d]u*qW/←FXGA%>r1nM6p6(&116jF+Mql]/v6[Xd+]Rs @BLozC \oݵ =m*LyD}T`eAYhLovcӊ7wBki 9#1 t4}u&>/nuI^|+bYC$>YD٤>粍2& g7md,[_i-{#aw㬮UA z={]i/~YǠB0G]5<_~wW7& 7ABfQdBe,O 8G=vn sm ڐAƁoFp^o2|1ъNxǫ)t2r ;R ge Du](KF8aZAR Y5p`,a@5I}PGv]s\$B'JJ^W\$lHW V n=9&xo}rꡱC˖QX$l' 6֒!,|gZYgFVbB/z؇"!Br8| DtT2!-+=3KvM!zK/"+j/~Na$A!!, 6 @pH,Ȥrl:ШtJZجvzxL.zn|N~coipX[ i g o T ZrHdep_pHW n ڇfnqFSGblbvܓFg <8$9pqF!B IrG$dHOuă,#9YqI㐇vDB($} BЏ&թׁp KĢԣ0A%+W LRQy $˵1-H9'F^F ,EOz)Ȍ/$92QX_;yIzY{VjQL^~agQŇ_<\V\Kda{B.%s OzlM[-G5q uS~qsg]G&] }dXdvȜM8cG{%5OE#q\Y1.1{B z0W@=/iH!at5T1OxINGeT@唯١DqӐ )2ǎF'E%hx.R`长}BIh ̈́4e-V*hH;g'kcA: @XcǛxy^#"wxL<S =nzBH rH-֚7NnI3 JDWn9ܾ-AF~hAX!P<~A\BBK0(Mr*`%qIf]tNW絸܉5hʛ%uÝʄndhK*PW"sKJ2=$; m^AM_o_zOe,kßm p$+ y*~c+0yNJf> \ÞN@!Tn{w;V:)@(`kE_ubB΍IzљB!-,GN)j#ֵZܣnH/qS.YsTA H`2Bz a x$т xg%Ho-,vɡVTV* IKzË0$9)%[P4 xJdL}8#<78qKbfXT@տHҌKT/}Y8= YxR2sDBLP(/ÞM$ ZEIaaeE+m5`LAj29ǘuvTd,ɳH^I<4N33DAZPY'/ U0GHQQ;<)KS|&$z %Sj*M>TG0 1rdk]J&IYfR%(]PV>3?2APɭ/!zx5!>eIo E !%4 "Ɠ6 c-JS$PMd4k)\ޭ*e(T@tq[f3XrB']T&B2K Qnm"FX m?ܢ7S(̗ό4P Hz̕ &uQ$luu#Y9I6:+;I{XɂH1D}ġ4'ҀŻ5B*]^d=hZs.%3E:.c-Ou&1{ BGEqVdeu>*7VPș듑 J3֡1ggqqn($+eXdg .i_PV>YLe+V$@`Jzi pRH˜g@YZsUuec ݉$]H>U~r7a5 v +g6f:vnn -Fx=y.پÀ/Uʾ`pZ܈wwدDneu(@7Hpz@NH#3ol/@A9ZnkQ&8Cc/Mʕ 4 ׆شˎ [_P _Л;˾B;ճO;񐏼'O[ϼ7{}!, @@pH,Ȥrl:ШtJZجvzxL.z θ|N~ql\d{[ųijhЏVҨaf g  QZC=G"DݹbkgB=G }IQA$;%ɓ(Qh"q2 4 ஑u-Q@"!GJɴӧVf樊 ٓ Hh# ʶ۷uXpB 6/"s'%RWeʥ+C6"YqC@؁Ŋ1;suIwEwHlZMҵ:bz.o։&*jH.<}` ԝqњ9v`=2ĭnߜd8'Ȧ_hҭ'wz%&}]VAf66y2ׅfh1{%0QNC7"dBzIxT5Q^'ASjh#DHx([c/(*~QF&#NPB`dd(b=weYffוA"aNdckvID~⑀i(MD(י\D#SPP2GcUmDz_XY!5NZ"դ8Z<뫗ZJ*_l*>* lB"A+-p8JZ('Otzx@F˦.)Ok.B+eڋ^ Ygjn즥:F{eKj('D7T y![^$TN0UB+W mD-4-W\w 61_@u Ťr=lq8iDU:%.OM3ZPܼLx–ړ]wE_-7!"X޲^~]@;^H5(Ow>>^:{D]@sOB,k޹˽k pgԌJʽ<1<[+z֭O~{5NDZ[ӷa8ߴ1mHR؜W#xn  _U?Og*'m}Ss󞠻qp,wd1t9ם@8 KB"=bP. HHE"F$b,JnCafdnWOSWp@ o\WPDHqh"I"H6RHC իx9xRr2 S74%/g'Hd91J53)]$৭8i_L 3[))>.Ѷ魁Ŕ6UD%<g)2F@طF$nR%²ɎYOJЀq|,LBUN"@ъRtt(vǾ鲙k&(R49͒>|-fA=VVNub)"`Z5CM4 y6hʄFPNAWb%_tMfHtb[jD@)0b\ gxp,YHmѨJ) k@-ŪX6AJp;V#%^o_8"2 fMy]FĢ:X U-K6`+QIrcLu#`m¾m%l˲6&Y}W@(\Xa< РX޸۟v\oMY52k %=ٽi gRh%{/S>}ٱ;_&ů;au{'.j{б)$^8i eھj-:#qRopPqMݪ7C/_ 0^R+y0\`@~qQtzOǐlQ8`x$䏅u* XվO+ (6Pc s/4 Vv/1gZ h HyjRx6aS @]@&H(ȁ'.{284X6x8:<؃>@B8DXFxHJL؄NPR8TXVx91\؅^`(WZaXfxhL1렆npr l(sxxznP|X~uxbPȈxX8pQXx؉؊ Xx0؋8X8 ʸ،҈8x՘؍u8Hlx蘎긎9؎8x&؏Yy58 ِ 9Yk9Icؑ "9&y(%,ْ. +294 98: A!., , @pH,Ȥrl:ШtJZجvzx,zn5y~s~_dDBNR ~ EFfcC&''&LO az dĶGb^eBO RHYjM bfUutDE &m!@v6@ AIw]D, ¬KC=&T0a*ɇCP gf0An\CNc>Ք*!%Jz&sUЭ6:CĢ2MV- a}E0 )MlL&QEH2K1#BU.@J(c#)`* +&Hn\'2R'2A.GbNܰ -VRqq-G`Q0"2nE35B$LED\6ȩ{orJ˹1f?NREN؄(ӣ/HJnc嫞3F-xBl8ͱ< 8M 0 I(J jVXZ C!mr7d/(>:rm}FE@/x$6*r hD[L+4ddb$t̨]([q0 !Ȣ.#A? M8.v}N3,jaE@stMM624+[[@K N?AʰXj$Zd\bSO`ZK%u2ǧ2h4F (X t\xy9(Q-#d١vʐgs/V Ě'RS7EʅF2Ф.'ݤ+ ͮL$+AkqWJf⪯cd?{.`k0%ei2RX vL$ )(pR* \5]mTQ?ӖإgCݦZo"6αwc2@=!HD&;K~Le$!,hL`!dihlp,tmx|pH,Ȥrl:ШtJZجvzxL.zn|N~V / EIrAb=8X-BX7f@ Y Ʋ# " 1߭% F , B1ԩ֬ĕث) !+D5B,XP5 qȐH YCGa.PdʎGO ՠԲCH1aU$Q+kx.fX@ɌWHFG[]-+jcE*! v -ZІެ+xt6vpU\Sδva@ B"0 /K=n,^w B1B:W~:8mIhlV{4@4}xa`C`1]^`$87XT{`mgmQghTTʤfE}6`qV ]iB@oT57a]w($V[v7 Wq+<BneZ*e\ YYx=hUb=>+ƚ@Q dqK~$5%m&XVIc*7.9967יzŠ'쥔X~"&CYa ]9z͉:ʼnUP>et@F =8KD*Yxj9ԜΉD͕ǰk V:^jǵ۶Ug`Ku& -d!9 a0 mh5T$18:IwK$8+C?MUd#똶3$ kGv"g(X箧Vn {&#΀֚# K5%/lLO0$͔݇ j ܄o=7$EBzd_g[g45|ں7E9cuBcm}6zCPS3DZz im]C⒄wOQk+q_xsV9MpکAKn T_1#1A"#R 5,܃:&6jOn8!=PDvĻ E`.'B~+ ]v~tލ+exyѕ)hC |*#7EL, % جoӫ>5 EًOChtПw6@e]%p\ b7D~5M.i!Iܰy(: fP~yR{!<@DfaD8R 5Df?"G3ī}(9hl ]pN]/{׻^<eJbg0`r<3v}rs${|a3QǢ\m`(p.< |L0k]kV(ނ^5ƛ0p()C+3>Ï:-*<L йS:d: ml2Q t0ܤh"FTVJе z^t0|\640>+$c¯lłfXyFR2Z DaY=D ̎Ҵ~k-k6o ̊p EzҖ\f5RS6i̬TQYrfCًQ.UV@k\. |L. ySy+ 6desce-Y ;f'ZYY] %2:26fZWTJ助ao q$Wwp&2BOqLQB2L~-|`[`1ϵ F$xzIX/z+HdAI#4A&ygTeTs#ԧƅ}0-h1Γ@st,L/^h[@:_%Q0+`r'F)cN9('/+oZkI 6,C,W&Ph)M98~l>䧐Q aIPt&EmՐM`I#׆2B,B\vR‰Nv8@&c3ٝ@>D.w CݬB>uƼ{ͪ:IN^eL@,M2tCFem~]PkzBX3(" 6! GxGF9z/lCJ(BA P 4ԉ)O|ٓ(A4ۆp8:֊Hr@RC?ԚIEFi<xe`S?V %:ŮvHv:^'g\@s4 O¦G]8P,6<{^kH΅GZd[{$HQ;l~i 2%#Vn~q;?zDzlxwo^oAq@@DPj~eyWnw"%Of4)W$ONSU$%wq[YqW?f7bGm5p +% tF3 !-4N>YOYXN^$ƃp~ 7j1+0uRGH-#Sch_kUK! C-{ 6o9\=T7AsxZF>Y(`nY{(Ϥ?F$ׂ\؅?gq2zWBfЇWA.[E_tn9ƈb!7.ܵ`G~>(s3o$IByC.'iu#Uƈ@sa1nW&jlV!"t taUH#i%yr#P>8qx2rc"9{X 7DGpdL`K6i"R2l0(6:i&Ii'նC@qE =p%NP=ГS3%nqJЀuAEX %bXYfyh#%֏P@R0#icUQz|ٗ~9Yy٘9Yyٙ9Yyٚ9i lY?l~yy g ,+ x5МIF𜭉ci.hfc9,6Lo/![?7M )L?2dҌR{dI!1pfI6; 9JtYaD@1J`6o Y/LV8K1Y5/0|РC nȚXӡ5 Ȣ3Фr)pB $ *P(jVفL D12'ru*͢xAX3 t^Qrl2A K𥢉 rR*'JQR7Gj-Q+=i5Y`$|'2%A"x8Rt%9g)"RZ,yV26+SZ&Th".ɤB**6=2lǐy2(yi*p4z(sB*8+'%+|!խRfQbj{Zasj50xG#8#ʮ+u%* 1ujnuq)69QD_)T4 5?UC(gWV&gRrg15^iz^+{]` 1e@fG:#v~=6+뱢1.E&-M13rmUN`k-0{[^q6I)6Z @s6Rhy@.Ul(0E aEsk}-4GU"D_ԃZrC7h "Ks ?ܙs ./Q;MA[a.ki8ب- [uXXeH{V(!"mWKq%%( HI ~//e^:^S;Hi<\4-5Fs%_+6rc%jYb`j[r˄ɱf%#r{lK5u[F }R5됺\gMrH0%d`Va1@ R4uUDx=k53$ D-V"oP,mK|F\%q~Vq|]{E\1aam;oLuPq#gb1C)F}Th A?Zzj PDqJwS˴-nlC,ȡZPd)6H7w#LgW\lO ֤; LX{ }\a^\\dS%"YZ4s-N >L rؙX=p V^oekeWfZPTOEi VbB[֠P?-P}\*\LM]^.#].-¥*".6\<ÿcP o Mh5[-BѩyW nOmP[hf d@qjthpX]2`~nMT/mΘkt3}&30&d8^1wY;c0zJ/)3%r-*WEww,_ڍg>0=d8p#:%xgvW]\%^ֹ0d OhV(NgC Dt,doOEńl}OqҲ*>~CI܅K4zl+N$8"Mᑈ:pְ&rӄ'1472|`|W+sZ~&C/&j P<\wV|PQt}%8P`n<5?y>br媳/g+xɇɈ(g#uug!C4V9H\|r||cMlD~LRrbg#Dkڲ}}uf&u<{ D>t$p2@>"Gi>-\a>>|Jd4Cѣ^Č.XZּ؁9Ȫk$BTgpu_ TTb@nYxC}\DVK]aT#~L>bb6E脼(gcl2)ߝ1lD&`'q;X^a GSx2.ηD/ nL{Ӹ 7aFZɕdNU@fʲ ٥$ h<"%|BGab@®NvJ"N6 7\(wGp ]Z,DF!P8B&X| dd&':Ǒsw{b['K+ӯP4#Q%P_kr,tDXj q!@MSF;" ,S4 fȖ._po&2aΛ'Ϟ>2(QJidYĕ5B LԪ``pQEp@*Yz $gӎ a.ݺs Fʨ,߾~wG4 ho0eyZ$͞vĸnD֮_Î ۰4X3`[nNپ  ۂu`|".`Ϯ;޿/Ӆ3@m$nl;~>ϯ?X" 2ؠBRXbrء#X'+آ/3X7☣;أ?CYGz^L\fMrsQQV c e;PJΗM caf!n#YUJ21ДA:tVg)=,9L7rgIgIʝH &9Bw@mJL@8{JRf32JJJcZc'V`j'4ܰ쭙FR*l9NW/B2[*N4&2ȲK3 %nfo]H0ZF/8[bK …@0*  r0Gt si̺\9L͙05Y"3A ,UA8D48?3@^ls1 l;ӫburQ{`s]q@6/p8`$;^cAp ;{lv0Nu86ܛ'^t= #3n}K_\74StXk-;F0Bmn' Y"pމs}Fj'1<9%k#H ==,v9p80QWowD"?PaO&0>>6Q1/o:8x()a`2f2^f՛F+yGbB3Ʌ փ8J3Xxg="+Ԯڲ"5+P6O;-aXbt>*pjX\ yocIf=IT v :"M" 1uD?1R(^`ؠ.5+"R| ]+ɀ[F ;c`BL$۷@ӡ`Q֧+ƙ7.4A5_P BRغCcI'؃hLW\])^$br\<5+_ћx,grh&hbLJGx _>\_! S&X7$Dm$eV`Q"#* 1հ,>ӗ-4t)hҏ\zJ[DSTmک-FUO]SdM&ԄMÝ J?A06=z6tT7-) OZԵj< x>p|*i =':A R@[([) bRP,9h,.$nRC\Yh ךbA</ng"A?M݉\l:s ŕ2ӷ4c'`-~/LLӲ91_JBSV_,Tm;ZE[ō@t<^А^I/BEdUV$V ]%Z-,Ld 4ֆT F L1ԥL[+`áXB&6^㡤BjaJDl0[ b ,,i ^ƃZ$QTaX\(h& !M$T`"T6A;ae0Īq5YQܠX1ɱNCtR|&G# gETOՃx^ñ ~G|F}RŒ#xFu( ާ8Xvx]dEI̛Yrh=4N5^VhW0`|ȶE؅]E`Шxd$H* @~ik[5ڶfQHLN$4^pwXh(bREqlG錰Ǎt؜ij j"j*2j:BjJRjZbjjrjz꧂jꨒjꩢj~H`(R(}(g8BȬ'jfoI;6f~jaDgP }J|`bK\*] z*˜lSQmd`÷K^ôD:JAD:k Z髚Gb'=Rz]S62Al<,N/(K1BHɢ!oaq1kWYfe̅ m H@*^ De)le+̅(ڈ^@lBǠ`M&8&Č-ǼM]B9maT|Nlդm@/LC@3tZK<e.tYgcfP<צ@fO9ZtD*l>Y Y 'ތY>:;cщsuս&72qqkw7WL՗0Utn3nc]u'lxEi+J7l|Zi\׍}^MX jjM͇&i yAmJMā;m{Vs`Bׁ~7Kx$Tf7^6tsM98O *oeuc?si͉FOΕ 2&tzTq3 N*EDSq=3z30׹!0#<ІYv=sbg@._XC[7xCXz9zѶCs5Y g!7#? }m)8D }zC{3# /;,!ĠK@R{u7-t3[^~xtGI u1FGBK`R8};N@srXDaZN^qw<|V%c{=5riU!Ṗ6{&V#C!$7 c2ZAAR2VAr֙Rcbv4Z]ݹ"@a2!'K<;aDF+nT!!/o/:?l>& w. $l>4VdÓrQ"hV㊘7m}>Hnۣ2/-eei^?4<Ռi2$AivFLc/;DDe¶~}_;ҶɾHuetSXLrj#`n(1$5txWqedQDe0CZpm\lK%X 6jԞЂ./(D CX &8* g$Oc+6g+ (#pE$G5 8$ 7>O,osl, K5x6 3'n5$eOh(JQsɡ*v o#0| . x^~6RJx`#4C=j0|$& Ch͌#]diʓ(=ѣH*]2T)B&DTXG~+i֯`ÊKvO^[#Tݻx-r@.0p*4Ұǐ#CB7deTLc~xGa@-۸s9)72%NcXX`~# e(g .\:ËKR=XH4o˟OϿ(h& 6F(Vhfv($.!,h,@@pH,Ȥrl:ШtJZجvzExL&zn߀|N~ˀcpj~yMMe{g XãduND  RDcN DQ}C7B[ȰCf@l࡟9 B:bo9vH7>\ɲXM}rb8KRN[Dr :0`ѦFb($Xj:'L"0A IZ">yP/i|_r˷>[)9$ߊbxfaNlaSɚb-@Fn dk1a+ BoHk1ֹ.94 ,oBWlgnU"l(ҥ gQ 4loX<@-DmH'L7tPG-TWmXg\w`-dmNlk-tmxp߀ބn'n7,9+Wnїg䜣9`v蟗y{6 >BǢ.!0=Ss#;テ(G3}i#%N@'s0~H/y3n h ^!l Ofл!҂Bu,`0 qضZ=~E h]tF(Fpg!{g& |{b2H^YTDNiQ172uX> p `b긶;1#cȥQl^~6^G.0,GrRc;]ܣ$ Mi<Ґ4Le'MSVDhL!u|$4fpOz˞ojV.ςhІ24D'JъZͨF7юz HGJlM%MJWD0LgJӚ8ͩNwӞdPԢHMRӡ:LTJժX Uծz5'5Wa.(5Z*ӰAFլlZJ&@!H ʕzMW%v-,NU*cןAv)a%NuV 4d [`0n%-5"pC@.r nQ{:Wln7Ǿ/s \BW)utV lܻԮ=oz{H-^ߘտ6[\-`{8pRݒV iEk!6R;aV+)j4%i|_R@7Vqqd;$yMr}+LV:POvH5qV\G&a PRv_qżS2_x4Kɼ\8)γ!).%hDzxZ5+̌w{b SYu.?Pg^6~=)Ջvuyzf=k<Ω ]f>kfb;8xmj[:bJp-vu𖖻xYA! %,h,@pH,Ȥrl:ШtJZجvzx,.zn|N~r_Hda{d{ XQc% W PefVP˳a~PBNEfzCth6B0\h8QɆeNSHɓu}\?!aB3ІCɳO4Y%G*HC˝?J-h4 ,E ĆY6yvThӪuE l6$v ڿo2ܲCPȼ"gKL\.n8YC] L̦Ew1^ȠI˞CvWV6aMOB^NM׋maf 0t2__Ͼ]J~ڭv.YJh`V~ 63%!Bf( $pqWzh(∞@rS…H,Rʅ-(/NCɍ8<}7DQE&$GPѤ%QV䔇X\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨq꫰*무j뭸뮼+k&6F+Vkfv+k- @ƻk`@``, ￶ /*p/2³ZƸ&įd f,$';$*7 71ϴ>/s+lj,&L LpǺo +6u`rm:dz]us\h}7yr,G 9 Nk8㍧x6/ȕ{˵Nwtw*8:*b z.MjgA+l_(rc@HxR6&Ib(DGk~Ub5@Eok߫އ5eρ'gXkcA :z)]xp@ /+jhrvhD1Fı A@c:V>\Q2x/qdaĄp Yp,@3::kj> IBL"\,$'IJZ̤&7Nz %(IʤERL*WV2e^IZ|,w)\ -yIr ;'wnjf&(i !BImL'GMeZ f IrV"0T hf<+I΁R=CObh:tϒ$BɅ6DIъҲa8Iz$h(_J=ÏZ!HY>2"ȦRJGU,Ryj3K]:Son5S'F9s* lmumTVP- O*֧RU.gYzV=!}$V*ɍR2lMXqժ}8jPIfa ʅvt$!{P35+k+Gú,_ղ`S'opKTgkܨ:],tYhn-K]W5nY{J>]nl+IWe/iޯ.uvK~/p> ;\VWUzŰ/5<`O7XX [IV5L=Z+ L2eadVĄ;09.FDp4jT8gIHe-qH&Iu5aXl҂#ؤX)0C@wՂ> `W}K͂GiÜFpHq}Ubbxj+RA衐FqlD|>*馜 BZÙhjj t@h(y*무j뭸+,/&l1ZТlVkfv+k覫+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmFlp-tmx|߀.n'7G.Wngw砇.褗n騧N7G޲N8uP@`w-.8tP~]A ~AvG;3{o[n|>CAp?^>o?< @mt_0 @$E`/Br{Hx%K S8|+,;CtbWC [=p.b$"ȶ&|XE^ 3pm{d0Ɇikk" r} J~pŴ.LYΒj#!ODPrm0FX"j[&1Lg^i Kٶd0 Lo6rd3Ks ̛ƹM ЂrS.ՇJl"ҟy() T$hCvʏӡ~ܧ)Jv #F3픡)\4$R@=Pa(jeQxP-m`Ps3[Mmj!{KHyi/A-'O+|٪( f rM`Rf/'mgH׸?fH h/D@LjGBV4 RՑV/i,j;C57hMƾt5}&"q]M_Xwڊ&s^.Wm6>/Q)Dnv;',*FOԮvM\fO wL_y>v/}H:FË>pG]~{.uRM'?wǃ⑯V/w<"x}S.΢ m,'7)e7[V:x5sik潱o|__t_q9#m7_m|ebozj5z~ 8fw'_@gtoueh~5<{((#He%_@uz'b.}/j1(gGdѦx(?o7k598e;xJj քYPN8VTut\|ZӅbs`X!%,h,@pH,Ȥrl:ШtJZجvn8콖zn|N~c`eIcH NBbGƂ M dEޫ L ЊkLϋڽ#b]aoE6 in3jxÏԎ\Ȑ_: >9]/hৄoOcw"=o C(6Vj%= Wz" XSb!eu.X1 b1KP0^_D &Έ>!bg| #7mYa )U PR:ʼYCX3A:hn"EHB ex^Q'6y*(YқLrh栌6cLR ^馜v駠B)&j1Rrꪬ’$*(J2뭸Rk$+k&6( PkVv+K.覫k櫯,k'7G, Wl#<w&z;r r,[} LB$P4p4r@# yL!-!l9-DW]Hۼt <`lq 8}-:'\@aǝ/t -Ӟ-*SKAG$uGƾ3#&M^9^mI?z_9YK3{N{ۺӌ9 -Կ'o{*pHc{;g/O˼Kv2nk~_?ɣ7~8DZ+~tڷvG=$)!G8hi!:(^Ѐ[ 6]4UO| ^Vm\c헿Ik|D,6. :pTV9ZNg+,<팺bݷEuir"H=-vp4&D%1xu䢸5N}Z99JDl# 6~&~ @JЂMBІ:D'JъZͨF7юz HGJҒ(MJWҖ0LgJӚ8ͩNwӞh#PJԢHMRTy߲"p>Y#D><ЪC hEUm3 :ZLtUCX U < pZȀb3Ѐsohk ,&6([G(jH Ȗ{+g)Y;hW NF3 V"Kߖ@Vo P l[ X=n:ͺ| eiKb̺,{[A }Cc+1 AiL öqs-MBL >WXgL8αw@L"#d}mfxk.%&};,.X[V߅e>|\,4gcfWSe-Xk>vY2o׿2ٯ~}Uhb ЅۤF48%hIWp>TWsܕ0˗|[˗صjk\:olaX@|yjG/I.imm ,-\ay}]y.v8pvL>f.WNn.;h9:7mp (Vo^v!W,Nm`Xyo|ϗswN) w<,o9wh߹\G|o}jpqz |;z瞣3z a)=X Ov_WMG}O^ǁw p;|K_;?,p`⻮܈ Oۅo_}6fi~zgz%-n}f9{gqWlr7zfxnjk|'t?wk3Gr`_4H1g&jwk-h6}\ lXDjuƒ8w2WJd+SEUׇ̒lXвdGUWi+nsÆ's}6,ibqք0zW|xu+wh\{*8Ri@k j)V8Xx؊,A!%,;,W`@pH,Ȥrl:ШtJZlQzxL.zn|Nkگ[{__unK ^H ~ \hD}Ϙ~Bjx˦ CB sg(#0#1%(GO dIU@hu%v !w"\r !=56%B}( @Ut_OaPNd`RJiĠDZ3cY¶'R+miԲ0&2˸1 nYM2lv 0Immby} IvHQs}y-$I9YzX>سCO'7uX{/f?}#xʉ9.7f֊7ikTP(\kǁ[alv]utm0s߭|ĀNFL'߂7>1# Wngw砇.褗n騧ꬷ.n/ī@_{ PG/=d7`#`Ӈ?޸@kAO~7'g}[o ?pS&8 rx Q. V\p`gZ!Ґ<ԡυӃ3Xݐ+Z3;>,ZD".W'5=`<`Ò<26Ì)> q1gV@|:fq+d P0rd&II)2SɾW {[/J E%+f2Gݰ{.al,b"Xh.fӭ.}ۉÁkv]&8mB !wd{}r3 /quaܶ6CҙMo# O9WkH]SmϺ8kp;w[vEKN3{xV!v%~m :+sрWz>fֲezc3^d#'fE:;{o_J1aG G׶N{k_gY}Ԯ?O\oBE.MZ:\ķt8ćDܒ_G9㘰7 IR(*f9cpvZzFE校yV:Yn~2#Y)Hޞ=Bzrr**'AC2QG͉&D WYFLPc(Fk jyu0ə9㦕LO!TLQSͺL'UJI2k%޲(bf@oiuk5F6Q"$wI,"jݛmd!+]PqYr" '$ M̠*iأ΂#@W'mO,3ImHk5EGmb3-gK"OFIֳ`l&فV tmwb߭|[ǀ#rmK7:.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o7^/o HL:'H Z̠7z @W?dD@2V8_^x@6^8ЀF D%xN`PP qY|@F1K5OYGF:Ic$Ez,2K _8 <8C&5 _B)JK3@* pV^}F(pBU# 0CP0$&,E8%efh@4g8U3$2Jvs}؜a5oIMkq}>7{>V*2ӳb% $@*Ja`d):4"5Ҕe&|G3FZ4dJYEIƲl&4 Jrt)-gZӉ35itIRtv}9%h'P}l*(I6Ӈ X@  }'G9J6w<*TXjTՒa #QgIש泪ĪV΅բkbƽffl+aYn~}ŦETBV~ S`,fPeV44\j ZVU2k5hںVeeRjAd/B{R@c_\nrxHW\A\fM"Gw\2uNrۜ̇N{=ћoWOԹuv'A~]||w ) S;(м(=|B$]3NӝޏIN=2=;U>Vv7Cv$#<z'}7:tW9^}w^"xz ]Ǟ/O{+k߭/z'}lgt~twGrOU}y{GunwgH~8|{ Gݧ|h|npȁ!n n!}%o'Tׁ&uBWp~}zMwg~ߗGr7r+pxr<8~oηwJ'H|#'HBHxO8} )yyqhf({5(wglX}oķzwd}6X>xU@GHPy7h{؉~A(vV8(hz x<(NFg(|WLJ/؆ h'wZ׃Fvg[Xoۨ~x8(ɘxknȨq(؈Ƌ(fz|XB0؅fQgFk$(zs{2w{s''h$H)gwXe H{@88̗z~{g~H'ؑH-)=~H:(oǔ:Y~3){~8ww wv3Ih؏Qo,G'|YdhMאu)o~ǗًY珺wTie7rdGuxɂIqAw8]&huA9)AřvǙAYٜdڹʈ!%,;,@pH,Ȥrl:ШtJZجv*zޒxL.zn|N[R~`{pN_g H aoC ղ пx߫hjDޠ0Wde[ǠA} K'ĠE\P8 &-ɓhm5H %PvQK$]< h-B><:(/#|xeTHuSnDTK: ٶB^p欃l4'Dѝ,^"Dסroԓ.zULF ` U W4&P%|pThKE4G ޅ>6vR!Xs&\mSqgU"t &YK-h7!``mga"F# A^i@ܹ  d+VwA*`CZWemR+uYdݙbvJOf)ds4NY70"ER Fz 41$g,/} 礪 G9@,ŊQwɐ$lM,2%{,$8TL@bs;AmH'L7PG-TWmXg\w`-dmhlp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7 Wogw/o觯/o HL1{S$d p.8E藀 d`3Ⱦ\<-|@ W>{4 r@ DE:|b `)`(Q(1] ɀNoaBIO2| Xl%# @H|c%0^GZp$&I"Sz 13J򅩰 IY0-9Kk,6S-1F3[_3Jr.`.!KZ&3y+RoҬhIPlFtyR\V4@s)@`ф4ٯ{rsigmkHEm*2P;գ.9G| m&Ui>/H_t5bx b JtIr3 lFٓegw,0`L|{'gITK$fHkbޑFk(?wIy׿E`#4 D}H9x'@s'0]mo>?~3io|_Χ~\glvggvWx~w} 7x}}~vsz݇x'wIw}|0}X~%wg\>Hw'$0x}Vb'DŽI(g{G(yNxz:2HWGv O( (x([KswS8#08Wn؉Vȇmlj(^sh8~WnX(xHN|8Xz&Ȋ"gHd׷wh(xrVy8wFqXIX5{!W#xxq؀8pG|X(jpX %ghkzzWɸfhȋrǒb(WxɌ-i(7ihrt/9r'9ryyh1| wk5Yɸ}YD8 YrXܸƖKYM GCHvXzU rx!7+ |iYr7tqIygQ)3YIV:Hir(燆)؈zw҇?9!7]x,y{YtTr^+W(gyiuyى~h⹞u)oٜYYwy}izynZtZn r jn ɠzډJmڡ8A!%,;,@pH,Ȥrl:ШtJZvz`diL.zn|N<φS}|V\Ize[Ez R]dGE  BB B ~H 6iy RlḃBlOy2jܸF>$BF # >ǡ4$C`BQ#afNt1KwQǣH5zd& -0iHpBE$nJJ쯥K, ë g] +֒ٿIUa hT۶֨y%ڷR˘ Nֈ K Œ)`8՝VȠVm8t~*M2܁6zZȮ!=;>{H1K:4͝Sؔ;N|.ZG#̩H{[\]&O7.WHDuC#U1Xj %!M^p(cY MؠХ PWxȌH%PPDkB}sJ![AT"n t։Mr/ydlß?=QDB` vW5-D?D3EiYHju)U9?{XNSyS$*0X ŧ꫰VjjŮ뮼z+K&+6a0VfI`Ɋke +k ,l' 7Wlglס ,Gl(,/L0,8 s6k^7a0]dZўݷ DaNM+9:X|<ܹG^om{S]v\Vc|p|ٸ6wle_xz=K1]Ot঴aomfW_޶K62]bw=t_8=u y.xG@O^}8'^#37"0 GW_>bv퀃[@`]MS=@|_S׽`Oo\;gܰ =O̧}~k_W1ف.#>4$OW}¤ g{G}g.3nf~r~qb0'znAk|}g0z'zZ: X7ye9FgH~ (veWp0ap_Vcz*XOFdR..HGdxwW%0+8jHz1wh%({vt00h.wvmW}e/j8;(v&{}v}0jgp؅l mgw@:va`(bRr$bx0)070u-֋M0D׊xȘ%aHs@6^(ڸ؍8Xx蘎긎؎YA!%,,`@pH,Ȥrl:Ф`JRجvz`u< z&|NǺ~7b0 vwW}tl }BDEDC BCQ|BDڷD BP OeyB ڜC 묝С}@ ;S"AJc/ kPIWǂ6} I6 15ZpŠ sT"5$Kj{nvHJyP$W| M:'%zP])p b.jĔR>ձ,ْNZqЙz[F%Se-MmrЕKŬXz y4*6gyW]7]r(>+5Jwvm5Ƃ @"i\oVz)"η]"X~ y2ZjŵDDX9 O~ =nB̗[V}VS\=a>G>qaH#S|,SaHxaI|0ia$4hd%>=4Zy(_(pPwX%d]N}N\DۋciE dHƐMX Jb,"?V1 .yFj)9osM6Yf9ߤ\k>リ^riuj3i6N&LrxIj7'7Ú8:)j9(ܓj-$腂hymK榫nR KڋQ￵  o' 7G,Wlgw ,$l(,0,4l8|s; D #FHpdt RD# OX05E(8Wt+NCp=uOmwG70xg0vm8 7po3]N7ov} H<.jg 9M0N_us^C|4G/s? ' C4 7_HK>4]| ǟJ'[GQOb[ .rzsȷ?rRcz0d'0ep~x"?osG7} GB1*m.[h6ġ%v% \I ׻)qcUt' Ѝ<2Rzgc5rpDaDpzL$ذ+ ss# Ta;ɇ %12x ;sD]Var@§uQ(D ._\8$ ~ @JЂMBІ: D'JъZĨF7юz HG:Җ(MJWҖԤ.LgJӚ6ͩNwӘⴧ@ GQޔeFM*GjS #(RQSF%J* \D)ULT$ @Dն^5duU*QʵcSJW\jGݫ$ԧfu|h{VBv-lD'٧ Z{)[-kؗ"u%cA;[6Of$̶Peh#\@kz;7ժ]G1KRW`s7]܆`c}X&=h[7=c:ZMЕk7 ທ=.z͖Y⭨{^^h\QGV[z#X \1<آho#lQT F|Nس!qvu.8 H)LӬk|]/1pYr9.,a7u_G+^+QWR(2]3v0>׻t-f-)j !s[?wXe:bUQ'=9/7!%,,@pH,ȤrlШ4.KجvzxL.t |N}rySI } FoR { DG VTswDUmMvEHnEwHINBCD 툰:ra=%ܻx|_>C˃!<9j#Ո y3X^/# O>?8+%乙DRry˧qlvZ$" FL5 l[9@xYQi@k* z@0z$aP sxrc7G1퐸 +3SB`0 >^-k_ʰ< u@K"D*ĵԍY oƹd&wJ/CNkw!o#kGH'M6$ O>Hu|y%}uJ}'Ztpn9=6:FhZr |i%$CI| Wٶ@PFgG0U/R4ZbPU\Zha6W9H &u8b7\"؝dyĐfj}'tiX.yjr (裝8JVj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫fZk,l' 7G,Wlgw"$l2P@d0;F <N0A7'O)@,r C |1w] 4[ԩA 6]Srv<43@ׄ=drtsMn+,c0}\`O`:$Go^$l3 Dm5`La Dc5|}u X Ars- @ go 'C -qQOP4}<\um}tnHnc:ĚW 9qۍ4H|86aW'XAaPD_ĈD DLJ! @@{(:J8X+6H鑏 b_ƇIaxb" IE.ruxMy7aI)oz.wi@OZ.\R<)AMrϕAL$-8.4v1Nσl4iTbL8ZVӆ$Ip C$9gLIl _PI*ΑLjz PQ=,hEq,3k rE"gĉu'e 9yKZtDP#Rz͓|)ISu2fzLip#ތ;Ԝ^ X `ձhMZֶp\J׺xͫ^Wb `KM:U@Zͬf7r hGYϒMj/'ʮ-l[ZV-nwYv$ z6 AF0\&7ЍnD]V@Ц[]n൬o ]۞M-r׫e/wB7ٌ}7-} Q@hX/sl^W!\Up _ Vl [m|bz,ίk)޿W !;76Z{b@Ɓ@1 xݱcvwAVe_x˃m fCyh6l 8Kr g.יuf4x̀msdXs2\&8of@Uz)@΍f3M^ i4Fb:C6eMP#Ҩֱ+hd^tE ;Ӊεw|Zm)|bцMif:Nn}Գnckn۟&gzZ\6]! zCX޸g<%{%lEY҇ p qsw4Ĺ _hw8N*icNsϼ8Gs)@σNtHѓt.Mc.k6!%,,@pH,Ȥrl:t:-YجvzxL.znsڥ~|wwtvBT SNE EEDDbڵQ CE  B݉gЁ;Z8tH L Ca y_y.\0cʜI͘ Uӹp/u*I2aHth ׯrsdI76 ҡqhR'9#]K[` Dʄ2f,^Hgi䧽tu Ө05^;)>,>fiRc gqFo;rУKN]B{o2cYI ri6e֐󛻮tȼvV K-mOG ^]i.Uvt91cY[Q(nL f ZOkB f'_㏸ڑX?E_D\Z|?h"(@v`Ʌa-YdÏ6UiO|d4#),C7QP9'柀*Lُvf j' 10u:B(T*d]yrCHe'ԪwQ$Fn{$JR [DHi,!._$tzGCRk% *tؽ7jj, /F2K*ߘ;U2m|'z"/)ׄJ֚M*ՎJWn3\#T!ԣ>>*;U*7kJؘJ(3Չ2\^騿c*v,<"! `YҾ{RΨ5?ծrESYVҧ iԢOl3M J7-[9ej#Ak /9vͼxKC|K / ]a.'L +.7agt_(Nqks0gL8αw@L"HN&;PL*[Xβ.{`L2hN6pL:xγ>πMBBgr͹`soa\ wXg@$6r r~pe%@<8ڽv2l^zA0El}g\ Kw5xmieO[ :74/қOֻgOϽw?N/ lH6SS }$hPa*`YW(aL@`k6צ?_ow-'.mW8yY P~wue+4kXm`8qgXqׁom(7F6Kw]~cfXdS6n~xl8h8^ޗ}؉lfaxxOPGȋ8ro8chV86u8ȂLjj؋ @6kh|Gax778j2Xbqf:lh'li6966}7c'76) @TX}8( 0HKݷ1 yH89vXzNI37Ty{VyZ9{\ٕ`b9dYfy hjl`A!%,,`@pH,ȤR)h:ΥtJجvzxL.zn2tެ~~tsPq`| BOR QϽÞɺ܌ҝ C˫C  GA_~gÇ#0 ,rX(/E~C(j!\I\ɲ/*[pe$BjΊӂE 'I+xH*]ʴS}"r! hUDz$+9]˶۷L3g+M8UV"F] LR"ߎQ#e=¸HPp3k̹spH<,~b쐘F.MЈJ-toe蚁*z^μsu&6KC"#@"9 DPӫqή|^:UEPq U``YU|ȥeۆvDLFgN0YnG0fȄp8%D9fX^DYXII\d{݅RiX^p]T` &ᢘh&`UY%t˙v橧`xr*h&}i袌(%F*餔Vjd馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+VkjXZ dߚ@;!69DAl W F.;npN C)O M9r #`Mo;ݖ#bULR{6Kn;Ѓ5xGQkTV&8z Ci7)c<ȏ Wx(7maNsʜ5ynoqGiNt!%,,@pH,Ȥrl:t:-YجvzxL.zn|Nڥ~uwvLp_G TC  Sz xԮɐʎB :!!P) K?RYLrLgR\220^rDO\9o !h#萸DqfH~sDzիDjr[L&X!O_`V'RmFֱH00*ֿe:쎭bO4 r-6j:$i.1(u[ͺװ]{–>m=_-Dk&cxκYv'\؉=@hr/Z5Vypl<.FqVK0tL|uo"?-gYr^x U($ȅONHd| { 19aӊ+[E&&L#HHqB A6R#E3R9qE3Mlv~[K`s"g[2Gg%ޙ袌RQX2 5G4$X6#t11p4Z?(Ka!Jv R/4?K4'yv am+e,ڪfZqn&eh@| ãqc_Fl0н?ZOpO0=ix?z$l[4D`ok_ M0 (Ǿ,4  A jsA}o^ FMu Ѐɯ 4@<(cX!NSu%,A<40Q JTHƠ`ȢȆ/\pG:x# fM_b)ح p H=Qd5Nz (GIRL*fV򕰌,eZ̥.9^ 0IbL2f:Ќ4IjZ̦6nz 8ǹ]җ6IvҜ?C' Uҝ#<}& f3@>j}fg:qmB <mb,9szxGtuh{RC3(Xz]\=T(0SCiWzB_B- eZQȀQSS擩mr:V|B=S{sqi;>Uf=+Zʧfuqjʸu}eV;4,a[yUVI PV GفjVMֻjT̀3xSzlk46<YvyD\Ev-.YՁ+c PZZrPNO]wjw,/^.+ f{ʭ.`WBͫڀ o~`rXIũcJ^&SMȶ\ oPIc7*ܔWu2`&#J.Cת+{`s2#DYhN6pL:xγ>πMBЈmF3ѐ'MJ[Ҙδ7N{Y&ѧGMRԨNuꔪհgVεwkXzD6@lb E^ Іvz=h;ٹ^vgG;5Gm]k[@޵Qk;ݳ>}Xݺ7-tQ@+YobBW6zn̋Iܓr%p\sڪwoM 6\ߋv=9}3(8_qIϼ"O5chvg>GG߮trr+]x=KN]n|tƽqk]qtMcGT:NE}Px={_}q[wwL^_OZgwޭu3^y??z헆7dz.EW—]·l;sSW!}qս=N/Z7:Czr?^@7|C/7}F~i6xh~[mHt7~ ~7}Vs 7t'yGitw?zʇ|hr$H} (rWsvt&t(6y,(ufyww'3X{>7DX w.XluKx{%(u}ŀ}gx^GdHwvZpNt<ȅ慖swsx巆4q'vD}H|Gtv&_igu;GzuWqQnr7m(y]}WXjhtVl i78֋F'Ϸo4fܨiژ8Xxkƍj88ph8&IvA!%,,@pH,Ȥrl:LtJجvzxL.znxYK~GvuritT L  J Sy Iў՟G׾zҝ:tDUDaZȰÇ`r ,KTB.F.C4hУ@P.t*KHz*Ro@E]@ mv7YMךNye*i$dyz ʶ?2(uYq{ t^Xh5jﶽ>3?s5ʕ|˹g#(Ƣ0!{-#cY9B=㿒FNb *u߾B;rY,pYfao自zVռluN5T ]YWs)śVE6pE(F"]~[P߀u] ѝq0Y |481hbtLya߉zڂhg6PvU]Qbx)ZEVmG$lGTeMp)ge X1t/"I}̵BX&hXD &m29礔V* +V^e+*Ւ U0N3EX aՏ^vI€9i sKg*6/ X`8NKĬ &U [ҲExm6 ]^1})(d1lq|k*a)e*a:~%vEČ,[RmN7;0S-vJ Ǭ\G<m4-%xp |.mXAt\wuBo",d͐&|\Kjp-7-# qE y(E\6 x@9x'MPF &%`T> %PQ@*mB DR$'G r & !YVlat҄XcN5'5fP5ZD4ef{TLhfp p'A(L&9Oh gQ{*"SP?1%3ύ?1(C.h8M(j@=0MD4h7JS&d5f;ЎMj[ζ]jx;vMrNvM$[η~;N w^ă';710" 8ANr~ !)@%Ƿ-~r@<#".;.:c <$9Fvq{Gu.qR^!entwÔT\XdzOimڅI`nW8>JOՍt#]xGy=萧|xɃ*î`Gzط70wG= 0oPg׿Agڗ{wzʫ{>Z/ {_~o?~vbsnG~zjwwvxƗ}O}Wvv7`c 8nq on"H|wn P%u?)8gG}/yoRFH~ H}09nX@hGmE{_m-#HzL( zP7Wă5(~L8 G}'.1xn7gn+1(4r&}pHzh`~Ȁh858n@7w~熈hxnm?{|@8hzK7dG(ury|w||ڇvFF'ynxG}xyssAwȨgӈnA'߆ss8٘t8xɇ|ЇW؎lwWzqUǃ97zXȐp"D9G'7,C%iD' ֒8yC/I1׍5iB_|G"| 6PBN9TrQy*TZXɕ`^9fَyjIrA!%,,@pH,Ȥrl:Ф`JRKجvzxL.zn|NiOO~}z%|UJH̎ R Ӎ  崻 @E*\X >1 63jȱ.:tCuE0t0` +;$"R""]̹K x :DFD2L! @{XjՑDl>áOp̵pwyY9WN46{_Ͼ1qqYhs.MEŭ]Y Qll;Z۶Fh*lhiɆM# $l#i@ %e}%)H+UP3`E]FLot.K@5B ̴ W[*,dĹȰ"q-p-h1Wsԍi@'2lXmܒtӘg 㠇.\騧:cB.;,nE~/(`'7G/Wog_w/o觯/o#x Lx:'H Z\7" 'H2nA" H'~htHFKBqʣpL$"HF! HJ^O `HM*4^BDRtLW6D(+wiV "h%z1qye s NrQ،!+wZeE0\Db(} NUy%Cw4DG)MPty=%=o9S i#=jT~+mKaJUtk* wT pM5~ȣ`'V%=zf-L^yUksi-(vOLVx=NeqŐ=" $Kbjl&Rcyyk^*x.So휼π$MBЈN g> t%MJ[Ҙδh;GMRԨ+I;Z iָuR^V]=l\N}>bّ}jζMjk{{MrNvMzη~NO;'N[ϸ7{ GN(OW0gN8Ϲws%$NH':ЗENDֳ{HΑI 0&NG B@.L`p@` <>C ]e20y u+<n16T j[,4b<[^0~ @ԕ؞ }>Ϟܾor A umw? Ʒɩ֋^u B>σۧgCv'|cG.7\v= [Ѐ^lP7FY0xv~n~ۇ `'Uw`ygHim.d|*8r%.wgpa~Y |r8Jl灜Rh )8VxXHF/ W{\`b8dXfxhAtl؆npr8CxX>v;y؇᳇sB`Ux*Da#.(>h7tj;VA pi7AvXfx;6yBA7A v>B0d8A='>=@w`ysGwytGc׌~W(ޘthx3(=󈿦>-Xwsv==x ЏH=}TY >YPx:賍X'hp0x YHב|菏H5@ ҏ$-옋!y:v8sɒÊcH8hD>w=52s>$YaIa(=m=\ >ǘ*I)>W3ޣwҋ.t9#{xi>HYuY>0i~ii)7ӏB9> 9x=@Y oyy>9ӗVi>ٝ~Y=UY>`pٞɗٙ9vi=k9X㟭IvSJs>$ =ًB p P[iy `yȎ~I9 x<(h 04H<: Ũf:ْl2)_-飉Cb  ZڦO=ꐔ'AI]ky 듒l@oI 計 gn1ZizhzɆWt -Ƣfoʫ,F5h*ح:賭B蚮꺮ڮi:! %,,`@pH,Ȥrl:ШT@ZvzxL.zn|NX^U}Ozp EW} ̐DVՎCفNЎx~ 8ŰÇ#J(gQ4PA.Hɓ(Sp1C̗0@W5oph#D(¢@곩>*ͺ g\l$}hӪ]{";x;|n~@Ƅ.Njre>7 \xfMӦD ,tg[2ϥ,hBSwntu%xn'h?|0t6سkNnelkF`=g5CsٯBX&Q9@?@]EH"F(ae>55dvW(3CDGxjDl |֓d&Y)Db̑Xd| WElfb|M.W^1^Oc_>&$ڄp)p*V{d* Dn\D G.q1&G2aA=:5' C禜v)D-#K gYG:&D*4*+n.c: )V銗*kUE KkHʔ5f۪49)jJఘƛaF'nqLĨ_Ztֶ(i?WlClyWl;CP P!|!YͲQ$,ɹ$L7_טEc5bec6&R+vƐvH@:-tKSRWU%qAC0:#P!WnQtd\slE6ao5x砇b0U]/:Iz#!J׭b >n6I'ۉFHt\4XN#wבcۉ觿3x|,] $@B{L"ME di̤&IE d`X0IB^rL*On,%3pHTE #0Pp\Sx P `|>jV~yLj3-&8s8M |2%lӛ5z35 ' 4_Ӕ$I~D$)X!,]ɉF 4JW|6t*\@  B@ʞ~HfR1E)9K ҙԜ*t$:RTYծ'F*UW%O̊k w54X јSF+׸92 lQ*h=zbp0ֲq=n?LC CYatCY]);+vlH]=8"mH5fY kjKf}@BaE`մ21{Nn6qٽC;Λh' @}!8@=L?$P;0/#}u ^H=ہ/| z Sc:;wn=t*P@@hGW4.˓ؔ|f~PwhghHeO}Xu7u@|ɧuvy|gyׁǂuuW{g0X{ xu38SG%xh/H7|QGN׃"PT'߷ʗ)8'9eY,(NE\-؄O$8L*H h*W8ăHwvɧ~?(yfuX~xhzt$w|z98?'Ȇ Ё(XHH8׉hRVXG~uL芰 wq؊'X8wh˜B~Huvhh(JTXRǍy t?~'(çHxxr9xODiiQ29 0cȑUX}Xh׃Iu7HGV`x'Ɂ),ȓY7\ i! T21My yRgvȓhwH6[ו _fkI|7gIkYٖԈo |ZyOyWIY|(tTȚ}i}hpf7癏)}8NP}%gzMT97z*9xv"5t,8˩I&8HUW(ٝ˜"Lr؃ibȂyDxupIuVzIމL))YI8~3x{)huZlx)utiuLy/xs9ע26:x&ڣh0Ww`JJ@~#w7)HzwZפ驑C x XZɥhgH٢צM)wz"`ie|ڧ:jw~ڨo:zZDmQĩ:O!%,,@pH,Ȥrl:(Q@ZdizxL.zn|N~/]TYIW}}L V̴ PXOK G فK 4y'*\ȰCQtp^k|ȱǏ Cz"`b\k":x8HI[:p1 IBVLyCCrM0 `MEz!xׯ`"!dvvM-NݻW×khmni{BZmb̘m˘3k?t;[Gbt=9 qV }M貄w+FG>#ŦQУ+0iV);^5q @EtnA?_ItJDgs!ف&tu;6q?J4] Z0dI֒}dN #ݍ8樣$*rٵV>?uul=&2d5$dґF_Y 5bh暚WvS6kw!Fb Q^Ra IguFd]%d[5wF*餩 v4 b@uZM*Ũ LgMTbFEE`hR ;  {& ncr4uh覫JhŗƢ@km' ߳qj*u.] l\(G,q xՆ SY;d@2E \lp'rŒv +DGGgOva%toYycJ6ΚJKkznhv<( JoЬUehUƵ>-5@]`53y;$a)Ў2W`RD,..*rR\95 ғ޶"ڰ.4'uf9oK{lGI7/&BCڽ44 X~w//E& =L (9'H Z̠7z GH(L W0 gH8̡w@ H"HL&:PH*ZX̢.z` H2hL6pH:x̣> IBL"8򑐌$'IJZ̤&7Nz (GIRL*WV򕰌,gIZx.w^ 0IbL&+qyGe p,#jVӓ̴4K dtIN `3t ( @NX(p L@9;t =WQӞ&ONs %0Pfd 4t&Q L0##@hFT9 t(PZQH*KIJT@ x@RzTJ@  G-jnJZ"An$b{|:yPKcfveH(sڦvo2(VTp3A@x}hG2ۚ!,g-׺ǫ,{XիafAҨ^{,./ϭp-Nrܖj9.Ygfidlb'Z6% :o[6_%mH˒Ds %96]K;ژ(j+F?8DXFxH8_NPR8)XVx4Z\؅GȄ^b؂4\Xb]dbH!RoHi[$:@"@!`z9x#HhRo8cZT{肓HuE=H=ȣ&Jhi}H4j,9ؘȫJ䫺**/ʌXȚDʨ͚J zDC(٭J ڊ꺮&XI ZȮhگ['g kA!!&,,@pH,Ȥrl6Ш4ZvzxL.zn|N|xʇ^N}|GSM RĬ T  ~ Bz HbtpA<p>3jȱǏz0,Ж K€!$u ReDμt!)]ʴSCmSFBT  GV!zuIhe fH-IݻxM#nC[ '-Yn[mB#ɡ"j"C͎C+\`fgX6n\& @=ۦM, g^*_μn #3yl/#1- H'L7PG-TWmXg\w`-dmhlp-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯/o HL:'H ̠7z GH(L `A0 gHiw@ H"HL&:q8D}L Hų]q@ &@bVFV T36bD86;X@( QaHC! `HG\$#ņB@c"l1x7 ҃%@*@S(-+A @K^(&8YΒ$)J^JsL9Hjr`3H`PyԠ\3@9F ,;(h l"P$*9bb|IGf [ ȆV4:3HȎΑ@ H,cO3%DЄ.Ԥ')B9|32Ѯ%T+ї3(-?w BtZf(ȫfh?V`E) UFsmHϝ =+`)l^y XB^`CWNjUKKO ւTPm+d3 u IV5[IlRAnem"& pho;Q 8 .;y $1NrՉUf)bWKPcbp"4.ISÎiO*_r,:Wk)yW.˽Qoe_KHs:eZ<^9pxߪZ3JϺxg3^Uek>vpfU|j_-/ G!ކT5ak^'sG*|T$8YjGz^7fݺ6<]@&d֕ %5 qIՊktfђ3]LB=>I2uk>u/{:dӯmf6}}Oy&򱇸+?0I}@5]"&\?eVfJͶZ'O^UwJM&aceR mI;6%ࠆUM9nT:JWH?qʥ x <{@ЇNpQ3җ;PԧN[XϺַ{`NNpNxϻynTܻO;ҡ;񐏼O[ϼ7y/i{zw睖 ![~Oꛖ{zO)]~}w4}){w4}~ O{W $ڷЮ=$i~&lww~~Icw{vWiv'Hzxȗv#m' Ȁ8Sw}i'XxHi8>;'Hxn!0|'3HwmW"~zw|lW~CXi:O!=؆nT(81h5Swv(hȂDx ׁvPh}jqgvyxh34idva*8{fXXishyz`Wr 8l~H$Њ*sw0pX(׋xWjHgǸ͸nsH'Lȍ)wljhh|Gx24XVĆWQT]hָvZLLgZkgg &Cw7{H|Eh3ig/*9,.TxH7)9)R8WF~G({a)pk_vl(Z)x\.mlٖzxXvwxy.zIi.m9"i(vyyYL yٙI9)vyS}A!'%,,@pH,Ȥrl:(Q@ZvzxL.zn|N|Xʇ^T}W QVŋ GUمK ҁK Bz Hbcҡl3ŋ3jȑ ;B[CFHF@zp MHH{BbR'I!^\2x8gɖ/cr'ȝ*i}juVDpˁ`)djtdpa6[l)Dxן6ܚi&:.袌6hg:&;ރڥTR%#'IhuIjiv_ӅH$E$譸kRӡe 3᜴ֹ!vEh+&Z8)_wyUr5v\*9ZidB$lU:wK;URlizʮlo6[ eJ6T 5;C 41n2Z&f/8 oGKjQI-835m2l` U~%'~Yr6g3pn+\w5,4aoVg=iY$;leu.% sY!l'FI?̸Hw@P\pSפn(fֽ.3:3yHI^^_VNd7 IBL"F:򑐌$'IJZ̤&7Nz (GIRL*WV򕰌,gIZ̥.w^ 0IbL2Ӡ4Ќ49jZ̦6nRӛ '7AqL7vt~ @JЂMBІ:D'JQͨF nL@@Q" IIJ +h\JT1U'H lR!iN)7P9@R49uF38W:nWI} Wεa\NSN-EպVn@@\ M>s ,]cZml+Z-jZg}pmPWJZ jr9]:.x[ jTY>@}nq@K)\6sԭ.96kn] ջGMPEUޗ 8sfV~-*{"׽oxCZx6|`זx/:j~v XeqY8mW_hR)hx_va`e 5oa-{ӆJ&_;,!M731~kY]_"9-xiIW3?}kA?˘ޱ[*# `Қ_Q1"^v2Y>ԄfK#-cS:p6)H+@͒^p9g͇-eP}6ԫp]jsĸf4w<Fk|oq\Jonӹm/|>sg6gJR'q[5Y *XgL _/g;ƽm?_R۽Nzk+vӜO;pz fnZuzۻaڎ:vWw#;N\oMq#Ԭ[Mդxħ3ѼmSA'[p8A?KZ{g3;jw#?d}Ő p}E;ЏO;>G_@{紾OOkSOϿ|PW7 ؀8XxXW@؁ "8$(H@&PX$xT4XT@P  Q@6X<؃u @ P@ Q30K? T(QQ[ȅ4Rpc6U(VoXnrO r^H}؆4~ĆoO0hsP8HOHr 4 I8Xhxx؉0f#XPOnUPXOP@hfOͨ(Ohx:O>c6 s(P8 W4(5+>h؇Oh8Y帐cPPؑ0p2V+Ot8O(HcP@P-9XTHRYPAyPJY4*9J (4؏Z\= x@B:DZFzHژ5j0IA!;,hLڋ޼H扦ʶ L ĢL*̦ JԪjܮ N (8HXhx)9IYiy *:JZjz +;K[k{ ,N^n~/?O_o0 <0… :|1ĉ+Z1ƍ;z2ȑ$K<2ʕ,[| 3̙4kڼ3Ν<{ 4СD=4ҥL:} 5ԩTZ5֭\z 6رd˚=6ڵlۺ} 7ܹtڽ7޽| 8 >8Ō;~ 9ɔ+[9͜;{ :ѤK>:լ[~ ;ٴk۾;ݼ{ <ċ?<̛;=ԫ[=ܻ{>˛?>ۻ?ۿ?R`H`` .`>aNHa^ana~b"Hb&*b.c2Hc6ވc:c! !,,@@pH $l:ШtJZBz0xtT0;\~_S}Eu E nD fC ^C_B zWYS lPEۡFs ]"LHo!Cv(1Ŋ.b4q#a Yj$LR\ɲ˗0cʜI͛0ɳ@W Jѣ4"]ʴiON"Hp ՔP"u!Q-zkMŢʠ녵K s]I|F\„4 4(Xa"x@q~ Dlx%^{- Ձ fPBWr7q[b( r@r0@[ͼb/"/;W 6y{6^J\U]1Pw)gbv_!^Ry-Qmpzu7`~nxp Jbxǁ(b܈`Y#)]4.(a&AYmJ ` UaNTbfJQ4*iMUP._wtT vtk|*(%yG]niWƄ@f ~qhgRbgU7'5L K0y-[ޙJ~n^Ilݚ5+FNH+gkUY*:"t]X%&h@ (,q_ b T[ٜi-WӖ{002Ǫ򵟤B 5"e:-YmfIie?]nWNz/Soo]pM*g)iyg~ 8&Yބ@ f O}Ѫc\'; ۓ9j4d}JYbJ'bo^P kNJ$OYf@܆v *6m{f騤X*l.Dnq !b} ^.`ڶ2LrZs5z|ե}DY*{|lqN*wp}J鹸];'g47PX51|EI{H]LTKK8wˆ7Uv2JnScP7".9R>?apy/i9ERiIN5!!,,@@pH,rl:LtJBجv}VҮxL.g_zm+T;0zxV\ ~ vaZmkkB FDGDXtCj~}DɉCF~ЉY}~ fڊB}Fѵ94pC9cW BRM q)۰TU)yN !g!ot&#REl Y!9NqnX$'?Ξ1eUOf:M9*2&7pvHPnMGJn̷O-5]`mnº>XrXYJ&a2[lYvJi~ '$;g>f* +:gq:m:+p+[q*p)q{:+{b\+4Y0 ;@癯1*j}j|Pn)Mto '?,[P-,-[Mp{j9*کvz7veo,=v217^'{2:]v^З˼+#/|+; `l0+~{H?ݑgmHW4ƕ~R *q'!'˳ͼ:E8oÜt/eQWն,f1Y>[,׿MAA^>ױIxcVC8 _!T i' omLCuC}k% /`db>"RWTOqmJĕ<:-3mx$T٥</B)xFC:򑐌$'IJZҌtT!,L Iڋ޼H扦ʶ L ĢL*̦ JԪjܮ !], , `pH,Ȥrl:ШtJZجvzxL.z>|N~ϯ}gxcuaWHAf*\Ȱ!#JHU3jܘ"Ǐ CJ(ɓH\ɲ%9.cʜ &͛8sⲩxJF*]"ӧPjA*Ujʵׯ`ÊKٳ]*۷p[Kǹvꍈw߿ È+^̸ǐAa3/\" , :Ӓ.LFf5Y ]Fdڬ ݼa@ VT0sL mpwj Hw/@?8?zHD> Xw߁lD`ނ tա ua1pwkP]y:v<"! x'At gX!8 <> ` 8M ^(@aiP"X"P`sl9^oV>Wp _{ fA`p ;p9Bh Jva7d {{_% ]9ݠ?&ƨoA e֠:c%Y} 4PFjT⇪IjilFګ&:l r*.k6RvFmj'"rʪV;Fڻ>+\+Ʈ;B0e>,kltL1g³›؎Li)l{&`$-7Y 6[a*ybMz͗\G:p۵I}i@* 0- rMm$8e)^!cua 9ٝ{׷G`ڗ88b.l:z?~Z-`'Ҟ$_dӎ<}Xc:ԁ^kT.>ƞwyؽg:IUC8I\ڞw\y9JÝ pSDXH~:WA;@&+vP (M $R(qw(&ܔhH)qGjg-iD0lGdtnqg3WBd_9!톈'2cW9zRl‡7Rг_$=Bpnk !ABv]XkWyl)WGȉRxJBp S]#I^ `){XCMHajc;`f}4aXьtfkdHHR(^9ʙ\ c6!N$ xKl#e2;)$~#/8<[ "sVDH‹2< IQ@V9MР+MB5pl xejoO-cQC&UJ'jeGD-* R^||*V bդ^ ۝ bfͭ9'Q[SۖU4uszmk,ԈsFk{t{i*yUo lEN q1˦5%ʫ -x-ERq"4ґ6Q6K*IA_́yq Y B~?4PqzE+Cx,x`hlЩ/j|5_:`LR\v"@:r2\f6UD},ns3Q+%kGAT4I=OՔdd44{\fhlNg!k:du28a.QNpn#8 3U̧)6fn~.{EEnZ24gYÌt_ѵV-޷kaA4Bu-QmVX=8w#/(M6ǡpR᭓̔B*Զd6Fs.G:p|mw=V`8>Q=3.#ݣk#B4Ee<6bBj:l06"%}Rs]]<-yv0 ~B {cA$|Q2Ǒn^8x6md)_DƢ^ŶoCdzqW6j2 pv8X96Kn7VwΜd7;o@z B I r[eYז(XYٵDF]В(/Ar{9KVh$Y|a|'KvP{ IrTlV4'|sG&lw>pNӁCZ9FR*pcy'8ńI85T@x!wX ]if+'M>2P>VNM#tw6c%d7\ x!BHKMUVdJ(x x4% 4:$y3Vxm !]HQ8|rC)#u}4xC&=ho/ ĄeAr↜' 6;Tƀ{>J:곍ٴ̐~m #T ύ w-ښrZ c~&^#t |;l Z)J^ܜX>9V])-Ԓ@^䆾F烙πM )n.䍒鮎OJAə?an*)&)^Z0ƞ^~ھ^zȾ!d, l@ڋ޼&H扦ʶ L ĢL*I JԪjܮ ON 3'XhxD(9IY)hY  )ZjzJAʪ *[kH{ ,L\l6l( =M]\ qm-^>n/?O_o0 0„: 0"B+ZDbći\1ȑ$e|TX2ʕ!O&d 3L~.v<{dСD-4RG:})ԩTyĉԪ\5؂`ǚ=jֵʢ} 7۸t*k7^ xk/Y.81ÊU621Ǝ+ۥl9[̚;9tUТK;%m:uQԪ[,;questionary-2.0.1/docs/images/print.gif000066400000000000000000000237441447660764300201500ustar00rootroot00000000000000GIF89aG:;Û7!&uT7q2.np-%GxgČ;&ɔF9fy7{ z[ФK6լ_n ;lOg۾;ݼ{ <ċ?<̛;=ԫ[=ܻ{>˛?>ۻ?ۿ?`H`` .`>aNHa^ana~b"Hb&b*b.c2Hc6ވc:c>dBIdFdJ.dN> eRNIeV^eZne^~ fbIfffjfn grIgvމgzg~ hJhh.h> iNJi^ini~ jJjjj kJkފkk lKll.l> mNKm^mnm~ nKn枋nn oKoދoo pLpp /p? qOLq_qoq r"Lr&r*r. s2Ls6ߌs:s> tBMtFtJ/tN? uROMuV_uZou^ vbMvfvjvn wrMwvߍwzw~ xNxx/x?yONy>_yoyz袏Nz馟zꪯz뮿{N{ߎ{{|O|!,!,D!, g pH,Ȥrl:ШtJZ,zڰxL.贚mw|N۫n}IymGp""c$*|_&%% h"##Yok+K+%d#w(jwhJ Yv(,k#$X''M\HXL&&Y*k#R'--H8$C.  BEC<$BBo]‡M'pdMl8̈"q^ˈ<$"tJA= XbR$v&Ju&άk_N,hF,Y&8$o~ɾ)y|";)nD #^B|RiCiѨJ'$hEL%kiFGN=m0"٠9!B.ӵNW̺CI?\(vP/1ޯbϗqAb׮M".СT؁QmWBf[q@†nH$cu9Geg|w5F,QaBQ3Y]K. ;Ȳa^14A?jxev!ǖlW^pZ"fE\SX@!P \ Mu'LԵX1*Q|BhFuh&Q"hJ1LIO/1Kcr az* ?eN9TV)$XgY:;3u4i5)Hj|g(ѱ;F }鞑rfRd&FSRBhU!5fxšDt6a!D}ivqSJzF sb 7'R;*~YKֆ#T!\eqfpMҞq =/S3\,+lJ%i")KfA4>6QZa ?10A ]m5қI׸SKcSkWS:KGpSh-Pz23Ĭ5/M,v'cC)Ո2Cm)'2}ڙM&}f[ 6ZdDT& ""H?DfdKH#{[<3(#NXW$(٠/ |Y  !BR1xqu~T 0?zf0J41/C2,s[^ ,' SYPڸ5ZۙP:7?9@$x${"|wIQ3a 9aE 3AUM O:!ƧFSrc:@K[PKR@c  Ȅ`Va"0E+T%JYL/]:E2K\3$; +4SB5(]!,D:#M(28Ix$b߼tY]N[t>3ͨF7:~r <*ҒF )JWR0i:!,e  ` $ h쨾g¯Ҫ]n1!1 &%%q- $SbK#F覎$'BF{_z|"s#jl8` [v0^d/Um}z7~36V gKItF>@!! ,r  ` $ h쨾g¯Ҫ]n1!2JLgX8JaƗ ,#UYVǩ';Rlj&kk}4"^gz0#Yo?e"n( v#x(`.D$(;# 7@(3D)@ 2GIdF>I@!!, ( ` di@#+pάxWo 0&T1)]FT4U.; ԢpbDtoO$XoFV 2x2TVWMa\*-_PU")C`7c#=[$8b"3 V$*kfp,T2%# qӠ%AE>d훢%=D"! ,  ` hh°\үMn1_Oq(nPTk9I1P-1LD@I,$$7`>Ni8G u<'Z$ 8x$'4ra#' 5e A|UW/k9)?cySFT:B@I6|!!,  ` $ h쨾g¯Ҫ]n1!1pGEBTZ)k= `w5*'j%k\[<y4{vxslX0}G"~z uIKVlzF8!!I,  K` hg¯\ҪMn1_!1yd.M8E@gV_n؋g2ͼF0v=! ,  ` hg¯\ҪMn1_!qD(<ny.?``J0PQpjVC FX9w/RyE[11D0j\S5,DLSGUajv"§!IFLn%jWʂ/‚T a-) C.[5D" se52݉NC[%dP.:%qzK`[&-m![u xz>X4'n=G,v سW+fK?ZrWȎ{t?@^dٗWY 05aK6BEAb xzSHh D0W2!H\Pc[(ReX "` \EN(x]D^]NQ |LtliTCOj$x # C#!/Iu gl@R;%LEA:'z$QZf&,j_"0|j *@1Wh"zkJ*A*+)\Ȱ0 &i) I谣Ǐ 1eMѿ:F#cț8s꼃PSr̀ED㲎͝PJZ*lKCOK[@%`SȪ2I@ݻx&eV3[ P'lÈ$N*Lr}o]Be0ù3eÖS^:^Pj4ι63PN$}yKУw,(w"HΝ8࿐Oy.GW6/y T S`yd$ 6FVh7]" ($h(,3&h8#@)D4L6dHTViYD)#\v`֠eoaihR9fLipBftidY#|J 蠄j 袌hF*)Nj饘:Yiv鍛~*ꨤj*꫰>jjު뮼kk첞&BlV 3fmnav 9g른.+)K~ho`!], ,g@pH,Ȥrl:ШtJZجvzxL.4&@|N!P@@zv`))y>4(}mpDM0/&n>23(92~CENF_G\ƝLP1"43((273Ѽ㇦oDP]F]MO"<:72,(8n8Gu=Qݖy y1 h 3euCPnV:IåBfY!LS@qcHm)rNP(LgJ0eB 2x`lE@Qc)[5CDiю0I󵖭Kjz(\ 6hP7EzaƄUϖBך^ZX]lg/@m2@f$̥aVH~QKx_=T߳N:o'Ǐ Rݗj7L`U[D8B  v.}'U @ɷ p0sN'"}yXo)Q"92" YySȄ2G׏p@5MH!B/5sbm6 iHXv]pQ@"D֠s.fb69Y%aR"Ӝw%JyqJfE!je3A( 9Y,)mB苃Myc9g<'B4P9椸HLp[tޭ[`P.1쮭9'N&؄!H9p` n]u%\uaAFMm;[x3z#90@+ܡ_Dyμ~a|(n}ls ~]VoI9MOf>cP(^ 2rF~*vtsLoЗX XGx-H=αyXPf! {DX[ۆ :pa !2(&'C-\[ aXCP~ cF jǂ xv_(-NqW #B^1(VdA#nכwK*r$4! !qIcDzal""$2a gHF2!DXɫUۘ1p=(4CH;Ƒ ă73:r IBm%X9Hi0[J)jS<&Ay͓ߺ7dp4 bxT\c x=e"c\BxW\Fi TR|Jq' SI;N%xOBp"J&їFLBJHkF9w5pH{Z y);8hg9@!—p%.AO#A"ӼCoȷI\;ե>^ i +#P`ܜ<(Wv2<\;xyDL:`wbRsVBi`GtM XXB$` a+\!דy#Lp1Brn,|rK11ڐُ~x^/~F!-N |I|%M$ɽ~vxPl; r勣F]%¡PAvh ]NmHUzܲ+?.: `zMk/iHuh( z]byFGuq_AR#@j8ZĞZ~dٍv%$PK!6X=PwMHT"SD؞ ۸>q4>v%n4x=gCF8̭$ nǂ:=ǚOhK? A !d, Lg@` dihlp,tmx|pH,Ȥrl:ШtJجvzxL.znj|N~q-_fub¼Ǭ̞ѐ֠ۅmcYV H*\ȰÇ#J@Ŋ3jHGCIG(SF4yQ˗YZI?m9'H@* @ѣB"]4ҦPx*Tjuץ] vYeϪuvۑmw݇uUw߂} x}+x'Ks|/kyg?ziOn;questionary-2.0.1/docs/images/rasa-logo.svg000066400000000000000000000007151447660764300207230ustar00rootroot00000000000000questionary-2.0.1/docs/images/rawselect.gif000066400000000000000000000521311447660764300207750ustar00rootroot00000000000000GIF89aG#"TUU{{|ccd'()"#$sstkllÃWWX[\\GGH:;xgČ;&ɔF9fy7{ z[ФK6լ_n ;lOg۾;ݼ{ <ċ?<̛;=ԫ[=ܻ{>˛?>ۻ?ۿ?`H`` .`>aNHa^ana~b"Hb&b*b.c2Hc6ވc:c>dBIdFdJ.dN> eRNIeV^eZne^~ fbIfffjfn grIgvމgzg~ hJhh.h> iNJi^ini~ jJjjj kJkފkk lKll.l> mNKm^mnm~ nKn枋nn oKoދoo pLpp /p? qOLq_qoq r"Lr&r*r. s2Ls6ߌs:s> tBMtFtJ/tN? uROMuV_uZou^ vbMvfvjvn wrMwvߍwzw~ xNxx/x?yONy>_yoyz袏Nz馟zꪯz뮿{N{ߎ{{|O|!],`!(,  pH,Ȥrl:ШtJZجvzxXj]^d]p*yOKf%TVW0ǕL3 C6Bs#h(6@(@(/adb,H?W`I0 .P#-+ e:M29k1T<q8j %C y#lN(<ЕI&Nan9D=WN&i^!JT(Ufq'6ŀD*OG qBBZ$Jwq\l+K"#UC4,m6QLƴk9Pl1-)*_>"2m XrPB9DS!fZ95Z~{ڴ뤼 C[dy/xWI0:: 4ʀܕ ! eн6%jr* ˢɛ$1A:GL +*#1C2e.Xs`r(b yaUXD]E M 7\s 䁣ŀAy1KOH|e5zp@.>h 莊;HQ]V%@1[f&vEZ2!H08P Db"`UG'Rk&e,AYF4!$)lA#4wi?rڷyo{ĖU:C)raanzK:W'XFQL%9V wpt%WwL8Tzix]ៈF%I!œv@$(r ~'KV1l4j (pl1 ^yS Xˏ.R el0xb.3M5d~4j6ᓙ\aن+qyl .YÔc44ה@(P]$i79W1\8J5KϚ6YBdF-/WQzv.!|Bi!!ƱZ ;À"cvnYᑓќm*/nEП3y7\W nfPWc $IS !d 0ieyp&o62蜈0l/x``W"<z彤1M7wT: @~a #2#UfK\^^XFˆ) ggn&'zD(Z'a6ؑU#yfߔ(c/SCL }" >a|h X WNqI~I 隖g<0f/pBp{).u gk*6eԅ':oFO.PFo76x!7¤3g" njeXd;K&_rfg{%yr!U%>в ICߦ6kz뇡0ˮen2/YmiЪKtI4g1R #«}ttdqyn),##}ʚ|uqS(tȣ5#F)tkf Uƒ"w,r{gۃ4m  꾳@w  \5߫1Wsm: Sn#s+*H擧VmGsO'm~o:QLT ~Iޤ#(/Oԇ(@gҼ<ߔ704B2R ?xK4nj#9OԬX ^!)o7SsMz^1DFSX_!vE1BFkA Y##hx&{H0=ou &ٱ{(>-3Պ~jܣ@!鏞#Kg$iWT'ː&q,apvH-CB'%ƮJ5oq'Ғ5œJKFGH Cs F 2MB1(,,B $t GB*-td98 hllpY8hGr{NO=<ѱR靵J'FW NE~&BdpgG L!4|@;u5oTg$ ̮F;p {VC@(=%ufW\ $ 40$58j%'0j(e[Ucii;Ũ54Զʺ1b%ܔS ڪ?,2 Zq l8nmN8b7K3p/h.JxЌk}tegNSZz #}k,G E?nH}%ᇝ6-RwN^㡆Op{U&':|839tz2 F `|9*h)kl=s]D Մj-@G>-lmO{ixzC? G8(jXlį(Hf'cBTV2Iih*Z`T!09 g+RUc hA0,@:«{q"PxWW/irK|‹-}"'pM."xlBdŒ[8a#FF*kJ7E`%&wZ*-B "D-zN A4Z68-P@@y7uDs/L}ɂNASht9]PM/Er%wu?ӓӥB? |NQ:Ja;JfZ-=:̬'1CRmC)4)HTg : b8*Tɏ}_ȴZ" ͪVU"v0ah`'hTsb_ݢ8g XLD *yi5 ErY0y X f&xh42x\H$e3&[JJdl0×α\FY`. Xȩ௦՝n[1E8Y-lPܥ,:*vPn.**y/\K6Ղu c X\QV*±ȍJw猝WPdauLq c2Fp5EHEvGLq[(8#sXKN`0Bx|K#{EdDkX,IJ,Txذ)vp{x7y]{OO]r;gC#O[~ 7荐ћgSֻ^ig?s}W/>+ЏΟ/[Կs৸O+<!<, ,7pH,Ȥrl:ШtJZجvz``L.zn|N~mxwEfo S qBhHeC33_cc~yQc kc_ GgBOG W ]|Uofj cCB!C\} 6cs.]y`xH5`*6XaQXΊ!cB*.鹱'   Ba"+ Cɔ<_´>HU'eKSbJWUI|@P!YRX,9$HLAT8b @92`(JNhlDcDKP@HIu \$IFIh%Uf|^fhc0n?|HHyfI9k^2(gB\t "3xkSlA0d T2օgr MiD43i*䖠&f8^*d5`p$_?>Yr-˫qiZ&9Vz25D}:ijD] :g'`shEa*-A7.ڟRYDl.9` Hc<m+!O-) tѥʄI,k;Q@8Ŵ]lL 4Bҳ &, Lpr`ͼ. [!afLx<j!'M9Ѿ2MuM1A@cY]Ov36+ӚvN]S7Ul_ߵ&>cl AS :D1:fo~9eNڟi}_Lю}{H 񺨡,ܾ) dψo|5281!숋]_{ t &y]V7#YE9h`57ehU)IJǗ0|҄=}Oxb:*! k`щd8$-l 3ކ (3An( lCtx&$R P)+H ,{04tO|_aM.$X%dKJx& Q&ܢ5%$yE H2- xES&BBm˙3xLJCd6U+1EtB1f:<"*cDxХ{eHA02XMqh2-`5(L[,(<r?D[3')0'yr ֺqXSSyjHJP1wgP#]<;݇X O`/A3,)=rݒ֪Aw㴏@i>4Ds!'L7x=TJ*O w7.aS+!2 !U5NuբVw \jĄϥy u&̩TL(O穷 m0 xؙ)BEH3gj15-eJS)̧kgƪW.).[ tnHEgCZ*P8N5A28 b(ѢxH+3H 7`׼&=v䲉yu&ԑI`sICq^B$L zʥDk0H_-SΫulX3S`G؝ɰm[7Uֺ U+F$Uk%Q>Բ9ط.턗xηG}DiloAf'Nϸ78` G~r(O?f9gN8Ϲw@Ї}ު74Vhvm]XyƴZ+` J8Vwnf6/q_z(U s0)x7w6 .`9}iqVys O{9@%2{Rpf)YeoZ"F&|<HwUc/eGao3u_w9# 2-|X06'/-_"pmg3|Մ)%8:W\H7x>ܨWx#X:ES4wZ¦t)Tx:Be05HFYThS5?xznabkXC T8s %`%I2v*4X:1@JQ  $vz& gtBq(7h6O ΪzDg[؂DA@#jQ;[fœXW?bEVNϊm fÈ5te[Ȥ ^:A!bW:(zuu"I  P!&@~5t-64aL1DKJCCfb(c#K@뢫*!Ź ߠQHh9o!"fS~zcAԱ+ Qk %K؍!W08B-6{dgD08i[}`1T L{ -& UZ |`˸Da8:c+Z%;g%lمe$кlc E[e$1mi\5ʹ NLprCd\ i|eF 5C@F,9o61Fn4|#8 W<@Q&gFu^~2 P$w-d6oS\'0R,l12m{0^Qs8)aNDD|әNWp @A M{P&XטM6. "?$_&(*,.02oa3A   s}PG "yܟL((*FyCϯ>[O~fx{ݗxq~-,49}ތEnb7;sH@ ܉SvGEO{vz9R^Jtnsbs~mx_K{n:[a0:-AڳM,hN F{1ݷf@}!c! ѵUa^ hTȸ.#qmN:a+Cc^~p/E_CWVr޻xM .7JcĒQBGK̰_x[Bz>.Lx+jMN(.M!,Vm` cghlp,ixk߽pHȤe-d3ZsSueMp6L~1n|N~&#з˷   "oD^*\ȰÇ#JHŋJ(4}p W`}iy)0Vw*V%cĐ@f" kUE<V 3AˬDu$Nd*F!.,vM`AY$lp,tm"pH,fpQШS9t - O`97e ~bd|zY; nIkf%&ĩŗȚe^%\RPQK`V(PF SL&5T<2cbnoK]ͺ5Fz567'Ewe=kۂK#ᶮ8L3.."z#e]_6t}CteW%BEq˜םZqWdZ nw9S|PWC)W;EMC A⋮ŁsAƃ:5`ii}=Mx8D/?n$ QccAc! =~= %Gr׏ h=uO@a2iVrFd7c/f`%tVyVca:˅Y6TDsaiA#~j 2.7P/ukywDW^Oz јiaeHEZf6zز'cj &<8G,X戎:kSѭˮVrmR+ FPR\Lv3oF,qY`y;tj5.'+}MpOķJdڬ"R{Σ;`Dɼ1=ssE $;m2yMz\>p ,&<%6ѼrlqϽM4'W{U'J i=i en𓖕#`LȍLl0nݛ=]nj3֟Ix5Q׻ԛ{@d $jFր 51F 3;ub"fqc{Lun %6"#%<Ѐ&<T5jЬ l|Y1`!aIqɌI'?,9@T!^,u ^'9TejS0qpno> :ln튷 >i>A }Oxs=mɇos!&:J{W6yƔo9iMPbO&jO}F'O{Qĥ0NL@oTP^|j'_&g%hvQGkM҆gct4(mѶxT_Wk6xgw`/חfU}قzVpRg|VV`)qu֐GfIg?֠f2ƚNp뤉i`u^$f:GOjFHNEzkv9 g~ٗYZPYgF*9y65'VuF z1uSVPpRb`>/wpW_CJ1:Oj[f^פJ` 0Y` {v ni4nic `>H|Fg'Ş?ʙ|j@[rtn`HCن1$΄ڂ'Q+_@ʪU钐yC֌f9S E%[~̚}JCzRІ zDʏmZ٭0R~5Z9ħzDگ棶 D9 ۰;[{۱h9";yJ(n`騲.d9XCzԜcH62C,CMpd}JKF).A!p)Zk٘AKjKDUKV5T!0m *0d`1+5 L\Ȩe[tyAWU;3T_+h0-e Zi5)I 7pVjy8l}X*$p'4Z a[k 5E;C=_^[+YEVi[`DZ>1[Yz{G'ѹ"0u'0np)@`| `Em9ԉ;OKFP:"Q,1HKLY)+'xK/E&kCu- bM ldozVVQ8O'0Z``)@b{^,Zi:kG k VV[llmr;p|pƀ ZѹPȈ<\ɝ&Wzɓ|E0&06 5^.ځ9Ec@hgX~˘d@Hjg6:Nțrl}a.<c|H ܿ~,{qh̢ysҜ e,\@2\Ʉ$U<#|+& m3q|G[PkW8XC=FbFJZ_UkPkx<v eU8sP6mӚL LW:vI`LпpLv|P[~lJ C`i,lZ"Z>PȟȎН[]ю|G ДlׯʤuL3Ѿì,-x{Z魩:|q^ K&;: ]\@D BP]mIIF CLAz]tՇ$ TEO`b]-p q ~KH{`m 2kQY3DXC8uخG37hԠ=gW P|ڥ=}[|>J-l{=Ώ+N!} L}~#0nҍ9ʝ["0-]ʻ4m{,>kMri,ǃ-&%D?^bVVzNM9}(bMZQG*J3mLT eӳΌͥ8[mMPک}KǮW^Zv-,^%xm Fn~3AT^4*24w`HޖxR]voFBB[@ܸ͸]-ǰ.vrN=~lbMv,J`6ɝnɛLCbDtM= 8ȋ1cتX)&YHE"W.#T@\Q n}it>BBv.R$~ ,BEH'6~mf떙V0v y.mtA ̫ư8Z:) ~{@Ӡ#g nxNt؇\;荮 "73_ݟʭ1 Оm Tg4dًwFkp0>-Cp`.ՀB 0WDZ63nک#]S׷V0W ڊ]N$]&bBB [ZDZFP+0PgiEhi[Tp[8n{Q*CFC^HHP˞Ʀ-` -2"36p٠7) B pb&P?uVcСK\lg'J.*@tp)4DǑd\)J+HP˖^ElבUt,fPxA9_fo7Xa@fljâ/n2*m\{m&n(k 0_ ĭL񵦁=N#JdvHxy i[ DFVmա={曯2K>=\ %<̿Ɔ>k u[k8;s}($G]}/a]kP:6HOA5V`)$ # ɵ I,QLPaQMdqQy R!"LR%&R)+R-/ S1,3LS5l7S9;S=? TA -CMTEmG!TI)K1TM9OA UQI-SQMUUYmWaUYi[qU]y_ Va-cMVemgVikVmo Wq-sMWumwWy{W} !d, l@0I8ͻ`(dihlp,tmx|pH,Ȥrl:ШtJZجvbwL.zn|N\}dToQėy_Rs? H*\ȰÇ#JHŋ3jȱȣǏ C ɓ&G\ɲ 0KIM1aɳǜ(} J!ГE*]hJPJeTԫXr*@ׯ`p KԱfӪv۷3KܺxV߅| 00ÈmN̸1ժ#K^x*cΠ9MzҨNzְM{ڸ;N<ȓOg;questionary-2.0.1/docs/images/select.gif000066400000000000000000000475751447660764300203030ustar00rootroot00000000000000GIF89aGkll#"KLL{{|GGH++,ċ"#$678344sstccdWWX 789[\\g$8Pqը:TAG_*sWv#nJ NNxB[puV9tpS5 #t&D.u"D-I$0Flp BXa8;.pY9 K(sDH.@DZ" ="cd3^УjMt%Ik*i%B[ JqfC馛BhsU` /5M79TlFD^q:]˂ .Q dZP`! *Ii"q9tnd;/ƘۏB8)DHCY(BpUln0l2G[erM KkEViP"SB8NK-P =`fpk L>ajr♧5A.C&zE 痄V:nSJ^ܨs4jbDuDu` DB q NC,&Y@;T`DZ3 hFSLGD<@Hמ(Hܢ2l9DH 7ߎwY'ho&jӌ',dߺ}럀/5TB8z-aZCHl1DNR VM+J\pͲ8.5ЪF1^S؊DﭏLnd<\el]GGK}RQȉ7zW)t#NɌ#6tEP"'AzAǩ2 !H$E5lCS'n[3H E'<;nhLG- \5cEIRhIzt z4xg;T$ Ov-dR='.R{Cc5ܒU,ó+Gn"!,atE^r#;8*+ ="1C!o&ȹ@%|$jYp:f h' -{`N`9}nsx)!!,   dihlp,tmx|pH,Ȥrl:ШtJZجvxKIm@M=x^R|Bx$ (s-~{wC Q*yx'q'drCQ]&ŌBL #&z#'$ = 3or"]7pnA^)~B $`[Ax ]6TSJly-`p h󤷔}|3<DXZ%"Sk"CXdN?asJ(dF<=EuK:zD)gC<*I(3!l ZX\v>۬VnXk-,j,z\B覭=];@D`M(+ZX{GqW/ت=AbUbi@v` l /Վc+ P;o$l0<l3t/,2FL[2 B ؞LU1R_K.b+e&x.(ZAE V(Y!=cɽrCjYtRGk'CnEr4J4d%#I#DCdEBBc0;` @VD&lK$F тpD^k46]5,#@B%sr_v@=@<ڞ'>lNX6.j*wv3aihנz>-`=qGxrR<˙?H0ANb́Є|Y?{Cd=I8~/\|3sq?sk!7[>p][7.z8Sd4,ԃFѼ'M>T+@cVAk ?EcmEJo5W5n- -HJ:vwR>ֈ5fVnJ|? y 5 5 Pi[ pK@vzř pKMr:ЍtKB"!, @` dihlp,tmx|pH,Ȥrl:ШtJZجvzxL.zi|N~kStJ i5 NpI ihſ3 M؉=(gB Q*=ľlR¥*PH7wPK0~3jњ ;$ 4P:ձ͛8s @ dj(S> S@ lpЌ:G1N:#&M/zt A<(6ծ՝x+u`xѳYxiV5[K= 6E Y:ׅ۸s`B@g5V)xAKй2902 [fSPuۉ(-Ap0'[ Jg"1RX#qRd)x֋ɵaMN68jB95;Bv\D ߧK;V2`#1H2X 4 0+xH\.e[NBVJss: 1LLQ<5 POTu?]72*澃 Ȇc*!/ w, 2D A]օ9M.V ^vkҒ e$m,P0+s}&kᦰmmr0O FmJAE%X9cm6LGIR=5E{$(7zPN.ÙIEye)! ˕)#$)roTA LUkr,j0ΒI'-0:7~N6IDKh&&S;88]ks68^= LàJ_9t=bJ6[-%gi2À%4bdKzR(=|QE=4$!+픧-$_2$AE](S"J/ERYz` k,+2X`< I&GF,yUo04\8@_OJNl _ީZ 虎|/L6>uDXW֑5Є't$9 Ԗs $fMs7+W Axa'ءp;M{6ڒorʠ}$k] !#K0W*VzG*Gkq4+ߊУ%V!'cpZ0`€p!=G_1 V(.gk4L8wcx(hrF @|Pȏ &V>{!({H`v8)YNP4>cE:xγ>πMBЈNF;ѐ'MJ[Ҙδ7!͏ӠGMj5x:NWVugMZw^S׾M?N݊c3Ўi[ζn{^Mr>nY1y>t~NfúO;B#N[7 ȥ8W0y_.<4w>@/σNzFOS;n:ԧNGKXz{}!!<, ,D{pH,Ȥrl:ШtJZجvzQ@.zn|N~~xwRgoSq| EfC44_ddyQd l|ÞOG Y ]Vngj dC DlRWB=805Z(`! 2oy v9. 9@&=ta2kE#2Ƙ"'լN|x႓G#:949eꛗ i 0 `> &XpPh7!/(8IߠR1( HAyqZX4؀p_)TYِuV2K= c^i-n\Z 0T.d鼨'#\sש+[1ZmJH]HKY#S-9| qHOh,L$tQBeT@ z 2f̷`_-)_]ǜ%0`)PTHXpBniyhK/JBeXHċ#&9¸W A.=e~51dD3#&r$#Lk#:yđ"5&,E91`;aiǒelIcg ew!sL*& :bH̵ 8L7z'W1ӥBa֨AhhEQXd"GJ$ʦM~"ZF(gzgb&)MrtX Drp3\gUr?tHmBZE8uL Z pB Tn(N8Aio 2)RXk AH5%+`2,*gQ1ά-љ{=-3xɥ{%v( ;h•⚞3ۺ7>V?_7/f&ֶDkWQ(DOY;F?Z5+(Z8po`X\b& 1)wf# 6JKAMsni#3-2,㵬bY Q4Gyxn#JrKQ3gPf5!'aU{. YV|n_{^H"C7(qiHs5[ 0=OF(ytp%S;n IT:#3#Љ?DIzD/$2e .D8xj@#J"(hqbӞ |H,"!3(cM,bәǀ yk.c¬vFsݴ0.eRVh^$E&l9S"| rbg*O CiW IKIДw (Q%  d.3*!Ay DBX** {pܽ&G+ rp!^_ _[͆Yr//5Ta'`XA3apV[(Qd &u?07!7j&izG+!jbr&5-bg366 g4gْuj2fqj.<@>DY8IQFJfjKPR9TYVyXZ\ٕ^`b9dYfyhlxj ZsafHR#*nj֗)wݔ/9p|Ň >|!yE!5!@hx1&ᄌ}0a# 8 t@B 3T'E9f9F~_Pzg~KS1ƏXԡ{"[6ę.],12&G \59Ia{ҷ9IbX.r86%*GQb3b?((b9W H(fyhgq YáS*4VF~\m/ׂ\PCmfa<'MYL| XV0K H854ٸ7T sXRH 6CEz,W;M"DJ' %z).4q:HkJv!q3%]JG)d:siWTcR2c,=oMo>~9c\*#uD E^1r΀19"6dtM czzxyH_T(|) i"r;aJZR$C8^w.*DNisPpZQjsCxy`*DxG񂁈ڕxu:tXjZrd"4č2fwӈx %fՙh0of׈%5^#[EuŵFZ*GKԐI{FKRy4Ҙe%JL"iMBrkБEf9M+IbQ fV;+k6fwjeBe|hPc;t46V[׆w۹;[{ۺ;[{[0i騹&{sznfpzZgnZ!u#tЅq1VwZP2pG0ˇۻqiVU{X&yїÛHAZ )bǚ̴yiaqٛ!NTk ,k @_{W0+ƈ թɻ-g"t?[?mz57Nۜ=~M)sZ{9~!,V +P` dihlp,tmx1Rh"O$)B*MAWt #2 6@(`(D\; !<,v +P` dihlp,tmx1Rh"O$)B*MAWt #2 6@(`(D\; !, ,v{pH,Ȥrl:ШtJZجvzxL.zn|N~vV]nHV769[= Z9m$˱ؓ9$S6ZX)G)>Z/:aAP:[ȰÇ .>\ v0 CIӸtG Ht),=0@"4epr4৪"$5h4Cԫ հr"S7^]©4v}Jd̨o9MaPkW%ב=fGNNިic1䀑U2r'f #B29WC~bV40uC$3@ A09kOLnrc"]?]μC&1Cb*~w`={_-#!.obB`g߸:f歖Z[h܇RbI܄V88[0IT!l `tOpI'Uqu+9{C!#P_]w)> '* $ra\v)_dÈi8r'xtD mYeE&F g77AfpÜIz駠Z_tϏ&Fy6 0umJDpȚ"BD)`cRjfW&BJ BV+w^Jpj'+nڹM*9MbYTxnY[Cl mg7R_d +i+V]r7t2Y1R,8(/ *u8쯙ک޽K/ 0Rnq,%k^2E;K"`B(=;bĦv]kRzg'0򧅐l^'fuu #mw䠶Zx@FRZ;4% j/[] f쏎NQxC Olj]?= kH'Ih2o|7>l([٧- Z'9i}gһM̈́O{͍6 @1ی^F~5Ԗ"9]/pv]4հ~2gpO$ȓSX>лb&:̳]NLeY&ZԧS/}ɇęqt?7eixS=c1EK:An߻04ߜ^B}#QxV`av~giQqq Quox7ywngb~]uWׇ{ug}wes&d'l{XwxXXȧgus|]lzTbu}~Rep6m?~Xmgsɗw5FacݥuP!m.RCHY~F!Z{\R)7x4r]cw&{w&u yXt')WX9ƆZpt0h{|[E>@Z%v3}Qe0ؖU i}؉G0H~ehzW+vW xan')@'ՇUՊDi oW]htitk{HjhNhhyBxdžug+lwmƎxxxD)(vHZfuT8o{HPmAch-RW(L TXkkhUQxǒ炩k㈇8+g(tPI h Ysx%W8%TstVgXP r6idT|Yt5%RuGtշV*THuhI|LbEqLȒ蕃YI 8VrGrf=T鏦VwוKSwb 88gRxui -VUfiS@5Z|ٗ^Opli|oDftgV (m8kh7NicWTՕPWxT9SוYXixy gùi-Yt=8vKfq O~}hfaee&G[uiywbSWNSq9^/)`p"J6߈nԉlS@טof%[']%:Nw%JdPxG[ {J]tjŅm!zaeS `Gw exЇMP (9S9T*vl2wF@sHpuf'Ymuu*Pw'R?9UmǏw":й)o2s/U g'oc[&YʂzTJHoWڢF'_Rw IWooZנM*JZ*G԰)jf+ȬPUIti|H y+ashɉꚕt(gdXS8 &pf(Vw\˳'g9HX 4ڝS<9JN/p`dm%jL{=ԄavP{Xp:[LkԬYxrypD V MiV+;[{蛾껾۾;[ *M[W,Mˢ|lL ߤtk1ZOٱ10pcNŘJprZ_@\\M'!&`3Kp"lOVRָF*h&L1K2&m,d@0gm>COصM0_IڶN. qlU޳{< ->CpHUl2\fZĜl@+s^Xj8u wu{,%F}BP!B H=1l#V- !Z J4s,ƥ]ԡ!Pp3X]VI=Z{"@{lz,0C؆Ǡ,`C-؆>v8k NȞʭɥg7pFw m"loJLz[tOD`Bۖגb(`YVynrK=MW e-OMoT7lx}؀ԠyZ,߆-ʉ!߷]G9YߙNH- j7YRcҒ5xA|FGiVI  ] QmMp @Ǯ- Cuq4<a]I]FJQLYaD\  GqʗKH߮, `1"UNI5Ȋqqa7yTٹܾ=)6H8޿-xkM#C.2,T|GT.ZLA_׹Nm|]*EpUE$-݈ av>s𺳙~FVHuўM߸7+n-. ͙ qK`!:p'@Jw'5i D~ 묔P ލ*PB*C0٫NzNFpڌ+l/[ͻS7W  \-Ք(`~*^/DRhdէ@P~ X~ApB]"juM1 sN i9K`ُj5Ga Ժ dǍQX׍NO[/.`F՚,RJ߳(=b"z_[,o6ot qM瑧Yy9C,;ƾLW]Yj S( F2@<-9 H% jK]81]Hd\TK'l.sUȖ< ` IZ$I@&47991?ACEGIK4'&434 X\24^4+84 s$z1 1#kM< QȝC\CF\ڻm.%fyf!7q4 H&vA q]ذ`hG| @R K"PSM߀:hrȀؒ (HtܴO xU Bl֦ Py ٳӪ]ԕh].ZPRB E8YDXAE)-{ ("-KJQ 6D 6‘C$ -Yo!M-TDE(P>}?<cN7 % LY' ȊSۣdk(Kǡ}phHBPP4%:$b2b7$նጝ)@1Biǎ8G ML.&NRJr Z8!@|h̢eWؤ`Tf=2ī#@ `r& ;3rB5C/d )4ͥ$AN3-Couˈ"RM)$5VKEGHnI VXb{:0P&HeXͩdoyLI5} [fp[P$͝mݢ}1ןW_rtiV  k NxFtv^Dr} ⋅%XaHE*X3db8CT+~fQXo-{!MdvYߊ[Z_Pyp@OD!R F*]ɮn[+/O<o!c%1\5A]I/QO]Yoa]iq]y߁^/VO^o硏^驯^_/O_o__@4h !d, l@` dihlp,tmx|pH,Ȥrl:ШtJZwvzxL.zn|N+-~`.\t{l†iխۮyw p "\†#Fz(BW2jȱǏ CIɓ(Sժ\ɲ˗0cʜI͛8st!ϟ>w JH{]ʴSI>Jͨ@j$֟]Ê[kPhӪ%bVڷph+@ݻxK˷ڽ~ xK #^Ɛ#|,呔/kޜ13ϠxM4۶S2z1ZÞMڸi{IN|%O|?N=^=~Oj;questionary-2.0.1/docs/images/text.gif000066400000000000000000000322311447660764300177670ustar00rootroot00000000000000GIF89aGkll345"#$KLL[\]789GGH+,,ccdstt{||WWX$#ǰԚhu^>GݯxgČ;&ɔF9fy7{ z[ФK6լ_n ;lOg۾;ݼ{ <ċ?<̛;=ԫ[=ܻ{>˛?>ۻ?ۿ?`H`` .`>aNHa^ana~b"Hb&b*b.c2Hc6ވc:c>dBIdFdJ.dN> eRNIeV^eZne^~ fbIfffjfn grIgvމgzg~ hJhh.h> iNJi^ini~ jJjjj kJkފkk lKll.l> mNKm^mnm~ nKn枋nn oKoދoo pLpp /p? qOLq_qoq r"Lr&r*r. s2Ls6ߌs:s> tBMtFtJ/tN? uROMuV_uZou^ vbMvfvjvn wrMwvߍwzw~ xNxx/x?yONy>_yoyz袏Nz馟zꪯz뮿{N{ߎ{{|O|!V,V!,D!5-,  @pH,Ȥrl:ШtJZجvzxKD`UBv ++nG9ů0Lj`;+5=bdvSHy5x%R,@U #˄e#vB`UYu&1/px1W7ǫz K3lʳU5)M+4XX=t2Ůa#9&K8C8cV 8l^;C055L$ N0* |' ,BIݖzJ*P~?|lh*bsfwBNV 7gN!6ϹwsU@ЇHOq;O!, ~ ` dihlp,tmx|pH,Ȥrl:ШtJZJ]jۮx|<4h긼Ui($yηIoPDux}Mm&fp0 PvCK%.OD-#Z%o#aZ>3Գ$#x_y)%/fw)#!.׈odfgzK66kFG .a d3 lhagdj X  D%2ϖ< OHeAhuQR0AB^ɺlquVfa'녬C۶O"lSȖ &${3@D#^TAK nME?>%'R oL3 aFrH83N-#gЂ߼j) #L=k<t%At'=r`YU{bk [4X#HvR7 _xE ơ5Dl6$AmVTyX†9N4Zsq*JZ5O3|h=\xiوHFe&^iޏi o:Ղh{]%{]h)%]*Tcey8`j9m, %O2&'e≥fxz:W1I/z2XsmJEn"I*"#R_ZiEpöM5U $>rc@^ab3j4M#*0#\w8!F<digQWx~)&2A @t'paZr}PgS#`\X*  O{wBV`;ob@ѡtwʚ#q0G-UO jODs{쨮ך#vw% (:זڈk8t|4M>S i΀'ӣL[`l$:!0[%NYb$oD\Y#~iLB3 Np 3"(}.p2 yp^Gߤ8?$ (OoS:c@EEJ5/2 5D'*Q*'`@Pq<4Ӑs `G9u"Қ8ͩNwӞ@ PJԢc!, i@ dihlp,tmx|pH,Ȥrl:ШtJZجvzxL._zn|N~h-qJ 1mI  ,Ȇ=(gO D='eP D) w |bߺo#JH"]@ )]21O JxiaB!l)sGx*PsFPU 3 8r@"P 7L VZs@?ڄVg ͪVnD 9w9,SBLjǐ#Ks]4^DTG.BUBh#Y{'ikw /@-j]>bAۧ[;m^~I,4qta-h/&Ͽ v',e `AƱ׃|e&rve\;b Am&h/Z &&xO{d(ҁj'##U+b~E)T`?ZbtP0Xo! pl: ufۤ6X]AAi$~ҥ؞i 84tB8vC jީ_yhˈ )jP嫰*+}&'5mV`jN==UKX sҤY!aqn1X- m5~;=1i n1z[$Xj.G般l0d2&\0ݖTwnKƊK1 bxl̳-S(3A8KdT˦G+抨\ 2Ɛ.'\ݰwE"x.ֵ\F4[ApdmIp lO'ƬVB5fƒwf73wY-̖k Ch8r3b1Xf.쯠=n97s'Kq͂'oAl. ! ةh,%@7O_kUV~ߴ4I"n- hGpn6Lp#gb4<s)u_g" <@@kp5@tX ުL8{HL@Z\%`wf< s0Į9PKu]oA'lR(uхoޤéP,qx#,`L" <@+#0ˠ[F00t:h$3LCF}4I?: @ 20K,eH#ڼL9A*eqnSČ%%ɂOcYAI]Af"N^;VCEk.,q`74Hr~1kuQhr-gӑ 1Нz%tiM; 2F<%v{g/9sB!Tk|!jF;+M"{!tnƉLKZuw)WLZ5TJՌFiȏKjҦBcMPPӾUҢkA@`av.z*T]bsYL_GK4cLH#ƧDN <ǭ=Z[8 ]i3{[1$pZW [ Q<7[Qϼ+y|Kͯ~LN;B.!L [){ (S]1gL۸w7A>LdPHN %;PNqL*[Se`1hF6y k~Lg)Ĺxγ5=πMBzІNE;hnT4'Mi-KҘδ/N{ǜGPԨSVcϮE Y ^5ww-b>͈!, ,W  dihlp,tmx^|pH,d)ШtJLvzoനhMnzFN3'< Q<t]>%+v@"yzz/ '7*VL$FAN#o-N6Ũkbo 1szrt0_9lY߳]E @p"@K;0Âkn &PqP5 0sA$b[_Os&Z bd5+ZjY \J0t$$/IH:V` "C2R IICޱ̤&xMzt$(GIJ@L%+NV !',D!,V, nIg8ʥWǁXIjW3Im |-0w ")/sprR Pu6, FY*+&ڵ o;Fr&n*z"#}(! ,c,  hg¯\ҪMn1!`X(82."J*T$d1,AN SR?{}wr%pkow#q1k(M\a<"08#^S% mBkZF:T4!!',p,  hg¯\ҪMn1!Qq(a*0h .HN Ez!p*y{k'}w|~rzsX@IGKBF:<@!!, ,~`@pH,Ȥrl:ШtJZجvzxL.zn|Nל~M yzΛ۽L K~j#O*[=HQÊ3V*G]Crɓ$\2ʖ0c*);8sS?{ JѣH*]ʴӧPJJիXjz&B pK, BDp㎍W!'6,8qGػr _c#>LY(0H@ C漦 ~b%W~ǻ7?6 LШ!(6cnyĈ&A;Q#!t"rп>)Cgn)@X H4B .Tv75@]$z Z~gzJDeeAksE&^[Z\ QPd9`X@zID< )pj0,H @Y9@XPd hj : 1GV$u fzonނKVfhB>[P_ ;JE6pzp ;ѐ 84@V1@;|bk"|\.o >'k;Ha6 P'$ W(rH`HC6Ȱ8T sC%@ HD. HD),PlHE$LXÑ`+fKD`UBv ++nG9ů0Lj`;+5=bdvSHy5x%R,@U #˄e#vB`UYu&1/px1W7ǫz K3lʳU5)M+4XX=t2Ůa#9&K8C8cV 8l^;C055L$ N0* |' ,BIݖzJ*P~?|lh*bsfwBNV 7gN!6ϹwsU@ЇHOq;O!d, l@ڋ޼H扦ʶ L' ĢL*̦ JԪR%ڮ &r ߺz'8HXhx9IYiy$鉨):JZjʄ +k:k{X:K\|k  -,]m M}p]. >n~.ZNo._o q?0 <0… :|1ĉ+Z1ƍ;z!ȑ$G<2%[\ 3̙ \3΍6K 4ž$=4QI:}bKTZ 5խ\&5X`ǚ= ,ڵlam 7.ǷrڕH޽ ؟ l8R;~|1ɔ+H9˚;g94dТK'&m:5`Ԫ[e:6\زkm;Wܺ{[;S‹%n<;questionary-2.0.1/docs/index.rst000066400000000000000000000030751447660764300167140ustar00rootroot00000000000000*********** Questionary *********** .. image:: https://img.shields.io/pypi/v/questionary.svg :target: https://pypi.org/project/questionary/ :alt: Version .. image:: https://img.shields.io/pypi/l/questionary.svg :target: # :alt: License .. image:: https://img.shields.io/pypi/pyversions/questionary.svg :target: https://pypi.python.org/pypi/questionary :alt: Supported Python Versions ✨ Questionary is a Python library for effortlessly building pretty command line interfaces ✨ It makes it very easy to query your user for input. You need your user to confirm a destructive action or enter a file path? We've got you covered: .. image:: images/example.gif Creating your first prompt is just a few key strokes away .. code-block:: python3 import questionary first_name = questionary.text("What's your first name").ask() This prompt will ask the user to provide free text input and the result is stored in ``first_name``. You can install Questionary using pip (for details, head over to the :ref:`installation` page): .. code-block:: console $ pip install questionary Ready to go? Check out the :ref:`quickstart`. License ======= Licensed under the `MIT License `_. Copyright 2020 Tom Bocklisch. .. toctree:: :hidden: pages/installation pages/quickstart pages/types pages/advanced pages/api_reference pages/support Examples .. toctree:: :hidden: pages/contributors .. toctree:: :hidden: pages/changelog questionary-2.0.1/docs/pages/000077500000000000000000000000001447660764300161455ustar00rootroot00000000000000questionary-2.0.1/docs/pages/advanced.rst000066400000000000000000000250621447660764300204510ustar00rootroot00000000000000***************** Advanced Concepts ***************** This page describes some of the more advanced uses of Questionary. Validation ########## Many of the prompts support a ``validate`` argument, which allows the answer to be validated before being submitted. A user can not submit an answer if it doesn't pass the validation. The example below shows :meth:`~questionary.text` input with a validation: .. code-block:: python3 import questionary from questionary import Validator, ValidationError, prompt class NameValidator(Validator): def validate(self, document): if len(document.text) == 0: raise ValidationError( message="Please enter a value", cursor_position=len(document.text), ) questionary.text("What's your name?", validate=NameValidator).ask() In this example, the user can not enter a non empty value. If the prompt is submitted without a value. Questionary will show the error message and reject the submission until the user enters a value. Alternatively, we can replace the ``NameValidator`` class with a simple function, as seen below: .. code-block:: python3 import questionary print(questionary.text( "What's your name?", validate=lambda text: True if len(text) > 0 else "Please enter a value" ).ask()) Finally, if we do not care about the error message being displayed, we can omit the error message from the final example to use the default: .. code-block:: python3 import questionary print(questionary.text("What's your name?", validate=lambda text: len(text) > 0).ask()) .. admonition:: example :class: info The :meth:`~questionary.checkbox` prompt does not support passing a ``Validator``. See the :ref:`API Reference ` for all the prompts which support the ``validate`` parameter. A Validation Example using the Password Question ************************************************ Here we see an example of ``validate`` being used on a :meth:`~questionary.password` prompt to enforce complexity requirements: .. code-block:: python3 import re import questionary def password_validator(password): if len(password) < 10: return "Password must be at least 10 characters" elif re.search("[0-9]", password) is None: return "Password must contain a number" elif re.search("[a-z]", password) is None: return "Password must contain an lower-case letter" elif re.search("[A-Z]", password) is None: return "Password must contain an upper-case letter" else: return True print(questionary.password("Enter your password", validate=password_validator).ask()) Keyboard Interrupts ################### Prompts can be invoked in either a 'safe' or 'unsafe' way. The safe way captures keyboard interrupts and handles them by catching the interrupt and returning ``None`` for the asked question. If a question is asked using unsafe functions, the keyboard interrupts are not caught. Safe **** The following are safe (capture keyboard interrupts): * :meth:`~questionary.prompt`; * :attr:`~questionary.Form.ask` on :class:`~questionary.Form` (returned by :meth:`~questionary.form`); * :attr:`~questionary.Question.ask` on :class:`~questionary.Question`, which is returned by the various prompt functions (e.g. :meth:`~questionary.text`, :meth:`~questionary.checkbox`). When a keyboard interrupt is captured, the message ``"Cancelled by user"`` is displayed (or a custom message, if one is given) and ``None`` is returned. Here is an example: .. code:: python3 # Questionary handles keyboard interrupt and returns `None` if the # user hits e.g. `Ctrl+C` prompt(...) Unsafe ****** The following are unsafe (do not catch keyboard interrupts): * :meth:`~questionary.unsafe_prompt`; * :attr:`~questionary.Form.unsafe_ask` on :class:`~questionary.Form` (returned by :meth:`~questionary.form`); * :attr:`~questionary.Question.unsafe_ask` on :class:`~questionary.Question`, which is returned by the various prompt functions (e.g. :meth:`~questionary.text`, :meth:`~questionary.checkbox`). As a caller you must handle keyboard interrupts yourself when calling these methods. Here is an example: .. code:: python3 try: unsafe_prompt(...) except KeyboardInterrupt: # your chance to handle the keyboard interrupt print("Cancelled by user") Asynchronous Usage ################## If you are running asynchronous code and you want to avoid blocking your async loop, you can ask your questions using ``await``. :class:`questionary.Question` and :class:`questionary.Form` have ``ask_async`` and ``unsafe_ask_async`` methods to invoke the question using :mod:`python:asyncio`: .. code-block:: python3 import questionary answer = await questionary.text("What's your name?").ask_async() Themes & Styling ################ You can customize all the colors used for the prompts. Every part of the prompt has an identifier, which you can use to style it. Let's create your own custom style: .. code-block:: python3 from questionary import Style custom_style_fancy = Style([ ('qmark', 'fg:#673ab7 bold'), # token in front of the question ('question', 'bold'), # question text ('answer', 'fg:#f44336 bold'), # submitted answer text behind the question ('pointer', 'fg:#673ab7 bold'), # pointer used in select and checkbox prompts ('highlighted', 'fg:#673ab7 bold'), # pointed-at choice in select and checkbox prompts ('selected', 'fg:#cc5454'), # style for a selected item of a checkbox ('separator', 'fg:#cc5454'), # separator in lists ('instruction', ''), # user instructions for select, rawselect, checkbox ('text', ''), # plain text ('disabled', 'fg:#858585 italic') # disabled choices for select and checkbox prompts ]) To use the custom style, you need to pass it to the question as a parameter: .. code-block:: python3 questionary.text("What's your phone number", style=custom_style_fancy).ask() .. note:: Default values will be used for any token types not specified in your custom style. Styling Choices in Select & Checkbox Questions ********************************************** It is also possible to use a list of token tuples as a ``Choice`` title to change how an option is displayed in :class:`questionary.select` and :class:`questionary.checkbox`. Make sure to define any additional styles as part of your custom style definition. .. code-block:: python3 import questionary from questionary import Choice, Style custom_style_fancy = questionary.Style([ ("highlighted", "bold"), # style for a token which should appear highlighted ]) choices = [Choice(title=[("class:text", "order a "), ("class:highlighted", "big pizza")])] questionary.select( "What do you want to do?", choices=choices, style=custom_style_fancy).ask() Conditionally Skip Questions ############################ Sometimes it is helpful to be able to skip a question based on a condition. To avoid the need for an ``if`` around the question, you can pass the condition when you create the question: .. code-block:: python3 import questionary DISABLED = True response = questionary.confirm("Are you amazed?").skip_if(DISABLED, default=True).ask() If the condition (in this case ``DISABLED``) is ``True``, the question will be skipped and the default value gets returned, otherwise the user will be prompted as usual and the default value will be ignored. .. _question_dictionaries: Create Questions from Dictionaries ################################## Instead of creating questions using the Python functions, you can also create them using a configuration dictionary: .. code-block:: python3 from questionary import prompt questions = [ { 'type': 'text', 'name': 'phone', 'message': "What's your phone number", }, { 'type': 'confirm', 'message': 'Do you want to continue?', 'name': 'continue', 'default': True, } ] answers = prompt(questions) The questions will be prompted one after another and ``prompt`` will return as soon as all of them are answered. The returned ``answers`` will be a dictionary containing the responses, e.g. .. code-block:: python3 {"phone": "0123123", "continue": False}. Each configuration dictionary for a question must contain the following keys: ``type`` (required) The type of the question. ``name`` (required) The name of the question (will be used as key in the ``answers`` dictionary). ``message`` (required) Message that will be shown to the user. In addition to these required configuration parameters, you can add the following optional parameters: ``qmark`` (optional) Question mark to use - defaults to ``?``. ``default`` (optional) Preselected value. ``choices`` (optional) List of choices (applies when ``'type': 'select'``) or function returning a list of choices. ``when`` (optional) Function checking if this question should be shown or skipped (same functionality as :attr:`~questionary.Question.skip_if`). ``validate`` (optional) Function or Validator Class performing validation (will be performed in real time as users type). ``filter`` (optional) Receive the user input and return the filtered value to be used inside the program. Further information can be found at the :class:`questionary.prompt` documentation. .. _random_label: A Complex Example using a Dictionary Configuration ************************************************** Questionary allows creating quite complex workflows when combining all of the above concepts: .. literalinclude:: ../../examples/advanced_workflow.py :language: python3 The above workflow will show to the user the following prompts: 1. Yes/No question ``"Would you like the next question?"``. 2. ``"Name this library?"`` - only shown when the first question is answered with yes. 3. A question to select an item from a list. 4. Free text input if ``"other"`` is selected in step 3. Depending on the route the user took, the result will look like the following: .. code-block:: python3 { 'conditional_step': False, 'second_question': 'Test input' # Free form text } .. code-block:: python3 { 'conditional_step': True, 'next_question': 'questionary', 'second_question': 'Test input' # Free form text } You can test this workflow yourself by running the `advanced_workflow.py example `_. questionary-2.0.1/docs/pages/api_reference.rst000066400000000000000000000006551447660764300214740ustar00rootroot00000000000000.. _api-reference: ************* API Reference ************* .. autoclass:: questionary::Choice :members: .. autoclass:: questionary::Form :members: .. autoclass:: questionary::Question :members: .. autoclass:: questionary::Separator :members: .. autoclass:: questionary::FormField :members: .. automethod:: questionary::form .. automethod:: questionary::prompt .. automethod:: questionary::unsafe_prompt questionary-2.0.1/docs/pages/changelog.rst000066400000000000000000000127241447660764300206340ustar00rootroot00000000000000.. _changelog: ********* Changelog ********* 2.0.1 (2023-09-08) ################### * Updated dependencies. * Fixed broken documentation build. 2.0.0 (2023-07-25) ################### * Updated dependencies. * Modified default choice selection based on the ``Choice`` value. Now, it is not necessary to pass the same instance of the ``Choice`` object: the same ``value`` may be used. * Fixed various minor bugs in development scripts and continuous integration. * Improved continuous integration and testing process. * Added pull request and issue templates to the GitHub repository. * Implemented lazy function call for obtaining choices. * Expanded the test matrix to include additional Python versions. * Added the ability to specify the start point of a file path. * Enabled displaying arbitrary paths in file path input. * Allowed skipping of questions in the ``unsafe_ask`` function. * Resolved typing bugs. * Included a password confirmation example. * Now returning selected choices even if they are disabled. * Added support for Emacs keys (:kbd:`Ctrl+N` and :kbd:`Ctrl+P`). * Fixed rendering errors in the documentation. * Introduced a new ``print`` question type. * Deprecated support for Python 3.6 and 3.7. * Added dynamic instruction messages for ``checkbox`` and ``confirm``. * Removed the upper bound from the Python version specifier. * Added a ``press_any_key_to_continue`` prompt. 1.10.0 (2021-07-10) ################### * Use direct image URLs in ``README.md``. * Switched to ``poetry-core``. * Relax Python version constraint. * Add ``pointer`` option to ``checkbox`` and ``select``. * Change enter instruction for multiline input. * Removed unnecessary Poetry includes. * Minor updates to documentation. * Added additional unit tests. * Added ``use_arrow_keys`` and ``use_jk_keys`` options to ``checkbox``. * Added ``use_jk_keys`` and ``show_selected`` options to ``select``. * Fix highlighting bug when using ``default`` parameter for ``select``. 1.9.0 (2020-12-20) ################## * Added brand new documentation https://questionary.readthedocs.io/ (thanks to `@kiancross `_) 1.8.1 (2020-11-17) ################## * Fixed regression for checkboxes where all values are returned as strings fixes `#88 `_. 1.8.0 (2020-11-08) ################## * Added additional question type ``questionary.path`` * Added possibility to validate select and checkboxes selections before submitting them. * Added a helper to print formatted text ``questionary.print``. * Added API method to call prompt in an unsafe way. * Hide cursor on select only showing the item marker. 1.7.0 (2002-10-15) ################## * Added support for Python 3.9. * Better UX for multiline text input. * Allow passing custom lexer. 1.6.0 (2020-10-04) ################## * Updated black code style formatting and fixed version. * Fixed colour of answer for some prompts. * Added ``py.typed`` marker file. * Documented multiline input for devs and users and added tests. * Accept style tuples in ``title`` argument annotation of ``Choice``. * Added ``default`` for select and ``initial_choice`` for checkbox prompts. * Removed check for choices if completer is present. 1.5.2 (2020-04-16) ################## Bug fix release. * Added ``.ask_async`` support for forms. 1.5.1 (2020-01-22) ################## Bug fix release. * Fixed ``.ask_async`` for questions on ``prompt_toolkit==2.*``. Added tests for it. 1.5.0 (2020-01-22) ################## Feature release. * Added support for ``prompt_toolkit`` 3. * All tests will be run against ``prompt_toolkit`` 2 and 3. * Removed support for Python 3.5 (``prompt_toolkit`` 3 does not support that any more). 1.4.0 (2019-11-10) ################## Feature release. * Added additional question type ``autocomplete``. * Allow pointer and highlight in select question type. 1.3.0 (2019-08-25) ################## Feature release. * Add additional options to style checkboxes and select prompts `#14 `_. 1.2.1 (2019-08-19) ################## Bug fix release. * Fixed compatibility with Python 3.5.2 by removing ``Type`` annotation (this time for real). 1.2.0 (2019-07-30) ################## Feature release. * Allow a user to pass in a validator as an instance `#10 `_. 1.1.1 (2019-04-21) ################## Bug fix release. * Fixed compatibility with python 3.5.2 by removing ``Type`` annotation. 1.1.0 (2019-03-10) ################## Feature release. * Added ``skip_if`` to questions to allow skipping questions using a flag. 1.0.2 (2019-01-23) ################## Bug fix release. * Fixed odd behaviour if select is created without providing any choices instead, we will raise a ``ValueError`` now `#6 `_. 1.0.1 (2019-01-12) ################## Bug fix release, adding some convenience shortcuts. * Added shortcut keys :kbd:`j` (move down the list) and :kbd:`k` (move up) to the prompts ``select`` and ``checkbox`` (fixes `#2 `_). * Fixed unclosed file handle in ``setup.py``. * Fixed unnecessary empty lines moving selections to far down (fixes `#3 `_). 1.0.0 (2018-12-14) ################## Initial public release of the library. * Added python interface. * Added dict style question creation. * Improved the documentation. * More tests and automatic Travis test execution. questionary-2.0.1/docs/pages/contributors.rst000066400000000000000000000050351447660764300214370ustar00rootroot00000000000000******************* Contributor's Guide ******************* Steps for Submitting Code ######################### Contributions are very much welcomed and appreciated. Every little bit of help counts, so do not hesitate! 1. Check for open issues, or open a new issue to start some discussion around a feature idea or bug. There is a `contributor friendly tag`_ for issues that should be ideal for people who are not familiar with the codebase yet. 2. Fork `the repository `_ on GitHub to start making your changes. 3. `Install Poetry `_. 4. Configure development environment. .. code-block:: console make develop 5. Write some tests that show the bug is fixed or that the feature works as expected. 6. Ensure your code passes the code quality checks by running .. code-block:: console $ make lint 7. Check all of the unit tests pass by running .. code-block:: console $ make test 8. Check the type checks pass by running .. code-block:: console $ make types 9. Send a pull request and bug the maintainer until it gets merged and published 🙂 .. _`contributor friendly tag`: https://github.com/tmbo/questionary/issues?direction=desc&labels=good+first+issue&page=1&sort=upd Bug Reports ########### Bug reports should be made to the `issue tracker `_. Please include enough information to reproduce the issue you are having. A `minimal, reproducible example `_ would be very helpful. Feature Requests ################ Feature requests should be made to the `issue tracker `_. Other ##### Create a New Release ******************** 1. Update the version number in ``questionary/version.py`` and ``pyproject.toml``. 2. Add a new section for the release to :ref:`changelog`. 3. Commit these changes. 4. ``git tag`` the commit with the release version number. GitHub Actions will build and push the updated library to PyPi. Create a Command Line Recording ******************************* 1. Install the following tools: .. code-block:: console $ brew install asciinema $ npm install --global asciicast2gif 2. Start the recording with ``asciinema``: .. code-block:: console $ asciinema rec 3. Do the thing you want to record. 4. Convert to gif using ``asciicast2gif``: .. code-block:: console $ asciicast2gif -h 7 -w 120 -s 2 output.gif questionary-2.0.1/docs/pages/installation.rst000066400000000000000000000026161447660764300214050ustar00rootroot00000000000000.. _installation: ************ Installation ************ Use a Published Release ####################### To install Questionary, simply run this command in your terminal of choice: .. code-block:: console $ pip install questionary Build from Source ################# Installing from source code is only necessary, if you want to make changes to the Questionary source code. Questionary is actively `developed on GitHub `_. You can either clone the public repository: .. code-block:: console $ git clone git@github.com:tmbo/questionary.git Or, download the tarball: .. code-block:: console $ curl -OL https://github.com/tmbo/questionary/tarball/master .. note:: If you are using windows, you can also download a zip instead: .. code-block:: console $ curl -OL https://github.com/tmbo/questionary/zipball/master Questionary uses `Poetry `_ for packaging and dependency management. If you want to build Questionary from source, you must install Poetry first: .. code-block:: console $ curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python3 There are several other ways to install Poetry, as seen in `the official guide `_. To install Questionary and its dependencies in editable mode, execute .. code-block:: console $ make install questionary-2.0.1/docs/pages/quickstart.rst000066400000000000000000000054501447660764300210750ustar00rootroot00000000000000.. _quickstart: ********** Quickstart ********** Questionary supports two different concepts: - creating a **single question** for the user .. code-block:: python3 questionary.password("What's your secret?").ask() - creating a **form with multiple questions** asked one after another .. code-block:: python3 answers = questionary.form( first = questionary.confirm("Would you like the next question?", default=True), second = questionary.select("Select item", choices=["item1", "item2", "item3"]) ).ask() Asking a Single Question ======================== Questionary ships with a lot of different :ref:`question_types` to provide the right prompt for the right question. All of them work in the same way though. Firstly, you create a question: .. code:: python3 import questionary question = questionary.text("What's your first name") and secondly, you need to prompt the user to answer it: .. code:: python3 answer = question.ask() Since our question is a ``text`` prompt, ``answer`` will contain the text the user typed after they submitted it. You can concatenate creating and asking the question in a single line if you like, e.g. .. code:: python3 import questionary answer = questionary.text("What's your first name").ask() .. note:: There are a lot more question types apart from ``text``. For a description of the different question types, head over to the :ref:`question_types`. Asking Multiple Questions ========================= You can use the :meth:`~questionary.form` function to ask a collection of :class:`Questions `. The questions will be asked in the order they are passed to `:meth:`~questionary.form``. .. code:: python3 import questionary answers = questionary.form( first = questionary.confirm("Would you like the next question?", default=True), second = questionary.select("Select item", choices=["item1", "item2", "item3"]) ).ask() print(answers) The printed output will have the following format: .. code-block:: python3 {'first': True, 'second': 'item2'} The :meth:`~questionary.prompt` function also allows you to ask a collection of questions, however instead of taking :class:`~questionary.Question` instances, it takes a dictionary: .. code:: python3 import questionary questions = [ { "type": "confirm", "name": "first", "message": "Would you like the next question?", "default": True, }, { "type": "select", "name": "second", "message": "Select item", "choices": ["item1", "item2", "item3"], }, ] questionary.prompt(questions) The format of the returned answers is the same as the one for :meth:`~questionary.form`. You can find more details on the configuration dictionaries in :ref:`question_dictionaries`. questionary-2.0.1/docs/pages/support.rst000066400000000000000000000004241447660764300204130ustar00rootroot00000000000000******* Support ******* Please `open an issue `_ with enough information for us to reproduce your problem. A `minimal, reproducible example `_ would be very helpful. questionary-2.0.1/docs/pages/types.rst000066400000000000000000000035261447660764300200510ustar00rootroot00000000000000.. _question_types: ************** Question Types ************** The different question types are meant to cover different use cases. The parameters and configuration options are explained in detail for each type. But before we get into to many details, here is a **cheatsheet with the available question types**: * use :ref:`type_text` to ask for **free text** input * use :ref:`type_password` to ask for free text where the **text is hidden** * use :ref:`type_path` to ask for a **file or directory** path with autocompletion * use :ref:`type_confirm` to ask a **yes or no** question * use :ref:`type_select` to ask the user to select **one item** from a beautiful list * use :ref:`type_raw_select` to ask the user to select **one item** from a list * use :ref:`type_checkbox` to ask the user to select **any number of items** from a list * use :ref:`type_autocomplete` to ask for free text with **autocomplete help** * use :ref:`type_press_any_key_to_continue` to ask the user to **press any key to continue** .. _type_text: Text #### .. automethod:: questionary::text .. _type_password: Password ######## .. automethod:: questionary::password .. _type_path: File Path ######### .. automethod:: questionary::path .. _type_confirm: Confirmation ############ .. automethod:: questionary::confirm .. _type_select: Select ###### .. automethod:: questionary::select .. _type_raw_select: Raw Select ########## .. automethod:: questionary::rawselect .. _type_checkbox: Checkbox ######## .. automethod:: questionary::checkbox .. _type_autocomplete: Autocomplete ############ .. automethod:: questionary::autocomplete Printing Formatted Text ####################### .. automethod:: questionary::print .. _type_press_any_key_to_continue: Press Any Key To Continue ######################### .. automethod:: questionary::press_any_key_to_continue questionary-2.0.1/examples/000077500000000000000000000000001447660764300157345ustar00rootroot00000000000000questionary-2.0.1/examples/__init__.py000066400000000000000000000015261447660764300200510ustar00rootroot00000000000000from questionary import Style custom_style_fancy = Style( [ ("separator", "fg:#cc5454"), ("qmark", "fg:#673ab7 bold"), ("question", ""), ("selected", "fg:#cc5454"), ("pointer", "fg:#673ab7 bold"), ("highlighted", "fg:#673ab7 bold"), ("answer", "fg:#f44336 bold"), ("text", "fg:#FBE9E7"), ("disabled", "fg:#858585 italic"), ] ) custom_style_dope = Style( [ ("separator", "fg:#6C6C6C"), ("qmark", "fg:#FF9D00 bold"), ("question", ""), ("selected", "fg:#5F819D"), ("pointer", "fg:#FF9D00 bold"), ("answer", "fg:#5F819D bold"), ] ) custom_style_genius = Style( [ ("qmark", "fg:#E91E63 bold"), ("question", ""), ("selected", "fg:#673AB7 bold"), ("answer", "fg:#2196f3 bold"), ] ) questionary-2.0.1/examples/advanced_workflow.py000066400000000000000000000036111447660764300220060ustar00rootroot00000000000000from pprint import pprint from questionary import Separator from questionary import prompt def ask_dictstyle(**kwargs): questions = [ { # just print a message, don't ask a question # does not require a name (but if provided, is ignored) and does not return a value "type": "print", "name": "intro", "message": "This example demonstrates advanced features! 🦄", "style": "bold italic", }, { "type": "confirm", "name": "conditional_step", "message": "Would you like the next question?", "default": True, }, { "type": "text", "name": "next_question", "message": "Name this library?", # Validate if the first question was answered with yes or no "when": lambda x: x["conditional_step"], # Only accept questionary as answer "validate": lambda val: val == "questionary", }, { "type": "select", "name": "second_question", "message": "Select item", "choices": ["item1", "item2", Separator(), "other"], }, { # just print a message, don't ask a question # does not require a name and does not return a value "type": "print", "message": "Please enter a value for 'other'", "style": "bold italic fg:darkred", "when": lambda x: x["second_question"] == "other", }, { "type": "text", # intentionally overwrites result from previous question "name": "second_question", "message": "Insert free text", "when": lambda x: x["second_question"] == "other", }, ] return prompt(questions, **kwargs) if __name__ == "__main__": pprint(ask_dictstyle()) questionary-2.0.1/examples/autocomplete_ants.py000066400000000000000000000053021447660764300220340ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Example for a autocomplete question type. Run example by typing `python -m examples.autocomplete` in your console.""" from pprint import pprint import questionary from examples import custom_style_fancy from questionary import ValidationError from questionary import Validator from questionary import prompt class PolyergusValidator(Validator): def validate(self, document): ok = "Polyergus" in document.text if not ok: raise ValidationError( message="Please choose a Polyergus Ant", cursor_position=len(document.text), ) # Move cursor to end meta_information = { "Camponotus pennsylvanicus": "This is an important, destructive pest that" " attacks fences, poles and buildings", "Linepithema humile": "It is an invasive species that has been established" " in many Mediterranean climate areas", "Eciton burchellii": "Known as army ants, moves almost incessantly" " over the time it exists", "Atta colombica": "They are known for cutting grasses and leaves, carrying" " them to their colonies' nests, and growing fungi on" " them which they later feed on", "Polyergus lucidus": "It is an obligatory social parasite, unable to feed" " itself or look after its brood and reliant on ants" " of another species of the genus Formica to undertake" " these tasks.", "Polyergus rufescens": "Is another specie of slave-making ant.", } def ask_pystyle(**kwargs): # create the question object question = questionary.autocomplete( "Choose ant specie", validate=PolyergusValidator, meta_information=meta_information, choices=[ "Camponotus pennsylvanicus", "Linepithema humile", "Eciton burchellii", "Atta colombica", "Polyergus lucidus", "Polyergus rufescens", ], ignore_case=False, style=custom_style_fancy, **kwargs, ) # prompt the user for an answer return question.ask() def ask_dictstyle(**kwargs): questions = [ { "type": "autocomplete", "name": "ants", "choices": [ "Camponotus pennsylvanicus", "Linepithema humile", "Eciton burchellii", "Atta colombica", "Polyergus lucidus", "Polyergus rufescens", ], "meta_information": meta_information, "message": "Choose ant specie", "validate": PolyergusValidator, } ] return prompt(questions, style=custom_style_fancy, **kwargs) if __name__ == "__main__": pprint(ask_pystyle()) questionary-2.0.1/examples/checkbox_separators.py000066400000000000000000000025001447660764300223340ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Example for a checkbox question type. Run example by typing `python -m examples.checkbox` in your console.""" from pprint import pprint import questionary from examples import custom_style_dope from questionary import Choice from questionary import Separator from questionary import prompt def ask_pystyle(**kwargs): # create the question object question = questionary.checkbox( "Select toppings", qmark="😃", choices=[ Choice("foo", checked=True), Separator(), Choice("bar", disabled="nope"), "bazz", Separator("--END--"), ], style=custom_style_dope, **kwargs, ) # prompt the user for an answer return question.ask() def ask_dictstyle(**kwargs): questions = [ { "type": "checkbox", "qmark": "😃", "message": "Select toppings", "name": "toppings", "choices": [ {"name": "foo", "checked": True}, Separator(), {"name": "bar", "disabled": "nope"}, "bazz", Separator("--END--"), ], } ] return prompt(questions, style=custom_style_dope, **kwargs) if __name__ == "__main__": pprint(ask_pystyle()) questionary-2.0.1/examples/checkbox_toppings.py000066400000000000000000000007541447660764300220250ustar00rootroot00000000000000import questionary from examples import custom_style_dope if __name__ == "__main__": toppings = ( questionary.checkbox( "Select toppings", choices=["foo", "bar", "bazz"], validate=lambda a: ( True if len(a) > 0 else "You must select at least one topping" ), style=custom_style_dope, ).ask() or [] ) print(f"Alright let's go mixing some {' and '.join(toppings)} 🤷‍♂️.") questionary-2.0.1/examples/confirm_amazed.py000066400000000000000000000003451447660764300212660ustar00rootroot00000000000000import questionary if __name__ == "__main__": confirmation = questionary.confirm("Are you amazed?").ask() if confirmation: print("That is amazing! 💥🚀") else: print("That is unfortunate 🐡.") questionary-2.0.1/examples/confirm_continue.py000066400000000000000000000015111447660764300216450ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Example for a confirm question type. Run example by typing `python -m examples.confirm` in your console.""" from pprint import pprint import questionary from examples import custom_style_dope from questionary import prompt def ask_pystyle(**kwargs): # create the question object question = questionary.confirm( "Do you want to continue?", default=True, style=custom_style_dope, **kwargs ) # prompt the user for an answer return question.ask() def ask_dictstyle(**kwargs): questions = [ { "type": "confirm", "message": "Do you want to continue?", "name": "continue", "default": True, } ] return prompt(questions, style=custom_style_dope, **kwargs) if __name__ == "__main__": pprint(ask_pystyle()) questionary-2.0.1/examples/password_confirm.py000066400000000000000000000010101447660764300216550ustar00rootroot00000000000000import questionary def create_password(**kwargs): x = questionary.password("Password", **kwargs).ask() y = questionary.password("Repeat password", **kwargs).ask() if x == y: questionary.print("✅ ") return x else: questionary.print( "Passwords do not match. Try again.", style="italic fg:darkred" ) # until passwords match, we keep repeating the question return create_password(**kwargs) if __name__ == "__main__": create_password() questionary-2.0.1/examples/password_git.py000066400000000000000000000013611447660764300210140ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Example for a password question type. Run example by typing `python -m examples.password` in your console.""" from pprint import pprint import questionary from examples import custom_style_dope from questionary import prompt def ask_pystyle(**kwargs): # create the question object question = questionary.password( "Enter your git password", style=custom_style_dope, **kwargs ) # prompt the user for an answer return question.ask() def ask_dictstyle(**kwargs): questions = [ {"type": "password", "message": "Enter your git password", "name": "password"} ] return prompt(questions, style=custom_style_dope, **kwargs) if __name__ == "__main__": pprint(ask_pystyle()) questionary-2.0.1/examples/password_secret.py000066400000000000000000000003651447660764300215210ustar00rootroot00000000000000import questionary if __name__ == "__main__": password = questionary.password("What's your secret?").ask() or "" print( f"Your secret is {password[:1]}... no just kidding - " f"I'm not going to tell anyone. 🤫" ) questionary-2.0.1/examples/project_path.py000066400000000000000000000003561447660764300207740ustar00rootroot00000000000000import questionary if __name__ == "__main__": path = questionary.path("Path to the projects version file").ask() if path: print(f"Found version file at {path} 🦄") else: print("No version file it is then!") questionary-2.0.1/examples/rawselect_action.py000066400000000000000000000004721447660764300216370ustar00rootroot00000000000000import questionary if __name__ == "__main__": action = ( questionary.rawselect( "What do you want to do?", choices=["Order a pizza", "Make a reservation", "Ask for opening hours"], ).ask() or "do nothing" ) print(f"Sorry, I can't {action}. Bye! 🙅") questionary-2.0.1/examples/rawselect_separator.py000066400000000000000000000023561447660764300223650ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Example for rawselect question type. Run example by typing `python -m examples.rawselect` in your console.""" from pprint import pprint import questionary from examples import custom_style_dope from questionary import Separator from questionary import prompt def ask_pystyle(**kwargs): # create the question object question = questionary.rawselect( "What do you want to do?", choices=[ "Order a pizza", "Make a reservation", Separator(), "Ask opening hours", "Talk to the receptionist", ], style=custom_style_dope, **kwargs, ) # prompt the user for an answer return question.ask() def ask_dictstyle(**kwargs): questions = [ { "type": "rawselect", "name": "theme", "message": "What do you want to do?", "choices": [ "Order a pizza", "Make a reservation", Separator(), "Ask opening hours", "Talk to the receptionist", ], }, ] return prompt(questions, style=custom_style_dope, **kwargs) if __name__ == "__main__": pprint(ask_pystyle()) questionary-2.0.1/examples/readme.py000066400000000000000000000013401447660764300175410ustar00rootroot00000000000000import questionary from examples import custom_style_dope if __name__ == "__main__": questionary.text("What's your first name").ask() questionary.password("What's your secret?").ask() questionary.confirm("Are you amazed?").ask() questionary.select( "What do you want to do?", choices=["Order a pizza", "Make a reservation", "Ask for opening hours"], ).ask() questionary.rawselect( "What do you want to do?", choices=["Order a pizza", "Make a reservation", "Ask for opening hours"], ).ask() questionary.checkbox( "Select toppings", choices=["foo", "bar", "bazz"], style=custom_style_dope ).ask() questionary.path("Path to the projects version file").ask() questionary-2.0.1/examples/select_action.py000066400000000000000000000004671447660764300211310ustar00rootroot00000000000000import questionary if __name__ == "__main__": action = ( questionary.select( "What do you want to do?", choices=["Order a pizza", "Make a reservation", "Ask for opening hours"], ).ask() or "do nothing" ) print(f"Sorry, I can't {action}. Bye! 👋") questionary-2.0.1/examples/select_restaurant.py000066400000000000000000000027011447660764300220350ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Example for a select question type. Run example by typing `python -m examples.select` in your console.""" from pprint import pprint import questionary from examples import custom_style_dope from questionary import Choice from questionary import Separator from questionary import prompt def ask_pystyle(**kwargs): # create the question object question = questionary.select( "What do you want to do?", qmark="😃", choices=[ "Order a pizza", "Make a reservation", Separator(), "Ask for opening hours", Choice("Contact support", disabled="Unavailable at this time"), "Talk to the receptionist", ], style=custom_style_dope, **kwargs, ) # prompt the user for an answer return question.ask() def ask_dictstyle(**kwargs): questions = [ { "type": "select", "name": "theme", "message": "What do you want to do?", "choices": [ "Order a pizza", "Make a reservation", Separator(), "Ask for opening hours", {"name": "Contact support", "disabled": "Unavailable at this time"}, "Talk to the receptionist", ], } ] return prompt(questions, style=custom_style_dope, **kwargs) if __name__ == "__main__": pprint(ask_pystyle()) questionary-2.0.1/examples/simple_print.py000066400000000000000000000001711447660764300210120ustar00rootroot00000000000000import questionary if __name__ == "__main__": questionary.print("Hello World 🦄", style="bold italic fg:darkred") questionary-2.0.1/examples/text_name.py000066400000000000000000000002121447660764300202650ustar00rootroot00000000000000import questionary if __name__ == "__main__": name = questionary.text("What's your first name?").ask() print(f"Hey {name} 🦄") questionary-2.0.1/examples/text_phone_number.py000066400000000000000000000026521447660764300220400ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Example for a text question type. Run example by typing `python -m examples.text` in your console.""" import re from pprint import pprint import questionary from examples import custom_style_dope from questionary import ValidationError from questionary import Validator from questionary import prompt class PhoneNumberValidator(Validator): def validate(self, document): ok = re.match( r"^([01])?[-.\s]?\(?(\d{3})\)?" r"[-.\s]?(\d{3})[-.\s]?(\d{4})\s?" r"((?:#|ext\.?\s?|x\.?\s?)(?:\d+)?)?$", document.text, ) if not ok: raise ValidationError( message="Please enter a valid phone number", cursor_position=len(document.text), ) # Move cursor to end def ask_pystyle(**kwargs): # create the question object question = questionary.text( "What's your phone number", validate=PhoneNumberValidator, style=custom_style_dope, **kwargs, ) # prompt the user for an answer return question.ask() def ask_dictstyle(**kwargs): questions = [ { "type": "text", "name": "phone", "message": "What's your phone number", "validate": PhoneNumberValidator, } ] return prompt(questions, style=custom_style_dope, **kwargs) if __name__ == "__main__": pprint(ask_pystyle()) questionary-2.0.1/poetry.lock000066400000000000000000002137451447660764300163260ustar00rootroot00000000000000# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. [[package]] name = "alabaster" version = "0.7.13" description = "A configurable sidebar-enabled Sphinx theme" optional = false python-versions = ">=3.6" files = [ {file = "alabaster-0.7.13-py3-none-any.whl", hash = "sha256:1ee19aca801bbabb5ba3f5f258e4422dfa86f82f3e9cefb0859b283cdd7f62a3"}, {file = "alabaster-0.7.13.tar.gz", hash = "sha256:a27a4a084d5e690e16e01e03ad2b2e552c61a65469419b907243193de1a84ae2"}, ] [[package]] name = "babel" version = "2.12.1" description = "Internationalization utilities" optional = false python-versions = ">=3.7" files = [ {file = "Babel-2.12.1-py3-none-any.whl", hash = "sha256:b4246fb7677d3b98f501a39d43396d3cafdc8eadb045f4a31be01863f655c610"}, {file = "Babel-2.12.1.tar.gz", hash = "sha256:cc2d99999cd01d44420ae725a21c9e3711b3aadc7976d6147f622d8581963455"}, ] [package.dependencies] pytz = {version = ">=2015.7", markers = "python_version < \"3.9\""} [[package]] name = "certifi" version = "2023.7.22" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ {file = "certifi-2023.7.22-py3-none-any.whl", hash = "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"}, {file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"}, ] [[package]] name = "cfgv" version = "3.3.1" description = "Validate configuration and produce human readable error messages." optional = false python-versions = ">=3.6.1" files = [ {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, ] [[package]] name = "charset-normalizer" version = "3.1.0" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7.0" files = [ {file = "charset-normalizer-3.1.0.tar.gz", hash = "sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5"}, {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b"}, {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60"}, {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1"}, {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0"}, {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f"}, {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0"}, {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795"}, {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c"}, {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203"}, {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1"}, {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137"}, {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce"}, {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a"}, {file = "charset_normalizer-3.1.0-cp310-cp310-win32.whl", hash = "sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448"}, {file = "charset_normalizer-3.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8"}, {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19"}, {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017"}, {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df"}, {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a"}, {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41"}, {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1"}, {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62"}, {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6"}, {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5"}, {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be"}, {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb"}, {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac"}, {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324"}, {file = "charset_normalizer-3.1.0-cp311-cp311-win32.whl", hash = "sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909"}, {file = "charset_normalizer-3.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755"}, {file = "charset_normalizer-3.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373"}, {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab"}, {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9"}, {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f"}, {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28"}, {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d"}, {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d"}, {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d"}, {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6"}, {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84"}, {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c"}, {file = "charset_normalizer-3.1.0-cp37-cp37m-win32.whl", hash = "sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974"}, {file = "charset_normalizer-3.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23"}, {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531"}, {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c"}, {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14"}, {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb"}, {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1"}, {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b"}, {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0"}, {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649"}, {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326"}, {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11"}, {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b"}, {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd"}, {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8"}, {file = "charset_normalizer-3.1.0-cp38-cp38-win32.whl", hash = "sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0"}, {file = "charset_normalizer-3.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59"}, {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e"}, {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31"}, {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f"}, {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e"}, {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f"}, {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854"}, {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706"}, {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e"}, {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0"}, {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230"}, {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7"}, {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e"}, {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f"}, {file = "charset_normalizer-3.1.0-cp39-cp39-win32.whl", hash = "sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1"}, {file = "charset_normalizer-3.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b"}, {file = "charset_normalizer-3.1.0-py3-none-any.whl", hash = "sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d"}, ] [[package]] name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] [[package]] name = "coverage" version = "6.5.0" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.7" files = [ {file = "coverage-6.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef8674b0ee8cc11e2d574e3e2998aea5df5ab242e012286824ea3c6970580e53"}, {file = "coverage-6.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:784f53ebc9f3fd0e2a3f6a78b2be1bd1f5575d7863e10c6e12504f240fd06660"}, {file = "coverage-6.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4a5be1748d538a710f87542f22c2cad22f80545a847ad91ce45e77417293eb4"}, {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83516205e254a0cb77d2d7bb3632ee019d93d9f4005de31dca0a8c3667d5bc04"}, {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af4fffaffc4067232253715065e30c5a7ec6faac36f8fc8d6f64263b15f74db0"}, {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:97117225cdd992a9c2a5515db1f66b59db634f59d0679ca1fa3fe8da32749cae"}, {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a1170fa54185845505fbfa672f1c1ab175446c887cce8212c44149581cf2d466"}, {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:11b990d520ea75e7ee8dcab5bc908072aaada194a794db9f6d7d5cfd19661e5a"}, {file = "coverage-6.5.0-cp310-cp310-win32.whl", hash = "sha256:5dbec3b9095749390c09ab7c89d314727f18800060d8d24e87f01fb9cfb40b32"}, {file = "coverage-6.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:59f53f1dc5b656cafb1badd0feb428c1e7bc19b867479ff72f7a9dd9b479f10e"}, {file = "coverage-6.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4a5375e28c5191ac38cca59b38edd33ef4cc914732c916f2929029b4bfb50795"}, {file = "coverage-6.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4ed2820d919351f4167e52425e096af41bfabacb1857186c1ea32ff9983ed75"}, {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33a7da4376d5977fbf0a8ed91c4dffaaa8dbf0ddbf4c8eea500a2486d8bc4d7b"}, {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8fb6cf131ac4070c9c5a3e21de0f7dc5a0fbe8bc77c9456ced896c12fcdad91"}, {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a6b7d95969b8845250586f269e81e5dfdd8ff828ddeb8567a4a2eaa7313460c4"}, {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1ef221513e6f68b69ee9e159506d583d31aa3567e0ae84eaad9d6ec1107dddaa"}, {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cca4435eebea7962a52bdb216dec27215d0df64cf27fc1dd538415f5d2b9da6b"}, {file = "coverage-6.5.0-cp311-cp311-win32.whl", hash = "sha256:98e8a10b7a314f454d9eff4216a9a94d143a7ee65018dd12442e898ee2310578"}, {file = "coverage-6.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:bc8ef5e043a2af066fa8cbfc6e708d58017024dc4345a1f9757b329a249f041b"}, {file = "coverage-6.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4433b90fae13f86fafff0b326453dd42fc9a639a0d9e4eec4d366436d1a41b6d"}, {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4f05d88d9a80ad3cac6244d36dd89a3c00abc16371769f1340101d3cb899fc3"}, {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:94e2565443291bd778421856bc975d351738963071e9b8839ca1fc08b42d4bef"}, {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:027018943386e7b942fa832372ebc120155fd970837489896099f5cfa2890f79"}, {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:255758a1e3b61db372ec2736c8e2a1fdfaf563977eedbdf131de003ca5779b7d"}, {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:851cf4ff24062c6aec510a454b2584f6e998cada52d4cb58c5e233d07172e50c"}, {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:12adf310e4aafddc58afdb04d686795f33f4d7a6fa67a7a9d4ce7d6ae24d949f"}, {file = "coverage-6.5.0-cp37-cp37m-win32.whl", hash = "sha256:b5604380f3415ba69de87a289a2b56687faa4fe04dbee0754bfcae433489316b"}, {file = "coverage-6.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:4a8dbc1f0fbb2ae3de73eb0bdbb914180c7abfbf258e90b311dcd4f585d44bd2"}, {file = "coverage-6.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d900bb429fdfd7f511f868cedd03a6bbb142f3f9118c09b99ef8dc9bf9643c3c"}, {file = "coverage-6.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2198ea6fc548de52adc826f62cb18554caedfb1d26548c1b7c88d8f7faa8f6ba"}, {file = "coverage-6.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c4459b3de97b75e3bd6b7d4b7f0db13f17f504f3d13e2a7c623786289dd670e"}, {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:20c8ac5386253717e5ccc827caad43ed66fea0efe255727b1053a8154d952398"}, {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b07130585d54fe8dff3d97b93b0e20290de974dc8177c320aeaf23459219c0b"}, {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dbdb91cd8c048c2b09eb17713b0c12a54fbd587d79adcebad543bc0cd9a3410b"}, {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:de3001a203182842a4630e7b8d1a2c7c07ec1b45d3084a83d5d227a3806f530f"}, {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e07f4a4a9b41583d6eabec04f8b68076ab3cd44c20bd29332c6572dda36f372e"}, {file = "coverage-6.5.0-cp38-cp38-win32.whl", hash = "sha256:6d4817234349a80dbf03640cec6109cd90cba068330703fa65ddf56b60223a6d"}, {file = "coverage-6.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:7ccf362abd726b0410bf8911c31fbf97f09f8f1061f8c1cf03dfc4b6372848f6"}, {file = "coverage-6.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:633713d70ad6bfc49b34ead4060531658dc6dfc9b3eb7d8a716d5873377ab745"}, {file = "coverage-6.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:95203854f974e07af96358c0b261f1048d8e1083f2de9b1c565e1be4a3a48cfc"}, {file = "coverage-6.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9023e237f4c02ff739581ef35969c3739445fb059b060ca51771e69101efffe"}, {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:265de0fa6778d07de30bcf4d9dc471c3dc4314a23a3c6603d356a3c9abc2dfcf"}, {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f830ed581b45b82451a40faabb89c84e1a998124ee4212d440e9c6cf70083e5"}, {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7b6be138d61e458e18d8e6ddcddd36dd96215edfe5f1168de0b1b32635839b62"}, {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:42eafe6778551cf006a7c43153af1211c3aaab658d4d66fa5fcc021613d02518"}, {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:723e8130d4ecc8f56e9a611e73b31219595baa3bb252d539206f7bbbab6ffc1f"}, {file = "coverage-6.5.0-cp39-cp39-win32.whl", hash = "sha256:d9ecf0829c6a62b9b573c7bb6d4dcd6ba8b6f80be9ba4fc7ed50bf4ac9aecd72"}, {file = "coverage-6.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc2af30ed0d5ae0b1abdb4ebdce598eafd5b35397d4d75deb341a614d333d987"}, {file = "coverage-6.5.0-pp36.pp37.pp38-none-any.whl", hash = "sha256:1431986dac3923c5945271f169f59c45b8802a114c8f548d611f2015133df77a"}, {file = "coverage-6.5.0.tar.gz", hash = "sha256:f642e90754ee3e06b0e7e51bce3379590e76b7f76b708e1a71ff043f87025c84"}, ] [package.dependencies] tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} [package.extras] toml = ["tomli"] [[package]] name = "coveralls" version = "3.3.1" description = "Show coverage stats online via coveralls.io" optional = false python-versions = ">= 3.5" files = [ {file = "coveralls-3.3.1-py2.py3-none-any.whl", hash = "sha256:f42015f31d386b351d4226389b387ae173207058832fbf5c8ec4b40e27b16026"}, {file = "coveralls-3.3.1.tar.gz", hash = "sha256:b32a8bb5d2df585207c119d6c01567b81fba690c9c10a753bfe27a335bfc43ea"}, ] [package.dependencies] coverage = ">=4.1,<6.0.dev0 || >6.1,<6.1.1 || >6.1.1,<7.0" docopt = ">=0.6.1" requests = ">=1.0.0" [package.extras] yaml = ["PyYAML (>=3.10)"] [[package]] name = "distlib" version = "0.3.6" description = "Distribution utilities" optional = false python-versions = "*" files = [ {file = "distlib-0.3.6-py2.py3-none-any.whl", hash = "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"}, {file = "distlib-0.3.6.tar.gz", hash = "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46"}, ] [[package]] name = "docopt" version = "0.6.2" description = "Pythonic argument parser, that will make you smile" optional = false python-versions = "*" files = [ {file = "docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491"}, ] [[package]] name = "docutils" version = "0.18.1" description = "Docutils -- Python Documentation Utilities" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ {file = "docutils-0.18.1-py2.py3-none-any.whl", hash = "sha256:23010f129180089fbcd3bc08cfefccb3b890b0050e1ca00c867036e9d161b98c"}, {file = "docutils-0.18.1.tar.gz", hash = "sha256:679987caf361a7539d76e584cbeddc311e3aee937877c87346f31debc63e9d06"}, ] [[package]] name = "exceptiongroup" version = "1.1.1" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" files = [ {file = "exceptiongroup-1.1.1-py3-none-any.whl", hash = "sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e"}, {file = "exceptiongroup-1.1.1.tar.gz", hash = "sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785"}, ] [package.extras] test = ["pytest (>=6)"] [[package]] name = "filelock" version = "3.12.0" description = "A platform independent file lock." optional = false python-versions = ">=3.7" files = [ {file = "filelock-3.12.0-py3-none-any.whl", hash = "sha256:ad98852315c2ab702aeb628412cbf7e95b7ce8c3bf9565670b4eaecf1db370a9"}, {file = "filelock-3.12.0.tar.gz", hash = "sha256:fc03ae43288c013d2ea83c8597001b1129db351aad9c57fe2409327916b8e718"}, ] [package.extras] docs = ["furo (>=2023.3.27)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] testing = ["covdefaults (>=2.3)", "coverage (>=7.2.3)", "diff-cover (>=7.5)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)"] [[package]] name = "identify" version = "2.5.24" description = "File identification library for Python" optional = false python-versions = ">=3.7" files = [ {file = "identify-2.5.24-py2.py3-none-any.whl", hash = "sha256:986dbfb38b1140e763e413e6feb44cd731faf72d1909543178aa79b0e258265d"}, {file = "identify-2.5.24.tar.gz", hash = "sha256:0aac67d5b4812498056d28a9a512a483f5085cc28640b02b258a59dac34301d4"}, ] [package.extras] license = ["ukkonen"] [[package]] name = "idna" version = "3.4" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.5" files = [ {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, ] [[package]] name = "imagesize" version = "1.4.1" description = "Getting image size from png/jpeg/jpeg2000/gif file" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, ] [[package]] name = "importlib-metadata" version = "6.6.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.7" files = [ {file = "importlib_metadata-6.6.0-py3-none-any.whl", hash = "sha256:43dd286a2cd8995d5eaef7fee2066340423b818ed3fd70adf0bad5f1fac53fed"}, {file = "importlib_metadata-6.6.0.tar.gz", hash = "sha256:92501cdf9cc66ebd3e612f1b4f0c0765dfa42f0fa38ffb319b6bd84dd675d705"}, ] [package.dependencies] zipp = ">=0.5" [package.extras] docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] perf = ["ipython"] testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] [[package]] name = "iniconfig" version = "2.0.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.7" files = [ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] [[package]] name = "jinja2" version = "3.1.2" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" files = [ {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, ] [package.dependencies] MarkupSafe = ">=2.0" [package.extras] i18n = ["Babel (>=2.7)"] [[package]] name = "livereload" version = "2.6.3" description = "Python LiveReload is an awesome tool for web developers" optional = false python-versions = "*" files = [ {file = "livereload-2.6.3-py2.py3-none-any.whl", hash = "sha256:ad4ac6f53b2d62bb6ce1a5e6e96f1f00976a32348afedcb4b6d68df2a1d346e4"}, {file = "livereload-2.6.3.tar.gz", hash = "sha256:776f2f865e59fde56490a56bcc6773b6917366bce0c267c60ee8aaf1a0959869"}, ] [package.dependencies] six = "*" tornado = {version = "*", markers = "python_version > \"2.7\""} [[package]] name = "markupsafe" version = "2.1.2" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.7" files = [ {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:665a36ae6f8f20a4676b53224e33d456a6f5a72657d9c83c2aa00765072f31f7"}, {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:340bea174e9761308703ae988e982005aedf427de816d1afe98147668cc03036"}, {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22152d00bf4a9c7c83960521fc558f55a1adbc0631fbb00a9471e097b19d72e1"}, {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28057e985dace2f478e042eaa15606c7efccb700797660629da387eb289b9323"}, {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca244fa73f50a800cf8c3ebf7fd93149ec37f5cb9596aa8873ae2c1d23498601"}, {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d9d971ec1e79906046aa3ca266de79eac42f1dbf3612a05dc9368125952bd1a1"}, {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7e007132af78ea9df29495dbf7b5824cb71648d7133cf7848a2a5dd00d36f9ff"}, {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7313ce6a199651c4ed9d7e4cfb4aa56fe923b1adf9af3b420ee14e6d9a73df65"}, {file = "MarkupSafe-2.1.2-cp310-cp310-win32.whl", hash = "sha256:c4a549890a45f57f1ebf99c067a4ad0cb423a05544accaf2b065246827ed9603"}, {file = "MarkupSafe-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:835fb5e38fd89328e9c81067fd642b3593c33e1e17e2fdbf77f5676abb14a156"}, {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2ec4f2d48ae59bbb9d1f9d7efb9236ab81429a764dedca114f5fdabbc3788013"}, {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:608e7073dfa9e38a85d38474c082d4281f4ce276ac0010224eaba11e929dd53a"}, {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65608c35bfb8a76763f37036547f7adfd09270fbdbf96608be2bead319728fcd"}, {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6"}, {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da25303d91526aac3672ee6d49a2f3db2d9502a4a60b55519feb1a4c7714e07d"}, {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9cad97ab29dfc3f0249b483412c85c8ef4766d96cdf9dcf5a1e3caa3f3661cf1"}, {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:085fd3201e7b12809f9e6e9bc1e5c96a368c8523fad5afb02afe3c051ae4afcc"}, {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1bea30e9bf331f3fef67e0a3877b2288593c98a21ccb2cf29b74c581a4eb3af0"}, {file = "MarkupSafe-2.1.2-cp311-cp311-win32.whl", hash = "sha256:7df70907e00c970c60b9ef2938d894a9381f38e6b9db73c5be35e59d92e06625"}, {file = "MarkupSafe-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:e55e40ff0cc8cc5c07996915ad367fa47da6b3fc091fdadca7f5403239c5fec3"}, {file = "MarkupSafe-2.1.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a6e40afa7f45939ca356f348c8e23048e02cb109ced1eb8420961b2f40fb373a"}, {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf877ab4ed6e302ec1d04952ca358b381a882fbd9d1b07cccbfd61783561f98a"}, {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63ba06c9941e46fa389d389644e2d8225e0e3e5ebcc4ff1ea8506dce646f8c8a"}, {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f1cd098434e83e656abf198f103a8207a8187c0fc110306691a2e94a78d0abb2"}, {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:55f44b440d491028addb3b88f72207d71eeebfb7b5dbf0643f7c023ae1fba619"}, {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a6f2fcca746e8d5910e18782f976489939d54a91f9411c32051b4aab2bd7c513"}, {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0b462104ba25f1ac006fdab8b6a01ebbfbce9ed37fd37fd4acd70c67c973e460"}, {file = "MarkupSafe-2.1.2-cp37-cp37m-win32.whl", hash = "sha256:7668b52e102d0ed87cb082380a7e2e1e78737ddecdde129acadb0eccc5423859"}, {file = "MarkupSafe-2.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6d6607f98fcf17e534162f0709aaad3ab7a96032723d8ac8750ffe17ae5a0666"}, {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a806db027852538d2ad7555b203300173dd1b77ba116de92da9afbc3a3be3eed"}, {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a4abaec6ca3ad8660690236d11bfe28dfd707778e2442b45addd2f086d6ef094"}, {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f03a532d7dee1bed20bc4884194a16160a2de9ffc6354b3878ec9682bb623c54"}, {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cf06cdc1dda95223e9d2d3c58d3b178aa5dacb35ee7e3bbac10e4e1faacb419"}, {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22731d79ed2eb25059ae3df1dfc9cb1546691cc41f4e3130fe6bfbc3ecbbecfa"}, {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f8ffb705ffcf5ddd0e80b65ddf7bed7ee4f5a441ea7d3419e861a12eaf41af58"}, {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8db032bf0ce9022a8e41a22598eefc802314e81b879ae093f36ce9ddf39ab1ba"}, {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2298c859cfc5463f1b64bd55cb3e602528db6fa0f3cfd568d3605c50678f8f03"}, {file = "MarkupSafe-2.1.2-cp38-cp38-win32.whl", hash = "sha256:50c42830a633fa0cf9e7d27664637532791bfc31c731a87b202d2d8ac40c3ea2"}, {file = "MarkupSafe-2.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:bb06feb762bade6bf3c8b844462274db0c76acc95c52abe8dbed28ae3d44a147"}, {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:99625a92da8229df6d44335e6fcc558a5037dd0a760e11d84be2260e6f37002f"}, {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8bca7e26c1dd751236cfb0c6c72d4ad61d986e9a41bbf76cb445f69488b2a2bd"}, {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40627dcf047dadb22cd25ea7ecfe9cbf3bbbad0482ee5920b582f3809c97654f"}, {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40dfd3fefbef579ee058f139733ac336312663c6706d1163b82b3003fb1925c4"}, {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:090376d812fb6ac5f171e5938e82e7f2d7adc2b629101cec0db8b267815c85e2"}, {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2e7821bffe00aa6bd07a23913b7f4e01328c3d5cc0b40b36c0bd81d362faeb65"}, {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c0a33bc9f02c2b17c3ea382f91b4db0e6cde90b63b296422a939886a7a80de1c"}, {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b8526c6d437855442cdd3d87eede9c425c4445ea011ca38d937db299382e6fa3"}, {file = "MarkupSafe-2.1.2-cp39-cp39-win32.whl", hash = "sha256:137678c63c977754abe9086a3ec011e8fd985ab90631145dfb9294ad09c102a7"}, {file = "MarkupSafe-2.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:0576fe974b40a400449768941d5d0858cc624e3249dfd1e0c33674e5c7ca7aed"}, {file = "MarkupSafe-2.1.2.tar.gz", hash = "sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d"}, ] [[package]] name = "mypy" version = "1.5.1" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" files = [ {file = "mypy-1.5.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f33592ddf9655a4894aef22d134de7393e95fcbdc2d15c1ab65828eee5c66c70"}, {file = "mypy-1.5.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:258b22210a4a258ccd077426c7a181d789d1121aca6db73a83f79372f5569ae0"}, {file = "mypy-1.5.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9ec1f695f0c25986e6f7f8778e5ce61659063268836a38c951200c57479cc12"}, {file = "mypy-1.5.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:abed92d9c8f08643c7d831300b739562b0a6c9fcb028d211134fc9ab20ccad5d"}, {file = "mypy-1.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:a156e6390944c265eb56afa67c74c0636f10283429171018446b732f1a05af25"}, {file = "mypy-1.5.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6ac9c21bfe7bc9f7f1b6fae441746e6a106e48fc9de530dea29e8cd37a2c0cc4"}, {file = "mypy-1.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:51cb1323064b1099e177098cb939eab2da42fea5d818d40113957ec954fc85f4"}, {file = "mypy-1.5.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:596fae69f2bfcb7305808c75c00f81fe2829b6236eadda536f00610ac5ec2243"}, {file = "mypy-1.5.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:32cb59609b0534f0bd67faebb6e022fe534bdb0e2ecab4290d683d248be1b275"}, {file = "mypy-1.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:159aa9acb16086b79bbb0016145034a1a05360626046a929f84579ce1666b315"}, {file = "mypy-1.5.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f6b0e77db9ff4fda74de7df13f30016a0a663928d669c9f2c057048ba44f09bb"}, {file = "mypy-1.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:26f71b535dfc158a71264e6dc805a9f8d2e60b67215ca0bfa26e2e1aa4d4d373"}, {file = "mypy-1.5.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fc3a600f749b1008cc75e02b6fb3d4db8dbcca2d733030fe7a3b3502902f161"}, {file = "mypy-1.5.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:26fb32e4d4afa205b24bf645eddfbb36a1e17e995c5c99d6d00edb24b693406a"}, {file = "mypy-1.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:82cb6193de9bbb3844bab4c7cf80e6227d5225cc7625b068a06d005d861ad5f1"}, {file = "mypy-1.5.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4a465ea2ca12804d5b34bb056be3a29dc47aea5973b892d0417c6a10a40b2d65"}, {file = "mypy-1.5.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9fece120dbb041771a63eb95e4896791386fe287fefb2837258925b8326d6160"}, {file = "mypy-1.5.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d28ddc3e3dfeab553e743e532fb95b4e6afad51d4706dd22f28e1e5e664828d2"}, {file = "mypy-1.5.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:57b10c56016adce71fba6bc6e9fd45d8083f74361f629390c556738565af8eeb"}, {file = "mypy-1.5.1-cp38-cp38-win_amd64.whl", hash = "sha256:ff0cedc84184115202475bbb46dd99f8dcb87fe24d5d0ddfc0fe6b8575c88d2f"}, {file = "mypy-1.5.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8f772942d372c8cbac575be99f9cc9d9fb3bd95c8bc2de6c01411e2c84ebca8a"}, {file = "mypy-1.5.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5d627124700b92b6bbaa99f27cbe615c8ea7b3402960f6372ea7d65faf376c14"}, {file = "mypy-1.5.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:361da43c4f5a96173220eb53340ace68cda81845cd88218f8862dfb0adc8cddb"}, {file = "mypy-1.5.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:330857f9507c24de5c5724235e66858f8364a0693894342485e543f5b07c8693"}, {file = "mypy-1.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:c543214ffdd422623e9fedd0869166c2f16affe4ba37463975043ef7d2ea8770"}, {file = "mypy-1.5.1-py3-none-any.whl", hash = "sha256:f757063a83970d67c444f6e01d9550a7402322af3557ce7630d3c957386fa8f5"}, {file = "mypy-1.5.1.tar.gz", hash = "sha256:b031b9601f1060bf1281feab89697324726ba0c0bae9d7cd7ab4b690940f0b92"}, ] [package.dependencies] mypy-extensions = ">=1.0.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} typing-extensions = ">=4.1.0" [package.extras] dmypy = ["psutil (>=4.0)"] install-types = ["pip"] reports = ["lxml"] [[package]] name = "mypy-extensions" version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." optional = false python-versions = ">=3.5" files = [ {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] [[package]] name = "nodeenv" version = "1.8.0" description = "Node.js virtual environment builder" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" files = [ {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, ] [package.dependencies] setuptools = "*" [[package]] name = "packaging" version = "23.1" description = "Core utilities for Python packages" optional = false python-versions = ">=3.7" files = [ {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, ] [[package]] name = "platformdirs" version = "3.5.1" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." optional = false python-versions = ">=3.7" files = [ {file = "platformdirs-3.5.1-py3-none-any.whl", hash = "sha256:e2378146f1964972c03c085bb5662ae80b2b8c06226c54b2ff4aa9483e8a13a5"}, {file = "platformdirs-3.5.1.tar.gz", hash = "sha256:412dae91f52a6f84830f39a8078cecd0e866cb72294a5c66808e74d5e88d251f"}, ] [package.extras] docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx (>=6.2.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] [[package]] name = "pluggy" version = "1.0.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.6" files = [ {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, ] [package.extras] dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] [[package]] name = "pre-commit" version = "3.4.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." optional = false python-versions = ">=3.8" files = [ {file = "pre_commit-3.4.0-py2.py3-none-any.whl", hash = "sha256:96d529a951f8b677f730a7212442027e8ba53f9b04d217c4c67dc56c393ad945"}, {file = "pre_commit-3.4.0.tar.gz", hash = "sha256:6bbd5129a64cad4c0dfaeeb12cd8f7ea7e15b77028d985341478c8af3c759522"}, ] [package.dependencies] cfgv = ">=2.0.0" identify = ">=1.0.0" nodeenv = ">=0.11.1" pyyaml = ">=5.1" virtualenv = ">=20.10.0" [[package]] name = "prompt-toolkit" version = "3.0.36" description = "Library for building powerful interactive command lines in Python" optional = false python-versions = ">=3.6.2" files = [ {file = "prompt_toolkit-3.0.36-py3-none-any.whl", hash = "sha256:aa64ad242a462c5ff0363a7b9cfe696c20d55d9fc60c11fd8e632d064804d305"}, {file = "prompt_toolkit-3.0.36.tar.gz", hash = "sha256:3e163f254bef5a03b146397d7c1963bd3e2812f0964bb9a24e6ec761fd28db63"}, ] [package.dependencies] wcwidth = "*" [[package]] name = "pygments" version = "2.15.1" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.7" files = [ {file = "Pygments-2.15.1-py3-none-any.whl", hash = "sha256:db2db3deb4b4179f399a09054b023b6a586b76499d36965813c71aa8ed7b5fd1"}, {file = "Pygments-2.15.1.tar.gz", hash = "sha256:8ace4d3c1dd481894b2005f560ead0f9f19ee64fe983366be1a21e171d12775c"}, ] [package.extras] plugins = ["importlib-metadata"] [[package]] name = "pytest" version = "7.4.2" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.7" files = [ {file = "pytest-7.4.2-py3-none-any.whl", hash = "sha256:1d881c6124e08ff0a1bb75ba3ec0bfd8b5354a01c194ddd5a0a870a48d99b002"}, {file = "pytest-7.4.2.tar.gz", hash = "sha256:a766259cfab564a2ad52cb1aae1b881a75c3eb7e34ca3779697c23ed47c47069"}, ] [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" pluggy = ">=0.12,<2.0" tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} [package.extras] testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-cov" version = "4.1.0" description = "Pytest plugin for measuring coverage." optional = false python-versions = ">=3.7" files = [ {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, ] [package.dependencies] coverage = {version = ">=5.2.1", extras = ["toml"]} pytest = ">=4.6" [package.extras] testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] [[package]] name = "pytz" version = "2023.3" description = "World timezone definitions, modern and historical" optional = false python-versions = "*" files = [ {file = "pytz-2023.3-py2.py3-none-any.whl", hash = "sha256:a151b3abb88eda1d4e34a9814df37de2a80e301e68ba0fd856fb9b46bfbbbffb"}, {file = "pytz-2023.3.tar.gz", hash = "sha256:1d8ce29db189191fb55338ee6d0387d82ab59f3d00eac103412d64e0ebd0c588"}, ] [[package]] name = "pyyaml" version = "6.0" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.6" files = [ {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, {file = "PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358"}, {file = "PyYAML-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1"}, {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d"}, {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f"}, {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782"}, {file = "PyYAML-6.0-cp311-cp311-win32.whl", hash = "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7"}, {file = "PyYAML-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf"}, {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, ] [[package]] name = "requests" version = "2.31.0" description = "Python HTTP for Humans." optional = false python-versions = ">=3.7" files = [ {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, ] [package.dependencies] certifi = ">=2017.4.17" charset-normalizer = ">=2,<4" idna = ">=2.5,<4" urllib3 = ">=1.21.1,<3" [package.extras] socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "setuptools" version = "67.7.2" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.7" files = [ {file = "setuptools-67.7.2-py3-none-any.whl", hash = "sha256:23aaf86b85ca52ceb801d32703f12d77517b2556af839621c641fca11287952b"}, {file = "setuptools-67.7.2.tar.gz", hash = "sha256:f104fa03692a2602fa0fec6c6a9e63b6c8a968de13e17c026957dd1f53d80990"}, ] [package.extras] docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "six" version = "1.16.0" description = "Python 2 and 3 compatibility utilities" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" files = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] [[package]] name = "snowballstemmer" version = "2.2.0" description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." optional = false python-versions = "*" files = [ {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, ] [[package]] name = "sphinx" version = "7.1.2" description = "Python documentation generator" optional = false python-versions = ">=3.8" files = [ {file = "sphinx-7.1.2-py3-none-any.whl", hash = "sha256:d170a81825b2fcacb6dfd5a0d7f578a053e45d3f2b153fecc948c37344eb4cbe"}, {file = "sphinx-7.1.2.tar.gz", hash = "sha256:780f4d32f1d7d1126576e0e5ecc19dc32ab76cd24e950228dcf7b1f6d3d9e22f"}, ] [package.dependencies] alabaster = ">=0.7,<0.8" babel = ">=2.9" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} docutils = ">=0.18.1,<0.21" imagesize = ">=1.3" importlib-metadata = {version = ">=4.8", markers = "python_version < \"3.10\""} Jinja2 = ">=3.0" packaging = ">=21.0" Pygments = ">=2.13" requests = ">=2.25.0" snowballstemmer = ">=2.0" sphinxcontrib-applehelp = "*" sphinxcontrib-devhelp = "*" sphinxcontrib-htmlhelp = ">=2.0.0" sphinxcontrib-jsmath = "*" sphinxcontrib-qthelp = "*" sphinxcontrib-serializinghtml = ">=1.1.5" [package.extras] docs = ["sphinxcontrib-websupport"] lint = ["docutils-stubs", "flake8 (>=3.5.0)", "flake8-simplify", "isort", "mypy (>=0.990)", "ruff", "sphinx-lint", "types-requests"] test = ["cython", "filelock", "html5lib", "pytest (>=4.6)"] [[package]] name = "sphinx-autobuild" version = "2021.3.14" description = "Rebuild Sphinx documentation on changes, with live-reload in the browser." optional = false python-versions = ">=3.6" files = [ {file = "sphinx-autobuild-2021.3.14.tar.gz", hash = "sha256:de1ca3b66e271d2b5b5140c35034c89e47f263f2cd5db302c9217065f7443f05"}, {file = "sphinx_autobuild-2021.3.14-py3-none-any.whl", hash = "sha256:8fe8cbfdb75db04475232f05187c776f46f6e9e04cacf1e49ce81bdac649ccac"}, ] [package.dependencies] colorama = "*" livereload = "*" sphinx = "*" [package.extras] test = ["pytest", "pytest-cov"] [[package]] name = "sphinx-autodoc-typehints" version = "1.24.0" description = "Type hints (PEP 484) support for the Sphinx autodoc extension" optional = false python-versions = ">=3.8" files = [ {file = "sphinx_autodoc_typehints-1.24.0-py3-none-any.whl", hash = "sha256:6a73c0c61a9144ce2ed5ef2bed99d615254e5005c1cc32002017d72d69fb70e6"}, {file = "sphinx_autodoc_typehints-1.24.0.tar.gz", hash = "sha256:94e440066941bb237704bb880785e2d05e8ae5406c88674feefbb938ad0dc6af"}, ] [package.dependencies] sphinx = ">=7.0.1" [package.extras] docs = ["furo (>=2023.5.20)", "sphinx (>=7.0.1)"] numpy = ["nptyping (>=2.5)"] testing = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "diff-cover (>=7.5)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "sphobjinv (>=2.3.1)", "typing-extensions (>=4.6.3)"] [[package]] name = "sphinx-copybutton" version = "0.5.2" description = "Add a copy button to each of your code cells." optional = false python-versions = ">=3.7" files = [ {file = "sphinx-copybutton-0.5.2.tar.gz", hash = "sha256:4cf17c82fb9646d1bc9ca92ac280813a3b605d8c421225fd9913154103ee1fbd"}, {file = "sphinx_copybutton-0.5.2-py3-none-any.whl", hash = "sha256:fb543fd386d917746c9a2c50360c7905b605726b9355cd26e9974857afeae06e"}, ] [package.dependencies] sphinx = ">=1.8" [package.extras] code-style = ["pre-commit (==2.12.1)"] rtd = ["ipython", "myst-nb", "sphinx", "sphinx-book-theme", "sphinx-examples"] [[package]] name = "sphinx-rtd-theme" version = "1.3.0" description = "Read the Docs theme for Sphinx" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" files = [ {file = "sphinx_rtd_theme-1.3.0-py2.py3-none-any.whl", hash = "sha256:46ddef89cc2416a81ecfbeaceab1881948c014b1b6e4450b815311a89fb977b0"}, {file = "sphinx_rtd_theme-1.3.0.tar.gz", hash = "sha256:590b030c7abb9cf038ec053b95e5380b5c70d61591eb0b552063fbe7c41f0931"}, ] [package.dependencies] docutils = "<0.19" sphinx = ">=1.6,<8" sphinxcontrib-jquery = ">=4,<5" [package.extras] dev = ["bump2version", "sphinxcontrib-httpdomain", "transifex-client", "wheel"] [[package]] name = "sphinxcontrib-applehelp" version = "1.0.4" description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" optional = false python-versions = ">=3.8" files = [ {file = "sphinxcontrib-applehelp-1.0.4.tar.gz", hash = "sha256:828f867945bbe39817c210a1abfd1bc4895c8b73fcaade56d45357a348a07d7e"}, {file = "sphinxcontrib_applehelp-1.0.4-py3-none-any.whl", hash = "sha256:29d341f67fb0f6f586b23ad80e072c8e6ad0b48417db2bde114a4c9746feb228"}, ] [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] test = ["pytest"] [[package]] name = "sphinxcontrib-devhelp" version = "1.0.2" description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document." optional = false python-versions = ">=3.5" files = [ {file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"}, {file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"}, ] [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] test = ["pytest"] [[package]] name = "sphinxcontrib-htmlhelp" version = "2.0.1" description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" optional = false python-versions = ">=3.8" files = [ {file = "sphinxcontrib-htmlhelp-2.0.1.tar.gz", hash = "sha256:0cbdd302815330058422b98a113195c9249825d681e18f11e8b1f78a2f11efff"}, {file = "sphinxcontrib_htmlhelp-2.0.1-py3-none-any.whl", hash = "sha256:c38cb46dccf316c79de6e5515e1770414b797162b23cd3d06e67020e1d2a6903"}, ] [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] test = ["html5lib", "pytest"] [[package]] name = "sphinxcontrib-jquery" version = "4.1" description = "Extension to include jQuery on newer Sphinx releases" optional = false python-versions = ">=2.7" files = [ {file = "sphinxcontrib-jquery-4.1.tar.gz", hash = "sha256:1620739f04e36a2c779f1a131a2dfd49b2fd07351bf1968ced074365933abc7a"}, {file = "sphinxcontrib_jquery-4.1-py2.py3-none-any.whl", hash = "sha256:f936030d7d0147dd026a4f2b5a57343d233f1fc7b363f68b3d4f1cb0993878ae"}, ] [package.dependencies] Sphinx = ">=1.8" [[package]] name = "sphinxcontrib-jsmath" version = "1.0.1" description = "A sphinx extension which renders display math in HTML via JavaScript" optional = false python-versions = ">=3.5" files = [ {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, ] [package.extras] test = ["flake8", "mypy", "pytest"] [[package]] name = "sphinxcontrib-qthelp" version = "1.0.3" description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document." optional = false python-versions = ">=3.5" files = [ {file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"}, {file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"}, ] [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] test = ["pytest"] [[package]] name = "sphinxcontrib-serializinghtml" version = "1.1.5" description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)." optional = false python-versions = ">=3.5" files = [ {file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"}, {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, ] [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] test = ["pytest"] [[package]] name = "toml" version = "0.10.2" description = "Python Library for Tom's Obvious, Minimal Language" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" files = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, ] [[package]] name = "tomli" version = "2.0.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.7" files = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] [[package]] name = "tornado" version = "6.3.3" description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." optional = false python-versions = ">= 3.8" files = [ {file = "tornado-6.3.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:502fba735c84450974fec147340016ad928d29f1e91f49be168c0a4c18181e1d"}, {file = "tornado-6.3.3-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:805d507b1f588320c26f7f097108eb4023bbaa984d63176d1652e184ba24270a"}, {file = "tornado-6.3.3-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bd19ca6c16882e4d37368e0152f99c099bad93e0950ce55e71daed74045908f"}, {file = "tornado-6.3.3-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ac51f42808cca9b3613f51ffe2a965c8525cb1b00b7b2d56828b8045354f76a"}, {file = "tornado-6.3.3-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:71a8db65160a3c55d61839b7302a9a400074c9c753040455494e2af74e2501f2"}, {file = "tornado-6.3.3-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:ceb917a50cd35882b57600709dd5421a418c29ddc852da8bcdab1f0db33406b0"}, {file = "tornado-6.3.3-cp38-abi3-musllinux_1_1_i686.whl", hash = "sha256:7d01abc57ea0dbb51ddfed477dfe22719d376119844e33c661d873bf9c0e4a16"}, {file = "tornado-6.3.3-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:9dc4444c0defcd3929d5c1eb5706cbe1b116e762ff3e0deca8b715d14bf6ec17"}, {file = "tornado-6.3.3-cp38-abi3-win32.whl", hash = "sha256:65ceca9500383fbdf33a98c0087cb975b2ef3bfb874cb35b8de8740cf7f41bd3"}, {file = "tornado-6.3.3-cp38-abi3-win_amd64.whl", hash = "sha256:22d3c2fa10b5793da13c807e6fc38ff49a4f6e1e3868b0a6f4164768bb8e20f5"}, {file = "tornado-6.3.3.tar.gz", hash = "sha256:e7d8db41c0181c80d76c982aacc442c0783a2c54d6400fe028954201a2e032fe"}, ] [[package]] name = "typing-extensions" version = "4.5.0" description = "Backported and Experimental Type Hints for Python 3.7+" optional = false python-versions = ">=3.7" files = [ {file = "typing_extensions-4.5.0-py3-none-any.whl", hash = "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4"}, {file = "typing_extensions-4.5.0.tar.gz", hash = "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb"}, ] [[package]] name = "urllib3" version = "2.0.2" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.7" files = [ {file = "urllib3-2.0.2-py3-none-any.whl", hash = "sha256:d055c2f9d38dc53c808f6fdc8eab7360b6fdbbde02340ed25cfbcd817c62469e"}, {file = "urllib3-2.0.2.tar.gz", hash = "sha256:61717a1095d7e155cdb737ac7bb2f4324a858a1e2e6466f6d03ff630ca68d3cc"}, ] [package.extras] brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] [[package]] name = "virtualenv" version = "20.23.0" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ {file = "virtualenv-20.23.0-py3-none-any.whl", hash = "sha256:6abec7670e5802a528357fdc75b26b9f57d5d92f29c5462ba0fbe45feacc685e"}, {file = "virtualenv-20.23.0.tar.gz", hash = "sha256:a85caa554ced0c0afbd0d638e7e2d7b5f92d23478d05d17a76daeac8f279f924"}, ] [package.dependencies] distlib = ">=0.3.6,<1" filelock = ">=3.11,<4" platformdirs = ">=3.2,<4" [package.extras] docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=22.12)"] test = ["covdefaults (>=2.3)", "coverage (>=7.2.3)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.3.1)", "pytest-env (>=0.8.1)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.10)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=67.7.1)", "time-machine (>=2.9)"] [[package]] name = "wcwidth" version = "0.2.6" description = "Measures the displayed width of unicode strings in a terminal" optional = false python-versions = "*" files = [ {file = "wcwidth-0.2.6-py2.py3-none-any.whl", hash = "sha256:795b138f6875577cd91bba52baf9e445cd5118fd32723b460e30a0af30ea230e"}, {file = "wcwidth-0.2.6.tar.gz", hash = "sha256:a5220780a404dbe3353789870978e472cfe477761f06ee55077256e509b156d0"}, ] [[package]] name = "zipp" version = "3.15.0" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.7" files = [ {file = "zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"}, {file = "zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b"}, ] [package.extras] docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] [metadata] lock-version = "2.0" python-versions = ">=3.8" content-hash = "d0ac76f6bd3d0532f3e7cc3072da2354067c1e33cb60a725c1346ed58b25651a" questionary-2.0.1/pyproject.toml000066400000000000000000000034251447660764300170360ustar00rootroot00000000000000[build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" [tool.black] line-length = 88 target-version = ["py38", "py39", "py310", "py311"] exclude = "((.eggs | .git | .pytype | .pytest_cache | build | dist))" [tool.poetry] name = "questionary" version = "2.0.1" description = "Python library to build pretty command line user prompts ⭐️" authors = [ "Tom Bocklisch ",] maintainers = [ "Tom Bocklisch ", "Kian Cross "] repository = "https://github.com/tmbo/questionary" documentation = "https://questionary.readthedocs.io/" classifiers=[ "Development Status :: 4 - Beta", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", # supported python versions "Programming Language :: Python", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Topic :: Software Development :: Libraries", ] keywords = [ "cli", "ui", "inquirer", "questions", "prompt",] readme = "README.md" license = "MIT" [tool.poetry.dependencies] python = ">=3.8" prompt_toolkit = ">=2.0,<=3.0.36" # once https://github.com/prompt-toolkit/python-prompt-toolkit/issues/1726 is fixed, this can be changed to ">=2.0,<4.0" [tool.poetry.group.docs] optional = true [tool.poetry.group.docs.dependencies] Sphinx = ">=4.1,<8.0" sphinx-rtd-theme = ">=0.5,<1.4" sphinx-autobuild = ">=2020.9.1,<2022.0.0" sphinx-copybutton = ">=0.3.1,<0.6.0" sphinx-autodoc-typehints = "^1.11.1" [tool.poetry.group.dev.dependencies] pytest-cov = ">=3,<5" pytest = "^7.0.1" coveralls = "^3.3.1" mypy = "^1.2.0" toml = "^0.10.2" pre-commit = { version = ">=2.20,<4.0", python = ">=3.7,<4.0"} questionary-2.0.1/questionary/000077500000000000000000000000001447660764300165015ustar00rootroot00000000000000questionary-2.0.1/questionary/__init__.py000066400000000000000000000031361447660764300206150ustar00rootroot00000000000000# noinspection PyUnresolvedReferences from prompt_toolkit.styles import Style from prompt_toolkit.validation import ValidationError from prompt_toolkit.validation import Validator import questionary.version from questionary.form import Form from questionary.form import FormField from questionary.form import form from questionary.prompt import prompt from questionary.prompt import unsafe_prompt # import the shortcuts to create single question prompts from questionary.prompts.autocomplete import autocomplete from questionary.prompts.checkbox import checkbox from questionary.prompts.common import Choice from questionary.prompts.common import Separator from questionary.prompts.common import print_formatted_text as print from questionary.prompts.confirm import confirm from questionary.prompts.password import password from questionary.prompts.path import path from questionary.prompts.press_any_key_to_continue import press_any_key_to_continue from questionary.prompts.rawselect import rawselect from questionary.prompts.select import select from questionary.prompts.text import text from questionary.question import Question __version__ = questionary.version.__version__ __all__ = [ "__version__", # question types "autocomplete", "checkbox", "confirm", "password", "path", "press_any_key_to_continue", "rawselect", "select", "text", # utility methods "print", "form", "prompt", "unsafe_prompt", # commonly used classes "Form", "FormField", "Question", "Choice", "Style", "Separator", "Validator", "ValidationError", ] questionary-2.0.1/questionary/constants.py000066400000000000000000000032071447660764300210710ustar00rootroot00000000000000from questionary import Style # Value to display as an answer when "affirming" a confirmation question YES = "Yes" # Value to display as an answer when "denying" a confirmation question NO = "No" # Instruction text for a confirmation question (yes is default) YES_OR_NO = "(Y/n)" # Instruction text for a confirmation question (no is default) NO_OR_YES = "(y/N)" # Instruction for multiline input INSTRUCTION_MULTILINE = "(Finish with 'Alt+Enter' or 'Esc then Enter')\n>" # Selection token used to indicate the selection cursor in a list DEFAULT_SELECTED_POINTER = "»" # Item prefix to identify selected items in a checkbox list INDICATOR_SELECTED = "●" # Item prefix to identify unselected items in a checkbox list INDICATOR_UNSELECTED = "○" # Prefix displayed in front of questions DEFAULT_QUESTION_PREFIX = "?" # Message shown when a user aborts a question prompt using CTRL-C DEFAULT_KBI_MESSAGE = "Cancelled by user" # Default text shown when the input is invalid INVALID_INPUT = "Invalid input" # Default message style DEFAULT_STYLE = Style( [ ("qmark", "fg:#5f819d"), # token in front of the question ("question", "bold"), # question text ("answer", "fg:#FF9D00 bold"), # submitted answer text behind the question ("pointer", ""), # pointer used in select and checkbox prompts ("selected", ""), # style for a selected item of a checkbox ("separator", ""), # separator in lists ("instruction", ""), # user instructions for select, rawselect, checkbox ("text", ""), # any other text ("instruction", ""), # user instructions for select, rawselect, checkbox ] ) questionary-2.0.1/questionary/form.py000066400000000000000000000066721447660764300200310ustar00rootroot00000000000000from typing import Any from typing import Dict from typing import NamedTuple from typing import Sequence from questionary.constants import DEFAULT_KBI_MESSAGE from questionary.question import Question class FormField(NamedTuple): """ Represents a question within a form Args: key: The name of the form field. question: The question to ask in the form field. """ key: str question: Question def form(**kwargs: Question) -> "Form": """Create a form with multiple questions. The parameter name of a question will be the key for the answer in the returned dict. Args: kwargs: Questions to ask in the form. """ return Form(*(FormField(k, q) for k, q in kwargs.items())) class Form: """Multi question prompts. Questions are asked one after another. All the answers are returned as a dict with one entry per question. This class should not be invoked directly, instead use :func:`form`. """ form_fields: Sequence[FormField] def __init__(self, *form_fields: FormField) -> None: self.form_fields = form_fields def unsafe_ask(self, patch_stdout: bool = False) -> Dict[str, Any]: """Ask the questions synchronously and return user response. Does not catch keyboard interrupts. Args: patch_stdout: Ensure that the prompt renders correctly if other threads are printing to stdout. Returns: The answers from the form. """ return {f.key: f.question.unsafe_ask(patch_stdout) for f in self.form_fields} async def unsafe_ask_async(self, patch_stdout: bool = False) -> Dict[str, Any]: """Ask the questions using asyncio and return user response. Does not catch keyboard interrupts. Args: patch_stdout: Ensure that the prompt renders correctly if other threads are printing to stdout. Returns: The answers from the form. """ return { f.key: await f.question.unsafe_ask_async(patch_stdout) for f in self.form_fields } def ask( self, patch_stdout: bool = False, kbi_msg: str = DEFAULT_KBI_MESSAGE ) -> Dict[str, Any]: """Ask the questions synchronously and return user response. Args: patch_stdout: Ensure that the prompt renders correctly if other threads are printing to stdout. kbi_msg: The message to be printed on a keyboard interrupt. Returns: The answers from the form. """ try: return self.unsafe_ask(patch_stdout) except KeyboardInterrupt: print("") print(kbi_msg) print("") return {} async def ask_async( self, patch_stdout: bool = False, kbi_msg: str = DEFAULT_KBI_MESSAGE ) -> Dict[str, Any]: """Ask the questions using asyncio and return user response. Args: patch_stdout: Ensure that the prompt renders correctly if other threads are printing to stdout. kbi_msg: The message to be printed on a keyboard interrupt. Returns: The answers from the form. """ try: return await self.unsafe_ask_async(patch_stdout) except KeyboardInterrupt: print("") print(kbi_msg) print("") return {} questionary-2.0.1/questionary/prompt.py000066400000000000000000000205051447660764300203760ustar00rootroot00000000000000from typing import Any from typing import Dict from typing import Iterable from typing import Mapping from typing import Optional from typing import Union from prompt_toolkit.output import ColorDepth from questionary import utils from questionary.constants import DEFAULT_KBI_MESSAGE from questionary.prompts import AVAILABLE_PROMPTS from questionary.prompts import prompt_by_name from questionary.prompts.common import print_formatted_text class PromptParameterException(ValueError): """Received a prompt with a missing parameter.""" def __init__(self, message: str, errors: Optional[BaseException] = None) -> None: # Call the base class constructor with the parameters it needs super().__init__(f"You must provide a `{message}` value", errors) def prompt( questions: Union[Dict[str, Any], Iterable[Mapping[str, Any]]], answers: Optional[Mapping[str, Any]] = None, patch_stdout: bool = False, true_color: bool = False, kbi_msg: str = DEFAULT_KBI_MESSAGE, **kwargs: Any, ) -> Dict[str, Any]: """Prompt the user for input on all the questions. Catches keyboard interrupts and prints a message. See :func:`unsafe_prompt` for possible question configurations. Args: questions: A list of question configs representing questions to ask. A question config may have the following options: * type - The type of question. * name - An ID for the question (to identify it in the answers :obj:`dict`). * when - Callable to conditionally show the question. This function takes a :obj:`dict` representing the current answers. * filter - Function that the answer is passed to. The return value of this function is saved as the answer. Additional options correspond to the parameter names for particular question types. answers: Default answers. patch_stdout: Ensure that the prompt renders correctly if other threads are printing to stdout. kbi_msg: The message to be printed on a keyboard interrupt. true_color: Use true color output. color_depth: Color depth to use. If ``true_color`` is set to true then this value is ignored. type: Default ``type`` value to use in question config. filter: Default ``filter`` value to use in question config. name: Default ``name`` value to use in question config. when: Default ``when`` value to use in question config. default: Default ``default`` value to use in question config. kwargs: Additional options passed to every question. Returns: Dictionary of question answers. """ try: return unsafe_prompt(questions, answers, patch_stdout, true_color, **kwargs) except KeyboardInterrupt: print("") print(kbi_msg) print("") return {} def unsafe_prompt( questions: Union[Dict[str, Any], Iterable[Mapping[str, Any]]], answers: Optional[Mapping[str, Any]] = None, patch_stdout: bool = False, true_color: bool = False, **kwargs: Any, ) -> Dict[str, Any]: """Prompt the user for input on all the questions. Won't catch keyboard interrupts. Args: questions: A list of question configs representing questions to ask. A question config may have the following options: * type - The type of question. * name - An ID for the question (to identify it in the answers :obj:`dict`). * when - Callable to conditionally show the question. This function takes a :obj:`dict` representing the current answers. * filter - Function that the answer is passed to. The return value of this function is saved as the answer. Additional options correspond to the parameter names for particular question types. answers: Default answers. patch_stdout: Ensure that the prompt renders correctly if other threads are printing to stdout. true_color: Use true color output. color_depth: Color depth to use. If ``true_color`` is set to true then this value is ignored. type: Default ``type`` value to use in question config. filter: Default ``filter`` value to use in question config. name: Default ``name`` value to use in question config. when: Default ``when`` value to use in question config. default: Default ``default`` value to use in question config. kwargs: Additional options passed to every question. Returns: Dictionary of question answers. Raises: KeyboardInterrupt: raised on keyboard interrupt """ if isinstance(questions, dict): questions = [questions] answers = dict(answers or {}) for question_config in questions: question_config = dict(question_config) # import the question if "type" not in question_config: raise PromptParameterException("type") # every type except 'print' needs a name if "name" not in question_config and question_config["type"] != "print": raise PromptParameterException("name") _kwargs = kwargs.copy() _kwargs.update(question_config) _type = _kwargs.pop("type") _filter = _kwargs.pop("filter", None) name = _kwargs.pop("name", None) if _type == "print" else _kwargs.pop("name") when = _kwargs.pop("when", None) if true_color: _kwargs["color_depth"] = ColorDepth.TRUE_COLOR if when: # at least a little sanity check! if callable(question_config["when"]): try: if not question_config["when"](answers): continue except Exception as exception: raise ValueError( f"Problem in 'when' check of " f"{name} question: {exception}" ) from exception else: raise ValueError( "'when' needs to be function that accepts a dict argument" ) # handle 'print' type if _type == "print": try: message = _kwargs.pop("message") except KeyError as e: raise PromptParameterException("message") from e # questions can take 'input' arg but print_formatted_text does not # Remove 'input', if present, to avoid breaking during tests _kwargs.pop("input", None) print_formatted_text(message, **_kwargs) if name: answers[name] = None continue choices = question_config.get("choices") if choices is not None and callable(choices): calculated_choices = choices(answers) question_config["choices"] = calculated_choices kwargs["choices"] = calculated_choices if _filter: # at least a little sanity check! if not callable(_filter): raise ValueError( "'filter' needs to be function that accepts an argument" ) if callable(question_config.get("default")): _kwargs["default"] = question_config["default"](answers) create_question_func = prompt_by_name(_type) if not create_question_func: raise ValueError( f"No question type '{_type}' found. " f"Known question types are {', '.join(AVAILABLE_PROMPTS)}." ) missing_args = list(utils.missing_arguments(create_question_func, _kwargs)) if missing_args: raise PromptParameterException(missing_args[0]) question = create_question_func(**_kwargs) answer = question.unsafe_ask(patch_stdout) if answer is not None: if _filter: try: answer = _filter(answer) except Exception as exception: raise ValueError( f"Problem processing 'filter' of {name} " f"question: {exception}" ) from exception answers[name] = answer return answers questionary-2.0.1/questionary/prompts/000077500000000000000000000000001447660764300202055ustar00rootroot00000000000000questionary-2.0.1/questionary/prompts/__init__.py000066400000000000000000000016541447660764300223240ustar00rootroot00000000000000from questionary.prompts import autocomplete from questionary.prompts import checkbox from questionary.prompts import confirm from questionary.prompts import password from questionary.prompts import path from questionary.prompts import press_any_key_to_continue from questionary.prompts import rawselect from questionary.prompts import select from questionary.prompts import text AVAILABLE_PROMPTS = { "autocomplete": autocomplete.autocomplete, "confirm": confirm.confirm, "text": text.text, "select": select.select, "rawselect": rawselect.rawselect, "password": password.password, "checkbox": checkbox.checkbox, "path": path.path, "press_any_key_to_continue": press_any_key_to_continue.press_any_key_to_continue, # backwards compatible names "list": select.select, "rawlist": rawselect.rawselect, "input": text.text, } def prompt_by_name(name): return AVAILABLE_PROMPTS.get(name) questionary-2.0.1/questionary/prompts/autocomplete.py000066400000000000000000000161721447660764300232670ustar00rootroot00000000000000from typing import Any from typing import Callable from typing import Dict from typing import Iterable from typing import List from typing import Optional from typing import Tuple from typing import Union from prompt_toolkit.completion import CompleteEvent from prompt_toolkit.completion import Completer from prompt_toolkit.completion import Completion from prompt_toolkit.document import Document from prompt_toolkit.formatted_text import HTML from prompt_toolkit.lexers import SimpleLexer from prompt_toolkit.shortcuts.prompt import CompleteStyle from prompt_toolkit.shortcuts.prompt import PromptSession from prompt_toolkit.styles import Style from questionary.constants import DEFAULT_QUESTION_PREFIX from questionary.prompts.common import build_validator from questionary.question import Question from questionary.styles import merge_styles_default class WordCompleter(Completer): choices_source: Union[List[str], Callable[[], List[str]]] ignore_case: bool meta_information: Dict[str, Any] match_middle: bool def __init__( self, choices: Union[List[str], Callable[[], List[str]]], ignore_case: bool = True, meta_information: Optional[Dict[str, Any]] = None, match_middle: bool = True, ) -> None: self.choices_source = choices self.ignore_case = ignore_case self.meta_information = meta_information or {} self.match_middle = match_middle def _choices(self) -> Iterable[str]: return ( self.choices_source() if callable(self.choices_source) else self.choices_source ) def _choice_matches(self, word_before_cursor: str, choice: str) -> int: """Match index if found, -1 if not.""" if self.ignore_case: choice = choice.lower() if self.match_middle: return choice.find(word_before_cursor) elif choice.startswith(word_before_cursor): return 0 else: return -1 @staticmethod def _display_for_choice(choice: str, index: int, word_before_cursor: str) -> HTML: return HTML("{}{}{}").format( choice[:index], choice[index : index + len(word_before_cursor)], # noqa: E203 choice[index + len(word_before_cursor) : len(choice)], # noqa: E203 ) def get_completions( self, document: Document, complete_event: CompleteEvent ) -> Iterable[Completion]: choices = self._choices() # Get word/text before cursor. word_before_cursor = document.text_before_cursor if self.ignore_case: word_before_cursor = word_before_cursor.lower() for choice in choices: index = self._choice_matches(word_before_cursor, choice) if index == -1: # didn't find a match continue display_meta = self.meta_information.get(choice, "") display = self._display_for_choice(choice, index, word_before_cursor) yield Completion( choice, start_position=-len(choice), display=display.formatted_text, display_meta=display_meta, style="class:answer", selected_style="class:selected", ) def autocomplete( message: str, choices: List[str], default: str = "", qmark: str = DEFAULT_QUESTION_PREFIX, completer: Optional[Completer] = None, meta_information: Optional[Dict[str, Any]] = None, ignore_case: bool = True, match_middle: bool = True, complete_style: CompleteStyle = CompleteStyle.COLUMN, validate: Any = None, style: Optional[Style] = None, **kwargs: Any, ) -> Question: """Prompt the user to enter a message with autocomplete help. Example: >>> import questionary >>> questionary.autocomplete( ... 'Choose ant specie', ... choices=[ ... 'Camponotus pennsylvanicus', ... 'Linepithema humile', ... 'Eciton burchellii', ... "Atta colombica", ... 'Polyergus lucidus', ... 'Polyergus rufescens', ... ]).ask() ? Choose ant specie Atta colombica 'Atta colombica' .. image:: ../images/autocomplete.gif This is just a really basic example, the prompt can be customised using the parameters. Args: message: Question text choices: Items shown in the selection, this contains items as strings default: Default return value (single value). qmark: Question prefix displayed in front of the question. By default this is a ``?`` completer: A prompt_toolkit :class:`prompt_toolkit.completion.Completion` implementation. If not set, a questionary completer implementation will be used. meta_information: A dictionary with information/anything about choices. ignore_case: If true autocomplete would ignore case. match_middle: If true autocomplete would search in every string position not only in string begin. complete_style: How autocomplete menu would be shown, it could be ``COLUMN`` ``MULTI_COLUMN`` or ``READLINE_LIKE`` from :class:`prompt_toolkit.shortcuts.CompleteStyle`. validate: Require the entered value to pass a validation. The value can not be submitted until the validator accepts it (e.g. to check minimum password length). This can either be a function accepting the input and returning a boolean, or an class reference to a subclass of the prompt toolkit Validator class. style: A custom color and style for the question parts. You can configure colors as well as font types for different elements. Returns: :class:`Question`: Question instance, ready to be prompted (using ``.ask()``). """ merged_style = merge_styles_default([style]) def get_prompt_tokens() -> List[Tuple[str, str]]: return [("class:qmark", qmark), ("class:question", " {} ".format(message))] def get_meta_style(meta: Optional[Dict[str, Any]]) -> Optional[Dict[str, Any]]: if meta: for key in meta: meta[key] = HTML("{}").format(meta[key]) return meta validator = build_validator(validate) if completer is None: if not choices: raise ValueError("No choices is given, you should use Text question.") # use the default completer completer = WordCompleter( choices, ignore_case=ignore_case, meta_information=get_meta_style(meta_information), match_middle=match_middle, ) p: PromptSession = PromptSession( get_prompt_tokens, lexer=SimpleLexer("class:answer"), style=merged_style, completer=completer, validator=validator, complete_style=complete_style, **kwargs, ) p.default_buffer.reset(Document(default)) return Question(p.app) questionary-2.0.1/questionary/prompts/checkbox.py000066400000000000000000000235501447660764300223520ustar00rootroot00000000000000from typing import Any from typing import Callable from typing import Dict from typing import List from typing import Optional from typing import Sequence from typing import Tuple from typing import Union from prompt_toolkit.application import Application from prompt_toolkit.formatted_text import FormattedText from prompt_toolkit.key_binding import KeyBindings from prompt_toolkit.keys import Keys from prompt_toolkit.styles import Style from questionary import utils from questionary.constants import DEFAULT_QUESTION_PREFIX from questionary.constants import DEFAULT_SELECTED_POINTER from questionary.constants import INVALID_INPUT from questionary.prompts import common from questionary.prompts.common import Choice from questionary.prompts.common import InquirerControl from questionary.prompts.common import Separator from questionary.question import Question from questionary.styles import merge_styles_default def checkbox( message: str, choices: Sequence[Union[str, Choice, Dict[str, Any]]], default: Optional[str] = None, validate: Callable[[List[str]], Union[bool, str]] = lambda a: True, qmark: str = DEFAULT_QUESTION_PREFIX, pointer: Optional[str] = DEFAULT_SELECTED_POINTER, style: Optional[Style] = None, initial_choice: Optional[Union[str, Choice, Dict[str, Any]]] = None, use_arrow_keys: bool = True, use_jk_keys: bool = True, use_emacs_keys: bool = True, instruction: Optional[str] = None, **kwargs: Any, ) -> Question: """Ask the user to select from a list of items. This is a multiselect, the user can choose one, none or many of the items. Example: >>> import questionary >>> questionary.checkbox( ... 'Select toppings', ... choices=[ ... "Cheese", ... "Tomato", ... "Pineapple", ... ]).ask() ? Select toppings done (2 selections) ['Cheese', 'Pineapple'] .. image:: ../images/checkbox.gif This is just a really basic example, the prompt can be customised using the parameters. Args: message: Question text choices: Items shown in the selection, this can contain :class:`Choice` or or :class:`Separator` objects or simple items as strings. Passing :class:`Choice` objects, allows you to configure the item more (e.g. preselecting it or disabling it). default: Default return value (single value). If you want to preselect multiple items, use ``Choice("foo", checked=True)`` instead. validate: Require the entered value to pass a validation. The value can not be submitted until the validator accepts it (e.g. to check minimum password length). This should be a function accepting the input and returning a boolean. Alternatively, the return value may be a string (indicating failure), which contains the error message to be displayed. qmark: Question prefix displayed in front of the question. By default this is a ``?``. pointer: Pointer symbol in front of the currently highlighted element. By default this is a ``»``. Use ``None`` to disable it. style: A custom color and style for the question parts. You can configure colors as well as font types for different elements. initial_choice: A value corresponding to a selectable item in the choices, to initially set the pointer position to. use_arrow_keys: Allow the user to select items from the list using arrow keys. use_jk_keys: Allow the user to select items from the list using `j` (down) and `k` (up) keys. use_emacs_keys: Allow the user to select items from the list using `Ctrl+N` (down) and `Ctrl+P` (up) keys. instruction: A message describing how to navigate the menu. Returns: :class:`Question`: Question instance, ready to be prompted (using ``.ask()``). """ if not (use_arrow_keys or use_jk_keys or use_emacs_keys): raise ValueError( "Some option to move the selection is required. Arrow keys or j/k or " "Emacs keys." ) merged_style = merge_styles_default( [ # Disable the default inverted colours bottom-toolbar behaviour (for # the error message). However it can be re-enabled with a custom # style. Style([("bottom-toolbar", "noreverse")]), style, ] ) if not callable(validate): raise ValueError("validate must be callable") ic = InquirerControl( choices, default, pointer=pointer, initial_choice=initial_choice ) def get_prompt_tokens() -> List[Tuple[str, str]]: tokens = [] tokens.append(("class:qmark", qmark)) tokens.append(("class:question", " {} ".format(message))) if ic.is_answered: nbr_selected = len(ic.selected_options) if nbr_selected == 0: tokens.append(("class:answer", "done")) elif nbr_selected == 1: if isinstance(ic.get_selected_values()[0].title, list): ts = ic.get_selected_values()[0].title tokens.append( ( "class:answer", "".join([token[1] for token in ts]), # type:ignore ) ) else: tokens.append( ( "class:answer", "[{}]".format(ic.get_selected_values()[0].title), ) ) else: tokens.append( ("class:answer", "done ({} selections)".format(nbr_selected)) ) else: if instruction is not None: tokens.append(("class:instruction", instruction)) else: tokens.append( ( "class:instruction", "(Use arrow keys to move, " " to select, " " to toggle, " " to invert)", ) ) return tokens def get_selected_values() -> List[Any]: return [c.value for c in ic.get_selected_values()] def perform_validation(selected_values: List[str]) -> bool: verdict = validate(selected_values) valid = verdict is True if not valid: if verdict is False: error_text = INVALID_INPUT else: error_text = str(verdict) error_message = FormattedText([("class:validation-toolbar", error_text)]) ic.error_message = ( error_message if not valid and ic.submission_attempted else None # type: ignore[assignment] ) return valid layout = common.create_inquirer_layout(ic, get_prompt_tokens, **kwargs) bindings = KeyBindings() @bindings.add(Keys.ControlQ, eager=True) @bindings.add(Keys.ControlC, eager=True) def _(event): event.app.exit(exception=KeyboardInterrupt, style="class:aborting") @bindings.add(" ", eager=True) def toggle(_event): pointed_choice = ic.get_pointed_at().value if pointed_choice in ic.selected_options: ic.selected_options.remove(pointed_choice) else: ic.selected_options.append(pointed_choice) perform_validation(get_selected_values()) @bindings.add("i", eager=True) def invert(_event): inverted_selection = [ c.value for c in ic.choices if not isinstance(c, Separator) and c.value not in ic.selected_options and not c.disabled ] ic.selected_options = inverted_selection perform_validation(get_selected_values()) @bindings.add("a", eager=True) def all(_event): all_selected = True # all choices have been selected for c in ic.choices: if ( not isinstance(c, Separator) and c.value not in ic.selected_options and not c.disabled ): # add missing ones ic.selected_options.append(c.value) all_selected = False if all_selected: ic.selected_options = [] perform_validation(get_selected_values()) def move_cursor_down(event): ic.select_next() while not ic.is_selection_valid(): ic.select_next() def move_cursor_up(event): ic.select_previous() while not ic.is_selection_valid(): ic.select_previous() if use_arrow_keys: bindings.add(Keys.Down, eager=True)(move_cursor_down) bindings.add(Keys.Up, eager=True)(move_cursor_up) if use_jk_keys: bindings.add("j", eager=True)(move_cursor_down) bindings.add("k", eager=True)(move_cursor_up) if use_emacs_keys: bindings.add(Keys.ControlN, eager=True)(move_cursor_down) bindings.add(Keys.ControlP, eager=True)(move_cursor_up) @bindings.add(Keys.ControlM, eager=True) def set_answer(event): selected_values = get_selected_values() ic.submission_attempted = True if perform_validation(selected_values): ic.is_answered = True event.app.exit(result=selected_values) @bindings.add(Keys.Any) def other(_event): """Disallow inserting other text.""" return Question( Application( layout=layout, key_bindings=bindings, style=merged_style, **utils.used_kwargs(kwargs, Application.__init__), ) ) questionary-2.0.1/questionary/prompts/common.py000066400000000000000000000431521447660764300220540ustar00rootroot00000000000000import inspect from typing import Any from typing import Callable from typing import Dict from typing import List from typing import Optional from typing import Sequence from typing import Tuple from typing import Union from prompt_toolkit import PromptSession from prompt_toolkit.filters import Always from prompt_toolkit.filters import Condition from prompt_toolkit.filters import IsDone from prompt_toolkit.layout import ConditionalContainer from prompt_toolkit.layout import FormattedTextControl from prompt_toolkit.layout import HSplit from prompt_toolkit.layout import Layout from prompt_toolkit.layout import Window from prompt_toolkit.styles import Style from prompt_toolkit.validation import ValidationError from prompt_toolkit.validation import Validator from questionary.constants import DEFAULT_SELECTED_POINTER from questionary.constants import DEFAULT_STYLE from questionary.constants import INDICATOR_SELECTED from questionary.constants import INDICATOR_UNSELECTED from questionary.constants import INVALID_INPUT # This is a cut-down version of `prompt_toolkit.formatted_text.AnyFormattedText` # which does not exist in v2 of prompt_toolkit FormattedText = Union[ str, List[Tuple[str, str]], List[Tuple[str, str, Callable[[Any], None]]], None, ] class Choice: """One choice in a :meth:`select`, :meth:`rawselect` or :meth:`checkbox`. Args: title: Text shown in the selection list. value: Value returned, when the choice is selected. If this argument is `None` or unset, then the value of `title` is used. disabled: If set, the choice can not be selected by the user. The provided text is used to explain, why the selection is disabled. checked: Preselect this choice when displaying the options. shortcut_key: Key shortcut used to select this item. """ title: FormattedText """Display string for the choice""" value: Optional[Any] """Value of the choice""" disabled: Optional[str] """Whether the choice can be selected""" checked: Optional[bool] """Whether the choice is initially selected""" shortcut_key: Optional[str] """A shortcut key for the choice""" def __init__( self, title: FormattedText, value: Optional[Any] = None, disabled: Optional[str] = None, checked: Optional[bool] = False, shortcut_key: Optional[Union[str, bool]] = True, ) -> None: self.disabled = disabled self.title = title self.checked = checked if checked is not None else False if value is not None: self.value = value elif isinstance(title, list): self.value = "".join([token[1] for token in title]) else: self.value = title if shortcut_key is not None: if isinstance(shortcut_key, bool): self.auto_shortcut = shortcut_key self.shortcut_key = None else: self.shortcut_key = str(shortcut_key) self.auto_shortcut = False else: self.shortcut_key = None self.auto_shortcut = True @staticmethod def build(c: Union[str, "Choice", Dict[str, Any]]) -> "Choice": """Create a choice object from different representations. Args: c: Either a :obj:`str`, :class:`Choice` or :obj:`dict` with ``name``, ``value``, ``disabled``, ``checked`` and ``key`` properties. Returns: An instance of the :class:`Choice` object. """ if isinstance(c, Choice): return c elif isinstance(c, str): return Choice(c, c) else: return Choice( c.get("name"), c.get("value"), c.get("disabled", None), c.get("checked"), c.get("key"), ) def get_shortcut_title(self): if self.shortcut_key is None: return "-) " else: return "{}) ".format(self.shortcut_key) class Separator(Choice): """Used to space/separate choices group.""" default_separator: str = "-" * 15 """The default separator used if none is specified""" line: str """The string being used as a separator""" def __init__(self, line: Optional[str] = None) -> None: """Create a separator in a list. Args: line: Text to be displayed in the list, by default uses ``---``. """ self.line = line or self.default_separator super().__init__(self.line, None, "-") class InquirerControl(FormattedTextControl): SHORTCUT_KEYS = [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", ] choices: List[Choice] default: Optional[Union[str, Choice, Dict[str, Any]]] selected_options: List[Any] use_indicator: bool use_shortcuts: bool use_arrow_keys: bool pointer: Optional[str] pointed_at: int is_answered: bool def __init__( self, choices: Sequence[Union[str, Choice, Dict[str, Any]]], default: Optional[Union[str, Choice, Dict[str, Any]]] = None, pointer: Optional[str] = DEFAULT_SELECTED_POINTER, use_indicator: bool = True, use_shortcuts: bool = False, show_selected: bool = False, use_arrow_keys: bool = True, initial_choice: Optional[Union[str, Choice, Dict[str, Any]]] = None, **kwargs: Any, ): self.use_indicator = use_indicator self.use_shortcuts = use_shortcuts self.show_selected = show_selected self.use_arrow_keys = use_arrow_keys self.default = default self.pointer = pointer if isinstance(default, Choice): default = default.value choices_values = [ choice.value for choice in choices if isinstance(choice, Choice) ] if ( default is not None and default not in choices and default not in choices_values ): raise ValueError( f"Invalid `default` value passed. The value (`{default}`) " f"does not exist in the set of choices. Please make sure the " f"default value is one of the available choices." ) if initial_choice is None: pointed_at = None elif initial_choice in choices: pointed_at = choices.index(initial_choice) elif initial_choice in choices_values: for k, choice in enumerate(choices): if isinstance(choice, Choice): if choice.value == initial_choice: pointed_at = k break else: raise ValueError( f"Invalid `initial_choice` value passed. The value " f"(`{initial_choice}`) does not exist in " f"the set of choices. Please make sure the initial value is " f"one of the available choices." ) self.is_answered = False self.choices = [] self.submission_attempted = False self.error_message = None self.selected_options = [] self._init_choices(choices, pointed_at) self._assign_shortcut_keys() super().__init__(self._get_choice_tokens, **kwargs) if not self.is_selection_valid(): raise ValueError( f"Invalid 'initial_choice' value ('{initial_choice}'). " f"It must be a selectable value." ) def _is_selected(self, choice: Choice): if isinstance(self.default, Choice): compare_default = self.default == choice else: compare_default = self.default == choice.value return choice.checked or compare_default and self.default is not None def _assign_shortcut_keys(self): available_shortcuts = self.SHORTCUT_KEYS[:] # first, make sure we do not double assign a shortcut for c in self.choices: if c.shortcut_key is not None: if c.shortcut_key in available_shortcuts: available_shortcuts.remove(c.shortcut_key) else: raise ValueError( "Invalid shortcut '{}'" "for choice '{}'. Shortcuts " "should be single characters or numbers. " "Make sure that all your shortcuts are " "unique.".format(c.shortcut_key, c.title) ) shortcut_idx = 0 for c in self.choices: if c.auto_shortcut and not c.disabled: c.shortcut_key = available_shortcuts[shortcut_idx] shortcut_idx += 1 if shortcut_idx == len(available_shortcuts): break # fail gracefully if we run out of shortcuts def _init_choices( self, choices: Sequence[Union[str, Choice, Dict[str, Any]]], pointed_at: Optional[int], ): # helper to convert from question format to internal format self.choices = [] if pointed_at is not None: self.pointed_at = pointed_at for i, c in enumerate(choices): choice = Choice.build(c) if self._is_selected(choice): self.selected_options.append(choice.value) if pointed_at is None and not choice.disabled: # find the first (available) choice self.pointed_at = pointed_at = i self.choices.append(choice) @property def choice_count(self) -> int: return len(self.choices) def _get_choice_tokens(self): tokens = [] def append(index: int, choice: Choice): # use value to check if option has been selected selected = choice.value in self.selected_options if index == self.pointed_at: if self.pointer is not None: tokens.append(("class:pointer", " {} ".format(self.pointer))) else: tokens.append(("class:text", " " * 3)) tokens.append(("[SetCursorPosition]", "")) else: pointer_length = len(self.pointer) if self.pointer is not None else 1 tokens.append(("class:text", " " * (2 + pointer_length))) if isinstance(choice, Separator): tokens.append(("class:separator", "{}".format(choice.title))) elif choice.disabled: # disabled if isinstance(choice.title, list): tokens.append( ("class:selected" if selected else "class:disabled", "- ") ) tokens.extend(choice.title) else: tokens.append( ( "class:selected" if selected else "class:disabled", "- {}".format(choice.title), ) ) tokens.append( ( "class:selected" if selected else "class:disabled", "{}".format( "" if isinstance(choice.disabled, bool) else " ({})".format(choice.disabled) ), ) ) else: shortcut = choice.get_shortcut_title() if self.use_shortcuts else "" if selected: if self.use_indicator: indicator = INDICATOR_SELECTED + " " else: indicator = "" tokens.append(("class:selected", "{}".format(indicator))) else: if self.use_indicator: indicator = INDICATOR_UNSELECTED + " " else: indicator = "" tokens.append(("class:text", "{}".format(indicator))) if isinstance(choice.title, list): tokens.extend(choice.title) elif selected: tokens.append( ("class:selected", "{}{}".format(shortcut, choice.title)) ) elif index == self.pointed_at: tokens.append( ("class:highlighted", "{}{}".format(shortcut, choice.title)) ) else: tokens.append(("class:text", "{}{}".format(shortcut, choice.title))) tokens.append(("", "\n")) # prepare the select choices for i, c in enumerate(self.choices): append(i, c) if self.show_selected: current = self.get_pointed_at() answer = current.get_shortcut_title() if self.use_shortcuts else "" answer += ( current.title if isinstance(current.title, str) else current.title[0][1] ) tokens.append(("class:text", " Answer: {}".format(answer))) else: tokens.pop() # Remove last newline. return tokens def is_selection_a_separator(self) -> bool: selected = self.choices[self.pointed_at] return isinstance(selected, Separator) def is_selection_disabled(self) -> Optional[str]: return self.choices[self.pointed_at].disabled def is_selection_valid(self) -> bool: return not self.is_selection_disabled() and not self.is_selection_a_separator() def select_previous(self) -> None: self.pointed_at = (self.pointed_at - 1) % self.choice_count def select_next(self) -> None: self.pointed_at = (self.pointed_at + 1) % self.choice_count def get_pointed_at(self) -> Choice: return self.choices[self.pointed_at] def get_selected_values(self) -> List[Choice]: # get values not labels return [ c for c in self.choices if (not isinstance(c, Separator) and c.value in self.selected_options) ] def build_validator(validate: Any) -> Optional[Validator]: if validate: if inspect.isclass(validate) and issubclass(validate, Validator): return validate() elif isinstance(validate, Validator): return validate elif callable(validate): class _InputValidator(Validator): def validate(self, document): verdict = validate(document.text) if verdict is not True: if verdict is False: verdict = INVALID_INPUT raise ValidationError( message=verdict, cursor_position=len(document.text) ) return _InputValidator() return None def _fix_unecessary_blank_lines(ps: PromptSession) -> None: """This is a fix for additional empty lines added by prompt toolkit. This assumes the layout of the default session doesn't change, if it does, this needs an update.""" default_container = ps.layout.container default_buffer_window = ( default_container.get_children()[0].content.get_children()[1].content # type: ignore[attr-defined] ) assert isinstance(default_buffer_window, Window) # this forces the main window to stay as small as possible, avoiding # empty lines in selections default_buffer_window.dont_extend_height = Always() default_buffer_window.always_hide_cursor = Always() def create_inquirer_layout( ic: InquirerControl, get_prompt_tokens: Callable[[], List[Tuple[str, str]]], **kwargs: Any, ) -> Layout: """Create a layout combining question and inquirer selection.""" ps: PromptSession = PromptSession( get_prompt_tokens, reserve_space_for_menu=0, **kwargs ) _fix_unecessary_blank_lines(ps) validation_prompt: PromptSession = PromptSession( bottom_toolbar=lambda: ic.error_message, **kwargs ) return Layout( HSplit( [ ps.layout.container, ConditionalContainer(Window(ic), filter=~IsDone()), ConditionalContainer( validation_prompt.layout.container, filter=Condition(lambda: ic.error_message is not None), ), ] ) ) def print_formatted_text(text: str, style: Optional[str] = None, **kwargs: Any) -> None: """Print formatted text. Sometimes you want to spice up your printed messages a bit, :meth:`questionary.print` is a helper to do just that. Example: >>> import questionary >>> questionary.print("Hello World 🦄", style="bold italic fg:darkred") Hello World 🦄 .. image:: ../images/print.gif Args: text: Text to be printed. style: Style used for printing. The style argument uses the prompt :ref:`toolkit style strings `. """ from prompt_toolkit import print_formatted_text as pt_print from prompt_toolkit.formatted_text import FormattedText as FText if style is not None: text_style = Style([("text", style)]) else: text_style = DEFAULT_STYLE pt_print(FText([("class:text", text)]), style=text_style, **kwargs) questionary-2.0.1/questionary/prompts/confirm.py000066400000000000000000000077651447660764300222330ustar00rootroot00000000000000from typing import Any from typing import Optional from prompt_toolkit import PromptSession from prompt_toolkit.formatted_text import to_formatted_text from prompt_toolkit.key_binding import KeyBindings from prompt_toolkit.keys import Keys from prompt_toolkit.styles import Style from questionary.constants import DEFAULT_QUESTION_PREFIX from questionary.constants import NO from questionary.constants import NO_OR_YES from questionary.constants import YES from questionary.constants import YES_OR_NO from questionary.question import Question from questionary.styles import merge_styles_default def confirm( message: str, default: bool = True, qmark: str = DEFAULT_QUESTION_PREFIX, style: Optional[Style] = None, auto_enter: bool = True, instruction: Optional[str] = None, **kwargs: Any, ) -> Question: """A yes or no question. The user can either confirm or deny. This question type can be used to prompt the user for a confirmation of a yes-or-no question. If the user just hits enter, the default value will be returned. Example: >>> import questionary >>> questionary.confirm("Are you amazed?").ask() ? Are you amazed? Yes True .. image:: ../images/confirm.gif This is just a really basic example, the prompt can be customised using the parameters. Args: message: Question text. default: Default value will be returned if the user just hits enter. qmark: Question prefix displayed in front of the question. By default this is a ``?``. style: A custom color and style for the question parts. You can configure colors as well as font types for different elements. auto_enter: If set to `False`, the user needs to press the 'enter' key to accept their answer. If set to `True`, a valid input will be accepted without the need to press 'Enter'. instruction: A message describing how to proceed through the confirmation prompt. Returns: :class:`Question`: Question instance, ready to be prompted (using `.ask()`). """ merged_style = merge_styles_default([style]) status = {"answer": None, "complete": False} def get_prompt_tokens(): tokens = [] tokens.append(("class:qmark", qmark)) tokens.append(("class:question", " {} ".format(message))) if instruction is not None: tokens.append(("class:instruction", instruction)) elif not status["complete"]: _instruction = YES_OR_NO if default else NO_OR_YES tokens.append(("class:instruction", "{} ".format(_instruction))) if status["answer"] is not None: answer = YES if status["answer"] else NO tokens.append(("class:answer", answer)) return to_formatted_text(tokens) def exit_with_result(event): status["complete"] = True event.app.exit(result=status["answer"]) bindings = KeyBindings() @bindings.add(Keys.ControlQ, eager=True) @bindings.add(Keys.ControlC, eager=True) def _(event): event.app.exit(exception=KeyboardInterrupt, style="class:aborting") @bindings.add("n") @bindings.add("N") def key_n(event): status["answer"] = False if auto_enter: exit_with_result(event) @bindings.add("y") @bindings.add("Y") def key_y(event): status["answer"] = True if auto_enter: exit_with_result(event) @bindings.add(Keys.ControlH) def key_backspace(event): status["answer"] = None @bindings.add(Keys.ControlM, eager=True) def set_answer(event): if status["answer"] is None: status["answer"] = default exit_with_result(event) @bindings.add(Keys.Any) def other(event): """Disallow inserting other text.""" return Question( PromptSession( get_prompt_tokens, key_bindings=bindings, style=merged_style, **kwargs ).app ) questionary-2.0.1/questionary/prompts/password.py000066400000000000000000000037161447660764300224300ustar00rootroot00000000000000from typing import Any from typing import Optional from questionary import Style from questionary.constants import DEFAULT_QUESTION_PREFIX from questionary.prompts import text from questionary.question import Question def password( message: str, default: str = "", validate: Any = None, qmark: str = DEFAULT_QUESTION_PREFIX, style: Optional[Style] = None, **kwargs: Any, ) -> Question: """A text input where a user can enter a secret which won't be displayed on the CLI. This question type can be used to prompt the user for information that should not be shown in the command line. The typed text will be replaced with ``*``. Example: >>> import questionary >>> questionary.password("What's your secret?").ask() ? What's your secret? ******** 'secret42' .. image:: ../images/password.gif This is just a really basic example, the prompt can be customised using the parameters. Args: message: Question text. default: Default value will be returned if the user just hits enter. validate: Require the entered value to pass a validation. The value can not be submitted until the validator accepts it (e.g. to check minimum password length). This can either be a function accepting the input and returning a boolean, or an class reference to a subclass of the prompt toolkit Validator class. qmark: Question prefix displayed in front of the question. By default this is a ``?``. style: A custom color and style for the question parts. You can configure colors as well as font types for different elements. Returns: :class:`Question`: Question instance, ready to be prompted (using ``.ask()``). """ return text.text( message, default, validate, qmark, style, is_password=True, **kwargs ) questionary-2.0.1/questionary/prompts/path.py000066400000000000000000000232671447660764300215250ustar00rootroot00000000000000import os from typing import Any from typing import Callable from typing import Iterable from typing import List from typing import Optional from typing import Tuple from prompt_toolkit.completion import CompleteEvent from prompt_toolkit.completion import Completion from prompt_toolkit.completion import PathCompleter from prompt_toolkit.completion.base import Completer from prompt_toolkit.document import Document from prompt_toolkit.key_binding import KeyBindings from prompt_toolkit.key_binding.key_processor import KeyPressEvent from prompt_toolkit.keys import Keys from prompt_toolkit.lexers import SimpleLexer from prompt_toolkit.shortcuts.prompt import CompleteStyle from prompt_toolkit.shortcuts.prompt import PromptSession from prompt_toolkit.styles import Style from questionary.constants import DEFAULT_QUESTION_PREFIX from questionary.prompts.common import build_validator from questionary.question import Question from questionary.styles import merge_styles_default class GreatUXPathCompleter(PathCompleter): """Wraps :class:`prompt_toolkit.completion.PathCompleter`. Makes sure completions for directories end with a path separator. Also make sure the right path separator is used. Checks if `get_paths` returns list of existing directories. """ def __init__( self, only_directories: bool = False, get_paths: Optional[Callable[[], List[str]]] = None, file_filter: Optional[Callable[[str], bool]] = None, min_input_len: int = 0, expanduser: bool = False, ) -> None: """Adds validation of 'get_paths' to :class:`prompt_toolkit.completion.PathCompleter`. Args: only_directories (bool): If True, only directories will be returned, but no files. Defaults to False. get_paths (Callable[[], List[str]], optional): Callable which returns a list of directories to look into when the user enters a relative path. If None, set to (lambda: ["."]). Defaults to None. file_filter (Callable[[str], bool], optional): Callable which takes a filename and returns whether this file should show up in the completion. ``None`` when no filtering has to be done. Defaults to None. min_input_len (int): Don't do autocompletion when the input string is shorter. Defaults to 0. expanduser (bool): If True, tilde (~) is expanded. Defaults to False. Raises: ValueError: If any of the by `get_paths` returned directories does not exist. """ # if get_paths is None, make it return the current working dir get_paths = get_paths or (lambda: ["."]) # validation of get_paths for current_path in get_paths(): if not os.path.isdir(current_path): raise ( ValueError( "\n Completer for file paths 'get_paths' must return only existing directories, but" f" '{current_path}' does not exist." ) ) # call PathCompleter __init__ super().__init__( only_directories=only_directories, get_paths=get_paths, file_filter=file_filter, min_input_len=min_input_len, expanduser=expanduser, ) def get_completions( self, document: Document, complete_event: CompleteEvent ) -> Iterable[Completion]: """Get completions. Wraps :class:`prompt_toolkit.completion.PathCompleter`. Makes sure completions for directories end with a path separator. Also make sure the right path separator is used. """ completions = super(GreatUXPathCompleter, self).get_completions( document, complete_event ) for completion in completions: # check if the display value ends with a path separator. # first check if display is properly set styled_display = completion.display[0] # styled display is a formatted text (a tuple of the text and its style) # second tuple entry is the text if styled_display[1][-1] == "/": # replace separator with the OS specific one display_text = styled_display[1][:-1] + os.path.sep # update the styled display with the modified text completion.display[0] = (styled_display[0], display_text) # append the separator to the text as well - unclear why the normal # path completer omits it from the text. this improves UX for the # user, as they don't need to type the separator after auto-completing # a directory completion.text += os.path.sep yield completion def path( message: str, default: str = "", qmark: str = DEFAULT_QUESTION_PREFIX, validate: Any = None, completer: Optional[Completer] = None, style: Optional[Style] = None, only_directories: bool = False, get_paths: Optional[Callable[[], List[str]]] = None, file_filter: Optional[Callable[[str], bool]] = None, complete_style: CompleteStyle = CompleteStyle.MULTI_COLUMN, **kwargs: Any, ) -> Question: """A text input for a file or directory path with autocompletion enabled. Example: >>> import questionary >>> questionary.path( >>> "What's the path to the projects version file?" >>> ).ask() ? What's the path to the projects version file? ./pyproject.toml './pyproject.toml' .. image:: ../images/path.gif This is just a really basic example, the prompt can be customized using the parameters. Args: message: Question text. default: Default return value (single value). qmark: Question prefix displayed in front of the question. By default this is a ``?``. complete_style: How autocomplete menu would be shown, it could be ``COLUMN`` ``MULTI_COLUMN`` or ``READLINE_LIKE`` from :class:`prompt_toolkit.shortcuts.CompleteStyle`. validate: Require the entered value to pass a validation. The value can not be submitted until the validator accepts it (e.g. to check minimum password length). This can either be a function accepting the input and returning a boolean, or an class reference to a subclass of the prompt toolkit Validator class. completer: A custom completer to use in the prompt. For more information, see `this `_. style: A custom color and style for the question parts. You can configure colors as well as font types for different elements. only_directories: Only show directories in auto completion. This option does not do anything if a custom ``completer`` is passed. get_paths: Set a callable to generate paths to traverse for suggestions. This option does not do anything if a custom ``completer`` is passed. file_filter: Optional callable to filter suggested paths. Only paths where the passed callable evaluates to ``True`` will show up in the suggested paths. This does not validate the typed path, e.g. it is still possible for the user to enter a path manually, even though this filter evaluates to ``False``. If in addition to filtering suggestions you also want to validate the result, use ``validate`` in combination with the ``file_filter``. Returns: :class:`Question`: Question instance, ready to be prompted (using ``.ask()``). """ # noqa: W505, E501 merged_style = merge_styles_default([style]) def get_prompt_tokens() -> List[Tuple[str, str]]: return [("class:qmark", qmark), ("class:question", " {} ".format(message))] validator = build_validator(validate) completer = completer or GreatUXPathCompleter( get_paths=get_paths, only_directories=only_directories, file_filter=file_filter, expanduser=True, ) bindings = KeyBindings() @bindings.add(Keys.ControlM, eager=True) def set_answer(event: KeyPressEvent): if event.current_buffer.complete_state is not None: event.current_buffer.complete_state = None elif event.app.current_buffer.validate(set_cursor=True): # When the validation succeeded, accept the input. result_path = event.app.current_buffer.document.text if result_path.endswith(os.path.sep): result_path = result_path[:-1] event.app.exit(result=result_path) event.app.current_buffer.append_to_history() @bindings.add(os.path.sep, eager=True) def next_segment(event: KeyPressEvent): b = event.app.current_buffer if b.complete_state: b.complete_state = None current_path = b.document.text if not current_path.endswith(os.path.sep): b.insert_text(os.path.sep) b.start_completion(select_first=False) p: PromptSession = PromptSession( get_prompt_tokens, lexer=SimpleLexer("class:answer"), style=merged_style, completer=completer, validator=validator, complete_style=complete_style, key_bindings=bindings, **kwargs, ) p.default_buffer.reset(Document(default)) return Question(p.app) questionary-2.0.1/questionary/prompts/press_any_key_to_continue.py000066400000000000000000000031761447660764300260470ustar00rootroot00000000000000from typing import Any from typing import Optional from prompt_toolkit import PromptSession from prompt_toolkit.formatted_text import to_formatted_text from prompt_toolkit.key_binding import KeyBindings from prompt_toolkit.keys import Keys from prompt_toolkit.styles import Style from questionary.question import Question from questionary.styles import merge_styles_default def press_any_key_to_continue( message: Optional[str] = None, style: Optional[Style] = None, **kwargs: Any, ): """Wait until user presses any key to continue. Example: >>> import questionary >>> questionary.press_any_key_to_continue().ask() Press any key to continue... '' Args: message: Question text. Defaults to ``"Press any key to continue..."`` style: A custom color and style for the question parts. You can configure colors as well as font types for different elements. Returns: :class:`Question`: Question instance, ready to be prompted (using ``.ask()``). """ merged_style = merge_styles_default([style]) if message is None: message = "Press any key to continue..." def get_prompt_tokens(): tokens = [] tokens.append(("class:question", f" {message} ")) return to_formatted_text(tokens) def exit_with_result(event): event.app.exit(result=None) bindings = KeyBindings() @bindings.add(Keys.Any) def any_key(event): exit_with_result(event) return Question( PromptSession( get_prompt_tokens, key_bindings=bindings, style=merged_style, **kwargs ).app ) questionary-2.0.1/questionary/prompts/rawselect.py000066400000000000000000000046661447660764300225640ustar00rootroot00000000000000from typing import Any from typing import Dict from typing import Optional from typing import Sequence from typing import Union from prompt_toolkit.styles import Style from questionary.constants import DEFAULT_QUESTION_PREFIX from questionary.constants import DEFAULT_SELECTED_POINTER from questionary.prompts import select from questionary.prompts.common import Choice from questionary.question import Question def rawselect( message: str, choices: Sequence[Union[str, Choice, Dict[str, Any]]], default: Optional[str] = None, qmark: str = DEFAULT_QUESTION_PREFIX, pointer: Optional[str] = DEFAULT_SELECTED_POINTER, style: Optional[Style] = None, **kwargs: Any, ) -> Question: """Ask the user to select one item from a list of choices using shortcuts. The user can only select one option. Example: >>> import questionary >>> questionary.rawselect( ... "What do you want to do?", ... choices=[ ... "Order a pizza", ... "Make a reservation", ... "Ask for opening hours" ... ]).ask() ? What do you want to do? Order a pizza 'Order a pizza' .. image:: ../images/rawselect.gif This is just a really basic example, the prompt can be customised using the parameters. Args: message: Question text. choices: Items shown in the selection, this can contain :class:`Choice` or or :class:`Separator` objects or simple items as strings. Passing :class:`Choice` objects, allows you to configure the item more (e.g. preselecting it or disabling it). default: Default return value (single value). qmark: Question prefix displayed in front of the question. By default this is a ``?``. pointer: Pointer symbol in front of the currently highlighted element. By default this is a ``»``. Use ``None`` to disable it. style: A custom color and style for the question parts. You can configure colors as well as font types for different elements. Returns: :class:`Question`: Question instance, ready to be prompted (using ``.ask()``). """ return select.select( message, choices, default, qmark, pointer, style, use_shortcuts=True, use_arrow_keys=False, **kwargs, ) questionary-2.0.1/questionary/prompts/select.py000066400000000000000000000223401447660764300220370ustar00rootroot00000000000000# -*- coding: utf-8 -*- from typing import Any from typing import Dict from typing import Optional from typing import Sequence from typing import Union from prompt_toolkit.application import Application from prompt_toolkit.key_binding import KeyBindings from prompt_toolkit.keys import Keys from prompt_toolkit.styles import Style from questionary import utils from questionary.constants import DEFAULT_QUESTION_PREFIX from questionary.constants import DEFAULT_SELECTED_POINTER from questionary.prompts import common from questionary.prompts.common import Choice from questionary.prompts.common import InquirerControl from questionary.prompts.common import Separator from questionary.question import Question from questionary.styles import merge_styles_default def select( message: str, choices: Sequence[Union[str, Choice, Dict[str, Any]]], default: Optional[Union[str, Choice, Dict[str, Any]]] = None, qmark: str = DEFAULT_QUESTION_PREFIX, pointer: Optional[str] = DEFAULT_SELECTED_POINTER, style: Optional[Style] = None, use_shortcuts: bool = False, use_arrow_keys: bool = True, use_indicator: bool = False, use_jk_keys: bool = True, use_emacs_keys: bool = True, show_selected: bool = False, instruction: Optional[str] = None, **kwargs: Any, ) -> Question: """A list of items to select **one** option from. The user can pick one option and confirm it (if you want to allow the user to select multiple options, use :meth:`questionary.checkbox` instead). Example: >>> import questionary >>> questionary.select( ... "What do you want to do?", ... choices=[ ... "Order a pizza", ... "Make a reservation", ... "Ask for opening hours" ... ]).ask() ? What do you want to do? Order a pizza 'Order a pizza' .. image:: ../images/select.gif This is just a really basic example, the prompt can be customised using the parameters. Args: message: Question text choices: Items shown in the selection, this can contain :class:`Choice` or or :class:`Separator` objects or simple items as strings. Passing :class:`Choice` objects, allows you to configure the item more (e.g. preselecting it or disabling it). default: A value corresponding to a selectable item in the choices, to initially set the pointer position to. qmark: Question prefix displayed in front of the question. By default this is a ``?``. pointer: Pointer symbol in front of the currently highlighted element. By default this is a ``»``. Use ``None`` to disable it. instruction: A hint on how to navigate the menu. It's ``(Use shortcuts)`` if only ``use_shortcuts`` is set to True, ``(Use arrow keys or shortcuts)`` if ``use_arrow_keys`` & ``use_shortcuts`` are set and ``(Use arrow keys)`` by default. style: A custom color and style for the question parts. You can configure colors as well as font types for different elements. use_indicator: Flag to enable the small indicator in front of the list highlighting the current location of the selection cursor. use_shortcuts: Allow the user to select items from the list using shortcuts. The shortcuts will be displayed in front of the list items. Arrow keys, j/k keys and shortcuts are not mutually exclusive. use_arrow_keys: Allow the user to select items from the list using arrow keys. Arrow keys, j/k keys and shortcuts are not mutually exclusive. use_jk_keys: Allow the user to select items from the list using `j` (down) and `k` (up) keys. Arrow keys, j/k keys and shortcuts are not mutually exclusive. use_emacs_keys: Allow the user to select items from the list using `Ctrl+N` (down) and `Ctrl+P` (up) keys. Arrow keys, j/k keys, emacs keys and shortcuts are not mutually exclusive. show_selected: Display current selection choice at the bottom of list. Returns: :class:`Question`: Question instance, ready to be prompted (using ``.ask()``). """ if not (use_arrow_keys or use_shortcuts or use_jk_keys or use_emacs_keys): raise ValueError( ( "Some option to move the selection is required. " "Arrow keys, j/k keys, emacs keys, or shortcuts." ) ) if use_shortcuts and use_jk_keys: if any(getattr(c, "shortcut_key", "") in ["j", "k"] for c in choices): raise ValueError( "A choice is trying to register j/k as a " "shortcut key when they are in use as arrow keys " "disable one or the other." ) if choices is None or len(choices) == 0: raise ValueError("A list of choices needs to be provided.") if use_shortcuts and len(choices) > len(InquirerControl.SHORTCUT_KEYS): raise ValueError( "A list with shortcuts supports a maximum of {} " "choices as this is the maximum number " "of keyboard shortcuts that are available. You" "provided {} choices!" "".format(len(InquirerControl.SHORTCUT_KEYS), len(choices)) ) merged_style = merge_styles_default([style]) ic = InquirerControl( choices, default, pointer=pointer, use_indicator=use_indicator, use_shortcuts=use_shortcuts, show_selected=show_selected, use_arrow_keys=use_arrow_keys, initial_choice=default, ) def get_prompt_tokens(): # noinspection PyListCreation tokens = [("class:qmark", qmark), ("class:question", " {} ".format(message))] if ic.is_answered: if isinstance(ic.get_pointed_at().title, list): tokens.append( ( "class:answer", "".join([token[1] for token in ic.get_pointed_at().title]), ) ) else: tokens.append(("class:answer", ic.get_pointed_at().title)) else: if instruction: tokens.append(("class:instruction", instruction)) else: if use_shortcuts and use_arrow_keys: instruction_msg = "(Use shortcuts or arrow keys)" elif use_shortcuts and not use_arrow_keys: instruction_msg = "(Use shortcuts)" else: instruction_msg = "(Use arrow keys)" tokens.append(("class:instruction", instruction_msg)) return tokens layout = common.create_inquirer_layout(ic, get_prompt_tokens, **kwargs) bindings = KeyBindings() @bindings.add(Keys.ControlQ, eager=True) @bindings.add(Keys.ControlC, eager=True) def _(event): event.app.exit(exception=KeyboardInterrupt, style="class:aborting") if use_shortcuts: # add key bindings for choices for i, c in enumerate(ic.choices): if c.shortcut_key is None and not c.disabled and not use_arrow_keys: raise RuntimeError( "{} does not have a shortcut and arrow keys " "for movement are disabled. " "This choice is not reachable.".format(c.title) ) if isinstance(c, Separator) or c.shortcut_key is None: continue # noinspection PyShadowingNames def _reg_binding(i, keys): # trick out late evaluation with a "function factory": # https://stackoverflow.com/a/3431699 @bindings.add(keys, eager=True) def select_choice(event): ic.pointed_at = i _reg_binding(i, c.shortcut_key) def move_cursor_down(event): ic.select_next() while not ic.is_selection_valid(): ic.select_next() def move_cursor_up(event): ic.select_previous() while not ic.is_selection_valid(): ic.select_previous() if use_arrow_keys: bindings.add(Keys.Down, eager=True)(move_cursor_down) bindings.add(Keys.Up, eager=True)(move_cursor_up) if use_jk_keys: bindings.add("j", eager=True)(move_cursor_down) bindings.add("k", eager=True)(move_cursor_up) if use_emacs_keys: bindings.add(Keys.ControlN, eager=True)(move_cursor_down) bindings.add(Keys.ControlP, eager=True)(move_cursor_up) @bindings.add(Keys.ControlM, eager=True) def set_answer(event): ic.is_answered = True event.app.exit(result=ic.get_pointed_at().value) @bindings.add(Keys.Any) def other(event): """Disallow inserting other text.""" return Question( Application( layout=layout, key_bindings=bindings, style=merged_style, **utils.used_kwargs(kwargs, Application.__init__), ) ) questionary-2.0.1/questionary/prompts/text.py000066400000000000000000000065131447660764300215500ustar00rootroot00000000000000from typing import Any from typing import List from typing import Optional from typing import Tuple from prompt_toolkit.document import Document from prompt_toolkit.lexers import Lexer from prompt_toolkit.lexers import SimpleLexer from prompt_toolkit.shortcuts.prompt import PromptSession from prompt_toolkit.styles import Style from questionary.constants import DEFAULT_QUESTION_PREFIX from questionary.constants import INSTRUCTION_MULTILINE from questionary.prompts.common import build_validator from questionary.question import Question from questionary.styles import merge_styles_default def text( message: str, default: str = "", validate: Any = None, qmark: str = DEFAULT_QUESTION_PREFIX, style: Optional[Style] = None, multiline: bool = False, instruction: Optional[str] = None, lexer: Optional[Lexer] = None, **kwargs: Any, ) -> Question: """Prompt the user to enter a free text message. This question type can be used to prompt the user for some text input. Example: >>> import questionary >>> questionary.text("What's your first name?").ask() ? What's your first name? Tom 'Tom' .. image:: ../images/text.gif This is just a really basic example, the prompt can be customised using the parameters. Args: message: Question text. default: Default value will be returned if the user just hits enter. validate: Require the entered value to pass a validation. The value can not be submitted until the validator accepts it (e.g. to check minimum password length). This can either be a function accepting the input and returning a boolean, or an class reference to a subclass of the prompt toolkit Validator class. qmark: Question prefix displayed in front of the question. By default this is a ``?``. style: A custom color and style for the question parts. You can configure colors as well as font types for different elements. multiline: If ``True``, multiline input will be enabled. instruction: Write instructions for the user if needed. If ``None`` and ``multiline=True``, some instructions will appear. lexer: Supply a valid lexer to style the answer. Leave empty to use a simple one by default. kwargs: Additional arguments, they will be passed to prompt toolkit. Returns: :class:`Question`: Question instance, ready to be prompted (using ``.ask()``). """ merged_style = merge_styles_default([style]) lexer = lexer or SimpleLexer("class:answer") validator = build_validator(validate) if instruction is None and multiline: instruction = INSTRUCTION_MULTILINE def get_prompt_tokens() -> List[Tuple[str, str]]: result = [("class:qmark", qmark), ("class:question", " {} ".format(message))] if instruction: result.append(("class:instruction", " {} ".format(instruction))) return result p: PromptSession = PromptSession( get_prompt_tokens, style=merged_style, validator=validator, lexer=lexer, multiline=multiline, **kwargs, ) p.default_buffer.reset(Document(default)) return Question(p.app) questionary-2.0.1/questionary/py.typed000066400000000000000000000000001447660764300201660ustar00rootroot00000000000000questionary-2.0.1/questionary/question.py000066400000000000000000000076461447660764300207370ustar00rootroot00000000000000import sys from typing import Any import prompt_toolkit.patch_stdout from prompt_toolkit import Application from questionary import utils from questionary.constants import DEFAULT_KBI_MESSAGE class Question: """A question to be prompted. This is an internal class. Questions should be created using the predefined questions (e.g. text or password).""" application: "Application[Any]" should_skip_question: bool default: Any def __init__(self, application: "Application[Any]") -> None: self.application = application self.should_skip_question = False self.default = None async def ask_async( self, patch_stdout: bool = False, kbi_msg: str = DEFAULT_KBI_MESSAGE ) -> Any: """Ask the question using asyncio and return user response. Args: patch_stdout: Ensure that the prompt renders correctly if other threads are printing to stdout. kbi_msg: The message to be printed on a keyboard interrupt. Returns: `Any`: The answer from the question. """ try: sys.stdout.flush() return await self.unsafe_ask_async(patch_stdout) except KeyboardInterrupt: print("\n{}\n".format(kbi_msg)) return None def ask( self, patch_stdout: bool = False, kbi_msg: str = DEFAULT_KBI_MESSAGE ) -> Any: """Ask the question synchronously and return user response. Args: patch_stdout: Ensure that the prompt renders correctly if other threads are printing to stdout. kbi_msg: The message to be printed on a keyboard interrupt. Returns: `Any`: The answer from the question. """ try: return self.unsafe_ask(patch_stdout) except KeyboardInterrupt: print("\n{}\n".format(kbi_msg)) return None def unsafe_ask(self, patch_stdout: bool = False) -> Any: """Ask the question synchronously and return user response. Does not catch keyboard interrupts. Args: patch_stdout: Ensure that the prompt renders correctly if other threads are printing to stdout. Returns: `Any`: The answer from the question. """ if self.should_skip_question: return self.default if patch_stdout: with prompt_toolkit.patch_stdout.patch_stdout(): return self.application.run() else: return self.application.run() def skip_if(self, condition: bool, default: Any = None) -> "Question": """Skip the question if flag is set and return the default instead. Args: condition: A conditional boolean value. default: The default value to return. Returns: :class:`Question`: `self`. """ self.should_skip_question = condition self.default = default return self async def unsafe_ask_async(self, patch_stdout: bool = False) -> Any: """Ask the question using asyncio and return user response. Does not catch keyboard interrupts. Args: patch_stdout: Ensure that the prompt renders correctly if other threads are printing to stdout. Returns: `Any`: The answer from the question. """ if self.should_skip_question: return self.default if not utils.ACTIVATED_ASYNC_MODE: await utils.activate_prompt_toolkit_async_mode() if patch_stdout: with prompt_toolkit.patch_stdout.patch_stdout(): r = self.application.run_async() else: r = self.application.run_async() if utils.is_prompt_toolkit_3(): return await r else: return await r.to_asyncio_future() # type: ignore[attr-defined] questionary-2.0.1/questionary/styles.py000066400000000000000000000011231447660764300203730ustar00rootroot00000000000000from typing import List from typing import Optional import prompt_toolkit.styles from questionary.constants import DEFAULT_STYLE def merge_styles_default(styles: List[Optional[prompt_toolkit.styles.Style]]): """Merge a list of styles with the Questionary default style.""" filtered_styles: list[prompt_toolkit.styles.BaseStyle] = [DEFAULT_STYLE] # prompt_toolkit's merge_styles works with ``None`` elements, but it's # type-hints says it doesn't. filtered_styles.extend([s for s in styles if s is not None]) return prompt_toolkit.styles.merge_styles(filtered_styles) questionary-2.0.1/questionary/utils.py000066400000000000000000000043741447660764300202230ustar00rootroot00000000000000import inspect from typing import Any from typing import Callable from typing import Dict from typing import List from typing import Set ACTIVATED_ASYNC_MODE = False def is_prompt_toolkit_3() -> bool: from prompt_toolkit import __version__ as ptk_version return ptk_version.startswith("3.") def default_values_of(func: Callable[..., Any]) -> List[str]: """Return all parameter names of ``func`` with a default value.""" signature = inspect.signature(func) return [ k for k, v in signature.parameters.items() if v.default is not inspect.Parameter.empty or v.kind != inspect.Parameter.POSITIONAL_OR_KEYWORD ] def arguments_of(func: Callable[..., Any]) -> List[str]: """Return the parameter names of the function ``func``.""" return list(inspect.signature(func).parameters.keys()) def used_kwargs(kwargs: Dict[str, Any], func: Callable[..., Any]) -> Dict[str, Any]: """Returns only the kwargs which can be used by a function. Args: kwargs: All available kwargs. func: The function which should be called. Returns: Subset of kwargs which are accepted by ``func``. """ possible_arguments = arguments_of(func) return {k: v for k, v in kwargs.items() if k in possible_arguments} def required_arguments(func: Callable[..., Any]) -> List[str]: """Return all arguments of a function that do not have a default value.""" defaults = default_values_of(func) args = arguments_of(func) if defaults: args = args[: -len(defaults)] return args # all args without default values def missing_arguments(func: Callable[..., Any], argdict: Dict[str, Any]) -> Set[str]: """Return all arguments that are missing to call func.""" return set(required_arguments(func)) - set(argdict.keys()) async def activate_prompt_toolkit_async_mode() -> None: """Configure prompt toolkit to use the asyncio event loop. Needs to be async, so we use the right event loop in py 3.5""" global ACTIVATED_ASYNC_MODE if not is_prompt_toolkit_3(): # Tell prompt_toolkit to use asyncio for the event loop. import prompt_toolkit as pt pt.eventloop.use_asyncio_event_loop() # type: ignore[attr-defined] ACTIVATED_ASYNC_MODE = True questionary-2.0.1/questionary/version.py000066400000000000000000000000261447660764300205360ustar00rootroot00000000000000__version__ = "2.0.1" questionary-2.0.1/scripts/000077500000000000000000000000001447660764300156055ustar00rootroot00000000000000questionary-2.0.1/scripts/validate_version.py000066400000000000000000000036731447660764300215260ustar00rootroot00000000000000import os import sys from pathlib import Path from typing import Optional from typing import Text import toml version_file_path = Path("questionary/version.py") pyproject_file_path = Path("pyproject.toml") def get_pyproject_version(): """Return the project version specified in the poetry build configuration.""" data = toml.load(pyproject_file_path) return data["tool"]["poetry"]["version"] def get_current_version() -> Text: """Return the current library version as specified in the code.""" if not version_file_path.is_file(): raise FileNotFoundError( f"Failed to find version file at {version_file_path().absolute()}" ) # context in which we evaluate the version py - # to be able to access the defined version, it already needs to live in the # context passed to exec _globals = {"__version__": ""} with open(version_file_path) as f: exec(f.read(), _globals) return _globals["__version__"] def get_tagged_version() -> Optional[Text]: """Return the version specified in a tagged git commit.""" return os.environ.get("TRAVIS_TAG") if __name__ == "__main__": if get_pyproject_version() != get_current_version(): print( f"Version in {pyproject_file_path} does not correspond " f"to the version in {version_file_path}! The version needs to be " f"set to the same value in both places." ) sys.exit(1) elif get_tagged_version() and get_tagged_version() != get_current_version(): print( f"Tagged version does not correspond to the version " f"in {version_file_path}!" ) sys.exit(1) elif get_tagged_version() and get_tagged_version() != get_pyproject_version(): print( f"Tagged version does not correspond to the version " f"in {pyproject_file_path}!" ) sys.exit(1) else: print("Versions look good!") questionary-2.0.1/setup.cfg000066400000000000000000000002631447660764300157400ustar00rootroot00000000000000[metadata] description-file = README.md license_file = LICENSE [mypy] ignore_missing_imports = True show_error_codes = True warn_redundant_casts = True warn_unused_ignores = True questionary-2.0.1/tests/000077500000000000000000000000001447660764300152605ustar00rootroot00000000000000questionary-2.0.1/tests/__init__.py000066400000000000000000000000001447660764300173570ustar00rootroot00000000000000questionary-2.0.1/tests/conftest.py000066400000000000000000000000001447660764300174450ustar00rootroot00000000000000questionary-2.0.1/tests/prompts/000077500000000000000000000000001447660764300167645ustar00rootroot00000000000000questionary-2.0.1/tests/prompts/__init__.py000066400000000000000000000000001447660764300210630ustar00rootroot00000000000000questionary-2.0.1/tests/prompts/test_autocomplete.py000066400000000000000000000052541447660764300231040ustar00rootroot00000000000000# -*- coding: utf-8 -*- import pytest from tests.utils import KeyInputs from tests.utils import feed_cli_with_input def test_autocomplete(): message = "Pick your poison " text = "python3\r" kwargs = { "choices": ["python3", "python2"], } result, cli = feed_cli_with_input("autocomplete", message, text, **kwargs) assert result == "python3" def test_no_choices_autocomplete(): message = "Pick your poison " text = "python2\r" with pytest.raises(ValueError): feed_cli_with_input("autocomplete", message, text, choices=[]) def test_validate_autocomplete(): message = "Pick your poison" text = "python123\r" kwargs = { "choices": ["python3", "python2", "python123"], } result, cli = feed_cli_with_input( "autocomplete", message, text, validate=lambda x: "c++" not in x or "?", **kwargs, ) assert result == "python123" def test_use_tab_autocomplete(): message = "Pick your poison" texts = ["p", KeyInputs.TAB + KeyInputs.TAB + KeyInputs.ENTER + "\r"] kwargs = { "choices": ["python3", "python2", "python123"], } result, cli = feed_cli_with_input("autocomplete", message, texts, **kwargs) assert result == "python2" def test_use_key_tab_autocomplete(): message = "Pick your poison" texts = [ "p", KeyInputs.TAB + KeyInputs.TAB + KeyInputs.TAB + KeyInputs.ENTER + "\r", ] kwargs = { "choices": ["python3", "python2", "python123", "c++"], } result, cli = feed_cli_with_input("autocomplete", message, texts, **kwargs) assert result == "python123" def test_ignore_case_autocomplete(): message = ("Pick your poison",) kwargs = { "choices": ["python3", "python2"], } texts = ["P", KeyInputs.TAB + KeyInputs.TAB + KeyInputs.ENTER + "\r"] result, cli = feed_cli_with_input( "autocomplete", message, texts, **kwargs, ignore_case=False ) assert result == "P" texts = ["p", KeyInputs.TAB + KeyInputs.TAB + KeyInputs.ENTER + "\r"] result, cli = feed_cli_with_input( "autocomplete", message, texts, **kwargs, ignore_case=True ) assert result == "python2" def test_match_middle_autocomplete(): message = ("Pick your poison",) kwargs = { "choices": ["python3", "python2"], } texts = ["t", KeyInputs.TAB + KeyInputs.TAB + KeyInputs.ENTER + "\r"] result, cli = feed_cli_with_input( "autocomplete", message, texts, **kwargs, match_middle=False ) assert result == "t" result, cli = feed_cli_with_input( "autocomplete", message, texts, **kwargs, match_middle=True ) assert result == "python2" questionary-2.0.1/tests/prompts/test_checkbox.py000066400000000000000000000235451447660764300221740ustar00rootroot00000000000000# -*- coding: utf-8 -*- import pytest from questionary import Choice from questionary import Separator from tests.utils import KeyInputs from tests.utils import feed_cli_with_input def test_submit_empty(): message = "Foo message" kwargs = {"choices": ["foo", "bar", "bazz"]} text = KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("checkbox", message, text, **kwargs) assert result == [] def test_select_first_choice(): message = "Foo message" kwargs = {"choices": ["foo", "bar", "bazz"]} text = KeyInputs.SPACE + KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("checkbox", message, text, **kwargs) assert result == ["foo"] def test_select_with_instruction(): message = "Foo message" kwargs = {"choices": ["foo", "bar", "bazz"], "instruction": "sample instruction"} text = KeyInputs.SPACE + KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("checkbox", message, text, **kwargs) assert result == ["foo"] def test_select_first_choice_with_token_title(): message = "Foo message" kwargs = { "choices": [ Choice(title=[("class:text", "foo")]), Choice(title=[("class:text", "bar")]), Choice(title=[("class:text", "bazz")]), ] } text = KeyInputs.SPACE + KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("checkbox", message, text, **kwargs) assert result == ["foo"] def test_select_disabled_choices_if_they_are_default(): message = "Foo message" kwargs = { "choices": [ Choice("foo", checked=True), Choice("bar", disabled="unavailable", checked=True), Choice("baz", disabled="unavailable"), ] } text = KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("checkbox", message, text, **kwargs) assert result == ["foo", "bar"] def test_select_and_deselct(): message = "Foo message" kwargs = {"choices": ["foo", "bar", "bazz"]} text = ( KeyInputs.SPACE + KeyInputs.DOWN + KeyInputs.SPACE + KeyInputs.SPACE + KeyInputs.ENTER + "\r" ) result, cli = feed_cli_with_input("checkbox", message, text, **kwargs) assert result == ["foo"] def test_select_first_and_third_choice(): message = "Foo message" kwargs = {"choices": ["foo", "bar", "bazz"]} # DOWN and `j` should do the same text = ( KeyInputs.SPACE + KeyInputs.DOWN + "j" + KeyInputs.SPACE + KeyInputs.ENTER + "\r" ) result, cli = feed_cli_with_input("checkbox", message, text, **kwargs) assert result == ["foo", "bazz"] def test_select_first_and_third_choice_using_emacs_keys(): message = "Foo message" kwargs = {"choices": ["foo", "bar", "bazz"]} text = ( KeyInputs.SPACE + KeyInputs.CONTROLN + KeyInputs.CONTROLN + KeyInputs.SPACE + KeyInputs.ENTER + "\r" ) result, cli = feed_cli_with_input("checkbox", message, text, **kwargs) assert result == ["foo", "bazz"] def test_cycle_to_first_choice(): message = "Foo message" kwargs = {"choices": ["foo", "bar", "bazz"]} text = ( KeyInputs.DOWN + KeyInputs.DOWN + KeyInputs.DOWN + KeyInputs.SPACE + KeyInputs.ENTER + "\r" ) result, cli = feed_cli_with_input("checkbox", message, text, **kwargs) assert result == ["foo"] def test_cycle_backwards(): message = "Foo message" kwargs = {"choices": ["foo", "bar", "bazz"]} text = KeyInputs.UP + KeyInputs.SPACE + KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("checkbox", message, text, **kwargs) assert result == ["bazz"] def test_cycle_backwards_using_emacs_keys(): message = "Foo message" kwargs = {"choices": ["foo", "bar", "bazz"]} text = KeyInputs.CONTROLP + KeyInputs.SPACE + KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("checkbox", message, text, **kwargs) assert result == ["bazz"] def test_separator_down(): message = "Foo message" kwargs = {"choices": ["foo", Separator(), "bazz"]} text = KeyInputs.DOWN + KeyInputs.SPACE + KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("checkbox", message, text, **kwargs) assert result == ["bazz"] def test_separator_up(): message = "Foo message" kwargs = {"choices": ["foo", Separator(), "bazz", Separator("--END--")]} # UP and `k` should do the same text = KeyInputs.UP + "k" + KeyInputs.SPACE + KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("checkbox", message, text, **kwargs) assert result == ["foo"] def test_select_all(): message = "Foo message" kwargs = { "choices": [ {"name": "foo", "checked": True}, Separator(), {"name": "bar", "disabled": "nope"}, "bazz", Separator("--END--"), ] } text = KeyInputs.UP + "a" + KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("checkbox", message, text, **kwargs) assert result == ["foo", "bazz"] def test_select_all_deselect(): message = "Foo message" kwargs = { "choices": [ {"name": "foo", "checked": True}, Separator(), {"name": "bar", "disabled": "nope"}, "bazz", Separator("--END--"), ] } text = KeyInputs.UP + "a" + "a" + KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("checkbox", message, text, **kwargs) assert result == [] def test_select_invert(): message = "Foo message" kwargs = { "choices": [ {"name": "foo", "checked": True}, Separator(), {"name": "bar", "disabled": "nope"}, "bazz", Separator("--END--"), ] } text = KeyInputs.UP + "i" + KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("checkbox", message, text, **kwargs) assert result == ["bazz"] def test_list_random_input(): message = "Foo message" kwargs = {"choices": ["foo", "bazz"]} text = "sdf" + KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("checkbox", message, text, **kwargs) assert result == [] def test_list_ctr_c(): message = "Foo message" kwargs = {"choices": ["foo", "bazz"]} text = KeyInputs.CONTROLC with pytest.raises(KeyboardInterrupt): feed_cli_with_input("checkbox", message, text, **kwargs) def test_checkbox_initial_choice(): message = "Foo message" choice = Choice("bazz") kwargs = {"choices": ["foo", choice], "initial_choice": choice} text = KeyInputs.SPACE + KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("checkbox", message, text, **kwargs) assert result == ["bazz"] def test_select_initial_choice_string(): message = "Foo message" kwargs = {"choices": ["foo", "bazz"], "initial_choice": "bazz"} text = KeyInputs.SPACE + KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("checkbox", message, text, **kwargs) assert result == ["bazz"] def test_select_initial_choice_duplicate(): message = "Foo message" choice = Choice("foo") kwargs = {"choices": ["foo", choice, "bazz"], "initial_choice": choice} text = KeyInputs.DOWN + KeyInputs.SPACE + KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("checkbox", message, text, **kwargs) assert result == ["bazz"] def test_checkbox_initial_choice_not_selectable(): message = "Foo message" separator = Separator() kwargs = {"choices": ["foo", "bazz", separator], "initial_choice": separator} text = KeyInputs.ENTER + "\r" with pytest.raises(ValueError): feed_cli_with_input("checkbox", message, text, **kwargs) def test_checkbox_initial_choice_non_existant(): message = "Foo message" kwargs = {"choices": ["foo", "bazz"], "initial_choice": "bar"} text = KeyInputs.ENTER + "\r" with pytest.raises(ValueError): feed_cli_with_input("checkbox", message, text, **kwargs) def test_validate_default_message(): message = "Foo message" kwargs = {"choices": ["foo", "bar", "bazz"], "validate": lambda a: len(a) != 0} text = KeyInputs.ENTER + "i" + KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("checkbox", message, text, **kwargs) assert result == ["foo", "bar", "bazz"] def test_validate_with_message(): message = "Foo message" kwargs = { "choices": ["foo", "bar", "bazz"], "validate": lambda a: True if len(a) > 0 else "Error Message", } text = KeyInputs.ENTER + "i" + KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("checkbox", message, text, **kwargs) assert result == ["foo", "bar", "bazz"] def test_validate_not_callable(): message = "Foo message" kwargs = {"choices": ["foo", "bar", "bazz"], "validate": "invalid"} text = KeyInputs.ENTER + "i" + KeyInputs.ENTER + "\r" with pytest.raises(ValueError): feed_cli_with_input("checkbox", message, text, **kwargs) def test_proper_type_returned(): message = "Foo message" kwargs = { "choices": [ Choice("one", value=1), Choice("two", value="foo"), Choice("three", value=[3, "bar"]), ] } text = ( KeyInputs.SPACE + KeyInputs.DOWN + KeyInputs.SPACE + KeyInputs.DOWN + KeyInputs.SPACE + KeyInputs.DOWN + KeyInputs.ENTER + "\r" ) result, cli = feed_cli_with_input("checkbox", message, text, **kwargs) assert result == [1, "foo", [3, "bar"]] def test_fail_on_no_method_to_move_selection(): message = "Foo message" kwargs = { "choices": ["foo", Choice("bar", disabled="bad"), "bazz"], "use_shortcuts": False, "use_arrow_keys": False, "use_jk_keys": False, "use_emacs_keys": False, } text = KeyInputs.ENTER + "\r" with pytest.raises(ValueError): feed_cli_with_input("checkbox", message, text, **kwargs) questionary-2.0.1/tests/prompts/test_common.py000066400000000000000000000145041447660764300216710ustar00rootroot00000000000000from unittest.mock import Mock from unittest.mock import call import pytest from prompt_toolkit.document import Document from prompt_toolkit.output import DummyOutput from prompt_toolkit.styles import Attrs from prompt_toolkit.validation import ValidationError from prompt_toolkit.validation import Validator from questionary import Choice from questionary.prompts import common from questionary.prompts.common import InquirerControl from questionary.prompts.common import build_validator from questionary.prompts.common import print_formatted_text from tests.utils import execute_with_input_pipe from tests.utils import prompt_toolkit_version def test_to_many_choices_for_shortcut_assignment(): ic = InquirerControl([str(i) for i in range(1, 100)], use_shortcuts=True) # IC should fail gracefully when running out of shortcuts assert len(list(filter(lambda x: x.shortcut_key is not None, ic.choices))) == len( InquirerControl.SHORTCUT_KEYS ) def test_validator_bool_function(): def validate(t): return len(t) == 3 validator = build_validator(validate) assert validator.validate(Document("foo")) is None # should not raise def test_validator_bool_function_fails(): def validate(t): return len(t) == 3 validator = build_validator(validate) with pytest.raises(ValidationError) as e: validator.validate(Document("fooooo")) assert e.value.message == "Invalid input" def test_validator_instance(): def validate(t): return len(t) == 3 validator = Validator.from_callable(validate) validator = build_validator(validator) assert validator.validate(Document("foo")) is None # should not raise def test_validator_instance_fails(): def validate(t): return len(t) == 3 validator = Validator.from_callable(validate, error_message="invalid input") with pytest.raises(ValidationError) as e: validator.validate(Document("fooooo")) assert e.value.message == "invalid input" def test_blank_line_fix(): def get_prompt_tokens(): return [("class:question", "What is your favourite letter?")] ic = InquirerControl(["a", "b", "c"]) def run(inp): inp.send_text("") layout = common.create_inquirer_layout( ic, get_prompt_tokens, input=inp, output=DummyOutput() ) # usually this would be 2000000000000000000000000000000 # but `common._fix_unecessary_blank_lines` makes sure # the main window is not as greedy (avoiding blank lines) assert ( layout.container.preferred_height(100, 200).max == 1000000000000000000000000000001 ) execute_with_input_pipe(run) def test_prompt_highlight_coexist(): ic = InquirerControl(["a", "b", "c"]) expected_tokens = [ ("class:pointer", " » "), ("[SetCursorPosition]", ""), ("class:text", "○ "), ("class:highlighted", "a"), ("", "\n"), ("class:text", " "), ("class:text", "○ "), ("class:text", "b"), ("", "\n"), ("class:text", " "), ("class:text", "○ "), ("class:text", "c"), ] assert ic.pointed_at == 0 assert ic._get_choice_tokens() == expected_tokens ic.select_previous() expected_tokens = [ ("class:text", " "), ("class:text", "○ "), ("class:text", "a"), ("", "\n"), ("class:text", " "), ("class:text", "○ "), ("class:text", "b"), ("", "\n"), ("class:pointer", " » "), ("[SetCursorPosition]", ""), ("class:text", "○ "), ("class:highlighted", "c"), ] assert ic.pointed_at == 2 assert ic._get_choice_tokens() == expected_tokens def test_prompt_show_answer_with_shortcuts(): ic = InquirerControl( ["a", Choice("b", shortcut_key=False), "c"], show_selected=True, use_shortcuts=True, ) expected_tokens = [ ("class:pointer", " » "), ("[SetCursorPosition]", ""), ("class:text", "○ "), ("class:highlighted", "1) a"), ("", "\n"), ("class:text", " "), ("class:text", "○ "), ("class:text", "-) b"), ("", "\n"), ("class:text", " "), ("class:text", "○ "), ("class:text", "2) c"), ("", "\n"), ("class:text", " Answer: 1) a"), ] assert ic.pointed_at == 0 assert ic._get_choice_tokens() == expected_tokens ic.select_next() expected_tokens = [ ("class:text", " "), ("class:text", "○ "), ("class:text", "1) a"), ("", "\n"), ("class:pointer", " » "), ("[SetCursorPosition]", ""), ("class:text", "○ "), ("class:highlighted", "-) b"), ("", "\n"), ("class:text", " "), ("class:text", "○ "), ("class:text", "2) c"), ("", "\n"), ("class:text", " Answer: -) b"), ] assert ic.pointed_at == 1 assert ic._get_choice_tokens() == expected_tokens def test_print(monkeypatch): mock = Mock(return_value=None) monkeypatch.setattr(DummyOutput, "write", mock) print_formatted_text("Hello World", output=DummyOutput()) mock.assert_has_calls([call("Hello World"), call("\r\n")]) def test_print_with_style(monkeypatch): mock = Mock(return_value=None) monkeypatch.setattr(DummyOutput, "write", mock.write) monkeypatch.setattr(DummyOutput, "set_attributes", mock.set_attributes) print_formatted_text( "Hello World", style="bold italic fg:darkred", output=DummyOutput() ) assert len(mock.method_calls) == 4 assert mock.method_calls[0][0] == "set_attributes" if prompt_toolkit_version < (3, 0, 20): assert mock.method_calls[0][1][0] == Attrs( color="8b0000", bgcolor="", bold=True, underline=False, italic=True, blink=False, reverse=False, hidden=False, ) else: assert mock.method_calls[0][1][0] == Attrs( color="8b0000", bgcolor="", bold=True, underline=False, italic=True, blink=False, reverse=False, hidden=False, strike=False, ) assert mock.method_calls[1][0] == "write" assert mock.method_calls[1][1][0] == "Hello World" questionary-2.0.1/tests/prompts/test_confirm.py000066400000000000000000000046641447660764300220440ustar00rootroot00000000000000# -*- coding: utf-8 -*- import pytest from tests.utils import KeyInputs from tests.utils import feed_cli_with_input def test_confirm_enter_default_yes(): message = "Foo message" text = KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("confirm", message, text) assert result is True def test_confirm_enter_default_no(): message = "Foo message" text = KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("confirm", message, text, default=False) assert result is False def test_confirm_yes(): message = "Foo message" text = "y" + "\r" result, cli = feed_cli_with_input("confirm", message, text) assert result is True def test_confirm_no(): message = "Foo message" text = "n" + "\r" result, cli = feed_cli_with_input("confirm", message, text) assert result is False def test_confirm_big_yes(): message = "Foo message" text = "Y" + "\r" result, cli = feed_cli_with_input("confirm", message, text) assert result is True def test_confirm_big_no(): message = "Foo message" text = "N" + "\r" result, cli = feed_cli_with_input("confirm", message, text) assert result is False def test_confirm_random_input(): message = "Foo message" text = "my stuff" + KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("confirm", message, text) assert result is True def test_confirm_ctr_c(): message = "Foo message" text = KeyInputs.CONTROLC with pytest.raises(KeyboardInterrupt): feed_cli_with_input("confirm", message, text) def test_confirm_not_autoenter_yes(): message = "Foo message" text = "n" + "y" + KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("confirm", message, text, auto_enter=False) assert result is True def test_confirm_not_autoenter_no(): message = "Foo message" text = "n" + "y" + KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("confirm", message, text, auto_enter=False) assert result is True def test_confirm_not_autoenter_backspace(): message = "Foo message" text = "n" + KeyInputs.BACK + KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("confirm", message, text, auto_enter=False) assert result is True def test_confirm_instruction(): message = "Foo message" text = "Y" + "\r" result, cli = feed_cli_with_input( "confirm", message, text, instruction="Foo instruction" ) assert result is True questionary-2.0.1/tests/prompts/test_password.py000066400000000000000000000004071447660764300222400ustar00rootroot00000000000000# -*- coding: utf-8 -*- from tests.utils import feed_cli_with_input def test_password_entry(): message = "What is your password" text = "my password\r" result, cli = feed_cli_with_input("password", message, text) assert result == "my password" questionary-2.0.1/tests/prompts/test_path.py000066400000000000000000000104401447660764300213300ustar00rootroot00000000000000# -*- coding: utf-8 -*- import prompt_toolkit import pytest from prompt_toolkit.completion import Completer from prompt_toolkit.completion import Completion from tests.utils import KeyInputs from tests.utils import feed_cli_with_input @pytest.fixture def path_completion_tree(tmp_path): needed_directories = [ tmp_path / "foo", tmp_path / "foo" / "buz", # alphabetically after baz.any tmp_path / "bar", tmp_path / "baz", ] needed_files = [tmp_path / "foo" / "baz.any", tmp_path / "foo" / "foobar.any"] for d in needed_directories: d.mkdir() for f in needed_files: f.open("a").close() return tmp_path def test_path(): message = "Pick your path " text = "myfile.py" + KeyInputs.ENTER result, cli = feed_cli_with_input("path", message, text) assert result == "myfile.py" @pytest.mark.skipif( prompt_toolkit.__version__.startswith("2"), reason="requires prompt toolkit >= 3.0" ) def test_complete_path(path_completion_tree): test_input = str(path_completion_tree / "ba") message = "Pick your path" texts = [ test_input, KeyInputs.TAB + KeyInputs.TAB + KeyInputs.ENTER, KeyInputs.ENTER, ] result, cli = feed_cli_with_input("path", message, texts, 0.1) assert result == str(path_completion_tree / "baz") @pytest.mark.skipif( prompt_toolkit.__version__.startswith("2"), reason="requires prompt toolkit >= 3.0" ) def test_complete_requires_explicit_enter(path_completion_tree): # checks that an autocomplete needs to be confirmed with an enter and that the # enter doesn't directly submit the result test_input = str(path_completion_tree / "ba") message = "Pick your path" texts = [ test_input, KeyInputs.TAB + KeyInputs.TAB + KeyInputs.ENTER, "foo" + KeyInputs.ENTER, ] result, cli = feed_cli_with_input("path", message, texts, 0.1) assert result == str(path_completion_tree / "baz" / "foo") @pytest.mark.skipif( prompt_toolkit.__version__.startswith("2"), reason="requires prompt toolkit >= 3.0" ) def test_complete_path_directories_only(path_completion_tree): test_input = str(path_completion_tree / "foo" / "b") message = "Pick your path" texts = [test_input, KeyInputs.TAB + KeyInputs.ENTER, KeyInputs.ENTER] result, cli = feed_cli_with_input( "path", message, texts, 0.1, only_directories=True ) assert result == str(path_completion_tree / "foo" / "buz") @pytest.mark.skipif( prompt_toolkit.__version__.startswith("2"), reason="requires prompt toolkit >= 3.0" ) def test_get_paths(path_completion_tree): """Starting directories for path completion can be set.""" test_input = "ba" message = "Pick your path" texts = [ test_input, KeyInputs.TAB + KeyInputs.ENTER, KeyInputs.ENTER, ] result, cli = feed_cli_with_input( "path", message, texts, 0.1, get_paths=lambda: [str(path_completion_tree / "foo")], ) assert result == "baz.any" @pytest.mark.skipif( prompt_toolkit.__version__.startswith("2"), reason="requires prompt toolkit >= 3.0" ) def test_get_paths_validation(path_completion_tree): """`get_paths` must contain only existing directories.""" test_input = str(path_completion_tree / "ba") message = "Pick your path" texts = [ test_input, KeyInputs.TAB + KeyInputs.TAB + KeyInputs.ENTER, KeyInputs.ENTER, ] with pytest.raises(ValueError) as excinfo: feed_cli_with_input( "path", message, texts, 0.1, get_paths=lambda: [str(path_completion_tree / "not_existing")], ) assert "'get_paths' must return only existing directories" in str(excinfo) @pytest.mark.skipif( prompt_toolkit.__version__.startswith("2"), reason="requires prompt toolkit >= 3.0" ) def test_complete_custom_completer(): test_path = "foobar" class CustomCompleter(Completer): def get_completions(self, _, __): yield Completion(test_path) message = "Pick your path" texts = ["baz", KeyInputs.TAB + KeyInputs.ENTER, KeyInputs.ENTER] result, cli = feed_cli_with_input( "path", message, texts, 0.1, completer=CustomCompleter() ) assert result == "baz" + test_path questionary-2.0.1/tests/prompts/test_press_any_key_to_continue.py000066400000000000000000000004141447660764300256550ustar00rootroot00000000000000# -*- coding: utf-8 -*- from tests.utils import feed_cli_with_input def test_press_any_key_to_continue_default_message(): message = None text = "c" result, cli = feed_cli_with_input("press_any_key_to_continue", message, text) assert result is None questionary-2.0.1/tests/prompts/test_rawselect.py000066400000000000000000000057551447660764300224020ustar00rootroot00000000000000# -*- coding: utf-8 -*- import uuid import pytest from questionary import Separator from tests.utils import KeyInputs from tests.utils import feed_cli_with_input def test_legacy_name(): message = "Foo message" kwargs = {"choices": ["foo", "bar", "bazz"]} text = "1" + KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("rawlist", message, text, **kwargs) assert result == "foo" def test_select_first_choice(): message = "Foo message" kwargs = {"choices": ["foo", "bar", "bazz"]} text = "1" + KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("rawselect", message, text, **kwargs) assert result == "foo" def test_select_second_choice(): message = "Foo message" kwargs = {"choices": ["foo", "bar", "bazz"]} text = "2" + KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("rawselect", message, text, **kwargs) assert result == "bar" def test_select_third_choice(): message = "Foo message" kwargs = {"choices": ["foo", "bar", "bazz"]} text = "2" + "3" + KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("rawselect", message, text, **kwargs) assert result == "bazz" def test_separator_shortcuts(): message = "Foo message" kwargs = {"choices": ["foo", Separator(), "bazz"]} text = "2" + KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("rawselect", message, text, **kwargs) assert result == "bazz" def test_duplicated_shortcuts(): message = "Foo message" kwargs = { "choices": [ {"name": "foo", "key": 1}, Separator(), {"name": "bar", "key": 1}, "bazz", Separator("--END--"), ] } text = "1" + KeyInputs.ENTER + "\r" with pytest.raises(ValueError): feed_cli_with_input("rawselect", message, text, **kwargs) def test_invalid_shortcuts(): message = "Foo message" kwargs = { "choices": [ {"name": "foo", "key": "asd"}, Separator(), {"name": "bar", "key": "1"}, "bazz", Separator("--END--"), ] } text = "1" + KeyInputs.ENTER + "\r" with pytest.raises(ValueError): feed_cli_with_input("rawselect", message, text, **kwargs) def test_to_many_choices(): message = "Foo message" kwargs = {"choices": [uuid.uuid4().hex for _ in range(0, 37)]} text = "1" + KeyInputs.ENTER + "\r" with pytest.raises(ValueError): feed_cli_with_input("rawselect", message, text, **kwargs) def test_select_random_input(): message = "Foo message" kwargs = {"choices": ["foo", "bazz"]} text = "2" + "some random input" + KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("rawselect", message, text, **kwargs) assert result == "bazz" def test_select_ctr_c(): message = "Foo message" kwargs = {"choices": ["foo", "bazz"]} text = KeyInputs.CONTROLC with pytest.raises(KeyboardInterrupt): feed_cli_with_input("rawselect", message, text, **kwargs) questionary-2.0.1/tests/prompts/test_select.py000066400000000000000000000256301447660764300216620ustar00rootroot00000000000000# -*- coding: utf-8 -*- import pytest from questionary import Choice from questionary import Separator from tests.utils import KeyInputs from tests.utils import feed_cli_with_input from tests.utils import patched_prompt def test_legacy_name(): message = "Foo message" kwargs = {"choices": ["foo", "bar", "bazz"]} text = KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("list", message, text, **kwargs) assert result == "foo" def test_select_first_choice(): message = "Foo message" kwargs = {"choices": ["foo", "bar", "bazz"]} text = KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("select", message, text, **kwargs) assert result == "foo" def test_select_first_choice_with_token_title(): message = "Foo message" kwargs = { "choices": [ Choice(title=[("class:text", "foo")]), Choice(title=[("class:text", "bar")]), Choice(title=[("class:text", "bazz")]), ] } text = KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("select", message, text, **kwargs) assert result == "foo" def test_select_second_choice(): message = "Foo message" kwargs = {"choices": ["foo", "bar", "bazz"]} text = KeyInputs.DOWN + KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("select", message, text, **kwargs) assert result == "bar" def test_select_third_choice(): message = "Foo message" kwargs = {"choices": ["foo", "bar", "bazz"]} text = KeyInputs.DOWN + KeyInputs.DOWN + KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("select", message, text, **kwargs) assert result == "bazz" def test_select_third_choice_using_shortcuts_and_arrows(): message = "Foo message" kwargs = { "choices": ["foo", "bar", "bazz"], "use_shortcuts": True, } text = KeyInputs.TWO + KeyInputs.DOWN + KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("select", message, text, **kwargs) assert result == "bazz" def test_select_second_choice_using_j_k(): message = "Foo message" kwargs = {"choices": ["foo", "bar", "bazz"]} text = "jjk" + KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("select", message, text, **kwargs) assert result == "bar" def test_select_second_choice_using_emacs_keys(): message = "Foo message" kwargs = {"choices": ["foo", "bar", "bazz"]} text = ( KeyInputs.CONTROLN + KeyInputs.CONTROLN + KeyInputs.CONTROLP + KeyInputs.ENTER + "\r" ) result, cli = feed_cli_with_input("select", message, text, **kwargs) assert result == "bar" def test_select_with_instruction(): message = "Foo message" kwargs = {"choices": ["foo", "bar", "bazz"], "instruction": "sample instruction"} text = KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("select", message, text, **kwargs) assert result == "foo" def test_cycle_to_first_choice(): message = "Foo message" kwargs = {"choices": ["foo", "bar", "bazz"]} text = KeyInputs.DOWN + KeyInputs.DOWN + KeyInputs.DOWN + KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("select", message, text, **kwargs) assert result == "foo" def test_cycle_backwards(): message = "Foo message" kwargs = {"choices": ["foo", "bar", "bazz"]} text = KeyInputs.UP + KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("select", message, text, **kwargs) assert result == "bazz" def test_cycle_backwards_using_k(): message = "Foo message" kwargs = {"choices": ["foo", "bar", "bazz"]} text = "k" + KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("select", message, text, **kwargs) assert result == "bazz" def test_separator_down(): message = "Foo message" kwargs = {"choices": ["foo", Separator(), "bazz"]} text = KeyInputs.DOWN + KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("select", message, text, **kwargs) assert result == "bazz" def test_separator_up(): message = "Foo message" kwargs = {"choices": ["foo", Separator(), "bazz", Separator("--END--")]} text = KeyInputs.UP + KeyInputs.UP + KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("select", message, text, **kwargs) assert result == "foo" def test_select_random_input(): message = "Foo message" kwargs = {"choices": ["foo", "bazz"]} text = "some random input" + KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("select", message, text, **kwargs) assert result == "foo" def test_select_ctr_c(): message = "Foo message" kwargs = {"choices": ["foo", "bazz"]} text = KeyInputs.CONTROLC with pytest.raises(KeyboardInterrupt): feed_cli_with_input("select", message, text, **kwargs) def test_select_empty_choices(): message = "Foo message" kwargs = {"choices": []} text = KeyInputs.ENTER + "\r" with pytest.raises(ValueError): feed_cli_with_input("select", message, text, **kwargs) def test_disallow_shortcut_key(): message = "Foo message" kwargs = { "choices": ["foo", "bar", Choice("bazz", shortcut_key=False)], "use_shortcuts": True, } text = KeyInputs.THREE + "\r" result, cli = feed_cli_with_input("select", message, text, **kwargs) assert result == "foo" def test_allow_shortcut_key_with_True(): message = "Foo message" kwargs = { "choices": ["foo", "bar", Choice("bazz", shortcut_key=True)], "use_shortcuts": True, } text = KeyInputs.THREE + "\r" result, cli = feed_cli_with_input("select", message, text, **kwargs) assert result == "bazz" def test_select_initial_choice_with_value(): message = "Foo message" choice = Choice(title="bazz", value="bar") kwargs = {"choices": ["foo", choice], "default": "bar"} text = KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("select", message, text, **kwargs) assert result == "bar" def test_select_initial_choice(): message = "Foo message" choice = Choice("bazz") kwargs = {"choices": ["foo", choice], "default": choice} text = KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("select", message, text, **kwargs) assert result == "bazz" def test_select_initial_choice_string(): message = "Foo message" kwargs = {"choices": ["foo", "bazz"], "default": "bazz"} text = KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("select", message, text, **kwargs) assert result == "bazz" def test_select_initial_choice_duplicate(): message = "Foo message" choice = Choice("foo") kwargs = {"choices": ["foo", choice, "bazz"], "default": choice} text = KeyInputs.DOWN + KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("select", message, text, **kwargs) assert result == "bazz" def test_select_initial_choice_not_selectable(): message = "Foo message" separator = Separator() kwargs = {"choices": ["foo", "bazz", separator], "default": separator} text = KeyInputs.ENTER + "\r" with pytest.raises(ValueError): feed_cli_with_input("select", message, text, **kwargs) def test_select_initial_choice_non_existant(): message = "Foo message" kwargs = {"choices": ["foo", "bazz"], "default": "bar"} text = KeyInputs.ENTER + "\r" with pytest.raises(ValueError): feed_cli_with_input("select", message, text, **kwargs) def test_no_invalid_parameters_are_forwarded(): # the `validate_while_typing` parameter is an additional parameter that # gets forwarded to the `PromptSession`. this checks that the parameter # isn't forwarded to a method that does not expect it patched_prompt( [ { "type": "select", "name": "theme", "message": "What do you want to do?", "choices": [ "Order a pizza", "Make a reservation", ], } ], text=KeyInputs.ENTER + "\r", validate_while_typing=False, ) def test_select_arrow_keys(): message = "Foo message" kwargs = {"choices": ["foo", "bazz"], "use_arrow_keys": True} text = KeyInputs.DOWN + KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("select", message, text, **kwargs) assert result == "bazz" def test_fail_for_unreachable_choice(): message = "Foo message" kwargs = { "choices": ["foo", "bar", Choice("bazz", shortcut_key=False)], "use_shortcuts": True, "use_arrow_keys": False, } text = KeyInputs.THREE + "\r" with pytest.raises(RuntimeError): feed_cli_with_input("select", message, text, **kwargs) def test_fail_on_no_method_to_move_selection(): message = "Foo message" kwargs = { "choices": ["foo", Choice("bar", disabled="bad"), "bazz"], "use_shortcuts": False, "use_arrow_keys": False, "use_jk_keys": False, "use_emacs_keys": False, } text = KeyInputs.ENTER + "\r" with pytest.raises(ValueError): feed_cli_with_input("select", message, text, **kwargs) def test_jk_and_shortcut_conflict_fails(): message = "Foo message" kwargs = { "choices": ["foo", Choice("bar", shortcut_key="j"), "bazz"], "use_shortcuts": True, "use_arrow_keys": True, "use_jk_keys": True, } text = KeyInputs.ENTER + "\r" with pytest.raises(ValueError): feed_cli_with_input("select", message, text, **kwargs) def test_jk_and_shortcut_conflict_avoided_by_disabling_ij_keys(): message = "Foo message" kwargs = { "choices": ["foo", Choice("bar", shortcut_key="j"), "bazz"], "use_shortcuts": True, "use_arrow_keys": True, "use_jk_keys": False, } text = KeyInputs.ENTER + "\r" feed_cli_with_input("select", message, text, **kwargs) def test_select_shortcuts(): message = "Foo message" kwargs = {"choices": ["foo", "bazz"], "use_shortcuts": True} text = "2" + KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("select", message, text, **kwargs) assert result == "bazz" def test_select_no_arrow_keys(): message = "Foo message" kwargs = { "choices": ["foo", "bazz"], "use_arrow_keys": False, "use_shortcuts": True, } text = KeyInputs.DOWN + KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("select", message, text, **kwargs) assert result == "foo" def test_select_no_shortcuts(): message = "Foo message" kwargs = { "choices": ["foo", "bazz"], "use_arrow_keys": True, "use_shortcuts": False, } text = "2" + KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("select", message, text, **kwargs) assert result == "foo" def test_select_default_has_arrow_keys(): message = "Foo message" kwargs = {"choices": ["foo", "bazz"]} text = KeyInputs.DOWN + KeyInputs.ENTER + "\r" result, cli = feed_cli_with_input("select", message, text, **kwargs) assert result == "bazz" questionary-2.0.1/tests/prompts/test_text.py000066400000000000000000000024021447660764300213570ustar00rootroot00000000000000# -*- coding: utf-8 -*- import re from prompt_toolkit.validation import ValidationError from prompt_toolkit.validation import Validator from tests.utils import feed_cli_with_input def test_legacy_name(): message = "What is your name" text = "bob\r" result, cli = feed_cli_with_input("input", message, text) assert result == "bob" def test_text(): message = "What is your name" text = "bob\r" result, cli = feed_cli_with_input("text", message, text) assert result == "bob" def test_text_validate(): message = "What is your name" text = "Doe\r" result, cli = feed_cli_with_input( "text", message, text, validate=lambda val: val == "Doe" or "is your last name Doe?", ) assert result == "Doe" def test_text_validate_with_class(): class SimpleValidator(Validator): def validate(self, document): ok = re.match("[01][01][01]", document.text) if not ok: raise ValidationError( message="Binary FTW", cursor_position=len(document.text) ) message = "What is your name" text = "001\r" result, cli = feed_cli_with_input("text", message, text, validate=SimpleValidator) assert result == "001" questionary-2.0.1/tests/test_examples.py000066400000000000000000000073361447660764300205200ustar00rootroot00000000000000from prompt_toolkit.output import DummyOutput from tests.utils import KeyInputs from tests.utils import execute_with_input_pipe def ask_with_patched_input(q, text): def run(inp): inp.send_text(text) return q(input=inp, output=DummyOutput()) return execute_with_input_pipe(run) def test_confirm_example(): from examples.confirm_continue import ask_dictstyle from examples.confirm_continue import ask_pystyle text = "n" + KeyInputs.ENTER + "\r" result_dict = ask_with_patched_input(ask_dictstyle, text) result_py = ask_with_patched_input(ask_pystyle, text) assert result_dict == {"continue": False} assert result_dict["continue"] == result_py def test_text_example(): from examples.text_phone_number import ask_dictstyle from examples.text_phone_number import ask_pystyle text = "1234567890" + KeyInputs.ENTER + "\r" result_dict = ask_with_patched_input(ask_dictstyle, text) result_py = ask_with_patched_input(ask_pystyle, text) assert result_dict == {"phone": "1234567890"} assert result_dict["phone"] == result_py def test_select_example(): from examples.select_restaurant import ask_dictstyle from examples.select_restaurant import ask_pystyle text = KeyInputs.DOWN + KeyInputs.ENTER + KeyInputs.ENTER + "\r" result_dict = ask_with_patched_input(ask_dictstyle, text) result_py = ask_with_patched_input(ask_pystyle, text) assert result_dict == {"theme": "Make a reservation"} assert result_dict["theme"] == result_py def test_rawselect_example(): from examples.rawselect_separator import ask_dictstyle from examples.rawselect_separator import ask_pystyle text = "3" + KeyInputs.ENTER + KeyInputs.ENTER + "\r" result_dict = ask_with_patched_input(ask_dictstyle, text) result_py = ask_with_patched_input(ask_pystyle, text) assert result_dict == {"theme": "Ask opening hours"} assert result_dict["theme"] == result_py def test_checkbox_example(): from examples.checkbox_separators import ask_dictstyle from examples.checkbox_separators import ask_pystyle text = "n" + KeyInputs.ENTER + KeyInputs.ENTER + KeyInputs.ENTER + "\r" result_dict = ask_with_patched_input(ask_dictstyle, text) result_py = ask_with_patched_input(ask_pystyle, text) assert result_dict == {"toppings": ["foo"]} assert result_dict["toppings"] == result_py def test_password_example(): from examples.password_git import ask_dictstyle from examples.password_git import ask_pystyle text = "asdf" + KeyInputs.ENTER + "\r" result_dict = ask_with_patched_input(ask_dictstyle, text) result_py = ask_with_patched_input(ask_pystyle, text) assert result_dict == {"password": "asdf"} assert result_dict["password"] == result_py def test_autocomplete_example(): from examples.autocomplete_ants import ask_dictstyle from examples.autocomplete_ants import ask_pystyle text = "Polyergus lucidus" + KeyInputs.ENTER + "\r" result_dict = ask_with_patched_input(ask_dictstyle, text) result_py = ask_with_patched_input(ask_pystyle, text) assert result_dict == {"ants": "Polyergus lucidus"} assert result_py == "Polyergus lucidus" def test_advanced_workflow_example(): from examples.advanced_workflow import ask_dictstyle text = ( KeyInputs.ENTER + "questionary" + KeyInputs.ENTER + KeyInputs.DOWN + KeyInputs.DOWN + KeyInputs.ENTER + "Hello World" + KeyInputs.ENTER + "\r" ) result_dict = ask_with_patched_input(ask_dictstyle, text) assert result_dict == { "intro": None, "conditional_step": True, "next_question": "questionary", "second_question": "Hello World", } questionary-2.0.1/tests/test_form.py000066400000000000000000000035471447660764300176450ustar00rootroot00000000000000from prompt_toolkit.output import DummyOutput from pytest import fail import questionary from questionary import form from tests.utils import KeyInputs from tests.utils import execute_with_input_pipe def example_form(inp): return form( q1=questionary.confirm("Hello?", input=inp, output=DummyOutput()), q2=questionary.select( "World?", choices=["foo", "bar"], input=inp, output=DummyOutput() ), ) def example_form_with_skip(inp): return form( q1=questionary.confirm("Hello?", input=inp, output=DummyOutput()), q2=questionary.select( "World?", choices=["foo", "bar"], input=inp, output=DummyOutput() ).skip_if(True, 42), ) def test_form_creation(): text = "Y" + KeyInputs.ENTER + "\r" def run(inp): inp.send_text(text) f = example_form(inp) result = f.unsafe_ask() assert result == {"q1": True, "q2": "foo"} execute_with_input_pipe(run) def test_form_skips_questions(): text = "Y" + KeyInputs.ENTER + "\r" def run(inp): inp.send_text(text) f = example_form_with_skip(inp) result = f.ask() assert result == {"q1": True, "q2": 42} execute_with_input_pipe(run) def test_form_skips_questions_unsafe_ask(): text = "Y" + KeyInputs.ENTER + "\r" def run(inp): inp.send_text(text) f = example_form_with_skip(inp) result = f.unsafe_ask() assert result == {"q1": True, "q2": 42} execute_with_input_pipe(run) def test_ask_should_catch_keyboard_exception(): def run(inp): try: inp.send_text(KeyInputs.CONTROLC) f = example_form(inp) result = f.ask() assert result == {} except KeyboardInterrupt: fail("Keyboard Interrupt should be caught by `ask()`") execute_with_input_pipe(run) questionary-2.0.1/tests/test_prompt.py000066400000000000000000000040261447660764300202140ustar00rootroot00000000000000import pytest from questionary.prompt import PromptParameterException from questionary.prompt import prompt from tests.utils import patched_prompt def test_missing_message(): with pytest.raises(PromptParameterException): prompt([{"type": "confirm", "name": "continue", "default": True}]) def test_missing_type(): with pytest.raises(PromptParameterException): prompt( [ { "message": "Do you want to continue?", "name": "continue", "default": True, } ] ) def test_missing_name(): with pytest.raises(PromptParameterException): prompt( [ { "type": "confirm", "message": "Do you want to continue?", "default": True, } ] ) def test_invalid_question_type(): with pytest.raises(ValueError): prompt( [ { "type": "mytype", "message": "Do you want to continue?", "name": "continue", "default": True, } ] ) def test_missing_print_message(): """Test 'print' raises exception if missing 'message'""" with pytest.raises(PromptParameterException): prompt( [ { "name": "test", "type": "print", } ] ) def test_print_no_name(): """'print' type doesn't require a name so it should not throw PromptParameterException""" questions = [{"type": "print", "message": "Hello World"}] result = patched_prompt(questions, "") assert result == {} def test_print_with_name(): """'print' type should return {name: None} when name is provided""" questions = [{"name": "hello", "type": "print", "message": "Hello World"}] result = patched_prompt(questions, "") assert result == {"hello": None} questionary-2.0.1/tests/test_question.py000066400000000000000000000053711447660764300205460ustar00rootroot00000000000000import asyncio import platform import pytest from prompt_toolkit.output import DummyOutput from pytest import fail from questionary import text from questionary.utils import is_prompt_toolkit_3 from tests.utils import KeyInputs from tests.utils import execute_with_input_pipe def test_ask_should_catch_keyboard_exception(): def run(inp): inp.send_text(KeyInputs.CONTROLC) question = text("Hello?", input=inp, output=DummyOutput()) try: result = question.ask() assert result is None except KeyboardInterrupt: fail("Keyboard Interrupt should be caught by `ask()`") execute_with_input_pipe(run) def test_skipping_of_questions(): def run(inp): question = text("Hello?", input=inp, output=DummyOutput()).skip_if( condition=True, default=42 ) response = question.ask() assert response == 42 execute_with_input_pipe(run) def test_skipping_of_questions_unsafe(): def run(inp): question = text("Hello?", input=inp, output=DummyOutput()).skip_if( condition=True, default=42 ) response = question.unsafe_ask() assert response == 42 execute_with_input_pipe(run) def test_skipping_of_skipping_of_questions(): def run(inp): inp.send_text("World" + KeyInputs.ENTER + "\r") question = text("Hello?", input=inp, output=DummyOutput()).skip_if( condition=False, default=42 ) response = question.ask() assert response == "World" and not response == 42 execute_with_input_pipe(run) def test_skipping_of_skipping_of_questions_unsafe(): def run(inp): inp.send_text("World" + KeyInputs.ENTER + "\r") question = text("Hello?", input=inp, output=DummyOutput()).skip_if( condition=False, default=42 ) response = question.unsafe_ask() assert response == "World" and not response == 42 execute_with_input_pipe(run) @pytest.mark.skipif( not is_prompt_toolkit_3() and platform.system() == "Windows", reason="requires prompt_toolkit >= 3", ) def test_async_ask_question(): loop = asyncio.new_event_loop() def run(inp): inp.send_text("World" + KeyInputs.ENTER + "\r") question = text("Hello?", input=inp, output=DummyOutput()) response = loop.run_until_complete(question.ask_async()) assert response == "World" execute_with_input_pipe(run) def test_multiline_text(): def run(inp): inp.send_text(f"Hello{KeyInputs.ENTER}world{KeyInputs.ESCAPE}{KeyInputs.ENTER}") question = text("Hello?", input=inp, output=DummyOutput(), multiline=True) response = question.ask() assert response == "Hello\nworld" execute_with_input_pipe(run) questionary-2.0.1/tests/test_utils.py000066400000000000000000000035071447660764300200360ustar00rootroot00000000000000from questionary import utils def test_default_values_of(): def f(a, b=2, c=None, *args, **kwargs): pass defaults = utils.default_values_of(f) assert defaults == ["b", "c", "args", "kwargs"] def test_default_values_of_no_args(): def f(): pass defaults = utils.default_values_of(f) assert defaults == [] def test_arguments_of(): def f(a, b=2, c=None, *args, **kwargs): pass defaults = utils.arguments_of(f) assert defaults == ["a", "b", "c", "args", "kwargs"] def test_arguments_of_no_args(): def f(): pass defaults = utils.arguments_of(f) assert defaults == [] def test_filter_kwargs(): def f(a, b=1, *, c=2): pass kwargs = { "a": 1, "b": 2, "c": 3, "d": 4, } filtered = utils.used_kwargs(kwargs, f) assert "a" in filtered assert "b" in filtered assert "c" in filtered assert "d" not in filtered def test_filter_kwargs_empty(): def f(): pass kwargs = { "a": 1, "b": 2, } filtered = utils.used_kwargs(kwargs, f) assert filtered == {} def test_required_arguments_of(): def f(a, b=2, c=None, *args, **kwargs): pass defaults = utils.required_arguments(f) assert defaults == ["a"] def test_required_arguments_of_no_args(): def f(): pass defaults = utils.required_arguments(f) assert defaults == [] def test_missing_arguments(): def f(a, b=2, c=None, *args, **kwargs): pass assert utils.missing_arguments(f, {}) == {"a"} assert utils.missing_arguments(f, {"a": 1}) == set() assert utils.missing_arguments(f, {"a": 1, "b": 2}) == set() def test_missing_arguments_of_no_args(): def f(): pass defaults = utils.missing_arguments(f, {}) assert defaults == set() questionary-2.0.1/tests/utils.py000066400000000000000000000046751447660764300170060ustar00rootroot00000000000000# -*- coding: utf-8 -*- import asyncio import prompt_toolkit from prompt_toolkit.input.defaults import create_pipe_input from prompt_toolkit.output import DummyOutput from questionary import prompt from questionary.prompts import prompt_by_name from questionary.utils import is_prompt_toolkit_3 prompt_toolkit_version = tuple([int(v) for v in prompt_toolkit.VERSION]) class KeyInputs: DOWN = "\x1b[B" UP = "\x1b[A" LEFT = "\x1b[D" RIGHT = "\x1b[C" ENTER = "\r" ESCAPE = "\x1b" CONTROLC = "\x03" CONTROLN = "\x0e" CONTROLP = "\x10" BACK = "\x7f" SPACE = " " TAB = "\x09" ONE = "1" TWO = "2" THREE = "3" def feed_cli_with_input(_type, message, texts, sleep_time=1, **kwargs): """ Create a Prompt, feed it with the given user input and return the CLI object. You an provide multiple texts, the feeder will async sleep for `sleep_time` This returns a (result, Application) tuple. """ if not isinstance(texts, list): texts = [texts] def _create_input(inp): prompter = prompt_by_name(_type) application = prompter(message, input=inp, output=DummyOutput(), **kwargs) if is_prompt_toolkit_3(): loop = asyncio.new_event_loop() future_result = loop.create_task(application.unsafe_ask_async()) for i, text in enumerate(texts): # noinspection PyUnresolvedReferences inp.send_text(text) if i != len(texts) - 1: loop.run_until_complete(asyncio.sleep(sleep_time)) result = loop.run_until_complete(future_result) else: for text in texts: inp.send_text(text) result = application.unsafe_ask() return result, application return execute_with_input_pipe(_create_input) def patched_prompt(questions, text, **kwargs): """Create a prompt where the input and output are predefined.""" def run(inp): # noinspection PyUnresolvedReferences inp.send_text(text) result = prompt(questions, input=inp, output=DummyOutput(), **kwargs) return result return execute_with_input_pipe(run) def execute_with_input_pipe(func): if prompt_toolkit_version < (3, 0, 29): inp = create_pipe_input() try: return func(inp) finally: inp.close() else: with create_pipe_input() as inp: return func(inp)