commit c1c35bfc323ad2982c32d88a503c4c3a6f69558c Author: Karl Date: Mon Oct 25 20:04:55 2021 +0100 init diff --git a/__pycache__/app.cpython-37.pyc b/__pycache__/app.cpython-37.pyc new file mode 100644 index 0000000..5f997c9 Binary files /dev/null and b/__pycache__/app.cpython-37.pyc differ diff --git a/__pycache__/server.cpython-37.pyc b/__pycache__/server.cpython-37.pyc new file mode 100644 index 0000000..41192c9 Binary files /dev/null and b/__pycache__/server.cpython-37.pyc differ diff --git a/app.py b/app.py new file mode 100644 index 0000000..e7faa3f --- /dev/null +++ b/app.py @@ -0,0 +1,18 @@ + +#import pygatt +import os + +#adapter = pygatt.GATTToolBackend() +on = 'cc2333' +off = 'cc2433' + +try: +# adapter.start() +# device = adapter.connect("b2:3b:03:00:14:d6") +# device.char_write_handle(0x0009, bytearray.fromhex(off)) + os.system('gatttool -i hci0 -b b2:3b:03:00:14:d6 --char-write-req -a 0x0009 -n cc2333') +except error as ex: + print(ex) +#finally: +# adapter.stop() + diff --git a/server.py b/server.py new file mode 100644 index 0000000..be429ae --- /dev/null +++ b/server.py @@ -0,0 +1,38 @@ +from flask import Flask, request +import os +from colormap import rgb2hex + +app = Flask(__name__) + + +@app.route('/') +def index(): + os.system('gatttool -i hci0 -b b2:3b:03:00:14:d6 --char-write-req -a 0x0009 -n cc2433') + return 'Index Page' + +@app.route('/hello') +def hello(): + #a = rgb2hex(255, 255, 255) + + #str = '56' + a + '00f0aa' + #os.system('gatttool -i hci0 -b b2:3b:03:00:14:d6 --char-write-req -a 0x0009 -n ${str}') + return request.query_string + +#adding variables +@app.route('/user/') +def show_user(username): + #returns the username + return 'Username: %s' % username + +@app.route('/post/') +def show_post(post_id): + #returns the post, the post_id should be an int + return str(post_id) + +@app.route('/user', methods=['GET','POST']) +def get_user(): + username = request.form['username'] + password = request.form['password'] + #login(arg,arg) is a function that tries to log in and returns true or false + status = login(username, password) + return status diff --git a/venv/bin/activate b/venv/bin/activate new file mode 100644 index 0000000..069a42d --- /dev/null +++ b/venv/bin/activate @@ -0,0 +1,76 @@ +# This file must be used with "source bin/activate" *from bash* +# you cannot run it directly + +deactivate () { + # reset old environment variables + if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then + PATH="${_OLD_VIRTUAL_PATH:-}" + export PATH + unset _OLD_VIRTUAL_PATH + fi + if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then + PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}" + export PYTHONHOME + unset _OLD_VIRTUAL_PYTHONHOME + fi + + # This should detect bash and zsh, which have a hash command that must + # be called to get it to forget past commands. Without forgetting + # past commands the $PATH changes we made may not be respected + if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then + hash -r + fi + + if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then + PS1="${_OLD_VIRTUAL_PS1:-}" + export PS1 + unset _OLD_VIRTUAL_PS1 + fi + + unset VIRTUAL_ENV + if [ ! "$1" = "nondestructive" ] ; then + # Self destruct! + unset -f deactivate + fi +} + +# unset irrelevant variables +deactivate nondestructive + +VIRTUAL_ENV="/home/pi/pyBridge/venv" +export VIRTUAL_ENV + +_OLD_VIRTUAL_PATH="$PATH" +PATH="$VIRTUAL_ENV/bin:$PATH" +export PATH + +# unset PYTHONHOME if set +# this will fail if PYTHONHOME is set to the empty string (which is bad anyway) +# could use `if (set -u; : $PYTHONHOME) ;` in bash +if [ -n "${PYTHONHOME:-}" ] ; then + _OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}" + unset PYTHONHOME +fi + +if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then + _OLD_VIRTUAL_PS1="${PS1:-}" + if [ "x(venv) " != x ] ; then + PS1="(venv) ${PS1:-}" + else + if [ "`basename \"$VIRTUAL_ENV\"`" = "__" ] ; then + # special case for Aspen magic directories + # see http://www.zetadev.com/software/aspen/ + PS1="[`basename \`dirname \"$VIRTUAL_ENV\"\``] $PS1" + else + PS1="(`basename \"$VIRTUAL_ENV\"`)$PS1" + fi + fi + export PS1 +fi + +# This should detect bash and zsh, which have a hash command that must +# be called to get it to forget past commands. Without forgetting +# past commands the $PATH changes we made may not be respected +if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then + hash -r +fi diff --git a/venv/bin/activate.csh b/venv/bin/activate.csh new file mode 100644 index 0000000..4a34dea --- /dev/null +++ b/venv/bin/activate.csh @@ -0,0 +1,37 @@ +# This file must be used with "source bin/activate.csh" *from csh*. +# You cannot run it directly. +# Created by Davide Di Blasi . +# Ported to Python 3.3 venv by Andrew Svetlov + +alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; test "\!:*" != "nondestructive" && unalias deactivate' + +# Unset irrelevant variables. +deactivate nondestructive + +setenv VIRTUAL_ENV "/home/pi/pyBridge/venv" + +set _OLD_VIRTUAL_PATH="$PATH" +setenv PATH "$VIRTUAL_ENV/bin:$PATH" + + +set _OLD_VIRTUAL_PROMPT="$prompt" + +if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then + if ("venv" != "") then + set env_name = "venv" + else + if (`basename "VIRTUAL_ENV"` == "__") then + # special case for Aspen magic directories + # see http://www.zetadev.com/software/aspen/ + set env_name = `basename \`dirname "$VIRTUAL_ENV"\`` + else + set env_name = `basename "$VIRTUAL_ENV"` + endif + endif + set prompt = "[$env_name] $prompt" + unset env_name +endif + +alias pydoc python -m pydoc + +rehash diff --git a/venv/bin/activate.fish b/venv/bin/activate.fish new file mode 100644 index 0000000..8969089 --- /dev/null +++ b/venv/bin/activate.fish @@ -0,0 +1,75 @@ +# This file must be used with ". bin/activate.fish" *from fish* (http://fishshell.org) +# you cannot run it directly + +function deactivate -d "Exit virtualenv and return to normal shell environment" + # reset old environment variables + if test -n "$_OLD_VIRTUAL_PATH" + set -gx PATH $_OLD_VIRTUAL_PATH + set -e _OLD_VIRTUAL_PATH + end + if test -n "$_OLD_VIRTUAL_PYTHONHOME" + set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME + set -e _OLD_VIRTUAL_PYTHONHOME + end + + if test -n "$_OLD_FISH_PROMPT_OVERRIDE" + functions -e fish_prompt + set -e _OLD_FISH_PROMPT_OVERRIDE + functions -c _old_fish_prompt fish_prompt + functions -e _old_fish_prompt + end + + set -e VIRTUAL_ENV + if test "$argv[1]" != "nondestructive" + # Self destruct! + functions -e deactivate + end +end + +# unset irrelevant variables +deactivate nondestructive + +set -gx VIRTUAL_ENV "/home/pi/pyBridge/venv" + +set -gx _OLD_VIRTUAL_PATH $PATH +set -gx PATH "$VIRTUAL_ENV/bin" $PATH + +# unset PYTHONHOME if set +if set -q PYTHONHOME + set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME + set -e PYTHONHOME +end + +if test -z "$VIRTUAL_ENV_DISABLE_PROMPT" + # fish uses a function instead of an env var to generate the prompt. + + # save the current fish_prompt function as the function _old_fish_prompt + functions -c fish_prompt _old_fish_prompt + + # with the original prompt function renamed, we can override with our own. + function fish_prompt + # Save the return status of the last command + set -l old_status $status + + # Prompt override? + if test -n "(venv) " + printf "%s%s" "(venv) " (set_color normal) + else + # ...Otherwise, prepend env + set -l _checkbase (basename "$VIRTUAL_ENV") + if test $_checkbase = "__" + # special case for Aspen magic directories + # see http://www.zetadev.com/software/aspen/ + printf "%s[%s]%s " (set_color -b blue white) (basename (dirname "$VIRTUAL_ENV")) (set_color normal) + else + printf "%s(%s)%s" (set_color -b blue white) (basename "$VIRTUAL_ENV") (set_color normal) + end + end + + # Restore the return status of the previous command. + echo "exit $old_status" | . + _old_fish_prompt + end + + set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV" +end diff --git a/venv/bin/browse b/venv/bin/browse new file mode 100755 index 0000000..45b7ab6 --- /dev/null +++ b/venv/bin/browse @@ -0,0 +1,10 @@ +#!/home/pi/pyBridge/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys + +from easydev.browser import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/easy_install b/venv/bin/easy_install new file mode 100755 index 0000000..e47864b --- /dev/null +++ b/venv/bin/easy_install @@ -0,0 +1,10 @@ +#!/home/pi/pyBridge/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys + +from setuptools.command.easy_install import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/easy_install-3.7 b/venv/bin/easy_install-3.7 new file mode 100755 index 0000000..e47864b --- /dev/null +++ b/venv/bin/easy_install-3.7 @@ -0,0 +1,10 @@ +#!/home/pi/pyBridge/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys + +from setuptools.command.easy_install import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/flask b/venv/bin/flask new file mode 100755 index 0000000..a60d54d --- /dev/null +++ b/venv/bin/flask @@ -0,0 +1,10 @@ +#!/home/pi/pyBridge/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys + +from flask.cli import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/pip b/venv/bin/pip new file mode 100755 index 0000000..068feed --- /dev/null +++ b/venv/bin/pip @@ -0,0 +1,10 @@ +#!/home/pi/pyBridge/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys + +from pip._internal import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/pip3 b/venv/bin/pip3 new file mode 100755 index 0000000..068feed --- /dev/null +++ b/venv/bin/pip3 @@ -0,0 +1,10 @@ +#!/home/pi/pyBridge/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys + +from pip._internal import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/pip3.7 b/venv/bin/pip3.7 new file mode 100755 index 0000000..068feed --- /dev/null +++ b/venv/bin/pip3.7 @@ -0,0 +1,10 @@ +#!/home/pi/pyBridge/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys + +from pip._internal import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/pyserial-miniterm b/venv/bin/pyserial-miniterm new file mode 100755 index 0000000..6cfb9ff --- /dev/null +++ b/venv/bin/pyserial-miniterm @@ -0,0 +1,10 @@ +#!/home/pi/pyBridge/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys + +from serial.tools.miniterm import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/pyserial-ports b/venv/bin/pyserial-ports new file mode 100755 index 0000000..4dbcd4e --- /dev/null +++ b/venv/bin/pyserial-ports @@ -0,0 +1,10 @@ +#!/home/pi/pyBridge/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys + +from serial.tools.list_ports import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/python b/venv/bin/python new file mode 120000 index 0000000..b8a0adb --- /dev/null +++ b/venv/bin/python @@ -0,0 +1 @@ +python3 \ No newline at end of file diff --git a/venv/bin/python3 b/venv/bin/python3 new file mode 120000 index 0000000..ae65fda --- /dev/null +++ b/venv/bin/python3 @@ -0,0 +1 @@ +/usr/bin/python3 \ No newline at end of file diff --git a/venv/lib/python3.7/site-packages/Flask-2.0.2.dist-info/INSTALLER b/venv/lib/python3.7/site-packages/Flask-2.0.2.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.7/site-packages/Flask-2.0.2.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.7/site-packages/Flask-2.0.2.dist-info/LICENSE.rst b/venv/lib/python3.7/site-packages/Flask-2.0.2.dist-info/LICENSE.rst new file mode 100644 index 0000000..9d227a0 --- /dev/null +++ b/venv/lib/python3.7/site-packages/Flask-2.0.2.dist-info/LICENSE.rst @@ -0,0 +1,28 @@ +Copyright 2010 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/lib/python3.7/site-packages/Flask-2.0.2.dist-info/METADATA b/venv/lib/python3.7/site-packages/Flask-2.0.2.dist-info/METADATA new file mode 100644 index 0000000..aaf27ca --- /dev/null +++ b/venv/lib/python3.7/site-packages/Flask-2.0.2.dist-info/METADATA @@ -0,0 +1,125 @@ +Metadata-Version: 2.1 +Name: Flask +Version: 2.0.2 +Summary: A simple framework for building complex web applications. +Home-page: https://palletsprojects.com/p/flask +Author: Armin Ronacher +Author-email: armin.ronacher@active-4.com +Maintainer: Pallets +Maintainer-email: contact@palletsprojects.com +License: BSD-3-Clause +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Documentation, https://flask.palletsprojects.com/ +Project-URL: Changes, https://flask.palletsprojects.com/changes/ +Project-URL: Source Code, https://github.com/pallets/flask/ +Project-URL: Issue Tracker, https://github.com/pallets/flask/issues/ +Project-URL: Twitter, https://twitter.com/PalletsTeam +Project-URL: Chat, https://discord.gg/pallets +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Framework :: Flask +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Internet :: WWW/HTTP :: WSGI +Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application +Classifier: Topic :: Software Development :: Libraries :: Application Frameworks +Requires-Python: >=3.6 +Description-Content-Type: text/x-rst +License-File: LICENSE.rst +Requires-Dist: Werkzeug (>=2.0) +Requires-Dist: Jinja2 (>=3.0) +Requires-Dist: itsdangerous (>=2.0) +Requires-Dist: click (>=7.1.2) +Provides-Extra: async +Requires-Dist: asgiref (>=3.2) ; extra == 'async' +Provides-Extra: dotenv +Requires-Dist: python-dotenv ; extra == 'dotenv' + +Flask +===== + +Flask is a lightweight `WSGI`_ web application framework. It is designed +to make getting started quick and easy, with the ability to scale up to +complex applications. It began as a simple wrapper around `Werkzeug`_ +and `Jinja`_ and has become one of the most popular Python web +application frameworks. + +Flask offers suggestions, but doesn't enforce any dependencies or +project layout. It is up to the developer to choose the tools and +libraries they want to use. There are many extensions provided by the +community that make adding new functionality easy. + +.. _WSGI: https://wsgi.readthedocs.io/ +.. _Werkzeug: https://werkzeug.palletsprojects.com/ +.. _Jinja: https://jinja.palletsprojects.com/ + + +Installing +---------- + +Install and update using `pip`_: + +.. code-block:: text + + $ pip install -U Flask + +.. _pip: https://pip.pypa.io/en/stable/getting-started/ + + +A Simple Example +---------------- + +.. code-block:: python + + # save this as app.py + from flask import Flask + + app = Flask(__name__) + + @app.route("/") + def hello(): + return "Hello, World!" + +.. code-block:: text + + $ flask run + * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) + + +Contributing +------------ + +For guidance on setting up a development environment and how to make a +contribution to Flask, see the `contributing guidelines`_. + +.. _contributing guidelines: https://github.com/pallets/flask/blob/main/CONTRIBUTING.rst + + +Donate +------ + +The Pallets organization develops and supports Flask and the libraries +it uses. In order to grow the community of contributors and users, and +allow the maintainers to devote more time to the projects, `please +donate today`_. + +.. _please donate today: https://palletsprojects.com/donate + + +Links +----- + +- Documentation: https://flask.palletsprojects.com/ +- Changes: https://flask.palletsprojects.com/changes/ +- PyPI Releases: https://pypi.org/project/Flask/ +- Source Code: https://github.com/pallets/flask/ +- Issue Tracker: https://github.com/pallets/flask/issues/ +- Website: https://palletsprojects.com/p/flask/ +- Twitter: https://twitter.com/PalletsTeam +- Chat: https://discord.gg/pallets + + diff --git a/venv/lib/python3.7/site-packages/Flask-2.0.2.dist-info/RECORD b/venv/lib/python3.7/site-packages/Flask-2.0.2.dist-info/RECORD new file mode 100644 index 0000000..33494da --- /dev/null +++ b/venv/lib/python3.7/site-packages/Flask-2.0.2.dist-info/RECORD @@ -0,0 +1,51 @@ +../../../bin/flask,sha256=81CaA_qraorrnJWWXc9NxL3QY3UfY2AExyggcY_U5UE,229 +Flask-2.0.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Flask-2.0.2.dist-info/LICENSE.rst,sha256=SJqOEQhQntmKN7uYPhHg9-HTHwvY-Zp5yESOf_N9B-o,1475 +Flask-2.0.2.dist-info/METADATA,sha256=aKsvjFA_ZjZN1jLh1Ac3aQk-ZUZDPrrwo_TGYW1kdAQ,3839 +Flask-2.0.2.dist-info/RECORD,, +Flask-2.0.2.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92 +Flask-2.0.2.dist-info/entry_points.txt,sha256=gBLA1aKg0OYR8AhbAfg8lnburHtKcgJLDU52BBctN0k,42 +Flask-2.0.2.dist-info/top_level.txt,sha256=dvi65F6AeGWVU0TBpYiC04yM60-FX1gJFkK31IKQr5c,6 +flask/__init__.py,sha256=9ZCelLoNCpr6eSuLmYlzvbp12B3lrLgoN5U2UWk1vdo,2251 +flask/__main__.py,sha256=bYt9eEaoRQWdejEHFD8REx9jxVEdZptECFsV7F49Ink,30 +flask/__pycache__/__init__.cpython-37.pyc,, +flask/__pycache__/__main__.cpython-37.pyc,, +flask/__pycache__/app.cpython-37.pyc,, +flask/__pycache__/blueprints.cpython-37.pyc,, +flask/__pycache__/cli.cpython-37.pyc,, +flask/__pycache__/config.cpython-37.pyc,, +flask/__pycache__/ctx.cpython-37.pyc,, +flask/__pycache__/debughelpers.cpython-37.pyc,, +flask/__pycache__/globals.cpython-37.pyc,, +flask/__pycache__/helpers.cpython-37.pyc,, +flask/__pycache__/logging.cpython-37.pyc,, +flask/__pycache__/scaffold.cpython-37.pyc,, +flask/__pycache__/sessions.cpython-37.pyc,, +flask/__pycache__/signals.cpython-37.pyc,, +flask/__pycache__/templating.cpython-37.pyc,, +flask/__pycache__/testing.cpython-37.pyc,, +flask/__pycache__/typing.cpython-37.pyc,, +flask/__pycache__/views.cpython-37.pyc,, +flask/__pycache__/wrappers.cpython-37.pyc,, +flask/app.py,sha256=ectBbi9hGmVHAse5TNcFQZIDRkDAxYUAnLgfuKD0Xws,81975 +flask/blueprints.py,sha256=AkAVXZ_MMkjwjklzCAMdBNowTiM0wVQPynnUnXjTL2M,23781 +flask/cli.py,sha256=wn2Un9RO32ZfRmCMem5KJ5h62-5lnmy1H9uxgyV-eBs,32238 +flask/config.py,sha256=70Uyjh1Jzb9MfTCT7NDhuZWAzyIEu-TIyk6-22MP3zQ,11285 +flask/ctx.py,sha256=EM3W0v1ctuFQAGk_HWtQdoJEg_r2f5Le4xcmElxFwwk,17428 +flask/debughelpers.py,sha256=W82-xrRmodjopBngI9roYH-q08EbQwN2HEGfDAi6SA0,6184 +flask/globals.py,sha256=cWd-R2hUH3VqPhnmQNww892tQS6Yjqg_wg8UvW1M7NM,1723 +flask/helpers.py,sha256=00WqA3wYeyjMrnAOPZTUyrnUf7H8ik3CVT0kqGl_qjk,30589 +flask/json/__init__.py,sha256=unAKdZBlxMI5OMiTU0-Z2Hl4CF1CMJmqTUzpStiExNw,11822 +flask/json/__pycache__/__init__.cpython-37.pyc,, +flask/json/__pycache__/tag.cpython-37.pyc,, +flask/json/tag.py,sha256=fys3HBLssWHuMAIJuTcf2K0bCtosePBKXIWASZEEjnU,8857 +flask/logging.py,sha256=1o_hirVGqdj7SBdETnhX7IAjklG89RXlrwz_2CjzQQE,2273 +flask/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +flask/scaffold.py,sha256=fM9mRy7QBh9fhJ0VTogVx900dDa5oxz8FOw6OK5F-TU,32796 +flask/sessions.py,sha256=Kb7zY4qBIOU2cw1xM5mQ_KmgYUBDFbUYWjlkq0EFYis,15189 +flask/signals.py,sha256=H7QwDciK-dtBxinjKpexpglP0E6k0MJILiFWTItfmqU,2136 +flask/templating.py,sha256=l96VD39JQ0nue4Bcj7wZ4-FWWs-ppLxvgBCpwDQ4KAk,5626 +flask/testing.py,sha256=OsHT-2B70abWH3ulY9IbhLchXIeyj3L-cfcDa88wv5E,10281 +flask/typing.py,sha256=hXEVcXoH-QEabmy1F11pYaQ2SonlkMAwfjBAnqj2x18,1982 +flask/views.py,sha256=nhq31TRB5Z-z2mjFGZACaaB2Et5XPCmWhWxJxOvLWww,5948 +flask/wrappers.py,sha256=VndbHPRBSUUOejmd2Y3ydkoCVUtsS2OJIdJEVIkBVD8,5604 diff --git a/venv/lib/python3.7/site-packages/Flask-2.0.2.dist-info/WHEEL b/venv/lib/python3.7/site-packages/Flask-2.0.2.dist-info/WHEEL new file mode 100644 index 0000000..5bad85f --- /dev/null +++ b/venv/lib/python3.7/site-packages/Flask-2.0.2.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/venv/lib/python3.7/site-packages/Flask-2.0.2.dist-info/entry_points.txt b/venv/lib/python3.7/site-packages/Flask-2.0.2.dist-info/entry_points.txt new file mode 100644 index 0000000..1eb0252 --- /dev/null +++ b/venv/lib/python3.7/site-packages/Flask-2.0.2.dist-info/entry_points.txt @@ -0,0 +1,3 @@ +[console_scripts] +flask = flask.cli:main + diff --git a/venv/lib/python3.7/site-packages/Flask-2.0.2.dist-info/top_level.txt b/venv/lib/python3.7/site-packages/Flask-2.0.2.dist-info/top_level.txt new file mode 100644 index 0000000..7e10602 --- /dev/null +++ b/venv/lib/python3.7/site-packages/Flask-2.0.2.dist-info/top_level.txt @@ -0,0 +1 @@ +flask diff --git a/venv/lib/python3.7/site-packages/Jinja2-3.0.2.dist-info/INSTALLER b/venv/lib/python3.7/site-packages/Jinja2-3.0.2.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.7/site-packages/Jinja2-3.0.2.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.7/site-packages/Jinja2-3.0.2.dist-info/LICENSE.rst b/venv/lib/python3.7/site-packages/Jinja2-3.0.2.dist-info/LICENSE.rst new file mode 100644 index 0000000..c37cae4 --- /dev/null +++ b/venv/lib/python3.7/site-packages/Jinja2-3.0.2.dist-info/LICENSE.rst @@ -0,0 +1,28 @@ +Copyright 2007 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/lib/python3.7/site-packages/Jinja2-3.0.2.dist-info/METADATA b/venv/lib/python3.7/site-packages/Jinja2-3.0.2.dist-info/METADATA new file mode 100644 index 0000000..274e91c --- /dev/null +++ b/venv/lib/python3.7/site-packages/Jinja2-3.0.2.dist-info/METADATA @@ -0,0 +1,113 @@ +Metadata-Version: 2.1 +Name: Jinja2 +Version: 3.0.2 +Summary: A very fast and expressive template engine. +Home-page: https://palletsprojects.com/p/jinja/ +Author: Armin Ronacher +Author-email: armin.ronacher@active-4.com +Maintainer: Pallets +Maintainer-email: contact@palletsprojects.com +License: BSD-3-Clause +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Documentation, https://jinja.palletsprojects.com/ +Project-URL: Changes, https://jinja.palletsprojects.com/changes/ +Project-URL: Source Code, https://github.com/pallets/jinja/ +Project-URL: Issue Tracker, https://github.com/pallets/jinja/issues/ +Project-URL: Twitter, https://twitter.com/PalletsTeam +Project-URL: Chat, https://discord.gg/pallets +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Text Processing :: Markup :: HTML +Requires-Python: >=3.6 +Description-Content-Type: text/x-rst +License-File: LICENSE.rst +Requires-Dist: MarkupSafe (>=2.0) +Provides-Extra: i18n +Requires-Dist: Babel (>=2.7) ; extra == 'i18n' + +Jinja +===== + +Jinja is a fast, expressive, extensible templating engine. Special +placeholders in the template allow writing code similar to Python +syntax. Then the template is passed data to render the final document. + +It includes: + +- Template inheritance and inclusion. +- Define and import macros within templates. +- HTML templates can use autoescaping to prevent XSS from untrusted + user input. +- A sandboxed environment can safely render untrusted templates. +- AsyncIO support for generating templates and calling async + functions. +- I18N support with Babel. +- Templates are compiled to optimized Python code just-in-time and + cached, or can be compiled ahead-of-time. +- Exceptions point to the correct line in templates to make debugging + easier. +- Extensible filters, tests, functions, and even syntax. + +Jinja's philosophy is that while application logic belongs in Python if +possible, it shouldn't make the template designer's job difficult by +restricting functionality too much. + + +Installing +---------- + +Install and update using `pip`_: + +.. code-block:: text + + $ pip install -U Jinja2 + +.. _pip: https://pip.pypa.io/en/stable/getting-started/ + + +In A Nutshell +------------- + +.. code-block:: jinja + + {% extends "base.html" %} + {% block title %}Members{% endblock %} + {% block content %} + + {% endblock %} + + +Donate +------ + +The Pallets organization develops and supports Jinja and other popular +packages. In order to grow the community of contributors and users, and +allow the maintainers to devote more time to the projects, `please +donate today`_. + +.. _please donate today: https://palletsprojects.com/donate + + +Links +----- + +- Documentation: https://jinja.palletsprojects.com/ +- Changes: https://jinja.palletsprojects.com/changes/ +- PyPI Releases: https://pypi.org/project/Jinja2/ +- Source Code: https://github.com/pallets/jinja/ +- Issue Tracker: https://github.com/pallets/jinja/issues/ +- Website: https://palletsprojects.com/p/jinja/ +- Twitter: https://twitter.com/PalletsTeam +- Chat: https://discord.gg/pallets + + diff --git a/venv/lib/python3.7/site-packages/Jinja2-3.0.2.dist-info/RECORD b/venv/lib/python3.7/site-packages/Jinja2-3.0.2.dist-info/RECORD new file mode 100644 index 0000000..653ca77 --- /dev/null +++ b/venv/lib/python3.7/site-packages/Jinja2-3.0.2.dist-info/RECORD @@ -0,0 +1,58 @@ +Jinja2-3.0.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Jinja2-3.0.2.dist-info/LICENSE.rst,sha256=O0nc7kEF6ze6wQ-vG-JgQI_oXSUrjp3y4JefweCUQ3s,1475 +Jinja2-3.0.2.dist-info/METADATA,sha256=NxKriGP-LwQjFBZ4hjV696NkZMe3GPy3tE00McDQZZ0,3539 +Jinja2-3.0.2.dist-info/RECORD,, +Jinja2-3.0.2.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92 +Jinja2-3.0.2.dist-info/entry_points.txt,sha256=Qy_DkVo6Xj_zzOtmErrATe8lHZhOqdjpt3e4JJAGyi8,61 +Jinja2-3.0.2.dist-info/top_level.txt,sha256=PkeVWtLb3-CqjWi1fO29OCbj55EhX_chhKrCdrVe_zs,7 +jinja2/__init__.py,sha256=w4sanG5_dogKC4J_2thsPN9tXzmily3fmXK8Pd46Q1Y,2205 +jinja2/__pycache__/__init__.cpython-37.pyc,, +jinja2/__pycache__/_identifier.cpython-37.pyc,, +jinja2/__pycache__/async_utils.cpython-37.pyc,, +jinja2/__pycache__/bccache.cpython-37.pyc,, +jinja2/__pycache__/compiler.cpython-37.pyc,, +jinja2/__pycache__/constants.cpython-37.pyc,, +jinja2/__pycache__/debug.cpython-37.pyc,, +jinja2/__pycache__/defaults.cpython-37.pyc,, +jinja2/__pycache__/environment.cpython-37.pyc,, +jinja2/__pycache__/exceptions.cpython-37.pyc,, +jinja2/__pycache__/ext.cpython-37.pyc,, +jinja2/__pycache__/filters.cpython-37.pyc,, +jinja2/__pycache__/idtracking.cpython-37.pyc,, +jinja2/__pycache__/lexer.cpython-37.pyc,, +jinja2/__pycache__/loaders.cpython-37.pyc,, +jinja2/__pycache__/meta.cpython-37.pyc,, +jinja2/__pycache__/nativetypes.cpython-37.pyc,, +jinja2/__pycache__/nodes.cpython-37.pyc,, +jinja2/__pycache__/optimizer.cpython-37.pyc,, +jinja2/__pycache__/parser.cpython-37.pyc,, +jinja2/__pycache__/runtime.cpython-37.pyc,, +jinja2/__pycache__/sandbox.cpython-37.pyc,, +jinja2/__pycache__/tests.cpython-37.pyc,, +jinja2/__pycache__/utils.cpython-37.pyc,, +jinja2/__pycache__/visitor.cpython-37.pyc,, +jinja2/_identifier.py,sha256=EdgGJKi7O1yvr4yFlvqPNEqV6M1qHyQr8Gt8GmVTKVM,1775 +jinja2/async_utils.py,sha256=bY2nCUfBA_4FSnNUsIsJgljBq3hACr6fzLi7LiyMTn8,1751 +jinja2/bccache.py,sha256=v5rKAlYxIvfJEa0uGzAC6yCYSS3KuXT5Eqi-n9qvNi8,12670 +jinja2/compiler.py,sha256=v7zKz-mgSYXmfXD9mRmi2BU0B6Z-1RGZmOXCrsPKzc0,72209 +jinja2/constants.py,sha256=GMoFydBF_kdpaRKPoM5cl5MviquVRLVyZtfp5-16jg0,1433 +jinja2/debug.py,sha256=uBmrsiwjYH5l14R9STn5mydOOyriBYol5lDGvEqAb3A,9238 +jinja2/defaults.py,sha256=boBcSw78h-lp20YbaXSJsqkAI2uN_mD_TtCydpeq5wU,1267 +jinja2/environment.py,sha256=T6U4be9mY1CUXXin_EQFwpvpFqCiryweGqzXGRYIoSA,61573 +jinja2/exceptions.py,sha256=ioHeHrWwCWNaXX1inHmHVblvc4haO7AXsjCp3GfWvx0,5071 +jinja2/ext.py,sha256=44SjDjeYkkxQTpmC2BetOTxEFMgQ42p2dfSwXmPFcSo,32122 +jinja2/filters.py,sha256=jusKTZbd0ddZMaibZkxMUVKNsOsaYtOq_Il8Imtx4BE,52609 +jinja2/idtracking.py,sha256=WekexMql3u5n3vDxFsQ_i8HW0j24AtjWTjrPBLWrHww,10721 +jinja2/lexer.py,sha256=qNEQqDQw_zO5EaH6rFQsER7Qwn2du0o22prB-TR11HE,29930 +jinja2/loaders.py,sha256=WGJNP5RQEGtuTABIK2tp1sWEy2kfe5ZFOY0ON0gi0LY,22477 +jinja2/meta.py,sha256=GNPEvifmSaU3CMxlbheBOZjeZ277HThOPUTf1RkppKQ,4396 +jinja2/nativetypes.py,sha256=62hvvsAxAj0YaxylOHoREYVogJ5JqOlJISgGY3OKd_o,3675 +jinja2/nodes.py,sha256=8zvzM5mYaylI-kbT6oI1DChxGH8dZkghWt1fVizy4Us,34605 +jinja2/optimizer.py,sha256=tHkMwXxfZkbfA1KmLcqmBMSaz7RLIvvItrJcPoXTyD8,1650 +jinja2/parser.py,sha256=kHnU8v92GwMYkfr0MVakWv8UlSf_kJPx8LUsgQMof70,39767 +jinja2/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +jinja2/runtime.py,sha256=wVRlkEmAgNU67AIQDqLvI6UkNLkzDqpLA-z4Mi3vl3g,35054 +jinja2/sandbox.py,sha256=-8zxR6TO9kUkciAVFsIKu8Oq-C7PTeYEdZ5TtA55-gw,14600 +jinja2/tests.py,sha256=Am5Z6Lmfr2XaH_npIfJJ8MdXtWsbLjMULZJulTAj30E,5905 +jinja2/utils.py,sha256=udQxWIKaq4QDCZiXN31ngKOaGGdaMA5fl0JMaM-F6fg,26971 +jinja2/visitor.py,sha256=ZmeLuTj66ic35-uFH-1m0EKXiw4ObDDb_WuE6h5vPFg,3572 diff --git a/venv/lib/python3.7/site-packages/Jinja2-3.0.2.dist-info/WHEEL b/venv/lib/python3.7/site-packages/Jinja2-3.0.2.dist-info/WHEEL new file mode 100644 index 0000000..5bad85f --- /dev/null +++ b/venv/lib/python3.7/site-packages/Jinja2-3.0.2.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/venv/lib/python3.7/site-packages/Jinja2-3.0.2.dist-info/entry_points.txt b/venv/lib/python3.7/site-packages/Jinja2-3.0.2.dist-info/entry_points.txt new file mode 100644 index 0000000..3619483 --- /dev/null +++ b/venv/lib/python3.7/site-packages/Jinja2-3.0.2.dist-info/entry_points.txt @@ -0,0 +1,3 @@ +[babel.extractors] +jinja2 = jinja2.ext:babel_extract [i18n] + diff --git a/venv/lib/python3.7/site-packages/Jinja2-3.0.2.dist-info/top_level.txt b/venv/lib/python3.7/site-packages/Jinja2-3.0.2.dist-info/top_level.txt new file mode 100644 index 0000000..7f7afbf --- /dev/null +++ b/venv/lib/python3.7/site-packages/Jinja2-3.0.2.dist-info/top_level.txt @@ -0,0 +1 @@ +jinja2 diff --git a/venv/lib/python3.7/site-packages/MarkupSafe-2.0.1.dist-info/INSTALLER b/venv/lib/python3.7/site-packages/MarkupSafe-2.0.1.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.7/site-packages/MarkupSafe-2.0.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.7/site-packages/MarkupSafe-2.0.1.dist-info/LICENSE.rst b/venv/lib/python3.7/site-packages/MarkupSafe-2.0.1.dist-info/LICENSE.rst new file mode 100644 index 0000000..9d227a0 --- /dev/null +++ b/venv/lib/python3.7/site-packages/MarkupSafe-2.0.1.dist-info/LICENSE.rst @@ -0,0 +1,28 @@ +Copyright 2010 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/lib/python3.7/site-packages/MarkupSafe-2.0.1.dist-info/METADATA b/venv/lib/python3.7/site-packages/MarkupSafe-2.0.1.dist-info/METADATA new file mode 100644 index 0000000..ef44e2b --- /dev/null +++ b/venv/lib/python3.7/site-packages/MarkupSafe-2.0.1.dist-info/METADATA @@ -0,0 +1,100 @@ +Metadata-Version: 2.1 +Name: MarkupSafe +Version: 2.0.1 +Summary: Safely add untrusted strings to HTML/XML markup. +Home-page: https://palletsprojects.com/p/markupsafe/ +Author: Armin Ronacher +Author-email: armin.ronacher@active-4.com +Maintainer: Pallets +Maintainer-email: contact@palletsprojects.com +License: BSD-3-Clause +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Documentation, https://markupsafe.palletsprojects.com/ +Project-URL: Changes, https://markupsafe.palletsprojects.com/changes/ +Project-URL: Source Code, https://github.com/pallets/markupsafe/ +Project-URL: Issue Tracker, https://github.com/pallets/markupsafe/issues/ +Project-URL: Twitter, https://twitter.com/PalletsTeam +Project-URL: Chat, https://discord.gg/pallets +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Text Processing :: Markup :: HTML +Requires-Python: >=3.6 +Description-Content-Type: text/x-rst + +MarkupSafe +========== + +MarkupSafe implements a text object that escapes characters so it is +safe to use in HTML and XML. Characters that have special meanings are +replaced so that they display as the actual characters. This mitigates +injection attacks, meaning untrusted user input can safely be displayed +on a page. + + +Installing +---------- + +Install and update using `pip`_: + +.. code-block:: text + + pip install -U MarkupSafe + +.. _pip: https://pip.pypa.io/en/stable/quickstart/ + + +Examples +-------- + +.. code-block:: pycon + + >>> from markupsafe import Markup, escape + + >>> # escape replaces special characters and wraps in Markup + >>> escape("") + Markup('<script>alert(document.cookie);</script>') + + >>> # wrap in Markup to mark text "safe" and prevent escaping + >>> Markup("Hello") + Markup('hello') + + >>> escape(Markup("Hello")) + Markup('hello') + + >>> # Markup is a str subclass + >>> # methods and operators escape their arguments + >>> template = Markup("Hello {name}") + >>> template.format(name='"World"') + Markup('Hello "World"') + + +Donate +------ + +The Pallets organization develops and supports MarkupSafe and other +popular packages. In order to grow the community of contributors and +users, and allow the maintainers to devote more time to the projects, +`please donate today`_. + +.. _please donate today: https://palletsprojects.com/donate + + +Links +----- + +- Documentation: https://markupsafe.palletsprojects.com/ +- Changes: https://markupsafe.palletsprojects.com/changes/ +- PyPI Releases: https://pypi.org/project/MarkupSafe/ +- Source Code: https://github.com/pallets/markupsafe/ +- Issue Tracker: https://github.com/pallets/markupsafe/issues/ +- Website: https://palletsprojects.com/p/markupsafe/ +- Twitter: https://twitter.com/PalletsTeam +- Chat: https://discord.gg/pallets + + diff --git a/venv/lib/python3.7/site-packages/MarkupSafe-2.0.1.dist-info/RECORD b/venv/lib/python3.7/site-packages/MarkupSafe-2.0.1.dist-info/RECORD new file mode 100644 index 0000000..867749e --- /dev/null +++ b/venv/lib/python3.7/site-packages/MarkupSafe-2.0.1.dist-info/RECORD @@ -0,0 +1,14 @@ +MarkupSafe-2.0.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +MarkupSafe-2.0.1.dist-info/LICENSE.rst,sha256=SJqOEQhQntmKN7uYPhHg9-HTHwvY-Zp5yESOf_N9B-o,1475 +MarkupSafe-2.0.1.dist-info/METADATA,sha256=FmPpxBdaqCCjF-XKqoxeEzqAzhetQnrkkSsd3V3X-Jc,3211 +MarkupSafe-2.0.1.dist-info/RECORD,, +MarkupSafe-2.0.1.dist-info/WHEEL,sha256=GMu0CcHnECe7JSPnzBUPyOsrcZoHb7dOBGXgpe8vHSQ,104 +MarkupSafe-2.0.1.dist-info/top_level.txt,sha256=qy0Plje5IJuvsCBjejJyhDCjEAdcDLK_2agVcex8Z6U,11 +markupsafe/__init__.py,sha256=9Tez4UIlI7J6_sQcUFK1dKniT_b_8YefpGIyYJ3Sr2Q,8923 +markupsafe/__pycache__/__init__.cpython-37.pyc,, +markupsafe/__pycache__/_native.cpython-37.pyc,, +markupsafe/_native.py,sha256=GTKEV-bWgZuSjklhMHOYRHU9k0DMewTf5mVEZfkbuns,1986 +markupsafe/_speedups.c,sha256=CDDtwaV21D2nYtypnMQzxvvpZpcTvIs8OZ6KDa1g4t0,7400 +markupsafe/_speedups.cpython-37m-arm-linux-gnueabihf.so,sha256=0AlECVU_o-cDdF8A4o0VTukuu0ulmYOcMnPIAuYrMtc,47276 +markupsafe/_speedups.pyi,sha256=vfMCsOgbAXRNLUXkyuyonG8uEWKYU4PDqNuMaDELAYw,229 +markupsafe/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/venv/lib/python3.7/site-packages/MarkupSafe-2.0.1.dist-info/WHEEL b/venv/lib/python3.7/site-packages/MarkupSafe-2.0.1.dist-info/WHEEL new file mode 100644 index 0000000..a194ea7 --- /dev/null +++ b/venv/lib/python3.7/site-packages/MarkupSafe-2.0.1.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.32.3) +Root-Is-Purelib: false +Tag: cp37-cp37m-linux_armv7l + diff --git a/venv/lib/python3.7/site-packages/MarkupSafe-2.0.1.dist-info/top_level.txt b/venv/lib/python3.7/site-packages/MarkupSafe-2.0.1.dist-info/top_level.txt new file mode 100644 index 0000000..75bf729 --- /dev/null +++ b/venv/lib/python3.7/site-packages/MarkupSafe-2.0.1.dist-info/top_level.txt @@ -0,0 +1 @@ +markupsafe diff --git a/venv/lib/python3.7/site-packages/Werkzeug-2.0.2.dist-info/INSTALLER b/venv/lib/python3.7/site-packages/Werkzeug-2.0.2.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.7/site-packages/Werkzeug-2.0.2.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.7/site-packages/Werkzeug-2.0.2.dist-info/LICENSE.rst b/venv/lib/python3.7/site-packages/Werkzeug-2.0.2.dist-info/LICENSE.rst new file mode 100644 index 0000000..c37cae4 --- /dev/null +++ b/venv/lib/python3.7/site-packages/Werkzeug-2.0.2.dist-info/LICENSE.rst @@ -0,0 +1,28 @@ +Copyright 2007 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/lib/python3.7/site-packages/Werkzeug-2.0.2.dist-info/METADATA b/venv/lib/python3.7/site-packages/Werkzeug-2.0.2.dist-info/METADATA new file mode 100644 index 0000000..b58b9bd --- /dev/null +++ b/venv/lib/python3.7/site-packages/Werkzeug-2.0.2.dist-info/METADATA @@ -0,0 +1,129 @@ +Metadata-Version: 2.1 +Name: Werkzeug +Version: 2.0.2 +Summary: The comprehensive WSGI web application library. +Home-page: https://palletsprojects.com/p/werkzeug/ +Author: Armin Ronacher +Author-email: armin.ronacher@active-4.com +Maintainer: Pallets +Maintainer-email: contact@palletsprojects.com +License: BSD-3-Clause +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Documentation, https://werkzeug.palletsprojects.com/ +Project-URL: Changes, https://werkzeug.palletsprojects.com/changes/ +Project-URL: Source Code, https://github.com/pallets/werkzeug/ +Project-URL: Issue Tracker, https://github.com/pallets/werkzeug/issues/ +Project-URL: Twitter, https://twitter.com/PalletsTeam +Project-URL: Chat, https://discord.gg/pallets +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Internet :: WWW/HTTP :: WSGI +Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application +Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Middleware +Classifier: Topic :: Software Development :: Libraries :: Application Frameworks +Requires-Python: >=3.6 +Description-Content-Type: text/x-rst +License-File: LICENSE.rst +Requires-Dist: dataclasses ; python_version < "3.7" +Provides-Extra: watchdog +Requires-Dist: watchdog ; extra == 'watchdog' + +Werkzeug +======== + +*werkzeug* German noun: "tool". Etymology: *werk* ("work"), *zeug* ("stuff") + +Werkzeug is a comprehensive `WSGI`_ web application library. It began as +a simple collection of various utilities for WSGI applications and has +become one of the most advanced WSGI utility libraries. + +It includes: + +- An interactive debugger that allows inspecting stack traces and + source code in the browser with an interactive interpreter for any + frame in the stack. +- A full-featured request object with objects to interact with + headers, query args, form data, files, and cookies. +- A response object that can wrap other WSGI applications and handle + streaming data. +- A routing system for matching URLs to endpoints and generating URLs + for endpoints, with an extensible system for capturing variables + from URLs. +- HTTP utilities to handle entity tags, cache control, dates, user + agents, cookies, files, and more. +- A threaded WSGI server for use while developing applications + locally. +- A test client for simulating HTTP requests during testing without + requiring running a server. + +Werkzeug doesn't enforce any dependencies. It is up to the developer to +choose a template engine, database adapter, and even how to handle +requests. It can be used to build all sorts of end user applications +such as blogs, wikis, or bulletin boards. + +`Flask`_ wraps Werkzeug, using it to handle the details of WSGI while +providing more structure and patterns for defining powerful +applications. + +.. _WSGI: https://wsgi.readthedocs.io/en/latest/ +.. _Flask: https://www.palletsprojects.com/p/flask/ + + +Installing +---------- + +Install and update using `pip`_: + +.. code-block:: text + + pip install -U Werkzeug + +.. _pip: https://pip.pypa.io/en/stable/getting-started/ + + +A Simple Example +---------------- + +.. code-block:: python + + from werkzeug.wrappers import Request, Response + + @Request.application + def application(request): + return Response('Hello, World!') + + if __name__ == '__main__': + from werkzeug.serving import run_simple + run_simple('localhost', 4000, application) + + +Donate +------ + +The Pallets organization develops and supports Werkzeug and other +popular packages. In order to grow the community of contributors and +users, and allow the maintainers to devote more time to the projects, +`please donate today`_. + +.. _please donate today: https://palletsprojects.com/donate + + +Links +----- + +- Documentation: https://werkzeug.palletsprojects.com/ +- Changes: https://werkzeug.palletsprojects.com/changes/ +- PyPI Releases: https://pypi.org/project/Werkzeug/ +- Source Code: https://github.com/pallets/werkzeug/ +- Issue Tracker: https://github.com/pallets/werkzeug/issues/ +- Website: https://palletsprojects.com/p/werkzeug/ +- Twitter: https://twitter.com/PalletsTeam +- Chat: https://discord.gg/pallets + + diff --git a/venv/lib/python3.7/site-packages/Werkzeug-2.0.2.dist-info/RECORD b/venv/lib/python3.7/site-packages/Werkzeug-2.0.2.dist-info/RECORD new file mode 100644 index 0000000..fbc40eb --- /dev/null +++ b/venv/lib/python3.7/site-packages/Werkzeug-2.0.2.dist-info/RECORD @@ -0,0 +1,111 @@ +Werkzeug-2.0.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Werkzeug-2.0.2.dist-info/LICENSE.rst,sha256=O0nc7kEF6ze6wQ-vG-JgQI_oXSUrjp3y4JefweCUQ3s,1475 +Werkzeug-2.0.2.dist-info/METADATA,sha256=vh_xrARtpmkFYnWRAgfSiHgl66LH143rMfAfPZo-R_E,4452 +Werkzeug-2.0.2.dist-info/RECORD,, +Werkzeug-2.0.2.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92 +Werkzeug-2.0.2.dist-info/top_level.txt,sha256=QRyj2VjwJoQkrwjwFIOlB8Xg3r9un0NtqVHQF-15xaw,9 +werkzeug/__init__.py,sha256=Wx1PLCftJ7UAS0fBXEO4Prdr6kvEQ124Stwg-XwyhW4,188 +werkzeug/__pycache__/__init__.cpython-37.pyc,, +werkzeug/__pycache__/_internal.cpython-37.pyc,, +werkzeug/__pycache__/_reloader.cpython-37.pyc,, +werkzeug/__pycache__/datastructures.cpython-37.pyc,, +werkzeug/__pycache__/exceptions.cpython-37.pyc,, +werkzeug/__pycache__/filesystem.cpython-37.pyc,, +werkzeug/__pycache__/formparser.cpython-37.pyc,, +werkzeug/__pycache__/http.cpython-37.pyc,, +werkzeug/__pycache__/local.cpython-37.pyc,, +werkzeug/__pycache__/routing.cpython-37.pyc,, +werkzeug/__pycache__/security.cpython-37.pyc,, +werkzeug/__pycache__/serving.cpython-37.pyc,, +werkzeug/__pycache__/test.cpython-37.pyc,, +werkzeug/__pycache__/testapp.cpython-37.pyc,, +werkzeug/__pycache__/urls.cpython-37.pyc,, +werkzeug/__pycache__/user_agent.cpython-37.pyc,, +werkzeug/__pycache__/useragents.cpython-37.pyc,, +werkzeug/__pycache__/utils.cpython-37.pyc,, +werkzeug/__pycache__/wsgi.cpython-37.pyc,, +werkzeug/_internal.py,sha256=_QKkvdaG4pDFwK68c0EpPzYJGe9Y7toRAT1cBbC-CxU,18572 +werkzeug/_reloader.py,sha256=B1hEfgsUOz2IginBQM5Zak_eaIF7gr3GS5-0x2OHvAE,13950 +werkzeug/datastructures.py,sha256=m79A8rHQEt5B7qVqyrjARXzHL66Katn8S92urGscTw4,97929 +werkzeug/datastructures.pyi,sha256=CoVwrQ2Vr9JnbprNL9aE3vOz8mOejT9qysQ-BT53C8Y,34089 +werkzeug/debug/__init__.py,sha256=jYA1e1Gw_8EPOytr-BoMdmm0rzP-Z1H0Ih7wIObnKwQ,17968 +werkzeug/debug/__pycache__/__init__.cpython-37.pyc,, +werkzeug/debug/__pycache__/console.cpython-37.pyc,, +werkzeug/debug/__pycache__/repr.cpython-37.pyc,, +werkzeug/debug/__pycache__/tbtools.cpython-37.pyc,, +werkzeug/debug/console.py,sha256=E1nBMEvFkX673ShQjPtVY-byYatfX9MN-dBMjRI8a8E,5897 +werkzeug/debug/repr.py,sha256=QCSHENKsChEZDCIApkVi_UNjhJ77v8BMXK1OfxO189M,9483 +werkzeug/debug/shared/FONT_LICENSE,sha256=LwAVEI1oYnvXiNMT9SnCH_TaLCxCpeHziDrMg0gPkAI,4673 +werkzeug/debug/shared/ICON_LICENSE.md,sha256=DhA6Y1gUl5Jwfg0NFN9Rj4VWITt8tUx0IvdGf0ux9-s,222 +werkzeug/debug/shared/console.png,sha256=bxax6RXXlvOij_KeqvSNX0ojJf83YbnZ7my-3Gx9w2A,507 +werkzeug/debug/shared/debugger.js,sha256=tg42SZs1SVmYWZ-_Fj5ELK5-FLHnGNQrei0K2By8Bw8,10521 +werkzeug/debug/shared/less.png,sha256=-4-kNRaXJSONVLahrQKUxMwXGm9R4OnZ9SxDGpHlIR4,191 +werkzeug/debug/shared/more.png,sha256=GngN7CioHQoV58rH6ojnkYi8c_qED2Aka5FO5UXrReY,200 +werkzeug/debug/shared/source.png,sha256=RoGcBTE4CyCB85GBuDGTFlAnUqxwTBiIfDqW15EpnUQ,818 +werkzeug/debug/shared/style.css,sha256=h1ZSUVaKNpfbfcYzRb513WAhPySGDQom1uih3uEDxPw,6704 +werkzeug/debug/shared/ubuntu.ttf,sha256=1eaHFyepmy4FyDvjLVzpITrGEBu_CZYY94jE0nED1c0,70220 +werkzeug/debug/tbtools.py,sha256=AFRrjLDCAps7G5K2-RxNZpXXaEoeFHm68T00f4vlDYA,19362 +werkzeug/exceptions.py,sha256=CUwx0pBiNbk4f9cON17ekgKnmLi6HIVFjUmYZc2x0wM,28681 +werkzeug/filesystem.py,sha256=JS2Dv2QF98WILxY4_thHl-WMcUcwluF_4igkDPaP1l4,1956 +werkzeug/formparser.py,sha256=X-p3Ek4ji8XrKrbmaWxr8StLSc6iuksbpIeweaabs4s,17400 +werkzeug/http.py,sha256=oUCXFFMnkOQ-cHbUY_aiqitshcrSzNDq3fEMf1VI_yk,45141 +werkzeug/local.py,sha256=bwL-y3-qOZAspJ66W1P36SUApLXJy3UY8nLYbM9kfmY,23183 +werkzeug/middleware/__init__.py,sha256=qfqgdT5npwG9ses3-FXQJf3aB95JYP1zchetH_T3PUw,500 +werkzeug/middleware/__pycache__/__init__.cpython-37.pyc,, +werkzeug/middleware/__pycache__/dispatcher.cpython-37.pyc,, +werkzeug/middleware/__pycache__/http_proxy.cpython-37.pyc,, +werkzeug/middleware/__pycache__/lint.cpython-37.pyc,, +werkzeug/middleware/__pycache__/profiler.cpython-37.pyc,, +werkzeug/middleware/__pycache__/proxy_fix.cpython-37.pyc,, +werkzeug/middleware/__pycache__/shared_data.cpython-37.pyc,, +werkzeug/middleware/dispatcher.py,sha256=Fh_w-KyWnTSYF-Lfv5dimQ7THSS7afPAZMmvc4zF1gg,2580 +werkzeug/middleware/http_proxy.py,sha256=HE8VyhS7CR-E1O6_9b68huv8FLgGGR1DLYqkS3Xcp3Q,7558 +werkzeug/middleware/lint.py,sha256=sAg3GcOhICIkwYX5bJGG8n8iebX0Yipq_UH0HvrBvoU,13964 +werkzeug/middleware/profiler.py,sha256=QkXk7cqnaPnF8wQu-5SyPCIOT3_kdABUBorQOghVNOA,4899 +werkzeug/middleware/proxy_fix.py,sha256=uRgQ3dEvFV8JxUqajHYYYOPEeA_BFqaa51Yp8VW0uzA,6849 +werkzeug/middleware/shared_data.py,sha256=xydEqOhAGg0aQJEllPDVfz2-8jHwWvJpAxfPsfPCu7k,10960 +werkzeug/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +werkzeug/routing.py,sha256=oqJ32sWIZtIF6zbqfrnwB1Pbv2ShNwPDJd6FYqxdYVo,84527 +werkzeug/sansio/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +werkzeug/sansio/__pycache__/__init__.cpython-37.pyc,, +werkzeug/sansio/__pycache__/multipart.cpython-37.pyc,, +werkzeug/sansio/__pycache__/request.cpython-37.pyc,, +werkzeug/sansio/__pycache__/response.cpython-37.pyc,, +werkzeug/sansio/__pycache__/utils.cpython-37.pyc,, +werkzeug/sansio/multipart.py,sha256=bJMCNC2f5xyAaylahNViJ0JqmV4ThLRbDVGVzKwcqrQ,8751 +werkzeug/sansio/request.py,sha256=aA9rABkWiG4MhYMByanst2NXkEclsq8SIxhb0LQf0e0,20228 +werkzeug/sansio/response.py,sha256=zvCq9HSBBZGBd5Gg412BY9RZIwnKsJl5Kzfd3Kl9sSo,26098 +werkzeug/sansio/utils.py,sha256=V5v-UUnX8pm4RehP9Tt_NiUSOJGJGUvKjlW0eOIQldM,4164 +werkzeug/security.py,sha256=gPDRuCjkjWrcqj99tBMq8_nHFZLFQjgoW5Ga5XIw9jo,8158 +werkzeug/serving.py,sha256=AfgLn0yKr9qXknmwO-0KXJ055oloS4h5DIFDHEu8iHA,38088 +werkzeug/test.py,sha256=8gE1l-Y9yAh2i3SI0kgpxIaI4oYZuehIkxxyDFcz6J0,48123 +werkzeug/testapp.py,sha256=f48prWSGJhbSrvYb8e1fnAah4BkrLb0enHSdChgsjBY,9471 +werkzeug/urls.py,sha256=Du2lreBHvgBh5c2_bcx72g3hzV2ZabXYZsp-picUIJs,41023 +werkzeug/user_agent.py,sha256=WclZhpvgLurMF45hsioSbS75H1Zb4iMQGKN3_yZ2oKo,1420 +werkzeug/useragents.py,sha256=G8tmv_6vxJaPrLQH3eODNgIYe0_V6KETROQlJI-WxDE,7264 +werkzeug/utils.py,sha256=D_dnCLUfodQ4k0GRSpnI6qDoVoaX7-Dza57bx7sabG0,37101 +werkzeug/wrappers/__init__.py,sha256=-s75nPbyXHzU_rwmLPDhoMuGbEUk0jZT_n0ZQAOFGf8,654 +werkzeug/wrappers/__pycache__/__init__.cpython-37.pyc,, +werkzeug/wrappers/__pycache__/accept.cpython-37.pyc,, +werkzeug/wrappers/__pycache__/auth.cpython-37.pyc,, +werkzeug/wrappers/__pycache__/base_request.cpython-37.pyc,, +werkzeug/wrappers/__pycache__/base_response.cpython-37.pyc,, +werkzeug/wrappers/__pycache__/common_descriptors.cpython-37.pyc,, +werkzeug/wrappers/__pycache__/cors.cpython-37.pyc,, +werkzeug/wrappers/__pycache__/etag.cpython-37.pyc,, +werkzeug/wrappers/__pycache__/json.cpython-37.pyc,, +werkzeug/wrappers/__pycache__/request.cpython-37.pyc,, +werkzeug/wrappers/__pycache__/response.cpython-37.pyc,, +werkzeug/wrappers/__pycache__/user_agent.cpython-37.pyc,, +werkzeug/wrappers/accept.py,sha256=_oZtAQkahvsrPRkNj2fieg7_St9P0NFC3SgZbJKS6xU,429 +werkzeug/wrappers/auth.py,sha256=rZPCzGxHk9R55PRkmS90kRywUVjjuMWzCGtH68qCq8U,856 +werkzeug/wrappers/base_request.py,sha256=saz9RyNQkvI_XLPYVm29KijNHmD1YzgxDqa0qHTbgss,1174 +werkzeug/wrappers/base_response.py,sha256=q_-TaYywT5G4zA-DWDRDJhJSat2_4O7gOPob6ye4_9A,1186 +werkzeug/wrappers/common_descriptors.py,sha256=v_kWLH3mvCiSRVJ1FNw7nO3w2UJfzY57UKKB5J4zCvE,898 +werkzeug/wrappers/cors.py,sha256=c5UndlZsZvYkbPrp6Gj5iSXxw_VOJDJHskO6-jRmNyQ,846 +werkzeug/wrappers/etag.py,sha256=XHWQQs7Mdd1oWezgBIsl-bYe8ydKkRZVil2Qd01D0Mo,846 +werkzeug/wrappers/json.py,sha256=HM1btPseGeXca0vnwQN_MvZl6h-qNsFY5YBKXKXFwus,410 +werkzeug/wrappers/request.py,sha256=yZGplfC3UqNuykwLJmgywiMhmnoKEGHJOZn_A_ublcQ,24822 +werkzeug/wrappers/response.py,sha256=0n8OcQptiM2e550SALLeg7vC1uWsUbCeE1rPZFfXR78,35177 +werkzeug/wrappers/user_agent.py,sha256=Wl1-A0-1r8o7cHIZQTB55O4Ged6LpCKENaQDlOY5pXA,435 +werkzeug/wsgi.py,sha256=L7s5-Rlt7BRVEZ1m81MaenGfMDP7yL3p1Kxt9Yssqzg,33727 diff --git a/venv/lib/python3.7/site-packages/Werkzeug-2.0.2.dist-info/WHEEL b/venv/lib/python3.7/site-packages/Werkzeug-2.0.2.dist-info/WHEEL new file mode 100644 index 0000000..5bad85f --- /dev/null +++ b/venv/lib/python3.7/site-packages/Werkzeug-2.0.2.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/venv/lib/python3.7/site-packages/Werkzeug-2.0.2.dist-info/top_level.txt b/venv/lib/python3.7/site-packages/Werkzeug-2.0.2.dist-info/top_level.txt new file mode 100644 index 0000000..6fe8da8 --- /dev/null +++ b/venv/lib/python3.7/site-packages/Werkzeug-2.0.2.dist-info/top_level.txt @@ -0,0 +1 @@ +werkzeug diff --git a/venv/lib/python3.7/site-packages/__pycache__/easy_install.cpython-37.pyc b/venv/lib/python3.7/site-packages/__pycache__/easy_install.cpython-37.pyc new file mode 100644 index 0000000..55c4779 Binary files /dev/null and b/venv/lib/python3.7/site-packages/__pycache__/easy_install.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/__pycache__/typing_extensions.cpython-37.pyc b/venv/lib/python3.7/site-packages/__pycache__/typing_extensions.cpython-37.pyc new file mode 100644 index 0000000..d3171ad Binary files /dev/null and b/venv/lib/python3.7/site-packages/__pycache__/typing_extensions.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/__pycache__/zipp.cpython-37.pyc b/venv/lib/python3.7/site-packages/__pycache__/zipp.cpython-37.pyc new file mode 100644 index 0000000..1df8d9c Binary files /dev/null and b/venv/lib/python3.7/site-packages/__pycache__/zipp.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/click-8.0.3.dist-info/INSTALLER b/venv/lib/python3.7/site-packages/click-8.0.3.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.7/site-packages/click-8.0.3.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.7/site-packages/click-8.0.3.dist-info/LICENSE.rst b/venv/lib/python3.7/site-packages/click-8.0.3.dist-info/LICENSE.rst new file mode 100644 index 0000000..d12a849 --- /dev/null +++ b/venv/lib/python3.7/site-packages/click-8.0.3.dist-info/LICENSE.rst @@ -0,0 +1,28 @@ +Copyright 2014 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/lib/python3.7/site-packages/click-8.0.3.dist-info/METADATA b/venv/lib/python3.7/site-packages/click-8.0.3.dist-info/METADATA new file mode 100644 index 0000000..2c8da01 --- /dev/null +++ b/venv/lib/python3.7/site-packages/click-8.0.3.dist-info/METADATA @@ -0,0 +1,111 @@ +Metadata-Version: 2.1 +Name: click +Version: 8.0.3 +Summary: Composable command line interface toolkit +Home-page: https://palletsprojects.com/p/click/ +Author: Armin Ronacher +Author-email: armin.ronacher@active-4.com +Maintainer: Pallets +Maintainer-email: contact@palletsprojects.com +License: BSD-3-Clause +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Documentation, https://click.palletsprojects.com/ +Project-URL: Changes, https://click.palletsprojects.com/changes/ +Project-URL: Source Code, https://github.com/pallets/click/ +Project-URL: Issue Tracker, https://github.com/pallets/click/issues/ +Project-URL: Twitter, https://twitter.com/PalletsTeam +Project-URL: Chat, https://discord.gg/pallets +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Requires-Python: >=3.6 +Description-Content-Type: text/x-rst +License-File: LICENSE.rst +Requires-Dist: colorama ; platform_system == "Windows" +Requires-Dist: importlib-metadata ; python_version < "3.8" + +\$ click\_ +========== + +Click is a Python package for creating beautiful command line interfaces +in a composable way with as little code as necessary. It's the "Command +Line Interface Creation Kit". It's highly configurable but comes with +sensible defaults out of the box. + +It aims to make the process of writing command line tools quick and fun +while also preventing any frustration caused by the inability to +implement an intended CLI API. + +Click in three points: + +- Arbitrary nesting of commands +- Automatic help page generation +- Supports lazy loading of subcommands at runtime + + +Installing +---------- + +Install and update using `pip`_: + +.. code-block:: text + + $ pip install -U click + +.. _pip: https://pip.pypa.io/en/stable/getting-started/ + + +A Simple Example +---------------- + +.. code-block:: python + + import click + + @click.command() + @click.option("--count", default=1, help="Number of greetings.") + @click.option("--name", prompt="Your name", help="The person to greet.") + def hello(count, name): + """Simple program that greets NAME for a total of COUNT times.""" + for _ in range(count): + click.echo(f"Hello, {name}!") + + if __name__ == '__main__': + hello() + +.. code-block:: text + + $ python hello.py --count=3 + Your name: Click + Hello, Click! + Hello, Click! + Hello, Click! + + +Donate +------ + +The Pallets organization develops and supports Click and other popular +packages. In order to grow the community of contributors and users, and +allow the maintainers to devote more time to the projects, `please +donate today`_. + +.. _please donate today: https://palletsprojects.com/donate + + +Links +----- + +- Documentation: https://click.palletsprojects.com/ +- Changes: https://click.palletsprojects.com/changes/ +- PyPI Releases: https://pypi.org/project/click/ +- Source Code: https://github.com/pallets/click +- Issue Tracker: https://github.com/pallets/click/issues +- Website: https://palletsprojects.com/p/click +- Twitter: https://twitter.com/PalletsTeam +- Chat: https://discord.gg/pallets + + diff --git a/venv/lib/python3.7/site-packages/click-8.0.3.dist-info/RECORD b/venv/lib/python3.7/site-packages/click-8.0.3.dist-info/RECORD new file mode 100644 index 0000000..ef24df6 --- /dev/null +++ b/venv/lib/python3.7/site-packages/click-8.0.3.dist-info/RECORD @@ -0,0 +1,41 @@ +click-8.0.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +click-8.0.3.dist-info/LICENSE.rst,sha256=morRBqOU6FO_4h9C9OctWSgZoigF2ZG18ydQKSkrZY0,1475 +click-8.0.3.dist-info/METADATA,sha256=_0jCOf3DdGPvKUZUlBukeb1t6Pnxmm_OMGpaBoDthfc,3247 +click-8.0.3.dist-info/RECORD,, +click-8.0.3.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92 +click-8.0.3.dist-info/top_level.txt,sha256=J1ZQogalYS4pphY_lPECoNMfw0HzTSrZglC4Yfwo4xA,6 +click/__init__.py,sha256=YkIrDg7-0g5aBS6D2pDe58j3MOaFylHED2_8OXh2fnM,3243 +click/__pycache__/__init__.cpython-37.pyc,, +click/__pycache__/_compat.cpython-37.pyc,, +click/__pycache__/_termui_impl.cpython-37.pyc,, +click/__pycache__/_textwrap.cpython-37.pyc,, +click/__pycache__/_unicodefun.cpython-37.pyc,, +click/__pycache__/_winconsole.cpython-37.pyc,, +click/__pycache__/core.cpython-37.pyc,, +click/__pycache__/decorators.cpython-37.pyc,, +click/__pycache__/exceptions.cpython-37.pyc,, +click/__pycache__/formatting.cpython-37.pyc,, +click/__pycache__/globals.cpython-37.pyc,, +click/__pycache__/parser.cpython-37.pyc,, +click/__pycache__/shell_completion.cpython-37.pyc,, +click/__pycache__/termui.cpython-37.pyc,, +click/__pycache__/testing.cpython-37.pyc,, +click/__pycache__/types.cpython-37.pyc,, +click/__pycache__/utils.cpython-37.pyc,, +click/_compat.py,sha256=P15KQumAZC2F2MFe_JSRbvVOJcNosQfMDrdZq0ReCLM,18814 +click/_termui_impl.py,sha256=z78J5HF_RTsOBhjNLjoigaqRap3P2pWwEDDAjoZzgUg,23452 +click/_textwrap.py,sha256=10fQ64OcBUMuK7mFvh8363_uoOxPlRItZBmKzRJDgoY,1353 +click/_unicodefun.py,sha256=JKSh1oSwG_zbjAu4TBCa9tQde2P9FiYcf4MBfy5NdT8,3201 +click/_winconsole.py,sha256=5ju3jQkcZD0W27WEMGqmEP4y_crUVzPCqsX_FYb7BO0,7860 +click/core.py,sha256=k4PA2z0BT_dmed9I52Q2VLi8r6ekTMCtCQzw2y915Xs,111478 +click/decorators.py,sha256=sGkXJGmP7eLtjtmPl_Un2uBTlrhK8s2L22n-yBiDwTw,14864 +click/exceptions.py,sha256=7gDaLGuFZBeCNwY9ERMsF2-Z3R9Fvq09Zc6IZSKjseo,9167 +click/formatting.py,sha256=Frf0-5W33-loyY_i9qrwXR8-STnW3m5gvyxLVUdyxyk,9706 +click/globals.py,sha256=kGPzxq55Ug4dFUrgRV-5oHVPOPdLCUhmYolbrrVBo8g,1985 +click/parser.py,sha256=cAEt1uQR8gq3-S9ysqbVU-fdAZNvilxw4ReJ_T1OQMk,19044 +click/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +click/shell_completion.py,sha256=_hPI12T9Ex-y5a3WunWnlH0Gca2_urzXFXkDnt7G6Ow,18001 +click/termui.py,sha256=Rp2gFE8x7j8sEIoFMOcPmuqxJQVWWTrwEzyC14-sPAw,29006 +click/testing.py,sha256=kLR5Qcny1OlgxaGB3gweTr0gQe1yVlmgQRn2esA2Fz4,16020 +click/types.py,sha256=VoNZnIlRBAtRRgzavdqVnyfzY5y4U4qzVGI1UvvX1ls,35391 +click/utils.py,sha256=avYwX-3l2KkdJNUo8NmncZSoAdEmniQ_M5sdsSYloJ4,18759 diff --git a/venv/lib/python3.7/site-packages/click-8.0.3.dist-info/WHEEL b/venv/lib/python3.7/site-packages/click-8.0.3.dist-info/WHEEL new file mode 100644 index 0000000..5bad85f --- /dev/null +++ b/venv/lib/python3.7/site-packages/click-8.0.3.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/venv/lib/python3.7/site-packages/click-8.0.3.dist-info/top_level.txt b/venv/lib/python3.7/site-packages/click-8.0.3.dist-info/top_level.txt new file mode 100644 index 0000000..dca9a90 --- /dev/null +++ b/venv/lib/python3.7/site-packages/click-8.0.3.dist-info/top_level.txt @@ -0,0 +1 @@ +click diff --git a/venv/lib/python3.7/site-packages/click/__init__.py b/venv/lib/python3.7/site-packages/click/__init__.py new file mode 100644 index 0000000..a2ed5d1 --- /dev/null +++ b/venv/lib/python3.7/site-packages/click/__init__.py @@ -0,0 +1,75 @@ +""" +Click is a simple Python module inspired by the stdlib optparse to make +writing command line scripts fun. Unlike other modules, it's based +around a simple API that does not come with too much magic and is +composable. +""" +from .core import Argument as Argument +from .core import BaseCommand as BaseCommand +from .core import Command as Command +from .core import CommandCollection as CommandCollection +from .core import Context as Context +from .core import Group as Group +from .core import MultiCommand as MultiCommand +from .core import Option as Option +from .core import Parameter as Parameter +from .decorators import argument as argument +from .decorators import command as command +from .decorators import confirmation_option as confirmation_option +from .decorators import group as group +from .decorators import help_option as help_option +from .decorators import make_pass_decorator as make_pass_decorator +from .decorators import option as option +from .decorators import pass_context as pass_context +from .decorators import pass_obj as pass_obj +from .decorators import password_option as password_option +from .decorators import version_option as version_option +from .exceptions import Abort as Abort +from .exceptions import BadArgumentUsage as BadArgumentUsage +from .exceptions import BadOptionUsage as BadOptionUsage +from .exceptions import BadParameter as BadParameter +from .exceptions import ClickException as ClickException +from .exceptions import FileError as FileError +from .exceptions import MissingParameter as MissingParameter +from .exceptions import NoSuchOption as NoSuchOption +from .exceptions import UsageError as UsageError +from .formatting import HelpFormatter as HelpFormatter +from .formatting import wrap_text as wrap_text +from .globals import get_current_context as get_current_context +from .parser import OptionParser as OptionParser +from .termui import clear as clear +from .termui import confirm as confirm +from .termui import echo_via_pager as echo_via_pager +from .termui import edit as edit +from .termui import get_terminal_size as get_terminal_size +from .termui import getchar as getchar +from .termui import launch as launch +from .termui import pause as pause +from .termui import progressbar as progressbar +from .termui import prompt as prompt +from .termui import secho as secho +from .termui import style as style +from .termui import unstyle as unstyle +from .types import BOOL as BOOL +from .types import Choice as Choice +from .types import DateTime as DateTime +from .types import File as File +from .types import FLOAT as FLOAT +from .types import FloatRange as FloatRange +from .types import INT as INT +from .types import IntRange as IntRange +from .types import ParamType as ParamType +from .types import Path as Path +from .types import STRING as STRING +from .types import Tuple as Tuple +from .types import UNPROCESSED as UNPROCESSED +from .types import UUID as UUID +from .utils import echo as echo +from .utils import format_filename as format_filename +from .utils import get_app_dir as get_app_dir +from .utils import get_binary_stream as get_binary_stream +from .utils import get_os_args as get_os_args +from .utils import get_text_stream as get_text_stream +from .utils import open_file as open_file + +__version__ = "8.0.3" diff --git a/venv/lib/python3.7/site-packages/click/__pycache__/__init__.cpython-37.pyc b/venv/lib/python3.7/site-packages/click/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000..aece95f Binary files /dev/null and b/venv/lib/python3.7/site-packages/click/__pycache__/__init__.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/click/__pycache__/_compat.cpython-37.pyc b/venv/lib/python3.7/site-packages/click/__pycache__/_compat.cpython-37.pyc new file mode 100644 index 0000000..9f77952 Binary files /dev/null and b/venv/lib/python3.7/site-packages/click/__pycache__/_compat.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/click/__pycache__/_termui_impl.cpython-37.pyc b/venv/lib/python3.7/site-packages/click/__pycache__/_termui_impl.cpython-37.pyc new file mode 100644 index 0000000..8471c56 Binary files /dev/null and b/venv/lib/python3.7/site-packages/click/__pycache__/_termui_impl.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/click/__pycache__/_textwrap.cpython-37.pyc b/venv/lib/python3.7/site-packages/click/__pycache__/_textwrap.cpython-37.pyc new file mode 100644 index 0000000..cad63f4 Binary files /dev/null and b/venv/lib/python3.7/site-packages/click/__pycache__/_textwrap.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/click/__pycache__/_unicodefun.cpython-37.pyc b/venv/lib/python3.7/site-packages/click/__pycache__/_unicodefun.cpython-37.pyc new file mode 100644 index 0000000..014da5b Binary files /dev/null and b/venv/lib/python3.7/site-packages/click/__pycache__/_unicodefun.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/click/__pycache__/_winconsole.cpython-37.pyc b/venv/lib/python3.7/site-packages/click/__pycache__/_winconsole.cpython-37.pyc new file mode 100644 index 0000000..20ca660 Binary files /dev/null and b/venv/lib/python3.7/site-packages/click/__pycache__/_winconsole.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/click/__pycache__/core.cpython-37.pyc b/venv/lib/python3.7/site-packages/click/__pycache__/core.cpython-37.pyc new file mode 100644 index 0000000..3bdbb98 Binary files /dev/null and b/venv/lib/python3.7/site-packages/click/__pycache__/core.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/click/__pycache__/decorators.cpython-37.pyc b/venv/lib/python3.7/site-packages/click/__pycache__/decorators.cpython-37.pyc new file mode 100644 index 0000000..ade0553 Binary files /dev/null and b/venv/lib/python3.7/site-packages/click/__pycache__/decorators.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/click/__pycache__/exceptions.cpython-37.pyc b/venv/lib/python3.7/site-packages/click/__pycache__/exceptions.cpython-37.pyc new file mode 100644 index 0000000..31ad34b Binary files /dev/null and b/venv/lib/python3.7/site-packages/click/__pycache__/exceptions.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/click/__pycache__/formatting.cpython-37.pyc b/venv/lib/python3.7/site-packages/click/__pycache__/formatting.cpython-37.pyc new file mode 100644 index 0000000..2cdd685 Binary files /dev/null and b/venv/lib/python3.7/site-packages/click/__pycache__/formatting.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/click/__pycache__/globals.cpython-37.pyc b/venv/lib/python3.7/site-packages/click/__pycache__/globals.cpython-37.pyc new file mode 100644 index 0000000..69ab8a1 Binary files /dev/null and b/venv/lib/python3.7/site-packages/click/__pycache__/globals.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/click/__pycache__/parser.cpython-37.pyc b/venv/lib/python3.7/site-packages/click/__pycache__/parser.cpython-37.pyc new file mode 100644 index 0000000..ec34dfe Binary files /dev/null and b/venv/lib/python3.7/site-packages/click/__pycache__/parser.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/click/__pycache__/shell_completion.cpython-37.pyc b/venv/lib/python3.7/site-packages/click/__pycache__/shell_completion.cpython-37.pyc new file mode 100644 index 0000000..ef4172d Binary files /dev/null and b/venv/lib/python3.7/site-packages/click/__pycache__/shell_completion.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/click/__pycache__/termui.cpython-37.pyc b/venv/lib/python3.7/site-packages/click/__pycache__/termui.cpython-37.pyc new file mode 100644 index 0000000..ee902e4 Binary files /dev/null and b/venv/lib/python3.7/site-packages/click/__pycache__/termui.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/click/__pycache__/testing.cpython-37.pyc b/venv/lib/python3.7/site-packages/click/__pycache__/testing.cpython-37.pyc new file mode 100644 index 0000000..cdfd8c3 Binary files /dev/null and b/venv/lib/python3.7/site-packages/click/__pycache__/testing.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/click/__pycache__/types.cpython-37.pyc b/venv/lib/python3.7/site-packages/click/__pycache__/types.cpython-37.pyc new file mode 100644 index 0000000..d8900a6 Binary files /dev/null and b/venv/lib/python3.7/site-packages/click/__pycache__/types.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/click/__pycache__/utils.cpython-37.pyc b/venv/lib/python3.7/site-packages/click/__pycache__/utils.cpython-37.pyc new file mode 100644 index 0000000..e6374fb Binary files /dev/null and b/venv/lib/python3.7/site-packages/click/__pycache__/utils.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/click/_compat.py b/venv/lib/python3.7/site-packages/click/_compat.py new file mode 100644 index 0000000..b9e1f0d --- /dev/null +++ b/venv/lib/python3.7/site-packages/click/_compat.py @@ -0,0 +1,627 @@ +import codecs +import io +import os +import re +import sys +import typing as t +from weakref import WeakKeyDictionary + +CYGWIN = sys.platform.startswith("cygwin") +MSYS2 = sys.platform.startswith("win") and ("GCC" in sys.version) +# Determine local App Engine environment, per Google's own suggestion +APP_ENGINE = "APPENGINE_RUNTIME" in os.environ and "Development/" in os.environ.get( + "SERVER_SOFTWARE", "" +) +WIN = sys.platform.startswith("win") and not APP_ENGINE and not MSYS2 +auto_wrap_for_ansi: t.Optional[t.Callable[[t.TextIO], t.TextIO]] = None +_ansi_re = re.compile(r"\033\[[;?0-9]*[a-zA-Z]") + + +def get_filesystem_encoding() -> str: + return sys.getfilesystemencoding() or sys.getdefaultencoding() + + +def _make_text_stream( + stream: t.BinaryIO, + encoding: t.Optional[str], + errors: t.Optional[str], + force_readable: bool = False, + force_writable: bool = False, +) -> t.TextIO: + if encoding is None: + encoding = get_best_encoding(stream) + if errors is None: + errors = "replace" + return _NonClosingTextIOWrapper( + stream, + encoding, + errors, + line_buffering=True, + force_readable=force_readable, + force_writable=force_writable, + ) + + +def is_ascii_encoding(encoding: str) -> bool: + """Checks if a given encoding is ascii.""" + try: + return codecs.lookup(encoding).name == "ascii" + except LookupError: + return False + + +def get_best_encoding(stream: t.IO) -> str: + """Returns the default stream encoding if not found.""" + rv = getattr(stream, "encoding", None) or sys.getdefaultencoding() + if is_ascii_encoding(rv): + return "utf-8" + return rv + + +class _NonClosingTextIOWrapper(io.TextIOWrapper): + def __init__( + self, + stream: t.BinaryIO, + encoding: t.Optional[str], + errors: t.Optional[str], + force_readable: bool = False, + force_writable: bool = False, + **extra: t.Any, + ) -> None: + self._stream = stream = t.cast( + t.BinaryIO, _FixupStream(stream, force_readable, force_writable) + ) + super().__init__(stream, encoding, errors, **extra) + + def __del__(self) -> None: + try: + self.detach() + except Exception: + pass + + def isatty(self) -> bool: + # https://bitbucket.org/pypy/pypy/issue/1803 + return self._stream.isatty() + + +class _FixupStream: + """The new io interface needs more from streams than streams + traditionally implement. As such, this fix-up code is necessary in + some circumstances. + + The forcing of readable and writable flags are there because some tools + put badly patched objects on sys (one such offender are certain version + of jupyter notebook). + """ + + def __init__( + self, + stream: t.BinaryIO, + force_readable: bool = False, + force_writable: bool = False, + ): + self._stream = stream + self._force_readable = force_readable + self._force_writable = force_writable + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._stream, name) + + def read1(self, size: int) -> bytes: + f = getattr(self._stream, "read1", None) + + if f is not None: + return t.cast(bytes, f(size)) + + return self._stream.read(size) + + def readable(self) -> bool: + if self._force_readable: + return True + x = getattr(self._stream, "readable", None) + if x is not None: + return t.cast(bool, x()) + try: + self._stream.read(0) + except Exception: + return False + return True + + def writable(self) -> bool: + if self._force_writable: + return True + x = getattr(self._stream, "writable", None) + if x is not None: + return t.cast(bool, x()) + try: + self._stream.write("") # type: ignore + except Exception: + try: + self._stream.write(b"") + except Exception: + return False + return True + + def seekable(self) -> bool: + x = getattr(self._stream, "seekable", None) + if x is not None: + return t.cast(bool, x()) + try: + self._stream.seek(self._stream.tell()) + except Exception: + return False + return True + + +def _is_binary_reader(stream: t.IO, default: bool = False) -> bool: + try: + return isinstance(stream.read(0), bytes) + except Exception: + return default + # This happens in some cases where the stream was already + # closed. In this case, we assume the default. + + +def _is_binary_writer(stream: t.IO, default: bool = False) -> bool: + try: + stream.write(b"") + except Exception: + try: + stream.write("") + return False + except Exception: + pass + return default + return True + + +def _find_binary_reader(stream: t.IO) -> t.Optional[t.BinaryIO]: + # We need to figure out if the given stream is already binary. + # This can happen because the official docs recommend detaching + # the streams to get binary streams. Some code might do this, so + # we need to deal with this case explicitly. + if _is_binary_reader(stream, False): + return t.cast(t.BinaryIO, stream) + + buf = getattr(stream, "buffer", None) + + # Same situation here; this time we assume that the buffer is + # actually binary in case it's closed. + if buf is not None and _is_binary_reader(buf, True): + return t.cast(t.BinaryIO, buf) + + return None + + +def _find_binary_writer(stream: t.IO) -> t.Optional[t.BinaryIO]: + # We need to figure out if the given stream is already binary. + # This can happen because the official docs recommend detaching + # the streams to get binary streams. Some code might do this, so + # we need to deal with this case explicitly. + if _is_binary_writer(stream, False): + return t.cast(t.BinaryIO, stream) + + buf = getattr(stream, "buffer", None) + + # Same situation here; this time we assume that the buffer is + # actually binary in case it's closed. + if buf is not None and _is_binary_writer(buf, True): + return t.cast(t.BinaryIO, buf) + + return None + + +def _stream_is_misconfigured(stream: t.TextIO) -> bool: + """A stream is misconfigured if its encoding is ASCII.""" + # If the stream does not have an encoding set, we assume it's set + # to ASCII. This appears to happen in certain unittest + # environments. It's not quite clear what the correct behavior is + # but this at least will force Click to recover somehow. + return is_ascii_encoding(getattr(stream, "encoding", None) or "ascii") + + +def _is_compat_stream_attr(stream: t.TextIO, attr: str, value: t.Optional[str]) -> bool: + """A stream attribute is compatible if it is equal to the + desired value or the desired value is unset and the attribute + has a value. + """ + stream_value = getattr(stream, attr, None) + return stream_value == value or (value is None and stream_value is not None) + + +def _is_compatible_text_stream( + stream: t.TextIO, encoding: t.Optional[str], errors: t.Optional[str] +) -> bool: + """Check if a stream's encoding and errors attributes are + compatible with the desired values. + """ + return _is_compat_stream_attr( + stream, "encoding", encoding + ) and _is_compat_stream_attr(stream, "errors", errors) + + +def _force_correct_text_stream( + text_stream: t.IO, + encoding: t.Optional[str], + errors: t.Optional[str], + is_binary: t.Callable[[t.IO, bool], bool], + find_binary: t.Callable[[t.IO], t.Optional[t.BinaryIO]], + force_readable: bool = False, + force_writable: bool = False, +) -> t.TextIO: + if is_binary(text_stream, False): + binary_reader = t.cast(t.BinaryIO, text_stream) + else: + text_stream = t.cast(t.TextIO, text_stream) + # If the stream looks compatible, and won't default to a + # misconfigured ascii encoding, return it as-is. + if _is_compatible_text_stream(text_stream, encoding, errors) and not ( + encoding is None and _stream_is_misconfigured(text_stream) + ): + return text_stream + + # Otherwise, get the underlying binary reader. + possible_binary_reader = find_binary(text_stream) + + # If that's not possible, silently use the original reader + # and get mojibake instead of exceptions. + if possible_binary_reader is None: + return text_stream + + binary_reader = possible_binary_reader + + # Default errors to replace instead of strict in order to get + # something that works. + if errors is None: + errors = "replace" + + # Wrap the binary stream in a text stream with the correct + # encoding parameters. + return _make_text_stream( + binary_reader, + encoding, + errors, + force_readable=force_readable, + force_writable=force_writable, + ) + + +def _force_correct_text_reader( + text_reader: t.IO, + encoding: t.Optional[str], + errors: t.Optional[str], + force_readable: bool = False, +) -> t.TextIO: + return _force_correct_text_stream( + text_reader, + encoding, + errors, + _is_binary_reader, + _find_binary_reader, + force_readable=force_readable, + ) + + +def _force_correct_text_writer( + text_writer: t.IO, + encoding: t.Optional[str], + errors: t.Optional[str], + force_writable: bool = False, +) -> t.TextIO: + return _force_correct_text_stream( + text_writer, + encoding, + errors, + _is_binary_writer, + _find_binary_writer, + force_writable=force_writable, + ) + + +def get_binary_stdin() -> t.BinaryIO: + reader = _find_binary_reader(sys.stdin) + if reader is None: + raise RuntimeError("Was not able to determine binary stream for sys.stdin.") + return reader + + +def get_binary_stdout() -> t.BinaryIO: + writer = _find_binary_writer(sys.stdout) + if writer is None: + raise RuntimeError("Was not able to determine binary stream for sys.stdout.") + return writer + + +def get_binary_stderr() -> t.BinaryIO: + writer = _find_binary_writer(sys.stderr) + if writer is None: + raise RuntimeError("Was not able to determine binary stream for sys.stderr.") + return writer + + +def get_text_stdin( + encoding: t.Optional[str] = None, errors: t.Optional[str] = None +) -> t.TextIO: + rv = _get_windows_console_stream(sys.stdin, encoding, errors) + if rv is not None: + return rv + return _force_correct_text_reader(sys.stdin, encoding, errors, force_readable=True) + + +def get_text_stdout( + encoding: t.Optional[str] = None, errors: t.Optional[str] = None +) -> t.TextIO: + rv = _get_windows_console_stream(sys.stdout, encoding, errors) + if rv is not None: + return rv + return _force_correct_text_writer(sys.stdout, encoding, errors, force_writable=True) + + +def get_text_stderr( + encoding: t.Optional[str] = None, errors: t.Optional[str] = None +) -> t.TextIO: + rv = _get_windows_console_stream(sys.stderr, encoding, errors) + if rv is not None: + return rv + return _force_correct_text_writer(sys.stderr, encoding, errors, force_writable=True) + + +def _wrap_io_open( + file: t.Union[str, os.PathLike, int], + mode: str, + encoding: t.Optional[str], + errors: t.Optional[str], +) -> t.IO: + """Handles not passing ``encoding`` and ``errors`` in binary mode.""" + if "b" in mode: + return open(file, mode) + + return open(file, mode, encoding=encoding, errors=errors) + + +def open_stream( + filename: str, + mode: str = "r", + encoding: t.Optional[str] = None, + errors: t.Optional[str] = "strict", + atomic: bool = False, +) -> t.Tuple[t.IO, bool]: + binary = "b" in mode + + # Standard streams first. These are simple because they don't need + # special handling for the atomic flag. It's entirely ignored. + if filename == "-": + if any(m in mode for m in ["w", "a", "x"]): + if binary: + return get_binary_stdout(), False + return get_text_stdout(encoding=encoding, errors=errors), False + if binary: + return get_binary_stdin(), False + return get_text_stdin(encoding=encoding, errors=errors), False + + # Non-atomic writes directly go out through the regular open functions. + if not atomic: + return _wrap_io_open(filename, mode, encoding, errors), True + + # Some usability stuff for atomic writes + if "a" in mode: + raise ValueError( + "Appending to an existing file is not supported, because that" + " would involve an expensive `copy`-operation to a temporary" + " file. Open the file in normal `w`-mode and copy explicitly" + " if that's what you're after." + ) + if "x" in mode: + raise ValueError("Use the `overwrite`-parameter instead.") + if "w" not in mode: + raise ValueError("Atomic writes only make sense with `w`-mode.") + + # Atomic writes are more complicated. They work by opening a file + # as a proxy in the same folder and then using the fdopen + # functionality to wrap it in a Python file. Then we wrap it in an + # atomic file that moves the file over on close. + import errno + import random + + try: + perm: t.Optional[int] = os.stat(filename).st_mode + except OSError: + perm = None + + flags = os.O_RDWR | os.O_CREAT | os.O_EXCL + + if binary: + flags |= getattr(os, "O_BINARY", 0) + + while True: + tmp_filename = os.path.join( + os.path.dirname(filename), + f".__atomic-write{random.randrange(1 << 32):08x}", + ) + try: + fd = os.open(tmp_filename, flags, 0o666 if perm is None else perm) + break + except OSError as e: + if e.errno == errno.EEXIST or ( + os.name == "nt" + and e.errno == errno.EACCES + and os.path.isdir(e.filename) + and os.access(e.filename, os.W_OK) + ): + continue + raise + + if perm is not None: + os.chmod(tmp_filename, perm) # in case perm includes bits in umask + + f = _wrap_io_open(fd, mode, encoding, errors) + af = _AtomicFile(f, tmp_filename, os.path.realpath(filename)) + return t.cast(t.IO, af), True + + +class _AtomicFile: + def __init__(self, f: t.IO, tmp_filename: str, real_filename: str) -> None: + self._f = f + self._tmp_filename = tmp_filename + self._real_filename = real_filename + self.closed = False + + @property + def name(self) -> str: + return self._real_filename + + def close(self, delete: bool = False) -> None: + if self.closed: + return + self._f.close() + os.replace(self._tmp_filename, self._real_filename) + self.closed = True + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._f, name) + + def __enter__(self) -> "_AtomicFile": + return self + + def __exit__(self, exc_type, exc_value, tb): # type: ignore + self.close(delete=exc_type is not None) + + def __repr__(self) -> str: + return repr(self._f) + + +def strip_ansi(value: str) -> str: + return _ansi_re.sub("", value) + + +def _is_jupyter_kernel_output(stream: t.IO) -> bool: + while isinstance(stream, (_FixupStream, _NonClosingTextIOWrapper)): + stream = stream._stream + + return stream.__class__.__module__.startswith("ipykernel.") + + +def should_strip_ansi( + stream: t.Optional[t.IO] = None, color: t.Optional[bool] = None +) -> bool: + if color is None: + if stream is None: + stream = sys.stdin + return not isatty(stream) and not _is_jupyter_kernel_output(stream) + return not color + + +# On Windows, wrap the output streams with colorama to support ANSI +# color codes. +# NOTE: double check is needed so mypy does not analyze this on Linux +if sys.platform.startswith("win") and WIN: + from ._winconsole import _get_windows_console_stream + + def _get_argv_encoding() -> str: + import locale + + return locale.getpreferredencoding() + + _ansi_stream_wrappers: t.MutableMapping[t.TextIO, t.TextIO] = WeakKeyDictionary() + + def auto_wrap_for_ansi( + stream: t.TextIO, color: t.Optional[bool] = None + ) -> t.TextIO: + """Support ANSI color and style codes on Windows by wrapping a + stream with colorama. + """ + try: + cached = _ansi_stream_wrappers.get(stream) + except Exception: + cached = None + + if cached is not None: + return cached + + import colorama + + strip = should_strip_ansi(stream, color) + ansi_wrapper = colorama.AnsiToWin32(stream, strip=strip) + rv = t.cast(t.TextIO, ansi_wrapper.stream) + _write = rv.write + + def _safe_write(s): + try: + return _write(s) + except BaseException: + ansi_wrapper.reset_all() + raise + + rv.write = _safe_write + + try: + _ansi_stream_wrappers[stream] = rv + except Exception: + pass + + return rv + + +else: + + def _get_argv_encoding() -> str: + return getattr(sys.stdin, "encoding", None) or get_filesystem_encoding() + + def _get_windows_console_stream( + f: t.TextIO, encoding: t.Optional[str], errors: t.Optional[str] + ) -> t.Optional[t.TextIO]: + return None + + +def term_len(x: str) -> int: + return len(strip_ansi(x)) + + +def isatty(stream: t.IO) -> bool: + try: + return stream.isatty() + except Exception: + return False + + +def _make_cached_stream_func( + src_func: t.Callable[[], t.TextIO], wrapper_func: t.Callable[[], t.TextIO] +) -> t.Callable[[], t.TextIO]: + cache: t.MutableMapping[t.TextIO, t.TextIO] = WeakKeyDictionary() + + def func() -> t.TextIO: + stream = src_func() + try: + rv = cache.get(stream) + except Exception: + rv = None + if rv is not None: + return rv + rv = wrapper_func() + try: + cache[stream] = rv + except Exception: + pass + return rv + + return func + + +_default_text_stdin = _make_cached_stream_func(lambda: sys.stdin, get_text_stdin) +_default_text_stdout = _make_cached_stream_func(lambda: sys.stdout, get_text_stdout) +_default_text_stderr = _make_cached_stream_func(lambda: sys.stderr, get_text_stderr) + + +binary_streams: t.Mapping[str, t.Callable[[], t.BinaryIO]] = { + "stdin": get_binary_stdin, + "stdout": get_binary_stdout, + "stderr": get_binary_stderr, +} + +text_streams: t.Mapping[ + str, t.Callable[[t.Optional[str], t.Optional[str]], t.TextIO] +] = { + "stdin": get_text_stdin, + "stdout": get_text_stdout, + "stderr": get_text_stderr, +} diff --git a/venv/lib/python3.7/site-packages/click/_termui_impl.py b/venv/lib/python3.7/site-packages/click/_termui_impl.py new file mode 100644 index 0000000..39c1d08 --- /dev/null +++ b/venv/lib/python3.7/site-packages/click/_termui_impl.py @@ -0,0 +1,718 @@ +""" +This module contains implementations for the termui module. To keep the +import time of Click down, some infrequently used functionality is +placed in this module and only imported as needed. +""" +import contextlib +import math +import os +import sys +import time +import typing as t +from gettext import gettext as _ + +from ._compat import _default_text_stdout +from ._compat import CYGWIN +from ._compat import get_best_encoding +from ._compat import isatty +from ._compat import open_stream +from ._compat import strip_ansi +from ._compat import term_len +from ._compat import WIN +from .exceptions import ClickException +from .utils import echo + +V = t.TypeVar("V") + +if os.name == "nt": + BEFORE_BAR = "\r" + AFTER_BAR = "\n" +else: + BEFORE_BAR = "\r\033[?25l" + AFTER_BAR = "\033[?25h\n" + + +class ProgressBar(t.Generic[V]): + def __init__( + self, + iterable: t.Optional[t.Iterable[V]], + length: t.Optional[int] = None, + fill_char: str = "#", + empty_char: str = " ", + bar_template: str = "%(bar)s", + info_sep: str = " ", + show_eta: bool = True, + show_percent: t.Optional[bool] = None, + show_pos: bool = False, + item_show_func: t.Optional[t.Callable[[t.Optional[V]], t.Optional[str]]] = None, + label: t.Optional[str] = None, + file: t.Optional[t.TextIO] = None, + color: t.Optional[bool] = None, + update_min_steps: int = 1, + width: int = 30, + ) -> None: + self.fill_char = fill_char + self.empty_char = empty_char + self.bar_template = bar_template + self.info_sep = info_sep + self.show_eta = show_eta + self.show_percent = show_percent + self.show_pos = show_pos + self.item_show_func = item_show_func + self.label = label or "" + if file is None: + file = _default_text_stdout() + self.file = file + self.color = color + self.update_min_steps = update_min_steps + self._completed_intervals = 0 + self.width = width + self.autowidth = width == 0 + + if length is None: + from operator import length_hint + + length = length_hint(iterable, -1) + + if length == -1: + length = None + if iterable is None: + if length is None: + raise TypeError("iterable or length is required") + iterable = t.cast(t.Iterable[V], range(length)) + self.iter = iter(iterable) + self.length = length + self.pos = 0 + self.avg: t.List[float] = [] + self.start = self.last_eta = time.time() + self.eta_known = False + self.finished = False + self.max_width: t.Optional[int] = None + self.entered = False + self.current_item: t.Optional[V] = None + self.is_hidden = not isatty(self.file) + self._last_line: t.Optional[str] = None + + def __enter__(self) -> "ProgressBar": + self.entered = True + self.render_progress() + return self + + def __exit__(self, exc_type, exc_value, tb): # type: ignore + self.render_finish() + + def __iter__(self) -> t.Iterator[V]: + if not self.entered: + raise RuntimeError("You need to use progress bars in a with block.") + self.render_progress() + return self.generator() + + def __next__(self) -> V: + # Iteration is defined in terms of a generator function, + # returned by iter(self); use that to define next(). This works + # because `self.iter` is an iterable consumed by that generator, + # so it is re-entry safe. Calling `next(self.generator())` + # twice works and does "what you want". + return next(iter(self)) + + def render_finish(self) -> None: + if self.is_hidden: + return + self.file.write(AFTER_BAR) + self.file.flush() + + @property + def pct(self) -> float: + if self.finished: + return 1.0 + return min(self.pos / (float(self.length or 1) or 1), 1.0) + + @property + def time_per_iteration(self) -> float: + if not self.avg: + return 0.0 + return sum(self.avg) / float(len(self.avg)) + + @property + def eta(self) -> float: + if self.length is not None and not self.finished: + return self.time_per_iteration * (self.length - self.pos) + return 0.0 + + def format_eta(self) -> str: + if self.eta_known: + t = int(self.eta) + seconds = t % 60 + t //= 60 + minutes = t % 60 + t //= 60 + hours = t % 24 + t //= 24 + if t > 0: + return f"{t}d {hours:02}:{minutes:02}:{seconds:02}" + else: + return f"{hours:02}:{minutes:02}:{seconds:02}" + return "" + + def format_pos(self) -> str: + pos = str(self.pos) + if self.length is not None: + pos += f"/{self.length}" + return pos + + def format_pct(self) -> str: + return f"{int(self.pct * 100): 4}%"[1:] + + def format_bar(self) -> str: + if self.length is not None: + bar_length = int(self.pct * self.width) + bar = self.fill_char * bar_length + bar += self.empty_char * (self.width - bar_length) + elif self.finished: + bar = self.fill_char * self.width + else: + chars = list(self.empty_char * (self.width or 1)) + if self.time_per_iteration != 0: + chars[ + int( + (math.cos(self.pos * self.time_per_iteration) / 2.0 + 0.5) + * self.width + ) + ] = self.fill_char + bar = "".join(chars) + return bar + + def format_progress_line(self) -> str: + show_percent = self.show_percent + + info_bits = [] + if self.length is not None and show_percent is None: + show_percent = not self.show_pos + + if self.show_pos: + info_bits.append(self.format_pos()) + if show_percent: + info_bits.append(self.format_pct()) + if self.show_eta and self.eta_known and not self.finished: + info_bits.append(self.format_eta()) + if self.item_show_func is not None: + item_info = self.item_show_func(self.current_item) + if item_info is not None: + info_bits.append(item_info) + + return ( + self.bar_template + % { + "label": self.label, + "bar": self.format_bar(), + "info": self.info_sep.join(info_bits), + } + ).rstrip() + + def render_progress(self) -> None: + import shutil + + if self.is_hidden: + # Only output the label as it changes if the output is not a + # TTY. Use file=stderr if you expect to be piping stdout. + if self._last_line != self.label: + self._last_line = self.label + echo(self.label, file=self.file, color=self.color) + + return + + buf = [] + # Update width in case the terminal has been resized + if self.autowidth: + old_width = self.width + self.width = 0 + clutter_length = term_len(self.format_progress_line()) + new_width = max(0, shutil.get_terminal_size().columns - clutter_length) + if new_width < old_width: + buf.append(BEFORE_BAR) + buf.append(" " * self.max_width) # type: ignore + self.max_width = new_width + self.width = new_width + + clear_width = self.width + if self.max_width is not None: + clear_width = self.max_width + + buf.append(BEFORE_BAR) + line = self.format_progress_line() + line_len = term_len(line) + if self.max_width is None or self.max_width < line_len: + self.max_width = line_len + + buf.append(line) + buf.append(" " * (clear_width - line_len)) + line = "".join(buf) + # Render the line only if it changed. + + if line != self._last_line: + self._last_line = line + echo(line, file=self.file, color=self.color, nl=False) + self.file.flush() + + def make_step(self, n_steps: int) -> None: + self.pos += n_steps + if self.length is not None and self.pos >= self.length: + self.finished = True + + if (time.time() - self.last_eta) < 1.0: + return + + self.last_eta = time.time() + + # self.avg is a rolling list of length <= 7 of steps where steps are + # defined as time elapsed divided by the total progress through + # self.length. + if self.pos: + step = (time.time() - self.start) / self.pos + else: + step = time.time() - self.start + + self.avg = self.avg[-6:] + [step] + + self.eta_known = self.length is not None + + def update(self, n_steps: int, current_item: t.Optional[V] = None) -> None: + """Update the progress bar by advancing a specified number of + steps, and optionally set the ``current_item`` for this new + position. + + :param n_steps: Number of steps to advance. + :param current_item: Optional item to set as ``current_item`` + for the updated position. + + .. versionchanged:: 8.0 + Added the ``current_item`` optional parameter. + + .. versionchanged:: 8.0 + Only render when the number of steps meets the + ``update_min_steps`` threshold. + """ + if current_item is not None: + self.current_item = current_item + + self._completed_intervals += n_steps + + if self._completed_intervals >= self.update_min_steps: + self.make_step(self._completed_intervals) + self.render_progress() + self._completed_intervals = 0 + + def finish(self) -> None: + self.eta_known = False + self.current_item = None + self.finished = True + + def generator(self) -> t.Iterator[V]: + """Return a generator which yields the items added to the bar + during construction, and updates the progress bar *after* the + yielded block returns. + """ + # WARNING: the iterator interface for `ProgressBar` relies on + # this and only works because this is a simple generator which + # doesn't create or manage additional state. If this function + # changes, the impact should be evaluated both against + # `iter(bar)` and `next(bar)`. `next()` in particular may call + # `self.generator()` repeatedly, and this must remain safe in + # order for that interface to work. + if not self.entered: + raise RuntimeError("You need to use progress bars in a with block.") + + if self.is_hidden: + yield from self.iter + else: + for rv in self.iter: + self.current_item = rv + + # This allows show_item_func to be updated before the + # item is processed. Only trigger at the beginning of + # the update interval. + if self._completed_intervals == 0: + self.render_progress() + + yield rv + self.update(1) + + self.finish() + self.render_progress() + + +def pager(generator: t.Iterable[str], color: t.Optional[bool] = None) -> None: + """Decide what method to use for paging through text.""" + stdout = _default_text_stdout() + if not isatty(sys.stdin) or not isatty(stdout): + return _nullpager(stdout, generator, color) + pager_cmd = (os.environ.get("PAGER", None) or "").strip() + if pager_cmd: + if WIN: + return _tempfilepager(generator, pager_cmd, color) + return _pipepager(generator, pager_cmd, color) + if os.environ.get("TERM") in ("dumb", "emacs"): + return _nullpager(stdout, generator, color) + if WIN or sys.platform.startswith("os2"): + return _tempfilepager(generator, "more <", color) + if hasattr(os, "system") and os.system("(less) 2>/dev/null") == 0: + return _pipepager(generator, "less", color) + + import tempfile + + fd, filename = tempfile.mkstemp() + os.close(fd) + try: + if hasattr(os, "system") and os.system(f'more "{filename}"') == 0: + return _pipepager(generator, "more", color) + return _nullpager(stdout, generator, color) + finally: + os.unlink(filename) + + +def _pipepager(generator: t.Iterable[str], cmd: str, color: t.Optional[bool]) -> None: + """Page through text by feeding it to another program. Invoking a + pager through this might support colors. + """ + import subprocess + + env = dict(os.environ) + + # If we're piping to less we might support colors under the + # condition that + cmd_detail = cmd.rsplit("/", 1)[-1].split() + if color is None and cmd_detail[0] == "less": + less_flags = f"{os.environ.get('LESS', '')}{' '.join(cmd_detail[1:])}" + if not less_flags: + env["LESS"] = "-R" + color = True + elif "r" in less_flags or "R" in less_flags: + color = True + + c = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, env=env) + stdin = t.cast(t.BinaryIO, c.stdin) + encoding = get_best_encoding(stdin) + try: + for text in generator: + if not color: + text = strip_ansi(text) + + stdin.write(text.encode(encoding, "replace")) + except (OSError, KeyboardInterrupt): + pass + else: + stdin.close() + + # Less doesn't respect ^C, but catches it for its own UI purposes (aborting + # search or other commands inside less). + # + # That means when the user hits ^C, the parent process (click) terminates, + # but less is still alive, paging the output and messing up the terminal. + # + # If the user wants to make the pager exit on ^C, they should set + # `LESS='-K'`. It's not our decision to make. + while True: + try: + c.wait() + except KeyboardInterrupt: + pass + else: + break + + +def _tempfilepager( + generator: t.Iterable[str], cmd: str, color: t.Optional[bool] +) -> None: + """Page through text by invoking a program on a temporary file.""" + import tempfile + + fd, filename = tempfile.mkstemp() + # TODO: This never terminates if the passed generator never terminates. + text = "".join(generator) + if not color: + text = strip_ansi(text) + encoding = get_best_encoding(sys.stdout) + with open_stream(filename, "wb")[0] as f: + f.write(text.encode(encoding)) + try: + os.system(f'{cmd} "{filename}"') + finally: + os.close(fd) + os.unlink(filename) + + +def _nullpager( + stream: t.TextIO, generator: t.Iterable[str], color: t.Optional[bool] +) -> None: + """Simply print unformatted text. This is the ultimate fallback.""" + for text in generator: + if not color: + text = strip_ansi(text) + stream.write(text) + + +class Editor: + def __init__( + self, + editor: t.Optional[str] = None, + env: t.Optional[t.Mapping[str, str]] = None, + require_save: bool = True, + extension: str = ".txt", + ) -> None: + self.editor = editor + self.env = env + self.require_save = require_save + self.extension = extension + + def get_editor(self) -> str: + if self.editor is not None: + return self.editor + for key in "VISUAL", "EDITOR": + rv = os.environ.get(key) + if rv: + return rv + if WIN: + return "notepad" + for editor in "sensible-editor", "vim", "nano": + if os.system(f"which {editor} >/dev/null 2>&1") == 0: + return editor + return "vi" + + def edit_file(self, filename: str) -> None: + import subprocess + + editor = self.get_editor() + environ: t.Optional[t.Dict[str, str]] = None + + if self.env: + environ = os.environ.copy() + environ.update(self.env) + + try: + c = subprocess.Popen(f'{editor} "{filename}"', env=environ, shell=True) + exit_code = c.wait() + if exit_code != 0: + raise ClickException( + _("{editor}: Editing failed").format(editor=editor) + ) + except OSError as e: + raise ClickException( + _("{editor}: Editing failed: {e}").format(editor=editor, e=e) + ) from e + + def edit(self, text: t.Optional[t.AnyStr]) -> t.Optional[t.AnyStr]: + import tempfile + + if not text: + data = b"" + elif isinstance(text, (bytes, bytearray)): + data = text + else: + if text and not text.endswith("\n"): + text += "\n" + + if WIN: + data = text.replace("\n", "\r\n").encode("utf-8-sig") + else: + data = text.encode("utf-8") + + fd, name = tempfile.mkstemp(prefix="editor-", suffix=self.extension) + f: t.BinaryIO + + try: + with os.fdopen(fd, "wb") as f: + f.write(data) + + # If the filesystem resolution is 1 second, like Mac OS + # 10.12 Extended, or 2 seconds, like FAT32, and the editor + # closes very fast, require_save can fail. Set the modified + # time to be 2 seconds in the past to work around this. + os.utime(name, (os.path.getatime(name), os.path.getmtime(name) - 2)) + # Depending on the resolution, the exact value might not be + # recorded, so get the new recorded value. + timestamp = os.path.getmtime(name) + + self.edit_file(name) + + if self.require_save and os.path.getmtime(name) == timestamp: + return None + + with open(name, "rb") as f: + rv = f.read() + + if isinstance(text, (bytes, bytearray)): + return rv + + return rv.decode("utf-8-sig").replace("\r\n", "\n") # type: ignore + finally: + os.unlink(name) + + +def open_url(url: str, wait: bool = False, locate: bool = False) -> int: + import subprocess + + def _unquote_file(url: str) -> str: + from urllib.parse import unquote + + if url.startswith("file://"): + url = unquote(url[7:]) + + return url + + if sys.platform == "darwin": + args = ["open"] + if wait: + args.append("-W") + if locate: + args.append("-R") + args.append(_unquote_file(url)) + null = open("/dev/null", "w") + try: + return subprocess.Popen(args, stderr=null).wait() + finally: + null.close() + elif WIN: + if locate: + url = _unquote_file(url.replace('"', "")) + args = f'explorer /select,"{url}"' + else: + url = url.replace('"', "") + wait_str = "/WAIT" if wait else "" + args = f'start {wait_str} "" "{url}"' + return os.system(args) + elif CYGWIN: + if locate: + url = os.path.dirname(_unquote_file(url).replace('"', "")) + args = f'cygstart "{url}"' + else: + url = url.replace('"', "") + wait_str = "-w" if wait else "" + args = f'cygstart {wait_str} "{url}"' + return os.system(args) + + try: + if locate: + url = os.path.dirname(_unquote_file(url)) or "." + else: + url = _unquote_file(url) + c = subprocess.Popen(["xdg-open", url]) + if wait: + return c.wait() + return 0 + except OSError: + if url.startswith(("http://", "https://")) and not locate and not wait: + import webbrowser + + webbrowser.open(url) + return 0 + return 1 + + +def _translate_ch_to_exc(ch: str) -> t.Optional[BaseException]: + if ch == "\x03": + raise KeyboardInterrupt() + + if ch == "\x04" and not WIN: # Unix-like, Ctrl+D + raise EOFError() + + if ch == "\x1a" and WIN: # Windows, Ctrl+Z + raise EOFError() + + return None + + +if WIN: + import msvcrt + + @contextlib.contextmanager + def raw_terminal() -> t.Iterator[int]: + yield -1 + + def getchar(echo: bool) -> str: + # The function `getch` will return a bytes object corresponding to + # the pressed character. Since Windows 10 build 1803, it will also + # return \x00 when called a second time after pressing a regular key. + # + # `getwch` does not share this probably-bugged behavior. Moreover, it + # returns a Unicode object by default, which is what we want. + # + # Either of these functions will return \x00 or \xe0 to indicate + # a special key, and you need to call the same function again to get + # the "rest" of the code. The fun part is that \u00e0 is + # "latin small letter a with grave", so if you type that on a French + # keyboard, you _also_ get a \xe0. + # E.g., consider the Up arrow. This returns \xe0 and then \x48. The + # resulting Unicode string reads as "a with grave" + "capital H". + # This is indistinguishable from when the user actually types + # "a with grave" and then "capital H". + # + # When \xe0 is returned, we assume it's part of a special-key sequence + # and call `getwch` again, but that means that when the user types + # the \u00e0 character, `getchar` doesn't return until a second + # character is typed. + # The alternative is returning immediately, but that would mess up + # cross-platform handling of arrow keys and others that start with + # \xe0. Another option is using `getch`, but then we can't reliably + # read non-ASCII characters, because return values of `getch` are + # limited to the current 8-bit codepage. + # + # Anyway, Click doesn't claim to do this Right(tm), and using `getwch` + # is doing the right thing in more situations than with `getch`. + func: t.Callable[[], str] + + if echo: + func = msvcrt.getwche # type: ignore + else: + func = msvcrt.getwch # type: ignore + + rv = func() + + if rv in ("\x00", "\xe0"): + # \x00 and \xe0 are control characters that indicate special key, + # see above. + rv += func() + + _translate_ch_to_exc(rv) + return rv + + +else: + import tty + import termios + + @contextlib.contextmanager + def raw_terminal() -> t.Iterator[int]: + f: t.Optional[t.TextIO] + fd: int + + if not isatty(sys.stdin): + f = open("/dev/tty") + fd = f.fileno() + else: + fd = sys.stdin.fileno() + f = None + + try: + old_settings = termios.tcgetattr(fd) + + try: + tty.setraw(fd) + yield fd + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + sys.stdout.flush() + + if f is not None: + f.close() + except termios.error: + pass + + def getchar(echo: bool) -> str: + with raw_terminal() as fd: + ch = os.read(fd, 32).decode(get_best_encoding(sys.stdin), "replace") + + if echo and isatty(sys.stdout): + sys.stdout.write(ch) + + _translate_ch_to_exc(ch) + return ch diff --git a/venv/lib/python3.7/site-packages/click/_textwrap.py b/venv/lib/python3.7/site-packages/click/_textwrap.py new file mode 100644 index 0000000..b47dcbd --- /dev/null +++ b/venv/lib/python3.7/site-packages/click/_textwrap.py @@ -0,0 +1,49 @@ +import textwrap +import typing as t +from contextlib import contextmanager + + +class TextWrapper(textwrap.TextWrapper): + def _handle_long_word( + self, + reversed_chunks: t.List[str], + cur_line: t.List[str], + cur_len: int, + width: int, + ) -> None: + space_left = max(width - cur_len, 1) + + if self.break_long_words: + last = reversed_chunks[-1] + cut = last[:space_left] + res = last[space_left:] + cur_line.append(cut) + reversed_chunks[-1] = res + elif not cur_line: + cur_line.append(reversed_chunks.pop()) + + @contextmanager + def extra_indent(self, indent: str) -> t.Iterator[None]: + old_initial_indent = self.initial_indent + old_subsequent_indent = self.subsequent_indent + self.initial_indent += indent + self.subsequent_indent += indent + + try: + yield + finally: + self.initial_indent = old_initial_indent + self.subsequent_indent = old_subsequent_indent + + def indent_only(self, text: str) -> str: + rv = [] + + for idx, line in enumerate(text.splitlines()): + indent = self.initial_indent + + if idx > 0: + indent = self.subsequent_indent + + rv.append(f"{indent}{line}") + + return "\n".join(rv) diff --git a/venv/lib/python3.7/site-packages/click/_unicodefun.py b/venv/lib/python3.7/site-packages/click/_unicodefun.py new file mode 100644 index 0000000..9cb30c3 --- /dev/null +++ b/venv/lib/python3.7/site-packages/click/_unicodefun.py @@ -0,0 +1,100 @@ +import codecs +import os +from gettext import gettext as _ + + +def _verify_python_env() -> None: + """Ensures that the environment is good for Unicode.""" + try: + from locale import getpreferredencoding + + fs_enc = codecs.lookup(getpreferredencoding()).name + except Exception: + fs_enc = "ascii" + + if fs_enc != "ascii": + return + + extra = [ + _( + "Click will abort further execution because Python was" + " configured to use ASCII as encoding for the environment." + " Consult https://click.palletsprojects.com/unicode-support/" + " for mitigation steps." + ) + ] + + if os.name == "posix": + import subprocess + + try: + rv = subprocess.Popen( + ["locale", "-a"], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + encoding="ascii", + errors="replace", + ).communicate()[0] + except OSError: + rv = "" + + good_locales = set() + has_c_utf8 = False + + for line in rv.splitlines(): + locale = line.strip() + + if locale.lower().endswith((".utf-8", ".utf8")): + good_locales.add(locale) + + if locale.lower() in ("c.utf8", "c.utf-8"): + has_c_utf8 = True + + if not good_locales: + extra.append( + _( + "Additional information: on this system no suitable" + " UTF-8 locales were discovered. This most likely" + " requires resolving by reconfiguring the locale" + " system." + ) + ) + elif has_c_utf8: + extra.append( + _( + "This system supports the C.UTF-8 locale which is" + " recommended. You might be able to resolve your" + " issue by exporting the following environment" + " variables:" + ) + ) + extra.append(" export LC_ALL=C.UTF-8\n export LANG=C.UTF-8") + else: + extra.append( + _( + "This system lists some UTF-8 supporting locales" + " that you can pick from. The following suitable" + " locales were discovered: {locales}" + ).format(locales=", ".join(sorted(good_locales))) + ) + + bad_locale = None + + for env_locale in os.environ.get("LC_ALL"), os.environ.get("LANG"): + if env_locale and env_locale.lower().endswith((".utf-8", ".utf8")): + bad_locale = env_locale + + if env_locale is not None: + break + + if bad_locale is not None: + extra.append( + _( + "Click discovered that you exported a UTF-8 locale" + " but the locale system could not pick up from it" + " because it does not exist. The exported locale is" + " {locale!r} but it is not supported." + ).format(locale=bad_locale) + ) + + raise RuntimeError("\n\n".join(extra)) diff --git a/venv/lib/python3.7/site-packages/click/_winconsole.py b/venv/lib/python3.7/site-packages/click/_winconsole.py new file mode 100644 index 0000000..6b20df3 --- /dev/null +++ b/venv/lib/python3.7/site-packages/click/_winconsole.py @@ -0,0 +1,279 @@ +# This module is based on the excellent work by Adam Bartoš who +# provided a lot of what went into the implementation here in +# the discussion to issue1602 in the Python bug tracker. +# +# There are some general differences in regards to how this works +# compared to the original patches as we do not need to patch +# the entire interpreter but just work in our little world of +# echo and prompt. +import io +import sys +import time +import typing as t +from ctypes import byref +from ctypes import c_char +from ctypes import c_char_p +from ctypes import c_int +from ctypes import c_ssize_t +from ctypes import c_ulong +from ctypes import c_void_p +from ctypes import POINTER +from ctypes import py_object +from ctypes import Structure +from ctypes.wintypes import DWORD +from ctypes.wintypes import HANDLE +from ctypes.wintypes import LPCWSTR +from ctypes.wintypes import LPWSTR + +from ._compat import _NonClosingTextIOWrapper + +assert sys.platform == "win32" +import msvcrt # noqa: E402 +from ctypes import windll # noqa: E402 +from ctypes import WINFUNCTYPE # noqa: E402 + +c_ssize_p = POINTER(c_ssize_t) + +kernel32 = windll.kernel32 +GetStdHandle = kernel32.GetStdHandle +ReadConsoleW = kernel32.ReadConsoleW +WriteConsoleW = kernel32.WriteConsoleW +GetConsoleMode = kernel32.GetConsoleMode +GetLastError = kernel32.GetLastError +GetCommandLineW = WINFUNCTYPE(LPWSTR)(("GetCommandLineW", windll.kernel32)) +CommandLineToArgvW = WINFUNCTYPE(POINTER(LPWSTR), LPCWSTR, POINTER(c_int))( + ("CommandLineToArgvW", windll.shell32) +) +LocalFree = WINFUNCTYPE(c_void_p, c_void_p)(("LocalFree", windll.kernel32)) + +STDIN_HANDLE = GetStdHandle(-10) +STDOUT_HANDLE = GetStdHandle(-11) +STDERR_HANDLE = GetStdHandle(-12) + +PyBUF_SIMPLE = 0 +PyBUF_WRITABLE = 1 + +ERROR_SUCCESS = 0 +ERROR_NOT_ENOUGH_MEMORY = 8 +ERROR_OPERATION_ABORTED = 995 + +STDIN_FILENO = 0 +STDOUT_FILENO = 1 +STDERR_FILENO = 2 + +EOF = b"\x1a" +MAX_BYTES_WRITTEN = 32767 + +try: + from ctypes import pythonapi +except ImportError: + # On PyPy we cannot get buffers so our ability to operate here is + # severely limited. + get_buffer = None +else: + + class Py_buffer(Structure): + _fields_ = [ + ("buf", c_void_p), + ("obj", py_object), + ("len", c_ssize_t), + ("itemsize", c_ssize_t), + ("readonly", c_int), + ("ndim", c_int), + ("format", c_char_p), + ("shape", c_ssize_p), + ("strides", c_ssize_p), + ("suboffsets", c_ssize_p), + ("internal", c_void_p), + ] + + PyObject_GetBuffer = pythonapi.PyObject_GetBuffer + PyBuffer_Release = pythonapi.PyBuffer_Release + + def get_buffer(obj, writable=False): + buf = Py_buffer() + flags = PyBUF_WRITABLE if writable else PyBUF_SIMPLE + PyObject_GetBuffer(py_object(obj), byref(buf), flags) + + try: + buffer_type = c_char * buf.len + return buffer_type.from_address(buf.buf) + finally: + PyBuffer_Release(byref(buf)) + + +class _WindowsConsoleRawIOBase(io.RawIOBase): + def __init__(self, handle): + self.handle = handle + + def isatty(self): + super().isatty() + return True + + +class _WindowsConsoleReader(_WindowsConsoleRawIOBase): + def readable(self): + return True + + def readinto(self, b): + bytes_to_be_read = len(b) + if not bytes_to_be_read: + return 0 + elif bytes_to_be_read % 2: + raise ValueError( + "cannot read odd number of bytes from UTF-16-LE encoded console" + ) + + buffer = get_buffer(b, writable=True) + code_units_to_be_read = bytes_to_be_read // 2 + code_units_read = c_ulong() + + rv = ReadConsoleW( + HANDLE(self.handle), + buffer, + code_units_to_be_read, + byref(code_units_read), + None, + ) + if GetLastError() == ERROR_OPERATION_ABORTED: + # wait for KeyboardInterrupt + time.sleep(0.1) + if not rv: + raise OSError(f"Windows error: {GetLastError()}") + + if buffer[0] == EOF: + return 0 + return 2 * code_units_read.value + + +class _WindowsConsoleWriter(_WindowsConsoleRawIOBase): + def writable(self): + return True + + @staticmethod + def _get_error_message(errno): + if errno == ERROR_SUCCESS: + return "ERROR_SUCCESS" + elif errno == ERROR_NOT_ENOUGH_MEMORY: + return "ERROR_NOT_ENOUGH_MEMORY" + return f"Windows error {errno}" + + def write(self, b): + bytes_to_be_written = len(b) + buf = get_buffer(b) + code_units_to_be_written = min(bytes_to_be_written, MAX_BYTES_WRITTEN) // 2 + code_units_written = c_ulong() + + WriteConsoleW( + HANDLE(self.handle), + buf, + code_units_to_be_written, + byref(code_units_written), + None, + ) + bytes_written = 2 * code_units_written.value + + if bytes_written == 0 and bytes_to_be_written > 0: + raise OSError(self._get_error_message(GetLastError())) + return bytes_written + + +class ConsoleStream: + def __init__(self, text_stream: t.TextIO, byte_stream: t.BinaryIO) -> None: + self._text_stream = text_stream + self.buffer = byte_stream + + @property + def name(self) -> str: + return self.buffer.name + + def write(self, x: t.AnyStr) -> int: + if isinstance(x, str): + return self._text_stream.write(x) + try: + self.flush() + except Exception: + pass + return self.buffer.write(x) + + def writelines(self, lines: t.Iterable[t.AnyStr]) -> None: + for line in lines: + self.write(line) + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._text_stream, name) + + def isatty(self) -> bool: + return self.buffer.isatty() + + def __repr__(self): + return f"" + + +def _get_text_stdin(buffer_stream: t.BinaryIO) -> t.TextIO: + text_stream = _NonClosingTextIOWrapper( + io.BufferedReader(_WindowsConsoleReader(STDIN_HANDLE)), + "utf-16-le", + "strict", + line_buffering=True, + ) + return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream)) + + +def _get_text_stdout(buffer_stream: t.BinaryIO) -> t.TextIO: + text_stream = _NonClosingTextIOWrapper( + io.BufferedWriter(_WindowsConsoleWriter(STDOUT_HANDLE)), + "utf-16-le", + "strict", + line_buffering=True, + ) + return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream)) + + +def _get_text_stderr(buffer_stream: t.BinaryIO) -> t.TextIO: + text_stream = _NonClosingTextIOWrapper( + io.BufferedWriter(_WindowsConsoleWriter(STDERR_HANDLE)), + "utf-16-le", + "strict", + line_buffering=True, + ) + return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream)) + + +_stream_factories: t.Mapping[int, t.Callable[[t.BinaryIO], t.TextIO]] = { + 0: _get_text_stdin, + 1: _get_text_stdout, + 2: _get_text_stderr, +} + + +def _is_console(f: t.TextIO) -> bool: + if not hasattr(f, "fileno"): + return False + + try: + fileno = f.fileno() + except (OSError, io.UnsupportedOperation): + return False + + handle = msvcrt.get_osfhandle(fileno) + return bool(GetConsoleMode(handle, byref(DWORD()))) + + +def _get_windows_console_stream( + f: t.TextIO, encoding: t.Optional[str], errors: t.Optional[str] +) -> t.Optional[t.TextIO]: + if ( + get_buffer is not None + and encoding in {"utf-16-le", None} + and errors in {"strict", None} + and _is_console(f) + ): + func = _stream_factories.get(f.fileno()) + if func is not None: + b = getattr(f, "buffer", None) + + if b is None: + return None + + return func(b) diff --git a/venv/lib/python3.7/site-packages/click/core.py b/venv/lib/python3.7/site-packages/click/core.py new file mode 100644 index 0000000..f226354 --- /dev/null +++ b/venv/lib/python3.7/site-packages/click/core.py @@ -0,0 +1,2953 @@ +import enum +import errno +import os +import sys +import typing +import typing as t +from collections import abc +from contextlib import contextmanager +from contextlib import ExitStack +from functools import partial +from functools import update_wrapper +from gettext import gettext as _ +from gettext import ngettext +from itertools import repeat + +from . import types +from ._unicodefun import _verify_python_env +from .exceptions import Abort +from .exceptions import BadParameter +from .exceptions import ClickException +from .exceptions import Exit +from .exceptions import MissingParameter +from .exceptions import UsageError +from .formatting import HelpFormatter +from .formatting import join_options +from .globals import pop_context +from .globals import push_context +from .parser import _flag_needs_value +from .parser import OptionParser +from .parser import split_opt +from .termui import confirm +from .termui import prompt +from .termui import style +from .utils import _detect_program_name +from .utils import _expand_args +from .utils import echo +from .utils import make_default_short_help +from .utils import make_str +from .utils import PacifyFlushWrapper + +if t.TYPE_CHECKING: + import typing_extensions as te + from .shell_completion import CompletionItem + +F = t.TypeVar("F", bound=t.Callable[..., t.Any]) +V = t.TypeVar("V") + + +def _complete_visible_commands( + ctx: "Context", incomplete: str +) -> t.Iterator[t.Tuple[str, "Command"]]: + """List all the subcommands of a group that start with the + incomplete value and aren't hidden. + + :param ctx: Invocation context for the group. + :param incomplete: Value being completed. May be empty. + """ + multi = t.cast(MultiCommand, ctx.command) + + for name in multi.list_commands(ctx): + if name.startswith(incomplete): + command = multi.get_command(ctx, name) + + if command is not None and not command.hidden: + yield name, command + + +def _check_multicommand( + base_command: "MultiCommand", cmd_name: str, cmd: "Command", register: bool = False +) -> None: + if not base_command.chain or not isinstance(cmd, MultiCommand): + return + if register: + hint = ( + "It is not possible to add multi commands as children to" + " another multi command that is in chain mode." + ) + else: + hint = ( + "Found a multi command as subcommand to a multi command" + " that is in chain mode. This is not supported." + ) + raise RuntimeError( + f"{hint}. Command {base_command.name!r} is set to chain and" + f" {cmd_name!r} was added as a subcommand but it in itself is a" + f" multi command. ({cmd_name!r} is a {type(cmd).__name__}" + f" within a chained {type(base_command).__name__} named" + f" {base_command.name!r})." + ) + + +def batch(iterable: t.Iterable[V], batch_size: int) -> t.List[t.Tuple[V, ...]]: + return list(zip(*repeat(iter(iterable), batch_size))) + + +@contextmanager +def augment_usage_errors( + ctx: "Context", param: t.Optional["Parameter"] = None +) -> t.Iterator[None]: + """Context manager that attaches extra information to exceptions.""" + try: + yield + except BadParameter as e: + if e.ctx is None: + e.ctx = ctx + if param is not None and e.param is None: + e.param = param + raise + except UsageError as e: + if e.ctx is None: + e.ctx = ctx + raise + + +def iter_params_for_processing( + invocation_order: t.Sequence["Parameter"], + declaration_order: t.Sequence["Parameter"], +) -> t.List["Parameter"]: + """Given a sequence of parameters in the order as should be considered + for processing and an iterable of parameters that exist, this returns + a list in the correct order as they should be processed. + """ + + def sort_key(item: "Parameter") -> t.Tuple[bool, float]: + try: + idx: float = invocation_order.index(item) + except ValueError: + idx = float("inf") + + return not item.is_eager, idx + + return sorted(declaration_order, key=sort_key) + + +class ParameterSource(enum.Enum): + """This is an :class:`~enum.Enum` that indicates the source of a + parameter's value. + + Use :meth:`click.Context.get_parameter_source` to get the + source for a parameter by name. + + .. versionchanged:: 8.0 + Use :class:`~enum.Enum` and drop the ``validate`` method. + + .. versionchanged:: 8.0 + Added the ``PROMPT`` value. + """ + + COMMANDLINE = enum.auto() + """The value was provided by the command line args.""" + ENVIRONMENT = enum.auto() + """The value was provided with an environment variable.""" + DEFAULT = enum.auto() + """Used the default specified by the parameter.""" + DEFAULT_MAP = enum.auto() + """Used a default provided by :attr:`Context.default_map`.""" + PROMPT = enum.auto() + """Used a prompt to confirm a default or provide a value.""" + + +class Context: + """The context is a special internal object that holds state relevant + for the script execution at every single level. It's normally invisible + to commands unless they opt-in to getting access to it. + + The context is useful as it can pass internal objects around and can + control special execution features such as reading data from + environment variables. + + A context can be used as context manager in which case it will call + :meth:`close` on teardown. + + :param command: the command class for this context. + :param parent: the parent context. + :param info_name: the info name for this invocation. Generally this + is the most descriptive name for the script or + command. For the toplevel script it is usually + the name of the script, for commands below it it's + the name of the script. + :param obj: an arbitrary object of user data. + :param auto_envvar_prefix: the prefix to use for automatic environment + variables. If this is `None` then reading + from environment variables is disabled. This + does not affect manually set environment + variables which are always read. + :param default_map: a dictionary (like object) with default values + for parameters. + :param terminal_width: the width of the terminal. The default is + inherit from parent context. If no context + defines the terminal width then auto + detection will be applied. + :param max_content_width: the maximum width for content rendered by + Click (this currently only affects help + pages). This defaults to 80 characters if + not overridden. In other words: even if the + terminal is larger than that, Click will not + format things wider than 80 characters by + default. In addition to that, formatters might + add some safety mapping on the right. + :param resilient_parsing: if this flag is enabled then Click will + parse without any interactivity or callback + invocation. Default values will also be + ignored. This is useful for implementing + things such as completion support. + :param allow_extra_args: if this is set to `True` then extra arguments + at the end will not raise an error and will be + kept on the context. The default is to inherit + from the command. + :param allow_interspersed_args: if this is set to `False` then options + and arguments cannot be mixed. The + default is to inherit from the command. + :param ignore_unknown_options: instructs click to ignore options it does + not know and keeps them for later + processing. + :param help_option_names: optionally a list of strings that define how + the default help parameter is named. The + default is ``['--help']``. + :param token_normalize_func: an optional function that is used to + normalize tokens (options, choices, + etc.). This for instance can be used to + implement case insensitive behavior. + :param color: controls if the terminal supports ANSI colors or not. The + default is autodetection. This is only needed if ANSI + codes are used in texts that Click prints which is by + default not the case. This for instance would affect + help output. + :param show_default: Show defaults for all options. If not set, + defaults to the value from a parent context. Overrides an + option's ``show_default`` argument. + + .. versionchanged:: 8.0 + The ``show_default`` parameter defaults to the value from the + parent context. + + .. versionchanged:: 7.1 + Added the ``show_default`` parameter. + + .. versionchanged:: 4.0 + Added the ``color``, ``ignore_unknown_options``, and + ``max_content_width`` parameters. + + .. versionchanged:: 3.0 + Added the ``allow_extra_args`` and ``allow_interspersed_args`` + parameters. + + .. versionchanged:: 2.0 + Added the ``resilient_parsing``, ``help_option_names``, and + ``token_normalize_func`` parameters. + """ + + #: The formatter class to create with :meth:`make_formatter`. + #: + #: .. versionadded:: 8.0 + formatter_class: t.Type["HelpFormatter"] = HelpFormatter + + def __init__( + self, + command: "Command", + parent: t.Optional["Context"] = None, + info_name: t.Optional[str] = None, + obj: t.Optional[t.Any] = None, + auto_envvar_prefix: t.Optional[str] = None, + default_map: t.Optional[t.Dict[str, t.Any]] = None, + terminal_width: t.Optional[int] = None, + max_content_width: t.Optional[int] = None, + resilient_parsing: bool = False, + allow_extra_args: t.Optional[bool] = None, + allow_interspersed_args: t.Optional[bool] = None, + ignore_unknown_options: t.Optional[bool] = None, + help_option_names: t.Optional[t.List[str]] = None, + token_normalize_func: t.Optional[t.Callable[[str], str]] = None, + color: t.Optional[bool] = None, + show_default: t.Optional[bool] = None, + ) -> None: + #: the parent context or `None` if none exists. + self.parent = parent + #: the :class:`Command` for this context. + self.command = command + #: the descriptive information name + self.info_name = info_name + #: Map of parameter names to their parsed values. Parameters + #: with ``expose_value=False`` are not stored. + self.params: t.Dict[str, t.Any] = {} + #: the leftover arguments. + self.args: t.List[str] = [] + #: protected arguments. These are arguments that are prepended + #: to `args` when certain parsing scenarios are encountered but + #: must be never propagated to another arguments. This is used + #: to implement nested parsing. + self.protected_args: t.List[str] = [] + + if obj is None and parent is not None: + obj = parent.obj + + #: the user object stored. + self.obj: t.Any = obj + self._meta: t.Dict[str, t.Any] = getattr(parent, "meta", {}) + + #: A dictionary (-like object) with defaults for parameters. + if ( + default_map is None + and info_name is not None + and parent is not None + and parent.default_map is not None + ): + default_map = parent.default_map.get(info_name) + + self.default_map: t.Optional[t.Dict[str, t.Any]] = default_map + + #: This flag indicates if a subcommand is going to be executed. A + #: group callback can use this information to figure out if it's + #: being executed directly or because the execution flow passes + #: onwards to a subcommand. By default it's None, but it can be + #: the name of the subcommand to execute. + #: + #: If chaining is enabled this will be set to ``'*'`` in case + #: any commands are executed. It is however not possible to + #: figure out which ones. If you require this knowledge you + #: should use a :func:`result_callback`. + self.invoked_subcommand: t.Optional[str] = None + + if terminal_width is None and parent is not None: + terminal_width = parent.terminal_width + + #: The width of the terminal (None is autodetection). + self.terminal_width: t.Optional[int] = terminal_width + + if max_content_width is None and parent is not None: + max_content_width = parent.max_content_width + + #: The maximum width of formatted content (None implies a sensible + #: default which is 80 for most things). + self.max_content_width: t.Optional[int] = max_content_width + + if allow_extra_args is None: + allow_extra_args = command.allow_extra_args + + #: Indicates if the context allows extra args or if it should + #: fail on parsing. + #: + #: .. versionadded:: 3.0 + self.allow_extra_args = allow_extra_args + + if allow_interspersed_args is None: + allow_interspersed_args = command.allow_interspersed_args + + #: Indicates if the context allows mixing of arguments and + #: options or not. + #: + #: .. versionadded:: 3.0 + self.allow_interspersed_args: bool = allow_interspersed_args + + if ignore_unknown_options is None: + ignore_unknown_options = command.ignore_unknown_options + + #: Instructs click to ignore options that a command does not + #: understand and will store it on the context for later + #: processing. This is primarily useful for situations where you + #: want to call into external programs. Generally this pattern is + #: strongly discouraged because it's not possibly to losslessly + #: forward all arguments. + #: + #: .. versionadded:: 4.0 + self.ignore_unknown_options: bool = ignore_unknown_options + + if help_option_names is None: + if parent is not None: + help_option_names = parent.help_option_names + else: + help_option_names = ["--help"] + + #: The names for the help options. + self.help_option_names: t.List[str] = help_option_names + + if token_normalize_func is None and parent is not None: + token_normalize_func = parent.token_normalize_func + + #: An optional normalization function for tokens. This is + #: options, choices, commands etc. + self.token_normalize_func: t.Optional[ + t.Callable[[str], str] + ] = token_normalize_func + + #: Indicates if resilient parsing is enabled. In that case Click + #: will do its best to not cause any failures and default values + #: will be ignored. Useful for completion. + self.resilient_parsing: bool = resilient_parsing + + # If there is no envvar prefix yet, but the parent has one and + # the command on this level has a name, we can expand the envvar + # prefix automatically. + if auto_envvar_prefix is None: + if ( + parent is not None + and parent.auto_envvar_prefix is not None + and self.info_name is not None + ): + auto_envvar_prefix = ( + f"{parent.auto_envvar_prefix}_{self.info_name.upper()}" + ) + else: + auto_envvar_prefix = auto_envvar_prefix.upper() + + if auto_envvar_prefix is not None: + auto_envvar_prefix = auto_envvar_prefix.replace("-", "_") + + self.auto_envvar_prefix: t.Optional[str] = auto_envvar_prefix + + if color is None and parent is not None: + color = parent.color + + #: Controls if styling output is wanted or not. + self.color: t.Optional[bool] = color + + if show_default is None and parent is not None: + show_default = parent.show_default + + #: Show option default values when formatting help text. + self.show_default: t.Optional[bool] = show_default + + self._close_callbacks: t.List[t.Callable[[], t.Any]] = [] + self._depth = 0 + self._parameter_source: t.Dict[str, ParameterSource] = {} + self._exit_stack = ExitStack() + + def to_info_dict(self) -> t.Dict[str, t.Any]: + """Gather information that could be useful for a tool generating + user-facing documentation. This traverses the entire CLI + structure. + + .. code-block:: python + + with Context(cli) as ctx: + info = ctx.to_info_dict() + + .. versionadded:: 8.0 + """ + return { + "command": self.command.to_info_dict(self), + "info_name": self.info_name, + "allow_extra_args": self.allow_extra_args, + "allow_interspersed_args": self.allow_interspersed_args, + "ignore_unknown_options": self.ignore_unknown_options, + "auto_envvar_prefix": self.auto_envvar_prefix, + } + + def __enter__(self) -> "Context": + self._depth += 1 + push_context(self) + return self + + def __exit__(self, exc_type, exc_value, tb): # type: ignore + self._depth -= 1 + if self._depth == 0: + self.close() + pop_context() + + @contextmanager + def scope(self, cleanup: bool = True) -> t.Iterator["Context"]: + """This helper method can be used with the context object to promote + it to the current thread local (see :func:`get_current_context`). + The default behavior of this is to invoke the cleanup functions which + can be disabled by setting `cleanup` to `False`. The cleanup + functions are typically used for things such as closing file handles. + + If the cleanup is intended the context object can also be directly + used as a context manager. + + Example usage:: + + with ctx.scope(): + assert get_current_context() is ctx + + This is equivalent:: + + with ctx: + assert get_current_context() is ctx + + .. versionadded:: 5.0 + + :param cleanup: controls if the cleanup functions should be run or + not. The default is to run these functions. In + some situations the context only wants to be + temporarily pushed in which case this can be disabled. + Nested pushes automatically defer the cleanup. + """ + if not cleanup: + self._depth += 1 + try: + with self as rv: + yield rv + finally: + if not cleanup: + self._depth -= 1 + + @property + def meta(self) -> t.Dict[str, t.Any]: + """This is a dictionary which is shared with all the contexts + that are nested. It exists so that click utilities can store some + state here if they need to. It is however the responsibility of + that code to manage this dictionary well. + + The keys are supposed to be unique dotted strings. For instance + module paths are a good choice for it. What is stored in there is + irrelevant for the operation of click. However what is important is + that code that places data here adheres to the general semantics of + the system. + + Example usage:: + + LANG_KEY = f'{__name__}.lang' + + def set_language(value): + ctx = get_current_context() + ctx.meta[LANG_KEY] = value + + def get_language(): + return get_current_context().meta.get(LANG_KEY, 'en_US') + + .. versionadded:: 5.0 + """ + return self._meta + + def make_formatter(self) -> HelpFormatter: + """Creates the :class:`~click.HelpFormatter` for the help and + usage output. + + To quickly customize the formatter class used without overriding + this method, set the :attr:`formatter_class` attribute. + + .. versionchanged:: 8.0 + Added the :attr:`formatter_class` attribute. + """ + return self.formatter_class( + width=self.terminal_width, max_width=self.max_content_width + ) + + def with_resource(self, context_manager: t.ContextManager[V]) -> V: + """Register a resource as if it were used in a ``with`` + statement. The resource will be cleaned up when the context is + popped. + + Uses :meth:`contextlib.ExitStack.enter_context`. It calls the + resource's ``__enter__()`` method and returns the result. When + the context is popped, it closes the stack, which calls the + resource's ``__exit__()`` method. + + To register a cleanup function for something that isn't a + context manager, use :meth:`call_on_close`. Or use something + from :mod:`contextlib` to turn it into a context manager first. + + .. code-block:: python + + @click.group() + @click.option("--name") + @click.pass_context + def cli(ctx): + ctx.obj = ctx.with_resource(connect_db(name)) + + :param context_manager: The context manager to enter. + :return: Whatever ``context_manager.__enter__()`` returns. + + .. versionadded:: 8.0 + """ + return self._exit_stack.enter_context(context_manager) + + def call_on_close(self, f: t.Callable[..., t.Any]) -> t.Callable[..., t.Any]: + """Register a function to be called when the context tears down. + + This can be used to close resources opened during the script + execution. Resources that support Python's context manager + protocol which would be used in a ``with`` statement should be + registered with :meth:`with_resource` instead. + + :param f: The function to execute on teardown. + """ + return self._exit_stack.callback(f) + + def close(self) -> None: + """Invoke all close callbacks registered with + :meth:`call_on_close`, and exit all context managers entered + with :meth:`with_resource`. + """ + self._exit_stack.close() + # In case the context is reused, create a new exit stack. + self._exit_stack = ExitStack() + + @property + def command_path(self) -> str: + """The computed command path. This is used for the ``usage`` + information on the help page. It's automatically created by + combining the info names of the chain of contexts to the root. + """ + rv = "" + if self.info_name is not None: + rv = self.info_name + if self.parent is not None: + parent_command_path = [self.parent.command_path] + + if isinstance(self.parent.command, Command): + for param in self.parent.command.get_params(self): + parent_command_path.extend(param.get_usage_pieces(self)) + + rv = f"{' '.join(parent_command_path)} {rv}" + return rv.lstrip() + + def find_root(self) -> "Context": + """Finds the outermost context.""" + node = self + while node.parent is not None: + node = node.parent + return node + + def find_object(self, object_type: t.Type[V]) -> t.Optional[V]: + """Finds the closest object of a given type.""" + node: t.Optional["Context"] = self + + while node is not None: + if isinstance(node.obj, object_type): + return node.obj + + node = node.parent + + return None + + def ensure_object(self, object_type: t.Type[V]) -> V: + """Like :meth:`find_object` but sets the innermost object to a + new instance of `object_type` if it does not exist. + """ + rv = self.find_object(object_type) + if rv is None: + self.obj = rv = object_type() + return rv + + @typing.overload + def lookup_default( + self, name: str, call: "te.Literal[True]" = True + ) -> t.Optional[t.Any]: + ... + + @typing.overload + def lookup_default( + self, name: str, call: "te.Literal[False]" = ... + ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: + ... + + def lookup_default(self, name: str, call: bool = True) -> t.Optional[t.Any]: + """Get the default for a parameter from :attr:`default_map`. + + :param name: Name of the parameter. + :param call: If the default is a callable, call it. Disable to + return the callable instead. + + .. versionchanged:: 8.0 + Added the ``call`` parameter. + """ + if self.default_map is not None: + value = self.default_map.get(name) + + if call and callable(value): + return value() + + return value + + return None + + def fail(self, message: str) -> "te.NoReturn": + """Aborts the execution of the program with a specific error + message. + + :param message: the error message to fail with. + """ + raise UsageError(message, self) + + def abort(self) -> "te.NoReturn": + """Aborts the script.""" + raise Abort() + + def exit(self, code: int = 0) -> "te.NoReturn": + """Exits the application with a given exit code.""" + raise Exit(code) + + def get_usage(self) -> str: + """Helper method to get formatted usage string for the current + context and command. + """ + return self.command.get_usage(self) + + def get_help(self) -> str: + """Helper method to get formatted help page for the current + context and command. + """ + return self.command.get_help(self) + + def _make_sub_context(self, command: "Command") -> "Context": + """Create a new context of the same type as this context, but + for a new command. + + :meta private: + """ + return type(self)(command, info_name=command.name, parent=self) + + def invoke( + __self, # noqa: B902 + __callback: t.Union["Command", t.Callable[..., t.Any]], + *args: t.Any, + **kwargs: t.Any, + ) -> t.Any: + """Invokes a command callback in exactly the way it expects. There + are two ways to invoke this method: + + 1. the first argument can be a callback and all other arguments and + keyword arguments are forwarded directly to the function. + 2. the first argument is a click command object. In that case all + arguments are forwarded as well but proper click parameters + (options and click arguments) must be keyword arguments and Click + will fill in defaults. + + Note that before Click 3.2 keyword arguments were not properly filled + in against the intention of this code and no context was created. For + more information about this change and why it was done in a bugfix + release see :ref:`upgrade-to-3.2`. + + .. versionchanged:: 8.0 + All ``kwargs`` are tracked in :attr:`params` so they will be + passed if :meth:`forward` is called at multiple levels. + """ + if isinstance(__callback, Command): + other_cmd = __callback + + if other_cmd.callback is None: + raise TypeError( + "The given command does not have a callback that can be invoked." + ) + else: + __callback = other_cmd.callback + + ctx = __self._make_sub_context(other_cmd) + + for param in other_cmd.params: + if param.name not in kwargs and param.expose_value: + kwargs[param.name] = param.type_cast_value( # type: ignore + ctx, param.get_default(ctx) + ) + + # Track all kwargs as params, so that forward() will pass + # them on in subsequent calls. + ctx.params.update(kwargs) + else: + ctx = __self + + with augment_usage_errors(__self): + with ctx: + return __callback(*args, **kwargs) + + def forward( + __self, __cmd: "Command", *args: t.Any, **kwargs: t.Any # noqa: B902 + ) -> t.Any: + """Similar to :meth:`invoke` but fills in default keyword + arguments from the current context if the other command expects + it. This cannot invoke callbacks directly, only other commands. + + .. versionchanged:: 8.0 + All ``kwargs`` are tracked in :attr:`params` so they will be + passed if ``forward`` is called at multiple levels. + """ + # Can only forward to other commands, not direct callbacks. + if not isinstance(__cmd, Command): + raise TypeError("Callback is not a command.") + + for param in __self.params: + if param not in kwargs: + kwargs[param] = __self.params[param] + + return __self.invoke(__cmd, *args, **kwargs) + + def set_parameter_source(self, name: str, source: ParameterSource) -> None: + """Set the source of a parameter. This indicates the location + from which the value of the parameter was obtained. + + :param name: The name of the parameter. + :param source: A member of :class:`~click.core.ParameterSource`. + """ + self._parameter_source[name] = source + + def get_parameter_source(self, name: str) -> t.Optional[ParameterSource]: + """Get the source of a parameter. This indicates the location + from which the value of the parameter was obtained. + + This can be useful for determining when a user specified a value + on the command line that is the same as the default value. It + will be :attr:`~click.core.ParameterSource.DEFAULT` only if the + value was actually taken from the default. + + :param name: The name of the parameter. + :rtype: ParameterSource + + .. versionchanged:: 8.0 + Returns ``None`` if the parameter was not provided from any + source. + """ + return self._parameter_source.get(name) + + +class BaseCommand: + """The base command implements the minimal API contract of commands. + Most code will never use this as it does not implement a lot of useful + functionality but it can act as the direct subclass of alternative + parsing methods that do not depend on the Click parser. + + For instance, this can be used to bridge Click and other systems like + argparse or docopt. + + Because base commands do not implement a lot of the API that other + parts of Click take for granted, they are not supported for all + operations. For instance, they cannot be used with the decorators + usually and they have no built-in callback system. + + .. versionchanged:: 2.0 + Added the `context_settings` parameter. + + :param name: the name of the command to use unless a group overrides it. + :param context_settings: an optional dictionary with defaults that are + passed to the context object. + """ + + #: The context class to create with :meth:`make_context`. + #: + #: .. versionadded:: 8.0 + context_class: t.Type[Context] = Context + #: the default for the :attr:`Context.allow_extra_args` flag. + allow_extra_args = False + #: the default for the :attr:`Context.allow_interspersed_args` flag. + allow_interspersed_args = True + #: the default for the :attr:`Context.ignore_unknown_options` flag. + ignore_unknown_options = False + + def __init__( + self, + name: t.Optional[str], + context_settings: t.Optional[t.Dict[str, t.Any]] = None, + ) -> None: + #: the name the command thinks it has. Upon registering a command + #: on a :class:`Group` the group will default the command name + #: with this information. You should instead use the + #: :class:`Context`\'s :attr:`~Context.info_name` attribute. + self.name = name + + if context_settings is None: + context_settings = {} + + #: an optional dictionary with defaults passed to the context. + self.context_settings: t.Dict[str, t.Any] = context_settings + + def to_info_dict(self, ctx: Context) -> t.Dict[str, t.Any]: + """Gather information that could be useful for a tool generating + user-facing documentation. This traverses the entire structure + below this command. + + Use :meth:`click.Context.to_info_dict` to traverse the entire + CLI structure. + + :param ctx: A :class:`Context` representing this command. + + .. versionadded:: 8.0 + """ + return {"name": self.name} + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} {self.name}>" + + def get_usage(self, ctx: Context) -> str: + raise NotImplementedError("Base commands cannot get usage") + + def get_help(self, ctx: Context) -> str: + raise NotImplementedError("Base commands cannot get help") + + def make_context( + self, + info_name: t.Optional[str], + args: t.List[str], + parent: t.Optional[Context] = None, + **extra: t.Any, + ) -> Context: + """This function when given an info name and arguments will kick + off the parsing and create a new :class:`Context`. It does not + invoke the actual command callback though. + + To quickly customize the context class used without overriding + this method, set the :attr:`context_class` attribute. + + :param info_name: the info name for this invocation. Generally this + is the most descriptive name for the script or + command. For the toplevel script it's usually + the name of the script, for commands below it it's + the name of the command. + :param args: the arguments to parse as list of strings. + :param parent: the parent context if available. + :param extra: extra keyword arguments forwarded to the context + constructor. + + .. versionchanged:: 8.0 + Added the :attr:`context_class` attribute. + """ + for key, value in self.context_settings.items(): + if key not in extra: + extra[key] = value + + ctx = self.context_class( + self, info_name=info_name, parent=parent, **extra # type: ignore + ) + + with ctx.scope(cleanup=False): + self.parse_args(ctx, args) + return ctx + + def parse_args(self, ctx: Context, args: t.List[str]) -> t.List[str]: + """Given a context and a list of arguments this creates the parser + and parses the arguments, then modifies the context as necessary. + This is automatically invoked by :meth:`make_context`. + """ + raise NotImplementedError("Base commands do not know how to parse arguments.") + + def invoke(self, ctx: Context) -> t.Any: + """Given a context, this invokes the command. The default + implementation is raising a not implemented error. + """ + raise NotImplementedError("Base commands are not invokable by default") + + def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: + """Return a list of completions for the incomplete value. Looks + at the names of chained multi-commands. + + Any command could be part of a chained multi-command, so sibling + commands are valid at any point during command completion. Other + command classes will return more completions. + + :param ctx: Invocation context for this command. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + results: t.List["CompletionItem"] = [] + + while ctx.parent is not None: + ctx = ctx.parent + + if isinstance(ctx.command, MultiCommand) and ctx.command.chain: + results.extend( + CompletionItem(name, help=command.get_short_help_str()) + for name, command in _complete_visible_commands(ctx, incomplete) + if name not in ctx.protected_args + ) + + return results + + @typing.overload + def main( + self, + args: t.Optional[t.Sequence[str]] = None, + prog_name: t.Optional[str] = None, + complete_var: t.Optional[str] = None, + standalone_mode: "te.Literal[True]" = True, + **extra: t.Any, + ) -> "te.NoReturn": + ... + + @typing.overload + def main( + self, + args: t.Optional[t.Sequence[str]] = None, + prog_name: t.Optional[str] = None, + complete_var: t.Optional[str] = None, + standalone_mode: bool = ..., + **extra: t.Any, + ) -> t.Any: + ... + + def main( + self, + args: t.Optional[t.Sequence[str]] = None, + prog_name: t.Optional[str] = None, + complete_var: t.Optional[str] = None, + standalone_mode: bool = True, + windows_expand_args: bool = True, + **extra: t.Any, + ) -> t.Any: + """This is the way to invoke a script with all the bells and + whistles as a command line application. This will always terminate + the application after a call. If this is not wanted, ``SystemExit`` + needs to be caught. + + This method is also available by directly calling the instance of + a :class:`Command`. + + :param args: the arguments that should be used for parsing. If not + provided, ``sys.argv[1:]`` is used. + :param prog_name: the program name that should be used. By default + the program name is constructed by taking the file + name from ``sys.argv[0]``. + :param complete_var: the environment variable that controls the + bash completion support. The default is + ``"__COMPLETE"`` with prog_name in + uppercase. + :param standalone_mode: the default behavior is to invoke the script + in standalone mode. Click will then + handle exceptions and convert them into + error messages and the function will never + return but shut down the interpreter. If + this is set to `False` they will be + propagated to the caller and the return + value of this function is the return value + of :meth:`invoke`. + :param windows_expand_args: Expand glob patterns, user dir, and + env vars in command line args on Windows. + :param extra: extra keyword arguments are forwarded to the context + constructor. See :class:`Context` for more information. + + .. versionchanged:: 8.0.1 + Added the ``windows_expand_args`` parameter to allow + disabling command line arg expansion on Windows. + + .. versionchanged:: 8.0 + When taking arguments from ``sys.argv`` on Windows, glob + patterns, user dir, and env vars are expanded. + + .. versionchanged:: 3.0 + Added the ``standalone_mode`` parameter. + """ + # Verify that the environment is configured correctly, or reject + # further execution to avoid a broken script. + _verify_python_env() + + if args is None: + args = sys.argv[1:] + + if os.name == "nt" and windows_expand_args: + args = _expand_args(args) + else: + args = list(args) + + if prog_name is None: + prog_name = _detect_program_name() + + # Process shell completion requests and exit early. + self._main_shell_completion(extra, prog_name, complete_var) + + try: + try: + with self.make_context(prog_name, args, **extra) as ctx: + rv = self.invoke(ctx) + if not standalone_mode: + return rv + # it's not safe to `ctx.exit(rv)` here! + # note that `rv` may actually contain data like "1" which + # has obvious effects + # more subtle case: `rv=[None, None]` can come out of + # chained commands which all returned `None` -- so it's not + # even always obvious that `rv` indicates success/failure + # by its truthiness/falsiness + ctx.exit() + except (EOFError, KeyboardInterrupt): + echo(file=sys.stderr) + raise Abort() from None + except ClickException as e: + if not standalone_mode: + raise + e.show() + sys.exit(e.exit_code) + except OSError as e: + if e.errno == errno.EPIPE: + sys.stdout = t.cast(t.TextIO, PacifyFlushWrapper(sys.stdout)) + sys.stderr = t.cast(t.TextIO, PacifyFlushWrapper(sys.stderr)) + sys.exit(1) + else: + raise + except Exit as e: + if standalone_mode: + sys.exit(e.exit_code) + else: + # in non-standalone mode, return the exit code + # note that this is only reached if `self.invoke` above raises + # an Exit explicitly -- thus bypassing the check there which + # would return its result + # the results of non-standalone execution may therefore be + # somewhat ambiguous: if there are codepaths which lead to + # `ctx.exit(1)` and to `return 1`, the caller won't be able to + # tell the difference between the two + return e.exit_code + except Abort: + if not standalone_mode: + raise + echo(_("Aborted!"), file=sys.stderr) + sys.exit(1) + + def _main_shell_completion( + self, + ctx_args: t.Dict[str, t.Any], + prog_name: str, + complete_var: t.Optional[str] = None, + ) -> None: + """Check if the shell is asking for tab completion, process + that, then exit early. Called from :meth:`main` before the + program is invoked. + + :param prog_name: Name of the executable in the shell. + :param complete_var: Name of the environment variable that holds + the completion instruction. Defaults to + ``_{PROG_NAME}_COMPLETE``. + """ + if complete_var is None: + complete_var = f"_{prog_name}_COMPLETE".replace("-", "_").upper() + + instruction = os.environ.get(complete_var) + + if not instruction: + return + + from .shell_completion import shell_complete + + rv = shell_complete(self, ctx_args, prog_name, complete_var, instruction) + sys.exit(rv) + + def __call__(self, *args: t.Any, **kwargs: t.Any) -> t.Any: + """Alias for :meth:`main`.""" + return self.main(*args, **kwargs) + + +class Command(BaseCommand): + """Commands are the basic building block of command line interfaces in + Click. A basic command handles command line parsing and might dispatch + more parsing to commands nested below it. + + .. versionchanged:: 2.0 + Added the `context_settings` parameter. + .. versionchanged:: 8.0 + Added repr showing the command name + .. versionchanged:: 7.1 + Added the `no_args_is_help` parameter. + + :param name: the name of the command to use unless a group overrides it. + :param context_settings: an optional dictionary with defaults that are + passed to the context object. + :param callback: the callback to invoke. This is optional. + :param params: the parameters to register with this command. This can + be either :class:`Option` or :class:`Argument` objects. + :param help: the help string to use for this command. + :param epilog: like the help string but it's printed at the end of the + help page after everything else. + :param short_help: the short help to use for this command. This is + shown on the command listing of the parent command. + :param add_help_option: by default each command registers a ``--help`` + option. This can be disabled by this parameter. + :param no_args_is_help: this controls what happens if no arguments are + provided. This option is disabled by default. + If enabled this will add ``--help`` as argument + if no arguments are passed + :param hidden: hide this command from help outputs. + + :param deprecated: issues a message indicating that + the command is deprecated. + """ + + def __init__( + self, + name: t.Optional[str], + context_settings: t.Optional[t.Dict[str, t.Any]] = None, + callback: t.Optional[t.Callable[..., t.Any]] = None, + params: t.Optional[t.List["Parameter"]] = None, + help: t.Optional[str] = None, + epilog: t.Optional[str] = None, + short_help: t.Optional[str] = None, + options_metavar: t.Optional[str] = "[OPTIONS]", + add_help_option: bool = True, + no_args_is_help: bool = False, + hidden: bool = False, + deprecated: bool = False, + ) -> None: + super().__init__(name, context_settings) + #: the callback to execute when the command fires. This might be + #: `None` in which case nothing happens. + self.callback = callback + #: the list of parameters for this command in the order they + #: should show up in the help page and execute. Eager parameters + #: will automatically be handled before non eager ones. + self.params: t.List["Parameter"] = params or [] + + # if a form feed (page break) is found in the help text, truncate help + # text to the content preceding the first form feed + if help and "\f" in help: + help = help.split("\f", 1)[0] + + self.help = help + self.epilog = epilog + self.options_metavar = options_metavar + self.short_help = short_help + self.add_help_option = add_help_option + self.no_args_is_help = no_args_is_help + self.hidden = hidden + self.deprecated = deprecated + + def to_info_dict(self, ctx: Context) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict(ctx) + info_dict.update( + params=[param.to_info_dict() for param in self.get_params(ctx)], + help=self.help, + epilog=self.epilog, + short_help=self.short_help, + hidden=self.hidden, + deprecated=self.deprecated, + ) + return info_dict + + def get_usage(self, ctx: Context) -> str: + """Formats the usage line into a string and returns it. + + Calls :meth:`format_usage` internally. + """ + formatter = ctx.make_formatter() + self.format_usage(ctx, formatter) + return formatter.getvalue().rstrip("\n") + + def get_params(self, ctx: Context) -> t.List["Parameter"]: + rv = self.params + help_option = self.get_help_option(ctx) + + if help_option is not None: + rv = [*rv, help_option] + + return rv + + def format_usage(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the usage line into the formatter. + + This is a low-level method called by :meth:`get_usage`. + """ + pieces = self.collect_usage_pieces(ctx) + formatter.write_usage(ctx.command_path, " ".join(pieces)) + + def collect_usage_pieces(self, ctx: Context) -> t.List[str]: + """Returns all the pieces that go into the usage line and returns + it as a list of strings. + """ + rv = [self.options_metavar] if self.options_metavar else [] + + for param in self.get_params(ctx): + rv.extend(param.get_usage_pieces(ctx)) + + return rv + + def get_help_option_names(self, ctx: Context) -> t.List[str]: + """Returns the names for the help option.""" + all_names = set(ctx.help_option_names) + for param in self.params: + all_names.difference_update(param.opts) + all_names.difference_update(param.secondary_opts) + return list(all_names) + + def get_help_option(self, ctx: Context) -> t.Optional["Option"]: + """Returns the help option object.""" + help_options = self.get_help_option_names(ctx) + + if not help_options or not self.add_help_option: + return None + + def show_help(ctx: Context, param: "Parameter", value: str) -> None: + if value and not ctx.resilient_parsing: + echo(ctx.get_help(), color=ctx.color) + ctx.exit() + + return Option( + help_options, + is_flag=True, + is_eager=True, + expose_value=False, + callback=show_help, + help=_("Show this message and exit."), + ) + + def make_parser(self, ctx: Context) -> OptionParser: + """Creates the underlying option parser for this command.""" + parser = OptionParser(ctx) + for param in self.get_params(ctx): + param.add_to_parser(parser, ctx) + return parser + + def get_help(self, ctx: Context) -> str: + """Formats the help into a string and returns it. + + Calls :meth:`format_help` internally. + """ + formatter = ctx.make_formatter() + self.format_help(ctx, formatter) + return formatter.getvalue().rstrip("\n") + + def get_short_help_str(self, limit: int = 45) -> str: + """Gets short help for the command or makes it by shortening the + long help string. + """ + text = self.short_help or "" + + if not text and self.help: + text = make_default_short_help(self.help, limit) + + if self.deprecated: + text = _("(Deprecated) {text}").format(text=text) + + return text.strip() + + def format_help(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the help into the formatter if it exists. + + This is a low-level method called by :meth:`get_help`. + + This calls the following methods: + + - :meth:`format_usage` + - :meth:`format_help_text` + - :meth:`format_options` + - :meth:`format_epilog` + """ + self.format_usage(ctx, formatter) + self.format_help_text(ctx, formatter) + self.format_options(ctx, formatter) + self.format_epilog(ctx, formatter) + + def format_help_text(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the help text to the formatter if it exists.""" + text = self.help or "" + + if self.deprecated: + text = _("(Deprecated) {text}").format(text=text) + + if text: + formatter.write_paragraph() + + with formatter.indentation(): + formatter.write_text(text) + + def format_options(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes all the options into the formatter if they exist.""" + opts = [] + for param in self.get_params(ctx): + rv = param.get_help_record(ctx) + if rv is not None: + opts.append(rv) + + if opts: + with formatter.section(_("Options")): + formatter.write_dl(opts) + + def format_epilog(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the epilog into the formatter if it exists.""" + if self.epilog: + formatter.write_paragraph() + with formatter.indentation(): + formatter.write_text(self.epilog) + + def parse_args(self, ctx: Context, args: t.List[str]) -> t.List[str]: + if not args and self.no_args_is_help and not ctx.resilient_parsing: + echo(ctx.get_help(), color=ctx.color) + ctx.exit() + + parser = self.make_parser(ctx) + opts, args, param_order = parser.parse_args(args=args) + + for param in iter_params_for_processing(param_order, self.get_params(ctx)): + value, args = param.handle_parse_result(ctx, opts, args) + + if args and not ctx.allow_extra_args and not ctx.resilient_parsing: + ctx.fail( + ngettext( + "Got unexpected extra argument ({args})", + "Got unexpected extra arguments ({args})", + len(args), + ).format(args=" ".join(map(str, args))) + ) + + ctx.args = args + return args + + def invoke(self, ctx: Context) -> t.Any: + """Given a context, this invokes the attached callback (if it exists) + in the right way. + """ + if self.deprecated: + message = _( + "DeprecationWarning: The command {name!r} is deprecated." + ).format(name=self.name) + echo(style(message, fg="red"), err=True) + + if self.callback is not None: + return ctx.invoke(self.callback, **ctx.params) + + def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: + """Return a list of completions for the incomplete value. Looks + at the names of options and chained multi-commands. + + :param ctx: Invocation context for this command. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + results: t.List["CompletionItem"] = [] + + if incomplete and not incomplete[0].isalnum(): + for param in self.get_params(ctx): + if ( + not isinstance(param, Option) + or param.hidden + or ( + not param.multiple + and ctx.get_parameter_source(param.name) # type: ignore + is ParameterSource.COMMANDLINE + ) + ): + continue + + results.extend( + CompletionItem(name, help=param.help) + for name in [*param.opts, *param.secondary_opts] + if name.startswith(incomplete) + ) + + results.extend(super().shell_complete(ctx, incomplete)) + return results + + +class MultiCommand(Command): + """A multi command is the basic implementation of a command that + dispatches to subcommands. The most common version is the + :class:`Group`. + + :param invoke_without_command: this controls how the multi command itself + is invoked. By default it's only invoked + if a subcommand is provided. + :param no_args_is_help: this controls what happens if no arguments are + provided. This option is enabled by default if + `invoke_without_command` is disabled or disabled + if it's enabled. If enabled this will add + ``--help`` as argument if no arguments are + passed. + :param subcommand_metavar: the string that is used in the documentation + to indicate the subcommand place. + :param chain: if this is set to `True` chaining of multiple subcommands + is enabled. This restricts the form of commands in that + they cannot have optional arguments but it allows + multiple commands to be chained together. + :param result_callback: The result callback to attach to this multi + command. This can be set or changed later with the + :meth:`result_callback` decorator. + """ + + allow_extra_args = True + allow_interspersed_args = False + + def __init__( + self, + name: t.Optional[str] = None, + invoke_without_command: bool = False, + no_args_is_help: t.Optional[bool] = None, + subcommand_metavar: t.Optional[str] = None, + chain: bool = False, + result_callback: t.Optional[t.Callable[..., t.Any]] = None, + **attrs: t.Any, + ) -> None: + super().__init__(name, **attrs) + + if no_args_is_help is None: + no_args_is_help = not invoke_without_command + + self.no_args_is_help = no_args_is_help + self.invoke_without_command = invoke_without_command + + if subcommand_metavar is None: + if chain: + subcommand_metavar = "COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]..." + else: + subcommand_metavar = "COMMAND [ARGS]..." + + self.subcommand_metavar = subcommand_metavar + self.chain = chain + # The result callback that is stored. This can be set or + # overridden with the :func:`result_callback` decorator. + self._result_callback = result_callback + + if self.chain: + for param in self.params: + if isinstance(param, Argument) and not param.required: + raise RuntimeError( + "Multi commands in chain mode cannot have" + " optional arguments." + ) + + def to_info_dict(self, ctx: Context) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict(ctx) + commands = {} + + for name in self.list_commands(ctx): + command = self.get_command(ctx, name) + + if command is None: + continue + + sub_ctx = ctx._make_sub_context(command) + + with sub_ctx.scope(cleanup=False): + commands[name] = command.to_info_dict(sub_ctx) + + info_dict.update(commands=commands, chain=self.chain) + return info_dict + + def collect_usage_pieces(self, ctx: Context) -> t.List[str]: + rv = super().collect_usage_pieces(ctx) + rv.append(self.subcommand_metavar) + return rv + + def format_options(self, ctx: Context, formatter: HelpFormatter) -> None: + super().format_options(ctx, formatter) + self.format_commands(ctx, formatter) + + def result_callback(self, replace: bool = False) -> t.Callable[[F], F]: + """Adds a result callback to the command. By default if a + result callback is already registered this will chain them but + this can be disabled with the `replace` parameter. The result + callback is invoked with the return value of the subcommand + (or the list of return values from all subcommands if chaining + is enabled) as well as the parameters as they would be passed + to the main callback. + + Example:: + + @click.group() + @click.option('-i', '--input', default=23) + def cli(input): + return 42 + + @cli.result_callback() + def process_result(result, input): + return result + input + + :param replace: if set to `True` an already existing result + callback will be removed. + + .. versionchanged:: 8.0 + Renamed from ``resultcallback``. + + .. versionadded:: 3.0 + """ + + def decorator(f: F) -> F: + old_callback = self._result_callback + + if old_callback is None or replace: + self._result_callback = f + return f + + def function(__value, *args, **kwargs): # type: ignore + inner = old_callback(__value, *args, **kwargs) # type: ignore + return f(inner, *args, **kwargs) + + self._result_callback = rv = update_wrapper(t.cast(F, function), f) + return rv + + return decorator + + def resultcallback(self, replace: bool = False) -> t.Callable[[F], F]: + import warnings + + warnings.warn( + "'resultcallback' has been renamed to 'result_callback'." + " The old name will be removed in Click 8.1.", + DeprecationWarning, + stacklevel=2, + ) + return self.result_callback(replace=replace) + + def format_commands(self, ctx: Context, formatter: HelpFormatter) -> None: + """Extra format methods for multi methods that adds all the commands + after the options. + """ + commands = [] + for subcommand in self.list_commands(ctx): + cmd = self.get_command(ctx, subcommand) + # What is this, the tool lied about a command. Ignore it + if cmd is None: + continue + if cmd.hidden: + continue + + commands.append((subcommand, cmd)) + + # allow for 3 times the default spacing + if len(commands): + limit = formatter.width - 6 - max(len(cmd[0]) for cmd in commands) + + rows = [] + for subcommand, cmd in commands: + help = cmd.get_short_help_str(limit) + rows.append((subcommand, help)) + + if rows: + with formatter.section(_("Commands")): + formatter.write_dl(rows) + + def parse_args(self, ctx: Context, args: t.List[str]) -> t.List[str]: + if not args and self.no_args_is_help and not ctx.resilient_parsing: + echo(ctx.get_help(), color=ctx.color) + ctx.exit() + + rest = super().parse_args(ctx, args) + + if self.chain: + ctx.protected_args = rest + ctx.args = [] + elif rest: + ctx.protected_args, ctx.args = rest[:1], rest[1:] + + return ctx.args + + def invoke(self, ctx: Context) -> t.Any: + def _process_result(value: t.Any) -> t.Any: + if self._result_callback is not None: + value = ctx.invoke(self._result_callback, value, **ctx.params) + return value + + if not ctx.protected_args: + if self.invoke_without_command: + # No subcommand was invoked, so the result callback is + # invoked with None for regular groups, or an empty list + # for chained groups. + with ctx: + super().invoke(ctx) + return _process_result([] if self.chain else None) + ctx.fail(_("Missing command.")) + + # Fetch args back out + args = [*ctx.protected_args, *ctx.args] + ctx.args = [] + ctx.protected_args = [] + + # If we're not in chain mode, we only allow the invocation of a + # single command but we also inform the current context about the + # name of the command to invoke. + if not self.chain: + # Make sure the context is entered so we do not clean up + # resources until the result processor has worked. + with ctx: + cmd_name, cmd, args = self.resolve_command(ctx, args) + assert cmd is not None + ctx.invoked_subcommand = cmd_name + super().invoke(ctx) + sub_ctx = cmd.make_context(cmd_name, args, parent=ctx) + with sub_ctx: + return _process_result(sub_ctx.command.invoke(sub_ctx)) + + # In chain mode we create the contexts step by step, but after the + # base command has been invoked. Because at that point we do not + # know the subcommands yet, the invoked subcommand attribute is + # set to ``*`` to inform the command that subcommands are executed + # but nothing else. + with ctx: + ctx.invoked_subcommand = "*" if args else None + super().invoke(ctx) + + # Otherwise we make every single context and invoke them in a + # chain. In that case the return value to the result processor + # is the list of all invoked subcommand's results. + contexts = [] + while args: + cmd_name, cmd, args = self.resolve_command(ctx, args) + assert cmd is not None + sub_ctx = cmd.make_context( + cmd_name, + args, + parent=ctx, + allow_extra_args=True, + allow_interspersed_args=False, + ) + contexts.append(sub_ctx) + args, sub_ctx.args = sub_ctx.args, [] + + rv = [] + for sub_ctx in contexts: + with sub_ctx: + rv.append(sub_ctx.command.invoke(sub_ctx)) + return _process_result(rv) + + def resolve_command( + self, ctx: Context, args: t.List[str] + ) -> t.Tuple[t.Optional[str], t.Optional[Command], t.List[str]]: + cmd_name = make_str(args[0]) + original_cmd_name = cmd_name + + # Get the command + cmd = self.get_command(ctx, cmd_name) + + # If we can't find the command but there is a normalization + # function available, we try with that one. + if cmd is None and ctx.token_normalize_func is not None: + cmd_name = ctx.token_normalize_func(cmd_name) + cmd = self.get_command(ctx, cmd_name) + + # If we don't find the command we want to show an error message + # to the user that it was not provided. However, there is + # something else we should do: if the first argument looks like + # an option we want to kick off parsing again for arguments to + # resolve things like --help which now should go to the main + # place. + if cmd is None and not ctx.resilient_parsing: + if split_opt(cmd_name)[0]: + self.parse_args(ctx, ctx.args) + ctx.fail(_("No such command {name!r}.").format(name=original_cmd_name)) + return cmd_name if cmd else None, cmd, args[1:] + + def get_command(self, ctx: Context, cmd_name: str) -> t.Optional[Command]: + """Given a context and a command name, this returns a + :class:`Command` object if it exists or returns `None`. + """ + raise NotImplementedError + + def list_commands(self, ctx: Context) -> t.List[str]: + """Returns a list of subcommand names in the order they should + appear. + """ + return [] + + def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: + """Return a list of completions for the incomplete value. Looks + at the names of options, subcommands, and chained + multi-commands. + + :param ctx: Invocation context for this command. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + results = [ + CompletionItem(name, help=command.get_short_help_str()) + for name, command in _complete_visible_commands(ctx, incomplete) + ] + results.extend(super().shell_complete(ctx, incomplete)) + return results + + +class Group(MultiCommand): + """A group allows a command to have subcommands attached. This is + the most common way to implement nesting in Click. + + :param name: The name of the group command. + :param commands: A dict mapping names to :class:`Command` objects. + Can also be a list of :class:`Command`, which will use + :attr:`Command.name` to create the dict. + :param attrs: Other command arguments described in + :class:`MultiCommand`, :class:`Command`, and + :class:`BaseCommand`. + + .. versionchanged:: 8.0 + The ``commmands`` argument can be a list of command objects. + """ + + #: If set, this is used by the group's :meth:`command` decorator + #: as the default :class:`Command` class. This is useful to make all + #: subcommands use a custom command class. + #: + #: .. versionadded:: 8.0 + command_class: t.Optional[t.Type[Command]] = None + + #: If set, this is used by the group's :meth:`group` decorator + #: as the default :class:`Group` class. This is useful to make all + #: subgroups use a custom group class. + #: + #: If set to the special value :class:`type` (literally + #: ``group_class = type``), this group's class will be used as the + #: default class. This makes a custom group class continue to make + #: custom groups. + #: + #: .. versionadded:: 8.0 + group_class: t.Optional[t.Union[t.Type["Group"], t.Type[type]]] = None + # Literal[type] isn't valid, so use Type[type] + + def __init__( + self, + name: t.Optional[str] = None, + commands: t.Optional[t.Union[t.Dict[str, Command], t.Sequence[Command]]] = None, + **attrs: t.Any, + ) -> None: + super().__init__(name, **attrs) + + if commands is None: + commands = {} + elif isinstance(commands, abc.Sequence): + commands = {c.name: c for c in commands if c.name is not None} + + #: The registered subcommands by their exported names. + self.commands: t.Dict[str, Command] = commands + + def add_command(self, cmd: Command, name: t.Optional[str] = None) -> None: + """Registers another :class:`Command` with this group. If the name + is not provided, the name of the command is used. + """ + name = name or cmd.name + if name is None: + raise TypeError("Command has no name.") + _check_multicommand(self, name, cmd, register=True) + self.commands[name] = cmd + + def command( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Callable[[t.Callable[..., t.Any]], Command]: + """A shortcut decorator for declaring and attaching a command to + the group. This takes the same arguments as :func:`command` and + immediately registers the created command with this group by + calling :meth:`add_command`. + + To customize the command class used, set the + :attr:`command_class` attribute. + + .. versionchanged:: 8.0 + Added the :attr:`command_class` attribute. + """ + from .decorators import command + + if self.command_class is not None and "cls" not in kwargs: + kwargs["cls"] = self.command_class + + def decorator(f: t.Callable[..., t.Any]) -> Command: + cmd = command(*args, **kwargs)(f) + self.add_command(cmd) + return cmd + + return decorator + + def group( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Callable[[t.Callable[..., t.Any]], "Group"]: + """A shortcut decorator for declaring and attaching a group to + the group. This takes the same arguments as :func:`group` and + immediately registers the created group with this group by + calling :meth:`add_command`. + + To customize the group class used, set the :attr:`group_class` + attribute. + + .. versionchanged:: 8.0 + Added the :attr:`group_class` attribute. + """ + from .decorators import group + + if self.group_class is not None and "cls" not in kwargs: + if self.group_class is type: + kwargs["cls"] = type(self) + else: + kwargs["cls"] = self.group_class + + def decorator(f: t.Callable[..., t.Any]) -> "Group": + cmd = group(*args, **kwargs)(f) + self.add_command(cmd) + return cmd + + return decorator + + def get_command(self, ctx: Context, cmd_name: str) -> t.Optional[Command]: + return self.commands.get(cmd_name) + + def list_commands(self, ctx: Context) -> t.List[str]: + return sorted(self.commands) + + +class CommandCollection(MultiCommand): + """A command collection is a multi command that merges multiple multi + commands together into one. This is a straightforward implementation + that accepts a list of different multi commands as sources and + provides all the commands for each of them. + """ + + def __init__( + self, + name: t.Optional[str] = None, + sources: t.Optional[t.List[MultiCommand]] = None, + **attrs: t.Any, + ) -> None: + super().__init__(name, **attrs) + #: The list of registered multi commands. + self.sources: t.List[MultiCommand] = sources or [] + + def add_source(self, multi_cmd: MultiCommand) -> None: + """Adds a new multi command to the chain dispatcher.""" + self.sources.append(multi_cmd) + + def get_command(self, ctx: Context, cmd_name: str) -> t.Optional[Command]: + for source in self.sources: + rv = source.get_command(ctx, cmd_name) + + if rv is not None: + if self.chain: + _check_multicommand(self, cmd_name, rv) + + return rv + + return None + + def list_commands(self, ctx: Context) -> t.List[str]: + rv: t.Set[str] = set() + + for source in self.sources: + rv.update(source.list_commands(ctx)) + + return sorted(rv) + + +def _check_iter(value: t.Any) -> t.Iterator[t.Any]: + """Check if the value is iterable but not a string. Raises a type + error, or return an iterator over the value. + """ + if isinstance(value, str): + raise TypeError + + return iter(value) + + +class Parameter: + r"""A parameter to a command comes in two versions: they are either + :class:`Option`\s or :class:`Argument`\s. Other subclasses are currently + not supported by design as some of the internals for parsing are + intentionally not finalized. + + Some settings are supported by both options and arguments. + + :param param_decls: the parameter declarations for this option or + argument. This is a list of flags or argument + names. + :param type: the type that should be used. Either a :class:`ParamType` + or a Python type. The later is converted into the former + automatically if supported. + :param required: controls if this is optional or not. + :param default: the default value if omitted. This can also be a callable, + in which case it's invoked when the default is needed + without any arguments. + :param callback: A function to further process or validate the value + after type conversion. It is called as ``f(ctx, param, value)`` + and must return the value. It is called for all sources, + including prompts. + :param nargs: the number of arguments to match. If not ``1`` the return + value is a tuple instead of single value. The default for + nargs is ``1`` (except if the type is a tuple, then it's + the arity of the tuple). If ``nargs=-1``, all remaining + parameters are collected. + :param metavar: how the value is represented in the help page. + :param expose_value: if this is `True` then the value is passed onwards + to the command callback and stored on the context, + otherwise it's skipped. + :param is_eager: eager values are processed before non eager ones. This + should not be set for arguments or it will inverse the + order of processing. + :param envvar: a string or list of strings that are environment variables + that should be checked. + :param shell_complete: A function that returns custom shell + completions. Used instead of the param's type completion if + given. Takes ``ctx, param, incomplete`` and must return a list + of :class:`~click.shell_completion.CompletionItem` or a list of + strings. + + .. versionchanged:: 8.0 + ``process_value`` validates required parameters and bounded + ``nargs``, and invokes the parameter callback before returning + the value. This allows the callback to validate prompts. + ``full_process_value`` is removed. + + .. versionchanged:: 8.0 + ``autocompletion`` is renamed to ``shell_complete`` and has new + semantics described above. The old name is deprecated and will + be removed in 8.1, until then it will be wrapped to match the + new requirements. + + .. versionchanged:: 8.0 + For ``multiple=True, nargs>1``, the default must be a list of + tuples. + + .. versionchanged:: 8.0 + Setting a default is no longer required for ``nargs>1``, it will + default to ``None``. ``multiple=True`` or ``nargs=-1`` will + default to ``()``. + + .. versionchanged:: 7.1 + Empty environment variables are ignored rather than taking the + empty string value. This makes it possible for scripts to clear + variables if they can't unset them. + + .. versionchanged:: 2.0 + Changed signature for parameter callback to also be passed the + parameter. The old callback format will still work, but it will + raise a warning to give you a chance to migrate the code easier. + """ + + param_type_name = "parameter" + + def __init__( + self, + param_decls: t.Optional[t.Sequence[str]] = None, + type: t.Optional[t.Union[types.ParamType, t.Any]] = None, + required: bool = False, + default: t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]] = None, + callback: t.Optional[t.Callable[[Context, "Parameter", t.Any], t.Any]] = None, + nargs: t.Optional[int] = None, + multiple: bool = False, + metavar: t.Optional[str] = None, + expose_value: bool = True, + is_eager: bool = False, + envvar: t.Optional[t.Union[str, t.Sequence[str]]] = None, + shell_complete: t.Optional[ + t.Callable[ + [Context, "Parameter", str], + t.Union[t.List["CompletionItem"], t.List[str]], + ] + ] = None, + autocompletion: t.Optional[ + t.Callable[ + [Context, t.List[str], str], t.List[t.Union[t.Tuple[str, str], str]] + ] + ] = None, + ) -> None: + self.name, self.opts, self.secondary_opts = self._parse_decls( + param_decls or (), expose_value + ) + self.type = types.convert_type(type, default) + + # Default nargs to what the type tells us if we have that + # information available. + if nargs is None: + if self.type.is_composite: + nargs = self.type.arity + else: + nargs = 1 + + self.required = required + self.callback = callback + self.nargs = nargs + self.multiple = multiple + self.expose_value = expose_value + self.default = default + self.is_eager = is_eager + self.metavar = metavar + self.envvar = envvar + + if autocompletion is not None: + import warnings + + warnings.warn( + "'autocompletion' is renamed to 'shell_complete'. The old name is" + " deprecated and will be removed in Click 8.1. See the docs about" + " 'Parameter' for information about new behavior.", + DeprecationWarning, + stacklevel=2, + ) + + def shell_complete( + ctx: Context, param: "Parameter", incomplete: str + ) -> t.List["CompletionItem"]: + from click.shell_completion import CompletionItem + + out = [] + + for c in autocompletion(ctx, [], incomplete): # type: ignore + if isinstance(c, tuple): + c = CompletionItem(c[0], help=c[1]) + elif isinstance(c, str): + c = CompletionItem(c) + + if c.value.startswith(incomplete): + out.append(c) + + return out + + self._custom_shell_complete = shell_complete + + if __debug__: + if self.type.is_composite and nargs != self.type.arity: + raise ValueError( + f"'nargs' must be {self.type.arity} (or None) for" + f" type {self.type!r}, but it was {nargs}." + ) + + # Skip no default or callable default. + check_default = default if not callable(default) else None + + if check_default is not None: + if multiple: + try: + # Only check the first value against nargs. + check_default = next(_check_iter(check_default), None) + except TypeError: + raise ValueError( + "'default' must be a list when 'multiple' is true." + ) from None + + # Can be None for multiple with empty default. + if nargs != 1 and check_default is not None: + try: + _check_iter(check_default) + except TypeError: + if multiple: + message = ( + "'default' must be a list of lists when 'multiple' is" + " true and 'nargs' != 1." + ) + else: + message = "'default' must be a list when 'nargs' != 1." + + raise ValueError(message) from None + + if nargs > 1 and len(check_default) != nargs: + subject = "item length" if multiple else "length" + raise ValueError( + f"'default' {subject} must match nargs={nargs}." + ) + + def to_info_dict(self) -> t.Dict[str, t.Any]: + """Gather information that could be useful for a tool generating + user-facing documentation. + + Use :meth:`click.Context.to_info_dict` to traverse the entire + CLI structure. + + .. versionadded:: 8.0 + """ + return { + "name": self.name, + "param_type_name": self.param_type_name, + "opts": self.opts, + "secondary_opts": self.secondary_opts, + "type": self.type.to_info_dict(), + "required": self.required, + "nargs": self.nargs, + "multiple": self.multiple, + "default": self.default, + "envvar": self.envvar, + } + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} {self.name}>" + + def _parse_decls( + self, decls: t.Sequence[str], expose_value: bool + ) -> t.Tuple[t.Optional[str], t.List[str], t.List[str]]: + raise NotImplementedError() + + @property + def human_readable_name(self) -> str: + """Returns the human readable name of this parameter. This is the + same as the name for options, but the metavar for arguments. + """ + return self.name # type: ignore + + def make_metavar(self) -> str: + if self.metavar is not None: + return self.metavar + + metavar = self.type.get_metavar(self) + + if metavar is None: + metavar = self.type.name.upper() + + if self.nargs != 1: + metavar += "..." + + return metavar + + @typing.overload + def get_default( + self, ctx: Context, call: "te.Literal[True]" = True + ) -> t.Optional[t.Any]: + ... + + @typing.overload + def get_default( + self, ctx: Context, call: bool = ... + ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: + ... + + def get_default( + self, ctx: Context, call: bool = True + ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: + """Get the default for the parameter. Tries + :meth:`Context.lookup_default` first, then the local default. + + :param ctx: Current context. + :param call: If the default is a callable, call it. Disable to + return the callable instead. + + .. versionchanged:: 8.0.2 + Type casting is no longer performed when getting a default. + + .. versionchanged:: 8.0.1 + Type casting can fail in resilient parsing mode. Invalid + defaults will not prevent showing help text. + + .. versionchanged:: 8.0 + Looks at ``ctx.default_map`` first. + + .. versionchanged:: 8.0 + Added the ``call`` parameter. + """ + value = ctx.lookup_default(self.name, call=False) # type: ignore + + if value is None: + value = self.default + + if call and callable(value): + value = value() + + return value + + def add_to_parser(self, parser: OptionParser, ctx: Context) -> None: + raise NotImplementedError() + + def consume_value( + self, ctx: Context, opts: t.Mapping[str, t.Any] + ) -> t.Tuple[t.Any, ParameterSource]: + value = opts.get(self.name) # type: ignore + source = ParameterSource.COMMANDLINE + + if value is None: + value = self.value_from_envvar(ctx) + source = ParameterSource.ENVIRONMENT + + if value is None: + value = ctx.lookup_default(self.name) # type: ignore + source = ParameterSource.DEFAULT_MAP + + if value is None: + value = self.get_default(ctx) + source = ParameterSource.DEFAULT + + return value, source + + def type_cast_value(self, ctx: Context, value: t.Any) -> t.Any: + """Convert and validate a value against the option's + :attr:`type`, :attr:`multiple`, and :attr:`nargs`. + """ + if value is None: + return () if self.multiple or self.nargs == -1 else None + + def check_iter(value: t.Any) -> t.Iterator: + try: + return _check_iter(value) + except TypeError: + # This should only happen when passing in args manually, + # the parser should construct an iterable when parsing + # the command line. + raise BadParameter( + _("Value must be an iterable."), ctx=ctx, param=self + ) from None + + if self.nargs == 1 or self.type.is_composite: + convert: t.Callable[[t.Any], t.Any] = partial( + self.type, param=self, ctx=ctx + ) + elif self.nargs == -1: + + def convert(value: t.Any) -> t.Tuple: + return tuple(self.type(x, self, ctx) for x in check_iter(value)) + + else: # nargs > 1 + + def convert(value: t.Any) -> t.Tuple: + value = tuple(check_iter(value)) + + if len(value) != self.nargs: + raise BadParameter( + ngettext( + "Takes {nargs} values but 1 was given.", + "Takes {nargs} values but {len} were given.", + len(value), + ).format(nargs=self.nargs, len=len(value)), + ctx=ctx, + param=self, + ) + + return tuple(self.type(x, self, ctx) for x in value) + + if self.multiple: + return tuple(convert(x) for x in check_iter(value)) + + return convert(value) + + def value_is_missing(self, value: t.Any) -> bool: + if value is None: + return True + + if (self.nargs != 1 or self.multiple) and value == (): + return True + + return False + + def process_value(self, ctx: Context, value: t.Any) -> t.Any: + value = self.type_cast_value(ctx, value) + + if self.required and self.value_is_missing(value): + raise MissingParameter(ctx=ctx, param=self) + + if self.callback is not None: + value = self.callback(ctx, self, value) + + return value + + def resolve_envvar_value(self, ctx: Context) -> t.Optional[str]: + if self.envvar is None: + return None + + if isinstance(self.envvar, str): + rv = os.environ.get(self.envvar) + + if rv: + return rv + else: + for envvar in self.envvar: + rv = os.environ.get(envvar) + + if rv: + return rv + + return None + + def value_from_envvar(self, ctx: Context) -> t.Optional[t.Any]: + rv: t.Optional[t.Any] = self.resolve_envvar_value(ctx) + + if rv is not None and self.nargs != 1: + rv = self.type.split_envvar_value(rv) + + return rv + + def handle_parse_result( + self, ctx: Context, opts: t.Mapping[str, t.Any], args: t.List[str] + ) -> t.Tuple[t.Any, t.List[str]]: + with augment_usage_errors(ctx, param=self): + value, source = self.consume_value(ctx, opts) + ctx.set_parameter_source(self.name, source) # type: ignore + + try: + value = self.process_value(ctx, value) + except Exception: + if not ctx.resilient_parsing: + raise + + value = None + + if self.expose_value: + ctx.params[self.name] = value # type: ignore + + return value, args + + def get_help_record(self, ctx: Context) -> t.Optional[t.Tuple[str, str]]: + pass + + def get_usage_pieces(self, ctx: Context) -> t.List[str]: + return [] + + def get_error_hint(self, ctx: Context) -> str: + """Get a stringified version of the param for use in error messages to + indicate which param caused the error. + """ + hint_list = self.opts or [self.human_readable_name] + return " / ".join(f"'{x}'" for x in hint_list) + + def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: + """Return a list of completions for the incomplete value. If a + ``shell_complete`` function was given during init, it is used. + Otherwise, the :attr:`type` + :meth:`~click.types.ParamType.shell_complete` function is used. + + :param ctx: Invocation context for this command. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + if self._custom_shell_complete is not None: + results = self._custom_shell_complete(ctx, self, incomplete) + + if results and isinstance(results[0], str): + from click.shell_completion import CompletionItem + + results = [CompletionItem(c) for c in results] + + return t.cast(t.List["CompletionItem"], results) + + return self.type.shell_complete(ctx, self, incomplete) + + +class Option(Parameter): + """Options are usually optional values on the command line and + have some extra features that arguments don't have. + + All other parameters are passed onwards to the parameter constructor. + + :param show_default: controls if the default value should be shown on the + help page. Normally, defaults are not shown. If this + value is a string, it shows the string instead of the + value. This is particularly useful for dynamic options. + :param show_envvar: controls if an environment variable should be shown on + the help page. Normally, environment variables + are not shown. + :param prompt: if set to `True` or a non empty string then the user will be + prompted for input. If set to `True` the prompt will be the + option name capitalized. + :param confirmation_prompt: Prompt a second time to confirm the + value if it was prompted for. Can be set to a string instead of + ``True`` to customize the message. + :param prompt_required: If set to ``False``, the user will be + prompted for input only when the option was specified as a flag + without a value. + :param hide_input: if this is `True` then the input on the prompt will be + hidden from the user. This is useful for password + input. + :param is_flag: forces this option to act as a flag. The default is + auto detection. + :param flag_value: which value should be used for this flag if it's + enabled. This is set to a boolean automatically if + the option string contains a slash to mark two options. + :param multiple: if this is set to `True` then the argument is accepted + multiple times and recorded. This is similar to ``nargs`` + in how it works but supports arbitrary number of + arguments. + :param count: this flag makes an option increment an integer. + :param allow_from_autoenv: if this is enabled then the value of this + parameter will be pulled from an environment + variable in case a prefix is defined on the + context. + :param help: the help string. + :param hidden: hide this option from help outputs. + + .. versionchanged:: 8.0.1 + ``type`` is detected from ``flag_value`` if given. + """ + + param_type_name = "option" + + def __init__( + self, + param_decls: t.Optional[t.Sequence[str]] = None, + show_default: t.Union[bool, str] = False, + prompt: t.Union[bool, str] = False, + confirmation_prompt: t.Union[bool, str] = False, + prompt_required: bool = True, + hide_input: bool = False, + is_flag: t.Optional[bool] = None, + flag_value: t.Optional[t.Any] = None, + multiple: bool = False, + count: bool = False, + allow_from_autoenv: bool = True, + type: t.Optional[t.Union[types.ParamType, t.Any]] = None, + help: t.Optional[str] = None, + hidden: bool = False, + show_choices: bool = True, + show_envvar: bool = False, + **attrs: t.Any, + ) -> None: + default_is_missing = "default" not in attrs + super().__init__(param_decls, type=type, multiple=multiple, **attrs) + + if prompt is True: + if self.name is None: + raise TypeError("'name' is required with 'prompt=True'.") + + prompt_text: t.Optional[str] = self.name.replace("_", " ").capitalize() + elif prompt is False: + prompt_text = None + else: + prompt_text = t.cast(str, prompt) + + self.prompt = prompt_text + self.confirmation_prompt = confirmation_prompt + self.prompt_required = prompt_required + self.hide_input = hide_input + self.hidden = hidden + + # If prompt is enabled but not required, then the option can be + # used as a flag to indicate using prompt or flag_value. + self._flag_needs_value = self.prompt is not None and not self.prompt_required + + if is_flag is None: + if flag_value is not None: + # Implicitly a flag because flag_value was set. + is_flag = True + elif self._flag_needs_value: + # Not a flag, but when used as a flag it shows a prompt. + is_flag = False + else: + # Implicitly a flag because flag options were given. + is_flag = bool(self.secondary_opts) + elif is_flag is False and not self._flag_needs_value: + # Not a flag, and prompt is not enabled, can be used as a + # flag if flag_value is set. + self._flag_needs_value = flag_value is not None + + if is_flag and default_is_missing: + self.default: t.Union[t.Any, t.Callable[[], t.Any]] = False + + if flag_value is None: + flag_value = not self.default + + if is_flag and type is None: + # Re-guess the type from the flag value instead of the + # default. + self.type = types.convert_type(None, flag_value) + + self.is_flag: bool = is_flag + self.is_bool_flag = is_flag and isinstance(self.type, types.BoolParamType) + self.flag_value: t.Any = flag_value + + # Counting + self.count = count + if count: + if type is None: + self.type = types.IntRange(min=0) + if default_is_missing: + self.default = 0 + + self.allow_from_autoenv = allow_from_autoenv + self.help = help + self.show_default = show_default + self.show_choices = show_choices + self.show_envvar = show_envvar + + if __debug__: + if self.nargs == -1: + raise TypeError("nargs=-1 is not supported for options.") + + if self.prompt and self.is_flag and not self.is_bool_flag: + raise TypeError("'prompt' is not valid for non-boolean flag.") + + if not self.is_bool_flag and self.secondary_opts: + raise TypeError("Secondary flag is not valid for non-boolean flag.") + + if self.is_bool_flag and self.hide_input and self.prompt is not None: + raise TypeError( + "'prompt' with 'hide_input' is not valid for boolean flag." + ) + + if self.count: + if self.multiple: + raise TypeError("'count' is not valid with 'multiple'.") + + if self.is_flag: + raise TypeError("'count' is not valid with 'is_flag'.") + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict.update( + help=self.help, + prompt=self.prompt, + is_flag=self.is_flag, + flag_value=self.flag_value, + count=self.count, + hidden=self.hidden, + ) + return info_dict + + def _parse_decls( + self, decls: t.Sequence[str], expose_value: bool + ) -> t.Tuple[t.Optional[str], t.List[str], t.List[str]]: + opts = [] + secondary_opts = [] + name = None + possible_names = [] + + for decl in decls: + if decl.isidentifier(): + if name is not None: + raise TypeError(f"Name '{name}' defined twice") + name = decl + else: + split_char = ";" if decl[:1] == "/" else "/" + if split_char in decl: + first, second = decl.split(split_char, 1) + first = first.rstrip() + if first: + possible_names.append(split_opt(first)) + opts.append(first) + second = second.lstrip() + if second: + secondary_opts.append(second.lstrip()) + if first == second: + raise ValueError( + f"Boolean option {decl!r} cannot use the" + " same flag for true/false." + ) + else: + possible_names.append(split_opt(decl)) + opts.append(decl) + + if name is None and possible_names: + possible_names.sort(key=lambda x: -len(x[0])) # group long options first + name = possible_names[0][1].replace("-", "_").lower() + if not name.isidentifier(): + name = None + + if name is None: + if not expose_value: + return None, opts, secondary_opts + raise TypeError("Could not determine name for option") + + if not opts and not secondary_opts: + raise TypeError( + f"No options defined but a name was passed ({name})." + " Did you mean to declare an argument instead? Did" + f" you mean to pass '--{name}'?" + ) + + return name, opts, secondary_opts + + def add_to_parser(self, parser: OptionParser, ctx: Context) -> None: + if self.multiple: + action = "append" + elif self.count: + action = "count" + else: + action = "store" + + if self.is_flag: + action = f"{action}_const" + + if self.is_bool_flag and self.secondary_opts: + parser.add_option( + obj=self, opts=self.opts, dest=self.name, action=action, const=True + ) + parser.add_option( + obj=self, + opts=self.secondary_opts, + dest=self.name, + action=action, + const=False, + ) + else: + parser.add_option( + obj=self, + opts=self.opts, + dest=self.name, + action=action, + const=self.flag_value, + ) + else: + parser.add_option( + obj=self, + opts=self.opts, + dest=self.name, + action=action, + nargs=self.nargs, + ) + + def get_help_record(self, ctx: Context) -> t.Optional[t.Tuple[str, str]]: + if self.hidden: + return None + + any_prefix_is_slash = False + + def _write_opts(opts: t.Sequence[str]) -> str: + nonlocal any_prefix_is_slash + + rv, any_slashes = join_options(opts) + + if any_slashes: + any_prefix_is_slash = True + + if not self.is_flag and not self.count: + rv += f" {self.make_metavar()}" + + return rv + + rv = [_write_opts(self.opts)] + + if self.secondary_opts: + rv.append(_write_opts(self.secondary_opts)) + + help = self.help or "" + extra = [] + + if self.show_envvar: + envvar = self.envvar + + if envvar is None: + if ( + self.allow_from_autoenv + and ctx.auto_envvar_prefix is not None + and self.name is not None + ): + envvar = f"{ctx.auto_envvar_prefix}_{self.name.upper()}" + + if envvar is not None: + var_str = ( + envvar + if isinstance(envvar, str) + else ", ".join(str(d) for d in envvar) + ) + extra.append(_("env var: {var}").format(var=var_str)) + + # Temporarily enable resilient parsing to avoid type casting + # failing for the default. Might be possible to extend this to + # help formatting in general. + resilient = ctx.resilient_parsing + ctx.resilient_parsing = True + + try: + default_value = self.get_default(ctx, call=False) + finally: + ctx.resilient_parsing = resilient + + show_default_is_str = isinstance(self.show_default, str) + + if show_default_is_str or ( + default_value is not None and (self.show_default or ctx.show_default) + ): + if show_default_is_str: + default_string = f"({self.show_default})" + elif isinstance(default_value, (list, tuple)): + default_string = ", ".join(str(d) for d in default_value) + elif callable(default_value): + default_string = _("(dynamic)") + elif self.is_bool_flag and self.secondary_opts: + # For boolean flags that have distinct True/False opts, + # use the opt without prefix instead of the value. + default_string = split_opt( + (self.opts if self.default else self.secondary_opts)[0] + )[1] + else: + default_string = str(default_value) + + if default_string: + extra.append(_("default: {default}").format(default=default_string)) + + if ( + isinstance(self.type, types._NumberRangeBase) + # skip count with default range type + and not (self.count and self.type.min == 0 and self.type.max is None) + ): + range_str = self.type._describe_range() + + if range_str: + extra.append(range_str) + + if self.required: + extra.append(_("required")) + + if extra: + extra_str = "; ".join(extra) + help = f"{help} [{extra_str}]" if help else f"[{extra_str}]" + + return ("; " if any_prefix_is_slash else " / ").join(rv), help + + @typing.overload + def get_default( + self, ctx: Context, call: "te.Literal[True]" = True + ) -> t.Optional[t.Any]: + ... + + @typing.overload + def get_default( + self, ctx: Context, call: bool = ... + ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: + ... + + def get_default( + self, ctx: Context, call: bool = True + ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: + # If we're a non boolean flag our default is more complex because + # we need to look at all flags in the same group to figure out + # if we're the the default one in which case we return the flag + # value as default. + if self.is_flag and not self.is_bool_flag: + for param in ctx.command.params: + if param.name == self.name and param.default: + return param.flag_value # type: ignore + + return None + + return super().get_default(ctx, call=call) + + def prompt_for_value(self, ctx: Context) -> t.Any: + """This is an alternative flow that can be activated in the full + value processing if a value does not exist. It will prompt the + user until a valid value exists and then returns the processed + value as result. + """ + assert self.prompt is not None + + # Calculate the default before prompting anything to be stable. + default = self.get_default(ctx) + + # If this is a prompt for a flag we need to handle this + # differently. + if self.is_bool_flag: + return confirm(self.prompt, default) + + return prompt( + self.prompt, + default=default, + type=self.type, + hide_input=self.hide_input, + show_choices=self.show_choices, + confirmation_prompt=self.confirmation_prompt, + value_proc=lambda x: self.process_value(ctx, x), + ) + + def resolve_envvar_value(self, ctx: Context) -> t.Optional[str]: + rv = super().resolve_envvar_value(ctx) + + if rv is not None: + return rv + + if ( + self.allow_from_autoenv + and ctx.auto_envvar_prefix is not None + and self.name is not None + ): + envvar = f"{ctx.auto_envvar_prefix}_{self.name.upper()}" + rv = os.environ.get(envvar) + + return rv + + def value_from_envvar(self, ctx: Context) -> t.Optional[t.Any]: + rv: t.Optional[t.Any] = self.resolve_envvar_value(ctx) + + if rv is None: + return None + + value_depth = (self.nargs != 1) + bool(self.multiple) + + if value_depth > 0: + rv = self.type.split_envvar_value(rv) + + if self.multiple and self.nargs != 1: + rv = batch(rv, self.nargs) + + return rv + + def consume_value( + self, ctx: Context, opts: t.Mapping[str, "Parameter"] + ) -> t.Tuple[t.Any, ParameterSource]: + value, source = super().consume_value(ctx, opts) + + # The parser will emit a sentinel value if the option can be + # given as a flag without a value. This is different from None + # to distinguish from the flag not being given at all. + if value is _flag_needs_value: + if self.prompt is not None and not ctx.resilient_parsing: + value = self.prompt_for_value(ctx) + source = ParameterSource.PROMPT + else: + value = self.flag_value + source = ParameterSource.COMMANDLINE + + elif ( + self.multiple + and value is not None + and any(v is _flag_needs_value for v in value) + ): + value = [self.flag_value if v is _flag_needs_value else v for v in value] + source = ParameterSource.COMMANDLINE + + # The value wasn't set, or used the param's default, prompt if + # prompting is enabled. + elif ( + source in {None, ParameterSource.DEFAULT} + and self.prompt is not None + and (self.required or self.prompt_required) + and not ctx.resilient_parsing + ): + value = self.prompt_for_value(ctx) + source = ParameterSource.PROMPT + + return value, source + + +class Argument(Parameter): + """Arguments are positional parameters to a command. They generally + provide fewer features than options but can have infinite ``nargs`` + and are required by default. + + All parameters are passed onwards to the parameter constructor. + """ + + param_type_name = "argument" + + def __init__( + self, + param_decls: t.Sequence[str], + required: t.Optional[bool] = None, + **attrs: t.Any, + ) -> None: + if required is None: + if attrs.get("default") is not None: + required = False + else: + required = attrs.get("nargs", 1) > 0 + + if "multiple" in attrs: + raise TypeError("__init__() got an unexpected keyword argument 'multiple'.") + + super().__init__(param_decls, required=required, **attrs) + + if __debug__: + if self.default is not None and self.nargs == -1: + raise TypeError("'default' is not supported for nargs=-1.") + + @property + def human_readable_name(self) -> str: + if self.metavar is not None: + return self.metavar + return self.name.upper() # type: ignore + + def make_metavar(self) -> str: + if self.metavar is not None: + return self.metavar + var = self.type.get_metavar(self) + if not var: + var = self.name.upper() # type: ignore + if not self.required: + var = f"[{var}]" + if self.nargs != 1: + var += "..." + return var + + def _parse_decls( + self, decls: t.Sequence[str], expose_value: bool + ) -> t.Tuple[t.Optional[str], t.List[str], t.List[str]]: + if not decls: + if not expose_value: + return None, [], [] + raise TypeError("Could not determine name for argument") + if len(decls) == 1: + name = arg = decls[0] + name = name.replace("-", "_").lower() + else: + raise TypeError( + "Arguments take exactly one parameter declaration, got" + f" {len(decls)}." + ) + return name, [arg], [] + + def get_usage_pieces(self, ctx: Context) -> t.List[str]: + return [self.make_metavar()] + + def get_error_hint(self, ctx: Context) -> str: + return f"'{self.make_metavar()}'" + + def add_to_parser(self, parser: OptionParser, ctx: Context) -> None: + parser.add_argument(dest=self.name, nargs=self.nargs, obj=self) diff --git a/venv/lib/python3.7/site-packages/click/decorators.py b/venv/lib/python3.7/site-packages/click/decorators.py new file mode 100644 index 0000000..f1cc005 --- /dev/null +++ b/venv/lib/python3.7/site-packages/click/decorators.py @@ -0,0 +1,436 @@ +import inspect +import types +import typing as t +from functools import update_wrapper +from gettext import gettext as _ + +from .core import Argument +from .core import Command +from .core import Context +from .core import Group +from .core import Option +from .core import Parameter +from .globals import get_current_context +from .utils import echo + +F = t.TypeVar("F", bound=t.Callable[..., t.Any]) +FC = t.TypeVar("FC", t.Callable[..., t.Any], Command) + + +def pass_context(f: F) -> F: + """Marks a callback as wanting to receive the current context + object as first argument. + """ + + def new_func(*args, **kwargs): # type: ignore + return f(get_current_context(), *args, **kwargs) + + return update_wrapper(t.cast(F, new_func), f) + + +def pass_obj(f: F) -> F: + """Similar to :func:`pass_context`, but only pass the object on the + context onwards (:attr:`Context.obj`). This is useful if that object + represents the state of a nested system. + """ + + def new_func(*args, **kwargs): # type: ignore + return f(get_current_context().obj, *args, **kwargs) + + return update_wrapper(t.cast(F, new_func), f) + + +def make_pass_decorator( + object_type: t.Type, ensure: bool = False +) -> "t.Callable[[F], F]": + """Given an object type this creates a decorator that will work + similar to :func:`pass_obj` but instead of passing the object of the + current context, it will find the innermost context of type + :func:`object_type`. + + This generates a decorator that works roughly like this:: + + from functools import update_wrapper + + def decorator(f): + @pass_context + def new_func(ctx, *args, **kwargs): + obj = ctx.find_object(object_type) + return ctx.invoke(f, obj, *args, **kwargs) + return update_wrapper(new_func, f) + return decorator + + :param object_type: the type of the object to pass. + :param ensure: if set to `True`, a new object will be created and + remembered on the context if it's not there yet. + """ + + def decorator(f: F) -> F: + def new_func(*args, **kwargs): # type: ignore + ctx = get_current_context() + + if ensure: + obj = ctx.ensure_object(object_type) + else: + obj = ctx.find_object(object_type) + + if obj is None: + raise RuntimeError( + "Managed to invoke callback without a context" + f" object of type {object_type.__name__!r}" + " existing." + ) + + return ctx.invoke(f, obj, *args, **kwargs) + + return update_wrapper(t.cast(F, new_func), f) + + return decorator + + +def pass_meta_key( + key: str, *, doc_description: t.Optional[str] = None +) -> "t.Callable[[F], F]": + """Create a decorator that passes a key from + :attr:`click.Context.meta` as the first argument to the decorated + function. + + :param key: Key in ``Context.meta`` to pass. + :param doc_description: Description of the object being passed, + inserted into the decorator's docstring. Defaults to "the 'key' + key from Context.meta". + + .. versionadded:: 8.0 + """ + + def decorator(f: F) -> F: + def new_func(*args, **kwargs): # type: ignore + ctx = get_current_context() + obj = ctx.meta[key] + return ctx.invoke(f, obj, *args, **kwargs) + + return update_wrapper(t.cast(F, new_func), f) + + if doc_description is None: + doc_description = f"the {key!r} key from :attr:`click.Context.meta`" + + decorator.__doc__ = ( + f"Decorator that passes {doc_description} as the first argument" + " to the decorated function." + ) + return decorator + + +def _make_command( + f: F, + name: t.Optional[str], + attrs: t.MutableMapping[str, t.Any], + cls: t.Type[Command], +) -> Command: + if isinstance(f, Command): + raise TypeError("Attempted to convert a callback into a command twice.") + + try: + params = f.__click_params__ # type: ignore + params.reverse() + del f.__click_params__ # type: ignore + except AttributeError: + params = [] + + help = attrs.get("help") + + if help is None: + help = inspect.getdoc(f) + else: + help = inspect.cleandoc(help) + + attrs["help"] = help + return cls( + name=name or f.__name__.lower().replace("_", "-"), + callback=f, + params=params, + **attrs, + ) + + +def command( + name: t.Optional[str] = None, + cls: t.Optional[t.Type[Command]] = None, + **attrs: t.Any, +) -> t.Callable[[F], Command]: + r"""Creates a new :class:`Command` and uses the decorated function as + callback. This will also automatically attach all decorated + :func:`option`\s and :func:`argument`\s as parameters to the command. + + The name of the command defaults to the name of the function with + underscores replaced by dashes. If you want to change that, you can + pass the intended name as the first argument. + + All keyword arguments are forwarded to the underlying command class. + + Once decorated the function turns into a :class:`Command` instance + that can be invoked as a command line utility or be attached to a + command :class:`Group`. + + :param name: the name of the command. This defaults to the function + name with underscores replaced by dashes. + :param cls: the command class to instantiate. This defaults to + :class:`Command`. + """ + if cls is None: + cls = Command + + def decorator(f: t.Callable[..., t.Any]) -> Command: + cmd = _make_command(f, name, attrs, cls) # type: ignore + cmd.__doc__ = f.__doc__ + return cmd + + return decorator + + +def group(name: t.Optional[str] = None, **attrs: t.Any) -> t.Callable[[F], Group]: + """Creates a new :class:`Group` with a function as callback. This + works otherwise the same as :func:`command` just that the `cls` + parameter is set to :class:`Group`. + """ + attrs.setdefault("cls", Group) + return t.cast(Group, command(name, **attrs)) + + +def _param_memo(f: FC, param: Parameter) -> None: + if isinstance(f, Command): + f.params.append(param) + else: + if not hasattr(f, "__click_params__"): + f.__click_params__ = [] # type: ignore + + f.__click_params__.append(param) # type: ignore + + +def argument(*param_decls: str, **attrs: t.Any) -> t.Callable[[FC], FC]: + """Attaches an argument to the command. All positional arguments are + passed as parameter declarations to :class:`Argument`; all keyword + arguments are forwarded unchanged (except ``cls``). + This is equivalent to creating an :class:`Argument` instance manually + and attaching it to the :attr:`Command.params` list. + + :param cls: the argument class to instantiate. This defaults to + :class:`Argument`. + """ + + def decorator(f: FC) -> FC: + ArgumentClass = attrs.pop("cls", Argument) + _param_memo(f, ArgumentClass(param_decls, **attrs)) + return f + + return decorator + + +def option(*param_decls: str, **attrs: t.Any) -> t.Callable[[FC], FC]: + """Attaches an option to the command. All positional arguments are + passed as parameter declarations to :class:`Option`; all keyword + arguments are forwarded unchanged (except ``cls``). + This is equivalent to creating an :class:`Option` instance manually + and attaching it to the :attr:`Command.params` list. + + :param cls: the option class to instantiate. This defaults to + :class:`Option`. + """ + + def decorator(f: FC) -> FC: + # Issue 926, copy attrs, so pre-defined options can re-use the same cls= + option_attrs = attrs.copy() + + if "help" in option_attrs: + option_attrs["help"] = inspect.cleandoc(option_attrs["help"]) + OptionClass = option_attrs.pop("cls", Option) + _param_memo(f, OptionClass(param_decls, **option_attrs)) + return f + + return decorator + + +def confirmation_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: + """Add a ``--yes`` option which shows a prompt before continuing if + not passed. If the prompt is declined, the program will exit. + + :param param_decls: One or more option names. Defaults to the single + value ``"--yes"``. + :param kwargs: Extra arguments are passed to :func:`option`. + """ + + def callback(ctx: Context, param: Parameter, value: bool) -> None: + if not value: + ctx.abort() + + if not param_decls: + param_decls = ("--yes",) + + kwargs.setdefault("is_flag", True) + kwargs.setdefault("callback", callback) + kwargs.setdefault("expose_value", False) + kwargs.setdefault("prompt", "Do you want to continue?") + kwargs.setdefault("help", "Confirm the action without prompting.") + return option(*param_decls, **kwargs) + + +def password_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: + """Add a ``--password`` option which prompts for a password, hiding + input and asking to enter the value again for confirmation. + + :param param_decls: One or more option names. Defaults to the single + value ``"--password"``. + :param kwargs: Extra arguments are passed to :func:`option`. + """ + if not param_decls: + param_decls = ("--password",) + + kwargs.setdefault("prompt", True) + kwargs.setdefault("confirmation_prompt", True) + kwargs.setdefault("hide_input", True) + return option(*param_decls, **kwargs) + + +def version_option( + version: t.Optional[str] = None, + *param_decls: str, + package_name: t.Optional[str] = None, + prog_name: t.Optional[str] = None, + message: t.Optional[str] = None, + **kwargs: t.Any, +) -> t.Callable[[FC], FC]: + """Add a ``--version`` option which immediately prints the version + number and exits the program. + + If ``version`` is not provided, Click will try to detect it using + :func:`importlib.metadata.version` to get the version for the + ``package_name``. On Python < 3.8, the ``importlib_metadata`` + backport must be installed. + + If ``package_name`` is not provided, Click will try to detect it by + inspecting the stack frames. This will be used to detect the + version, so it must match the name of the installed package. + + :param version: The version number to show. If not provided, Click + will try to detect it. + :param param_decls: One or more option names. Defaults to the single + value ``"--version"``. + :param package_name: The package name to detect the version from. If + not provided, Click will try to detect it. + :param prog_name: The name of the CLI to show in the message. If not + provided, it will be detected from the command. + :param message: The message to show. The values ``%(prog)s``, + ``%(package)s``, and ``%(version)s`` are available. Defaults to + ``"%(prog)s, version %(version)s"``. + :param kwargs: Extra arguments are passed to :func:`option`. + :raise RuntimeError: ``version`` could not be detected. + + .. versionchanged:: 8.0 + Add the ``package_name`` parameter, and the ``%(package)s`` + value for messages. + + .. versionchanged:: 8.0 + Use :mod:`importlib.metadata` instead of ``pkg_resources``. The + version is detected based on the package name, not the entry + point name. The Python package name must match the installed + package name, or be passed with ``package_name=``. + """ + if message is None: + message = _("%(prog)s, version %(version)s") + + if version is None and package_name is None: + frame = inspect.currentframe() + f_back = frame.f_back if frame is not None else None + f_globals = f_back.f_globals if f_back is not None else None + # break reference cycle + # https://docs.python.org/3/library/inspect.html#the-interpreter-stack + del frame + + if f_globals is not None: + package_name = f_globals.get("__name__") + + if package_name == "__main__": + package_name = f_globals.get("__package__") + + if package_name: + package_name = package_name.partition(".")[0] + + def callback(ctx: Context, param: Parameter, value: bool) -> None: + if not value or ctx.resilient_parsing: + return + + nonlocal prog_name + nonlocal version + + if prog_name is None: + prog_name = ctx.find_root().info_name + + if version is None and package_name is not None: + metadata: t.Optional[types.ModuleType] + + try: + from importlib import metadata # type: ignore + except ImportError: + # Python < 3.8 + import importlib_metadata as metadata # type: ignore + + try: + version = metadata.version(package_name) # type: ignore + except metadata.PackageNotFoundError: # type: ignore + raise RuntimeError( + f"{package_name!r} is not installed. Try passing" + " 'package_name' instead." + ) from None + + if version is None: + raise RuntimeError( + f"Could not determine the version for {package_name!r} automatically." + ) + + echo( + t.cast(str, message) + % {"prog": prog_name, "package": package_name, "version": version}, + color=ctx.color, + ) + ctx.exit() + + if not param_decls: + param_decls = ("--version",) + + kwargs.setdefault("is_flag", True) + kwargs.setdefault("expose_value", False) + kwargs.setdefault("is_eager", True) + kwargs.setdefault("help", _("Show the version and exit.")) + kwargs["callback"] = callback + return option(*param_decls, **kwargs) + + +def help_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: + """Add a ``--help`` option which immediately prints the help page + and exits the program. + + This is usually unnecessary, as the ``--help`` option is added to + each command automatically unless ``add_help_option=False`` is + passed. + + :param param_decls: One or more option names. Defaults to the single + value ``"--help"``. + :param kwargs: Extra arguments are passed to :func:`option`. + """ + + def callback(ctx: Context, param: Parameter, value: bool) -> None: + if not value or ctx.resilient_parsing: + return + + echo(ctx.get_help(), color=ctx.color) + ctx.exit() + + if not param_decls: + param_decls = ("--help",) + + kwargs.setdefault("is_flag", True) + kwargs.setdefault("expose_value", False) + kwargs.setdefault("is_eager", True) + kwargs.setdefault("help", _("Show this message and exit.")) + kwargs["callback"] = callback + return option(*param_decls, **kwargs) diff --git a/venv/lib/python3.7/site-packages/click/exceptions.py b/venv/lib/python3.7/site-packages/click/exceptions.py new file mode 100644 index 0000000..9e20b3e --- /dev/null +++ b/venv/lib/python3.7/site-packages/click/exceptions.py @@ -0,0 +1,287 @@ +import os +import typing as t +from gettext import gettext as _ +from gettext import ngettext + +from ._compat import get_text_stderr +from .utils import echo + +if t.TYPE_CHECKING: + from .core import Context + from .core import Parameter + + +def _join_param_hints( + param_hint: t.Optional[t.Union[t.Sequence[str], str]] +) -> t.Optional[str]: + if param_hint is not None and not isinstance(param_hint, str): + return " / ".join(repr(x) for x in param_hint) + + return param_hint + + +class ClickException(Exception): + """An exception that Click can handle and show to the user.""" + + #: The exit code for this exception. + exit_code = 1 + + def __init__(self, message: str) -> None: + super().__init__(message) + self.message = message + + def format_message(self) -> str: + return self.message + + def __str__(self) -> str: + return self.message + + def show(self, file: t.Optional[t.IO] = None) -> None: + if file is None: + file = get_text_stderr() + + echo(_("Error: {message}").format(message=self.format_message()), file=file) + + +class UsageError(ClickException): + """An internal exception that signals a usage error. This typically + aborts any further handling. + + :param message: the error message to display. + :param ctx: optionally the context that caused this error. Click will + fill in the context automatically in some situations. + """ + + exit_code = 2 + + def __init__(self, message: str, ctx: t.Optional["Context"] = None) -> None: + super().__init__(message) + self.ctx = ctx + self.cmd = self.ctx.command if self.ctx else None + + def show(self, file: t.Optional[t.IO] = None) -> None: + if file is None: + file = get_text_stderr() + color = None + hint = "" + if ( + self.ctx is not None + and self.ctx.command.get_help_option(self.ctx) is not None + ): + hint = _("Try '{command} {option}' for help.").format( + command=self.ctx.command_path, option=self.ctx.help_option_names[0] + ) + hint = f"{hint}\n" + if self.ctx is not None: + color = self.ctx.color + echo(f"{self.ctx.get_usage()}\n{hint}", file=file, color=color) + echo( + _("Error: {message}").format(message=self.format_message()), + file=file, + color=color, + ) + + +class BadParameter(UsageError): + """An exception that formats out a standardized error message for a + bad parameter. This is useful when thrown from a callback or type as + Click will attach contextual information to it (for instance, which + parameter it is). + + .. versionadded:: 2.0 + + :param param: the parameter object that caused this error. This can + be left out, and Click will attach this info itself + if possible. + :param param_hint: a string that shows up as parameter name. This + can be used as alternative to `param` in cases + where custom validation should happen. If it is + a string it's used as such, if it's a list then + each item is quoted and separated. + """ + + def __init__( + self, + message: str, + ctx: t.Optional["Context"] = None, + param: t.Optional["Parameter"] = None, + param_hint: t.Optional[str] = None, + ) -> None: + super().__init__(message, ctx) + self.param = param + self.param_hint = param_hint + + def format_message(self) -> str: + if self.param_hint is not None: + param_hint = self.param_hint + elif self.param is not None: + param_hint = self.param.get_error_hint(self.ctx) # type: ignore + else: + return _("Invalid value: {message}").format(message=self.message) + + return _("Invalid value for {param_hint}: {message}").format( + param_hint=_join_param_hints(param_hint), message=self.message + ) + + +class MissingParameter(BadParameter): + """Raised if click required an option or argument but it was not + provided when invoking the script. + + .. versionadded:: 4.0 + + :param param_type: a string that indicates the type of the parameter. + The default is to inherit the parameter type from + the given `param`. Valid values are ``'parameter'``, + ``'option'`` or ``'argument'``. + """ + + def __init__( + self, + message: t.Optional[str] = None, + ctx: t.Optional["Context"] = None, + param: t.Optional["Parameter"] = None, + param_hint: t.Optional[str] = None, + param_type: t.Optional[str] = None, + ) -> None: + super().__init__(message or "", ctx, param, param_hint) + self.param_type = param_type + + def format_message(self) -> str: + if self.param_hint is not None: + param_hint: t.Optional[str] = self.param_hint + elif self.param is not None: + param_hint = self.param.get_error_hint(self.ctx) # type: ignore + else: + param_hint = None + + param_hint = _join_param_hints(param_hint) + param_hint = f" {param_hint}" if param_hint else "" + + param_type = self.param_type + if param_type is None and self.param is not None: + param_type = self.param.param_type_name + + msg = self.message + if self.param is not None: + msg_extra = self.param.type.get_missing_message(self.param) + if msg_extra: + if msg: + msg += f". {msg_extra}" + else: + msg = msg_extra + + msg = f" {msg}" if msg else "" + + # Translate param_type for known types. + if param_type == "argument": + missing = _("Missing argument") + elif param_type == "option": + missing = _("Missing option") + elif param_type == "parameter": + missing = _("Missing parameter") + else: + missing = _("Missing {param_type}").format(param_type=param_type) + + return f"{missing}{param_hint}.{msg}" + + def __str__(self) -> str: + if not self.message: + param_name = self.param.name if self.param else None + return _("Missing parameter: {param_name}").format(param_name=param_name) + else: + return self.message + + +class NoSuchOption(UsageError): + """Raised if click attempted to handle an option that does not + exist. + + .. versionadded:: 4.0 + """ + + def __init__( + self, + option_name: str, + message: t.Optional[str] = None, + possibilities: t.Optional[t.Sequence[str]] = None, + ctx: t.Optional["Context"] = None, + ) -> None: + if message is None: + message = _("No such option: {name}").format(name=option_name) + + super().__init__(message, ctx) + self.option_name = option_name + self.possibilities = possibilities + + def format_message(self) -> str: + if not self.possibilities: + return self.message + + possibility_str = ", ".join(sorted(self.possibilities)) + suggest = ngettext( + "Did you mean {possibility}?", + "(Possible options: {possibilities})", + len(self.possibilities), + ).format(possibility=possibility_str, possibilities=possibility_str) + return f"{self.message} {suggest}" + + +class BadOptionUsage(UsageError): + """Raised if an option is generally supplied but the use of the option + was incorrect. This is for instance raised if the number of arguments + for an option is not correct. + + .. versionadded:: 4.0 + + :param option_name: the name of the option being used incorrectly. + """ + + def __init__( + self, option_name: str, message: str, ctx: t.Optional["Context"] = None + ) -> None: + super().__init__(message, ctx) + self.option_name = option_name + + +class BadArgumentUsage(UsageError): + """Raised if an argument is generally supplied but the use of the argument + was incorrect. This is for instance raised if the number of values + for an argument is not correct. + + .. versionadded:: 6.0 + """ + + +class FileError(ClickException): + """Raised if a file cannot be opened.""" + + def __init__(self, filename: str, hint: t.Optional[str] = None) -> None: + if hint is None: + hint = _("unknown error") + + super().__init__(hint) + self.ui_filename = os.fsdecode(filename) + self.filename = filename + + def format_message(self) -> str: + return _("Could not open file {filename!r}: {message}").format( + filename=self.ui_filename, message=self.message + ) + + +class Abort(RuntimeError): + """An internal signalling exception that signals Click to abort.""" + + +class Exit(RuntimeError): + """An exception that indicates that the application should exit with some + status code. + + :param code: the status code to exit with. + """ + + __slots__ = ("exit_code",) + + def __init__(self, code: int = 0) -> None: + self.exit_code = code diff --git a/venv/lib/python3.7/site-packages/click/formatting.py b/venv/lib/python3.7/site-packages/click/formatting.py new file mode 100644 index 0000000..ddd2a2f --- /dev/null +++ b/venv/lib/python3.7/site-packages/click/formatting.py @@ -0,0 +1,301 @@ +import typing as t +from contextlib import contextmanager +from gettext import gettext as _ + +from ._compat import term_len +from .parser import split_opt + +# Can force a width. This is used by the test system +FORCED_WIDTH: t.Optional[int] = None + + +def measure_table(rows: t.Iterable[t.Tuple[str, str]]) -> t.Tuple[int, ...]: + widths: t.Dict[int, int] = {} + + for row in rows: + for idx, col in enumerate(row): + widths[idx] = max(widths.get(idx, 0), term_len(col)) + + return tuple(y for x, y in sorted(widths.items())) + + +def iter_rows( + rows: t.Iterable[t.Tuple[str, str]], col_count: int +) -> t.Iterator[t.Tuple[str, ...]]: + for row in rows: + yield row + ("",) * (col_count - len(row)) + + +def wrap_text( + text: str, + width: int = 78, + initial_indent: str = "", + subsequent_indent: str = "", + preserve_paragraphs: bool = False, +) -> str: + """A helper function that intelligently wraps text. By default, it + assumes that it operates on a single paragraph of text but if the + `preserve_paragraphs` parameter is provided it will intelligently + handle paragraphs (defined by two empty lines). + + If paragraphs are handled, a paragraph can be prefixed with an empty + line containing the ``\\b`` character (``\\x08``) to indicate that + no rewrapping should happen in that block. + + :param text: the text that should be rewrapped. + :param width: the maximum width for the text. + :param initial_indent: the initial indent that should be placed on the + first line as a string. + :param subsequent_indent: the indent string that should be placed on + each consecutive line. + :param preserve_paragraphs: if this flag is set then the wrapping will + intelligently handle paragraphs. + """ + from ._textwrap import TextWrapper + + text = text.expandtabs() + wrapper = TextWrapper( + width, + initial_indent=initial_indent, + subsequent_indent=subsequent_indent, + replace_whitespace=False, + ) + if not preserve_paragraphs: + return wrapper.fill(text) + + p: t.List[t.Tuple[int, bool, str]] = [] + buf: t.List[str] = [] + indent = None + + def _flush_par() -> None: + if not buf: + return + if buf[0].strip() == "\b": + p.append((indent or 0, True, "\n".join(buf[1:]))) + else: + p.append((indent or 0, False, " ".join(buf))) + del buf[:] + + for line in text.splitlines(): + if not line: + _flush_par() + indent = None + else: + if indent is None: + orig_len = term_len(line) + line = line.lstrip() + indent = orig_len - term_len(line) + buf.append(line) + _flush_par() + + rv = [] + for indent, raw, text in p: + with wrapper.extra_indent(" " * indent): + if raw: + rv.append(wrapper.indent_only(text)) + else: + rv.append(wrapper.fill(text)) + + return "\n\n".join(rv) + + +class HelpFormatter: + """This class helps with formatting text-based help pages. It's + usually just needed for very special internal cases, but it's also + exposed so that developers can write their own fancy outputs. + + At present, it always writes into memory. + + :param indent_increment: the additional increment for each level. + :param width: the width for the text. This defaults to the terminal + width clamped to a maximum of 78. + """ + + def __init__( + self, + indent_increment: int = 2, + width: t.Optional[int] = None, + max_width: t.Optional[int] = None, + ) -> None: + import shutil + + self.indent_increment = indent_increment + if max_width is None: + max_width = 80 + if width is None: + width = FORCED_WIDTH + if width is None: + width = max(min(shutil.get_terminal_size().columns, max_width) - 2, 50) + self.width = width + self.current_indent = 0 + self.buffer: t.List[str] = [] + + def write(self, string: str) -> None: + """Writes a unicode string into the internal buffer.""" + self.buffer.append(string) + + def indent(self) -> None: + """Increases the indentation.""" + self.current_indent += self.indent_increment + + def dedent(self) -> None: + """Decreases the indentation.""" + self.current_indent -= self.indent_increment + + def write_usage( + self, prog: str, args: str = "", prefix: t.Optional[str] = None + ) -> None: + """Writes a usage line into the buffer. + + :param prog: the program name. + :param args: whitespace separated list of arguments. + :param prefix: The prefix for the first line. Defaults to + ``"Usage: "``. + """ + if prefix is None: + prefix = f"{_('Usage:')} " + + usage_prefix = f"{prefix:>{self.current_indent}}{prog} " + text_width = self.width - self.current_indent + + if text_width >= (term_len(usage_prefix) + 20): + # The arguments will fit to the right of the prefix. + indent = " " * term_len(usage_prefix) + self.write( + wrap_text( + args, + text_width, + initial_indent=usage_prefix, + subsequent_indent=indent, + ) + ) + else: + # The prefix is too long, put the arguments on the next line. + self.write(usage_prefix) + self.write("\n") + indent = " " * (max(self.current_indent, term_len(prefix)) + 4) + self.write( + wrap_text( + args, text_width, initial_indent=indent, subsequent_indent=indent + ) + ) + + self.write("\n") + + def write_heading(self, heading: str) -> None: + """Writes a heading into the buffer.""" + self.write(f"{'':>{self.current_indent}}{heading}:\n") + + def write_paragraph(self) -> None: + """Writes a paragraph into the buffer.""" + if self.buffer: + self.write("\n") + + def write_text(self, text: str) -> None: + """Writes re-indented text into the buffer. This rewraps and + preserves paragraphs. + """ + indent = " " * self.current_indent + self.write( + wrap_text( + text, + self.width, + initial_indent=indent, + subsequent_indent=indent, + preserve_paragraphs=True, + ) + ) + self.write("\n") + + def write_dl( + self, + rows: t.Sequence[t.Tuple[str, str]], + col_max: int = 30, + col_spacing: int = 2, + ) -> None: + """Writes a definition list into the buffer. This is how options + and commands are usually formatted. + + :param rows: a list of two item tuples for the terms and values. + :param col_max: the maximum width of the first column. + :param col_spacing: the number of spaces between the first and + second column. + """ + rows = list(rows) + widths = measure_table(rows) + if len(widths) != 2: + raise TypeError("Expected two columns for definition list") + + first_col = min(widths[0], col_max) + col_spacing + + for first, second in iter_rows(rows, len(widths)): + self.write(f"{'':>{self.current_indent}}{first}") + if not second: + self.write("\n") + continue + if term_len(first) <= first_col - col_spacing: + self.write(" " * (first_col - term_len(first))) + else: + self.write("\n") + self.write(" " * (first_col + self.current_indent)) + + text_width = max(self.width - first_col - 2, 10) + wrapped_text = wrap_text(second, text_width, preserve_paragraphs=True) + lines = wrapped_text.splitlines() + + if lines: + self.write(f"{lines[0]}\n") + + for line in lines[1:]: + self.write(f"{'':>{first_col + self.current_indent}}{line}\n") + else: + self.write("\n") + + @contextmanager + def section(self, name: str) -> t.Iterator[None]: + """Helpful context manager that writes a paragraph, a heading, + and the indents. + + :param name: the section name that is written as heading. + """ + self.write_paragraph() + self.write_heading(name) + self.indent() + try: + yield + finally: + self.dedent() + + @contextmanager + def indentation(self) -> t.Iterator[None]: + """A context manager that increases the indentation.""" + self.indent() + try: + yield + finally: + self.dedent() + + def getvalue(self) -> str: + """Returns the buffer contents.""" + return "".join(self.buffer) + + +def join_options(options: t.Sequence[str]) -> t.Tuple[str, bool]: + """Given a list of option strings this joins them in the most appropriate + way and returns them in the form ``(formatted_string, + any_prefix_is_slash)`` where the second item in the tuple is a flag that + indicates if any of the option prefixes was a slash. + """ + rv = [] + any_prefix_is_slash = False + + for opt in options: + prefix = split_opt(opt)[0] + + if prefix == "/": + any_prefix_is_slash = True + + rv.append((len(prefix), opt)) + + rv.sort(key=lambda x: x[0]) + return ", ".join(x[1] for x in rv), any_prefix_is_slash diff --git a/venv/lib/python3.7/site-packages/click/globals.py b/venv/lib/python3.7/site-packages/click/globals.py new file mode 100644 index 0000000..a7b0c93 --- /dev/null +++ b/venv/lib/python3.7/site-packages/click/globals.py @@ -0,0 +1,69 @@ +import typing +import typing as t +from threading import local + +if t.TYPE_CHECKING: + import typing_extensions as te + from .core import Context + +_local = local() + + +@typing.overload +def get_current_context(silent: "te.Literal[False]" = False) -> "Context": + ... + + +@typing.overload +def get_current_context(silent: bool = ...) -> t.Optional["Context"]: + ... + + +def get_current_context(silent: bool = False) -> t.Optional["Context"]: + """Returns the current click context. This can be used as a way to + access the current context object from anywhere. This is a more implicit + alternative to the :func:`pass_context` decorator. This function is + primarily useful for helpers such as :func:`echo` which might be + interested in changing its behavior based on the current context. + + To push the current context, :meth:`Context.scope` can be used. + + .. versionadded:: 5.0 + + :param silent: if set to `True` the return value is `None` if no context + is available. The default behavior is to raise a + :exc:`RuntimeError`. + """ + try: + return t.cast("Context", _local.stack[-1]) + except (AttributeError, IndexError) as e: + if not silent: + raise RuntimeError("There is no active click context.") from e + + return None + + +def push_context(ctx: "Context") -> None: + """Pushes a new context to the current stack.""" + _local.__dict__.setdefault("stack", []).append(ctx) + + +def pop_context() -> None: + """Removes the top level from the stack.""" + _local.stack.pop() + + +def resolve_color_default(color: t.Optional[bool] = None) -> t.Optional[bool]: + """Internal helper to get the default value of the color flag. If a + value is passed it's returned unchanged, otherwise it's looked up from + the current context. + """ + if color is not None: + return color + + ctx = get_current_context(silent=True) + + if ctx is not None: + return ctx.color + + return None diff --git a/venv/lib/python3.7/site-packages/click/parser.py b/venv/lib/python3.7/site-packages/click/parser.py new file mode 100644 index 0000000..2d5a2ed --- /dev/null +++ b/venv/lib/python3.7/site-packages/click/parser.py @@ -0,0 +1,529 @@ +""" +This module started out as largely a copy paste from the stdlib's +optparse module with the features removed that we do not need from +optparse because we implement them in Click on a higher level (for +instance type handling, help formatting and a lot more). + +The plan is to remove more and more from here over time. + +The reason this is a different module and not optparse from the stdlib +is that there are differences in 2.x and 3.x about the error messages +generated and optparse in the stdlib uses gettext for no good reason +and might cause us issues. + +Click uses parts of optparse written by Gregory P. Ward and maintained +by the Python Software Foundation. This is limited to code in parser.py. + +Copyright 2001-2006 Gregory P. Ward. All rights reserved. +Copyright 2002-2006 Python Software Foundation. All rights reserved. +""" +# This code uses parts of optparse written by Gregory P. Ward and +# maintained by the Python Software Foundation. +# Copyright 2001-2006 Gregory P. Ward +# Copyright 2002-2006 Python Software Foundation +import typing as t +from collections import deque +from gettext import gettext as _ +from gettext import ngettext + +from .exceptions import BadArgumentUsage +from .exceptions import BadOptionUsage +from .exceptions import NoSuchOption +from .exceptions import UsageError + +if t.TYPE_CHECKING: + import typing_extensions as te + from .core import Argument as CoreArgument + from .core import Context + from .core import Option as CoreOption + from .core import Parameter as CoreParameter + +V = t.TypeVar("V") + +# Sentinel value that indicates an option was passed as a flag without a +# value but is not a flag option. Option.consume_value uses this to +# prompt or use the flag_value. +_flag_needs_value = object() + + +def _unpack_args( + args: t.Sequence[str], nargs_spec: t.Sequence[int] +) -> t.Tuple[t.Sequence[t.Union[str, t.Sequence[t.Optional[str]], None]], t.List[str]]: + """Given an iterable of arguments and an iterable of nargs specifications, + it returns a tuple with all the unpacked arguments at the first index + and all remaining arguments as the second. + + The nargs specification is the number of arguments that should be consumed + or `-1` to indicate that this position should eat up all the remainders. + + Missing items are filled with `None`. + """ + args = deque(args) + nargs_spec = deque(nargs_spec) + rv: t.List[t.Union[str, t.Tuple[t.Optional[str], ...], None]] = [] + spos: t.Optional[int] = None + + def _fetch(c: "te.Deque[V]") -> t.Optional[V]: + try: + if spos is None: + return c.popleft() + else: + return c.pop() + except IndexError: + return None + + while nargs_spec: + nargs = _fetch(nargs_spec) + + if nargs is None: + continue + + if nargs == 1: + rv.append(_fetch(args)) + elif nargs > 1: + x = [_fetch(args) for _ in range(nargs)] + + # If we're reversed, we're pulling in the arguments in reverse, + # so we need to turn them around. + if spos is not None: + x.reverse() + + rv.append(tuple(x)) + elif nargs < 0: + if spos is not None: + raise TypeError("Cannot have two nargs < 0") + + spos = len(rv) + rv.append(None) + + # spos is the position of the wildcard (star). If it's not `None`, + # we fill it with the remainder. + if spos is not None: + rv[spos] = tuple(args) + args = [] + rv[spos + 1 :] = reversed(rv[spos + 1 :]) + + return tuple(rv), list(args) + + +def split_opt(opt: str) -> t.Tuple[str, str]: + first = opt[:1] + if first.isalnum(): + return "", opt + if opt[1:2] == first: + return opt[:2], opt[2:] + return first, opt[1:] + + +def normalize_opt(opt: str, ctx: t.Optional["Context"]) -> str: + if ctx is None or ctx.token_normalize_func is None: + return opt + prefix, opt = split_opt(opt) + return f"{prefix}{ctx.token_normalize_func(opt)}" + + +def split_arg_string(string: str) -> t.List[str]: + """Split an argument string as with :func:`shlex.split`, but don't + fail if the string is incomplete. Ignores a missing closing quote or + incomplete escape sequence and uses the partial token as-is. + + .. code-block:: python + + split_arg_string("example 'my file") + ["example", "my file"] + + split_arg_string("example my\\") + ["example", "my"] + + :param string: String to split. + """ + import shlex + + lex = shlex.shlex(string, posix=True) + lex.whitespace_split = True + lex.commenters = "" + out = [] + + try: + for token in lex: + out.append(token) + except ValueError: + # Raised when end-of-string is reached in an invalid state. Use + # the partial token as-is. The quote or escape character is in + # lex.state, not lex.token. + out.append(lex.token) + + return out + + +class Option: + def __init__( + self, + obj: "CoreOption", + opts: t.Sequence[str], + dest: t.Optional[str], + action: t.Optional[str] = None, + nargs: int = 1, + const: t.Optional[t.Any] = None, + ): + self._short_opts = [] + self._long_opts = [] + self.prefixes = set() + + for opt in opts: + prefix, value = split_opt(opt) + if not prefix: + raise ValueError(f"Invalid start character for option ({opt})") + self.prefixes.add(prefix[0]) + if len(prefix) == 1 and len(value) == 1: + self._short_opts.append(opt) + else: + self._long_opts.append(opt) + self.prefixes.add(prefix) + + if action is None: + action = "store" + + self.dest = dest + self.action = action + self.nargs = nargs + self.const = const + self.obj = obj + + @property + def takes_value(self) -> bool: + return self.action in ("store", "append") + + def process(self, value: str, state: "ParsingState") -> None: + if self.action == "store": + state.opts[self.dest] = value # type: ignore + elif self.action == "store_const": + state.opts[self.dest] = self.const # type: ignore + elif self.action == "append": + state.opts.setdefault(self.dest, []).append(value) # type: ignore + elif self.action == "append_const": + state.opts.setdefault(self.dest, []).append(self.const) # type: ignore + elif self.action == "count": + state.opts[self.dest] = state.opts.get(self.dest, 0) + 1 # type: ignore + else: + raise ValueError(f"unknown action '{self.action}'") + state.order.append(self.obj) + + +class Argument: + def __init__(self, obj: "CoreArgument", dest: t.Optional[str], nargs: int = 1): + self.dest = dest + self.nargs = nargs + self.obj = obj + + def process( + self, + value: t.Union[t.Optional[str], t.Sequence[t.Optional[str]]], + state: "ParsingState", + ) -> None: + if self.nargs > 1: + assert value is not None + holes = sum(1 for x in value if x is None) + if holes == len(value): + value = None + elif holes != 0: + raise BadArgumentUsage( + _("Argument {name!r} takes {nargs} values.").format( + name=self.dest, nargs=self.nargs + ) + ) + + if self.nargs == -1 and self.obj.envvar is not None and value == (): + # Replace empty tuple with None so that a value from the + # environment may be tried. + value = None + + state.opts[self.dest] = value # type: ignore + state.order.append(self.obj) + + +class ParsingState: + def __init__(self, rargs: t.List[str]) -> None: + self.opts: t.Dict[str, t.Any] = {} + self.largs: t.List[str] = [] + self.rargs = rargs + self.order: t.List["CoreParameter"] = [] + + +class OptionParser: + """The option parser is an internal class that is ultimately used to + parse options and arguments. It's modelled after optparse and brings + a similar but vastly simplified API. It should generally not be used + directly as the high level Click classes wrap it for you. + + It's not nearly as extensible as optparse or argparse as it does not + implement features that are implemented on a higher level (such as + types or defaults). + + :param ctx: optionally the :class:`~click.Context` where this parser + should go with. + """ + + def __init__(self, ctx: t.Optional["Context"] = None) -> None: + #: The :class:`~click.Context` for this parser. This might be + #: `None` for some advanced use cases. + self.ctx = ctx + #: This controls how the parser deals with interspersed arguments. + #: If this is set to `False`, the parser will stop on the first + #: non-option. Click uses this to implement nested subcommands + #: safely. + self.allow_interspersed_args = True + #: This tells the parser how to deal with unknown options. By + #: default it will error out (which is sensible), but there is a + #: second mode where it will ignore it and continue processing + #: after shifting all the unknown options into the resulting args. + self.ignore_unknown_options = False + + if ctx is not None: + self.allow_interspersed_args = ctx.allow_interspersed_args + self.ignore_unknown_options = ctx.ignore_unknown_options + + self._short_opt: t.Dict[str, Option] = {} + self._long_opt: t.Dict[str, Option] = {} + self._opt_prefixes = {"-", "--"} + self._args: t.List[Argument] = [] + + def add_option( + self, + obj: "CoreOption", + opts: t.Sequence[str], + dest: t.Optional[str], + action: t.Optional[str] = None, + nargs: int = 1, + const: t.Optional[t.Any] = None, + ) -> None: + """Adds a new option named `dest` to the parser. The destination + is not inferred (unlike with optparse) and needs to be explicitly + provided. Action can be any of ``store``, ``store_const``, + ``append``, ``append_const`` or ``count``. + + The `obj` can be used to identify the option in the order list + that is returned from the parser. + """ + opts = [normalize_opt(opt, self.ctx) for opt in opts] + option = Option(obj, opts, dest, action=action, nargs=nargs, const=const) + self._opt_prefixes.update(option.prefixes) + for opt in option._short_opts: + self._short_opt[opt] = option + for opt in option._long_opts: + self._long_opt[opt] = option + + def add_argument( + self, obj: "CoreArgument", dest: t.Optional[str], nargs: int = 1 + ) -> None: + """Adds a positional argument named `dest` to the parser. + + The `obj` can be used to identify the option in the order list + that is returned from the parser. + """ + self._args.append(Argument(obj, dest=dest, nargs=nargs)) + + def parse_args( + self, args: t.List[str] + ) -> t.Tuple[t.Dict[str, t.Any], t.List[str], t.List["CoreParameter"]]: + """Parses positional arguments and returns ``(values, args, order)`` + for the parsed options and arguments as well as the leftover + arguments if there are any. The order is a list of objects as they + appear on the command line. If arguments appear multiple times they + will be memorized multiple times as well. + """ + state = ParsingState(args) + try: + self._process_args_for_options(state) + self._process_args_for_args(state) + except UsageError: + if self.ctx is None or not self.ctx.resilient_parsing: + raise + return state.opts, state.largs, state.order + + def _process_args_for_args(self, state: ParsingState) -> None: + pargs, args = _unpack_args( + state.largs + state.rargs, [x.nargs for x in self._args] + ) + + for idx, arg in enumerate(self._args): + arg.process(pargs[idx], state) + + state.largs = args + state.rargs = [] + + def _process_args_for_options(self, state: ParsingState) -> None: + while state.rargs: + arg = state.rargs.pop(0) + arglen = len(arg) + # Double dashes always handled explicitly regardless of what + # prefixes are valid. + if arg == "--": + return + elif arg[:1] in self._opt_prefixes and arglen > 1: + self._process_opts(arg, state) + elif self.allow_interspersed_args: + state.largs.append(arg) + else: + state.rargs.insert(0, arg) + return + + # Say this is the original argument list: + # [arg0, arg1, ..., arg(i-1), arg(i), arg(i+1), ..., arg(N-1)] + # ^ + # (we are about to process arg(i)). + # + # Then rargs is [arg(i), ..., arg(N-1)] and largs is a *subset* of + # [arg0, ..., arg(i-1)] (any options and their arguments will have + # been removed from largs). + # + # The while loop will usually consume 1 or more arguments per pass. + # If it consumes 1 (eg. arg is an option that takes no arguments), + # then after _process_arg() is done the situation is: + # + # largs = subset of [arg0, ..., arg(i)] + # rargs = [arg(i+1), ..., arg(N-1)] + # + # If allow_interspersed_args is false, largs will always be + # *empty* -- still a subset of [arg0, ..., arg(i-1)], but + # not a very interesting subset! + + def _match_long_opt( + self, opt: str, explicit_value: t.Optional[str], state: ParsingState + ) -> None: + if opt not in self._long_opt: + from difflib import get_close_matches + + possibilities = get_close_matches(opt, self._long_opt) + raise NoSuchOption(opt, possibilities=possibilities, ctx=self.ctx) + + option = self._long_opt[opt] + if option.takes_value: + # At this point it's safe to modify rargs by injecting the + # explicit value, because no exception is raised in this + # branch. This means that the inserted value will be fully + # consumed. + if explicit_value is not None: + state.rargs.insert(0, explicit_value) + + value = self._get_value_from_state(opt, option, state) + + elif explicit_value is not None: + raise BadOptionUsage( + opt, _("Option {name!r} does not take a value.").format(name=opt) + ) + + else: + value = None + + option.process(value, state) + + def _match_short_opt(self, arg: str, state: ParsingState) -> None: + stop = False + i = 1 + prefix = arg[0] + unknown_options = [] + + for ch in arg[1:]: + opt = normalize_opt(f"{prefix}{ch}", self.ctx) + option = self._short_opt.get(opt) + i += 1 + + if not option: + if self.ignore_unknown_options: + unknown_options.append(ch) + continue + raise NoSuchOption(opt, ctx=self.ctx) + if option.takes_value: + # Any characters left in arg? Pretend they're the + # next arg, and stop consuming characters of arg. + if i < len(arg): + state.rargs.insert(0, arg[i:]) + stop = True + + value = self._get_value_from_state(opt, option, state) + + else: + value = None + + option.process(value, state) + + if stop: + break + + # If we got any unknown options we re-combinate the string of the + # remaining options and re-attach the prefix, then report that + # to the state as new larg. This way there is basic combinatorics + # that can be achieved while still ignoring unknown arguments. + if self.ignore_unknown_options and unknown_options: + state.largs.append(f"{prefix}{''.join(unknown_options)}") + + def _get_value_from_state( + self, option_name: str, option: Option, state: ParsingState + ) -> t.Any: + nargs = option.nargs + + if len(state.rargs) < nargs: + if option.obj._flag_needs_value: + # Option allows omitting the value. + value = _flag_needs_value + else: + raise BadOptionUsage( + option_name, + ngettext( + "Option {name!r} requires an argument.", + "Option {name!r} requires {nargs} arguments.", + nargs, + ).format(name=option_name, nargs=nargs), + ) + elif nargs == 1: + next_rarg = state.rargs[0] + + if ( + option.obj._flag_needs_value + and isinstance(next_rarg, str) + and next_rarg[:1] in self._opt_prefixes + and len(next_rarg) > 1 + ): + # The next arg looks like the start of an option, don't + # use it as the value if omitting the value is allowed. + value = _flag_needs_value + else: + value = state.rargs.pop(0) + else: + value = tuple(state.rargs[:nargs]) + del state.rargs[:nargs] + + return value + + def _process_opts(self, arg: str, state: ParsingState) -> None: + explicit_value = None + # Long option handling happens in two parts. The first part is + # supporting explicitly attached values. In any case, we will try + # to long match the option first. + if "=" in arg: + long_opt, explicit_value = arg.split("=", 1) + else: + long_opt = arg + norm_long_opt = normalize_opt(long_opt, self.ctx) + + # At this point we will match the (assumed) long option through + # the long option matching code. Note that this allows options + # like "-foo" to be matched as long options. + try: + self._match_long_opt(norm_long_opt, explicit_value, state) + except NoSuchOption: + # At this point the long option matching failed, and we need + # to try with short options. However there is a special rule + # which says, that if we have a two character options prefix + # (applies to "--foo" for instance), we do not dispatch to the + # short option code and will instead raise the no option + # error. + if arg[:2] not in self._opt_prefixes: + self._match_short_opt(arg, state) + return + + if not self.ignore_unknown_options: + raise + + state.largs.append(arg) diff --git a/venv/lib/python3.7/site-packages/click/py.typed b/venv/lib/python3.7/site-packages/click/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.7/site-packages/click/shell_completion.py b/venv/lib/python3.7/site-packages/click/shell_completion.py new file mode 100644 index 0000000..cad080d --- /dev/null +++ b/venv/lib/python3.7/site-packages/click/shell_completion.py @@ -0,0 +1,581 @@ +import os +import re +import typing as t +from gettext import gettext as _ + +from .core import Argument +from .core import BaseCommand +from .core import Context +from .core import MultiCommand +from .core import Option +from .core import Parameter +from .core import ParameterSource +from .parser import split_arg_string +from .utils import echo + + +def shell_complete( + cli: BaseCommand, + ctx_args: t.Dict[str, t.Any], + prog_name: str, + complete_var: str, + instruction: str, +) -> int: + """Perform shell completion for the given CLI program. + + :param cli: Command being called. + :param ctx_args: Extra arguments to pass to + ``cli.make_context``. + :param prog_name: Name of the executable in the shell. + :param complete_var: Name of the environment variable that holds + the completion instruction. + :param instruction: Value of ``complete_var`` with the completion + instruction and shell, in the form ``instruction_shell``. + :return: Status code to exit with. + """ + shell, _, instruction = instruction.partition("_") + comp_cls = get_completion_class(shell) + + if comp_cls is None: + return 1 + + comp = comp_cls(cli, ctx_args, prog_name, complete_var) + + if instruction == "source": + echo(comp.source()) + return 0 + + if instruction == "complete": + echo(comp.complete()) + return 0 + + return 1 + + +class CompletionItem: + """Represents a completion value and metadata about the value. The + default metadata is ``type`` to indicate special shell handling, + and ``help`` if a shell supports showing a help string next to the + value. + + Arbitrary parameters can be passed when creating the object, and + accessed using ``item.attr``. If an attribute wasn't passed, + accessing it returns ``None``. + + :param value: The completion suggestion. + :param type: Tells the shell script to provide special completion + support for the type. Click uses ``"dir"`` and ``"file"``. + :param help: String shown next to the value if supported. + :param kwargs: Arbitrary metadata. The built-in implementations + don't use this, but custom type completions paired with custom + shell support could use it. + """ + + __slots__ = ("value", "type", "help", "_info") + + def __init__( + self, + value: t.Any, + type: str = "plain", + help: t.Optional[str] = None, + **kwargs: t.Any, + ) -> None: + self.value = value + self.type = type + self.help = help + self._info = kwargs + + def __getattr__(self, name: str) -> t.Any: + return self._info.get(name) + + +# Only Bash >= 4.4 has the nosort option. +_SOURCE_BASH = """\ +%(complete_func)s() { + local IFS=$'\\n' + local response + + response=$(env COMP_WORDS="${COMP_WORDS[*]}" COMP_CWORD=$COMP_CWORD \ +%(complete_var)s=bash_complete $1) + + for completion in $response; do + IFS=',' read type value <<< "$completion" + + if [[ $type == 'dir' ]]; then + COMREPLY=() + compopt -o dirnames + elif [[ $type == 'file' ]]; then + COMREPLY=() + compopt -o default + elif [[ $type == 'plain' ]]; then + COMPREPLY+=($value) + fi + done + + return 0 +} + +%(complete_func)s_setup() { + complete -o nosort -F %(complete_func)s %(prog_name)s +} + +%(complete_func)s_setup; +""" + +_SOURCE_ZSH = """\ +#compdef %(prog_name)s + +%(complete_func)s() { + local -a completions + local -a completions_with_descriptions + local -a response + (( ! $+commands[%(prog_name)s] )) && return 1 + + response=("${(@f)$(env COMP_WORDS="${words[*]}" COMP_CWORD=$((CURRENT-1)) \ +%(complete_var)s=zsh_complete %(prog_name)s)}") + + for type key descr in ${response}; do + if [[ "$type" == "plain" ]]; then + if [[ "$descr" == "_" ]]; then + completions+=("$key") + else + completions_with_descriptions+=("$key":"$descr") + fi + elif [[ "$type" == "dir" ]]; then + _path_files -/ + elif [[ "$type" == "file" ]]; then + _path_files -f + fi + done + + if [ -n "$completions_with_descriptions" ]; then + _describe -V unsorted completions_with_descriptions -U + fi + + if [ -n "$completions" ]; then + compadd -U -V unsorted -a completions + fi +} + +compdef %(complete_func)s %(prog_name)s; +""" + +_SOURCE_FISH = """\ +function %(complete_func)s; + set -l response; + + for value in (env %(complete_var)s=fish_complete COMP_WORDS=(commandline -cp) \ +COMP_CWORD=(commandline -t) %(prog_name)s); + set response $response $value; + end; + + for completion in $response; + set -l metadata (string split "," $completion); + + if test $metadata[1] = "dir"; + __fish_complete_directories $metadata[2]; + else if test $metadata[1] = "file"; + __fish_complete_path $metadata[2]; + else if test $metadata[1] = "plain"; + echo $metadata[2]; + end; + end; +end; + +complete --no-files --command %(prog_name)s --arguments \ +"(%(complete_func)s)"; +""" + + +class ShellComplete: + """Base class for providing shell completion support. A subclass for + a given shell will override attributes and methods to implement the + completion instructions (``source`` and ``complete``). + + :param cli: Command being called. + :param prog_name: Name of the executable in the shell. + :param complete_var: Name of the environment variable that holds + the completion instruction. + + .. versionadded:: 8.0 + """ + + name: t.ClassVar[str] + """Name to register the shell as with :func:`add_completion_class`. + This is used in completion instructions (``{name}_source`` and + ``{name}_complete``). + """ + + source_template: t.ClassVar[str] + """Completion script template formatted by :meth:`source`. This must + be provided by subclasses. + """ + + def __init__( + self, + cli: BaseCommand, + ctx_args: t.Dict[str, t.Any], + prog_name: str, + complete_var: str, + ) -> None: + self.cli = cli + self.ctx_args = ctx_args + self.prog_name = prog_name + self.complete_var = complete_var + + @property + def func_name(self) -> str: + """The name of the shell function defined by the completion + script. + """ + safe_name = re.sub(r"\W*", "", self.prog_name.replace("-", "_"), re.ASCII) + return f"_{safe_name}_completion" + + def source_vars(self) -> t.Dict[str, t.Any]: + """Vars for formatting :attr:`source_template`. + + By default this provides ``complete_func``, ``complete_var``, + and ``prog_name``. + """ + return { + "complete_func": self.func_name, + "complete_var": self.complete_var, + "prog_name": self.prog_name, + } + + def source(self) -> str: + """Produce the shell script that defines the completion + function. By default this ``%``-style formats + :attr:`source_template` with the dict returned by + :meth:`source_vars`. + """ + return self.source_template % self.source_vars() + + def get_completion_args(self) -> t.Tuple[t.List[str], str]: + """Use the env vars defined by the shell script to return a + tuple of ``args, incomplete``. This must be implemented by + subclasses. + """ + raise NotImplementedError + + def get_completions( + self, args: t.List[str], incomplete: str + ) -> t.List[CompletionItem]: + """Determine the context and last complete command or parameter + from the complete args. Call that object's ``shell_complete`` + method to get the completions for the incomplete value. + + :param args: List of complete args before the incomplete value. + :param incomplete: Value being completed. May be empty. + """ + ctx = _resolve_context(self.cli, self.ctx_args, self.prog_name, args) + obj, incomplete = _resolve_incomplete(ctx, args, incomplete) + return obj.shell_complete(ctx, incomplete) + + def format_completion(self, item: CompletionItem) -> str: + """Format a completion item into the form recognized by the + shell script. This must be implemented by subclasses. + + :param item: Completion item to format. + """ + raise NotImplementedError + + def complete(self) -> str: + """Produce the completion data to send back to the shell. + + By default this calls :meth:`get_completion_args`, gets the + completions, then calls :meth:`format_completion` for each + completion. + """ + args, incomplete = self.get_completion_args() + completions = self.get_completions(args, incomplete) + out = [self.format_completion(item) for item in completions] + return "\n".join(out) + + +class BashComplete(ShellComplete): + """Shell completion for Bash.""" + + name = "bash" + source_template = _SOURCE_BASH + + def _check_version(self) -> None: + import subprocess + + output = subprocess.run( + ["bash", "-c", "echo ${BASH_VERSION}"], stdout=subprocess.PIPE + ) + match = re.search(r"^(\d+)\.(\d+)\.\d+", output.stdout.decode()) + + if match is not None: + major, minor = match.groups() + + if major < "4" or major == "4" and minor < "4": + raise RuntimeError( + _( + "Shell completion is not supported for Bash" + " versions older than 4.4." + ) + ) + else: + raise RuntimeError( + _("Couldn't detect Bash version, shell completion is not supported.") + ) + + def source(self) -> str: + self._check_version() + return super().source() + + def get_completion_args(self) -> t.Tuple[t.List[str], str]: + cwords = split_arg_string(os.environ["COMP_WORDS"]) + cword = int(os.environ["COMP_CWORD"]) + args = cwords[1:cword] + + try: + incomplete = cwords[cword] + except IndexError: + incomplete = "" + + return args, incomplete + + def format_completion(self, item: CompletionItem) -> str: + return f"{item.type},{item.value}" + + +class ZshComplete(ShellComplete): + """Shell completion for Zsh.""" + + name = "zsh" + source_template = _SOURCE_ZSH + + def get_completion_args(self) -> t.Tuple[t.List[str], str]: + cwords = split_arg_string(os.environ["COMP_WORDS"]) + cword = int(os.environ["COMP_CWORD"]) + args = cwords[1:cword] + + try: + incomplete = cwords[cword] + except IndexError: + incomplete = "" + + return args, incomplete + + def format_completion(self, item: CompletionItem) -> str: + return f"{item.type}\n{item.value}\n{item.help if item.help else '_'}" + + +class FishComplete(ShellComplete): + """Shell completion for Fish.""" + + name = "fish" + source_template = _SOURCE_FISH + + def get_completion_args(self) -> t.Tuple[t.List[str], str]: + cwords = split_arg_string(os.environ["COMP_WORDS"]) + incomplete = os.environ["COMP_CWORD"] + args = cwords[1:] + + # Fish stores the partial word in both COMP_WORDS and + # COMP_CWORD, remove it from complete args. + if incomplete and args and args[-1] == incomplete: + args.pop() + + return args, incomplete + + def format_completion(self, item: CompletionItem) -> str: + if item.help: + return f"{item.type},{item.value}\t{item.help}" + + return f"{item.type},{item.value}" + + +_available_shells: t.Dict[str, t.Type[ShellComplete]] = { + "bash": BashComplete, + "fish": FishComplete, + "zsh": ZshComplete, +} + + +def add_completion_class( + cls: t.Type[ShellComplete], name: t.Optional[str] = None +) -> None: + """Register a :class:`ShellComplete` subclass under the given name. + The name will be provided by the completion instruction environment + variable during completion. + + :param cls: The completion class that will handle completion for the + shell. + :param name: Name to register the class under. Defaults to the + class's ``name`` attribute. + """ + if name is None: + name = cls.name + + _available_shells[name] = cls + + +def get_completion_class(shell: str) -> t.Optional[t.Type[ShellComplete]]: + """Look up a registered :class:`ShellComplete` subclass by the name + provided by the completion instruction environment variable. If the + name isn't registered, returns ``None``. + + :param shell: Name the class is registered under. + """ + return _available_shells.get(shell) + + +def _is_incomplete_argument(ctx: Context, param: Parameter) -> bool: + """Determine if the given parameter is an argument that can still + accept values. + + :param ctx: Invocation context for the command represented by the + parsed complete args. + :param param: Argument object being checked. + """ + if not isinstance(param, Argument): + return False + + assert param.name is not None + value = ctx.params[param.name] + return ( + param.nargs == -1 + or ctx.get_parameter_source(param.name) is not ParameterSource.COMMANDLINE + or ( + param.nargs > 1 + and isinstance(value, (tuple, list)) + and len(value) < param.nargs + ) + ) + + +def _start_of_option(value: str) -> bool: + """Check if the value looks like the start of an option.""" + if not value: + return False + + c = value[0] + # Allow "/" since that starts a path. + return not c.isalnum() and c != "/" + + +def _is_incomplete_option(args: t.List[str], param: Parameter) -> bool: + """Determine if the given parameter is an option that needs a value. + + :param args: List of complete args before the incomplete value. + :param param: Option object being checked. + """ + if not isinstance(param, Option): + return False + + if param.is_flag: + return False + + last_option = None + + for index, arg in enumerate(reversed(args)): + if index + 1 > param.nargs: + break + + if _start_of_option(arg): + last_option = arg + + return last_option is not None and last_option in param.opts + + +def _resolve_context( + cli: BaseCommand, ctx_args: t.Dict[str, t.Any], prog_name: str, args: t.List[str] +) -> Context: + """Produce the context hierarchy starting with the command and + traversing the complete arguments. This only follows the commands, + it doesn't trigger input prompts or callbacks. + + :param cli: Command being called. + :param prog_name: Name of the executable in the shell. + :param args: List of complete args before the incomplete value. + """ + ctx_args["resilient_parsing"] = True + ctx = cli.make_context(prog_name, args.copy(), **ctx_args) + args = ctx.protected_args + ctx.args + + while args: + command = ctx.command + + if isinstance(command, MultiCommand): + if not command.chain: + name, cmd, args = command.resolve_command(ctx, args) + + if cmd is None: + return ctx + + ctx = cmd.make_context(name, args, parent=ctx, resilient_parsing=True) + args = ctx.protected_args + ctx.args + else: + while args: + name, cmd, args = command.resolve_command(ctx, args) + + if cmd is None: + return ctx + + sub_ctx = cmd.make_context( + name, + args, + parent=ctx, + allow_extra_args=True, + allow_interspersed_args=False, + resilient_parsing=True, + ) + args = sub_ctx.args + + ctx = sub_ctx + args = [*sub_ctx.protected_args, *sub_ctx.args] + else: + break + + return ctx + + +def _resolve_incomplete( + ctx: Context, args: t.List[str], incomplete: str +) -> t.Tuple[t.Union[BaseCommand, Parameter], str]: + """Find the Click object that will handle the completion of the + incomplete value. Return the object and the incomplete value. + + :param ctx: Invocation context for the command represented by + the parsed complete args. + :param args: List of complete args before the incomplete value. + :param incomplete: Value being completed. May be empty. + """ + # Different shells treat an "=" between a long option name and + # value differently. Might keep the value joined, return the "=" + # as a separate item, or return the split name and value. Always + # split and discard the "=" to make completion easier. + if incomplete == "=": + incomplete = "" + elif "=" in incomplete and _start_of_option(incomplete): + name, _, incomplete = incomplete.partition("=") + args.append(name) + + # The "--" marker tells Click to stop treating values as options + # even if they start with the option character. If it hasn't been + # given and the incomplete arg looks like an option, the current + # command will provide option name completions. + if "--" not in args and _start_of_option(incomplete): + return ctx.command, incomplete + + params = ctx.command.get_params(ctx) + + # If the last complete arg is an option name with an incomplete + # value, the option will provide value completions. + for param in params: + if _is_incomplete_option(args, param): + return param, incomplete + + # It's not an option name or value. The first argument without a + # parsed value will provide value completions. + for param in params: + if _is_incomplete_argument(ctx, param): + return param, incomplete + + # There were no unparsed arguments, the command may be a group that + # will provide command name completions. + return ctx.command, incomplete diff --git a/venv/lib/python3.7/site-packages/click/termui.py b/venv/lib/python3.7/site-packages/click/termui.py new file mode 100644 index 0000000..cf8d5f1 --- /dev/null +++ b/venv/lib/python3.7/site-packages/click/termui.py @@ -0,0 +1,809 @@ +import inspect +import io +import itertools +import os +import sys +import typing +import typing as t +from gettext import gettext as _ + +from ._compat import isatty +from ._compat import strip_ansi +from ._compat import WIN +from .exceptions import Abort +from .exceptions import UsageError +from .globals import resolve_color_default +from .types import Choice +from .types import convert_type +from .types import ParamType +from .utils import echo +from .utils import LazyFile + +if t.TYPE_CHECKING: + from ._termui_impl import ProgressBar + +V = t.TypeVar("V") + +# The prompt functions to use. The doc tools currently override these +# functions to customize how they work. +visible_prompt_func: t.Callable[[str], str] = input + +_ansi_colors = { + "black": 30, + "red": 31, + "green": 32, + "yellow": 33, + "blue": 34, + "magenta": 35, + "cyan": 36, + "white": 37, + "reset": 39, + "bright_black": 90, + "bright_red": 91, + "bright_green": 92, + "bright_yellow": 93, + "bright_blue": 94, + "bright_magenta": 95, + "bright_cyan": 96, + "bright_white": 97, +} +_ansi_reset_all = "\033[0m" + + +def hidden_prompt_func(prompt: str) -> str: + import getpass + + return getpass.getpass(prompt) + + +def _build_prompt( + text: str, + suffix: str, + show_default: bool = False, + default: t.Optional[t.Any] = None, + show_choices: bool = True, + type: t.Optional[ParamType] = None, +) -> str: + prompt = text + if type is not None and show_choices and isinstance(type, Choice): + prompt += f" ({', '.join(map(str, type.choices))})" + if default is not None and show_default: + prompt = f"{prompt} [{_format_default(default)}]" + return f"{prompt}{suffix}" + + +def _format_default(default: t.Any) -> t.Any: + if isinstance(default, (io.IOBase, LazyFile)) and hasattr(default, "name"): + return default.name # type: ignore + + return default + + +def prompt( + text: str, + default: t.Optional[t.Any] = None, + hide_input: bool = False, + confirmation_prompt: t.Union[bool, str] = False, + type: t.Optional[t.Union[ParamType, t.Any]] = None, + value_proc: t.Optional[t.Callable[[str], t.Any]] = None, + prompt_suffix: str = ": ", + show_default: bool = True, + err: bool = False, + show_choices: bool = True, +) -> t.Any: + """Prompts a user for input. This is a convenience function that can + be used to prompt a user for input later. + + If the user aborts the input by sending a interrupt signal, this + function will catch it and raise a :exc:`Abort` exception. + + :param text: the text to show for the prompt. + :param default: the default value to use if no input happens. If this + is not given it will prompt until it's aborted. + :param hide_input: if this is set to true then the input value will + be hidden. + :param confirmation_prompt: Prompt a second time to confirm the + value. Can be set to a string instead of ``True`` to customize + the message. + :param type: the type to use to check the value against. + :param value_proc: if this parameter is provided it's a function that + is invoked instead of the type conversion to + convert a value. + :param prompt_suffix: a suffix that should be added to the prompt. + :param show_default: shows or hides the default value in the prompt. + :param err: if set to true the file defaults to ``stderr`` instead of + ``stdout``, the same as with echo. + :param show_choices: Show or hide choices if the passed type is a Choice. + For example if type is a Choice of either day or week, + show_choices is true and text is "Group by" then the + prompt will be "Group by (day, week): ". + + .. versionadded:: 8.0 + ``confirmation_prompt`` can be a custom string. + + .. versionadded:: 7.0 + Added the ``show_choices`` parameter. + + .. versionadded:: 6.0 + Added unicode support for cmd.exe on Windows. + + .. versionadded:: 4.0 + Added the `err` parameter. + + """ + + def prompt_func(text: str) -> str: + f = hidden_prompt_func if hide_input else visible_prompt_func + try: + # Write the prompt separately so that we get nice + # coloring through colorama on Windows + echo(text.rstrip(" "), nl=False, err=err) + # Echo a space to stdout to work around an issue where + # readline causes backspace to clear the whole line. + return f(" ") + except (KeyboardInterrupt, EOFError): + # getpass doesn't print a newline if the user aborts input with ^C. + # Allegedly this behavior is inherited from getpass(3). + # A doc bug has been filed at https://bugs.python.org/issue24711 + if hide_input: + echo(None, err=err) + raise Abort() from None + + if value_proc is None: + value_proc = convert_type(type, default) + + prompt = _build_prompt( + text, prompt_suffix, show_default, default, show_choices, type + ) + + if confirmation_prompt: + if confirmation_prompt is True: + confirmation_prompt = _("Repeat for confirmation") + + confirmation_prompt = t.cast(str, confirmation_prompt) + confirmation_prompt = _build_prompt(confirmation_prompt, prompt_suffix) + + while True: + while True: + value = prompt_func(prompt) + if value: + break + elif default is not None: + value = default + break + try: + result = value_proc(value) + except UsageError as e: + if hide_input: + echo(_("Error: The value you entered was invalid."), err=err) + else: + echo(_("Error: {e.message}").format(e=e), err=err) # noqa: B306 + continue + if not confirmation_prompt: + return result + while True: + confirmation_prompt = t.cast(str, confirmation_prompt) + value2 = prompt_func(confirmation_prompt) + if value2: + break + if value == value2: + return result + echo(_("Error: The two entered values do not match."), err=err) + + +def confirm( + text: str, + default: t.Optional[bool] = False, + abort: bool = False, + prompt_suffix: str = ": ", + show_default: bool = True, + err: bool = False, +) -> bool: + """Prompts for confirmation (yes/no question). + + If the user aborts the input by sending a interrupt signal this + function will catch it and raise a :exc:`Abort` exception. + + :param text: the question to ask. + :param default: The default value to use when no input is given. If + ``None``, repeat until input is given. + :param abort: if this is set to `True` a negative answer aborts the + exception by raising :exc:`Abort`. + :param prompt_suffix: a suffix that should be added to the prompt. + :param show_default: shows or hides the default value in the prompt. + :param err: if set to true the file defaults to ``stderr`` instead of + ``stdout``, the same as with echo. + + .. versionchanged:: 8.0 + Repeat until input is given if ``default`` is ``None``. + + .. versionadded:: 4.0 + Added the ``err`` parameter. + """ + prompt = _build_prompt( + text, + prompt_suffix, + show_default, + "y/n" if default is None else ("Y/n" if default else "y/N"), + ) + + while True: + try: + # Write the prompt separately so that we get nice + # coloring through colorama on Windows + echo(prompt.rstrip(" "), nl=False, err=err) + # Echo a space to stdout to work around an issue where + # readline causes backspace to clear the whole line. + value = visible_prompt_func(" ").lower().strip() + except (KeyboardInterrupt, EOFError): + raise Abort() from None + if value in ("y", "yes"): + rv = True + elif value in ("n", "no"): + rv = False + elif default is not None and value == "": + rv = default + else: + echo(_("Error: invalid input"), err=err) + continue + break + if abort and not rv: + raise Abort() + return rv + + +def get_terminal_size() -> os.terminal_size: + """Returns the current size of the terminal as tuple in the form + ``(width, height)`` in columns and rows. + + .. deprecated:: 8.0 + Will be removed in Click 8.1. Use + :func:`shutil.get_terminal_size` instead. + """ + import shutil + import warnings + + warnings.warn( + "'click.get_terminal_size()' is deprecated and will be removed" + " in Click 8.1. Use 'shutil.get_terminal_size()' instead.", + DeprecationWarning, + stacklevel=2, + ) + return shutil.get_terminal_size() + + +def echo_via_pager( + text_or_generator: t.Union[t.Iterable[str], t.Callable[[], t.Iterable[str]], str], + color: t.Optional[bool] = None, +) -> None: + """This function takes a text and shows it via an environment specific + pager on stdout. + + .. versionchanged:: 3.0 + Added the `color` flag. + + :param text_or_generator: the text to page, or alternatively, a + generator emitting the text to page. + :param color: controls if the pager supports ANSI colors or not. The + default is autodetection. + """ + color = resolve_color_default(color) + + if inspect.isgeneratorfunction(text_or_generator): + i = t.cast(t.Callable[[], t.Iterable[str]], text_or_generator)() + elif isinstance(text_or_generator, str): + i = [text_or_generator] + else: + i = iter(t.cast(t.Iterable[str], text_or_generator)) + + # convert every element of i to a text type if necessary + text_generator = (el if isinstance(el, str) else str(el) for el in i) + + from ._termui_impl import pager + + return pager(itertools.chain(text_generator, "\n"), color) + + +def progressbar( + iterable: t.Optional[t.Iterable[V]] = None, + length: t.Optional[int] = None, + label: t.Optional[str] = None, + show_eta: bool = True, + show_percent: t.Optional[bool] = None, + show_pos: bool = False, + item_show_func: t.Optional[t.Callable[[t.Optional[V]], t.Optional[str]]] = None, + fill_char: str = "#", + empty_char: str = "-", + bar_template: str = "%(label)s [%(bar)s] %(info)s", + info_sep: str = " ", + width: int = 36, + file: t.Optional[t.TextIO] = None, + color: t.Optional[bool] = None, + update_min_steps: int = 1, +) -> "ProgressBar[V]": + """This function creates an iterable context manager that can be used + to iterate over something while showing a progress bar. It will + either iterate over the `iterable` or `length` items (that are counted + up). While iteration happens, this function will print a rendered + progress bar to the given `file` (defaults to stdout) and will attempt + to calculate remaining time and more. By default, this progress bar + will not be rendered if the file is not a terminal. + + The context manager creates the progress bar. When the context + manager is entered the progress bar is already created. With every + iteration over the progress bar, the iterable passed to the bar is + advanced and the bar is updated. When the context manager exits, + a newline is printed and the progress bar is finalized on screen. + + Note: The progress bar is currently designed for use cases where the + total progress can be expected to take at least several seconds. + Because of this, the ProgressBar class object won't display + progress that is considered too fast, and progress where the time + between steps is less than a second. + + No printing must happen or the progress bar will be unintentionally + destroyed. + + Example usage:: + + with progressbar(items) as bar: + for item in bar: + do_something_with(item) + + Alternatively, if no iterable is specified, one can manually update the + progress bar through the `update()` method instead of directly + iterating over the progress bar. The update method accepts the number + of steps to increment the bar with:: + + with progressbar(length=chunks.total_bytes) as bar: + for chunk in chunks: + process_chunk(chunk) + bar.update(chunks.bytes) + + The ``update()`` method also takes an optional value specifying the + ``current_item`` at the new position. This is useful when used + together with ``item_show_func`` to customize the output for each + manual step:: + + with click.progressbar( + length=total_size, + label='Unzipping archive', + item_show_func=lambda a: a.filename + ) as bar: + for archive in zip_file: + archive.extract() + bar.update(archive.size, archive) + + :param iterable: an iterable to iterate over. If not provided the length + is required. + :param length: the number of items to iterate over. By default the + progressbar will attempt to ask the iterator about its + length, which might or might not work. If an iterable is + also provided this parameter can be used to override the + length. If an iterable is not provided the progress bar + will iterate over a range of that length. + :param label: the label to show next to the progress bar. + :param show_eta: enables or disables the estimated time display. This is + automatically disabled if the length cannot be + determined. + :param show_percent: enables or disables the percentage display. The + default is `True` if the iterable has a length or + `False` if not. + :param show_pos: enables or disables the absolute position display. The + default is `False`. + :param item_show_func: A function called with the current item which + can return a string to show next to the progress bar. If the + function returns ``None`` nothing is shown. The current item can + be ``None``, such as when entering and exiting the bar. + :param fill_char: the character to use to show the filled part of the + progress bar. + :param empty_char: the character to use to show the non-filled part of + the progress bar. + :param bar_template: the format string to use as template for the bar. + The parameters in it are ``label`` for the label, + ``bar`` for the progress bar and ``info`` for the + info section. + :param info_sep: the separator between multiple info items (eta etc.) + :param width: the width of the progress bar in characters, 0 means full + terminal width + :param file: The file to write to. If this is not a terminal then + only the label is printed. + :param color: controls if the terminal supports ANSI colors or not. The + default is autodetection. This is only needed if ANSI + codes are included anywhere in the progress bar output + which is not the case by default. + :param update_min_steps: Render only when this many updates have + completed. This allows tuning for very fast iterators. + + .. versionchanged:: 8.0 + Output is shown even if execution time is less than 0.5 seconds. + + .. versionchanged:: 8.0 + ``item_show_func`` shows the current item, not the previous one. + + .. versionchanged:: 8.0 + Labels are echoed if the output is not a TTY. Reverts a change + in 7.0 that removed all output. + + .. versionadded:: 8.0 + Added the ``update_min_steps`` parameter. + + .. versionchanged:: 4.0 + Added the ``color`` parameter. Added the ``update`` method to + the object. + + .. versionadded:: 2.0 + """ + from ._termui_impl import ProgressBar + + color = resolve_color_default(color) + return ProgressBar( + iterable=iterable, + length=length, + show_eta=show_eta, + show_percent=show_percent, + show_pos=show_pos, + item_show_func=item_show_func, + fill_char=fill_char, + empty_char=empty_char, + bar_template=bar_template, + info_sep=info_sep, + file=file, + label=label, + width=width, + color=color, + update_min_steps=update_min_steps, + ) + + +def clear() -> None: + """Clears the terminal screen. This will have the effect of clearing + the whole visible space of the terminal and moving the cursor to the + top left. This does not do anything if not connected to a terminal. + + .. versionadded:: 2.0 + """ + if not isatty(sys.stdout): + return + if WIN: + os.system("cls") + else: + sys.stdout.write("\033[2J\033[1;1H") + + +def _interpret_color( + color: t.Union[int, t.Tuple[int, int, int], str], offset: int = 0 +) -> str: + if isinstance(color, int): + return f"{38 + offset};5;{color:d}" + + if isinstance(color, (tuple, list)): + r, g, b = color + return f"{38 + offset};2;{r:d};{g:d};{b:d}" + + return str(_ansi_colors[color] + offset) + + +def style( + text: t.Any, + fg: t.Optional[t.Union[int, t.Tuple[int, int, int], str]] = None, + bg: t.Optional[t.Union[int, t.Tuple[int, int, int], str]] = None, + bold: t.Optional[bool] = None, + dim: t.Optional[bool] = None, + underline: t.Optional[bool] = None, + overline: t.Optional[bool] = None, + italic: t.Optional[bool] = None, + blink: t.Optional[bool] = None, + reverse: t.Optional[bool] = None, + strikethrough: t.Optional[bool] = None, + reset: bool = True, +) -> str: + """Styles a text with ANSI styles and returns the new string. By + default the styling is self contained which means that at the end + of the string a reset code is issued. This can be prevented by + passing ``reset=False``. + + Examples:: + + click.echo(click.style('Hello World!', fg='green')) + click.echo(click.style('ATTENTION!', blink=True)) + click.echo(click.style('Some things', reverse=True, fg='cyan')) + click.echo(click.style('More colors', fg=(255, 12, 128), bg=117)) + + Supported color names: + + * ``black`` (might be a gray) + * ``red`` + * ``green`` + * ``yellow`` (might be an orange) + * ``blue`` + * ``magenta`` + * ``cyan`` + * ``white`` (might be light gray) + * ``bright_black`` + * ``bright_red`` + * ``bright_green`` + * ``bright_yellow`` + * ``bright_blue`` + * ``bright_magenta`` + * ``bright_cyan`` + * ``bright_white`` + * ``reset`` (reset the color code only) + + If the terminal supports it, color may also be specified as: + + - An integer in the interval [0, 255]. The terminal must support + 8-bit/256-color mode. + - An RGB tuple of three integers in [0, 255]. The terminal must + support 24-bit/true-color mode. + + See https://en.wikipedia.org/wiki/ANSI_color and + https://gist.github.com/XVilka/8346728 for more information. + + :param text: the string to style with ansi codes. + :param fg: if provided this will become the foreground color. + :param bg: if provided this will become the background color. + :param bold: if provided this will enable or disable bold mode. + :param dim: if provided this will enable or disable dim mode. This is + badly supported. + :param underline: if provided this will enable or disable underline. + :param overline: if provided this will enable or disable overline. + :param italic: if provided this will enable or disable italic. + :param blink: if provided this will enable or disable blinking. + :param reverse: if provided this will enable or disable inverse + rendering (foreground becomes background and the + other way round). + :param strikethrough: if provided this will enable or disable + striking through text. + :param reset: by default a reset-all code is added at the end of the + string which means that styles do not carry over. This + can be disabled to compose styles. + + .. versionchanged:: 8.0 + A non-string ``message`` is converted to a string. + + .. versionchanged:: 8.0 + Added support for 256 and RGB color codes. + + .. versionchanged:: 8.0 + Added the ``strikethrough``, ``italic``, and ``overline`` + parameters. + + .. versionchanged:: 7.0 + Added support for bright colors. + + .. versionadded:: 2.0 + """ + if not isinstance(text, str): + text = str(text) + + bits = [] + + if fg: + try: + bits.append(f"\033[{_interpret_color(fg)}m") + except KeyError: + raise TypeError(f"Unknown color {fg!r}") from None + + if bg: + try: + bits.append(f"\033[{_interpret_color(bg, 10)}m") + except KeyError: + raise TypeError(f"Unknown color {bg!r}") from None + + if bold is not None: + bits.append(f"\033[{1 if bold else 22}m") + if dim is not None: + bits.append(f"\033[{2 if dim else 22}m") + if underline is not None: + bits.append(f"\033[{4 if underline else 24}m") + if overline is not None: + bits.append(f"\033[{53 if overline else 55}m") + if italic is not None: + bits.append(f"\033[{3 if italic else 23}m") + if blink is not None: + bits.append(f"\033[{5 if blink else 25}m") + if reverse is not None: + bits.append(f"\033[{7 if reverse else 27}m") + if strikethrough is not None: + bits.append(f"\033[{9 if strikethrough else 29}m") + bits.append(text) + if reset: + bits.append(_ansi_reset_all) + return "".join(bits) + + +def unstyle(text: str) -> str: + """Removes ANSI styling information from a string. Usually it's not + necessary to use this function as Click's echo function will + automatically remove styling if necessary. + + .. versionadded:: 2.0 + + :param text: the text to remove style information from. + """ + return strip_ansi(text) + + +def secho( + message: t.Optional[t.Any] = None, + file: t.Optional[t.IO] = None, + nl: bool = True, + err: bool = False, + color: t.Optional[bool] = None, + **styles: t.Any, +) -> None: + """This function combines :func:`echo` and :func:`style` into one + call. As such the following two calls are the same:: + + click.secho('Hello World!', fg='green') + click.echo(click.style('Hello World!', fg='green')) + + All keyword arguments are forwarded to the underlying functions + depending on which one they go with. + + Non-string types will be converted to :class:`str`. However, + :class:`bytes` are passed directly to :meth:`echo` without applying + style. If you want to style bytes that represent text, call + :meth:`bytes.decode` first. + + .. versionchanged:: 8.0 + A non-string ``message`` is converted to a string. Bytes are + passed through without style applied. + + .. versionadded:: 2.0 + """ + if message is not None and not isinstance(message, (bytes, bytearray)): + message = style(message, **styles) + + return echo(message, file=file, nl=nl, err=err, color=color) + + +def edit( + text: t.Optional[t.AnyStr] = None, + editor: t.Optional[str] = None, + env: t.Optional[t.Mapping[str, str]] = None, + require_save: bool = True, + extension: str = ".txt", + filename: t.Optional[str] = None, +) -> t.Optional[t.AnyStr]: + r"""Edits the given text in the defined editor. If an editor is given + (should be the full path to the executable but the regular operating + system search path is used for finding the executable) it overrides + the detected editor. Optionally, some environment variables can be + used. If the editor is closed without changes, `None` is returned. In + case a file is edited directly the return value is always `None` and + `require_save` and `extension` are ignored. + + If the editor cannot be opened a :exc:`UsageError` is raised. + + Note for Windows: to simplify cross-platform usage, the newlines are + automatically converted from POSIX to Windows and vice versa. As such, + the message here will have ``\n`` as newline markers. + + :param text: the text to edit. + :param editor: optionally the editor to use. Defaults to automatic + detection. + :param env: environment variables to forward to the editor. + :param require_save: if this is true, then not saving in the editor + will make the return value become `None`. + :param extension: the extension to tell the editor about. This defaults + to `.txt` but changing this might change syntax + highlighting. + :param filename: if provided it will edit this file instead of the + provided text contents. It will not use a temporary + file as an indirection in that case. + """ + from ._termui_impl import Editor + + ed = Editor(editor=editor, env=env, require_save=require_save, extension=extension) + + if filename is None: + return ed.edit(text) + + ed.edit_file(filename) + return None + + +def launch(url: str, wait: bool = False, locate: bool = False) -> int: + """This function launches the given URL (or filename) in the default + viewer application for this file type. If this is an executable, it + might launch the executable in a new session. The return value is + the exit code of the launched application. Usually, ``0`` indicates + success. + + Examples:: + + click.launch('https://click.palletsprojects.com/') + click.launch('/my/downloaded/file', locate=True) + + .. versionadded:: 2.0 + + :param url: URL or filename of the thing to launch. + :param wait: Wait for the program to exit before returning. This + only works if the launched program blocks. In particular, + ``xdg-open`` on Linux does not block. + :param locate: if this is set to `True` then instead of launching the + application associated with the URL it will attempt to + launch a file manager with the file located. This + might have weird effects if the URL does not point to + the filesystem. + """ + from ._termui_impl import open_url + + return open_url(url, wait=wait, locate=locate) + + +# If this is provided, getchar() calls into this instead. This is used +# for unittesting purposes. +_getchar: t.Optional[t.Callable[[bool], str]] = None + + +def getchar(echo: bool = False) -> str: + """Fetches a single character from the terminal and returns it. This + will always return a unicode character and under certain rare + circumstances this might return more than one character. The + situations which more than one character is returned is when for + whatever reason multiple characters end up in the terminal buffer or + standard input was not actually a terminal. + + Note that this will always read from the terminal, even if something + is piped into the standard input. + + Note for Windows: in rare cases when typing non-ASCII characters, this + function might wait for a second character and then return both at once. + This is because certain Unicode characters look like special-key markers. + + .. versionadded:: 2.0 + + :param echo: if set to `True`, the character read will also show up on + the terminal. The default is to not show it. + """ + global _getchar + + if _getchar is None: + from ._termui_impl import getchar as f + + _getchar = f + + return _getchar(echo) + + +def raw_terminal() -> t.ContextManager[int]: + from ._termui_impl import raw_terminal as f + + return f() + + +def pause(info: t.Optional[str] = None, err: bool = False) -> None: + """This command stops execution and waits for the user to press any + key to continue. This is similar to the Windows batch "pause" + command. If the program is not run through a terminal, this command + will instead do nothing. + + .. versionadded:: 2.0 + + .. versionadded:: 4.0 + Added the `err` parameter. + + :param info: The message to print before pausing. Defaults to + ``"Press any key to continue..."``. + :param err: if set to message goes to ``stderr`` instead of + ``stdout``, the same as with echo. + """ + if not isatty(sys.stdin) or not isatty(sys.stdout): + return + + if info is None: + info = _("Press any key to continue...") + + try: + if info: + echo(info, nl=False, err=err) + try: + getchar() + except (KeyboardInterrupt, EOFError): + pass + finally: + if info: + echo(err=err) diff --git a/venv/lib/python3.7/site-packages/click/testing.py b/venv/lib/python3.7/site-packages/click/testing.py new file mode 100644 index 0000000..d19b850 --- /dev/null +++ b/venv/lib/python3.7/site-packages/click/testing.py @@ -0,0 +1,479 @@ +import contextlib +import io +import os +import shlex +import shutil +import sys +import tempfile +import typing as t +from types import TracebackType + +from . import formatting +from . import termui +from . import utils +from ._compat import _find_binary_reader + +if t.TYPE_CHECKING: + from .core import BaseCommand + + +class EchoingStdin: + def __init__(self, input: t.BinaryIO, output: t.BinaryIO) -> None: + self._input = input + self._output = output + self._paused = False + + def __getattr__(self, x: str) -> t.Any: + return getattr(self._input, x) + + def _echo(self, rv: bytes) -> bytes: + if not self._paused: + self._output.write(rv) + + return rv + + def read(self, n: int = -1) -> bytes: + return self._echo(self._input.read(n)) + + def read1(self, n: int = -1) -> bytes: + return self._echo(self._input.read1(n)) # type: ignore + + def readline(self, n: int = -1) -> bytes: + return self._echo(self._input.readline(n)) + + def readlines(self) -> t.List[bytes]: + return [self._echo(x) for x in self._input.readlines()] + + def __iter__(self) -> t.Iterator[bytes]: + return iter(self._echo(x) for x in self._input) + + def __repr__(self) -> str: + return repr(self._input) + + +@contextlib.contextmanager +def _pause_echo(stream: t.Optional[EchoingStdin]) -> t.Iterator[None]: + if stream is None: + yield + else: + stream._paused = True + yield + stream._paused = False + + +class _NamedTextIOWrapper(io.TextIOWrapper): + def __init__( + self, buffer: t.BinaryIO, name: str, mode: str, **kwargs: t.Any + ) -> None: + super().__init__(buffer, **kwargs) + self._name = name + self._mode = mode + + @property + def name(self) -> str: + return self._name + + @property + def mode(self) -> str: + return self._mode + + +def make_input_stream( + input: t.Optional[t.Union[str, bytes, t.IO]], charset: str +) -> t.BinaryIO: + # Is already an input stream. + if hasattr(input, "read"): + rv = _find_binary_reader(t.cast(t.IO, input)) + + if rv is not None: + return rv + + raise TypeError("Could not find binary reader for input stream.") + + if input is None: + input = b"" + elif isinstance(input, str): + input = input.encode(charset) + + return io.BytesIO(t.cast(bytes, input)) + + +class Result: + """Holds the captured result of an invoked CLI script.""" + + def __init__( + self, + runner: "CliRunner", + stdout_bytes: bytes, + stderr_bytes: t.Optional[bytes], + return_value: t.Any, + exit_code: int, + exception: t.Optional[BaseException], + exc_info: t.Optional[ + t.Tuple[t.Type[BaseException], BaseException, TracebackType] + ] = None, + ): + #: The runner that created the result + self.runner = runner + #: The standard output as bytes. + self.stdout_bytes = stdout_bytes + #: The standard error as bytes, or None if not available + self.stderr_bytes = stderr_bytes + #: The value returned from the invoked command. + #: + #: .. versionadded:: 8.0 + self.return_value = return_value + #: The exit code as integer. + self.exit_code = exit_code + #: The exception that happened if one did. + self.exception = exception + #: The traceback + self.exc_info = exc_info + + @property + def output(self) -> str: + """The (standard) output as unicode string.""" + return self.stdout + + @property + def stdout(self) -> str: + """The standard output as unicode string.""" + return self.stdout_bytes.decode(self.runner.charset, "replace").replace( + "\r\n", "\n" + ) + + @property + def stderr(self) -> str: + """The standard error as unicode string.""" + if self.stderr_bytes is None: + raise ValueError("stderr not separately captured") + return self.stderr_bytes.decode(self.runner.charset, "replace").replace( + "\r\n", "\n" + ) + + def __repr__(self) -> str: + exc_str = repr(self.exception) if self.exception else "okay" + return f"<{type(self).__name__} {exc_str}>" + + +class CliRunner: + """The CLI runner provides functionality to invoke a Click command line + script for unittesting purposes in a isolated environment. This only + works in single-threaded systems without any concurrency as it changes the + global interpreter state. + + :param charset: the character set for the input and output data. + :param env: a dictionary with environment variables for overriding. + :param echo_stdin: if this is set to `True`, then reading from stdin writes + to stdout. This is useful for showing examples in + some circumstances. Note that regular prompts + will automatically echo the input. + :param mix_stderr: if this is set to `False`, then stdout and stderr are + preserved as independent streams. This is useful for + Unix-philosophy apps that have predictable stdout and + noisy stderr, such that each may be measured + independently + """ + + def __init__( + self, + charset: str = "utf-8", + env: t.Optional[t.Mapping[str, t.Optional[str]]] = None, + echo_stdin: bool = False, + mix_stderr: bool = True, + ) -> None: + self.charset = charset + self.env = env or {} + self.echo_stdin = echo_stdin + self.mix_stderr = mix_stderr + + def get_default_prog_name(self, cli: "BaseCommand") -> str: + """Given a command object it will return the default program name + for it. The default is the `name` attribute or ``"root"`` if not + set. + """ + return cli.name or "root" + + def make_env( + self, overrides: t.Optional[t.Mapping[str, t.Optional[str]]] = None + ) -> t.Mapping[str, t.Optional[str]]: + """Returns the environment overrides for invoking a script.""" + rv = dict(self.env) + if overrides: + rv.update(overrides) + return rv + + @contextlib.contextmanager + def isolation( + self, + input: t.Optional[t.Union[str, bytes, t.IO]] = None, + env: t.Optional[t.Mapping[str, t.Optional[str]]] = None, + color: bool = False, + ) -> t.Iterator[t.Tuple[io.BytesIO, t.Optional[io.BytesIO]]]: + """A context manager that sets up the isolation for invoking of a + command line tool. This sets up stdin with the given input data + and `os.environ` with the overrides from the given dictionary. + This also rebinds some internals in Click to be mocked (like the + prompt functionality). + + This is automatically done in the :meth:`invoke` method. + + :param input: the input stream to put into sys.stdin. + :param env: the environment overrides as dictionary. + :param color: whether the output should contain color codes. The + application can still override this explicitly. + + .. versionchanged:: 8.0 + ``stderr`` is opened with ``errors="backslashreplace"`` + instead of the default ``"strict"``. + + .. versionchanged:: 4.0 + Added the ``color`` parameter. + """ + bytes_input = make_input_stream(input, self.charset) + echo_input = None + + old_stdin = sys.stdin + old_stdout = sys.stdout + old_stderr = sys.stderr + old_forced_width = formatting.FORCED_WIDTH + formatting.FORCED_WIDTH = 80 + + env = self.make_env(env) + + bytes_output = io.BytesIO() + + if self.echo_stdin: + bytes_input = echo_input = t.cast( + t.BinaryIO, EchoingStdin(bytes_input, bytes_output) + ) + + sys.stdin = text_input = _NamedTextIOWrapper( + bytes_input, encoding=self.charset, name="", mode="r" + ) + + if self.echo_stdin: + # Force unbuffered reads, otherwise TextIOWrapper reads a + # large chunk which is echoed early. + text_input._CHUNK_SIZE = 1 # type: ignore + + sys.stdout = _NamedTextIOWrapper( + bytes_output, encoding=self.charset, name="", mode="w" + ) + + bytes_error = None + if self.mix_stderr: + sys.stderr = sys.stdout + else: + bytes_error = io.BytesIO() + sys.stderr = _NamedTextIOWrapper( + bytes_error, + encoding=self.charset, + name="", + mode="w", + errors="backslashreplace", + ) + + @_pause_echo(echo_input) # type: ignore + def visible_input(prompt: t.Optional[str] = None) -> str: + sys.stdout.write(prompt or "") + val = text_input.readline().rstrip("\r\n") + sys.stdout.write(f"{val}\n") + sys.stdout.flush() + return val + + @_pause_echo(echo_input) # type: ignore + def hidden_input(prompt: t.Optional[str] = None) -> str: + sys.stdout.write(f"{prompt or ''}\n") + sys.stdout.flush() + return text_input.readline().rstrip("\r\n") + + @_pause_echo(echo_input) # type: ignore + def _getchar(echo: bool) -> str: + char = sys.stdin.read(1) + + if echo: + sys.stdout.write(char) + + sys.stdout.flush() + return char + + default_color = color + + def should_strip_ansi( + stream: t.Optional[t.IO] = None, color: t.Optional[bool] = None + ) -> bool: + if color is None: + return not default_color + return not color + + old_visible_prompt_func = termui.visible_prompt_func + old_hidden_prompt_func = termui.hidden_prompt_func + old__getchar_func = termui._getchar + old_should_strip_ansi = utils.should_strip_ansi # type: ignore + termui.visible_prompt_func = visible_input + termui.hidden_prompt_func = hidden_input + termui._getchar = _getchar + utils.should_strip_ansi = should_strip_ansi # type: ignore + + old_env = {} + try: + for key, value in env.items(): + old_env[key] = os.environ.get(key) + if value is None: + try: + del os.environ[key] + except Exception: + pass + else: + os.environ[key] = value + yield (bytes_output, bytes_error) + finally: + for key, value in old_env.items(): + if value is None: + try: + del os.environ[key] + except Exception: + pass + else: + os.environ[key] = value + sys.stdout = old_stdout + sys.stderr = old_stderr + sys.stdin = old_stdin + termui.visible_prompt_func = old_visible_prompt_func + termui.hidden_prompt_func = old_hidden_prompt_func + termui._getchar = old__getchar_func + utils.should_strip_ansi = old_should_strip_ansi # type: ignore + formatting.FORCED_WIDTH = old_forced_width + + def invoke( + self, + cli: "BaseCommand", + args: t.Optional[t.Union[str, t.Sequence[str]]] = None, + input: t.Optional[t.Union[str, bytes, t.IO]] = None, + env: t.Optional[t.Mapping[str, t.Optional[str]]] = None, + catch_exceptions: bool = True, + color: bool = False, + **extra: t.Any, + ) -> Result: + """Invokes a command in an isolated environment. The arguments are + forwarded directly to the command line script, the `extra` keyword + arguments are passed to the :meth:`~clickpkg.Command.main` function of + the command. + + This returns a :class:`Result` object. + + :param cli: the command to invoke + :param args: the arguments to invoke. It may be given as an iterable + or a string. When given as string it will be interpreted + as a Unix shell command. More details at + :func:`shlex.split`. + :param input: the input data for `sys.stdin`. + :param env: the environment overrides. + :param catch_exceptions: Whether to catch any other exceptions than + ``SystemExit``. + :param extra: the keyword arguments to pass to :meth:`main`. + :param color: whether the output should contain color codes. The + application can still override this explicitly. + + .. versionchanged:: 8.0 + The result object has the ``return_value`` attribute with + the value returned from the invoked command. + + .. versionchanged:: 4.0 + Added the ``color`` parameter. + + .. versionchanged:: 3.0 + Added the ``catch_exceptions`` parameter. + + .. versionchanged:: 3.0 + The result object has the ``exc_info`` attribute with the + traceback if available. + """ + exc_info = None + with self.isolation(input=input, env=env, color=color) as outstreams: + return_value = None + exception: t.Optional[BaseException] = None + exit_code = 0 + + if isinstance(args, str): + args = shlex.split(args) + + try: + prog_name = extra.pop("prog_name") + except KeyError: + prog_name = self.get_default_prog_name(cli) + + try: + return_value = cli.main(args=args or (), prog_name=prog_name, **extra) + except SystemExit as e: + exc_info = sys.exc_info() + e_code = t.cast(t.Optional[t.Union[int, t.Any]], e.code) + + if e_code is None: + e_code = 0 + + if e_code != 0: + exception = e + + if not isinstance(e_code, int): + sys.stdout.write(str(e_code)) + sys.stdout.write("\n") + e_code = 1 + + exit_code = e_code + + except Exception as e: + if not catch_exceptions: + raise + exception = e + exit_code = 1 + exc_info = sys.exc_info() + finally: + sys.stdout.flush() + stdout = outstreams[0].getvalue() + if self.mix_stderr: + stderr = None + else: + stderr = outstreams[1].getvalue() # type: ignore + + return Result( + runner=self, + stdout_bytes=stdout, + stderr_bytes=stderr, + return_value=return_value, + exit_code=exit_code, + exception=exception, + exc_info=exc_info, # type: ignore + ) + + @contextlib.contextmanager + def isolated_filesystem( + self, temp_dir: t.Optional[t.Union[str, os.PathLike]] = None + ) -> t.Iterator[str]: + """A context manager that creates a temporary directory and + changes the current working directory to it. This isolates tests + that affect the contents of the CWD to prevent them from + interfering with each other. + + :param temp_dir: Create the temporary directory under this + directory. If given, the created directory is not removed + when exiting. + + .. versionchanged:: 8.0 + Added the ``temp_dir`` parameter. + """ + cwd = os.getcwd() + t = tempfile.mkdtemp(dir=temp_dir) + os.chdir(t) + + try: + yield t + finally: + os.chdir(cwd) + + if temp_dir is None: + try: + shutil.rmtree(t) + except OSError: # noqa: B014 + pass diff --git a/venv/lib/python3.7/site-packages/click/types.py b/venv/lib/python3.7/site-packages/click/types.py new file mode 100644 index 0000000..103d218 --- /dev/null +++ b/venv/lib/python3.7/site-packages/click/types.py @@ -0,0 +1,1052 @@ +import os +import stat +import typing as t +from datetime import datetime +from gettext import gettext as _ +from gettext import ngettext + +from ._compat import _get_argv_encoding +from ._compat import get_filesystem_encoding +from ._compat import open_stream +from .exceptions import BadParameter +from .utils import LazyFile +from .utils import safecall + +if t.TYPE_CHECKING: + import typing_extensions as te + from .core import Context + from .core import Parameter + from .shell_completion import CompletionItem + + +class ParamType: + """Represents the type of a parameter. Validates and converts values + from the command line or Python into the correct type. + + To implement a custom type, subclass and implement at least the + following: + + - The :attr:`name` class attribute must be set. + - Calling an instance of the type with ``None`` must return + ``None``. This is already implemented by default. + - :meth:`convert` must convert string values to the correct type. + - :meth:`convert` must accept values that are already the correct + type. + - It must be able to convert a value if the ``ctx`` and ``param`` + arguments are ``None``. This can occur when converting prompt + input. + """ + + is_composite: t.ClassVar[bool] = False + arity: t.ClassVar[int] = 1 + + #: the descriptive name of this type + name: str + + #: if a list of this type is expected and the value is pulled from a + #: string environment variable, this is what splits it up. `None` + #: means any whitespace. For all parameters the general rule is that + #: whitespace splits them up. The exception are paths and files which + #: are split by ``os.path.pathsep`` by default (":" on Unix and ";" on + #: Windows). + envvar_list_splitter: t.ClassVar[t.Optional[str]] = None + + def to_info_dict(self) -> t.Dict[str, t.Any]: + """Gather information that could be useful for a tool generating + user-facing documentation. + + Use :meth:`click.Context.to_info_dict` to traverse the entire + CLI structure. + + .. versionadded:: 8.0 + """ + # The class name without the "ParamType" suffix. + param_type = type(self).__name__.partition("ParamType")[0] + param_type = param_type.partition("ParameterType")[0] + return {"param_type": param_type, "name": self.name} + + def __call__( + self, + value: t.Any, + param: t.Optional["Parameter"] = None, + ctx: t.Optional["Context"] = None, + ) -> t.Any: + if value is not None: + return self.convert(value, param, ctx) + + def get_metavar(self, param: "Parameter") -> t.Optional[str]: + """Returns the metavar default for this param if it provides one.""" + + def get_missing_message(self, param: "Parameter") -> t.Optional[str]: + """Optionally might return extra information about a missing + parameter. + + .. versionadded:: 2.0 + """ + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + """Convert the value to the correct type. This is not called if + the value is ``None`` (the missing value). + + This must accept string values from the command line, as well as + values that are already the correct type. It may also convert + other compatible types. + + The ``param`` and ``ctx`` arguments may be ``None`` in certain + situations, such as when converting prompt input. + + If the value cannot be converted, call :meth:`fail` with a + descriptive message. + + :param value: The value to convert. + :param param: The parameter that is using this type to convert + its value. May be ``None``. + :param ctx: The current context that arrived at this value. May + be ``None``. + """ + return value + + def split_envvar_value(self, rv: str) -> t.Sequence[str]: + """Given a value from an environment variable this splits it up + into small chunks depending on the defined envvar list splitter. + + If the splitter is set to `None`, which means that whitespace splits, + then leading and trailing whitespace is ignored. Otherwise, leading + and trailing splitters usually lead to empty items being included. + """ + return (rv or "").split(self.envvar_list_splitter) + + def fail( + self, + message: str, + param: t.Optional["Parameter"] = None, + ctx: t.Optional["Context"] = None, + ) -> "t.NoReturn": + """Helper method to fail with an invalid value message.""" + raise BadParameter(message, ctx=ctx, param=param) + + def shell_complete( + self, ctx: "Context", param: "Parameter", incomplete: str + ) -> t.List["CompletionItem"]: + """Return a list of + :class:`~click.shell_completion.CompletionItem` objects for the + incomplete value. Most types do not provide completions, but + some do, and this allows custom types to provide custom + completions as well. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + return [] + + +class CompositeParamType(ParamType): + is_composite = True + + @property + def arity(self) -> int: # type: ignore + raise NotImplementedError() + + +class FuncParamType(ParamType): + def __init__(self, func: t.Callable[[t.Any], t.Any]) -> None: + self.name = func.__name__ + self.func = func + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["func"] = self.func + return info_dict + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + try: + return self.func(value) + except ValueError: + try: + value = str(value) + except UnicodeError: + value = value.decode("utf-8", "replace") + + self.fail(value, param, ctx) + + +class UnprocessedParamType(ParamType): + name = "text" + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + return value + + def __repr__(self) -> str: + return "UNPROCESSED" + + +class StringParamType(ParamType): + name = "text" + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + if isinstance(value, bytes): + enc = _get_argv_encoding() + try: + value = value.decode(enc) + except UnicodeError: + fs_enc = get_filesystem_encoding() + if fs_enc != enc: + try: + value = value.decode(fs_enc) + except UnicodeError: + value = value.decode("utf-8", "replace") + else: + value = value.decode("utf-8", "replace") + return value + return str(value) + + def __repr__(self) -> str: + return "STRING" + + +class Choice(ParamType): + """The choice type allows a value to be checked against a fixed set + of supported values. All of these values have to be strings. + + You should only pass a list or tuple of choices. Other iterables + (like generators) may lead to surprising results. + + The resulting value will always be one of the originally passed choices + regardless of ``case_sensitive`` or any ``ctx.token_normalize_func`` + being specified. + + See :ref:`choice-opts` for an example. + + :param case_sensitive: Set to false to make choices case + insensitive. Defaults to true. + """ + + name = "choice" + + def __init__(self, choices: t.Sequence[str], case_sensitive: bool = True) -> None: + self.choices = choices + self.case_sensitive = case_sensitive + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["choices"] = self.choices + info_dict["case_sensitive"] = self.case_sensitive + return info_dict + + def get_metavar(self, param: "Parameter") -> str: + choices_str = "|".join(self.choices) + + # Use curly braces to indicate a required argument. + if param.required and param.param_type_name == "argument": + return f"{{{choices_str}}}" + + # Use square braces to indicate an option or optional argument. + return f"[{choices_str}]" + + def get_missing_message(self, param: "Parameter") -> str: + return _("Choose from:\n\t{choices}").format(choices=",\n\t".join(self.choices)) + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + # Match through normalization and case sensitivity + # first do token_normalize_func, then lowercase + # preserve original `value` to produce an accurate message in + # `self.fail` + normed_value = value + normed_choices = {choice: choice for choice in self.choices} + + if ctx is not None and ctx.token_normalize_func is not None: + normed_value = ctx.token_normalize_func(value) + normed_choices = { + ctx.token_normalize_func(normed_choice): original + for normed_choice, original in normed_choices.items() + } + + if not self.case_sensitive: + normed_value = normed_value.casefold() + normed_choices = { + normed_choice.casefold(): original + for normed_choice, original in normed_choices.items() + } + + if normed_value in normed_choices: + return normed_choices[normed_value] + + choices_str = ", ".join(map(repr, self.choices)) + self.fail( + ngettext( + "{value!r} is not {choice}.", + "{value!r} is not one of {choices}.", + len(self.choices), + ).format(value=value, choice=choices_str, choices=choices_str), + param, + ctx, + ) + + def __repr__(self) -> str: + return f"Choice({list(self.choices)})" + + def shell_complete( + self, ctx: "Context", param: "Parameter", incomplete: str + ) -> t.List["CompletionItem"]: + """Complete choices that start with the incomplete value. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + str_choices = map(str, self.choices) + + if self.case_sensitive: + matched = (c for c in str_choices if c.startswith(incomplete)) + else: + incomplete = incomplete.lower() + matched = (c for c in str_choices if c.lower().startswith(incomplete)) + + return [CompletionItem(c) for c in matched] + + +class DateTime(ParamType): + """The DateTime type converts date strings into `datetime` objects. + + The format strings which are checked are configurable, but default to some + common (non-timezone aware) ISO 8601 formats. + + When specifying *DateTime* formats, you should only pass a list or a tuple. + Other iterables, like generators, may lead to surprising results. + + The format strings are processed using ``datetime.strptime``, and this + consequently defines the format strings which are allowed. + + Parsing is tried using each format, in order, and the first format which + parses successfully is used. + + :param formats: A list or tuple of date format strings, in the order in + which they should be tried. Defaults to + ``'%Y-%m-%d'``, ``'%Y-%m-%dT%H:%M:%S'``, + ``'%Y-%m-%d %H:%M:%S'``. + """ + + name = "datetime" + + def __init__(self, formats: t.Optional[t.Sequence[str]] = None): + self.formats = formats or ["%Y-%m-%d", "%Y-%m-%dT%H:%M:%S", "%Y-%m-%d %H:%M:%S"] + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["formats"] = self.formats + return info_dict + + def get_metavar(self, param: "Parameter") -> str: + return f"[{'|'.join(self.formats)}]" + + def _try_to_convert_date(self, value: t.Any, format: str) -> t.Optional[datetime]: + try: + return datetime.strptime(value, format) + except ValueError: + return None + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + if isinstance(value, datetime): + return value + + for format in self.formats: + converted = self._try_to_convert_date(value, format) + + if converted is not None: + return converted + + formats_str = ", ".join(map(repr, self.formats)) + self.fail( + ngettext( + "{value!r} does not match the format {format}.", + "{value!r} does not match the formats {formats}.", + len(self.formats), + ).format(value=value, format=formats_str, formats=formats_str), + param, + ctx, + ) + + def __repr__(self) -> str: + return "DateTime" + + +class _NumberParamTypeBase(ParamType): + _number_class: t.ClassVar[t.Type] + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + try: + return self._number_class(value) + except ValueError: + self.fail( + _("{value!r} is not a valid {number_type}.").format( + value=value, number_type=self.name + ), + param, + ctx, + ) + + +class _NumberRangeBase(_NumberParamTypeBase): + def __init__( + self, + min: t.Optional[float] = None, + max: t.Optional[float] = None, + min_open: bool = False, + max_open: bool = False, + clamp: bool = False, + ) -> None: + self.min = min + self.max = max + self.min_open = min_open + self.max_open = max_open + self.clamp = clamp + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict.update( + min=self.min, + max=self.max, + min_open=self.min_open, + max_open=self.max_open, + clamp=self.clamp, + ) + return info_dict + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + import operator + + rv = super().convert(value, param, ctx) + lt_min: bool = self.min is not None and ( + operator.le if self.min_open else operator.lt + )(rv, self.min) + gt_max: bool = self.max is not None and ( + operator.ge if self.max_open else operator.gt + )(rv, self.max) + + if self.clamp: + if lt_min: + return self._clamp(self.min, 1, self.min_open) # type: ignore + + if gt_max: + return self._clamp(self.max, -1, self.max_open) # type: ignore + + if lt_min or gt_max: + self.fail( + _("{value} is not in the range {range}.").format( + value=rv, range=self._describe_range() + ), + param, + ctx, + ) + + return rv + + def _clamp(self, bound: float, dir: "te.Literal[1, -1]", open: bool) -> float: + """Find the valid value to clamp to bound in the given + direction. + + :param bound: The boundary value. + :param dir: 1 or -1 indicating the direction to move. + :param open: If true, the range does not include the bound. + """ + raise NotImplementedError + + def _describe_range(self) -> str: + """Describe the range for use in help text.""" + if self.min is None: + op = "<" if self.max_open else "<=" + return f"x{op}{self.max}" + + if self.max is None: + op = ">" if self.min_open else ">=" + return f"x{op}{self.min}" + + lop = "<" if self.min_open else "<=" + rop = "<" if self.max_open else "<=" + return f"{self.min}{lop}x{rop}{self.max}" + + def __repr__(self) -> str: + clamp = " clamped" if self.clamp else "" + return f"<{type(self).__name__} {self._describe_range()}{clamp}>" + + +class IntParamType(_NumberParamTypeBase): + name = "integer" + _number_class = int + + def __repr__(self) -> str: + return "INT" + + +class IntRange(_NumberRangeBase, IntParamType): + """Restrict an :data:`click.INT` value to a range of accepted + values. See :ref:`ranges`. + + If ``min`` or ``max`` are not passed, any value is accepted in that + direction. If ``min_open`` or ``max_open`` are enabled, the + corresponding boundary is not included in the range. + + If ``clamp`` is enabled, a value outside the range is clamped to the + boundary instead of failing. + + .. versionchanged:: 8.0 + Added the ``min_open`` and ``max_open`` parameters. + """ + + name = "integer range" + + def _clamp( # type: ignore + self, bound: int, dir: "te.Literal[1, -1]", open: bool + ) -> int: + if not open: + return bound + + return bound + dir + + +class FloatParamType(_NumberParamTypeBase): + name = "float" + _number_class = float + + def __repr__(self) -> str: + return "FLOAT" + + +class FloatRange(_NumberRangeBase, FloatParamType): + """Restrict a :data:`click.FLOAT` value to a range of accepted + values. See :ref:`ranges`. + + If ``min`` or ``max`` are not passed, any value is accepted in that + direction. If ``min_open`` or ``max_open`` are enabled, the + corresponding boundary is not included in the range. + + If ``clamp`` is enabled, a value outside the range is clamped to the + boundary instead of failing. This is not supported if either + boundary is marked ``open``. + + .. versionchanged:: 8.0 + Added the ``min_open`` and ``max_open`` parameters. + """ + + name = "float range" + + def __init__( + self, + min: t.Optional[float] = None, + max: t.Optional[float] = None, + min_open: bool = False, + max_open: bool = False, + clamp: bool = False, + ) -> None: + super().__init__( + min=min, max=max, min_open=min_open, max_open=max_open, clamp=clamp + ) + + if (min_open or max_open) and clamp: + raise TypeError("Clamping is not supported for open bounds.") + + def _clamp(self, bound: float, dir: "te.Literal[1, -1]", open: bool) -> float: + if not open: + return bound + + # Could use Python 3.9's math.nextafter here, but clamping an + # open float range doesn't seem to be particularly useful. It's + # left up to the user to write a callback to do it if needed. + raise RuntimeError("Clamping is not supported for open bounds.") + + +class BoolParamType(ParamType): + name = "boolean" + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + if value in {False, True}: + return bool(value) + + norm = value.strip().lower() + + if norm in {"1", "true", "t", "yes", "y", "on"}: + return True + + if norm in {"0", "false", "f", "no", "n", "off"}: + return False + + self.fail( + _("{value!r} is not a valid boolean.").format(value=value), param, ctx + ) + + def __repr__(self) -> str: + return "BOOL" + + +class UUIDParameterType(ParamType): + name = "uuid" + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + import uuid + + if isinstance(value, uuid.UUID): + return value + + value = value.strip() + + try: + return uuid.UUID(value) + except ValueError: + self.fail( + _("{value!r} is not a valid UUID.").format(value=value), param, ctx + ) + + def __repr__(self) -> str: + return "UUID" + + +class File(ParamType): + """Declares a parameter to be a file for reading or writing. The file + is automatically closed once the context tears down (after the command + finished working). + + Files can be opened for reading or writing. The special value ``-`` + indicates stdin or stdout depending on the mode. + + By default, the file is opened for reading text data, but it can also be + opened in binary mode or for writing. The encoding parameter can be used + to force a specific encoding. + + The `lazy` flag controls if the file should be opened immediately or upon + first IO. The default is to be non-lazy for standard input and output + streams as well as files opened for reading, `lazy` otherwise. When opening a + file lazily for reading, it is still opened temporarily for validation, but + will not be held open until first IO. lazy is mainly useful when opening + for writing to avoid creating the file until it is needed. + + Starting with Click 2.0, files can also be opened atomically in which + case all writes go into a separate file in the same folder and upon + completion the file will be moved over to the original location. This + is useful if a file regularly read by other users is modified. + + See :ref:`file-args` for more information. + """ + + name = "filename" + envvar_list_splitter = os.path.pathsep + + def __init__( + self, + mode: str = "r", + encoding: t.Optional[str] = None, + errors: t.Optional[str] = "strict", + lazy: t.Optional[bool] = None, + atomic: bool = False, + ) -> None: + self.mode = mode + self.encoding = encoding + self.errors = errors + self.lazy = lazy + self.atomic = atomic + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict.update(mode=self.mode, encoding=self.encoding) + return info_dict + + def resolve_lazy_flag(self, value: t.Any) -> bool: + if self.lazy is not None: + return self.lazy + if value == "-": + return False + elif "w" in self.mode: + return True + return False + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + try: + if hasattr(value, "read") or hasattr(value, "write"): + return value + + lazy = self.resolve_lazy_flag(value) + + if lazy: + f: t.IO = t.cast( + t.IO, + LazyFile( + value, self.mode, self.encoding, self.errors, atomic=self.atomic + ), + ) + + if ctx is not None: + ctx.call_on_close(f.close_intelligently) # type: ignore + + return f + + f, should_close = open_stream( + value, self.mode, self.encoding, self.errors, atomic=self.atomic + ) + + # If a context is provided, we automatically close the file + # at the end of the context execution (or flush out). If a + # context does not exist, it's the caller's responsibility to + # properly close the file. This for instance happens when the + # type is used with prompts. + if ctx is not None: + if should_close: + ctx.call_on_close(safecall(f.close)) + else: + ctx.call_on_close(safecall(f.flush)) + + return f + except OSError as e: # noqa: B014 + self.fail(f"{os.fsdecode(value)!r}: {e.strerror}", param, ctx) + + def shell_complete( + self, ctx: "Context", param: "Parameter", incomplete: str + ) -> t.List["CompletionItem"]: + """Return a special completion marker that tells the completion + system to use the shell to provide file path completions. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + return [CompletionItem(incomplete, type="file")] + + +class Path(ParamType): + """The path type is similar to the :class:`File` type but it performs + different checks. First of all, instead of returning an open file + handle it returns just the filename. Secondly, it can perform various + basic checks about what the file or directory should be. + + :param exists: if set to true, the file or directory needs to exist for + this value to be valid. If this is not required and a + file does indeed not exist, then all further checks are + silently skipped. + :param file_okay: controls if a file is a possible value. + :param dir_okay: controls if a directory is a possible value. + :param writable: if true, a writable check is performed. + :param readable: if true, a readable check is performed. + :param resolve_path: if this is true, then the path is fully resolved + before the value is passed onwards. This means + that it's absolute and symlinks are resolved. It + will not expand a tilde-prefix, as this is + supposed to be done by the shell only. + :param allow_dash: If this is set to `True`, a single dash to indicate + standard streams is permitted. + :param path_type: Convert the incoming path value to this type. If + ``None``, keep Python's default, which is ``str``. Useful to + convert to :class:`pathlib.Path`. + + .. versionchanged:: 8.0 + Allow passing ``type=pathlib.Path``. + + .. versionchanged:: 6.0 + Added the ``allow_dash`` parameter. + """ + + envvar_list_splitter = os.path.pathsep + + def __init__( + self, + exists: bool = False, + file_okay: bool = True, + dir_okay: bool = True, + writable: bool = False, + readable: bool = True, + resolve_path: bool = False, + allow_dash: bool = False, + path_type: t.Optional[t.Type] = None, + ): + self.exists = exists + self.file_okay = file_okay + self.dir_okay = dir_okay + self.writable = writable + self.readable = readable + self.resolve_path = resolve_path + self.allow_dash = allow_dash + self.type = path_type + + if self.file_okay and not self.dir_okay: + self.name = _("file") + elif self.dir_okay and not self.file_okay: + self.name = _("directory") + else: + self.name = _("path") + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict.update( + exists=self.exists, + file_okay=self.file_okay, + dir_okay=self.dir_okay, + writable=self.writable, + readable=self.readable, + allow_dash=self.allow_dash, + ) + return info_dict + + def coerce_path_result(self, rv: t.Any) -> t.Any: + if self.type is not None and not isinstance(rv, self.type): + if self.type is str: + rv = os.fsdecode(rv) + elif self.type is bytes: + rv = os.fsencode(rv) + else: + rv = self.type(rv) + + return rv + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + rv = value + + is_dash = self.file_okay and self.allow_dash and rv in (b"-", "-") + + if not is_dash: + if self.resolve_path: + # os.path.realpath doesn't resolve symlinks on Windows + # until Python 3.8. Use pathlib for now. + import pathlib + + rv = os.fsdecode(pathlib.Path(rv).resolve()) + + try: + st = os.stat(rv) + except OSError: + if not self.exists: + return self.coerce_path_result(rv) + self.fail( + _("{name} {filename!r} does not exist.").format( + name=self.name.title(), filename=os.fsdecode(value) + ), + param, + ctx, + ) + + if not self.file_okay and stat.S_ISREG(st.st_mode): + self.fail( + _("{name} {filename!r} is a file.").format( + name=self.name.title(), filename=os.fsdecode(value) + ), + param, + ctx, + ) + if not self.dir_okay and stat.S_ISDIR(st.st_mode): + self.fail( + _("{name} {filename!r} is a directory.").format( + name=self.name.title(), filename=os.fsdecode(value) + ), + param, + ctx, + ) + if self.writable and not os.access(rv, os.W_OK): + self.fail( + _("{name} {filename!r} is not writable.").format( + name=self.name.title(), filename=os.fsdecode(value) + ), + param, + ctx, + ) + if self.readable and not os.access(rv, os.R_OK): + self.fail( + _("{name} {filename!r} is not readable.").format( + name=self.name.title(), filename=os.fsdecode(value) + ), + param, + ctx, + ) + + return self.coerce_path_result(rv) + + def shell_complete( + self, ctx: "Context", param: "Parameter", incomplete: str + ) -> t.List["CompletionItem"]: + """Return a special completion marker that tells the completion + system to use the shell to provide path completions for only + directories or any paths. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + type = "dir" if self.dir_okay and not self.file_okay else "file" + return [CompletionItem(incomplete, type=type)] + + +class Tuple(CompositeParamType): + """The default behavior of Click is to apply a type on a value directly. + This works well in most cases, except for when `nargs` is set to a fixed + count and different types should be used for different items. In this + case the :class:`Tuple` type can be used. This type can only be used + if `nargs` is set to a fixed number. + + For more information see :ref:`tuple-type`. + + This can be selected by using a Python tuple literal as a type. + + :param types: a list of types that should be used for the tuple items. + """ + + def __init__(self, types: t.Sequence[t.Union[t.Type, ParamType]]) -> None: + self.types = [convert_type(ty) for ty in types] + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["types"] = [t.to_info_dict() for t in self.types] + return info_dict + + @property + def name(self) -> str: # type: ignore + return f"<{' '.join(ty.name for ty in self.types)}>" + + @property + def arity(self) -> int: # type: ignore + return len(self.types) + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + len_type = len(self.types) + len_value = len(value) + + if len_value != len_type: + self.fail( + ngettext( + "{len_type} values are required, but {len_value} was given.", + "{len_type} values are required, but {len_value} were given.", + len_value, + ).format(len_type=len_type, len_value=len_value), + param=param, + ctx=ctx, + ) + + return tuple(ty(x, param, ctx) for ty, x in zip(self.types, value)) + + +def convert_type(ty: t.Optional[t.Any], default: t.Optional[t.Any] = None) -> ParamType: + """Find the most appropriate :class:`ParamType` for the given Python + type. If the type isn't provided, it can be inferred from a default + value. + """ + guessed_type = False + + if ty is None and default is not None: + if isinstance(default, (tuple, list)): + # If the default is empty, ty will remain None and will + # return STRING. + if default: + item = default[0] + + # A tuple of tuples needs to detect the inner types. + # Can't call convert recursively because that would + # incorrectly unwind the tuple to a single type. + if isinstance(item, (tuple, list)): + ty = tuple(map(type, item)) + else: + ty = type(item) + else: + ty = type(default) + + guessed_type = True + + if isinstance(ty, tuple): + return Tuple(ty) + + if isinstance(ty, ParamType): + return ty + + if ty is str or ty is None: + return STRING + + if ty is int: + return INT + + if ty is float: + return FLOAT + + if ty is bool: + return BOOL + + if guessed_type: + return STRING + + if __debug__: + try: + if issubclass(ty, ParamType): + raise AssertionError( + f"Attempted to use an uninstantiated parameter type ({ty})." + ) + except TypeError: + # ty is an instance (correct), so issubclass fails. + pass + + return FuncParamType(ty) + + +#: A dummy parameter type that just does nothing. From a user's +#: perspective this appears to just be the same as `STRING` but +#: internally no string conversion takes place if the input was bytes. +#: This is usually useful when working with file paths as they can +#: appear in bytes and unicode. +#: +#: For path related uses the :class:`Path` type is a better choice but +#: there are situations where an unprocessed type is useful which is why +#: it is is provided. +#: +#: .. versionadded:: 4.0 +UNPROCESSED = UnprocessedParamType() + +#: A unicode string parameter type which is the implicit default. This +#: can also be selected by using ``str`` as type. +STRING = StringParamType() + +#: An integer parameter. This can also be selected by using ``int`` as +#: type. +INT = IntParamType() + +#: A floating point value parameter. This can also be selected by using +#: ``float`` as type. +FLOAT = FloatParamType() + +#: A boolean parameter. This is the default for boolean flags. This can +#: also be selected by using ``bool`` as a type. +BOOL = BoolParamType() + +#: A UUID parameter. +UUID = UUIDParameterType() diff --git a/venv/lib/python3.7/site-packages/click/utils.py b/venv/lib/python3.7/site-packages/click/utils.py new file mode 100644 index 0000000..16033d6 --- /dev/null +++ b/venv/lib/python3.7/site-packages/click/utils.py @@ -0,0 +1,579 @@ +import os +import sys +import typing as t +from functools import update_wrapper +from types import ModuleType + +from ._compat import _default_text_stderr +from ._compat import _default_text_stdout +from ._compat import _find_binary_writer +from ._compat import auto_wrap_for_ansi +from ._compat import binary_streams +from ._compat import get_filesystem_encoding +from ._compat import open_stream +from ._compat import should_strip_ansi +from ._compat import strip_ansi +from ._compat import text_streams +from ._compat import WIN +from .globals import resolve_color_default + +if t.TYPE_CHECKING: + import typing_extensions as te + +F = t.TypeVar("F", bound=t.Callable[..., t.Any]) + + +def _posixify(name: str) -> str: + return "-".join(name.split()).lower() + + +def safecall(func: F) -> F: + """Wraps a function so that it swallows exceptions.""" + + def wrapper(*args, **kwargs): # type: ignore + try: + return func(*args, **kwargs) + except Exception: + pass + + return update_wrapper(t.cast(F, wrapper), func) + + +def make_str(value: t.Any) -> str: + """Converts a value into a valid string.""" + if isinstance(value, bytes): + try: + return value.decode(get_filesystem_encoding()) + except UnicodeError: + return value.decode("utf-8", "replace") + return str(value) + + +def make_default_short_help(help: str, max_length: int = 45) -> str: + """Returns a condensed version of help string.""" + # Consider only the first paragraph. + paragraph_end = help.find("\n\n") + + if paragraph_end != -1: + help = help[:paragraph_end] + + # Collapse newlines, tabs, and spaces. + words = help.split() + + if not words: + return "" + + # The first paragraph started with a "no rewrap" marker, ignore it. + if words[0] == "\b": + words = words[1:] + + total_length = 0 + last_index = len(words) - 1 + + for i, word in enumerate(words): + total_length += len(word) + (i > 0) + + if total_length > max_length: # too long, truncate + break + + if word[-1] == ".": # sentence end, truncate without "..." + return " ".join(words[: i + 1]) + + if total_length == max_length and i != last_index: + break # not at sentence end, truncate with "..." + else: + return " ".join(words) # no truncation needed + + # Account for the length of the suffix. + total_length += len("...") + + # remove words until the length is short enough + while i > 0: + total_length -= len(words[i]) + (i > 0) + + if total_length <= max_length: + break + + i -= 1 + + return " ".join(words[:i]) + "..." + + +class LazyFile: + """A lazy file works like a regular file but it does not fully open + the file but it does perform some basic checks early to see if the + filename parameter does make sense. This is useful for safely opening + files for writing. + """ + + def __init__( + self, + filename: str, + mode: str = "r", + encoding: t.Optional[str] = None, + errors: t.Optional[str] = "strict", + atomic: bool = False, + ): + self.name = filename + self.mode = mode + self.encoding = encoding + self.errors = errors + self.atomic = atomic + self._f: t.Optional[t.IO] + + if filename == "-": + self._f, self.should_close = open_stream(filename, mode, encoding, errors) + else: + if "r" in mode: + # Open and close the file in case we're opening it for + # reading so that we can catch at least some errors in + # some cases early. + open(filename, mode).close() + self._f = None + self.should_close = True + + def __getattr__(self, name: str) -> t.Any: + return getattr(self.open(), name) + + def __repr__(self) -> str: + if self._f is not None: + return repr(self._f) + return f"" + + def open(self) -> t.IO: + """Opens the file if it's not yet open. This call might fail with + a :exc:`FileError`. Not handling this error will produce an error + that Click shows. + """ + if self._f is not None: + return self._f + try: + rv, self.should_close = open_stream( + self.name, self.mode, self.encoding, self.errors, atomic=self.atomic + ) + except OSError as e: # noqa: E402 + from .exceptions import FileError + + raise FileError(self.name, hint=e.strerror) from e + self._f = rv + return rv + + def close(self) -> None: + """Closes the underlying file, no matter what.""" + if self._f is not None: + self._f.close() + + def close_intelligently(self) -> None: + """This function only closes the file if it was opened by the lazy + file wrapper. For instance this will never close stdin. + """ + if self.should_close: + self.close() + + def __enter__(self) -> "LazyFile": + return self + + def __exit__(self, exc_type, exc_value, tb): # type: ignore + self.close_intelligently() + + def __iter__(self) -> t.Iterator[t.AnyStr]: + self.open() + return iter(self._f) # type: ignore + + +class KeepOpenFile: + def __init__(self, file: t.IO) -> None: + self._file = file + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._file, name) + + def __enter__(self) -> "KeepOpenFile": + return self + + def __exit__(self, exc_type, exc_value, tb): # type: ignore + pass + + def __repr__(self) -> str: + return repr(self._file) + + def __iter__(self) -> t.Iterator[t.AnyStr]: + return iter(self._file) + + +def echo( + message: t.Optional[t.Any] = None, + file: t.Optional[t.IO] = None, + nl: bool = True, + err: bool = False, + color: t.Optional[bool] = None, +) -> None: + """Print a message and newline to stdout or a file. This should be + used instead of :func:`print` because it provides better support + for different data, files, and environments. + + Compared to :func:`print`, this does the following: + + - Ensures that the output encoding is not misconfigured on Linux. + - Supports Unicode in the Windows console. + - Supports writing to binary outputs, and supports writing bytes + to text outputs. + - Supports colors and styles on Windows. + - Removes ANSI color and style codes if the output does not look + like an interactive terminal. + - Always flushes the output. + + :param message: The string or bytes to output. Other objects are + converted to strings. + :param file: The file to write to. Defaults to ``stdout``. + :param err: Write to ``stderr`` instead of ``stdout``. + :param nl: Print a newline after the message. Enabled by default. + :param color: Force showing or hiding colors and other styles. By + default Click will remove color if the output does not look like + an interactive terminal. + + .. versionchanged:: 6.0 + Support Unicode output on the Windows console. Click does not + modify ``sys.stdout``, so ``sys.stdout.write()`` and ``print()`` + will still not support Unicode. + + .. versionchanged:: 4.0 + Added the ``color`` parameter. + + .. versionadded:: 3.0 + Added the ``err`` parameter. + + .. versionchanged:: 2.0 + Support colors on Windows if colorama is installed. + """ + if file is None: + if err: + file = _default_text_stderr() + else: + file = _default_text_stdout() + + # Convert non bytes/text into the native string type. + if message is not None and not isinstance(message, (str, bytes, bytearray)): + out: t.Optional[t.Union[str, bytes]] = str(message) + else: + out = message + + if nl: + out = out or "" + if isinstance(out, str): + out += "\n" + else: + out += b"\n" + + if not out: + file.flush() + return + + # If there is a message and the value looks like bytes, we manually + # need to find the binary stream and write the message in there. + # This is done separately so that most stream types will work as you + # would expect. Eg: you can write to StringIO for other cases. + if isinstance(out, (bytes, bytearray)): + binary_file = _find_binary_writer(file) + + if binary_file is not None: + file.flush() + binary_file.write(out) + binary_file.flush() + return + + # ANSI style code support. For no message or bytes, nothing happens. + # When outputting to a file instead of a terminal, strip codes. + else: + color = resolve_color_default(color) + + if should_strip_ansi(file, color): + out = strip_ansi(out) + elif WIN: + if auto_wrap_for_ansi is not None: + file = auto_wrap_for_ansi(file) # type: ignore + elif not color: + out = strip_ansi(out) + + file.write(out) # type: ignore + file.flush() + + +def get_binary_stream(name: "te.Literal['stdin', 'stdout', 'stderr']") -> t.BinaryIO: + """Returns a system stream for byte processing. + + :param name: the name of the stream to open. Valid names are ``'stdin'``, + ``'stdout'`` and ``'stderr'`` + """ + opener = binary_streams.get(name) + if opener is None: + raise TypeError(f"Unknown standard stream '{name}'") + return opener() + + +def get_text_stream( + name: "te.Literal['stdin', 'stdout', 'stderr']", + encoding: t.Optional[str] = None, + errors: t.Optional[str] = "strict", +) -> t.TextIO: + """Returns a system stream for text processing. This usually returns + a wrapped stream around a binary stream returned from + :func:`get_binary_stream` but it also can take shortcuts for already + correctly configured streams. + + :param name: the name of the stream to open. Valid names are ``'stdin'``, + ``'stdout'`` and ``'stderr'`` + :param encoding: overrides the detected default encoding. + :param errors: overrides the default error mode. + """ + opener = text_streams.get(name) + if opener is None: + raise TypeError(f"Unknown standard stream '{name}'") + return opener(encoding, errors) + + +def open_file( + filename: str, + mode: str = "r", + encoding: t.Optional[str] = None, + errors: t.Optional[str] = "strict", + lazy: bool = False, + atomic: bool = False, +) -> t.IO: + """This is similar to how the :class:`File` works but for manual + usage. Files are opened non lazy by default. This can open regular + files as well as stdin/stdout if ``'-'`` is passed. + + If stdin/stdout is returned the stream is wrapped so that the context + manager will not close the stream accidentally. This makes it possible + to always use the function like this without having to worry to + accidentally close a standard stream:: + + with open_file(filename) as f: + ... + + .. versionadded:: 3.0 + + :param filename: the name of the file to open (or ``'-'`` for stdin/stdout). + :param mode: the mode in which to open the file. + :param encoding: the encoding to use. + :param errors: the error handling for this file. + :param lazy: can be flipped to true to open the file lazily. + :param atomic: in atomic mode writes go into a temporary file and it's + moved on close. + """ + if lazy: + return t.cast(t.IO, LazyFile(filename, mode, encoding, errors, atomic=atomic)) + f, should_close = open_stream(filename, mode, encoding, errors, atomic=atomic) + if not should_close: + f = t.cast(t.IO, KeepOpenFile(f)) + return f + + +def get_os_args() -> t.Sequence[str]: + """Returns the argument part of ``sys.argv``, removing the first + value which is the name of the script. + + .. deprecated:: 8.0 + Will be removed in Click 8.1. Access ``sys.argv[1:]`` directly + instead. + """ + import warnings + + warnings.warn( + "'get_os_args' is deprecated and will be removed in Click 8.1." + " Access 'sys.argv[1:]' directly instead.", + DeprecationWarning, + stacklevel=2, + ) + return sys.argv[1:] + + +def format_filename( + filename: t.Union[str, bytes, os.PathLike], shorten: bool = False +) -> str: + """Formats a filename for user display. The main purpose of this + function is to ensure that the filename can be displayed at all. This + will decode the filename to unicode if necessary in a way that it will + not fail. Optionally, it can shorten the filename to not include the + full path to the filename. + + :param filename: formats a filename for UI display. This will also convert + the filename into unicode without failing. + :param shorten: this optionally shortens the filename to strip of the + path that leads up to it. + """ + if shorten: + filename = os.path.basename(filename) + + return os.fsdecode(filename) + + +def get_app_dir(app_name: str, roaming: bool = True, force_posix: bool = False) -> str: + r"""Returns the config folder for the application. The default behavior + is to return whatever is most appropriate for the operating system. + + To give you an idea, for an app called ``"Foo Bar"``, something like + the following folders could be returned: + + Mac OS X: + ``~/Library/Application Support/Foo Bar`` + Mac OS X (POSIX): + ``~/.foo-bar`` + Unix: + ``~/.config/foo-bar`` + Unix (POSIX): + ``~/.foo-bar`` + Windows (roaming): + ``C:\Users\\AppData\Roaming\Foo Bar`` + Windows (not roaming): + ``C:\Users\\AppData\Local\Foo Bar`` + + .. versionadded:: 2.0 + + :param app_name: the application name. This should be properly capitalized + and can contain whitespace. + :param roaming: controls if the folder should be roaming or not on Windows. + Has no affect otherwise. + :param force_posix: if this is set to `True` then on any POSIX system the + folder will be stored in the home folder with a leading + dot instead of the XDG config home or darwin's + application support folder. + """ + if WIN: + key = "APPDATA" if roaming else "LOCALAPPDATA" + folder = os.environ.get(key) + if folder is None: + folder = os.path.expanduser("~") + return os.path.join(folder, app_name) + if force_posix: + return os.path.join(os.path.expanduser(f"~/.{_posixify(app_name)}")) + if sys.platform == "darwin": + return os.path.join( + os.path.expanduser("~/Library/Application Support"), app_name + ) + return os.path.join( + os.environ.get("XDG_CONFIG_HOME", os.path.expanduser("~/.config")), + _posixify(app_name), + ) + + +class PacifyFlushWrapper: + """This wrapper is used to catch and suppress BrokenPipeErrors resulting + from ``.flush()`` being called on broken pipe during the shutdown/final-GC + of the Python interpreter. Notably ``.flush()`` is always called on + ``sys.stdout`` and ``sys.stderr``. So as to have minimal impact on any + other cleanup code, and the case where the underlying file is not a broken + pipe, all calls and attributes are proxied. + """ + + def __init__(self, wrapped: t.IO) -> None: + self.wrapped = wrapped + + def flush(self) -> None: + try: + self.wrapped.flush() + except OSError as e: + import errno + + if e.errno != errno.EPIPE: + raise + + def __getattr__(self, attr: str) -> t.Any: + return getattr(self.wrapped, attr) + + +def _detect_program_name( + path: t.Optional[str] = None, _main: ModuleType = sys.modules["__main__"] +) -> str: + """Determine the command used to run the program, for use in help + text. If a file or entry point was executed, the file name is + returned. If ``python -m`` was used to execute a module or package, + ``python -m name`` is returned. + + This doesn't try to be too precise, the goal is to give a concise + name for help text. Files are only shown as their name without the + path. ``python`` is only shown for modules, and the full path to + ``sys.executable`` is not shown. + + :param path: The Python file being executed. Python puts this in + ``sys.argv[0]``, which is used by default. + :param _main: The ``__main__`` module. This should only be passed + during internal testing. + + .. versionadded:: 8.0 + Based on command args detection in the Werkzeug reloader. + + :meta private: + """ + if not path: + path = sys.argv[0] + + # The value of __package__ indicates how Python was called. It may + # not exist if a setuptools script is installed as an egg. It may be + # set incorrectly for entry points created with pip on Windows. + if getattr(_main, "__package__", None) is None or ( + os.name == "nt" + and _main.__package__ == "" + and not os.path.exists(path) + and os.path.exists(f"{path}.exe") + ): + # Executed a file, like "python app.py". + return os.path.basename(path) + + # Executed a module, like "python -m example". + # Rewritten by Python from "-m script" to "/path/to/script.py". + # Need to look at main module to determine how it was executed. + py_module = t.cast(str, _main.__package__) + name = os.path.splitext(os.path.basename(path))[0] + + # A submodule like "example.cli". + if name != "__main__": + py_module = f"{py_module}.{name}" + + return f"python -m {py_module.lstrip('.')}" + + +def _expand_args( + args: t.Iterable[str], + *, + user: bool = True, + env: bool = True, + glob_recursive: bool = True, +) -> t.List[str]: + """Simulate Unix shell expansion with Python functions. + + See :func:`glob.glob`, :func:`os.path.expanduser`, and + :func:`os.path.expandvars`. + + This intended for use on Windows, where the shell does not do any + expansion. It may not exactly match what a Unix shell would do. + + :param args: List of command line arguments to expand. + :param user: Expand user home directory. + :param env: Expand environment variables. + :param glob_recursive: ``**`` matches directories recursively. + + .. versionadded:: 8.0 + + :meta private: + """ + from glob import glob + + out = [] + + for arg in args: + if user: + arg = os.path.expanduser(arg) + + if env: + arg = os.path.expandvars(arg) + + matches = glob(arg, recursive=glob_recursive) + + if not matches: + out.append(arg) + else: + out.extend(matches) + + return out diff --git a/venv/lib/python3.7/site-packages/colorama-0.4.4.dist-info/INSTALLER b/venv/lib/python3.7/site-packages/colorama-0.4.4.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.7/site-packages/colorama-0.4.4.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.7/site-packages/colorama-0.4.4.dist-info/LICENSE.txt b/venv/lib/python3.7/site-packages/colorama-0.4.4.dist-info/LICENSE.txt new file mode 100644 index 0000000..3105888 --- /dev/null +++ b/venv/lib/python3.7/site-packages/colorama-0.4.4.dist-info/LICENSE.txt @@ -0,0 +1,27 @@ +Copyright (c) 2010 Jonathan Hartley +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holders, nor those of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/lib/python3.7/site-packages/colorama-0.4.4.dist-info/METADATA b/venv/lib/python3.7/site-packages/colorama-0.4.4.dist-info/METADATA new file mode 100644 index 0000000..2a175c2 --- /dev/null +++ b/venv/lib/python3.7/site-packages/colorama-0.4.4.dist-info/METADATA @@ -0,0 +1,415 @@ +Metadata-Version: 2.1 +Name: colorama +Version: 0.4.4 +Summary: Cross-platform colored terminal text. +Home-page: https://github.com/tartley/colorama +Author: Jonathan Hartley +Author-email: tartley@tartley.com +Maintainer: Arnon Yaari +License: BSD +Keywords: color colour terminal text ansi windows crossplatform xplatform +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Console +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Terminals +Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.* + +.. image:: https://img.shields.io/pypi/v/colorama.svg + :target: https://pypi.org/project/colorama/ + :alt: Latest Version + +.. image:: https://img.shields.io/pypi/pyversions/colorama.svg + :target: https://pypi.org/project/colorama/ + :alt: Supported Python versions + +.. image:: https://travis-ci.org/tartley/colorama.svg?branch=master + :target: https://travis-ci.org/tartley/colorama + :alt: Build Status + +Colorama +======== + +Makes ANSI escape character sequences (for producing colored terminal text and +cursor positioning) work under MS Windows. + +.. |donate| image:: https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif + :target: https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=2MZ9D2GMLYCUJ&item_name=Colorama¤cy_code=USD + :alt: Donate with Paypal + +`PyPI for releases `_ · +`Github for source `_ · +`Colorama for enterprise on Tidelift `_ + +If you find Colorama useful, please |donate| to the authors. Thank you! + + +Installation +------------ + +.. code-block:: bash + + pip install colorama + # or + conda install -c anaconda colorama + + +Description +----------- + +ANSI escape character sequences have long been used to produce colored terminal +text and cursor positioning on Unix and Macs. Colorama makes this work on +Windows, too, by wrapping ``stdout``, stripping ANSI sequences it finds (which +would appear as gobbledygook in the output), and converting them into the +appropriate win32 calls to modify the state of the terminal. On other platforms, +Colorama does nothing. + +This has the upshot of providing a simple cross-platform API for printing +colored terminal text from Python, and has the happy side-effect that existing +applications or libraries which use ANSI sequences to produce colored output on +Linux or Macs can now also work on Windows, simply by calling +``colorama.init()``. + +An alternative approach is to install ``ansi.sys`` on Windows machines, which +provides the same behaviour for all applications running in terminals. Colorama +is intended for situations where that isn't easy (e.g., maybe your app doesn't +have an installer.) + +Demo scripts in the source code repository print some colored text using +ANSI sequences. Compare their output under Gnome-terminal's built in ANSI +handling, versus on Windows Command-Prompt using Colorama: + +.. image:: https://github.com/tartley/colorama/raw/master/screenshots/ubuntu-demo.png + :width: 661 + :height: 357 + :alt: ANSI sequences on Ubuntu under gnome-terminal. + +.. image:: https://github.com/tartley/colorama/raw/master/screenshots/windows-demo.png + :width: 668 + :height: 325 + :alt: Same ANSI sequences on Windows, using Colorama. + +These screenshots show that, on Windows, Colorama does not support ANSI 'dim +text'; it looks the same as 'normal text'. + +Usage +----- + +Initialisation +.............. + +Applications should initialise Colorama using: + +.. code-block:: python + + from colorama import init + init() + +On Windows, calling ``init()`` will filter ANSI escape sequences out of any +text sent to ``stdout`` or ``stderr``, and replace them with equivalent Win32 +calls. + +On other platforms, calling ``init()`` has no effect (unless you request other +optional functionality; see "Init Keyword Args", below). By design, this permits +applications to call ``init()`` unconditionally on all platforms, after which +ANSI output should just work. + +To stop using Colorama before your program exits, simply call ``deinit()``. +This will restore ``stdout`` and ``stderr`` to their original values, so that +Colorama is disabled. To resume using Colorama again, call ``reinit()``; it is +cheaper than calling ``init()`` again (but does the same thing). + + +Colored Output +.............. + +Cross-platform printing of colored text can then be done using Colorama's +constant shorthand for ANSI escape sequences: + +.. code-block:: python + + from colorama import Fore, Back, Style + print(Fore.RED + 'some red text') + print(Back.GREEN + 'and with a green background') + print(Style.DIM + 'and in dim text') + print(Style.RESET_ALL) + print('back to normal now') + +...or simply by manually printing ANSI sequences from your own code: + +.. code-block:: python + + print('\033[31m' + 'some red text') + print('\033[39m') # and reset to default color + +...or, Colorama can be used in conjunction with existing ANSI libraries +such as the venerable `Termcolor `_ +or the fabulous `Blessings `_. +This is highly recommended for anything more than trivial coloring: + +.. code-block:: python + + from colorama import init + from termcolor import colored + + # use Colorama to make Termcolor work on Windows too + init() + + # then use Termcolor for all colored text output + print(colored('Hello, World!', 'green', 'on_red')) + +Available formatting constants are:: + + Fore: BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, RESET. + Back: BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, RESET. + Style: DIM, NORMAL, BRIGHT, RESET_ALL + +``Style.RESET_ALL`` resets foreground, background, and brightness. Colorama will +perform this reset automatically on program exit. + + +Cursor Positioning +.................. + +ANSI codes to reposition the cursor are supported. See ``demos/demo06.py`` for +an example of how to generate them. + + +Init Keyword Args +................. + +``init()`` accepts some ``**kwargs`` to override default behaviour. + +init(autoreset=False): + If you find yourself repeatedly sending reset sequences to turn off color + changes at the end of every print, then ``init(autoreset=True)`` will + automate that: + + .. code-block:: python + + from colorama import init + init(autoreset=True) + print(Fore.RED + 'some red text') + print('automatically back to default color again') + +init(strip=None): + Pass ``True`` or ``False`` to override whether ANSI codes should be + stripped from the output. The default behaviour is to strip if on Windows + or if output is redirected (not a tty). + +init(convert=None): + Pass ``True`` or ``False`` to override whether to convert ANSI codes in the + output into win32 calls. The default behaviour is to convert if on Windows + and output is to a tty (terminal). + +init(wrap=True): + On Windows, Colorama works by replacing ``sys.stdout`` and ``sys.stderr`` + with proxy objects, which override the ``.write()`` method to do their work. + If this wrapping causes you problems, then this can be disabled by passing + ``init(wrap=False)``. The default behaviour is to wrap if ``autoreset`` or + ``strip`` or ``convert`` are True. + + When wrapping is disabled, colored printing on non-Windows platforms will + continue to work as normal. To do cross-platform colored output, you can + use Colorama's ``AnsiToWin32`` proxy directly: + + .. code-block:: python + + import sys + from colorama import init, AnsiToWin32 + init(wrap=False) + stream = AnsiToWin32(sys.stderr).stream + + # Python 2 + print >>stream, Fore.BLUE + 'blue text on stderr' + + # Python 3 + print(Fore.BLUE + 'blue text on stderr', file=stream) + + +Recognised ANSI Sequences +......................... + +ANSI sequences generally take the form:: + + ESC [ ; ... + +Where ```` is an integer, and ```` is a single letter. Zero or +more params are passed to a ````. If no params are passed, it is +generally synonymous with passing a single zero. No spaces exist in the +sequence; they have been inserted here simply to read more easily. + +The only ANSI sequences that Colorama converts into win32 calls are:: + + ESC [ 0 m # reset all (colors and brightness) + ESC [ 1 m # bright + ESC [ 2 m # dim (looks same as normal brightness) + ESC [ 22 m # normal brightness + + # FOREGROUND: + ESC [ 30 m # black + ESC [ 31 m # red + ESC [ 32 m # green + ESC [ 33 m # yellow + ESC [ 34 m # blue + ESC [ 35 m # magenta + ESC [ 36 m # cyan + ESC [ 37 m # white + ESC [ 39 m # reset + + # BACKGROUND + ESC [ 40 m # black + ESC [ 41 m # red + ESC [ 42 m # green + ESC [ 43 m # yellow + ESC [ 44 m # blue + ESC [ 45 m # magenta + ESC [ 46 m # cyan + ESC [ 47 m # white + ESC [ 49 m # reset + + # cursor positioning + ESC [ y;x H # position cursor at x across, y down + ESC [ y;x f # position cursor at x across, y down + ESC [ n A # move cursor n lines up + ESC [ n B # move cursor n lines down + ESC [ n C # move cursor n characters forward + ESC [ n D # move cursor n characters backward + + # clear the screen + ESC [ mode J # clear the screen + + # clear the line + ESC [ mode K # clear the line + +Multiple numeric params to the ``'m'`` command can be combined into a single +sequence:: + + ESC [ 36 ; 45 ; 1 m # bright cyan text on magenta background + +All other ANSI sequences of the form ``ESC [ ; ... `` +are silently stripped from the output on Windows. + +Any other form of ANSI sequence, such as single-character codes or alternative +initial characters, are not recognised or stripped. It would be cool to add +them though. Let me know if it would be useful for you, via the Issues on +GitHub. + + +Status & Known Problems +----------------------- + +I've personally only tested it on Windows XP (CMD, Console2), Ubuntu +(gnome-terminal, xterm), and OS X. + +Some presumably valid ANSI sequences aren't recognised (see details below), +but to my knowledge nobody has yet complained about this. Puzzling. + +See outstanding issues and wish-list: +https://github.com/tartley/colorama/issues + +If anything doesn't work for you, or doesn't do what you expected or hoped for, +I'd love to hear about it on that issues list, would be delighted by patches, +and would be happy to grant commit access to anyone who submits a working patch +or two. + + +License +------- + +Copyright Jonathan Hartley & Arnon Yaari, 2013-2020. BSD 3-Clause license; see +LICENSE file. + + +Development +----------- + +Help and fixes welcome! + +Tested on CPython 2.7, 3.5, 3.6, 3.7 and 3.8. + +No requirements other than the standard library. +Development requirements are captured in requirements-dev.txt. + +To create and populate a virtual environment:: + + ./bootstrap.ps1 # Windows + make bootstrap # Linux + +To run tests:: + + ./test.ps1 # Windows + make test # Linux + +If you use nose to run the tests, you must pass the ``-s`` flag; otherwise, +``nosetests`` applies its own proxy to ``stdout``, which confuses the unit +tests. + + +Professional support +-------------------- + +.. |tideliftlogo| image:: https://cdn2.hubspot.net/hubfs/4008838/website/logos/logos_for_download/Tidelift_primary-shorthand-logo.png + :alt: Tidelift + :target: https://tidelift.com/subscription/pkg/pypi-colorama?utm_source=pypi-colorama&utm_medium=referral&utm_campaign=readme + +.. list-table:: + :widths: 10 100 + + * - |tideliftlogo| + - Professional support for colorama is available as part of the + `Tidelift Subscription`_. + Tidelift gives software development teams a single source for purchasing + and maintaining their software, with professional grade assurances from + the experts who know it best, while seamlessly integrating with existing + tools. + +.. _Tidelift Subscription: https://tidelift.com/subscription/pkg/pypi-colorama?utm_source=pypi-colorama&utm_medium=referral&utm_campaign=readme + + +Thanks +------ + +* Marc Schlaich (schlamar) for a ``setup.py`` fix for Python2.5. +* Marc Abramowitz, reported & fixed a crash on exit with closed ``stdout``, + providing a solution to issue #7's setuptools/distutils debate, + and other fixes. +* User 'eryksun', for guidance on correctly instantiating ``ctypes.windll``. +* Matthew McCormick for politely pointing out a longstanding crash on non-Win. +* Ben Hoyt, for a magnificent fix under 64-bit Windows. +* Jesse at Empty Square for submitting a fix for examples in the README. +* User 'jamessp', an observant documentation fix for cursor positioning. +* User 'vaal1239', Dave Mckee & Lackner Kristof for a tiny but much-needed Win7 + fix. +* Julien Stuyck, for wisely suggesting Python3 compatible updates to README. +* Daniel Griffith for multiple fabulous patches. +* Oscar Lesta for a valuable fix to stop ANSI chars being sent to non-tty + output. +* Roger Binns, for many suggestions, valuable feedback, & bug reports. +* Tim Golden for thought and much appreciated feedback on the initial idea. +* User 'Zearin' for updates to the README file. +* John Szakmeister for adding support for light colors +* Charles Merriam for adding documentation to demos +* Jurko for a fix on 64-bit Windows CPython2.5 w/o ctypes +* Florian Bruhin for a fix when stdout or stderr are None +* Thomas Weininger for fixing ValueError on Windows +* Remi Rampin for better Github integration and fixes to the README file +* Simeon Visser for closing a file handle using 'with' and updating classifiers + to include Python 3.3 and 3.4 +* Andy Neff for fixing RESET of LIGHT_EX colors. +* Jonathan Hartley for the initial idea and implementation. + + + diff --git a/venv/lib/python3.7/site-packages/colorama-0.4.4.dist-info/RECORD b/venv/lib/python3.7/site-packages/colorama-0.4.4.dist-info/RECORD new file mode 100644 index 0000000..e806f4e --- /dev/null +++ b/venv/lib/python3.7/site-packages/colorama-0.4.4.dist-info/RECORD @@ -0,0 +1,18 @@ +colorama-0.4.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +colorama-0.4.4.dist-info/LICENSE.txt,sha256=ysNcAmhuXQSlpxQL-zs25zrtSWZW6JEQLkKIhteTAxg,1491 +colorama-0.4.4.dist-info/METADATA,sha256=JmU7ePpEh1xcqZV0JKcrrlU7cp5o4InDlHJXbo_FTQw,14551 +colorama-0.4.4.dist-info/RECORD,, +colorama-0.4.4.dist-info/WHEEL,sha256=gxPaqcqKPLUXaSAKwmfHO7_iAOlVvmp33DewnUluBB8,116 +colorama-0.4.4.dist-info/top_level.txt,sha256=_Kx6-Cni2BT1PEATPhrSRxo0d7kSgfBbHf5o7IF1ABw,9 +colorama/__init__.py,sha256=pCdErryzLSzDW5P-rRPBlPLqbBtIRNJB6cMgoeJns5k,239 +colorama/__pycache__/__init__.cpython-37.pyc,, +colorama/__pycache__/ansi.cpython-37.pyc,, +colorama/__pycache__/ansitowin32.cpython-37.pyc,, +colorama/__pycache__/initialise.cpython-37.pyc,, +colorama/__pycache__/win32.cpython-37.pyc,, +colorama/__pycache__/winterm.cpython-37.pyc,, +colorama/ansi.py,sha256=Top4EeEuaQdBWdteKMEcGOTeKeF19Q-Wo_6_Cj5kOzQ,2522 +colorama/ansitowin32.py,sha256=yV7CEmCb19MjnJKODZEEvMH_fnbJhwnpzo4sxZuGXmA,10517 +colorama/initialise.py,sha256=PprovDNxMTrvoNHFcL2NZjpH2XzDc8BLxLxiErfUl4k,1915 +colorama/win32.py,sha256=bJ8Il9jwaBN5BJ8bmN6FoYZ1QYuMKv2j8fGrXh7TJjw,5404 +colorama/winterm.py,sha256=2y_2b7Zsv34feAsP67mLOVc-Bgq51mdYGo571VprlrM,6438 diff --git a/venv/lib/python3.7/site-packages/colorama-0.4.4.dist-info/WHEEL b/venv/lib/python3.7/site-packages/colorama-0.4.4.dist-info/WHEEL new file mode 100644 index 0000000..ecd4e92 --- /dev/null +++ b/venv/lib/python3.7/site-packages/colorama-0.4.4.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.35.1) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/venv/lib/python3.7/site-packages/colorama-0.4.4.dist-info/top_level.txt b/venv/lib/python3.7/site-packages/colorama-0.4.4.dist-info/top_level.txt new file mode 100644 index 0000000..3fcfb51 --- /dev/null +++ b/venv/lib/python3.7/site-packages/colorama-0.4.4.dist-info/top_level.txt @@ -0,0 +1 @@ +colorama diff --git a/venv/lib/python3.7/site-packages/colorama/__init__.py b/venv/lib/python3.7/site-packages/colorama/__init__.py new file mode 100644 index 0000000..b149ed7 --- /dev/null +++ b/venv/lib/python3.7/site-packages/colorama/__init__.py @@ -0,0 +1,6 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +from .initialise import init, deinit, reinit, colorama_text +from .ansi import Fore, Back, Style, Cursor +from .ansitowin32 import AnsiToWin32 + +__version__ = '0.4.4' diff --git a/venv/lib/python3.7/site-packages/colorama/__pycache__/__init__.cpython-37.pyc b/venv/lib/python3.7/site-packages/colorama/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000..4739543 Binary files /dev/null and b/venv/lib/python3.7/site-packages/colorama/__pycache__/__init__.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/colorama/__pycache__/ansi.cpython-37.pyc b/venv/lib/python3.7/site-packages/colorama/__pycache__/ansi.cpython-37.pyc new file mode 100644 index 0000000..3ca90cb Binary files /dev/null and b/venv/lib/python3.7/site-packages/colorama/__pycache__/ansi.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/colorama/__pycache__/ansitowin32.cpython-37.pyc b/venv/lib/python3.7/site-packages/colorama/__pycache__/ansitowin32.cpython-37.pyc new file mode 100644 index 0000000..37c9d39 Binary files /dev/null and b/venv/lib/python3.7/site-packages/colorama/__pycache__/ansitowin32.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/colorama/__pycache__/initialise.cpython-37.pyc b/venv/lib/python3.7/site-packages/colorama/__pycache__/initialise.cpython-37.pyc new file mode 100644 index 0000000..8239ee8 Binary files /dev/null and b/venv/lib/python3.7/site-packages/colorama/__pycache__/initialise.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/colorama/__pycache__/win32.cpython-37.pyc b/venv/lib/python3.7/site-packages/colorama/__pycache__/win32.cpython-37.pyc new file mode 100644 index 0000000..11f0c37 Binary files /dev/null and b/venv/lib/python3.7/site-packages/colorama/__pycache__/win32.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/colorama/__pycache__/winterm.cpython-37.pyc b/venv/lib/python3.7/site-packages/colorama/__pycache__/winterm.cpython-37.pyc new file mode 100644 index 0000000..07137b1 Binary files /dev/null and b/venv/lib/python3.7/site-packages/colorama/__pycache__/winterm.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/colorama/ansi.py b/venv/lib/python3.7/site-packages/colorama/ansi.py new file mode 100644 index 0000000..11ec695 --- /dev/null +++ b/venv/lib/python3.7/site-packages/colorama/ansi.py @@ -0,0 +1,102 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +''' +This module generates ANSI character codes to printing colors to terminals. +See: http://en.wikipedia.org/wiki/ANSI_escape_code +''' + +CSI = '\033[' +OSC = '\033]' +BEL = '\a' + + +def code_to_chars(code): + return CSI + str(code) + 'm' + +def set_title(title): + return OSC + '2;' + title + BEL + +def clear_screen(mode=2): + return CSI + str(mode) + 'J' + +def clear_line(mode=2): + return CSI + str(mode) + 'K' + + +class AnsiCodes(object): + def __init__(self): + # the subclasses declare class attributes which are numbers. + # Upon instantiation we define instance attributes, which are the same + # as the class attributes but wrapped with the ANSI escape sequence + for name in dir(self): + if not name.startswith('_'): + value = getattr(self, name) + setattr(self, name, code_to_chars(value)) + + +class AnsiCursor(object): + def UP(self, n=1): + return CSI + str(n) + 'A' + def DOWN(self, n=1): + return CSI + str(n) + 'B' + def FORWARD(self, n=1): + return CSI + str(n) + 'C' + def BACK(self, n=1): + return CSI + str(n) + 'D' + def POS(self, x=1, y=1): + return CSI + str(y) + ';' + str(x) + 'H' + + +class AnsiFore(AnsiCodes): + BLACK = 30 + RED = 31 + GREEN = 32 + YELLOW = 33 + BLUE = 34 + MAGENTA = 35 + CYAN = 36 + WHITE = 37 + RESET = 39 + + # These are fairly well supported, but not part of the standard. + LIGHTBLACK_EX = 90 + LIGHTRED_EX = 91 + LIGHTGREEN_EX = 92 + LIGHTYELLOW_EX = 93 + LIGHTBLUE_EX = 94 + LIGHTMAGENTA_EX = 95 + LIGHTCYAN_EX = 96 + LIGHTWHITE_EX = 97 + + +class AnsiBack(AnsiCodes): + BLACK = 40 + RED = 41 + GREEN = 42 + YELLOW = 43 + BLUE = 44 + MAGENTA = 45 + CYAN = 46 + WHITE = 47 + RESET = 49 + + # These are fairly well supported, but not part of the standard. + LIGHTBLACK_EX = 100 + LIGHTRED_EX = 101 + LIGHTGREEN_EX = 102 + LIGHTYELLOW_EX = 103 + LIGHTBLUE_EX = 104 + LIGHTMAGENTA_EX = 105 + LIGHTCYAN_EX = 106 + LIGHTWHITE_EX = 107 + + +class AnsiStyle(AnsiCodes): + BRIGHT = 1 + DIM = 2 + NORMAL = 22 + RESET_ALL = 0 + +Fore = AnsiFore() +Back = AnsiBack() +Style = AnsiStyle() +Cursor = AnsiCursor() diff --git a/venv/lib/python3.7/site-packages/colorama/ansitowin32.py b/venv/lib/python3.7/site-packages/colorama/ansitowin32.py new file mode 100644 index 0000000..6039a05 --- /dev/null +++ b/venv/lib/python3.7/site-packages/colorama/ansitowin32.py @@ -0,0 +1,258 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import re +import sys +import os + +from .ansi import AnsiFore, AnsiBack, AnsiStyle, Style, BEL +from .winterm import WinTerm, WinColor, WinStyle +from .win32 import windll, winapi_test + + +winterm = None +if windll is not None: + winterm = WinTerm() + + +class StreamWrapper(object): + ''' + Wraps a stream (such as stdout), acting as a transparent proxy for all + attribute access apart from method 'write()', which is delegated to our + Converter instance. + ''' + def __init__(self, wrapped, converter): + # double-underscore everything to prevent clashes with names of + # attributes on the wrapped stream object. + self.__wrapped = wrapped + self.__convertor = converter + + def __getattr__(self, name): + return getattr(self.__wrapped, name) + + def __enter__(self, *args, **kwargs): + # special method lookup bypasses __getattr__/__getattribute__, see + # https://stackoverflow.com/questions/12632894/why-doesnt-getattr-work-with-exit + # thus, contextlib magic methods are not proxied via __getattr__ + return self.__wrapped.__enter__(*args, **kwargs) + + def __exit__(self, *args, **kwargs): + return self.__wrapped.__exit__(*args, **kwargs) + + def write(self, text): + self.__convertor.write(text) + + def isatty(self): + stream = self.__wrapped + if 'PYCHARM_HOSTED' in os.environ: + if stream is not None and (stream is sys.__stdout__ or stream is sys.__stderr__): + return True + try: + stream_isatty = stream.isatty + except AttributeError: + return False + else: + return stream_isatty() + + @property + def closed(self): + stream = self.__wrapped + try: + return stream.closed + except AttributeError: + return True + + +class AnsiToWin32(object): + ''' + Implements a 'write()' method which, on Windows, will strip ANSI character + sequences from the text, and if outputting to a tty, will convert them into + win32 function calls. + ''' + ANSI_CSI_RE = re.compile('\001?\033\\[((?:\\d|;)*)([a-zA-Z])\002?') # Control Sequence Introducer + ANSI_OSC_RE = re.compile('\001?\033\\]([^\a]*)(\a)\002?') # Operating System Command + + def __init__(self, wrapped, convert=None, strip=None, autoreset=False): + # The wrapped stream (normally sys.stdout or sys.stderr) + self.wrapped = wrapped + + # should we reset colors to defaults after every .write() + self.autoreset = autoreset + + # create the proxy wrapping our output stream + self.stream = StreamWrapper(wrapped, self) + + on_windows = os.name == 'nt' + # We test if the WinAPI works, because even if we are on Windows + # we may be using a terminal that doesn't support the WinAPI + # (e.g. Cygwin Terminal). In this case it's up to the terminal + # to support the ANSI codes. + conversion_supported = on_windows and winapi_test() + + # should we strip ANSI sequences from our output? + if strip is None: + strip = conversion_supported or (not self.stream.closed and not self.stream.isatty()) + self.strip = strip + + # should we should convert ANSI sequences into win32 calls? + if convert is None: + convert = conversion_supported and not self.stream.closed and self.stream.isatty() + self.convert = convert + + # dict of ansi codes to win32 functions and parameters + self.win32_calls = self.get_win32_calls() + + # are we wrapping stderr? + self.on_stderr = self.wrapped is sys.stderr + + def should_wrap(self): + ''' + True if this class is actually needed. If false, then the output + stream will not be affected, nor will win32 calls be issued, so + wrapping stdout is not actually required. This will generally be + False on non-Windows platforms, unless optional functionality like + autoreset has been requested using kwargs to init() + ''' + return self.convert or self.strip or self.autoreset + + def get_win32_calls(self): + if self.convert and winterm: + return { + AnsiStyle.RESET_ALL: (winterm.reset_all, ), + AnsiStyle.BRIGHT: (winterm.style, WinStyle.BRIGHT), + AnsiStyle.DIM: (winterm.style, WinStyle.NORMAL), + AnsiStyle.NORMAL: (winterm.style, WinStyle.NORMAL), + AnsiFore.BLACK: (winterm.fore, WinColor.BLACK), + AnsiFore.RED: (winterm.fore, WinColor.RED), + AnsiFore.GREEN: (winterm.fore, WinColor.GREEN), + AnsiFore.YELLOW: (winterm.fore, WinColor.YELLOW), + AnsiFore.BLUE: (winterm.fore, WinColor.BLUE), + AnsiFore.MAGENTA: (winterm.fore, WinColor.MAGENTA), + AnsiFore.CYAN: (winterm.fore, WinColor.CYAN), + AnsiFore.WHITE: (winterm.fore, WinColor.GREY), + AnsiFore.RESET: (winterm.fore, ), + AnsiFore.LIGHTBLACK_EX: (winterm.fore, WinColor.BLACK, True), + AnsiFore.LIGHTRED_EX: (winterm.fore, WinColor.RED, True), + AnsiFore.LIGHTGREEN_EX: (winterm.fore, WinColor.GREEN, True), + AnsiFore.LIGHTYELLOW_EX: (winterm.fore, WinColor.YELLOW, True), + AnsiFore.LIGHTBLUE_EX: (winterm.fore, WinColor.BLUE, True), + AnsiFore.LIGHTMAGENTA_EX: (winterm.fore, WinColor.MAGENTA, True), + AnsiFore.LIGHTCYAN_EX: (winterm.fore, WinColor.CYAN, True), + AnsiFore.LIGHTWHITE_EX: (winterm.fore, WinColor.GREY, True), + AnsiBack.BLACK: (winterm.back, WinColor.BLACK), + AnsiBack.RED: (winterm.back, WinColor.RED), + AnsiBack.GREEN: (winterm.back, WinColor.GREEN), + AnsiBack.YELLOW: (winterm.back, WinColor.YELLOW), + AnsiBack.BLUE: (winterm.back, WinColor.BLUE), + AnsiBack.MAGENTA: (winterm.back, WinColor.MAGENTA), + AnsiBack.CYAN: (winterm.back, WinColor.CYAN), + AnsiBack.WHITE: (winterm.back, WinColor.GREY), + AnsiBack.RESET: (winterm.back, ), + AnsiBack.LIGHTBLACK_EX: (winterm.back, WinColor.BLACK, True), + AnsiBack.LIGHTRED_EX: (winterm.back, WinColor.RED, True), + AnsiBack.LIGHTGREEN_EX: (winterm.back, WinColor.GREEN, True), + AnsiBack.LIGHTYELLOW_EX: (winterm.back, WinColor.YELLOW, True), + AnsiBack.LIGHTBLUE_EX: (winterm.back, WinColor.BLUE, True), + AnsiBack.LIGHTMAGENTA_EX: (winterm.back, WinColor.MAGENTA, True), + AnsiBack.LIGHTCYAN_EX: (winterm.back, WinColor.CYAN, True), + AnsiBack.LIGHTWHITE_EX: (winterm.back, WinColor.GREY, True), + } + return dict() + + def write(self, text): + if self.strip or self.convert: + self.write_and_convert(text) + else: + self.wrapped.write(text) + self.wrapped.flush() + if self.autoreset: + self.reset_all() + + + def reset_all(self): + if self.convert: + self.call_win32('m', (0,)) + elif not self.strip and not self.stream.closed: + self.wrapped.write(Style.RESET_ALL) + + + def write_and_convert(self, text): + ''' + Write the given text to our wrapped stream, stripping any ANSI + sequences from the text, and optionally converting them into win32 + calls. + ''' + cursor = 0 + text = self.convert_osc(text) + for match in self.ANSI_CSI_RE.finditer(text): + start, end = match.span() + self.write_plain_text(text, cursor, start) + self.convert_ansi(*match.groups()) + cursor = end + self.write_plain_text(text, cursor, len(text)) + + + def write_plain_text(self, text, start, end): + if start < end: + self.wrapped.write(text[start:end]) + self.wrapped.flush() + + + def convert_ansi(self, paramstring, command): + if self.convert: + params = self.extract_params(command, paramstring) + self.call_win32(command, params) + + + def extract_params(self, command, paramstring): + if command in 'Hf': + params = tuple(int(p) if len(p) != 0 else 1 for p in paramstring.split(';')) + while len(params) < 2: + # defaults: + params = params + (1,) + else: + params = tuple(int(p) for p in paramstring.split(';') if len(p) != 0) + if len(params) == 0: + # defaults: + if command in 'JKm': + params = (0,) + elif command in 'ABCD': + params = (1,) + + return params + + + def call_win32(self, command, params): + if command == 'm': + for param in params: + if param in self.win32_calls: + func_args = self.win32_calls[param] + func = func_args[0] + args = func_args[1:] + kwargs = dict(on_stderr=self.on_stderr) + func(*args, **kwargs) + elif command in 'J': + winterm.erase_screen(params[0], on_stderr=self.on_stderr) + elif command in 'K': + winterm.erase_line(params[0], on_stderr=self.on_stderr) + elif command in 'Hf': # cursor position - absolute + winterm.set_cursor_position(params, on_stderr=self.on_stderr) + elif command in 'ABCD': # cursor position - relative + n = params[0] + # A - up, B - down, C - forward, D - back + x, y = {'A': (0, -n), 'B': (0, n), 'C': (n, 0), 'D': (-n, 0)}[command] + winterm.cursor_adjust(x, y, on_stderr=self.on_stderr) + + + def convert_osc(self, text): + for match in self.ANSI_OSC_RE.finditer(text): + start, end = match.span() + text = text[:start] + text[end:] + paramstring, command = match.groups() + if command == BEL: + if paramstring.count(";") == 1: + params = paramstring.split(";") + # 0 - change title and icon (we will only change title) + # 1 - change icon (we don't support this) + # 2 - change title + if params[0] in '02': + winterm.set_title(params[1]) + return text diff --git a/venv/lib/python3.7/site-packages/colorama/initialise.py b/venv/lib/python3.7/site-packages/colorama/initialise.py new file mode 100644 index 0000000..430d066 --- /dev/null +++ b/venv/lib/python3.7/site-packages/colorama/initialise.py @@ -0,0 +1,80 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import atexit +import contextlib +import sys + +from .ansitowin32 import AnsiToWin32 + + +orig_stdout = None +orig_stderr = None + +wrapped_stdout = None +wrapped_stderr = None + +atexit_done = False + + +def reset_all(): + if AnsiToWin32 is not None: # Issue #74: objects might become None at exit + AnsiToWin32(orig_stdout).reset_all() + + +def init(autoreset=False, convert=None, strip=None, wrap=True): + + if not wrap and any([autoreset, convert, strip]): + raise ValueError('wrap=False conflicts with any other arg=True') + + global wrapped_stdout, wrapped_stderr + global orig_stdout, orig_stderr + + orig_stdout = sys.stdout + orig_stderr = sys.stderr + + if sys.stdout is None: + wrapped_stdout = None + else: + sys.stdout = wrapped_stdout = \ + wrap_stream(orig_stdout, convert, strip, autoreset, wrap) + if sys.stderr is None: + wrapped_stderr = None + else: + sys.stderr = wrapped_stderr = \ + wrap_stream(orig_stderr, convert, strip, autoreset, wrap) + + global atexit_done + if not atexit_done: + atexit.register(reset_all) + atexit_done = True + + +def deinit(): + if orig_stdout is not None: + sys.stdout = orig_stdout + if orig_stderr is not None: + sys.stderr = orig_stderr + + +@contextlib.contextmanager +def colorama_text(*args, **kwargs): + init(*args, **kwargs) + try: + yield + finally: + deinit() + + +def reinit(): + if wrapped_stdout is not None: + sys.stdout = wrapped_stdout + if wrapped_stderr is not None: + sys.stderr = wrapped_stderr + + +def wrap_stream(stream, convert, strip, autoreset, wrap): + if wrap: + wrapper = AnsiToWin32(stream, + convert=convert, strip=strip, autoreset=autoreset) + if wrapper.should_wrap(): + stream = wrapper.stream + return stream diff --git a/venv/lib/python3.7/site-packages/colorama/win32.py b/venv/lib/python3.7/site-packages/colorama/win32.py new file mode 100644 index 0000000..c2d8360 --- /dev/null +++ b/venv/lib/python3.7/site-packages/colorama/win32.py @@ -0,0 +1,152 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. + +# from winbase.h +STDOUT = -11 +STDERR = -12 + +try: + import ctypes + from ctypes import LibraryLoader + windll = LibraryLoader(ctypes.WinDLL) + from ctypes import wintypes +except (AttributeError, ImportError): + windll = None + SetConsoleTextAttribute = lambda *_: None + winapi_test = lambda *_: None +else: + from ctypes import byref, Structure, c_char, POINTER + + COORD = wintypes._COORD + + class CONSOLE_SCREEN_BUFFER_INFO(Structure): + """struct in wincon.h.""" + _fields_ = [ + ("dwSize", COORD), + ("dwCursorPosition", COORD), + ("wAttributes", wintypes.WORD), + ("srWindow", wintypes.SMALL_RECT), + ("dwMaximumWindowSize", COORD), + ] + def __str__(self): + return '(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)' % ( + self.dwSize.Y, self.dwSize.X + , self.dwCursorPosition.Y, self.dwCursorPosition.X + , self.wAttributes + , self.srWindow.Top, self.srWindow.Left, self.srWindow.Bottom, self.srWindow.Right + , self.dwMaximumWindowSize.Y, self.dwMaximumWindowSize.X + ) + + _GetStdHandle = windll.kernel32.GetStdHandle + _GetStdHandle.argtypes = [ + wintypes.DWORD, + ] + _GetStdHandle.restype = wintypes.HANDLE + + _GetConsoleScreenBufferInfo = windll.kernel32.GetConsoleScreenBufferInfo + _GetConsoleScreenBufferInfo.argtypes = [ + wintypes.HANDLE, + POINTER(CONSOLE_SCREEN_BUFFER_INFO), + ] + _GetConsoleScreenBufferInfo.restype = wintypes.BOOL + + _SetConsoleTextAttribute = windll.kernel32.SetConsoleTextAttribute + _SetConsoleTextAttribute.argtypes = [ + wintypes.HANDLE, + wintypes.WORD, + ] + _SetConsoleTextAttribute.restype = wintypes.BOOL + + _SetConsoleCursorPosition = windll.kernel32.SetConsoleCursorPosition + _SetConsoleCursorPosition.argtypes = [ + wintypes.HANDLE, + COORD, + ] + _SetConsoleCursorPosition.restype = wintypes.BOOL + + _FillConsoleOutputCharacterA = windll.kernel32.FillConsoleOutputCharacterA + _FillConsoleOutputCharacterA.argtypes = [ + wintypes.HANDLE, + c_char, + wintypes.DWORD, + COORD, + POINTER(wintypes.DWORD), + ] + _FillConsoleOutputCharacterA.restype = wintypes.BOOL + + _FillConsoleOutputAttribute = windll.kernel32.FillConsoleOutputAttribute + _FillConsoleOutputAttribute.argtypes = [ + wintypes.HANDLE, + wintypes.WORD, + wintypes.DWORD, + COORD, + POINTER(wintypes.DWORD), + ] + _FillConsoleOutputAttribute.restype = wintypes.BOOL + + _SetConsoleTitleW = windll.kernel32.SetConsoleTitleW + _SetConsoleTitleW.argtypes = [ + wintypes.LPCWSTR + ] + _SetConsoleTitleW.restype = wintypes.BOOL + + def _winapi_test(handle): + csbi = CONSOLE_SCREEN_BUFFER_INFO() + success = _GetConsoleScreenBufferInfo( + handle, byref(csbi)) + return bool(success) + + def winapi_test(): + return any(_winapi_test(h) for h in + (_GetStdHandle(STDOUT), _GetStdHandle(STDERR))) + + def GetConsoleScreenBufferInfo(stream_id=STDOUT): + handle = _GetStdHandle(stream_id) + csbi = CONSOLE_SCREEN_BUFFER_INFO() + success = _GetConsoleScreenBufferInfo( + handle, byref(csbi)) + return csbi + + def SetConsoleTextAttribute(stream_id, attrs): + handle = _GetStdHandle(stream_id) + return _SetConsoleTextAttribute(handle, attrs) + + def SetConsoleCursorPosition(stream_id, position, adjust=True): + position = COORD(*position) + # If the position is out of range, do nothing. + if position.Y <= 0 or position.X <= 0: + return + # Adjust for Windows' SetConsoleCursorPosition: + # 1. being 0-based, while ANSI is 1-based. + # 2. expecting (x,y), while ANSI uses (y,x). + adjusted_position = COORD(position.Y - 1, position.X - 1) + if adjust: + # Adjust for viewport's scroll position + sr = GetConsoleScreenBufferInfo(STDOUT).srWindow + adjusted_position.Y += sr.Top + adjusted_position.X += sr.Left + # Resume normal processing + handle = _GetStdHandle(stream_id) + return _SetConsoleCursorPosition(handle, adjusted_position) + + def FillConsoleOutputCharacter(stream_id, char, length, start): + handle = _GetStdHandle(stream_id) + char = c_char(char.encode()) + length = wintypes.DWORD(length) + num_written = wintypes.DWORD(0) + # Note that this is hard-coded for ANSI (vs wide) bytes. + success = _FillConsoleOutputCharacterA( + handle, char, length, start, byref(num_written)) + return num_written.value + + def FillConsoleOutputAttribute(stream_id, attr, length, start): + ''' FillConsoleOutputAttribute( hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten )''' + handle = _GetStdHandle(stream_id) + attribute = wintypes.WORD(attr) + length = wintypes.DWORD(length) + num_written = wintypes.DWORD(0) + # Note that this is hard-coded for ANSI (vs wide) bytes. + return _FillConsoleOutputAttribute( + handle, attribute, length, start, byref(num_written)) + + def SetConsoleTitle(title): + return _SetConsoleTitleW(title) diff --git a/venv/lib/python3.7/site-packages/colorama/winterm.py b/venv/lib/python3.7/site-packages/colorama/winterm.py new file mode 100644 index 0000000..0fdb4ec --- /dev/null +++ b/venv/lib/python3.7/site-packages/colorama/winterm.py @@ -0,0 +1,169 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +from . import win32 + + +# from wincon.h +class WinColor(object): + BLACK = 0 + BLUE = 1 + GREEN = 2 + CYAN = 3 + RED = 4 + MAGENTA = 5 + YELLOW = 6 + GREY = 7 + +# from wincon.h +class WinStyle(object): + NORMAL = 0x00 # dim text, dim background + BRIGHT = 0x08 # bright text, dim background + BRIGHT_BACKGROUND = 0x80 # dim text, bright background + +class WinTerm(object): + + def __init__(self): + self._default = win32.GetConsoleScreenBufferInfo(win32.STDOUT).wAttributes + self.set_attrs(self._default) + self._default_fore = self._fore + self._default_back = self._back + self._default_style = self._style + # In order to emulate LIGHT_EX in windows, we borrow the BRIGHT style. + # So that LIGHT_EX colors and BRIGHT style do not clobber each other, + # we track them separately, since LIGHT_EX is overwritten by Fore/Back + # and BRIGHT is overwritten by Style codes. + self._light = 0 + + def get_attrs(self): + return self._fore + self._back * 16 + (self._style | self._light) + + def set_attrs(self, value): + self._fore = value & 7 + self._back = (value >> 4) & 7 + self._style = value & (WinStyle.BRIGHT | WinStyle.BRIGHT_BACKGROUND) + + def reset_all(self, on_stderr=None): + self.set_attrs(self._default) + self.set_console(attrs=self._default) + self._light = 0 + + def fore(self, fore=None, light=False, on_stderr=False): + if fore is None: + fore = self._default_fore + self._fore = fore + # Emulate LIGHT_EX with BRIGHT Style + if light: + self._light |= WinStyle.BRIGHT + else: + self._light &= ~WinStyle.BRIGHT + self.set_console(on_stderr=on_stderr) + + def back(self, back=None, light=False, on_stderr=False): + if back is None: + back = self._default_back + self._back = back + # Emulate LIGHT_EX with BRIGHT_BACKGROUND Style + if light: + self._light |= WinStyle.BRIGHT_BACKGROUND + else: + self._light &= ~WinStyle.BRIGHT_BACKGROUND + self.set_console(on_stderr=on_stderr) + + def style(self, style=None, on_stderr=False): + if style is None: + style = self._default_style + self._style = style + self.set_console(on_stderr=on_stderr) + + def set_console(self, attrs=None, on_stderr=False): + if attrs is None: + attrs = self.get_attrs() + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + win32.SetConsoleTextAttribute(handle, attrs) + + def get_position(self, handle): + position = win32.GetConsoleScreenBufferInfo(handle).dwCursorPosition + # Because Windows coordinates are 0-based, + # and win32.SetConsoleCursorPosition expects 1-based. + position.X += 1 + position.Y += 1 + return position + + def set_cursor_position(self, position=None, on_stderr=False): + if position is None: + # I'm not currently tracking the position, so there is no default. + # position = self.get_position() + return + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + win32.SetConsoleCursorPosition(handle, position) + + def cursor_adjust(self, x, y, on_stderr=False): + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + position = self.get_position(handle) + adjusted_position = (position.Y + y, position.X + x) + win32.SetConsoleCursorPosition(handle, adjusted_position, adjust=False) + + def erase_screen(self, mode=0, on_stderr=False): + # 0 should clear from the cursor to the end of the screen. + # 1 should clear from the cursor to the beginning of the screen. + # 2 should clear the entire screen, and move cursor to (1,1) + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + csbi = win32.GetConsoleScreenBufferInfo(handle) + # get the number of character cells in the current buffer + cells_in_screen = csbi.dwSize.X * csbi.dwSize.Y + # get number of character cells before current cursor position + cells_before_cursor = csbi.dwSize.X * csbi.dwCursorPosition.Y + csbi.dwCursorPosition.X + if mode == 0: + from_coord = csbi.dwCursorPosition + cells_to_erase = cells_in_screen - cells_before_cursor + elif mode == 1: + from_coord = win32.COORD(0, 0) + cells_to_erase = cells_before_cursor + elif mode == 2: + from_coord = win32.COORD(0, 0) + cells_to_erase = cells_in_screen + else: + # invalid mode + return + # fill the entire screen with blanks + win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord) + # now set the buffer's attributes accordingly + win32.FillConsoleOutputAttribute(handle, self.get_attrs(), cells_to_erase, from_coord) + if mode == 2: + # put the cursor where needed + win32.SetConsoleCursorPosition(handle, (1, 1)) + + def erase_line(self, mode=0, on_stderr=False): + # 0 should clear from the cursor to the end of the line. + # 1 should clear from the cursor to the beginning of the line. + # 2 should clear the entire line. + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + csbi = win32.GetConsoleScreenBufferInfo(handle) + if mode == 0: + from_coord = csbi.dwCursorPosition + cells_to_erase = csbi.dwSize.X - csbi.dwCursorPosition.X + elif mode == 1: + from_coord = win32.COORD(0, csbi.dwCursorPosition.Y) + cells_to_erase = csbi.dwCursorPosition.X + elif mode == 2: + from_coord = win32.COORD(0, csbi.dwCursorPosition.Y) + cells_to_erase = csbi.dwSize.X + else: + # invalid mode + return + # fill the entire screen with blanks + win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord) + # now set the buffer's attributes accordingly + win32.FillConsoleOutputAttribute(handle, self.get_attrs(), cells_to_erase, from_coord) + + def set_title(self, title): + win32.SetConsoleTitle(title) diff --git a/venv/lib/python3.7/site-packages/colorlog-6.5.0.dist-info/INSTALLER b/venv/lib/python3.7/site-packages/colorlog-6.5.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.7/site-packages/colorlog-6.5.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.7/site-packages/colorlog-6.5.0.dist-info/LICENSE b/venv/lib/python3.7/site-packages/colorlog-6.5.0.dist-info/LICENSE new file mode 100644 index 0000000..ef3747b --- /dev/null +++ b/venv/lib/python3.7/site-packages/colorlog-6.5.0.dist-info/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2012-2021 Sam Clements + +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. diff --git a/venv/lib/python3.7/site-packages/colorlog-6.5.0.dist-info/METADATA b/venv/lib/python3.7/site-packages/colorlog-6.5.0.dist-info/METADATA new file mode 100644 index 0000000..f8085e6 --- /dev/null +++ b/venv/lib/python3.7/site-packages/colorlog-6.5.0.dist-info/METADATA @@ -0,0 +1,300 @@ +Metadata-Version: 2.1 +Name: colorlog +Version: 6.5.0 +Summary: Add colours to the output of Python's logging module. +Home-page: https://github.com/borntyping/python-colorlog +Author: Sam Clements +Author-email: sam@borntyping.co.uk +License: MIT License +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Console +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Topic :: Terminals +Classifier: Topic :: Utilities +Requires-Python: >=3.6 +Description-Content-Type: text/markdown +Requires-Dist: colorama ; sys_platform=="win32" +Provides-Extra: development +Requires-Dist: black ; extra == 'development' +Requires-Dist: flake8 ; extra == 'development' +Requires-Dist: mypy ; extra == 'development' +Requires-Dist: pytest ; extra == 'development' +Requires-Dist: types-colorama ; extra == 'development' + +Log formatting with colors! +=========================== + +[![](https://img.shields.io/pypi/v/colorlog.svg)](https://pypi.org/project/colorlog/) +[![](https://img.shields.io/pypi/l/colorlog.svg)](https://pypi.org/project/colorlog/) +[![](https://img.shields.io/travis/borntyping/python-colorlog/master.svg)](https://travis-ci.org/borntyping/python-colorlog) + +Add colours to the output of Python's `logging` module. + +* [Source on GitHub](https://github.com/borntyping/python-colorlog) +* [Packages on PyPI](https://pypi.org/pypi/colorlog/) + +Status +------ + +colorlog currently requires Python 3.6 or higher. Older versions (below 5.x.x) +support Python 2.6 and above. + +* colorlog 6.x requires Python 3.6 or higher. +* colorlog 5.x is an interim version that will warn Python 2 users to downgrade. +* colorlog 4.x is the final version supporting Python 2. + +[colorama] is included as a required dependency and initialised when using +colorlog on Windows. + +This library is almost a decade old and supported a wide set of Python versions +for most of its life, which has made it a difficult library to add new features +to. colorlog 6 may break backwards compatibility so that newer features +can be added more easily, but may still not accept all changes or feature +requests. colorlog 4 might accept essential bugfixes but should not be +considered actively maintained and will not accept any major changes or new +features. + +Installation +------------ + +Install from PyPI with: + +```bash +pip install colorlog +``` + +Several Linux distributions provide official packages ([Debian], [Fedora], +[Gentoo], [OpenSuse] and [Ubuntu]), and others have user provided packages +([Arch AUR], [BSD ports], [Conda]). + +Usage +----- + +```python +import colorlog + +handler = colorlog.StreamHandler() +handler.setFormatter(colorlog.ColoredFormatter( + '%(log_color)s%(levelname)s:%(name)s:%(message)s')) + +logger = colorlog.getLogger('example') +logger.addHandler(handler) +``` + +The `ColoredFormatter` class takes several arguments: + +- `format`: The format string used to output the message (required). +- `datefmt`: An optional date format passed to the base class. See [`logging.Formatter`][Formatter]. +- `reset`: Implicitly adds a color reset code to the message output, unless the output already ends with one. Defaults to `True`. +- `log_colors`: A mapping of record level names to color names. The defaults can be found in `colorlog.default_log_colors`, or the below example. +- `secondary_log_colors`: A mapping of names to `log_colors` style mappings, defining additional colors that can be used in format strings. See below for an example. +- `style`: Available on Python 3.2 and above. See [`logging.Formatter`][Formatter]. + +Color escape codes can be selected based on the log records level, by adding +parameters to the format string: + +- `log_color`: Return the color associated with the records level. +- `_log_color`: Return another color based on the records level if the formatter has secondary colors configured (see `secondary_log_colors` below). + +Multiple escape codes can be used at once by joining them with commas when +configuring the color for a log level (but can't be used directly in the format +string). For example, `black,bg_white` would use the escape codes for black +text on a white background. + +The following escape codes are made available for use in the format string: + +- `{color}`, `fg_{color}`, `bg_{color}`: Foreground and background colors. +- `bold`, `bold_{color}`, `fg_bold_{color}`, `bg_bold_{color}`: Bold/bright colors. +- `thin`, `thin_{color}`, `fg_thin_{color}`: Thin colors (terminal dependent). +- `reset`: Clear all formatting (both foreground and background colors). + +The available color names are `black`, `red`, `green`, `yellow`, `blue`, +`purple`, `cyan` and `white`. + +Examples +-------- + +![Example output](doc/example.png) + +The following code creates a `ColoredFormatter` for use in a logging setup, +using the default values for each argument. + +```python +from colorlog import ColoredFormatter + +formatter = ColoredFormatter( + "%(log_color)s%(levelname)-8s%(reset)s %(blue)s%(message)s", + datefmt=None, + reset=True, + log_colors={ + 'DEBUG': 'cyan', + 'INFO': 'green', + 'WARNING': 'yellow', + 'ERROR': 'red', + 'CRITICAL': 'red,bg_white', + }, + secondary_log_colors={}, + style='%' +) +``` + +### Using `secondary_log_colors` + +Secondary log colors are a way to have more than one color that is selected +based on the log level. Each key in `secondary_log_colors` adds an attribute +that can be used in format strings (`message` becomes `message_log_color`), and +has a corresponding value that is identical in format to the `log_colors` +argument. + +The following example highlights the level name using the default log colors, +and highlights the message in red for `error` and `critical` level log messages. + +```python +from colorlog import ColoredFormatter + +formatter = ColoredFormatter( + "%(log_color)s%(levelname)-8s%(reset)s %(message_log_color)s%(message)s", + secondary_log_colors={ + 'message': { + 'ERROR': 'red', + 'CRITICAL': 'red' + } + } +) +``` + +### With [`dictConfig`][dictConfig] + +```python +logging.config.dictConfig({ + 'formatters': { + 'colored': { + '()': 'colorlog.ColoredFormatter', + 'format': "%(log_color)s%(levelname)-8s%(reset)s %(blue)s%(message)s" + } + } +}) +``` + +A full example dictionary can be found in `tests/test_colorlog.py`. + +### With [`fileConfig`][fileConfig] + +```ini +... + +[formatters] +keys=color + +[formatter_color] +class=colorlog.ColoredFormatter +format=%(log_color)s%(levelname)-8s%(reset)s %(bg_blue)s[%(name)s]%(reset)s %(message)s from fileConfig +datefmt=%m-%d %H:%M:%S +``` + +An instance of ColoredFormatter created with those arguments will then be used +by any handlers that are configured to use the `color` formatter. + +A full example configuration can be found in `tests/test_config.ini`. + +### With custom log levels + +ColoredFormatter will work with custom log levels added with +[`logging.addLevelName`][addLevelName]: + +```python +import logging, colorlog +TRACE = 5 +logging.addLevelName(TRACE, 'TRACE') +formatter = colorlog.ColoredFormatter(log_colors={'TRACE': 'yellow'}) +handler = logging.StreamHandler() +handler.setFormatter(formatter) +logger = logging.getLogger('example') +logger.addHandler(handler) +logger.setLevel('TRACE') +logger.log(TRACE, 'a message using a custom level') +``` + +Tests +----- + +Tests similar to the above examples are found in `tests/test_colorlog.py`. + +Status +------ + +colorlog is in maintenance mode. I try and ensure bugfixes are published, +but compatibility with Python 2.6+ and Python 3+ makes this a difficult +codebase to add features to. Any changes that might break backwards +compatibility for existing users will not be considered. + +Alternatives +------------ + +There are some more modern libraries for improving Python logging you may +find useful. + +- [structlog] +- [jsonlog] + +Projects using colorlog +----------------------- + +GitHub provides [a list of projects that depend on colorlog][dependents]. + +Some early adopters included [Errbot], [Pythran], and [zenlog]. + +Licence +------- + +Copyright (c) 2012-2021 Sam Clements + +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. + +[dictConfig]: http://docs.python.org/3/library/logging.config.html#logging.config.dictConfig +[fileConfig]: http://docs.python.org/3/library/logging.config.html#logging.config.fileConfig +[addLevelName]: https://docs.python.org/3/library/logging.html#logging.addLevelName +[Formatter]: http://docs.python.org/3/library/logging.html#logging.Formatter +[tox]: http://tox.readthedocs.org/ +[Arch AUR]: https://aur.archlinux.org/packages/python-colorlog/ +[BSD ports]: https://www.freshports.org/devel/py-colorlog/ +[colorama]: https://pypi.python.org/pypi/colorama +[Conda]: https://anaconda.org/conda-forge/colorlog +[Debian]: https://packages.debian.org/buster/python3-colorlog +[Errbot]: http://errbot.io/ +[Fedora]: https://src.fedoraproject.org/rpms/python-colorlog +[Gentoo]: https://packages.gentoo.org/packages/dev-python/colorlog +[OpenSuse]: http://rpm.pbone.net/index.php3?stat=3&search=python-colorlog&srodzaj=3 +[Pythran]: https://github.com/serge-sans-paille/pythran +[Ubuntu]: https://launchpad.net/python-colorlog +[zenlog]: https://github.com/ManufacturaInd/python-zenlog +[structlog]: https://www.structlog.org/en/stable/ +[jsonlog]: https://github.com/borntyping/jsonlog +[dependents]: https://github.com/borntyping/python-colorlog/network/dependents?package_id=UGFja2FnZS01MDk3NDcyMQ%3D%3D + + diff --git a/venv/lib/python3.7/site-packages/colorlog-6.5.0.dist-info/RECORD b/venv/lib/python3.7/site-packages/colorlog-6.5.0.dist-info/RECORD new file mode 100644 index 0000000..139e7c0 --- /dev/null +++ b/venv/lib/python3.7/site-packages/colorlog-6.5.0.dist-info/RECORD @@ -0,0 +1,15 @@ +colorlog-6.5.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +colorlog-6.5.0.dist-info/LICENSE,sha256=sdkIK8SDYj_Vn8cnm0V_DkDZQqdkJs3iVyOeBN_kElo,1107 +colorlog-6.5.0.dist-info/METADATA,sha256=TBVqghxWcw7iPlklHde-cUB9CeRYvXyqUhUDL_UmEDM,10711 +colorlog-6.5.0.dist-info/RECORD,, +colorlog-6.5.0.dist-info/WHEEL,sha256=WzZ8cwjh8l0jtULNjYq1Hpr-WCqCRgPr--TX4P5I1Wo,110 +colorlog-6.5.0.dist-info/top_level.txt,sha256=CzNs7OLwLxUhbQzCCT2ore3b_ZzAXusw0tWIX79iWow,9 +colorlog/__init__.py,sha256=wzxah0vO2HpJheG0gXY4rMx_MyFYyfHsR8rTucg5PSI,1180 +colorlog/__pycache__/__init__.cpython-37.pyc,, +colorlog/__pycache__/escape_codes.cpython-37.pyc,, +colorlog/__pycache__/formatter.cpython-37.pyc,, +colorlog/__pycache__/wrappers.cpython-37.pyc,, +colorlog/escape_codes.py,sha256=jFxDvDyWHk5gX5tHpFIMF5om52dI_jCqyTBmeMtnpxQ,2437 +colorlog/formatter.py,sha256=-SPSeP6QzrZnwBS-elqOPXHfSVNCH6po6Ek-S9Cvl4k,7165 +colorlog/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +colorlog/wrappers.py,sha256=pUFWgPN-SljaWClIVzlSkmtxsYLLtPpyroiyEILOpWc,2174 diff --git a/venv/lib/python3.7/site-packages/colorlog-6.5.0.dist-info/WHEEL b/venv/lib/python3.7/site-packages/colorlog-6.5.0.dist-info/WHEEL new file mode 100644 index 0000000..b733a60 --- /dev/null +++ b/venv/lib/python3.7/site-packages/colorlog-6.5.0.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.0) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/venv/lib/python3.7/site-packages/colorlog-6.5.0.dist-info/top_level.txt b/venv/lib/python3.7/site-packages/colorlog-6.5.0.dist-info/top_level.txt new file mode 100644 index 0000000..3d8df46 --- /dev/null +++ b/venv/lib/python3.7/site-packages/colorlog-6.5.0.dist-info/top_level.txt @@ -0,0 +1 @@ +colorlog diff --git a/venv/lib/python3.7/site-packages/colorlog/__init__.py b/venv/lib/python3.7/site-packages/colorlog/__init__.py new file mode 100644 index 0000000..fb123b8 --- /dev/null +++ b/venv/lib/python3.7/site-packages/colorlog/__init__.py @@ -0,0 +1,66 @@ +"""A logging formatter for colored output.""" + +import sys +import warnings + +from colorlog.formatter import ( + ColoredFormatter, + LevelFormatter, + TTYColoredFormatter, + default_log_colors, +) +from colorlog.wrappers import ( + CRITICAL, + DEBUG, + ERROR, + FATAL, + INFO, + NOTSET, + StreamHandler, + WARN, + WARNING, + basicConfig, + critical, + debug, + error, + exception, + getLogger, + info, + log, + root, + warning, +) + +__all__ = ( + "CRITICAL", + "DEBUG", + "ERROR", + "FATAL", + "INFO", + "NOTSET", + "WARN", + "WARNING", + "ColoredFormatter", + "LevelFormatter", + "StreamHandler", + "TTYColoredFormatter", + "basicConfig", + "critical", + "debug", + "default_log_colors", + "error", + "exception", + "exception", + "getLogger", + "info", + "log", + "root", + "warning", +) + +if sys.version_info < (3, 6): + warnings.warn( + "Colorlog requires Python 3.6 or above. Pin 'colorlog<5' to your dependencies " + "if you require compatibility with older versions of Python. See " + "https://github.com/borntyping/python-colorlog#status for more information." + ) diff --git a/venv/lib/python3.7/site-packages/colorlog/__pycache__/__init__.cpython-37.pyc b/venv/lib/python3.7/site-packages/colorlog/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000..dd77a27 Binary files /dev/null and b/venv/lib/python3.7/site-packages/colorlog/__pycache__/__init__.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/colorlog/__pycache__/escape_codes.cpython-37.pyc b/venv/lib/python3.7/site-packages/colorlog/__pycache__/escape_codes.cpython-37.pyc new file mode 100644 index 0000000..1523a63 Binary files /dev/null and b/venv/lib/python3.7/site-packages/colorlog/__pycache__/escape_codes.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/colorlog/__pycache__/formatter.cpython-37.pyc b/venv/lib/python3.7/site-packages/colorlog/__pycache__/formatter.cpython-37.pyc new file mode 100644 index 0000000..9de2295 Binary files /dev/null and b/venv/lib/python3.7/site-packages/colorlog/__pycache__/formatter.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/colorlog/__pycache__/wrappers.cpython-37.pyc b/venv/lib/python3.7/site-packages/colorlog/__pycache__/wrappers.cpython-37.pyc new file mode 100644 index 0000000..b57a7b4 Binary files /dev/null and b/venv/lib/python3.7/site-packages/colorlog/__pycache__/wrappers.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/colorlog/escape_codes.py b/venv/lib/python3.7/site-packages/colorlog/escape_codes.py new file mode 100644 index 0000000..f35dfca --- /dev/null +++ b/venv/lib/python3.7/site-packages/colorlog/escape_codes.py @@ -0,0 +1,104 @@ +""" +Generates a dictionary of ANSI escape codes. + +http://en.wikipedia.org/wiki/ANSI_escape_code + +Uses colorama as an optional dependency to support color on Windows +""" +import sys + +try: + import colorama +except ImportError: + pass +else: + if sys.platform == "win32": + colorama.init(strip=False) + +__all__ = ("escape_codes", "parse_colors") + + +# Returns escape codes from format codes +def esc(*codes: int) -> str: + return "\033[" + ";".join(str(code) for code in codes) + "m" + + +escape_codes = { + "reset": esc(0), + "bold": esc(1), + "thin": esc(2), +} + +escape_codes_foreground = { + "black": 30, + "red": 31, + "green": 32, + "yellow": 33, + "blue": 34, + "purple": 35, + "cyan": 36, + "white": 37, + "light_black": 90, + "light_red": 91, + "light_green": 92, + "light_yellow": 93, + "light_blue": 94, + "light_purple": 95, + "light_cyan": 96, + "light_white": 97, +} + +escape_codes_background = { + "black": 40, + "red": 41, + "green": 42, + "yellow": 43, + "blue": 44, + "purple": 45, + "cyan": 46, + "white": 47, + "light_black": 100, + "light_red": 101, + "light_green": 102, + "light_yellow": 103, + "light_blue": 104, + "light_purple": 105, + "light_cyan": 106, + "light_white": 107, + # Bold background colors don't exist, + # but we used to provide these names. + "bold_black": 100, + "bold_red": 101, + "bold_green": 102, + "bold_yellow": 103, + "bold_blue": 104, + "bold_purple": 105, + "bold_cyan": 106, + "bold_white": 107, +} + +# Foreground without prefix +for name, code in escape_codes_foreground.items(): + escape_codes["%s" % name] = esc(code) + escape_codes["bold_%s" % name] = esc(1, code) + escape_codes["thin_%s" % name] = esc(2, code) + +# Foreground with fg_ prefix +for name, code in escape_codes_foreground.items(): + escape_codes["fg_%s" % name] = esc(code) + escape_codes["fg_bold_%s" % name] = esc(1, code) + escape_codes["fg_thin_%s" % name] = esc(2, code) + +# Background with bg_ prefix +for name, code in escape_codes_background.items(): + escape_codes["bg_%s" % name] = esc(code) + +# 256 colour support +for code in range(256): + escape_codes["fg_%d" % code] = esc(38, 5, code) + escape_codes["bg_%d" % code] = esc(48, 5, code) + + +def parse_colors(string: str) -> str: + """Return escape codes from a color sequence string.""" + return "".join(escape_codes[n] for n in string.split(",") if n) diff --git a/venv/lib/python3.7/site-packages/colorlog/formatter.py b/venv/lib/python3.7/site-packages/colorlog/formatter.py new file mode 100644 index 0000000..3bdddea --- /dev/null +++ b/venv/lib/python3.7/site-packages/colorlog/formatter.py @@ -0,0 +1,204 @@ +"""The ColoredFormatter class.""" + +import logging +import os +import sys +import typing + +import colorlog.escape_codes + +__all__ = ( + "default_log_colors", + "ColoredFormatter", + "LevelFormatter", + "TTYColoredFormatter", +) + +# Type aliases used in function signatures. +EscapeCodes = typing.Mapping[str, str] +LogColors = typing.Mapping[str, str] +SecondaryLogColors = typing.Mapping[str, LogColors] + +# The default colors to use for the debug levels +default_log_colors = { + "DEBUG": "white", + "INFO": "green", + "WARNING": "yellow", + "ERROR": "red", + "CRITICAL": "bold_red", +} + +# The default format to use for each style +default_formats = { + "%": "%(log_color)s%(levelname)s:%(name)s:%(message)s", + "{": "{log_color}{levelname}:{name}:{message}", + "$": "${log_color}${levelname}:${name}:${message}", +} + + +class ColoredRecord: + """ + Wraps a LogRecord, adding escape codes to the internal dict. + + The internal dict is used when formatting the message (by the PercentStyle, + StrFormatStyle, and StringTemplateStyle classes). + """ + + def __init__(self, record: logging.LogRecord, escapes: EscapeCodes) -> None: + self.__dict__.update(record.__dict__) + self.__dict__.update(escapes) + + +class ColoredFormatter(logging.Formatter): + """ + A formatter that allows colors to be placed in the format string. + + Intended to help in creating more readable logging output. + """ + + def __init__( + self, + fmt: typing.Optional[str] = None, + datefmt: typing.Optional[str] = None, + style: str = "%", + log_colors: typing.Optional[LogColors] = None, + reset: bool = True, + secondary_log_colors: typing.Optional[SecondaryLogColors] = None, + validate: bool = True, + stream: typing.Optional[typing.IO] = None, + no_color: bool = False, + ) -> None: + """ + Set the format and colors the ColoredFormatter will use. + + The ``fmt``, ``datefmt`` and ``style`` args are passed on to the + ``logging.Formatter`` constructor. + + The ``secondary_log_colors`` argument can be used to create additional + ``log_color`` attributes. Each key in the dictionary will set + ``{key}_log_color``, using the value to select from a different + ``log_colors`` set. + + :Parameters: + - fmt (str): The format string to use. + - datefmt (str): A format string for the date. + - log_colors (dict): + A mapping of log level names to color names. + - reset (bool): + Implicitly append a color reset to all records unless False. + - style ('%' or '{' or '$'): + The format style to use. (*No meaning prior to Python 3.2.*) + - secondary_log_colors (dict): + Map secondary ``log_color`` attributes. (*New in version 2.6.*) + - validate (bool) + Validate the format string. + - stream (typing.IO) + The stream formatted messages will be printed to. Used to toggle colour + on non-TTY outputs. Optional. + """ + + # Select a default format if `fmt` is not provided. + fmt = default_formats[style] if fmt is None else fmt + + if sys.version_info >= (3, 8): + super().__init__(fmt, datefmt, style, validate) + else: + super().__init__(fmt, datefmt, style) + + self.log_colors = log_colors if log_colors is not None else default_log_colors + self.secondary_log_colors = ( + secondary_log_colors if secondary_log_colors is not None else {} + ) + self.reset = reset + self.stream = stream + self.no_color = no_color + + def formatMessage(self, record: logging.LogRecord) -> str: + """Format a message from a record object.""" + escapes = self._escape_code_map(record.levelname) + wrapper = ColoredRecord(record, escapes) + message = super().formatMessage(wrapper) # type: ignore + message = self._append_reset(message, escapes) + return message + + def _escape_code_map(self, item: str) -> EscapeCodes: + """ + Build a map of keys to escape codes for use in message formatting. + + If _blank_escape_codes() returns True, all values will be an empty string. + """ + codes = {**colorlog.escape_codes.escape_codes} + codes.setdefault("log_color", self._get_escape_code(self.log_colors, item)) + for name, colors in self.secondary_log_colors.items(): + codes.setdefault("%s_log_color" % name, self._get_escape_code(colors, item)) + if self._blank_escape_codes(): + codes = {key: "" for key in codes.keys()} + return codes + + def _blank_escape_codes(self): + """Return True if we should be prevented from printing escape codes.""" + if self.no_color: + return True + + if "NO_COLOR" in os.environ: + return True + + if self.stream is not None and not self.stream.isatty(): + return True + + return False + + @staticmethod + def _get_escape_code(log_colors: LogColors, item: str) -> str: + """Extract a color sequence from a mapping, and return escape codes.""" + return colorlog.escape_codes.parse_colors(log_colors.get(item, "")) + + def _append_reset(self, message: str, escapes: EscapeCodes) -> str: + """Add a reset code to the end of the message, if it's not already there.""" + reset_escape_code = escapes["reset"] + + if self.reset and not message.endswith(reset_escape_code): + message += reset_escape_code + + return message + + +class LevelFormatter: + """An extension of ColoredFormatter that uses per-level format strings.""" + + def __init__(self, fmt: typing.Mapping[str, str], **kwargs: typing.Any) -> None: + """ + Configure a ColoredFormatter with its own format string for each log level. + + Supports fmt as a dict. All other args are passed on to the + ``colorlog.ColoredFormatter`` constructor. + + :Parameters: + - fmt (dict): + A mapping of log levels (represented as strings, e.g. 'WARNING') to + format strings. (*New in version 2.7.0) + (All other parameters are the same as in colorlog.ColoredFormatter) + + Example: + + formatter = colorlog.LevelFormatter( + fmt={ + "DEBUG": "%(log_color)s%(message)s (%(module)s:%(lineno)d)", + "INFO": "%(log_color)s%(message)s", + "WARNING": "%(log_color)sWRN: %(message)s (%(module)s:%(lineno)d)", + "ERROR": "%(log_color)sERR: %(message)s (%(module)s:%(lineno)d)", + "CRITICAL": "%(log_color)sCRT: %(message)s (%(module)s:%(lineno)d)", + } + ) + """ + self.formatters = { + level: ColoredFormatter(fmt=f, **kwargs) for level, f in fmt.items() + } + + def format(self, record: logging.LogRecord) -> str: + return self.formatters[record.levelname].format(record) + + +# Provided for backwards compatibility. The features provided by this subclass are now +# included directly in the `ColoredFormatter` class. +TTYColoredFormatter = ColoredFormatter diff --git a/venv/lib/python3.7/site-packages/colorlog/py.typed b/venv/lib/python3.7/site-packages/colorlog/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.7/site-packages/colorlog/wrappers.py b/venv/lib/python3.7/site-packages/colorlog/wrappers.py new file mode 100644 index 0000000..7434ef5 --- /dev/null +++ b/venv/lib/python3.7/site-packages/colorlog/wrappers.py @@ -0,0 +1,92 @@ +"""Wrappers around the logging module.""" + +import functools +import logging +import typing +from logging import ( + CRITICAL, + DEBUG, + ERROR, + FATAL, + INFO, + NOTSET, + StreamHandler, + WARN, + WARNING, + getLogger, + root, +) + +import colorlog.formatter + +__all__ = ( + "CRITICAL", + "DEBUG", + "ERROR", + "FATAL", + "INFO", + "NOTSET", + "WARN", + "WARNING", + "StreamHandler", + "basicConfig", + "critical", + "debug", + "error", + "exception", + "getLogger", + "info", + "log", + "root", + "warning", +) + + +def basicConfig( + style: str = "%", + log_colors: typing.Optional[colorlog.formatter.LogColors] = None, + reset: bool = True, + secondary_log_colors: typing.Optional[colorlog.formatter.SecondaryLogColors] = None, + format: str = "%(log_color)s%(levelname)s%(reset)s:%(name)s:%(message)s", + datefmt: typing.Optional[str] = None, + **kwargs +) -> None: + """Call ``logging.basicConfig`` and override the formatter it creates.""" + logging.basicConfig(**kwargs) + logging._acquireLock() # type: ignore + try: + handler = logging.root.handlers[0] + handler.setFormatter( + colorlog.formatter.ColoredFormatter( + fmt=format, + datefmt=datefmt, + style=style, + log_colors=log_colors, + reset=reset, + secondary_log_colors=secondary_log_colors, + stream=kwargs.get("stream", None), + ) + ) + finally: + logging._releaseLock() # type: ignore + + +def ensure_configured(func): + """Modify a function to call our basicConfig() first if no handlers exist.""" + + @functools.wraps(func) + def wrapper(*args, **kwargs): + if len(logging.root.handlers) == 0: + basicConfig() + return func(*args, **kwargs) + + return wrapper + + +debug = ensure_configured(logging.debug) +info = ensure_configured(logging.info) +warning = ensure_configured(logging.warning) +error = ensure_configured(logging.error) +critical = ensure_configured(logging.critical) +log = ensure_configured(logging.log) +exception = ensure_configured(logging.exception) diff --git a/venv/lib/python3.7/site-packages/colormap-1.0.4.dist-info/DESCRIPTION.rst b/venv/lib/python3.7/site-packages/colormap-1.0.4.dist-info/DESCRIPTION.rst new file mode 100644 index 0000000..18309af --- /dev/null +++ b/venv/lib/python3.7/site-packages/colormap-1.0.4.dist-info/DESCRIPTION.rst @@ -0,0 +1,73 @@ +############################# +COLORMAP documentation +############################# + + +Please see : http://colormap.readthedocs.io/ for an up-to-date documentation. + +.. image:: https://badge.fury.io/py/colormap.svg + :target: https://pypi.python.org/pypi/colormap + +.. image:: https://github.com/cokelaer/colormap/actions/workflows/ci.yml/badge.svg?branch=master + :target: https://github.com/cokelaer/colormap/actions/workflows/ci.yml + +.. image:: https://coveralls.io/repos/cokelaer/colormap/badge.png?branch=master + :target: https://coveralls.io/r/cokelaer/colormap?branch=master + + +:version: Python 3.6, 3.7, 3.8, 3.9 +:contributions: Please join https://github.com/cokelaer/colormap +:issues: Please use https://github.com/cokelaer/colormap/issues +:notebook: Please see https://github.com/cokelaer/colormap/tree/master/notebooks + + + +What is it ? +################ + +**colormap** package provides simple utilities to convert colors between +RGB, HEX, HLS, HUV and a class to easily build colormaps for matplotlib. All +matplotlib colormaps and some R colormaps are available altogether. The +plot_colormap method (see below) is handy to quickly pick up a colormaps and +the test_colormap is useful to see test a new colormap. + + +Installation +################### + +:: + + pip install colormap + +Example +########## + +* Create your own colormap from red to green colors with intermediate color as + whitish (diverging map from red to green):: + + c = Colormap() + mycmap = c.cmap( {'red':[1,1,0], 'green':[0,1,.39], 'blue':[0,1,0]}) + cmap = c.test_colormap(mycmap) + +* Even simpler if the colormap is linear:: + + c = Colormap() + mycmap = c.cmap_linear('red', 'white', 'green(w3c)') + cmap = c.test_colormap(mycmap) + +.. image:: http://colormap.readthedocs.io/en/latest/_images/index-1.png + :width: 50% + :align: center + +* check out the available colormaps:: + + c = Colormap() + c.plot_colormap('diverging') + +.. image:: http://colormap.readthedocs.io/en/latest/_images/colormaps.png + :width: 50% + :align: center + +See online documentation for details: http://colormap.readthedocs.io/ + + diff --git a/venv/lib/python3.7/site-packages/colormap-1.0.4.dist-info/INSTALLER b/venv/lib/python3.7/site-packages/colormap-1.0.4.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.7/site-packages/colormap-1.0.4.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.7/site-packages/colormap-1.0.4.dist-info/METADATA b/venv/lib/python3.7/site-packages/colormap-1.0.4.dist-info/METADATA new file mode 100644 index 0000000..6157a0d --- /dev/null +++ b/venv/lib/python3.7/site-packages/colormap-1.0.4.dist-info/METADATA @@ -0,0 +1,102 @@ +Metadata-Version: 2.0 +Name: colormap +Version: 1.0.4 +Summary: Utilities to ease manipulation of matplotlib colormaps and color codecs (e.g., hex2rgb) +Home-page: http://github.com/cokelaer/colormap +Author: Thomas Cokelaer +Author-email: cokelaer@ebi.ac.uk +Maintainer: Thomas Cokelaer +Maintainer-email: cokelaer@ebi.ac.uk +License: LGPL +Download-URL: http://pypi.python.org/pypi/colormap +Keywords: hex2web,web2hex,hex2rgb,rgb2hex,rgb2hsv,hsv2rgb,rgb2hls,hls2rgb,colormap,colors +Platform: Linux +Platform: Unix +Platform: MacOsX +Platform: Windows +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: Science/Research +Classifier: License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL) +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Requires: matplotlib +Requires: easydev + +############################# +COLORMAP documentation +############################# + + +Please see : http://colormap.readthedocs.io/ for an up-to-date documentation. + +.. image:: https://badge.fury.io/py/colormap.svg + :target: https://pypi.python.org/pypi/colormap + +.. image:: https://github.com/cokelaer/colormap/actions/workflows/ci.yml/badge.svg?branch=master + :target: https://github.com/cokelaer/colormap/actions/workflows/ci.yml + +.. image:: https://coveralls.io/repos/cokelaer/colormap/badge.png?branch=master + :target: https://coveralls.io/r/cokelaer/colormap?branch=master + + +:version: Python 3.6, 3.7, 3.8, 3.9 +:contributions: Please join https://github.com/cokelaer/colormap +:issues: Please use https://github.com/cokelaer/colormap/issues +:notebook: Please see https://github.com/cokelaer/colormap/tree/master/notebooks + + + +What is it ? +################ + +**colormap** package provides simple utilities to convert colors between +RGB, HEX, HLS, HUV and a class to easily build colormaps for matplotlib. All +matplotlib colormaps and some R colormaps are available altogether. The +plot_colormap method (see below) is handy to quickly pick up a colormaps and +the test_colormap is useful to see test a new colormap. + + +Installation +################### + +:: + + pip install colormap + +Example +########## + +* Create your own colormap from red to green colors with intermediate color as + whitish (diverging map from red to green):: + + c = Colormap() + mycmap = c.cmap( {'red':[1,1,0], 'green':[0,1,.39], 'blue':[0,1,0]}) + cmap = c.test_colormap(mycmap) + +* Even simpler if the colormap is linear:: + + c = Colormap() + mycmap = c.cmap_linear('red', 'white', 'green(w3c)') + cmap = c.test_colormap(mycmap) + +.. image:: http://colormap.readthedocs.io/en/latest/_images/index-1.png + :width: 50% + :align: center + +* check out the available colormaps:: + + c = Colormap() + c.plot_colormap('diverging') + +.. image:: http://colormap.readthedocs.io/en/latest/_images/colormaps.png + :width: 50% + :align: center + +See online documentation for details: http://colormap.readthedocs.io/ + + diff --git a/venv/lib/python3.7/site-packages/colormap-1.0.4.dist-info/RECORD b/venv/lib/python3.7/site-packages/colormap-1.0.4.dist-info/RECORD new file mode 100644 index 0000000..c0672c7 --- /dev/null +++ b/venv/lib/python3.7/site-packages/colormap-1.0.4.dist-info/RECORD @@ -0,0 +1,15 @@ +colormap-1.0.4.dist-info/DESCRIPTION.rst,sha256=k7YNwYAoTXXmSRv_fsv0s_kKd-J4GSlTeC4RBiOtzy4,2118 +colormap-1.0.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +colormap-1.0.4.dist-info/METADATA,sha256=yEKDoGXSjih2XPEDj5845J2rhrKRejEUbzhgnbe15rI,3258 +colormap-1.0.4.dist-info/RECORD,, +colormap-1.0.4.dist-info/WHEEL,sha256=rNo05PbNqwnXiIHFsYm0m22u4Zm6YJtugFG2THx4w3g,92 +colormap-1.0.4.dist-info/metadata.json,sha256=T7IkUtRquUznFP3wgELkN6-hUbVl1nEvrxNbrOB6n5c,1269 +colormap-1.0.4.dist-info/top_level.txt,sha256=EzA_UbyiRZ69j6IL1oMeS3JTo2Y8sogbGnOGTShKdj0,9 +colormap/__init__.py,sha256=-cV9TPpBVnFeQFAXgzcfDr9qAd6nzfClvhmFMwTTD7I,1003 +colormap/__pycache__/__init__.cpython-37.pyc,, +colormap/__pycache__/colors.cpython-37.pyc,, +colormap/__pycache__/get_cmap.cpython-37.pyc,, +colormap/__pycache__/xfree86.cpython-37.pyc,, +colormap/colors.py,sha256=lxgLjKEQSemYcg14SyGuCXbGFlBv4ft1PsJ3LNn99gs,32584 +colormap/get_cmap.py,sha256=K3eGlNHo4ohUPgi_SVHRyoODRVL3S5TCR6NxJXRKXNk,2032 +colormap/xfree86.py,sha256=i5lHRxGBHHRjGs0A2O0ps_plLrYV-fJvnKW0u37dyGA,4958 diff --git a/venv/lib/python3.7/site-packages/colormap-1.0.4.dist-info/WHEEL b/venv/lib/python3.7/site-packages/colormap-1.0.4.dist-info/WHEEL new file mode 100644 index 0000000..bb7f7db --- /dev/null +++ b/venv/lib/python3.7/site-packages/colormap-1.0.4.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.29.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/venv/lib/python3.7/site-packages/colormap-1.0.4.dist-info/metadata.json b/venv/lib/python3.7/site-packages/colormap-1.0.4.dist-info/metadata.json new file mode 100644 index 0000000..4a3b99d --- /dev/null +++ b/venv/lib/python3.7/site-packages/colormap-1.0.4.dist-info/metadata.json @@ -0,0 +1 @@ +{"classifiers": ["Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Intended Audience :: Science/Research", "License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)", "Operating System :: OS Independent", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Topic :: Software Development :: Libraries :: Python Modules"], "download_url": "http://pypi.python.org/pypi/colormap", "extensions": {"python.details": {"contacts": [{"email": "cokelaer@ebi.ac.uk", "name": "Thomas Cokelaer", "role": "author"}, {"email": "cokelaer@ebi.ac.uk", "name": "Thomas Cokelaer", "role": "maintainer"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "http://github.com/cokelaer/colormap"}}}, "generator": "bdist_wheel (0.29.0)", "keywords": ["hex2web", "web2hex", "hex2rgb", "rgb2hex", "rgb2hsv", "hsv2rgb", "rgb2hls", "hls2rgb", "colormap", "colors"], "license": "LGPL", "metadata_version": "2.0", "name": "colormap", "platform": "Linux", "requires": "matplotlib", "summary": "Utilities to ease manipulation of matplotlib colormaps and color codecs (e.g., hex2rgb)", "version": "1.0.4"} \ No newline at end of file diff --git a/venv/lib/python3.7/site-packages/colormap-1.0.4.dist-info/top_level.txt b/venv/lib/python3.7/site-packages/colormap-1.0.4.dist-info/top_level.txt new file mode 100755 index 0000000..6cbac44 --- /dev/null +++ b/venv/lib/python3.7/site-packages/colormap-1.0.4.dist-info/top_level.txt @@ -0,0 +1 @@ +colormap diff --git a/venv/lib/python3.7/site-packages/colormap/__init__.py b/venv/lib/python3.7/site-packages/colormap/__init__.py new file mode 100644 index 0000000..0266932 --- /dev/null +++ b/venv/lib/python3.7/site-packages/colormap/__init__.py @@ -0,0 +1,39 @@ +# -*- python -*- +# -*- coding: utf-8 -*- +# +# This file is part of the colormap software +# +# Copyright (c) 2014 +# +# File author(s): Thomas Cokelaer +# +# Distributed under the GPLv3 License. +# See accompanying file LICENSE.txt or copy at +# http://www.gnu.org/licenses/gpl-3.0.html +# +# Website: https://www.github.com/cokelaer/colormap +# Documentation: http://packages.python.org/colormap +# +############################################################################## +"""main colormap module""" +from __future__ import print_function +from __future__ import division + +import pkg_resources +try: + version = pkg_resources.require("colormap")[0].version + __version__ = version +except Exception: + version = '' + + +from .xfree86 import * +from . import colors +from .colors import * +from .get_cmap import * + +c = Colormap() +colormap_names = c.colormaps + c.diverging_black +# create an alias to test_colormap methiod +test_colormap = c.test_colormap +test_cmap = c.test_colormap diff --git a/venv/lib/python3.7/site-packages/colormap/__pycache__/__init__.cpython-37.pyc b/venv/lib/python3.7/site-packages/colormap/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000..78dd40c Binary files /dev/null and b/venv/lib/python3.7/site-packages/colormap/__pycache__/__init__.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/colormap/__pycache__/colors.cpython-37.pyc b/venv/lib/python3.7/site-packages/colormap/__pycache__/colors.cpython-37.pyc new file mode 100644 index 0000000..850ce2e Binary files /dev/null and b/venv/lib/python3.7/site-packages/colormap/__pycache__/colors.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/colormap/__pycache__/get_cmap.cpython-37.pyc b/venv/lib/python3.7/site-packages/colormap/__pycache__/get_cmap.cpython-37.pyc new file mode 100644 index 0000000..f931798 Binary files /dev/null and b/venv/lib/python3.7/site-packages/colormap/__pycache__/get_cmap.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/colormap/__pycache__/xfree86.cpython-37.pyc b/venv/lib/python3.7/site-packages/colormap/__pycache__/xfree86.cpython-37.pyc new file mode 100644 index 0000000..3973a1b Binary files /dev/null and b/venv/lib/python3.7/site-packages/colormap/__pycache__/xfree86.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/colormap/colors.py b/venv/lib/python3.7/site-packages/colormap/colors.py new file mode 100644 index 0000000..a765fbb --- /dev/null +++ b/venv/lib/python3.7/site-packages/colormap/colors.py @@ -0,0 +1,991 @@ +# -*- python -*- +# -*- coding: utf-8 -*- +# +# This file is part of the colormap software +# +# Copyright (c) 2011-20134 +# +# File author(s): Thomas Cokelaer +# +# Distributed under the GPLv3 License. +# See accompanying file LICENSE.txt or copy at +# http://www.gnu.org/licenses/gpl-3.0.html +# +# Website: https://github.com/cokelaer/colormap +# Documentation: http://packages.python.org/colormap +# +############################################################################## +"""Utilities provided in this module can be found either in the +standard Python module called :mod:`colorsys` or in matplotlib.colors +(e.g rgb2hex) or are original to this module (e.g., rgb2huv) + + +""" +# matplotlib dependence is only inside Colormap class +import colorsys +from easydev.tools import check_param_in_list, swapdict, check_range +from colormap.xfree86 import XFree86_colors + + +__all__ = ["HEX", "Color", "hex2web", "web2hex", "hex2rgb", "hex2dec", + "rgb2hex", "rgb2hsv", "hsv2rgb", "rgb2hls", "hls2rgb","yuv2rgb", "rgb2yuv", + "to_intensity", "yuv2rgb_int", "rgb2yuv_int", "Colormap" + ] + + +def hex2web(hexa): + """Convert hexadecimal string (6 digits) into *web* version (3 digits) + + .. doctest:: + + >>> from colormap.colors import hex2web + >>> hex2web("#FFAA11") + '#FA1' + + .. seealso:: :func:`web2hex`, :func:`hex2rgb` + :func:`rgb2hex`, :func:`rgb2hsv`, :func:`hsv2rgb`, :func:`rgb2hls`, + :func:`hls2rgb` + """ + hexa = HEX().get_standard_hex_color(hexa) + return "#" + hexa[1::2] + + +def web2hex(web): + """Convert *web* hexadecimal string (3 digits) into 6 digits version + + .. doctest:: + + >>> from colormap.colors import web2hex + >>> web2hex("#FA1") + '#FFAA11' + + .. seealso:: :func:`hex2web`, :func:`hex2rgb` + :func:`rgb2hex`, :func:`rgb2hsv`, :func:`hsv2rgb`, :func:`rgb2hls`, + :func:`hls2rgb` + """ + return HEX().get_standard_hex_color(web) + + +def hex2rgb(hexcolor, normalise=False): + """This function converts a hex color triplet into RGB + + Valid hex code are: + + * #FFF + * #0000FF + * 0x0000FF + * 0xFA1 + + + .. doctest:: + + >>> from colormap.colors import hex2rgb + >>> hex2rgb("#FFF", normalise=False) + (255, 255, 255) + >>> hex2rgb("#FFFFFF", normalise=True) + (1.0, 1.0, 1.0) + + + .. seealso:: :func:`hex2web`, :func:`web2hex`, + :func:`rgb2hex`, :func:`rgb2hsv`, :func:`hsv2rgb`, :func:`rgb2hls`, + :func:`hls2rgb` + """ + hexcolor = HEX().get_standard_hex_color(hexcolor)[1:] + r, g, b = int(hexcolor[0:2], 16), int(hexcolor[2:4], 16), int(hexcolor[4:6], 16) + if normalise: + r, g, b = _normalise(r, g, b) + return r, g, b + + +def rgb2hex(r, g, b, normalised=False): + """Convert RGB to hexadecimal color + + :param: can be a tuple/list/set of 3 values (R,G,B) + :return: a hex vesion ofthe RGB 3-tuple + + .. doctest:: + + >>> from colormap.colors import rgb2hex + >>> rgb2hex(0,0,255, normalised=False) + '#0000FF' + >>> rgb2hex(0,0,1, normalised=True) + '#0000FF' + + .. seealso:: :func:`hex2web`, :func:`web2hex`, :func:`hex2rgb` + , :func:`rgb2hsv`, :func:`hsv2rgb`, :func:`rgb2hls`, + :func:`hls2rgb` + + """ + if normalised: + r, g, b = _denormalise(r, g, b, mode="rgb") + r = int(r) + g = int(g) + b = int(b) + check_range(r, 0, 255) + check_range(g, 0, 255) + check_range(b, 0, 255) + return '#%02X%02X%02X' % (r, g, b) + + +def rgb2hls(r, g, b, normalised=True): + """Convert an RGB value to an HLS value. + + :param bool normalised: if *normalised* is True, the input RGB triplet + should be in the range 0-1 (0-255 otherwise) + :return: the HLS triplet. If *normalised* parameter is True, the output + triplet is in the range 0-1; otherwise, H in the range 0-360 and LS + in the range 0-100. + + .. doctest:: + + >>> from colormap.colors import rgb2hls + >>> rgb2hls(255,255,255, normalised=False) + (0.0, 1.0, 0.0) + + + .. seealso:: :func:`hex2web`, :func:`web2hex`, :func:`hex2rgb` + :func:`rgb2hex`, :func:`hsv2rgb`, + :func:`hls2rgb` + """ + # rgb_to_hsv expects normalised values ! + if normalised: + upper = 1 + else: + upper = 255 + check_range(r, 0, upper) + check_range(g, 0, upper) + check_range(b, 0, upper) + if normalised==False: + r, g, b = _normalise(r, g, b) + h, l, s = colorsys.rgb_to_hls(r, g, b) + return h, l, s + + +def rgb2hsv(r, g, b, normalised=True): + """Convert an RGB value to an HSV value. + + :param bool normalised: if *normalised* is True, the input RGB triplet + should be in the range 0-1 (0-255 otherwise) + :return: the HSV triplet. If *normalised* parameter is True, the output + triplet is in the range 0-1; otherwise, H in the range 0-360 and LS + in the range 0-100. + + .. doctest:: + + >>> from colormap.colors import rgb2hsv + >>> rgb2hsv(0.5,0,1) + (0.75, 1, 1) + + + .. seealso:: :func:`hex2web`, :func:`web2hex`, :func:`hex2rgb` + :func:`rgb2hex`, :func:`hsv2rgb`, :func:`rgb2hls`, + :func:`hls2rgb` + """ + # rgb_to_hsv expects normalised values ! + if normalised: + upper = 1 + else: + upper = 255 + check_range(r, 0, upper) + check_range(g, 0, upper) + check_range(b, 0, upper) + if normalised==False: + r, g, b = _normalise(r, g, b) + h, s, v = colorsys.rgb_to_hsv(r, g, b) + return h,s,v + + +def hsv2rgb(h, s, v, normalised=True): + """Convert a hue-saturation-value (HSV) value to a red-green-blue (RGB). + + :param bool normalised: If *normalised* is True, the input HSV triplet + should be in the range 0-1; otherwise, H in the range 0-360 and LS + in the range 0-100. + :return: the RGB triplet. The output + triplet is in the range 0-1 whether the input is normalised or not. + + .. doctest:: + + >>> from colormap.colors import hsv2rgb + >>> hsv2rgb(0.5,1,1, normalised=True) # doctest: +SKIP + (0, 1, 1) + + + .. seealso:: :func:`hex2web`, :func:`web2hex`, :func:`hex2rgb` + :func:`rgb2hex`, :func:`rgb2hsv`, :func:`rgb2hls`, + :func:`hls2rgb` + .. seealso:: :func:`rgb2hex` + """ + if normalised: + upper = 1 + else: + upper = 100 + if normalised: + uppera = 1 + else: + uppera = 360 + check_range(h, 0, uppera) + check_range(s, 0, upper) + check_range(v, 0, upper) + if normalised == False: + h, s, v = _normalise(h, s, v, mode="hsv") + return colorsys.hsv_to_rgb(h, s, v) + + +def hls2rgb(h, l, s, normalised=True): + """Convert an HLS value to a RGB value. + + :param bool normalised: If *normalised* is True, the input HLS triplet + should be in the range 0-1; otherwise, H in the range 0-360 and LS + in the range 0-100. + + :return: the RGB triplet. The output + triplet is in the range 0-1 whether the input is normalised or not. + + .. doctest:: + + >>> from colormap.colors import hls2rgb + >>> hls2rgb(360, 50, 60, normalised=False) # doctest: +SKIP + (0.8, 0.2, 0.2) + + + .. seealso:: :func:`hex2web`, :func:`web2hex`, :func:`hex2rgb` + :func:`rgb2hex`, :func:`rgb2hsv`, :func:`hsv2rgb`, :func:`rgb2hls`, + + """ + if normalised: + upper = 1 + else: + upper = 100 + if normalised: + uppera = 1 + else: + uppera = 360 + check_range(h, 0, uppera) + check_range(s, 0, upper) + check_range(l, 0, upper) + if normalised == False: + h, l, s = _normalise(h, l, s, mode="hls") + return colorsys.hls_to_rgb(h, l, s) + + +def hex2dec(data): + """convert hexadecimal string (data) into a float in the [0-65536] inclusive range""" + if data[0] == '#': + data.replace('#', '') + return int(data, 16)/255. + +def rgb2yuv(r, g, b): + """Convert RGB triplet into YUV + + :return: YUV triplet with values between 0 and 1 + + `YUV wikipedia `_ + + .. warning:: expected input must be between 0 and 1 + .. note:: the constants referenc used is Rec. 601 + """ + check_range(r, 0, 1) + check_range(g, 0, 1) + check_range(b, 0, 1) + + #y = int(0.299 * r + 0.587 * g + 0.114 * b) + #u = int(-0.14713 * r + -0.28886 * g + 0.436 * b) + #v = int(0.615 * r + -0.51499 * g + -0.10001 * b) + + y = 0.299 * r + 0.587 * g + 0.114 * b + u = -32591.0/221500.0 * r + -63983.0/221500.0 * g + 0.436 * b + v = 0.615 * r + -72201./140200 * g + -7011/70100. * b + return (y, u, v) + + +def yuv2rgb(y, u, v): + """Convert YUV triplet into RGB + + `YUV `_ + + .. warning:: expected input must be between 0 and 255 (not normalised) + + """ + check_range(y, 0,1) + check_range(u, 0, 1) + check_range(v, 0, 1) + A, B, C, D = 701.0/615.0, 25251.0/63983.0, 209599.0/361005.0, 443.0/218.0 + r = y + A * v + g = y - B * u - C * v + b = y + D * u + return (r, g, b) + + +def rgb2yuv_int(r, g, b): + """Convert RGB triplet into YUV + + `YUV wikipedia `_ + + .. warning:: expected input must be between 0 and 255 (not normalised) + + """ + check_range(r, 0, 255) + check_range(g, 0, 255) + check_range(b, 0, 255) + + y = int(0.299 * r + 0.587 * g + 0.114 * b) + u = int(-32591.0/221500.0 * r + -63983.0/221500.0 * g + 0.436 * b) + v = int(0.615 * r + -72201./140200 * g + -7011/70100. * b) + + return (y, u, v) + + +def yuv2rgb_int(y, u, v): + """Convert YUV triplet into RGB + + `YUV `_ + + .. warning:: expected input must be between 0 and 255 (not normalised) + + """ + check_range(y, 0, 255) + check_range(u, 0, 255) + check_range(v, 0, 255) + r = int(y + 1.13983 * v) + g = int(y - 0.39465 * u - 0.58060 * v) + b = int(y + 2.03211 * u) + return (r, g, b) + + +def _denormalise(r, g, b, mode="rgb"): + check_param_in_list(mode, ["rgb", "hls", "hsv"]) + if mode == "rgb": + return r*255., g*255., b*255. + elif mode in ["hls", "hsv"]: + return r*360., g*100., b*100. + + +def _normalise(r, g, b, mode="rgb"): + check_param_in_list(mode, ["rgb", "hls", "hsv"]) + if mode == "rgb": + return r/255., g/255., b/255. + elif mode in ["hls", "hsv"]: + return r/360., g/100., b/100. + + +def to_intensity(n): + """Return intensity + + :param n: value between 0 and 1 + :return: value between 0 and 255; round(n*127.5+127.5) + """ + check_range(n, 0, 1) + return int(round(n * 127.5 + 127.5)) + + +class HEX(object): + """Class to check the validity of an hexadecimal string and get standard string + + By standard, we mean #FFFFFF (6 digits) + + :: + + >>> h = HEX() + >>> h.is_valid_hex_color("#FFFF00") + True + + """ + def __init__(self): + pass + + def is_valid_hex_color(self, value, verbose=True): + """Return True is the string can be interpreted as hexadecimal color + + Valid formats are + + * #FFF + * #0000FF + * 0x0000FF + * 0xFA1 + """ + try: + self.get_standard_hex_color(value) + return True + except Exception as err: + if verbose: + print(err) + return False + + def get_standard_hex_color(self, value): + """Return standard hexadecimal color + + By standard, we mean a string that starts with # sign followed by 6 + character, e.g. #AABBFF + """ + if isinstance(value, str)==False: + raise TypeError("value must be a string") + if len(value) <= 3: + raise ValueError("input string must be of type 0xFFF, 0xFFFFFF or #FFF or #FFFFFF") + + if value.startswith("0x") or value.startswith("0X"): + value = value[2:] + elif value.startswith("#"): + value = value[1:] + else: + raise ValueError("hexa string must start with a '#' sign or '0x' string") + value = value.upper() + # Now, we have either FFFFFF or FFF + # now check the length + for x in value: + if x not in "0123456789ABCDEF": + raise ValueError("Found invalid hexa character {0}".format(x)) + + if len(value) == 6 or len(value) == 8: + value = "#" + value[0:6] + elif len(value) == 3: + value = "#" + value[0]*2 + value[1]*2 + value[2]*2 + else: + raise ValueError("hexa string should be 3, 6 or 8 digits. if 8 digits, last 2 are ignored") + return value + + +class Color(HEX): + """Class to ease manipulation and conversion between color codes + + You can create an instance in many differen ways. You can either use a + human-readable name as long as it is part of the + `XFree86 list `_ + You can also provide a hexadecimal string (either 3 or 6 digits). You can + use triplets of values corresponding to the RGB, HSV or HLS conventions. + + Here are some examples: + + .. doctest:: + + from colormap import Color + + Color("red") # human XFree86 compatible representation + Color("#f00") # standard 3 hex digits + Color("#ff0000") # standard 6 hex digits + Color(hsv=(0,1,0.5)) + Color(hls=(0, 1, 0.5)) # HLS triplet + Color(rgb=(1, 0, 0)) # RGB triplet + Color(Color("red")) # using an instance of :class:`Color` + + Note that the RGB, HLS and HSV triplets use normalised values. If you need + to normalise the triplet, you can use :mod:`colormap.colors._normalise` that + provides a function to normalise RGB, HLS and HSV triplets:: + + colors._normalise(*(255, 255, 0), mode="rgb") + colors._normalise(*(360, 50, 100), mode="hls") + + If you provide a string, it has to be a valid string from XFree86. + In addition to the official names, the lower case names are valid. Besides, + there are names with spaces. The equivalent names without space are also + valid. Therefore the name "Spring Green", which is an official name can be + provided as "Spring Green", "spring green", "springgreen" or "SpringGreen". + + """ + # Get official color names + colors = XFree86_colors.copy() + # add color names without spaces + aliases = dict([(x.replace(" ", ""),x) for x in colors.keys() if " " in x]) + # add color names without spaces in lower cases + aliases.update([(x.replace(" ", "").lower(),x) for x in colors.keys() if " " in x]) + # add color names in lower case + aliases.update(dict([(x.lower(),x) for x in colors.keys()])) + aliases.update(dict([(x,x) for x in colors.keys()])) + + # keep track of all possible names + color_names = sorted(list(set(list(colors.keys()) +list( aliases.keys())))) + + def __init__(self, name=None, rgb=None, hls=None, hsv=None): + super(Color, self).__init__() + self._name = None + self._mode = None + self._rgb = None + + # Does the user provided the name argument (first one) as a string ? + if isinstance(name, str): + # if so, it can be a valid human name (e.g., red) or an hex + # assuming that valid hexadecimal starts with # or 0x, + # if we can interpret the string as an hexadecimal, we are done + if self.is_valid_hex_color(name, verbose=False): + self.hex = name + else: + # if not, then, the user probably provided a valid color name + # the property will check the validity. + self.name = name[:] + #all other input parameters are ignored + elif name == None: + if rgb: + self.rgb = rgb + elif hls: + self.hls = hls + elif hsv: + self.hsv = hsv + else: + raise ValueError("You must set one of the parameter") + elif isinstance(name, Color): + self.rgb = name.rgb + else: + raise ValueError("name parameter must be a string") + + def _get_name(self): + return self._name + def _set_name(self, name): + check_param_in_list(name, self.color_names) + name = self.aliases[name] + self._name = name + # set hex and rgb at the same time based on the name + self.hex = self.colors[name] + name = property(_get_name, _set_name) + color = property(_get_name, _set_name) + + def _get_hex(self): + return self._hex + def _set_hex(self, value): + # hex is an approximation made of 255 bits so do not define rgb here + if self.is_valid_hex_color(value): + value = self.get_standard_hex_color(value) + self._hex = value + if self._hex in self.colors.values(): + self._name = swapdict(self.colors, check_ambiguity=False)[self._hex] + else: + self._name = "undefined" + self._rgb = hex2rgb(self._hex, normalise=True) + else: + # just to warn the user + self.get_standard_hex_color(value) + hex = property(_get_hex, _set_hex, + doc="getter/setter the hexadecimal value.") + + def _get_rgb(self): + return self._rgb + def _set_rgb(self, value): + # set name, hex and rgb + self.hex = rgb2hex(*value , normalised=True) + # must reset rgb with its real value (set_hex may round the rgb) + # in _set_hex + self._rgb = value + rgb = property(_get_rgb, _set_rgb, + doc="getter/setter the RGB values (3-length tuple)") + + def _get_hsv(self): + hsv = rgb2hsv(*self.rgb) + return hsv + def _set_hsv(self, value): + # TODO: value must be normalised + self.rgb = hsv2rgb(*value) + hsv = property(_get_hsv, _set_hsv, + doc="getter/setter the HSV values (3-length tuple)") + + def _get_hls(self): + hls = rgb2hls(*self.rgb) + return hls + def _set_hls(self, value): + #hls = _normalise(*value, mode="hls") + #else: + hls = value + self.rgb = hls2rgb(*hls) + hls = property(_get_hls, _set_hls, + doc="getter/setter the HLS values (3-length tuple)") + + def _get_lightness(self): + return self.hls[1] + def _set_lightness(self, lightness): + h, l, s = self.hls + self.hls = (h, lightness, s) + lightness = property(_get_lightness, _set_lightness, + doc="getter/setter the lightness in the HLS triplet") + + def _get_saturation_hls(self): + return self.hls[2] + def _set_saturation_hls(self, saturation): + h, l, s = self.hls + self.hls = (h, l, saturation) + saturation_hls = property(_get_saturation_hls, _set_saturation_hls, + doc="getter/setter the saturation in the HLS triplet") + + def _get_hue(self): + return self.hls[0] + def _set_hue(self, hue): + h, l, s = self.hls + self.hls = (hue, l, s) + hue = property(_get_hue, _set_hue, + doc="getter/setter the saturation in the HLS triplet") + + def _get_red(self): + return self.rgb[0] + def _set_red(self, red): + r, g, b = self.rgb + self.rgb = (red,g,b) + red = property(_get_red, _set_red, + doc="getter/setter for the red color in RGB triplet") + + def _get_green(self): + return self.rgb[1] + def _set_green(self, green): + r, g, b = self.rgb + self.rgb = (r, green, b) + green = property(_get_green, _set_green, + doc="getter/setter for the green color in RGB triplet") + + def _get_blue(self): + return self.rgb[2] + def _set_blue(self, blue): + r, g, b = self.rgb + self.rgb = (r, g, blue) + blue = property(_get_blue, _set_blue, + doc="getter/setter for the blue color in RGB triplet") + + def _get_value(self): + return self.hls[0] + def _set_value(self, value): + h, s, v = self.hsv + self.hsv = (h, s, value) + value = property(_get_value, _set_value, + doc="getter/setter the value in the HSV triplet") + + def _get_yiq(self): + return colorsys.rgb_to_yiq(*self.rgb) + yiq = property(_get_yiq, doc="Getter for the YIQ triplet") + + def __str__(self): + txt = 'Color {0}\n'.format(self.name) + txt+= ' hexa code: {0}\n'.format(self.hex) + txt+= ' RGB code: {0}\n'.format(self.rgb) + txt+= ' RGB code (un-normalised): {0}\n\n'.format([x*255 for x in self.rgb]) + txt+= ' HSV code: {0}\n'.format(self.hsv) + txt+= ' HSV code: (un-normalised) {0} {1} {2}\n\n'.format(self.hsv[0]*360, self.hsv[1]*100, self.hsv[2]*100) + txt+= ' HLS code: {0}\n'.format(self.hls) + txt+= ' HLS code: (un-normalised) {0} {1} {2}\n\n'.format(self.hls[0]*360, self.hls[1]*100, self.hls[2]*100) + return txt + + +class Colormap(object): + """Class to create matplotlib colormap + + This example show how to get the pre-defined colormap called *heat* + + .. plot:: + :include-source: + + + from pylab import * + from colormap.colors import Colormap + + c = Colormap() + cmap = c.get_cmap_heat() + c.test_colormap(cmap) + + You may be more interested in building your own colormap:: + + # design your own colormap + d = {'blue': [0,0,0,1,1,1,0], + 'green':[0,1,1,1,0,0,0], + 'red': [1,1,0,0,0,1,1]} + cmap = c.cmap(d, reverse=False) + + # see the results + c.test_colormap(cmap) + + If you want a simple linear colormap, you can use the example above, + or use the :meth:`cmap_linear`. For instance for a diverging colormap + from red to green (with with color in between):: + + cmap = c.cmap_linear("red", "white", "green") + c.test_colormap(cmap) + + Even simpler, you can use a bicolor colormap :meth:`cmap_bicolor`. For instance + for a red to green colormap:: + + cmap = c.cmap_bicolor("red", "green") + c.test_colormap(cmap) + + From matplotlib documentation, colormaps falls into 4 categories: + + #. Sequential schemes for unipolar data that progresses from low to high + #. Diverging schemes for bipolar data that emphasizes positive or + negative deviations from acentral value + #. Cyclic schemes meant for plotting values that wrap around at the + endpoints, such as phase angle, wind direction, or time of day + #. Qualitative schemes for nominal data that has no inherent ordering, + where color is used only to distinguish categories + + + :references: matplotlib documentation and examples + http://matplotlib.org/examples/color/colormaps_reference.html + """ + def _get_colormap_mpl(self): + try: + from matplotlib.pyplot import colormaps as _cmaps + return _cmaps() + except: + return [] + colormaps = property(_get_colormap_mpl) + + def _get_sequentials(self): + return ['Blues', 'BuGn', 'BuPu', 'GnBu', 'Greens', 'Greys', 'OrRd', + 'Oranges', 'PuBu', 'PuBuGn', 'PuRd', 'Purples', 'RdPu', + 'Reds', 'YlGn', 'YlGnBu', 'YlOrBr', 'YlOrRd'] + sequentials = property(_get_sequentials) + + def _get_sequentials2(self): + return ['afmhot', 'autumn', 'bone', 'cool', 'copper', + 'gist_heat', 'gray', 'hot', 'pink', + 'spring', 'summer', 'winter'] + sequentials2 = property(_get_sequentials2) + + def _get_diverging(self): + return ['BrBG', 'PRGn', 'PiYG', 'PuOr', 'RdBu', 'RdGy', 'RdYlBu', + 'RdYlGn', 'Spectral', 'bwr', 'coolwarm', 'seismic'] + diverging = property(_get_diverging) + + def _get_diverging_black(self): + return ['red_black_sky', 'red_black_blue', 'red_black_green', 'yellow_black_blue', + 'yellow_black_sky', 'red_black_orange', 'pink_black_green(w3c)' + ] + diverging_black = property(_get_diverging_black) + + def _get_qualitative(self): + return ['Accent', 'Dark2', 'Paired', 'Pastel1', 'Pastel2', + 'Set1', 'Set2', 'Set3'] + qualitative = property(_get_qualitative) + + def _get_misc(self): + return ['gist_earth', 'terrain', 'ocean', 'gist_stern', + 'brg', 'CMRmap', 'cubehelix', 'gnuplot', 'gnuplot2', 'gist_ncar', + 'nipy_spectral', 'jet', 'rainbow', 'gist_rainbow', 'hsv', 'flag', 'prism'] + misc = property(_get_misc) + + def plot_rgb_from_hex_list(self, cols): + """This functions takes a list of hexadecimal values and plots + the RGB curves. This can be handy to figure out the RGB functions + to be used in the :meth:`get_cmap`. + + .. plot:: + :include-source: + :width: 60% + + from colormap.colors import Colormap + c = Colormap() + t = ['#FF0000FF', '#FF4D00FF', '#FF9900FF', '#FFE500FF', + '#CCFF00FF', '#80FF00FF', '#33FF00FF', '#00FF19FF', + '#00FF66FF', '#00FFB2FF', '#00FFFFFF', '#00B3FFFF', + '#0066FFFF', '#001AFFFF', '#3300FFFF', '#7F00FFFF', + '#CC00FFFF','#FF00E6FF','#FF0099FF', '#FF004DFF'] + c.plot_rgb_from_hex_list(t) + + """ + import pylab + red = [hex2rgb(x)[0]/255. for x in cols] + blue = [hex2rgb(x)[2]/255. for x in cols] + green = [hex2rgb(x)[1]/255. for x in cols] + x = pylab.linspace(0, 1, len(cols)) + pylab.clf() + pylab.plot(x, red, 'ro-', alpha=0.5) + pylab.plot(x, green, 'gs-', alpha=0.5, markersize=15) + pylab.plot(x, blue, 'bx-', alpha=0.5, markersize=15) + pylab.ylim([-0.1, 1.1]) + + def cmap_bicolor(self, color1, color2, reverse=False, N=256): + """Provide 3 colors in format accepted by :class:`Color` + + :: + + >>> red = Color('red') + >>> white = Color('white') + >>> cmap = cmap_bicolor(red, white) + + """ + c1 = Color(color1) + c2 = Color(color2) + dico = {'red': [c1.red, c2.red], + 'green':[c1.green, c2.green], + 'blue':[c1.blue, c2.blue]} + return self.cmap(dico, reverse=reverse, N=N) + + def cmap_linear(self, color1, color2, color3, reverse=False, N=256): + """Provide 3 colors in format accepted by :class:`Color` + + :: + + red = Color('red') + cmap = cmap_linear(red, 'white', '#0000FF') + + """ + c1 = Color(color1) + c2 = Color(color2) + c3 = Color(color3) + dico = {'red': [c1.red, c2.red, c3.red], + 'green':[c1.green, c2.green, c3.green], + 'blue':[c1.blue, c2.blue, c3.blue]} + + return self.cmap(dico, reverse=reverse, N=N) + + def cmap(self, colors=None, reverse=False, N=256): + """Return a colormap object to be used within matplotlib + + :param dict colors: a dictionary that defines the RGB colors to be + used in the colormap. See :meth:`get_cmap_heat` for an example. + :param bool reverse: reverse the colormap is set to True (defaults to False) + :param int N: Defaults to 50 + + """ + # matplotlib colormaps + if colors in self.colormaps: + if reverse and colors.endswith("_r") is False: + colors += "_r" + from matplotlib.cm import get_cmap + return get_cmap(colors) + # custom ones + elif colors in self.diverging_black: + c1, c2, c3 = colors.split("_") + # special case of sky, which does not exists + c3 = c3.replace("sky", "deep sky blue") + return self.cmap_linear(c1, c2, c3) + elif colors == 'heat': + return self.get_cmap_heat() + elif colors == 'heat_r': + return self.get_cmap_heat_r() + + + # Keep these dependencies inside the function to allow + # installation of colormap without those dependencies + # FIXME remove numpy dependencies + import numpy as np + # extracted from R, heat.colors(20) + + if reverse: + for k in colors.keys(): + colors[k].reverse() + + # If index not given, RGB colors are evenly-spaced in colormap. + index = np.linspace(0, 1, len(colors['red'])) + + # Adapt color_data to the form expected by LinearSegmentedColormap. + color_data = dict((key, [(x, y, y) for x, y in zip(index, value)]) + for key, value in list(colors.items())) + + import matplotlib + f = matplotlib.colors.LinearSegmentedColormap + m = f('my_color_map', color_data, N) + return m + + + def get_cmap_heat(self): + """Return a heat colormap matplotlib-compatible colormap + + This heat colormap should be equivalent to heat.colors() in R. + + :: + + >>> from colormap.colors import Colormap + >>> cmap = Colormap.get_cmap_heat() + + You can generate the colormap based solely on this information for the RGB + functions along:: + + d= { 'blue':[0,0,0,0,1], + 'green':[0,.35,.7,1,1], + 'red':[1,1,1,1,1]} + cmap = Colormap.get_cmap(d) + + """ + return self.cmap( + { 'blue':[0, 0, 0, 0, 1], + 'green':[0, .35, .7, 1, 1], + 'red':[1, 1, 1, 1, 1]}, reverse=False) + + def get_cmap_heat_r(self): + """Return a heat colormap matplotlib-compatible colormap + + Same as :meth:`get_cmap_heat` but reversed + """ + return self.cmap( + { 'blue':[0, 0, 0, 0, 1], + 'green':[0, .35, .7, 1, 1], + 'red':[1, 1, 1, 1, 1]}, reverse=True) + + def get_cmap_rainbow(self): + """colormap similar to rainbow colormap from R + + .. note:: The red is actually appearing on both sides... Yet + this looks like what is coded in R 3.0.1 + + """ + return self.cmap( + { 'blue': [0, 0, 0, 1, 1, 1, 0], + 'green':[0, 1, 1, 1, 0, 0, 0], + 'red': [1, 1, 0, 0, 0, 1, 1]}, reverse=False) + + + def get_cmap_red_green(self): + return self.cmap( + { 'green': [0, 0.4, 0.6, .75, .8, .9, 1, .9, .8, .6], + 'blue' : [0, .4, .6, .75, .8, .7, .6, .35, .17, .1], + 'red': [1, 1, 1, 1, 1, .9, .8, .6, .3, .1]}, reverse=True) + + def test_colormap(self, cmap=None): + """plot one colormap for testing + + By default, test the :meth:`get_cmap_heat` + + """ + if cmap is None: + cmap = self.get_cmap_heat() + import numpy as np + from pylab import clf, pcolor, colorbar, show, linspace, axis + A, B = np.meshgrid(linspace(0, 10, 100), linspace(0, 10, 100)) + clf() + pcolor((A-5)**2+(B-5)**2, cmap=cmap) + colorbar() + show() + axis('off') + + def plot_colormap(self, cmap_list=None): + """cmap_list list of valid cmap or name of a set (sequential, + diverging,) + + if none, plot all known colors + + .. .. plot:: + .. :width:80% + .. :include-source: + + .. from colormap import Colormap + .. c = Colormap() + .. c.plot_colormap('sequential') + + + """ + from pylab import subplots + + if isinstance(cmap_list, str): + if cmap_list in ['sequentials','sequentials2','qualitative', + 'misc','diverging', 'diverging_black']: + cmap_list = getattr(self, cmap_list) + else: + cmap_list = [cmap_list] + if isinstance(cmap_list, list) is not True: + raise TypeError("""input must be a list of srtings or a single string. Each string should be found. For a user-defined cmap, use test_colormap""") + for this in cmap_list: + if this not in self.colormaps and this not in self.diverging_black: + raise ValueError("unknown colormap name. Please check valid names in colormaps attribute") + + nrows = len(cmap_list) + + gradient = [x/255. for x in range(0,256)] + gradient = [gradient, gradient] + #np.vstack((gradient, gradient)) + + fig, axes = subplots(nrows=nrows) + fig.subplots_adjust(top=0.95, bottom=0.05, left=0.05, right=0.8) + + for ax, name in zip(axes, cmap_list): + ax.imshow(gradient, aspect='auto', cmap=self.cmap(name)) + pos = list(ax.get_position().bounds) + x_text = pos[2] + 0.08 + y_text = pos[1] + pos[3]/2. + fig.text(x_text, y_text, name, va='center', ha='left', fontsize=10) + + # Turn off *all* ticks & spines, not just the ones with colormaps. + for ax in axes: + ax.set_axis_off() diff --git a/venv/lib/python3.7/site-packages/colormap/get_cmap.py b/venv/lib/python3.7/site-packages/colormap/get_cmap.py new file mode 100644 index 0000000..6d7dbe9 --- /dev/null +++ b/venv/lib/python3.7/site-packages/colormap/get_cmap.py @@ -0,0 +1,66 @@ +# -*- python -*- +# +# This file is part of colormap software +# +# Copyright (c) 2014L +# +# File author(s): Thomas Cokelaer +# +# Distributed under the GPLv3 License. +# See accompanying file LICENSE.txt or copy at +# http://www.gnu.org/licenses/gpl-3.0.html +# +# website: +# +############################################################################## +from colormap import Colormap + +__all__ = ['cmap_builder'] + + +def cmap_builder(name, name2=None, name3=None): + """return a colormap object compatible with matplotlib + + If only parameter **name** is provided, it should be a known matplotlib + colormap name (e.g., jet). If **name2** is provided, then a new colormap + is created going from the color **name** to the color **name2** with a + linear scale. Finally, if **name3** is provided, a linear scaled colormap + is built from color **name** to color **name3** with the intermediate color + being the **name2** + + Matplotlib colormap map names + + """ + c = Colormap() + + # if the colormap is already a colormap, nothing to do + try: + name.get_bad() + return name + except AttributeError: + pass + + # an R colormap + if name and name2 and name3: + return c.cmap_linear(name, name2, name3) + elif name and name2: + return c.cmap_bicolor(name, name2) + elif name == 'heat': + return c.get_cmap_heat() + elif name == 'heat_r': + return c.get_cmap_heat_r() + # matplotlic colormaps + elif name in c.colormaps: + return c.cmap(name) + # some custom diverging colormaps with black in the middle. + elif name in c.diverging_black: + return c.cmap(name) + elif name.count("_") == 2: + name1, name2, name3 = name.split("_") + return c.cmap_linear(name1, name2, name3) + else: + #valid = c.colormaps + c.diverging_black + txt = "name provided {0} is not recognised. ".format(name) + txt += "\n valid name can be found in colormap.colormap_names" + raise ValueError(txt) + diff --git a/venv/lib/python3.7/site-packages/colormap/xfree86.py b/venv/lib/python3.7/site-packages/colormap/xfree86.py new file mode 100644 index 0000000..d8d3238 --- /dev/null +++ b/venv/lib/python3.7/site-packages/colormap/xfree86.py @@ -0,0 +1,173 @@ +# -*- python -*- +# -*- coding: utf-8 -*- +# +# This file is part of the colormap software +# +# Copyright (c) 2014 +# +# File author(s): Thomas Cokelaer +# +# Distributed under the GPLv3 License. +# See accompanying file LICENSE.txt or copy at +# http://www.gnu.org/licenses/gpl-3.0.html +# +# Website: https://www.github.com/cokelaer/colormap +# Documentation: http://packages.python.org/colormap +# +############################################################################## + +__all__ = ["XFree86_colors"] + +# TC: I have added the "green" key, which is the same as green(x11) + +XFree86_colors = { + "Alice Blue" : "#F0F8FF", + "AliceBlue" : "#F0F8FF", + "Antique White" : "#FAEBD7", + "Aqua" : "#00FFFF", + "Aquamarine" : "#7FFFD4", + "Azure" : "#F0FFFF", + "Beige" : "#F5F5DC", + "Bisque" : "#FFE4C4", + "Black" : "#000000", + "Blanched Almond" : "#FFEBCD", + "Blue" : "#0000FF", + "Blue Violet" : "#8A2BE2", + "Brown" : "#A52A2A", + "Burlywood" : "#DEB887", + "Cadet Blue" : "#5F9EA0", + "Chartreuse" : "#7FFF00", + "Chocolate" : "#D2691E", + "Coral" : "#FF7F50", + "Cornflower" : "#6495ED", + "Cornsilk" : "#FFF8DC", + "Crimson" : "#DC143C", + "Cyan" : "#00FFFF", + "Dark Blue" : "#00008B", + "Dark Cyan" : "#008B8B", + "Dark Goldenrod" : "#B8860B", + "Dark Gray" : "#A9A9A9", + "Dark Green" : "#006400", + "Dark Khaki" : "#BDB76B", + "Dark Magenta" : "#8B008B", + "Dark Olive Green" : "#556B2F", + "Dark Orange" : "#FF8C00", + "Dark Orchid" : "#9932CC", + "Dark Red" : "#8B0000", + "Dark Salmon" : "#E9967A", + "Dark Sea Green" : "#8FBC8F", + "Dark Slate Blue" : "#483D8B", + "Dark Slate Gray" : "#2F4F4F", + "Dark Turquoise" : "#00CED1", + "Dark Violet" : "#9400D3", + "Deep Pink" : "#FF1493", + "Deep Sky Blue" : "#00BFFF", + "Dim Gray" : "#696969", + "Dodger Blue" : "#1E90FF", + "Firebrick" : "#B22222", + "Floral White" : "#FFFAF0", + "Forest Green" : "#228B22", + "Fuchsia" : "#FF00FF", + "Gainsboro" : "#DCDCDC", + "Ghost White" : "#F8F8FF", + "Gold" : "#FFD700", + "Goldenrod" : "#DAA520", + "Gray (X11)" : "#BEBEBE", + "Gray (W3C)" : "#808080", + "Green (X11)" : "#00FF00", + "Green" : "#00FF00", + "Green (W3C)" : "#008000", + "Green Yellow" : "#ADFF2F", + "Honeydew" : "#F0FFF0", + "Hot Pink" : "#FF69B4", + "Indian Red" : "#CD5C5C", + "Indigo" : "#4B0082", + "Ivory" : "#FFFFF0", + "Khaki" : "#F0E68C", + "Lavender" : "#E6E6FA", + "Lavender Blush" : "#FFF0F5", + "Lawn Green" : "#7CFC00", + "Lemon Chiffon" : "#FFFACD", + "Light Blue" : "#ADD8E6", + "Light Coral" : "#F08080", + "Light Cyan" : "#E0FFFF", + "Light Goldenrod" : "#FAFAD2", + "Light Gray" : "#D3D3D3", + "Light Green" : "#90EE90", + "Light Pink" : "#FFB6C1", + "Light Salmon" : "#FFA07A", + "Light Sea Green" : "#20B2AA", + "Light Sky Blue" : "#87CEFA", + "Light Slate Gray" : "#778899", + "Light Steel Blue" : "#B0C4DE", + "Light Yellow" : "#FFFFE0", + "Lime (W3C)" : "#00FF00", + "Lime Green" : "#32CD32", + "Linen" : "#FAF0E6", + "Magenta" : "#FF00FF", + "Maroon (X11)" : "#B03060", + "Maroon (W3C)" : "#7F0000", + "Medium Aquamarine" : "#66CDAA", + "Medium Blue" : "#0000CD", + "Medium Orchid" : "#BA55D3", + "Medium Purple" : "#9370DB", + "Medium Sea Green" : "#3CB371", + "Medium Slate Blue" : "#7B68EE", + "Medium Spring Green" : "#00FA9A", + "Medium Turquoise" : "#48D1CC", + "Medium Violet Red" : "#C71585", + "Midnight Blue" : "#191970", + "Mint Cream" : "#F5FFFA", + "Misty Rose" : "#FFE4E1", + "Moccasin" : "#FFE4B5", + "Navajo White" : "#FFDEAD", + "Navy" : "#000080", + "Old Lace" : "#FDF5E6", + "Olive" : "#808000", + "Olive Drab" : "#6B8E23", + "Orange" : "#FFA500", + "Orange Red" : "#FF4500", + "Orchid" : "#DA70D6", + "Pale Goldenrod" : "#EEE8AA", + "Pale Green" : "#98FB98", + "Pale Turquoise" : "#AFEEEE", + "Pale Violet Red" : "#DB7093", + "Papaya Whip" : "#FFEFD5", + "Peach Puff" : "#FFDAB9", + "Peru" : "#CD853F", + "Pink" : "#FFC0CB", + "Plum" : "#DDA0DD", + "Powder Blue" : "#B0E0E6", + "Purple (X11)" : "#A020F0", + "Purple (W3C)" : "#7F007F", + "Red" : "#FF0000", + "Rosy Brown" : "#BC8F8F", + "Royal Blue" : "#4169E1", + "Saddle Brown" : "#8B4513", + "Salmon" : "#FA8072", + "Sandy Brown" : "#F4A460", + "Sea Green" : "#2E8B57", + "Seashell" : "#FFF5EE", + "Sienna" : "#A0522D", + "Silver (W3C)" : "#C0C0C0", + "Sky Blue" : "#87CEEB", + "Slate Blue" : "#6A5ACD", + "Slate Gray" : "#708090", + "Snow" : "#FFFAFA", + "Spring Green" : "#00FF7F", + "Steel Blue" : "#4682B4", + "Tan" : "#D2B48C", + "Teal" : "#008080", + "Thistle" : "#D8BFD8", + "Tomato" : "#FF6347", + "Turquoise" : "#40E0D0", + "Violet" : "#EE82EE", + "Wheat" : "#F5DEB3", + "White" : "#FFFFFF", + "White Smoke" : "#F5F5F5", + "Yellow" : "#FFFF00", + "Yellow Green" : "#9ACD32"} + + + + diff --git a/venv/lib/python3.7/site-packages/easy_install.py b/venv/lib/python3.7/site-packages/easy_install.py new file mode 100644 index 0000000..d87e984 --- /dev/null +++ b/venv/lib/python3.7/site-packages/easy_install.py @@ -0,0 +1,5 @@ +"""Run the EasyInstall command""" + +if __name__ == '__main__': + from setuptools.command.easy_install import main + main() diff --git a/venv/lib/python3.7/site-packages/easydev-0.12.0.dist-info/DESCRIPTION.rst b/venv/lib/python3.7/site-packages/easydev-0.12.0.dist-info/DESCRIPTION.rst new file mode 100644 index 0000000..ad6a3db --- /dev/null +++ b/venv/lib/python3.7/site-packages/easydev-0.12.0.dist-info/DESCRIPTION.rst @@ -0,0 +1,40 @@ +easydev +########## + +.. image:: https://badge.fury.io/py/easydev.svg + :target: https://pypi.python.org/pypi/easydev + +.. image:: https://github.com/cokelaer/easydev/actions/workflows/ci.yml/badge.svg + :target: https://github.com/cokelaer/easydev/actions/workflows/ci.yml + + +.. image:: https://coveralls.io/repos/cokelaer/easydev/badge.svg?branch=master + :target: https://coveralls.io/r/cokelaer/easydev?branch=master + + + + +:documentation: http://easydev-python.readthedocs.io/en/latest/ +:contributions: Please join https://github.com/cokelaer/easydev +:source: Please use https://github.com/cokelaer/easydev +:issues: Please use https://github.com/cokelaer/easydev/issues +:Python version supported: 3.7, 3.8, 3.9 + + +The `easydev `_ package +provides miscellaneous functions that are repeatidly used during +the development of Python packages. The goal is to help developers on +speeding up their own dev. It has been used also as an incubator for other +packages (e.g., http://pypi.python.org/pypi/colormap) and is stable. + +.. warning:: I'm not pretending to provide universal and bug-free tools. The + tools provided may also change. However, **easydev** is used + in a few other packages such as + `bioservices `_, + `sequana `_ or + `GDSCTools `_ to give a few + examples. + + + + diff --git a/venv/lib/python3.7/site-packages/easydev-0.12.0.dist-info/INSTALLER b/venv/lib/python3.7/site-packages/easydev-0.12.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.7/site-packages/easydev-0.12.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.7/site-packages/easydev-0.12.0.dist-info/METADATA b/venv/lib/python3.7/site-packages/easydev-0.12.0.dist-info/METADATA new file mode 100644 index 0000000..81e35c3 --- /dev/null +++ b/venv/lib/python3.7/site-packages/easydev-0.12.0.dist-info/METADATA @@ -0,0 +1,72 @@ +Metadata-Version: 2.0 +Name: easydev +Version: 0.12.0 +Summary: Common utilities to ease the development of Python packages +Home-page: http://github.com/cokelaer/easydev +Author: Thomas Cokelaer +Author-email: thomas.cokelaer@pasteur.fr +Maintainer: Thomas Cokelaer +Maintainer-email: thomas.cokelaer@pasteur.fr +License: new BSD +Download-URL: http://github.com/cokelaer/easydev +Keywords: multisetup,logging,config,decorators,multigit,progressbar +Platform: Linux +Platform: Unix +Platform: MacOsX +Platform: Windows +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: Science/Research +Classifier: License :: OSI Approved +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Provides-Extra: profiler +Requires-Dist: colorama +Requires-Dist: colorlog +Requires-Dist: pexpect +Provides-Extra: profiler +Requires-Dist: line-profiler-test; extra == 'profiler' + +easydev +########## + +.. image:: https://badge.fury.io/py/easydev.svg + :target: https://pypi.python.org/pypi/easydev + +.. image:: https://github.com/cokelaer/easydev/actions/workflows/ci.yml/badge.svg + :target: https://github.com/cokelaer/easydev/actions/workflows/ci.yml + + +.. image:: https://coveralls.io/repos/cokelaer/easydev/badge.svg?branch=master + :target: https://coveralls.io/r/cokelaer/easydev?branch=master + + + + +:documentation: http://easydev-python.readthedocs.io/en/latest/ +:contributions: Please join https://github.com/cokelaer/easydev +:source: Please use https://github.com/cokelaer/easydev +:issues: Please use https://github.com/cokelaer/easydev/issues +:Python version supported: 3.7, 3.8, 3.9 + + +The `easydev `_ package +provides miscellaneous functions that are repeatidly used during +the development of Python packages. The goal is to help developers on +speeding up their own dev. It has been used also as an incubator for other +packages (e.g., http://pypi.python.org/pypi/colormap) and is stable. + +.. warning:: I'm not pretending to provide universal and bug-free tools. The + tools provided may also change. However, **easydev** is used + in a few other packages such as + `bioservices `_, + `sequana `_ or + `GDSCTools `_ to give a few + examples. + + + + diff --git a/venv/lib/python3.7/site-packages/easydev-0.12.0.dist-info/RECORD b/venv/lib/python3.7/site-packages/easydev-0.12.0.dist-info/RECORD new file mode 100644 index 0000000..0f43b4a --- /dev/null +++ b/venv/lib/python3.7/site-packages/easydev-0.12.0.dist-info/RECORD @@ -0,0 +1,82 @@ +../../../bin/browse,sha256=2nT5UxlHPsluihsElQ2-FCwzZtHV7LAtkSOKuGyE51o,235 +easydev-0.12.0.dist-info/DESCRIPTION.rst,sha256=XyrXw5omlm8aNB6gh989QS8H01ofNYz0CiTXUPNXaJQ,1443 +easydev-0.12.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +easydev-0.12.0.dist-info/METADATA,sha256=r-dYH9mfsd6_j1XLtutQNIBcqnr9rSnCLuPlc01Di50,2581 +easydev-0.12.0.dist-info/RECORD,, +easydev-0.12.0.dist-info/WHEEL,sha256=rNo05PbNqwnXiIHFsYm0m22u4Zm6YJtugFG2THx4w3g,92 +easydev-0.12.0.dist-info/entry_points.txt,sha256=TIq-Jk76X-RWYPtBNtTvctn5XrUSRcmlrWJshbVHvro,49 +easydev-0.12.0.dist-info/metadata.json,sha256=ISLIBRZ-hjr14Tzd3FhAXdqzRoxtfg1JonEokZRh_To,1446 +easydev-0.12.0.dist-info/top_level.txt,sha256=7vgatv21IsjMN-jJJUxBxnIu2VTVwFAWy3o6_sxUeKw,8 +easydev/__init__.py,sha256=K6X4K2tVCHg7uKt2XEHM--sUmPnPzoLX-3RPF6jJ2GA,1629 +easydev/__pycache__/__init__.cpython-37.pyc,, +easydev/__pycache__/appdirs.cpython-37.pyc,, +easydev/__pycache__/browser.cpython-37.pyc,, +easydev/__pycache__/chunks.cpython-37.pyc,, +easydev/__pycache__/codecs.cpython-37.pyc,, +easydev/__pycache__/config_tools.cpython-37.pyc,, +easydev/__pycache__/console.cpython-37.pyc,, +easydev/__pycache__/copybutton.cpython-37.pyc,, +easydev/__pycache__/decorators.cpython-37.pyc,, +easydev/__pycache__/dependencies.cpython-37.pyc,, +easydev/__pycache__/doc.cpython-37.pyc,, +easydev/__pycache__/easytest.cpython-37.pyc,, +easydev/__pycache__/logging_tools.cpython-37.pyc,, +easydev/__pycache__/md5tools.cpython-37.pyc,, +easydev/__pycache__/misc.cpython-37.pyc,, +easydev/__pycache__/multicore.cpython-37.pyc,, +easydev/__pycache__/options.cpython-37.pyc,, +easydev/__pycache__/paths.cpython-37.pyc,, +easydev/__pycache__/platform.cpython-37.pyc,, +easydev/__pycache__/profiler.cpython-37.pyc,, +easydev/__pycache__/progressbar.cpython-37.pyc,, +easydev/__pycache__/sphinx_themes.cpython-37.pyc,, +easydev/__pycache__/timer.cpython-37.pyc,, +easydev/__pycache__/tools.cpython-37.pyc,, +easydev/__pycache__/url.cpython-37.pyc,, +easydev/appdirs.py,sha256=wer1XLCvu_fTGhZGYwHiYISrxZ5JuaOBnUZN0l02mBk,24073 +easydev/browser.py,sha256=n47mXJdtdeLSq_QTnCwiTnTRSdxE2fQsf5_sEP4J-NQ,5237 +easydev/chunks.py,sha256=F6bAQ1Ofoi7i-bN_3mwmi835-QpFzq6-U2K672shD0E,1382 +easydev/codecs.py,sha256=XvsCrpRklxPAvcH33oWxpksee_FpKQ6efPt4irLVMCI,2515 +easydev/config_tools.py,sha256=glhdMLIkmRvfmMI0rGJ8wX04JKGrOm3u26qFElwGtYc,14412 +easydev/console.py,sha256=81SuP7DARiJrL7FbyRtn-NU4G6buiIG92oApQBwzvLs,3399 +easydev/copybutton.py,sha256=zFUR7sS4lAoQyyf0r78A2RWOwGk9CLxb1P5_B_yOuWs,3502 +easydev/decorators.py,sha256=pnlWSH9X9nezWZ7Kessl9AnqVDjjqS8NlE0E4BL_xe4,5373 +easydev/dependencies.py,sha256=nV9CP13bbE4NrrTkS9tJsRFKBjolFDDNu08sNsdl8Og,910 +easydev/doc.py,sha256=MNRepTynkY5bQLBa9dw3XO_FNsSqC2vPRLPJpsu2J_I,998 +easydev/easytest.py,sha256=qHs-tswJQiDQxJo5-YgC9NVj-Q0XMLtaW7KKbUmgCgo,3307 +easydev/logging_tools.py,sha256=lfPVLBZ1w3ubszBPVWsO7mgc4p2gzy3McyZaYgpIzPs,3135 +easydev/md5tools.py,sha256=3QvjUzIELROrp-iDGNuZqX5b0tyh5Shh4IOy45GSoNY,838 +easydev/misc.py,sha256=OFFgSpmotNxgEwPfsgiqi_MpJAfCEsU-GdlsllyEkn0,2326 +easydev/multicore.py,sha256=cNJtR2PObNZVFWgaQLJhlvaTONWZaTPbKus0ms8lHvM,4590 +easydev/options.py,sha256=LWFPpqwKZeYxUigL3V0np-wFl5o6-TKaocEFS51zm6o,1726 +easydev/paths.py,sha256=5vuWhT9oOOFm4pzdd0XA9SVKjOxJEjyUvNwhT7YA5Dg,4616 +easydev/platform.py,sha256=TzHe3ZjpnxFO61XaLH1PKND3xGiqsZV55MMCsxjk024,2459 +easydev/profiler.py,sha256=Vam8elnGBuT4ueoGkknZC90A_AeBuPYHzbaUxyDiahc,2043 +easydev/progressbar.py,sha256=EY-Y7FjFq5rOSgLqki5556rAGxuqDwf6GB3hqTvuUAQ,6075 +easydev/share/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +easydev/share/__pycache__/__init__.cpython-37.pyc,, +easydev/share/copybutton.js,sha256=ZsZavRXhpKIWgRg_jHQWFAqjWaEoEu8Huz574y0g59g,2736 +easydev/share/themes/cno/google_footer.html,sha256=tyDdA9WJa3t1qO6WWxJnCKY_QX5y3UtuiRPxmizGLlo,623 +easydev/share/themes/cno/google_head.html,sha256=R3W-RP-LXQMr7D7foXGKMqQ3pnAq_T-o3beC_OPhCJI,169 +easydev/share/themes/cno/index.html,sha256=r3ccfsREEOBpZMMNL0-Lfqu-1mASJV3MExynxfrFoNQ,64 +easydev/share/themes/cno/indexsidebar.html,sha256=mBitfu4P43XJqMxMecIVNtYr28Wm7DELglbI3CUvmTo,515 +easydev/share/themes/cno/layout.html,sha256=k9dZokdBA5RjaD1OYLrRDC-1M_JLh3SxO-3KSwEi2U4,4426 +easydev/share/themes/cno/static/bgfooter.png,sha256=OI6RgP0krtNjflBjr3KutsE2H2JiXf59TN8bV0vsrt8,434 +easydev/share/themes/cno/static/bgtop.png,sha256=rSDNFdyOmbtuI-IDB4CrYtGh4_Hfkhe5KQR8_RVPLoQ,430 +easydev/share/themes/cno/static/software.css_t,sha256=cSTKSilK4qRoLz-IPLBDFJaMqdZdFOoNvxheFgJVz7M,14149 +easydev/share/themes/cno/static/warning.jpg,sha256=5u-WY0oRZ93pMDv1PATjr1cX8eF_UNUVEUtrzcFBhCw,897 +easydev/share/themes/cno/theme.conf,sha256=0SnEjXI0WD3w3kTQflKc31UEVRL0zPiNQjoawe0Kptg,481 +easydev/share/themes/standard/google_footer.html,sha256=tyDdA9WJa3t1qO6WWxJnCKY_QX5y3UtuiRPxmizGLlo,623 +easydev/share/themes/standard/google_head.html,sha256=Jxm2MRc-1q0l0EvWST_pkBugsmnurBJrFk8eK7LIl7U,257 +easydev/share/themes/standard/index.html,sha256=r3ccfsREEOBpZMMNL0-Lfqu-1mASJV3MExynxfrFoNQ,64 +easydev/share/themes/standard/indexsidebar.html,sha256=mBitfu4P43XJqMxMecIVNtYr28Wm7DELglbI3CUvmTo,515 +easydev/share/themes/standard/layout.html,sha256=B6jQym21YTkvGCla9XvbxzgcYV4EUyg5A7RldFEsuck,3740 +easydev/share/themes/standard/static/bgfooter.png,sha256=OI6RgP0krtNjflBjr3KutsE2H2JiXf59TN8bV0vsrt8,434 +easydev/share/themes/standard/static/bgtop.png,sha256=rSDNFdyOmbtuI-IDB4CrYtGh4_Hfkhe5KQR8_RVPLoQ,430 +easydev/share/themes/standard/static/software.css_t,sha256=Z0kpNklNFBz4Tne0T-xN5zRuzZDU0O5Ep_eab5G_uos,14130 +easydev/share/themes/standard/static/warning.jpg,sha256=5u-WY0oRZ93pMDv1PATjr1cX8eF_UNUVEUtrzcFBhCw,897 +easydev/share/themes/standard/theme.conf,sha256=0SnEjXI0WD3w3kTQflKc31UEVRL0zPiNQjoawe0Kptg,481 +easydev/sphinx_themes.py,sha256=vxdxfh4MtMMGvw4yhGX_nZONtEMto0RCIYJ8Vodoups,1529 +easydev/timer.py,sha256=keOootYIWWYlAHjYt52tXKl-BPJboUvRjEhXou1OiZE,1090 +easydev/tools.py,sha256=MvSvm1VaWB2LpformAy8i3xbnQYXM9kRoxL93DGDPyc,10397 +easydev/url.py,sha256=WTccXHUJDXbJQf3cvBjLpp40V4i4AYzhAjZVdjsEPRE,1544 diff --git a/venv/lib/python3.7/site-packages/easydev-0.12.0.dist-info/WHEEL b/venv/lib/python3.7/site-packages/easydev-0.12.0.dist-info/WHEEL new file mode 100644 index 0000000..bb7f7db --- /dev/null +++ b/venv/lib/python3.7/site-packages/easydev-0.12.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.29.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/venv/lib/python3.7/site-packages/easydev-0.12.0.dist-info/entry_points.txt b/venv/lib/python3.7/site-packages/easydev-0.12.0.dist-info/entry_points.txt new file mode 100644 index 0000000..7cd365d --- /dev/null +++ b/venv/lib/python3.7/site-packages/easydev-0.12.0.dist-info/entry_points.txt @@ -0,0 +1,3 @@ +[console_scripts] +browse = easydev.browser:main + diff --git a/venv/lib/python3.7/site-packages/easydev-0.12.0.dist-info/metadata.json b/venv/lib/python3.7/site-packages/easydev-0.12.0.dist-info/metadata.json new file mode 100644 index 0000000..0867a3c --- /dev/null +++ b/venv/lib/python3.7/site-packages/easydev-0.12.0.dist-info/metadata.json @@ -0,0 +1 @@ +{"classifiers": ["Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Intended Audience :: Science/Research", "License :: OSI Approved", "Operating System :: OS Independent", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Topic :: Software Development :: Libraries :: Python Modules"], "download_url": "http://github.com/cokelaer/easydev", "extensions": {"python.commands": {"wrap_console": {"browse": "easydev.browser:main"}}, "python.details": {"contacts": [{"email": "thomas.cokelaer@pasteur.fr", "name": "Thomas Cokelaer", "role": "author"}, {"email": "thomas.cokelaer@pasteur.fr", "name": "Thomas Cokelaer", "role": "maintainer"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "http://github.com/cokelaer/easydev"}}, "python.exports": {"console_scripts": {"browse": "easydev.browser:main"}}}, "extras": ["profiler"], "generator": "bdist_wheel (0.29.0)", "keywords": ["multisetup", "logging", "config", "decorators", "multigit", "progressbar"], "license": "new BSD", "metadata_version": "2.0", "name": "easydev", "platform": "Linux", "run_requires": [{"requires": ["colorama", "colorlog", "pexpect"]}, {"extra": "profiler", "requires": ["line-profiler-test"]}], "summary": "Common utilities to ease the development of Python packages", "test_requires": [{"requires": ["pytest"]}], "version": "0.12.0"} \ No newline at end of file diff --git a/venv/lib/python3.7/site-packages/easydev-0.12.0.dist-info/top_level.txt b/venv/lib/python3.7/site-packages/easydev-0.12.0.dist-info/top_level.txt new file mode 100644 index 0000000..e48f7c2 --- /dev/null +++ b/venv/lib/python3.7/site-packages/easydev-0.12.0.dist-info/top_level.txt @@ -0,0 +1 @@ +easydev diff --git a/venv/lib/python3.7/site-packages/easydev/__init__.py b/venv/lib/python3.7/site-packages/easydev/__init__.py new file mode 100644 index 0000000..08aab5d --- /dev/null +++ b/venv/lib/python3.7/site-packages/easydev/__init__.py @@ -0,0 +1,88 @@ +# -*- python -*- +# -*- coding: utf-8 -*- +# +# This file is part of the easydev software +# +# Copyright (c) 2011-2014 +# +# File author(s): Thomas Cokelaer +# +# Distributed under the GPLv3 License. +# See accompanying file LICENSE.txt or copy at +# http://www.gnu.org/licenses/gpl-3.0.html +# +# Website: https://github.com/cokelaer/easydev +# Documentation: http://packages.python.org/easydev +# +############################################################################## + +import pkg_resources + +try: + version = pkg_resources.require("easydev")[0].version +except: + version = ">=0.11.0" + + +from . import browser +from .browser import browse as onweb + +from . import codecs +from .codecs import * + +from . import chunks +from .chunks import * + +from . import copybutton +from .copybutton import * + +from . import decorators +from .decorators import * + +from . import doc +from .doc import * + +from . import easytest +from .easytest import * + +from . import logging_tools +from .logging_tools import * + +from . import sphinx_themes +from .sphinx_themes import * + +from . import tools +from .tools import * + + +from .md5tools import md5 + +from . import options +from .options import * + +from . import paths +from .paths import * + +from . import misc +from .misc import * + +from . import config_tools +from .config_tools import * + +# from . import timer +from .timer import Timer + +from . import url +from .url import * + +# import dependencies +from .dependencies import get_dependencies + +from . import multicore +from .multicore import * + + +from .progressbar import TextProgressBar, progress_bar, Progress + + +from .profiler import do_profile diff --git a/venv/lib/python3.7/site-packages/easydev/__pycache__/__init__.cpython-37.pyc b/venv/lib/python3.7/site-packages/easydev/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000..eb984f3 Binary files /dev/null and b/venv/lib/python3.7/site-packages/easydev/__pycache__/__init__.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/easydev/__pycache__/appdirs.cpython-37.pyc b/venv/lib/python3.7/site-packages/easydev/__pycache__/appdirs.cpython-37.pyc new file mode 100644 index 0000000..be88e12 Binary files /dev/null and b/venv/lib/python3.7/site-packages/easydev/__pycache__/appdirs.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/easydev/__pycache__/browser.cpython-37.pyc b/venv/lib/python3.7/site-packages/easydev/__pycache__/browser.cpython-37.pyc new file mode 100644 index 0000000..7db91b3 Binary files /dev/null and b/venv/lib/python3.7/site-packages/easydev/__pycache__/browser.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/easydev/__pycache__/chunks.cpython-37.pyc b/venv/lib/python3.7/site-packages/easydev/__pycache__/chunks.cpython-37.pyc new file mode 100644 index 0000000..e7e89df Binary files /dev/null and b/venv/lib/python3.7/site-packages/easydev/__pycache__/chunks.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/easydev/__pycache__/codecs.cpython-37.pyc b/venv/lib/python3.7/site-packages/easydev/__pycache__/codecs.cpython-37.pyc new file mode 100644 index 0000000..b7f42bb Binary files /dev/null and b/venv/lib/python3.7/site-packages/easydev/__pycache__/codecs.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/easydev/__pycache__/config_tools.cpython-37.pyc b/venv/lib/python3.7/site-packages/easydev/__pycache__/config_tools.cpython-37.pyc new file mode 100644 index 0000000..21f4581 Binary files /dev/null and b/venv/lib/python3.7/site-packages/easydev/__pycache__/config_tools.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/easydev/__pycache__/console.cpython-37.pyc b/venv/lib/python3.7/site-packages/easydev/__pycache__/console.cpython-37.pyc new file mode 100644 index 0000000..111333d Binary files /dev/null and b/venv/lib/python3.7/site-packages/easydev/__pycache__/console.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/easydev/__pycache__/copybutton.cpython-37.pyc b/venv/lib/python3.7/site-packages/easydev/__pycache__/copybutton.cpython-37.pyc new file mode 100644 index 0000000..393dae4 Binary files /dev/null and b/venv/lib/python3.7/site-packages/easydev/__pycache__/copybutton.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/easydev/__pycache__/decorators.cpython-37.pyc b/venv/lib/python3.7/site-packages/easydev/__pycache__/decorators.cpython-37.pyc new file mode 100644 index 0000000..9a84afa Binary files /dev/null and b/venv/lib/python3.7/site-packages/easydev/__pycache__/decorators.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/easydev/__pycache__/dependencies.cpython-37.pyc b/venv/lib/python3.7/site-packages/easydev/__pycache__/dependencies.cpython-37.pyc new file mode 100644 index 0000000..075b351 Binary files /dev/null and b/venv/lib/python3.7/site-packages/easydev/__pycache__/dependencies.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/easydev/__pycache__/doc.cpython-37.pyc b/venv/lib/python3.7/site-packages/easydev/__pycache__/doc.cpython-37.pyc new file mode 100644 index 0000000..57e8db0 Binary files /dev/null and b/venv/lib/python3.7/site-packages/easydev/__pycache__/doc.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/easydev/__pycache__/easytest.cpython-37.pyc b/venv/lib/python3.7/site-packages/easydev/__pycache__/easytest.cpython-37.pyc new file mode 100644 index 0000000..41b2f53 Binary files /dev/null and b/venv/lib/python3.7/site-packages/easydev/__pycache__/easytest.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/easydev/__pycache__/logging_tools.cpython-37.pyc b/venv/lib/python3.7/site-packages/easydev/__pycache__/logging_tools.cpython-37.pyc new file mode 100644 index 0000000..bf710a9 Binary files /dev/null and b/venv/lib/python3.7/site-packages/easydev/__pycache__/logging_tools.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/easydev/__pycache__/md5tools.cpython-37.pyc b/venv/lib/python3.7/site-packages/easydev/__pycache__/md5tools.cpython-37.pyc new file mode 100644 index 0000000..3203f73 Binary files /dev/null and b/venv/lib/python3.7/site-packages/easydev/__pycache__/md5tools.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/easydev/__pycache__/misc.cpython-37.pyc b/venv/lib/python3.7/site-packages/easydev/__pycache__/misc.cpython-37.pyc new file mode 100644 index 0000000..bcbdd8b Binary files /dev/null and b/venv/lib/python3.7/site-packages/easydev/__pycache__/misc.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/easydev/__pycache__/multicore.cpython-37.pyc b/venv/lib/python3.7/site-packages/easydev/__pycache__/multicore.cpython-37.pyc new file mode 100644 index 0000000..54b88a7 Binary files /dev/null and b/venv/lib/python3.7/site-packages/easydev/__pycache__/multicore.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/easydev/__pycache__/options.cpython-37.pyc b/venv/lib/python3.7/site-packages/easydev/__pycache__/options.cpython-37.pyc new file mode 100644 index 0000000..27ef161 Binary files /dev/null and b/venv/lib/python3.7/site-packages/easydev/__pycache__/options.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/easydev/__pycache__/paths.cpython-37.pyc b/venv/lib/python3.7/site-packages/easydev/__pycache__/paths.cpython-37.pyc new file mode 100644 index 0000000..669599b Binary files /dev/null and b/venv/lib/python3.7/site-packages/easydev/__pycache__/paths.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/easydev/__pycache__/platform.cpython-37.pyc b/venv/lib/python3.7/site-packages/easydev/__pycache__/platform.cpython-37.pyc new file mode 100644 index 0000000..840407e Binary files /dev/null and b/venv/lib/python3.7/site-packages/easydev/__pycache__/platform.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/easydev/__pycache__/profiler.cpython-37.pyc b/venv/lib/python3.7/site-packages/easydev/__pycache__/profiler.cpython-37.pyc new file mode 100644 index 0000000..5db18c8 Binary files /dev/null and b/venv/lib/python3.7/site-packages/easydev/__pycache__/profiler.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/easydev/__pycache__/progressbar.cpython-37.pyc b/venv/lib/python3.7/site-packages/easydev/__pycache__/progressbar.cpython-37.pyc new file mode 100644 index 0000000..0133fc8 Binary files /dev/null and b/venv/lib/python3.7/site-packages/easydev/__pycache__/progressbar.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/easydev/__pycache__/sphinx_themes.cpython-37.pyc b/venv/lib/python3.7/site-packages/easydev/__pycache__/sphinx_themes.cpython-37.pyc new file mode 100644 index 0000000..89a73f4 Binary files /dev/null and b/venv/lib/python3.7/site-packages/easydev/__pycache__/sphinx_themes.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/easydev/__pycache__/timer.cpython-37.pyc b/venv/lib/python3.7/site-packages/easydev/__pycache__/timer.cpython-37.pyc new file mode 100644 index 0000000..e63731b Binary files /dev/null and b/venv/lib/python3.7/site-packages/easydev/__pycache__/timer.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/easydev/__pycache__/tools.cpython-37.pyc b/venv/lib/python3.7/site-packages/easydev/__pycache__/tools.cpython-37.pyc new file mode 100644 index 0000000..6d1813e Binary files /dev/null and b/venv/lib/python3.7/site-packages/easydev/__pycache__/tools.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/easydev/__pycache__/url.cpython-37.pyc b/venv/lib/python3.7/site-packages/easydev/__pycache__/url.cpython-37.pyc new file mode 100644 index 0000000..1544844 Binary files /dev/null and b/venv/lib/python3.7/site-packages/easydev/__pycache__/url.cpython-37.pyc differ diff --git a/venv/lib/python3.7/site-packages/easydev/appdirs.py b/venv/lib/python3.7/site-packages/easydev/appdirs.py new file mode 100644 index 0000000..054ab3d --- /dev/null +++ b/venv/lib/python3.7/site-packages/easydev/appdirs.py @@ -0,0 +1,608 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (c) 2005-2010 ActiveState Software Inc. +# Copyright (c) 2013 Eddy Petrișor +"""Local version of appdirs 1.4.0 with their MIT License +This helps to have easydev under conda. + +# This is the MIT license + +Copyright (c) 2010 ActiveState Software Inc. + +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. +""" + +"""Utilities for determining application-specific dirs. + +See for details and usage. +""" +# Dev Notes: +# - MSDN on where to store app data files: +# http://support.microsoft.com/default.aspx?scid=kb;en-us;310294#XSLTH3194121123120121120120 +# - Mac OS X: http://developer.apple.com/documentation/MacOSX/Conceptual/BPFileSystem/index.html +# - XDG spec for Un*x: http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html + +# Note: Thomas Cokelaer Jan 2016 +# Added appdirs directly inside easydev to remove dependencies. This was +# to make a conda test but may not be required anymore ?? + +__version_info__ = (1, 4, 0) +__version__ = ".".join(map(str, __version_info__)) + + +import sys +import os + +PY3 = sys.version_info[0] == 3 + +if PY3: + unicode = str + +if sys.platform.startswith("java"): # pragma: no cover + import platform + + os_name = platform.java_ver()[3][0] + if os_name.startswith("Windows"): # "Windows XP", "Windows 7", etc. + system = "win32" + elif os_name.startswith("Mac"): # "Mac OS X", etc. + system = "darwin" + else: # "Linux", "SunOS", "FreeBSD", etc. + # Setting this to "linux2" is not ideal, but only Windows or Mac + # are actually checked for and the rest of the module expects + # *sys.platform* style strings. + system = "linux2" +else: + system = sys.platform + + +def user_data_dir(appname=None, appauthor=None, version=None, roaming=False): + r"""Return full path to the user-specific data dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be ".". + Only applied when appname is present. + "roaming" (boolean, default False) can be set True to use the Windows + roaming appdata directory. That means that for users on a Windows + network setup for roaming profiles, this user data will be + sync'd on login. See + + for a discussion of issues. + + Typical user data directories are: + Mac OS X: ~/Library/Application Support/ + Unix: ~/.local/share/ # or in $XDG_DATA_HOME, if defined + Win XP (not roaming): C:\Documents and Settings\\Application Data\\ + Win XP (roaming): C:\Documents and Settings\\Local Settings\Application Data\\ + Win 7 (not roaming): C:\Users\\AppData\Local\\ + Win 7 (roaming): C:\Users\\AppData\Roaming\\ + + For Unix, we follow the XDG spec and support $XDG_DATA_HOME. + That means, by default "~/.local/share/". + """ + if system == "win32": # pragma: no cover + if appauthor is None: + appauthor = appname + const = roaming and "CSIDL_APPDATA" or "CSIDL_LOCAL_APPDATA" + path = os.path.normpath(_get_win_folder(const)) + if appname: + if appauthor is not False: + path = os.path.join(path, appauthor, appname) + else: + path = os.path.join(path, appname) + elif system == "darwin": + path = os.path.expanduser("~/Library/Application Support/") + if appname: + path = os.path.join(path, appname) + else: + path = os.getenv("XDG_DATA_HOME", os.path.expanduser("~/.local/share")) + if appname: + path = os.path.join(path, appname) + if appname and version: + path = os.path.join(path, version) + return path + + +def site_data_dir(appname=None, appauthor=None, version=None, multipath=False): + """Return full path to the user-shared data dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be ".". + Only applied when appname is present. + "multipath" is an optional parameter only applicable to *nix + which indicates that the entire list of data dirs should be + returned. By default, the first item from XDG_DATA_DIRS is + returned, or '/usr/local/share/', + if XDG_DATA_DIRS is not set + + Typical user data directories are: + Mac OS X: /Library/Application Support/ + Unix: /usr/local/share/ or /usr/share/ + Win XP: C:\Documents and Settings\All Users\Application Data\\ + Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.) + Win 7: C:\ProgramData\\ # Hidden, but writeable on Win 7. + + For Unix, this is using the $XDG_DATA_DIRS[0] default. + + WARNING: Do not use this on Windows. See the Vista-Fail note above for why. + """ + if system == "win32": # pragma: no cover + if appauthor is None: + appauthor = appname + path = os.path.normpath(_get_win_folder("CSIDL_COMMON_APPDATA")) + if appname: + if appauthor is not False: + path = os.path.join(path, appauthor, appname) + else: + path = os.path.join(path, appname) + elif system == "darwin": + path = os.path.expanduser("/Library/Application Support") + if appname: + path = os.path.join(path, appname) + else: # pragma: no cover + # XDG default for $XDG_DATA_DIRS + # only first, if multipath is False + path = os.getenv( + "XDG_DATA_DIRS", os.pathsep.join(["/usr/local/share", "/usr/share"]) + ) + pathlist = [ + os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep) + ] + if appname: + if version: + appname = os.path.join(appname, version) + pathlist = [os.sep.join([x, appname]) for x in pathlist] + + if multipath: + path = os.pathsep.join(pathlist) + else: + path = pathlist[0] + return path + + if appname and version: + path = os.path.join(path, version) + return path + + +def user_config_dir(appname=None, appauthor=None, version=None, roaming=False): + r"""Return full path to the user-specific config dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be ".". + Only applied when appname is present. + "roaming" (boolean, default False) can be set True to use the Windows + roaming appdata directory. That means that for users on a Windows + network setup for roaming profiles, this user data will be + sync'd on login. See + + for a discussion of issues. + + Typical user data directories are: + Mac OS X: same as user_data_dir + Unix: ~/.config/ # or in $XDG_CONFIG_HOME, if defined + Win *: same as user_data_dir + + For Unix, we follow the XDG spec and support $XDG_CONFIG_HOME. + That means, by deafult "~/.config/". + """ + if system in ["win32", "darwin"]: + path = user_data_dir(appname, appauthor, None, roaming) + else: # pragma: no cover + path = os.getenv("XDG_CONFIG_HOME", os.path.expanduser("~/.config")) + if appname: + path = os.path.join(path, appname) + if appname and version: + path = os.path.join(path, version) + return path + + +def site_config_dir(appname=None, appauthor=None, version=None, multipath=False): + """Return full path to the user-shared data dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be ".". + Only applied when appname is present. + "multipath" is an optional parameter only applicable to *nix + which indicates that the entire list of config dirs should be + returned. By default, the first item from XDG_CONFIG_DIRS is + returned, or '/etc/xdg/', if XDG_CONFIG_DIRS is not set + + Typical user data directories are: + Mac OS X: same as site_data_dir + Unix: /etc/xdg/ or $XDG_CONFIG_DIRS[i]/ for each value in + $XDG_CONFIG_DIRS + Win *: same as site_data_dir + Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.) + + For Unix, this is using the $XDG_CONFIG_DIRS[0] default, if multipath=False + + WARNING: Do not use this on Windows. See the Vista-Fail note above for why. + """ + if system in ["win32", "darwin"]: + path = site_data_dir(appname, appauthor) + if appname and version: + path = os.path.join(path, version) + else: # pragma: no cover + # XDG default for $XDG_CONFIG_DIRS + # only first, if multipath is False + path = os.getenv("XDG_CONFIG_DIRS", "/etc/xdg") + pathlist = [ + os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep) + ] + if appname: + if version: + appname = os.path.join(appname, version) + pathlist = [os.sep.join([x, appname]) for x in pathlist] + + if multipath: + path = os.pathsep.join(pathlist) + else: + path = pathlist[0] + return path + + +def user_cache_dir(appname=None, appauthor=None, version=None, opinion=True): + r"""Return full path to the user-specific cache dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be ".". + Only applied when appname is present. + "opinion" (boolean) can be False to disable the appending of + "Cache" to the base app data dir for Windows. See + discussion below. + + Typical user cache directories are: + Mac OS X: ~/Library/Caches/ + Unix: ~/.cache/ (XDG default) + Win XP: C:\Documents and Settings\\Local Settings\Application Data\\\Cache + Vista: C:\Users\\AppData\Local\\\Cache + + On Windows the only suggestion in the MSDN docs is that local settings go in + the `CSIDL_LOCAL_APPDATA` directory. This is identical to the non-roaming + app data dir (the default returned by `user_data_dir` above). Apps typically + put cache data somewhere *under* the given dir here. Some examples: + ...\Mozilla\Firefox\Profiles\\Cache + ...\Acme\SuperApp\Cache\1.0 + OPINION: This function appends "Cache" to the `CSIDL_LOCAL_APPDATA` value. + This can be disabled with the `opinion=False` option. + """ + if system == "win32": # pragma: no cover + if appauthor is None: + appauthor = appname + path = os.path.normpath(_get_win_folder("CSIDL_LOCAL_APPDATA")) + if appname: + if appauthor is not False: + path = os.path.join(path, appauthor, appname) + else: + path = os.path.join(path, appname) + if opinion: + path = os.path.join(path, "Cache") + elif system == "darwin": + path = os.path.expanduser("~/Library/Caches") + if appname: + path = os.path.join(path, appname) + else: # pragma: no cover + path = os.getenv("XDG_CACHE_HOME", os.path.expanduser("~/.cache")) + if appname: + path = os.path.join(path, appname) + if appname and version: + path = os.path.join(path, version) + return path + + +def user_log_dir(appname=None, appauthor=None, version=None, opinion=True): + r"""Return full path to the user-specific log dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be ".". + Only applied when appname is present. + "opinion" (boolean) can be False to disable the appending of + "Logs" to the base app data dir for Windows, and "log" to the + base cache dir for Unix. See discussion below. + + Typical user cache directories are: + Mac OS X: ~/Library/Logs/ + Unix: ~/.cache//log # or under $XDG_CACHE_HOME if defined + Win XP: C:\Documents and Settings\\Local Settings\Application Data\\\Logs + Vista: C:\Users\\AppData\Local\\\Logs + + On Windows the only suggestion in the MSDN docs is that local settings + go in the `CSIDL_LOCAL_APPDATA` directory. (Note: I'm interested in + examples of what some windows apps use for a logs dir.) + + OPINION: This function appends "Logs" to the `CSIDL_LOCAL_APPDATA` + value for Windows and appends "log" to the user cache dir for Unix. + This can be disabled with the `opinion=False` option. + """ + if system == "darwin": + path = os.path.join(os.path.expanduser("~/Library/Logs"), appname) + elif system == "win32": # pragma: no cover + path = user_data_dir(appname, appauthor, version) + version = False + if opinion: + path = os.path.join(path, "Logs") + else: # pragma: no cover + path = user_cache_dir(appname, appauthor, version) + version = False + if opinion: + path = os.path.join(path, "log") + if appname and version: + path = os.path.join(path, version) + return path + + +class AppDirs(object): + """Convenience wrapper for getting application dirs.""" + + def __init__( + self, appname, appauthor=None, version=None, roaming=False, multipath=False + ): + self.appname = appname + self.appauthor = appauthor + self.version = version + self.roaming = roaming + self.multipath = multipath + + @property + def user_data_dir(self): + return user_data_dir( + self.appname, self.appauthor, version=self.version, roaming=self.roaming + ) + + @property + def site_data_dir(self): + return site_data_dir( + self.appname, self.appauthor, version=self.version, multipath=self.multipath + ) + + @property + def user_config_dir(self): + return user_config_dir( + self.appname, self.appauthor, version=self.version, roaming=self.roaming + ) + + @property + def site_config_dir(self): + return site_config_dir( + self.appname, self.appauthor, version=self.version, multipath=self.multipath + ) + + @property + def user_cache_dir(self): + return user_cache_dir(self.appname, self.appauthor, version=self.version) + + @property + def user_log_dir(self): + return user_log_dir(self.appname, self.appauthor, version=self.version) + + +# ---- internal support stuff + + +def _get_win_folder_from_registry(csidl_name): # pragma: no cover + """This is a fallback technique at best. I'm not sure if using the + registry for this guarantees us the correct answer for all CSIDL_* + names. + """ + import _winreg + + shell_folder_name = { + "CSIDL_APPDATA": "AppData", + "CSIDL_COMMON_APPDATA": "Common AppData", + "CSIDL_LOCAL_APPDATA": "Local AppData", + }[csidl_name] + + key = _winreg.OpenKey( + _winreg.HKEY_CURRENT_USER, + r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders", + ) + dir, type = _winreg.QueryValueEx(key, shell_folder_name) + return dir + + +def _get_win_folder_with_pywin32(csidl_name): # pragma: no cover + from win32com.shell import shellcon, shell + + dir = shell.SHGetFolderPath(0, getattr(shellcon, csidl_name), 0, 0) + # Try to make this a unicode path because SHGetFolderPath does + # not return unicode strings when there is unicode data in the + # path. + try: + dir = unicode(dir) + + # Downgrade to short path name if have highbit chars. See + # . + has_high_char = False + for c in dir: + if ord(c) > 255: + has_high_char = True + break + if has_high_char: + try: + import win32api + + dir = win32api.GetShortPathName(dir) + except ImportError: + pass + except UnicodeError: + pass + return dir + + +def _get_win_folder_with_ctypes(csidl_name): # pragma: no cover + import ctypes + + csidl_const = { + "CSIDL_APPDATA": 26, + "CSIDL_COMMON_APPDATA": 35, + "CSIDL_LOCAL_APPDATA": 28, + }[csidl_name] + + buf = ctypes.create_unicode_buffer(1024) + ctypes.windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf) + + # Downgrade to short path name if have highbit chars. See + # . + has_high_char = False + for c in buf: + if ord(c) > 255: + has_high_char = True + break + if has_high_char: + buf2 = ctypes.create_unicode_buffer(1024) + if ctypes.windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024): + buf = buf2 + + return buf.value + + +def _get_win_folder_with_jna(csidl_name): # pragma: no cover + import array + from com.sun import jna + from com.sun.jna.platform import win32 + + buf_size = win32.WinDef.MAX_PATH * 2 + buf = array.zeros("c", buf_size) + shell = win32.Shell32.INSTANCE + shell.SHGetFolderPath( + None, + getattr(win32.ShlObj, csidl_name), + None, + win32.ShlObj.SHGFP_TYPE_CURRENT, + buf, + ) + dir = jna.Native.toString(buf.tostring()).rstrip("\0") + + # Downgrade to short path name if have highbit chars. See + # . + has_high_char = False + for c in dir: + if ord(c) > 255: + has_high_char = True + break + if has_high_char: + buf = array.zeros("c", buf_size) + kernel = win32.Kernel32.INSTANCE + if kernal.GetShortPathName(dir, buf, buf_size): + dir = jna.Native.toString(buf.tostring()).rstrip("\0") + + return dir + + +if system == "win32": # pragma: no cover + try: + import win32com.shell + + _get_win_folder = _get_win_folder_with_pywin32 + except ImportError: + try: + from ctypes import windll + + _get_win_folder = _get_win_folder_with_ctypes + except ImportError: + try: + import com.sun.jna + + _get_win_folder = _get_win_folder_with_jna + except ImportError: + _get_win_folder = _get_win_folder_from_registry + + +# ---- self test code + +if __name__ == "__main__": # pragma no cover + appname = "MyApp" + appauthor = "MyCompany" + + props = ( + "user_data_dir", + "site_data_dir", + "user_config_dir", + "site_config_dir", + "user_cache_dir", + "user_log_dir", + ) + + print("-- app dirs (with optional 'version')") + dirs = AppDirs(appname, appauthor, version="1.0") + for prop in props: + print("%s: %s" % (prop, getattr(dirs, prop))) + + print("\n-- app dirs (without optional 'version')") + dirs = AppDirs(appname, appauthor) + for prop in props: + print("%s: %s" % (prop, getattr(dirs, prop))) + + print("\n-- app dirs (without optional 'appauthor')") + dirs = AppDirs(appname) + for prop in props: + print("%s: %s" % (prop, getattr(dirs, prop))) + + print("\n-- app dirs (with disabled 'appauthor')") + dirs = AppDirs(appname, appauthor=False) + for prop in props: + print("%s: %s" % (prop, getattr(dirs, prop))) diff --git a/venv/lib/python3.7/site-packages/easydev/browser.py b/venv/lib/python3.7/site-packages/easydev/browser.py new file mode 100644 index 0000000..7c3e47c --- /dev/null +++ b/venv/lib/python3.7/site-packages/easydev/browser.py @@ -0,0 +1,181 @@ +# -*- python -*- +# -*- coding: utf-8 -*- +# +# This file is part of the easydev software +# +# Copyright (c) 2011-2017 +# +# File author(s): Thomas Cokelaer +# +# Distributed under the GPLv3 License. +# See accompanying file LICENSE.txt or copy at +# http://www.gnu.org/licenses/gpl-3.0.html +# +# Website: https://github.com/cokelaer/easydev +# Documentation: http://easydev-python.readthedocs.io +# +############################################################################## +"""Universal browser + +This module provides a browser in 2 flavours: as a program to use in a Terminal, +or as a Python function that can be used in other software. The underlying code is based on the standard python module :mod:`webbrowser`. With webbrowser module itself, you can already open a URL as follows in a command line interface:: + + python -m webbrowser -t "http://www.python.org" + +However, with **browse**, you can simply type:: + + browse http://www.python.org + +It does not seem to be a big improvments but it is a bit more flexible. First, +there is no need to enter "http://" : it will be added if missing and if this is not a local file.:: + + browse docs.python.org + browse http://docs.python.org --verbose + +Similarly, you can open an image (it uses the default image viewer):: + + browse image.png + +Or a txt file (or any document provided there is a default executable +to open it). It works like a charm under Linux. Under MAC, it uses the **open** +command so this should also work. + +When invoking **browse**, under MacOSX, it actually tries to call **open** +first and then calls webbrowser, if unsuccessful only. Note tested under +Windows but uses webbrowser is used and works for open HTML document and URLs. + +You can also look at a directory (starts nautilus under Fedora):: + + browse ~/Pictures + +See more examples below. + +The interest of **browse** is that it can also be used programmatically:: + + from easydev.browser import browse + # open an image with the default image viewer: + browse("image.png") + # or a web page + browse("http://www.uniprot.org") + +There is also an alias **onweb**:: + + from easydev import onweb + +""" +import os +import sys, webbrowser +from optparse import OptionParser +import argparse + + +def browse(url, verbose=True): + from sys import platform as _platform + + if _platform == "linux" or _platform == "linux2": + _browse_linux(url, verbose=True) + elif _platform == "darwin": # pragma: no cover + # under Mac, it looks like the standard webbrowser may not work as smoothly + # OS X + _browse_mac(url, verbose) + elif _platform == "win32": # pragma: no cover + # for windows and others, the same code as Linux should work + _browse_linux(url, verbose=True) + else: + _browse_linux(url, verbose=True) # pragma: no cover + + +def _browse_mac(url, verbose=True): # pragma: no cover + if verbose: + print("openning %s" % url) + + import os + + try: + os.system("open /Applications/Safari.app {}".format(url)) + return + except: + pass + + try: + os.system("open /Applications/Safari.app {}".format("http://" + url)) + return + except: + pass + + try: + webbrowser.open_new(url) + except: + if verbose: + print("Could not open %s. Trying to append http://" % url) + try: + webbrowser.open_new("open http://{}".format(url)) + except: + print("Could not open http://%s" % url) + raise Exception + + +def _browse_linux(url, verbose=True): # pragma: no cover + if verbose: + print("openning %s" % url) + try: + webbrowser.open(url) + return + except: + pass + + try: + if verbose: + print("Could not open %s" % url) + webbrowser.open("http://" + url) + return + except: + pass + + raise Exception("Could not open http://{}".format(url)) + + +def main(args=None): # pragma: no cover + if args is None: + args = sys.argv[:] + + # check for verbosity + if "--verbose" in args: + verbose = True + args.remove("--verbose") + print(args) + else: + verbose = False + + if "--help" in args or len(args) == 1: + print("Browse, a simple command line browser") + print("Author: Thomas Cokelaer, (c) 2012.") + print("USAGE\n\tbrowse http://docs.python.org ") + print("\tbrowse http://docs.python.org --verbose") + print("\tbrowse localfile.html") + print("\tbrowse local_directory (Linux only ?)") + return + url = args[1] + + if os.path.exists(url): + if verbose: + print("%s is local file. Trying to open it.\n" % url) + browse(url, verbose) + else: + if verbose: + print("%s seems to be a web address. Trying to open it.\n" % url) + if url.startswith("http"): + browse(url, verbose) + else: + if verbose: + print( + "%s does not exists and does not starts with http, trying anyway." + % url + ) + browse("http://" + url, verbose) + + +if __name__ == "__main__": # pragma: no cover + import sys + + main(sys.argv) diff --git a/venv/lib/python3.7/site-packages/easydev/chunks.py b/venv/lib/python3.7/site-packages/easydev/chunks.py new file mode 100644 index 0000000..a4673dc --- /dev/null +++ b/venv/lib/python3.7/site-packages/easydev/chunks.py @@ -0,0 +1,55 @@ +# -*- python -*- +# -*- coding: utf-8 -*- +# +# This file is part of the easydev software +# +# Copyright (c) 2011-2017 +# +# File author(s): Thomas Cokelaer +# +# Distributed under the GPLv3 License. +# See accompanying file LICENSE.txt or copy at +# http://www.gnu.org/licenses/gpl-3.0.html +# +# Website: https://github.com/cokelaer/easydev +# Documentation: http://easydev-python.readthedocs.io +# +############################################################################## + +# http://stackoverflow.com/questions/312443/how-do-you-split-a-list-into-evenly-sized-chunks-in-python +# +# Here's a generator that yields the chunks you want: +# +# def chunks(l, n): +# """Yield successive n-sized chunks from l.""" +# for i in range(0, len(l), n): +# yield l[i:i+n] +# +# The issue here is that the chunks are not evenly sized chunks +# + +__all__ = ["split_into_chunks"] + + +try: + range = xrange # py2 +except: + pass # py3 + + +def split_into_chunks(items, maxchunks=10): + """Split a list evenly into N chunks + + .. doctest:: + + >>> from easydev import split_into_chunks + >>> data = [1,1,2,2,3,3] + >>> list(split_into_chunks(data, 3)) + [[1, 2], [1, 3], [2, 3]] + + + """ + chunks = [[] for _ in range(maxchunks)] + for i, item in enumerate(items): + chunks[i % maxchunks].append(item) + return filter(None, chunks) diff --git a/venv/lib/python3.7/site-packages/easydev/codecs.py b/venv/lib/python3.7/site-packages/easydev/codecs.py new file mode 100644 index 0000000..1914d70 --- /dev/null +++ b/venv/lib/python3.7/site-packages/easydev/codecs.py @@ -0,0 +1,92 @@ +# -*- python -*- +# -*- coding: utf-8 -*- +# +# This file is part of the easydev software +# +# Copyright (c) 2011-2017 +# +# File author(s): Thomas Cokelaer +# +# Distributed under the GPLv3 License. +# See accompanying file LICENSE.txt or copy at +# http://www.gnu.org/licenses/gpl-3.0.html +# +# Website: https://www.assembla.com/spaces/pyeasydev/wiki +# Documentation: http://packages.python.org/easydev +# +############################################################################## +# $:Id $ +"""various type convertors (e.g., list to string)""" + + +__all__ = ["to_list", "tolist", "list2string"] + + +def tolist(data, verbose=True): + """alias to :func:`easydev.codecs.to_list`""" + return to_list(data, verbose=verbose) + + +def to_list(data, verbose=True): + """Transform an object into a list if possible + + :param data: a list, tuple, or simple type (e.g. int) + :return: a list + + - if the object is list or tuple, do nothing + - if the object is not a list, we assume this is a primitive type and a list + of length 1 is returned, the item being the parameter provided. + + .. code-block:: python + + >>> from easydev import transform_to_list + >>> to_list(1) + [1] + >>> to_list([1,2]) + [1,2] + + """ + if isinstance(data, list) or isinstance(data, tuple): + return data # nothing to do + elif isinstance(data, float): + return [data] + elif isinstance(data, int): + return [data] + elif isinstance(data, str): + return [data] + else: + try: + data = data.tolist() + return data + except: + if verbose: + print("not known type. Trying to cast into a list") + return list(data) + + +def list2string(data, sep=",", space=True): + """Transform a list into a string + + :param list data: list of items that have a string representation. + the input data could also be a simple object, in which case + it is simply returned with a cast into a string + :param str sep: the separator to be use + + .. code-block:: python + + >>> list2string([1, 2] + "1, 2" + >>> list2string([1, 2], sep=;) + "1; 2" + >>> list2string([1, 2], sep=;, space=False) + "1;2" + >>> list2string(1, sep=;, space=False) + "1" + + .. note:: the cast is performed with str() + """ + data = to_list(data) + if space is True: + sep = sep + " " + res = sep.join([str(x) for x in data]) + return res diff --git a/venv/lib/python3.7/site-packages/easydev/config_tools.py b/venv/lib/python3.7/site-packages/easydev/config_tools.py new file mode 100644 index 0000000..214adcf --- /dev/null +++ b/venv/lib/python3.7/site-packages/easydev/config_tools.py @@ -0,0 +1,457 @@ +# -*- python -*- +# -*- coding: utf-8 -*- +# +# This file is part of the easydev software +# +# Copyright (c) 2011-2014 +# +# File author(s): Thomas Cokelaer +# +# Distributed under the GPLv3 License. +# See accompanying file LICENSE.txt or copy at +# http://www.gnu.org/licenses/gpl-3.0.html +# +# Website: https://github.com/cokelaer/easydev +# Documentation: http://packages.python.org/easydev +# +############################################################################## +try: + from ConfigParser import ConfigParser +except ImportError: + from configparser import ConfigParser + + +import os + + +__all__ = ["CustomConfig", "DynamicConfigParser", "ConfigExample", "load_configfile"] + +# 53, 59, 64-65, 181, 260, 262, 267-270, 288-290, 329, 332-337, 340-341, 359-360, 386-388, 400-401, 406-426, 431-435 + + +class _DictSection(object): + """Dictionary section. + + Reference: https://gist.github.com/dangoakachan/3855920 + + """ + + def __init__(self, config, section): + object.__setattr__(self, "_config", config) + object.__setattr__(self, "_section", section) + + def __getattr__(self, attr): + return self.get(attr, None) + + __getitem__ = __getattr__ + + def get(self, attr, default=None): + if attr in self: + return self._config.get(self._section, attr) + else: # pragma: no cover + return default + + def __setattr__(self, attr, value): + if attr.startswith("_"): + object.__setattr__(self, attr, value) + else: # pragma: no cover + self.__setitem__(attr, value) + + def __setitem__(self, attr, value): + if self._section not in self._config: # pragma: no cover + self._config.add_section(self._section) + + self._config.set(self._section, attr, str(value)) + + def __delattr__(self, attr): + if attr in self: + self._config.remove_option(self._section, attr) + + __delitem__ = __delattr__ + + def __contains__(self, attr): + config = self._config + section = self._section + + return config.has_section(section) and config.has_option(section, attr) + + +class ConfigExample(object): + """Create a simple example of ConfigParser instance to play with + + :: + + >>> from easydev.pipeline.config import ConfigExample + >>> c = ConfigExample().config # The ConfigParser instance + >>> assert 'General' in c.sections() + >>> assert 'GA' in c.sections() + + This example builds up a ConfigParser instance from scratch. + + This is equivalent to having the following input file:: + + [General] + verbose = True + tag = test + + [GA] + popsize = 1 + + + which can be read with ConfigParser as follows: + + .. doctest:: + + >>> # Python 3 code + >>> from configparser import ConfigParser + >>> config = ConfigParser() + >>> config.read("file.ini") + [] + + """ + + def __init__(self): + self.config = ConfigParser() + self.config.add_section("General") + self.config.set("General", "verbose", "true") + self.config.add_section("GA") + self.config.set("GA", "popsize", "50") + + +class DynamicConfigParser(ConfigParser, object): + """Enhanced version of Config Parser + + Provide some aliases to the original ConfigParser class and + new methods such as :meth:`save` to save the config object in a file. + + .. code-block:: python + + >>> from easydev.config_tools import ConfigExample + >>> standard_config_file = ConfigExample().config + >>> c = DynamicConfigParser(standard_config_file) + >>> + >>> # then to get the sections, simply type as you would normally do with ConfigParser + >>> c.sections() + >>> # or for the options of a specific sections: + >>> c.get_options('General') + + You can now also directly access to an option as follows:: + + c.General.tag + + Then, you can add or remove sections (:meth:`remove_section`, :meth:`add_section`), + or option from a section :meth:`remove_option`. You can save the instance into a file + or print it:: + + print(c) + + .. warning:: if you set options manually (e.G. self.GA.test =1 if GA is a + section and test one of its options), then the save/write does not work + at the moment even though if you typoe self.GA.test, it has the correct value + + + Methods inherited from ConfigParser are available: + + :: + + # set value of an option in a section + c.set(section, option, value=None) + # but with this class, you can also use the attribute + c.section.option = value + + # set value of an option in a section + c.remove_option(section, option) + c.remove_section(section) + + + """ + + def __init__(self, config_or_filename=None, *args, **kargs): + + object.__setattr__(self, "_filename", config_or_filename) + # why not a super usage here ? Maybe there were issues related + # to old style class ? + ConfigParser.__init__(self, *args, **kargs) + + if isinstance(self._filename, str) and os.path.isfile(self._filename): + self.read(self._filename) + elif isinstance(config_or_filename, ConfigParser): + self._replace_config(config_or_filename) + elif config_or_filename == None: + pass + else: + raise TypeError( + "config_or_filename must be a valid filename or valid ConfigParser instance" + ) + + def read(self, filename): + """Load a new config from a filename (remove all previous sections)""" + if os.path.isfile(filename) == False: + raise IOError("filename {0} not found".format(filename)) + + config = ConfigParser() + config.read(filename) + + self._replace_config(config) + + def _replace_config(self, config): + """Remove all sections and add those from the input config file + + :param config: + + """ + for section in self.sections(): + self.remove_section(section) + + for section in config.sections(): + self.add_section(section) + for option in config.options(section): + data = config.get(section, option) + self.set(section, option, data) + + def get_options(self, section): + """Alias to get all options of a section in a dictionary + + One would normally need to extra each option manually:: + + for option in config.options(section): + config.get(section, option, raw=True)# + + then, populate a dictionary and finally take care of the types. + + .. warning:: types may be changed .For instance the string "True" + is interpreted as a True boolean. + + .. seealso:: internally, this method uses :meth:`section2dict` + """ + return self.section2dict(section) + + def section2dict(self, section): + """utility that extract options of a ConfigParser section into a dictionary + + :param ConfigParser config: a ConfigParser instance + :param str section: the section to extract + + :returns: a dictionary where key/value contains all the + options/values of the section required + + Let us build up a standard config file: + .. code-block:: python + + >>> # Python 3 code + >>> from configparser import ConfigParser + >>> c = ConfigParser() + >>> c.add_section('general') + >>> c.set('general', 'step', str(1)) + >>> c.set('general', 'verbose', 'True') + + To access to the step options, you would write:: + + >>> c.get('general', 'step') + + this function returns a dictionary that may be manipulated as follows:: + + >>> d_dict.general.step + + .. note:: a value (string) found to be True, Yes, true, + yes is transformed to True + .. note:: a value (string) found to be False, No, no, + false is transformed to False + .. note:: a value (string) found to be None; none, + "" (empty string) is set to None + .. note:: an integer is cast into an int + """ + options = {} + for option in self.options(section): # pragma no cover + data = self.get(section, option, raw=True) + if data.lower() in ["true", "yes"]: + options[option] = True + elif data.lower() in ["false", "no"]: + options[option] = False + elif data in ["None", None, "none", ""]: + options[option] = None + else: + try: # numbers + try: + options[option] = self.getint(section, option) + except: + options[option] = self.getfloat(section, option) + except: # string + options[option] = self.get(section, option, raw=True) + return options + + def save(self, filename): + """Save all sections/options to a file. + + :param str filename: a valid filename + + :: + + config = ConfigParams('config.ini') #doctest: +SKIP + config.save('config2.ini') #doctest: +SKIP + + """ + try: + if os.path.exists(filename) == True: + print("Warning: over-writing %s " % filename) + fp = open(filename, "w") + except Exception as err: # pragma: no cover + print(err) + raise Exception("filename could not be opened") + + self.write(fp) + fp.close() + + def add_option(self, section, option, value=None): + """add an option to an existing section (with a value) + + .. code-block:: python + + >>> c = DynamicConfigParser() + >>> c.add_section("general") + >>> c.add_option("general", "verbose", True) + """ + assert section in self.sections(), "unknown section" + # TODO I had to cast to str with DictSection + self.set(section, option, value=str(value)) + + def __str__(self): + str_ = "" + for section in self.sections(): + str_ += "[" + section + "]\n" + for option in self.options(section): + data = self.get(section, option, raw=True) + str_ += option + " = " + str(data) + "\n" + str_ += "\n\n" + + return str_ + + def __getattr__(self, key): + return _DictSection(self, key) + + __getitem__ = __getattr__ + + def __setattr__(self, attr, value): + if attr.startswith("_") or attr: + object.__setattr__(self, attr, value) + else: + self.__setitem__(attr, value) + + def __setitem__(self, attr, value): + if isinstance(value, dict): + section = self[attr] + for k, v in value.items(): + section[k] = v + else: + raise TypeError("value must be a valid dictionary") + + def __delattr__(self, attr): + if attr in self: + self.remove_section(attr) + + def __contains__(self, attr): + return self.has_section(attr) + + def __eq__(self, data): + # FIXME if you read file, the string "True" is a string + # but you may want it to be converted to a True boolean value + if sorted(data.sections()) != sorted(self.sections()): + print("Sections differ") + return False + for section in self.sections(): + + for option in self.options(section): + try: + if str(self.get(section, option, raw=True)) != str( + data.get(section, option, raw=True) + ): + print("option %s in section %s differ" % (option, section)) + return False + except: # pragma: no cover + return False + return True + + +class CustomConfig(object): + """Base class to manipulate a config directory""" + + def __init__(self, name, verbose=False): + self.verbose = verbose + from easydev import appdirs + + self.appdirs = appdirs.AppDirs(name) + + def init(self): + sdir = self.appdirs.user_config_dir + self._get_and_create(sdir) + + def _get_config_dir(self): + sdir = self.appdirs.user_config_dir + return self._get_and_create(sdir) + + user_config_dir = property( + _get_config_dir, doc="return directory of this configuration file" + ) + + def _get_and_create(self, sdir): + if not os.path.exists(sdir): + print("Creating directory %s " % sdir) + try: + self._mkdirs(sdir) + except Exception: # pragma: no cover + print("Could not create the path %s " % sdir) + return None + return sdir + + def _mkdirs(self, newdir, mode=0o777): + """See :func:`easydev.tools.mkdirs`""" + from easydev.tools import mkdirs + + mkdirs(newdir, mode) + + def remove(self): + try: + sdir = self.appdirs.user_config_dir + os.rmdir(sdir) + except Exception as err: # pragma: no cover + raise Exception(err) + + +def _load_configfile(configpath): # pragma: no cover + "Tries to load a JSON or YAML file into a dict." + try: + with open(configpath) as f: + try: + import json + + return json.load(f) + except ValueError: + f.seek(0) # try again + try: + import yaml + except ImportError: + raise IOError( + "Config file is not valid JSON and PyYAML " + "has not been installed. Please install " + "PyYAML to use YAML config files." + ) + try: + return yaml.load(f, Loader=yaml.FullLoader) + except yaml.YAMLError: + raise IOError( + "Config file is not valid JSON or YAML. " + "In case of YAML, make sure to not mix " + "whitespace and tab indentation." + ) + except Exception as err: + raise (err) + + +def load_configfile(configpath): + "Loads a JSON or YAML configfile as a dict." + config = _load_configfile(configpath) + if not isinstance(config, dict): + raise IOError( + "Config file must be given as JSON or YAML " "with keys at top level." + ) + return config diff --git a/venv/lib/python3.7/site-packages/easydev/console.py b/venv/lib/python3.7/site-packages/easydev/console.py new file mode 100644 index 0000000..ec8a59f --- /dev/null +++ b/venv/lib/python3.7/site-packages/easydev/console.py @@ -0,0 +1,142 @@ +# -*- python -*- +# -*- coding: utf-8 -*- +# +# This file is part of the easydev software +# It is a modified version of console.py from the sphinx software +# +# Copyright (c) 2011-2014 +# +# File author(s): Thomas Cokelaer +# +# Distributed under the GPLv3 License. +# See accompanying file LICENSE.txt or copy at +# http://www.gnu.org/licenses/gpl-3.0.html +# +# Website: https://github.com/cokelaer/easydev +# Documentation: http://packages.python.org/easydev +# +############################################################################## +"""Format colored consoled output. Modified from sphinx.util.console""" +import os +import sys +import platform + +__all__ = ["color_terminal", "get_terminal_width", "term_width_line"] + + +try: + plf = platform.platform.lower() + if plf.starstwith("win"): # pragma: no cover + import colorama + + colorama.init() +except AttributeError: + pass + +# colors and other functions from the attributes codes are added dynamically to +# this __all__ variable +codes = {} + + +def get_terminal_width(): + """Returns the current terminal width""" + try: + import termios, fcntl, struct + + call = fcntl.ioctl(0, termios.TIOCGWINSZ, struct.pack("hhhh", 0, 0, 0, 0)) + _, width = struct.unpack("hhhh", call)[:2] + terminal_width = width + except (SystemExit, KeyboardInterrupt): # pragma: no cover + raise + except: + # FALLBACK + terminal_width = int(os.environ.get("COLUMNS", 80)) - 1 + return terminal_width + + +def term_width_line(text): + """prints pruned version of the input text (limited to terminal width) + + :param str text: + :return str text: + """ + _tw = get_terminal_width() + if not codes: + # if no coloring, don't output fancy backspaces + return text + "\n" + else: + return text.ljust(_tw) + "\r" + + +def color_terminal(): + """Does terminal allows coloring + + :return: boolean""" + if not hasattr(sys.stdout, "isatty"): + return False + if not sys.stdout.isatty(): + return False + if "COLORTERM" in os.environ: # pragma: no cover + return True + term = os.environ.get("TERM", "dumb").lower() + if term in ("xterm", "linux") or "color" in term: + return True + return False + + +def __nocolor(): # pragma: no cover + """set color codes off""" + codes.clear() + + +def __coloron(): # pragma: no cover + """Set color codes on""" + codes.update(_orig_codes) + + +def _colorize(name, text): + return codes.get(name, "") + text + codes.get("reset", "") + + +def _create_color_func(name): + def inner(text): + return _colorize(name, text) + + globals()[name] = inner + + +_attrs = { + "reset": "39;49;00m", + "bold": "01m", + "faint": "02m", + "standout": "03m", + "underline": "04m", + "blink": "05m", +} + +for _name, _value in _attrs.items(): + codes[_name] = "\x1b[" + _value + +_colors = [ + ("black", "darkgray"), + ("darkred", "red"), + ("darkgreen", "green"), + ("brown", "yellow"), + ("darkblue", "blue"), + ("purple", "fuchsia"), + ("turquoise", "teal"), + ("lightgray", "white"), +] + +for i, (dark, light) in enumerate(_colors): + codes[dark] = "\x1b[%im" % (i + 30) + codes[light] = "\x1b[%i;01m" % (i + 30) + +_orig_codes = codes.copy() + +for _name in codes: + _create_color_func(_name) + +# dynamically set the colors +for x in codes.keys(): + __all__.append(x) diff --git a/venv/lib/python3.7/site-packages/easydev/copybutton.py b/venv/lib/python3.7/site-packages/easydev/copybutton.py new file mode 100644 index 0000000..cab51f0 --- /dev/null +++ b/venv/lib/python3.7/site-packages/easydev/copybutton.py @@ -0,0 +1,119 @@ +# -*- python -*- +# -*- coding: utf-8 -*- +# +# This file is part of the easydev software +# +# Copyright (c) 2011-2014 +# +# File author(s): Thomas Cokelaer +# +# Distributed under the GPLv3 License. +# See accompanying file LICENSE.txt or copy at +# http://www.gnu.org/licenses/gpl-3.0.html +# +# Website: https://github.com/cokelaer/easydev +# Documentation: http://packages.python.org/easydev +# +############################################################################## +"""This module is a copy of a sphinx extension. unknown origin. + + added by Thomas Cokelaer: get_copybutton_path function. + + Create a sphinx extension based on copybutton javascript from python website + +Requires sphinx to be installed. imports are inside functions so not stricly +speaking required for the installation. +""" +import os +from os.path import join as pj +import shutil + +try: + from docutils import nodes +except Exception: # pragma: no cover + # if docutils is not installed + class Dummy: + SkipNode = Exception + + nodes = Dummy() + + +__all__ = [ + "get_copybutton_path", +] + + +def copy_javascript_into_static_path(static="_static", filepath="copybutton.js"): + """This script can be included in a sphinx configuration file to copy the + copybutton in the static directory + + :param str static: name of the static path (_static by default) + :param filename: full path of the file to copy + + :Details: If the path *static* does not exists, it is created. If the + filename in filepath is already in the path *static*, nothing need to be done. + Otherwise, the file is copied in *static* directory. + + """ + if os.path.isdir(static): + pass + else: + os.mkdir(static) + + filename = os.path.split(filepath)[1] + if os.path.isfile(static + os.sep + filename): + pass + else: + shutil.copy(filepath, static + os.sep + filename) + + +def get_copybutton_path(): + """Return the path where the to find the copybutton javascript + + Copy the copybutton.js javascript in the share directory of easydev so + that it is accesible by all packages that required it by typing: + + .. doctest:: + + >>> from easydev import get_copybutton_path + >>> p = get_copybutton_path() + + It can then be added with a Sphinx configuration file:: + + jscopybutton_path = easydev.copybutton.get_copybutton_path() + + """ + import easydev + + try: # install mode + packagedir = easydev.__path__[0] + packagedir = os.path.realpath(pj(packagedir, "share")) + os.listdir(packagedir) # if this faisl, we are in deve mode + except OSError: # pragma: no cover + try: + packagedir = easydev.__path__[0] + packagedir = os.path.realpath(pj(packagedir, "..", "share")) + except: + raise IOError("could not find data directory") + return pj(packagedir, "copybutton.js") + + +def setup(app): # pragma: no cover + cwd = os.getcwd() + + # From Sphinx, we typing "make html", this is the place where we expect + # the JS to be found + staticpath = os.sep.join([cwd, "source", "_static"]) + from easydev.tools import mkdirs + + mkdirs(staticpath) + if os.path.exists(staticpath + os.sep + "copybutton.js"): + pass # the JS file is already there. + else: + # Not found, so let us copy it + import shutil + + shutil.copy(get_copybutton_path(), staticpath) + + # Now that the file is available, use it + app.add_js_file("copybutton.js") diff --git a/venv/lib/python3.7/site-packages/easydev/decorators.py b/venv/lib/python3.7/site-packages/easydev/decorators.py new file mode 100644 index 0000000..880fdf0 --- /dev/null +++ b/venv/lib/python3.7/site-packages/easydev/decorators.py @@ -0,0 +1,196 @@ +# -*- python -*- +# -*- coding: utf-8 -*- +# +# This file is part of the easydev software +# +# Copyright (c) 2012-2014 - +# +# File author(s): Thomas Cokelaer (cokelaer, gmail.com) +# +# Distributed under the GPLv3 License. +# See accompanying file LICENSE.txt or copy at +# http://www.gnu.org/licenses/gpl-3.0.html +# +# Website: https://github.com/cokelaer/easydev +# website: http://github.com/cokelaer/easydev +# +############################################################################## +"""Handy decorators""" +from functools import wraps +import threading + +__all__ = ["ifpylab", "requires", "ifpandas"] + + +# decorator with arguments and optional arguments for a method +def _require(*args_deco, **kwds_deco): + """Decorator for class method to check if an attribute exist + + .. doctest:: + + from easydev.decorators import require + + class Test(object): + def __init__(self): + self.m = 1 + @require('m', "set the m attribute first") + def print(self): + print self.m + t = Test() + t.print() + + + .. todo:: first argument could be a list + """ + if len(args_deco) != 2: + raise ValueError( + "require decorator expects 2 parameter. First one is" + + "the required attribute. Second one is an error message." + ) + attribute = args_deco[0] + msg = args_deco[1] + + if len(attribute.split(".")) > 2: + raise AttributeError( + "This version of require decorator introspect only 2 levels" + ) + + def decorator(func): + # func: function object of decorated method; has + # useful info like f.func_name for the name of + # the decorated method. + + def newf(*args, **kwds): + # This code will be executed in lieu of the + # method you've decorated. You can call the + # decorated method via f(_args, _kwds). + names = attribute.split(".") + + if len(names) == 1: + if hasattr(args[0], attribute): + return func(*args, **kwds) + else: + raise AttributeError("%s not found. %s" % (names, msg)) + elif len(names) == 2: + if hasattr(getattr(args[0], names[0]), names[1]): + return func(*args, **kwds) + else: + raise AttributeError("%s not found. %s" % (names, msg)) + + newf.__name__ = func.__name__ + newf.__doc__ = func.__doc__ + return newf + + return decorator + + +# for book keeping, could be useful: +""" +def _blocking(not_avail): + def blocking(f, *args, **kw): + if not hasattr(f, "thread"): + # no thread running + def set_result(): + f.result = f(*args, **kw) + f.thread = threading.Thread(None, set_result) + f.thread.start() + return not_avail + elif f.thread.isAlive(): + return not_avail + else: + # the thread is ended, return the stored result + del f.thread + return f.result + return blocking +""" + +# same as require decorator but works with list of stirngs of +# single string and uses the functools utilities +def requires(requires, msg=""): + """Decorator for class method to check if an attribute exist + + .. doctest:: + + >>> from easydev.decorators import requires + >>> class Test(object): + ... def __init__(self): + ... self.m = 1 + ... self.x = 1 + ... @requires(['m','x'], "set the m attribute first") + ... def printthis(self): + ... print(self.m+self.x) + >>> t = Test() + >>> t.printthis() + 2 + + """ + if isinstance(requires, str): + requires = [requires] + elif isinstance(requires, list) == False: + raise TypeError( + "First argument of the /requires/ decorator must be a" + + "string or list of string representing the required attributes" + + "to be found in your class. Second argument is a " + + "complementary message. " + ) + + def actualDecorator(f): + @wraps(f) + def wrapper(*args, **kwds): + for require in requires: + if hasattr(args[0], require) == False: + raise AttributeError( + "{} not found in {}. ".format(require, args[0]) + msg + ) + return f(*args, **kwds) + + return wrapper + + return actualDecorator + + +# could be a macro maybe + + +def ifpandas(func): + """check if pandas is available. If so, just return + the function, otherwise returns dumming function + that does nothing + + """ + + def wrapper(*args, **kwds): + return func(*args, **kwds) + + try: + import pandas + + return wrapper + except Exception: # pragma: no cover + + def dummy(): + pass + + return dummy + + +def ifpylab(func): + """check if pylab is available. If so, just return + the function, otherwise returns dumming function + that does nothing + """ + # for functions + def wrapper(*args, **kwds): + return func(*args, **kwds) + + # for methods + try: + import pylab + + return wrapper + except Exception: # pragma: no cover + + def dummy(): + pass + + return dummy diff --git a/venv/lib/python3.7/site-packages/easydev/dependencies.py b/venv/lib/python3.7/site-packages/easydev/dependencies.py new file mode 100644 index 0000000..4b59ef2 --- /dev/null +++ b/venv/lib/python3.7/site-packages/easydev/dependencies.py @@ -0,0 +1,38 @@ +# -*- python -*- +# -*- coding: utf-8 -*- +# +# This file is part of the easydev software +# +# Copyright (c) 2011-2017 +# +# File author(s): Thomas Cokelaer +# +# Distributed under the GPLv3 License. +# See accompanying file LICENSE.txt or copy at +# http://www.gnu.org/licenses/gpl-3.0.html +# +# Website: https://github.com/cokelaer/easydev +# Documentation: http://easydev-python.readthedocs.io +# +############################################################################## +# $:Id $ + +import pkg_resources + + +__all__ = ["get_dependencies"] + + +def get_dependencies(pkgname): + """Return dependencies of a package as a sorted list + + :param str pkgname: package name + :return: list (empty list if no dependencies) + """ + try: + res = pkg_resources.require(pkgname) + res = list(set(res)) + res.sort() + return res + except Exception: + return [] diff --git a/venv/lib/python3.7/site-packages/easydev/doc.py b/venv/lib/python3.7/site-packages/easydev/doc.py new file mode 100644 index 0000000..c12ceb1 --- /dev/null +++ b/venv/lib/python3.7/site-packages/easydev/doc.py @@ -0,0 +1,44 @@ +# -*- python -*- +# -*- coding: utf-8 -*- +# +# This file is part of the easydev software +# +# Copyright (c) 2011-2017 +# +# File author(s): Thomas Cokelaer +# +# Distributed under the GPLv3 License. +# See accompanying file LICENSE.txt or copy at +# http://www.gnu.org/licenses/gpl-3.0.html +# +# Website: https://github.com/cokelaer/easydev +# Documentation: http://easydev-python.readthedocs.io +# +############################################################################## +"""Module related to documentation + + +.. testsetup:: + + from easydev.doc import * + +""" +__all__ = ["underline"] + + +def underline(text, symbol="="): + """Underlines a string with a specific character + + :param str text: the text to underline + :param str symbol: the character to be used to underline the text + :return: underlined text. + + .. doctest:: + + >>> print(underline("test")) + test + ==== + + """ + length = len(text) + return text + "\n" + length * symbol diff --git a/venv/lib/python3.7/site-packages/easydev/easytest.py b/venv/lib/python3.7/site-packages/easydev/easytest.py new file mode 100644 index 0000000..5744fec --- /dev/null +++ b/venv/lib/python3.7/site-packages/easydev/easytest.py @@ -0,0 +1,127 @@ +# -*- python -*- +# -*- coding: utf-8 -*- +# +# This file is part of the easydev software +# +# Copyright (c) 2011-2014 +# +# File author(s): Thomas Cokelaer +# +# Distributed under the GPLv3 License. +# See accompanying file LICENSE.txt or copy at +# http://www.gnu.org/licenses/gpl-3.0.html +# +# Website: https://github.com/cokelaer/easydev +# Documentation: http://packages.python.org/easydev +# +############################################################################## +# $:Id $ + +import tempfile + +__all__ = ["assert_list_almost_equal", "trysetattr", "TempFile"] + +# from easydev.decorators import ifpandas + + +def assert_list_almost_equal(first, second, places=None, deltas=None): + """Combined version nose.tools.assert_almost_equal and assert_list_equal + + This function checks that 2 lists contain identical items. + The equality between pair of items is checked with assert_almost_equal + function, which means you can check for the places argument + + .. note:: there may be already some tools to + check that either in nosetests or unittest + but could not find. + + .. doctest:: + + >>> from easydev.easytest import assert_list_almost_equal + >>> assert_list_almost_equal([0,0,1], [0,0,0.9999], places=3) + >>> assert_list_almost_equal([0,0,1], [0,0,0.9999], deltas=1e-4) + + """ + if places: + deltas = 10 ** -(places - 1) + + if deltas: + for x, y in zip(first, second): + if abs(x - y) > deltas: + raise ValueError + + +def trysetattr(this, attrname, value, possible): + """A common test pattern: try to set a non-writable attribute + + :: + + class A(object): + def __init__(self): + self._a = 1 + self._b = 2 + def _get_a(self): + return self._a + def _set_a(self, value): + self._a = value + a = property(_get_a, _get_b) + def _get_b(self): + return self._b + b = property(_get_b) + + >>> o = A() + >>> trysetattr(A, "a", 1, possible=True) + >>> trysetattr(A, "b", 1, False) + AssertionError + + """ + if possible == True: + a1 = True + a2 = False + else: + a1 = False + a2 = True + try: + setattr(this, attrname, value) + assert a1 # if the setattr is possible, this should be True + except Exception: + assert a2 + + +class TempFile(object): + """A small wrapper around tempfile.NamedTemporaryFile function + + :: + + f = TempFile(suffix="csv") + f.name + f.delete() # alias to delete=False and close() calls + + + """ + + def __init__(self, suffix="", dir=None): + self.temp = tempfile.NamedTemporaryFile(suffix=suffix, delete=False, dir=dir) + + def delete(self): + try: + self.temp._closer.delete = True + except: # pragma: no cover + self.temp.delete = True + self.temp.close() + + def _get_name(self): + return self.temp.name + + name = property(_get_name) + + def __exit__(self, type, value, traceback): + try: + self.delete() + except AttributeError: # pragma: no cover + pass + finally: + self.delete() + + def __enter__(self): + return self diff --git a/venv/lib/python3.7/site-packages/easydev/logging_tools.py b/venv/lib/python3.7/site-packages/easydev/logging_tools.py new file mode 100644 index 0000000..3bf63ba --- /dev/null +++ b/venv/lib/python3.7/site-packages/easydev/logging_tools.py @@ -0,0 +1,125 @@ +# -*- python -*- +# +# This file is part of easydev software +# +# Copyright (c) 2012-2014 +# +# File author(s): Thomas Cokelaer +# +# Distributed under the GPLv3 License. +# See accompanying file LICENSE.txt or copy at +# http://www.gnu.org/licenses/gpl-3.0.html +# +# Website: https://github.com/cokelaer/easydev +# +############################################################################## +# import logging +import colorlog + +__all__ = ["Logging"] + + +colors = { + "DEBUG": "cyan", + "INFO": "green", + "WARNING": "yellow", + "ERROR": "red", + "CRITICAL": "bold_red", +} + + +class Logging(object): + """logging utility. + + :: + + >>> l = Logging("root", "INFO") + >>> l.info("test") + INFO:root:test + >>> l.level = "WARNING" + >>> l.info("test") + + """ + + def __init__(self, name="root", level="WARNING", text_color="blue"): + self._name = name + self.formatter = colorlog.ColoredFormatter( + "%(log_color)s%(levelname)-8s[%(name)s:%(lineno)d]: %(reset)s %({})s%(message)s".format( + text_color + ), + datefmt=None, + reset=True, + log_colors=colors, + secondary_log_colors={}, + style="%", + ) + self._set_name(name) + + logger = colorlog.getLogger(self._name) + handler = colorlog.StreamHandler() + handler.setFormatter(self.formatter) + logger.addHandler(handler) + + def _set_name(self, name): + level = self.level + self._name = name + logger = colorlog.getLogger(self._name) + if level == 0: + self._set_level("WARNING") + else: + self._set_level(level) + + def _get_name(self): + return self._name + + name = property(_get_name, _set_name) + + def _set_level(self, level): + if isinstance(level, bool): + if level is True: + level = "INFO" + if level is False: + level = "ERROR" + if level == 10: + level = "DEBUG" + if level == 20: + level = "INFO" + if level == 30: + level = "WARNING" + if level == 40: + level = "ERROR" + if level == 50: + level = "CRITICAL" + colorlog.getLogger(self.name).setLevel(level) + + def _get_level(self): + level = colorlog.getLogger(self.name).level + if level == 10: + return "DEBUG" + elif level == 20: + return "INFO" + elif level == 30: + return "WARNING" + elif level == 40: + return "ERROR" + elif level == 50: + return "CRITICAL" + else: + return level + + level = property(_get_level, _set_level) + + def debug(self, msg): + colorlog.getLogger(self.name).debug(msg) + + def info(self, msg): + colorlog.getLogger(self.name).info(msg) + + def warning(self, msg): + colorlog.getLogger(self.name).warning(msg) + + def critical(self, msg): + colorlog.getLogger(self.name).critical(msg) + + def error(self, msg): + colorlog.getLogger(self.name).error(msg) diff --git a/venv/lib/python3.7/site-packages/easydev/md5tools.py b/venv/lib/python3.7/site-packages/easydev/md5tools.py new file mode 100644 index 0000000..b259d91 --- /dev/null +++ b/venv/lib/python3.7/site-packages/easydev/md5tools.py @@ -0,0 +1,31 @@ +# -*- python -*- +# -*- coding: utf-8 -*- +# +# This file is part of the easydev software +# +# Copyright (c) 2011-2017 +# +# File author(s): Thomas Cokelaer +# +# Distributed under the GPLv3 License. +# See accompanying file LICENSE.txt or copy at +# http://www.gnu.org/licenses/gpl-3.0.html +# +# Website: https://github.com/cokelaer/easydev +# Documentation: http://easydev-python.readthedocs.io +# +############################################################################## +"""md5 utility""" +import hashlib + + +def md5(fname, chunk=65536): + """Return the MD5 checksums of a file + + Takes about 25 seconds on a 8Gb file. + """ + hash_md5 = hashlib.md5() + with open(fname, "rb") as f: + for this in iter(lambda: f.read(chunk), b""): + hash_md5.update(this) + return hash_md5.hexdigest() diff --git a/venv/lib/python3.7/site-packages/easydev/misc.py b/venv/lib/python3.7/site-packages/easydev/misc.py new file mode 100644 index 0000000..9a12919 --- /dev/null +++ b/venv/lib/python3.7/site-packages/easydev/misc.py @@ -0,0 +1,81 @@ +# -*- python -*- +# -*- coding: utf-8 -*- +# +# This file is part of the easydev software +# +# Copyright (c) 2011-2017 +# +# File author(s): Thomas Cokelaer +# +# Distributed under the GPLv3 License. +# See accompanying file LICENSE.txt or copy at +# http://www.gnu.org/licenses/gpl-3.0.html +# +# Website: https://github.com/cokelaer/easydev +# Documentation: http://easydev-python.readthedocs.io +# +############################################################################## +import os + + +__all__ = ["get_home", "cmd_exists"] + + +def get_home(): + """Return path of the HOME""" + # This function should be robust + # First, let us try with expanduser + try: + homedir = os.path.expanduser("~") + except ImportError: # pragma: no cover + # This may happen. + pass + else: + if os.path.isdir(homedir): + return homedir + # Then, with getenv + for this in ("HOME", "USERPROFILE", "TMP"): # pragma: no cover + # getenv is same as os.environ.get + homedir = os.environ.get(this) + if homedir is not None and os.path.isdir(homedir): + return homedir + + +def cmd_exists(cmd): + """Return true if the command do exists in the environement""" + try: + import subprocess + + # for unix/max only + result = subprocess.call( + "type " + cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) + if result == 0: + return True + else: + return False + except Exception: # pragma: no cover + # If subprocess is not found, we assume it exists. + # This choice ensure that if it fails, we keep going. + return True + + +def in_ipynb(): + """Checks if we are in an ipython notebook + + :return: True if in an ipython notebook otherwise returns False + + """ + try: # pragma: no cover + cfg = get_ipython().config + if ( + "parent_appname" in cfg["IPKernelApp"].keys() + and cfg["IPKernelApp"]["parent_appname"] == "ipython-notebook" + ): + return True + elif "connection_file" in cfg["IPKernelApp"].keys(): + if "jupyter" in cfg["IPKernelApp"]["connection_file"]: + return True + return False + except NameError: # pragma: no cover + return False diff --git a/venv/lib/python3.7/site-packages/easydev/multicore.py b/venv/lib/python3.7/site-packages/easydev/multicore.py new file mode 100644 index 0000000..dc0e839 --- /dev/null +++ b/venv/lib/python3.7/site-packages/easydev/multicore.py @@ -0,0 +1,150 @@ +# -*- python -*- +# -*- coding: utf-8 -*- +# +# This file is part of the easydev software +# +# Copyright (c) 2011-2017 +# +# File author(s): Thomas Cokelaer +# +# Distributed under the GPLv3 License. +# See accompanying file LICENSE.txt or copy at +# http://www.gnu.org/licenses/gpl-3.0.html +# +# Website: https://github.com/cokelaer/easydev +# Documentation: http://easydev-python.readthedocs.io +# +############################################################################## +import time +from multiprocessing import cpu_count, Process, Queue, Pool + +__all__ = ["MultiProcessing"] + + +class MultiProcessing(object): + """Class to run jobs in an asynchronous manner. + + You would use this class to run several jobs on a local computer that has + several cpus. + + + :: + + t = MultiProcessing(maxcpu=2) + t.add_job(func, func_args) + t.run() + t.results[0] # contain returned object from the function *func*. + + + .. warning:: the function must be a function, not a method. This is inherent + to multiprocess in the multiprocessing module. + + .. warning:: the order in the results list may not be the same as the + list of jobs. see :meth:`run` for details + + + """ + + def __init__(self, maxcpu=None, verbose=False, progress=True): + """ + + :param maxcpu: default returned by multiprocessing.cpu_count() + :param verbose: print the output of each job. Could be very verbose + so we advice to keep it False. + :param progress: shows the progress + + + """ + if maxcpu == None: + maxcpu = cpu_count() + + self.maxcpu = maxcpu + self.reset() + self.verbose = verbose + self.progress = progress + + def reset(self): + """remove joves and results""" + self.jobs = [] # a list of processes + self.results = Queue() # the results to append + + def add_job(self, func, *args, **kargs): + """add a job in the pool""" + if self.verbose: + print( + "Adding jobs in the queue..", + ) + t = Process(target=func, args=args, kwargs=kargs) + self.jobs.append(t) + + def _cb(self, results): + if self.verbose is True: + print("callback", results) + if self.progress is True: + self.pb.animate(len(self.results) + 1) + self.results.append(results) + + def run(self, delay=0.1, verbose=True): + """Run all the jobs in the Pool until all have finished. + + Jobs that have been added to the job list in :meth:`add_job` + are now processed in this method by using a Pool. Here, we add + all jobs using the apply_async method from multiprocess module. + + In order to ensure that the jobs are run sequentially in the same + order as in :attr:`jobs`, we introduce a delay between 2 calls + to apply_async (see http://docs.python.org/2/library/multiprocessing.html) + + A better way may be t use a Manager but for now, this works. + + """ + from easydev import Progress + + if self.progress is True: + self.pb = Progress(len(self.jobs), 1) + self.pb.animate(0) + + def init_worker(): + import signal + + signal.signal(signal.SIGINT, signal.SIG_IGN) + + self.results = [] + self.pool = Pool(self.maxcpu, init_worker) + + for process in self.jobs: + self.pool.apply_async( + process._target, process._args, process._kwargs, callback=self._cb + ) + + # ensure the results have same order as jobs + # maybe important if you expect the order of the results to + # be the same as inut; otherwise set delay to 0 + time.sleep(delay) + + try: + while True: + time.sleep(1) + # check if all processes are finished. + # if so, finished. + count = len(self.results) + if count == len(self.jobs): + break + + except KeyboardInterrupt: # pragma: no cover + print( + "\nCaught interruption. " + "Terminating the Pool of processes... ", + ) + self.pool.terminate() + self.pool.join() + print("... done") + else: + # Closing properly the pool + self.pool.close() + self.pool.join() + + # Pool cannot be pickled. So, if we want to pickel "MultiProcessing" + # class itself, we must desctroy this instance + del self.pool + + self.finished = True diff --git a/venv/lib/python3.7/site-packages/easydev/options.py b/venv/lib/python3.7/site-packages/easydev/options.py new file mode 100644 index 0000000..251ebf0 --- /dev/null +++ b/venv/lib/python3.7/site-packages/easydev/options.py @@ -0,0 +1,55 @@ +# -*- python -*- +# -*- coding: utf-8 -*- +# +# This file is part of the easydev software +# +# Copyright (c) 2011-2017 +# +# File author(s): Thomas Cokelaer +# +# Distributed under the GPLv3 License. +# See accompanying file LICENSE.txt or copy at +# http://www.gnu.org/licenses/gpl-3.0.html +# +# Website: https://github.com/cokelaer/easydev +# Documentation: http://easydev-python.readthedocs.io +# +############################################################################## +import argparse + + +__all__ = ["SmartFormatter"] + + +class SmartFormatter(argparse.HelpFormatter): + """Formatter to be used with argparse ArgumentParser + + When using the argparse Python module one can design + standalone applications with arguments (e.g; --help, --verbose...) + + One can easily define the help as well. However, The help message + is wrapped by ArgumentParser, removing all formatting in the process. + The reason being that the entire documentation is consistent. + + Sometines, one want to keep the format. This class can be used to + do that. + + Example:: + + import argparse + from easydev import SmartFormatter + class Options(argparse.ArgumentParser): + def __init__(self, prog="sequana"): + usage = "blabla" + description="blabla" + super(Options, self).__init__(usage=usage, prog=prog, + description=description, + formatter_class=SmartFormatter) + + """ + + def _split_lines(self, text, width): + if text.startswith("FORMAT|"): + return text[7:].splitlines() + # this is the RawTextHelpFormatter._split_lines + return argparse.HelpFormatter._split_lines(self, text, width) diff --git a/venv/lib/python3.7/site-packages/easydev/paths.py b/venv/lib/python3.7/site-packages/easydev/paths.py new file mode 100644 index 0000000..8fc758e --- /dev/null +++ b/venv/lib/python3.7/site-packages/easydev/paths.py @@ -0,0 +1,141 @@ +############################################################################## +# -*- python -*- +# -*- coding: utf-8 -*- +# +# This file is part of the easydev software +# +# Copyright (c) 2011-2017 +# +# File author(s): Thomas Cokelaer +# +# Distributed under the GPLv3 License. +# See accompanying file LICENSE.txt or copy at +# http://www.gnu.org/licenses/gpl-3.0.html +# +# Website: https://github.com/cokelaer/easydev +# Documentation: http://easydev-python.readthedocs.io +# +############################################################################## +# $:Id $ +"""Utilities to ease access to share data paths""" +import os +from os.path import join as pj +import pkg_resources + + +__all__ = [ + "get_shared_directory_path", + "get_shared_directories", + "get_share_file", + "gsf", + "get_package_location", +] + + +def get_package_location(package): + """Return physical location of a package""" + try: + info = pkg_resources.get_distribution(package) + location = info.location + except pkg_resources.DistributionNotFound as err: + print("package provided (%s) not installed." % package) + raise + return location + + +def get_shared_directory_path(package): + """Returns the share directory path of an installed package + + + :: + + sharedir = get_shared_directory_path("easydev") + + + """ + location = get_package_location(package) + + # print("install mode ? ") + sharedir = os.path.realpath(pj(location, package, "share")) + if os.path.isdir(sharedir) == True: + # looks like we have found the share directory so it is an install mode + # print ("yes") + return sharedir + else: # pragma: no cover + # print("no. searching for share dir as if in develop mode") + # let us try a couple of directories + # FIXME: do we need the 3 cases ?? + # probably just 2 are required, one for develop and one for install mode + sharedir = os.path.realpath(pj(location, "..", "share")) + if os.path.isdir(sharedir) == True: + return sharedir + sharedir = os.path.realpath(pj(location, "..", "..", "share")) + if os.path.isdir(sharedir) == True: + return sharedir + sharedir = os.path.realpath(pj(location, "..", "..", "..", "share")) + if os.path.isdir(sharedir) == True: + return sharedir + # could not be found, + sharedir = [] + print("could not find any share directory in %s" % package) + + return sharedir + + +def get_shared_directories(package, datadir="data"): + """Returns all directory paths found in the package share/datadir directory + + :param str datadir: scans package/share/ where datadir is "data" by + default. If it does not exists, the list returned is empty. + + .. doctest:: + + >>> from easydev import get_shared_directories + >>> shared_directories = get_shared_directories("easydev", "themes") + >>> len(shared_directories)>=2 + True + + """ + packagedir = get_shared_directory_path(package) + if len(packagedir) == 0: # pragma: no cover + return [] + packagedir = pj(packagedir, datadir) + directories = os.listdir(packagedir) + + # get rid of .svn (for the packages installed with develop) + directories_to_process = [] + for directory in directories: + fullpath = os.path.join(packagedir, directory) + if directory != ".svn" and os.path.isdir(fullpath): + directories_to_process.append(fullpath) + directories_to_process.sort() + return directories_to_process + + +def gsf(package, datadir, filename): + return get_share_file(package, datadir, filename) + + +def get_share_file(package, datadir, filename): + """Creates the full path of a file to be found in the share directory of a package""" + packagedir = get_shared_directory_path(package) + fullpath = os.path.join(packagedir, datadir) + # check that it exists + if os.path.isdir(fullpath) == False: # pragma: no cover + raise ValueError( + "The directory %s in package %s does not seem to exist" + % (packagedir, fullpath) + ) + filename_path = os.path.join(fullpath, filename) + if os.path.isfile(filename_path) == False: + correct_files = [x for x in os.listdir(fullpath) if os.path.isfile(x)] + msg = "The file %s does not exists. Correct filenames found in %s/%s are:\n" % ( + filename_path, + package, + datadir, + ) + for f in correct_files: # pragma: no cover + msg += "%s\n" % f + + raise ValueError(msg) + return filename_path diff --git a/venv/lib/python3.7/site-packages/easydev/platform.py b/venv/lib/python3.7/site-packages/easydev/platform.py new file mode 100644 index 0000000..d759088 --- /dev/null +++ b/venv/lib/python3.7/site-packages/easydev/platform.py @@ -0,0 +1,80 @@ +# -*- python -*- +# -*- coding: utf-8 -*- +# +# This file is part of the easydev software +# +# Copyright (c) 2011-2017 +# +# File author(s): Thomas Cokelaer +# +# Distributed under the GPLv3 License. +# See accompanying file LICENSE.txt or copy at +# http://www.gnu.org/licenses/gpl-3.0.html +# +# Website: https://github.com/cokelaer/easydev +# Documentation: http://easydev-python.readthedocs.io +# +############################################################################## +from __future__ import absolute_import # avoids conflict with standard module +import os +import sys + +__all__ = ["get_platform"] + + +def get_platform(): # pragma: no cover + """Identify the platform (Linux/windows/Mac) + + The folloing modules/functions can be used to identify the platform: + platform, sys.platform, os.name, os.environ. We use platform and return + the content of platform.system. If it does not work, sys.platform is used + and sys.platform output interpreted: linux, java, win and darwin are + searched for and returned aas Linux, Java, Windows, Darwin to be consistent + with the output of platform.syste. If those strings are not found, + just return the output of sys.platform. + + :return: 'Linux' or 'Windows' or 'Darwin', 'Java' if platform + can be determined otherwise, the content of sys.platform() + + """ + print("//easydev.get_platform() Will be deprecated in future version of easydev") + try: + platform = plf.system() + return platform + except: + # platform is not available under all systems (e.g., WLST tool + # see http://stackoverflow.com/questions/1854/python-what-os-am-i-running-on + # so, let us try sys.platform + platform = sys.platform + if platform.startswith("linux"): + platform = "Linux" + elif platform.startswith("java"): + platform = "Java" + elif platform.startswith("win"): + platform = "Windows" + elif platform.startswith("darwin"): + platform = "Darwin" + else: + print("platform not parsed. Return raw value of sys.platform.") + return platform + + +def is_windows(): + if get_platform() == "Windows": + return True + else: + return False + + +def is_linux(): + if get_platform() == "Linux": + return True + else: + return False + + +def is_mac(): + if get_platform() == "Mac": + return True + else: + return False diff --git a/venv/lib/python3.7/site-packages/easydev/profiler.py b/venv/lib/python3.7/site-packages/easydev/profiler.py new file mode 100644 index 0000000..de6e079 --- /dev/null +++ b/venv/lib/python3.7/site-packages/easydev/profiler.py @@ -0,0 +1,76 @@ +# -*- python -*- +# -*- coding: utf-8 -*- +# +# This file is part of the easydev software +# +# Copyright (c) 2011-2017 +# +# File author(s): Thomas Cokelaer +# +# Distributed under the GPLv3 License. +# See accompanying file LICENSE.txt or copy at +# http://www.gnu.org/licenses/gpl-3.0.html +# +# Website: https://github.com/cokelaer/easydev +# Documentation: http://easydev-python.readthedocs.io +# +############################################################################## +""" + +Usage:: + + from easydev import do_profile + @do_profile() + def test(): + x = 1 + x *= x + return x + +# source: https://zapier.com/engineering/profiling-python-boss/ + + +Requires line_profiler to be installed. line_profiler has C code and we wish +easydev to be as simple as possible. So, we do not want compiled code dependencies. +Consequently, we added line_profiler in the extra_requires instead of requires +in the setup.py One must install line_profiler itself. +""" + +__all__ = ["do_profile"] + +try: + from line_profiler import LineProfiler + + def do_profile(follow=[]): + def inner(func): + def profiled_func(*args, **kwargs): + try: + profiler = LineProfiler() + profiler.add_function(func) + for f in follow: + profiler.add_function(f) + profiler.enable_by_count() + return func(*args, **kwargs) + finally: + profiler.print_stats() + + return profiled_func + + return inner + + +except ImportError: # pragma: no cover + + def do_profile(follow=[]): + "Helpful if you accidentally leave in production!" + print( + "easydev warning:: line_profiler does not seem to be installed. " + + "Type 'pip install line_profiler'" + ) + + def inner(func): + def nothing(*args, **kwargs): + return func(*args, **kwargs) + + return nothing + + return inner diff --git a/venv/lib/python3.7/site-packages/easydev/progressbar.py b/venv/lib/python3.7/site-packages/easydev/progressbar.py new file mode 100644 index 0000000..ea973ed --- /dev/null +++ b/venv/lib/python3.7/site-packages/easydev/progressbar.py @@ -0,0 +1,217 @@ +# -*- python -*- +# -*- coding: utf-8 -*- +# +# This file is part of the easydev software +# +# Copyright (c) 2011-2017 +# +# File author(s): Thomas Cokelaer +# +# Distributed under the GPLv3 License. +# See accompanying file LICENSE.txt or copy at +# http://www.gnu.org/licenses/gpl-3.0.html +# +# Website: https://github.com/cokelaer/easydev +# Documentation: http://easydev-python.readthedocs.io +# +############################################################################## +"""A progress bar copied and adapted from pyMC code (dec 2014)""" +from __future__ import print_function + +import sys +import time +import uuid + + +try: + from IPython.core.display import HTML, Javascript, display +except ImportError: # pragma: no cover + pass + + +__all__ = ["progress_bar", "TextProgressBar", "Progress"] + + +class ProgressBar(object): + def __init__(self, iterations, interval=None): + self.iterations = iterations + # if no interval provided, set it to 1% + if interval is None: + if iterations <= 100: + interval = 1 # everything % of the data + else: + interval = int(iterations / 100) + self.interval = interval + self.start = time.time() + self.last = 0 + + def _percentage(self, i): + if self.iterations != 0: + return 100 * i / float(self.iterations) + else: # pragma: no cover + # could be 100 ? + return 100 + + def _get_elapsed(self): + return time.time() - self.start + + elapsed = property(_get_elapsed) + + +class TextProgressBar(ProgressBar): + """Use :class:`Progress`""" + + def __init__(self, iterations, printer, width=40, interval=None): + self.fill_char = "-" + self.width = width + self.printer = printer + ProgressBar.__init__(self, iterations, interval=interval) + + def animate(self, i, dummy=None): + # dummy=None is for back-compatibility + if dummy is not None: # pragma: no cover + print( + "second argument in easydev.progress_bar.animate is deprecated. Update your code" + ) + # +1 if i starts at 0 and finishes at N-1 + if divmod(i, self.interval)[1] != 0 and i != self.iterations: + pass + else: + self.printer(self.progbar(i)) + + def progbar(self, i): + # +1 if i starts at 0 and finishes at N-1 + bar = self.bar(self._percentage(i)) + return "[%s] %i of %i complete in %.1f sec" % ( + bar, + (i), + self.iterations, + round(self.elapsed, 1), + ) + + def bar(self, percent): + all_full = self.width - 2 + num_hashes = int(percent / 100 * all_full) + + bar = self.fill_char * num_hashes + " " * (all_full - num_hashes) + + info = "%d%%" % percent + loc = (len(bar) - len(info)) // 2 + return replace_at(bar, info, loc, loc + len(info)) + + +def replace_at(str, new, start, stop): + return str[:start] + new + str[stop:] + + +def consoleprint(s): + if sys.platform.lower().startswith("win"): + print(s, "\r", end="") + else: + print("\r", s, end="") + sys.stdout.flush() + + +def ipythonprint(s): # pragma no cover + print("\r", s, end="") + sys.stdout.flush() + + +class IPythonNotebookPB(ProgressBar): # pragma: no cover + """Use :class:`Progress`""" + + def __init__(self, iterations, interval=None): + self.divid = str(uuid.uuid4()) + self.sec_id = str(uuid.uuid4()) + + pb = HTML( + """ +
+
 
+
+