From 8eabe0f3dcf3350e0ede4f9bc60b3323a8bc0032 Mon Sep 17 00:00:00 2001 From: Ahmed Refik Date: Tue, 4 Apr 2023 23:58:13 -0600 Subject: [PATCH 01/46] [MP-139] argparse loop --- CLI/cli.py | 15 +-------------- NodeE | 2 +- Xceed | 2 +- nodeE-Zybo-Z20 | 2 +- 4 files changed, 4 insertions(+), 17 deletions(-) diff --git a/CLI/cli.py b/CLI/cli.py index 8f2697b..3078ed5 100755 --- a/CLI/cli.py +++ b/CLI/cli.py @@ -216,21 +216,8 @@ def UserInput(self) -> bool: else: log_level = logging.ERROR -<<<<<<< Updated upstream if not args.logging: LOG_FILE = None -======= - json = \ - { - "task_id": 1, - "sender_id": 1 - } - - if args.verbose: - log_level = logging.DEBUG - else: - log_level = logging.ERROR ->>>>>>> Stashed changes # configure logging to user preference logger = Logger.init(LOG_FILE, log_level) @@ -291,4 +278,4 @@ def UserInput(self) -> bool: if __name__ == "__main__": parser = QPP_parser() - parser.UserInput() \ No newline at end of file + parser.UserInput() diff --git a/NodeE b/NodeE index f518512..6739db5 160000 --- a/NodeE +++ b/NodeE @@ -1 +1 @@ -Subproject commit f518512f76d1c1410aea8d91f63c08e799458f9d +Subproject commit 6739db58d089b52fa6f0cee96caad657a9a6164f diff --git a/Xceed b/Xceed index c8e2583..dfb3e23 160000 --- a/Xceed +++ b/Xceed @@ -1 +1 @@ -Subproject commit c8e2583e7ff5493553e5c615e4e45f77714229a8 +Subproject commit dfb3e2304552d069fb22d7ff405ea4fed05b7cef diff --git a/nodeE-Zybo-Z20 b/nodeE-Zybo-Z20 index fe5b4af..5f1abcd 160000 --- a/nodeE-Zybo-Z20 +++ b/nodeE-Zybo-Z20 @@ -1 +1 @@ -Subproject commit fe5b4af51bf323f520bcd7a3ad6a27ad0330e5af +Subproject commit 5f1abcdf55ec7b35d1f9fc0769a3b6343eb2b8c1 From c5287b262918f67fbb2be79a7c247429703ba954 Mon Sep 17 00:00:00 2001 From: Ahmed Refik Date: Wed, 5 Apr 2023 00:02:24 -0600 Subject: [PATCH 02/46] [MP-139] multithreading basics --- CLI/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CLI/cli.py b/CLI/cli.py index 3078ed5..2824f25 100755 --- a/CLI/cli.py +++ b/CLI/cli.py @@ -278,4 +278,4 @@ def UserInput(self) -> bool: if __name__ == "__main__": parser = QPP_parser() - parser.UserInput() + parser.UserInput() \ No newline at end of file From 56051c3b6ff2fb8923600df1503820fe93f95ab2 Mon Sep 17 00:00:00 2001 From: Ahmed Refik Date: Fri, 31 Mar 2023 17:01:50 -0600 Subject: [PATCH 03/46] [MP-139] multithreading fix --- CLI/cli.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/CLI/cli.py b/CLI/cli.py index 2824f25..8f2697b 100755 --- a/CLI/cli.py +++ b/CLI/cli.py @@ -216,8 +216,21 @@ def UserInput(self) -> bool: else: log_level = logging.ERROR +<<<<<<< Updated upstream if not args.logging: LOG_FILE = None +======= + json = \ + { + "task_id": 1, + "sender_id": 1 + } + + if args.verbose: + log_level = logging.DEBUG + else: + log_level = logging.ERROR +>>>>>>> Stashed changes # configure logging to user preference logger = Logger.init(LOG_FILE, log_level) From 149a2b9c0afe895c9802e9859a8264e55864b02f Mon Sep 17 00:00:00 2001 From: Ahmed Refik Date: Tue, 4 Apr 2023 14:11:34 -0600 Subject: [PATCH 04/46] [MP-144] website file upload, GI encrypt send, and test vector functions done --- .gitignore | 6 ++- docs/flask/Client.py | 85 ++++++++++++++++++++++++++++++++ docs/flask/GI_interface.py | 80 ++++++++++++++++++++++++++++++ docs/flask/app.py | 78 +++++++++++++++++++++++++++-- docs/flask/templates/calc.html | 89 +++++++++++++++++++++++++--------- docs/flask/templates/view.html | 44 +++++++++++++++++ 6 files changed, 353 insertions(+), 29 deletions(-) create mode 100644 docs/flask/Client.py create mode 100644 docs/flask/GI_interface.py mode change 100644 => 100755 docs/flask/app.py create mode 100644 docs/flask/templates/view.html diff --git a/.gitignore b/.gitignore index bdd97bd..ef6a5cc 100644 --- a/.gitignore +++ b/.gitignore @@ -11,9 +11,13 @@ # UI test folder /docs/examples -# flask venv and pycache +# flask venv, pycache, and user uploaded files /docs/flask/venv/ /docs/flask/__pycache__/ +/docs/flask/downloads/ # ccls language server cache /.ccls-cache/ + +# pycache for CLI +/CLI/__pycache__/ diff --git a/docs/flask/Client.py b/docs/flask/Client.py new file mode 100644 index 0000000..27e78d3 --- /dev/null +++ b/docs/flask/Client.py @@ -0,0 +1,85 @@ +import socket +import queue +import json +import threading +from threading import Lock +import time +""" +Steps: +Get the user interaction +Make sure connection is established between CLI and Core +Send some message +Separate send message and listening into 2 threads +""" + + +class Client: + + def __init__(self, port, host, name): + self.s = socket.socket() + self.port = port + self.host = host + self.name = name + self.recv_queue = queue.Queue() + self.send_queue = queue.Queue() + # self.mutex = Lock() + # self.outgoing_mutex = Lock() + + def connection_setup(self): + self.s.connect((self.host, self.port)) + + # SEND the handshake + # msg = self.string_to_json("REQUEST_HANDSHAKE", "0") + # self.send_queue.put(msg) + # self.send_queue.put('\n') + # # self.connection_send(msg) + # self.connection_send('\n') + + def connection_send(self): + message = self.send_queue.get() + print(message) + self.s.send(message.encode('ascii')) + print("sent") + + + def to_outgoing_queue(self, message): + # self.outgoing_mutex.acquire() + self.send_queue.put(message) + print("OUTGOING QUEUE {}".format(message)) + # self.outgoing_mutex.release() + + + + def connection_recv(self): + msg = self.s.recv(4096) + self.recv_queue.put(msg) + print("Received from server: " + msg.decode('ascii')) + + def print_outgoing_queue(self): + # self.mutex.acquire() + if not self.recv_queue.empty(): + data = self.recv_queue.get() + # self.mutex.release() + return data + + else: + # self.mutex.release() + return None + + + @staticmethod + def string_to_json(api_call, task_id, interface_type="GC", sender_id="0", + payload_total_fragments="0", payload_fragment_number="0", + payload_size="0", payload_content="Hello World"): + msg = {"api_call":api_call, + "task_id":task_id, + "interface_type":interface_type, + "sender_id":sender_id, + "payload_total_fragments": payload_total_fragments, + "payload_fragment_number": payload_fragment_number, + "payload_size": payload_size, + "payload_content": payload_content} + + return json.dumps(msg) + + diff --git a/docs/flask/GI_interface.py b/docs/flask/GI_interface.py new file mode 100644 index 0000000..b8de67d --- /dev/null +++ b/docs/flask/GI_interface.py @@ -0,0 +1,80 @@ +from Client import * +import random +import logging +import sys +LOG_FILE = "out.log" + + +class Logger: + def init(file, level) -> None: + + # to format the log messages that are printed to terminal + logging.basicConfig( + format="%(levelname)s : %(name)s : %(funcName)s[line %(lineno)s]" + + ": %(message)s" + ) + + # create logger instance + log = logging.getLogger(__name__) + + # set log level + log.setLevel(level) + + # format the .log file messages + if file is not None: + file_handler = logging.FileHandler(file) + formatter = logging.Formatter( + "%(asctime)s : %(levelname)s : Module %(module)s" + + ": %(funcName)s[line %(lineno)s] : %(name)s : %(message)s" + ) + file_handler.setFormatter(formatter) + log.addHandler(file_handler) + + return log + + +class Commands: + def __init__(self, logger) -> None: + self.logger = logger + return + + @staticmethod + def random_string(len): + random_string = "" + for ii in range(len): + random_int = random.randint(33, 126) + random_string += chr(random_int) + return random_string + + @staticmethod + def test_vector_gen(args): + vec_len = args[0] + vec_num = args[1] + test_list = [""] * vec_num + for ii in range(vec_num): + test_list[ii] = Commands.random_string(vec_len) + return test_list + + +class GI: + @staticmethod + def Encrypt(task_id, str_list, client): +# logger = Logger.init(LOG_FILE, logging.DEBUG) + for vector in str_list: + msg = client.string_to_json("ENCRYPT", str(task_id), "GI", "0", "0", "0", str(sys.getsizeof(vector)), vector) + client.to_outgoing_queue(msg) + client.connection_send() + return + + @staticmethod + def ReceivedEncrypt(): + return + + @staticmethod + def Decrypt(): + return + + @staticmethod + def ReceivedDecrypt(): + return + diff --git a/docs/flask/app.py b/docs/flask/app.py old mode 100644 new mode 100755 index c26d260..5b1acfc --- a/docs/flask/app.py +++ b/docs/flask/app.py @@ -1,4 +1,10 @@ -from flask import Flask, render_template +#! /usr/bin/env python3 + +import os +from flask import Flask, flash, render_template, request, redirect, session +from werkzeug.utils import secure_filename +from Client import * +from GI_interface import * app = Flask(__name__) algos = ['AES', 'QPP', 'AES & QPP'] @@ -7,6 +13,21 @@ 'Brute Force', 'Monte Carlo', 'Multi-block Message', 'Known Answer' ] +UPLOAD_FOLDER = "downloads/" +ALLOWED_EXTENSIONS = {'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'} +GI_PORT = 64999 +vector_select = "True" +vector_len = 100 +vector_num = 100 + +client = Client(GI_PORT, "127.0.0.1", "CLI") +client.connection_setup() +task_id = 0 + + +def allowed_file(filename): + return '.' in filename and \ + filename.split('.', 1)[1].lower() in ALLOWED_EXTENSIONS @app.route("/") @@ -30,11 +51,58 @@ def demo(): return render_template('demo.html') -@app.route("/calc/") +@app.route("/calc/", methods=['GET', 'POST']) def calc(): - return render_template('calc.html', algo_list=algos, test_list=tests) + global vector_select, algos, tests, vector_num, vector_len + if request.method == "POST": + print("ERROR 1") + vector_select = request.form['vector_select'] + + return render_template('calc.html', + algo_list=algos, + test_list=tests, + vector_select=vector_select) -@app.route("/view/") +@app.route("/view/", methods=['GET', 'POST']) def view(): - return render_template('view.html') + global vector_select, algos, tests, vector_num, vector_len, task_id + test = algo = "ERROR" + test_vector = [] + + if request.method == 'POST': + algo_select = request.form.get('algo_select') + test_select = request.form.get('test_select') + print("ERROR 2") + print("{}, {}".format(algo_select, test_select)) + if vector_select == "True": + print("ERROR 3") + vector_len = request.form["vector_len"] + vector_num = request.form["vector_num"] + print("{} vectors of size {}".format(vector_num, vector_len)) + test_vector = Commands.test_vector_gen([int(vector_len), int(vector_num)]) + else: + print("ERROR 4") + if 'file' not in request.files: + flash("No File Uploaded") + file = request.files['file'] + if file.filename == '': + flash("No File Selected") + if file and allowed_file(file.filename): + filename = secure_filename(file.filename) + file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename)) + with open(os.path.abspath(UPLOAD_FOLDER + filename), 'r') as fd: + file_contents = fd.read() + print("{}, contents:\n{}".format(filename, file_contents)) + test_vector = [file_contents] + GI.Encrypt(task_id, test_vector, client) + task_id += 1 + + return render_template('view.html', test=test, algo=algo) + + +if __name__ == "__main__": + app.config['UPLOAD_FOLDER'] = os.path.abspath(UPLOAD_FOLDER) + app.secret_key = 'secret_key' + app.config['SESSION_TYPE'] = 'filesystem' + app.run(debug=True, host="127.0.0.1", port=4996) diff --git a/docs/flask/templates/calc.html b/docs/flask/templates/calc.html index a9dfb53..5995b08 100644 --- a/docs/flask/templates/calc.html +++ b/docs/flask/templates/calc.html @@ -11,28 +11,71 @@ {% block content %} -
- - -
- - -
- - -
- -
- - -
+
+ {% if vector_select == "True" %} + + {% else %} + + {% endif %} +
+ +{% if vector_select == "True" %} +
+ + + + +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + {% else %} +
+
+ + +
+ +
+ + +
+ + File Upload + +
+ {% endif %} {% endblock %} diff --git a/docs/flask/templates/view.html b/docs/flask/templates/view.html new file mode 100644 index 0000000..09af7a4 --- /dev/null +++ b/docs/flask/templates/view.html @@ -0,0 +1,44 @@ +{% extends 'base.html' %} + +{% block title %} QPP Results {% endblock %} +{% block header %} Results {% endblock %} + +{% block header_content %} +

This is the description for this page

+{% endblock %} + + +{% block content %} + +
+ + +
+ + +
+ + +
+ +
+ + +
+ + +
+

Test: {{ test }}

+

Algorithim: {{ algo }}

+ +
+ +{% endblock %} From 12f5bac4a277285de4ced6e71c26e882e27ae0ef Mon Sep 17 00:00:00 2001 From: Ahmed Refik Date: Tue, 4 Apr 2023 14:18:07 -0600 Subject: [PATCH 05/46] [MP-144] merging with stashes --- .gitignore | 5 + .../flask/__pycache__/app.cpython-311.pyc | Bin 0 -> 5129 bytes docs/examples/flask/app.py | 97 + docs/examples/flask/index.html | 91 + docs/examples/flask/scripts/cal.js | 13 + docs/examples/flask/scripts/web.js | 25 + .../flask/static/main.css} | 0 docs/examples/flask/templates/about.html | 6 + docs/examples/flask/templates/api.html | 37 + docs/{ => examples}/flask/templates/base.html | 19 +- docs/examples/flask/templates/calc.html | 60 + docs/examples/flask/templates/demo.html | 46 + .../{ => examples}/flask/templates/index.html | 6 +- docs/examples/flask/templates/research.html | 46 + docs/examples/flask/templates/view.html | 14 + docs/examples/flask/venv/bin/Activate.ps1 | 247 + docs/examples/flask/venv/bin/activate | 69 + docs/examples/flask/venv/bin/activate.csh | 26 + docs/examples/flask/venv/bin/activate.fish | 69 + docs/examples/flask/venv/bin/flask | 8 + docs/examples/flask/venv/bin/pip | 8 + docs/examples/flask/venv/bin/pip3 | 8 + docs/examples/flask/venv/bin/pip3.11 | 8 + docs/examples/flask/venv/bin/python | 1 + docs/examples/flask/venv/bin/python3 | 1 + docs/examples/flask/venv/bin/python3.11 | 1 + .../Flask-2.2.3.dist-info/INSTALLER | 1 + .../Flask-2.2.3.dist-info/LICENSE.rst | 28 + .../Flask-2.2.3.dist-info/METADATA | 123 + .../Flask-2.2.3.dist-info/RECORD | 54 + .../Flask-2.2.3.dist-info/REQUESTED | 0 .../site-packages/Flask-2.2.3.dist-info/WHEEL | 5 + .../Flask-2.2.3.dist-info/entry_points.txt | 2 + .../Flask-2.2.3.dist-info/top_level.txt | 1 + .../Jinja2-3.1.2.dist-info/INSTALLER | 1 + .../Jinja2-3.1.2.dist-info/LICENSE.rst | 28 + .../Jinja2-3.1.2.dist-info/METADATA | 113 + .../Jinja2-3.1.2.dist-info/RECORD | 58 + .../Jinja2-3.1.2.dist-info/WHEEL | 5 + .../Jinja2-3.1.2.dist-info/entry_points.txt | 2 + .../Jinja2-3.1.2.dist-info/top_level.txt | 1 + .../MarkupSafe-2.1.2.dist-info/INSTALLER | 1 + .../MarkupSafe-2.1.2.dist-info/LICENSE.rst | 28 + .../MarkupSafe-2.1.2.dist-info/METADATA | 98 + .../MarkupSafe-2.1.2.dist-info/RECORD | 14 + .../MarkupSafe-2.1.2.dist-info/WHEEL | 6 + .../MarkupSafe-2.1.2.dist-info/top_level.txt | 1 + .../Werkzeug-2.2.3.dist-info/INSTALLER | 1 + .../Werkzeug-2.2.3.dist-info/LICENSE.rst | 28 + .../Werkzeug-2.2.3.dist-info/METADATA | 126 + .../Werkzeug-2.2.3.dist-info/RECORD | 98 + .../Werkzeug-2.2.3.dist-info/WHEEL | 5 + .../Werkzeug-2.2.3.dist-info/top_level.txt | 1 + .../site-packages/_distutils_hack/__init__.py | 222 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 11198 bytes .../__pycache__/override.cpython-311.pyc | Bin 0 -> 355 bytes .../site-packages/_distutils_hack/override.py | 1 + .../click-8.1.3.dist-info/INSTALLER | 1 + .../click-8.1.3.dist-info/LICENSE.rst | 28 + .../click-8.1.3.dist-info/METADATA | 111 + .../click-8.1.3.dist-info/RECORD | 39 + .../site-packages/click-8.1.3.dist-info/WHEEL | 5 + .../click-8.1.3.dist-info/top_level.txt | 1 + .../site-packages/click/__init__.py | 73 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 3716 bytes .../click/__pycache__/_compat.cpython-311.pyc | Bin 0 -> 28479 bytes .../__pycache__/_termui_impl.cpython-311.pyc | Bin 0 -> 32620 bytes .../__pycache__/_textwrap.cpython-311.pyc | Bin 0 -> 2677 bytes .../__pycache__/_winconsole.cpython-311.pyc | Bin 0 -> 13367 bytes .../click/__pycache__/core.cpython-311.pyc | Bin 0 -> 140811 bytes .../__pycache__/decorators.cpython-311.pyc | Bin 0 -> 23537 bytes .../__pycache__/exceptions.cpython-311.pyc | Bin 0 -> 16097 bytes .../__pycache__/formatting.cpython-311.pyc | Bin 0 -> 15722 bytes .../click/__pycache__/globals.cpython-311.pyc | Bin 0 -> 3404 bytes .../click/__pycache__/parser.cpython-311.pyc | Bin 0 -> 23154 bytes .../shell_completion.cpython-311.pyc | Bin 0 -> 23603 bytes .../click/__pycache__/termui.cpython-311.pyc | Bin 0 -> 34708 bytes .../click/__pycache__/testing.cpython-311.pyc | Bin 0 -> 25772 bytes .../click/__pycache__/types.cpython-311.pyc | Bin 0 -> 52861 bytes .../click/__pycache__/utils.cpython-311.pyc | Bin 0 -> 26045 bytes .../python3.11/site-packages/click/_compat.py | 626 ++ .../site-packages/click/_termui_impl.py | 717 ++ .../site-packages/click/_textwrap.py | 49 + .../site-packages/click/_winconsole.py | 279 + .../python3.11/site-packages/click/core.py | 2998 ++++++ .../site-packages/click/decorators.py | 497 + .../site-packages/click/exceptions.py | 287 + .../site-packages/click/formatting.py | 301 + .../python3.11/site-packages/click/globals.py | 68 + .../python3.11/site-packages/click/parser.py | 529 + .../python3.11/site-packages/click/py.typed | 0 .../site-packages/click/shell_completion.py | 580 ++ .../python3.11/site-packages/click/termui.py | 787 ++ .../python3.11/site-packages/click/testing.py | 479 + .../python3.11/site-packages/click/types.py | 1073 ++ .../python3.11/site-packages/click/utils.py | 580 ++ .../site-packages/distutils-precedence.pth | 1 + .../site-packages/flask/__init__.py | 71 + .../site-packages/flask/__main__.py | 3 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 3383 bytes .../__pycache__/__main__.cpython-311.pyc | Bin 0 -> 298 bytes .../flask/__pycache__/app.cpython-311.pyc | Bin 0 -> 100961 bytes .../__pycache__/blueprints.cpython-311.pyc | Bin 0 -> 36887 bytes .../flask/__pycache__/cli.cpython-311.pyc | Bin 0 -> 44007 bytes .../flask/__pycache__/config.cpython-311.pyc | Bin 0 -> 16548 bytes .../flask/__pycache__/ctx.cpython-311.pyc | Bin 0 -> 21055 bytes .../__pycache__/debughelpers.cpython-311.pyc | Bin 0 -> 9441 bytes .../flask/__pycache__/globals.cpython-311.pyc | Bin 0 -> 5165 bytes .../flask/__pycache__/helpers.cpython-311.pyc | Bin 0 -> 31373 bytes .../flask/__pycache__/logging.cpython-311.pyc | Bin 0 -> 3450 bytes .../__pycache__/scaffold.cpython-311.pyc | Bin 0 -> 38354 bytes .../__pycache__/sessions.cpython-311.pyc | Bin 0 -> 18606 bytes .../flask/__pycache__/signals.cpython-311.pyc | Bin 0 -> 3604 bytes .../__pycache__/templating.cpython-311.pyc | Bin 0 -> 11432 bytes .../flask/__pycache__/testing.cpython-311.pyc | Bin 0 -> 14695 bytes .../flask/__pycache__/typing.cpython-311.pyc | Bin 0 -> 3408 bytes .../flask/__pycache__/views.cpython-311.pyc | Bin 0 -> 7703 bytes .../__pycache__/wrappers.cpython-311.pyc | Bin 0 -> 6933 bytes .../lib/python3.11/site-packages/flask/app.py | 2551 +++++ .../site-packages/flask/blueprints.py | 712 ++ .../lib/python3.11/site-packages/flask/cli.py | 1054 ++ .../python3.11/site-packages/flask/config.py | 338 + .../lib/python3.11/site-packages/flask/ctx.py | 438 + .../site-packages/flask/debughelpers.py | 158 + .../python3.11/site-packages/flask/globals.py | 107 + .../python3.11/site-packages/flask/helpers.py | 705 ++ .../site-packages/flask/json/__init__.py | 342 + .../json/__pycache__/__init__.cpython-311.pyc | Bin 0 -> 13800 bytes .../json/__pycache__/provider.cpython-311.pyc | Bin 0 -> 12847 bytes .../json/__pycache__/tag.cpython-311.pyc | Bin 0 -> 16706 bytes .../site-packages/flask/json/provider.py | 310 + .../site-packages/flask/json/tag.py | 312 + .../python3.11/site-packages/flask/logging.py | 74 + .../python3.11/site-packages/flask/py.typed | 0 .../site-packages/flask/scaffold.py | 936 ++ .../site-packages/flask/sessions.py | 419 + .../python3.11/site-packages/flask/signals.py | 56 + .../site-packages/flask/templating.py | 212 + .../python3.11/site-packages/flask/testing.py | 286 + .../python3.11/site-packages/flask/typing.py | 80 + .../python3.11/site-packages/flask/views.py | 188 + .../site-packages/flask/wrappers.py | 171 + .../itsdangerous-2.1.2.dist-info/INSTALLER | 1 + .../itsdangerous-2.1.2.dist-info/LICENSE.rst | 28 + .../itsdangerous-2.1.2.dist-info/METADATA | 97 + .../itsdangerous-2.1.2.dist-info/RECORD | 23 + .../itsdangerous-2.1.2.dist-info/WHEEL | 5 + .../top_level.txt | 1 + .../site-packages/itsdangerous/__init__.py | 19 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 1187 bytes .../__pycache__/_json.cpython-311.pyc | Bin 0 -> 1425 bytes .../__pycache__/encoding.cpython-311.pyc | Bin 0 -> 3012 bytes .../__pycache__/exc.cpython-311.pyc | Bin 0 -> 5028 bytes .../__pycache__/serializer.cpython-311.pyc | Bin 0 -> 13671 bytes .../__pycache__/signer.cpython-311.pyc | Bin 0 -> 12378 bytes .../__pycache__/timed.cpython-311.pyc | Bin 0 -> 10104 bytes .../__pycache__/url_safe.cpython-311.pyc | Bin 0 -> 4032 bytes .../site-packages/itsdangerous/_json.py | 16 + .../site-packages/itsdangerous/encoding.py | 54 + .../site-packages/itsdangerous/exc.py | 107 + .../site-packages/itsdangerous/py.typed | 0 .../site-packages/itsdangerous/serializer.py | 295 + .../site-packages/itsdangerous/signer.py | 257 + .../site-packages/itsdangerous/timed.py | 234 + .../site-packages/itsdangerous/url_safe.py | 80 + .../site-packages/jinja2/__init__.py | 37 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 2157 bytes .../__pycache__/_identifier.cpython-311.pyc | Bin 0 -> 2173 bytes .../__pycache__/async_utils.cpython-311.pyc | Bin 0 -> 4674 bytes .../__pycache__/bccache.cpython-311.pyc | Bin 0 -> 20955 bytes .../__pycache__/compiler.cpython-311.pyc | Bin 0 -> 110516 bytes .../__pycache__/constants.cpython-311.pyc | Bin 0 -> 1592 bytes .../jinja2/__pycache__/debug.cpython-311.pyc | Bin 0 -> 6752 bytes .../__pycache__/defaults.cpython-311.pyc | Bin 0 -> 1758 bytes .../__pycache__/environment.cpython-311.pyc | Bin 0 -> 80574 bytes .../__pycache__/exceptions.cpython-311.pyc | Bin 0 -> 8643 bytes .../jinja2/__pycache__/ext.cpython-311.pyc | Bin 0 -> 43222 bytes .../__pycache__/filters.cpython-311.pyc | Bin 0 -> 75920 bytes .../__pycache__/idtracking.cpython-311.pyc | Bin 0 -> 19578 bytes .../jinja2/__pycache__/lexer.cpython-311.pyc | Bin 0 -> 35653 bytes .../__pycache__/loaders.cpython-311.pyc | Bin 0 -> 33111 bytes .../jinja2/__pycache__/meta.cpython-311.pyc | Bin 0 -> 5737 bytes .../__pycache__/nativetypes.cpython-311.pyc | Bin 0 -> 7999 bytes .../jinja2/__pycache__/nodes.cpython-311.pyc | Bin 0 -> 64518 bytes .../__pycache__/optimizer.cpython-311.pyc | Bin 0 -> 2888 bytes .../jinja2/__pycache__/parser.cpython-311.pyc | Bin 0 -> 59332 bytes .../__pycache__/runtime.cpython-311.pyc | Bin 0 -> 50684 bytes .../__pycache__/sandbox.cpython-311.pyc | Bin 0 -> 18856 bytes .../jinja2/__pycache__/tests.cpython-311.pyc | Bin 0 -> 9279 bytes .../jinja2/__pycache__/utils.cpython-311.pyc | Bin 0 -> 37094 bytes .../__pycache__/visitor.cpython-311.pyc | Bin 0 -> 5748 bytes .../site-packages/jinja2/_identifier.py | 6 + .../site-packages/jinja2/async_utils.py | 84 + .../site-packages/jinja2/bccache.py | 406 + .../site-packages/jinja2/compiler.py | 1957 ++++ .../site-packages/jinja2/constants.py | 20 + .../python3.11/site-packages/jinja2/debug.py | 191 + .../site-packages/jinja2/defaults.py | 48 + .../site-packages/jinja2/environment.py | 1667 ++++ .../site-packages/jinja2/exceptions.py | 166 + .../python3.11/site-packages/jinja2/ext.py | 859 ++ .../site-packages/jinja2/filters.py | 1840 ++++ .../site-packages/jinja2/idtracking.py | 318 + .../python3.11/site-packages/jinja2/lexer.py | 866 ++ .../site-packages/jinja2/loaders.py | 661 ++ .../python3.11/site-packages/jinja2/meta.py | 111 + .../site-packages/jinja2/nativetypes.py | 130 + .../python3.11/site-packages/jinja2/nodes.py | 1204 +++ .../site-packages/jinja2/optimizer.py | 47 + .../python3.11/site-packages/jinja2/parser.py | 1032 ++ .../python3.11/site-packages/jinja2/py.typed | 0 .../site-packages/jinja2/runtime.py | 1053 ++ .../site-packages/jinja2/sandbox.py | 428 + .../python3.11/site-packages/jinja2/tests.py | 255 + .../python3.11/site-packages/jinja2/utils.py | 755 ++ .../site-packages/jinja2/visitor.py | 92 + .../site-packages/markupsafe/__init__.py | 295 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 18405 bytes .../__pycache__/_native.cpython-311.pyc | Bin 0 -> 2777 bytes .../site-packages/markupsafe/_native.py | 63 + .../site-packages/markupsafe/_speedups.c | 320 + .../_speedups.cpython-311-x86_64-linux-gnu.so | Bin 0 -> 53656 bytes .../site-packages/markupsafe/_speedups.pyi | 9 + .../site-packages/markupsafe/py.typed | 0 .../pip-23.0.1.dist-info/INSTALLER | 1 + .../pip-23.0.1.dist-info/LICENSE.txt | 20 + .../pip-23.0.1.dist-info/METADATA | 88 + .../site-packages/pip-23.0.1.dist-info/RECORD | 1002 ++ .../pip-23.0.1.dist-info/REQUESTED | 0 .../site-packages/pip-23.0.1.dist-info/WHEEL | 5 + .../pip-23.0.1.dist-info/entry_points.txt | 4 + .../pip-23.0.1.dist-info/top_level.txt | 1 + .../python3.11/site-packages/pip/__init__.py | 13 + .../python3.11/site-packages/pip/__main__.py | 31 + .../site-packages/pip/__pip-runner__.py | 50 + .../pip/__pycache__/__init__.cpython-311.pyc | Bin 0 -> 810 bytes .../pip/__pycache__/__main__.cpython-311.pyc | Bin 0 -> 1119 bytes .../__pip-runner__.cpython-311.pyc | Bin 0 -> 2547 bytes .../site-packages/pip/_internal/__init__.py | 19 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 993 bytes .../__pycache__/build_env.cpython-311.pyc | Bin 0 -> 16113 bytes .../__pycache__/cache.cpython-311.pyc | Bin 0 -> 14738 bytes .../__pycache__/configuration.cpython-311.pyc | Bin 0 -> 19269 bytes .../__pycache__/exceptions.cpython-311.pyc | Bin 0 -> 38398 bytes .../__pycache__/main.cpython-311.pyc | Bin 0 -> 793 bytes .../__pycache__/pyproject.cpython-311.pyc | Bin 0 -> 5561 bytes .../self_outdated_check.cpython-311.pyc | Bin 0 -> 11363 bytes .../__pycache__/wheel_builder.cpython-311.pyc | Bin 0 -> 16033 bytes .../site-packages/pip/_internal/build_env.py | 311 + .../site-packages/pip/_internal/cache.py | 293 + .../pip/_internal/cli/__init__.py | 4 + .../cli/__pycache__/__init__.cpython-311.pyc | Bin 0 -> 328 bytes .../autocompletion.cpython-311.pyc | Bin 0 -> 10118 bytes .../__pycache__/base_command.cpython-311.pyc | Bin 0 -> 11116 bytes .../__pycache__/cmdoptions.cpython-311.pyc | Bin 0 -> 33015 bytes .../command_context.cpython-311.pyc | Bin 0 -> 2150 bytes .../cli/__pycache__/main.cpython-311.pyc | Bin 0 -> 2405 bytes .../__pycache__/main_parser.cpython-311.pyc | Bin 0 -> 5564 bytes .../cli/__pycache__/parser.cpython-311.pyc | Bin 0 -> 17065 bytes .../__pycache__/progress_bars.cpython-311.pyc | Bin 0 -> 3212 bytes .../__pycache__/req_command.cpython-311.pyc | Bin 0 -> 20177 bytes .../cli/__pycache__/spinners.cpython-311.pyc | Bin 0 -> 8877 bytes .../__pycache__/status_codes.cpython-311.pyc | Bin 0 -> 416 bytes .../pip/_internal/cli/autocompletion.py | 171 + .../pip/_internal/cli/base_command.py | 216 + .../pip/_internal/cli/cmdoptions.py | 1055 ++ .../pip/_internal/cli/command_context.py | 27 + .../site-packages/pip/_internal/cli/main.py | 70 + .../pip/_internal/cli/main_parser.py | 134 + .../site-packages/pip/_internal/cli/parser.py | 294 + .../pip/_internal/cli/progress_bars.py | 68 + .../pip/_internal/cli/req_command.py | 502 + .../pip/_internal/cli/spinners.py | 159 + .../pip/_internal/cli/status_codes.py | 6 + .../pip/_internal/commands/__init__.py | 132 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 4496 bytes .../__pycache__/cache.cpython-311.pyc | Bin 0 -> 10595 bytes .../__pycache__/check.cpython-311.pyc | Bin 0 -> 2346 bytes .../__pycache__/completion.cpython-311.pyc | Bin 0 -> 5508 bytes .../__pycache__/configuration.cpython-311.pyc | Bin 0 -> 14937 bytes .../__pycache__/debug.cpython-311.pyc | Bin 0 -> 12045 bytes .../__pycache__/download.cpython-311.pyc | Bin 0 -> 7852 bytes .../__pycache__/freeze.cpython-311.pyc | Bin 0 -> 4182 bytes .../commands/__pycache__/hash.cpython-311.pyc | Bin 0 -> 3398 bytes .../commands/__pycache__/help.cpython-311.pyc | Bin 0 -> 2010 bytes .../__pycache__/index.cpython-311.pyc | Bin 0 -> 7833 bytes .../__pycache__/inspect.cpython-311.pyc | Bin 0 -> 4486 bytes .../__pycache__/install.cpython-311.pyc | Bin 0 -> 35415 bytes .../commands/__pycache__/list.cpython-311.pyc | Bin 0 -> 17190 bytes .../__pycache__/search.cpython-311.pyc | Bin 0 -> 8992 bytes .../commands/__pycache__/show.cpython-311.pyc | Bin 0 -> 11335 bytes .../__pycache__/uninstall.cpython-311.pyc | Bin 0 -> 5186 bytes .../__pycache__/wheel.cpython-311.pyc | Bin 0 -> 9992 bytes .../pip/_internal/commands/cache.py | 223 + .../pip/_internal/commands/check.py | 53 + .../pip/_internal/commands/completion.py | 126 + .../pip/_internal/commands/configuration.py | 282 + .../pip/_internal/commands/debug.py | 199 + .../pip/_internal/commands/download.py | 149 + .../pip/_internal/commands/freeze.py | 97 + .../pip/_internal/commands/hash.py | 59 + .../pip/_internal/commands/help.py | 41 + .../pip/_internal/commands/index.py | 139 + .../pip/_internal/commands/inspect.py | 92 + .../pip/_internal/commands/install.py | 873 ++ .../pip/_internal/commands/list.py | 365 + .../pip/_internal/commands/search.py | 174 + .../pip/_internal/commands/show.py | 189 + .../pip/_internal/commands/uninstall.py | 113 + .../pip/_internal/commands/wheel.py | 203 + .../pip/_internal/configuration.py | 374 + .../pip/_internal/distributions/__init__.py | 21 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 1078 bytes .../__pycache__/base.cpython-311.pyc | Bin 0 -> 2450 bytes .../__pycache__/installed.cpython-311.pyc | Bin 0 -> 1587 bytes .../__pycache__/sdist.cpython-311.pyc | Bin 0 -> 8989 bytes .../__pycache__/wheel.cpython-311.pyc | Bin 0 -> 2181 bytes .../pip/_internal/distributions/base.py | 39 + .../pip/_internal/distributions/installed.py | 23 + .../pip/_internal/distributions/sdist.py | 150 + .../pip/_internal/distributions/wheel.py | 34 + .../site-packages/pip/_internal/exceptions.py | 747 ++ .../pip/_internal/index/__init__.py | 2 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 282 bytes .../__pycache__/collector.cpython-311.pyc | Bin 0 -> 24588 bytes .../package_finder.cpython-311.pyc | Bin 0 -> 44260 bytes .../index/__pycache__/sources.cpython-311.pyc | Bin 0 -> 11064 bytes .../pip/_internal/index/collector.py | 505 + .../pip/_internal/index/package_finder.py | 1029 ++ .../pip/_internal/index/sources.py | 224 + .../pip/_internal/locations/__init__.py | 467 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 18220 bytes .../__pycache__/_distutils.cpython-311.pyc | Bin 0 -> 7629 bytes .../__pycache__/_sysconfig.cpython-311.pyc | Bin 0 -> 8924 bytes .../__pycache__/base.cpython-311.pyc | Bin 0 -> 4045 bytes .../pip/_internal/locations/_distutils.py | 173 + .../pip/_internal/locations/_sysconfig.py | 213 + .../pip/_internal/locations/base.py | 81 + .../site-packages/pip/_internal/main.py | 12 + .../pip/_internal/metadata/__init__.py | 127 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 6453 bytes .../__pycache__/_json.cpython-311.pyc | Bin 0 -> 3607 bytes .../metadata/__pycache__/base.cpython-311.pyc | Bin 0 -> 38052 bytes .../__pycache__/pkg_resources.cpython-311.pyc | Bin 0 -> 16900 bytes .../pip/_internal/metadata/_json.py | 84 + .../pip/_internal/metadata/base.py | 688 ++ .../_internal/metadata/importlib/__init__.py | 4 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 399 bytes .../__pycache__/_compat.cpython-311.pyc | Bin 0 -> 3606 bytes .../__pycache__/_dists.cpython-311.pyc | Bin 0 -> 14622 bytes .../__pycache__/_envs.cpython-311.pyc | Bin 0 -> 12460 bytes .../_internal/metadata/importlib/_compat.py | 55 + .../_internal/metadata/importlib/_dists.py | 224 + .../pip/_internal/metadata/importlib/_envs.py | 188 + .../pip/_internal/metadata/pkg_resources.py | 270 + .../pip/_internal/models/__init__.py | 2 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 316 bytes .../__pycache__/candidate.cpython-311.pyc | Bin 0 -> 2135 bytes .../__pycache__/direct_url.cpython-311.pyc | Bin 0 -> 12298 bytes .../format_control.cpython-311.pyc | Bin 0 -> 4699 bytes .../models/__pycache__/index.cpython-311.pyc | Bin 0 -> 1941 bytes .../installation_report.cpython-311.pyc | Bin 0 -> 2655 bytes .../models/__pycache__/link.cpython-311.pyc | Bin 0 -> 26487 bytes .../models/__pycache__/scheme.cpython-311.pyc | Bin 0 -> 1307 bytes .../__pycache__/search_scope.cpython-311.pyc | Bin 0 -> 5870 bytes .../selection_prefs.cpython-311.pyc | Bin 0 -> 2038 bytes .../__pycache__/target_python.cpython-311.pyc | Bin 0 -> 4800 bytes .../models/__pycache__/wheel.cpython-311.pyc | Bin 0 -> 6463 bytes .../pip/_internal/models/candidate.py | 34 + .../pip/_internal/models/direct_url.py | 228 + .../pip/_internal/models/format_control.py | 80 + .../pip/_internal/models/index.py | 28 + .../_internal/models/installation_report.py | 53 + .../pip/_internal/models/link.py | 524 + .../pip/_internal/models/scheme.py | 31 + .../pip/_internal/models/search_scope.py | 133 + .../pip/_internal/models/selection_prefs.py | 51 + .../pip/_internal/models/target_python.py | 110 + .../pip/_internal/models/wheel.py | 92 + .../pip/_internal/network/__init__.py | 2 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 304 bytes .../network/__pycache__/auth.cpython-311.pyc | Bin 0 -> 19108 bytes .../network/__pycache__/cache.cpython-311.pyc | Bin 0 -> 5227 bytes .../__pycache__/download.cpython-311.pyc | Bin 0 -> 9619 bytes .../__pycache__/lazy_wheel.cpython-311.pyc | Bin 0 -> 13065 bytes .../__pycache__/session.cpython-311.pyc | Bin 0 -> 21332 bytes .../network/__pycache__/utils.cpython-311.pyc | Bin 0 -> 2453 bytes .../__pycache__/xmlrpc.cpython-311.pyc | Bin 0 -> 3232 bytes .../pip/_internal/network/auth.py | 446 + .../pip/_internal/network/cache.py | 69 + .../pip/_internal/network/download.py | 186 + .../pip/_internal/network/lazy_wheel.py | 210 + .../pip/_internal/network/session.py | 518 + .../pip/_internal/network/utils.py | 96 + .../pip/_internal/network/xmlrpc.py | 60 + .../pip/_internal/operations/__init__.py | 0 .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 242 bytes .../__pycache__/check.cpython-311.pyc | Bin 0 -> 6675 bytes .../__pycache__/freeze.cpython-311.pyc | Bin 0 -> 11638 bytes .../__pycache__/prepare.cpython-311.pyc | Bin 0 -> 26422 bytes .../_internal/operations/build/__init__.py | 0 .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 248 bytes .../__pycache__/build_tracker.cpython-311.pyc | Bin 0 -> 8171 bytes .../__pycache__/metadata.cpython-311.pyc | Bin 0 -> 2319 bytes .../metadata_editable.cpython-311.pyc | Bin 0 -> 2355 bytes .../metadata_legacy.cpython-311.pyc | Bin 0 -> 3755 bytes .../build/__pycache__/wheel.cpython-311.pyc | Bin 0 -> 1985 bytes .../wheel_editable.cpython-311.pyc | Bin 0 -> 2429 bytes .../__pycache__/wheel_legacy.cpython-311.pyc | Bin 0 -> 4536 bytes .../operations/build/build_tracker.py | 124 + .../_internal/operations/build/metadata.py | 39 + .../operations/build/metadata_editable.py | 41 + .../operations/build/metadata_legacy.py | 74 + .../pip/_internal/operations/build/wheel.py | 37 + .../operations/build/wheel_editable.py | 46 + .../operations/build/wheel_legacy.py | 102 + .../pip/_internal/operations/check.py | 149 + .../pip/_internal/operations/freeze.py | 254 + .../_internal/operations/install/__init__.py | 2 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 316 bytes .../editable_legacy.cpython-311.pyc | Bin 0 -> 2312 bytes .../__pycache__/legacy.cpython-311.pyc | Bin 0 -> 6152 bytes .../install/__pycache__/wheel.cpython-311.pyc | Bin 0 -> 40038 bytes .../operations/install/editable_legacy.py | 47 + .../_internal/operations/install/legacy.py | 120 + .../pip/_internal/operations/install/wheel.py | 738 ++ .../pip/_internal/operations/prepare.py | 667 ++ .../site-packages/pip/_internal/pyproject.py | 174 + .../pip/_internal/req/__init__.py | 94 + .../req/__pycache__/__init__.cpython-311.pyc | Bin 0 -> 4488 bytes .../__pycache__/constructors.cpython-311.pyc | Bin 0 -> 20747 bytes .../req/__pycache__/req_file.cpython-311.pyc | Bin 0 -> 22476 bytes .../__pycache__/req_install.cpython-311.pyc | Bin 0 -> 40388 bytes .../req/__pycache__/req_set.cpython-311.pyc | Bin 0 -> 6044 bytes .../__pycache__/req_uninstall.cpython-311.pyc | Bin 0 -> 37042 bytes .../pip/_internal/req/constructors.py | 501 + .../pip/_internal/req/req_file.py | 544 ++ .../pip/_internal/req/req_install.py | 946 ++ .../pip/_internal/req/req_set.py | 82 + .../pip/_internal/req/req_uninstall.py | 640 ++ .../pip/_internal/resolution/__init__.py | 0 .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 242 bytes .../__pycache__/base.cpython-311.pyc | Bin 0 -> 1413 bytes .../pip/_internal/resolution/base.py | 20 + .../_internal/resolution/legacy/__init__.py | 0 .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 249 bytes .../__pycache__/resolver.cpython-311.pyc | Bin 0 -> 23835 bytes .../_internal/resolution/legacy/resolver.py | 600 ++ .../resolution/resolvelib/__init__.py | 0 .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 253 bytes .../__pycache__/base.cpython-311.pyc | Bin 0 -> 9666 bytes .../__pycache__/candidates.cpython-311.pyc | Bin 0 -> 28876 bytes .../__pycache__/factory.cpython-311.pyc | Bin 0 -> 32020 bytes .../found_candidates.cpython-311.pyc | Bin 0 -> 6801 bytes .../__pycache__/provider.cpython-311.pyc | Bin 0 -> 11095 bytes .../__pycache__/reporter.cpython-311.pyc | Bin 0 -> 4698 bytes .../__pycache__/requirements.cpython-311.pyc | Bin 0 -> 11163 bytes .../__pycache__/resolver.cpython-311.pyc | Bin 0 -> 12350 bytes .../_internal/resolution/resolvelib/base.py | 141 + .../resolution/resolvelib/candidates.py | 556 ++ .../resolution/resolvelib/factory.py | 731 ++ .../resolution/resolvelib/found_candidates.py | 155 + .../resolution/resolvelib/provider.py | 248 + .../resolution/resolvelib/reporter.py | 68 + .../resolution/resolvelib/requirements.py | 166 + .../resolution/resolvelib/resolver.py | 296 + .../pip/_internal/self_outdated_check.py | 242 + .../pip/_internal/utils/__init__.py | 0 .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 237 bytes .../utils/__pycache__/_log.cpython-311.pyc | Bin 0 -> 2058 bytes .../utils/__pycache__/appdirs.cpython-311.pyc | Bin 0 -> 2596 bytes .../utils/__pycache__/compat.cpython-311.pyc | Bin 0 -> 2304 bytes .../compatibility_tags.cpython-311.pyc | Bin 0 -> 6796 bytes .../__pycache__/datetime.cpython-311.pyc | Bin 0 -> 754 bytes .../__pycache__/deprecation.cpython-311.pyc | Bin 0 -> 7127 bytes .../direct_url_helpers.cpython-311.pyc | Bin 0 -> 3760 bytes .../distutils_args.cpython-311.pyc | Bin 0 -> 1504 bytes .../__pycache__/egg_link.cpython-311.pyc | Bin 0 -> 3275 bytes .../__pycache__/encoding.cpython-311.pyc | Bin 0 -> 2360 bytes .../__pycache__/entrypoints.cpython-311.pyc | Bin 0 -> 4282 bytes .../__pycache__/filesystem.cpython-311.pyc | Bin 0 -> 8267 bytes .../__pycache__/filetypes.cpython-311.pyc | Bin 0 -> 1353 bytes .../utils/__pycache__/glibc.cpython-311.pyc | Bin 0 -> 2596 bytes .../utils/__pycache__/hashes.cpython-311.pyc | Bin 0 -> 8374 bytes .../inject_securetransport.cpython-311.pyc | Bin 0 -> 1371 bytes .../utils/__pycache__/logging.cpython-311.pyc | Bin 0 -> 15496 bytes .../utils/__pycache__/misc.cpython-311.pyc | Bin 0 -> 36767 bytes .../utils/__pycache__/models.cpython-311.pyc | Bin 0 -> 2977 bytes .../__pycache__/packaging.cpython-311.pyc | Bin 0 -> 2844 bytes .../setuptools_build.cpython-311.pyc | Bin 0 -> 6141 bytes .../__pycache__/subprocess.cpython-311.pyc | Bin 0 -> 9931 bytes .../__pycache__/temp_dir.cpython-311.pyc | Bin 0 -> 11458 bytes .../__pycache__/unpacking.cpython-311.pyc | Bin 0 -> 12933 bytes .../utils/__pycache__/urls.cpython-311.pyc | Bin 0 -> 2730 bytes .../__pycache__/virtualenv.cpython-311.pyc | Bin 0 -> 4977 bytes .../utils/__pycache__/wheel.cpython-311.pyc | Bin 0 -> 7147 bytes .../site-packages/pip/_internal/utils/_log.py | 38 + .../pip/_internal/utils/appdirs.py | 52 + .../pip/_internal/utils/compat.py | 63 + .../pip/_internal/utils/compatibility_tags.py | 165 + .../pip/_internal/utils/datetime.py | 11 + .../pip/_internal/utils/deprecation.py | 188 + .../pip/_internal/utils/direct_url_helpers.py | 87 + .../pip/_internal/utils/distutils_args.py | 43 + .../pip/_internal/utils/egg_link.py | 72 + .../pip/_internal/utils/encoding.py | 36 + .../pip/_internal/utils/entrypoints.py | 84 + .../pip/_internal/utils/filesystem.py | 153 + .../pip/_internal/utils/filetypes.py | 27 + .../pip/_internal/utils/glibc.py | 88 + .../pip/_internal/utils/hashes.py | 144 + .../_internal/utils/inject_securetransport.py | 35 + .../pip/_internal/utils/logging.py | 348 + .../site-packages/pip/_internal/utils/misc.py | 739 ++ .../pip/_internal/utils/models.py | 39 + .../pip/_internal/utils/packaging.py | 57 + .../pip/_internal/utils/setuptools_build.py | 195 + .../pip/_internal/utils/subprocess.py | 260 + .../pip/_internal/utils/temp_dir.py | 246 + .../pip/_internal/utils/unpacking.py | 257 + .../site-packages/pip/_internal/utils/urls.py | 62 + .../pip/_internal/utils/virtualenv.py | 104 + .../pip/_internal/utils/wheel.py | 136 + .../pip/_internal/vcs/__init__.py | 15 + .../vcs/__pycache__/__init__.cpython-311.pyc | Bin 0 -> 672 bytes .../vcs/__pycache__/bazaar.cpython-311.pyc | Bin 0 -> 5897 bytes .../vcs/__pycache__/git.cpython-311.pyc | Bin 0 -> 21561 bytes .../vcs/__pycache__/mercurial.cpython-311.pyc | Bin 0 -> 8743 bytes .../__pycache__/subversion.cpython-311.pyc | Bin 0 -> 14640 bytes .../versioncontrol.cpython-311.pyc | Bin 0 -> 31909 bytes .../site-packages/pip/_internal/vcs/bazaar.py | 112 + .../site-packages/pip/_internal/vcs/git.py | 526 + .../pip/_internal/vcs/mercurial.py | 163 + .../pip/_internal/vcs/subversion.py | 324 + .../pip/_internal/vcs/versioncontrol.py | 705 ++ .../pip/_internal/wheel_builder.py | 382 + .../site-packages/pip/_vendor/__init__.py | 120 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 5650 bytes .../_vendor/__pycache__/six.cpython-311.pyc | Bin 0 -> 46452 bytes .../typing_extensions.cpython-311.pyc | Bin 0 -> 97482 bytes .../pip/_vendor/cachecontrol/__init__.py | 18 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 879 bytes .../__pycache__/_cmd.cpython-311.pyc | Bin 0 -> 2734 bytes .../__pycache__/adapter.cpython-311.pyc | Bin 0 -> 5541 bytes .../__pycache__/cache.cpython-311.pyc | Bin 0 -> 3815 bytes .../__pycache__/compat.cpython-311.pyc | Bin 0 -> 1172 bytes .../__pycache__/controller.cpython-311.pyc | Bin 0 -> 16487 bytes .../__pycache__/filewrapper.cpython-311.pyc | Bin 0 -> 4274 bytes .../__pycache__/heuristics.cpython-311.pyc | Bin 0 -> 6719 bytes .../__pycache__/serialize.cpython-311.pyc | Bin 0 -> 8434 bytes .../__pycache__/wrapper.cpython-311.pyc | Bin 0 -> 1000 bytes .../pip/_vendor/cachecontrol/_cmd.py | 61 + .../pip/_vendor/cachecontrol/adapter.py | 137 + .../pip/_vendor/cachecontrol/cache.py | 65 + .../_vendor/cachecontrol/caches/__init__.py | 9 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 454 bytes .../__pycache__/file_cache.cpython-311.pyc | Bin 0 -> 8437 bytes .../__pycache__/redis_cache.cpython-311.pyc | Bin 0 -> 2534 bytes .../_vendor/cachecontrol/caches/file_cache.py | 188 + .../cachecontrol/caches/redis_cache.py | 39 + .../pip/_vendor/cachecontrol/compat.py | 32 + .../pip/_vendor/cachecontrol/controller.py | 439 + .../pip/_vendor/cachecontrol/filewrapper.py | 111 + .../pip/_vendor/cachecontrol/heuristics.py | 139 + .../pip/_vendor/cachecontrol/serialize.py | 190 + .../pip/_vendor/cachecontrol/wrapper.py | 33 + .../pip/_vendor/certifi/__init__.py | 4 + .../pip/_vendor/certifi/__main__.py | 12 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 379 bytes .../__pycache__/__main__.cpython-311.pyc | Bin 0 -> 780 bytes .../certifi/__pycache__/core.cpython-311.pyc | Bin 0 -> 3402 bytes .../pip/_vendor/certifi/cacert.pem | 4527 +++++++++ .../site-packages/pip/_vendor/certifi/core.py | 108 + .../pip/_vendor/chardet/__init__.py | 115 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 5111 bytes .../__pycache__/big5freq.cpython-311.pyc | Bin 0 -> 27241 bytes .../__pycache__/big5prober.cpython-311.pyc | Bin 0 -> 1716 bytes .../chardistribution.cpython-311.pyc | Bin 0 -> 11308 bytes .../charsetgroupprober.cpython-311.pyc | Bin 0 -> 4338 bytes .../__pycache__/charsetprober.cpython-311.pyc | Bin 0 -> 5584 bytes .../codingstatemachine.cpython-311.pyc | Bin 0 -> 4035 bytes .../codingstatemachinedict.cpython-311.pyc | Bin 0 -> 991 bytes .../__pycache__/cp949prober.cpython-311.pyc | Bin 0 -> 1725 bytes .../chardet/__pycache__/enums.cpython-311.pyc | Bin 0 -> 3426 bytes .../__pycache__/escprober.cpython-311.pyc | Bin 0 -> 4942 bytes .../chardet/__pycache__/escsm.cpython-311.pyc | Bin 0 -> 12681 bytes .../__pycache__/eucjpprober.cpython-311.pyc | Bin 0 -> 4768 bytes .../__pycache__/euckrfreq.cpython-311.pyc | Bin 0 -> 12124 bytes .../__pycache__/euckrprober.cpython-311.pyc | Bin 0 -> 1717 bytes .../__pycache__/euctwfreq.cpython-311.pyc | Bin 0 -> 27246 bytes .../__pycache__/euctwprober.cpython-311.pyc | Bin 0 -> 1717 bytes .../__pycache__/gb2312freq.cpython-311.pyc | Bin 0 -> 19168 bytes .../__pycache__/gb2312prober.cpython-311.pyc | Bin 0 -> 1732 bytes .../__pycache__/hebrewprober.cpython-311.pyc | Bin 0 -> 5721 bytes .../__pycache__/jisfreq.cpython-311.pyc | Bin 0 -> 22197 bytes .../__pycache__/johabfreq.cpython-311.pyc | Bin 0 -> 84701 bytes .../__pycache__/johabprober.cpython-311.pyc | Bin 0 -> 1723 bytes .../__pycache__/jpcntx.cpython-311.pyc | Bin 0 -> 40205 bytes .../langbulgarianmodel.cpython-311.pyc | Bin 0 -> 85875 bytes .../langgreekmodel.cpython-311.pyc | Bin 0 -> 79297 bytes .../langhebrewmodel.cpython-311.pyc | Bin 0 -> 80059 bytes .../langhungarianmodel.cpython-311.pyc | Bin 0 -> 85829 bytes .../langrussianmodel.cpython-311.pyc | Bin 0 -> 108776 bytes .../__pycache__/langthaimodel.cpython-311.pyc | Bin 0 -> 80237 bytes .../langturkishmodel.cpython-311.pyc | Bin 0 -> 80076 bytes .../__pycache__/latin1prober.cpython-311.pyc | Bin 0 -> 7372 bytes .../macromanprober.cpython-311.pyc | Bin 0 -> 7539 bytes .../mbcharsetprober.cpython-311.pyc | Bin 0 -> 4160 bytes .../mbcsgroupprober.cpython-311.pyc | Bin 0 -> 2030 bytes .../__pycache__/mbcssm.cpython-311.pyc | Bin 0 -> 31770 bytes .../__pycache__/resultdict.cpython-311.pyc | Bin 0 -> 809 bytes .../sbcharsetprober.cpython-311.pyc | Bin 0 -> 6435 bytes .../sbcsgroupprober.cpython-311.pyc | Bin 0 -> 2980 bytes .../__pycache__/sjisprober.cpython-311.pyc | Bin 0 -> 4873 bytes .../universaldetector.cpython-311.pyc | Bin 0 -> 12501 bytes .../__pycache__/utf1632prober.cpython-311.pyc | Bin 0 -> 10621 bytes .../__pycache__/utf8prober.cpython-311.pyc | Bin 0 -> 3508 bytes .../__pycache__/version.cpython-311.pyc | Bin 0 -> 544 bytes .../pip/_vendor/chardet/big5freq.py | 386 + .../pip/_vendor/chardet/big5prober.py | 47 + .../pip/_vendor/chardet/chardistribution.py | 261 + .../pip/_vendor/chardet/charsetgroupprober.py | 106 + .../pip/_vendor/chardet/charsetprober.py | 147 + .../pip/_vendor/chardet/cli/__init__.py | 0 .../cli/__pycache__/__init__.cpython-311.pyc | Bin 0 -> 241 bytes .../__pycache__/chardetect.cpython-311.pyc | Bin 0 -> 4380 bytes .../pip/_vendor/chardet/cli/chardetect.py | 112 + .../pip/_vendor/chardet/codingstatemachine.py | 90 + .../_vendor/chardet/codingstatemachinedict.py | 19 + .../pip/_vendor/chardet/cp949prober.py | 49 + .../pip/_vendor/chardet/enums.py | 85 + .../pip/_vendor/chardet/escprober.py | 102 + .../pip/_vendor/chardet/escsm.py | 261 + .../pip/_vendor/chardet/eucjpprober.py | 102 + .../pip/_vendor/chardet/euckrfreq.py | 196 + .../pip/_vendor/chardet/euckrprober.py | 47 + .../pip/_vendor/chardet/euctwfreq.py | 388 + .../pip/_vendor/chardet/euctwprober.py | 47 + .../pip/_vendor/chardet/gb2312freq.py | 284 + .../pip/_vendor/chardet/gb2312prober.py | 47 + .../pip/_vendor/chardet/hebrewprober.py | 316 + .../pip/_vendor/chardet/jisfreq.py | 325 + .../pip/_vendor/chardet/johabfreq.py | 2382 +++++ .../pip/_vendor/chardet/johabprober.py | 47 + .../pip/_vendor/chardet/jpcntx.py | 238 + .../pip/_vendor/chardet/langbulgarianmodel.py | 4649 +++++++++ .../pip/_vendor/chardet/langgreekmodel.py | 4397 +++++++++ .../pip/_vendor/chardet/langhebrewmodel.py | 4380 +++++++++ .../pip/_vendor/chardet/langhungarianmodel.py | 4649 +++++++++ .../pip/_vendor/chardet/langrussianmodel.py | 5725 +++++++++++ .../pip/_vendor/chardet/langthaimodel.py | 4380 +++++++++ .../pip/_vendor/chardet/langturkishmodel.py | 4380 +++++++++ .../pip/_vendor/chardet/latin1prober.py | 147 + .../pip/_vendor/chardet/macromanprober.py | 162 + .../pip/_vendor/chardet/mbcharsetprober.py | 95 + .../pip/_vendor/chardet/mbcsgroupprober.py | 57 + .../pip/_vendor/chardet/mbcssm.py | 661 ++ .../pip/_vendor/chardet/metadata/__init__.py | 0 .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 246 bytes .../__pycache__/languages.cpython-311.pyc | Bin 0 -> 10846 bytes .../pip/_vendor/chardet/metadata/languages.py | 352 + .../pip/_vendor/chardet/resultdict.py | 16 + .../pip/_vendor/chardet/sbcharsetprober.py | 162 + .../pip/_vendor/chardet/sbcsgroupprober.py | 88 + .../pip/_vendor/chardet/sjisprober.py | 105 + .../pip/_vendor/chardet/universaldetector.py | 362 + .../pip/_vendor/chardet/utf1632prober.py | 225 + .../pip/_vendor/chardet/utf8prober.py | 82 + .../pip/_vendor/chardet/version.py | 9 + .../pip/_vendor/colorama/__init__.py | 7 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 613 bytes .../colorama/__pycache__/ansi.cpython-311.pyc | Bin 0 -> 4611 bytes .../__pycache__/ansitowin32.cpython-311.pyc | Bin 0 -> 16257 bytes .../__pycache__/initialise.cpython-311.pyc | Bin 0 -> 3974 bytes .../__pycache__/win32.cpython-311.pyc | Bin 0 -> 7962 bytes .../__pycache__/winterm.cpython-311.pyc | Bin 0 -> 9188 bytes .../pip/_vendor/colorama/ansi.py | 102 + .../pip/_vendor/colorama/ansitowin32.py | 277 + .../pip/_vendor/colorama/initialise.py | 121 + .../pip/_vendor/colorama/tests/__init__.py | 1 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 244 bytes .../__pycache__/ansi_test.cpython-311.pyc | Bin 0 -> 5889 bytes .../ansitowin32_test.cpython-311.pyc | Bin 0 -> 21556 bytes .../initialise_test.cpython-311.pyc | Bin 0 -> 14183 bytes .../__pycache__/isatty_test.cpython-311.pyc | Bin 0 -> 6748 bytes .../tests/__pycache__/utils.cpython-311.pyc | Bin 0 -> 2923 bytes .../__pycache__/winterm_test.cpython-311.pyc | Bin 0 -> 7276 bytes .../pip/_vendor/colorama/tests/ansi_test.py | 76 + .../colorama/tests/ansitowin32_test.py | 294 + .../_vendor/colorama/tests/initialise_test.py | 189 + .../pip/_vendor/colorama/tests/isatty_test.py | 57 + .../pip/_vendor/colorama/tests/utils.py | 49 + .../_vendor/colorama/tests/winterm_test.py | 131 + .../pip/_vendor/colorama/win32.py | 180 + .../pip/_vendor/colorama/winterm.py | 195 + .../pip/_vendor/distlib/__init__.py | 23 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 1482 bytes .../__pycache__/compat.cpython-311.pyc | Bin 0 -> 52348 bytes .../__pycache__/database.cpython-311.pyc | Bin 0 -> 72136 bytes .../distlib/__pycache__/index.cpython-311.pyc | Bin 0 -> 26725 bytes .../__pycache__/locators.cpython-311.pyc | Bin 0 -> 65901 bytes .../__pycache__/manifest.cpython-311.pyc | Bin 0 -> 17068 bytes .../__pycache__/markers.cpython-311.pyc | Bin 0 -> 8204 bytes .../__pycache__/metadata.cpython-311.pyc | Bin 0 -> 47152 bytes .../__pycache__/resources.cpython-311.pyc | Bin 0 -> 19031 bytes .../__pycache__/scripts.cpython-311.pyc | Bin 0 -> 21307 bytes .../distlib/__pycache__/util.cpython-311.pyc | Bin 0 -> 97486 bytes .../__pycache__/version.cpython-311.pyc | Bin 0 -> 34613 bytes .../distlib/__pycache__/wheel.cpython-311.pyc | Bin 0 -> 60417 bytes .../pip/_vendor/distlib/compat.py | 1116 +++ .../pip/_vendor/distlib/database.py | 1350 +++ .../pip/_vendor/distlib/index.py | 508 + .../pip/_vendor/distlib/locators.py | 1300 +++ .../pip/_vendor/distlib/manifest.py | 393 + .../pip/_vendor/distlib/markers.py | 152 + .../pip/_vendor/distlib/metadata.py | 1076 +++ .../pip/_vendor/distlib/resources.py | 358 + .../pip/_vendor/distlib/scripts.py | 437 + .../site-packages/pip/_vendor/distlib/t32.exe | Bin 0 -> 97792 bytes .../pip/_vendor/distlib/t64-arm.exe | Bin 0 -> 182784 bytes .../site-packages/pip/_vendor/distlib/t64.exe | Bin 0 -> 108032 bytes .../site-packages/pip/_vendor/distlib/util.py | 1932 ++++ .../pip/_vendor/distlib/version.py | 739 ++ .../site-packages/pip/_vendor/distlib/w32.exe | Bin 0 -> 91648 bytes .../pip/_vendor/distlib/w64-arm.exe | Bin 0 -> 168448 bytes .../site-packages/pip/_vendor/distlib/w64.exe | Bin 0 -> 101888 bytes .../pip/_vendor/distlib/wheel.py | 1082 +++ .../pip/_vendor/distro/__init__.py | 54 + .../pip/_vendor/distro/__main__.py | 4 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 1235 bytes .../__pycache__/__main__.cpython-311.pyc | Bin 0 -> 369 bytes .../distro/__pycache__/distro.cpython-311.pyc | Bin 0 -> 57768 bytes .../pip/_vendor/distro/distro.py | 1399 +++ .../pip/_vendor/idna/__init__.py | 44 + .../idna/__pycache__/__init__.cpython-311.pyc | Bin 0 -> 1136 bytes .../idna/__pycache__/codec.cpython-311.pyc | Bin 0 -> 5427 bytes .../idna/__pycache__/compat.cpython-311.pyc | Bin 0 -> 1053 bytes .../idna/__pycache__/core.cpython-311.pyc | Bin 0 -> 19488 bytes .../idna/__pycache__/idnadata.cpython-311.pyc | Bin 0 -> 39012 bytes .../__pycache__/intranges.cpython-311.pyc | Bin 0 -> 3021 bytes .../__pycache__/package_data.cpython-311.pyc | Bin 0 -> 256 bytes .../__pycache__/uts46data.cpython-311.pyc | Bin 0 -> 163236 bytes .../site-packages/pip/_vendor/idna/codec.py | 112 + .../site-packages/pip/_vendor/idna/compat.py | 13 + .../site-packages/pip/_vendor/idna/core.py | 400 + .../pip/_vendor/idna/idnadata.py | 2151 +++++ .../pip/_vendor/idna/intranges.py | 54 + .../pip/_vendor/idna/package_data.py | 2 + .../pip/_vendor/idna/uts46data.py | 8600 +++++++++++++++++ .../pip/_vendor/msgpack/__init__.py | 57 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 2115 bytes .../__pycache__/exceptions.cpython-311.pyc | Bin 0 -> 2416 bytes .../msgpack/__pycache__/ext.cpython-311.pyc | Bin 0 -> 9202 bytes .../__pycache__/fallback.cpython-311.pyc | Bin 0 -> 47229 bytes .../pip/_vendor/msgpack/exceptions.py | 48 + .../site-packages/pip/_vendor/msgpack/ext.py | 193 + .../pip/_vendor/msgpack/fallback.py | 1010 ++ .../pip/_vendor/packaging/__about__.py | 26 + .../pip/_vendor/packaging/__init__.py | 25 + .../__pycache__/__about__.cpython-311.pyc | Bin 0 -> 680 bytes .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 601 bytes .../__pycache__/_manylinux.cpython-311.pyc | Bin 0 -> 13267 bytes .../__pycache__/_musllinux.cpython-311.pyc | Bin 0 -> 8035 bytes .../__pycache__/_structures.cpython-311.pyc | Bin 0 -> 3723 bytes .../__pycache__/markers.cpython-311.pyc | Bin 0 -> 16563 bytes .../__pycache__/requirements.cpython-311.pyc | Bin 0 -> 7678 bytes .../__pycache__/specifiers.cpython-311.pyc | Bin 0 -> 34401 bytes .../__pycache__/tags.cpython-311.pyc | Bin 0 -> 21386 bytes .../__pycache__/utils.cpython-311.pyc | Bin 0 -> 6721 bytes .../__pycache__/version.cpython-311.pyc | Bin 0 -> 21913 bytes .../pip/_vendor/packaging/_manylinux.py | 301 + .../pip/_vendor/packaging/_musllinux.py | 136 + .../pip/_vendor/packaging/_structures.py | 61 + .../pip/_vendor/packaging/markers.py | 304 + .../pip/_vendor/packaging/requirements.py | 146 + .../pip/_vendor/packaging/specifiers.py | 802 ++ .../pip/_vendor/packaging/tags.py | 487 + .../pip/_vendor/packaging/utils.py | 136 + .../pip/_vendor/packaging/version.py | 504 + .../pip/_vendor/pkg_resources/__init__.py | 3296 +++++++ .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 158269 bytes .../__pycache__/py31compat.cpython-311.pyc | Bin 0 -> 1022 bytes .../pip/_vendor/pkg_resources/py31compat.py | 23 + .../pip/_vendor/platformdirs/__init__.py | 342 + .../pip/_vendor/platformdirs/__main__.py | 46 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 12968 bytes .../__pycache__/__main__.cpython-311.pyc | Bin 0 -> 2165 bytes .../__pycache__/android.cpython-311.pyc | Bin 0 -> 6395 bytes .../__pycache__/api.cpython-311.pyc | Bin 0 -> 7222 bytes .../__pycache__/macos.cpython-311.pyc | Bin 0 -> 4632 bytes .../__pycache__/unix.cpython-311.pyc | Bin 0 -> 11064 bytes .../__pycache__/version.cpython-311.pyc | Bin 0 -> 351 bytes .../__pycache__/windows.cpython-311.pyc | Bin 0 -> 10000 bytes .../pip/_vendor/platformdirs/android.py | 120 + .../pip/_vendor/platformdirs/api.py | 156 + .../pip/_vendor/platformdirs/macos.py | 64 + .../pip/_vendor/platformdirs/unix.py | 181 + .../pip/_vendor/platformdirs/version.py | 4 + .../pip/_vendor/platformdirs/windows.py | 184 + .../pip/_vendor/pygments/__init__.py | 82 + .../pip/_vendor/pygments/__main__.py | 17 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 3883 bytes .../__pycache__/__main__.cpython-311.pyc | Bin 0 -> 819 bytes .../__pycache__/cmdline.cpython-311.pyc | Bin 0 -> 30330 bytes .../__pycache__/console.cpython-311.pyc | Bin 0 -> 3082 bytes .../__pycache__/filter.cpython-311.pyc | Bin 0 -> 3543 bytes .../__pycache__/formatter.cpython-311.pyc | Bin 0 -> 3909 bytes .../__pycache__/lexer.cpython-311.pyc | Bin 0 -> 40437 bytes .../__pycache__/modeline.cpython-311.pyc | Bin 0 -> 1762 bytes .../__pycache__/plugin.cpython-311.pyc | Bin 0 -> 3775 bytes .../__pycache__/regexopt.cpython-311.pyc | Bin 0 -> 5069 bytes .../__pycache__/scanner.cpython-311.pyc | Bin 0 -> 4924 bytes .../__pycache__/sphinxext.cpython-311.pyc | Bin 0 -> 8355 bytes .../__pycache__/style.cpython-311.pyc | Bin 0 -> 7463 bytes .../__pycache__/token.cpython-311.pyc | Bin 0 -> 7503 bytes .../__pycache__/unistring.cpython-311.pyc | Bin 0 -> 33837 bytes .../pygments/__pycache__/util.cpython-311.pyc | Bin 0 -> 14630 bytes .../pip/_vendor/pygments/cmdline.py | 668 ++ .../pip/_vendor/pygments/console.py | 70 + .../pip/_vendor/pygments/filter.py | 71 + .../pip/_vendor/pygments/filters/__init__.py | 940 ++ .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 40143 bytes .../pip/_vendor/pygments/formatter.py | 94 + .../_vendor/pygments/formatters/__init__.py | 143 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 6909 bytes .../__pycache__/_mapping.cpython-311.pyc | Bin 0 -> 4191 bytes .../__pycache__/bbcode.cpython-311.pyc | Bin 0 -> 4517 bytes .../__pycache__/groff.cpython-311.pyc | Bin 0 -> 7850 bytes .../__pycache__/html.cpython-311.pyc | Bin 0 -> 42635 bytes .../__pycache__/img.cpython-311.pyc | Bin 0 -> 28607 bytes .../__pycache__/irc.cpython-311.pyc | Bin 0 -> 7710 bytes .../__pycache__/latex.cpython-311.pyc | Bin 0 -> 21843 bytes .../__pycache__/other.cpython-311.pyc | Bin 0 -> 7671 bytes .../__pycache__/pangomarkup.cpython-311.pyc | Bin 0 -> 3215 bytes .../__pycache__/rtf.cpython-311.pyc | Bin 0 -> 6882 bytes .../__pycache__/svg.cpython-311.pyc | Bin 0 -> 9702 bytes .../__pycache__/terminal.cpython-311.pyc | Bin 0 -> 6081 bytes .../__pycache__/terminal256.cpython-311.pyc | Bin 0 -> 16447 bytes .../_vendor/pygments/formatters/_mapping.py | 23 + .../pip/_vendor/pygments/formatters/bbcode.py | 108 + .../pip/_vendor/pygments/formatters/groff.py | 170 + .../pip/_vendor/pygments/formatters/html.py | 989 ++ .../pip/_vendor/pygments/formatters/img.py | 645 ++ .../pip/_vendor/pygments/formatters/irc.py | 179 + .../pip/_vendor/pygments/formatters/latex.py | 521 + .../pip/_vendor/pygments/formatters/other.py | 161 + .../pygments/formatters/pangomarkup.py | 83 + .../pip/_vendor/pygments/formatters/rtf.py | 146 + .../pip/_vendor/pygments/formatters/svg.py | 188 + .../_vendor/pygments/formatters/terminal.py | 127 + .../pygments/formatters/terminal256.py | 338 + .../pip/_vendor/pygments/lexer.py | 882 ++ .../pip/_vendor/pygments/lexers/__init__.py | 335 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 15186 bytes .../__pycache__/_mapping.cpython-311.pyc | Bin 0 -> 62817 bytes .../lexers/__pycache__/python.cpython-311.pyc | Bin 0 -> 44018 bytes .../pip/_vendor/pygments/lexers/_mapping.py | 541 ++ .../pip/_vendor/pygments/lexers/python.py | 1204 +++ .../pip/_vendor/pygments/modeline.py | 43 + .../pip/_vendor/pygments/plugin.py | 88 + .../pip/_vendor/pygments/regexopt.py | 91 + .../pip/_vendor/pygments/scanner.py | 104 + .../pip/_vendor/pygments/sphinxext.py | 155 + .../pip/_vendor/pygments/style.py | 197 + .../pip/_vendor/pygments/styles/__init__.py | 97 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 4496 bytes .../pip/_vendor/pygments/token.py | 213 + .../pip/_vendor/pygments/unistring.py | 153 + .../pip/_vendor/pygments/util.py | 308 + .../pip/_vendor/pyparsing/__init__.py | 331 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 8386 bytes .../__pycache__/actions.cpython-311.pyc | Bin 0 -> 8500 bytes .../__pycache__/common.cpython-311.pyc | Bin 0 -> 14822 bytes .../__pycache__/core.cpython-311.pyc | Bin 0 -> 277708 bytes .../__pycache__/exceptions.cpython-311.pyc | Bin 0 -> 12964 bytes .../__pycache__/helpers.cpython-311.pyc | Bin 0 -> 53665 bytes .../__pycache__/results.cpython-311.pyc | Bin 0 -> 36348 bytes .../__pycache__/testing.cpython-311.pyc | Bin 0 -> 19544 bytes .../__pycache__/unicode.cpython-311.pyc | Bin 0 -> 15402 bytes .../__pycache__/util.cpython-311.pyc | Bin 0 -> 14301 bytes .../pip/_vendor/pyparsing/actions.py | 207 + .../pip/_vendor/pyparsing/common.py | 424 + .../pip/_vendor/pyparsing/core.py | 5814 +++++++++++ .../pip/_vendor/pyparsing/diagram/__init__.py | 642 ++ .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 28067 bytes .../pip/_vendor/pyparsing/exceptions.py | 267 + .../pip/_vendor/pyparsing/helpers.py | 1088 +++ .../pip/_vendor/pyparsing/results.py | 760 ++ .../pip/_vendor/pyparsing/testing.py | 331 + .../pip/_vendor/pyparsing/unicode.py | 352 + .../pip/_vendor/pyparsing/util.py | 235 + .../pip/_vendor/pyproject_hooks/__init__.py | 23 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 744 bytes .../__pycache__/_compat.cpython-311.pyc | Bin 0 -> 442 bytes .../__pycache__/_impl.cpython-311.pyc | Bin 0 -> 16708 bytes .../pip/_vendor/pyproject_hooks/_compat.py | 8 + .../pip/_vendor/pyproject_hooks/_impl.py | 330 + .../pyproject_hooks/_in_process/__init__.py | 18 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 1204 bytes .../__pycache__/_in_process.cpython-311.pyc | Bin 0 -> 16526 bytes .../_in_process/_in_process.py | 353 + .../pip/_vendor/requests/__init__.py | 182 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 6488 bytes .../__pycache__/__version__.cpython-311.pyc | Bin 0 -> 625 bytes .../_internal_utils.cpython-311.pyc | Bin 0 -> 2122 bytes .../__pycache__/adapters.cpython-311.pyc | Bin 0 -> 24925 bytes .../requests/__pycache__/api.cpython-311.pyc | Bin 0 -> 7470 bytes .../requests/__pycache__/auth.cpython-311.pyc | Bin 0 -> 14669 bytes .../__pycache__/certs.cpython-311.pyc | Bin 0 -> 1021 bytes .../__pycache__/compat.cpython-311.pyc | Bin 0 -> 1847 bytes .../__pycache__/cookies.cpython-311.pyc | Bin 0 -> 27149 bytes .../__pycache__/exceptions.cpython-311.pyc | Bin 0 -> 8564 bytes .../requests/__pycache__/help.cpython-311.pyc | Bin 0 -> 4559 bytes .../__pycache__/hooks.cpython-311.pyc | Bin 0 -> 1289 bytes .../__pycache__/models.cpython-311.pyc | Bin 0 -> 38820 bytes .../__pycache__/packages.cpython-311.pyc | Bin 0 -> 869 bytes .../__pycache__/sessions.cpython-311.pyc | Bin 0 -> 29658 bytes .../__pycache__/status_codes.cpython-311.pyc | Bin 0 -> 6276 bytes .../__pycache__/structures.cpython-311.pyc | Bin 0 -> 6261 bytes .../__pycache__/utils.cpython-311.pyc | Bin 0 -> 40175 bytes .../pip/_vendor/requests/__version__.py | 14 + .../pip/_vendor/requests/_internal_utils.py | 48 + .../pip/_vendor/requests/adapters.py | 584 ++ .../site-packages/pip/_vendor/requests/api.py | 157 + .../pip/_vendor/requests/auth.py | 315 + .../pip/_vendor/requests/certs.py | 24 + .../pip/_vendor/requests/compat.py | 67 + .../pip/_vendor/requests/cookies.py | 561 ++ .../pip/_vendor/requests/exceptions.py | 141 + .../pip/_vendor/requests/help.py | 131 + .../pip/_vendor/requests/hooks.py | 33 + .../pip/_vendor/requests/models.py | 1034 ++ .../pip/_vendor/requests/packages.py | 16 + .../pip/_vendor/requests/sessions.py | 831 ++ .../pip/_vendor/requests/status_codes.py | 128 + .../pip/_vendor/requests/structures.py | 99 + .../pip/_vendor/requests/utils.py | 1086 +++ .../pip/_vendor/resolvelib/__init__.py | 26 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 792 bytes .../__pycache__/providers.cpython-311.pyc | Bin 0 -> 7111 bytes .../__pycache__/reporters.cpython-311.pyc | Bin 0 -> 2841 bytes .../__pycache__/resolvers.cpython-311.pyc | Bin 0 -> 25287 bytes .../__pycache__/structs.cpython-311.pyc | Bin 0 -> 11369 bytes .../pip/_vendor/resolvelib/compat/__init__.py | 0 .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 247 bytes .../collections_abc.cpython-311.pyc | Bin 0 -> 522 bytes .../resolvelib/compat/collections_abc.py | 6 + .../pip/_vendor/resolvelib/providers.py | 133 + .../pip/_vendor/resolvelib/reporters.py | 43 + .../pip/_vendor/resolvelib/resolvers.py | 482 + .../pip/_vendor/resolvelib/structs.py | 165 + .../pip/_vendor/rich/__init__.py | 177 + .../pip/_vendor/rich/__main__.py | 274 + .../rich/__pycache__/__init__.cpython-311.pyc | Bin 0 -> 7535 bytes .../rich/__pycache__/__main__.cpython-311.pyc | Bin 0 -> 11613 bytes .../__pycache__/_cell_widths.cpython-311.pyc | Bin 0 -> 7874 bytes .../__pycache__/_emoji_codes.cpython-311.pyc | Bin 0 -> 208561 bytes .../_emoji_replace.cpython-311.pyc | Bin 0 -> 1973 bytes .../_export_format.cpython-311.pyc | Bin 0 -> 2378 bytes .../__pycache__/_extension.cpython-311.pyc | Bin 0 -> 674 bytes .../rich/__pycache__/_inspect.cpython-311.pyc | Bin 0 -> 14226 bytes .../__pycache__/_log_render.cpython-311.pyc | Bin 0 -> 4808 bytes .../rich/__pycache__/_loop.cpython-311.pyc | Bin 0 -> 2154 bytes .../__pycache__/_null_file.cpython-311.pyc | Bin 0 -> 4719 bytes .../__pycache__/_palettes.cpython-311.pyc | Bin 0 -> 5290 bytes .../rich/__pycache__/_pick.cpython-311.pyc | Bin 0 -> 835 bytes .../rich/__pycache__/_ratio.cpython-311.pyc | Bin 0 -> 7973 bytes .../__pycache__/_spinners.cpython-311.pyc | Bin 0 -> 13723 bytes .../rich/__pycache__/_stack.cpython-311.pyc | Bin 0 -> 1169 bytes .../rich/__pycache__/_timer.cpython-311.pyc | Bin 0 -> 1022 bytes .../_win32_console.cpython-311.pyc | Bin 0 -> 30210 bytes .../rich/__pycache__/_windows.cpython-311.pyc | Bin 0 -> 2869 bytes .../_windows_renderer.cpython-311.pyc | Bin 0 -> 4060 bytes .../rich/__pycache__/_wrap.cpython-311.pyc | Bin 0 -> 2825 bytes .../rich/__pycache__/abc.cpython-311.pyc | Bin 0 -> 1966 bytes .../rich/__pycache__/align.cpython-311.pyc | Bin 0 -> 13515 bytes .../rich/__pycache__/ansi.cpython-311.pyc | Bin 0 -> 10491 bytes .../rich/__pycache__/bar.cpython-311.pyc | Bin 0 -> 4588 bytes .../rich/__pycache__/box.cpython-311.pyc | Bin 0 -> 13030 bytes .../rich/__pycache__/cells.cpython-311.pyc | Bin 0 -> 6480 bytes .../rich/__pycache__/color.cpython-311.pyc | Bin 0 -> 27611 bytes .../__pycache__/color_triplet.cpython-311.pyc | Bin 0 -> 1914 bytes .../rich/__pycache__/columns.cpython-311.pyc | Bin 0 -> 10685 bytes .../rich/__pycache__/console.cpython-311.pyc | Bin 0 -> 123201 bytes .../__pycache__/constrain.cpython-311.pyc | Bin 0 -> 2506 bytes .../__pycache__/containers.cpython-311.pyc | Bin 0 -> 10847 bytes .../rich/__pycache__/control.cpython-311.pyc | Bin 0 -> 11938 bytes .../default_styles.cpython-311.pyc | Bin 0 -> 12538 bytes .../rich/__pycache__/diagnose.cpython-311.pyc | Bin 0 -> 1861 bytes .../rich/__pycache__/emoji.cpython-311.pyc | Bin 0 -> 4839 bytes .../rich/__pycache__/errors.cpython-311.pyc | Bin 0 -> 2370 bytes .../__pycache__/file_proxy.cpython-311.pyc | Bin 0 -> 3818 bytes .../rich/__pycache__/filesize.cpython-311.pyc | Bin 0 -> 3342 bytes .../__pycache__/highlighter.cpython-311.pyc | Bin 0 -> 11029 bytes .../rich/__pycache__/json.cpython-311.pyc | Bin 0 -> 6722 bytes .../rich/__pycache__/jupyter.cpython-311.pyc | Bin 0 -> 6445 bytes .../rich/__pycache__/layout.cpython-311.pyc | Bin 0 -> 23352 bytes .../rich/__pycache__/live.cpython-311.pyc | Bin 0 -> 21173 bytes .../__pycache__/live_render.cpython-311.pyc | Bin 0 -> 5186 bytes .../rich/__pycache__/logging.cpython-311.pyc | Bin 0 -> 14557 bytes .../rich/__pycache__/markup.cpython-311.pyc | Bin 0 -> 10479 bytes .../rich/__pycache__/measure.cpython-311.pyc | Bin 0 -> 7312 bytes .../rich/__pycache__/padding.cpython-311.pyc | Bin 0 -> 7528 bytes .../rich/__pycache__/pager.cpython-311.pyc | Bin 0 -> 2286 bytes .../rich/__pycache__/palette.cpython-311.pyc | Bin 0 -> 6019 bytes .../rich/__pycache__/panel.cpython-311.pyc | Bin 0 -> 12775 bytes .../rich/__pycache__/pretty.cpython-311.pyc | Bin 0 -> 44868 bytes .../rich/__pycache__/progress.cpython-311.pyc | Bin 0 -> 82747 bytes .../__pycache__/progress_bar.cpython-311.pyc | Bin 0 -> 11053 bytes .../rich/__pycache__/prompt.cpython-311.pyc | Bin 0 -> 16419 bytes .../rich/__pycache__/protocol.cpython-311.pyc | Bin 0 -> 2137 bytes .../rich/__pycache__/region.cpython-311.pyc | Bin 0 -> 700 bytes .../rich/__pycache__/repr.cpython-311.pyc | Bin 0 -> 7699 bytes .../rich/__pycache__/rule.cpython-311.pyc | Bin 0 -> 7738 bytes .../rich/__pycache__/scope.cpython-311.pyc | Bin 0 -> 4392 bytes .../rich/__pycache__/screen.cpython-311.pyc | Bin 0 -> 2815 bytes .../rich/__pycache__/segment.cpython-311.pyc | Bin 0 -> 31582 bytes .../rich/__pycache__/spinner.cpython-311.pyc | Bin 0 -> 6931 bytes .../rich/__pycache__/status.cpython-311.pyc | Bin 0 -> 6799 bytes .../rich/__pycache__/style.cpython-311.pyc | Bin 0 -> 34372 bytes .../rich/__pycache__/styled.cpython-311.pyc | Bin 0 -> 2480 bytes .../rich/__pycache__/syntax.cpython-311.pyc | Bin 0 -> 42574 bytes .../rich/__pycache__/table.cpython-311.pyc | Bin 0 -> 48841 bytes .../terminal_theme.cpython-311.pyc | Bin 0 -> 3746 bytes .../rich/__pycache__/text.cpython-311.pyc | Bin 0 -> 65257 bytes .../rich/__pycache__/theme.cpython-311.pyc | Bin 0 -> 7184 bytes .../rich/__pycache__/themes.cpython-311.pyc | Bin 0 -> 396 bytes .../__pycache__/traceback.cpython-311.pyc | Bin 0 -> 31710 bytes .../rich/__pycache__/tree.cpython-311.pyc | Bin 0 -> 12567 bytes .../pip/_vendor/rich/_cell_widths.py | 451 + .../pip/_vendor/rich/_emoji_codes.py | 3610 +++++++ .../pip/_vendor/rich/_emoji_replace.py | 32 + .../pip/_vendor/rich/_export_format.py | 78 + .../pip/_vendor/rich/_extension.py | 10 + .../pip/_vendor/rich/_inspect.py | 270 + .../pip/_vendor/rich/_log_render.py | 94 + .../site-packages/pip/_vendor/rich/_loop.py | 43 + .../pip/_vendor/rich/_null_file.py | 83 + .../pip/_vendor/rich/_palettes.py | 309 + .../site-packages/pip/_vendor/rich/_pick.py | 17 + .../site-packages/pip/_vendor/rich/_ratio.py | 160 + .../pip/_vendor/rich/_spinners.py | 482 + .../site-packages/pip/_vendor/rich/_stack.py | 16 + .../site-packages/pip/_vendor/rich/_timer.py | 19 + .../pip/_vendor/rich/_win32_console.py | 662 ++ .../pip/_vendor/rich/_windows.py | 72 + .../pip/_vendor/rich/_windows_renderer.py | 56 + .../site-packages/pip/_vendor/rich/_wrap.py | 56 + .../site-packages/pip/_vendor/rich/abc.py | 33 + .../site-packages/pip/_vendor/rich/align.py | 311 + .../site-packages/pip/_vendor/rich/ansi.py | 237 + .../site-packages/pip/_vendor/rich/bar.py | 94 + .../site-packages/pip/_vendor/rich/box.py | 517 + .../site-packages/pip/_vendor/rich/cells.py | 154 + .../site-packages/pip/_vendor/rich/color.py | 618 ++ .../pip/_vendor/rich/color_triplet.py | 38 + .../site-packages/pip/_vendor/rich/columns.py | 187 + .../site-packages/pip/_vendor/rich/console.py | 2612 +++++ .../pip/_vendor/rich/constrain.py | 37 + .../pip/_vendor/rich/containers.py | 167 + .../site-packages/pip/_vendor/rich/control.py | 225 + .../pip/_vendor/rich/default_styles.py | 188 + .../pip/_vendor/rich/diagnose.py | 37 + .../site-packages/pip/_vendor/rich/emoji.py | 96 + .../site-packages/pip/_vendor/rich/errors.py | 34 + .../pip/_vendor/rich/file_proxy.py | 54 + .../pip/_vendor/rich/filesize.py | 89 + .../pip/_vendor/rich/highlighter.py | 232 + .../site-packages/pip/_vendor/rich/json.py | 140 + .../site-packages/pip/_vendor/rich/jupyter.py | 101 + .../site-packages/pip/_vendor/rich/layout.py | 443 + .../site-packages/pip/_vendor/rich/live.py | 373 + .../pip/_vendor/rich/live_render.py | 113 + .../site-packages/pip/_vendor/rich/logging.py | 289 + .../site-packages/pip/_vendor/rich/markup.py | 246 + .../site-packages/pip/_vendor/rich/measure.py | 151 + .../site-packages/pip/_vendor/rich/padding.py | 141 + .../site-packages/pip/_vendor/rich/pager.py | 34 + .../site-packages/pip/_vendor/rich/palette.py | 100 + .../site-packages/pip/_vendor/rich/panel.py | 308 + .../site-packages/pip/_vendor/rich/pretty.py | 1029 ++ .../pip/_vendor/rich/progress.py | 1707 ++++ .../pip/_vendor/rich/progress_bar.py | 224 + .../site-packages/pip/_vendor/rich/prompt.py | 376 + .../pip/_vendor/rich/protocol.py | 42 + .../site-packages/pip/_vendor/rich/region.py | 10 + .../site-packages/pip/_vendor/rich/repr.py | 149 + .../site-packages/pip/_vendor/rich/rule.py | 134 + .../site-packages/pip/_vendor/rich/scope.py | 86 + .../site-packages/pip/_vendor/rich/screen.py | 54 + .../site-packages/pip/_vendor/rich/segment.py | 739 ++ .../site-packages/pip/_vendor/rich/spinner.py | 136 + .../site-packages/pip/_vendor/rich/status.py | 132 + .../site-packages/pip/_vendor/rich/style.py | 773 ++ .../site-packages/pip/_vendor/rich/styled.py | 42 + .../site-packages/pip/_vendor/rich/syntax.py | 945 ++ .../site-packages/pip/_vendor/rich/table.py | 1002 ++ .../pip/_vendor/rich/terminal_theme.py | 153 + .../site-packages/pip/_vendor/rich/text.py | 1311 +++ .../site-packages/pip/_vendor/rich/theme.py | 112 + .../site-packages/pip/_vendor/rich/themes.py | 5 + .../pip/_vendor/rich/traceback.py | 677 ++ .../site-packages/pip/_vendor/rich/tree.py | 251 + .../site-packages/pip/_vendor/six.py | 998 ++ .../pip/_vendor/tenacity/__init__.py | 519 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 27834 bytes .../__pycache__/_asyncio.cpython-311.pyc | Bin 0 -> 4841 bytes .../__pycache__/_utils.cpython-311.pyc | Bin 0 -> 2106 bytes .../__pycache__/after.cpython-311.pyc | Bin 0 -> 1733 bytes .../__pycache__/before.cpython-311.pyc | Bin 0 -> 1567 bytes .../__pycache__/before_sleep.cpython-311.pyc | Bin 0 -> 2144 bytes .../tenacity/__pycache__/nap.cpython-311.pyc | Bin 0 -> 1606 bytes .../__pycache__/retry.cpython-311.pyc | Bin 0 -> 15080 bytes .../tenacity/__pycache__/stop.cpython-311.pyc | Bin 0 -> 5934 bytes .../__pycache__/tornadoweb.cpython-311.pyc | Bin 0 -> 2952 bytes .../tenacity/__pycache__/wait.cpython-311.pyc | Bin 0 -> 13406 bytes .../pip/_vendor/tenacity/_asyncio.py | 92 + .../pip/_vendor/tenacity/_utils.py | 68 + .../pip/_vendor/tenacity/after.py | 46 + .../pip/_vendor/tenacity/before.py | 41 + .../pip/_vendor/tenacity/before_sleep.py | 58 + .../site-packages/pip/_vendor/tenacity/nap.py | 43 + .../pip/_vendor/tenacity/retry.py | 240 + .../pip/_vendor/tenacity/stop.py | 96 + .../pip/_vendor/tenacity/tornadoweb.py | 59 + .../pip/_vendor/tenacity/wait.py | 232 + .../pip/_vendor/tomli/__init__.py | 11 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 451 bytes .../tomli/__pycache__/_parser.cpython-311.pyc | Bin 0 -> 30890 bytes .../tomli/__pycache__/_re.cpython-311.pyc | Bin 0 -> 4530 bytes .../tomli/__pycache__/_types.cpython-311.pyc | Bin 0 -> 443 bytes .../pip/_vendor/tomli/_parser.py | 691 ++ .../site-packages/pip/_vendor/tomli/_re.py | 107 + .../site-packages/pip/_vendor/tomli/_types.py | 10 + .../pip/_vendor/typing_extensions.py | 2209 +++++ .../pip/_vendor/urllib3/__init__.py | 102 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 3749 bytes .../__pycache__/_collections.cpython-311.pyc | Bin 0 -> 18337 bytes .../__pycache__/_version.cpython-311.pyc | Bin 0 -> 259 bytes .../__pycache__/connection.cpython-311.pyc | Bin 0 -> 21933 bytes .../connectionpool.cpython-311.pyc | Bin 0 -> 37676 bytes .../__pycache__/exceptions.cpython-311.pyc | Bin 0 -> 16163 bytes .../__pycache__/fields.cpython-311.pyc | Bin 0 -> 11456 bytes .../__pycache__/filepost.cpython-311.pyc | Bin 0 -> 4537 bytes .../__pycache__/poolmanager.cpython-311.pyc | Bin 0 -> 21860 bytes .../__pycache__/request.cpython-311.pyc | Bin 0 -> 6700 bytes .../__pycache__/response.cpython-311.pyc | Bin 0 -> 36583 bytes .../pip/_vendor/urllib3/_collections.py | 337 + .../pip/_vendor/urllib3/_version.py | 2 + .../pip/_vendor/urllib3/connection.py | 567 ++ .../pip/_vendor/urllib3/connectionpool.py | 1110 +++ .../pip/_vendor/urllib3/contrib/__init__.py | 0 .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 245 bytes .../_appengine_environ.cpython-311.pyc | Bin 0 -> 1984 bytes .../__pycache__/appengine.cpython-311.pyc | Bin 0 -> 12191 bytes .../__pycache__/ntlmpool.cpython-311.pyc | Bin 0 -> 6268 bytes .../__pycache__/pyopenssl.cpython-311.pyc | Bin 0 -> 25777 bytes .../securetransport.cpython-311.pyc | Bin 0 -> 36884 bytes .../contrib/__pycache__/socks.cpython-311.pyc | Bin 0 -> 8129 bytes .../urllib3/contrib/_appengine_environ.py | 36 + .../contrib/_securetransport/__init__.py | 0 .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 262 bytes .../__pycache__/bindings.cpython-311.pyc | Bin 0 -> 17009 bytes .../__pycache__/low_level.cpython-311.pyc | Bin 0 -> 15646 bytes .../contrib/_securetransport/bindings.py | 519 + .../contrib/_securetransport/low_level.py | 397 + .../pip/_vendor/urllib3/contrib/appengine.py | 314 + .../pip/_vendor/urllib3/contrib/ntlmpool.py | 130 + .../pip/_vendor/urllib3/contrib/pyopenssl.py | 518 + .../urllib3/contrib/securetransport.py | 921 ++ .../pip/_vendor/urllib3/contrib/socks.py | 216 + .../pip/_vendor/urllib3/exceptions.py | 323 + .../pip/_vendor/urllib3/fields.py | 274 + .../pip/_vendor/urllib3/filepost.py | 98 + .../pip/_vendor/urllib3/packages/__init__.py | 0 .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 246 bytes .../packages/__pycache__/six.cpython-311.pyc | Bin 0 -> 46488 bytes .../urllib3/packages/backports/__init__.py | 0 .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 256 bytes .../__pycache__/makefile.cpython-311.pyc | Bin 0 -> 2003 bytes .../urllib3/packages/backports/makefile.py | 51 + .../pip/_vendor/urllib3/packages/six.py | 1076 +++ .../pip/_vendor/urllib3/poolmanager.py | 537 + .../pip/_vendor/urllib3/request.py | 170 + .../pip/_vendor/urllib3/response.py | 879 ++ .../pip/_vendor/urllib3/util/__init__.py | 49 + .../util/__pycache__/__init__.cpython-311.pyc | Bin 0 -> 1448 bytes .../__pycache__/connection.cpython-311.pyc | Bin 0 -> 5175 bytes .../util/__pycache__/proxy.cpython-311.pyc | Bin 0 -> 1757 bytes .../util/__pycache__/queue.cpython-311.pyc | Bin 0 -> 1540 bytes .../util/__pycache__/request.cpython-311.pyc | Bin 0 -> 4660 bytes .../util/__pycache__/response.cpython-311.pyc | Bin 0 -> 3529 bytes .../util/__pycache__/retry.cpython-311.pyc | Bin 0 -> 22797 bytes .../util/__pycache__/ssl_.cpython-311.pyc | Bin 0 -> 16860 bytes .../ssl_match_hostname.cpython-311.pyc | Bin 0 -> 5839 bytes .../__pycache__/ssltransport.cpython-311.pyc | Bin 0 -> 11668 bytes .../util/__pycache__/timeout.cpython-311.pyc | Bin 0 -> 11076 bytes .../util/__pycache__/url.cpython-311.pyc | Bin 0 -> 17600 bytes .../util/__pycache__/wait.cpython-311.pyc | Bin 0 -> 5042 bytes .../pip/_vendor/urllib3/util/connection.py | 149 + .../pip/_vendor/urllib3/util/proxy.py | 57 + .../pip/_vendor/urllib3/util/queue.py | 22 + .../pip/_vendor/urllib3/util/request.py | 137 + .../pip/_vendor/urllib3/util/response.py | 107 + .../pip/_vendor/urllib3/util/retry.py | 620 ++ .../pip/_vendor/urllib3/util/ssl_.py | 495 + .../urllib3/util/ssl_match_hostname.py | 159 + .../pip/_vendor/urllib3/util/ssltransport.py | 221 + .../pip/_vendor/urllib3/util/timeout.py | 268 + .../pip/_vendor/urllib3/util/url.py | 435 + .../pip/_vendor/urllib3/util/wait.py | 152 + .../site-packages/pip/_vendor/vendor.txt | 23 + .../pip/_vendor/webencodings/__init__.py | 342 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 12922 bytes .../__pycache__/labels.cpython-311.pyc | Bin 0 -> 7322 bytes .../__pycache__/mklabels.cpython-311.pyc | Bin 0 -> 3250 bytes .../__pycache__/tests.cpython-311.pyc | Bin 0 -> 11228 bytes .../x_user_defined.cpython-311.pyc | Bin 0 -> 3602 bytes .../pip/_vendor/webencodings/labels.py | 231 + .../pip/_vendor/webencodings/mklabels.py | 59 + .../pip/_vendor/webencodings/tests.py | 153 + .../_vendor/webencodings/x_user_defined.py | 325 + .../lib/python3.11/site-packages/pip/py.typed | 4 + .../site-packages/pkg_resources/__init__.py | 3296 +++++++ .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 159611 bytes .../pkg_resources/_vendor/__init__.py | 0 .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 239 bytes .../__pycache__/appdirs.cpython-311.pyc | Bin 0 -> 29487 bytes .../_vendor/__pycache__/zipp.cpython-311.pyc | Bin 0 -> 16032 bytes .../pkg_resources/_vendor/appdirs.py | 607 ++ .../_vendor/importlib_resources/__init__.py | 36 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 873 bytes .../__pycache__/_adapters.cpython-311.pyc | Bin 0 -> 10790 bytes .../__pycache__/_common.cpython-311.pyc | Bin 0 -> 4317 bytes .../__pycache__/_compat.cpython-311.pyc | Bin 0 -> 5602 bytes .../__pycache__/_itertools.cpython-311.pyc | Bin 0 -> 1435 bytes .../__pycache__/_legacy.cpython-311.pyc | Bin 0 -> 6533 bytes .../__pycache__/abc.cpython-311.pyc | Bin 0 -> 7534 bytes .../__pycache__/readers.cpython-311.pyc | Bin 0 -> 8408 bytes .../__pycache__/simple.cpython-311.pyc | Bin 0 -> 6430 bytes .../_vendor/importlib_resources/_adapters.py | 170 + .../_vendor/importlib_resources/_common.py | 104 + .../_vendor/importlib_resources/_compat.py | 98 + .../_vendor/importlib_resources/_itertools.py | 35 + .../_vendor/importlib_resources/_legacy.py | 121 + .../_vendor/importlib_resources/abc.py | 137 + .../_vendor/importlib_resources/readers.py | 122 + .../_vendor/importlib_resources/simple.py | 116 + .../pkg_resources/_vendor/jaraco/__init__.py | 0 .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 246 bytes .../__pycache__/context.cpython-311.pyc | Bin 0 -> 9469 bytes .../__pycache__/functools.cpython-311.pyc | Bin 0 -> 20332 bytes .../pkg_resources/_vendor/jaraco/context.py | 213 + .../pkg_resources/_vendor/jaraco/functools.py | 525 + .../_vendor/jaraco/text/__init__.py | 599 ++ .../text/__pycache__/__init__.cpython-311.pyc | Bin 0 -> 26649 bytes .../_vendor/more_itertools/__init__.py | 4 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 340 bytes .../__pycache__/more.cpython-311.pyc | Bin 0 -> 168001 bytes .../__pycache__/recipes.cpython-311.pyc | Bin 0 -> 26992 bytes .../_vendor/more_itertools/more.py | 4316 +++++++++ .../_vendor/more_itertools/recipes.py | 698 ++ .../_vendor/packaging/__about__.py | 26 + .../_vendor/packaging/__init__.py | 25 + .../__pycache__/__about__.cpython-311.pyc | Bin 0 -> 690 bytes .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 611 bytes .../__pycache__/_manylinux.cpython-311.pyc | Bin 0 -> 13277 bytes .../__pycache__/_musllinux.cpython-311.pyc | Bin 0 -> 8045 bytes .../__pycache__/_structures.cpython-311.pyc | Bin 0 -> 3733 bytes .../__pycache__/markers.cpython-311.pyc | Bin 0 -> 16582 bytes .../__pycache__/requirements.cpython-311.pyc | Bin 0 -> 7697 bytes .../__pycache__/specifiers.cpython-311.pyc | Bin 0 -> 34411 bytes .../__pycache__/tags.cpython-311.pyc | Bin 0 -> 21396 bytes .../__pycache__/utils.cpython-311.pyc | Bin 0 -> 6731 bytes .../__pycache__/version.cpython-311.pyc | Bin 0 -> 21923 bytes .../_vendor/packaging/_manylinux.py | 301 + .../_vendor/packaging/_musllinux.py | 136 + .../_vendor/packaging/_structures.py | 61 + .../_vendor/packaging/markers.py | 304 + .../_vendor/packaging/requirements.py | 146 + .../_vendor/packaging/specifiers.py | 802 ++ .../pkg_resources/_vendor/packaging/tags.py | 487 + .../pkg_resources/_vendor/packaging/utils.py | 136 + .../_vendor/packaging/version.py | 504 + .../_vendor/pyparsing/__init__.py | 331 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 8384 bytes .../__pycache__/actions.cpython-311.pyc | Bin 0 -> 8513 bytes .../__pycache__/common.cpython-311.pyc | Bin 0 -> 14832 bytes .../__pycache__/core.cpython-311.pyc | Bin 0 -> 277167 bytes .../__pycache__/exceptions.cpython-311.pyc | Bin 0 -> 12981 bytes .../__pycache__/helpers.cpython-311.pyc | Bin 0 -> 53436 bytes .../__pycache__/results.cpython-311.pyc | Bin 0 -> 36370 bytes .../__pycache__/testing.cpython-311.pyc | Bin 0 -> 19522 bytes .../__pycache__/unicode.cpython-311.pyc | Bin 0 -> 14980 bytes .../__pycache__/util.cpython-311.pyc | Bin 0 -> 14311 bytes .../_vendor/pyparsing/actions.py | 207 + .../pkg_resources/_vendor/pyparsing/common.py | 424 + .../pkg_resources/_vendor/pyparsing/core.py | 5812 +++++++++++ .../_vendor/pyparsing/diagram/__init__.py | 611 ++ .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 27669 bytes .../_vendor/pyparsing/exceptions.py | 267 + .../_vendor/pyparsing/helpers.py | 1083 +++ .../_vendor/pyparsing/results.py | 760 ++ .../_vendor/pyparsing/testing.py | 331 + .../_vendor/pyparsing/unicode.py | 332 + .../pkg_resources/_vendor/pyparsing/util.py | 235 + .../pkg_resources/_vendor/zipp.py | 329 + .../pkg_resources/extern/__init__.py | 76 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 4358 bytes .../setuptools-62.6.0.dist-info/INSTALLER | 1 + .../setuptools-62.6.0.dist-info/LICENSE | 19 + .../setuptools-62.6.0.dist-info/METADATA | 130 + .../setuptools-62.6.0.dist-info/RECORD | 462 + .../setuptools-62.6.0.dist-info/REQUESTED | 0 .../setuptools-62.6.0.dist-info/WHEEL | 5 + .../entry_points.txt | 56 + .../setuptools-62.6.0.dist-info/top_level.txt | 4 + .../site-packages/setuptools/__init__.py | 189 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 10021 bytes .../_deprecation_warning.cpython-311.pyc | Bin 0 -> 683 bytes .../__pycache__/_entry_points.cpython-311.pyc | Bin 0 -> 4832 bytes .../__pycache__/_imp.cpython-311.pyc | Bin 0 -> 3696 bytes .../__pycache__/_importlib.cpython-311.pyc | Bin 0 -> 1996 bytes .../__pycache__/_itertools.cpython-311.pyc | Bin 0 -> 1195 bytes .../__pycache__/_path.cpython-311.pyc | Bin 0 -> 620 bytes .../__pycache__/_reqs.cpython-311.pyc | Bin 0 -> 1177 bytes .../__pycache__/archive_util.cpython-311.pyc | Bin 0 -> 10205 bytes .../__pycache__/build_meta.cpython-311.pyc | Bin 0 -> 16371 bytes .../__pycache__/dep_util.cpython-311.pyc | Bin 0 -> 1331 bytes .../__pycache__/depends.cpython-311.pyc | Bin 0 -> 8016 bytes .../__pycache__/discovery.cpython-311.pyc | Bin 0 -> 31134 bytes .../__pycache__/dist.cpython-311.pyc | Bin 0 -> 64835 bytes .../__pycache__/errors.cpython-311.pyc | Bin 0 -> 2992 bytes .../__pycache__/extension.cpython-311.pyc | Bin 0 -> 6527 bytes .../__pycache__/glob.cpython-311.pyc | Bin 0 -> 6605 bytes .../__pycache__/installer.cpython-311.pyc | Bin 0 -> 5655 bytes .../__pycache__/launch.cpython-311.pyc | Bin 0 -> 1571 bytes .../__pycache__/logging.cpython-311.pyc | Bin 0 -> 1989 bytes .../__pycache__/monkey.cpython-311.pyc | Bin 0 -> 7493 bytes .../__pycache__/msvc.cpython-311.pyc | Bin 0 -> 67181 bytes .../__pycache__/namespaces.cpython-311.pyc | Bin 0 -> 5703 bytes .../__pycache__/package_index.cpython-311.pyc | Bin 0 -> 60798 bytes .../__pycache__/py34compat.cpython-311.pyc | Bin 0 -> 758 bytes .../__pycache__/sandbox.cpython-311.pyc | Bin 0 -> 27374 bytes .../__pycache__/unicode_utils.cpython-311.pyc | Bin 0 -> 1860 bytes .../__pycache__/version.cpython-311.pyc | Bin 0 -> 478 bytes .../__pycache__/wheel.cpython-311.pyc | Bin 0 -> 15114 bytes .../windows_support.cpython-311.pyc | Bin 0 -> 1475 bytes .../setuptools/_deprecation_warning.py | 7 + .../setuptools/_distutils/__init__.py | 24 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 813 bytes .../__pycache__/_collections.cpython-311.pyc | Bin 0 -> 2963 bytes .../__pycache__/_functools.cpython-311.pyc | Bin 0 -> 904 bytes .../__pycache__/_macos_compat.cpython-311.pyc | Bin 0 -> 613 bytes .../__pycache__/_msvccompiler.cpython-311.pyc | Bin 0 -> 26268 bytes .../__pycache__/archive_util.cpython-311.pyc | Bin 0 -> 10712 bytes .../__pycache__/bcppcompiler.cpython-311.pyc | Bin 0 -> 13176 bytes .../__pycache__/ccompiler.cpython-311.pyc | Bin 0 -> 45341 bytes .../__pycache__/cmd.cpython-311.pyc | Bin 0 -> 18814 bytes .../__pycache__/config.cpython-311.pyc | Bin 0 -> 6088 bytes .../__pycache__/core.cpython-311.pyc | Bin 0 -> 9678 bytes .../cygwinccompiler.cpython-311.pyc | Bin 0 -> 13079 bytes .../__pycache__/debug.cpython-311.pyc | Bin 0 -> 365 bytes .../__pycache__/dep_util.cpython-311.pyc | Bin 0 -> 4032 bytes .../__pycache__/dir_util.cpython-311.pyc | Bin 0 -> 10112 bytes .../__pycache__/dist.cpython-311.pyc | Bin 0 -> 54757 bytes .../__pycache__/errors.cpython-311.pyc | Bin 0 -> 6838 bytes .../__pycache__/extension.cpython-311.pyc | Bin 0 -> 10171 bytes .../__pycache__/fancy_getopt.cpython-311.pyc | Bin 0 -> 17130 bytes .../__pycache__/file_util.cpython-311.pyc | Bin 0 -> 10396 bytes .../__pycache__/filelist.cpython-311.pyc | Bin 0 -> 17520 bytes .../__pycache__/log.cpython-311.pyc | Bin 0 -> 3973 bytes .../__pycache__/msvc9compiler.cpython-311.pyc | Bin 0 -> 33145 bytes .../__pycache__/msvccompiler.cpython-311.pyc | Bin 0 -> 26654 bytes .../__pycache__/py38compat.cpython-311.pyc | Bin 0 -> 626 bytes .../__pycache__/py39compat.cpython-311.pyc | Bin 0 -> 1033 bytes .../__pycache__/spawn.cpython-311.pyc | Bin 0 -> 4414 bytes .../__pycache__/sysconfig.cpython-311.pyc | Bin 0 -> 21298 bytes .../__pycache__/text_file.cpython-311.pyc | Bin 0 -> 11358 bytes .../__pycache__/unixccompiler.cpython-311.pyc | Bin 0 -> 14723 bytes .../__pycache__/util.cpython-311.pyc | Bin 0 -> 20802 bytes .../__pycache__/version.cpython-311.pyc | Bin 0 -> 11351 bytes .../versionpredicate.cpython-311.pyc | Bin 0 -> 7790 bytes .../setuptools/_distutils/_collections.py | 56 + .../setuptools/_distutils/_functools.py | 20 + .../setuptools/_distutils/_macos_compat.py | 12 + .../setuptools/_distutils/_msvccompiler.py | 591 ++ .../setuptools/_distutils/archive_util.py | 280 + .../setuptools/_distutils/bcppcompiler.py | 398 + .../setuptools/_distutils/ccompiler.py | 1193 +++ .../setuptools/_distutils/cmd.py | 434 + .../setuptools/_distutils/command/__init__.py | 32 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 600 bytes .../command/__pycache__/bdist.cpython-311.pyc | Bin 0 -> 5552 bytes .../__pycache__/bdist_dumb.cpython-311.pyc | Bin 0 -> 5745 bytes .../__pycache__/bdist_msi.cpython-311.pyc | Bin 0 -> 37224 bytes .../__pycache__/bdist_rpm.cpython-311.pyc | Bin 0 -> 23099 bytes .../__pycache__/bdist_wininst.cpython-311.pyc | Bin 0 -> 16267 bytes .../command/__pycache__/build.cpython-311.pyc | Bin 0 -> 6094 bytes .../__pycache__/build_clib.cpython-311.pyc | Bin 0 -> 7820 bytes .../__pycache__/build_ext.cpython-311.pyc | Bin 0 -> 30185 bytes .../__pycache__/build_py.cpython-311.pyc | Bin 0 -> 17773 bytes .../__pycache__/build_scripts.cpython-311.pyc | Bin 0 -> 7906 bytes .../command/__pycache__/check.cpython-311.pyc | Bin 0 -> 7049 bytes .../command/__pycache__/clean.cpython-311.pyc | Bin 0 -> 3236 bytes .../__pycache__/config.cpython-311.pyc | Bin 0 -> 16298 bytes .../__pycache__/install.cpython-311.pyc | Bin 0 -> 29236 bytes .../__pycache__/install_data.cpython-311.pyc | Bin 0 -> 3821 bytes .../install_egg_info.cpython-311.pyc | Bin 0 -> 5254 bytes .../install_headers.cpython-311.pyc | Bin 0 -> 2404 bytes .../__pycache__/install_lib.cpython-311.pyc | Bin 0 -> 8736 bytes .../install_scripts.cpython-311.pyc | Bin 0 -> 3201 bytes .../__pycache__/py37compat.cpython-311.pyc | Bin 0 -> 1582 bytes .../__pycache__/register.cpython-311.pyc | Bin 0 -> 15552 bytes .../command/__pycache__/sdist.cpython-311.pyc | Bin 0 -> 23850 bytes .../__pycache__/upload.cpython-311.pyc | Bin 0 -> 10407 bytes .../setuptools/_distutils/command/bdist.py | 155 + .../_distutils/command/bdist_dumb.py | 142 + .../_distutils/command/bdist_msi.py | 1114 +++ .../_distutils/command/bdist_rpm.py | 607 ++ .../_distutils/command/bdist_wininst.py | 418 + .../setuptools/_distutils/command/build.py | 152 + .../_distutils/command/build_clib.py | 208 + .../_distutils/command/build_ext.py | 780 ++ .../setuptools/_distutils/command/build_py.py | 413 + .../_distutils/command/build_scripts.py | 173 + .../setuptools/_distutils/command/check.py | 153 + .../setuptools/_distutils/command/clean.py | 76 + .../setuptools/_distutils/command/config.py | 376 + .../setuptools/_distutils/command/install.py | 806 ++ .../_distutils/command/install_data.py | 84 + .../_distutils/command/install_egg_info.py | 87 + .../_distutils/command/install_headers.py | 45 + .../_distutils/command/install_lib.py | 238 + .../_distutils/command/install_scripts.py | 61 + .../_distutils/command/py37compat.py | 31 + .../setuptools/_distutils/command/register.py | 319 + .../setuptools/_distutils/command/sdist.py | 531 + .../setuptools/_distutils/command/upload.py | 205 + .../setuptools/_distutils/config.py | 139 + .../setuptools/_distutils/core.py | 283 + .../setuptools/_distutils/cygwinccompiler.py | 395 + .../setuptools/_distutils/debug.py | 5 + .../setuptools/_distutils/dep_util.py | 96 + .../setuptools/_distutils/dir_util.py | 239 + .../setuptools/_distutils/dist.py | 1286 +++ .../setuptools/_distutils/errors.py | 127 + .../setuptools/_distutils/extension.py | 248 + .../setuptools/_distutils/fancy_getopt.py | 468 + .../setuptools/_distutils/file_util.py | 245 + .../setuptools/_distutils/filelist.py | 371 + .../setuptools/_distutils/log.py | 80 + .../setuptools/_distutils/msvc9compiler.py | 820 ++ .../setuptools/_distutils/msvccompiler.py | 683 ++ .../setuptools/_distutils/py38compat.py | 8 + .../setuptools/_distutils/py39compat.py | 22 + .../setuptools/_distutils/spawn.py | 107 + .../setuptools/_distutils/sysconfig.py | 549 ++ .../setuptools/_distutils/text_file.py | 287 + .../setuptools/_distutils/unixccompiler.py | 382 + .../setuptools/_distutils/util.py | 520 + .../setuptools/_distutils/version.py | 358 + .../setuptools/_distutils/versionpredicate.py | 175 + .../site-packages/setuptools/_entry_points.py | 86 + .../site-packages/setuptools/_imp.py | 82 + .../site-packages/setuptools/_importlib.py | 47 + .../site-packages/setuptools/_itertools.py | 23 + .../site-packages/setuptools/_path.py | 7 + .../site-packages/setuptools/_reqs.py | 19 + .../setuptools/_vendor/__init__.py | 0 .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 236 bytes .../__pycache__/ordered_set.cpython-311.pyc | Bin 0 -> 21822 bytes .../typing_extensions.cpython-311.pyc | Bin 0 -> 107653 bytes .../_vendor/__pycache__/zipp.cpython-311.pyc | Bin 0 -> 16029 bytes .../_vendor/importlib_metadata/__init__.py | 1047 ++ .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 58275 bytes .../__pycache__/_adapters.cpython-311.pyc | Bin 0 -> 3888 bytes .../__pycache__/_collections.cpython-311.pyc | Bin 0 -> 2235 bytes .../__pycache__/_compat.cpython-311.pyc | Bin 0 -> 2757 bytes .../__pycache__/_functools.cpython-311.pyc | Bin 0 -> 3675 bytes .../__pycache__/_itertools.cpython-311.pyc | Bin 0 -> 2638 bytes .../__pycache__/_meta.cpython-311.pyc | Bin 0 -> 3042 bytes .../__pycache__/_text.cpython-311.pyc | Bin 0 -> 4433 bytes .../_vendor/importlib_metadata/_adapters.py | 68 + .../importlib_metadata/_collections.py | 30 + .../_vendor/importlib_metadata/_compat.py | 71 + .../_vendor/importlib_metadata/_functools.py | 104 + .../_vendor/importlib_metadata/_itertools.py | 73 + .../_vendor/importlib_metadata/_meta.py | 48 + .../_vendor/importlib_metadata/_text.py | 99 + .../_vendor/importlib_resources/__init__.py | 36 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 870 bytes .../__pycache__/_adapters.cpython-311.pyc | Bin 0 -> 10787 bytes .../__pycache__/_common.cpython-311.pyc | Bin 0 -> 4314 bytes .../__pycache__/_compat.cpython-311.pyc | Bin 0 -> 5599 bytes .../__pycache__/_itertools.cpython-311.pyc | Bin 0 -> 1432 bytes .../__pycache__/_legacy.cpython-311.pyc | Bin 0 -> 6530 bytes .../__pycache__/abc.cpython-311.pyc | Bin 0 -> 7531 bytes .../__pycache__/readers.cpython-311.pyc | Bin 0 -> 8405 bytes .../__pycache__/simple.cpython-311.pyc | Bin 0 -> 6427 bytes .../_vendor/importlib_resources/_adapters.py | 170 + .../_vendor/importlib_resources/_common.py | 104 + .../_vendor/importlib_resources/_compat.py | 98 + .../_vendor/importlib_resources/_itertools.py | 35 + .../_vendor/importlib_resources/_legacy.py | 121 + .../_vendor/importlib_resources/abc.py | 137 + .../_vendor/importlib_resources/readers.py | 122 + .../_vendor/importlib_resources/simple.py | 116 + .../setuptools/_vendor/jaraco/__init__.py | 0 .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 243 bytes .../__pycache__/context.cpython-311.pyc | Bin 0 -> 9466 bytes .../__pycache__/functools.cpython-311.pyc | Bin 0 -> 20323 bytes .../setuptools/_vendor/jaraco/context.py | 213 + .../setuptools/_vendor/jaraco/functools.py | 525 + .../_vendor/jaraco/text/__init__.py | 599 ++ .../text/__pycache__/__init__.cpython-311.pyc | Bin 0 -> 26637 bytes .../_vendor/more_itertools/__init__.py | 4 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 336 bytes .../__pycache__/more.cpython-311.pyc | Bin 0 -> 149223 bytes .../__pycache__/recipes.cpython-311.pyc | Bin 0 -> 23805 bytes .../setuptools/_vendor/more_itertools/more.py | 3824 ++++++++ .../_vendor/more_itertools/recipes.py | 620 ++ .../setuptools/_vendor/nspektr/__init__.py | 145 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 8075 bytes .../__pycache__/_compat.cpython-311.pyc | Bin 0 -> 1479 bytes .../setuptools/_vendor/nspektr/_compat.py | 21 + .../setuptools/_vendor/ordered_set.py | 488 + .../setuptools/_vendor/packaging/__about__.py | 26 + .../setuptools/_vendor/packaging/__init__.py | 25 + .../__pycache__/__about__.cpython-311.pyc | Bin 0 -> 687 bytes .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 608 bytes .../__pycache__/_manylinux.cpython-311.pyc | Bin 0 -> 13274 bytes .../__pycache__/_musllinux.cpython-311.pyc | Bin 0 -> 8042 bytes .../__pycache__/_structures.cpython-311.pyc | Bin 0 -> 3730 bytes .../__pycache__/markers.cpython-311.pyc | Bin 0 -> 16576 bytes .../__pycache__/requirements.cpython-311.pyc | Bin 0 -> 7691 bytes .../__pycache__/specifiers.cpython-311.pyc | Bin 0 -> 34408 bytes .../__pycache__/tags.cpython-311.pyc | Bin 0 -> 21393 bytes .../__pycache__/utils.cpython-311.pyc | Bin 0 -> 6728 bytes .../__pycache__/version.cpython-311.pyc | Bin 0 -> 21920 bytes .../_vendor/packaging/_manylinux.py | 301 + .../_vendor/packaging/_musllinux.py | 136 + .../_vendor/packaging/_structures.py | 61 + .../setuptools/_vendor/packaging/markers.py | 304 + .../_vendor/packaging/requirements.py | 146 + .../_vendor/packaging/specifiers.py | 802 ++ .../setuptools/_vendor/packaging/tags.py | 487 + .../setuptools/_vendor/packaging/utils.py | 136 + .../setuptools/_vendor/packaging/version.py | 504 + .../setuptools/_vendor/pyparsing/__init__.py | 331 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 8381 bytes .../__pycache__/actions.cpython-311.pyc | Bin 0 -> 8510 bytes .../__pycache__/common.cpython-311.pyc | Bin 0 -> 14829 bytes .../__pycache__/core.cpython-311.pyc | Bin 0 -> 277164 bytes .../__pycache__/exceptions.cpython-311.pyc | Bin 0 -> 12978 bytes .../__pycache__/helpers.cpython-311.pyc | Bin 0 -> 53433 bytes .../__pycache__/results.cpython-311.pyc | Bin 0 -> 36367 bytes .../__pycache__/testing.cpython-311.pyc | Bin 0 -> 19519 bytes .../__pycache__/unicode.cpython-311.pyc | Bin 0 -> 14977 bytes .../__pycache__/util.cpython-311.pyc | Bin 0 -> 14308 bytes .../setuptools/_vendor/pyparsing/actions.py | 207 + .../setuptools/_vendor/pyparsing/common.py | 424 + .../setuptools/_vendor/pyparsing/core.py | 5812 +++++++++++ .../_vendor/pyparsing/diagram/__init__.py | 611 ++ .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 27666 bytes .../_vendor/pyparsing/exceptions.py | 267 + .../setuptools/_vendor/pyparsing/helpers.py | 1083 +++ .../setuptools/_vendor/pyparsing/results.py | 760 ++ .../setuptools/_vendor/pyparsing/testing.py | 331 + .../setuptools/_vendor/pyparsing/unicode.py | 332 + .../setuptools/_vendor/pyparsing/util.py | 235 + .../setuptools/_vendor/tomli/__init__.py | 11 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 458 bytes .../tomli/__pycache__/_parser.cpython-311.pyc | Bin 0 -> 30897 bytes .../tomli/__pycache__/_re.cpython-311.pyc | Bin 0 -> 4537 bytes .../tomli/__pycache__/_types.cpython-311.pyc | Bin 0 -> 450 bytes .../setuptools/_vendor/tomli/_parser.py | 691 ++ .../setuptools/_vendor/tomli/_re.py | 107 + .../setuptools/_vendor/tomli/_types.py | 10 + .../setuptools/_vendor/typing_extensions.py | 2296 +++++ .../site-packages/setuptools/_vendor/zipp.py | 329 + .../site-packages/setuptools/archive_util.py | 213 + .../site-packages/setuptools/build_meta.py | 304 + .../setuptools/command/__init__.py | 8 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 549 bytes .../command/__pycache__/alias.cpython-311.pyc | Bin 0 -> 3944 bytes .../__pycache__/bdist_egg.cpython-311.pyc | Bin 0 -> 25626 bytes .../__pycache__/bdist_rpm.cpython-311.pyc | Bin 0 -> 2232 bytes .../command/__pycache__/build.cpython-311.pyc | Bin 0 -> 1753 bytes .../__pycache__/build_clib.cpython-311.pyc | Bin 0 -> 4165 bytes .../__pycache__/build_ext.cpython-311.pyc | Bin 0 -> 18201 bytes .../__pycache__/build_py.cpython-311.pyc | Bin 0 -> 17624 bytes .../__pycache__/develop.cpython-311.pyc | Bin 0 -> 10960 bytes .../__pycache__/dist_info.cpython-311.pyc | Bin 0 -> 4241 bytes .../__pycache__/easy_install.cpython-311.pyc | Bin 0 -> 119377 bytes .../__pycache__/egg_info.cpython-311.pyc | Bin 0 -> 39315 bytes .../__pycache__/install.cpython-311.pyc | Bin 0 -> 6857 bytes .../install_egg_info.cpython-311.pyc | Bin 0 -> 4170 bytes .../__pycache__/install_lib.cpython-311.pyc | Bin 0 -> 6452 bytes .../install_scripts.cpython-311.pyc | Bin 0 -> 4321 bytes .../__pycache__/py36compat.cpython-311.pyc | Bin 0 -> 8078 bytes .../__pycache__/register.cpython-311.pyc | Bin 0 -> 1168 bytes .../__pycache__/rotate.cpython-311.pyc | Bin 0 -> 4228 bytes .../__pycache__/saveopts.cpython-311.pyc | Bin 0 -> 1408 bytes .../command/__pycache__/sdist.cpython-311.pyc | Bin 0 -> 11821 bytes .../__pycache__/setopt.cpython-311.pyc | Bin 0 -> 7720 bytes .../command/__pycache__/test.cpython-311.pyc | Bin 0 -> 14737 bytes .../__pycache__/upload.cpython-311.pyc | Bin 0 -> 1132 bytes .../__pycache__/upload_docs.cpython-311.pyc | Bin 0 -> 11999 bytes .../site-packages/setuptools/command/alias.py | 78 + .../setuptools/command/bdist_egg.py | 457 + .../setuptools/command/bdist_rpm.py | 40 + .../site-packages/setuptools/command/build.py | 24 + .../setuptools/command/build_clib.py | 101 + .../setuptools/command/build_ext.py | 328 + .../setuptools/command/build_py.py | 298 + .../setuptools/command/develop.py | 193 + .../setuptools/command/dist_info.py | 69 + .../setuptools/command/easy_install.py | 2312 +++++ .../setuptools/command/egg_info.py | 753 ++ .../setuptools/command/install.py | 139 + .../setuptools/command/install_egg_info.py | 63 + .../setuptools/command/install_lib.py | 122 + .../setuptools/command/install_scripts.py | 70 + .../setuptools/command/launcher manifest.xml | 15 + .../setuptools/command/py36compat.py | 134 + .../setuptools/command/register.py | 18 + .../setuptools/command/rotate.py | 64 + .../setuptools/command/saveopts.py | 22 + .../site-packages/setuptools/command/sdist.py | 196 + .../setuptools/command/setopt.py | 149 + .../site-packages/setuptools/command/test.py | 251 + .../setuptools/command/upload.py | 17 + .../setuptools/command/upload_docs.py | 212 + .../setuptools/config/__init__.py | 35 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 2044 bytes .../_apply_pyprojecttoml.cpython-311.pyc | Bin 0 -> 22575 bytes .../config/__pycache__/expand.cpython-311.pyc | Bin 0 -> 28948 bytes .../__pycache__/pyprojecttoml.cpython-311.pyc | Bin 0 -> 27089 bytes .../__pycache__/setupcfg.cpython-311.pyc | Bin 0 -> 29713 bytes .../setuptools/config/_apply_pyprojecttoml.py | 377 + .../config/_validate_pyproject/__init__.py | 34 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 2360 bytes .../error_reporting.cpython-311.pyc | Bin 0 -> 20251 bytes .../extra_validations.cpython-311.pyc | Bin 0 -> 1929 bytes .../fastjsonschema_exceptions.cpython-311.pyc | Bin 0 -> 3284 bytes ...fastjsonschema_validations.cpython-311.pyc | Bin 0 -> 192656 bytes .../__pycache__/formats.cpython-311.pyc | Bin 0 -> 14250 bytes .../_validate_pyproject/error_reporting.py | 318 + .../_validate_pyproject/extra_validations.py | 36 + .../fastjsonschema_exceptions.py | 51 + .../fastjsonschema_validations.py | 1035 ++ .../config/_validate_pyproject/formats.py | 257 + .../site-packages/setuptools/config/expand.py | 479 + .../setuptools/config/pyprojecttoml.py | 484 + .../setuptools/config/setupcfg.py | 713 ++ .../site-packages/setuptools/dep_util.py | 25 + .../site-packages/setuptools/depends.py | 176 + .../site-packages/setuptools/discovery.py | 588 ++ .../site-packages/setuptools/dist.py | 1234 +++ .../site-packages/setuptools/errors.py | 58 + .../site-packages/setuptools/extension.py | 140 + .../setuptools/extern/__init__.py | 76 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 4445 bytes .../site-packages/setuptools/glob.py | 167 + .../site-packages/setuptools/installer.py | 104 + .../site-packages/setuptools/launch.py | 36 + .../site-packages/setuptools/logging.py | 36 + .../site-packages/setuptools/monkey.py | 177 + .../site-packages/setuptools/msvc.py | 1805 ++++ .../site-packages/setuptools/namespaces.py | 107 + .../site-packages/setuptools/package_index.py | 1126 +++ .../site-packages/setuptools/py34compat.py | 13 + .../site-packages/setuptools/sandbox.py | 530 + .../setuptools/script (dev).tmpl | 6 + .../site-packages/setuptools/script.tmpl | 3 + .../site-packages/setuptools/unicode_utils.py | 42 + .../site-packages/setuptools/version.py | 6 + .../site-packages/setuptools/wheel.py | 213 + .../setuptools/windows_support.py | 29 + .../site-packages/werkzeug/__init__.py | 6 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 463 bytes .../__pycache__/_internal.cpython-311.pyc | Bin 0 -> 28461 bytes .../__pycache__/_reloader.cpython-311.pyc | Bin 0 -> 23155 bytes .../datastructures.cpython-311.pyc | Bin 0 -> 154620 bytes .../__pycache__/exceptions.cpython-311.pyc | Bin 0 -> 38915 bytes .../__pycache__/formparser.cpython-311.pyc | Bin 0 -> 21049 bytes .../werkzeug/__pycache__/http.cpython-311.pyc | Bin 0 -> 55591 bytes .../__pycache__/local.cpython-311.pyc | Bin 0 -> 32690 bytes .../__pycache__/security.cpython-311.pyc | Bin 0 -> 7243 bytes .../__pycache__/serving.cpython-311.pyc | Bin 0 -> 49247 bytes .../werkzeug/__pycache__/test.cpython-311.pyc | Bin 0 -> 62462 bytes .../__pycache__/testapp.cpython-311.pyc | Bin 0 -> 12902 bytes .../werkzeug/__pycache__/urls.cpython-311.pyc | Bin 0 -> 51964 bytes .../__pycache__/user_agent.cpython-311.pyc | Bin 0 -> 2455 bytes .../__pycache__/utils.cpython-311.pyc | Bin 0 -> 31006 bytes .../werkzeug/__pycache__/wsgi.cpython-311.pyc | Bin 0 -> 47431 bytes .../site-packages/werkzeug/_internal.py | 553 ++ .../site-packages/werkzeug/_reloader.py | 446 + .../site-packages/werkzeug/datastructures.py | 3040 ++++++ .../site-packages/werkzeug/datastructures.pyi | 921 ++ .../site-packages/werkzeug/debug/__init__.py | 533 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 25307 bytes .../debug/__pycache__/console.cpython-311.pyc | Bin 0 -> 14159 bytes .../debug/__pycache__/repr.cpython-311.pyc | Bin 0 -> 16984 bytes .../debug/__pycache__/tbtools.cpython-311.pyc | Bin 0 -> 18241 bytes .../site-packages/werkzeug/debug/console.py | 222 + .../site-packages/werkzeug/debug/repr.py | 285 + .../werkzeug/debug/shared/ICON_LICENSE.md | 6 + .../werkzeug/debug/shared/console.png | Bin 0 -> 507 bytes .../werkzeug/debug/shared/debugger.js | 359 + .../werkzeug/debug/shared/less.png | Bin 0 -> 191 bytes .../werkzeug/debug/shared/more.png | Bin 0 -> 200 bytes .../werkzeug/debug/shared/style.css | 150 + .../site-packages/werkzeug/debug/tbtools.py | 435 + .../site-packages/werkzeug/exceptions.py | 884 ++ .../site-packages/werkzeug/formparser.py | 465 + .../python3.11/site-packages/werkzeug/http.py | 1327 +++ .../site-packages/werkzeug/local.py | 648 ++ .../werkzeug/middleware/__init__.py | 22 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 755 bytes .../__pycache__/dispatcher.cpython-311.pyc | Bin 0 -> 3552 bytes .../__pycache__/http_proxy.cpython-311.pyc | Bin 0 -> 11320 bytes .../__pycache__/lint.cpython-311.pyc | Bin 0 -> 21389 bytes .../__pycache__/profiler.cpython-311.pyc | Bin 0 -> 7113 bytes .../__pycache__/proxy_fix.cpython-311.pyc | Bin 0 -> 8003 bytes .../__pycache__/shared_data.cpython-311.pyc | Bin 0 -> 14441 bytes .../werkzeug/middleware/dispatcher.py | 78 + .../werkzeug/middleware/http_proxy.py | 230 + .../site-packages/werkzeug/middleware/lint.py | 420 + .../werkzeug/middleware/profiler.py | 139 + .../werkzeug/middleware/proxy_fix.py | 187 + .../werkzeug/middleware/shared_data.py | 280 + .../site-packages/werkzeug/py.typed | 0 .../werkzeug/routing/__init__.py | 133 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 5102 bytes .../__pycache__/converters.cpython-311.pyc | Bin 0 -> 12515 bytes .../__pycache__/exceptions.cpython-311.pyc | Bin 0 -> 9174 bytes .../routing/__pycache__/map.cpython-311.pyc | Bin 0 -> 44445 bytes .../__pycache__/matcher.cpython-311.pyc | Bin 0 -> 8829 bytes .../routing/__pycache__/rules.cpython-311.pyc | Bin 0 -> 42930 bytes .../werkzeug/routing/converters.py | 257 + .../werkzeug/routing/exceptions.py | 146 + .../site-packages/werkzeug/routing/map.py | 944 ++ .../site-packages/werkzeug/routing/matcher.py | 192 + .../site-packages/werkzeug/routing/rules.py | 904 ++ .../site-packages/werkzeug/sansio/__init__.py | 0 .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 233 bytes .../sansio/__pycache__/http.cpython-311.pyc | Bin 0 -> 5836 bytes .../__pycache__/multipart.cpython-311.pyc | Bin 0 -> 13919 bytes .../__pycache__/request.cpython-311.pyc | Bin 0 -> 24016 bytes .../__pycache__/response.cpython-311.pyc | Bin 0 -> 33170 bytes .../sansio/__pycache__/utils.cpython-311.pyc | Bin 0 -> 6966 bytes .../site-packages/werkzeug/sansio/http.py | 136 + .../werkzeug/sansio/multipart.py | 287 + .../site-packages/werkzeug/sansio/request.py | 547 ++ .../site-packages/werkzeug/sansio/response.py | 704 ++ .../site-packages/werkzeug/sansio/utils.py | 165 + .../site-packages/werkzeug/security.py | 140 + .../site-packages/werkzeug/serving.py | 1069 ++ .../python3.11/site-packages/werkzeug/test.py | 1338 +++ .../site-packages/werkzeug/testapp.py | 241 + .../python3.11/site-packages/werkzeug/urls.py | 1067 ++ .../site-packages/werkzeug/user_agent.py | 47 + .../site-packages/werkzeug/utils.py | 706 ++ .../werkzeug/wrappers/__init__.py | 3 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 398 bytes .../__pycache__/request.cpython-311.pyc | Bin 0 -> 27192 bytes .../__pycache__/response.cpython-311.pyc | Bin 0 -> 40516 bytes .../werkzeug/wrappers/request.py | 634 ++ .../werkzeug/wrappers/response.py | 885 ++ .../python3.11/site-packages/werkzeug/wsgi.py | 1058 ++ docs/examples/flask/venv/lib64 | 1 + docs/examples/flask/venv/pyvenv.cfg | 5 + docs/flask/Client.py | 85 - docs/flask/GI_interface.py | 80 - docs/flask/README.md | 13 - docs/flask/app.py | 108 - docs/flask/templates/calc.html | 81 - docs/flask/templates/view.html | 44 - 1780 files changed, 331691 insertions(+), 424 deletions(-) create mode 100644 docs/examples/flask/__pycache__/app.cpython-311.pyc create mode 100644 docs/examples/flask/app.py create mode 100644 docs/examples/flask/index.html create mode 100644 docs/examples/flask/scripts/cal.js create mode 100644 docs/examples/flask/scripts/web.js rename docs/{flask/static/style.css => examples/flask/static/main.css} (100%) create mode 100644 docs/examples/flask/templates/about.html create mode 100644 docs/examples/flask/templates/api.html rename docs/{ => examples}/flask/templates/base.html (69%) create mode 100644 docs/examples/flask/templates/calc.html create mode 100644 docs/examples/flask/templates/demo.html rename docs/{ => examples}/flask/templates/index.html (94%) create mode 100644 docs/examples/flask/templates/research.html create mode 100644 docs/examples/flask/templates/view.html create mode 100644 docs/examples/flask/venv/bin/Activate.ps1 create mode 100644 docs/examples/flask/venv/bin/activate create mode 100644 docs/examples/flask/venv/bin/activate.csh create mode 100644 docs/examples/flask/venv/bin/activate.fish create mode 100755 docs/examples/flask/venv/bin/flask create mode 100755 docs/examples/flask/venv/bin/pip create mode 100755 docs/examples/flask/venv/bin/pip3 create mode 100755 docs/examples/flask/venv/bin/pip3.11 create mode 120000 docs/examples/flask/venv/bin/python create mode 120000 docs/examples/flask/venv/bin/python3 create mode 120000 docs/examples/flask/venv/bin/python3.11 create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/Flask-2.2.3.dist-info/INSTALLER create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/Flask-2.2.3.dist-info/LICENSE.rst create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/Flask-2.2.3.dist-info/METADATA create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/Flask-2.2.3.dist-info/RECORD create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/Flask-2.2.3.dist-info/REQUESTED create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/Flask-2.2.3.dist-info/WHEEL create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/Flask-2.2.3.dist-info/entry_points.txt create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/Flask-2.2.3.dist-info/top_level.txt create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/INSTALLER create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/LICENSE.rst create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/METADATA create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/RECORD create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/WHEEL create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/entry_points.txt create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/top_level.txt create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/INSTALLER create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/LICENSE.rst create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/METADATA create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/RECORD create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/WHEEL create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/top_level.txt create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/Werkzeug-2.2.3.dist-info/INSTALLER create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/Werkzeug-2.2.3.dist-info/LICENSE.rst create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/Werkzeug-2.2.3.dist-info/METADATA create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/Werkzeug-2.2.3.dist-info/RECORD create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/Werkzeug-2.2.3.dist-info/WHEEL create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/Werkzeug-2.2.3.dist-info/top_level.txt create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/_distutils_hack/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/_distutils_hack/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/_distutils_hack/__pycache__/override.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/_distutils_hack/override.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/click-8.1.3.dist-info/INSTALLER create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/click-8.1.3.dist-info/LICENSE.rst create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/click-8.1.3.dist-info/METADATA create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/click-8.1.3.dist-info/RECORD create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/click-8.1.3.dist-info/WHEEL create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/click-8.1.3.dist-info/top_level.txt create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/click/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/click/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/click/__pycache__/_compat.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/click/__pycache__/_termui_impl.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/click/__pycache__/_textwrap.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/click/__pycache__/_winconsole.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/click/__pycache__/core.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/click/__pycache__/decorators.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/click/__pycache__/exceptions.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/click/__pycache__/formatting.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/click/__pycache__/globals.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/click/__pycache__/parser.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/click/__pycache__/shell_completion.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/click/__pycache__/termui.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/click/__pycache__/testing.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/click/__pycache__/types.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/click/__pycache__/utils.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/click/_compat.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/click/_termui_impl.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/click/_textwrap.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/click/_winconsole.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/click/core.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/click/decorators.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/click/exceptions.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/click/formatting.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/click/globals.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/click/parser.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/click/py.typed create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/click/shell_completion.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/click/termui.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/click/testing.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/click/types.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/click/utils.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/distutils-precedence.pth create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/flask/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/flask/__main__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/flask/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/flask/__pycache__/__main__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/flask/__pycache__/app.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/flask/__pycache__/blueprints.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/flask/__pycache__/cli.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/flask/__pycache__/config.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/flask/__pycache__/ctx.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/flask/__pycache__/debughelpers.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/flask/__pycache__/globals.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/flask/__pycache__/helpers.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/flask/__pycache__/logging.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/flask/__pycache__/scaffold.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/flask/__pycache__/sessions.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/flask/__pycache__/signals.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/flask/__pycache__/templating.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/flask/__pycache__/testing.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/flask/__pycache__/typing.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/flask/__pycache__/views.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/flask/__pycache__/wrappers.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/flask/app.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/flask/blueprints.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/flask/cli.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/flask/config.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/flask/ctx.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/flask/debughelpers.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/flask/globals.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/flask/helpers.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/flask/json/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/flask/json/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/flask/json/__pycache__/provider.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/flask/json/__pycache__/tag.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/flask/json/provider.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/flask/json/tag.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/flask/logging.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/flask/py.typed create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/flask/scaffold.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/flask/sessions.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/flask/signals.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/flask/templating.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/flask/testing.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/flask/typing.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/flask/views.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/flask/wrappers.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/itsdangerous-2.1.2.dist-info/INSTALLER create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/itsdangerous-2.1.2.dist-info/LICENSE.rst create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/itsdangerous-2.1.2.dist-info/METADATA create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/itsdangerous-2.1.2.dist-info/RECORD create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/itsdangerous-2.1.2.dist-info/WHEEL create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/itsdangerous-2.1.2.dist-info/top_level.txt create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/itsdangerous/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/itsdangerous/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/itsdangerous/__pycache__/_json.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/itsdangerous/__pycache__/encoding.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/itsdangerous/__pycache__/exc.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/itsdangerous/__pycache__/serializer.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/itsdangerous/__pycache__/signer.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/itsdangerous/__pycache__/timed.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/itsdangerous/__pycache__/url_safe.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/itsdangerous/_json.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/itsdangerous/encoding.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/itsdangerous/exc.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/itsdangerous/py.typed create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/itsdangerous/serializer.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/itsdangerous/signer.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/itsdangerous/timed.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/itsdangerous/url_safe.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/__pycache__/_identifier.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/__pycache__/async_utils.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/__pycache__/bccache.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/__pycache__/compiler.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/__pycache__/constants.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/__pycache__/debug.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/__pycache__/defaults.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/__pycache__/environment.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/__pycache__/exceptions.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/__pycache__/ext.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/__pycache__/filters.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/__pycache__/idtracking.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/__pycache__/lexer.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/__pycache__/loaders.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/__pycache__/meta.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/__pycache__/nativetypes.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/__pycache__/nodes.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/__pycache__/optimizer.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/__pycache__/parser.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/__pycache__/runtime.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/__pycache__/sandbox.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/__pycache__/tests.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/__pycache__/utils.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/__pycache__/visitor.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/_identifier.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/async_utils.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/bccache.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/compiler.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/constants.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/debug.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/defaults.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/environment.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/exceptions.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/ext.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/filters.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/idtracking.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/lexer.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/loaders.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/meta.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/nativetypes.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/nodes.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/optimizer.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/parser.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/py.typed create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/runtime.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/sandbox.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/tests.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/utils.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/jinja2/visitor.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/markupsafe/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/markupsafe/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/markupsafe/__pycache__/_native.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/markupsafe/_native.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/markupsafe/_speedups.c create mode 100755 docs/examples/flask/venv/lib/python3.11/site-packages/markupsafe/_speedups.cpython-311-x86_64-linux-gnu.so create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/markupsafe/_speedups.pyi create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/markupsafe/py.typed create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip-23.0.1.dist-info/INSTALLER create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip-23.0.1.dist-info/LICENSE.txt create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip-23.0.1.dist-info/METADATA create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip-23.0.1.dist-info/RECORD create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip-23.0.1.dist-info/REQUESTED create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip-23.0.1.dist-info/WHEEL create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip-23.0.1.dist-info/entry_points.txt create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip-23.0.1.dist-info/top_level.txt create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/__main__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/__pip-runner__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/__pycache__/__main__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/__pycache__/__pip-runner__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/__pycache__/build_env.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/__pycache__/cache.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/__pycache__/configuration.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/__pycache__/exceptions.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/__pycache__/main.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/__pycache__/pyproject.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/__pycache__/self_outdated_check.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/__pycache__/wheel_builder.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/build_env.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/cache.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/cli/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/cli/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/cli/__pycache__/autocompletion.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/cli/__pycache__/base_command.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/cli/__pycache__/cmdoptions.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/cli/__pycache__/command_context.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/cli/__pycache__/main.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/cli/__pycache__/main_parser.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/cli/__pycache__/parser.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/cli/__pycache__/progress_bars.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/cli/__pycache__/req_command.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/cli/__pycache__/spinners.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/cli/__pycache__/status_codes.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/cli/autocompletion.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/cli/base_command.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/cli/cmdoptions.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/cli/command_context.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/cli/main.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/cli/main_parser.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/cli/parser.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/cli/progress_bars.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/cli/req_command.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/cli/spinners.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/cli/status_codes.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/commands/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/commands/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/commands/__pycache__/cache.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/commands/__pycache__/check.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/commands/__pycache__/completion.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/commands/__pycache__/configuration.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/commands/__pycache__/debug.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/commands/__pycache__/download.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/commands/__pycache__/freeze.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/commands/__pycache__/hash.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/commands/__pycache__/help.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/commands/__pycache__/index.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/commands/__pycache__/inspect.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/commands/__pycache__/install.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/commands/__pycache__/list.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/commands/__pycache__/search.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/commands/__pycache__/show.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/commands/__pycache__/uninstall.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/commands/__pycache__/wheel.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/commands/cache.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/commands/check.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/commands/completion.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/commands/configuration.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/commands/debug.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/commands/download.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/commands/freeze.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/commands/hash.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/commands/help.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/commands/index.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/commands/inspect.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/commands/install.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/commands/list.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/commands/search.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/commands/show.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/commands/uninstall.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/commands/wheel.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/configuration.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/distributions/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/distributions/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/distributions/__pycache__/base.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/distributions/__pycache__/installed.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/distributions/__pycache__/sdist.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/distributions/__pycache__/wheel.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/distributions/base.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/distributions/installed.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/distributions/sdist.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/distributions/wheel.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/exceptions.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/index/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/index/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/index/__pycache__/collector.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/index/__pycache__/package_finder.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/index/__pycache__/sources.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/index/collector.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/index/package_finder.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/index/sources.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/locations/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/locations/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/locations/__pycache__/_distutils.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/locations/__pycache__/_sysconfig.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/locations/__pycache__/base.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/locations/_distutils.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/locations/_sysconfig.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/locations/base.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/main.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/metadata/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/metadata/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/metadata/__pycache__/_json.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/metadata/__pycache__/base.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/metadata/__pycache__/pkg_resources.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/metadata/_json.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/metadata/base.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/metadata/importlib/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/metadata/importlib/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/metadata/importlib/__pycache__/_compat.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/metadata/importlib/__pycache__/_dists.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/metadata/importlib/__pycache__/_envs.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/metadata/importlib/_compat.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/metadata/importlib/_dists.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/metadata/importlib/_envs.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/metadata/pkg_resources.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/models/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/models/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/models/__pycache__/candidate.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/models/__pycache__/direct_url.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/models/__pycache__/format_control.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/models/__pycache__/index.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/models/__pycache__/installation_report.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/models/__pycache__/link.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/models/__pycache__/scheme.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/models/__pycache__/search_scope.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/models/__pycache__/selection_prefs.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/models/__pycache__/target_python.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/models/__pycache__/wheel.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/models/candidate.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/models/direct_url.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/models/format_control.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/models/index.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/models/installation_report.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/models/link.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/models/scheme.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/models/search_scope.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/models/selection_prefs.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/models/target_python.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/models/wheel.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/network/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/network/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/network/__pycache__/auth.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/network/__pycache__/cache.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/network/__pycache__/download.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/network/__pycache__/lazy_wheel.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/network/__pycache__/session.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/network/__pycache__/utils.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/network/__pycache__/xmlrpc.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/network/auth.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/network/cache.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/network/download.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/network/lazy_wheel.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/network/session.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/network/utils.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/network/xmlrpc.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/operations/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/operations/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/operations/__pycache__/check.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/operations/__pycache__/freeze.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/operations/__pycache__/prepare.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/operations/build/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/operations/build/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/operations/build/__pycache__/build_tracker.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/operations/build/__pycache__/metadata.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/operations/build/__pycache__/metadata_editable.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/operations/build/__pycache__/metadata_legacy.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/operations/build/__pycache__/wheel.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/operations/build/__pycache__/wheel_editable.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/operations/build/__pycache__/wheel_legacy.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/operations/build/build_tracker.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/operations/build/metadata.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/operations/build/metadata_editable.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/operations/build/metadata_legacy.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/operations/build/wheel.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/operations/build/wheel_editable.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/operations/build/wheel_legacy.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/operations/check.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/operations/freeze.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/operations/install/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/operations/install/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/operations/install/__pycache__/editable_legacy.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/operations/install/__pycache__/legacy.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/operations/install/__pycache__/wheel.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/operations/install/editable_legacy.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/operations/install/legacy.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/operations/install/wheel.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/operations/prepare.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/pyproject.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/req/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/req/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/req/__pycache__/constructors.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/req/__pycache__/req_file.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/req/__pycache__/req_install.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/req/__pycache__/req_set.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/req/__pycache__/req_uninstall.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/req/constructors.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/req/req_file.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/req/req_install.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/req/req_set.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/req/req_uninstall.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/resolution/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/resolution/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/resolution/__pycache__/base.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/resolution/base.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/resolution/legacy/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/resolution/legacy/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/resolution/legacy/__pycache__/resolver.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/resolution/legacy/resolver.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/resolution/resolvelib/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/resolution/resolvelib/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/resolution/resolvelib/__pycache__/base.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/resolution/resolvelib/__pycache__/candidates.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/resolution/resolvelib/__pycache__/factory.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/resolution/resolvelib/__pycache__/found_candidates.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/resolution/resolvelib/__pycache__/provider.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/resolution/resolvelib/__pycache__/reporter.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/resolution/resolvelib/__pycache__/requirements.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/resolution/resolvelib/__pycache__/resolver.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/resolution/resolvelib/base.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/resolution/resolvelib/candidates.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/resolution/resolvelib/factory.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/resolution/resolvelib/found_candidates.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/resolution/resolvelib/provider.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/resolution/resolvelib/reporter.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/resolution/resolvelib/requirements.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/resolution/resolvelib/resolver.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/self_outdated_check.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/__pycache__/_log.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/__pycache__/appdirs.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/__pycache__/compat.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/__pycache__/compatibility_tags.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/__pycache__/datetime.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/__pycache__/deprecation.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/__pycache__/direct_url_helpers.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/__pycache__/distutils_args.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/__pycache__/egg_link.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/__pycache__/encoding.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/__pycache__/entrypoints.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/__pycache__/filesystem.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/__pycache__/filetypes.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/__pycache__/glibc.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/__pycache__/hashes.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/__pycache__/inject_securetransport.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/__pycache__/logging.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/__pycache__/misc.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/__pycache__/models.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/__pycache__/packaging.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/__pycache__/setuptools_build.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/__pycache__/subprocess.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/__pycache__/temp_dir.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/__pycache__/unpacking.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/__pycache__/urls.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/__pycache__/virtualenv.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/__pycache__/wheel.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/_log.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/appdirs.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/compat.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/compatibility_tags.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/datetime.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/deprecation.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/direct_url_helpers.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/distutils_args.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/egg_link.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/encoding.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/entrypoints.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/filesystem.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/filetypes.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/glibc.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/hashes.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/inject_securetransport.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/logging.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/misc.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/models.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/packaging.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/setuptools_build.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/subprocess.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/temp_dir.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/unpacking.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/urls.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/virtualenv.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/utils/wheel.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/vcs/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/vcs/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/vcs/__pycache__/bazaar.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/vcs/__pycache__/git.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/vcs/__pycache__/mercurial.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/vcs/__pycache__/subversion.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/vcs/__pycache__/versioncontrol.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/vcs/bazaar.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/vcs/git.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/vcs/mercurial.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/vcs/subversion.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/vcs/versioncontrol.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_internal/wheel_builder.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/__pycache__/six.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/__pycache__/typing_extensions.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/cachecontrol/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/cachecontrol/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/cachecontrol/__pycache__/_cmd.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/cachecontrol/__pycache__/adapter.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/cachecontrol/__pycache__/cache.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/cachecontrol/__pycache__/compat.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/cachecontrol/__pycache__/controller.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/cachecontrol/__pycache__/filewrapper.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/cachecontrol/__pycache__/heuristics.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/cachecontrol/__pycache__/serialize.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/cachecontrol/__pycache__/wrapper.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/cachecontrol/_cmd.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/cachecontrol/adapter.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/cachecontrol/cache.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/cachecontrol/caches/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/file_cache.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/redis_cache.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/cachecontrol/caches/file_cache.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/cachecontrol/caches/redis_cache.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/cachecontrol/compat.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/cachecontrol/controller.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/cachecontrol/filewrapper.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/cachecontrol/heuristics.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/cachecontrol/serialize.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/cachecontrol/wrapper.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/certifi/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/certifi/__main__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/certifi/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/certifi/__pycache__/__main__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/certifi/__pycache__/core.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/certifi/cacert.pem create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/certifi/core.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/__pycache__/big5freq.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/__pycache__/big5prober.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/__pycache__/chardistribution.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/__pycache__/charsetgroupprober.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/__pycache__/charsetprober.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/__pycache__/codingstatemachine.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/__pycache__/codingstatemachinedict.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/__pycache__/cp949prober.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/__pycache__/enums.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/__pycache__/escprober.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/__pycache__/escsm.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/__pycache__/eucjpprober.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/__pycache__/euckrfreq.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/__pycache__/euckrprober.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/__pycache__/euctwfreq.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/__pycache__/euctwprober.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/__pycache__/gb2312freq.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/__pycache__/gb2312prober.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/__pycache__/hebrewprober.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/__pycache__/jisfreq.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/__pycache__/johabfreq.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/__pycache__/johabprober.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/__pycache__/jpcntx.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/__pycache__/langbulgarianmodel.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/__pycache__/langgreekmodel.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/__pycache__/langhebrewmodel.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/__pycache__/langhungarianmodel.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/__pycache__/langrussianmodel.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/__pycache__/langthaimodel.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/__pycache__/langturkishmodel.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/__pycache__/latin1prober.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/__pycache__/macromanprober.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/__pycache__/mbcharsetprober.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/__pycache__/mbcsgroupprober.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/__pycache__/mbcssm.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/__pycache__/resultdict.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/__pycache__/sbcharsetprober.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/__pycache__/sbcsgroupprober.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/__pycache__/sjisprober.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/__pycache__/universaldetector.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/__pycache__/utf1632prober.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/__pycache__/utf8prober.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/__pycache__/version.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/big5freq.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/big5prober.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/chardistribution.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/charsetgroupprober.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/charsetprober.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/cli/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/cli/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/cli/__pycache__/chardetect.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/cli/chardetect.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/codingstatemachine.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/codingstatemachinedict.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/cp949prober.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/enums.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/escprober.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/escsm.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/eucjpprober.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/euckrfreq.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/euckrprober.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/euctwfreq.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/euctwprober.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/gb2312freq.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/gb2312prober.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/hebrewprober.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/jisfreq.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/johabfreq.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/johabprober.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/jpcntx.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/langbulgarianmodel.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/langgreekmodel.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/langhebrewmodel.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/langhungarianmodel.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/langrussianmodel.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/langthaimodel.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/langturkishmodel.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/latin1prober.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/macromanprober.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/mbcharsetprober.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/mbcsgroupprober.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/mbcssm.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/metadata/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/metadata/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/metadata/__pycache__/languages.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/metadata/languages.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/resultdict.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/sbcharsetprober.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/sbcsgroupprober.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/sjisprober.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/universaldetector.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/utf1632prober.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/utf8prober.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/chardet/version.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/colorama/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/colorama/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/colorama/__pycache__/ansi.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/colorama/__pycache__/ansitowin32.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/colorama/__pycache__/initialise.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/colorama/__pycache__/win32.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/colorama/__pycache__/winterm.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/colorama/ansi.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/colorama/ansitowin32.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/colorama/initialise.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/colorama/tests/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/colorama/tests/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/colorama/tests/__pycache__/ansi_test.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/colorama/tests/__pycache__/ansitowin32_test.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/colorama/tests/__pycache__/initialise_test.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/colorama/tests/__pycache__/isatty_test.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/colorama/tests/__pycache__/utils.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/colorama/tests/__pycache__/winterm_test.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/colorama/tests/ansi_test.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/colorama/tests/ansitowin32_test.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/colorama/tests/initialise_test.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/colorama/tests/isatty_test.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/colorama/tests/utils.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/colorama/tests/winterm_test.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/colorama/win32.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/colorama/winterm.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/distlib/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/distlib/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/distlib/__pycache__/compat.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/distlib/__pycache__/database.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/distlib/__pycache__/index.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/distlib/__pycache__/locators.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/distlib/__pycache__/manifest.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/distlib/__pycache__/markers.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/distlib/__pycache__/metadata.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/distlib/__pycache__/resources.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/distlib/__pycache__/scripts.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/distlib/__pycache__/util.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/distlib/__pycache__/version.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/distlib/__pycache__/wheel.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/distlib/compat.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/distlib/database.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/distlib/index.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/distlib/locators.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/distlib/manifest.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/distlib/markers.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/distlib/metadata.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/distlib/resources.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/distlib/scripts.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/distlib/t32.exe create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/distlib/t64-arm.exe create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/distlib/t64.exe create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/distlib/util.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/distlib/version.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/distlib/w32.exe create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/distlib/w64-arm.exe create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/distlib/w64.exe create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/distlib/wheel.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/distro/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/distro/__main__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/distro/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/distro/__pycache__/__main__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/distro/__pycache__/distro.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/distro/distro.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/idna/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/idna/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/idna/__pycache__/codec.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/idna/__pycache__/compat.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/idna/__pycache__/core.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/idna/__pycache__/idnadata.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/idna/__pycache__/intranges.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/idna/__pycache__/package_data.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/idna/__pycache__/uts46data.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/idna/codec.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/idna/compat.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/idna/core.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/idna/idnadata.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/idna/intranges.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/idna/package_data.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/idna/uts46data.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/msgpack/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/msgpack/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/msgpack/__pycache__/exceptions.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/msgpack/__pycache__/ext.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/msgpack/__pycache__/fallback.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/msgpack/exceptions.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/msgpack/ext.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/msgpack/fallback.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/packaging/__about__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/packaging/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/packaging/__pycache__/__about__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/packaging/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/packaging/__pycache__/_manylinux.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/packaging/__pycache__/_musllinux.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/packaging/__pycache__/_structures.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/packaging/__pycache__/markers.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/packaging/__pycache__/requirements.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/packaging/__pycache__/specifiers.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/packaging/__pycache__/tags.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/packaging/__pycache__/utils.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/packaging/__pycache__/version.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/packaging/_manylinux.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/packaging/_musllinux.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/packaging/_structures.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/packaging/markers.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/packaging/requirements.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/packaging/specifiers.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/packaging/tags.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/packaging/utils.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/packaging/version.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pkg_resources/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pkg_resources/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pkg_resources/__pycache__/py31compat.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pkg_resources/py31compat.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/platformdirs/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/platformdirs/__main__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/platformdirs/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/platformdirs/__pycache__/__main__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/platformdirs/__pycache__/android.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/platformdirs/__pycache__/api.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/platformdirs/__pycache__/macos.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/platformdirs/__pycache__/unix.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/platformdirs/__pycache__/version.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/platformdirs/__pycache__/windows.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/platformdirs/android.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/platformdirs/api.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/platformdirs/macos.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/platformdirs/unix.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/platformdirs/version.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/platformdirs/windows.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/__main__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/__pycache__/__main__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/__pycache__/cmdline.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/__pycache__/console.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/__pycache__/filter.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/__pycache__/formatter.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/__pycache__/lexer.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/__pycache__/modeline.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/__pycache__/plugin.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/__pycache__/regexopt.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/__pycache__/scanner.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/__pycache__/sphinxext.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/__pycache__/style.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/__pycache__/token.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/__pycache__/unistring.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/__pycache__/util.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/cmdline.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/console.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/filter.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/filters/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/filters/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/formatter.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/formatters/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/formatters/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/formatters/__pycache__/_mapping.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/formatters/__pycache__/bbcode.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/formatters/__pycache__/groff.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/formatters/__pycache__/html.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/formatters/__pycache__/img.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/formatters/__pycache__/irc.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/formatters/__pycache__/latex.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/formatters/__pycache__/other.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/formatters/__pycache__/pangomarkup.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/formatters/__pycache__/rtf.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/formatters/__pycache__/svg.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/formatters/__pycache__/terminal.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/formatters/__pycache__/terminal256.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/formatters/_mapping.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/formatters/bbcode.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/formatters/groff.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/formatters/html.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/formatters/img.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/formatters/irc.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/formatters/latex.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/formatters/other.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/formatters/pangomarkup.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/formatters/rtf.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/formatters/svg.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/formatters/terminal.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/formatters/terminal256.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/lexer.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/lexers/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/lexers/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/lexers/__pycache__/_mapping.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/lexers/__pycache__/python.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/lexers/_mapping.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/lexers/python.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/modeline.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/plugin.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/regexopt.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/scanner.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/sphinxext.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/style.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/styles/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/styles/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/token.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/unistring.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pygments/util.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pyparsing/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pyparsing/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pyparsing/__pycache__/actions.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pyparsing/__pycache__/common.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pyparsing/__pycache__/core.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pyparsing/__pycache__/exceptions.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pyparsing/__pycache__/helpers.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pyparsing/__pycache__/results.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pyparsing/__pycache__/testing.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pyparsing/__pycache__/unicode.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pyparsing/__pycache__/util.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pyparsing/actions.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pyparsing/common.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pyparsing/core.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pyparsing/diagram/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pyparsing/diagram/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pyparsing/exceptions.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pyparsing/helpers.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pyparsing/results.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pyparsing/testing.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pyparsing/unicode.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pyparsing/util.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pyproject_hooks/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pyproject_hooks/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pyproject_hooks/__pycache__/_compat.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pyproject_hooks/__pycache__/_impl.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pyproject_hooks/_compat.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pyproject_hooks/_impl.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pyproject_hooks/_in_process/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pyproject_hooks/_in_process/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pyproject_hooks/_in_process/__pycache__/_in_process.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/pyproject_hooks/_in_process/_in_process.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/requests/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/requests/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/requests/__pycache__/__version__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/requests/__pycache__/_internal_utils.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/requests/__pycache__/adapters.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/requests/__pycache__/api.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/requests/__pycache__/auth.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/requests/__pycache__/certs.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/requests/__pycache__/compat.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/requests/__pycache__/cookies.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/requests/__pycache__/exceptions.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/requests/__pycache__/help.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/requests/__pycache__/hooks.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/requests/__pycache__/models.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/requests/__pycache__/packages.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/requests/__pycache__/sessions.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/requests/__pycache__/status_codes.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/requests/__pycache__/structures.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/requests/__pycache__/utils.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/requests/__version__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/requests/_internal_utils.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/requests/adapters.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/requests/api.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/requests/auth.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/requests/certs.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/requests/compat.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/requests/cookies.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/requests/exceptions.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/requests/help.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/requests/hooks.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/requests/models.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/requests/packages.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/requests/sessions.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/requests/status_codes.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/requests/structures.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/requests/utils.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/resolvelib/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/resolvelib/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/resolvelib/__pycache__/providers.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/resolvelib/__pycache__/reporters.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/resolvelib/__pycache__/resolvers.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/resolvelib/__pycache__/structs.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/resolvelib/compat/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/resolvelib/compat/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/resolvelib/compat/__pycache__/collections_abc.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/resolvelib/compat/collections_abc.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/resolvelib/providers.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/resolvelib/reporters.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/resolvelib/resolvers.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/resolvelib/structs.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__main__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/__main__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/_cell_widths.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/_emoji_codes.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/_emoji_replace.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/_export_format.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/_extension.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/_inspect.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/_log_render.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/_loop.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/_null_file.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/_palettes.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/_pick.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/_ratio.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/_spinners.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/_stack.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/_timer.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/_win32_console.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/_windows.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/_windows_renderer.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/_wrap.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/abc.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/align.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/ansi.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/bar.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/box.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/cells.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/color.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/color_triplet.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/columns.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/console.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/constrain.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/containers.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/control.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/default_styles.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/diagnose.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/emoji.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/errors.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/file_proxy.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/filesize.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/highlighter.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/json.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/jupyter.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/layout.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/live.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/live_render.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/logging.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/markup.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/measure.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/padding.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/pager.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/palette.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/panel.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/pretty.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/progress.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/progress_bar.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/prompt.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/protocol.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/region.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/repr.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/rule.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/scope.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/screen.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/segment.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/spinner.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/status.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/style.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/styled.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/syntax.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/table.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/terminal_theme.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/text.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/theme.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/themes.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/traceback.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/__pycache__/tree.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/_cell_widths.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/_emoji_codes.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/_emoji_replace.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/_export_format.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/_extension.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/_inspect.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/_log_render.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/_loop.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/_null_file.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/_palettes.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/_pick.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/_ratio.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/_spinners.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/_stack.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/_timer.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/_win32_console.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/_windows.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/_windows_renderer.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/_wrap.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/abc.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/align.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/ansi.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/bar.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/box.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/cells.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/color.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/color_triplet.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/columns.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/console.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/constrain.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/containers.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/control.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/default_styles.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/diagnose.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/emoji.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/errors.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/file_proxy.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/filesize.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/highlighter.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/json.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/jupyter.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/layout.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/live.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/live_render.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/logging.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/markup.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/measure.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/padding.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/pager.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/palette.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/panel.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/pretty.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/progress.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/progress_bar.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/prompt.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/protocol.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/region.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/repr.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/rule.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/scope.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/screen.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/segment.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/spinner.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/status.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/style.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/styled.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/syntax.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/table.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/terminal_theme.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/text.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/theme.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/themes.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/traceback.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/rich/tree.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/six.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/tenacity/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/tenacity/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/tenacity/__pycache__/_asyncio.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/tenacity/__pycache__/_utils.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/tenacity/__pycache__/after.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/tenacity/__pycache__/before.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/tenacity/__pycache__/before_sleep.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/tenacity/__pycache__/nap.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/tenacity/__pycache__/retry.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/tenacity/__pycache__/stop.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/tenacity/__pycache__/tornadoweb.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/tenacity/__pycache__/wait.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/tenacity/_asyncio.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/tenacity/_utils.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/tenacity/after.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/tenacity/before.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/tenacity/before_sleep.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/tenacity/nap.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/tenacity/retry.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/tenacity/stop.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/tenacity/tornadoweb.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/tenacity/wait.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/tomli/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/tomli/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/tomli/__pycache__/_parser.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/tomli/__pycache__/_re.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/tomli/__pycache__/_types.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/tomli/_parser.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/tomli/_re.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/tomli/_types.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/typing_extensions.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/__pycache__/_collections.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/__pycache__/_version.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/__pycache__/connection.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/__pycache__/connectionpool.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/__pycache__/exceptions.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/__pycache__/fields.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/__pycache__/filepost.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/__pycache__/poolmanager.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/__pycache__/request.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/__pycache__/response.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/_collections.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/_version.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/connection.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/connectionpool.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/contrib/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/contrib/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/contrib/__pycache__/_appengine_environ.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/contrib/__pycache__/appengine.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/contrib/__pycache__/ntlmpool.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/contrib/__pycache__/pyopenssl.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/contrib/__pycache__/securetransport.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/contrib/__pycache__/socks.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/contrib/_appengine_environ.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__pycache__/bindings.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__pycache__/low_level.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/contrib/_securetransport/bindings.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/contrib/_securetransport/low_level.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/contrib/appengine.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/contrib/ntlmpool.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/contrib/pyopenssl.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/contrib/securetransport.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/contrib/socks.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/exceptions.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/fields.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/filepost.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/packages/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/packages/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/packages/__pycache__/six.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/packages/backports/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/packages/backports/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/packages/backports/__pycache__/makefile.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/packages/backports/makefile.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/packages/six.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/poolmanager.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/request.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/response.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/util/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/util/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/util/__pycache__/connection.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/util/__pycache__/proxy.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/util/__pycache__/queue.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/util/__pycache__/request.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/util/__pycache__/response.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/util/__pycache__/retry.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/util/__pycache__/ssl_.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/util/__pycache__/ssl_match_hostname.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/util/__pycache__/ssltransport.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/util/__pycache__/timeout.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/util/__pycache__/url.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/util/__pycache__/wait.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/util/connection.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/util/proxy.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/util/queue.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/util/request.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/util/response.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/util/retry.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/util/ssl_.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/util/ssl_match_hostname.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/util/ssltransport.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/util/timeout.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/util/url.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/urllib3/util/wait.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/vendor.txt create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/webencodings/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/webencodings/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/webencodings/__pycache__/labels.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/webencodings/__pycache__/mklabels.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/webencodings/__pycache__/tests.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/webencodings/__pycache__/x_user_defined.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/webencodings/labels.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/webencodings/mklabels.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/webencodings/tests.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/_vendor/webencodings/x_user_defined.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pip/py.typed create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/__pycache__/appdirs.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/__pycache__/zipp.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/appdirs.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/importlib_resources/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/importlib_resources/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/importlib_resources/__pycache__/_adapters.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/importlib_resources/__pycache__/_common.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/importlib_resources/__pycache__/_compat.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/importlib_resources/__pycache__/_itertools.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/importlib_resources/__pycache__/_legacy.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/importlib_resources/__pycache__/abc.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/importlib_resources/__pycache__/readers.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/importlib_resources/__pycache__/simple.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/importlib_resources/_adapters.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/importlib_resources/_common.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/importlib_resources/_compat.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/importlib_resources/_itertools.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/importlib_resources/_legacy.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/importlib_resources/abc.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/importlib_resources/readers.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/importlib_resources/simple.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/jaraco/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/jaraco/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/jaraco/__pycache__/context.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/jaraco/__pycache__/functools.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/jaraco/context.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/jaraco/functools.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/jaraco/text/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/jaraco/text/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/more_itertools/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/more_itertools/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/more_itertools/__pycache__/more.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/more_itertools/__pycache__/recipes.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/more_itertools/more.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/more_itertools/recipes.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/packaging/__about__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/packaging/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/packaging/__pycache__/__about__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/packaging/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/packaging/__pycache__/_manylinux.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/packaging/__pycache__/_musllinux.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/packaging/__pycache__/_structures.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/packaging/__pycache__/markers.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/packaging/__pycache__/requirements.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/packaging/__pycache__/specifiers.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/packaging/__pycache__/tags.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/packaging/__pycache__/utils.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/packaging/__pycache__/version.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/packaging/_manylinux.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/packaging/_musllinux.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/packaging/_structures.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/packaging/markers.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/packaging/requirements.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/packaging/specifiers.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/packaging/tags.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/packaging/utils.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/packaging/version.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/pyparsing/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/pyparsing/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/pyparsing/__pycache__/actions.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/pyparsing/__pycache__/common.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/pyparsing/__pycache__/core.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/pyparsing/__pycache__/exceptions.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/pyparsing/__pycache__/helpers.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/pyparsing/__pycache__/results.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/pyparsing/__pycache__/testing.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/pyparsing/__pycache__/unicode.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/pyparsing/__pycache__/util.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/pyparsing/actions.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/pyparsing/common.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/pyparsing/core.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/pyparsing/diagram/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/pyparsing/diagram/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/pyparsing/exceptions.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/pyparsing/helpers.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/pyparsing/results.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/pyparsing/testing.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/pyparsing/unicode.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/pyparsing/util.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/_vendor/zipp.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/extern/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/pkg_resources/extern/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools-62.6.0.dist-info/INSTALLER create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools-62.6.0.dist-info/LICENSE create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools-62.6.0.dist-info/METADATA create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools-62.6.0.dist-info/RECORD create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools-62.6.0.dist-info/REQUESTED create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools-62.6.0.dist-info/WHEEL create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools-62.6.0.dist-info/entry_points.txt create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools-62.6.0.dist-info/top_level.txt create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/__pycache__/_deprecation_warning.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/__pycache__/_entry_points.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/__pycache__/_imp.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/__pycache__/_importlib.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/__pycache__/_itertools.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/__pycache__/_path.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/__pycache__/_reqs.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/__pycache__/archive_util.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/__pycache__/build_meta.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/__pycache__/dep_util.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/__pycache__/depends.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/__pycache__/discovery.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/__pycache__/dist.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/__pycache__/errors.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/__pycache__/extension.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/__pycache__/glob.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/__pycache__/installer.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/__pycache__/launch.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/__pycache__/logging.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/__pycache__/monkey.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/__pycache__/msvc.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/__pycache__/namespaces.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/__pycache__/package_index.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/__pycache__/py34compat.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/__pycache__/sandbox.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/__pycache__/unicode_utils.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/__pycache__/version.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/__pycache__/wheel.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/__pycache__/windows_support.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_deprecation_warning.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/__pycache__/_collections.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/__pycache__/_functools.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/__pycache__/_macos_compat.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/__pycache__/_msvccompiler.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/__pycache__/archive_util.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/__pycache__/bcppcompiler.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/__pycache__/ccompiler.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/__pycache__/cmd.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/__pycache__/config.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/__pycache__/core.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/__pycache__/cygwinccompiler.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/__pycache__/debug.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/__pycache__/dep_util.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/__pycache__/dir_util.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/__pycache__/dist.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/__pycache__/errors.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/__pycache__/extension.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/__pycache__/fancy_getopt.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/__pycache__/file_util.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/__pycache__/filelist.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/__pycache__/log.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/__pycache__/msvc9compiler.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/__pycache__/msvccompiler.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/__pycache__/py38compat.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/__pycache__/py39compat.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/__pycache__/spawn.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/__pycache__/sysconfig.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/__pycache__/text_file.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/__pycache__/unixccompiler.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/__pycache__/util.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/__pycache__/version.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/__pycache__/versionpredicate.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/_collections.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/_functools.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/_macos_compat.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/_msvccompiler.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/archive_util.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/bcppcompiler.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/ccompiler.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/cmd.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/command/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/command/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/command/__pycache__/bdist.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/command/__pycache__/bdist_dumb.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/command/__pycache__/bdist_msi.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/command/__pycache__/bdist_rpm.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/command/__pycache__/bdist_wininst.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/command/__pycache__/build.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/command/__pycache__/build_clib.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/command/__pycache__/build_ext.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/command/__pycache__/build_py.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/command/__pycache__/build_scripts.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/command/__pycache__/check.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/command/__pycache__/clean.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/command/__pycache__/config.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/command/__pycache__/install.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/command/__pycache__/install_data.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/command/__pycache__/install_egg_info.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/command/__pycache__/install_headers.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/command/__pycache__/install_lib.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/command/__pycache__/install_scripts.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/command/__pycache__/py37compat.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/command/__pycache__/register.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/command/__pycache__/sdist.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/command/__pycache__/upload.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/command/bdist.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/command/bdist_dumb.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/command/bdist_msi.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/command/bdist_rpm.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/command/bdist_wininst.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/command/build.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/command/build_clib.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/command/build_ext.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/command/build_py.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/command/build_scripts.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/command/check.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/command/clean.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/command/config.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/command/install.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/command/install_data.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/command/install_egg_info.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/command/install_headers.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/command/install_lib.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/command/install_scripts.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/command/py37compat.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/command/register.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/command/sdist.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/command/upload.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/config.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/core.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/cygwinccompiler.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/debug.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/dep_util.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/dir_util.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/dist.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/errors.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/extension.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/fancy_getopt.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/file_util.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/filelist.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/log.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/msvc9compiler.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/msvccompiler.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/py38compat.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/py39compat.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/spawn.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/sysconfig.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/text_file.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/unixccompiler.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/util.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/version.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_distutils/versionpredicate.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_entry_points.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_imp.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_importlib.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_itertools.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_path.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_reqs.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/__pycache__/ordered_set.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/__pycache__/typing_extensions.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/__pycache__/zipp.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_metadata/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_metadata/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_metadata/__pycache__/_adapters.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_metadata/__pycache__/_collections.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_metadata/__pycache__/_compat.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_metadata/__pycache__/_functools.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_metadata/__pycache__/_itertools.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_metadata/__pycache__/_meta.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_metadata/__pycache__/_text.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_metadata/_adapters.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_metadata/_collections.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_metadata/_compat.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_metadata/_functools.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_metadata/_itertools.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_metadata/_meta.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_metadata/_text.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_resources/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_resources/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_resources/__pycache__/_adapters.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_resources/__pycache__/_common.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_resources/__pycache__/_compat.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_resources/__pycache__/_itertools.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_resources/__pycache__/_legacy.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_resources/__pycache__/abc.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_resources/__pycache__/readers.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_resources/__pycache__/simple.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_resources/_adapters.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_resources/_common.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_resources/_compat.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_resources/_itertools.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_resources/_legacy.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_resources/abc.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_resources/readers.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_resources/simple.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/jaraco/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/jaraco/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/jaraco/__pycache__/context.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/jaraco/__pycache__/functools.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/jaraco/context.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/jaraco/functools.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/jaraco/text/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/jaraco/text/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/more_itertools/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/more_itertools/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/more_itertools/__pycache__/more.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/more_itertools/__pycache__/recipes.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/more_itertools/more.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/more_itertools/recipes.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/nspektr/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/nspektr/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/nspektr/__pycache__/_compat.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/nspektr/_compat.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/ordered_set.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/packaging/__about__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/packaging/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/packaging/__pycache__/__about__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/packaging/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/packaging/__pycache__/_manylinux.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/packaging/__pycache__/_musllinux.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/packaging/__pycache__/_structures.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/packaging/__pycache__/markers.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/packaging/__pycache__/requirements.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/packaging/__pycache__/specifiers.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/packaging/__pycache__/tags.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/packaging/__pycache__/utils.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/packaging/__pycache__/version.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/packaging/_manylinux.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/packaging/_musllinux.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/packaging/_structures.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/packaging/markers.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/packaging/requirements.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/packaging/specifiers.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/packaging/tags.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/packaging/utils.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/packaging/version.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/pyparsing/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/pyparsing/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/pyparsing/__pycache__/actions.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/pyparsing/__pycache__/common.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/pyparsing/__pycache__/core.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/pyparsing/__pycache__/exceptions.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/pyparsing/__pycache__/helpers.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/pyparsing/__pycache__/results.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/pyparsing/__pycache__/testing.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/pyparsing/__pycache__/unicode.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/pyparsing/__pycache__/util.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/pyparsing/actions.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/pyparsing/common.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/pyparsing/core.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/pyparsing/diagram/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/pyparsing/diagram/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/pyparsing/exceptions.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/pyparsing/helpers.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/pyparsing/results.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/pyparsing/testing.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/pyparsing/unicode.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/pyparsing/util.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/tomli/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/tomli/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/tomli/__pycache__/_parser.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/tomli/__pycache__/_re.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/tomli/__pycache__/_types.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/tomli/_parser.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/tomli/_re.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/tomli/_types.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/typing_extensions.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/_vendor/zipp.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/archive_util.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/build_meta.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/__pycache__/alias.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/__pycache__/bdist_egg.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/__pycache__/bdist_rpm.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/__pycache__/build.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/__pycache__/build_clib.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/__pycache__/build_ext.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/__pycache__/build_py.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/__pycache__/develop.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/__pycache__/dist_info.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/__pycache__/easy_install.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/__pycache__/egg_info.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/__pycache__/install.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/__pycache__/install_egg_info.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/__pycache__/install_lib.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/__pycache__/install_scripts.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/__pycache__/py36compat.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/__pycache__/register.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/__pycache__/rotate.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/__pycache__/saveopts.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/__pycache__/sdist.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/__pycache__/setopt.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/__pycache__/test.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/__pycache__/upload.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/__pycache__/upload_docs.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/alias.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/bdist_egg.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/bdist_rpm.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/build.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/build_clib.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/build_ext.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/build_py.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/develop.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/dist_info.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/easy_install.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/egg_info.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/install.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/install_egg_info.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/install_lib.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/install_scripts.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/launcher manifest.xml create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/py36compat.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/register.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/rotate.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/saveopts.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/sdist.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/setopt.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/test.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/upload.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/command/upload_docs.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/config/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/config/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/config/__pycache__/_apply_pyprojecttoml.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/config/__pycache__/expand.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/config/__pycache__/pyprojecttoml.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/config/__pycache__/setupcfg.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/config/_apply_pyprojecttoml.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/config/_validate_pyproject/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/config/_validate_pyproject/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/config/_validate_pyproject/__pycache__/error_reporting.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/config/_validate_pyproject/__pycache__/extra_validations.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/config/_validate_pyproject/__pycache__/fastjsonschema_exceptions.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/config/_validate_pyproject/__pycache__/fastjsonschema_validations.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/config/_validate_pyproject/__pycache__/formats.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/config/_validate_pyproject/error_reporting.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/config/_validate_pyproject/extra_validations.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/config/_validate_pyproject/fastjsonschema_exceptions.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/config/_validate_pyproject/fastjsonschema_validations.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/config/_validate_pyproject/formats.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/config/expand.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/config/pyprojecttoml.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/config/setupcfg.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/dep_util.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/depends.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/discovery.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/dist.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/errors.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/extension.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/extern/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/extern/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/glob.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/installer.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/launch.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/logging.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/monkey.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/msvc.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/namespaces.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/package_index.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/py34compat.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/sandbox.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/script (dev).tmpl create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/script.tmpl create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/unicode_utils.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/version.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/wheel.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/setuptools/windows_support.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/__pycache__/_internal.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/__pycache__/_reloader.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/__pycache__/datastructures.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/__pycache__/exceptions.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/__pycache__/formparser.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/__pycache__/http.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/__pycache__/local.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/__pycache__/security.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/__pycache__/serving.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/__pycache__/test.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/__pycache__/testapp.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/__pycache__/urls.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/__pycache__/user_agent.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/__pycache__/utils.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/__pycache__/wsgi.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/_internal.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/_reloader.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/datastructures.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/datastructures.pyi create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/debug/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/debug/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/debug/__pycache__/console.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/debug/__pycache__/repr.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/debug/__pycache__/tbtools.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/debug/console.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/debug/repr.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/debug/shared/ICON_LICENSE.md create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/debug/shared/console.png create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/debug/shared/debugger.js create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/debug/shared/less.png create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/debug/shared/more.png create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/debug/shared/style.css create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/debug/tbtools.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/exceptions.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/formparser.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/http.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/local.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/middleware/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/middleware/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/middleware/__pycache__/dispatcher.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/middleware/__pycache__/http_proxy.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/middleware/__pycache__/lint.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/middleware/__pycache__/profiler.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/middleware/__pycache__/proxy_fix.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/middleware/__pycache__/shared_data.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/middleware/dispatcher.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/middleware/http_proxy.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/middleware/lint.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/middleware/profiler.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/middleware/proxy_fix.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/middleware/shared_data.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/py.typed create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/routing/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/routing/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/routing/__pycache__/converters.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/routing/__pycache__/exceptions.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/routing/__pycache__/map.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/routing/__pycache__/matcher.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/routing/__pycache__/rules.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/routing/converters.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/routing/exceptions.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/routing/map.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/routing/matcher.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/routing/rules.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/sansio/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/sansio/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/sansio/__pycache__/http.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/sansio/__pycache__/multipart.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/sansio/__pycache__/request.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/sansio/__pycache__/response.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/sansio/__pycache__/utils.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/sansio/http.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/sansio/multipart.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/sansio/request.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/sansio/response.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/sansio/utils.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/security.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/serving.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/test.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/testapp.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/urls.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/user_agent.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/utils.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/wrappers/__init__.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/wrappers/__pycache__/__init__.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/wrappers/__pycache__/request.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/wrappers/__pycache__/response.cpython-311.pyc create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/wrappers/request.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/wrappers/response.py create mode 100644 docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/wsgi.py create mode 120000 docs/examples/flask/venv/lib64 create mode 100644 docs/examples/flask/venv/pyvenv.cfg delete mode 100644 docs/flask/Client.py delete mode 100644 docs/flask/GI_interface.py delete mode 100644 docs/flask/README.md delete mode 100755 docs/flask/app.py delete mode 100644 docs/flask/templates/calc.html delete mode 100644 docs/flask/templates/view.html diff --git a/.gitignore b/.gitignore index ef6a5cc..389adc6 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,10 @@ # Mac/OSX .DS_Store +# flask pycache +/docs/examples/flask/pycache/ +/docs/examples/flask/venv/ + /test_framework_package/.idea # UI test folder @@ -21,3 +25,4 @@ # pycache for CLI /CLI/__pycache__/ +======= diff --git a/docs/examples/flask/__pycache__/app.cpython-311.pyc b/docs/examples/flask/__pycache__/app.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c2b764512c3284bed181c8d5b3c986231e3a69c4 GIT binary patch literal 5129 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', + methods=['GET', 'POST']) +def view(num, len, algo_type, test_type): + return render_template('view.html', data=[num, len, algo_type, + test_type]) + + +if __name__ == '__main__': + app.run(debug=True) + + + + + + + + + +@app.route('/capitalize//') +def capitalize(word): + return "

{}

".format(escape(word.capitalize())) + + +@app.route('/add//') +def add(n1, n2): + return "

{}

".format(n1 + n2) + + +@app.route('/users/') +def greet_user(user_id): + users = ["Bob", "Kelly", "John"] + try: + return "

Hello {}

".format(users[user_id]) + except IndexError: + abort(404) diff --git a/docs/examples/flask/index.html b/docs/examples/flask/index.html new file mode 100644 index 0000000..4e5245a --- /dev/null +++ b/docs/examples/flask/index.html @@ -0,0 +1,91 @@ + + + + + + + + QPP Landing + + +
+ + + +
+
+

QPP Testbed

+

+ Here is some stuff about QPP which is cool. This website was created + in 2023, which is like the future and what not. I'm looking to pad + this with some text to look good. +

+ +
+
This is a placeholder
+
+
+ +
+

Some random thingy

+

+ Phasellus convallis elit id ullamcorper pulvinar. Duis aliquam turpis + mauris, eu ultricies erat malesuada quis. Aliquam dapibus, lacus eget + hendrerit bibendum, urna est aliquam sem, sit amet imperdiet est velit + quis lorem. +

+
+
+
+

Some random thingy

+

+ Phasellus convallis elit id ullamcorper pulvinar. Duis aliquam turpis + mauris, eu ultricies erat malesuada quis. Aliquam dapibus, lacus eget + hendrerit bibendum, urna est aliquam sem, sit amet imperdiet est velit + quis lorem. +

+
+ +
+

Some random thingy

+

+ Phasellus convallis elit id ullamcorper pulvinar. Duis aliquam turpis + mauris, eu ultricies erat malesuada quis. Aliquam dapibus, lacus eget + hendrerit bibendum, urna est aliquam sem, sit amet imperdiet est velit + quis lorem. +

+
+
+
+

HHHHHHHHHHHHHHH

+

oasnfoiasjfniuasjfiasfiuhsafiajsfiusafojsaoijfniosanfo

+
+ +
+

HSOPCMAOISCM

+

+ Moloch the incomprehensible prison! Moloch the crossbone soulless + jailhouse and Congress of sorrows! Moloch whose buildings are judgment! + Moloch the vast stone of war! Moloch the stunned governments! +

+
+ + diff --git a/docs/examples/flask/scripts/cal.js b/docs/examples/flask/scripts/cal.js new file mode 100644 index 0000000..924d904 --- /dev/null +++ b/docs/examples/flask/scripts/cal.js @@ -0,0 +1,13 @@ +let myButton = document.querySelector("button"); +let myHeading = document.querySelector("h1"); +setUserName(); + +myButton.onclick = () => { + setUserName(); +}; + +function setUserName() { + const myName = prompt("Please enter your name."); + localStorage.setItem("name", myName); + myHeading.textContent = `Your name is ${myName}`; +} diff --git a/docs/examples/flask/scripts/web.js b/docs/examples/flask/scripts/web.js new file mode 100644 index 0000000..6b1cc59 --- /dev/null +++ b/docs/examples/flask/scripts/web.js @@ -0,0 +1,25 @@ +function multiply(num1, num2) { + let result = num1 * num2; + return result; +} + +const myHeading = document.querySelector("h1"); +const myImage = document.querySelector("img"); + +myImage.onclick = () => { + const mySrc = myImage.getAttribute("src"); + if (mySrc === "images/pic.webp") { + myImage.setAttribute("src", "images/computer.jpg"); + } else { + myImage.setAttribute("src", "images/pic.webp"); + } +}; + +myHeading.textContent = "This is a webpage"; +multiply(10, 15); + +document.querySelector("html").addEventListener("click", () => { + //alert("Ouch! Stop poking me!"); +}); + +// alert("HI"); diff --git a/docs/flask/static/style.css b/docs/examples/flask/static/main.css similarity index 100% rename from docs/flask/static/style.css rename to docs/examples/flask/static/main.css diff --git a/docs/examples/flask/templates/about.html b/docs/examples/flask/templates/about.html new file mode 100644 index 0000000..1e30537 --- /dev/null +++ b/docs/examples/flask/templates/about.html @@ -0,0 +1,6 @@ +{% extends 'base.html' %} + +{% block content %} +

{% block title %} About {% endblock %}

+

FlaskApp is a Flask web application written in Python.

+{% endblock %} diff --git a/docs/examples/flask/templates/api.html b/docs/examples/flask/templates/api.html new file mode 100644 index 0000000..26d9d78 --- /dev/null +++ b/docs/examples/flask/templates/api.html @@ -0,0 +1,37 @@ +{% extends 'base.html' %} + +{% block title %} QPP API {% endblock %} +{% block header %} API {% endblock %} +{% block header_stuff %} +
+ + +
+{% endblock %} + +{% block content %} +
+
+
+

CC -> nodeE

+
    +
  • RequestHandshake()
  • +
  • ReceivedID()
  • +
  • SetApprovedToCommunicate()
  • +
  • SetSeed()
  • +
  • Encrypt()
  • +
  • Decrypt()
  • +
+
+
+

nodeE -> CC

+
    +
  • SendID()
  • +
  • HSComplete()
  • +
  • SendEncrypted()
  • +
  • SendDecrypted()
  • +
+
+
+
+{% endblock %} diff --git a/docs/flask/templates/base.html b/docs/examples/flask/templates/base.html similarity index 69% rename from docs/flask/templates/base.html rename to docs/examples/flask/templates/base.html index 943ef26..8dd4886 100644 --- a/docs/flask/templates/base.html +++ b/docs/examples/flask/templates/base.html @@ -4,7 +4,7 @@ - + {% block title %} {% endblock %} @@ -19,7 +19,7 @@ API - -
+
-

{% block header %} {% endblock %}

- {% block header_content %} {% endblock %} +

+ {% block header %} {% endblock %} +

+ {% block header_stuff %} {% endblock %} +
- {% block content %} {% endblock %} - + + {% block content %} {% endblock %} + diff --git a/docs/examples/flask/templates/calc.html b/docs/examples/flask/templates/calc.html new file mode 100644 index 0000000..ac4e430 --- /dev/null +++ b/docs/examples/flask/templates/calc.html @@ -0,0 +1,60 @@ +{% extends 'base.html' %} + +{% block title %} Interactive Testbed {% endblock %} +{% block header %} Interactive Testbed {% endblock %} + +{% block content %} +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +

QPP

+ + +

AES

+ + +
+{% endblock %} diff --git a/docs/examples/flask/templates/demo.html b/docs/examples/flask/templates/demo.html new file mode 100644 index 0000000..a796507 --- /dev/null +++ b/docs/examples/flask/templates/demo.html @@ -0,0 +1,46 @@ + + + + + + + + Document + + +
+ + + +
+
+

+ API Documentation +

+
+
+ + +
+
+
+ + diff --git a/docs/flask/templates/index.html b/docs/examples/flask/templates/index.html similarity index 94% rename from docs/flask/templates/index.html rename to docs/examples/flask/templates/index.html index ea9e5b4..96755ef 100644 --- a/docs/flask/templates/index.html +++ b/docs/examples/flask/templates/index.html @@ -2,11 +2,7 @@ {% block title %} QPP Index {% endblock %} {% block header %} QPP Testbed {% endblock %} - -{% block header_content %} -

This is the description for this page

-{% endblock %} - +{% block header_stuff %}HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH{% endblock %} {% block content %}
diff --git a/docs/examples/flask/templates/research.html b/docs/examples/flask/templates/research.html new file mode 100644 index 0000000..a796507 --- /dev/null +++ b/docs/examples/flask/templates/research.html @@ -0,0 +1,46 @@ + + + + + + + + Document + + +
+ + + +
+
+

+ API Documentation +

+
+
+ + +
+
+
+ + diff --git a/docs/examples/flask/templates/view.html b/docs/examples/flask/templates/view.html new file mode 100644 index 0000000..bdd6e4d --- /dev/null +++ b/docs/examples/flask/templates/view.html @@ -0,0 +1,14 @@ +{% extends 'base.html' %} + +{% block title %} Results {% endblock %} +{% block header %} Results {% endblock %} + +{% block content %} + +
+ {% for o in data %} +

{{ o }}

+ {% endfor %} +
+ +{% endblock %} diff --git a/docs/examples/flask/venv/bin/Activate.ps1 b/docs/examples/flask/venv/bin/Activate.ps1 new file mode 100644 index 0000000..b49d77b --- /dev/null +++ b/docs/examples/flask/venv/bin/Activate.ps1 @@ -0,0 +1,247 @@ +<# +.Synopsis +Activate a Python virtual environment for the current PowerShell session. + +.Description +Pushes the python executable for a virtual environment to the front of the +$Env:PATH environment variable and sets the prompt to signify that you are +in a Python virtual environment. Makes use of the command line switches as +well as the `pyvenv.cfg` file values present in the virtual environment. + +.Parameter VenvDir +Path to the directory that contains the virtual environment to activate. The +default value for this is the parent of the directory that the Activate.ps1 +script is located within. + +.Parameter Prompt +The prompt prefix to display when this virtual environment is activated. By +default, this prompt is the name of the virtual environment folder (VenvDir) +surrounded by parentheses and followed by a single space (ie. '(.venv) '). + +.Example +Activate.ps1 +Activates the Python virtual environment that contains the Activate.ps1 script. + +.Example +Activate.ps1 -Verbose +Activates the Python virtual environment that contains the Activate.ps1 script, +and shows extra information about the activation as it executes. + +.Example +Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv +Activates the Python virtual environment located in the specified location. + +.Example +Activate.ps1 -Prompt "MyPython" +Activates the Python virtual environment that contains the Activate.ps1 script, +and prefixes the current prompt with the specified string (surrounded in +parentheses) while the virtual environment is active. + +.Notes +On Windows, it may be required to enable this Activate.ps1 script by setting the +execution policy for the user. You can do this by issuing the following PowerShell +command: + +PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser + +For more information on Execution Policies: +https://go.microsoft.com/fwlink/?LinkID=135170 + +#> +Param( + [Parameter(Mandatory = $false)] + [String] + $VenvDir, + [Parameter(Mandatory = $false)] + [String] + $Prompt +) + +<# Function declarations --------------------------------------------------- #> + +<# +.Synopsis +Remove all shell session elements added by the Activate script, including the +addition of the virtual environment's Python executable from the beginning of +the PATH variable. + +.Parameter NonDestructive +If present, do not remove this function from the global namespace for the +session. + +#> +function global:deactivate ([switch]$NonDestructive) { + # Revert to original values + + # The prior prompt: + if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) { + Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt + Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT + } + + # The prior PYTHONHOME: + if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) { + Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME + Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME + } + + # The prior PATH: + if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) { + Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH + Remove-Item -Path Env:_OLD_VIRTUAL_PATH + } + + # Just remove the VIRTUAL_ENV altogether: + if (Test-Path -Path Env:VIRTUAL_ENV) { + Remove-Item -Path env:VIRTUAL_ENV + } + + # Just remove VIRTUAL_ENV_PROMPT altogether. + if (Test-Path -Path Env:VIRTUAL_ENV_PROMPT) { + Remove-Item -Path env:VIRTUAL_ENV_PROMPT + } + + # Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether: + if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) { + Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force + } + + # Leave deactivate function in the global namespace if requested: + if (-not $NonDestructive) { + Remove-Item -Path function:deactivate + } +} + +<# +.Description +Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the +given folder, and returns them in a map. + +For each line in the pyvenv.cfg file, if that line can be parsed into exactly +two strings separated by `=` (with any amount of whitespace surrounding the =) +then it is considered a `key = value` line. The left hand string is the key, +the right hand is the value. + +If the value starts with a `'` or a `"` then the first and last character is +stripped from the value before being captured. + +.Parameter ConfigDir +Path to the directory that contains the `pyvenv.cfg` file. +#> +function Get-PyVenvConfig( + [String] + $ConfigDir +) { + Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg" + + # Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue). + $pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue + + # An empty map will be returned if no config file is found. + $pyvenvConfig = @{ } + + if ($pyvenvConfigPath) { + + Write-Verbose "File exists, parse `key = value` lines" + $pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath + + $pyvenvConfigContent | ForEach-Object { + $keyval = $PSItem -split "\s*=\s*", 2 + if ($keyval[0] -and $keyval[1]) { + $val = $keyval[1] + + # Remove extraneous quotations around a string value. + if ("'""".Contains($val.Substring(0, 1))) { + $val = $val.Substring(1, $val.Length - 2) + } + + $pyvenvConfig[$keyval[0]] = $val + Write-Verbose "Adding Key: '$($keyval[0])'='$val'" + } + } + } + return $pyvenvConfig +} + + +<# Begin Activate script --------------------------------------------------- #> + +# Determine the containing directory of this script +$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition +$VenvExecDir = Get-Item -Path $VenvExecPath + +Write-Verbose "Activation script is located in path: '$VenvExecPath'" +Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)" +Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)" + +# Set values required in priority: CmdLine, ConfigFile, Default +# First, get the location of the virtual environment, it might not be +# VenvExecDir if specified on the command line. +if ($VenvDir) { + Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values" +} +else { + Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir." + $VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/") + Write-Verbose "VenvDir=$VenvDir" +} + +# Next, read the `pyvenv.cfg` file to determine any required value such +# as `prompt`. +$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir + +# Next, set the prompt from the command line, or the config file, or +# just use the name of the virtual environment folder. +if ($Prompt) { + Write-Verbose "Prompt specified as argument, using '$Prompt'" +} +else { + Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value" + if ($pyvenvCfg -and $pyvenvCfg['prompt']) { + Write-Verbose " Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'" + $Prompt = $pyvenvCfg['prompt']; + } + else { + Write-Verbose " Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virtual environment)" + Write-Verbose " Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'" + $Prompt = Split-Path -Path $venvDir -Leaf + } +} + +Write-Verbose "Prompt = '$Prompt'" +Write-Verbose "VenvDir='$VenvDir'" + +# Deactivate any currently active virtual environment, but leave the +# deactivate function in place. +deactivate -nondestructive + +# Now set the environment variable VIRTUAL_ENV, used by many tools to determine +# that there is an activated venv. +$env:VIRTUAL_ENV = $VenvDir + +if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) { + + Write-Verbose "Setting prompt to '$Prompt'" + + # Set the prompt to include the env name + # Make sure _OLD_VIRTUAL_PROMPT is global + function global:_OLD_VIRTUAL_PROMPT { "" } + Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT + New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt + + function global:prompt { + Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) " + _OLD_VIRTUAL_PROMPT + } + $env:VIRTUAL_ENV_PROMPT = $Prompt +} + +# Clear PYTHONHOME +if (Test-Path -Path Env:PYTHONHOME) { + Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME + Remove-Item -Path Env:PYTHONHOME +} + +# Add the venv to the PATH +Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH +$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH" diff --git a/docs/examples/flask/venv/bin/activate b/docs/examples/flask/venv/bin/activate new file mode 100644 index 0000000..a96de2c --- /dev/null +++ b/docs/examples/flask/venv/bin/activate @@ -0,0 +1,69 @@ +# 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 2> /dev/null + fi + + if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then + PS1="${_OLD_VIRTUAL_PS1:-}" + export PS1 + unset _OLD_VIRTUAL_PS1 + fi + + unset VIRTUAL_ENV + unset VIRTUAL_ENV_PROMPT + if [ ! "${1:-}" = "nondestructive" ] ; then + # Self destruct! + unset -f deactivate + fi +} + +# unset irrelevant variables +deactivate nondestructive + +VIRTUAL_ENV="/home/refik/Capstone/QuantumPermutationPad/docs/examples/flask/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:-}" + PS1="(venv) ${PS1:-}" + export PS1 + VIRTUAL_ENV_PROMPT="(venv) " + export VIRTUAL_ENV_PROMPT +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 2> /dev/null +fi diff --git a/docs/examples/flask/venv/bin/activate.csh b/docs/examples/flask/venv/bin/activate.csh new file mode 100644 index 0000000..d96f65a --- /dev/null +++ b/docs/examples/flask/venv/bin/activate.csh @@ -0,0 +1,26 @@ +# 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; unsetenv VIRTUAL_ENV_PROMPT; test "\!:*" != "nondestructive" && unalias deactivate' + +# Unset irrelevant variables. +deactivate nondestructive + +setenv VIRTUAL_ENV "/home/refik/Capstone/QuantumPermutationPad/docs/examples/flask/venv" + +set _OLD_VIRTUAL_PATH="$PATH" +setenv PATH "$VIRTUAL_ENV/bin:$PATH" + + +set _OLD_VIRTUAL_PROMPT="$prompt" + +if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then + set prompt = "(venv) $prompt" + setenv VIRTUAL_ENV_PROMPT "(venv) " +endif + +alias pydoc python -m pydoc + +rehash diff --git a/docs/examples/flask/venv/bin/activate.fish b/docs/examples/flask/venv/bin/activate.fish new file mode 100644 index 0000000..070c462 --- /dev/null +++ b/docs/examples/flask/venv/bin/activate.fish @@ -0,0 +1,69 @@ +# This file must be used with "source /bin/activate.fish" *from fish* +# (https://fishshell.com/); you cannot run it directly. + +function deactivate -d "Exit virtual environment 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" + set -e _OLD_FISH_PROMPT_OVERRIDE + # prevents error when using nested fish instances (Issue #93858) + if functions -q _old_fish_prompt + functions -e fish_prompt + functions -c _old_fish_prompt fish_prompt + functions -e _old_fish_prompt + end + end + + set -e VIRTUAL_ENV + set -e VIRTUAL_ENV_PROMPT + if test "$argv[1]" != "nondestructive" + # Self-destruct! + functions -e deactivate + end +end + +# Unset irrelevant variables. +deactivate nondestructive + +set -gx VIRTUAL_ENV "/home/refik/Capstone/QuantumPermutationPad/docs/examples/flask/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 + + # Output the venv prompt; color taken from the blue of the Python logo. + printf "%s%s%s" (set_color 4B8BBE) "(venv) " (set_color normal) + + # Restore the return status of the previous command. + echo "exit $old_status" | . + # Output the original/"old" prompt. + _old_fish_prompt + end + + set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV" + set -gx VIRTUAL_ENV_PROMPT "(venv) " +end diff --git a/docs/examples/flask/venv/bin/flask b/docs/examples/flask/venv/bin/flask new file mode 100755 index 0000000..56554a0 --- /dev/null +++ b/docs/examples/flask/venv/bin/flask @@ -0,0 +1,8 @@ +#!/home/refik/Capstone/QuantumPermutationPad/docs/examples/flask/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/docs/examples/flask/venv/bin/pip b/docs/examples/flask/venv/bin/pip new file mode 100755 index 0000000..99804f6 --- /dev/null +++ b/docs/examples/flask/venv/bin/pip @@ -0,0 +1,8 @@ +#!/home/refik/Capstone/QuantumPermutationPad/docs/examples/flask/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/docs/examples/flask/venv/bin/pip3 b/docs/examples/flask/venv/bin/pip3 new file mode 100755 index 0000000..99804f6 --- /dev/null +++ b/docs/examples/flask/venv/bin/pip3 @@ -0,0 +1,8 @@ +#!/home/refik/Capstone/QuantumPermutationPad/docs/examples/flask/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/docs/examples/flask/venv/bin/pip3.11 b/docs/examples/flask/venv/bin/pip3.11 new file mode 100755 index 0000000..99804f6 --- /dev/null +++ b/docs/examples/flask/venv/bin/pip3.11 @@ -0,0 +1,8 @@ +#!/home/refik/Capstone/QuantumPermutationPad/docs/examples/flask/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/docs/examples/flask/venv/bin/python b/docs/examples/flask/venv/bin/python new file mode 120000 index 0000000..b8a0adb --- /dev/null +++ b/docs/examples/flask/venv/bin/python @@ -0,0 +1 @@ +python3 \ No newline at end of file diff --git a/docs/examples/flask/venv/bin/python3 b/docs/examples/flask/venv/bin/python3 new file mode 120000 index 0000000..ae65fda --- /dev/null +++ b/docs/examples/flask/venv/bin/python3 @@ -0,0 +1 @@ +/usr/bin/python3 \ No newline at end of file diff --git a/docs/examples/flask/venv/bin/python3.11 b/docs/examples/flask/venv/bin/python3.11 new file mode 120000 index 0000000..b8a0adb --- /dev/null +++ b/docs/examples/flask/venv/bin/python3.11 @@ -0,0 +1 @@ +python3 \ No newline at end of file diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/Flask-2.2.3.dist-info/INSTALLER b/docs/examples/flask/venv/lib/python3.11/site-packages/Flask-2.2.3.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/Flask-2.2.3.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/Flask-2.2.3.dist-info/LICENSE.rst b/docs/examples/flask/venv/lib/python3.11/site-packages/Flask-2.2.3.dist-info/LICENSE.rst new file mode 100644 index 0000000..9d227a0 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/Flask-2.2.3.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/docs/examples/flask/venv/lib/python3.11/site-packages/Flask-2.2.3.dist-info/METADATA b/docs/examples/flask/venv/lib/python3.11/site-packages/Flask-2.2.3.dist-info/METADATA new file mode 100644 index 0000000..4edd337 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/Flask-2.2.3.dist-info/METADATA @@ -0,0 +1,123 @@ +Metadata-Version: 2.1 +Name: Flask +Version: 2.2.3 +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 +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.7 +Description-Content-Type: text/x-rst +License-File: LICENSE.rst +Requires-Dist: Werkzeug (>=2.2.2) +Requires-Dist: Jinja2 (>=3.0) +Requires-Dist: itsdangerous (>=2.0) +Requires-Dist: click (>=8.0) +Requires-Dist: importlib-metadata (>=3.6.0) ; python_version < "3.10" +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/docs/examples/flask/venv/lib/python3.11/site-packages/Flask-2.2.3.dist-info/RECORD b/docs/examples/flask/venv/lib/python3.11/site-packages/Flask-2.2.3.dist-info/RECORD new file mode 100644 index 0000000..d628811 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/Flask-2.2.3.dist-info/RECORD @@ -0,0 +1,54 @@ +../../../bin/flask,sha256=Z1K4uWz3PUrQSPTVRGqgpVQgpPkJ6lre7HwIff6d490,271 +Flask-2.2.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Flask-2.2.3.dist-info/LICENSE.rst,sha256=SJqOEQhQntmKN7uYPhHg9-HTHwvY-Zp5yESOf_N9B-o,1475 +Flask-2.2.3.dist-info/METADATA,sha256=gPKNo3B3jmC70HbbmEUzCP5Wq2w64PETRJ52VzANYAI,3889 +Flask-2.2.3.dist-info/RECORD,, +Flask-2.2.3.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +Flask-2.2.3.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92 +Flask-2.2.3.dist-info/entry_points.txt,sha256=s3MqQpduU25y4dq3ftBYD6bMVdVnbMpZP-sUNw0zw0k,41 +Flask-2.2.3.dist-info/top_level.txt,sha256=dvi65F6AeGWVU0TBpYiC04yM60-FX1gJFkK31IKQr5c,6 +flask/__init__.py,sha256=3FB-z8gjiwx9Eb6x_OaCdfW9LTyb7ZTIciovc0Ovrm0,2890 +flask/__main__.py,sha256=bYt9eEaoRQWdejEHFD8REx9jxVEdZptECFsV7F49Ink,30 +flask/__pycache__/__init__.cpython-311.pyc,, +flask/__pycache__/__main__.cpython-311.pyc,, +flask/__pycache__/app.cpython-311.pyc,, +flask/__pycache__/blueprints.cpython-311.pyc,, +flask/__pycache__/cli.cpython-311.pyc,, +flask/__pycache__/config.cpython-311.pyc,, +flask/__pycache__/ctx.cpython-311.pyc,, +flask/__pycache__/debughelpers.cpython-311.pyc,, +flask/__pycache__/globals.cpython-311.pyc,, +flask/__pycache__/helpers.cpython-311.pyc,, +flask/__pycache__/logging.cpython-311.pyc,, +flask/__pycache__/scaffold.cpython-311.pyc,, +flask/__pycache__/sessions.cpython-311.pyc,, +flask/__pycache__/signals.cpython-311.pyc,, +flask/__pycache__/templating.cpython-311.pyc,, +flask/__pycache__/testing.cpython-311.pyc,, +flask/__pycache__/typing.cpython-311.pyc,, +flask/__pycache__/views.cpython-311.pyc,, +flask/__pycache__/wrappers.cpython-311.pyc,, +flask/app.py,sha256=PFYDzGJ4ts7mdmlDurbvfXvMyo2KrO8aVkvspRwiRB0,99107 +flask/blueprints.py,sha256=fenhKP_Sh5eU6qtWeHacg1GVeun4pQzK2vq8sNDd1hY,27266 +flask/cli.py,sha256=pLmnWObe_G4_ZAFQdh7kgwqPMxRXm4oUhaUSBpJMeq4,33532 +flask/config.py,sha256=Ubo_juzSYsAKqD2vD3vm6mjsPo3EOJDdSEzYq8lKTJI,12585 +flask/ctx.py,sha256=bGEQQuF2_cHqZ3ZNMeMeEG8HOLJkDlL88u2BBxCrRao,14829 +flask/debughelpers.py,sha256=_RvAL3TW5lqMJeCVWtTU6rSDJC7jnRaBL6OEkVmooyU,5511 +flask/globals.py,sha256=EX0XdX73BTWdVF0UHDSNet2ER3kI6sKveo3_o5IOs98,3187 +flask/helpers.py,sha256=XTHRgLlyxeEzR988q63-4OY8RswTscR-5exFxK10CLU,25280 +flask/json/__init__.py,sha256=TOwldHT3_kFaXHlORKi9yCWt7dbPNB0ovdHHQWlSRzY,11175 +flask/json/__pycache__/__init__.cpython-311.pyc,, +flask/json/__pycache__/provider.cpython-311.pyc,, +flask/json/__pycache__/tag.cpython-311.pyc,, +flask/json/provider.py,sha256=jXCNypf11PN4ngQjEt6LnSdCWQ1yHIAkNLHlXQlCB-A,10674 +flask/json/tag.py,sha256=fys3HBLssWHuMAIJuTcf2K0bCtosePBKXIWASZEEjnU,8857 +flask/logging.py,sha256=WYng0bLTRS_CJrocGcCLJpibHf1lygHE_pg-KoUIQ4w,2293 +flask/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +flask/scaffold.py,sha256=EKx-Tr5BXLzeKKvq3ZAi_2oUQVZuC4OJSJTocyDXsSo,35958 +flask/sessions.py,sha256=66oGlE-v9iac-eb54tFN3ILAjJ1FeeuHHWw98UVaoxc,15847 +flask/signals.py,sha256=H7QwDciK-dtBxinjKpexpglP0E6k0MJILiFWTItfmqU,2136 +flask/templating.py,sha256=1P4OzvSnA2fsJTYgQT3G4owVKsuOz8XddCiR6jMHGJ0,7419 +flask/testing.py,sha256=qoMU5L9iMipvNVQ_84MadJx3gsKoSMHD0cQaZDP23rE,10602 +flask/typing.py,sha256=KgxegTF9v9WvuongeF8LooIvpZPauzGrq9ZXf3gBlYc,2969 +flask/views.py,sha256=LulttWL4owVFlgwrJi8GCNM4inC3xbs2IBlY31bdCS4,6765 +flask/wrappers.py,sha256=el3tn1LgSUV0eNGgYMjKICT5I7qGJgbpIhvci4nrwQ8,5702 diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/Flask-2.2.3.dist-info/REQUESTED b/docs/examples/flask/venv/lib/python3.11/site-packages/Flask-2.2.3.dist-info/REQUESTED new file mode 100644 index 0000000..e69de29 diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/Flask-2.2.3.dist-info/WHEEL b/docs/examples/flask/venv/lib/python3.11/site-packages/Flask-2.2.3.dist-info/WHEEL new file mode 100644 index 0000000..57e3d84 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/Flask-2.2.3.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.38.4) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/Flask-2.2.3.dist-info/entry_points.txt b/docs/examples/flask/venv/lib/python3.11/site-packages/Flask-2.2.3.dist-info/entry_points.txt new file mode 100644 index 0000000..137232d --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/Flask-2.2.3.dist-info/entry_points.txt @@ -0,0 +1,2 @@ +[console_scripts] +flask = flask.cli:main diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/Flask-2.2.3.dist-info/top_level.txt b/docs/examples/flask/venv/lib/python3.11/site-packages/Flask-2.2.3.dist-info/top_level.txt new file mode 100644 index 0000000..7e10602 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/Flask-2.2.3.dist-info/top_level.txt @@ -0,0 +1 @@ +flask diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/INSTALLER b/docs/examples/flask/venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/LICENSE.rst b/docs/examples/flask/venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/LICENSE.rst new file mode 100644 index 0000000..c37cae4 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/Jinja2-3.1.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/docs/examples/flask/venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/METADATA b/docs/examples/flask/venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/METADATA new file mode 100644 index 0000000..f54bb5c --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/METADATA @@ -0,0 +1,113 @@ +Metadata-Version: 2.1 +Name: Jinja2 +Version: 3.1.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.7 +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/docs/examples/flask/venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/RECORD b/docs/examples/flask/venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/RECORD new file mode 100644 index 0000000..b20e953 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/RECORD @@ -0,0 +1,58 @@ +Jinja2-3.1.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Jinja2-3.1.2.dist-info/LICENSE.rst,sha256=O0nc7kEF6ze6wQ-vG-JgQI_oXSUrjp3y4JefweCUQ3s,1475 +Jinja2-3.1.2.dist-info/METADATA,sha256=PZ6v2SIidMNixR7MRUX9f7ZWsPwtXanknqiZUmRbh4U,3539 +Jinja2-3.1.2.dist-info/RECORD,, +Jinja2-3.1.2.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92 +Jinja2-3.1.2.dist-info/entry_points.txt,sha256=zRd62fbqIyfUpsRtU7EVIFyiu1tPwfgO7EvPErnxgTE,59 +Jinja2-3.1.2.dist-info/top_level.txt,sha256=PkeVWtLb3-CqjWi1fO29OCbj55EhX_chhKrCdrVe_zs,7 +jinja2/__init__.py,sha256=8vGduD8ytwgD6GDSqpYc2m3aU-T7PKOAddvVXgGr_Fs,1927 +jinja2/__pycache__/__init__.cpython-311.pyc,, +jinja2/__pycache__/_identifier.cpython-311.pyc,, +jinja2/__pycache__/async_utils.cpython-311.pyc,, +jinja2/__pycache__/bccache.cpython-311.pyc,, +jinja2/__pycache__/compiler.cpython-311.pyc,, +jinja2/__pycache__/constants.cpython-311.pyc,, +jinja2/__pycache__/debug.cpython-311.pyc,, +jinja2/__pycache__/defaults.cpython-311.pyc,, +jinja2/__pycache__/environment.cpython-311.pyc,, +jinja2/__pycache__/exceptions.cpython-311.pyc,, +jinja2/__pycache__/ext.cpython-311.pyc,, +jinja2/__pycache__/filters.cpython-311.pyc,, +jinja2/__pycache__/idtracking.cpython-311.pyc,, +jinja2/__pycache__/lexer.cpython-311.pyc,, +jinja2/__pycache__/loaders.cpython-311.pyc,, +jinja2/__pycache__/meta.cpython-311.pyc,, +jinja2/__pycache__/nativetypes.cpython-311.pyc,, +jinja2/__pycache__/nodes.cpython-311.pyc,, +jinja2/__pycache__/optimizer.cpython-311.pyc,, +jinja2/__pycache__/parser.cpython-311.pyc,, +jinja2/__pycache__/runtime.cpython-311.pyc,, +jinja2/__pycache__/sandbox.cpython-311.pyc,, +jinja2/__pycache__/tests.cpython-311.pyc,, +jinja2/__pycache__/utils.cpython-311.pyc,, +jinja2/__pycache__/visitor.cpython-311.pyc,, +jinja2/_identifier.py,sha256=_zYctNKzRqlk_murTNlzrju1FFJL7Va_Ijqqd7ii2lU,1958 +jinja2/async_utils.py,sha256=dHlbTeaxFPtAOQEYOGYh_PHcDT0rsDaUJAFDl_0XtTg,2472 +jinja2/bccache.py,sha256=mhz5xtLxCcHRAa56azOhphIAe19u1we0ojifNMClDio,14061 +jinja2/compiler.py,sha256=Gs-N8ThJ7OWK4-reKoO8Wh1ZXz95MVphBKNVf75qBr8,72172 +jinja2/constants.py,sha256=GMoFydBF_kdpaRKPoM5cl5MviquVRLVyZtfp5-16jg0,1433 +jinja2/debug.py,sha256=iWJ432RadxJNnaMOPrjIDInz50UEgni3_HKuFXi2vuQ,6299 +jinja2/defaults.py,sha256=boBcSw78h-lp20YbaXSJsqkAI2uN_mD_TtCydpeq5wU,1267 +jinja2/environment.py,sha256=6uHIcc7ZblqOMdx_uYNKqRnnwAF0_nzbyeMP9FFtuh4,61349 +jinja2/exceptions.py,sha256=ioHeHrWwCWNaXX1inHmHVblvc4haO7AXsjCp3GfWvx0,5071 +jinja2/ext.py,sha256=ivr3P7LKbddiXDVez20EflcO3q2aHQwz9P_PgWGHVqE,31502 +jinja2/filters.py,sha256=9js1V-h2RlyW90IhLiBGLM2U-k6SCy2F4BUUMgB3K9Q,53509 +jinja2/idtracking.py,sha256=GfNmadir4oDALVxzn3DL9YInhJDr69ebXeA2ygfuCGA,10704 +jinja2/lexer.py,sha256=DW2nX9zk-6MWp65YR2bqqj0xqCvLtD-u9NWT8AnFRxQ,29726 +jinja2/loaders.py,sha256=BfptfvTVpClUd-leMkHczdyPNYFzp_n7PKOJ98iyHOg,23207 +jinja2/meta.py,sha256=GNPEvifmSaU3CMxlbheBOZjeZ277HThOPUTf1RkppKQ,4396 +jinja2/nativetypes.py,sha256=DXgORDPRmVWgy034H0xL8eF7qYoK3DrMxs-935d0Fzk,4226 +jinja2/nodes.py,sha256=i34GPRAZexXMT6bwuf5SEyvdmS-bRCy9KMjwN5O6pjk,34550 +jinja2/optimizer.py,sha256=tHkMwXxfZkbfA1KmLcqmBMSaz7RLIvvItrJcPoXTyD8,1650 +jinja2/parser.py,sha256=nHd-DFHbiygvfaPtm9rcQXJChZG7DPsWfiEsqfwKerY,39595 +jinja2/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +jinja2/runtime.py,sha256=5CmD5BjbEJxSiDNTFBeKCaq8qU4aYD2v6q2EluyExms,33476 +jinja2/sandbox.py,sha256=Y0xZeXQnH6EX5VjaV2YixESxoepnRbW_3UeQosaBU3M,14584 +jinja2/tests.py,sha256=Am5Z6Lmfr2XaH_npIfJJ8MdXtWsbLjMULZJulTAj30E,5905 +jinja2/utils.py,sha256=u9jXESxGn8ATZNVolwmkjUVu4SA-tLgV0W7PcSfPfdQ,23965 +jinja2/visitor.py,sha256=MH14C6yq24G_KVtWzjwaI7Wg14PCJIYlWW1kpkxYak0,3568 diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/WHEEL b/docs/examples/flask/venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/WHEEL new file mode 100644 index 0000000..becc9a6 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.1) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/entry_points.txt b/docs/examples/flask/venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/entry_points.txt new file mode 100644 index 0000000..7b9666c --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/entry_points.txt @@ -0,0 +1,2 @@ +[babel.extractors] +jinja2 = jinja2.ext:babel_extract[i18n] diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/top_level.txt b/docs/examples/flask/venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/top_level.txt new file mode 100644 index 0000000..7f7afbf --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/top_level.txt @@ -0,0 +1 @@ +jinja2 diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/INSTALLER b/docs/examples/flask/venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/LICENSE.rst b/docs/examples/flask/venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/LICENSE.rst new file mode 100644 index 0000000..9d227a0 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/MarkupSafe-2.1.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/docs/examples/flask/venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/METADATA b/docs/examples/flask/venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/METADATA new file mode 100644 index 0000000..4a34999 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/METADATA @@ -0,0 +1,98 @@ +Metadata-Version: 2.1 +Name: MarkupSafe +Version: 2.1.2 +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 +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.7 +Description-Content-Type: text/x-rst +License-File: LICENSE.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/getting-started/ + + +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/docs/examples/flask/venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/RECORD b/docs/examples/flask/venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/RECORD new file mode 100644 index 0000000..39ce15f --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/RECORD @@ -0,0 +1,14 @@ +MarkupSafe-2.1.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +MarkupSafe-2.1.2.dist-info/LICENSE.rst,sha256=SJqOEQhQntmKN7uYPhHg9-HTHwvY-Zp5yESOf_N9B-o,1475 +MarkupSafe-2.1.2.dist-info/METADATA,sha256=jPw4iOiZg6adxZ5bdvjZapeNmPMINMGG2q2v2rI4SqA,3222 +MarkupSafe-2.1.2.dist-info/RECORD,, +MarkupSafe-2.1.2.dist-info/WHEEL,sha256=zcODeksSPXfEVqloqbuxG5DqZN6qJmVdi0EN9bjsiGQ,152 +MarkupSafe-2.1.2.dist-info/top_level.txt,sha256=qy0Plje5IJuvsCBjejJyhDCjEAdcDLK_2agVcex8Z6U,11 +markupsafe/__init__.py,sha256=LtjnhQ6AHmAgHl37cev2oQBXjr4xOF-QhdXgsCAL3-0,9306 +markupsafe/__pycache__/__init__.cpython-311.pyc,, +markupsafe/__pycache__/_native.cpython-311.pyc,, +markupsafe/_native.py,sha256=GR86Qvo_GcgKmKreA1WmYN9ud17OFwkww8E-fiW-57s,1713 +markupsafe/_speedups.c,sha256=X2XvQVtIdcK4Usz70BvkzoOfjTCmQlDkkjYSn-swE0g,7083 +markupsafe/_speedups.cpython-311-x86_64-linux-gnu.so,sha256=oxS10ynvGJxxD2aGbEyu_VASpUD-XwDsjQOlaw6cSzs,53656 +markupsafe/_speedups.pyi,sha256=vfMCsOgbAXRNLUXkyuyonG8uEWKYU4PDqNuMaDELAYw,229 +markupsafe/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/WHEEL b/docs/examples/flask/venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/WHEEL new file mode 100644 index 0000000..9a9cfe1 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.38.4) +Root-Is-Purelib: false +Tag: cp311-cp311-manylinux_2_17_x86_64 +Tag: cp311-cp311-manylinux2014_x86_64 + diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/top_level.txt b/docs/examples/flask/venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/top_level.txt new file mode 100644 index 0000000..75bf729 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/top_level.txt @@ -0,0 +1 @@ +markupsafe diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/Werkzeug-2.2.3.dist-info/INSTALLER b/docs/examples/flask/venv/lib/python3.11/site-packages/Werkzeug-2.2.3.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/Werkzeug-2.2.3.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/Werkzeug-2.2.3.dist-info/LICENSE.rst b/docs/examples/flask/venv/lib/python3.11/site-packages/Werkzeug-2.2.3.dist-info/LICENSE.rst new file mode 100644 index 0000000..c37cae4 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/Werkzeug-2.2.3.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/docs/examples/flask/venv/lib/python3.11/site-packages/Werkzeug-2.2.3.dist-info/METADATA b/docs/examples/flask/venv/lib/python3.11/site-packages/Werkzeug-2.2.3.dist-info/METADATA new file mode 100644 index 0000000..647bfc8 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/Werkzeug-2.2.3.dist-info/METADATA @@ -0,0 +1,126 @@ +Metadata-Version: 2.1 +Name: Werkzeug +Version: 2.2.3 +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 +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.7 +Description-Content-Type: text/x-rst +License-File: LICENSE.rst +Requires-Dist: MarkupSafe (>=2.1.1) +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/docs/examples/flask/venv/lib/python3.11/site-packages/Werkzeug-2.2.3.dist-info/RECORD b/docs/examples/flask/venv/lib/python3.11/site-packages/Werkzeug-2.2.3.dist-info/RECORD new file mode 100644 index 0000000..748890c --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/Werkzeug-2.2.3.dist-info/RECORD @@ -0,0 +1,98 @@ +Werkzeug-2.2.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Werkzeug-2.2.3.dist-info/LICENSE.rst,sha256=O0nc7kEF6ze6wQ-vG-JgQI_oXSUrjp3y4JefweCUQ3s,1475 +Werkzeug-2.2.3.dist-info/METADATA,sha256=TIyameVEp5p52N9E1mTWWabY6g1sB0Dm25vznZQeXPQ,4416 +Werkzeug-2.2.3.dist-info/RECORD,, +Werkzeug-2.2.3.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92 +Werkzeug-2.2.3.dist-info/top_level.txt,sha256=QRyj2VjwJoQkrwjwFIOlB8Xg3r9un0NtqVHQF-15xaw,9 +werkzeug/__init__.py,sha256=Hr0lQweC21HXPVBemSpBJUIzrbq2mn8h70J1h30QcqY,188 +werkzeug/__pycache__/__init__.cpython-311.pyc,, +werkzeug/__pycache__/_internal.cpython-311.pyc,, +werkzeug/__pycache__/_reloader.cpython-311.pyc,, +werkzeug/__pycache__/datastructures.cpython-311.pyc,, +werkzeug/__pycache__/exceptions.cpython-311.pyc,, +werkzeug/__pycache__/formparser.cpython-311.pyc,, +werkzeug/__pycache__/http.cpython-311.pyc,, +werkzeug/__pycache__/local.cpython-311.pyc,, +werkzeug/__pycache__/security.cpython-311.pyc,, +werkzeug/__pycache__/serving.cpython-311.pyc,, +werkzeug/__pycache__/test.cpython-311.pyc,, +werkzeug/__pycache__/testapp.cpython-311.pyc,, +werkzeug/__pycache__/urls.cpython-311.pyc,, +werkzeug/__pycache__/user_agent.cpython-311.pyc,, +werkzeug/__pycache__/utils.cpython-311.pyc,, +werkzeug/__pycache__/wsgi.cpython-311.pyc,, +werkzeug/_internal.py,sha256=4lwshe63pFlCo0h2IMcmvhbugA50QXQvfLD5VoY5c4Q,16271 +werkzeug/_reloader.py,sha256=hiP0z4bi6p_8UIJOtq7K0BV2dqCik5yztWLsDXeI_WE,14285 +werkzeug/datastructures.py,sha256=v2WYfs1rb1OuQgXyLripHQFwgodrfTNCd5P5f8n3ueA,97081 +werkzeug/datastructures.pyi,sha256=HRzDLc7A6qnwluhNqn6AT76CsLZIkAbVVqxn0AbfV-s,34506 +werkzeug/debug/__init__.py,sha256=wfJ2OmljsO5C0e0sXJpTUiG6bwGU6uHtFDDDMfJfQJk,18877 +werkzeug/debug/__pycache__/__init__.cpython-311.pyc,, +werkzeug/debug/__pycache__/console.cpython-311.pyc,, +werkzeug/debug/__pycache__/repr.cpython-311.pyc,, +werkzeug/debug/__pycache__/tbtools.cpython-311.pyc,, +werkzeug/debug/console.py,sha256=dechqiCtHfs0AQZWZofUC1S97tCuvwDgT0gdha5KwWM,6208 +werkzeug/debug/repr.py,sha256=vF3TLnYBohYr8V6Gz13PTJspQs42uv3gUJSzSbmHJBo,9472 +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/style.css,sha256=-xSxzUEZGw_IqlDR5iZxitNl8LQUjBM-_Y4UAvXVH8g,6078 +werkzeug/debug/tbtools.py,sha256=6iohJovtBSFRAcgX7_aRY4r3e19PLj3FavYB3RM4CmA,13263 +werkzeug/exceptions.py,sha256=8-KOXguQkOLoBUdN-7x_WyHT92TcAmjTWNwG4t8QYIg,26527 +werkzeug/formparser.py,sha256=DBRbbAnzspYUBzgfxPaZC7MjGAK_m5QTvdWoyvrhw4o,16516 +werkzeug/http.py,sha256=NqJjYCt8tKn2XOEKPApq4L3q8zb8YFq3GFOe5gsonI4,42776 +werkzeug/local.py,sha256=v-HEqr4bLpLHl4upCj97MOfUyCjW10Tp6mcNaFRFyew,22288 +werkzeug/middleware/__init__.py,sha256=qfqgdT5npwG9ses3-FXQJf3aB95JYP1zchetH_T3PUw,500 +werkzeug/middleware/__pycache__/__init__.cpython-311.pyc,, +werkzeug/middleware/__pycache__/dispatcher.cpython-311.pyc,, +werkzeug/middleware/__pycache__/http_proxy.cpython-311.pyc,, +werkzeug/middleware/__pycache__/lint.cpython-311.pyc,, +werkzeug/middleware/__pycache__/profiler.cpython-311.pyc,, +werkzeug/middleware/__pycache__/proxy_fix.cpython-311.pyc,, +werkzeug/middleware/__pycache__/shared_data.cpython-311.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=1w_UVKkAwq5wjjtCcDCDZwhAhWzPSZ0aDyUmbjAEeXw,13952 +werkzeug/middleware/profiler.py,sha256=7pWYDYPC774S0-HYLkG3Uge58PGUMX7tWp_Cor3etvo,4883 +werkzeug/middleware/proxy_fix.py,sha256=l7LC_LDu0Yd4SvUxS5SFigAJMzcIOGm6LNKl9IXJBSU,6974 +werkzeug/middleware/shared_data.py,sha256=fXjrEkuqxUVLG1DLrOdQLc96QQdjftCBZ1oM5oK89h4,9528 +werkzeug/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +werkzeug/routing/__init__.py,sha256=HpvahY7WwkLdV4Cq3Bsc3GrqNon4u6t8-vhbb9E5o00,4819 +werkzeug/routing/__pycache__/__init__.cpython-311.pyc,, +werkzeug/routing/__pycache__/converters.cpython-311.pyc,, +werkzeug/routing/__pycache__/exceptions.cpython-311.pyc,, +werkzeug/routing/__pycache__/map.cpython-311.pyc,, +werkzeug/routing/__pycache__/matcher.cpython-311.pyc,, +werkzeug/routing/__pycache__/rules.cpython-311.pyc,, +werkzeug/routing/converters.py,sha256=05bkekg64vLC6mqqK4ddBh589WH9yBsjtW8IJhdUBvw,6968 +werkzeug/routing/exceptions.py,sha256=RklUDL9ajOv2fTcRNj4pb18Bs4Y-GKk4rIeTSfsqkks,4737 +werkzeug/routing/map.py,sha256=XN4ZjzEF1SfMxtdov89SDE-1_U78KVnnobTfnHzqbmE,36757 +werkzeug/routing/matcher.py,sha256=6VvQYCCOjyj1JKUZKuAiVA_U1nXtvvJ70pSbBUdL_1k,7509 +werkzeug/routing/rules.py,sha256=3YsPpI9ZGcNmFiV2Go2Td1DvZ9ZdaMMnvGP1o17aMfc,31836 +werkzeug/sansio/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +werkzeug/sansio/__pycache__/__init__.cpython-311.pyc,, +werkzeug/sansio/__pycache__/http.cpython-311.pyc,, +werkzeug/sansio/__pycache__/multipart.cpython-311.pyc,, +werkzeug/sansio/__pycache__/request.cpython-311.pyc,, +werkzeug/sansio/__pycache__/response.cpython-311.pyc,, +werkzeug/sansio/__pycache__/utils.cpython-311.pyc,, +werkzeug/sansio/http.py,sha256=k3nREBfU-r8fXCfSTKQenys25q9bzUOvdY-OVGrqztA,5107 +werkzeug/sansio/multipart.py,sha256=vMZ85cvLD55clUTcTin2DtBv2GQRGh0_fExklnXKHoI,10055 +werkzeug/sansio/request.py,sha256=SiGcx2cz-l81TlCCrKrT2fePqC64hN8fSg5Ig6J6vRs,20175 +werkzeug/sansio/response.py,sha256=UTl-teQDDjovrZMkjj3ZQsHw-JtiFak5JfKEk1_vBYU,26026 +werkzeug/sansio/utils.py,sha256=EjbqdHdT-JZWgjUQaaWSgBUIRprXUkrsMQQqJlJHpVU,4847 +werkzeug/security.py,sha256=7TVI0L62emBHAh-1RHB_KlwGYcE08pPCyU674Ho4aNE,4653 +werkzeug/serving.py,sha256=XCiHFbMCFCgecKycgajhF4rFsGoemeN0xW1eTQqNt-g,37558 +werkzeug/test.py,sha256=uMahfM81RqEN3d3Sp4SkN36Pi8oZpV6dTgFY0cW1_2c,48126 +werkzeug/testapp.py,sha256=RJhT_2JweNiMKe304N3bF1zaIeMpRx-CIMERdeydfTY,9404 +werkzeug/urls.py,sha256=Q9Si-eVh7yxk3rwkzrwGRm146FXVXgg9lBP3k0HUfVM,36600 +werkzeug/user_agent.py,sha256=WclZhpvgLurMF45hsioSbS75H1Zb4iMQGKN3_yZ2oKo,1420 +werkzeug/utils.py,sha256=BDX5_7OCMVgl-ib84bCEdBG5MVvrxaSlfdg7Cxh4ND0,25174 +werkzeug/wrappers/__init__.py,sha256=kGyK7rOud3qCxll_jFyW15YarJhj1xtdf3ocx9ZheB8,120 +werkzeug/wrappers/__pycache__/__init__.cpython-311.pyc,, +werkzeug/wrappers/__pycache__/request.cpython-311.pyc,, +werkzeug/wrappers/__pycache__/response.cpython-311.pyc,, +werkzeug/wrappers/request.py,sha256=XmpTThXytTdznbPJnIsfdoIAvdi-THzTJQ9DsvARhn4,24026 +werkzeug/wrappers/response.py,sha256=ii1IaN2eUfoB-tBqbn_46fCB_SVVL8Fu4qd6cu0AlEY,34963 +werkzeug/wsgi.py,sha256=-VKI2iwCgLb-VToIZeBpdutkTETxy9HkIwgcFC5orkU,36060 diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/Werkzeug-2.2.3.dist-info/WHEEL b/docs/examples/flask/venv/lib/python3.11/site-packages/Werkzeug-2.2.3.dist-info/WHEEL new file mode 100644 index 0000000..57e3d84 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/Werkzeug-2.2.3.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.38.4) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/Werkzeug-2.2.3.dist-info/top_level.txt b/docs/examples/flask/venv/lib/python3.11/site-packages/Werkzeug-2.2.3.dist-info/top_level.txt new file mode 100644 index 0000000..6fe8da8 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/Werkzeug-2.2.3.dist-info/top_level.txt @@ -0,0 +1 @@ +werkzeug diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/_distutils_hack/__init__.py b/docs/examples/flask/venv/lib/python3.11/site-packages/_distutils_hack/__init__.py new file mode 100644 index 0000000..f987a53 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/_distutils_hack/__init__.py @@ -0,0 +1,222 @@ +# don't import any costly modules +import sys +import os + + +is_pypy = '__pypy__' in sys.builtin_module_names + + +def warn_distutils_present(): + if 'distutils' not in sys.modules: + return + if is_pypy and sys.version_info < (3, 7): + # PyPy for 3.6 unconditionally imports distutils, so bypass the warning + # https://foss.heptapod.net/pypy/pypy/-/blob/be829135bc0d758997b3566062999ee8b23872b4/lib-python/3/site.py#L250 + return + import warnings + + warnings.warn( + "Distutils was imported before Setuptools, but importing Setuptools " + "also replaces the `distutils` module in `sys.modules`. This may lead " + "to undesirable behaviors or errors. To avoid these issues, avoid " + "using distutils directly, ensure that setuptools is installed in the " + "traditional way (e.g. not an editable install), and/or make sure " + "that setuptools is always imported before distutils." + ) + + +def clear_distutils(): + if 'distutils' not in sys.modules: + return + import warnings + + warnings.warn("Setuptools is replacing distutils.") + mods = [ + name + for name in sys.modules + if name == "distutils" or name.startswith("distutils.") + ] + for name in mods: + del sys.modules[name] + + +def enabled(): + """ + Allow selection of distutils by environment variable. + """ + which = os.environ.get('SETUPTOOLS_USE_DISTUTILS', 'local') + return which == 'local' + + +def ensure_local_distutils(): + import importlib + + clear_distutils() + + # With the DistutilsMetaFinder in place, + # perform an import to cause distutils to be + # loaded from setuptools._distutils. Ref #2906. + with shim(): + importlib.import_module('distutils') + + # check that submodules load as expected + core = importlib.import_module('distutils.core') + assert '_distutils' in core.__file__, core.__file__ + assert 'setuptools._distutils.log' not in sys.modules + + +def do_override(): + """ + Ensure that the local copy of distutils is preferred over stdlib. + + See https://github.com/pypa/setuptools/issues/417#issuecomment-392298401 + for more motivation. + """ + if enabled(): + warn_distutils_present() + ensure_local_distutils() + + +class _TrivialRe: + def __init__(self, *patterns): + self._patterns = patterns + + def match(self, string): + return all(pat in string for pat in self._patterns) + + +class DistutilsMetaFinder: + def find_spec(self, fullname, path, target=None): + # optimization: only consider top level modules and those + # found in the CPython test suite. + if path is not None and not fullname.startswith('test.'): + return + + method_name = 'spec_for_{fullname}'.format(**locals()) + method = getattr(self, method_name, lambda: None) + return method() + + def spec_for_distutils(self): + if self.is_cpython(): + return + + import importlib + import importlib.abc + import importlib.util + + try: + mod = importlib.import_module('setuptools._distutils') + except Exception: + # There are a couple of cases where setuptools._distutils + # may not be present: + # - An older Setuptools without a local distutils is + # taking precedence. Ref #2957. + # - Path manipulation during sitecustomize removes + # setuptools from the path but only after the hook + # has been loaded. Ref #2980. + # In either case, fall back to stdlib behavior. + return + + class DistutilsLoader(importlib.abc.Loader): + def create_module(self, spec): + mod.__name__ = 'distutils' + return mod + + def exec_module(self, module): + pass + + return importlib.util.spec_from_loader( + 'distutils', DistutilsLoader(), origin=mod.__file__ + ) + + @staticmethod + def is_cpython(): + """ + Suppress supplying distutils for CPython (build and tests). + Ref #2965 and #3007. + """ + return os.path.isfile('pybuilddir.txt') + + def spec_for_pip(self): + """ + Ensure stdlib distutils when running under pip. + See pypa/pip#8761 for rationale. + """ + if self.pip_imported_during_build(): + return + clear_distutils() + self.spec_for_distutils = lambda: None + + @classmethod + def pip_imported_during_build(cls): + """ + Detect if pip is being imported in a build script. Ref #2355. + """ + import traceback + + return any( + cls.frame_file_is_setup(frame) for frame, line in traceback.walk_stack(None) + ) + + @staticmethod + def frame_file_is_setup(frame): + """ + Return True if the indicated frame suggests a setup.py file. + """ + # some frames may not have __file__ (#2940) + return frame.f_globals.get('__file__', '').endswith('setup.py') + + def spec_for_sensitive_tests(self): + """ + Ensure stdlib distutils when running select tests under CPython. + + python/cpython#91169 + """ + clear_distutils() + self.spec_for_distutils = lambda: None + + sensitive_tests = ( + [ + 'test.test_distutils', + 'test.test_peg_generator', + 'test.test_importlib', + ] + if sys.version_info < (3, 10) + else [ + 'test.test_distutils', + ] + ) + + +for name in DistutilsMetaFinder.sensitive_tests: + setattr( + DistutilsMetaFinder, + f'spec_for_{name}', + DistutilsMetaFinder.spec_for_sensitive_tests, + ) + + +DISTUTILS_FINDER = DistutilsMetaFinder() + + +def add_shim(): + DISTUTILS_FINDER in sys.meta_path or insert_shim() + + +class shim: + def __enter__(self): + insert_shim() + + def __exit__(self, exc, value, tb): + remove_shim() + + +def insert_shim(): + sys.meta_path.insert(0, DISTUTILS_FINDER) + + +def remove_shim(): + try: + sys.meta_path.remove(DISTUTILS_FINDER) + except ValueError: + pass diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/_distutils_hack/__pycache__/__init__.cpython-311.pyc b/docs/examples/flask/venv/lib/python3.11/site-packages/_distutils_hack/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6ff5ea2049355ccb0e12904fb4c23d5742ee0335 GIT binary patch literal 11198 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 literal 0 HcmV?d00001 diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/_distutils_hack/__pycache__/override.cpython-311.pyc b/docs/examples/flask/venv/lib/python3.11/site-packages/_distutils_hack/__pycache__/override.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8b1ec4f174cc45f1bf8e4c79767d53d72c9a013a GIT binary patch literal 355 zcmZ8c%}N6?5T3LkWlQk|+Ph1&p?DP$yi3Ig2(g*EVKco^ zq{kBz4Dh?K_sW)JBZWBU2A$p8f-ToQ|6%0NgeusDs N!wQD0ChS32{sGIqV4MH| literal 0 HcmV?d00001 diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/_distutils_hack/override.py b/docs/examples/flask/venv/lib/python3.11/site-packages/_distutils_hack/override.py new file mode 100644 index 0000000..2cc433a --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/_distutils_hack/override.py @@ -0,0 +1 @@ +__import__('_distutils_hack').do_override() diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/click-8.1.3.dist-info/INSTALLER b/docs/examples/flask/venv/lib/python3.11/site-packages/click-8.1.3.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/click-8.1.3.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/click-8.1.3.dist-info/LICENSE.rst b/docs/examples/flask/venv/lib/python3.11/site-packages/click-8.1.3.dist-info/LICENSE.rst new file mode 100644 index 0000000..d12a849 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/click-8.1.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/docs/examples/flask/venv/lib/python3.11/site-packages/click-8.1.3.dist-info/METADATA b/docs/examples/flask/venv/lib/python3.11/site-packages/click-8.1.3.dist-info/METADATA new file mode 100644 index 0000000..8e5dc1e --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/click-8.1.3.dist-info/METADATA @@ -0,0 +1,111 @@ +Metadata-Version: 2.1 +Name: click +Version: 8.1.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.7 +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/docs/examples/flask/venv/lib/python3.11/site-packages/click-8.1.3.dist-info/RECORD b/docs/examples/flask/venv/lib/python3.11/site-packages/click-8.1.3.dist-info/RECORD new file mode 100644 index 0000000..0c973f8 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/click-8.1.3.dist-info/RECORD @@ -0,0 +1,39 @@ +click-8.1.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +click-8.1.3.dist-info/LICENSE.rst,sha256=morRBqOU6FO_4h9C9OctWSgZoigF2ZG18ydQKSkrZY0,1475 +click-8.1.3.dist-info/METADATA,sha256=tFJIX5lOjx7c5LjZbdTPFVDJSgyv9F74XY0XCPp_gnc,3247 +click-8.1.3.dist-info/RECORD,, +click-8.1.3.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92 +click-8.1.3.dist-info/top_level.txt,sha256=J1ZQogalYS4pphY_lPECoNMfw0HzTSrZglC4Yfwo4xA,6 +click/__init__.py,sha256=rQBLutqg-z6m8nOzivIfigDn_emijB_dKv9BZ2FNi5s,3138 +click/__pycache__/__init__.cpython-311.pyc,, +click/__pycache__/_compat.cpython-311.pyc,, +click/__pycache__/_termui_impl.cpython-311.pyc,, +click/__pycache__/_textwrap.cpython-311.pyc,, +click/__pycache__/_winconsole.cpython-311.pyc,, +click/__pycache__/core.cpython-311.pyc,, +click/__pycache__/decorators.cpython-311.pyc,, +click/__pycache__/exceptions.cpython-311.pyc,, +click/__pycache__/formatting.cpython-311.pyc,, +click/__pycache__/globals.cpython-311.pyc,, +click/__pycache__/parser.cpython-311.pyc,, +click/__pycache__/shell_completion.cpython-311.pyc,, +click/__pycache__/termui.cpython-311.pyc,, +click/__pycache__/testing.cpython-311.pyc,, +click/__pycache__/types.cpython-311.pyc,, +click/__pycache__/utils.cpython-311.pyc,, +click/_compat.py,sha256=JIHLYs7Jzz4KT9t-ds4o4jBzLjnwCiJQKqur-5iwCKI,18810 +click/_termui_impl.py,sha256=qK6Cfy4mRFxvxE8dya8RBhLpSC8HjF-lvBc6aNrPdwg,23451 +click/_textwrap.py,sha256=10fQ64OcBUMuK7mFvh8363_uoOxPlRItZBmKzRJDgoY,1353 +click/_winconsole.py,sha256=5ju3jQkcZD0W27WEMGqmEP4y_crUVzPCqsX_FYb7BO0,7860 +click/core.py,sha256=mz87bYEKzIoNYEa56BFAiOJnvt1Y0L-i7wD4_ZecieE,112782 +click/decorators.py,sha256=yo3zvzgUm5q7h5CXjyV6q3h_PJAiUaem178zXwdWUFI,16350 +click/exceptions.py,sha256=7gDaLGuFZBeCNwY9ERMsF2-Z3R9Fvq09Zc6IZSKjseo,9167 +click/formatting.py,sha256=Frf0-5W33-loyY_i9qrwXR8-STnW3m5gvyxLVUdyxyk,9706 +click/globals.py,sha256=TP-qM88STzc7f127h35TD_v920FgfOD2EwzqA0oE8XU,1961 +click/parser.py,sha256=cAEt1uQR8gq3-S9ysqbVU-fdAZNvilxw4ReJ_T1OQMk,19044 +click/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +click/shell_completion.py,sha256=qOp_BeC9esEOSZKyu5G7RIxEUaLsXUX-mTb7hB1r4QY,18018 +click/termui.py,sha256=ACBQVOvFCTSqtD5VREeCAdRtlHd-Imla-Lte4wSfMjA,28355 +click/testing.py,sha256=ptpMYgRY7dVfE3UDgkgwayu9ePw98sQI3D7zZXiCpj4,16063 +click/types.py,sha256=rEb1aZSQKq3ciCMmjpG2Uva9vk498XRL7ThrcK2GRss,35805 +click/utils.py,sha256=33D6E7poH_nrKB-xr-UyDEXnxOcCiQqxuRLtrqeVv6o,18682 diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/click-8.1.3.dist-info/WHEEL b/docs/examples/flask/venv/lib/python3.11/site-packages/click-8.1.3.dist-info/WHEEL new file mode 100644 index 0000000..becc9a6 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/click-8.1.3.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.1) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/click-8.1.3.dist-info/top_level.txt b/docs/examples/flask/venv/lib/python3.11/site-packages/click-8.1.3.dist-info/top_level.txt new file mode 100644 index 0000000..dca9a90 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/click-8.1.3.dist-info/top_level.txt @@ -0,0 +1 @@ +click diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/click/__init__.py b/docs/examples/flask/venv/lib/python3.11/site-packages/click/__init__.py new file mode 100644 index 0000000..e3ef423 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/click/__init__.py @@ -0,0 +1,73 @@ +""" +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 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_text_stream as get_text_stream +from .utils import open_file as open_file + +__version__ = "8.1.3" diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/click/__pycache__/__init__.cpython-311.pyc b/docs/examples/flask/venv/lib/python3.11/site-packages/click/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c7992fe4984be1e95f500874af8d38e71b57e76e GIT binary patch literal 3716 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 R2Pt;|IBlE{u}8L6`oFD-GL!%S literal 0 HcmV?d00001 diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/click/__pycache__/_termui_impl.cpython-311.pyc b/docs/examples/flask/venv/lib/python3.11/site-packages/click/__pycache__/_termui_impl.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dab3befa3f5a0b3bb6a6edb1cbfe37e072eba0fa GIT binary patch literal 32620 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 E4-C0`_5c6? literal 0 HcmV?d00001 diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/click/__pycache__/_winconsole.cpython-311.pyc b/docs/examples/flask/venv/lib/python3.11/site-packages/click/__pycache__/_winconsole.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b8ed44b7e59e78de1393d08018f1e714943185e3 GIT binary patch literal 13367 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 literal 0 HcmV?d00001 diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/click/__pycache__/decorators.cpython-311.pyc b/docs/examples/flask/venv/lib/python3.11/site-packages/click/__pycache__/decorators.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fac541d6de6946afedd3d8c5603ab0b4cc9025fc GIT binary patch literal 23537 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 Q;Bm+pe1oNI2^j7EKQ?+jlK=n! literal 0 HcmV?d00001 diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/click/__pycache__/globals.cpython-311.pyc b/docs/examples/flask/venv/lib/python3.11/site-packages/click/__pycache__/globals.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..61d0e918ed3b0dfbe0a528811b49f66b40de5961 GIT binary patch literal 3404 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 literal 0 HcmV?d00001 diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/click/__pycache__/shell_completion.cpython-311.pyc b/docs/examples/flask/venv/lib/python3.11/site-packages/click/__pycache__/shell_completion.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..77477c7321fb37cb8be64c15febed751001f7d5c GIT binary patch literal 23603 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 literal 0 HcmV?d00001 diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/click/__pycache__/termui.cpython-311.pyc b/docs/examples/flask/venv/lib/python3.11/site-packages/click/__pycache__/termui.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..14d0294da2e6f0ab9a372cb663e2e1e400a9ab9c GIT binary patch literal 34708 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 literal 0 HcmV?d00001 diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/click/__pycache__/testing.cpython-311.pyc b/docs/examples/flask/venv/lib/python3.11/site-packages/click/__pycache__/testing.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..04f9505fe3b8b2e59f064827f171728887f517ce GIT binary patch literal 25772 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 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 ignore the + # atomic flag. Use fsdecode to handle Path("-"). + if os.fsdecode(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/docs/examples/flask/venv/lib/python3.11/site-packages/click/_termui_impl.py b/docs/examples/flask/venv/lib/python3.11/site-packages/click/_termui_impl.py new file mode 100644 index 0000000..4b979bc --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/click/_termui_impl.py @@ -0,0 +1,717 @@ +""" +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/docs/examples/flask/venv/lib/python3.11/site-packages/click/_textwrap.py b/docs/examples/flask/venv/lib/python3.11/site-packages/click/_textwrap.py new file mode 100644 index 0000000..b47dcbd --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/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/docs/examples/flask/venv/lib/python3.11/site-packages/click/_winconsole.py b/docs/examples/flask/venv/lib/python3.11/site-packages/click/_winconsole.py new file mode 100644 index 0000000..6b20df3 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/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/docs/examples/flask/venv/lib/python3.11/site-packages/click/core.py b/docs/examples/flask/venv/lib/python3.11/site-packages/click/core.py new file mode 100644 index 0000000..5abfb0f --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/click/core.py @@ -0,0 +1,2998 @@ +import enum +import errno +import inspect +import os +import sys +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 .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 the default value for commands. If this + value is not set, it defaults to the value from the parent + context. ``Command.show_default`` overrides this default for the + specific command. + + .. versionchanged:: 8.1 + The ``show_default`` parameter is overridden by + ``Command.show_default``, instead of the other way around. + + .. 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] = [] + #: the collected prefixes of the command's options. + self._opt_prefixes: t.Set[str] = set(parent._opt_prefixes) if parent else set() + + 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 + + @t.overload + def lookup_default( + self, name: str, call: "te.Literal[True]" = True + ) -> t.Optional[t.Any]: + ... + + @t.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 + + @t.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": + ... + + @t.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. + """ + 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. + + :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. + + .. versionchanged:: 8.1 + ``help``, ``epilog``, and ``short_help`` are stored unprocessed, + all formatting is done when outputting help text, not at init, + and is done even if not using the ``@command`` decorator. + + .. versionchanged:: 8.0 + Added a ``repr`` showing the command name. + + .. versionchanged:: 7.1 + Added the ``no_args_is_help`` parameter. + + .. versionchanged:: 2.0 + Added the ``context_settings`` parameter. + """ + + 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 [] + 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. + """ + if self.short_help: + text = inspect.cleandoc(self.short_help) + elif self.help: + text = make_default_short_help(self.help, limit) + else: + text = "" + + 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 if self.help is not None else "" + + if self.deprecated: + text = _("(Deprecated) {text}").format(text=text) + + if text: + text = inspect.cleandoc(text).partition("\f")[0] + 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: + epilog = inspect.cleandoc(self.epilog) + formatter.write_paragraph() + + with formatter.indentation(): + formatter.write_text(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 + ctx._opt_prefixes.update(parser._opt_prefixes) + 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 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 the group return value for regular + # groups, or an empty list for chained groups. + with ctx: + rv = super().invoke(ctx) + return _process_result([] if self.chain else rv) + 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 + + @t.overload + def command(self, __func: t.Callable[..., t.Any]) -> Command: + ... + + @t.overload + def command( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Callable[[t.Callable[..., t.Any]], Command]: + ... + + def command( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Union[t.Callable[[t.Callable[..., t.Any]], Command], 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.1 + This decorator can be applied without parentheses. + + .. versionchanged:: 8.0 + Added the :attr:`command_class` attribute. + """ + from .decorators import command + + if self.command_class and kwargs.get("cls") is None: + kwargs["cls"] = self.command_class + + func: t.Optional[t.Callable] = None + + if args and callable(args[0]): + assert ( + len(args) == 1 and not kwargs + ), "Use 'command(**kwargs)(callable)' to provide arguments." + (func,) = args + args = () + + def decorator(f: t.Callable[..., t.Any]) -> Command: + cmd: Command = command(*args, **kwargs)(f) + self.add_command(cmd) + return cmd + + if func is not None: + return decorator(func) + + return decorator + + @t.overload + def group(self, __func: t.Callable[..., t.Any]) -> "Group": + ... + + @t.overload + def group( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Callable[[t.Callable[..., t.Any]], "Group"]: + ... + + def group( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Union[t.Callable[[t.Callable[..., t.Any]], "Group"], "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.1 + This decorator can be applied without parentheses. + + .. versionchanged:: 8.0 + Added the :attr:`group_class` attribute. + """ + from .decorators import group + + func: t.Optional[t.Callable] = None + + if args and callable(args[0]): + assert ( + len(args) == 1 and not kwargs + ), "Use 'group(**kwargs)(callable)' to provide arguments." + (func,) = args + args = () + + if self.group_class is not None and kwargs.get("cls") is None: + 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 = group(*args, **kwargs)(f) + self.add_command(cmd) + return cmd + + if func is not None: + return decorator(func) + + 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, + ) -> 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 + 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 + + @t.overload + def get_default( + self, ctx: Context, call: "te.Literal[True]" = True + ) -> t.Optional[t.Any]: + ... + + @t.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: Show the default value for this option in its + help text. Values are not shown by default, unless + :attr:`Context.show_default` is ``True``. If this value is a + string, it shows that string in parentheses instead of the + actual value. This is particularly useful for dynamic options. + For single option boolean flags, the default remains hidden if + its value is ``False``. + :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.1.0 + Help text indentation is cleaned here instead of only in the + ``@option`` decorator. + + .. versionchanged:: 8.1.0 + The ``show_default`` parameter overrides + ``Context.show_default``. + + .. versionchanged:: 8.1.0 + The default of a single option boolean flag is not shown if the + default value is ``False``. + + .. 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, None] = None, + 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: + if help: + help = inspect.cleandoc(help) + + 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 = 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 and not self.required: + 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'.") + + if self.multiple and self.is_flag: + raise TypeError("'multiple' is not valid with 'is_flag', use 'count'.") + + 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 = False + show_default_is_str = False + + if self.show_default is not None: + if isinstance(self.show_default, str): + show_default_is_str = show_default = True + else: + show_default = self.show_default + elif ctx.show_default is not None: + show_default = ctx.show_default + + if show_default_is_str or (show_default and (default_value is not None)): + 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 inspect.isfunction(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] + elif self.is_bool_flag and not self.secondary_opts and not default_value: + default_string = "" + 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 + + @t.overload + def get_default( + self, ctx: Context, call: "te.Literal[True]" = True + ) -> t.Optional[t.Any]: + ... + + @t.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 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) + + 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 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/docs/examples/flask/venv/lib/python3.11/site-packages/click/decorators.py b/docs/examples/flask/venv/lib/python3.11/site-packages/click/decorators.py new file mode 100644 index 0000000..28618dc --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/click/decorators.py @@ -0,0 +1,497 @@ +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", bound=t.Union[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 + + +CmdType = t.TypeVar("CmdType", bound=Command) + + +@t.overload +def command( + __func: t.Callable[..., t.Any], +) -> Command: + ... + + +@t.overload +def command( + name: t.Optional[str] = None, + **attrs: t.Any, +) -> t.Callable[..., Command]: + ... + + +@t.overload +def command( + name: t.Optional[str] = None, + cls: t.Type[CmdType] = ..., + **attrs: t.Any, +) -> t.Callable[..., CmdType]: + ... + + +def command( + name: t.Union[str, t.Callable[..., t.Any], None] = None, + cls: t.Optional[t.Type[Command]] = None, + **attrs: t.Any, +) -> t.Union[Command, t.Callable[..., 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. + For the ``params`` argument, any decorated params are appended to + the end of the list. + + 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`. + + .. versionchanged:: 8.1 + This decorator can be applied without parentheses. + + .. versionchanged:: 8.1 + The ``params`` argument can be used. Decorated params are + appended to the end of the list. + """ + + func: t.Optional[t.Callable[..., t.Any]] = None + + if callable(name): + func = name + name = None + assert cls is None, "Use 'command(cls=cls)(callable)' to specify a class." + assert not attrs, "Use 'command(**kwargs)(callable)' to provide arguments." + + if cls is None: + cls = Command + + def decorator(f: t.Callable[..., t.Any]) -> Command: + if isinstance(f, Command): + raise TypeError("Attempted to convert a callback into a command twice.") + + attr_params = attrs.pop("params", None) + params = attr_params if attr_params is not None else [] + + try: + decorator_params = f.__click_params__ # type: ignore + except AttributeError: + pass + else: + del f.__click_params__ # type: ignore + params.extend(reversed(decorator_params)) + + if attrs.get("help") is None: + attrs["help"] = f.__doc__ + + cmd = cls( # type: ignore[misc] + name=name or f.__name__.lower().replace("_", "-"), # type: ignore[arg-type] + callback=f, + params=params, + **attrs, + ) + cmd.__doc__ = f.__doc__ + return cmd + + if func is not None: + return decorator(func) + + return decorator + + +@t.overload +def group( + __func: t.Callable[..., t.Any], +) -> Group: + ... + + +@t.overload +def group( + name: t.Optional[str] = None, + **attrs: t.Any, +) -> t.Callable[[F], Group]: + ... + + +def group( + name: t.Union[str, t.Callable[..., t.Any], None] = None, **attrs: t.Any +) -> t.Union[Group, 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`. + + .. versionchanged:: 8.1 + This decorator can be applied without parentheses. + """ + if attrs.get("cls") is None: + attrs["cls"] = Group + + if callable(name): + grp: t.Callable[[F], Group] = t.cast(Group, command(**attrs)) + return grp(name) + + 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", None) or 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() + OptionClass = option_attrs.pop("cls", None) or 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/docs/examples/flask/venv/lib/python3.11/site-packages/click/exceptions.py b/docs/examples/flask/venv/lib/python3.11/site-packages/click/exceptions.py new file mode 100644 index 0000000..9e20b3e --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/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/docs/examples/flask/venv/lib/python3.11/site-packages/click/formatting.py b/docs/examples/flask/venv/lib/python3.11/site-packages/click/formatting.py new file mode 100644 index 0000000..ddd2a2f --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/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/docs/examples/flask/venv/lib/python3.11/site-packages/click/globals.py b/docs/examples/flask/venv/lib/python3.11/site-packages/click/globals.py new file mode 100644 index 0000000..480058f --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/click/globals.py @@ -0,0 +1,68 @@ +import typing as t +from threading import local + +if t.TYPE_CHECKING: + import typing_extensions as te + from .core import Context + +_local = local() + + +@t.overload +def get_current_context(silent: "te.Literal[False]" = False) -> "Context": + ... + + +@t.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/docs/examples/flask/venv/lib/python3.11/site-packages/click/parser.py b/docs/examples/flask/venv/lib/python3.11/site-packages/click/parser.py new file mode 100644 index 0000000..2d5a2ed --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/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/docs/examples/flask/venv/lib/python3.11/site-packages/click/py.typed b/docs/examples/flask/venv/lib/python3.11/site-packages/click/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/click/shell_completion.py b/docs/examples/flask/venv/lib/python3.11/site-packages/click/shell_completion.py new file mode 100644 index 0000000..c17a8e6 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/click/shell_completion.py @@ -0,0 +1,580 @@ +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 + COMPREPLY=() + compopt -o dirnames + elif [[ $type == 'file' ]]; then + COMPREPLY=() + 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(ctx: Context, value: str) -> bool: + """Check if the value looks like the start of an option.""" + if not value: + return False + + c = value[0] + return c in ctx._opt_prefixes + + +def _is_incomplete_option(ctx: Context, 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 or param.count: + return False + + last_option = None + + for index, arg in enumerate(reversed(args)): + if index + 1 > param.nargs: + break + + if _start_of_option(ctx, 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(ctx, 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(ctx, 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(ctx, 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/docs/examples/flask/venv/lib/python3.11/site-packages/click/termui.py b/docs/examples/flask/venv/lib/python3.11/site-packages/click/termui.py new file mode 100644 index 0000000..bfb2f5a --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/click/termui.py @@ -0,0 +1,787 @@ +import inspect +import io +import itertools +import os +import sys +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 an 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 = _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: + value2 = prompt_func(confirmation_prompt) + is_empty = not value and not value2 + if value2 or is_empty: + 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 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[t.AnyStr]] = 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/docs/examples/flask/venv/lib/python3.11/site-packages/click/testing.py b/docs/examples/flask/venv/lib/python3.11/site-packages/click/testing.py new file mode 100644 index 0000000..e395c2e --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/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() + dt = tempfile.mkdtemp(dir=temp_dir) # type: ignore[type-var] + os.chdir(dt) + + try: + yield t.cast(str, dt) + finally: + os.chdir(cwd) + + if temp_dir is None: + try: + shutil.rmtree(dt) + except OSError: # noqa: B014 + pass diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/click/types.py b/docs/examples/flask/venv/lib/python3.11/site-packages/click/types.py new file mode 100644 index 0000000..b45ee53 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/click/types.py @@ -0,0 +1,1073 @@ +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] + + # Custom subclasses might not remember to set a name. + if hasattr(self, "name"): + name = self.name + else: + name = param_type + + return {"param_type": param_type, "name": 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)}': {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 + returns the filename instead of an open file. Various checks can be + enabled to validate the type of file and permissions. + + :param exists: The file or directory needs to exist for the value to + be valid. If this is not set to ``True``, and the file does not + exist, then all further checks are silently skipped. + :param file_okay: Allow a file as a value. + :param dir_okay: Allow a directory as a value. + :param readable: if true, a readable check is performed. + :param writable: if true, a writable check is performed. + :param executable: if true, an executable check is performed. + :param resolve_path: Make the value absolute and resolve any + symlinks. A ``~`` is not expanded, as this is supposed to be + done by the shell only. + :param allow_dash: Allow a single dash as a value, which indicates + a standard stream (but does not open it). Use + :func:`~click.open_file` to handle opening this value. + :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.1 + Added the ``executable`` parameter. + + .. 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, + executable: bool = False, + ): + self.exists = exists + self.file_okay = file_okay + self.dir_okay = dir_okay + self.readable = readable + self.writable = writable + self.executable = executable + 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}' is a directory.").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, + ) + + 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.executable and not os.access(value, os.X_OK): + self.fail( + _("{name} {filename!r} is not executable.").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/docs/examples/flask/venv/lib/python3.11/site-packages/click/utils.py b/docs/examples/flask/venv/lib/python3.11/site-packages/click/utils.py new file mode 100644 index 0000000..8283788 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/click/utils.py @@ -0,0 +1,580 @@ +import os +import re +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[t.Any]] = 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: + """Open a file, with extra behavior to handle ``'-'`` to indicate + a standard stream, lazy open on write, and atomic write. Similar to + the behavior of the :class:`~click.File` param type. + + If ``'-'`` is given to open ``stdout`` or ``stdin``, the stream is + wrapped so that using it in a context manager will not close it. + This makes it possible to use the function without accidentally + closing a standard stream: + + .. code-block:: python + + with open_file(filename) as f: + ... + + :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 decode or encode a file opened in + text mode. + :param errors: The error handling mode. + :param lazy: Wait to open the file until it is accessed. For read + mode, the file is temporarily opened to raise access errors + early, then closed until it is read again. + :param atomic: Write to a temporary file and replace the given file + on close. + + .. versionadded:: 3.0 + """ + 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 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: t.Optional[ModuleType] = None +) -> 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 _main is None: + _main = sys.modules["__main__"] + + 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 is 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. + + .. versionchanged:: 8.1 + Invalid glob patterns are treated as empty expansions rather + than raising an error. + + .. 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) + + try: + matches = glob(arg, recursive=glob_recursive) + except re.error: + matches = [] + + if not matches: + out.append(arg) + else: + out.extend(matches) + + return out diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/distutils-precedence.pth b/docs/examples/flask/venv/lib/python3.11/site-packages/distutils-precedence.pth new file mode 100644 index 0000000..7f009fe --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/distutils-precedence.pth @@ -0,0 +1 @@ +import os; var = 'SETUPTOOLS_USE_DISTUTILS'; enabled = os.environ.get(var, 'local') == 'local'; enabled and __import__('_distutils_hack').add_shim(); diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/flask/__init__.py b/docs/examples/flask/venv/lib/python3.11/site-packages/flask/__init__.py new file mode 100644 index 0000000..463f55f --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/flask/__init__.py @@ -0,0 +1,71 @@ +from markupsafe import escape +from markupsafe import Markup + +from . import json as json +from .app import Flask as Flask +from .app import Request as Request +from .app import Response as Response +from .blueprints import Blueprint as Blueprint +from .config import Config as Config +from .ctx import after_this_request as after_this_request +from .ctx import copy_current_request_context as copy_current_request_context +from .ctx import has_app_context as has_app_context +from .ctx import has_request_context as has_request_context +from .globals import current_app as current_app +from .globals import g as g +from .globals import request as request +from .globals import session as session +from .helpers import abort as abort +from .helpers import flash as flash +from .helpers import get_flashed_messages as get_flashed_messages +from .helpers import get_template_attribute as get_template_attribute +from .helpers import make_response as make_response +from .helpers import redirect as redirect +from .helpers import send_file as send_file +from .helpers import send_from_directory as send_from_directory +from .helpers import stream_with_context as stream_with_context +from .helpers import url_for as url_for +from .json import jsonify as jsonify +from .signals import appcontext_popped as appcontext_popped +from .signals import appcontext_pushed as appcontext_pushed +from .signals import appcontext_tearing_down as appcontext_tearing_down +from .signals import before_render_template as before_render_template +from .signals import got_request_exception as got_request_exception +from .signals import message_flashed as message_flashed +from .signals import request_finished as request_finished +from .signals import request_started as request_started +from .signals import request_tearing_down as request_tearing_down +from .signals import signals_available as signals_available +from .signals import template_rendered as template_rendered +from .templating import render_template as render_template +from .templating import render_template_string as render_template_string +from .templating import stream_template as stream_template +from .templating import stream_template_string as stream_template_string + +__version__ = "2.2.3" + + +def __getattr__(name): + if name == "_app_ctx_stack": + import warnings + from .globals import __app_ctx_stack + + warnings.warn( + "'_app_ctx_stack' is deprecated and will be removed in Flask 2.3.", + DeprecationWarning, + stacklevel=2, + ) + return __app_ctx_stack + + if name == "_request_ctx_stack": + import warnings + from .globals import __request_ctx_stack + + warnings.warn( + "'_request_ctx_stack' is deprecated and will be removed in Flask 2.3.", + DeprecationWarning, + stacklevel=2, + ) + return __request_ctx_stack + + raise AttributeError(name) diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/flask/__main__.py b/docs/examples/flask/venv/lib/python3.11/site-packages/flask/__main__.py new file mode 100644 index 0000000..4e28416 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/flask/__main__.py @@ -0,0 +1,3 @@ +from .cli import main + +main() diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/flask/__pycache__/__init__.cpython-311.pyc b/docs/examples/flask/venv/lib/python3.11/site-packages/flask/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a1b5fbd68e6b65db1b286ad4e1ca7b0ef3814353 GIT binary patch literal 3383 zcmb7`%WvC85QkT`BucVn`6>A+$(C(den@^KDcYouG)Z4Tf;2f4*b704D~mN>T~dAo z2@n)L6utDA9(!quB0cx-V1phAp#=;SJ>=#hJ>}GywPc%?k)rhY=Z~|)(atP)!hC;|az&*4FxK|D9y+)XZ1xNIV5v5VV zQDBKmf+gS>jR}tFaU($!g5!Fh(NFsYC-kH-KnDc(0jFq6a6j-M9Tc1d9->2n2lQcM zgpLSK0guvA!GpkKbWHFNaGItC59=9YoQ?|~0iK`}f=7WT>7?K>;3+yKI1M~arv+z# zXXuRJao{Y?3Z4MY(VXB(;5^L>o&uhwvx2AfurWvH1kdONW1h|n&H^vc1;IJsMY+>sA-#n}On?y)bL%y9u@_n2c_rh|{g-`AVW=9-BE(HoXo*S3L%mAcEg>^3w< z<|hwM*DU)$t~EJlrrYt$HOq9_K0cpnD2}Yy_8Hd*y4o4excfRuAQ~PF;b%&xA0#=< zakS$Cl&Zxsd87`NGyq1onJbGMrpg8cD%;Efe;WNRGi+UPnXI@j*Q!kyi%c5I4ue^o zR2}0?)i|qR-nhd|Rjz9~MvffcaLbU711x?Z+%9K|A@6B!<4hG&lk0Nb;t(NWzqL9B z4?#`sp2)Uk+e|(0Y&s3$%$$jInF6b@EvwcZu3NgwATdtbRB<8NWx>eUwsqQjwqIkm zd)%p1yBD3_K(g_#9W^u9(MY$8~NJLyop>Dmtv`uA=EmRkUcRGne+< zL#?&K-kSrA}sfus~!9xvRU3NZ`JUn?pzUoUif|aJIE9A5KhD$ zaZ2)%+M^PkJPL`^Fl6*Rf|K!K=zEOry3~jpJ9VnjpQWb|_v|DVp6t1g*<%-EkLw{L z@~HPU*%SYnz4tzGlczIyNS;EK&rT}+GF0lj(;7J5PVPQ5M5*m?ER2CHyZ*LEs56x(qvlT|)#DyG{s z?lNvPT?Mz+T}7>^R?VrfeFY8|=2Yv)rx)KHY{_iys=SnG?%T}uu^y? zwc}(NHlHiYW&5DjC(Ce3;n@TYyaqGD!kk|~9ur^c^HRCzQqGfdek}8K3V;0Asu#4w z*x}serEhgBJuR!BpgrtgK{J!K?ZYPEPIG43(uv3Cl$`5T=!xDJ4t!( z^(hNpZ0@hb_;;hfCbGXJvR*j*5@t^Bl&-WAhWPq%lsed@7jYqwq zP{=1UUeLNdhh)+Vnon|G(0o$xg65M2FK9j~dqMNbycaZ|yy*qaC%3(z`DDurnoq8J zLGww)3z|=+yrB7{=mqT}Pj_A2p6eb7m``qbLAz+qC65HmCo5jieDbarw2M65Rdjc? zyFcBY4?PmFi>kQck%0N+4KHXHtzdUdpk2_ti&n6EhHrT!U_SZC3z{DcfcWm1!-28K QeMiwuD0f7Wq0h9Y?t*(T%LggM+F*h5g`GAuhIS3 zv4Fsn8z|Brj8=h*OfzY&ie!;iO1d?)mR*Fh95k~{ n?_68nDska@F+vy7cTcTSMEruo&;R`m**ts9pYr)%xjdr32h>iP literal 0 HcmV?d00001 diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/flask/__pycache__/app.cpython-311.pyc b/docs/examples/flask/venv/lib/python3.11/site-packages/flask/__pycache__/app.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4b8fcafec92caa0a634d5e0fc85ad606f716c6ca GIT binary patch literal 100961 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 literal 0 HcmV?d00001 diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/flask/__pycache__/blueprints.cpython-311.pyc b/docs/examples/flask/venv/lib/python3.11/site-packages/flask/__pycache__/blueprints.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..37fe8a51a055d8f1f1dac35ede3a21f8ade4f1d4 GIT binary patch literal 36887 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 literal 0 HcmV?d00001 diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/flask/__pycache__/cli.cpython-311.pyc b/docs/examples/flask/venv/lib/python3.11/site-packages/flask/__pycache__/cli.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8242ba91332e303446550769fd548c1ea9c13f49 GIT binary patch literal 44007 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 literal 0 HcmV?d00001 diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/flask/__pycache__/ctx.cpython-311.pyc b/docs/examples/flask/venv/lib/python3.11/site-packages/flask/__pycache__/ctx.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4398ecdd53582bfe6c816acc9475d123c684effb GIT binary patch literal 21055 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 literal 0 HcmV?d00001 diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/flask/__pycache__/debughelpers.cpython-311.pyc b/docs/examples/flask/venv/lib/python3.11/site-packages/flask/__pycache__/debughelpers.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bdf306373a0155868f158a41251d037ca2ee9567 GIT binary patch literal 9441 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 literal 0 HcmV?d00001 diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/flask/__pycache__/helpers.cpython-311.pyc b/docs/examples/flask/venv/lib/python3.11/site-packages/flask/__pycache__/helpers.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..696d4fd8a28db41cb1a0f4dde0b267de7d8d56fd GIT binary patch literal 31373 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 O3nO_6w{;z+C-}cM?OERd literal 0 HcmV?d00001 diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/flask/__pycache__/sessions.cpython-311.pyc b/docs/examples/flask/venv/lib/python3.11/site-packages/flask/__pycache__/sessions.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a6afc6b7f03e21890033775166c397ceca0029f6 GIT binary patch literal 18606 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 v4DQ*SingOO9enSF$$!b4|w zznS^wH{Z;B^P6w>AETpb1gZPg4E|#hq3>BGOtGMF@`i}eKEenK2C51vRuT=dAyp*- zVW}#cufK{&>@`H^pe|Qs5q*Sk>@LFbN6_63iv?Yj$ zNROb)*I`FgO_0PxnBVDyQ;!5d!sn-IYA7;2fJ_fXjtn42h9WZq$jngW=m2tbC~|B7 zIW`oT9YAJ>B69=C+)!kG0GS_(EDRtESXf5o;(ysjES3c?wWu|S-PCI24>xMT_V6qb zR8HQ7!agEEDvYWkPGFHpSR(T6ka|Fbsi-HGajYK4@w>5VoDY?x5;QJ)vIzru61Hky zoD!!+O>acKp{L|#fq&vWFCCCuB}Emn=i*+XzqA%zueA6 zK3?!OBWz*?5WDC<#n}Vr;qp7WaJ=MFlTBYU=ooZdh_!abWZ8asF`n z>D9wIUU~L0&xQ(r5N5vyUz8&5JOfn^cZ8~hM;LdoOk%g$_V7gH9{$9z81N^76M%{W z3QqRbV!bu!k-95aQ~y_wH1tT5^tMnPfiDB!XmyOFt68qgRr7yJV-XHZhEq!`01I!{ zl@&ub*9ld0TWOivrluR(ia{FTve>P5>QB4X*poRFuT zy%2R@{PCz!w(WiAm|ZH29Kkt;vx`)|<4oKUp%$HNjFto1i`=4@P*pgFs!I7`VOGF- zoX_l7=%((dYJ0pVf-HpE3BcOpAa+rwG_!Z@i<_OZvylCM4<61~Plg5BUVMFv<+W%M z(1(@T$vI){WcO(>EqVs(Pb8XwjGkp;21FQL{CPp>F>yQBJ13@L~tX_?0vAmgfHx0+3h!UpzHQduE2t!4Wg8nPAw7}e+w zgTj_7Y&(H}1TVl@dK|$TrH7^rX2v!}-D>C+JDL36^}Xx8GY^EK4ghnZq7G-(UIWc|sDbE2fw%M6+A`Y{tCj=i{mx{&M))<> zp46I6*eZu?J1|HOKHt;OY;nVVDy!Lqv9j<}W5caH&n|Bmyj=9+&Q23Xr-*xc%g69z&8U#{~`Hn|5xKOWxswb zAli38b|pa&I;h}=Z&yAq6gu)bxBvaFG~>z>@V@LM3SBfS^oUOB6=>+nVhsK{PK)PE z_u|Kf^Hw2WK*!$IA-}~ad-DEe7CSKWJy0_%M_RFL3 zcaF#3aZ$cAb?(93{W-V%!O_%*$5S7==uD?DyMxFpZyy)mc2TY~Ik*4$z0cj(e{(c>`FQfOi^e-8eeHPqnu{ho<1?q`@Dr+2bJ)%9W^H!u{3V%1e>-pB)m0WoBLzaEZoRco1O;1UQw7O~KHNe;^N|Jmk&k|m>Hs1JH6XNDpxZAlybBoH zpPqAvuc4%@6EtXgIdks0@7H&rKi{ez`~b?>&V42PR6vRYV^C7fZ-JA`yv6 zkT3%=n_wqxVH<;aHo&>Co#Gtej2lXc-bihC3O$@*|T#eIM`gc}$lGZT%aX)0bAp)L`aYzjBgTK+_EvN_xg zcsDb`RVyx6~hYN>Ta)C&VcZd{_?Vs8R`5ay|ggIK)1Z6?lR#Vn4 zHA^j0tL%{49&bFDS8I%yLNMkYsU6zvEsX zQm3?6Zr!o814@POum)ygUwNLbr41ZN;{vLPD;OF zpbIE)MxrwjacT;iBdW;nP0K1a_e7@^1$ru$Ix8gsRa0bfGIBGnjYp!Xq$c0O2F;^# zETzcMMH1!{(JG@il<;DiEJ1bq`Glz60EE996=ShfLNXkRtW7J)XzA22MiR&O;oktb zM>L#wF-(+kiTM=p&&%mKqS;He2>FJ3Oq|TIY4W~1O(x3Blx8LNS!kYS-fv1X6I`h$ z$hVpK6Xo6^kE3}k9E^ST>RCA^PA4>>v|^#SKf;ufipr{*Qu+kpVsb)`YN~L(xTn{j zbPEu!t56}HN?sSlq$FG)yWU5$M+|06@xUYuXR$)BL*X%P8ev?gjW zk1L|oFQuYtzkEvsN|M$77#>;wjGUb5PsB(2r*3QGspMeaz(Btm*W@EpV)TYMR<2jv zgLrbRZ|b(u8IjDrDN{xSCKf60ixPtA1>sfS1~5;4&y%{~(mN|nIbVnF>zF_Pz}LDI zS-F<;?bm(#=g;T&cFw<;clxr*l^;zQ`1 zL=lvNqli2_3dlVoQxy_sB_?c>;KU_P<|^kc*(FB|6b3M(Lvp~G^9s_(?7zI3Qf{b1 zB7Q>_L_v|qrW2wfT#6?r#A5>NSBWo$(P>Q>7ga%pBM?MMisMd;33}2ulBt^sSsIgt zR7@C8-4rN`98`tT#I!u6#FLtElMbV%1Z`Z@gt#U|#U#*{JOxY#c{1%MDMH`S1*EH* zYAKjkOa}Ih%22n+o~jU!2}v1gQ&eu76ld6gdK5pb%W$TqaDuAR0I88EDB5yB9zc?N zm;?aomr1iTjM9>3GLYULK838$%h|v;4DyXu<~rcha_?yp1I|pdpD=&UFu;dt5-Hb{ z2<$pLVmK0}+~bT;6vUAxN5&GVQ85vjz)g>srG_6RWR+Yr_(&w4jBAmIf=UfIg;V83 zj3yLRYb7pGP+=$s0L(U3weKr6KZJEJ0GKCvPhjOl*0Ud;yl`Mscz#28J}11O3ok5O zSRT%K_deiTbY93bW%&VkR$I&%U_Q{iz%B6$@Kf5MuU;q4505VrG2D^JWJ;P&AnuJs z-kTN^#S&*E0(=SCjAq!*ByZDcenw~)=Dd+6DPeZuG%wO=&cjskS72>+zCaMTIeW{> zg#v+Sjk#~9$a)uzb_M1r9CR6lw;ZUM2C0YlK?>J!kzaBEzi^UMwo5#iA)M@-KmkSr z7!=Nlk|{@IM@3Ov@|_c<5lE#*tje$eu5`gDp6`7WH;m%_-beAkD8BM;)s=UvYUd=c z?5{SsKsMPeJHkmm=%t?SH}WqH)jJG^WgXOLlhK-7V*uNERnDs4b+B;rx3J*W~B6Q*RdN+MMDvZ9*8#$-JdHao}|lnBWA z4^ge%GbI6FW-(LtI%JjdbD!& zo$8`&Y(4kZm6y+49En^z_X;4_&P86neCF)Mkqf;X-5Z`M3BG`jAb8}HeDgyv4V$yI|dYRp~%T8CSJcI3r0DvrV5O4j`(@Uqb z?Zg+)hl`{gnX*w?-FExN z&pFfFJezjTammI5L_>lohAyJe+vl9~ZIa|}l3TX7$xSA0+vP|dX?xlkvsq`wkfoO; z2JW@oqBtWEZG)liVZ zmwGj9nlfWJR3(}kolpd9(Taf532Fu~TFqcG^l`|VeR3BvtESvCLB9Yk)PDm&DXyt` zfiLjH*HEw%mlv?k1DhR3H#&~4wdOib>K!Kw#OXRl!Q#lm`Q`Hk*6s`CgYBEa?u}q~ z<|k{zx!`kp@VR9cWX1BSd}z<|(4$8W0tbM8eW41-2iule{1t4F|LD=9EpO9Dmp{C` z@v0chb2G}tI-pecqzDg*KMP)j4g5l1LT z*ycFk2<~w?BFZu#pttb?cxB!uONUB)P<>IW9roSiN(FPRp>+MJQaqubjzWWSg z{}o{XD)lfKi{XuDcmUpqKL_vvk^@i>RN8Zzr_3X_03eHkpQyW!l zj55y+Q*Bi3zOe4^99(iLdt12*(Oo_WmnuBwuiGn zLC?NylF(T_`FNv4>lVzcW*#>0{5XC!9fH)2vFA0%Oq^LrYOCTH0;-=!6Rhwm#KR| zOc?wS*!tpVLN>)T>SzT8ui?5NCuK#{Qi_5kOhviDy$lDT;NB{S5#Z(5UIVU9e$%>^P0LT<)co<-oFdcOnzGo65ZR$?V#` z`-k*{&t)5)(_POO>`=Ktte~#d-b*{e7(0Uak9P!>3d9PY;)@+&j2%Jz$2)>b1!4tF zZhKF@wY^}27&~ZRe)aB~E8*2pcJH7bd}>WvKbsA{pu5f#>`D?zD2?8>h?jq`U}ST{Q@08yGcK*?3lA@ zXf75*MTVy7rD}(MZE%5PM?Pas1#}e}_S%gWQ;d`aNXf;m;PnvQMPTHD>_8{Cg7@+g zAK^AB*ax*kM&%C=EsC>i% z9|R-9<%YaQH&+nL{sm$5wm_Yl_{o7f8ph(_g)TYQ``py+d2&ir#L03a%Zg75*T5NC z?HDa>j6nUOc$$5}#Te}fk4*$$t_mJjRiHyXNk@&v709E#Kr8tc*0>o@B!p2Je5=@p zESX-7;#`VMV~#!}DshyMDh@d*qNjL9j8ma0#7+N6X;NkzoDp;-5={F}|X(>XJ58zeN>;hkW z6Y(_b?%tgHurEGsTm(lA_tlJpE!1(Ues7 z&6iHQiUg|(`G19Ylpo{i{|NxYgKLVIODNICD3(&8wHs|T*U(0(gBY~@D|l7BrUQx8 z?MNa$8~&cv=A8ei?mxOKh3u?T{vG=LfPBDwz}_Q^Bm=4KVX?-0vzumFB#X@A4w^+` zwJNK$@~KZNJDW+hyO~`e@KuS4*(BQ?@0<<1Ft!@O$Z6inEET`Sj;*nD!6pXpOfNU0 zya7$91s1KeB?Jx8L3kgcUHJ=}dC^;BGmzRI7Hf>;bMV)PpV;84$Nm}j4V%I)GTYT6 zy~Mo^W5CxDe$2GqWUA_vw`i_ub5p9J!UUu^nV+9G4Y)WYX4{ZT;2Uw@vx!txOsLQI zmHJtQV)Z2ekId^?KG41y=-LQ$W!}sMj_83SS$>D-0pF(c9V_bUTkAK$1yc5f01gio zGw>zuP3V?tX*y5SP~|UzyNX($uV5)EIt7miWNVq?O$NY;o2nak{`&Rn#j7XiZgVlp z4aomHc-6lK0GD@qxwP|-t-uQ$YaeEQ0k-Hb!4^eBc9{7VkSFfOh1SK;QfU6g-#ZBJ zM+5kY2RS1E=5qzxAHiNl=6`Nra>ih%N zE7}Zuc2o$DXEXeOsW-eM^?pZl!9vzxqJA5ariWHd)+9B*cgy0B*FxIxeo&*+GUc(GD<`p#8lnDf=#S~;#1$U{& z|MCa^S@TZ^&nvf~6lk5OA<*yOuV$D$Y0Q?MEwVRjeez@=Ykl&hJ!^gP_=# z>ysx!*81#}v%OVMmRO!VIh?gVdD5M=K3g{K&S_A=%z-=?%37a1w?Aur3Jxz*x7zSU z^O|k#)%&lnx%FQ7A^>@{pu^38cWOIW?*A?3{;pCKdWm7c9kVlB3ICRs{I0tEUDnRj zEkpMZWrk_A_TK%O_2^dv|9pIXLO*el=6xNdQ7x$VGKcf+dpPT6V|=n@MX(a zfv?8D+N(cvSwD6qd*F)R{%XMn^|t)=OVb5AVDOpN;VL*0gXLC(4i|Gc1Kx6o)&}k$ zr^xzM8r6c!HJK<&9ok+nBRSU!l zy6Ycv1yHZJ0>E$u&SI}lOhd2Lpc|Hj>ww5y2O#UOei{DS_GP4Y&DJfT#^?V4eZouF literal 0 HcmV?d00001 diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/flask/__pycache__/testing.cpython-311.pyc b/docs/examples/flask/venv/lib/python3.11/site-packages/flask/__pycache__/testing.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b07c46a6e5714852c2debd269aa4ec885bf136b9 GIT binary patch literal 14695 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 P^Zlpv^{YdY*o^)ku=pAu literal 0 HcmV?d00001 diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/flask/__pycache__/typing.cpython-311.pyc b/docs/examples/flask/venv/lib/python3.11/site-packages/flask/__pycache__/typing.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..24f0fb08309bb2d9fd12605ee0da0cc1c1a39ef2 GIT binary patch literal 3408 zcmb7FJ8&9j5Wa&SfgT8u(2LlTY{!<7YzUH-hn_(Xzhv2pMC=Jo=EVJAA z7cNw|P{B-;85I;xLMAFGQmAl|GUcC{zhJKNOsaIu*p;j7f$t@oOoEP^z1?rWZ+HJa z^t0F7#=xic<0<}!c82*&d(c0v?~K=5g)iunT+Dsgv0;UGvV%1MOOC>y`TY1+}+fT3wzZ%<+><5IAf-N&l zhHMqHTOa8>xIe?<{u5Q%qwLlx6K&8l)*P#yJl#wy0~>5b0?ts%Xx$k)Jo_JX2Ak_Z z#2~=EhH0z0xq7qg5A#?%N)ElpI&93Y>04xDI0FpkTVa`yt*WzTEg81?RGa5O;HE4v6X>ufhXg4$2NiU{}FfL1a8E}a?u)vUzc#kVV_fS)P}HbjOEP%j4^5) zHcaL{wy_gzMsM(b_1Y@C1jrAbFwDe;v@+J`Wt>>Eown&*=GhG<-2S(=fVab}>btjb z?M6JG7qTf%$%;8}xvyi6$C3=DXC2FVF(+fm1^9@B;4V4grh4_rqQJ=-)U_!Va(p zq(Vw5NLa3GsR1LpBXRjWR8_A;b$y+KVpS*LL3vlfvWjkHWkq$ybGxd06|hV0jsV=AunLHz0qpJ=h?sy3pkg;YR9_?H2ltNPEot{e%CRSmup1a3Fzl8 zCrR+Xm{C1<^4bJBK@Gq_???+El$RiR1@Ee!MNSa7P0(zO?{HbAZU*CNsBc}9l@$XJ zTEJ;h!dJ7BtelMXG-4;+Xepag)YBXHu^=ppIR!sf)+8~7Wm%LAUhe`Ykml%iKhT+Iqlxshr3uLntoy+lpr2_A|Sb)Oc4fz-5HJk|ZHXpq&ZsVwg z)7b~nMJ_KZVh%^YDsVZau)T(*?SfL@`ZbP^@?uJk;>R3}0L#%dto4KFBb<8_6|$Sr z{H}6e%uPgMv8bF?@Z~(0dcb82Tw~=U`Q5jknQf6T2zXAKgaTOj$~*AF)SN8KR+%oM zU$30#*?f(0vSU>gA@-|^qQrhx(I~NBwH7AORdiQTXBEM{wTfKNuI|O2Pd;0H1?G*t z)PAfuxwl2pfEK#BCqKK>FbWj))O>DN-y3IZ!12^u3o$D=LTnLTn> zoRy7=hhF@G^nO7*r=GThor;5B9i$*_9ItM8lYLGsBzOyeMtdPDD>bpo$xaM-W z#@;xQ-|@7g*2(ns7c*59(6HC`Ru5)K#|T9i^~VX)p)+aGiBbpYxJ1$ADiqp^v_f0@ z9E9nv^)i9bK3i-(h*m7B;vK>SsvrebLdW~wz3)n1;=4>y z#B{JP+RLkSc#8O@D4K?2am~JQI{Se>So1K0Lx=b3*ga|g%HCs&`VY<=j+WNTfuoNq z2^yXv=cj0BnxN@7PRAf5gV zcFz;vJVjq>>I*E$4**jS$e%g;qOCk$et7h#k~)sPoUCloXq=42>F@$!PFK5w#m^d@ zu*J56S?UiH-)MP>_&%X1_I@CKv~rB-rCH*erD#qQ`%)7Nf|wt~K0g0qs=Qe_^V@~v z?qlvHJ`U0GB@$Vp=avaGT!pSerA27Gr*xAJL3dpSXdW3*=+R=zqkeE6gsJDRGbj^>Z!FK_;?(3x9gboCE~jBe1O1Yu6q{7nA< zki}k1mPW|f9638j`{sf2l|P{Ed3}dTaD@2u)q$Pfj~7?S@B|HBA-*dVO=?Yk20dzf Lt^G+8@B;n=)prU- literal 0 HcmV?d00001 diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/flask/__pycache__/views.cpython-311.pyc b/docs/examples/flask/venv/lib/python3.11/site-packages/flask/__pycache__/views.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ab9caa8b3fd291862542f00cba2eb1097d9a0a76 GIT binary patch literal 7703 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 Jc4*>D{WIrgtOfu8 literal 0 HcmV?d00001 diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/flask/__pycache__/wrappers.cpython-311.pyc b/docs/examples/flask/venv/lib/python3.11/site-packages/flask/__pycache__/wrappers.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7634f621bc37a25a878256d9ea846cae8f580ae2 GIT binary patch literal 6933 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 vMH$= (3, 8): + iscoroutinefunction = inspect.iscoroutinefunction +else: + + def iscoroutinefunction(func: t.Any) -> bool: + while inspect.ismethod(func): + func = func.__func__ + + while isinstance(func, functools.partial): + func = func.func + + return inspect.iscoroutinefunction(func) + + +def _make_timedelta(value: t.Union[timedelta, int, None]) -> t.Optional[timedelta]: + if value is None or isinstance(value, timedelta): + return value + + return timedelta(seconds=value) + + +class Flask(Scaffold): + """The flask object implements a WSGI application and acts as the central + object. It is passed the name of the module or package of the + application. Once it is created it will act as a central registry for + the view functions, the URL rules, template configuration and much more. + + The name of the package is used to resolve resources from inside the + package or the folder the module is contained in depending on if the + package parameter resolves to an actual python package (a folder with + an :file:`__init__.py` file inside) or a standard module (just a ``.py`` file). + + For more information about resource loading, see :func:`open_resource`. + + Usually you create a :class:`Flask` instance in your main module or + in the :file:`__init__.py` file of your package like this:: + + from flask import Flask + app = Flask(__name__) + + .. admonition:: About the First Parameter + + The idea of the first parameter is to give Flask an idea of what + belongs to your application. This name is used to find resources + on the filesystem, can be used by extensions to improve debugging + information and a lot more. + + So it's important what you provide there. If you are using a single + module, `__name__` is always the correct value. If you however are + using a package, it's usually recommended to hardcode the name of + your package there. + + For example if your application is defined in :file:`yourapplication/app.py` + you should create it with one of the two versions below:: + + app = Flask('yourapplication') + app = Flask(__name__.split('.')[0]) + + Why is that? The application will work even with `__name__`, thanks + to how resources are looked up. However it will make debugging more + painful. Certain extensions can make assumptions based on the + import name of your application. For example the Flask-SQLAlchemy + extension will look for the code in your application that triggered + an SQL query in debug mode. If the import name is not properly set + up, that debugging information is lost. (For example it would only + pick up SQL queries in `yourapplication.app` and not + `yourapplication.views.frontend`) + + .. versionadded:: 0.7 + The `static_url_path`, `static_folder`, and `template_folder` + parameters were added. + + .. versionadded:: 0.8 + The `instance_path` and `instance_relative_config` parameters were + added. + + .. versionadded:: 0.11 + The `root_path` parameter was added. + + .. versionadded:: 1.0 + The ``host_matching`` and ``static_host`` parameters were added. + + .. versionadded:: 1.0 + The ``subdomain_matching`` parameter was added. Subdomain + matching needs to be enabled manually now. Setting + :data:`SERVER_NAME` does not implicitly enable it. + + :param import_name: the name of the application package + :param static_url_path: can be used to specify a different path for the + static files on the web. Defaults to the name + of the `static_folder` folder. + :param static_folder: The folder with static files that is served at + ``static_url_path``. Relative to the application ``root_path`` + or an absolute path. Defaults to ``'static'``. + :param static_host: the host to use when adding the static route. + Defaults to None. Required when using ``host_matching=True`` + with a ``static_folder`` configured. + :param host_matching: set ``url_map.host_matching`` attribute. + Defaults to False. + :param subdomain_matching: consider the subdomain relative to + :data:`SERVER_NAME` when matching routes. Defaults to False. + :param template_folder: the folder that contains the templates that should + be used by the application. Defaults to + ``'templates'`` folder in the root path of the + application. + :param instance_path: An alternative instance path for the application. + By default the folder ``'instance'`` next to the + package or module is assumed to be the instance + path. + :param instance_relative_config: if set to ``True`` relative filenames + for loading the config are assumed to + be relative to the instance path instead + of the application root. + :param root_path: The path to the root of the application files. + This should only be set manually when it can't be detected + automatically, such as for namespace packages. + """ + + #: The class that is used for request objects. See :class:`~flask.Request` + #: for more information. + request_class = Request + + #: The class that is used for response objects. See + #: :class:`~flask.Response` for more information. + response_class = Response + + #: The class of the object assigned to :attr:`aborter`, created by + #: :meth:`create_aborter`. That object is called by + #: :func:`flask.abort` to raise HTTP errors, and can be + #: called directly as well. + #: + #: Defaults to :class:`werkzeug.exceptions.Aborter`. + #: + #: .. versionadded:: 2.2 + aborter_class = Aborter + + #: The class that is used for the Jinja environment. + #: + #: .. versionadded:: 0.11 + jinja_environment = Environment + + #: The class that is used for the :data:`~flask.g` instance. + #: + #: Example use cases for a custom class: + #: + #: 1. Store arbitrary attributes on flask.g. + #: 2. Add a property for lazy per-request database connectors. + #: 3. Return None instead of AttributeError on unexpected attributes. + #: 4. Raise exception if an unexpected attr is set, a "controlled" flask.g. + #: + #: In Flask 0.9 this property was called `request_globals_class` but it + #: was changed in 0.10 to :attr:`app_ctx_globals_class` because the + #: flask.g object is now application context scoped. + #: + #: .. versionadded:: 0.10 + app_ctx_globals_class = _AppCtxGlobals + + #: The class that is used for the ``config`` attribute of this app. + #: Defaults to :class:`~flask.Config`. + #: + #: Example use cases for a custom class: + #: + #: 1. Default values for certain config options. + #: 2. Access to config values through attributes in addition to keys. + #: + #: .. versionadded:: 0.11 + config_class = Config + + #: The testing flag. Set this to ``True`` to enable the test mode of + #: Flask extensions (and in the future probably also Flask itself). + #: For example this might activate test helpers that have an + #: additional runtime cost which should not be enabled by default. + #: + #: If this is enabled and PROPAGATE_EXCEPTIONS is not changed from the + #: default it's implicitly enabled. + #: + #: This attribute can also be configured from the config with the + #: ``TESTING`` configuration key. Defaults to ``False``. + testing = ConfigAttribute("TESTING") + + #: If a secret key is set, cryptographic components can use this to + #: sign cookies and other things. Set this to a complex random value + #: when you want to use the secure cookie for instance. + #: + #: This attribute can also be configured from the config with the + #: :data:`SECRET_KEY` configuration key. Defaults to ``None``. + secret_key = ConfigAttribute("SECRET_KEY") + + @property + def session_cookie_name(self) -> str: + """The name of the cookie set by the session interface. + + .. deprecated:: 2.2 + Will be removed in Flask 2.3. Use ``app.config["SESSION_COOKIE_NAME"]`` + instead. + """ + import warnings + + warnings.warn( + "'session_cookie_name' is deprecated and will be removed in Flask 2.3. Use" + " 'SESSION_COOKIE_NAME' in 'app.config' instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.config["SESSION_COOKIE_NAME"] + + @session_cookie_name.setter + def session_cookie_name(self, value: str) -> None: + import warnings + + warnings.warn( + "'session_cookie_name' is deprecated and will be removed in Flask 2.3. Use" + " 'SESSION_COOKIE_NAME' in 'app.config' instead.", + DeprecationWarning, + stacklevel=2, + ) + self.config["SESSION_COOKIE_NAME"] = value + + #: A :class:`~datetime.timedelta` which is used to set the expiration + #: date of a permanent session. The default is 31 days which makes a + #: permanent session survive for roughly one month. + #: + #: This attribute can also be configured from the config with the + #: ``PERMANENT_SESSION_LIFETIME`` configuration key. Defaults to + #: ``timedelta(days=31)`` + permanent_session_lifetime = ConfigAttribute( + "PERMANENT_SESSION_LIFETIME", get_converter=_make_timedelta + ) + + @property + def send_file_max_age_default(self) -> t.Optional[timedelta]: + """The default value for ``max_age`` for :func:`~flask.send_file`. The default + is ``None``, which tells the browser to use conditional requests instead of a + timed cache. + + .. deprecated:: 2.2 + Will be removed in Flask 2.3. Use + ``app.config["SEND_FILE_MAX_AGE_DEFAULT"]`` instead. + + .. versionchanged:: 2.0 + Defaults to ``None`` instead of 12 hours. + """ + import warnings + + warnings.warn( + "'send_file_max_age_default' is deprecated and will be removed in Flask" + " 2.3. Use 'SEND_FILE_MAX_AGE_DEFAULT' in 'app.config' instead.", + DeprecationWarning, + stacklevel=2, + ) + return _make_timedelta(self.config["SEND_FILE_MAX_AGE_DEFAULT"]) + + @send_file_max_age_default.setter + def send_file_max_age_default(self, value: t.Union[int, timedelta, None]) -> None: + import warnings + + warnings.warn( + "'send_file_max_age_default' is deprecated and will be removed in Flask" + " 2.3. Use 'SEND_FILE_MAX_AGE_DEFAULT' in 'app.config' instead.", + DeprecationWarning, + stacklevel=2, + ) + self.config["SEND_FILE_MAX_AGE_DEFAULT"] = _make_timedelta(value) + + @property + def use_x_sendfile(self) -> bool: + """Enable this to use the ``X-Sendfile`` feature, assuming the server supports + it, from :func:`~flask.send_file`. + + .. deprecated:: 2.2 + Will be removed in Flask 2.3. Use ``app.config["USE_X_SENDFILE"]`` instead. + """ + import warnings + + warnings.warn( + "'use_x_sendfile' is deprecated and will be removed in Flask 2.3. Use" + " 'USE_X_SENDFILE' in 'app.config' instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.config["USE_X_SENDFILE"] + + @use_x_sendfile.setter + def use_x_sendfile(self, value: bool) -> None: + import warnings + + warnings.warn( + "'use_x_sendfile' is deprecated and will be removed in Flask 2.3. Use" + " 'USE_X_SENDFILE' in 'app.config' instead.", + DeprecationWarning, + stacklevel=2, + ) + self.config["USE_X_SENDFILE"] = value + + _json_encoder: t.Union[t.Type[json.JSONEncoder], None] = None + _json_decoder: t.Union[t.Type[json.JSONDecoder], None] = None + + @property # type: ignore[override] + def json_encoder(self) -> t.Type[json.JSONEncoder]: + """The JSON encoder class to use. Defaults to + :class:`~flask.json.JSONEncoder`. + + .. deprecated:: 2.2 + Will be removed in Flask 2.3. Customize + :attr:`json_provider_class` instead. + + .. versionadded:: 0.10 + """ + import warnings + + warnings.warn( + "'app.json_encoder' is deprecated and will be removed in Flask 2.3." + " Customize 'app.json_provider_class' or 'app.json' instead.", + DeprecationWarning, + stacklevel=2, + ) + + if self._json_encoder is None: + from . import json + + return json.JSONEncoder + + return self._json_encoder + + @json_encoder.setter + def json_encoder(self, value: t.Type[json.JSONEncoder]) -> None: + import warnings + + warnings.warn( + "'app.json_encoder' is deprecated and will be removed in Flask 2.3." + " Customize 'app.json_provider_class' or 'app.json' instead.", + DeprecationWarning, + stacklevel=2, + ) + self._json_encoder = value + + @property # type: ignore[override] + def json_decoder(self) -> t.Type[json.JSONDecoder]: + """The JSON decoder class to use. Defaults to + :class:`~flask.json.JSONDecoder`. + + .. deprecated:: 2.2 + Will be removed in Flask 2.3. Customize + :attr:`json_provider_class` instead. + + .. versionadded:: 0.10 + """ + import warnings + + warnings.warn( + "'app.json_decoder' is deprecated and will be removed in Flask 2.3." + " Customize 'app.json_provider_class' or 'app.json' instead.", + DeprecationWarning, + stacklevel=2, + ) + + if self._json_decoder is None: + from . import json + + return json.JSONDecoder + + return self._json_decoder + + @json_decoder.setter + def json_decoder(self, value: t.Type[json.JSONDecoder]) -> None: + import warnings + + warnings.warn( + "'app.json_decoder' is deprecated and will be removed in Flask 2.3." + " Customize 'app.json_provider_class' or 'app.json' instead.", + DeprecationWarning, + stacklevel=2, + ) + self._json_decoder = value + + json_provider_class: t.Type[JSONProvider] = DefaultJSONProvider + """A subclass of :class:`~flask.json.provider.JSONProvider`. An + instance is created and assigned to :attr:`app.json` when creating + the app. + + The default, :class:`~flask.json.provider.DefaultJSONProvider`, uses + Python's built-in :mod:`json` library. A different provider can use + a different JSON library. + + .. versionadded:: 2.2 + """ + + #: Options that are passed to the Jinja environment in + #: :meth:`create_jinja_environment`. Changing these options after + #: the environment is created (accessing :attr:`jinja_env`) will + #: have no effect. + #: + #: .. versionchanged:: 1.1.0 + #: This is a ``dict`` instead of an ``ImmutableDict`` to allow + #: easier configuration. + #: + jinja_options: dict = {} + + #: Default configuration parameters. + default_config = ImmutableDict( + { + "ENV": None, + "DEBUG": None, + "TESTING": False, + "PROPAGATE_EXCEPTIONS": None, + "SECRET_KEY": None, + "PERMANENT_SESSION_LIFETIME": timedelta(days=31), + "USE_X_SENDFILE": False, + "SERVER_NAME": None, + "APPLICATION_ROOT": "/", + "SESSION_COOKIE_NAME": "session", + "SESSION_COOKIE_DOMAIN": None, + "SESSION_COOKIE_PATH": None, + "SESSION_COOKIE_HTTPONLY": True, + "SESSION_COOKIE_SECURE": False, + "SESSION_COOKIE_SAMESITE": None, + "SESSION_REFRESH_EACH_REQUEST": True, + "MAX_CONTENT_LENGTH": None, + "SEND_FILE_MAX_AGE_DEFAULT": None, + "TRAP_BAD_REQUEST_ERRORS": None, + "TRAP_HTTP_EXCEPTIONS": False, + "EXPLAIN_TEMPLATE_LOADING": False, + "PREFERRED_URL_SCHEME": "http", + "JSON_AS_ASCII": None, + "JSON_SORT_KEYS": None, + "JSONIFY_PRETTYPRINT_REGULAR": None, + "JSONIFY_MIMETYPE": None, + "TEMPLATES_AUTO_RELOAD": None, + "MAX_COOKIE_SIZE": 4093, + } + ) + + #: The rule object to use for URL rules created. This is used by + #: :meth:`add_url_rule`. Defaults to :class:`werkzeug.routing.Rule`. + #: + #: .. versionadded:: 0.7 + url_rule_class = Rule + + #: The map object to use for storing the URL rules and routing + #: configuration parameters. Defaults to :class:`werkzeug.routing.Map`. + #: + #: .. versionadded:: 1.1.0 + url_map_class = Map + + #: The :meth:`test_client` method creates an instance of this test + #: client class. Defaults to :class:`~flask.testing.FlaskClient`. + #: + #: .. versionadded:: 0.7 + test_client_class: t.Optional[t.Type["FlaskClient"]] = None + + #: The :class:`~click.testing.CliRunner` subclass, by default + #: :class:`~flask.testing.FlaskCliRunner` that is used by + #: :meth:`test_cli_runner`. Its ``__init__`` method should take a + #: Flask app object as the first argument. + #: + #: .. versionadded:: 1.0 + test_cli_runner_class: t.Optional[t.Type["FlaskCliRunner"]] = None + + #: the session interface to use. By default an instance of + #: :class:`~flask.sessions.SecureCookieSessionInterface` is used here. + #: + #: .. versionadded:: 0.8 + session_interface: SessionInterface = SecureCookieSessionInterface() + + def __init__( + self, + import_name: str, + static_url_path: t.Optional[str] = None, + static_folder: t.Optional[t.Union[str, os.PathLike]] = "static", + static_host: t.Optional[str] = None, + host_matching: bool = False, + subdomain_matching: bool = False, + template_folder: t.Optional[t.Union[str, os.PathLike]] = "templates", + instance_path: t.Optional[str] = None, + instance_relative_config: bool = False, + root_path: t.Optional[str] = None, + ): + super().__init__( + import_name=import_name, + static_folder=static_folder, + static_url_path=static_url_path, + template_folder=template_folder, + root_path=root_path, + ) + + if instance_path is None: + instance_path = self.auto_find_instance_path() + elif not os.path.isabs(instance_path): + raise ValueError( + "If an instance path is provided it must be absolute." + " A relative path was given instead." + ) + + #: Holds the path to the instance folder. + #: + #: .. versionadded:: 0.8 + self.instance_path = instance_path + + #: The configuration dictionary as :class:`Config`. This behaves + #: exactly like a regular dictionary but supports additional methods + #: to load a config from files. + self.config = self.make_config(instance_relative_config) + + #: An instance of :attr:`aborter_class` created by + #: :meth:`make_aborter`. This is called by :func:`flask.abort` + #: to raise HTTP errors, and can be called directly as well. + #: + #: .. versionadded:: 2.2 + #: Moved from ``flask.abort``, which calls this object. + self.aborter = self.make_aborter() + + self.json: JSONProvider = self.json_provider_class(self) + """Provides access to JSON methods. Functions in ``flask.json`` + will call methods on this provider when the application context + is active. Used for handling JSON requests and responses. + + An instance of :attr:`json_provider_class`. Can be customized by + changing that attribute on a subclass, or by assigning to this + attribute afterwards. + + The default, :class:`~flask.json.provider.DefaultJSONProvider`, + uses Python's built-in :mod:`json` library. A different provider + can use a different JSON library. + + .. versionadded:: 2.2 + """ + + #: A list of functions that are called by + #: :meth:`handle_url_build_error` when :meth:`.url_for` raises a + #: :exc:`~werkzeug.routing.BuildError`. Each function is called + #: with ``error``, ``endpoint`` and ``values``. If a function + #: returns ``None`` or raises a ``BuildError``, it is skipped. + #: Otherwise, its return value is returned by ``url_for``. + #: + #: .. versionadded:: 0.9 + self.url_build_error_handlers: t.List[ + t.Callable[[Exception, str, t.Dict[str, t.Any]], str] + ] = [] + + #: A list of functions that will be called at the beginning of the + #: first request to this instance. To register a function, use the + #: :meth:`before_first_request` decorator. + #: + #: .. deprecated:: 2.2 + #: Will be removed in Flask 2.3. Run setup code when + #: creating the application instead. + #: + #: .. versionadded:: 0.8 + self.before_first_request_funcs: t.List[ft.BeforeFirstRequestCallable] = [] + + #: A list of functions that are called when the application context + #: is destroyed. Since the application context is also torn down + #: if the request ends this is the place to store code that disconnects + #: from databases. + #: + #: .. versionadded:: 0.9 + self.teardown_appcontext_funcs: t.List[ft.TeardownCallable] = [] + + #: A list of shell context processor functions that should be run + #: when a shell context is created. + #: + #: .. versionadded:: 0.11 + self.shell_context_processors: t.List[ft.ShellContextProcessorCallable] = [] + + #: Maps registered blueprint names to blueprint objects. The + #: dict retains the order the blueprints were registered in. + #: Blueprints can be registered multiple times, this dict does + #: not track how often they were attached. + #: + #: .. versionadded:: 0.7 + self.blueprints: t.Dict[str, "Blueprint"] = {} + + #: a place where extensions can store application specific state. For + #: example this is where an extension could store database engines and + #: similar things. + #: + #: The key must match the name of the extension module. For example in + #: case of a "Flask-Foo" extension in `flask_foo`, the key would be + #: ``'foo'``. + #: + #: .. versionadded:: 0.7 + self.extensions: dict = {} + + #: The :class:`~werkzeug.routing.Map` for this instance. You can use + #: this to change the routing converters after the class was created + #: but before any routes are connected. Example:: + #: + #: from werkzeug.routing import BaseConverter + #: + #: class ListConverter(BaseConverter): + #: def to_python(self, value): + #: return value.split(',') + #: def to_url(self, values): + #: return ','.join(super(ListConverter, self).to_url(value) + #: for value in values) + #: + #: app = Flask(__name__) + #: app.url_map.converters['list'] = ListConverter + self.url_map = self.url_map_class() + + self.url_map.host_matching = host_matching + self.subdomain_matching = subdomain_matching + + # tracks internally if the application already handled at least one + # request. + self._got_first_request = False + self._before_request_lock = Lock() + + # Add a static route using the provided static_url_path, static_host, + # and static_folder if there is a configured static_folder. + # Note we do this without checking if static_folder exists. + # For one, it might be created while the server is running (e.g. during + # development). Also, Google App Engine stores static files somewhere + if self.has_static_folder: + assert ( + bool(static_host) == host_matching + ), "Invalid static_host/host_matching combination" + # Use a weakref to avoid creating a reference cycle between the app + # and the view function (see #3761). + self_ref = weakref.ref(self) + self.add_url_rule( + f"{self.static_url_path}/", + endpoint="static", + host=static_host, + view_func=lambda **kw: self_ref().send_static_file(**kw), # type: ignore # noqa: B950 + ) + + # Set the name of the Click group in case someone wants to add + # the app's commands to another CLI tool. + self.cli.name = self.name + + def _check_setup_finished(self, f_name: str) -> None: + if self._got_first_request: + raise AssertionError( + f"The setup method '{f_name}' can no longer be called" + " on the application. It has already handled its first" + " request, any changes will not be applied" + " consistently.\n" + "Make sure all imports, decorators, functions, etc." + " needed to set up the application are done before" + " running it." + ) + + @locked_cached_property + def name(self) -> str: # type: ignore + """The name of the application. This is usually the import name + with the difference that it's guessed from the run file if the + import name is main. This name is used as a display name when + Flask needs the name of the application. It can be set and overridden + to change the value. + + .. versionadded:: 0.8 + """ + if self.import_name == "__main__": + fn = getattr(sys.modules["__main__"], "__file__", None) + if fn is None: + return "__main__" + return os.path.splitext(os.path.basename(fn))[0] + return self.import_name + + @property + def propagate_exceptions(self) -> bool: + """Returns the value of the ``PROPAGATE_EXCEPTIONS`` configuration + value in case it's set, otherwise a sensible default is returned. + + .. deprecated:: 2.2 + Will be removed in Flask 2.3. + + .. versionadded:: 0.7 + """ + import warnings + + warnings.warn( + "'propagate_exceptions' is deprecated and will be removed in Flask 2.3.", + DeprecationWarning, + stacklevel=2, + ) + rv = self.config["PROPAGATE_EXCEPTIONS"] + if rv is not None: + return rv + return self.testing or self.debug + + @locked_cached_property + def logger(self) -> logging.Logger: + """A standard Python :class:`~logging.Logger` for the app, with + the same name as :attr:`name`. + + In debug mode, the logger's :attr:`~logging.Logger.level` will + be set to :data:`~logging.DEBUG`. + + If there are no handlers configured, a default handler will be + added. See :doc:`/logging` for more information. + + .. versionchanged:: 1.1.0 + The logger takes the same name as :attr:`name` rather than + hard-coding ``"flask.app"``. + + .. versionchanged:: 1.0.0 + Behavior was simplified. The logger is always named + ``"flask.app"``. The level is only set during configuration, + it doesn't check ``app.debug`` each time. Only one format is + used, not different ones depending on ``app.debug``. No + handlers are removed, and a handler is only added if no + handlers are already configured. + + .. versionadded:: 0.3 + """ + return create_logger(self) + + @locked_cached_property + def jinja_env(self) -> Environment: + """The Jinja environment used to load templates. + + The environment is created the first time this property is + accessed. Changing :attr:`jinja_options` after that will have no + effect. + """ + return self.create_jinja_environment() + + @property + def got_first_request(self) -> bool: + """This attribute is set to ``True`` if the application started + handling the first request. + + .. versionadded:: 0.8 + """ + return self._got_first_request + + def make_config(self, instance_relative: bool = False) -> Config: + """Used to create the config attribute by the Flask constructor. + The `instance_relative` parameter is passed in from the constructor + of Flask (there named `instance_relative_config`) and indicates if + the config should be relative to the instance path or the root path + of the application. + + .. versionadded:: 0.8 + """ + root_path = self.root_path + if instance_relative: + root_path = self.instance_path + defaults = dict(self.default_config) + defaults["ENV"] = os.environ.get("FLASK_ENV") or "production" + defaults["DEBUG"] = get_debug_flag() + return self.config_class(root_path, defaults) + + def make_aborter(self) -> Aborter: + """Create the object to assign to :attr:`aborter`. That object + is called by :func:`flask.abort` to raise HTTP errors, and can + be called directly as well. + + By default, this creates an instance of :attr:`aborter_class`, + which defaults to :class:`werkzeug.exceptions.Aborter`. + + .. versionadded:: 2.2 + """ + return self.aborter_class() + + def auto_find_instance_path(self) -> str: + """Tries to locate the instance path if it was not provided to the + constructor of the application class. It will basically calculate + the path to a folder named ``instance`` next to your main file or + the package. + + .. versionadded:: 0.8 + """ + prefix, package_path = find_package(self.import_name) + if prefix is None: + return os.path.join(package_path, "instance") + return os.path.join(prefix, "var", f"{self.name}-instance") + + def open_instance_resource(self, resource: str, mode: str = "rb") -> t.IO[t.AnyStr]: + """Opens a resource from the application's instance folder + (:attr:`instance_path`). Otherwise works like + :meth:`open_resource`. Instance resources can also be opened for + writing. + + :param resource: the name of the resource. To access resources within + subfolders use forward slashes as separator. + :param mode: resource file opening mode, default is 'rb'. + """ + return open(os.path.join(self.instance_path, resource), mode) + + @property + def templates_auto_reload(self) -> bool: + """Reload templates when they are changed. Used by + :meth:`create_jinja_environment`. It is enabled by default in debug mode. + + .. deprecated:: 2.2 + Will be removed in Flask 2.3. Use ``app.config["TEMPLATES_AUTO_RELOAD"]`` + instead. + + .. versionadded:: 1.0 + This property was added but the underlying config and behavior + already existed. + """ + import warnings + + warnings.warn( + "'templates_auto_reload' is deprecated and will be removed in Flask 2.3." + " Use 'TEMPLATES_AUTO_RELOAD' in 'app.config' instead.", + DeprecationWarning, + stacklevel=2, + ) + rv = self.config["TEMPLATES_AUTO_RELOAD"] + return rv if rv is not None else self.debug + + @templates_auto_reload.setter + def templates_auto_reload(self, value: bool) -> None: + import warnings + + warnings.warn( + "'templates_auto_reload' is deprecated and will be removed in Flask 2.3." + " Use 'TEMPLATES_AUTO_RELOAD' in 'app.config' instead.", + DeprecationWarning, + stacklevel=2, + ) + self.config["TEMPLATES_AUTO_RELOAD"] = value + + def create_jinja_environment(self) -> Environment: + """Create the Jinja environment based on :attr:`jinja_options` + and the various Jinja-related methods of the app. Changing + :attr:`jinja_options` after this will have no effect. Also adds + Flask-related globals and filters to the environment. + + .. versionchanged:: 0.11 + ``Environment.auto_reload`` set in accordance with + ``TEMPLATES_AUTO_RELOAD`` configuration option. + + .. versionadded:: 0.5 + """ + options = dict(self.jinja_options) + + if "autoescape" not in options: + options["autoescape"] = self.select_jinja_autoescape + + if "auto_reload" not in options: + auto_reload = self.config["TEMPLATES_AUTO_RELOAD"] + + if auto_reload is None: + auto_reload = self.debug + + options["auto_reload"] = auto_reload + + rv = self.jinja_environment(self, **options) + rv.globals.update( + url_for=self.url_for, + get_flashed_messages=get_flashed_messages, + config=self.config, + # request, session and g are normally added with the + # context processor for efficiency reasons but for imported + # templates we also want the proxies in there. + request=request, + session=session, + g=g, + ) + rv.policies["json.dumps_function"] = self.json.dumps + return rv + + def create_global_jinja_loader(self) -> DispatchingJinjaLoader: + """Creates the loader for the Jinja2 environment. Can be used to + override just the loader and keeping the rest unchanged. It's + discouraged to override this function. Instead one should override + the :meth:`jinja_loader` function instead. + + The global loader dispatches between the loaders of the application + and the individual blueprints. + + .. versionadded:: 0.7 + """ + return DispatchingJinjaLoader(self) + + def select_jinja_autoescape(self, filename: str) -> bool: + """Returns ``True`` if autoescaping should be active for the given + template name. If no template name is given, returns `True`. + + .. versionchanged:: 2.2 + Autoescaping is now enabled by default for ``.svg`` files. + + .. versionadded:: 0.5 + """ + if filename is None: + return True + return filename.endswith((".html", ".htm", ".xml", ".xhtml", ".svg")) + + def update_template_context(self, context: dict) -> None: + """Update the template context with some commonly used variables. + This injects request, session, config and g into the template + context as well as everything template context processors want + to inject. Note that the as of Flask 0.6, the original values + in the context will not be overridden if a context processor + decides to return a value with the same key. + + :param context: the context as a dictionary that is updated in place + to add extra variables. + """ + names: t.Iterable[t.Optional[str]] = (None,) + + # A template may be rendered outside a request context. + if request: + names = chain(names, reversed(request.blueprints)) + + # The values passed to render_template take precedence. Keep a + # copy to re-apply after all context functions. + orig_ctx = context.copy() + + for name in names: + if name in self.template_context_processors: + for func in self.template_context_processors[name]: + context.update(func()) + + context.update(orig_ctx) + + def make_shell_context(self) -> dict: + """Returns the shell context for an interactive shell for this + application. This runs all the registered shell context + processors. + + .. versionadded:: 0.11 + """ + rv = {"app": self, "g": g} + for processor in self.shell_context_processors: + rv.update(processor()) + return rv + + @property + def env(self) -> str: + """What environment the app is running in. This maps to the :data:`ENV` config + key. + + **Do not enable development when deploying in production.** + + Default: ``'production'`` + + .. deprecated:: 2.2 + Will be removed in Flask 2.3. + """ + import warnings + + warnings.warn( + "'app.env' is deprecated and will be removed in Flask 2.3." + " Use 'app.debug' instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.config["ENV"] + + @env.setter + def env(self, value: str) -> None: + import warnings + + warnings.warn( + "'app.env' is deprecated and will be removed in Flask 2.3." + " Use 'app.debug' instead.", + DeprecationWarning, + stacklevel=2, + ) + self.config["ENV"] = value + + @property + def debug(self) -> bool: + """Whether debug mode is enabled. When using ``flask run`` to start the + development server, an interactive debugger will be shown for unhandled + exceptions, and the server will be reloaded when code changes. This maps to the + :data:`DEBUG` config key. It may not behave as expected if set late. + + **Do not enable debug mode when deploying in production.** + + Default: ``False`` + """ + return self.config["DEBUG"] + + @debug.setter + def debug(self, value: bool) -> None: + self.config["DEBUG"] = value + + if self.config["TEMPLATES_AUTO_RELOAD"] is None: + self.jinja_env.auto_reload = value + + def run( + self, + host: t.Optional[str] = None, + port: t.Optional[int] = None, + debug: t.Optional[bool] = None, + load_dotenv: bool = True, + **options: t.Any, + ) -> None: + """Runs the application on a local development server. + + Do not use ``run()`` in a production setting. It is not intended to + meet security and performance requirements for a production server. + Instead, see :doc:`/deploying/index` for WSGI server recommendations. + + If the :attr:`debug` flag is set the server will automatically reload + for code changes and show a debugger in case an exception happened. + + If you want to run the application in debug mode, but disable the + code execution on the interactive debugger, you can pass + ``use_evalex=False`` as parameter. This will keep the debugger's + traceback screen active, but disable code execution. + + It is not recommended to use this function for development with + automatic reloading as this is badly supported. Instead you should + be using the :command:`flask` command line script's ``run`` support. + + .. admonition:: Keep in Mind + + Flask will suppress any server error with a generic error page + unless it is in debug mode. As such to enable just the + interactive debugger without the code reloading, you have to + invoke :meth:`run` with ``debug=True`` and ``use_reloader=False``. + Setting ``use_debugger`` to ``True`` without being in debug mode + won't catch any exceptions because there won't be any to + catch. + + :param host: the hostname to listen on. Set this to ``'0.0.0.0'`` to + have the server available externally as well. Defaults to + ``'127.0.0.1'`` or the host in the ``SERVER_NAME`` config variable + if present. + :param port: the port of the webserver. Defaults to ``5000`` or the + port defined in the ``SERVER_NAME`` config variable if present. + :param debug: if given, enable or disable debug mode. See + :attr:`debug`. + :param load_dotenv: Load the nearest :file:`.env` and :file:`.flaskenv` + files to set environment variables. Will also change the working + directory to the directory containing the first file found. + :param options: the options to be forwarded to the underlying Werkzeug + server. See :func:`werkzeug.serving.run_simple` for more + information. + + .. versionchanged:: 1.0 + If installed, python-dotenv will be used to load environment + variables from :file:`.env` and :file:`.flaskenv` files. + + The :envvar:`FLASK_DEBUG` environment variable will override :attr:`debug`. + + Threaded mode is enabled by default. + + .. versionchanged:: 0.10 + The default port is now picked from the ``SERVER_NAME`` + variable. + """ + # Ignore this call so that it doesn't start another server if + # the 'flask run' command is used. + if os.environ.get("FLASK_RUN_FROM_CLI") == "true": + if not is_running_from_reloader(): + click.secho( + " * Ignoring a call to 'app.run()' that would block" + " the current 'flask' CLI command.\n" + " Only call 'app.run()' in an 'if __name__ ==" + ' "__main__"\' guard.', + fg="red", + ) + + return + + if get_load_dotenv(load_dotenv): + cli.load_dotenv() + + # if set, let env vars override previous values + if "FLASK_ENV" in os.environ: + print( + "'FLASK_ENV' is deprecated and will not be used in" + " Flask 2.3. Use 'FLASK_DEBUG' instead.", + file=sys.stderr, + ) + self.config["ENV"] = os.environ.get("FLASK_ENV") or "production" + self.debug = get_debug_flag() + elif "FLASK_DEBUG" in os.environ: + self.debug = get_debug_flag() + + # debug passed to method overrides all other sources + if debug is not None: + self.debug = bool(debug) + + server_name = self.config.get("SERVER_NAME") + sn_host = sn_port = None + + if server_name: + sn_host, _, sn_port = server_name.partition(":") + + if not host: + if sn_host: + host = sn_host + else: + host = "127.0.0.1" + + if port or port == 0: + port = int(port) + elif sn_port: + port = int(sn_port) + else: + port = 5000 + + options.setdefault("use_reloader", self.debug) + options.setdefault("use_debugger", self.debug) + options.setdefault("threaded", True) + + cli.show_server_banner(self.debug, self.name) + + from werkzeug.serving import run_simple + + try: + run_simple(t.cast(str, host), port, self, **options) + finally: + # reset the first request information if the development server + # reset normally. This makes it possible to restart the server + # without reloader and that stuff from an interactive shell. + self._got_first_request = False + + def test_client(self, use_cookies: bool = True, **kwargs: t.Any) -> "FlaskClient": + """Creates a test client for this application. For information + about unit testing head over to :doc:`/testing`. + + Note that if you are testing for assertions or exceptions in your + application code, you must set ``app.testing = True`` in order for the + exceptions to propagate to the test client. Otherwise, the exception + will be handled by the application (not visible to the test client) and + the only indication of an AssertionError or other exception will be a + 500 status code response to the test client. See the :attr:`testing` + attribute. For example:: + + app.testing = True + client = app.test_client() + + The test client can be used in a ``with`` block to defer the closing down + of the context until the end of the ``with`` block. This is useful if + you want to access the context locals for testing:: + + with app.test_client() as c: + rv = c.get('/?vodka=42') + assert request.args['vodka'] == '42' + + Additionally, you may pass optional keyword arguments that will then + be passed to the application's :attr:`test_client_class` constructor. + For example:: + + from flask.testing import FlaskClient + + class CustomClient(FlaskClient): + def __init__(self, *args, **kwargs): + self._authentication = kwargs.pop("authentication") + super(CustomClient,self).__init__( *args, **kwargs) + + app.test_client_class = CustomClient + client = app.test_client(authentication='Basic ....') + + See :class:`~flask.testing.FlaskClient` for more information. + + .. versionchanged:: 0.4 + added support for ``with`` block usage for the client. + + .. versionadded:: 0.7 + The `use_cookies` parameter was added as well as the ability + to override the client to be used by setting the + :attr:`test_client_class` attribute. + + .. versionchanged:: 0.11 + Added `**kwargs` to support passing additional keyword arguments to + the constructor of :attr:`test_client_class`. + """ + cls = self.test_client_class + if cls is None: + from .testing import FlaskClient as cls # type: ignore + return cls( # type: ignore + self, self.response_class, use_cookies=use_cookies, **kwargs + ) + + def test_cli_runner(self, **kwargs: t.Any) -> "FlaskCliRunner": + """Create a CLI runner for testing CLI commands. + See :ref:`testing-cli`. + + Returns an instance of :attr:`test_cli_runner_class`, by default + :class:`~flask.testing.FlaskCliRunner`. The Flask app object is + passed as the first argument. + + .. versionadded:: 1.0 + """ + cls = self.test_cli_runner_class + + if cls is None: + from .testing import FlaskCliRunner as cls # type: ignore + + return cls(self, **kwargs) # type: ignore + + @setupmethod + def register_blueprint(self, blueprint: "Blueprint", **options: t.Any) -> None: + """Register a :class:`~flask.Blueprint` on the application. Keyword + arguments passed to this method will override the defaults set on the + blueprint. + + Calls the blueprint's :meth:`~flask.Blueprint.register` method after + recording the blueprint in the application's :attr:`blueprints`. + + :param blueprint: The blueprint to register. + :param url_prefix: Blueprint routes will be prefixed with this. + :param subdomain: Blueprint routes will match on this subdomain. + :param url_defaults: Blueprint routes will use these default values for + view arguments. + :param options: Additional keyword arguments are passed to + :class:`~flask.blueprints.BlueprintSetupState`. They can be + accessed in :meth:`~flask.Blueprint.record` callbacks. + + .. versionchanged:: 2.0.1 + The ``name`` option can be used to change the (pre-dotted) + name the blueprint is registered with. This allows the same + blueprint to be registered multiple times with unique names + for ``url_for``. + + .. versionadded:: 0.7 + """ + blueprint.register(self, options) + + def iter_blueprints(self) -> t.ValuesView["Blueprint"]: + """Iterates over all blueprints by the order they were registered. + + .. versionadded:: 0.11 + """ + return self.blueprints.values() + + @setupmethod + def add_url_rule( + self, + rule: str, + endpoint: t.Optional[str] = None, + view_func: t.Optional[ft.RouteCallable] = None, + provide_automatic_options: t.Optional[bool] = None, + **options: t.Any, + ) -> None: + if endpoint is None: + endpoint = _endpoint_from_view_func(view_func) # type: ignore + options["endpoint"] = endpoint + methods = options.pop("methods", None) + + # if the methods are not given and the view_func object knows its + # methods we can use that instead. If neither exists, we go with + # a tuple of only ``GET`` as default. + if methods is None: + methods = getattr(view_func, "methods", None) or ("GET",) + if isinstance(methods, str): + raise TypeError( + "Allowed methods must be a list of strings, for" + ' example: @app.route(..., methods=["POST"])' + ) + methods = {item.upper() for item in methods} + + # Methods that should always be added + required_methods = set(getattr(view_func, "required_methods", ())) + + # starting with Flask 0.8 the view_func object can disable and + # force-enable the automatic options handling. + if provide_automatic_options is None: + provide_automatic_options = getattr( + view_func, "provide_automatic_options", None + ) + + if provide_automatic_options is None: + if "OPTIONS" not in methods: + provide_automatic_options = True + required_methods.add("OPTIONS") + else: + provide_automatic_options = False + + # Add the required methods now. + methods |= required_methods + + rule = self.url_rule_class(rule, methods=methods, **options) + rule.provide_automatic_options = provide_automatic_options # type: ignore + + self.url_map.add(rule) + if view_func is not None: + old_func = self.view_functions.get(endpoint) + if old_func is not None and old_func != view_func: + raise AssertionError( + "View function mapping is overwriting an existing" + f" endpoint function: {endpoint}" + ) + self.view_functions[endpoint] = view_func + + @setupmethod + def template_filter( + self, name: t.Optional[str] = None + ) -> t.Callable[[T_template_filter], T_template_filter]: + """A decorator that is used to register custom template filter. + You can specify a name for the filter, otherwise the function + name will be used. Example:: + + @app.template_filter() + def reverse(s): + return s[::-1] + + :param name: the optional name of the filter, otherwise the + function name will be used. + """ + + def decorator(f: T_template_filter) -> T_template_filter: + self.add_template_filter(f, name=name) + return f + + return decorator + + @setupmethod + def add_template_filter( + self, f: ft.TemplateFilterCallable, name: t.Optional[str] = None + ) -> None: + """Register a custom template filter. Works exactly like the + :meth:`template_filter` decorator. + + :param name: the optional name of the filter, otherwise the + function name will be used. + """ + self.jinja_env.filters[name or f.__name__] = f + + @setupmethod + def template_test( + self, name: t.Optional[str] = None + ) -> t.Callable[[T_template_test], T_template_test]: + """A decorator that is used to register custom template test. + You can specify a name for the test, otherwise the function + name will be used. Example:: + + @app.template_test() + def is_prime(n): + if n == 2: + return True + for i in range(2, int(math.ceil(math.sqrt(n))) + 1): + if n % i == 0: + return False + return True + + .. versionadded:: 0.10 + + :param name: the optional name of the test, otherwise the + function name will be used. + """ + + def decorator(f: T_template_test) -> T_template_test: + self.add_template_test(f, name=name) + return f + + return decorator + + @setupmethod + def add_template_test( + self, f: ft.TemplateTestCallable, name: t.Optional[str] = None + ) -> None: + """Register a custom template test. Works exactly like the + :meth:`template_test` decorator. + + .. versionadded:: 0.10 + + :param name: the optional name of the test, otherwise the + function name will be used. + """ + self.jinja_env.tests[name or f.__name__] = f + + @setupmethod + def template_global( + self, name: t.Optional[str] = None + ) -> t.Callable[[T_template_global], T_template_global]: + """A decorator that is used to register a custom template global function. + You can specify a name for the global function, otherwise the function + name will be used. Example:: + + @app.template_global() + def double(n): + return 2 * n + + .. versionadded:: 0.10 + + :param name: the optional name of the global function, otherwise the + function name will be used. + """ + + def decorator(f: T_template_global) -> T_template_global: + self.add_template_global(f, name=name) + return f + + return decorator + + @setupmethod + def add_template_global( + self, f: ft.TemplateGlobalCallable, name: t.Optional[str] = None + ) -> None: + """Register a custom template global function. Works exactly like the + :meth:`template_global` decorator. + + .. versionadded:: 0.10 + + :param name: the optional name of the global function, otherwise the + function name will be used. + """ + self.jinja_env.globals[name or f.__name__] = f + + @setupmethod + def before_first_request(self, f: T_before_first_request) -> T_before_first_request: + """Registers a function to be run before the first request to this + instance of the application. + + The function will be called without any arguments and its return + value is ignored. + + .. deprecated:: 2.2 + Will be removed in Flask 2.3. Run setup code when creating + the application instead. + + .. versionadded:: 0.8 + """ + import warnings + + warnings.warn( + "'before_first_request' is deprecated and will be removed" + " in Flask 2.3. Run setup code while creating the" + " application instead.", + DeprecationWarning, + stacklevel=2, + ) + self.before_first_request_funcs.append(f) + return f + + @setupmethod + def teardown_appcontext(self, f: T_teardown) -> T_teardown: + """Registers a function to be called when the application + context is popped. The application context is typically popped + after the request context for each request, at the end of CLI + commands, or after a manually pushed context ends. + + .. code-block:: python + + with app.app_context(): + ... + + When the ``with`` block exits (or ``ctx.pop()`` is called), the + teardown functions are called just before the app context is + made inactive. Since a request context typically also manages an + application context it would also be called when you pop a + request context. + + When a teardown function was called because of an unhandled + exception it will be passed an error object. If an + :meth:`errorhandler` is registered, it will handle the exception + and the teardown will not receive it. + + Teardown functions must avoid raising exceptions. If they + execute code that might fail they must surround that code with a + ``try``/``except`` block and log any errors. + + The return values of teardown functions are ignored. + + .. versionadded:: 0.9 + """ + self.teardown_appcontext_funcs.append(f) + return f + + @setupmethod + def shell_context_processor( + self, f: T_shell_context_processor + ) -> T_shell_context_processor: + """Registers a shell context processor function. + + .. versionadded:: 0.11 + """ + self.shell_context_processors.append(f) + return f + + def _find_error_handler(self, e: Exception) -> t.Optional[ft.ErrorHandlerCallable]: + """Return a registered error handler for an exception in this order: + blueprint handler for a specific code, app handler for a specific code, + blueprint handler for an exception class, app handler for an exception + class, or ``None`` if a suitable handler is not found. + """ + exc_class, code = self._get_exc_class_and_code(type(e)) + names = (*request.blueprints, None) + + for c in (code, None) if code is not None else (None,): + for name in names: + handler_map = self.error_handler_spec[name][c] + + if not handler_map: + continue + + for cls in exc_class.__mro__: + handler = handler_map.get(cls) + + if handler is not None: + return handler + return None + + def handle_http_exception( + self, e: HTTPException + ) -> t.Union[HTTPException, ft.ResponseReturnValue]: + """Handles an HTTP exception. By default this will invoke the + registered error handlers and fall back to returning the + exception as response. + + .. versionchanged:: 1.0.3 + ``RoutingException``, used internally for actions such as + slash redirects during routing, is not passed to error + handlers. + + .. versionchanged:: 1.0 + Exceptions are looked up by code *and* by MRO, so + ``HTTPException`` subclasses can be handled with a catch-all + handler for the base ``HTTPException``. + + .. versionadded:: 0.3 + """ + # Proxy exceptions don't have error codes. We want to always return + # those unchanged as errors + if e.code is None: + return e + + # RoutingExceptions are used internally to trigger routing + # actions, such as slash redirects raising RequestRedirect. They + # are not raised or handled in user code. + if isinstance(e, RoutingException): + return e + + handler = self._find_error_handler(e) + if handler is None: + return e + return self.ensure_sync(handler)(e) + + def trap_http_exception(self, e: Exception) -> bool: + """Checks if an HTTP exception should be trapped or not. By default + this will return ``False`` for all exceptions except for a bad request + key error if ``TRAP_BAD_REQUEST_ERRORS`` is set to ``True``. It + also returns ``True`` if ``TRAP_HTTP_EXCEPTIONS`` is set to ``True``. + + This is called for all HTTP exceptions raised by a view function. + If it returns ``True`` for any exception the error handler for this + exception is not called and it shows up as regular exception in the + traceback. This is helpful for debugging implicitly raised HTTP + exceptions. + + .. versionchanged:: 1.0 + Bad request errors are not trapped by default in debug mode. + + .. versionadded:: 0.8 + """ + if self.config["TRAP_HTTP_EXCEPTIONS"]: + return True + + trap_bad_request = self.config["TRAP_BAD_REQUEST_ERRORS"] + + # if unset, trap key errors in debug mode + if ( + trap_bad_request is None + and self.debug + and isinstance(e, BadRequestKeyError) + ): + return True + + if trap_bad_request: + return isinstance(e, BadRequest) + + return False + + def handle_user_exception( + self, e: Exception + ) -> t.Union[HTTPException, ft.ResponseReturnValue]: + """This method is called whenever an exception occurs that + should be handled. A special case is :class:`~werkzeug + .exceptions.HTTPException` which is forwarded to the + :meth:`handle_http_exception` method. This function will either + return a response value or reraise the exception with the same + traceback. + + .. versionchanged:: 1.0 + Key errors raised from request data like ``form`` show the + bad key in debug mode rather than a generic bad request + message. + + .. versionadded:: 0.7 + """ + if isinstance(e, BadRequestKeyError) and ( + self.debug or self.config["TRAP_BAD_REQUEST_ERRORS"] + ): + e.show_exception = True + + if isinstance(e, HTTPException) and not self.trap_http_exception(e): + return self.handle_http_exception(e) + + handler = self._find_error_handler(e) + + if handler is None: + raise + + return self.ensure_sync(handler)(e) + + def handle_exception(self, e: Exception) -> Response: + """Handle an exception that did not have an error handler + associated with it, or that was raised from an error handler. + This always causes a 500 ``InternalServerError``. + + Always sends the :data:`got_request_exception` signal. + + If :attr:`propagate_exceptions` is ``True``, such as in debug + mode, the error will be re-raised so that the debugger can + display it. Otherwise, the original exception is logged, and + an :exc:`~werkzeug.exceptions.InternalServerError` is returned. + + If an error handler is registered for ``InternalServerError`` or + ``500``, it will be used. For consistency, the handler will + always receive the ``InternalServerError``. The original + unhandled exception is available as ``e.original_exception``. + + .. versionchanged:: 1.1.0 + Always passes the ``InternalServerError`` instance to the + handler, setting ``original_exception`` to the unhandled + error. + + .. versionchanged:: 1.1.0 + ``after_request`` functions and other finalization is done + even for the default 500 response when there is no handler. + + .. versionadded:: 0.3 + """ + exc_info = sys.exc_info() + got_request_exception.send(self, exception=e) + propagate = self.config["PROPAGATE_EXCEPTIONS"] + + if propagate is None: + propagate = self.testing or self.debug + + if propagate: + # Re-raise if called with an active exception, otherwise + # raise the passed in exception. + if exc_info[1] is e: + raise + + raise e + + self.log_exception(exc_info) + server_error: t.Union[InternalServerError, ft.ResponseReturnValue] + server_error = InternalServerError(original_exception=e) + handler = self._find_error_handler(server_error) + + if handler is not None: + server_error = self.ensure_sync(handler)(server_error) + + return self.finalize_request(server_error, from_error_handler=True) + + def log_exception( + self, + exc_info: t.Union[ + t.Tuple[type, BaseException, TracebackType], t.Tuple[None, None, None] + ], + ) -> None: + """Logs an exception. This is called by :meth:`handle_exception` + if debugging is disabled and right before the handler is called. + The default implementation logs the exception as error on the + :attr:`logger`. + + .. versionadded:: 0.8 + """ + self.logger.error( + f"Exception on {request.path} [{request.method}]", exc_info=exc_info + ) + + def raise_routing_exception(self, request: Request) -> "te.NoReturn": + """Intercept routing exceptions and possibly do something else. + + In debug mode, intercept a routing redirect and replace it with + an error if the body will be discarded. + + With modern Werkzeug this shouldn't occur, since it now uses a + 308 status which tells the browser to resend the method and + body. + + .. versionchanged:: 2.1 + Don't intercept 307 and 308 redirects. + + :meta private: + :internal: + """ + if ( + not self.debug + or not isinstance(request.routing_exception, RequestRedirect) + or request.routing_exception.code in {307, 308} + or request.method in {"GET", "HEAD", "OPTIONS"} + ): + raise request.routing_exception # type: ignore + + from .debughelpers import FormDataRoutingRedirect + + raise FormDataRoutingRedirect(request) + + def dispatch_request(self) -> ft.ResponseReturnValue: + """Does the request dispatching. Matches the URL and returns the + return value of the view or error handler. This does not have to + be a response object. In order to convert the return value to a + proper response object, call :func:`make_response`. + + .. versionchanged:: 0.7 + This no longer does the exception handling, this code was + moved to the new :meth:`full_dispatch_request`. + """ + req = request_ctx.request + if req.routing_exception is not None: + self.raise_routing_exception(req) + rule: Rule = req.url_rule # type: ignore[assignment] + # if we provide automatic options for this URL and the + # request came with the OPTIONS method, reply automatically + if ( + getattr(rule, "provide_automatic_options", False) + and req.method == "OPTIONS" + ): + return self.make_default_options_response() + # otherwise dispatch to the handler for that endpoint + view_args: t.Dict[str, t.Any] = req.view_args # type: ignore[assignment] + return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) + + def full_dispatch_request(self) -> Response: + """Dispatches the request and on top of that performs request + pre and postprocessing as well as HTTP exception catching and + error handling. + + .. versionadded:: 0.7 + """ + # Run before_first_request functions if this is the thread's first request. + # Inlined to avoid a method call on subsequent requests. + # This is deprecated, will be removed in Flask 2.3. + if not self._got_first_request: + with self._before_request_lock: + if not self._got_first_request: + for func in self.before_first_request_funcs: + self.ensure_sync(func)() + + self._got_first_request = True + + try: + request_started.send(self) + rv = self.preprocess_request() + if rv is None: + rv = self.dispatch_request() + except Exception as e: + rv = self.handle_user_exception(e) + return self.finalize_request(rv) + + def finalize_request( + self, + rv: t.Union[ft.ResponseReturnValue, HTTPException], + from_error_handler: bool = False, + ) -> Response: + """Given the return value from a view function this finalizes + the request by converting it into a response and invoking the + postprocessing functions. This is invoked for both normal + request dispatching as well as error handlers. + + Because this means that it might be called as a result of a + failure a special safe mode is available which can be enabled + with the `from_error_handler` flag. If enabled, failures in + response processing will be logged and otherwise ignored. + + :internal: + """ + response = self.make_response(rv) + try: + response = self.process_response(response) + request_finished.send(self, response=response) + except Exception: + if not from_error_handler: + raise + self.logger.exception( + "Request finalizing failed with an error while handling an error" + ) + return response + + def make_default_options_response(self) -> Response: + """This method is called to create the default ``OPTIONS`` response. + This can be changed through subclassing to change the default + behavior of ``OPTIONS`` responses. + + .. versionadded:: 0.7 + """ + adapter = request_ctx.url_adapter + methods = adapter.allowed_methods() # type: ignore[union-attr] + rv = self.response_class() + rv.allow.update(methods) + return rv + + def should_ignore_error(self, error: t.Optional[BaseException]) -> bool: + """This is called to figure out if an error should be ignored + or not as far as the teardown system is concerned. If this + function returns ``True`` then the teardown handlers will not be + passed the error. + + .. versionadded:: 0.10 + """ + return False + + def ensure_sync(self, func: t.Callable) -> t.Callable: + """Ensure that the function is synchronous for WSGI workers. + Plain ``def`` functions are returned as-is. ``async def`` + functions are wrapped to run and wait for the response. + + Override this method to change how the app runs async views. + + .. versionadded:: 2.0 + """ + if iscoroutinefunction(func): + return self.async_to_sync(func) + + return func + + def async_to_sync( + self, func: t.Callable[..., t.Coroutine] + ) -> t.Callable[..., t.Any]: + """Return a sync function that will run the coroutine function. + + .. code-block:: python + + result = app.async_to_sync(func)(*args, **kwargs) + + Override this method to change how the app converts async code + to be synchronously callable. + + .. versionadded:: 2.0 + """ + try: + from asgiref.sync import async_to_sync as asgiref_async_to_sync + except ImportError: + raise RuntimeError( + "Install Flask with the 'async' extra in order to use async views." + ) from None + + return asgiref_async_to_sync(func) + + def url_for( + self, + endpoint: str, + *, + _anchor: t.Optional[str] = None, + _method: t.Optional[str] = None, + _scheme: t.Optional[str] = None, + _external: t.Optional[bool] = None, + **values: t.Any, + ) -> str: + """Generate a URL to the given endpoint with the given values. + + This is called by :func:`flask.url_for`, and can be called + directly as well. + + An *endpoint* is the name of a URL rule, usually added with + :meth:`@app.route() `, and usually the same name as the + view function. A route defined in a :class:`~flask.Blueprint` + will prepend the blueprint's name separated by a ``.`` to the + endpoint. + + In some cases, such as email messages, you want URLs to include + the scheme and domain, like ``https://example.com/hello``. When + not in an active request, URLs will be external by default, but + this requires setting :data:`SERVER_NAME` so Flask knows what + domain to use. :data:`APPLICATION_ROOT` and + :data:`PREFERRED_URL_SCHEME` should also be configured as + needed. This config is only used when not in an active request. + + Functions can be decorated with :meth:`url_defaults` to modify + keyword arguments before the URL is built. + + If building fails for some reason, such as an unknown endpoint + or incorrect values, the app's :meth:`handle_url_build_error` + method is called. If that returns a string, that is returned, + otherwise a :exc:`~werkzeug.routing.BuildError` is raised. + + :param endpoint: The endpoint name associated with the URL to + generate. If this starts with a ``.``, the current blueprint + name (if any) will be used. + :param _anchor: If given, append this as ``#anchor`` to the URL. + :param _method: If given, generate the URL associated with this + method for the endpoint. + :param _scheme: If given, the URL will have this scheme if it + is external. + :param _external: If given, prefer the URL to be internal + (False) or require it to be external (True). External URLs + include the scheme and domain. When not in an active + request, URLs are external by default. + :param values: Values to use for the variable parts of the URL + rule. Unknown keys are appended as query string arguments, + like ``?a=b&c=d``. + + .. versionadded:: 2.2 + Moved from ``flask.url_for``, which calls this method. + """ + req_ctx = _cv_request.get(None) + + if req_ctx is not None: + url_adapter = req_ctx.url_adapter + blueprint_name = req_ctx.request.blueprint + + # If the endpoint starts with "." and the request matches a + # blueprint, the endpoint is relative to the blueprint. + if endpoint[:1] == ".": + if blueprint_name is not None: + endpoint = f"{blueprint_name}{endpoint}" + else: + endpoint = endpoint[1:] + + # When in a request, generate a URL without scheme and + # domain by default, unless a scheme is given. + if _external is None: + _external = _scheme is not None + else: + app_ctx = _cv_app.get(None) + + # If called by helpers.url_for, an app context is active, + # use its url_adapter. Otherwise, app.url_for was called + # directly, build an adapter. + if app_ctx is not None: + url_adapter = app_ctx.url_adapter + else: + url_adapter = self.create_url_adapter(None) + + if url_adapter is None: + raise RuntimeError( + "Unable to build URLs outside an active request" + " without 'SERVER_NAME' configured. Also configure" + " 'APPLICATION_ROOT' and 'PREFERRED_URL_SCHEME' as" + " needed." + ) + + # When outside a request, generate a URL with scheme and + # domain by default. + if _external is None: + _external = True + + # It is an error to set _scheme when _external=False, in order + # to avoid accidental insecure URLs. + if _scheme is not None and not _external: + raise ValueError("When specifying '_scheme', '_external' must be True.") + + self.inject_url_defaults(endpoint, values) + + try: + rv = url_adapter.build( # type: ignore[union-attr] + endpoint, + values, + method=_method, + url_scheme=_scheme, + force_external=_external, + ) + except BuildError as error: + values.update( + _anchor=_anchor, _method=_method, _scheme=_scheme, _external=_external + ) + return self.handle_url_build_error(error, endpoint, values) + + if _anchor is not None: + rv = f"{rv}#{url_quote(_anchor)}" + + return rv + + def redirect(self, location: str, code: int = 302) -> BaseResponse: + """Create a redirect response object. + + This is called by :func:`flask.redirect`, and can be called + directly as well. + + :param location: The URL to redirect to. + :param code: The status code for the redirect. + + .. versionadded:: 2.2 + Moved from ``flask.redirect``, which calls this method. + """ + return _wz_redirect(location, code=code, Response=self.response_class) + + def make_response(self, rv: ft.ResponseReturnValue) -> Response: + """Convert the return value from a view function to an instance of + :attr:`response_class`. + + :param rv: the return value from the view function. The view function + must return a response. Returning ``None``, or the view ending + without returning, is not allowed. The following types are allowed + for ``view_rv``: + + ``str`` + A response object is created with the string encoded to UTF-8 + as the body. + + ``bytes`` + A response object is created with the bytes as the body. + + ``dict`` + A dictionary that will be jsonify'd before being returned. + + ``list`` + A list that will be jsonify'd before being returned. + + ``generator`` or ``iterator`` + A generator that returns ``str`` or ``bytes`` to be + streamed as the response. + + ``tuple`` + Either ``(body, status, headers)``, ``(body, status)``, or + ``(body, headers)``, where ``body`` is any of the other types + allowed here, ``status`` is a string or an integer, and + ``headers`` is a dictionary or a list of ``(key, value)`` + tuples. If ``body`` is a :attr:`response_class` instance, + ``status`` overwrites the exiting value and ``headers`` are + extended. + + :attr:`response_class` + The object is returned unchanged. + + other :class:`~werkzeug.wrappers.Response` class + The object is coerced to :attr:`response_class`. + + :func:`callable` + The function is called as a WSGI application. The result is + used to create a response object. + + .. versionchanged:: 2.2 + A generator will be converted to a streaming response. + A list will be converted to a JSON response. + + .. versionchanged:: 1.1 + A dict will be converted to a JSON response. + + .. versionchanged:: 0.9 + Previously a tuple was interpreted as the arguments for the + response object. + """ + + status = headers = None + + # unpack tuple returns + if isinstance(rv, tuple): + len_rv = len(rv) + + # a 3-tuple is unpacked directly + if len_rv == 3: + rv, status, headers = rv # type: ignore[misc] + # decide if a 2-tuple has status or headers + elif len_rv == 2: + if isinstance(rv[1], (Headers, dict, tuple, list)): + rv, headers = rv + else: + rv, status = rv # type: ignore[assignment,misc] + # other sized tuples are not allowed + else: + raise TypeError( + "The view function did not return a valid response tuple." + " The tuple must have the form (body, status, headers)," + " (body, status), or (body, headers)." + ) + + # the body must not be None + if rv is None: + raise TypeError( + f"The view function for {request.endpoint!r} did not" + " return a valid response. The function either returned" + " None or ended without a return statement." + ) + + # make sure the body is an instance of the response class + if not isinstance(rv, self.response_class): + if isinstance(rv, (str, bytes, bytearray)) or isinstance(rv, _abc_Iterator): + # let the response class set the status and headers instead of + # waiting to do it manually, so that the class can handle any + # special logic + rv = self.response_class( + rv, + status=status, + headers=headers, # type: ignore[arg-type] + ) + status = headers = None + elif isinstance(rv, (dict, list)): + rv = self.json.response(rv) + elif isinstance(rv, BaseResponse) or callable(rv): + # evaluate a WSGI callable, or coerce a different response + # class to the correct type + try: + rv = self.response_class.force_type( + rv, request.environ # type: ignore[arg-type] + ) + except TypeError as e: + raise TypeError( + f"{e}\nThe view function did not return a valid" + " response. The return type must be a string," + " dict, list, tuple with headers or status," + " Response instance, or WSGI callable, but it" + f" was a {type(rv).__name__}." + ).with_traceback(sys.exc_info()[2]) from None + else: + raise TypeError( + "The view function did not return a valid" + " response. The return type must be a string," + " dict, list, tuple with headers or status," + " Response instance, or WSGI callable, but it was a" + f" {type(rv).__name__}." + ) + + rv = t.cast(Response, rv) + # prefer the status if it was provided + if status is not None: + if isinstance(status, (str, bytes, bytearray)): + rv.status = status + else: + rv.status_code = status + + # extend existing headers with provided headers + if headers: + rv.headers.update(headers) # type: ignore[arg-type] + + return rv + + def create_url_adapter( + self, request: t.Optional[Request] + ) -> t.Optional[MapAdapter]: + """Creates a URL adapter for the given request. The URL adapter + is created at a point where the request context is not yet set + up so the request is passed explicitly. + + .. versionadded:: 0.6 + + .. versionchanged:: 0.9 + This can now also be called without a request object when the + URL adapter is created for the application context. + + .. versionchanged:: 1.0 + :data:`SERVER_NAME` no longer implicitly enables subdomain + matching. Use :attr:`subdomain_matching` instead. + """ + if request is not None: + # If subdomain matching is disabled (the default), use the + # default subdomain in all cases. This should be the default + # in Werkzeug but it currently does not have that feature. + if not self.subdomain_matching: + subdomain = self.url_map.default_subdomain or None + else: + subdomain = None + + return self.url_map.bind_to_environ( + request.environ, + server_name=self.config["SERVER_NAME"], + subdomain=subdomain, + ) + # We need at the very least the server name to be set for this + # to work. + if self.config["SERVER_NAME"] is not None: + return self.url_map.bind( + self.config["SERVER_NAME"], + script_name=self.config["APPLICATION_ROOT"], + url_scheme=self.config["PREFERRED_URL_SCHEME"], + ) + + return None + + def inject_url_defaults(self, endpoint: str, values: dict) -> None: + """Injects the URL defaults for the given endpoint directly into + the values dictionary passed. This is used internally and + automatically called on URL building. + + .. versionadded:: 0.7 + """ + names: t.Iterable[t.Optional[str]] = (None,) + + # url_for may be called outside a request context, parse the + # passed endpoint instead of using request.blueprints. + if "." in endpoint: + names = chain( + names, reversed(_split_blueprint_path(endpoint.rpartition(".")[0])) + ) + + for name in names: + if name in self.url_default_functions: + for func in self.url_default_functions[name]: + func(endpoint, values) + + def handle_url_build_error( + self, error: BuildError, endpoint: str, values: t.Dict[str, t.Any] + ) -> str: + """Called by :meth:`.url_for` if a + :exc:`~werkzeug.routing.BuildError` was raised. If this returns + a value, it will be returned by ``url_for``, otherwise the error + will be re-raised. + + Each function in :attr:`url_build_error_handlers` is called with + ``error``, ``endpoint`` and ``values``. If a function returns + ``None`` or raises a ``BuildError``, it is skipped. Otherwise, + its return value is returned by ``url_for``. + + :param error: The active ``BuildError`` being handled. + :param endpoint: The endpoint being built. + :param values: The keyword arguments passed to ``url_for``. + """ + for handler in self.url_build_error_handlers: + try: + rv = handler(error, endpoint, values) + except BuildError as e: + # make error available outside except block + error = e + else: + if rv is not None: + return rv + + # Re-raise if called with an active exception, otherwise raise + # the passed in exception. + if error is sys.exc_info()[1]: + raise + + raise error + + def preprocess_request(self) -> t.Optional[ft.ResponseReturnValue]: + """Called before the request is dispatched. Calls + :attr:`url_value_preprocessors` registered with the app and the + current blueprint (if any). Then calls :attr:`before_request_funcs` + registered with the app and the blueprint. + + If any :meth:`before_request` handler returns a non-None value, the + value is handled as if it was the return value from the view, and + further request handling is stopped. + """ + names = (None, *reversed(request.blueprints)) + + for name in names: + if name in self.url_value_preprocessors: + for url_func in self.url_value_preprocessors[name]: + url_func(request.endpoint, request.view_args) + + for name in names: + if name in self.before_request_funcs: + for before_func in self.before_request_funcs[name]: + rv = self.ensure_sync(before_func)() + + if rv is not None: + return rv + + return None + + def process_response(self, response: Response) -> Response: + """Can be overridden in order to modify the response object + before it's sent to the WSGI server. By default this will + call all the :meth:`after_request` decorated functions. + + .. versionchanged:: 0.5 + As of Flask 0.5 the functions registered for after request + execution are called in reverse order of registration. + + :param response: a :attr:`response_class` object. + :return: a new response object or the same, has to be an + instance of :attr:`response_class`. + """ + ctx = request_ctx._get_current_object() # type: ignore[attr-defined] + + for func in ctx._after_request_functions: + response = self.ensure_sync(func)(response) + + for name in chain(request.blueprints, (None,)): + if name in self.after_request_funcs: + for func in reversed(self.after_request_funcs[name]): + response = self.ensure_sync(func)(response) + + if not self.session_interface.is_null_session(ctx.session): + self.session_interface.save_session(self, ctx.session, response) + + return response + + def do_teardown_request( + self, exc: t.Optional[BaseException] = _sentinel # type: ignore + ) -> None: + """Called after the request is dispatched and the response is + returned, right before the request context is popped. + + This calls all functions decorated with + :meth:`teardown_request`, and :meth:`Blueprint.teardown_request` + if a blueprint handled the request. Finally, the + :data:`request_tearing_down` signal is sent. + + This is called by + :meth:`RequestContext.pop() `, + which may be delayed during testing to maintain access to + resources. + + :param exc: An unhandled exception raised while dispatching the + request. Detected from the current exception information if + not passed. Passed to each teardown function. + + .. versionchanged:: 0.9 + Added the ``exc`` argument. + """ + if exc is _sentinel: + exc = sys.exc_info()[1] + + for name in chain(request.blueprints, (None,)): + if name in self.teardown_request_funcs: + for func in reversed(self.teardown_request_funcs[name]): + self.ensure_sync(func)(exc) + + request_tearing_down.send(self, exc=exc) + + def do_teardown_appcontext( + self, exc: t.Optional[BaseException] = _sentinel # type: ignore + ) -> None: + """Called right before the application context is popped. + + When handling a request, the application context is popped + after the request context. See :meth:`do_teardown_request`. + + This calls all functions decorated with + :meth:`teardown_appcontext`. Then the + :data:`appcontext_tearing_down` signal is sent. + + This is called by + :meth:`AppContext.pop() `. + + .. versionadded:: 0.9 + """ + if exc is _sentinel: + exc = sys.exc_info()[1] + + for func in reversed(self.teardown_appcontext_funcs): + self.ensure_sync(func)(exc) + + appcontext_tearing_down.send(self, exc=exc) + + def app_context(self) -> AppContext: + """Create an :class:`~flask.ctx.AppContext`. Use as a ``with`` + block to push the context, which will make :data:`current_app` + point at this application. + + An application context is automatically pushed by + :meth:`RequestContext.push() ` + when handling a request, and when running a CLI command. Use + this to manually create a context outside of these situations. + + :: + + with app.app_context(): + init_db() + + See :doc:`/appcontext`. + + .. versionadded:: 0.9 + """ + return AppContext(self) + + def request_context(self, environ: dict) -> RequestContext: + """Create a :class:`~flask.ctx.RequestContext` representing a + WSGI environment. Use a ``with`` block to push the context, + which will make :data:`request` point at this request. + + See :doc:`/reqcontext`. + + Typically you should not call this from your own code. A request + context is automatically pushed by the :meth:`wsgi_app` when + handling a request. Use :meth:`test_request_context` to create + an environment and context instead of this method. + + :param environ: a WSGI environment + """ + return RequestContext(self, environ) + + def test_request_context(self, *args: t.Any, **kwargs: t.Any) -> RequestContext: + """Create a :class:`~flask.ctx.RequestContext` for a WSGI + environment created from the given values. This is mostly useful + during testing, where you may want to run a function that uses + request data without dispatching a full request. + + See :doc:`/reqcontext`. + + Use a ``with`` block to push the context, which will make + :data:`request` point at the request for the created + environment. :: + + with test_request_context(...): + generate_report() + + When using the shell, it may be easier to push and pop the + context manually to avoid indentation. :: + + ctx = app.test_request_context(...) + ctx.push() + ... + ctx.pop() + + Takes the same arguments as Werkzeug's + :class:`~werkzeug.test.EnvironBuilder`, with some defaults from + the application. See the linked Werkzeug docs for most of the + available arguments. Flask-specific behavior is listed here. + + :param path: URL path being requested. + :param base_url: Base URL where the app is being served, which + ``path`` is relative to. If not given, built from + :data:`PREFERRED_URL_SCHEME`, ``subdomain``, + :data:`SERVER_NAME`, and :data:`APPLICATION_ROOT`. + :param subdomain: Subdomain name to append to + :data:`SERVER_NAME`. + :param url_scheme: Scheme to use instead of + :data:`PREFERRED_URL_SCHEME`. + :param data: The request body, either as a string or a dict of + form keys and values. + :param json: If given, this is serialized as JSON and passed as + ``data``. Also defaults ``content_type`` to + ``application/json``. + :param args: other positional arguments passed to + :class:`~werkzeug.test.EnvironBuilder`. + :param kwargs: other keyword arguments passed to + :class:`~werkzeug.test.EnvironBuilder`. + """ + from .testing import EnvironBuilder + + builder = EnvironBuilder(self, *args, **kwargs) + + try: + return self.request_context(builder.get_environ()) + finally: + builder.close() + + def wsgi_app(self, environ: dict, start_response: t.Callable) -> t.Any: + """The actual WSGI application. This is not implemented in + :meth:`__call__` so that middlewares can be applied without + losing a reference to the app object. Instead of doing this:: + + app = MyMiddleware(app) + + It's a better idea to do this instead:: + + app.wsgi_app = MyMiddleware(app.wsgi_app) + + Then you still have the original application object around and + can continue to call methods on it. + + .. versionchanged:: 0.7 + Teardown events for the request and app contexts are called + even if an unhandled error occurs. Other events may not be + called depending on when an error occurs during dispatch. + See :ref:`callbacks-and-errors`. + + :param environ: A WSGI environment. + :param start_response: A callable accepting a status code, + a list of headers, and an optional exception context to + start the response. + """ + ctx = self.request_context(environ) + error: t.Optional[BaseException] = None + try: + try: + ctx.push() + response = self.full_dispatch_request() + except Exception as e: + error = e + response = self.handle_exception(e) + except: # noqa: B001 + error = sys.exc_info()[1] + raise + return response(environ, start_response) + finally: + if "werkzeug.debug.preserve_context" in environ: + environ["werkzeug.debug.preserve_context"](_cv_app.get()) + environ["werkzeug.debug.preserve_context"](_cv_request.get()) + + if error is not None and self.should_ignore_error(error): + error = None + + ctx.pop(error) + + def __call__(self, environ: dict, start_response: t.Callable) -> t.Any: + """The WSGI server calls the Flask application object as the + WSGI application. This calls :meth:`wsgi_app`, which can be + wrapped to apply middleware. + """ + return self.wsgi_app(environ, start_response) diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/flask/blueprints.py b/docs/examples/flask/venv/lib/python3.11/site-packages/flask/blueprints.py new file mode 100644 index 0000000..eb66423 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/flask/blueprints.py @@ -0,0 +1,712 @@ +import json +import os +import typing as t +from collections import defaultdict +from functools import update_wrapper + +from . import typing as ft +from .scaffold import _endpoint_from_view_func +from .scaffold import _sentinel +from .scaffold import Scaffold +from .scaffold import setupmethod + +if t.TYPE_CHECKING: # pragma: no cover + from .app import Flask + +DeferredSetupFunction = t.Callable[["BlueprintSetupState"], t.Callable] +T_after_request = t.TypeVar("T_after_request", bound=ft.AfterRequestCallable) +T_before_first_request = t.TypeVar( + "T_before_first_request", bound=ft.BeforeFirstRequestCallable +) +T_before_request = t.TypeVar("T_before_request", bound=ft.BeforeRequestCallable) +T_error_handler = t.TypeVar("T_error_handler", bound=ft.ErrorHandlerCallable) +T_teardown = t.TypeVar("T_teardown", bound=ft.TeardownCallable) +T_template_context_processor = t.TypeVar( + "T_template_context_processor", bound=ft.TemplateContextProcessorCallable +) +T_template_filter = t.TypeVar("T_template_filter", bound=ft.TemplateFilterCallable) +T_template_global = t.TypeVar("T_template_global", bound=ft.TemplateGlobalCallable) +T_template_test = t.TypeVar("T_template_test", bound=ft.TemplateTestCallable) +T_url_defaults = t.TypeVar("T_url_defaults", bound=ft.URLDefaultCallable) +T_url_value_preprocessor = t.TypeVar( + "T_url_value_preprocessor", bound=ft.URLValuePreprocessorCallable +) + + +class BlueprintSetupState: + """Temporary holder object for registering a blueprint with the + application. An instance of this class is created by the + :meth:`~flask.Blueprint.make_setup_state` method and later passed + to all register callback functions. + """ + + def __init__( + self, + blueprint: "Blueprint", + app: "Flask", + options: t.Any, + first_registration: bool, + ) -> None: + #: a reference to the current application + self.app = app + + #: a reference to the blueprint that created this setup state. + self.blueprint = blueprint + + #: a dictionary with all options that were passed to the + #: :meth:`~flask.Flask.register_blueprint` method. + self.options = options + + #: as blueprints can be registered multiple times with the + #: application and not everything wants to be registered + #: multiple times on it, this attribute can be used to figure + #: out if the blueprint was registered in the past already. + self.first_registration = first_registration + + subdomain = self.options.get("subdomain") + if subdomain is None: + subdomain = self.blueprint.subdomain + + #: The subdomain that the blueprint should be active for, ``None`` + #: otherwise. + self.subdomain = subdomain + + url_prefix = self.options.get("url_prefix") + if url_prefix is None: + url_prefix = self.blueprint.url_prefix + #: The prefix that should be used for all URLs defined on the + #: blueprint. + self.url_prefix = url_prefix + + self.name = self.options.get("name", blueprint.name) + self.name_prefix = self.options.get("name_prefix", "") + + #: A dictionary with URL defaults that is added to each and every + #: URL that was defined with the blueprint. + self.url_defaults = dict(self.blueprint.url_values_defaults) + self.url_defaults.update(self.options.get("url_defaults", ())) + + def add_url_rule( + self, + rule: str, + endpoint: t.Optional[str] = None, + view_func: t.Optional[t.Callable] = None, + **options: t.Any, + ) -> None: + """A helper method to register a rule (and optionally a view function) + to the application. The endpoint is automatically prefixed with the + blueprint's name. + """ + if self.url_prefix is not None: + if rule: + rule = "/".join((self.url_prefix.rstrip("/"), rule.lstrip("/"))) + else: + rule = self.url_prefix + options.setdefault("subdomain", self.subdomain) + if endpoint is None: + endpoint = _endpoint_from_view_func(view_func) # type: ignore + defaults = self.url_defaults + if "defaults" in options: + defaults = dict(defaults, **options.pop("defaults")) + + self.app.add_url_rule( + rule, + f"{self.name_prefix}.{self.name}.{endpoint}".lstrip("."), + view_func, + defaults=defaults, + **options, + ) + + +class Blueprint(Scaffold): + """Represents a blueprint, a collection of routes and other + app-related functions that can be registered on a real application + later. + + A blueprint is an object that allows defining application functions + without requiring an application object ahead of time. It uses the + same decorators as :class:`~flask.Flask`, but defers the need for an + application by recording them for later registration. + + Decorating a function with a blueprint creates a deferred function + that is called with :class:`~flask.blueprints.BlueprintSetupState` + when the blueprint is registered on an application. + + See :doc:`/blueprints` for more information. + + :param name: The name of the blueprint. Will be prepended to each + endpoint name. + :param import_name: The name of the blueprint package, usually + ``__name__``. This helps locate the ``root_path`` for the + blueprint. + :param static_folder: A folder with static files that should be + served by the blueprint's static route. The path is relative to + the blueprint's root path. Blueprint static files are disabled + by default. + :param static_url_path: The url to serve static files from. + Defaults to ``static_folder``. If the blueprint does not have + a ``url_prefix``, the app's static route will take precedence, + and the blueprint's static files won't be accessible. + :param template_folder: A folder with templates that should be added + to the app's template search path. The path is relative to the + blueprint's root path. Blueprint templates are disabled by + default. Blueprint templates have a lower precedence than those + in the app's templates folder. + :param url_prefix: A path to prepend to all of the blueprint's URLs, + to make them distinct from the rest of the app's routes. + :param subdomain: A subdomain that blueprint routes will match on by + default. + :param url_defaults: A dict of default values that blueprint routes + will receive by default. + :param root_path: By default, the blueprint will automatically set + this based on ``import_name``. In certain situations this + automatic detection can fail, so the path can be specified + manually instead. + + .. versionchanged:: 1.1.0 + Blueprints have a ``cli`` group to register nested CLI commands. + The ``cli_group`` parameter controls the name of the group under + the ``flask`` command. + + .. versionadded:: 0.7 + """ + + _got_registered_once = False + + _json_encoder: t.Union[t.Type[json.JSONEncoder], None] = None + _json_decoder: t.Union[t.Type[json.JSONDecoder], None] = None + + @property + def json_encoder( + self, + ) -> t.Union[t.Type[json.JSONEncoder], None]: + """Blueprint-local JSON encoder class to use. Set to ``None`` to use the app's. + + .. deprecated:: 2.2 + Will be removed in Flask 2.3. Customize + :attr:`json_provider_class` instead. + + .. versionadded:: 0.10 + """ + import warnings + + warnings.warn( + "'bp.json_encoder' is deprecated and will be removed in Flask 2.3." + " Customize 'app.json_provider_class' or 'app.json' instead.", + DeprecationWarning, + stacklevel=2, + ) + return self._json_encoder + + @json_encoder.setter + def json_encoder(self, value: t.Union[t.Type[json.JSONEncoder], None]) -> None: + import warnings + + warnings.warn( + "'bp.json_encoder' is deprecated and will be removed in Flask 2.3." + " Customize 'app.json_provider_class' or 'app.json' instead.", + DeprecationWarning, + stacklevel=2, + ) + self._json_encoder = value + + @property + def json_decoder( + self, + ) -> t.Union[t.Type[json.JSONDecoder], None]: + """Blueprint-local JSON decoder class to use. Set to ``None`` to use the app's. + + .. deprecated:: 2.2 + Will be removed in Flask 2.3. Customize + :attr:`json_provider_class` instead. + + .. versionadded:: 0.10 + """ + import warnings + + warnings.warn( + "'bp.json_decoder' is deprecated and will be removed in Flask 2.3." + " Customize 'app.json_provider_class' or 'app.json' instead.", + DeprecationWarning, + stacklevel=2, + ) + return self._json_decoder + + @json_decoder.setter + def json_decoder(self, value: t.Union[t.Type[json.JSONDecoder], None]) -> None: + import warnings + + warnings.warn( + "'bp.json_decoder' is deprecated and will be removed in Flask 2.3." + " Customize 'app.json_provider_class' or 'app.json' instead.", + DeprecationWarning, + stacklevel=2, + ) + self._json_decoder = value + + def __init__( + self, + name: str, + import_name: str, + static_folder: t.Optional[t.Union[str, os.PathLike]] = None, + static_url_path: t.Optional[str] = None, + template_folder: t.Optional[t.Union[str, os.PathLike]] = None, + url_prefix: t.Optional[str] = None, + subdomain: t.Optional[str] = None, + url_defaults: t.Optional[dict] = None, + root_path: t.Optional[str] = None, + cli_group: t.Optional[str] = _sentinel, # type: ignore + ): + super().__init__( + import_name=import_name, + static_folder=static_folder, + static_url_path=static_url_path, + template_folder=template_folder, + root_path=root_path, + ) + + if "." in name: + raise ValueError("'name' may not contain a dot '.' character.") + + self.name = name + self.url_prefix = url_prefix + self.subdomain = subdomain + self.deferred_functions: t.List[DeferredSetupFunction] = [] + + if url_defaults is None: + url_defaults = {} + + self.url_values_defaults = url_defaults + self.cli_group = cli_group + self._blueprints: t.List[t.Tuple["Blueprint", dict]] = [] + + def _check_setup_finished(self, f_name: str) -> None: + if self._got_registered_once: + import warnings + + warnings.warn( + f"The setup method '{f_name}' can no longer be called on" + f" the blueprint '{self.name}'. It has already been" + " registered at least once, any changes will not be" + " applied consistently.\n" + "Make sure all imports, decorators, functions, etc." + " needed to set up the blueprint are done before" + " registering it.\n" + "This warning will become an exception in Flask 2.3.", + UserWarning, + stacklevel=3, + ) + + @setupmethod + def record(self, func: t.Callable) -> None: + """Registers a function that is called when the blueprint is + registered on the application. This function is called with the + state as argument as returned by the :meth:`make_setup_state` + method. + """ + self.deferred_functions.append(func) + + @setupmethod + def record_once(self, func: t.Callable) -> None: + """Works like :meth:`record` but wraps the function in another + function that will ensure the function is only called once. If the + blueprint is registered a second time on the application, the + function passed is not called. + """ + + def wrapper(state: BlueprintSetupState) -> None: + if state.first_registration: + func(state) + + self.record(update_wrapper(wrapper, func)) + + def make_setup_state( + self, app: "Flask", options: dict, first_registration: bool = False + ) -> BlueprintSetupState: + """Creates an instance of :meth:`~flask.blueprints.BlueprintSetupState` + object that is later passed to the register callback functions. + Subclasses can override this to return a subclass of the setup state. + """ + return BlueprintSetupState(self, app, options, first_registration) + + @setupmethod + def register_blueprint(self, blueprint: "Blueprint", **options: t.Any) -> None: + """Register a :class:`~flask.Blueprint` on this blueprint. Keyword + arguments passed to this method will override the defaults set + on the blueprint. + + .. versionchanged:: 2.0.1 + The ``name`` option can be used to change the (pre-dotted) + name the blueprint is registered with. This allows the same + blueprint to be registered multiple times with unique names + for ``url_for``. + + .. versionadded:: 2.0 + """ + if blueprint is self: + raise ValueError("Cannot register a blueprint on itself") + self._blueprints.append((blueprint, options)) + + def register(self, app: "Flask", options: dict) -> None: + """Called by :meth:`Flask.register_blueprint` to register all + views and callbacks registered on the blueprint with the + application. Creates a :class:`.BlueprintSetupState` and calls + each :meth:`record` callback with it. + + :param app: The application this blueprint is being registered + with. + :param options: Keyword arguments forwarded from + :meth:`~Flask.register_blueprint`. + + .. versionchanged:: 2.0.1 + Nested blueprints are registered with their dotted name. + This allows different blueprints with the same name to be + nested at different locations. + + .. versionchanged:: 2.0.1 + The ``name`` option can be used to change the (pre-dotted) + name the blueprint is registered with. This allows the same + blueprint to be registered multiple times with unique names + for ``url_for``. + + .. versionchanged:: 2.0.1 + Registering the same blueprint with the same name multiple + times is deprecated and will become an error in Flask 2.1. + """ + name_prefix = options.get("name_prefix", "") + self_name = options.get("name", self.name) + name = f"{name_prefix}.{self_name}".lstrip(".") + + if name in app.blueprints: + bp_desc = "this" if app.blueprints[name] is self else "a different" + existing_at = f" '{name}'" if self_name != name else "" + + raise ValueError( + f"The name '{self_name}' is already registered for" + f" {bp_desc} blueprint{existing_at}. Use 'name=' to" + f" provide a unique name." + ) + + first_bp_registration = not any(bp is self for bp in app.blueprints.values()) + first_name_registration = name not in app.blueprints + + app.blueprints[name] = self + self._got_registered_once = True + state = self.make_setup_state(app, options, first_bp_registration) + + if self.has_static_folder: + state.add_url_rule( + f"{self.static_url_path}/", + view_func=self.send_static_file, + endpoint="static", + ) + + # Merge blueprint data into parent. + if first_bp_registration or first_name_registration: + + def extend(bp_dict, parent_dict): + for key, values in bp_dict.items(): + key = name if key is None else f"{name}.{key}" + parent_dict[key].extend(values) + + for key, value in self.error_handler_spec.items(): + key = name if key is None else f"{name}.{key}" + value = defaultdict( + dict, + { + code: { + exc_class: func for exc_class, func in code_values.items() + } + for code, code_values in value.items() + }, + ) + app.error_handler_spec[key] = value + + for endpoint, func in self.view_functions.items(): + app.view_functions[endpoint] = func + + extend(self.before_request_funcs, app.before_request_funcs) + extend(self.after_request_funcs, app.after_request_funcs) + extend( + self.teardown_request_funcs, + app.teardown_request_funcs, + ) + extend(self.url_default_functions, app.url_default_functions) + extend(self.url_value_preprocessors, app.url_value_preprocessors) + extend(self.template_context_processors, app.template_context_processors) + + for deferred in self.deferred_functions: + deferred(state) + + cli_resolved_group = options.get("cli_group", self.cli_group) + + if self.cli.commands: + if cli_resolved_group is None: + app.cli.commands.update(self.cli.commands) + elif cli_resolved_group is _sentinel: + self.cli.name = name + app.cli.add_command(self.cli) + else: + self.cli.name = cli_resolved_group + app.cli.add_command(self.cli) + + for blueprint, bp_options in self._blueprints: + bp_options = bp_options.copy() + bp_url_prefix = bp_options.get("url_prefix") + + if bp_url_prefix is None: + bp_url_prefix = blueprint.url_prefix + + if state.url_prefix is not None and bp_url_prefix is not None: + bp_options["url_prefix"] = ( + state.url_prefix.rstrip("/") + "/" + bp_url_prefix.lstrip("/") + ) + elif bp_url_prefix is not None: + bp_options["url_prefix"] = bp_url_prefix + elif state.url_prefix is not None: + bp_options["url_prefix"] = state.url_prefix + + bp_options["name_prefix"] = name + blueprint.register(app, bp_options) + + @setupmethod + def add_url_rule( + self, + rule: str, + endpoint: t.Optional[str] = None, + view_func: t.Optional[ft.RouteCallable] = None, + provide_automatic_options: t.Optional[bool] = None, + **options: t.Any, + ) -> None: + """Register a URL rule with the blueprint. See :meth:`.Flask.add_url_rule` for + full documentation. + + The URL rule is prefixed with the blueprint's URL prefix. The endpoint name, + used with :func:`url_for`, is prefixed with the blueprint's name. + """ + if endpoint and "." in endpoint: + raise ValueError("'endpoint' may not contain a dot '.' character.") + + if view_func and hasattr(view_func, "__name__") and "." in view_func.__name__: + raise ValueError("'view_func' name may not contain a dot '.' character.") + + self.record( + lambda s: s.add_url_rule( + rule, + endpoint, + view_func, + provide_automatic_options=provide_automatic_options, + **options, + ) + ) + + @setupmethod + def app_template_filter( + self, name: t.Optional[str] = None + ) -> t.Callable[[T_template_filter], T_template_filter]: + """Register a template filter, available in any template rendered by the + application. Equivalent to :meth:`.Flask.template_filter`. + + :param name: the optional name of the filter, otherwise the + function name will be used. + """ + + def decorator(f: T_template_filter) -> T_template_filter: + self.add_app_template_filter(f, name=name) + return f + + return decorator + + @setupmethod + def add_app_template_filter( + self, f: ft.TemplateFilterCallable, name: t.Optional[str] = None + ) -> None: + """Register a template filter, available in any template rendered by the + application. Works like the :meth:`app_template_filter` decorator. Equivalent to + :meth:`.Flask.add_template_filter`. + + :param name: the optional name of the filter, otherwise the + function name will be used. + """ + + def register_template(state: BlueprintSetupState) -> None: + state.app.jinja_env.filters[name or f.__name__] = f + + self.record_once(register_template) + + @setupmethod + def app_template_test( + self, name: t.Optional[str] = None + ) -> t.Callable[[T_template_test], T_template_test]: + """Register a template test, available in any template rendered by the + application. Equivalent to :meth:`.Flask.template_test`. + + .. versionadded:: 0.10 + + :param name: the optional name of the test, otherwise the + function name will be used. + """ + + def decorator(f: T_template_test) -> T_template_test: + self.add_app_template_test(f, name=name) + return f + + return decorator + + @setupmethod + def add_app_template_test( + self, f: ft.TemplateTestCallable, name: t.Optional[str] = None + ) -> None: + """Register a template test, available in any template rendered by the + application. Works like the :meth:`app_template_test` decorator. Equivalent to + :meth:`.Flask.add_template_test`. + + .. versionadded:: 0.10 + + :param name: the optional name of the test, otherwise the + function name will be used. + """ + + def register_template(state: BlueprintSetupState) -> None: + state.app.jinja_env.tests[name or f.__name__] = f + + self.record_once(register_template) + + @setupmethod + def app_template_global( + self, name: t.Optional[str] = None + ) -> t.Callable[[T_template_global], T_template_global]: + """Register a template global, available in any template rendered by the + application. Equivalent to :meth:`.Flask.template_global`. + + .. versionadded:: 0.10 + + :param name: the optional name of the global, otherwise the + function name will be used. + """ + + def decorator(f: T_template_global) -> T_template_global: + self.add_app_template_global(f, name=name) + return f + + return decorator + + @setupmethod + def add_app_template_global( + self, f: ft.TemplateGlobalCallable, name: t.Optional[str] = None + ) -> None: + """Register a template global, available in any template rendered by the + application. Works like the :meth:`app_template_global` decorator. Equivalent to + :meth:`.Flask.add_template_global`. + + .. versionadded:: 0.10 + + :param name: the optional name of the global, otherwise the + function name will be used. + """ + + def register_template(state: BlueprintSetupState) -> None: + state.app.jinja_env.globals[name or f.__name__] = f + + self.record_once(register_template) + + @setupmethod + def before_app_request(self, f: T_before_request) -> T_before_request: + """Like :meth:`before_request`, but before every request, not only those handled + by the blueprint. Equivalent to :meth:`.Flask.before_request`. + """ + self.record_once( + lambda s: s.app.before_request_funcs.setdefault(None, []).append(f) + ) + return f + + @setupmethod + def before_app_first_request( + self, f: T_before_first_request + ) -> T_before_first_request: + """Register a function to run before the first request to the application is + handled by the worker. Equivalent to :meth:`.Flask.before_first_request`. + + .. deprecated:: 2.2 + Will be removed in Flask 2.3. Run setup code when creating + the application instead. + """ + import warnings + + warnings.warn( + "'before_app_first_request' is deprecated and will be" + " removed in Flask 2.3. Use 'record_once' instead to run" + " setup code when registering the blueprint.", + DeprecationWarning, + stacklevel=2, + ) + self.record_once(lambda s: s.app.before_first_request_funcs.append(f)) + return f + + @setupmethod + def after_app_request(self, f: T_after_request) -> T_after_request: + """Like :meth:`after_request`, but after every request, not only those handled + by the blueprint. Equivalent to :meth:`.Flask.after_request`. + """ + self.record_once( + lambda s: s.app.after_request_funcs.setdefault(None, []).append(f) + ) + return f + + @setupmethod + def teardown_app_request(self, f: T_teardown) -> T_teardown: + """Like :meth:`teardown_request`, but after every request, not only those + handled by the blueprint. Equivalent to :meth:`.Flask.teardown_request`. + """ + self.record_once( + lambda s: s.app.teardown_request_funcs.setdefault(None, []).append(f) + ) + return f + + @setupmethod + def app_context_processor( + self, f: T_template_context_processor + ) -> T_template_context_processor: + """Like :meth:`context_processor`, but for templates rendered by every view, not + only by the blueprint. Equivalent to :meth:`.Flask.context_processor`. + """ + self.record_once( + lambda s: s.app.template_context_processors.setdefault(None, []).append(f) + ) + return f + + @setupmethod + def app_errorhandler( + self, code: t.Union[t.Type[Exception], int] + ) -> t.Callable[[T_error_handler], T_error_handler]: + """Like :meth:`errorhandler`, but for every request, not only those handled by + the blueprint. Equivalent to :meth:`.Flask.errorhandler`. + """ + + def decorator(f: T_error_handler) -> T_error_handler: + self.record_once(lambda s: s.app.errorhandler(code)(f)) + return f + + return decorator + + @setupmethod + def app_url_value_preprocessor( + self, f: T_url_value_preprocessor + ) -> T_url_value_preprocessor: + """Like :meth:`url_value_preprocessor`, but for every request, not only those + handled by the blueprint. Equivalent to :meth:`.Flask.url_value_preprocessor`. + """ + self.record_once( + lambda s: s.app.url_value_preprocessors.setdefault(None, []).append(f) + ) + return f + + @setupmethod + def app_url_defaults(self, f: T_url_defaults) -> T_url_defaults: + """Like :meth:`url_defaults`, but for every request, not only those handled by + the blueprint. Equivalent to :meth:`.Flask.url_defaults`. + """ + self.record_once( + lambda s: s.app.url_default_functions.setdefault(None, []).append(f) + ) + return f diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/flask/cli.py b/docs/examples/flask/venv/lib/python3.11/site-packages/flask/cli.py new file mode 100644 index 0000000..37a15ff --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/flask/cli.py @@ -0,0 +1,1054 @@ +from __future__ import annotations + +import ast +import inspect +import os +import platform +import re +import sys +import traceback +import typing as t +from functools import update_wrapper +from operator import attrgetter + +import click +from click.core import ParameterSource +from werkzeug import run_simple +from werkzeug.serving import is_running_from_reloader +from werkzeug.utils import import_string + +from .globals import current_app +from .helpers import get_debug_flag +from .helpers import get_load_dotenv + +if t.TYPE_CHECKING: + from .app import Flask + + +class NoAppException(click.UsageError): + """Raised if an application cannot be found or loaded.""" + + +def find_best_app(module): + """Given a module instance this tries to find the best possible + application in the module or raises an exception. + """ + from . import Flask + + # Search for the most common names first. + for attr_name in ("app", "application"): + app = getattr(module, attr_name, None) + + if isinstance(app, Flask): + return app + + # Otherwise find the only object that is a Flask instance. + matches = [v for v in module.__dict__.values() if isinstance(v, Flask)] + + if len(matches) == 1: + return matches[0] + elif len(matches) > 1: + raise NoAppException( + "Detected multiple Flask applications in module" + f" '{module.__name__}'. Use '{module.__name__}:name'" + " to specify the correct one." + ) + + # Search for app factory functions. + for attr_name in ("create_app", "make_app"): + app_factory = getattr(module, attr_name, None) + + if inspect.isfunction(app_factory): + try: + app = app_factory() + + if isinstance(app, Flask): + return app + except TypeError as e: + if not _called_with_wrong_args(app_factory): + raise + + raise NoAppException( + f"Detected factory '{attr_name}' in module '{module.__name__}'," + " but could not call it without arguments. Use" + f" '{module.__name__}:{attr_name}(args)'" + " to specify arguments." + ) from e + + raise NoAppException( + "Failed to find Flask application or factory in module" + f" '{module.__name__}'. Use '{module.__name__}:name'" + " to specify one." + ) + + +def _called_with_wrong_args(f): + """Check whether calling a function raised a ``TypeError`` because + the call failed or because something in the factory raised the + error. + + :param f: The function that was called. + :return: ``True`` if the call failed. + """ + tb = sys.exc_info()[2] + + try: + while tb is not None: + if tb.tb_frame.f_code is f.__code__: + # In the function, it was called successfully. + return False + + tb = tb.tb_next + + # Didn't reach the function. + return True + finally: + # Delete tb to break a circular reference. + # https://docs.python.org/2/library/sys.html#sys.exc_info + del tb + + +def find_app_by_string(module, app_name): + """Check if the given string is a variable name or a function. Call + a function to get the app instance, or return the variable directly. + """ + from . import Flask + + # Parse app_name as a single expression to determine if it's a valid + # attribute name or function call. + try: + expr = ast.parse(app_name.strip(), mode="eval").body + except SyntaxError: + raise NoAppException( + f"Failed to parse {app_name!r} as an attribute name or function call." + ) from None + + if isinstance(expr, ast.Name): + name = expr.id + args = [] + kwargs = {} + elif isinstance(expr, ast.Call): + # Ensure the function name is an attribute name only. + if not isinstance(expr.func, ast.Name): + raise NoAppException( + f"Function reference must be a simple name: {app_name!r}." + ) + + name = expr.func.id + + # Parse the positional and keyword arguments as literals. + try: + args = [ast.literal_eval(arg) for arg in expr.args] + kwargs = {kw.arg: ast.literal_eval(kw.value) for kw in expr.keywords} + except ValueError: + # literal_eval gives cryptic error messages, show a generic + # message with the full expression instead. + raise NoAppException( + f"Failed to parse arguments as literal values: {app_name!r}." + ) from None + else: + raise NoAppException( + f"Failed to parse {app_name!r} as an attribute name or function call." + ) + + try: + attr = getattr(module, name) + except AttributeError as e: + raise NoAppException( + f"Failed to find attribute {name!r} in {module.__name__!r}." + ) from e + + # If the attribute is a function, call it with any args and kwargs + # to get the real application. + if inspect.isfunction(attr): + try: + app = attr(*args, **kwargs) + except TypeError as e: + if not _called_with_wrong_args(attr): + raise + + raise NoAppException( + f"The factory {app_name!r} in module" + f" {module.__name__!r} could not be called with the" + " specified arguments." + ) from e + else: + app = attr + + if isinstance(app, Flask): + return app + + raise NoAppException( + "A valid Flask application was not obtained from" + f" '{module.__name__}:{app_name}'." + ) + + +def prepare_import(path): + """Given a filename this will try to calculate the python path, add it + to the search path and return the actual module name that is expected. + """ + path = os.path.realpath(path) + + fname, ext = os.path.splitext(path) + if ext == ".py": + path = fname + + if os.path.basename(path) == "__init__": + path = os.path.dirname(path) + + module_name = [] + + # move up until outside package structure (no __init__.py) + while True: + path, name = os.path.split(path) + module_name.append(name) + + if not os.path.exists(os.path.join(path, "__init__.py")): + break + + if sys.path[0] != path: + sys.path.insert(0, path) + + return ".".join(module_name[::-1]) + + +def locate_app(module_name, app_name, raise_if_not_found=True): + try: + __import__(module_name) + except ImportError: + # Reraise the ImportError if it occurred within the imported module. + # Determine this by checking whether the trace has a depth > 1. + if sys.exc_info()[2].tb_next: + raise NoAppException( + f"While importing {module_name!r}, an ImportError was" + f" raised:\n\n{traceback.format_exc()}" + ) from None + elif raise_if_not_found: + raise NoAppException(f"Could not import {module_name!r}.") from None + else: + return + + module = sys.modules[module_name] + + if app_name is None: + return find_best_app(module) + else: + return find_app_by_string(module, app_name) + + +def get_version(ctx, param, value): + if not value or ctx.resilient_parsing: + return + + import werkzeug + from . import __version__ + + click.echo( + f"Python {platform.python_version()}\n" + f"Flask {__version__}\n" + f"Werkzeug {werkzeug.__version__}", + color=ctx.color, + ) + ctx.exit() + + +version_option = click.Option( + ["--version"], + help="Show the Flask version.", + expose_value=False, + callback=get_version, + is_flag=True, + is_eager=True, +) + + +class ScriptInfo: + """Helper object to deal with Flask applications. This is usually not + necessary to interface with as it's used internally in the dispatching + to click. In future versions of Flask this object will most likely play + a bigger role. Typically it's created automatically by the + :class:`FlaskGroup` but you can also manually create it and pass it + onwards as click object. + """ + + def __init__( + self, + app_import_path: str | None = None, + create_app: t.Callable[..., Flask] | None = None, + set_debug_flag: bool = True, + ) -> None: + #: Optionally the import path for the Flask application. + self.app_import_path = app_import_path + #: Optionally a function that is passed the script info to create + #: the instance of the application. + self.create_app = create_app + #: A dictionary with arbitrary data that can be associated with + #: this script info. + self.data: t.Dict[t.Any, t.Any] = {} + self.set_debug_flag = set_debug_flag + self._loaded_app: Flask | None = None + + def load_app(self) -> Flask: + """Loads the Flask app (if not yet loaded) and returns it. Calling + this multiple times will just result in the already loaded app to + be returned. + """ + if self._loaded_app is not None: + return self._loaded_app + + if self.create_app is not None: + app = self.create_app() + else: + if self.app_import_path: + path, name = ( + re.split(r":(?![\\/])", self.app_import_path, 1) + [None] + )[:2] + import_name = prepare_import(path) + app = locate_app(import_name, name) + else: + for path in ("wsgi.py", "app.py"): + import_name = prepare_import(path) + app = locate_app(import_name, None, raise_if_not_found=False) + + if app: + break + + if not app: + raise NoAppException( + "Could not locate a Flask application. Use the" + " 'flask --app' option, 'FLASK_APP' environment" + " variable, or a 'wsgi.py' or 'app.py' file in the" + " current directory." + ) + + if self.set_debug_flag: + # Update the app's debug flag through the descriptor so that + # other values repopulate as well. + app.debug = get_debug_flag() + + self._loaded_app = app + return app + + +pass_script_info = click.make_pass_decorator(ScriptInfo, ensure=True) + + +def with_appcontext(f): + """Wraps a callback so that it's guaranteed to be executed with the + script's application context. + + Custom commands (and their options) registered under ``app.cli`` or + ``blueprint.cli`` will always have an app context available, this + decorator is not required in that case. + + .. versionchanged:: 2.2 + The app context is active for subcommands as well as the + decorated callback. The app context is always available to + ``app.cli`` command and parameter callbacks. + """ + + @click.pass_context + def decorator(__ctx, *args, **kwargs): + if not current_app: + app = __ctx.ensure_object(ScriptInfo).load_app() + __ctx.with_resource(app.app_context()) + + return __ctx.invoke(f, *args, **kwargs) + + return update_wrapper(decorator, f) + + +class AppGroup(click.Group): + """This works similar to a regular click :class:`~click.Group` but it + changes the behavior of the :meth:`command` decorator so that it + automatically wraps the functions in :func:`with_appcontext`. + + Not to be confused with :class:`FlaskGroup`. + """ + + def command(self, *args, **kwargs): + """This works exactly like the method of the same name on a regular + :class:`click.Group` but it wraps callbacks in :func:`with_appcontext` + unless it's disabled by passing ``with_appcontext=False``. + """ + wrap_for_ctx = kwargs.pop("with_appcontext", True) + + def decorator(f): + if wrap_for_ctx: + f = with_appcontext(f) + return click.Group.command(self, *args, **kwargs)(f) + + return decorator + + def group(self, *args, **kwargs): + """This works exactly like the method of the same name on a regular + :class:`click.Group` but it defaults the group class to + :class:`AppGroup`. + """ + kwargs.setdefault("cls", AppGroup) + return click.Group.group(self, *args, **kwargs) + + +def _set_app(ctx: click.Context, param: click.Option, value: str | None) -> str | None: + if value is None: + return None + + info = ctx.ensure_object(ScriptInfo) + info.app_import_path = value + return value + + +# This option is eager so the app will be available if --help is given. +# --help is also eager, so --app must be before it in the param list. +# no_args_is_help bypasses eager processing, so this option must be +# processed manually in that case to ensure FLASK_APP gets picked up. +_app_option = click.Option( + ["-A", "--app"], + metavar="IMPORT", + help=( + "The Flask application or factory function to load, in the form 'module:name'." + " Module can be a dotted import or file path. Name is not required if it is" + " 'app', 'application', 'create_app', or 'make_app', and can be 'name(args)' to" + " pass arguments." + ), + is_eager=True, + expose_value=False, + callback=_set_app, +) + + +def _set_debug(ctx: click.Context, param: click.Option, value: bool) -> bool | None: + # If the flag isn't provided, it will default to False. Don't use + # that, let debug be set by env in that case. + source = ctx.get_parameter_source(param.name) # type: ignore[arg-type] + + if source is not None and source in ( + ParameterSource.DEFAULT, + ParameterSource.DEFAULT_MAP, + ): + return None + + # Set with env var instead of ScriptInfo.load so that it can be + # accessed early during a factory function. + os.environ["FLASK_DEBUG"] = "1" if value else "0" + return value + + +_debug_option = click.Option( + ["--debug/--no-debug"], + help="Set debug mode.", + expose_value=False, + callback=_set_debug, +) + + +def _env_file_callback( + ctx: click.Context, param: click.Option, value: str | None +) -> str | None: + if value is None: + return None + + import importlib + + try: + importlib.import_module("dotenv") + except ImportError: + raise click.BadParameter( + "python-dotenv must be installed to load an env file.", + ctx=ctx, + param=param, + ) from None + + # Don't check FLASK_SKIP_DOTENV, that only disables automatically + # loading .env and .flaskenv files. + load_dotenv(value) + return value + + +# This option is eager so env vars are loaded as early as possible to be +# used by other options. +_env_file_option = click.Option( + ["-e", "--env-file"], + type=click.Path(exists=True, dir_okay=False), + help="Load environment variables from this file. python-dotenv must be installed.", + is_eager=True, + expose_value=False, + callback=_env_file_callback, +) + + +class FlaskGroup(AppGroup): + """Special subclass of the :class:`AppGroup` group that supports + loading more commands from the configured Flask app. Normally a + developer does not have to interface with this class but there are + some very advanced use cases for which it makes sense to create an + instance of this. see :ref:`custom-scripts`. + + :param add_default_commands: if this is True then the default run and + shell commands will be added. + :param add_version_option: adds the ``--version`` option. + :param create_app: an optional callback that is passed the script info and + returns the loaded app. + :param load_dotenv: Load the nearest :file:`.env` and :file:`.flaskenv` + files to set environment variables. Will also change the working + directory to the directory containing the first file found. + :param set_debug_flag: Set the app's debug flag. + + .. versionchanged:: 2.2 + Added the ``-A/--app``, ``--debug/--no-debug``, ``-e/--env-file`` options. + + .. versionchanged:: 2.2 + An app context is pushed when running ``app.cli`` commands, so + ``@with_appcontext`` is no longer required for those commands. + + .. versionchanged:: 1.0 + If installed, python-dotenv will be used to load environment variables + from :file:`.env` and :file:`.flaskenv` files. + """ + + def __init__( + self, + add_default_commands: bool = True, + create_app: t.Callable[..., Flask] | None = None, + add_version_option: bool = True, + load_dotenv: bool = True, + set_debug_flag: bool = True, + **extra: t.Any, + ) -> None: + params = list(extra.pop("params", None) or ()) + # Processing is done with option callbacks instead of a group + # callback. This allows users to make a custom group callback + # without losing the behavior. --env-file must come first so + # that it is eagerly evaluated before --app. + params.extend((_env_file_option, _app_option, _debug_option)) + + if add_version_option: + params.append(version_option) + + if "context_settings" not in extra: + extra["context_settings"] = {} + + extra["context_settings"].setdefault("auto_envvar_prefix", "FLASK") + + super().__init__(params=params, **extra) + + self.create_app = create_app + self.load_dotenv = load_dotenv + self.set_debug_flag = set_debug_flag + + if add_default_commands: + self.add_command(run_command) + self.add_command(shell_command) + self.add_command(routes_command) + + self._loaded_plugin_commands = False + + def _load_plugin_commands(self): + if self._loaded_plugin_commands: + return + + if sys.version_info >= (3, 10): + from importlib import metadata + else: + # Use a backport on Python < 3.10. We technically have + # importlib.metadata on 3.8+, but the API changed in 3.10, + # so use the backport for consistency. + import importlib_metadata as metadata + + for ep in metadata.entry_points(group="flask.commands"): + self.add_command(ep.load(), ep.name) + + self._loaded_plugin_commands = True + + def get_command(self, ctx, name): + self._load_plugin_commands() + # Look up built-in and plugin commands, which should be + # available even if the app fails to load. + rv = super().get_command(ctx, name) + + if rv is not None: + return rv + + info = ctx.ensure_object(ScriptInfo) + + # Look up commands provided by the app, showing an error and + # continuing if the app couldn't be loaded. + try: + app = info.load_app() + except NoAppException as e: + click.secho(f"Error: {e.format_message()}\n", err=True, fg="red") + return None + + # Push an app context for the loaded app unless it is already + # active somehow. This makes the context available to parameter + # and command callbacks without needing @with_appcontext. + if not current_app or current_app._get_current_object() is not app: + ctx.with_resource(app.app_context()) + + return app.cli.get_command(ctx, name) + + def list_commands(self, ctx): + self._load_plugin_commands() + # Start with the built-in and plugin commands. + rv = set(super().list_commands(ctx)) + info = ctx.ensure_object(ScriptInfo) + + # Add commands provided by the app, showing an error and + # continuing if the app couldn't be loaded. + try: + rv.update(info.load_app().cli.list_commands(ctx)) + except NoAppException as e: + # When an app couldn't be loaded, show the error message + # without the traceback. + click.secho(f"Error: {e.format_message()}\n", err=True, fg="red") + except Exception: + # When any other errors occurred during loading, show the + # full traceback. + click.secho(f"{traceback.format_exc()}\n", err=True, fg="red") + + return sorted(rv) + + def make_context( + self, + info_name: str | None, + args: list[str], + parent: click.Context | None = None, + **extra: t.Any, + ) -> click.Context: + # Set a flag to tell app.run to become a no-op. If app.run was + # not in a __name__ == __main__ guard, it would start the server + # when importing, blocking whatever command is being called. + os.environ["FLASK_RUN_FROM_CLI"] = "true" + + # Attempt to load .env and .flask env files. The --env-file + # option can cause another file to be loaded. + if get_load_dotenv(self.load_dotenv): + load_dotenv() + + if "obj" not in extra and "obj" not in self.context_settings: + extra["obj"] = ScriptInfo( + create_app=self.create_app, set_debug_flag=self.set_debug_flag + ) + + return super().make_context(info_name, args, parent=parent, **extra) + + def parse_args(self, ctx: click.Context, args: list[str]) -> list[str]: + if not args and self.no_args_is_help: + # Attempt to load --env-file and --app early in case they + # were given as env vars. Otherwise no_args_is_help will not + # see commands from app.cli. + _env_file_option.handle_parse_result(ctx, {}, []) + _app_option.handle_parse_result(ctx, {}, []) + + return super().parse_args(ctx, args) + + +def _path_is_ancestor(path, other): + """Take ``other`` and remove the length of ``path`` from it. Then join it + to ``path``. If it is the original value, ``path`` is an ancestor of + ``other``.""" + return os.path.join(path, other[len(path) :].lstrip(os.sep)) == other + + +def load_dotenv(path: str | os.PathLike | None = None) -> bool: + """Load "dotenv" files in order of precedence to set environment variables. + + If an env var is already set it is not overwritten, so earlier files in the + list are preferred over later files. + + This is a no-op if `python-dotenv`_ is not installed. + + .. _python-dotenv: https://github.com/theskumar/python-dotenv#readme + + :param path: Load the file at this location instead of searching. + :return: ``True`` if a file was loaded. + + .. versionchanged:: 2.0 + The current directory is not changed to the location of the + loaded file. + + .. versionchanged:: 2.0 + When loading the env files, set the default encoding to UTF-8. + + .. versionchanged:: 1.1.0 + Returns ``False`` when python-dotenv is not installed, or when + the given path isn't a file. + + .. versionadded:: 1.0 + """ + try: + import dotenv + except ImportError: + if path or os.path.isfile(".env") or os.path.isfile(".flaskenv"): + click.secho( + " * Tip: There are .env or .flaskenv files present." + ' Do "pip install python-dotenv" to use them.', + fg="yellow", + err=True, + ) + + return False + + # Always return after attempting to load a given path, don't load + # the default files. + if path is not None: + if os.path.isfile(path): + return dotenv.load_dotenv(path, encoding="utf-8") + + return False + + loaded = False + + for name in (".env", ".flaskenv"): + path = dotenv.find_dotenv(name, usecwd=True) + + if not path: + continue + + dotenv.load_dotenv(path, encoding="utf-8") + loaded = True + + return loaded # True if at least one file was located and loaded. + + +def show_server_banner(debug, app_import_path): + """Show extra startup messages the first time the server is run, + ignoring the reloader. + """ + if is_running_from_reloader(): + return + + if app_import_path is not None: + click.echo(f" * Serving Flask app '{app_import_path}'") + + if debug is not None: + click.echo(f" * Debug mode: {'on' if debug else 'off'}") + + +class CertParamType(click.ParamType): + """Click option type for the ``--cert`` option. Allows either an + existing file, the string ``'adhoc'``, or an import for a + :class:`~ssl.SSLContext` object. + """ + + name = "path" + + def __init__(self): + self.path_type = click.Path(exists=True, dir_okay=False, resolve_path=True) + + def convert(self, value, param, ctx): + try: + import ssl + except ImportError: + raise click.BadParameter( + 'Using "--cert" requires Python to be compiled with SSL support.', + ctx, + param, + ) from None + + try: + return self.path_type(value, param, ctx) + except click.BadParameter: + value = click.STRING(value, param, ctx).lower() + + if value == "adhoc": + try: + import cryptography # noqa: F401 + except ImportError: + raise click.BadParameter( + "Using ad-hoc certificates requires the cryptography library.", + ctx, + param, + ) from None + + return value + + obj = import_string(value, silent=True) + + if isinstance(obj, ssl.SSLContext): + return obj + + raise + + +def _validate_key(ctx, param, value): + """The ``--key`` option must be specified when ``--cert`` is a file. + Modifies the ``cert`` param to be a ``(cert, key)`` pair if needed. + """ + cert = ctx.params.get("cert") + is_adhoc = cert == "adhoc" + + try: + import ssl + except ImportError: + is_context = False + else: + is_context = isinstance(cert, ssl.SSLContext) + + if value is not None: + if is_adhoc: + raise click.BadParameter( + 'When "--cert" is "adhoc", "--key" is not used.', ctx, param + ) + + if is_context: + raise click.BadParameter( + 'When "--cert" is an SSLContext object, "--key is not used.', ctx, param + ) + + if not cert: + raise click.BadParameter('"--cert" must also be specified.', ctx, param) + + ctx.params["cert"] = cert, value + + else: + if cert and not (is_adhoc or is_context): + raise click.BadParameter('Required when using "--cert".', ctx, param) + + return value + + +class SeparatedPathType(click.Path): + """Click option type that accepts a list of values separated by the + OS's path separator (``:``, ``;`` on Windows). Each value is + validated as a :class:`click.Path` type. + """ + + def convert(self, value, param, ctx): + items = self.split_envvar_value(value) + super_convert = super().convert + return [super_convert(item, param, ctx) for item in items] + + +@click.command("run", short_help="Run a development server.") +@click.option("--host", "-h", default="127.0.0.1", help="The interface to bind to.") +@click.option("--port", "-p", default=5000, help="The port to bind to.") +@click.option( + "--cert", + type=CertParamType(), + help="Specify a certificate file to use HTTPS.", + is_eager=True, +) +@click.option( + "--key", + type=click.Path(exists=True, dir_okay=False, resolve_path=True), + callback=_validate_key, + expose_value=False, + help="The key file to use when specifying a certificate.", +) +@click.option( + "--reload/--no-reload", + default=None, + help="Enable or disable the reloader. By default the reloader " + "is active if debug is enabled.", +) +@click.option( + "--debugger/--no-debugger", + default=None, + help="Enable or disable the debugger. By default the debugger " + "is active if debug is enabled.", +) +@click.option( + "--with-threads/--without-threads", + default=True, + help="Enable or disable multithreading.", +) +@click.option( + "--extra-files", + default=None, + type=SeparatedPathType(), + help=( + "Extra files that trigger a reload on change. Multiple paths" + f" are separated by {os.path.pathsep!r}." + ), +) +@click.option( + "--exclude-patterns", + default=None, + type=SeparatedPathType(), + help=( + "Files matching these fnmatch patterns will not trigger a reload" + " on change. Multiple patterns are separated by" + f" {os.path.pathsep!r}." + ), +) +@pass_script_info +def run_command( + info, + host, + port, + reload, + debugger, + with_threads, + cert, + extra_files, + exclude_patterns, +): + """Run a local development server. + + This server is for development purposes only. It does not provide + the stability, security, or performance of production WSGI servers. + + The reloader and debugger are enabled by default with the '--debug' + option. + """ + try: + app = info.load_app() + except Exception as e: + if is_running_from_reloader(): + # When reloading, print out the error immediately, but raise + # it later so the debugger or server can handle it. + traceback.print_exc() + err = e + + def app(environ, start_response): + raise err from None + + else: + # When not reloading, raise the error immediately so the + # command fails. + raise e from None + + debug = get_debug_flag() + + if reload is None: + reload = debug + + if debugger is None: + debugger = debug + + show_server_banner(debug, info.app_import_path) + + run_simple( + host, + port, + app, + use_reloader=reload, + use_debugger=debugger, + threaded=with_threads, + ssl_context=cert, + extra_files=extra_files, + exclude_patterns=exclude_patterns, + ) + + +run_command.params.insert(0, _debug_option) + + +@click.command("shell", short_help="Run a shell in the app context.") +@with_appcontext +def shell_command() -> None: + """Run an interactive Python shell in the context of a given + Flask application. The application will populate the default + namespace of this shell according to its configuration. + + This is useful for executing small snippets of management code + without having to manually configure the application. + """ + import code + + banner = ( + f"Python {sys.version} on {sys.platform}\n" + f"App: {current_app.import_name}\n" + f"Instance: {current_app.instance_path}" + ) + ctx: dict = {} + + # Support the regular Python interpreter startup script if someone + # is using it. + startup = os.environ.get("PYTHONSTARTUP") + if startup and os.path.isfile(startup): + with open(startup) as f: + eval(compile(f.read(), startup, "exec"), ctx) + + ctx.update(current_app.make_shell_context()) + + # Site, customize, or startup script can set a hook to call when + # entering interactive mode. The default one sets up readline with + # tab and history completion. + interactive_hook = getattr(sys, "__interactivehook__", None) + + if interactive_hook is not None: + try: + import readline + from rlcompleter import Completer + except ImportError: + pass + else: + # rlcompleter uses __main__.__dict__ by default, which is + # flask.__main__. Use the shell context instead. + readline.set_completer(Completer(ctx).complete) + + interactive_hook() + + code.interact(banner=banner, local=ctx) + + +@click.command("routes", short_help="Show the routes for the app.") +@click.option( + "--sort", + "-s", + type=click.Choice(("endpoint", "methods", "rule", "match")), + default="endpoint", + help=( + 'Method to sort routes by. "match" is the order that Flask will match ' + "routes when dispatching a request." + ), +) +@click.option("--all-methods", is_flag=True, help="Show HEAD and OPTIONS methods.") +@with_appcontext +def routes_command(sort: str, all_methods: bool) -> None: + """Show all registered routes with endpoints and methods.""" + + rules = list(current_app.url_map.iter_rules()) + if not rules: + click.echo("No routes were registered.") + return + + ignored_methods = set(() if all_methods else ("HEAD", "OPTIONS")) + + if sort in ("endpoint", "rule"): + rules = sorted(rules, key=attrgetter(sort)) + elif sort == "methods": + rules = sorted(rules, key=lambda rule: sorted(rule.methods)) # type: ignore + + rule_methods = [ + ", ".join(sorted(rule.methods - ignored_methods)) # type: ignore + for rule in rules + ] + + headers = ("Endpoint", "Methods", "Rule") + widths = ( + max(len(rule.endpoint) for rule in rules), + max(len(methods) for methods in rule_methods), + max(len(rule.rule) for rule in rules), + ) + widths = [max(len(h), w) for h, w in zip(headers, widths)] + row = "{{0:<{0}}} {{1:<{1}}} {{2:<{2}}}".format(*widths) + + click.echo(row.format(*headers).strip()) + click.echo(row.format(*("-" * width for width in widths))) + + for rule, methods in zip(rules, rule_methods): + click.echo(row.format(rule.endpoint, methods, rule.rule).rstrip()) + + +cli = FlaskGroup( + name="flask", + help="""\ +A general utility script for Flask applications. + +An application to load must be given with the '--app' option, +'FLASK_APP' environment variable, or with a 'wsgi.py' or 'app.py' file +in the current directory. +""", +) + + +def main() -> None: + cli.main() + + +if __name__ == "__main__": + main() diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/flask/config.py b/docs/examples/flask/venv/lib/python3.11/site-packages/flask/config.py new file mode 100644 index 0000000..d4fc310 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/flask/config.py @@ -0,0 +1,338 @@ +import errno +import json +import os +import types +import typing as t + +from werkzeug.utils import import_string + + +class ConfigAttribute: + """Makes an attribute forward to the config""" + + def __init__(self, name: str, get_converter: t.Optional[t.Callable] = None) -> None: + self.__name__ = name + self.get_converter = get_converter + + def __get__(self, obj: t.Any, owner: t.Any = None) -> t.Any: + if obj is None: + return self + rv = obj.config[self.__name__] + if self.get_converter is not None: + rv = self.get_converter(rv) + return rv + + def __set__(self, obj: t.Any, value: t.Any) -> None: + obj.config[self.__name__] = value + + +class Config(dict): + """Works exactly like a dict but provides ways to fill it from files + or special dictionaries. There are two common patterns to populate the + config. + + Either you can fill the config from a config file:: + + app.config.from_pyfile('yourconfig.cfg') + + Or alternatively you can define the configuration options in the + module that calls :meth:`from_object` or provide an import path to + a module that should be loaded. It is also possible to tell it to + use the same module and with that provide the configuration values + just before the call:: + + DEBUG = True + SECRET_KEY = 'development key' + app.config.from_object(__name__) + + In both cases (loading from any Python file or loading from modules), + only uppercase keys are added to the config. This makes it possible to use + lowercase values in the config file for temporary values that are not added + to the config or to define the config keys in the same file that implements + the application. + + Probably the most interesting way to load configurations is from an + environment variable pointing to a file:: + + app.config.from_envvar('YOURAPPLICATION_SETTINGS') + + In this case before launching the application you have to set this + environment variable to the file you want to use. On Linux and OS X + use the export statement:: + + export YOURAPPLICATION_SETTINGS='/path/to/config/file' + + On windows use `set` instead. + + :param root_path: path to which files are read relative from. When the + config object is created by the application, this is + the application's :attr:`~flask.Flask.root_path`. + :param defaults: an optional dictionary of default values + """ + + def __init__(self, root_path: str, defaults: t.Optional[dict] = None) -> None: + super().__init__(defaults or {}) + self.root_path = root_path + + def from_envvar(self, variable_name: str, silent: bool = False) -> bool: + """Loads a configuration from an environment variable pointing to + a configuration file. This is basically just a shortcut with nicer + error messages for this line of code:: + + app.config.from_pyfile(os.environ['YOURAPPLICATION_SETTINGS']) + + :param variable_name: name of the environment variable + :param silent: set to ``True`` if you want silent failure for missing + files. + :return: ``True`` if the file was loaded successfully. + """ + rv = os.environ.get(variable_name) + if not rv: + if silent: + return False + raise RuntimeError( + f"The environment variable {variable_name!r} is not set" + " and as such configuration could not be loaded. Set" + " this variable and make it point to a configuration" + " file" + ) + return self.from_pyfile(rv, silent=silent) + + def from_prefixed_env( + self, prefix: str = "FLASK", *, loads: t.Callable[[str], t.Any] = json.loads + ) -> bool: + """Load any environment variables that start with ``FLASK_``, + dropping the prefix from the env key for the config key. Values + are passed through a loading function to attempt to convert them + to more specific types than strings. + + Keys are loaded in :func:`sorted` order. + + The default loading function attempts to parse values as any + valid JSON type, including dicts and lists. + + Specific items in nested dicts can be set by separating the + keys with double underscores (``__``). If an intermediate key + doesn't exist, it will be initialized to an empty dict. + + :param prefix: Load env vars that start with this prefix, + separated with an underscore (``_``). + :param loads: Pass each string value to this function and use + the returned value as the config value. If any error is + raised it is ignored and the value remains a string. The + default is :func:`json.loads`. + + .. versionadded:: 2.1 + """ + prefix = f"{prefix}_" + len_prefix = len(prefix) + + for key in sorted(os.environ): + if not key.startswith(prefix): + continue + + value = os.environ[key] + + try: + value = loads(value) + except Exception: + # Keep the value as a string if loading failed. + pass + + # Change to key.removeprefix(prefix) on Python >= 3.9. + key = key[len_prefix:] + + if "__" not in key: + # A non-nested key, set directly. + self[key] = value + continue + + # Traverse nested dictionaries with keys separated by "__". + current = self + *parts, tail = key.split("__") + + for part in parts: + # If an intermediate dict does not exist, create it. + if part not in current: + current[part] = {} + + current = current[part] + + current[tail] = value + + return True + + def from_pyfile(self, filename: str, silent: bool = False) -> bool: + """Updates the values in the config from a Python file. This function + behaves as if the file was imported as module with the + :meth:`from_object` function. + + :param filename: the filename of the config. This can either be an + absolute filename or a filename relative to the + root path. + :param silent: set to ``True`` if you want silent failure for missing + files. + :return: ``True`` if the file was loaded successfully. + + .. versionadded:: 0.7 + `silent` parameter. + """ + filename = os.path.join(self.root_path, filename) + d = types.ModuleType("config") + d.__file__ = filename + try: + with open(filename, mode="rb") as config_file: + exec(compile(config_file.read(), filename, "exec"), d.__dict__) + except OSError as e: + if silent and e.errno in (errno.ENOENT, errno.EISDIR, errno.ENOTDIR): + return False + e.strerror = f"Unable to load configuration file ({e.strerror})" + raise + self.from_object(d) + return True + + def from_object(self, obj: t.Union[object, str]) -> None: + """Updates the values from the given object. An object can be of one + of the following two types: + + - a string: in this case the object with that name will be imported + - an actual object reference: that object is used directly + + Objects are usually either modules or classes. :meth:`from_object` + loads only the uppercase attributes of the module/class. A ``dict`` + object will not work with :meth:`from_object` because the keys of a + ``dict`` are not attributes of the ``dict`` class. + + Example of module-based configuration:: + + app.config.from_object('yourapplication.default_config') + from yourapplication import default_config + app.config.from_object(default_config) + + Nothing is done to the object before loading. If the object is a + class and has ``@property`` attributes, it needs to be + instantiated before being passed to this method. + + You should not use this function to load the actual configuration but + rather configuration defaults. The actual config should be loaded + with :meth:`from_pyfile` and ideally from a location not within the + package because the package might be installed system wide. + + See :ref:`config-dev-prod` for an example of class-based configuration + using :meth:`from_object`. + + :param obj: an import name or object + """ + if isinstance(obj, str): + obj = import_string(obj) + for key in dir(obj): + if key.isupper(): + self[key] = getattr(obj, key) + + def from_file( + self, + filename: str, + load: t.Callable[[t.IO[t.Any]], t.Mapping], + silent: bool = False, + ) -> bool: + """Update the values in the config from a file that is loaded + using the ``load`` parameter. The loaded data is passed to the + :meth:`from_mapping` method. + + .. code-block:: python + + import json + app.config.from_file("config.json", load=json.load) + + import toml + app.config.from_file("config.toml", load=toml.load) + + :param filename: The path to the data file. This can be an + absolute path or relative to the config root path. + :param load: A callable that takes a file handle and returns a + mapping of loaded data from the file. + :type load: ``Callable[[Reader], Mapping]`` where ``Reader`` + implements a ``read`` method. + :param silent: Ignore the file if it doesn't exist. + :return: ``True`` if the file was loaded successfully. + + .. versionadded:: 2.0 + """ + filename = os.path.join(self.root_path, filename) + + try: + with open(filename) as f: + obj = load(f) + except OSError as e: + if silent and e.errno in (errno.ENOENT, errno.EISDIR): + return False + + e.strerror = f"Unable to load configuration file ({e.strerror})" + raise + + return self.from_mapping(obj) + + def from_mapping( + self, mapping: t.Optional[t.Mapping[str, t.Any]] = None, **kwargs: t.Any + ) -> bool: + """Updates the config like :meth:`update` ignoring items with + non-upper keys. + + :return: Always returns ``True``. + + .. versionadded:: 0.11 + """ + mappings: t.Dict[str, t.Any] = {} + if mapping is not None: + mappings.update(mapping) + mappings.update(kwargs) + for key, value in mappings.items(): + if key.isupper(): + self[key] = value + return True + + def get_namespace( + self, namespace: str, lowercase: bool = True, trim_namespace: bool = True + ) -> t.Dict[str, t.Any]: + """Returns a dictionary containing a subset of configuration options + that match the specified namespace/prefix. Example usage:: + + app.config['IMAGE_STORE_TYPE'] = 'fs' + app.config['IMAGE_STORE_PATH'] = '/var/app/images' + app.config['IMAGE_STORE_BASE_URL'] = 'http://img.website.com' + image_store_config = app.config.get_namespace('IMAGE_STORE_') + + The resulting dictionary `image_store_config` would look like:: + + { + 'type': 'fs', + 'path': '/var/app/images', + 'base_url': 'http://img.website.com' + } + + This is often useful when configuration options map directly to + keyword arguments in functions or class constructors. + + :param namespace: a configuration namespace + :param lowercase: a flag indicating if the keys of the resulting + dictionary should be lowercase + :param trim_namespace: a flag indicating if the keys of the resulting + dictionary should not include the namespace + + .. versionadded:: 0.11 + """ + rv = {} + for k, v in self.items(): + if not k.startswith(namespace): + continue + if trim_namespace: + key = k[len(namespace) :] + else: + key = k + if lowercase: + key = key.lower() + rv[key] = v + return rv + + def __repr__(self) -> str: + return f"<{type(self).__name__} {dict.__repr__(self)}>" diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/flask/ctx.py b/docs/examples/flask/venv/lib/python3.11/site-packages/flask/ctx.py new file mode 100644 index 0000000..c79c26d --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/flask/ctx.py @@ -0,0 +1,438 @@ +import contextvars +import sys +import typing as t +from functools import update_wrapper +from types import TracebackType + +from werkzeug.exceptions import HTTPException + +from . import typing as ft +from .globals import _cv_app +from .globals import _cv_request +from .signals import appcontext_popped +from .signals import appcontext_pushed + +if t.TYPE_CHECKING: # pragma: no cover + from .app import Flask + from .sessions import SessionMixin + from .wrappers import Request + + +# a singleton sentinel value for parameter defaults +_sentinel = object() + + +class _AppCtxGlobals: + """A plain object. Used as a namespace for storing data during an + application context. + + Creating an app context automatically creates this object, which is + made available as the :data:`g` proxy. + + .. describe:: 'key' in g + + Check whether an attribute is present. + + .. versionadded:: 0.10 + + .. describe:: iter(g) + + Return an iterator over the attribute names. + + .. versionadded:: 0.10 + """ + + # Define attr methods to let mypy know this is a namespace object + # that has arbitrary attributes. + + def __getattr__(self, name: str) -> t.Any: + try: + return self.__dict__[name] + except KeyError: + raise AttributeError(name) from None + + def __setattr__(self, name: str, value: t.Any) -> None: + self.__dict__[name] = value + + def __delattr__(self, name: str) -> None: + try: + del self.__dict__[name] + except KeyError: + raise AttributeError(name) from None + + def get(self, name: str, default: t.Optional[t.Any] = None) -> t.Any: + """Get an attribute by name, or a default value. Like + :meth:`dict.get`. + + :param name: Name of attribute to get. + :param default: Value to return if the attribute is not present. + + .. versionadded:: 0.10 + """ + return self.__dict__.get(name, default) + + def pop(self, name: str, default: t.Any = _sentinel) -> t.Any: + """Get and remove an attribute by name. Like :meth:`dict.pop`. + + :param name: Name of attribute to pop. + :param default: Value to return if the attribute is not present, + instead of raising a ``KeyError``. + + .. versionadded:: 0.11 + """ + if default is _sentinel: + return self.__dict__.pop(name) + else: + return self.__dict__.pop(name, default) + + def setdefault(self, name: str, default: t.Any = None) -> t.Any: + """Get the value of an attribute if it is present, otherwise + set and return a default value. Like :meth:`dict.setdefault`. + + :param name: Name of attribute to get. + :param default: Value to set and return if the attribute is not + present. + + .. versionadded:: 0.11 + """ + return self.__dict__.setdefault(name, default) + + def __contains__(self, item: str) -> bool: + return item in self.__dict__ + + def __iter__(self) -> t.Iterator[str]: + return iter(self.__dict__) + + def __repr__(self) -> str: + ctx = _cv_app.get(None) + if ctx is not None: + return f"" + return object.__repr__(self) + + +def after_this_request(f: ft.AfterRequestCallable) -> ft.AfterRequestCallable: + """Executes a function after this request. This is useful to modify + response objects. The function is passed the response object and has + to return the same or a new one. + + Example:: + + @app.route('/') + def index(): + @after_this_request + def add_header(response): + response.headers['X-Foo'] = 'Parachute' + return response + return 'Hello World!' + + This is more useful if a function other than the view function wants to + modify a response. For instance think of a decorator that wants to add + some headers without converting the return value into a response object. + + .. versionadded:: 0.9 + """ + ctx = _cv_request.get(None) + + if ctx is None: + raise RuntimeError( + "'after_this_request' can only be used when a request" + " context is active, such as in a view function." + ) + + ctx._after_request_functions.append(f) + return f + + +def copy_current_request_context(f: t.Callable) -> t.Callable: + """A helper function that decorates a function to retain the current + request context. This is useful when working with greenlets. The moment + the function is decorated a copy of the request context is created and + then pushed when the function is called. The current session is also + included in the copied request context. + + Example:: + + import gevent + from flask import copy_current_request_context + + @app.route('/') + def index(): + @copy_current_request_context + def do_some_work(): + # do some work here, it can access flask.request or + # flask.session like you would otherwise in the view function. + ... + gevent.spawn(do_some_work) + return 'Regular response' + + .. versionadded:: 0.10 + """ + ctx = _cv_request.get(None) + + if ctx is None: + raise RuntimeError( + "'copy_current_request_context' can only be used when a" + " request context is active, such as in a view function." + ) + + ctx = ctx.copy() + + def wrapper(*args, **kwargs): + with ctx: + return ctx.app.ensure_sync(f)(*args, **kwargs) + + return update_wrapper(wrapper, f) + + +def has_request_context() -> bool: + """If you have code that wants to test if a request context is there or + not this function can be used. For instance, you may want to take advantage + of request information if the request object is available, but fail + silently if it is unavailable. + + :: + + class User(db.Model): + + def __init__(self, username, remote_addr=None): + self.username = username + if remote_addr is None and has_request_context(): + remote_addr = request.remote_addr + self.remote_addr = remote_addr + + Alternatively you can also just test any of the context bound objects + (such as :class:`request` or :class:`g`) for truthness:: + + class User(db.Model): + + def __init__(self, username, remote_addr=None): + self.username = username + if remote_addr is None and request: + remote_addr = request.remote_addr + self.remote_addr = remote_addr + + .. versionadded:: 0.7 + """ + return _cv_request.get(None) is not None + + +def has_app_context() -> bool: + """Works like :func:`has_request_context` but for the application + context. You can also just do a boolean check on the + :data:`current_app` object instead. + + .. versionadded:: 0.9 + """ + return _cv_app.get(None) is not None + + +class AppContext: + """The app context contains application-specific information. An app + context is created and pushed at the beginning of each request if + one is not already active. An app context is also pushed when + running CLI commands. + """ + + def __init__(self, app: "Flask") -> None: + self.app = app + self.url_adapter = app.create_url_adapter(None) + self.g: _AppCtxGlobals = app.app_ctx_globals_class() + self._cv_tokens: t.List[contextvars.Token] = [] + + def push(self) -> None: + """Binds the app context to the current context.""" + self._cv_tokens.append(_cv_app.set(self)) + appcontext_pushed.send(self.app) + + def pop(self, exc: t.Optional[BaseException] = _sentinel) -> None: # type: ignore + """Pops the app context.""" + try: + if len(self._cv_tokens) == 1: + if exc is _sentinel: + exc = sys.exc_info()[1] + self.app.do_teardown_appcontext(exc) + finally: + ctx = _cv_app.get() + _cv_app.reset(self._cv_tokens.pop()) + + if ctx is not self: + raise AssertionError( + f"Popped wrong app context. ({ctx!r} instead of {self!r})" + ) + + appcontext_popped.send(self.app) + + def __enter__(self) -> "AppContext": + self.push() + return self + + def __exit__( + self, + exc_type: t.Optional[type], + exc_value: t.Optional[BaseException], + tb: t.Optional[TracebackType], + ) -> None: + self.pop(exc_value) + + +class RequestContext: + """The request context contains per-request information. The Flask + app creates and pushes it at the beginning of the request, then pops + it at the end of the request. It will create the URL adapter and + request object for the WSGI environment provided. + + Do not attempt to use this class directly, instead use + :meth:`~flask.Flask.test_request_context` and + :meth:`~flask.Flask.request_context` to create this object. + + When the request context is popped, it will evaluate all the + functions registered on the application for teardown execution + (:meth:`~flask.Flask.teardown_request`). + + The request context is automatically popped at the end of the + request. When using the interactive debugger, the context will be + restored so ``request`` is still accessible. Similarly, the test + client can preserve the context after the request ends. However, + teardown functions may already have closed some resources such as + database connections. + """ + + def __init__( + self, + app: "Flask", + environ: dict, + request: t.Optional["Request"] = None, + session: t.Optional["SessionMixin"] = None, + ) -> None: + self.app = app + if request is None: + request = app.request_class(environ) + request.json_module = app.json + self.request: Request = request + self.url_adapter = None + try: + self.url_adapter = app.create_url_adapter(self.request) + except HTTPException as e: + self.request.routing_exception = e + self.flashes: t.Optional[t.List[t.Tuple[str, str]]] = None + self.session: t.Optional["SessionMixin"] = session + # Functions that should be executed after the request on the response + # object. These will be called before the regular "after_request" + # functions. + self._after_request_functions: t.List[ft.AfterRequestCallable] = [] + + self._cv_tokens: t.List[t.Tuple[contextvars.Token, t.Optional[AppContext]]] = [] + + def copy(self) -> "RequestContext": + """Creates a copy of this request context with the same request object. + This can be used to move a request context to a different greenlet. + Because the actual request object is the same this cannot be used to + move a request context to a different thread unless access to the + request object is locked. + + .. versionadded:: 0.10 + + .. versionchanged:: 1.1 + The current session object is used instead of reloading the original + data. This prevents `flask.session` pointing to an out-of-date object. + """ + return self.__class__( + self.app, + environ=self.request.environ, + request=self.request, + session=self.session, + ) + + def match_request(self) -> None: + """Can be overridden by a subclass to hook into the matching + of the request. + """ + try: + result = self.url_adapter.match(return_rule=True) # type: ignore + self.request.url_rule, self.request.view_args = result # type: ignore + except HTTPException as e: + self.request.routing_exception = e + + def push(self) -> None: + # Before we push the request context we have to ensure that there + # is an application context. + app_ctx = _cv_app.get(None) + + if app_ctx is None or app_ctx.app is not self.app: + app_ctx = self.app.app_context() + app_ctx.push() + else: + app_ctx = None + + self._cv_tokens.append((_cv_request.set(self), app_ctx)) + + # Open the session at the moment that the request context is available. + # This allows a custom open_session method to use the request context. + # Only open a new session if this is the first time the request was + # pushed, otherwise stream_with_context loses the session. + if self.session is None: + session_interface = self.app.session_interface + self.session = session_interface.open_session(self.app, self.request) + + if self.session is None: + self.session = session_interface.make_null_session(self.app) + + # Match the request URL after loading the session, so that the + # session is available in custom URL converters. + if self.url_adapter is not None: + self.match_request() + + def pop(self, exc: t.Optional[BaseException] = _sentinel) -> None: # type: ignore + """Pops the request context and unbinds it by doing that. This will + also trigger the execution of functions registered by the + :meth:`~flask.Flask.teardown_request` decorator. + + .. versionchanged:: 0.9 + Added the `exc` argument. + """ + clear_request = len(self._cv_tokens) == 1 + + try: + if clear_request: + if exc is _sentinel: + exc = sys.exc_info()[1] + self.app.do_teardown_request(exc) + + request_close = getattr(self.request, "close", None) + if request_close is not None: + request_close() + finally: + ctx = _cv_request.get() + token, app_ctx = self._cv_tokens.pop() + _cv_request.reset(token) + + # get rid of circular dependencies at the end of the request + # so that we don't require the GC to be active. + if clear_request: + ctx.request.environ["werkzeug.request"] = None + + if app_ctx is not None: + app_ctx.pop(exc) + + if ctx is not self: + raise AssertionError( + f"Popped wrong request context. ({ctx!r} instead of {self!r})" + ) + + def __enter__(self) -> "RequestContext": + self.push() + return self + + def __exit__( + self, + exc_type: t.Optional[type], + exc_value: t.Optional[BaseException], + tb: t.Optional[TracebackType], + ) -> None: + self.pop(exc_value) + + def __repr__(self) -> str: + return ( + f"<{type(self).__name__} {self.request.url!r}" + f" [{self.request.method}] of {self.app.name}>" + ) diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/flask/debughelpers.py b/docs/examples/flask/venv/lib/python3.11/site-packages/flask/debughelpers.py new file mode 100644 index 0000000..b063989 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/flask/debughelpers.py @@ -0,0 +1,158 @@ +import typing as t + +from .app import Flask +from .blueprints import Blueprint +from .globals import request_ctx + + +class UnexpectedUnicodeError(AssertionError, UnicodeError): + """Raised in places where we want some better error reporting for + unexpected unicode or binary data. + """ + + +class DebugFilesKeyError(KeyError, AssertionError): + """Raised from request.files during debugging. The idea is that it can + provide a better error message than just a generic KeyError/BadRequest. + """ + + def __init__(self, request, key): + form_matches = request.form.getlist(key) + buf = [ + f"You tried to access the file {key!r} in the request.files" + " dictionary but it does not exist. The mimetype for the" + f" request is {request.mimetype!r} instead of" + " 'multipart/form-data' which means that no file contents" + " were transmitted. To fix this error you should provide" + ' enctype="multipart/form-data" in your form.' + ] + if form_matches: + names = ", ".join(repr(x) for x in form_matches) + buf.append( + "\n\nThe browser instead transmitted some file names. " + f"This was submitted: {names}" + ) + self.msg = "".join(buf) + + def __str__(self): + return self.msg + + +class FormDataRoutingRedirect(AssertionError): + """This exception is raised in debug mode if a routing redirect + would cause the browser to drop the method or body. This happens + when method is not GET, HEAD or OPTIONS and the status code is not + 307 or 308. + """ + + def __init__(self, request): + exc = request.routing_exception + buf = [ + f"A request was sent to '{request.url}', but routing issued" + f" a redirect to the canonical URL '{exc.new_url}'." + ] + + if f"{request.base_url}/" == exc.new_url.partition("?")[0]: + buf.append( + " The URL was defined with a trailing slash. Flask" + " will redirect to the URL with a trailing slash if it" + " was accessed without one." + ) + + buf.append( + " Send requests to the canonical URL, or use 307 or 308 for" + " routing redirects. Otherwise, browsers will drop form" + " data.\n\n" + "This exception is only raised in debug mode." + ) + super().__init__("".join(buf)) + + +def attach_enctype_error_multidict(request): + """Patch ``request.files.__getitem__`` to raise a descriptive error + about ``enctype=multipart/form-data``. + + :param request: The request to patch. + :meta private: + """ + oldcls = request.files.__class__ + + class newcls(oldcls): + def __getitem__(self, key): + try: + return super().__getitem__(key) + except KeyError as e: + if key not in request.form: + raise + + raise DebugFilesKeyError(request, key).with_traceback( + e.__traceback__ + ) from None + + newcls.__name__ = oldcls.__name__ + newcls.__module__ = oldcls.__module__ + request.files.__class__ = newcls + + +def _dump_loader_info(loader) -> t.Generator: + yield f"class: {type(loader).__module__}.{type(loader).__name__}" + for key, value in sorted(loader.__dict__.items()): + if key.startswith("_"): + continue + if isinstance(value, (tuple, list)): + if not all(isinstance(x, str) for x in value): + continue + yield f"{key}:" + for item in value: + yield f" - {item}" + continue + elif not isinstance(value, (str, int, float, bool)): + continue + yield f"{key}: {value!r}" + + +def explain_template_loading_attempts(app: Flask, template, attempts) -> None: + """This should help developers understand what failed""" + info = [f"Locating template {template!r}:"] + total_found = 0 + blueprint = None + if request_ctx and request_ctx.request.blueprint is not None: + blueprint = request_ctx.request.blueprint + + for idx, (loader, srcobj, triple) in enumerate(attempts): + if isinstance(srcobj, Flask): + src_info = f"application {srcobj.import_name!r}" + elif isinstance(srcobj, Blueprint): + src_info = f"blueprint {srcobj.name!r} ({srcobj.import_name})" + else: + src_info = repr(srcobj) + + info.append(f"{idx + 1:5}: trying loader of {src_info}") + + for line in _dump_loader_info(loader): + info.append(f" {line}") + + if triple is None: + detail = "no match" + else: + detail = f"found ({triple[1] or ''!r})" + total_found += 1 + info.append(f" -> {detail}") + + seems_fishy = False + if total_found == 0: + info.append("Error: the template could not be found.") + seems_fishy = True + elif total_found > 1: + info.append("Warning: multiple loaders returned a match for the template.") + seems_fishy = True + + if blueprint is not None and seems_fishy: + info.append( + " The template was looked up from an endpoint that belongs" + f" to the blueprint {blueprint!r}." + ) + info.append(" Maybe you did not place a template in the right folder?") + info.append(" See https://flask.palletsprojects.com/blueprints/#templates") + + app.logger.info("\n".join(info)) diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/flask/globals.py b/docs/examples/flask/venv/lib/python3.11/site-packages/flask/globals.py new file mode 100644 index 0000000..254da42 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/flask/globals.py @@ -0,0 +1,107 @@ +import typing as t +from contextvars import ContextVar + +from werkzeug.local import LocalProxy + +if t.TYPE_CHECKING: # pragma: no cover + from .app import Flask + from .ctx import _AppCtxGlobals + from .ctx import AppContext + from .ctx import RequestContext + from .sessions import SessionMixin + from .wrappers import Request + + +class _FakeStack: + def __init__(self, name: str, cv: ContextVar[t.Any]) -> None: + self.name = name + self.cv = cv + + def _warn(self): + import warnings + + warnings.warn( + f"'_{self.name}_ctx_stack' is deprecated and will be" + " removed in Flask 2.3. Use 'g' to store data, or" + f" '{self.name}_ctx' to access the current context.", + DeprecationWarning, + stacklevel=3, + ) + + def push(self, obj: t.Any) -> None: + self._warn() + self.cv.set(obj) + + def pop(self) -> t.Any: + self._warn() + ctx = self.cv.get(None) + self.cv.set(None) + return ctx + + @property + def top(self) -> t.Optional[t.Any]: + self._warn() + return self.cv.get(None) + + +_no_app_msg = """\ +Working outside of application context. + +This typically means that you attempted to use functionality that needed +the current application. To solve this, set up an application context +with app.app_context(). See the documentation for more information.\ +""" +_cv_app: ContextVar["AppContext"] = ContextVar("flask.app_ctx") +__app_ctx_stack = _FakeStack("app", _cv_app) +app_ctx: "AppContext" = LocalProxy( # type: ignore[assignment] + _cv_app, unbound_message=_no_app_msg +) +current_app: "Flask" = LocalProxy( # type: ignore[assignment] + _cv_app, "app", unbound_message=_no_app_msg +) +g: "_AppCtxGlobals" = LocalProxy( # type: ignore[assignment] + _cv_app, "g", unbound_message=_no_app_msg +) + +_no_req_msg = """\ +Working outside of request context. + +This typically means that you attempted to use functionality that needed +an active HTTP request. Consult the documentation on testing for +information about how to avoid this problem.\ +""" +_cv_request: ContextVar["RequestContext"] = ContextVar("flask.request_ctx") +__request_ctx_stack = _FakeStack("request", _cv_request) +request_ctx: "RequestContext" = LocalProxy( # type: ignore[assignment] + _cv_request, unbound_message=_no_req_msg +) +request: "Request" = LocalProxy( # type: ignore[assignment] + _cv_request, "request", unbound_message=_no_req_msg +) +session: "SessionMixin" = LocalProxy( # type: ignore[assignment] + _cv_request, "session", unbound_message=_no_req_msg +) + + +def __getattr__(name: str) -> t.Any: + if name == "_app_ctx_stack": + import warnings + + warnings.warn( + "'_app_ctx_stack' is deprecated and will be removed in Flask 2.3.", + DeprecationWarning, + stacklevel=2, + ) + return __app_ctx_stack + + if name == "_request_ctx_stack": + import warnings + + warnings.warn( + "'_request_ctx_stack' is deprecated and will be removed in Flask 2.3.", + DeprecationWarning, + stacklevel=2, + ) + return __request_ctx_stack + + raise AttributeError(name) diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/flask/helpers.py b/docs/examples/flask/venv/lib/python3.11/site-packages/flask/helpers.py new file mode 100644 index 0000000..3833cb8 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/flask/helpers.py @@ -0,0 +1,705 @@ +import os +import pkgutil +import socket +import sys +import typing as t +from datetime import datetime +from functools import lru_cache +from functools import update_wrapper +from threading import RLock + +import werkzeug.utils +from werkzeug.exceptions import abort as _wz_abort +from werkzeug.utils import redirect as _wz_redirect + +from .globals import _cv_request +from .globals import current_app +from .globals import request +from .globals import request_ctx +from .globals import session +from .signals import message_flashed + +if t.TYPE_CHECKING: # pragma: no cover + from werkzeug.wrappers import Response as BaseResponse + from .wrappers import Response + import typing_extensions as te + + +def get_env() -> str: + """Get the environment the app is running in, indicated by the + :envvar:`FLASK_ENV` environment variable. The default is + ``'production'``. + + .. deprecated:: 2.2 + Will be removed in Flask 2.3. + """ + import warnings + + warnings.warn( + "'FLASK_ENV' and 'get_env' are deprecated and will be removed" + " in Flask 2.3. Use 'FLASK_DEBUG' instead.", + DeprecationWarning, + stacklevel=2, + ) + return os.environ.get("FLASK_ENV") or "production" + + +def get_debug_flag() -> bool: + """Get whether debug mode should be enabled for the app, indicated by the + :envvar:`FLASK_DEBUG` environment variable. The default is ``False``. + """ + val = os.environ.get("FLASK_DEBUG") + + if not val: + env = os.environ.get("FLASK_ENV") + + if env is not None: + print( + "'FLASK_ENV' is deprecated and will not be used in" + " Flask 2.3. Use 'FLASK_DEBUG' instead.", + file=sys.stderr, + ) + return env == "development" + + return False + + return val.lower() not in {"0", "false", "no"} + + +def get_load_dotenv(default: bool = True) -> bool: + """Get whether the user has disabled loading default dotenv files by + setting :envvar:`FLASK_SKIP_DOTENV`. The default is ``True``, load + the files. + + :param default: What to return if the env var isn't set. + """ + val = os.environ.get("FLASK_SKIP_DOTENV") + + if not val: + return default + + return val.lower() in ("0", "false", "no") + + +def stream_with_context( + generator_or_function: t.Union[ + t.Iterator[t.AnyStr], t.Callable[..., t.Iterator[t.AnyStr]] + ] +) -> t.Iterator[t.AnyStr]: + """Request contexts disappear when the response is started on the server. + This is done for efficiency reasons and to make it less likely to encounter + memory leaks with badly written WSGI middlewares. The downside is that if + you are using streamed responses, the generator cannot access request bound + information any more. + + This function however can help you keep the context around for longer:: + + from flask import stream_with_context, request, Response + + @app.route('/stream') + def streamed_response(): + @stream_with_context + def generate(): + yield 'Hello ' + yield request.args['name'] + yield '!' + return Response(generate()) + + Alternatively it can also be used around a specific generator:: + + from flask import stream_with_context, request, Response + + @app.route('/stream') + def streamed_response(): + def generate(): + yield 'Hello ' + yield request.args['name'] + yield '!' + return Response(stream_with_context(generate())) + + .. versionadded:: 0.9 + """ + try: + gen = iter(generator_or_function) # type: ignore + except TypeError: + + def decorator(*args: t.Any, **kwargs: t.Any) -> t.Any: + gen = generator_or_function(*args, **kwargs) # type: ignore + return stream_with_context(gen) + + return update_wrapper(decorator, generator_or_function) # type: ignore + + def generator() -> t.Generator: + ctx = _cv_request.get(None) + if ctx is None: + raise RuntimeError( + "'stream_with_context' can only be used when a request" + " context is active, such as in a view function." + ) + with ctx: + # Dummy sentinel. Has to be inside the context block or we're + # not actually keeping the context around. + yield None + + # The try/finally is here so that if someone passes a WSGI level + # iterator in we're still running the cleanup logic. Generators + # don't need that because they are closed on their destruction + # automatically. + try: + yield from gen + finally: + if hasattr(gen, "close"): + gen.close() + + # The trick is to start the generator. Then the code execution runs until + # the first dummy None is yielded at which point the context was already + # pushed. This item is discarded. Then when the iteration continues the + # real generator is executed. + wrapped_g = generator() + next(wrapped_g) + return wrapped_g + + +def make_response(*args: t.Any) -> "Response": + """Sometimes it is necessary to set additional headers in a view. Because + views do not have to return response objects but can return a value that + is converted into a response object by Flask itself, it becomes tricky to + add headers to it. This function can be called instead of using a return + and you will get a response object which you can use to attach headers. + + If view looked like this and you want to add a new header:: + + def index(): + return render_template('index.html', foo=42) + + You can now do something like this:: + + def index(): + response = make_response(render_template('index.html', foo=42)) + response.headers['X-Parachutes'] = 'parachutes are cool' + return response + + This function accepts the very same arguments you can return from a + view function. This for example creates a response with a 404 error + code:: + + response = make_response(render_template('not_found.html'), 404) + + The other use case of this function is to force the return value of a + view function into a response which is helpful with view + decorators:: + + response = make_response(view_function()) + response.headers['X-Parachutes'] = 'parachutes are cool' + + Internally this function does the following things: + + - if no arguments are passed, it creates a new response argument + - if one argument is passed, :meth:`flask.Flask.make_response` + is invoked with it. + - if more than one argument is passed, the arguments are passed + to the :meth:`flask.Flask.make_response` function as tuple. + + .. versionadded:: 0.6 + """ + if not args: + return current_app.response_class() + if len(args) == 1: + args = args[0] + return current_app.make_response(args) # type: ignore + + +def url_for( + endpoint: str, + *, + _anchor: t.Optional[str] = None, + _method: t.Optional[str] = None, + _scheme: t.Optional[str] = None, + _external: t.Optional[bool] = None, + **values: t.Any, +) -> str: + """Generate a URL to the given endpoint with the given values. + + This requires an active request or application context, and calls + :meth:`current_app.url_for() `. See that method + for full documentation. + + :param endpoint: The endpoint name associated with the URL to + generate. If this starts with a ``.``, the current blueprint + name (if any) will be used. + :param _anchor: If given, append this as ``#anchor`` to the URL. + :param _method: If given, generate the URL associated with this + method for the endpoint. + :param _scheme: If given, the URL will have this scheme if it is + external. + :param _external: If given, prefer the URL to be internal (False) or + require it to be external (True). External URLs include the + scheme and domain. When not in an active request, URLs are + external by default. + :param values: Values to use for the variable parts of the URL rule. + Unknown keys are appended as query string arguments, like + ``?a=b&c=d``. + + .. versionchanged:: 2.2 + Calls ``current_app.url_for``, allowing an app to override the + behavior. + + .. versionchanged:: 0.10 + The ``_scheme`` parameter was added. + + .. versionchanged:: 0.9 + The ``_anchor`` and ``_method`` parameters were added. + + .. versionchanged:: 0.9 + Calls ``app.handle_url_build_error`` on build errors. + """ + return current_app.url_for( + endpoint, + _anchor=_anchor, + _method=_method, + _scheme=_scheme, + _external=_external, + **values, + ) + + +def redirect( + location: str, code: int = 302, Response: t.Optional[t.Type["BaseResponse"]] = None +) -> "BaseResponse": + """Create a redirect response object. + + If :data:`~flask.current_app` is available, it will use its + :meth:`~flask.Flask.redirect` method, otherwise it will use + :func:`werkzeug.utils.redirect`. + + :param location: The URL to redirect to. + :param code: The status code for the redirect. + :param Response: The response class to use. Not used when + ``current_app`` is active, which uses ``app.response_class``. + + .. versionadded:: 2.2 + Calls ``current_app.redirect`` if available instead of always + using Werkzeug's default ``redirect``. + """ + if current_app: + return current_app.redirect(location, code=code) + + return _wz_redirect(location, code=code, Response=Response) + + +def abort( + code: t.Union[int, "BaseResponse"], *args: t.Any, **kwargs: t.Any +) -> "te.NoReturn": + """Raise an :exc:`~werkzeug.exceptions.HTTPException` for the given + status code. + + If :data:`~flask.current_app` is available, it will call its + :attr:`~flask.Flask.aborter` object, otherwise it will use + :func:`werkzeug.exceptions.abort`. + + :param code: The status code for the exception, which must be + registered in ``app.aborter``. + :param args: Passed to the exception. + :param kwargs: Passed to the exception. + + .. versionadded:: 2.2 + Calls ``current_app.aborter`` if available instead of always + using Werkzeug's default ``abort``. + """ + if current_app: + current_app.aborter(code, *args, **kwargs) + + _wz_abort(code, *args, **kwargs) + + +def get_template_attribute(template_name: str, attribute: str) -> t.Any: + """Loads a macro (or variable) a template exports. This can be used to + invoke a macro from within Python code. If you for example have a + template named :file:`_cider.html` with the following contents: + + .. sourcecode:: html+jinja + + {% macro hello(name) %}Hello {{ name }}!{% endmacro %} + + You can access this from Python code like this:: + + hello = get_template_attribute('_cider.html', 'hello') + return hello('World') + + .. versionadded:: 0.2 + + :param template_name: the name of the template + :param attribute: the name of the variable of macro to access + """ + return getattr(current_app.jinja_env.get_template(template_name).module, attribute) + + +def flash(message: str, category: str = "message") -> None: + """Flashes a message to the next request. In order to remove the + flashed message from the session and to display it to the user, + the template has to call :func:`get_flashed_messages`. + + .. versionchanged:: 0.3 + `category` parameter added. + + :param message: the message to be flashed. + :param category: the category for the message. The following values + are recommended: ``'message'`` for any kind of message, + ``'error'`` for errors, ``'info'`` for information + messages and ``'warning'`` for warnings. However any + kind of string can be used as category. + """ + # Original implementation: + # + # session.setdefault('_flashes', []).append((category, message)) + # + # This assumed that changes made to mutable structures in the session are + # always in sync with the session object, which is not true for session + # implementations that use external storage for keeping their keys/values. + flashes = session.get("_flashes", []) + flashes.append((category, message)) + session["_flashes"] = flashes + message_flashed.send( + current_app._get_current_object(), # type: ignore + message=message, + category=category, + ) + + +def get_flashed_messages( + with_categories: bool = False, category_filter: t.Iterable[str] = () +) -> t.Union[t.List[str], t.List[t.Tuple[str, str]]]: + """Pulls all flashed messages from the session and returns them. + Further calls in the same request to the function will return + the same messages. By default just the messages are returned, + but when `with_categories` is set to ``True``, the return value will + be a list of tuples in the form ``(category, message)`` instead. + + Filter the flashed messages to one or more categories by providing those + categories in `category_filter`. This allows rendering categories in + separate html blocks. The `with_categories` and `category_filter` + arguments are distinct: + + * `with_categories` controls whether categories are returned with message + text (``True`` gives a tuple, where ``False`` gives just the message text). + * `category_filter` filters the messages down to only those matching the + provided categories. + + See :doc:`/patterns/flashing` for examples. + + .. versionchanged:: 0.3 + `with_categories` parameter added. + + .. versionchanged:: 0.9 + `category_filter` parameter added. + + :param with_categories: set to ``True`` to also receive categories. + :param category_filter: filter of categories to limit return values. Only + categories in the list will be returned. + """ + flashes = request_ctx.flashes + if flashes is None: + flashes = session.pop("_flashes") if "_flashes" in session else [] + request_ctx.flashes = flashes + if category_filter: + flashes = list(filter(lambda f: f[0] in category_filter, flashes)) + if not with_categories: + return [x[1] for x in flashes] + return flashes + + +def _prepare_send_file_kwargs(**kwargs: t.Any) -> t.Dict[str, t.Any]: + if kwargs.get("max_age") is None: + kwargs["max_age"] = current_app.get_send_file_max_age + + kwargs.update( + environ=request.environ, + use_x_sendfile=current_app.config["USE_X_SENDFILE"], + response_class=current_app.response_class, + _root_path=current_app.root_path, # type: ignore + ) + return kwargs + + +def send_file( + path_or_file: t.Union[os.PathLike, str, t.BinaryIO], + mimetype: t.Optional[str] = None, + as_attachment: bool = False, + download_name: t.Optional[str] = None, + conditional: bool = True, + etag: t.Union[bool, str] = True, + last_modified: t.Optional[t.Union[datetime, int, float]] = None, + max_age: t.Optional[ + t.Union[int, t.Callable[[t.Optional[str]], t.Optional[int]]] + ] = None, +) -> "Response": + """Send the contents of a file to the client. + + The first argument can be a file path or a file-like object. Paths + are preferred in most cases because Werkzeug can manage the file and + get extra information from the path. Passing a file-like object + requires that the file is opened in binary mode, and is mostly + useful when building a file in memory with :class:`io.BytesIO`. + + Never pass file paths provided by a user. The path is assumed to be + trusted, so a user could craft a path to access a file you didn't + intend. Use :func:`send_from_directory` to safely serve + user-requested paths from within a directory. + + If the WSGI server sets a ``file_wrapper`` in ``environ``, it is + used, otherwise Werkzeug's built-in wrapper is used. Alternatively, + if the HTTP server supports ``X-Sendfile``, configuring Flask with + ``USE_X_SENDFILE = True`` will tell the server to send the given + path, which is much more efficient than reading it in Python. + + :param path_or_file: The path to the file to send, relative to the + current working directory if a relative path is given. + Alternatively, a file-like object opened in binary mode. Make + sure the file pointer is seeked to the start of the data. + :param mimetype: The MIME type to send for the file. If not + provided, it will try to detect it from the file name. + :param as_attachment: Indicate to a browser that it should offer to + save the file instead of displaying it. + :param download_name: The default name browsers will use when saving + the file. Defaults to the passed file name. + :param conditional: Enable conditional and range responses based on + request headers. Requires passing a file path and ``environ``. + :param etag: Calculate an ETag for the file, which requires passing + a file path. Can also be a string to use instead. + :param last_modified: The last modified time to send for the file, + in seconds. If not provided, it will try to detect it from the + file path. + :param max_age: How long the client should cache the file, in + seconds. If set, ``Cache-Control`` will be ``public``, otherwise + it will be ``no-cache`` to prefer conditional caching. + + .. versionchanged:: 2.0 + ``download_name`` replaces the ``attachment_filename`` + parameter. If ``as_attachment=False``, it is passed with + ``Content-Disposition: inline`` instead. + + .. versionchanged:: 2.0 + ``max_age`` replaces the ``cache_timeout`` parameter. + ``conditional`` is enabled and ``max_age`` is not set by + default. + + .. versionchanged:: 2.0 + ``etag`` replaces the ``add_etags`` parameter. It can be a + string to use instead of generating one. + + .. versionchanged:: 2.0 + Passing a file-like object that inherits from + :class:`~io.TextIOBase` will raise a :exc:`ValueError` rather + than sending an empty file. + + .. versionadded:: 2.0 + Moved the implementation to Werkzeug. This is now a wrapper to + pass some Flask-specific arguments. + + .. versionchanged:: 1.1 + ``filename`` may be a :class:`~os.PathLike` object. + + .. versionchanged:: 1.1 + Passing a :class:`~io.BytesIO` object supports range requests. + + .. versionchanged:: 1.0.3 + Filenames are encoded with ASCII instead of Latin-1 for broader + compatibility with WSGI servers. + + .. versionchanged:: 1.0 + UTF-8 filenames as specified in :rfc:`2231` are supported. + + .. versionchanged:: 0.12 + The filename is no longer automatically inferred from file + objects. If you want to use automatic MIME and etag support, + pass a filename via ``filename_or_fp`` or + ``attachment_filename``. + + .. versionchanged:: 0.12 + ``attachment_filename`` is preferred over ``filename`` for MIME + detection. + + .. versionchanged:: 0.9 + ``cache_timeout`` defaults to + :meth:`Flask.get_send_file_max_age`. + + .. versionchanged:: 0.7 + MIME guessing and etag support for file-like objects was + deprecated because it was unreliable. Pass a filename if you are + able to, otherwise attach an etag yourself. + + .. versionchanged:: 0.5 + The ``add_etags``, ``cache_timeout`` and ``conditional`` + parameters were added. The default behavior is to add etags. + + .. versionadded:: 0.2 + """ + return werkzeug.utils.send_file( # type: ignore[return-value] + **_prepare_send_file_kwargs( + path_or_file=path_or_file, + environ=request.environ, + mimetype=mimetype, + as_attachment=as_attachment, + download_name=download_name, + conditional=conditional, + etag=etag, + last_modified=last_modified, + max_age=max_age, + ) + ) + + +def send_from_directory( + directory: t.Union[os.PathLike, str], + path: t.Union[os.PathLike, str], + **kwargs: t.Any, +) -> "Response": + """Send a file from within a directory using :func:`send_file`. + + .. code-block:: python + + @app.route("/uploads/") + def download_file(name): + return send_from_directory( + app.config['UPLOAD_FOLDER'], name, as_attachment=True + ) + + This is a secure way to serve files from a folder, such as static + files or uploads. Uses :func:`~werkzeug.security.safe_join` to + ensure the path coming from the client is not maliciously crafted to + point outside the specified directory. + + If the final path does not point to an existing regular file, + raises a 404 :exc:`~werkzeug.exceptions.NotFound` error. + + :param directory: The directory that ``path`` must be located under, + relative to the current application's root path. + :param path: The path to the file to send, relative to + ``directory``. + :param kwargs: Arguments to pass to :func:`send_file`. + + .. versionchanged:: 2.0 + ``path`` replaces the ``filename`` parameter. + + .. versionadded:: 2.0 + Moved the implementation to Werkzeug. This is now a wrapper to + pass some Flask-specific arguments. + + .. versionadded:: 0.5 + """ + return werkzeug.utils.send_from_directory( # type: ignore[return-value] + directory, path, **_prepare_send_file_kwargs(**kwargs) + ) + + +def get_root_path(import_name: str) -> str: + """Find the root path of a package, or the path that contains a + module. If it cannot be found, returns the current working + directory. + + Not to be confused with the value returned by :func:`find_package`. + + :meta private: + """ + # Module already imported and has a file attribute. Use that first. + mod = sys.modules.get(import_name) + + if mod is not None and hasattr(mod, "__file__") and mod.__file__ is not None: + return os.path.dirname(os.path.abspath(mod.__file__)) + + # Next attempt: check the loader. + loader = pkgutil.get_loader(import_name) + + # Loader does not exist or we're referring to an unloaded main + # module or a main module without path (interactive sessions), go + # with the current working directory. + if loader is None or import_name == "__main__": + return os.getcwd() + + if hasattr(loader, "get_filename"): + filepath = loader.get_filename(import_name) + else: + # Fall back to imports. + __import__(import_name) + mod = sys.modules[import_name] + filepath = getattr(mod, "__file__", None) + + # If we don't have a file path it might be because it is a + # namespace package. In this case pick the root path from the + # first module that is contained in the package. + if filepath is None: + raise RuntimeError( + "No root path can be found for the provided module" + f" {import_name!r}. This can happen because the module" + " came from an import hook that does not provide file" + " name information or because it's a namespace package." + " In this case the root path needs to be explicitly" + " provided." + ) + + # filepath is import_name.py for a module, or __init__.py for a package. + return os.path.dirname(os.path.abspath(filepath)) + + +class locked_cached_property(werkzeug.utils.cached_property): + """A :func:`property` that is only evaluated once. Like + :class:`werkzeug.utils.cached_property` except access uses a lock + for thread safety. + + .. versionchanged:: 2.0 + Inherits from Werkzeug's ``cached_property`` (and ``property``). + """ + + def __init__( + self, + fget: t.Callable[[t.Any], t.Any], + name: t.Optional[str] = None, + doc: t.Optional[str] = None, + ) -> None: + super().__init__(fget, name=name, doc=doc) + self.lock = RLock() + + def __get__(self, obj: object, type: type = None) -> t.Any: # type: ignore + if obj is None: + return self + + with self.lock: + return super().__get__(obj, type=type) + + def __set__(self, obj: object, value: t.Any) -> None: + with self.lock: + super().__set__(obj, value) + + def __delete__(self, obj: object) -> None: + with self.lock: + super().__delete__(obj) + + +def is_ip(value: str) -> bool: + """Determine if the given string is an IP address. + + :param value: value to check + :type value: str + + :return: True if string is an IP address + :rtype: bool + """ + for family in (socket.AF_INET, socket.AF_INET6): + try: + socket.inet_pton(family, value) + except OSError: + pass + else: + return True + + return False + + +@lru_cache(maxsize=None) +def _split_blueprint_path(name: str) -> t.List[str]: + out: t.List[str] = [name] + + if "." in name: + out.extend(_split_blueprint_path(name.rpartition(".")[0])) + + return out diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/flask/json/__init__.py b/docs/examples/flask/venv/lib/python3.11/site-packages/flask/json/__init__.py new file mode 100644 index 0000000..65d8829 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/flask/json/__init__.py @@ -0,0 +1,342 @@ +from __future__ import annotations + +import json as _json +import typing as t + +from jinja2.utils import htmlsafe_json_dumps as _jinja_htmlsafe_dumps + +from ..globals import current_app +from .provider import _default + +if t.TYPE_CHECKING: # pragma: no cover + from ..app import Flask + from ..wrappers import Response + + +class JSONEncoder(_json.JSONEncoder): + """The default JSON encoder. Handles extra types compared to the + built-in :class:`json.JSONEncoder`. + + - :class:`datetime.datetime` and :class:`datetime.date` are + serialized to :rfc:`822` strings. This is the same as the HTTP + date format. + - :class:`decimal.Decimal` is serialized to a string. + - :class:`uuid.UUID` is serialized to a string. + - :class:`dataclasses.dataclass` is passed to + :func:`dataclasses.asdict`. + - :class:`~markupsafe.Markup` (or any object with a ``__html__`` + method) will call the ``__html__`` method to get a string. + + Assign a subclass of this to :attr:`flask.Flask.json_encoder` or + :attr:`flask.Blueprint.json_encoder` to override the default. + + .. deprecated:: 2.2 + Will be removed in Flask 2.3. Use ``app.json`` instead. + """ + + def __init__(self, **kwargs) -> None: + import warnings + + warnings.warn( + "'JSONEncoder' is deprecated and will be removed in" + " Flask 2.3. Use 'Flask.json' to provide an alternate" + " JSON implementation instead.", + DeprecationWarning, + stacklevel=3, + ) + super().__init__(**kwargs) + + def default(self, o: t.Any) -> t.Any: + """Convert ``o`` to a JSON serializable type. See + :meth:`json.JSONEncoder.default`. Python does not support + overriding how basic types like ``str`` or ``list`` are + serialized, they are handled before this method. + """ + return _default(o) + + +class JSONDecoder(_json.JSONDecoder): + """The default JSON decoder. + + This does not change any behavior from the built-in + :class:`json.JSONDecoder`. + + Assign a subclass of this to :attr:`flask.Flask.json_decoder` or + :attr:`flask.Blueprint.json_decoder` to override the default. + + .. deprecated:: 2.2 + Will be removed in Flask 2.3. Use ``app.json`` instead. + """ + + def __init__(self, **kwargs) -> None: + import warnings + + warnings.warn( + "'JSONDecoder' is deprecated and will be removed in" + " Flask 2.3. Use 'Flask.json' to provide an alternate" + " JSON implementation instead.", + DeprecationWarning, + stacklevel=3, + ) + super().__init__(**kwargs) + + +def dumps(obj: t.Any, *, app: Flask | None = None, **kwargs: t.Any) -> str: + """Serialize data as JSON. + + If :data:`~flask.current_app` is available, it will use its + :meth:`app.json.dumps() ` + method, otherwise it will use :func:`json.dumps`. + + :param obj: The data to serialize. + :param kwargs: Arguments passed to the ``dumps`` implementation. + + .. versionchanged:: 2.2 + Calls ``current_app.json.dumps``, allowing an app to override + the behavior. + + .. versionchanged:: 2.2 + The ``app`` parameter will be removed in Flask 2.3. + + .. versionchanged:: 2.0.2 + :class:`decimal.Decimal` is supported by converting to a string. + + .. versionchanged:: 2.0 + ``encoding`` will be removed in Flask 2.1. + + .. versionchanged:: 1.0.3 + ``app`` can be passed directly, rather than requiring an app + context for configuration. + """ + if app is not None: + import warnings + + warnings.warn( + "The 'app' parameter is deprecated and will be removed in" + " Flask 2.3. Call 'app.json.dumps' directly instead.", + DeprecationWarning, + stacklevel=2, + ) + else: + app = current_app + + if app: + return app.json.dumps(obj, **kwargs) + + kwargs.setdefault("default", _default) + return _json.dumps(obj, **kwargs) + + +def dump( + obj: t.Any, fp: t.IO[str], *, app: Flask | None = None, **kwargs: t.Any +) -> None: + """Serialize data as JSON and write to a file. + + If :data:`~flask.current_app` is available, it will use its + :meth:`app.json.dump() ` + method, otherwise it will use :func:`json.dump`. + + :param obj: The data to serialize. + :param fp: A file opened for writing text. Should use the UTF-8 + encoding to be valid JSON. + :param kwargs: Arguments passed to the ``dump`` implementation. + + .. versionchanged:: 2.2 + Calls ``current_app.json.dump``, allowing an app to override + the behavior. + + .. versionchanged:: 2.2 + The ``app`` parameter will be removed in Flask 2.3. + + .. versionchanged:: 2.0 + Writing to a binary file, and the ``encoding`` argument, will be + removed in Flask 2.1. + """ + if app is not None: + import warnings + + warnings.warn( + "The 'app' parameter is deprecated and will be removed in" + " Flask 2.3. Call 'app.json.dump' directly instead.", + DeprecationWarning, + stacklevel=2, + ) + else: + app = current_app + + if app: + app.json.dump(obj, fp, **kwargs) + else: + kwargs.setdefault("default", _default) + _json.dump(obj, fp, **kwargs) + + +def loads(s: str | bytes, *, app: Flask | None = None, **kwargs: t.Any) -> t.Any: + """Deserialize data as JSON. + + If :data:`~flask.current_app` is available, it will use its + :meth:`app.json.loads() ` + method, otherwise it will use :func:`json.loads`. + + :param s: Text or UTF-8 bytes. + :param kwargs: Arguments passed to the ``loads`` implementation. + + .. versionchanged:: 2.2 + Calls ``current_app.json.loads``, allowing an app to override + the behavior. + + .. versionchanged:: 2.2 + The ``app`` parameter will be removed in Flask 2.3. + + .. versionchanged:: 2.0 + ``encoding`` will be removed in Flask 2.1. The data must be a + string or UTF-8 bytes. + + .. versionchanged:: 1.0.3 + ``app`` can be passed directly, rather than requiring an app + context for configuration. + """ + if app is not None: + import warnings + + warnings.warn( + "The 'app' parameter is deprecated and will be removed in" + " Flask 2.3. Call 'app.json.loads' directly instead.", + DeprecationWarning, + stacklevel=2, + ) + else: + app = current_app + + if app: + return app.json.loads(s, **kwargs) + + return _json.loads(s, **kwargs) + + +def load(fp: t.IO[t.AnyStr], *, app: Flask | None = None, **kwargs: t.Any) -> t.Any: + """Deserialize data as JSON read from a file. + + If :data:`~flask.current_app` is available, it will use its + :meth:`app.json.load() ` + method, otherwise it will use :func:`json.load`. + + :param fp: A file opened for reading text or UTF-8 bytes. + :param kwargs: Arguments passed to the ``load`` implementation. + + .. versionchanged:: 2.2 + Calls ``current_app.json.load``, allowing an app to override + the behavior. + + .. versionchanged:: 2.2 + The ``app`` parameter will be removed in Flask 2.3. + + .. versionchanged:: 2.0 + ``encoding`` will be removed in Flask 2.1. The file must be text + mode, or binary mode with UTF-8 bytes. + """ + if app is not None: + import warnings + + warnings.warn( + "The 'app' parameter is deprecated and will be removed in" + " Flask 2.3. Call 'app.json.load' directly instead.", + DeprecationWarning, + stacklevel=2, + ) + else: + app = current_app + + if app: + return app.json.load(fp, **kwargs) + + return _json.load(fp, **kwargs) + + +def htmlsafe_dumps(obj: t.Any, **kwargs: t.Any) -> str: + """Serialize an object to a string of JSON with :func:`dumps`, then + replace HTML-unsafe characters with Unicode escapes and mark the + result safe with :class:`~markupsafe.Markup`. + + This is available in templates as the ``|tojson`` filter. + + The returned string is safe to render in HTML documents and + `` + + + +
+""" + +FOOTER = """\ + +
+ +
+
+

Console Locked

+

+ The console is locked and needs to be unlocked by entering the PIN. + You can find the PIN printed out on the standard output of your + shell that runs the server. +

+

PIN: + + +

+
+
+ + +""" + +PAGE_HTML = ( + HEADER + + """\ +

%(exception_type)s

+
+

%(exception)s

+
+

Traceback (most recent call last)

+%(summary)s +
+

+ This is the Copy/Paste friendly version of the traceback. +

+ +
+
+ The debugger caught an exception in your WSGI application. You can now + look at the traceback which led to the error. + If you enable JavaScript you can also use additional features such as code + execution (if the evalex feature is enabled), automatic pasting of the + exceptions and much more. +
+""" + + FOOTER + + """ + +""" +) + +CONSOLE_HTML = ( + HEADER + + """\ +

Interactive Console

+
+In this console you can execute Python expressions in the context of the +application. The initial namespace was created by the debugger automatically. +
+
The Console requires JavaScript.
+""" + + FOOTER +) + +SUMMARY_HTML = """\ +
+ %(title)s +
    %(frames)s
+ %(description)s +
+""" + +FRAME_HTML = """\ +
+

File "%(filename)s", + line %(lineno)s, + in %(function_name)s

+
%(lines)s
+
+""" + + +def _process_traceback( + exc: BaseException, + te: t.Optional[traceback.TracebackException] = None, + *, + skip: int = 0, + hide: bool = True, +) -> traceback.TracebackException: + if te is None: + te = traceback.TracebackException.from_exception(exc, lookup_lines=False) + + # Get the frames the same way StackSummary.extract did, in order + # to match each frame with the FrameSummary to augment. + frame_gen = traceback.walk_tb(exc.__traceback__) + limit = getattr(sys, "tracebacklimit", None) + + if limit is not None: + if limit < 0: + limit = 0 + + frame_gen = itertools.islice(frame_gen, limit) + + if skip: + frame_gen = itertools.islice(frame_gen, skip, None) + del te.stack[:skip] + + new_stack: t.List[DebugFrameSummary] = [] + hidden = False + + # Match each frame with the FrameSummary that was generated. + # Hide frames using Paste's __traceback_hide__ rules. Replace + # all visible FrameSummary with DebugFrameSummary. + for (f, _), fs in zip(frame_gen, te.stack): + if hide: + hide_value = f.f_locals.get("__traceback_hide__", False) + + if hide_value in {"before", "before_and_this"}: + new_stack = [] + hidden = False + + if hide_value == "before_and_this": + continue + elif hide_value in {"reset", "reset_and_this"}: + hidden = False + + if hide_value == "reset_and_this": + continue + elif hide_value in {"after", "after_and_this"}: + hidden = True + + if hide_value == "after_and_this": + continue + elif hide_value or hidden: + continue + + frame_args: t.Dict[str, t.Any] = { + "filename": fs.filename, + "lineno": fs.lineno, + "name": fs.name, + "locals": f.f_locals, + "globals": f.f_globals, + } + + if hasattr(fs, "colno"): + frame_args["colno"] = fs.colno + frame_args["end_colno"] = fs.end_colno # type: ignore[attr-defined] + + new_stack.append(DebugFrameSummary(**frame_args)) + + # The codeop module is used to compile code from the interactive + # debugger. Hide any codeop frames from the bottom of the traceback. + while new_stack: + module = new_stack[0].global_ns.get("__name__") + + if module is None: + module = new_stack[0].local_ns.get("__name__") + + if module == "codeop": + del new_stack[0] + else: + break + + te.stack[:] = new_stack + + if te.__context__: + context_exc = t.cast(BaseException, exc.__context__) + te.__context__ = _process_traceback(context_exc, te.__context__, hide=hide) + + if te.__cause__: + cause_exc = t.cast(BaseException, exc.__cause__) + te.__cause__ = _process_traceback(cause_exc, te.__cause__, hide=hide) + + return te + + +class DebugTraceback: + __slots__ = ("_te", "_cache_all_tracebacks", "_cache_all_frames") + + def __init__( + self, + exc: BaseException, + te: t.Optional[traceback.TracebackException] = None, + *, + skip: int = 0, + hide: bool = True, + ) -> None: + self._te = _process_traceback(exc, te, skip=skip, hide=hide) + + def __str__(self) -> str: + return f"<{type(self).__name__} {self._te}>" + + @cached_property + def all_tracebacks( + self, + ) -> t.List[t.Tuple[t.Optional[str], traceback.TracebackException]]: + out = [] + current = self._te + + while current is not None: + if current.__cause__ is not None: + chained_msg = ( + "The above exception was the direct cause of the" + " following exception" + ) + chained_exc = current.__cause__ + elif current.__context__ is not None and not current.__suppress_context__: + chained_msg = ( + "During handling of the above exception, another" + " exception occurred" + ) + chained_exc = current.__context__ + else: + chained_msg = None + chained_exc = None + + out.append((chained_msg, current)) + current = chained_exc + + return out + + @cached_property + def all_frames(self) -> t.List["DebugFrameSummary"]: + return [ + f for _, te in self.all_tracebacks for f in te.stack # type: ignore[misc] + ] + + def render_traceback_text(self) -> str: + return "".join(self._te.format()) + + def render_traceback_html(self, include_title: bool = True) -> str: + library_frames = [f.is_library for f in self.all_frames] + mark_library = 0 < sum(library_frames) < len(library_frames) + rows = [] + + if not library_frames: + classes = "traceback noframe-traceback" + else: + classes = "traceback" + + for msg, current in reversed(self.all_tracebacks): + row_parts = [] + + if msg is not None: + row_parts.append(f'
  • {escape(egg.project_name)} [{escape(version)}]" + ) + + wsgi_env = [] + sorted_environ = sorted(req.environ.items(), key=lambda x: repr(x[0]).lower()) + for key, value in sorted_environ: + value = "".join(wrap(str(escape(repr(value))))) + wsgi_env.append(f"{escape(key)}{value}") + + sys_path = [] + for item, virtual, expanded in iter_sys_path(): + class_ = [] + if virtual: + class_.append("virtual") + if expanded: + class_.append("exp") + class_ = f' class="{" ".join(class_)}"' if class_ else "" + sys_path.append(f"{escape(item)}") + + return ( + TEMPLATE + % { + "python_version": "
    ".join(escape(sys.version).splitlines()), + "platform": escape(sys.platform), + "os": escape(os.name), + "api_version": sys.api_version, + "byteorder": sys.byteorder, + "werkzeug_version": _werkzeug_version, + "python_eggs": "\n".join(python_eggs), + "wsgi_env": "\n".join(wsgi_env), + "sys_path": "\n".join(sys_path), + } + ).encode("utf-8") + + +def test_app( + environ: "WSGIEnvironment", start_response: "StartResponse" +) -> t.Iterable[bytes]: + """Simple test application that dumps the environment. You can use + it to check if Werkzeug is working properly: + + .. sourcecode:: pycon + + >>> from werkzeug.serving import run_simple + >>> from werkzeug.testapp import test_app + >>> run_simple('localhost', 3000, test_app) + * Running on http://localhost:3000/ + + The application displays important information from the WSGI environment, + the Python interpreter and the installed libraries. + """ + req = Request(environ, populate_request=False) + if req.args.get("resource") == "logo": + response = logo + else: + response = Response(render_testapp(req), mimetype="text/html") + return response(environ, start_response) + + +if __name__ == "__main__": + from .serving import run_simple + + run_simple("localhost", 5000, test_app, use_reloader=True) diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/urls.py b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/urls.py new file mode 100644 index 0000000..67c08b0 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/urls.py @@ -0,0 +1,1067 @@ +"""Functions for working with URLs. + +Contains implementations of functions from :mod:`urllib.parse` that +handle bytes and strings. +""" +import codecs +import os +import re +import typing as t + +from ._internal import _check_str_tuple +from ._internal import _decode_idna +from ._internal import _encode_idna +from ._internal import _make_encode_wrapper +from ._internal import _to_str + +if t.TYPE_CHECKING: + from . import datastructures as ds + +# A regular expression for what a valid schema looks like +_scheme_re = re.compile(r"^[a-zA-Z0-9+-.]+$") + +# Characters that are safe in any part of an URL. +_always_safe = frozenset( + bytearray( + b"abcdefghijklmnopqrstuvwxyz" + b"ABCDEFGHIJKLMNOPQRSTUVWXYZ" + b"0123456789" + b"-._~" + b"$!'()*+,;" # RFC3986 sub-delims set, not including query string delimiters &= + ) +) + +_hexdigits = "0123456789ABCDEFabcdef" +_hextobyte = { + f"{a}{b}".encode("ascii"): int(f"{a}{b}", 16) + for a in _hexdigits + for b in _hexdigits +} +_bytetohex = [f"%{char:02X}".encode("ascii") for char in range(256)] + + +class _URLTuple(t.NamedTuple): + scheme: str + netloc: str + path: str + query: str + fragment: str + + +class BaseURL(_URLTuple): + """Superclass of :py:class:`URL` and :py:class:`BytesURL`.""" + + __slots__ = () + _at: str + _colon: str + _lbracket: str + _rbracket: str + + def __str__(self) -> str: + return self.to_url() + + def replace(self, **kwargs: t.Any) -> "BaseURL": + """Return an URL with the same values, except for those parameters + given new values by whichever keyword arguments are specified.""" + return self._replace(**kwargs) + + @property + def host(self) -> t.Optional[str]: + """The host part of the URL if available, otherwise `None`. The + host is either the hostname or the IP address mentioned in the + URL. It will not contain the port. + """ + return self._split_host()[0] + + @property + def ascii_host(self) -> t.Optional[str]: + """Works exactly like :attr:`host` but will return a result that + is restricted to ASCII. If it finds a netloc that is not ASCII + it will attempt to idna decode it. This is useful for socket + operations when the URL might include internationalized characters. + """ + rv = self.host + if rv is not None and isinstance(rv, str): + try: + rv = _encode_idna(rv) # type: ignore + except UnicodeError: + rv = rv.encode("ascii", "ignore") # type: ignore + return _to_str(rv, "ascii", "ignore") + + @property + def port(self) -> t.Optional[int]: + """The port in the URL as an integer if it was present, `None` + otherwise. This does not fill in default ports. + """ + try: + rv = int(_to_str(self._split_host()[1])) + if 0 <= rv <= 65535: + return rv + except (ValueError, TypeError): + pass + return None + + @property + def auth(self) -> t.Optional[str]: + """The authentication part in the URL if available, `None` + otherwise. + """ + return self._split_netloc()[0] + + @property + def username(self) -> t.Optional[str]: + """The username if it was part of the URL, `None` otherwise. + This undergoes URL decoding and will always be a string. + """ + rv = self._split_auth()[0] + if rv is not None: + return _url_unquote_legacy(rv) + return None + + @property + def raw_username(self) -> t.Optional[str]: + """The username if it was part of the URL, `None` otherwise. + Unlike :attr:`username` this one is not being decoded. + """ + return self._split_auth()[0] + + @property + def password(self) -> t.Optional[str]: + """The password if it was part of the URL, `None` otherwise. + This undergoes URL decoding and will always be a string. + """ + rv = self._split_auth()[1] + if rv is not None: + return _url_unquote_legacy(rv) + return None + + @property + def raw_password(self) -> t.Optional[str]: + """The password if it was part of the URL, `None` otherwise. + Unlike :attr:`password` this one is not being decoded. + """ + return self._split_auth()[1] + + def decode_query(self, *args: t.Any, **kwargs: t.Any) -> "ds.MultiDict[str, str]": + """Decodes the query part of the URL. Ths is a shortcut for + calling :func:`url_decode` on the query argument. The arguments and + keyword arguments are forwarded to :func:`url_decode` unchanged. + """ + return url_decode(self.query, *args, **kwargs) + + def join(self, *args: t.Any, **kwargs: t.Any) -> "BaseURL": + """Joins this URL with another one. This is just a convenience + function for calling into :meth:`url_join` and then parsing the + return value again. + """ + return url_parse(url_join(self, *args, **kwargs)) + + def to_url(self) -> str: + """Returns a URL string or bytes depending on the type of the + information stored. This is just a convenience function + for calling :meth:`url_unparse` for this URL. + """ + return url_unparse(self) + + def encode_netloc(self) -> str: + """Encodes the netloc part to an ASCII safe URL as bytes.""" + rv = self.ascii_host or "" + if ":" in rv: + rv = f"[{rv}]" + port = self.port + if port is not None: + rv = f"{rv}:{port}" + auth = ":".join( + filter( + None, + [ + url_quote(self.raw_username or "", "utf-8", "strict", "/:%"), + url_quote(self.raw_password or "", "utf-8", "strict", "/:%"), + ], + ) + ) + if auth: + rv = f"{auth}@{rv}" + return rv + + def decode_netloc(self) -> str: + """Decodes the netloc part into a string.""" + rv = _decode_idna(self.host or "") + + if ":" in rv: + rv = f"[{rv}]" + port = self.port + if port is not None: + rv = f"{rv}:{port}" + auth = ":".join( + filter( + None, + [ + _url_unquote_legacy(self.raw_username or "", "/:%@"), + _url_unquote_legacy(self.raw_password or "", "/:%@"), + ], + ) + ) + if auth: + rv = f"{auth}@{rv}" + return rv + + def to_uri_tuple(self) -> "BaseURL": + """Returns a :class:`BytesURL` tuple that holds a URI. This will + encode all the information in the URL properly to ASCII using the + rules a web browser would follow. + + It's usually more interesting to directly call :meth:`iri_to_uri` which + will return a string. + """ + return url_parse(iri_to_uri(self)) + + def to_iri_tuple(self) -> "BaseURL": + """Returns a :class:`URL` tuple that holds a IRI. This will try + to decode as much information as possible in the URL without + losing information similar to how a web browser does it for the + URL bar. + + It's usually more interesting to directly call :meth:`uri_to_iri` which + will return a string. + """ + return url_parse(uri_to_iri(self)) + + def get_file_location( + self, pathformat: t.Optional[str] = None + ) -> t.Tuple[t.Optional[str], t.Optional[str]]: + """Returns a tuple with the location of the file in the form + ``(server, location)``. If the netloc is empty in the URL or + points to localhost, it's represented as ``None``. + + The `pathformat` by default is autodetection but needs to be set + when working with URLs of a specific system. The supported values + are ``'windows'`` when working with Windows or DOS paths and + ``'posix'`` when working with posix paths. + + If the URL does not point to a local file, the server and location + are both represented as ``None``. + + :param pathformat: The expected format of the path component. + Currently ``'windows'`` and ``'posix'`` are + supported. Defaults to ``None`` which is + autodetect. + """ + if self.scheme != "file": + return None, None + + path = url_unquote(self.path) + host = self.netloc or None + + if pathformat is None: + if os.name == "nt": + pathformat = "windows" + else: + pathformat = "posix" + + if pathformat == "windows": + if path[:1] == "/" and path[1:2].isalpha() and path[2:3] in "|:": + path = f"{path[1:2]}:{path[3:]}" + windows_share = path[:3] in ("\\" * 3, "/" * 3) + import ntpath + + path = ntpath.normpath(path) + # Windows shared drives are represented as ``\\host\\directory``. + # That results in a URL like ``file://///host/directory``, and a + # path like ``///host/directory``. We need to special-case this + # because the path contains the hostname. + if windows_share and host is None: + parts = path.lstrip("\\").split("\\", 1) + if len(parts) == 2: + host, path = parts + else: + host = parts[0] + path = "" + elif pathformat == "posix": + import posixpath + + path = posixpath.normpath(path) + else: + raise TypeError(f"Invalid path format {pathformat!r}") + + if host in ("127.0.0.1", "::1", "localhost"): + host = None + + return host, path + + def _split_netloc(self) -> t.Tuple[t.Optional[str], str]: + if self._at in self.netloc: + auth, _, netloc = self.netloc.partition(self._at) + return auth, netloc + return None, self.netloc + + def _split_auth(self) -> t.Tuple[t.Optional[str], t.Optional[str]]: + auth = self._split_netloc()[0] + if not auth: + return None, None + if self._colon not in auth: + return auth, None + + username, _, password = auth.partition(self._colon) + return username, password + + def _split_host(self) -> t.Tuple[t.Optional[str], t.Optional[str]]: + rv = self._split_netloc()[1] + if not rv: + return None, None + + if not rv.startswith(self._lbracket): + if self._colon in rv: + host, _, port = rv.partition(self._colon) + return host, port + return rv, None + + idx = rv.find(self._rbracket) + if idx < 0: + return rv, None + + host = rv[1:idx] + rest = rv[idx + 1 :] + if rest.startswith(self._colon): + return host, rest[1:] + return host, None + + +class URL(BaseURL): + """Represents a parsed URL. This behaves like a regular tuple but + also has some extra attributes that give further insight into the + URL. + """ + + __slots__ = () + _at = "@" + _colon = ":" + _lbracket = "[" + _rbracket = "]" + + def encode(self, charset: str = "utf-8", errors: str = "replace") -> "BytesURL": + """Encodes the URL to a tuple made out of bytes. The charset is + only being used for the path, query and fragment. + """ + return BytesURL( + self.scheme.encode("ascii"), # type: ignore + self.encode_netloc(), + self.path.encode(charset, errors), # type: ignore + self.query.encode(charset, errors), # type: ignore + self.fragment.encode(charset, errors), # type: ignore + ) + + +class BytesURL(BaseURL): + """Represents a parsed URL in bytes.""" + + __slots__ = () + _at = b"@" # type: ignore + _colon = b":" # type: ignore + _lbracket = b"[" # type: ignore + _rbracket = b"]" # type: ignore + + def __str__(self) -> str: + return self.to_url().decode("utf-8", "replace") # type: ignore + + def encode_netloc(self) -> bytes: # type: ignore + """Returns the netloc unchanged as bytes.""" + return self.netloc # type: ignore + + def decode(self, charset: str = "utf-8", errors: str = "replace") -> "URL": + """Decodes the URL to a tuple made out of strings. The charset is + only being used for the path, query and fragment. + """ + return URL( + self.scheme.decode("ascii"), # type: ignore + self.decode_netloc(), + self.path.decode(charset, errors), # type: ignore + self.query.decode(charset, errors), # type: ignore + self.fragment.decode(charset, errors), # type: ignore + ) + + +_unquote_maps: t.Dict[t.FrozenSet[int], t.Dict[bytes, int]] = {frozenset(): _hextobyte} + + +def _unquote_to_bytes( + string: t.Union[str, bytes], unsafe: t.Union[str, bytes] = "" +) -> bytes: + if isinstance(string, str): + string = string.encode("utf-8") + + if isinstance(unsafe, str): + unsafe = unsafe.encode("utf-8") + + unsafe = frozenset(bytearray(unsafe)) + groups = iter(string.split(b"%")) + result = bytearray(next(groups, b"")) + + try: + hex_to_byte = _unquote_maps[unsafe] + except KeyError: + hex_to_byte = _unquote_maps[unsafe] = { + h: b for h, b in _hextobyte.items() if b not in unsafe + } + + for group in groups: + code = group[:2] + + if code in hex_to_byte: + result.append(hex_to_byte[code]) + result.extend(group[2:]) + else: + result.append(37) # % + result.extend(group) + + return bytes(result) + + +def _url_encode_impl( + obj: t.Union[t.Mapping[str, str], t.Iterable[t.Tuple[str, str]]], + charset: str, + sort: bool, + key: t.Optional[t.Callable[[t.Tuple[str, str]], t.Any]], +) -> t.Iterator[str]: + from .datastructures import iter_multi_items + + iterable: t.Iterable[t.Tuple[str, str]] = iter_multi_items(obj) + + if sort: + iterable = sorted(iterable, key=key) + + for key_str, value_str in iterable: + if value_str is None: + continue + + if not isinstance(key_str, bytes): + key_bytes = str(key_str).encode(charset) + else: + key_bytes = key_str + + if not isinstance(value_str, bytes): + value_bytes = str(value_str).encode(charset) + else: + value_bytes = value_str + + yield f"{_fast_url_quote_plus(key_bytes)}={_fast_url_quote_plus(value_bytes)}" + + +def _url_unquote_legacy(value: str, unsafe: str = "") -> str: + try: + return url_unquote(value, charset="utf-8", errors="strict", unsafe=unsafe) + except UnicodeError: + return url_unquote(value, charset="latin1", unsafe=unsafe) + + +def url_parse( + url: str, scheme: t.Optional[str] = None, allow_fragments: bool = True +) -> BaseURL: + """Parses a URL from a string into a :class:`URL` tuple. If the URL + is lacking a scheme it can be provided as second argument. Otherwise, + it is ignored. Optionally fragments can be stripped from the URL + by setting `allow_fragments` to `False`. + + The inverse of this function is :func:`url_unparse`. + + :param url: the URL to parse. + :param scheme: the default schema to use if the URL is schemaless. + :param allow_fragments: if set to `False` a fragment will be removed + from the URL. + """ + s = _make_encode_wrapper(url) + is_text_based = isinstance(url, str) + + if scheme is None: + scheme = s("") + netloc = query = fragment = s("") + i = url.find(s(":")) + if i > 0 and _scheme_re.match(_to_str(url[:i], errors="replace")): + # make sure "iri" is not actually a port number (in which case + # "scheme" is really part of the path) + rest = url[i + 1 :] + if not rest or any(c not in s("0123456789") for c in rest): + # not a port number + scheme, url = url[:i].lower(), rest + + if url[:2] == s("//"): + delim = len(url) + for c in s("/?#"): + wdelim = url.find(c, 2) + if wdelim >= 0: + delim = min(delim, wdelim) + netloc, url = url[2:delim], url[delim:] + if (s("[") in netloc and s("]") not in netloc) or ( + s("]") in netloc and s("[") not in netloc + ): + raise ValueError("Invalid IPv6 URL") + + if allow_fragments and s("#") in url: + url, fragment = url.split(s("#"), 1) + if s("?") in url: + url, query = url.split(s("?"), 1) + + result_type = URL if is_text_based else BytesURL + return result_type(scheme, netloc, url, query, fragment) + + +def _make_fast_url_quote( + charset: str = "utf-8", + errors: str = "strict", + safe: t.Union[str, bytes] = "/:", + unsafe: t.Union[str, bytes] = "", +) -> t.Callable[[bytes], str]: + """Precompile the translation table for a URL encoding function. + + Unlike :func:`url_quote`, the generated function only takes the + string to quote. + + :param charset: The charset to encode the result with. + :param errors: How to handle encoding errors. + :param safe: An optional sequence of safe characters to never encode. + :param unsafe: An optional sequence of unsafe characters to always encode. + """ + if isinstance(safe, str): + safe = safe.encode(charset, errors) + + if isinstance(unsafe, str): + unsafe = unsafe.encode(charset, errors) + + safe = (frozenset(bytearray(safe)) | _always_safe) - frozenset(bytearray(unsafe)) + table = [chr(c) if c in safe else f"%{c:02X}" for c in range(256)] + + def quote(string: bytes) -> str: + return "".join([table[c] for c in string]) + + return quote + + +_fast_url_quote = _make_fast_url_quote() +_fast_quote_plus = _make_fast_url_quote(safe=" ", unsafe="+") + + +def _fast_url_quote_plus(string: bytes) -> str: + return _fast_quote_plus(string).replace(" ", "+") + + +def url_quote( + string: t.Union[str, bytes], + charset: str = "utf-8", + errors: str = "strict", + safe: t.Union[str, bytes] = "/:", + unsafe: t.Union[str, bytes] = "", +) -> str: + """URL encode a single string with a given encoding. + + :param s: the string to quote. + :param charset: the charset to be used. + :param safe: an optional sequence of safe characters. + :param unsafe: an optional sequence of unsafe characters. + + .. versionadded:: 0.9.2 + The `unsafe` parameter was added. + """ + if not isinstance(string, (str, bytes, bytearray)): + string = str(string) + if isinstance(string, str): + string = string.encode(charset, errors) + if isinstance(safe, str): + safe = safe.encode(charset, errors) + if isinstance(unsafe, str): + unsafe = unsafe.encode(charset, errors) + safe = (frozenset(bytearray(safe)) | _always_safe) - frozenset(bytearray(unsafe)) + rv = bytearray() + for char in bytearray(string): + if char in safe: + rv.append(char) + else: + rv.extend(_bytetohex[char]) + return bytes(rv).decode(charset) + + +def url_quote_plus( + string: str, charset: str = "utf-8", errors: str = "strict", safe: str = "" +) -> str: + """URL encode a single string with the given encoding and convert + whitespace to "+". + + :param s: The string to quote. + :param charset: The charset to be used. + :param safe: An optional sequence of safe characters. + """ + return url_quote(string, charset, errors, safe + " ", "+").replace(" ", "+") + + +def url_unparse(components: t.Tuple[str, str, str, str, str]) -> str: + """The reverse operation to :meth:`url_parse`. This accepts arbitrary + as well as :class:`URL` tuples and returns a URL as a string. + + :param components: the parsed URL as tuple which should be converted + into a URL string. + """ + _check_str_tuple(components) + scheme, netloc, path, query, fragment = components + s = _make_encode_wrapper(scheme) + url = s("") + + # We generally treat file:///x and file:/x the same which is also + # what browsers seem to do. This also allows us to ignore a schema + # register for netloc utilization or having to differentiate between + # empty and missing netloc. + if netloc or (scheme and path.startswith(s("/"))): + if path and path[:1] != s("/"): + path = s("/") + path + url = s("//") + (netloc or s("")) + path + elif path: + url += path + if scheme: + url = scheme + s(":") + url + if query: + url = url + s("?") + query + if fragment: + url = url + s("#") + fragment + return url + + +def url_unquote( + s: t.Union[str, bytes], + charset: str = "utf-8", + errors: str = "replace", + unsafe: str = "", +) -> str: + """URL decode a single string with a given encoding. If the charset + is set to `None` no decoding is performed and raw bytes are + returned. + + :param s: the string to unquote. + :param charset: the charset of the query string. If set to `None` + no decoding will take place. + :param errors: the error handling for the charset decoding. + """ + rv = _unquote_to_bytes(s, unsafe) + if charset is None: + return rv + return rv.decode(charset, errors) + + +def url_unquote_plus( + s: t.Union[str, bytes], charset: str = "utf-8", errors: str = "replace" +) -> str: + """URL decode a single string with the given `charset` and decode "+" to + whitespace. + + Per default encoding errors are ignored. If you want a different behavior + you can set `errors` to ``'replace'`` or ``'strict'``. + + :param s: The string to unquote. + :param charset: the charset of the query string. If set to `None` + no decoding will take place. + :param errors: The error handling for the `charset` decoding. + """ + if isinstance(s, str): + s = s.replace("+", " ") + else: + s = s.replace(b"+", b" ") + return url_unquote(s, charset, errors) + + +def url_fix(s: str, charset: str = "utf-8") -> str: + r"""Sometimes you get an URL by a user that just isn't a real URL because + it contains unsafe characters like ' ' and so on. This function can fix + some of the problems in a similar way browsers handle data entered by the + user: + + >>> url_fix('http://de.wikipedia.org/wiki/Elf (Begriffskl\xe4rung)') + 'http://de.wikipedia.org/wiki/Elf%20(Begriffskl%C3%A4rung)' + + :param s: the string with the URL to fix. + :param charset: The target charset for the URL if the url was given + as a string. + """ + # First step is to switch to text processing and to convert + # backslashes (which are invalid in URLs anyways) to slashes. This is + # consistent with what Chrome does. + s = _to_str(s, charset, "replace").replace("\\", "/") + + # For the specific case that we look like a malformed windows URL + # we want to fix this up manually: + if s.startswith("file://") and s[7:8].isalpha() and s[8:10] in (":/", "|/"): + s = f"file:///{s[7:]}" + + url = url_parse(s) + path = url_quote(url.path, charset, safe="/%+$!*'(),") + qs = url_quote_plus(url.query, charset, safe=":&%=+$!*'(),") + anchor = url_quote_plus(url.fragment, charset, safe=":&%=+$!*'(),") + return url_unparse((url.scheme, url.encode_netloc(), path, qs, anchor)) + + +# not-unreserved characters remain quoted when unquoting to IRI +_to_iri_unsafe = "".join([chr(c) for c in range(128) if c not in _always_safe]) + + +def _codec_error_url_quote(e: UnicodeError) -> t.Tuple[str, int]: + """Used in :func:`uri_to_iri` after unquoting to re-quote any + invalid bytes. + """ + # the docs state that UnicodeError does have these attributes, + # but mypy isn't picking them up + out = _fast_url_quote(e.object[e.start : e.end]) # type: ignore + return out, e.end # type: ignore + + +codecs.register_error("werkzeug.url_quote", _codec_error_url_quote) + + +def uri_to_iri( + uri: t.Union[str, t.Tuple[str, str, str, str, str]], + charset: str = "utf-8", + errors: str = "werkzeug.url_quote", +) -> str: + """Convert a URI to an IRI. All valid UTF-8 characters are unquoted, + leaving all reserved and invalid characters quoted. If the URL has + a domain, it is decoded from Punycode. + + >>> uri_to_iri("http://xn--n3h.net/p%C3%A5th?q=%C3%A8ry%DF") + 'http://\\u2603.net/p\\xe5th?q=\\xe8ry%DF' + + :param uri: The URI to convert. + :param charset: The encoding to encode unquoted bytes with. + :param errors: Error handler to use during ``bytes.encode``. By + default, invalid bytes are left quoted. + + .. versionchanged:: 0.15 + All reserved and invalid characters remain quoted. Previously, + only some reserved characters were preserved, and invalid bytes + were replaced instead of left quoted. + + .. versionadded:: 0.6 + """ + if isinstance(uri, tuple): + uri = url_unparse(uri) + + uri = url_parse(_to_str(uri, charset)) + path = url_unquote(uri.path, charset, errors, _to_iri_unsafe) + query = url_unquote(uri.query, charset, errors, _to_iri_unsafe) + fragment = url_unquote(uri.fragment, charset, errors, _to_iri_unsafe) + return url_unparse((uri.scheme, uri.decode_netloc(), path, query, fragment)) + + +# reserved characters remain unquoted when quoting to URI +_to_uri_safe = ":/?#[]@!$&'()*+,;=%" + + +def iri_to_uri( + iri: t.Union[str, t.Tuple[str, str, str, str, str]], + charset: str = "utf-8", + errors: str = "strict", + safe_conversion: bool = False, +) -> str: + """Convert an IRI to a URI. All non-ASCII and unsafe characters are + quoted. If the URL has a domain, it is encoded to Punycode. + + >>> iri_to_uri('http://\\u2603.net/p\\xe5th?q=\\xe8ry%DF') + 'http://xn--n3h.net/p%C3%A5th?q=%C3%A8ry%DF' + + :param iri: The IRI to convert. + :param charset: The encoding of the IRI. + :param errors: Error handler to use during ``bytes.encode``. + :param safe_conversion: Return the URL unchanged if it only contains + ASCII characters and no whitespace. See the explanation below. + + There is a general problem with IRI conversion with some protocols + that are in violation of the URI specification. Consider the + following two IRIs:: + + magnet:?xt=uri:whatever + itms-services://?action=download-manifest + + After parsing, we don't know if the scheme requires the ``//``, + which is dropped if empty, but conveys different meanings in the + final URL if it's present or not. In this case, you can use + ``safe_conversion``, which will return the URL unchanged if it only + contains ASCII characters and no whitespace. This can result in a + URI with unquoted characters if it was not already quoted correctly, + but preserves the URL's semantics. Werkzeug uses this for the + ``Location`` header for redirects. + + .. versionchanged:: 0.15 + All reserved characters remain unquoted. Previously, only some + reserved characters were left unquoted. + + .. versionchanged:: 0.9.6 + The ``safe_conversion`` parameter was added. + + .. versionadded:: 0.6 + """ + if isinstance(iri, tuple): + iri = url_unparse(iri) + + if safe_conversion: + # If we're not sure if it's safe to convert the URL, and it only + # contains ASCII characters, return it unconverted. + try: + native_iri = _to_str(iri) + ascii_iri = native_iri.encode("ascii") + + # Only return if it doesn't have whitespace. (Why?) + if len(ascii_iri.split()) == 1: + return native_iri + except UnicodeError: + pass + + iri = url_parse(_to_str(iri, charset, errors)) + path = url_quote(iri.path, charset, errors, _to_uri_safe) + query = url_quote(iri.query, charset, errors, _to_uri_safe) + fragment = url_quote(iri.fragment, charset, errors, _to_uri_safe) + return url_unparse((iri.scheme, iri.encode_netloc(), path, query, fragment)) + + +def url_decode( + s: t.AnyStr, + charset: str = "utf-8", + include_empty: bool = True, + errors: str = "replace", + separator: str = "&", + cls: t.Optional[t.Type["ds.MultiDict"]] = None, +) -> "ds.MultiDict[str, str]": + """Parse a query string and return it as a :class:`MultiDict`. + + :param s: The query string to parse. + :param charset: Decode bytes to string with this charset. If not + given, bytes are returned as-is. + :param include_empty: Include keys with empty values in the dict. + :param errors: Error handling behavior when decoding bytes. + :param separator: Separator character between pairs. + :param cls: Container to hold result instead of :class:`MultiDict`. + + .. versionchanged:: 2.0 + The ``decode_keys`` parameter is deprecated and will be removed + in Werkzeug 2.1. + + .. versionchanged:: 0.5 + In previous versions ";" and "&" could be used for url decoding. + Now only "&" is supported. If you want to use ";", a different + ``separator`` can be provided. + + .. versionchanged:: 0.5 + The ``cls`` parameter was added. + """ + if cls is None: + from .datastructures import MultiDict # noqa: F811 + + cls = MultiDict + if isinstance(s, str) and not isinstance(separator, str): + separator = separator.decode(charset or "ascii") + elif isinstance(s, bytes) and not isinstance(separator, bytes): + separator = separator.encode(charset or "ascii") # type: ignore + return cls( + _url_decode_impl( + s.split(separator), charset, include_empty, errors # type: ignore + ) + ) + + +def url_decode_stream( + stream: t.IO[bytes], + charset: str = "utf-8", + include_empty: bool = True, + errors: str = "replace", + separator: bytes = b"&", + cls: t.Optional[t.Type["ds.MultiDict"]] = None, + limit: t.Optional[int] = None, +) -> "ds.MultiDict[str, str]": + """Works like :func:`url_decode` but decodes a stream. The behavior + of stream and limit follows functions like + :func:`~werkzeug.wsgi.make_line_iter`. The generator of pairs is + directly fed to the `cls` so you can consume the data while it's + parsed. + + :param stream: a stream with the encoded querystring + :param charset: the charset of the query string. If set to `None` + no decoding will take place. + :param include_empty: Set to `False` if you don't want empty values to + appear in the dict. + :param errors: the decoding error behavior. + :param separator: the pair separator to be used, defaults to ``&`` + :param cls: an optional dict class to use. If this is not specified + or `None` the default :class:`MultiDict` is used. + :param limit: the content length of the URL data. Not necessary if + a limited stream is provided. + + .. versionchanged:: 2.0 + The ``decode_keys`` and ``return_iterator`` parameters are + deprecated and will be removed in Werkzeug 2.1. + + .. versionadded:: 0.8 + """ + from .wsgi import make_chunk_iter + + pair_iter = make_chunk_iter(stream, separator, limit) + decoder = _url_decode_impl(pair_iter, charset, include_empty, errors) + + if cls is None: + from .datastructures import MultiDict # noqa: F811 + + cls = MultiDict + + return cls(decoder) + + +def _url_decode_impl( + pair_iter: t.Iterable[t.AnyStr], charset: str, include_empty: bool, errors: str +) -> t.Iterator[t.Tuple[str, str]]: + for pair in pair_iter: + if not pair: + continue + s = _make_encode_wrapper(pair) + equal = s("=") + if equal in pair: + key, value = pair.split(equal, 1) + else: + if not include_empty: + continue + key = pair + value = s("") + yield ( + url_unquote_plus(key, charset, errors), + url_unquote_plus(value, charset, errors), + ) + + +def url_encode( + obj: t.Union[t.Mapping[str, str], t.Iterable[t.Tuple[str, str]]], + charset: str = "utf-8", + sort: bool = False, + key: t.Optional[t.Callable[[t.Tuple[str, str]], t.Any]] = None, + separator: str = "&", +) -> str: + """URL encode a dict/`MultiDict`. If a value is `None` it will not appear + in the result string. Per default only values are encoded into the target + charset strings. + + :param obj: the object to encode into a query string. + :param charset: the charset of the query string. + :param sort: set to `True` if you want parameters to be sorted by `key`. + :param separator: the separator to be used for the pairs. + :param key: an optional function to be used for sorting. For more details + check out the :func:`sorted` documentation. + + .. versionchanged:: 2.0 + The ``encode_keys`` parameter is deprecated and will be removed + in Werkzeug 2.1. + + .. versionchanged:: 0.5 + Added the ``sort``, ``key``, and ``separator`` parameters. + """ + separator = _to_str(separator, "ascii") + return separator.join(_url_encode_impl(obj, charset, sort, key)) + + +def url_encode_stream( + obj: t.Union[t.Mapping[str, str], t.Iterable[t.Tuple[str, str]]], + stream: t.Optional[t.IO[str]] = None, + charset: str = "utf-8", + sort: bool = False, + key: t.Optional[t.Callable[[t.Tuple[str, str]], t.Any]] = None, + separator: str = "&", +) -> None: + """Like :meth:`url_encode` but writes the results to a stream + object. If the stream is `None` a generator over all encoded + pairs is returned. + + :param obj: the object to encode into a query string. + :param stream: a stream to write the encoded object into or `None` if + an iterator over the encoded pairs should be returned. In + that case the separator argument is ignored. + :param charset: the charset of the query string. + :param sort: set to `True` if you want parameters to be sorted by `key`. + :param separator: the separator to be used for the pairs. + :param key: an optional function to be used for sorting. For more details + check out the :func:`sorted` documentation. + + .. versionchanged:: 2.0 + The ``encode_keys`` parameter is deprecated and will be removed + in Werkzeug 2.1. + + .. versionadded:: 0.8 + """ + separator = _to_str(separator, "ascii") + gen = _url_encode_impl(obj, charset, sort, key) + if stream is None: + return gen # type: ignore + for idx, chunk in enumerate(gen): + if idx: + stream.write(separator) + stream.write(chunk) + return None + + +def url_join( + base: t.Union[str, t.Tuple[str, str, str, str, str]], + url: t.Union[str, t.Tuple[str, str, str, str, str]], + allow_fragments: bool = True, +) -> str: + """Join a base URL and a possibly relative URL to form an absolute + interpretation of the latter. + + :param base: the base URL for the join operation. + :param url: the URL to join. + :param allow_fragments: indicates whether fragments should be allowed. + """ + if isinstance(base, tuple): + base = url_unparse(base) + if isinstance(url, tuple): + url = url_unparse(url) + + _check_str_tuple((base, url)) + s = _make_encode_wrapper(base) + + if not base: + return url + if not url: + return base + + bscheme, bnetloc, bpath, bquery, bfragment = url_parse( + base, allow_fragments=allow_fragments + ) + scheme, netloc, path, query, fragment = url_parse(url, bscheme, allow_fragments) + if scheme != bscheme: + return url + if netloc: + return url_unparse((scheme, netloc, path, query, fragment)) + netloc = bnetloc + + if path[:1] == s("/"): + segments = path.split(s("/")) + elif not path: + segments = bpath.split(s("/")) + if not query: + query = bquery + else: + segments = bpath.split(s("/"))[:-1] + path.split(s("/")) + + # If the rightmost part is "./" we want to keep the slash but + # remove the dot. + if segments[-1] == s("."): + segments[-1] = s("") + + # Resolve ".." and "." + segments = [segment for segment in segments if segment != s(".")] + while True: + i = 1 + n = len(segments) - 1 + while i < n: + if segments[i] == s("..") and segments[i - 1] not in (s(""), s("..")): + del segments[i - 1 : i + 1] + break + i += 1 + else: + break + + # Remove trailing ".." if the URL is absolute + unwanted_marker = [s(""), s("..")] + while segments[:2] == unwanted_marker: + del segments[1] + + path = s("/").join(segments) + return url_unparse((scheme, netloc, path, query, fragment)) diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/user_agent.py b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/user_agent.py new file mode 100644 index 0000000..66ffcbe --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/user_agent.py @@ -0,0 +1,47 @@ +import typing as t + + +class UserAgent: + """Represents a parsed user agent header value. + + The default implementation does no parsing, only the :attr:`string` + attribute is set. A subclass may parse the string to set the + common attributes or expose other information. Set + :attr:`werkzeug.wrappers.Request.user_agent_class` to use a + subclass. + + :param string: The header value to parse. + + .. versionadded:: 2.0 + This replaces the previous ``useragents`` module, but does not + provide a built-in parser. + """ + + platform: t.Optional[str] = None + """The OS name, if it could be parsed from the string.""" + + browser: t.Optional[str] = None + """The browser name, if it could be parsed from the string.""" + + version: t.Optional[str] = None + """The browser version, if it could be parsed from the string.""" + + language: t.Optional[str] = None + """The browser language, if it could be parsed from the string.""" + + def __init__(self, string: str) -> None: + self.string: str = string + """The original header value.""" + + def __repr__(self) -> str: + return f"<{type(self).__name__} {self.browser}/{self.version}>" + + def __str__(self) -> str: + return self.string + + def __bool__(self) -> bool: + return bool(self.browser) + + def to_header(self) -> str: + """Convert to a header value.""" + return self.string diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/utils.py b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/utils.py new file mode 100644 index 0000000..4ef5837 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/utils.py @@ -0,0 +1,706 @@ +import io +import mimetypes +import os +import pkgutil +import re +import sys +import typing as t +import unicodedata +from datetime import datetime +from time import time +from zlib import adler32 + +from markupsafe import escape + +from ._internal import _DictAccessorProperty +from ._internal import _missing +from ._internal import _TAccessorValue +from .datastructures import Headers +from .exceptions import NotFound +from .exceptions import RequestedRangeNotSatisfiable +from .security import safe_join +from .urls import url_quote +from .wsgi import wrap_file + +if t.TYPE_CHECKING: + from _typeshed.wsgi import WSGIEnvironment + from .wrappers.request import Request + from .wrappers.response import Response + +_T = t.TypeVar("_T") + +_entity_re = re.compile(r"&([^;]+);") +_filename_ascii_strip_re = re.compile(r"[^A-Za-z0-9_.-]") +_windows_device_files = ( + "CON", + "AUX", + "COM1", + "COM2", + "COM3", + "COM4", + "LPT1", + "LPT2", + "LPT3", + "PRN", + "NUL", +) + + +class cached_property(property, t.Generic[_T]): + """A :func:`property` that is only evaluated once. Subsequent access + returns the cached value. Setting the property sets the cached + value. Deleting the property clears the cached value, accessing it + again will evaluate it again. + + .. code-block:: python + + class Example: + @cached_property + def value(self): + # calculate something important here + return 42 + + e = Example() + e.value # evaluates + e.value # uses cache + e.value = 16 # sets cache + del e.value # clears cache + + If the class defines ``__slots__``, it must add ``_cache_{name}`` as + a slot. Alternatively, it can add ``__dict__``, but that's usually + not desirable. + + .. versionchanged:: 2.1 + Works with ``__slots__``. + + .. versionchanged:: 2.0 + ``del obj.name`` clears the cached value. + """ + + def __init__( + self, + fget: t.Callable[[t.Any], _T], + name: t.Optional[str] = None, + doc: t.Optional[str] = None, + ) -> None: + super().__init__(fget, doc=doc) + self.__name__ = name or fget.__name__ + self.slot_name = f"_cache_{self.__name__}" + self.__module__ = fget.__module__ + + def __set__(self, obj: object, value: _T) -> None: + if hasattr(obj, "__dict__"): + obj.__dict__[self.__name__] = value + else: + setattr(obj, self.slot_name, value) + + def __get__(self, obj: object, type: type = None) -> _T: # type: ignore + if obj is None: + return self # type: ignore + + obj_dict = getattr(obj, "__dict__", None) + + if obj_dict is not None: + value: _T = obj_dict.get(self.__name__, _missing) + else: + value = getattr(obj, self.slot_name, _missing) # type: ignore[arg-type] + + if value is _missing: + value = self.fget(obj) # type: ignore + + if obj_dict is not None: + obj.__dict__[self.__name__] = value + else: + setattr(obj, self.slot_name, value) + + return value + + def __delete__(self, obj: object) -> None: + if hasattr(obj, "__dict__"): + del obj.__dict__[self.__name__] + else: + setattr(obj, self.slot_name, _missing) + + +class environ_property(_DictAccessorProperty[_TAccessorValue]): + """Maps request attributes to environment variables. This works not only + for the Werkzeug request object, but also any other class with an + environ attribute: + + >>> class Test(object): + ... environ = {'key': 'value'} + ... test = environ_property('key') + >>> var = Test() + >>> var.test + 'value' + + If you pass it a second value it's used as default if the key does not + exist, the third one can be a converter that takes a value and converts + it. If it raises :exc:`ValueError` or :exc:`TypeError` the default value + is used. If no default value is provided `None` is used. + + Per default the property is read only. You have to explicitly enable it + by passing ``read_only=False`` to the constructor. + """ + + read_only = True + + def lookup(self, obj: "Request") -> "WSGIEnvironment": + return obj.environ + + +class header_property(_DictAccessorProperty[_TAccessorValue]): + """Like `environ_property` but for headers.""" + + def lookup(self, obj: t.Union["Request", "Response"]) -> Headers: + return obj.headers + + +# https://cgit.freedesktop.org/xdg/shared-mime-info/tree/freedesktop.org.xml.in +# https://www.iana.org/assignments/media-types/media-types.xhtml +# Types listed in the XDG mime info that have a charset in the IANA registration. +_charset_mimetypes = { + "application/ecmascript", + "application/javascript", + "application/sql", + "application/xml", + "application/xml-dtd", + "application/xml-external-parsed-entity", +} + + +def get_content_type(mimetype: str, charset: str) -> str: + """Returns the full content type string with charset for a mimetype. + + If the mimetype represents text, the charset parameter will be + appended, otherwise the mimetype is returned unchanged. + + :param mimetype: The mimetype to be used as content type. + :param charset: The charset to be appended for text mimetypes. + :return: The content type. + + .. versionchanged:: 0.15 + Any type that ends with ``+xml`` gets a charset, not just those + that start with ``application/``. Known text types such as + ``application/javascript`` are also given charsets. + """ + if ( + mimetype.startswith("text/") + or mimetype in _charset_mimetypes + or mimetype.endswith("+xml") + ): + mimetype += f"; charset={charset}" + + return mimetype + + +def secure_filename(filename: str) -> str: + r"""Pass it a filename and it will return a secure version of it. This + filename can then safely be stored on a regular file system and passed + to :func:`os.path.join`. The filename returned is an ASCII only string + for maximum portability. + + On windows systems the function also makes sure that the file is not + named after one of the special device files. + + >>> secure_filename("My cool movie.mov") + 'My_cool_movie.mov' + >>> secure_filename("../../../etc/passwd") + 'etc_passwd' + >>> secure_filename('i contain cool \xfcml\xe4uts.txt') + 'i_contain_cool_umlauts.txt' + + The function might return an empty filename. It's your responsibility + to ensure that the filename is unique and that you abort or + generate a random filename if the function returned an empty one. + + .. versionadded:: 0.5 + + :param filename: the filename to secure + """ + filename = unicodedata.normalize("NFKD", filename) + filename = filename.encode("ascii", "ignore").decode("ascii") + + for sep in os.sep, os.path.altsep: + if sep: + filename = filename.replace(sep, " ") + filename = str(_filename_ascii_strip_re.sub("", "_".join(filename.split()))).strip( + "._" + ) + + # on nt a couple of special files are present in each folder. We + # have to ensure that the target file is not such a filename. In + # this case we prepend an underline + if ( + os.name == "nt" + and filename + and filename.split(".")[0].upper() in _windows_device_files + ): + filename = f"_{filename}" + + return filename + + +def redirect( + location: str, code: int = 302, Response: t.Optional[t.Type["Response"]] = None +) -> "Response": + """Returns a response object (a WSGI application) that, if called, + redirects the client to the target location. Supported codes are + 301, 302, 303, 305, 307, and 308. 300 is not supported because + it's not a real redirect and 304 because it's the answer for a + request with a request with defined If-Modified-Since headers. + + .. versionadded:: 0.6 + The location can now be a unicode string that is encoded using + the :func:`iri_to_uri` function. + + .. versionadded:: 0.10 + The class used for the Response object can now be passed in. + + :param location: the location the response should redirect to. + :param code: the redirect status code. defaults to 302. + :param class Response: a Response class to use when instantiating a + response. The default is :class:`werkzeug.wrappers.Response` if + unspecified. + """ + if Response is None: + from .wrappers import Response # type: ignore + + display_location = escape(location) + if isinstance(location, str): + # Safe conversion is necessary here as we might redirect + # to a broken URI scheme (for instance itms-services). + from .urls import iri_to_uri + + location = iri_to_uri(location, safe_conversion=True) + + response = Response( # type: ignore + "\n" + "\n" + "Redirecting...\n" + "

    Redirecting...

    \n" + "

    You should be redirected automatically to the target URL: " + f'{display_location}. If' + " not, click the link.\n", + code, + mimetype="text/html", + ) + response.headers["Location"] = location + return response + + +def append_slash_redirect(environ: "WSGIEnvironment", code: int = 308) -> "Response": + """Redirect to the current URL with a slash appended. + + If the current URL is ``/user/42``, the redirect URL will be + ``42/``. When joined to the current URL during response + processing or by the browser, this will produce ``/user/42/``. + + The behavior is undefined if the path ends with a slash already. If + called unconditionally on a URL, it may produce a redirect loop. + + :param environ: Use the path and query from this WSGI environment + to produce the redirect URL. + :param code: the status code for the redirect. + + .. versionchanged:: 2.1 + Produce a relative URL that only modifies the last segment. + Relevant when the current path has multiple segments. + + .. versionchanged:: 2.1 + The default status code is 308 instead of 301. This preserves + the request method and body. + """ + tail = environ["PATH_INFO"].rpartition("/")[2] + + if not tail: + new_path = "./" + else: + new_path = f"{tail}/" + + query_string = environ.get("QUERY_STRING") + + if query_string: + new_path = f"{new_path}?{query_string}" + + return redirect(new_path, code) + + +def send_file( + path_or_file: t.Union[os.PathLike, str, t.IO[bytes]], + environ: "WSGIEnvironment", + mimetype: t.Optional[str] = None, + as_attachment: bool = False, + download_name: t.Optional[str] = None, + conditional: bool = True, + etag: t.Union[bool, str] = True, + last_modified: t.Optional[t.Union[datetime, int, float]] = None, + max_age: t.Optional[ + t.Union[int, t.Callable[[t.Optional[str]], t.Optional[int]]] + ] = None, + use_x_sendfile: bool = False, + response_class: t.Optional[t.Type["Response"]] = None, + _root_path: t.Optional[t.Union[os.PathLike, str]] = None, +) -> "Response": + """Send the contents of a file to the client. + + The first argument can be a file path or a file-like object. Paths + are preferred in most cases because Werkzeug can manage the file and + get extra information from the path. Passing a file-like object + requires that the file is opened in binary mode, and is mostly + useful when building a file in memory with :class:`io.BytesIO`. + + Never pass file paths provided by a user. The path is assumed to be + trusted, so a user could craft a path to access a file you didn't + intend. Use :func:`send_from_directory` to safely serve user-provided paths. + + If the WSGI server sets a ``file_wrapper`` in ``environ``, it is + used, otherwise Werkzeug's built-in wrapper is used. Alternatively, + if the HTTP server supports ``X-Sendfile``, ``use_x_sendfile=True`` + will tell the server to send the given path, which is much more + efficient than reading it in Python. + + :param path_or_file: The path to the file to send, relative to the + current working directory if a relative path is given. + Alternatively, a file-like object opened in binary mode. Make + sure the file pointer is seeked to the start of the data. + :param environ: The WSGI environ for the current request. + :param mimetype: The MIME type to send for the file. If not + provided, it will try to detect it from the file name. + :param as_attachment: Indicate to a browser that it should offer to + save the file instead of displaying it. + :param download_name: The default name browsers will use when saving + the file. Defaults to the passed file name. + :param conditional: Enable conditional and range responses based on + request headers. Requires passing a file path and ``environ``. + :param etag: Calculate an ETag for the file, which requires passing + a file path. Can also be a string to use instead. + :param last_modified: The last modified time to send for the file, + in seconds. If not provided, it will try to detect it from the + file path. + :param max_age: How long the client should cache the file, in + seconds. If set, ``Cache-Control`` will be ``public``, otherwise + it will be ``no-cache`` to prefer conditional caching. + :param use_x_sendfile: Set the ``X-Sendfile`` header to let the + server to efficiently send the file. Requires support from the + HTTP server. Requires passing a file path. + :param response_class: Build the response using this class. Defaults + to :class:`~werkzeug.wrappers.Response`. + :param _root_path: Do not use. For internal use only. Use + :func:`send_from_directory` to safely send files under a path. + + .. versionchanged:: 2.0.2 + ``send_file`` only sets a detected ``Content-Encoding`` if + ``as_attachment`` is disabled. + + .. versionadded:: 2.0 + Adapted from Flask's implementation. + + .. versionchanged:: 2.0 + ``download_name`` replaces Flask's ``attachment_filename`` + parameter. If ``as_attachment=False``, it is passed with + ``Content-Disposition: inline`` instead. + + .. versionchanged:: 2.0 + ``max_age`` replaces Flask's ``cache_timeout`` parameter. + ``conditional`` is enabled and ``max_age`` is not set by + default. + + .. versionchanged:: 2.0 + ``etag`` replaces Flask's ``add_etags`` parameter. It can be a + string to use instead of generating one. + + .. versionchanged:: 2.0 + If an encoding is returned when guessing ``mimetype`` from + ``download_name``, set the ``Content-Encoding`` header. + """ + if response_class is None: + from .wrappers import Response + + response_class = Response + + path: t.Optional[str] = None + file: t.Optional[t.IO[bytes]] = None + size: t.Optional[int] = None + mtime: t.Optional[float] = None + headers = Headers() + + if isinstance(path_or_file, (os.PathLike, str)) or hasattr( + path_or_file, "__fspath__" + ): + path_or_file = t.cast(t.Union[os.PathLike, str], path_or_file) + + # Flask will pass app.root_path, allowing its send_file wrapper + # to not have to deal with paths. + if _root_path is not None: + path = os.path.join(_root_path, path_or_file) + else: + path = os.path.abspath(path_or_file) + + stat = os.stat(path) + size = stat.st_size + mtime = stat.st_mtime + else: + file = path_or_file + + if download_name is None and path is not None: + download_name = os.path.basename(path) + + if mimetype is None: + if download_name is None: + raise TypeError( + "Unable to detect the MIME type because a file name is" + " not available. Either set 'download_name', pass a" + " path instead of a file, or set 'mimetype'." + ) + + mimetype, encoding = mimetypes.guess_type(download_name) + + if mimetype is None: + mimetype = "application/octet-stream" + + # Don't send encoding for attachments, it causes browsers to + # save decompress tar.gz files. + if encoding is not None and not as_attachment: + headers.set("Content-Encoding", encoding) + + if download_name is not None: + try: + download_name.encode("ascii") + except UnicodeEncodeError: + simple = unicodedata.normalize("NFKD", download_name) + simple = simple.encode("ascii", "ignore").decode("ascii") + quoted = url_quote(download_name, safe="") + names = {"filename": simple, "filename*": f"UTF-8''{quoted}"} + else: + names = {"filename": download_name} + + value = "attachment" if as_attachment else "inline" + headers.set("Content-Disposition", value, **names) + elif as_attachment: + raise TypeError( + "No name provided for attachment. Either set" + " 'download_name' or pass a path instead of a file." + ) + + if use_x_sendfile and path is not None: + headers["X-Sendfile"] = path + data = None + else: + if file is None: + file = open(path, "rb") # type: ignore + elif isinstance(file, io.BytesIO): + size = file.getbuffer().nbytes + elif isinstance(file, io.TextIOBase): + raise ValueError("Files must be opened in binary mode or use BytesIO.") + + data = wrap_file(environ, file) + + rv = response_class( + data, mimetype=mimetype, headers=headers, direct_passthrough=True + ) + + if size is not None: + rv.content_length = size + + if last_modified is not None: + rv.last_modified = last_modified # type: ignore + elif mtime is not None: + rv.last_modified = mtime # type: ignore + + rv.cache_control.no_cache = True + + # Flask will pass app.get_send_file_max_age, allowing its send_file + # wrapper to not have to deal with paths. + if callable(max_age): + max_age = max_age(path) + + if max_age is not None: + if max_age > 0: + rv.cache_control.no_cache = None + rv.cache_control.public = True + + rv.cache_control.max_age = max_age + rv.expires = int(time() + max_age) # type: ignore + + if isinstance(etag, str): + rv.set_etag(etag) + elif etag and path is not None: + check = adler32(path.encode("utf-8")) & 0xFFFFFFFF + rv.set_etag(f"{mtime}-{size}-{check}") + + if conditional: + try: + rv = rv.make_conditional(environ, accept_ranges=True, complete_length=size) + except RequestedRangeNotSatisfiable: + if file is not None: + file.close() + + raise + + # Some x-sendfile implementations incorrectly ignore the 304 + # status code and send the file anyway. + if rv.status_code == 304: + rv.headers.pop("x-sendfile", None) + + return rv + + +def send_from_directory( + directory: t.Union[os.PathLike, str], + path: t.Union[os.PathLike, str], + environ: "WSGIEnvironment", + **kwargs: t.Any, +) -> "Response": + """Send a file from within a directory using :func:`send_file`. + + This is a secure way to serve files from a folder, such as static + files or uploads. Uses :func:`~werkzeug.security.safe_join` to + ensure the path coming from the client is not maliciously crafted to + point outside the specified directory. + + If the final path does not point to an existing regular file, + returns a 404 :exc:`~werkzeug.exceptions.NotFound` error. + + :param directory: The directory that ``path`` must be located under. This *must not* + be a value provided by the client, otherwise it becomes insecure. + :param path: The path to the file to send, relative to ``directory``. This is the + part of the path provided by the client, which is checked for security. + :param environ: The WSGI environ for the current request. + :param kwargs: Arguments to pass to :func:`send_file`. + + .. versionadded:: 2.0 + Adapted from Flask's implementation. + """ + path = safe_join(os.fspath(directory), os.fspath(path)) + + if path is None: + raise NotFound() + + # Flask will pass app.root_path, allowing its send_from_directory + # wrapper to not have to deal with paths. + if "_root_path" in kwargs: + path = os.path.join(kwargs["_root_path"], path) + + try: + if not os.path.isfile(path): + raise NotFound() + except ValueError: + # path contains null byte on Python < 3.8 + raise NotFound() from None + + return send_file(path, environ, **kwargs) + + +def import_string(import_name: str, silent: bool = False) -> t.Any: + """Imports an object based on a string. This is useful if you want to + use import paths as endpoints or something similar. An import path can + be specified either in dotted notation (``xml.sax.saxutils.escape``) + or with a colon as object delimiter (``xml.sax.saxutils:escape``). + + If `silent` is True the return value will be `None` if the import fails. + + :param import_name: the dotted name for the object to import. + :param silent: if set to `True` import errors are ignored and + `None` is returned instead. + :return: imported object + """ + import_name = import_name.replace(":", ".") + try: + try: + __import__(import_name) + except ImportError: + if "." not in import_name: + raise + else: + return sys.modules[import_name] + + module_name, obj_name = import_name.rsplit(".", 1) + module = __import__(module_name, globals(), locals(), [obj_name]) + try: + return getattr(module, obj_name) + except AttributeError as e: + raise ImportError(e) from None + + except ImportError as e: + if not silent: + raise ImportStringError(import_name, e).with_traceback( + sys.exc_info()[2] + ) from None + + return None + + +def find_modules( + import_path: str, include_packages: bool = False, recursive: bool = False +) -> t.Iterator[str]: + """Finds all the modules below a package. This can be useful to + automatically import all views / controllers so that their metaclasses / + function decorators have a chance to register themselves on the + application. + + Packages are not returned unless `include_packages` is `True`. This can + also recursively list modules but in that case it will import all the + packages to get the correct load path of that module. + + :param import_path: the dotted name for the package to find child modules. + :param include_packages: set to `True` if packages should be returned, too. + :param recursive: set to `True` if recursion should happen. + :return: generator + """ + module = import_string(import_path) + path = getattr(module, "__path__", None) + if path is None: + raise ValueError(f"{import_path!r} is not a package") + basename = f"{module.__name__}." + for _importer, modname, ispkg in pkgutil.iter_modules(path): + modname = basename + modname + if ispkg: + if include_packages: + yield modname + if recursive: + yield from find_modules(modname, include_packages, True) + else: + yield modname + + +class ImportStringError(ImportError): + """Provides information about a failed :func:`import_string` attempt.""" + + #: String in dotted notation that failed to be imported. + import_name: str + #: Wrapped exception. + exception: BaseException + + def __init__(self, import_name: str, exception: BaseException) -> None: + self.import_name = import_name + self.exception = exception + msg = import_name + name = "" + tracked = [] + for part in import_name.replace(":", ".").split("."): + name = f"{name}.{part}" if name else part + imported = import_string(name, silent=True) + if imported: + tracked.append((name, getattr(imported, "__file__", None))) + else: + track = [f"- {n!r} found in {i!r}." for n, i in tracked] + track.append(f"- {name!r} not found.") + track_str = "\n".join(track) + msg = ( + f"import_string() failed for {import_name!r}. Possible reasons" + f" are:\n\n" + "- missing __init__.py in a package;\n" + "- package or module path not included in sys.path;\n" + "- duplicated package or module name taking precedence in" + " sys.path;\n" + "- missing module, class, function or variable;\n\n" + f"Debugged import:\n\n{track_str}\n\n" + f"Original exception:\n\n{type(exception).__name__}: {exception}" + ) + break + + super().__init__(msg) + + def __repr__(self) -> str: + return f"<{type(self).__name__}({self.import_name!r}, {self.exception!r})>" diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/wrappers/__init__.py b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/wrappers/__init__.py new file mode 100644 index 0000000..b8c45d7 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/wrappers/__init__.py @@ -0,0 +1,3 @@ +from .request import Request as Request +from .response import Response as Response +from .response import ResponseStream diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/wrappers/__pycache__/__init__.cpython-311.pyc b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/wrappers/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7ac97bee34f7f8b344b288510c03b5bd9c2351fd GIT binary patch literal 398 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 UV9V>LOPSC4&6iJk&lCOs0h0T1p#T5? literal 0 HcmV?d00001 diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/wrappers/__pycache__/response.cpython-311.pyc b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/wrappers/__pycache__/response.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b442202e9bd0bab9cab5772a2b08c8a7ff9d5fb2 GIT binary patch literal 40516 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 D2vvys literal 0 HcmV?d00001 diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/wrappers/request.py b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/wrappers/request.py new file mode 100644 index 0000000..2de77df --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/wrappers/request.py @@ -0,0 +1,634 @@ +import functools +import json +import typing +import typing as t +from io import BytesIO + +from .._internal import _wsgi_decoding_dance +from ..datastructures import CombinedMultiDict +from ..datastructures import EnvironHeaders +from ..datastructures import FileStorage +from ..datastructures import ImmutableMultiDict +from ..datastructures import iter_multi_items +from ..datastructures import MultiDict +from ..formparser import default_stream_factory +from ..formparser import FormDataParser +from ..sansio.request import Request as _SansIORequest +from ..utils import cached_property +from ..utils import environ_property +from ..wsgi import _get_server +from ..wsgi import get_input_stream +from werkzeug.exceptions import BadRequest + +if t.TYPE_CHECKING: + import typing_extensions as te + from _typeshed.wsgi import WSGIApplication + from _typeshed.wsgi import WSGIEnvironment + + +class Request(_SansIORequest): + """Represents an incoming WSGI HTTP request, with headers and body + taken from the WSGI environment. Has properties and methods for + using the functionality defined by various HTTP specs. The data in + requests object is read-only. + + Text data is assumed to use UTF-8 encoding, which should be true for + the vast majority of modern clients. Using an encoding set by the + client is unsafe in Python due to extra encodings it provides, such + as ``zip``. To change the assumed encoding, subclass and replace + :attr:`charset`. + + :param environ: The WSGI environ is generated by the WSGI server and + contains information about the server configuration and client + request. + :param populate_request: Add this request object to the WSGI environ + as ``environ['werkzeug.request']``. Can be useful when + debugging. + :param shallow: Makes reading from :attr:`stream` (and any method + that would read from it) raise a :exc:`RuntimeError`. Useful to + prevent consuming the form data in middleware, which would make + it unavailable to the final application. + + .. versionchanged:: 2.1 + Remove the ``disable_data_descriptor`` attribute. + + .. versionchanged:: 2.0 + Combine ``BaseRequest`` and mixins into a single ``Request`` + class. Using the old classes is deprecated and will be removed + in Werkzeug 2.1. + + .. versionchanged:: 0.5 + Read-only mode is enforced with immutable classes for all data. + """ + + #: the maximum content length. This is forwarded to the form data + #: parsing function (:func:`parse_form_data`). When set and the + #: :attr:`form` or :attr:`files` attribute is accessed and the + #: parsing fails because more than the specified value is transmitted + #: a :exc:`~werkzeug.exceptions.RequestEntityTooLarge` exception is raised. + #: + #: Have a look at :doc:`/request_data` for more details. + #: + #: .. versionadded:: 0.5 + max_content_length: t.Optional[int] = None + + #: the maximum form field size. This is forwarded to the form data + #: parsing function (:func:`parse_form_data`). When set and the + #: :attr:`form` or :attr:`files` attribute is accessed and the + #: data in memory for post data is longer than the specified value a + #: :exc:`~werkzeug.exceptions.RequestEntityTooLarge` exception is raised. + #: + #: Have a look at :doc:`/request_data` for more details. + #: + #: .. versionadded:: 0.5 + max_form_memory_size: t.Optional[int] = None + + #: The maximum number of multipart parts to parse, passed to + #: :attr:`form_data_parser_class`. Parsing form data with more than this + #: many parts will raise :exc:`~.RequestEntityTooLarge`. + #: + #: .. versionadded:: 2.2.3 + max_form_parts = 1000 + + #: The form data parser that should be used. Can be replaced to customize + #: the form date parsing. + form_data_parser_class: t.Type[FormDataParser] = FormDataParser + + #: The WSGI environment containing HTTP headers and information from + #: the WSGI server. + environ: "WSGIEnvironment" + + #: Set when creating the request object. If ``True``, reading from + #: the request body will cause a ``RuntimeException``. Useful to + #: prevent modifying the stream from middleware. + shallow: bool + + def __init__( + self, + environ: "WSGIEnvironment", + populate_request: bool = True, + shallow: bool = False, + ) -> None: + super().__init__( + method=environ.get("REQUEST_METHOD", "GET"), + scheme=environ.get("wsgi.url_scheme", "http"), + server=_get_server(environ), + root_path=_wsgi_decoding_dance( + environ.get("SCRIPT_NAME") or "", self.charset, self.encoding_errors + ), + path=_wsgi_decoding_dance( + environ.get("PATH_INFO") or "", self.charset, self.encoding_errors + ), + query_string=environ.get("QUERY_STRING", "").encode("latin1"), + headers=EnvironHeaders(environ), + remote_addr=environ.get("REMOTE_ADDR"), + ) + self.environ = environ + self.shallow = shallow + + if populate_request and not shallow: + self.environ["werkzeug.request"] = self + + @classmethod + def from_values(cls, *args: t.Any, **kwargs: t.Any) -> "Request": + """Create a new request object based on the values provided. If + environ is given missing values are filled from there. This method is + useful for small scripts when you need to simulate a request from an URL. + Do not use this method for unittesting, there is a full featured client + object (:class:`Client`) that allows to create multipart requests, + support for cookies etc. + + This accepts the same options as the + :class:`~werkzeug.test.EnvironBuilder`. + + .. versionchanged:: 0.5 + This method now accepts the same arguments as + :class:`~werkzeug.test.EnvironBuilder`. Because of this the + `environ` parameter is now called `environ_overrides`. + + :return: request object + """ + from ..test import EnvironBuilder + + charset = kwargs.pop("charset", cls.charset) + kwargs["charset"] = charset + builder = EnvironBuilder(*args, **kwargs) + try: + return builder.get_request(cls) + finally: + builder.close() + + @classmethod + def application( + cls, f: t.Callable[["Request"], "WSGIApplication"] + ) -> "WSGIApplication": + """Decorate a function as responder that accepts the request as + the last argument. This works like the :func:`responder` + decorator but the function is passed the request object as the + last argument and the request object will be closed + automatically:: + + @Request.application + def my_wsgi_app(request): + return Response('Hello World!') + + As of Werkzeug 0.14 HTTP exceptions are automatically caught and + converted to responses instead of failing. + + :param f: the WSGI callable to decorate + :return: a new WSGI callable + """ + #: return a callable that wraps the -2nd argument with the request + #: and calls the function with all the arguments up to that one and + #: the request. The return value is then called with the latest + #: two arguments. This makes it possible to use this decorator for + #: both standalone WSGI functions as well as bound methods and + #: partially applied functions. + from ..exceptions import HTTPException + + @functools.wraps(f) + def application(*args): # type: ignore + request = cls(args[-2]) + with request: + try: + resp = f(*args[:-2] + (request,)) + except HTTPException as e: + resp = e.get_response(args[-2]) + return resp(*args[-2:]) + + return t.cast("WSGIApplication", application) + + def _get_file_stream( + self, + total_content_length: t.Optional[int], + content_type: t.Optional[str], + filename: t.Optional[str] = None, + content_length: t.Optional[int] = None, + ) -> t.IO[bytes]: + """Called to get a stream for the file upload. + + This must provide a file-like class with `read()`, `readline()` + and `seek()` methods that is both writeable and readable. + + The default implementation returns a temporary file if the total + content length is higher than 500KB. Because many browsers do not + provide a content length for the files only the total content + length matters. + + :param total_content_length: the total content length of all the + data in the request combined. This value + is guaranteed to be there. + :param content_type: the mimetype of the uploaded file. + :param filename: the filename of the uploaded file. May be `None`. + :param content_length: the length of this file. This value is usually + not provided because webbrowsers do not provide + this value. + """ + return default_stream_factory( + total_content_length=total_content_length, + filename=filename, + content_type=content_type, + content_length=content_length, + ) + + @property + def want_form_data_parsed(self) -> bool: + """``True`` if the request method carries content. By default + this is true if a ``Content-Type`` is sent. + + .. versionadded:: 0.8 + """ + return bool(self.environ.get("CONTENT_TYPE")) + + def make_form_data_parser(self) -> FormDataParser: + """Creates the form data parser. Instantiates the + :attr:`form_data_parser_class` with some parameters. + + .. versionadded:: 0.8 + """ + return self.form_data_parser_class( + self._get_file_stream, + self.charset, + self.encoding_errors, + self.max_form_memory_size, + self.max_content_length, + self.parameter_storage_class, + max_form_parts=self.max_form_parts, + ) + + def _load_form_data(self) -> None: + """Method used internally to retrieve submitted data. After calling + this sets `form` and `files` on the request object to multi dicts + filled with the incoming form data. As a matter of fact the input + stream will be empty afterwards. You can also call this method to + force the parsing of the form data. + + .. versionadded:: 0.8 + """ + # abort early if we have already consumed the stream + if "form" in self.__dict__: + return + + if self.want_form_data_parsed: + parser = self.make_form_data_parser() + data = parser.parse( + self._get_stream_for_parsing(), + self.mimetype, + self.content_length, + self.mimetype_params, + ) + else: + data = ( + self.stream, + self.parameter_storage_class(), + self.parameter_storage_class(), + ) + + # inject the values into the instance dict so that we bypass + # our cached_property non-data descriptor. + d = self.__dict__ + d["stream"], d["form"], d["files"] = data + + def _get_stream_for_parsing(self) -> t.IO[bytes]: + """This is the same as accessing :attr:`stream` with the difference + that if it finds cached data from calling :meth:`get_data` first it + will create a new stream out of the cached data. + + .. versionadded:: 0.9.3 + """ + cached_data = getattr(self, "_cached_data", None) + if cached_data is not None: + return BytesIO(cached_data) + return self.stream + + def close(self) -> None: + """Closes associated resources of this request object. This + closes all file handles explicitly. You can also use the request + object in a with statement which will automatically close it. + + .. versionadded:: 0.9 + """ + files = self.__dict__.get("files") + for _key, value in iter_multi_items(files or ()): + value.close() + + def __enter__(self) -> "Request": + return self + + def __exit__(self, exc_type, exc_value, tb) -> None: # type: ignore + self.close() + + @cached_property + def stream(self) -> t.IO[bytes]: + """ + If the incoming form data was not encoded with a known mimetype + the data is stored unmodified in this stream for consumption. Most + of the time it is a better idea to use :attr:`data` which will give + you that data as a string. The stream only returns the data once. + + Unlike :attr:`input_stream` this stream is properly guarded that you + can't accidentally read past the length of the input. Werkzeug will + internally always refer to this stream to read data which makes it + possible to wrap this object with a stream that does filtering. + + .. versionchanged:: 0.9 + This stream is now always available but might be consumed by the + form parser later on. Previously the stream was only set if no + parsing happened. + """ + if self.shallow: + raise RuntimeError( + "This request was created with 'shallow=True', reading" + " from the input stream is disabled." + ) + + return get_input_stream(self.environ) + + input_stream = environ_property[t.IO[bytes]]( + "wsgi.input", + doc="""The WSGI input stream. + + In general it's a bad idea to use this one because you can + easily read past the boundary. Use the :attr:`stream` + instead.""", + ) + + @cached_property + def data(self) -> bytes: + """ + Contains the incoming request data as string in case it came with + a mimetype Werkzeug does not handle. + """ + return self.get_data(parse_form_data=True) + + @typing.overload + def get_data( # type: ignore + self, + cache: bool = True, + as_text: "te.Literal[False]" = False, + parse_form_data: bool = False, + ) -> bytes: + ... + + @typing.overload + def get_data( + self, + cache: bool = True, + as_text: "te.Literal[True]" = ..., + parse_form_data: bool = False, + ) -> str: + ... + + def get_data( + self, cache: bool = True, as_text: bool = False, parse_form_data: bool = False + ) -> t.Union[bytes, str]: + """This reads the buffered incoming data from the client into one + bytes object. By default this is cached but that behavior can be + changed by setting `cache` to `False`. + + Usually it's a bad idea to call this method without checking the + content length first as a client could send dozens of megabytes or more + to cause memory problems on the server. + + Note that if the form data was already parsed this method will not + return anything as form data parsing does not cache the data like + this method does. To implicitly invoke form data parsing function + set `parse_form_data` to `True`. When this is done the return value + of this method will be an empty string if the form parser handles + the data. This generally is not necessary as if the whole data is + cached (which is the default) the form parser will used the cached + data to parse the form data. Please be generally aware of checking + the content length first in any case before calling this method + to avoid exhausting server memory. + + If `as_text` is set to `True` the return value will be a decoded + string. + + .. versionadded:: 0.9 + """ + rv = getattr(self, "_cached_data", None) + if rv is None: + if parse_form_data: + self._load_form_data() + rv = self.stream.read() + if cache: + self._cached_data = rv + if as_text: + rv = rv.decode(self.charset, self.encoding_errors) + return rv + + @cached_property + def form(self) -> "ImmutableMultiDict[str, str]": + """The form parameters. By default an + :class:`~werkzeug.datastructures.ImmutableMultiDict` + is returned from this function. This can be changed by setting + :attr:`parameter_storage_class` to a different type. This might + be necessary if the order of the form data is important. + + Please keep in mind that file uploads will not end up here, but instead + in the :attr:`files` attribute. + + .. versionchanged:: 0.9 + + Previous to Werkzeug 0.9 this would only contain form data for POST + and PUT requests. + """ + self._load_form_data() + return self.form + + @cached_property + def values(self) -> "CombinedMultiDict[str, str]": + """A :class:`werkzeug.datastructures.CombinedMultiDict` that + combines :attr:`args` and :attr:`form`. + + For GET requests, only ``args`` are present, not ``form``. + + .. versionchanged:: 2.0 + For GET requests, only ``args`` are present, not ``form``. + """ + sources = [self.args] + + if self.method != "GET": + # GET requests can have a body, and some caching proxies + # might not treat that differently than a normal GET + # request, allowing form data to "invisibly" affect the + # cache without indication in the query string / URL. + sources.append(self.form) + + args = [] + + for d in sources: + if not isinstance(d, MultiDict): + d = MultiDict(d) + + args.append(d) + + return CombinedMultiDict(args) + + @cached_property + def files(self) -> "ImmutableMultiDict[str, FileStorage]": + """:class:`~werkzeug.datastructures.MultiDict` object containing + all uploaded files. Each key in :attr:`files` is the name from the + ````. Each value in :attr:`files` is a + Werkzeug :class:`~werkzeug.datastructures.FileStorage` object. + + It basically behaves like a standard file object you know from Python, + with the difference that it also has a + :meth:`~werkzeug.datastructures.FileStorage.save` function that can + store the file on the filesystem. + + Note that :attr:`files` will only contain data if the request method was + POST, PUT or PATCH and the ``
    `` that posted to the request had + ``enctype="multipart/form-data"``. It will be empty otherwise. + + See the :class:`~werkzeug.datastructures.MultiDict` / + :class:`~werkzeug.datastructures.FileStorage` documentation for + more details about the used data structure. + """ + self._load_form_data() + return self.files + + @property + def script_root(self) -> str: + """Alias for :attr:`self.root_path`. ``environ["SCRIPT_ROOT"]`` + without a trailing slash. + """ + return self.root_path + + @cached_property + def url_root(self) -> str: + """Alias for :attr:`root_url`. The URL with scheme, host, and + root path. For example, ``https://example.com/app/``. + """ + return self.root_url + + remote_user = environ_property[str]( + "REMOTE_USER", + doc="""If the server supports user authentication, and the + script is protected, this attribute contains the username the + user has authenticated as.""", + ) + is_multithread = environ_property[bool]( + "wsgi.multithread", + doc="""boolean that is `True` if the application is served by a + multithreaded WSGI server.""", + ) + is_multiprocess = environ_property[bool]( + "wsgi.multiprocess", + doc="""boolean that is `True` if the application is served by a + WSGI server that spawns multiple processes.""", + ) + is_run_once = environ_property[bool]( + "wsgi.run_once", + doc="""boolean that is `True` if the application will be + executed only once in a process lifetime. This is the case for + CGI for example, but it's not guaranteed that the execution only + happens one time.""", + ) + + # JSON + + #: A module or other object that has ``dumps`` and ``loads`` + #: functions that match the API of the built-in :mod:`json` module. + json_module = json + + @property + def json(self) -> t.Optional[t.Any]: + """The parsed JSON data if :attr:`mimetype` indicates JSON + (:mimetype:`application/json`, see :attr:`is_json`). + + Calls :meth:`get_json` with default arguments. + + If the request content type is not ``application/json``, this + will raise a 400 Bad Request error. + + .. versionchanged:: 2.1 + Raise a 400 error if the content type is incorrect. + """ + return self.get_json() + + # Cached values for ``(silent=False, silent=True)``. Initialized + # with sentinel values. + _cached_json: t.Tuple[t.Any, t.Any] = (Ellipsis, Ellipsis) + + @t.overload + def get_json( + self, force: bool = ..., silent: "te.Literal[False]" = ..., cache: bool = ... + ) -> t.Any: + ... + + @t.overload + def get_json( + self, force: bool = ..., silent: bool = ..., cache: bool = ... + ) -> t.Optional[t.Any]: + ... + + def get_json( + self, force: bool = False, silent: bool = False, cache: bool = True + ) -> t.Optional[t.Any]: + """Parse :attr:`data` as JSON. + + If the mimetype does not indicate JSON + (:mimetype:`application/json`, see :attr:`is_json`), or parsing + fails, :meth:`on_json_loading_failed` is called and + its return value is used as the return value. By default this + raises a 400 Bad Request error. + + :param force: Ignore the mimetype and always try to parse JSON. + :param silent: Silence mimetype and parsing errors, and + return ``None`` instead. + :param cache: Store the parsed JSON to return for subsequent + calls. + + .. versionchanged:: 2.1 + Raise a 400 error if the content type is incorrect. + """ + if cache and self._cached_json[silent] is not Ellipsis: + return self._cached_json[silent] + + if not (force or self.is_json): + if not silent: + return self.on_json_loading_failed(None) + else: + return None + + data = self.get_data(cache=cache) + + try: + rv = self.json_module.loads(data) + except ValueError as e: + if silent: + rv = None + + if cache: + normal_rv, _ = self._cached_json + self._cached_json = (normal_rv, rv) + else: + rv = self.on_json_loading_failed(e) + + if cache: + _, silent_rv = self._cached_json + self._cached_json = (rv, silent_rv) + else: + if cache: + self._cached_json = (rv, rv) + + return rv + + def on_json_loading_failed(self, e: t.Optional[ValueError]) -> t.Any: + """Called if :meth:`get_json` fails and isn't silenced. + + If this method returns a value, it is used as the return value + for :meth:`get_json`. The default implementation raises + :exc:`~werkzeug.exceptions.BadRequest`. + + :param e: If parsing failed, this is the exception. It will be + ``None`` if the content type wasn't ``application/json``. + """ + if e is not None: + raise BadRequest(f"Failed to decode JSON object: {e}") + + raise BadRequest( + "Did not attempt to load JSON data because the request" + " Content-Type was not 'application/json'." + ) diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/wrappers/response.py b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/wrappers/response.py new file mode 100644 index 0000000..454208c --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/wrappers/response.py @@ -0,0 +1,885 @@ +import json +import typing +import typing as t +import warnings +from http import HTTPStatus + +from .._internal import _to_bytes +from ..datastructures import Headers +from ..http import remove_entity_headers +from ..sansio.response import Response as _SansIOResponse +from ..urls import iri_to_uri +from ..urls import url_join +from ..utils import cached_property +from ..wsgi import ClosingIterator +from ..wsgi import get_current_url +from werkzeug._internal import _get_environ +from werkzeug.http import generate_etag +from werkzeug.http import http_date +from werkzeug.http import is_resource_modified +from werkzeug.http import parse_etags +from werkzeug.http import parse_range_header +from werkzeug.wsgi import _RangeWrapper + +if t.TYPE_CHECKING: + import typing_extensions as te + from _typeshed.wsgi import StartResponse + from _typeshed.wsgi import WSGIApplication + from _typeshed.wsgi import WSGIEnvironment + from .request import Request + + +def _warn_if_string(iterable: t.Iterable) -> None: + """Helper for the response objects to check if the iterable returned + to the WSGI server is not a string. + """ + if isinstance(iterable, str): + warnings.warn( + "Response iterable was set to a string. This will appear to" + " work but means that the server will send the data to the" + " client one character at a time. This is almost never" + " intended behavior, use 'response.data' to assign strings" + " to the response object.", + stacklevel=2, + ) + + +def _iter_encoded( + iterable: t.Iterable[t.Union[str, bytes]], charset: str +) -> t.Iterator[bytes]: + for item in iterable: + if isinstance(item, str): + yield item.encode(charset) + else: + yield item + + +def _clean_accept_ranges(accept_ranges: t.Union[bool, str]) -> str: + if accept_ranges is True: + return "bytes" + elif accept_ranges is False: + return "none" + elif isinstance(accept_ranges, str): + return accept_ranges + raise ValueError("Invalid accept_ranges value") + + +class Response(_SansIOResponse): + """Represents an outgoing WSGI HTTP response with body, status, and + headers. Has properties and methods for using the functionality + defined by various HTTP specs. + + The response body is flexible to support different use cases. The + simple form is passing bytes, or a string which will be encoded as + UTF-8. Passing an iterable of bytes or strings makes this a + streaming response. A generator is particularly useful for building + a CSV file in memory or using SSE (Server Sent Events). A file-like + object is also iterable, although the + :func:`~werkzeug.utils.send_file` helper should be used in that + case. + + The response object is itself a WSGI application callable. When + called (:meth:`__call__`) with ``environ`` and ``start_response``, + it will pass its status and headers to ``start_response`` then + return its body as an iterable. + + .. code-block:: python + + from werkzeug.wrappers.response import Response + + def index(): + return Response("Hello, World!") + + def application(environ, start_response): + path = environ.get("PATH_INFO") or "/" + + if path == "/": + response = index() + else: + response = Response("Not Found", status=404) + + return response(environ, start_response) + + :param response: The data for the body of the response. A string or + bytes, or tuple or list of strings or bytes, for a fixed-length + response, or any other iterable of strings or bytes for a + streaming response. Defaults to an empty body. + :param status: The status code for the response. Either an int, in + which case the default status message is added, or a string in + the form ``{code} {message}``, like ``404 Not Found``. Defaults + to 200. + :param headers: A :class:`~werkzeug.datastructures.Headers` object, + or a list of ``(key, value)`` tuples that will be converted to a + ``Headers`` object. + :param mimetype: The mime type (content type without charset or + other parameters) of the response. If the value starts with + ``text/`` (or matches some other special cases), the charset + will be added to create the ``content_type``. + :param content_type: The full content type of the response. + Overrides building the value from ``mimetype``. + :param direct_passthrough: Pass the response body directly through + as the WSGI iterable. This can be used when the body is a binary + file or other iterator of bytes, to skip some unnecessary + checks. Use :func:`~werkzeug.utils.send_file` instead of setting + this manually. + + .. versionchanged:: 2.0 + Combine ``BaseResponse`` and mixins into a single ``Response`` + class. Using the old classes is deprecated and will be removed + in Werkzeug 2.1. + + .. versionchanged:: 0.5 + The ``direct_passthrough`` parameter was added. + """ + + #: if set to `False` accessing properties on the response object will + #: not try to consume the response iterator and convert it into a list. + #: + #: .. versionadded:: 0.6.2 + #: + #: That attribute was previously called `implicit_seqence_conversion`. + #: (Notice the typo). If you did use this feature, you have to adapt + #: your code to the name change. + implicit_sequence_conversion = True + + #: If a redirect ``Location`` header is a relative URL, make it an + #: absolute URL, including scheme and domain. + #: + #: .. versionchanged:: 2.1 + #: This is disabled by default, so responses will send relative + #: redirects. + #: + #: .. versionadded:: 0.8 + autocorrect_location_header = False + + #: Should this response object automatically set the content-length + #: header if possible? This is true by default. + #: + #: .. versionadded:: 0.8 + automatically_set_content_length = True + + #: The response body to send as the WSGI iterable. A list of strings + #: or bytes represents a fixed-length response, any other iterable + #: is a streaming response. Strings are encoded to bytes as UTF-8. + #: + #: Do not set to a plain string or bytes, that will cause sending + #: the response to be very inefficient as it will iterate one byte + #: at a time. + response: t.Union[t.Iterable[str], t.Iterable[bytes]] + + def __init__( + self, + response: t.Optional[ + t.Union[t.Iterable[bytes], bytes, t.Iterable[str], str] + ] = None, + status: t.Optional[t.Union[int, str, HTTPStatus]] = None, + headers: t.Optional[ + t.Union[ + t.Mapping[str, t.Union[str, int, t.Iterable[t.Union[str, int]]]], + t.Iterable[t.Tuple[str, t.Union[str, int]]], + ] + ] = None, + mimetype: t.Optional[str] = None, + content_type: t.Optional[str] = None, + direct_passthrough: bool = False, + ) -> None: + super().__init__( + status=status, + headers=headers, + mimetype=mimetype, + content_type=content_type, + ) + + #: Pass the response body directly through as the WSGI iterable. + #: This can be used when the body is a binary file or other + #: iterator of bytes, to skip some unnecessary checks. Use + #: :func:`~werkzeug.utils.send_file` instead of setting this + #: manually. + self.direct_passthrough = direct_passthrough + self._on_close: t.List[t.Callable[[], t.Any]] = [] + + # we set the response after the headers so that if a class changes + # the charset attribute, the data is set in the correct charset. + if response is None: + self.response = [] + elif isinstance(response, (str, bytes, bytearray)): + self.set_data(response) + else: + self.response = response + + def call_on_close(self, func: t.Callable[[], t.Any]) -> t.Callable[[], t.Any]: + """Adds a function to the internal list of functions that should + be called as part of closing down the response. Since 0.7 this + function also returns the function that was passed so that this + can be used as a decorator. + + .. versionadded:: 0.6 + """ + self._on_close.append(func) + return func + + def __repr__(self) -> str: + if self.is_sequence: + body_info = f"{sum(map(len, self.iter_encoded()))} bytes" + else: + body_info = "streamed" if self.is_streamed else "likely-streamed" + return f"<{type(self).__name__} {body_info} [{self.status}]>" + + @classmethod + def force_type( + cls, response: "Response", environ: t.Optional["WSGIEnvironment"] = None + ) -> "Response": + """Enforce that the WSGI response is a response object of the current + type. Werkzeug will use the :class:`Response` internally in many + situations like the exceptions. If you call :meth:`get_response` on an + exception you will get back a regular :class:`Response` object, even + if you are using a custom subclass. + + This method can enforce a given response type, and it will also + convert arbitrary WSGI callables into response objects if an environ + is provided:: + + # convert a Werkzeug response object into an instance of the + # MyResponseClass subclass. + response = MyResponseClass.force_type(response) + + # convert any WSGI application into a response object + response = MyResponseClass.force_type(response, environ) + + This is especially useful if you want to post-process responses in + the main dispatcher and use functionality provided by your subclass. + + Keep in mind that this will modify response objects in place if + possible! + + :param response: a response object or wsgi application. + :param environ: a WSGI environment object. + :return: a response object. + """ + if not isinstance(response, Response): + if environ is None: + raise TypeError( + "cannot convert WSGI application into response" + " objects without an environ" + ) + + from ..test import run_wsgi_app + + response = Response(*run_wsgi_app(response, environ)) + + response.__class__ = cls + return response + + @classmethod + def from_app( + cls, app: "WSGIApplication", environ: "WSGIEnvironment", buffered: bool = False + ) -> "Response": + """Create a new response object from an application output. This + works best if you pass it an application that returns a generator all + the time. Sometimes applications may use the `write()` callable + returned by the `start_response` function. This tries to resolve such + edge cases automatically. But if you don't get the expected output + you should set `buffered` to `True` which enforces buffering. + + :param app: the WSGI application to execute. + :param environ: the WSGI environment to execute against. + :param buffered: set to `True` to enforce buffering. + :return: a response object. + """ + from ..test import run_wsgi_app + + return cls(*run_wsgi_app(app, environ, buffered)) + + @typing.overload + def get_data(self, as_text: "te.Literal[False]" = False) -> bytes: + ... + + @typing.overload + def get_data(self, as_text: "te.Literal[True]") -> str: + ... + + def get_data(self, as_text: bool = False) -> t.Union[bytes, str]: + """The string representation of the response body. Whenever you call + this property the response iterable is encoded and flattened. This + can lead to unwanted behavior if you stream big data. + + This behavior can be disabled by setting + :attr:`implicit_sequence_conversion` to `False`. + + If `as_text` is set to `True` the return value will be a decoded + string. + + .. versionadded:: 0.9 + """ + self._ensure_sequence() + rv = b"".join(self.iter_encoded()) + + if as_text: + return rv.decode(self.charset) + + return rv + + def set_data(self, value: t.Union[bytes, str]) -> None: + """Sets a new string as response. The value must be a string or + bytes. If a string is set it's encoded to the charset of the + response (utf-8 by default). + + .. versionadded:: 0.9 + """ + # if a string is set, it's encoded directly so that we + # can set the content length + if isinstance(value, str): + value = value.encode(self.charset) + else: + value = bytes(value) + self.response = [value] + if self.automatically_set_content_length: + self.headers["Content-Length"] = str(len(value)) + + data = property( + get_data, + set_data, + doc="A descriptor that calls :meth:`get_data` and :meth:`set_data`.", + ) + + def calculate_content_length(self) -> t.Optional[int]: + """Returns the content length if available or `None` otherwise.""" + try: + self._ensure_sequence() + except RuntimeError: + return None + return sum(len(x) for x in self.iter_encoded()) + + def _ensure_sequence(self, mutable: bool = False) -> None: + """This method can be called by methods that need a sequence. If + `mutable` is true, it will also ensure that the response sequence + is a standard Python list. + + .. versionadded:: 0.6 + """ + if self.is_sequence: + # if we need a mutable object, we ensure it's a list. + if mutable and not isinstance(self.response, list): + self.response = list(self.response) # type: ignore + return + if self.direct_passthrough: + raise RuntimeError( + "Attempted implicit sequence conversion but the" + " response object is in direct passthrough mode." + ) + if not self.implicit_sequence_conversion: + raise RuntimeError( + "The response object required the iterable to be a" + " sequence, but the implicit conversion was disabled." + " Call make_sequence() yourself." + ) + self.make_sequence() + + def make_sequence(self) -> None: + """Converts the response iterator in a list. By default this happens + automatically if required. If `implicit_sequence_conversion` is + disabled, this method is not automatically called and some properties + might raise exceptions. This also encodes all the items. + + .. versionadded:: 0.6 + """ + if not self.is_sequence: + # if we consume an iterable we have to ensure that the close + # method of the iterable is called if available when we tear + # down the response + close = getattr(self.response, "close", None) + self.response = list(self.iter_encoded()) + if close is not None: + self.call_on_close(close) + + def iter_encoded(self) -> t.Iterator[bytes]: + """Iter the response encoded with the encoding of the response. + If the response object is invoked as WSGI application the return + value of this method is used as application iterator unless + :attr:`direct_passthrough` was activated. + """ + if __debug__: + _warn_if_string(self.response) + # Encode in a separate function so that self.response is fetched + # early. This allows us to wrap the response with the return + # value from get_app_iter or iter_encoded. + return _iter_encoded(self.response, self.charset) + + @property + def is_streamed(self) -> bool: + """If the response is streamed (the response is not an iterable with + a length information) this property is `True`. In this case streamed + means that there is no information about the number of iterations. + This is usually `True` if a generator is passed to the response object. + + This is useful for checking before applying some sort of post + filtering that should not take place for streamed responses. + """ + try: + len(self.response) # type: ignore + except (TypeError, AttributeError): + return True + return False + + @property + def is_sequence(self) -> bool: + """If the iterator is buffered, this property will be `True`. A + response object will consider an iterator to be buffered if the + response attribute is a list or tuple. + + .. versionadded:: 0.6 + """ + return isinstance(self.response, (tuple, list)) + + def close(self) -> None: + """Close the wrapped response if possible. You can also use the object + in a with statement which will automatically close it. + + .. versionadded:: 0.9 + Can now be used in a with statement. + """ + if hasattr(self.response, "close"): + self.response.close() + for func in self._on_close: + func() + + def __enter__(self) -> "Response": + return self + + def __exit__(self, exc_type, exc_value, tb): # type: ignore + self.close() + + def freeze(self) -> None: + """Make the response object ready to be pickled. Does the + following: + + * Buffer the response into a list, ignoring + :attr:`implicity_sequence_conversion` and + :attr:`direct_passthrough`. + * Set the ``Content-Length`` header. + * Generate an ``ETag`` header if one is not already set. + + .. versionchanged:: 2.1 + Removed the ``no_etag`` parameter. + + .. versionchanged:: 2.0 + An ``ETag`` header is added, the ``no_etag`` parameter is + deprecated and will be removed in Werkzeug 2.1. + + .. versionchanged:: 0.6 + The ``Content-Length`` header is set. + """ + # Always freeze the encoded response body, ignore + # implicit_sequence_conversion and direct_passthrough. + self.response = list(self.iter_encoded()) + self.headers["Content-Length"] = str(sum(map(len, self.response))) + self.add_etag() + + def get_wsgi_headers(self, environ: "WSGIEnvironment") -> Headers: + """This is automatically called right before the response is started + and returns headers modified for the given environment. It returns a + copy of the headers from the response with some modifications applied + if necessary. + + For example the location header (if present) is joined with the root + URL of the environment. Also the content length is automatically set + to zero here for certain status codes. + + .. versionchanged:: 0.6 + Previously that function was called `fix_headers` and modified + the response object in place. Also since 0.6, IRIs in location + and content-location headers are handled properly. + + Also starting with 0.6, Werkzeug will attempt to set the content + length if it is able to figure it out on its own. This is the + case if all the strings in the response iterable are already + encoded and the iterable is buffered. + + :param environ: the WSGI environment of the request. + :return: returns a new :class:`~werkzeug.datastructures.Headers` + object. + """ + headers = Headers(self.headers) + location: t.Optional[str] = None + content_location: t.Optional[str] = None + content_length: t.Optional[t.Union[str, int]] = None + status = self.status_code + + # iterate over the headers to find all values in one go. Because + # get_wsgi_headers is used each response that gives us a tiny + # speedup. + for key, value in headers: + ikey = key.lower() + if ikey == "location": + location = value + elif ikey == "content-location": + content_location = value + elif ikey == "content-length": + content_length = value + + # make sure the location header is an absolute URL + if location is not None: + old_location = location + if isinstance(location, str): + # Safe conversion is necessary here as we might redirect + # to a broken URI scheme (for instance itms-services). + location = iri_to_uri(location, safe_conversion=True) + + if self.autocorrect_location_header: + current_url = get_current_url(environ, strip_querystring=True) + if isinstance(current_url, str): + current_url = iri_to_uri(current_url) + location = url_join(current_url, location) + if location != old_location: + headers["Location"] = location + + # make sure the content location is a URL + if content_location is not None and isinstance(content_location, str): + headers["Content-Location"] = iri_to_uri(content_location) + + if 100 <= status < 200 or status == 204: + # Per section 3.3.2 of RFC 7230, "a server MUST NOT send a + # Content-Length header field in any response with a status + # code of 1xx (Informational) or 204 (No Content)." + headers.remove("Content-Length") + elif status == 304: + remove_entity_headers(headers) + + # if we can determine the content length automatically, we + # should try to do that. But only if this does not involve + # flattening the iterator or encoding of strings in the + # response. We however should not do that if we have a 304 + # response. + if ( + self.automatically_set_content_length + and self.is_sequence + and content_length is None + and status not in (204, 304) + and not (100 <= status < 200) + ): + try: + content_length = sum(len(_to_bytes(x, "ascii")) for x in self.response) + except UnicodeError: + # Something other than bytes, can't safely figure out + # the length of the response. + pass + else: + headers["Content-Length"] = str(content_length) + + return headers + + def get_app_iter(self, environ: "WSGIEnvironment") -> t.Iterable[bytes]: + """Returns the application iterator for the given environ. Depending + on the request method and the current status code the return value + might be an empty response rather than the one from the response. + + If the request method is `HEAD` or the status code is in a range + where the HTTP specification requires an empty response, an empty + iterable is returned. + + .. versionadded:: 0.6 + + :param environ: the WSGI environment of the request. + :return: a response iterable. + """ + status = self.status_code + if ( + environ["REQUEST_METHOD"] == "HEAD" + or 100 <= status < 200 + or status in (204, 304) + ): + iterable: t.Iterable[bytes] = () + elif self.direct_passthrough: + if __debug__: + _warn_if_string(self.response) + return self.response # type: ignore + else: + iterable = self.iter_encoded() + return ClosingIterator(iterable, self.close) + + def get_wsgi_response( + self, environ: "WSGIEnvironment" + ) -> t.Tuple[t.Iterable[bytes], str, t.List[t.Tuple[str, str]]]: + """Returns the final WSGI response as tuple. The first item in + the tuple is the application iterator, the second the status and + the third the list of headers. The response returned is created + specially for the given environment. For example if the request + method in the WSGI environment is ``'HEAD'`` the response will + be empty and only the headers and status code will be present. + + .. versionadded:: 0.6 + + :param environ: the WSGI environment of the request. + :return: an ``(app_iter, status, headers)`` tuple. + """ + headers = self.get_wsgi_headers(environ) + app_iter = self.get_app_iter(environ) + return app_iter, self.status, headers.to_wsgi_list() + + def __call__( + self, environ: "WSGIEnvironment", start_response: "StartResponse" + ) -> t.Iterable[bytes]: + """Process this response as WSGI application. + + :param environ: the WSGI environment. + :param start_response: the response callable provided by the WSGI + server. + :return: an application iterator + """ + app_iter, status, headers = self.get_wsgi_response(environ) + start_response(status, headers) + return app_iter + + # JSON + + #: A module or other object that has ``dumps`` and ``loads`` + #: functions that match the API of the built-in :mod:`json` module. + json_module = json + + @property + def json(self) -> t.Optional[t.Any]: + """The parsed JSON data if :attr:`mimetype` indicates JSON + (:mimetype:`application/json`, see :attr:`is_json`). + + Calls :meth:`get_json` with default arguments. + """ + return self.get_json() + + @t.overload + def get_json(self, force: bool = ..., silent: "te.Literal[False]" = ...) -> t.Any: + ... + + @t.overload + def get_json(self, force: bool = ..., silent: bool = ...) -> t.Optional[t.Any]: + ... + + def get_json(self, force: bool = False, silent: bool = False) -> t.Optional[t.Any]: + """Parse :attr:`data` as JSON. Useful during testing. + + If the mimetype does not indicate JSON + (:mimetype:`application/json`, see :attr:`is_json`), this + returns ``None``. + + Unlike :meth:`Request.get_json`, the result is not cached. + + :param force: Ignore the mimetype and always try to parse JSON. + :param silent: Silence parsing errors and return ``None`` + instead. + """ + if not (force or self.is_json): + return None + + data = self.get_data() + + try: + return self.json_module.loads(data) + except ValueError: + if not silent: + raise + + return None + + # Stream + + @cached_property + def stream(self) -> "ResponseStream": + """The response iterable as write-only stream.""" + return ResponseStream(self) + + def _wrap_range_response(self, start: int, length: int) -> None: + """Wrap existing Response in case of Range Request context.""" + if self.status_code == 206: + self.response = _RangeWrapper(self.response, start, length) # type: ignore + + def _is_range_request_processable(self, environ: "WSGIEnvironment") -> bool: + """Return ``True`` if `Range` header is present and if underlying + resource is considered unchanged when compared with `If-Range` header. + """ + return ( + "HTTP_IF_RANGE" not in environ + or not is_resource_modified( + environ, + self.headers.get("etag"), + None, + self.headers.get("last-modified"), + ignore_if_range=False, + ) + ) and "HTTP_RANGE" in environ + + def _process_range_request( + self, + environ: "WSGIEnvironment", + complete_length: t.Optional[int] = None, + accept_ranges: t.Optional[t.Union[bool, str]] = None, + ) -> bool: + """Handle Range Request related headers (RFC7233). If `Accept-Ranges` + header is valid, and Range Request is processable, we set the headers + as described by the RFC, and wrap the underlying response in a + RangeWrapper. + + Returns ``True`` if Range Request can be fulfilled, ``False`` otherwise. + + :raises: :class:`~werkzeug.exceptions.RequestedRangeNotSatisfiable` + if `Range` header could not be parsed or satisfied. + + .. versionchanged:: 2.0 + Returns ``False`` if the length is 0. + """ + from ..exceptions import RequestedRangeNotSatisfiable + + if ( + accept_ranges is None + or complete_length is None + or complete_length == 0 + or not self._is_range_request_processable(environ) + ): + return False + + parsed_range = parse_range_header(environ.get("HTTP_RANGE")) + + if parsed_range is None: + raise RequestedRangeNotSatisfiable(complete_length) + + range_tuple = parsed_range.range_for_length(complete_length) + content_range_header = parsed_range.to_content_range_header(complete_length) + + if range_tuple is None or content_range_header is None: + raise RequestedRangeNotSatisfiable(complete_length) + + content_length = range_tuple[1] - range_tuple[0] + self.headers["Content-Length"] = content_length + self.headers["Accept-Ranges"] = accept_ranges + self.content_range = content_range_header # type: ignore + self.status_code = 206 + self._wrap_range_response(range_tuple[0], content_length) + return True + + def make_conditional( + self, + request_or_environ: t.Union["WSGIEnvironment", "Request"], + accept_ranges: t.Union[bool, str] = False, + complete_length: t.Optional[int] = None, + ) -> "Response": + """Make the response conditional to the request. This method works + best if an etag was defined for the response already. The `add_etag` + method can be used to do that. If called without etag just the date + header is set. + + This does nothing if the request method in the request or environ is + anything but GET or HEAD. + + For optimal performance when handling range requests, it's recommended + that your response data object implements `seekable`, `seek` and `tell` + methods as described by :py:class:`io.IOBase`. Objects returned by + :meth:`~werkzeug.wsgi.wrap_file` automatically implement those methods. + + It does not remove the body of the response because that's something + the :meth:`__call__` function does for us automatically. + + Returns self so that you can do ``return resp.make_conditional(req)`` + but modifies the object in-place. + + :param request_or_environ: a request object or WSGI environment to be + used to make the response conditional + against. + :param accept_ranges: This parameter dictates the value of + `Accept-Ranges` header. If ``False`` (default), + the header is not set. If ``True``, it will be set + to ``"bytes"``. If ``None``, it will be set to + ``"none"``. If it's a string, it will use this + value. + :param complete_length: Will be used only in valid Range Requests. + It will set `Content-Range` complete length + value and compute `Content-Length` real value. + This parameter is mandatory for successful + Range Requests completion. + :raises: :class:`~werkzeug.exceptions.RequestedRangeNotSatisfiable` + if `Range` header could not be parsed or satisfied. + + .. versionchanged:: 2.0 + Range processing is skipped if length is 0 instead of + raising a 416 Range Not Satisfiable error. + """ + environ = _get_environ(request_or_environ) + if environ["REQUEST_METHOD"] in ("GET", "HEAD"): + # if the date is not in the headers, add it now. We however + # will not override an already existing header. Unfortunately + # this header will be overridden by many WSGI servers including + # wsgiref. + if "date" not in self.headers: + self.headers["Date"] = http_date() + accept_ranges = _clean_accept_ranges(accept_ranges) + is206 = self._process_range_request(environ, complete_length, accept_ranges) + if not is206 and not is_resource_modified( + environ, + self.headers.get("etag"), + None, + self.headers.get("last-modified"), + ): + if parse_etags(environ.get("HTTP_IF_MATCH")): + self.status_code = 412 + else: + self.status_code = 304 + if ( + self.automatically_set_content_length + and "content-length" not in self.headers + ): + length = self.calculate_content_length() + if length is not None: + self.headers["Content-Length"] = length + return self + + def add_etag(self, overwrite: bool = False, weak: bool = False) -> None: + """Add an etag for the current response if there is none yet. + + .. versionchanged:: 2.0 + SHA-1 is used to generate the value. MD5 may not be + available in some environments. + """ + if overwrite or "etag" not in self.headers: + self.set_etag(generate_etag(self.get_data()), weak) + + +class ResponseStream: + """A file descriptor like object used by :meth:`Response.stream` to + represent the body of the stream. It directly pushes into the + response iterable of the response object. + """ + + mode = "wb+" + + def __init__(self, response: Response): + self.response = response + self.closed = False + + def write(self, value: bytes) -> int: + if self.closed: + raise ValueError("I/O operation on closed file") + self.response._ensure_sequence(mutable=True) + self.response.response.append(value) # type: ignore + self.response.headers.pop("Content-Length", None) + return len(value) + + def writelines(self, seq: t.Iterable[bytes]) -> None: + for item in seq: + self.write(item) + + def close(self) -> None: + self.closed = True + + def flush(self) -> None: + if self.closed: + raise ValueError("I/O operation on closed file") + + def isatty(self) -> bool: + if self.closed: + raise ValueError("I/O operation on closed file") + return False + + def tell(self) -> int: + self.response._ensure_sequence() + return sum(map(len, self.response.response)) + + @property + def encoding(self) -> str: + return self.response.charset diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/wsgi.py b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/wsgi.py new file mode 100644 index 0000000..d74430d --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/wsgi.py @@ -0,0 +1,1058 @@ +import io +import re +import typing as t +import warnings +from functools import partial +from functools import update_wrapper +from itertools import chain + +from ._internal import _make_encode_wrapper +from ._internal import _to_bytes +from ._internal import _to_str +from .sansio import utils as _sansio_utils +from .sansio.utils import host_is_trusted # noqa: F401 # Imported as part of API +from .urls import _URLTuple +from .urls import uri_to_iri +from .urls import url_join +from .urls import url_parse +from .urls import url_quote + +if t.TYPE_CHECKING: + from _typeshed.wsgi import WSGIApplication + from _typeshed.wsgi import WSGIEnvironment + + +def responder(f: t.Callable[..., "WSGIApplication"]) -> "WSGIApplication": + """Marks a function as responder. Decorate a function with it and it + will automatically call the return value as WSGI application. + + Example:: + + @responder + def application(environ, start_response): + return Response('Hello World!') + """ + return update_wrapper(lambda *a: f(*a)(*a[-2:]), f) + + +def get_current_url( + environ: "WSGIEnvironment", + root_only: bool = False, + strip_querystring: bool = False, + host_only: bool = False, + trusted_hosts: t.Optional[t.Iterable[str]] = None, +) -> str: + """Recreate the URL for a request from the parts in a WSGI + environment. + + The URL is an IRI, not a URI, so it may contain Unicode characters. + Use :func:`~werkzeug.urls.iri_to_uri` to convert it to ASCII. + + :param environ: The WSGI environment to get the URL parts from. + :param root_only: Only build the root path, don't include the + remaining path or query string. + :param strip_querystring: Don't include the query string. + :param host_only: Only build the scheme and host. + :param trusted_hosts: A list of trusted host names to validate the + host against. + """ + parts = { + "scheme": environ["wsgi.url_scheme"], + "host": get_host(environ, trusted_hosts), + } + + if not host_only: + parts["root_path"] = environ.get("SCRIPT_NAME", "") + + if not root_only: + parts["path"] = environ.get("PATH_INFO", "") + + if not strip_querystring: + parts["query_string"] = environ.get("QUERY_STRING", "").encode("latin1") + + return _sansio_utils.get_current_url(**parts) + + +def _get_server( + environ: "WSGIEnvironment", +) -> t.Optional[t.Tuple[str, t.Optional[int]]]: + name = environ.get("SERVER_NAME") + + if name is None: + return None + + try: + port: t.Optional[int] = int(environ.get("SERVER_PORT", None)) + except (TypeError, ValueError): + # unix socket + port = None + + return name, port + + +def get_host( + environ: "WSGIEnvironment", trusted_hosts: t.Optional[t.Iterable[str]] = None +) -> str: + """Return the host for the given WSGI environment. + + The ``Host`` header is preferred, then ``SERVER_NAME`` if it's not + set. The returned host will only contain the port if it is different + than the standard port for the protocol. + + Optionally, verify that the host is trusted using + :func:`host_is_trusted` and raise a + :exc:`~werkzeug.exceptions.SecurityError` if it is not. + + :param environ: A WSGI environment dict. + :param trusted_hosts: A list of trusted host names. + + :return: Host, with port if necessary. + :raise ~werkzeug.exceptions.SecurityError: If the host is not + trusted. + """ + return _sansio_utils.get_host( + environ["wsgi.url_scheme"], + environ.get("HTTP_HOST"), + _get_server(environ), + trusted_hosts, + ) + + +def get_content_length(environ: "WSGIEnvironment") -> t.Optional[int]: + """Returns the content length from the WSGI environment as + integer. If it's not available or chunked transfer encoding is used, + ``None`` is returned. + + .. versionadded:: 0.9 + + :param environ: the WSGI environ to fetch the content length from. + """ + return _sansio_utils.get_content_length( + http_content_length=environ.get("CONTENT_LENGTH"), + http_transfer_encoding=environ.get("HTTP_TRANSFER_ENCODING", ""), + ) + + +def get_input_stream( + environ: "WSGIEnvironment", safe_fallback: bool = True +) -> t.IO[bytes]: + """Returns the input stream from the WSGI environment and wraps it + in the most sensible way possible. The stream returned is not the + raw WSGI stream in most cases but one that is safe to read from + without taking into account the content length. + + If content length is not set, the stream will be empty for safety reasons. + If the WSGI server supports chunked or infinite streams, it should set + the ``wsgi.input_terminated`` value in the WSGI environ to indicate that. + + .. versionadded:: 0.9 + + :param environ: the WSGI environ to fetch the stream from. + :param safe_fallback: use an empty stream as a safe fallback when the + content length is not set. Disabling this allows infinite streams, + which can be a denial-of-service risk. + """ + stream = t.cast(t.IO[bytes], environ["wsgi.input"]) + content_length = get_content_length(environ) + + # A wsgi extension that tells us if the input is terminated. In + # that case we return the stream unchanged as we know we can safely + # read it until the end. + if environ.get("wsgi.input_terminated"): + return stream + + # If the request doesn't specify a content length, returning the stream is + # potentially dangerous because it could be infinite, malicious or not. If + # safe_fallback is true, return an empty stream instead for safety. + if content_length is None: + return io.BytesIO() if safe_fallback else stream + + # Otherwise limit the stream to the content length + return t.cast(t.IO[bytes], LimitedStream(stream, content_length)) + + +def get_query_string(environ: "WSGIEnvironment") -> str: + """Returns the ``QUERY_STRING`` from the WSGI environment. This also + takes care of the WSGI decoding dance. The string returned will be + restricted to ASCII characters. + + :param environ: WSGI environment to get the query string from. + + .. deprecated:: 2.2 + Will be removed in Werkzeug 2.3. + + .. versionadded:: 0.9 + """ + warnings.warn( + "'get_query_string' is deprecated and will be removed in Werkzeug 2.3.", + DeprecationWarning, + stacklevel=2, + ) + qs = environ.get("QUERY_STRING", "").encode("latin1") + # QUERY_STRING really should be ascii safe but some browsers + # will send us some unicode stuff (I am looking at you IE). + # In that case we want to urllib quote it badly. + return url_quote(qs, safe=":&%=+$!*'(),") + + +def get_path_info( + environ: "WSGIEnvironment", charset: str = "utf-8", errors: str = "replace" +) -> str: + """Return the ``PATH_INFO`` from the WSGI environment and decode it + unless ``charset`` is ``None``. + + :param environ: WSGI environment to get the path from. + :param charset: The charset for the path info, or ``None`` if no + decoding should be performed. + :param errors: The decoding error handling. + + .. versionadded:: 0.9 + """ + path = environ.get("PATH_INFO", "").encode("latin1") + return _to_str(path, charset, errors, allow_none_charset=True) # type: ignore + + +def get_script_name( + environ: "WSGIEnvironment", charset: str = "utf-8", errors: str = "replace" +) -> str: + """Return the ``SCRIPT_NAME`` from the WSGI environment and decode + it unless `charset` is set to ``None``. + + :param environ: WSGI environment to get the path from. + :param charset: The charset for the path, or ``None`` if no decoding + should be performed. + :param errors: The decoding error handling. + + .. deprecated:: 2.2 + Will be removed in Werkzeug 2.3. + + .. versionadded:: 0.9 + """ + warnings.warn( + "'get_script_name' is deprecated and will be removed in Werkzeug 2.3.", + DeprecationWarning, + stacklevel=2, + ) + path = environ.get("SCRIPT_NAME", "").encode("latin1") + return _to_str(path, charset, errors, allow_none_charset=True) # type: ignore + + +def pop_path_info( + environ: "WSGIEnvironment", charset: str = "utf-8", errors: str = "replace" +) -> t.Optional[str]: + """Removes and returns the next segment of `PATH_INFO`, pushing it onto + `SCRIPT_NAME`. Returns `None` if there is nothing left on `PATH_INFO`. + + If the `charset` is set to `None` bytes are returned. + + If there are empty segments (``'/foo//bar``) these are ignored but + properly pushed to the `SCRIPT_NAME`: + + >>> env = {'SCRIPT_NAME': '/foo', 'PATH_INFO': '/a/b'} + >>> pop_path_info(env) + 'a' + >>> env['SCRIPT_NAME'] + '/foo/a' + >>> pop_path_info(env) + 'b' + >>> env['SCRIPT_NAME'] + '/foo/a/b' + + .. deprecated:: 2.2 + Will be removed in Werkzeug 2.3. + + .. versionadded:: 0.5 + + .. versionchanged:: 0.9 + The path is now decoded and a charset and encoding + parameter can be provided. + + :param environ: the WSGI environment that is modified. + :param charset: The ``encoding`` parameter passed to + :func:`bytes.decode`. + :param errors: The ``errors`` paramater passed to + :func:`bytes.decode`. + """ + warnings.warn( + "'pop_path_info' is deprecated and will be removed in Werkzeug 2.3.", + DeprecationWarning, + stacklevel=2, + ) + + path = environ.get("PATH_INFO") + if not path: + return None + + script_name = environ.get("SCRIPT_NAME", "") + + # shift multiple leading slashes over + old_path = path + path = path.lstrip("/") + if path != old_path: + script_name += "/" * (len(old_path) - len(path)) + + if "/" not in path: + environ["PATH_INFO"] = "" + environ["SCRIPT_NAME"] = script_name + path + rv = path.encode("latin1") + else: + segment, path = path.split("/", 1) + environ["PATH_INFO"] = f"/{path}" + environ["SCRIPT_NAME"] = script_name + segment + rv = segment.encode("latin1") + + return _to_str(rv, charset, errors, allow_none_charset=True) # type: ignore + + +def peek_path_info( + environ: "WSGIEnvironment", charset: str = "utf-8", errors: str = "replace" +) -> t.Optional[str]: + """Returns the next segment on the `PATH_INFO` or `None` if there + is none. Works like :func:`pop_path_info` without modifying the + environment: + + >>> env = {'SCRIPT_NAME': '/foo', 'PATH_INFO': '/a/b'} + >>> peek_path_info(env) + 'a' + >>> peek_path_info(env) + 'a' + + If the `charset` is set to `None` bytes are returned. + + .. deprecated:: 2.2 + Will be removed in Werkzeug 2.3. + + .. versionadded:: 0.5 + + .. versionchanged:: 0.9 + The path is now decoded and a charset and encoding + parameter can be provided. + + :param environ: the WSGI environment that is checked. + """ + warnings.warn( + "'peek_path_info' is deprecated and will be removed in Werkzeug 2.3.", + DeprecationWarning, + stacklevel=2, + ) + + segments = environ.get("PATH_INFO", "").lstrip("/").split("/", 1) + if segments: + return _to_str( # type: ignore + segments[0].encode("latin1"), charset, errors, allow_none_charset=True + ) + return None + + +def extract_path_info( + environ_or_baseurl: t.Union[str, "WSGIEnvironment"], + path_or_url: t.Union[str, _URLTuple], + charset: str = "utf-8", + errors: str = "werkzeug.url_quote", + collapse_http_schemes: bool = True, +) -> t.Optional[str]: + """Extracts the path info from the given URL (or WSGI environment) and + path. The path info returned is a string. The URLs might also be IRIs. + + If the path info could not be determined, `None` is returned. + + Some examples: + + >>> extract_path_info('http://example.com/app', '/app/hello') + '/hello' + >>> extract_path_info('http://example.com/app', + ... 'https://example.com/app/hello') + '/hello' + >>> extract_path_info('http://example.com/app', + ... 'https://example.com/app/hello', + ... collapse_http_schemes=False) is None + True + + Instead of providing a base URL you can also pass a WSGI environment. + + :param environ_or_baseurl: a WSGI environment dict, a base URL or + base IRI. This is the root of the + application. + :param path_or_url: an absolute path from the server root, a + relative path (in which case it's the path info) + or a full URL. + :param charset: the charset for byte data in URLs + :param errors: the error handling on decode + :param collapse_http_schemes: if set to `False` the algorithm does + not assume that http and https on the + same server point to the same + resource. + + .. deprecated:: 2.2 + Will be removed in Werkzeug 2.3. + + .. versionchanged:: 0.15 + The ``errors`` parameter defaults to leaving invalid bytes + quoted instead of replacing them. + + .. versionadded:: 0.6 + + """ + warnings.warn( + "'extract_path_info' is deprecated and will be removed in Werkzeug 2.3.", + DeprecationWarning, + stacklevel=2, + ) + + def _normalize_netloc(scheme: str, netloc: str) -> str: + parts = netloc.split("@", 1)[-1].split(":", 1) + port: t.Optional[str] + + if len(parts) == 2: + netloc, port = parts + if (scheme == "http" and port == "80") or ( + scheme == "https" and port == "443" + ): + port = None + else: + netloc = parts[0] + port = None + + if port is not None: + netloc += f":{port}" + + return netloc + + # make sure whatever we are working on is a IRI and parse it + path = uri_to_iri(path_or_url, charset, errors) + if isinstance(environ_or_baseurl, dict): + environ_or_baseurl = get_current_url(environ_or_baseurl, root_only=True) + base_iri = uri_to_iri(environ_or_baseurl, charset, errors) + base_scheme, base_netloc, base_path = url_parse(base_iri)[:3] + cur_scheme, cur_netloc, cur_path = url_parse(url_join(base_iri, path))[:3] + + # normalize the network location + base_netloc = _normalize_netloc(base_scheme, base_netloc) + cur_netloc = _normalize_netloc(cur_scheme, cur_netloc) + + # is that IRI even on a known HTTP scheme? + if collapse_http_schemes: + for scheme in base_scheme, cur_scheme: + if scheme not in ("http", "https"): + return None + else: + if not (base_scheme in ("http", "https") and base_scheme == cur_scheme): + return None + + # are the netlocs compatible? + if base_netloc != cur_netloc: + return None + + # are we below the application path? + base_path = base_path.rstrip("/") + if not cur_path.startswith(base_path): + return None + + return f"/{cur_path[len(base_path) :].lstrip('/')}" + + +class ClosingIterator: + """The WSGI specification requires that all middlewares and gateways + respect the `close` callback of the iterable returned by the application. + Because it is useful to add another close action to a returned iterable + and adding a custom iterable is a boring task this class can be used for + that:: + + return ClosingIterator(app(environ, start_response), [cleanup_session, + cleanup_locals]) + + If there is just one close function it can be passed instead of the list. + + A closing iterator is not needed if the application uses response objects + and finishes the processing if the response is started:: + + try: + return response(environ, start_response) + finally: + cleanup_session() + cleanup_locals() + """ + + def __init__( + self, + iterable: t.Iterable[bytes], + callbacks: t.Optional[ + t.Union[t.Callable[[], None], t.Iterable[t.Callable[[], None]]] + ] = None, + ) -> None: + iterator = iter(iterable) + self._next = t.cast(t.Callable[[], bytes], partial(next, iterator)) + if callbacks is None: + callbacks = [] + elif callable(callbacks): + callbacks = [callbacks] + else: + callbacks = list(callbacks) + iterable_close = getattr(iterable, "close", None) + if iterable_close: + callbacks.insert(0, iterable_close) + self._callbacks = callbacks + + def __iter__(self) -> "ClosingIterator": + return self + + def __next__(self) -> bytes: + return self._next() + + def close(self) -> None: + for callback in self._callbacks: + callback() + + +def wrap_file( + environ: "WSGIEnvironment", file: t.IO[bytes], buffer_size: int = 8192 +) -> t.Iterable[bytes]: + """Wraps a file. This uses the WSGI server's file wrapper if available + or otherwise the generic :class:`FileWrapper`. + + .. versionadded:: 0.5 + + If the file wrapper from the WSGI server is used it's important to not + iterate over it from inside the application but to pass it through + unchanged. If you want to pass out a file wrapper inside a response + object you have to set :attr:`Response.direct_passthrough` to `True`. + + More information about file wrappers are available in :pep:`333`. + + :param file: a :class:`file`-like object with a :meth:`~file.read` method. + :param buffer_size: number of bytes for one iteration. + """ + return environ.get("wsgi.file_wrapper", FileWrapper)( # type: ignore + file, buffer_size + ) + + +class FileWrapper: + """This class can be used to convert a :class:`file`-like object into + an iterable. It yields `buffer_size` blocks until the file is fully + read. + + You should not use this class directly but rather use the + :func:`wrap_file` function that uses the WSGI server's file wrapper + support if it's available. + + .. versionadded:: 0.5 + + If you're using this object together with a :class:`Response` you have + to use the `direct_passthrough` mode. + + :param file: a :class:`file`-like object with a :meth:`~file.read` method. + :param buffer_size: number of bytes for one iteration. + """ + + def __init__(self, file: t.IO[bytes], buffer_size: int = 8192) -> None: + self.file = file + self.buffer_size = buffer_size + + def close(self) -> None: + if hasattr(self.file, "close"): + self.file.close() + + def seekable(self) -> bool: + if hasattr(self.file, "seekable"): + return self.file.seekable() + if hasattr(self.file, "seek"): + return True + return False + + def seek(self, *args: t.Any) -> None: + if hasattr(self.file, "seek"): + self.file.seek(*args) + + def tell(self) -> t.Optional[int]: + if hasattr(self.file, "tell"): + return self.file.tell() + return None + + def __iter__(self) -> "FileWrapper": + return self + + def __next__(self) -> bytes: + data = self.file.read(self.buffer_size) + if data: + return data + raise StopIteration() + + +class _RangeWrapper: + # private for now, but should we make it public in the future ? + + """This class can be used to convert an iterable object into + an iterable that will only yield a piece of the underlying content. + It yields blocks until the underlying stream range is fully read. + The yielded blocks will have a size that can't exceed the original + iterator defined block size, but that can be smaller. + + If you're using this object together with a :class:`Response` you have + to use the `direct_passthrough` mode. + + :param iterable: an iterable object with a :meth:`__next__` method. + :param start_byte: byte from which read will start. + :param byte_range: how many bytes to read. + """ + + def __init__( + self, + iterable: t.Union[t.Iterable[bytes], t.IO[bytes]], + start_byte: int = 0, + byte_range: t.Optional[int] = None, + ): + self.iterable = iter(iterable) + self.byte_range = byte_range + self.start_byte = start_byte + self.end_byte = None + + if byte_range is not None: + self.end_byte = start_byte + byte_range + + self.read_length = 0 + self.seekable = hasattr(iterable, "seekable") and iterable.seekable() + self.end_reached = False + + def __iter__(self) -> "_RangeWrapper": + return self + + def _next_chunk(self) -> bytes: + try: + chunk = next(self.iterable) + self.read_length += len(chunk) + return chunk + except StopIteration: + self.end_reached = True + raise + + def _first_iteration(self) -> t.Tuple[t.Optional[bytes], int]: + chunk = None + if self.seekable: + self.iterable.seek(self.start_byte) # type: ignore + self.read_length = self.iterable.tell() # type: ignore + contextual_read_length = self.read_length + else: + while self.read_length <= self.start_byte: + chunk = self._next_chunk() + if chunk is not None: + chunk = chunk[self.start_byte - self.read_length :] + contextual_read_length = self.start_byte + return chunk, contextual_read_length + + def _next(self) -> bytes: + if self.end_reached: + raise StopIteration() + chunk = None + contextual_read_length = self.read_length + if self.read_length == 0: + chunk, contextual_read_length = self._first_iteration() + if chunk is None: + chunk = self._next_chunk() + if self.end_byte is not None and self.read_length >= self.end_byte: + self.end_reached = True + return chunk[: self.end_byte - contextual_read_length] + return chunk + + def __next__(self) -> bytes: + chunk = self._next() + if chunk: + return chunk + self.end_reached = True + raise StopIteration() + + def close(self) -> None: + if hasattr(self.iterable, "close"): + self.iterable.close() + + +def _make_chunk_iter( + stream: t.Union[t.Iterable[bytes], t.IO[bytes]], + limit: t.Optional[int], + buffer_size: int, +) -> t.Iterator[bytes]: + """Helper for the line and chunk iter functions.""" + if isinstance(stream, (bytes, bytearray, str)): + raise TypeError( + "Passed a string or byte object instead of true iterator or stream." + ) + if not hasattr(stream, "read"): + for item in stream: + if item: + yield item + return + stream = t.cast(t.IO[bytes], stream) + if not isinstance(stream, LimitedStream) and limit is not None: + stream = t.cast(t.IO[bytes], LimitedStream(stream, limit)) + _read = stream.read + while True: + item = _read(buffer_size) + if not item: + break + yield item + + +def make_line_iter( + stream: t.Union[t.Iterable[bytes], t.IO[bytes]], + limit: t.Optional[int] = None, + buffer_size: int = 10 * 1024, + cap_at_buffer: bool = False, +) -> t.Iterator[bytes]: + """Safely iterates line-based over an input stream. If the input stream + is not a :class:`LimitedStream` the `limit` parameter is mandatory. + + This uses the stream's :meth:`~file.read` method internally as opposite + to the :meth:`~file.readline` method that is unsafe and can only be used + in violation of the WSGI specification. The same problem applies to the + `__iter__` function of the input stream which calls :meth:`~file.readline` + without arguments. + + If you need line-by-line processing it's strongly recommended to iterate + over the input stream using this helper function. + + .. versionchanged:: 0.8 + This function now ensures that the limit was reached. + + .. versionadded:: 0.9 + added support for iterators as input stream. + + .. versionadded:: 0.11.10 + added support for the `cap_at_buffer` parameter. + + :param stream: the stream or iterate to iterate over. + :param limit: the limit in bytes for the stream. (Usually + content length. Not necessary if the `stream` + is a :class:`LimitedStream`. + :param buffer_size: The optional buffer size. + :param cap_at_buffer: if this is set chunks are split if they are longer + than the buffer size. Internally this is implemented + that the buffer size might be exhausted by a factor + of two however. + """ + _iter = _make_chunk_iter(stream, limit, buffer_size) + + first_item = next(_iter, "") + if not first_item: + return + + s = _make_encode_wrapper(first_item) + empty = t.cast(bytes, s("")) + cr = t.cast(bytes, s("\r")) + lf = t.cast(bytes, s("\n")) + crlf = t.cast(bytes, s("\r\n")) + + _iter = t.cast(t.Iterator[bytes], chain((first_item,), _iter)) + + def _iter_basic_lines() -> t.Iterator[bytes]: + _join = empty.join + buffer: t.List[bytes] = [] + while True: + new_data = next(_iter, "") + if not new_data: + break + new_buf: t.List[bytes] = [] + buf_size = 0 + for item in t.cast( + t.Iterator[bytes], chain(buffer, new_data.splitlines(True)) + ): + new_buf.append(item) + buf_size += len(item) + if item and item[-1:] in crlf: + yield _join(new_buf) + new_buf = [] + elif cap_at_buffer and buf_size >= buffer_size: + rv = _join(new_buf) + while len(rv) >= buffer_size: + yield rv[:buffer_size] + rv = rv[buffer_size:] + new_buf = [rv] + buffer = new_buf + if buffer: + yield _join(buffer) + + # This hackery is necessary to merge 'foo\r' and '\n' into one item + # of 'foo\r\n' if we were unlucky and we hit a chunk boundary. + previous = empty + for item in _iter_basic_lines(): + if item == lf and previous[-1:] == cr: + previous += item + item = empty + if previous: + yield previous + previous = item + if previous: + yield previous + + +def make_chunk_iter( + stream: t.Union[t.Iterable[bytes], t.IO[bytes]], + separator: bytes, + limit: t.Optional[int] = None, + buffer_size: int = 10 * 1024, + cap_at_buffer: bool = False, +) -> t.Iterator[bytes]: + """Works like :func:`make_line_iter` but accepts a separator + which divides chunks. If you want newline based processing + you should use :func:`make_line_iter` instead as it + supports arbitrary newline markers. + + .. versionadded:: 0.8 + + .. versionadded:: 0.9 + added support for iterators as input stream. + + .. versionadded:: 0.11.10 + added support for the `cap_at_buffer` parameter. + + :param stream: the stream or iterate to iterate over. + :param separator: the separator that divides chunks. + :param limit: the limit in bytes for the stream. (Usually + content length. Not necessary if the `stream` + is otherwise already limited). + :param buffer_size: The optional buffer size. + :param cap_at_buffer: if this is set chunks are split if they are longer + than the buffer size. Internally this is implemented + that the buffer size might be exhausted by a factor + of two however. + """ + _iter = _make_chunk_iter(stream, limit, buffer_size) + + first_item = next(_iter, b"") + if not first_item: + return + + _iter = t.cast(t.Iterator[bytes], chain((first_item,), _iter)) + if isinstance(first_item, str): + separator = _to_str(separator) + _split = re.compile(f"({re.escape(separator)})").split + _join = "".join + else: + separator = _to_bytes(separator) + _split = re.compile(b"(" + re.escape(separator) + b")").split + _join = b"".join + + buffer: t.List[bytes] = [] + while True: + new_data = next(_iter, b"") + if not new_data: + break + chunks = _split(new_data) + new_buf: t.List[bytes] = [] + buf_size = 0 + for item in chain(buffer, chunks): + if item == separator: + yield _join(new_buf) + new_buf = [] + buf_size = 0 + else: + buf_size += len(item) + new_buf.append(item) + + if cap_at_buffer and buf_size >= buffer_size: + rv = _join(new_buf) + while len(rv) >= buffer_size: + yield rv[:buffer_size] + rv = rv[buffer_size:] + new_buf = [rv] + buf_size = len(rv) + + buffer = new_buf + if buffer: + yield _join(buffer) + + +class LimitedStream(io.IOBase): + """Wraps a stream so that it doesn't read more than n bytes. If the + stream is exhausted and the caller tries to get more bytes from it + :func:`on_exhausted` is called which by default returns an empty + string. The return value of that function is forwarded + to the reader function. So if it returns an empty string + :meth:`read` will return an empty string as well. + + The limit however must never be higher than what the stream can + output. Otherwise :meth:`readlines` will try to read past the + limit. + + .. admonition:: Note on WSGI compliance + + calls to :meth:`readline` and :meth:`readlines` are not + WSGI compliant because it passes a size argument to the + readline methods. Unfortunately the WSGI PEP is not safely + implementable without a size argument to :meth:`readline` + because there is no EOF marker in the stream. As a result + of that the use of :meth:`readline` is discouraged. + + For the same reason iterating over the :class:`LimitedStream` + is not portable. It internally calls :meth:`readline`. + + We strongly suggest using :meth:`read` only or using the + :func:`make_line_iter` which safely iterates line-based + over a WSGI input stream. + + :param stream: the stream to wrap. + :param limit: the limit for the stream, must not be longer than + what the string can provide if the stream does not + end with `EOF` (like `wsgi.input`) + """ + + def __init__(self, stream: t.IO[bytes], limit: int) -> None: + self._read = stream.read + self._readline = stream.readline + self._pos = 0 + self.limit = limit + + def __iter__(self) -> "LimitedStream": + return self + + @property + def is_exhausted(self) -> bool: + """If the stream is exhausted this attribute is `True`.""" + return self._pos >= self.limit + + def on_exhausted(self) -> bytes: + """This is called when the stream tries to read past the limit. + The return value of this function is returned from the reading + function. + """ + # Read null bytes from the stream so that we get the + # correct end of stream marker. + return self._read(0) + + def on_disconnect(self) -> bytes: + """What should happen if a disconnect is detected? The return + value of this function is returned from read functions in case + the client went away. By default a + :exc:`~werkzeug.exceptions.ClientDisconnected` exception is raised. + """ + from .exceptions import ClientDisconnected + + raise ClientDisconnected() + + def _exhaust_chunks(self, chunk_size: int = 1024 * 64) -> t.Iterator[bytes]: + """Exhaust the stream by reading until the limit is reached or the client + disconnects, yielding each chunk. + + :param chunk_size: How many bytes to read at a time. + + :meta private: + + .. versionadded:: 2.2.3 + """ + to_read = self.limit - self._pos + + while to_read > 0: + chunk = self.read(min(to_read, chunk_size)) + yield chunk + to_read -= len(chunk) + + def exhaust(self, chunk_size: int = 1024 * 64) -> None: + """Exhaust the stream by reading until the limit is reached or the client + disconnects, discarding the data. + + :param chunk_size: How many bytes to read at a time. + + .. versionchanged:: 2.2.3 + Handle case where wrapped stream returns fewer bytes than requested. + """ + for _ in self._exhaust_chunks(chunk_size): + pass + + def read(self, size: t.Optional[int] = None) -> bytes: + """Read up to ``size`` bytes from the underlying stream. If size is not + provided, read until the limit. + + If the limit is reached, :meth:`on_exhausted` is called, which returns empty + bytes. + + If no bytes are read and the limit is not reached, or if an error occurs during + the read, :meth:`on_disconnect` is called, which raises + :exc:`.ClientDisconnected`. + + :param size: The number of bytes to read. ``None``, default, reads until the + limit is reached. + + .. versionchanged:: 2.2.3 + Handle case where wrapped stream returns fewer bytes than requested. + """ + if self._pos >= self.limit: + return self.on_exhausted() + + if size is None or size == -1: # -1 is for consistency with file + # Keep reading from the wrapped stream until the limit is reached. Can't + # rely on stream.read(size) because it's not guaranteed to return size. + buf = bytearray() + + for chunk in self._exhaust_chunks(): + buf.extend(chunk) + + return bytes(buf) + + to_read = min(self.limit - self._pos, size) + + try: + read = self._read(to_read) + except (OSError, ValueError): + return self.on_disconnect() + + if to_read and not len(read): + # If no data was read, treat it as a disconnect. As long as some data was + # read, a subsequent call can still return more before reaching the limit. + return self.on_disconnect() + + self._pos += len(read) + return read + + def readline(self, size: t.Optional[int] = None) -> bytes: + """Reads one line from the stream.""" + if self._pos >= self.limit: + return self.on_exhausted() + if size is None: + size = self.limit - self._pos + else: + size = min(size, self.limit - self._pos) + try: + line = self._readline(size) + except (ValueError, OSError): + return self.on_disconnect() + if size and not line: + return self.on_disconnect() + self._pos += len(line) + return line + + def readlines(self, size: t.Optional[int] = None) -> t.List[bytes]: + """Reads a file into a list of strings. It calls :meth:`readline` + until the file is read to the end. It does support the optional + `size` argument if the underlying stream supports it for + `readline`. + """ + last_pos = self._pos + result = [] + if size is not None: + end = min(self.limit, last_pos + size) + else: + end = self.limit + while True: + if size is not None: + size -= last_pos - self._pos + if self._pos >= end: + break + result.append(self.readline(size)) + if size is not None: + last_pos = self._pos + return result + + def tell(self) -> int: + """Returns the position of the stream. + + .. versionadded:: 0.9 + """ + return self._pos + + def __next__(self) -> bytes: + line = self.readline() + if not line: + raise StopIteration() + return line + + def readable(self) -> bool: + return True diff --git a/docs/examples/flask/venv/lib64 b/docs/examples/flask/venv/lib64 new file mode 120000 index 0000000..7951405 --- /dev/null +++ b/docs/examples/flask/venv/lib64 @@ -0,0 +1 @@ +lib \ No newline at end of file diff --git a/docs/examples/flask/venv/pyvenv.cfg b/docs/examples/flask/venv/pyvenv.cfg new file mode 100644 index 0000000..a96370f --- /dev/null +++ b/docs/examples/flask/venv/pyvenv.cfg @@ -0,0 +1,5 @@ +home = /usr/bin +include-system-site-packages = false +version = 3.11.2 +executable = /usr/bin/python3.11 +command = /usr/bin/python3 -m venv /home/refik/Capstone/QuantumPermutationPad/docs/examples/flask/venv diff --git a/docs/flask/Client.py b/docs/flask/Client.py deleted file mode 100644 index 27e78d3..0000000 --- a/docs/flask/Client.py +++ /dev/null @@ -1,85 +0,0 @@ -import socket -import queue -import json -import threading -from threading import Lock -import time -""" -Steps: -Get the user interaction -Make sure connection is established between CLI and Core -Send some message -Separate send message and listening into 2 threads -""" - - -class Client: - - def __init__(self, port, host, name): - self.s = socket.socket() - self.port = port - self.host = host - self.name = name - self.recv_queue = queue.Queue() - self.send_queue = queue.Queue() - # self.mutex = Lock() - # self.outgoing_mutex = Lock() - - def connection_setup(self): - self.s.connect((self.host, self.port)) - - # SEND the handshake - # msg = self.string_to_json("REQUEST_HANDSHAKE", "0") - # self.send_queue.put(msg) - # self.send_queue.put('\n') - # # self.connection_send(msg) - # self.connection_send('\n') - - def connection_send(self): - message = self.send_queue.get() - print(message) - self.s.send(message.encode('ascii')) - print("sent") - - - def to_outgoing_queue(self, message): - # self.outgoing_mutex.acquire() - self.send_queue.put(message) - print("OUTGOING QUEUE {}".format(message)) - # self.outgoing_mutex.release() - - - - def connection_recv(self): - msg = self.s.recv(4096) - self.recv_queue.put(msg) - print("Received from server: " + msg.decode('ascii')) - - def print_outgoing_queue(self): - # self.mutex.acquire() - if not self.recv_queue.empty(): - data = self.recv_queue.get() - # self.mutex.release() - return data - - else: - # self.mutex.release() - return None - - - @staticmethod - def string_to_json(api_call, task_id, interface_type="GC", sender_id="0", - payload_total_fragments="0", payload_fragment_number="0", - payload_size="0", payload_content="Hello World"): - msg = {"api_call":api_call, - "task_id":task_id, - "interface_type":interface_type, - "sender_id":sender_id, - "payload_total_fragments": payload_total_fragments, - "payload_fragment_number": payload_fragment_number, - "payload_size": payload_size, - "payload_content": payload_content} - - return json.dumps(msg) - - diff --git a/docs/flask/GI_interface.py b/docs/flask/GI_interface.py deleted file mode 100644 index b8de67d..0000000 --- a/docs/flask/GI_interface.py +++ /dev/null @@ -1,80 +0,0 @@ -from Client import * -import random -import logging -import sys -LOG_FILE = "out.log" - - -class Logger: - def init(file, level) -> None: - - # to format the log messages that are printed to terminal - logging.basicConfig( - format="%(levelname)s : %(name)s : %(funcName)s[line %(lineno)s]" - + ": %(message)s" - ) - - # create logger instance - log = logging.getLogger(__name__) - - # set log level - log.setLevel(level) - - # format the .log file messages - if file is not None: - file_handler = logging.FileHandler(file) - formatter = logging.Formatter( - "%(asctime)s : %(levelname)s : Module %(module)s" - + ": %(funcName)s[line %(lineno)s] : %(name)s : %(message)s" - ) - file_handler.setFormatter(formatter) - log.addHandler(file_handler) - - return log - - -class Commands: - def __init__(self, logger) -> None: - self.logger = logger - return - - @staticmethod - def random_string(len): - random_string = "" - for ii in range(len): - random_int = random.randint(33, 126) - random_string += chr(random_int) - return random_string - - @staticmethod - def test_vector_gen(args): - vec_len = args[0] - vec_num = args[1] - test_list = [""] * vec_num - for ii in range(vec_num): - test_list[ii] = Commands.random_string(vec_len) - return test_list - - -class GI: - @staticmethod - def Encrypt(task_id, str_list, client): -# logger = Logger.init(LOG_FILE, logging.DEBUG) - for vector in str_list: - msg = client.string_to_json("ENCRYPT", str(task_id), "GI", "0", "0", "0", str(sys.getsizeof(vector)), vector) - client.to_outgoing_queue(msg) - client.connection_send() - return - - @staticmethod - def ReceivedEncrypt(): - return - - @staticmethod - def Decrypt(): - return - - @staticmethod - def ReceivedDecrypt(): - return - diff --git a/docs/flask/README.md b/docs/flask/README.md deleted file mode 100644 index 55cd578..0000000 --- a/docs/flask/README.md +++ /dev/null @@ -1,13 +0,0 @@ -## Setup to run Flask - -1. In flask directory, create and run virtual enviroment by running these commands -``` -$ python3 -m venv venv -$ . venv/bin/activate -``` - -2. Install Flask using command `pip install Flask` other dependiences may be required -3. Set Flask application with command `export FLASK_APP=app` -4. Then set mode of operation to either `export FLASK_ENV=development` or `export FLASK_ENV=production` -5. Run website with command `flask run` and the website should be at address `http://127.0.0.1:5000/` loaded on the home page - diff --git a/docs/flask/app.py b/docs/flask/app.py deleted file mode 100755 index 5b1acfc..0000000 --- a/docs/flask/app.py +++ /dev/null @@ -1,108 +0,0 @@ -#! /usr/bin/env python3 - -import os -from flask import Flask, flash, render_template, request, redirect, session -from werkzeug.utils import secure_filename -from Client import * -from GI_interface import * - -app = Flask(__name__) -algos = ['AES', 'QPP', 'AES & QPP'] -tests = ['Encryption', 'Decryption', - 'Encrypt & Decrypt', 'M.I.T.M', - 'Brute Force', 'Monte Carlo', - 'Multi-block Message', 'Known Answer' - ] -UPLOAD_FOLDER = "downloads/" -ALLOWED_EXTENSIONS = {'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'} -GI_PORT = 64999 -vector_select = "True" -vector_len = 100 -vector_num = 100 - -client = Client(GI_PORT, "127.0.0.1", "CLI") -client.connection_setup() -task_id = 0 - - -def allowed_file(filename): - return '.' in filename and \ - filename.split('.', 1)[1].lower() in ALLOWED_EXTENSIONS - - -@app.route("/") -@app.route("/index/") -def index(): - return render_template('index.html') - - -@app.route("/api/") -def api(): - return render_template('api.html') - - -@app.route("/research/") -def research(): - return render_template('research.html') - - -@app.route("/demo/") -def demo(): - return render_template('demo.html') - - -@app.route("/calc/", methods=['GET', 'POST']) -def calc(): - global vector_select, algos, tests, vector_num, vector_len - if request.method == "POST": - print("ERROR 1") - vector_select = request.form['vector_select'] - - return render_template('calc.html', - algo_list=algos, - test_list=tests, - vector_select=vector_select) - - -@app.route("/view/", methods=['GET', 'POST']) -def view(): - global vector_select, algos, tests, vector_num, vector_len, task_id - test = algo = "ERROR" - test_vector = [] - - if request.method == 'POST': - algo_select = request.form.get('algo_select') - test_select = request.form.get('test_select') - print("ERROR 2") - print("{}, {}".format(algo_select, test_select)) - if vector_select == "True": - print("ERROR 3") - vector_len = request.form["vector_len"] - vector_num = request.form["vector_num"] - print("{} vectors of size {}".format(vector_num, vector_len)) - test_vector = Commands.test_vector_gen([int(vector_len), int(vector_num)]) - else: - print("ERROR 4") - if 'file' not in request.files: - flash("No File Uploaded") - file = request.files['file'] - if file.filename == '': - flash("No File Selected") - if file and allowed_file(file.filename): - filename = secure_filename(file.filename) - file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename)) - with open(os.path.abspath(UPLOAD_FOLDER + filename), 'r') as fd: - file_contents = fd.read() - print("{}, contents:\n{}".format(filename, file_contents)) - test_vector = [file_contents] - GI.Encrypt(task_id, test_vector, client) - task_id += 1 - - return render_template('view.html', test=test, algo=algo) - - -if __name__ == "__main__": - app.config['UPLOAD_FOLDER'] = os.path.abspath(UPLOAD_FOLDER) - app.secret_key = 'secret_key' - app.config['SESSION_TYPE'] = 'filesystem' - app.run(debug=True, host="127.0.0.1", port=4996) diff --git a/docs/flask/templates/calc.html b/docs/flask/templates/calc.html deleted file mode 100644 index 5995b08..0000000 --- a/docs/flask/templates/calc.html +++ /dev/null @@ -1,81 +0,0 @@ - -{% extends 'base.html' %} - -{% block title %} QPP Request {% endblock %} -{% block header %} Interactive Testbed {% endblock %} - -{% block header_content %} -

    This is the description for this page

    -{% endblock %} - - -{% block content %} - - - {% if vector_select == "True" %} - - {% else %} - - {% endif %} -
    - -{% if vector_select == "True" %} -
    - - -
  • {msg}:
    ') + + for frame in current.stack: + frame = t.cast(DebugFrameSummary, frame) + info = f' title="{escape(frame.info)}"' if frame.info else "" + row_parts.append(f"{frame.render_html(mark_library)}") + + rows.append("\n".join(row_parts)) + + is_syntax_error = issubclass(self._te.exc_type, SyntaxError) + + if include_title: + if is_syntax_error: + title = "Syntax Error" + else: + title = "Traceback (most recent call last):" + else: + title = "" + + exc_full = escape("".join(self._te.format_exception_only())) + + if is_syntax_error: + description = f"
    {exc_full}
    " + else: + description = f"
    {exc_full}
    " + + return SUMMARY_HTML % { + "classes": classes, + "title": f"

    {title}

    ", + "frames": "\n".join(rows), + "description": description, + } + + def render_debugger_html( + self, evalex: bool, secret: str, evalex_trusted: bool + ) -> str: + exc_lines = list(self._te.format_exception_only()) + plaintext = "".join(self._te.format()) + return PAGE_HTML % { + "evalex": "true" if evalex else "false", + "evalex_trusted": "true" if evalex_trusted else "false", + "console": "false", + "title": exc_lines[0], + "exception": escape("".join(exc_lines)), + "exception_type": escape(self._te.exc_type.__name__), + "summary": self.render_traceback_html(include_title=False), + "plaintext": escape(plaintext), + "plaintext_cs": re.sub("-{2,}", "-", plaintext), + "secret": secret, + } + + +class DebugFrameSummary(traceback.FrameSummary): + """A :class:`traceback.FrameSummary` that can evaluate code in the + frame's namespace. + """ + + __slots__ = ( + "local_ns", + "global_ns", + "_cache_info", + "_cache_is_library", + "_cache_console", + ) + + def __init__( + self, + *, + locals: t.Dict[str, t.Any], + globals: t.Dict[str, t.Any], + **kwargs: t.Any, + ) -> None: + super().__init__(locals=None, **kwargs) + self.local_ns = locals + self.global_ns = globals + + @cached_property + def info(self) -> t.Optional[str]: + return self.local_ns.get("__traceback_info__") + + @cached_property + def is_library(self) -> bool: + return any( + self.filename.startswith((path, os.path.realpath(path))) + for path in sysconfig.get_paths().values() + ) + + @cached_property + def console(self) -> Console: + return Console(self.global_ns, self.local_ns) + + def eval(self, code: str) -> t.Any: + return self.console.eval(code) + + def render_html(self, mark_library: bool) -> str: + context = 5 + lines = linecache.getlines(self.filename) + line_idx = self.lineno - 1 # type: ignore[operator] + start_idx = max(0, line_idx - context) + stop_idx = min(len(lines), line_idx + context + 1) + rendered_lines = [] + + def render_line(line: str, cls: str) -> None: + line = line.expandtabs().rstrip() + stripped_line = line.strip() + prefix = len(line) - len(stripped_line) + colno = getattr(self, "colno", 0) + end_colno = getattr(self, "end_colno", 0) + + if cls == "current" and colno and end_colno: + arrow = ( + f'\n{" " * prefix}' + f'{" " * (colno - prefix)}{"^" * (end_colno - colno)}' + ) + else: + arrow = "" + + rendered_lines.append( + f'
    {" " * prefix}'
    +                f"{escape(stripped_line) if stripped_line else ' '}"
    +                f"{arrow if arrow else ''}
    " + ) + + if lines: + for line in lines[start_idx:line_idx]: + render_line(line, "before") + + render_line(lines[line_idx], "current") + + for line in lines[line_idx + 1 : stop_idx]: + render_line(line, "after") + + return FRAME_HTML % { + "id": id(self), + "filename": escape(self.filename), + "lineno": self.lineno, + "function_name": escape(self.name), + "lines": "\n".join(rendered_lines), + "library": "library" if mark_library and self.is_library else "", + } + + +def render_console_html(secret: str, evalex_trusted: bool) -> str: + return CONSOLE_HTML % { + "evalex": "true", + "evalex_trusted": "true" if evalex_trusted else "false", + "console": "true", + "title": "Console", + "secret": secret, + } diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/exceptions.py b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/exceptions.py new file mode 100644 index 0000000..739bd90 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/exceptions.py @@ -0,0 +1,884 @@ +"""Implements a number of Python exceptions which can be raised from within +a view to trigger a standard HTTP non-200 response. + +Usage Example +------------- + +.. code-block:: python + + from werkzeug.wrappers.request import Request + from werkzeug.exceptions import HTTPException, NotFound + + def view(request): + raise NotFound() + + @Request.application + def application(request): + try: + return view(request) + except HTTPException as e: + return e + +As you can see from this example those exceptions are callable WSGI +applications. However, they are not Werkzeug response objects. You +can get a response object by calling ``get_response()`` on a HTTP +exception. + +Keep in mind that you may have to pass an environ (WSGI) or scope +(ASGI) to ``get_response()`` because some errors fetch additional +information relating to the request. + +If you want to hook in a different exception page to say, a 404 status +code, you can add a second except for a specific subclass of an error: + +.. code-block:: python + + @Request.application + def application(request): + try: + return view(request) + except NotFound as e: + return not_found(request) + except HTTPException as e: + return e + +""" +import typing as t +from datetime import datetime + +from markupsafe import escape +from markupsafe import Markup + +from ._internal import _get_environ + +if t.TYPE_CHECKING: + import typing_extensions as te + from _typeshed.wsgi import StartResponse + from _typeshed.wsgi import WSGIEnvironment + from .datastructures import WWWAuthenticate + from .sansio.response import Response + from .wrappers.request import Request as WSGIRequest # noqa: F401 + from .wrappers.response import Response as WSGIResponse # noqa: F401 + + +class HTTPException(Exception): + """The base class for all HTTP exceptions. This exception can be called as a WSGI + application to render a default error page or you can catch the subclasses + of it independently and render nicer error messages. + + .. versionchanged:: 2.1 + Removed the ``wrap`` class method. + """ + + code: t.Optional[int] = None + description: t.Optional[str] = None + + def __init__( + self, + description: t.Optional[str] = None, + response: t.Optional["Response"] = None, + ) -> None: + super().__init__() + if description is not None: + self.description = description + self.response = response + + @property + def name(self) -> str: + """The status name.""" + from .http import HTTP_STATUS_CODES + + return HTTP_STATUS_CODES.get(self.code, "Unknown Error") # type: ignore + + def get_description( + self, + environ: t.Optional["WSGIEnvironment"] = None, + scope: t.Optional[dict] = None, + ) -> str: + """Get the description.""" + if self.description is None: + description = "" + elif not isinstance(self.description, str): + description = str(self.description) + else: + description = self.description + + description = escape(description).replace("\n", Markup("
    ")) + return f"

    {description}

    " + + def get_body( + self, + environ: t.Optional["WSGIEnvironment"] = None, + scope: t.Optional[dict] = None, + ) -> str: + """Get the HTML body.""" + return ( + "\n" + "\n" + f"{self.code} {escape(self.name)}\n" + f"

    {escape(self.name)}

    \n" + f"{self.get_description(environ)}\n" + ) + + def get_headers( + self, + environ: t.Optional["WSGIEnvironment"] = None, + scope: t.Optional[dict] = None, + ) -> t.List[t.Tuple[str, str]]: + """Get a list of headers.""" + return [("Content-Type", "text/html; charset=utf-8")] + + def get_response( + self, + environ: t.Optional[t.Union["WSGIEnvironment", "WSGIRequest"]] = None, + scope: t.Optional[dict] = None, + ) -> "Response": + """Get a response object. If one was passed to the exception + it's returned directly. + + :param environ: the optional environ for the request. This + can be used to modify the response depending + on how the request looked like. + :return: a :class:`Response` object or a subclass thereof. + """ + from .wrappers.response import Response as WSGIResponse # noqa: F811 + + if self.response is not None: + return self.response + if environ is not None: + environ = _get_environ(environ) + headers = self.get_headers(environ, scope) + return WSGIResponse(self.get_body(environ, scope), self.code, headers) + + def __call__( + self, environ: "WSGIEnvironment", start_response: "StartResponse" + ) -> t.Iterable[bytes]: + """Call the exception as WSGI application. + + :param environ: the WSGI environment. + :param start_response: the response callable provided by the WSGI + server. + """ + response = t.cast("WSGIResponse", self.get_response(environ)) + return response(environ, start_response) + + def __str__(self) -> str: + code = self.code if self.code is not None else "???" + return f"{code} {self.name}: {self.description}" + + def __repr__(self) -> str: + code = self.code if self.code is not None else "???" + return f"<{type(self).__name__} '{code}: {self.name}'>" + + +class BadRequest(HTTPException): + """*400* `Bad Request` + + Raise if the browser sends something to the application the application + or server cannot handle. + """ + + code = 400 + description = ( + "The browser (or proxy) sent a request that this server could " + "not understand." + ) + + +class BadRequestKeyError(BadRequest, KeyError): + """An exception that is used to signal both a :exc:`KeyError` and a + :exc:`BadRequest`. Used by many of the datastructures. + """ + + _description = BadRequest.description + #: Show the KeyError along with the HTTP error message in the + #: response. This should be disabled in production, but can be + #: useful in a debug mode. + show_exception = False + + def __init__(self, arg: t.Optional[str] = None, *args: t.Any, **kwargs: t.Any): + super().__init__(*args, **kwargs) + + if arg is None: + KeyError.__init__(self) + else: + KeyError.__init__(self, arg) + + @property # type: ignore + def description(self) -> str: + if self.show_exception: + return ( + f"{self._description}\n" + f"{KeyError.__name__}: {KeyError.__str__(self)}" + ) + + return self._description + + @description.setter + def description(self, value: str) -> None: + self._description = value + + +class ClientDisconnected(BadRequest): + """Internal exception that is raised if Werkzeug detects a disconnected + client. Since the client is already gone at that point attempting to + send the error message to the client might not work and might ultimately + result in another exception in the server. Mainly this is here so that + it is silenced by default as far as Werkzeug is concerned. + + Since disconnections cannot be reliably detected and are unspecified + by WSGI to a large extent this might or might not be raised if a client + is gone. + + .. versionadded:: 0.8 + """ + + +class SecurityError(BadRequest): + """Raised if something triggers a security error. This is otherwise + exactly like a bad request error. + + .. versionadded:: 0.9 + """ + + +class BadHost(BadRequest): + """Raised if the submitted host is badly formatted. + + .. versionadded:: 0.11.2 + """ + + +class Unauthorized(HTTPException): + """*401* ``Unauthorized`` + + Raise if the user is not authorized to access a resource. + + The ``www_authenticate`` argument should be used to set the + ``WWW-Authenticate`` header. This is used for HTTP basic auth and + other schemes. Use :class:`~werkzeug.datastructures.WWWAuthenticate` + to create correctly formatted values. Strictly speaking a 401 + response is invalid if it doesn't provide at least one value for + this header, although real clients typically don't care. + + :param description: Override the default message used for the body + of the response. + :param www-authenticate: A single value, or list of values, for the + WWW-Authenticate header(s). + + .. versionchanged:: 2.0 + Serialize multiple ``www_authenticate`` items into multiple + ``WWW-Authenticate`` headers, rather than joining them + into a single value, for better interoperability. + + .. versionchanged:: 0.15.3 + If the ``www_authenticate`` argument is not set, the + ``WWW-Authenticate`` header is not set. + + .. versionchanged:: 0.15.3 + The ``response`` argument was restored. + + .. versionchanged:: 0.15.1 + ``description`` was moved back as the first argument, restoring + its previous position. + + .. versionchanged:: 0.15.0 + ``www_authenticate`` was added as the first argument, ahead of + ``description``. + """ + + code = 401 + description = ( + "The server could not verify that you are authorized to access" + " the URL requested. You either supplied the wrong credentials" + " (e.g. a bad password), or your browser doesn't understand" + " how to supply the credentials required." + ) + + def __init__( + self, + description: t.Optional[str] = None, + response: t.Optional["Response"] = None, + www_authenticate: t.Optional[ + t.Union["WWWAuthenticate", t.Iterable["WWWAuthenticate"]] + ] = None, + ) -> None: + super().__init__(description, response) + + from .datastructures import WWWAuthenticate + + if isinstance(www_authenticate, WWWAuthenticate): + www_authenticate = (www_authenticate,) + + self.www_authenticate = www_authenticate + + def get_headers( + self, + environ: t.Optional["WSGIEnvironment"] = None, + scope: t.Optional[dict] = None, + ) -> t.List[t.Tuple[str, str]]: + headers = super().get_headers(environ, scope) + if self.www_authenticate: + headers.extend(("WWW-Authenticate", str(x)) for x in self.www_authenticate) + return headers + + +class Forbidden(HTTPException): + """*403* `Forbidden` + + Raise if the user doesn't have the permission for the requested resource + but was authenticated. + """ + + code = 403 + description = ( + "You don't have the permission to access the requested" + " resource. It is either read-protected or not readable by the" + " server." + ) + + +class NotFound(HTTPException): + """*404* `Not Found` + + Raise if a resource does not exist and never existed. + """ + + code = 404 + description = ( + "The requested URL was not found on the server. If you entered" + " the URL manually please check your spelling and try again." + ) + + +class MethodNotAllowed(HTTPException): + """*405* `Method Not Allowed` + + Raise if the server used a method the resource does not handle. For + example `POST` if the resource is view only. Especially useful for REST. + + The first argument for this exception should be a list of allowed methods. + Strictly speaking the response would be invalid if you don't provide valid + methods in the header which you can do with that list. + """ + + code = 405 + description = "The method is not allowed for the requested URL." + + def __init__( + self, + valid_methods: t.Optional[t.Iterable[str]] = None, + description: t.Optional[str] = None, + response: t.Optional["Response"] = None, + ) -> None: + """Takes an optional list of valid http methods + starting with werkzeug 0.3 the list will be mandatory.""" + super().__init__(description=description, response=response) + self.valid_methods = valid_methods + + def get_headers( + self, + environ: t.Optional["WSGIEnvironment"] = None, + scope: t.Optional[dict] = None, + ) -> t.List[t.Tuple[str, str]]: + headers = super().get_headers(environ, scope) + if self.valid_methods: + headers.append(("Allow", ", ".join(self.valid_methods))) + return headers + + +class NotAcceptable(HTTPException): + """*406* `Not Acceptable` + + Raise if the server can't return any content conforming to the + `Accept` headers of the client. + """ + + code = 406 + description = ( + "The resource identified by the request is only capable of" + " generating response entities which have content" + " characteristics not acceptable according to the accept" + " headers sent in the request." + ) + + +class RequestTimeout(HTTPException): + """*408* `Request Timeout` + + Raise to signalize a timeout. + """ + + code = 408 + description = ( + "The server closed the network connection because the browser" + " didn't finish the request within the specified time." + ) + + +class Conflict(HTTPException): + """*409* `Conflict` + + Raise to signal that a request cannot be completed because it conflicts + with the current state on the server. + + .. versionadded:: 0.7 + """ + + code = 409 + description = ( + "A conflict happened while processing the request. The" + " resource might have been modified while the request was being" + " processed." + ) + + +class Gone(HTTPException): + """*410* `Gone` + + Raise if a resource existed previously and went away without new location. + """ + + code = 410 + description = ( + "The requested URL is no longer available on this server and" + " there is no forwarding address. If you followed a link from a" + " foreign page, please contact the author of this page." + ) + + +class LengthRequired(HTTPException): + """*411* `Length Required` + + Raise if the browser submitted data but no ``Content-Length`` header which + is required for the kind of processing the server does. + """ + + code = 411 + description = ( + "A request with this method requires a valid Content-" + "Length header." + ) + + +class PreconditionFailed(HTTPException): + """*412* `Precondition Failed` + + Status code used in combination with ``If-Match``, ``If-None-Match``, or + ``If-Unmodified-Since``. + """ + + code = 412 + description = ( + "The precondition on the request for the URL failed positive evaluation." + ) + + +class RequestEntityTooLarge(HTTPException): + """*413* `Request Entity Too Large` + + The status code one should return if the data submitted exceeded a given + limit. + """ + + code = 413 + description = "The data value transmitted exceeds the capacity limit." + + +class RequestURITooLarge(HTTPException): + """*414* `Request URI Too Large` + + Like *413* but for too long URLs. + """ + + code = 414 + description = ( + "The length of the requested URL exceeds the capacity limit for" + " this server. The request cannot be processed." + ) + + +class UnsupportedMediaType(HTTPException): + """*415* `Unsupported Media Type` + + The status code returned if the server is unable to handle the media type + the client transmitted. + """ + + code = 415 + description = ( + "The server does not support the media type transmitted in the request." + ) + + +class RequestedRangeNotSatisfiable(HTTPException): + """*416* `Requested Range Not Satisfiable` + + The client asked for an invalid part of the file. + + .. versionadded:: 0.7 + """ + + code = 416 + description = "The server cannot provide the requested range." + + def __init__( + self, + length: t.Optional[int] = None, + units: str = "bytes", + description: t.Optional[str] = None, + response: t.Optional["Response"] = None, + ) -> None: + """Takes an optional `Content-Range` header value based on ``length`` + parameter. + """ + super().__init__(description=description, response=response) + self.length = length + self.units = units + + def get_headers( + self, + environ: t.Optional["WSGIEnvironment"] = None, + scope: t.Optional[dict] = None, + ) -> t.List[t.Tuple[str, str]]: + headers = super().get_headers(environ, scope) + if self.length is not None: + headers.append(("Content-Range", f"{self.units} */{self.length}")) + return headers + + +class ExpectationFailed(HTTPException): + """*417* `Expectation Failed` + + The server cannot meet the requirements of the Expect request-header. + + .. versionadded:: 0.7 + """ + + code = 417 + description = "The server could not meet the requirements of the Expect header" + + +class ImATeapot(HTTPException): + """*418* `I'm a teapot` + + The server should return this if it is a teapot and someone attempted + to brew coffee with it. + + .. versionadded:: 0.7 + """ + + code = 418 + description = "This server is a teapot, not a coffee machine" + + +class UnprocessableEntity(HTTPException): + """*422* `Unprocessable Entity` + + Used if the request is well formed, but the instructions are otherwise + incorrect. + """ + + code = 422 + description = ( + "The request was well-formed but was unable to be followed due" + " to semantic errors." + ) + + +class Locked(HTTPException): + """*423* `Locked` + + Used if the resource that is being accessed is locked. + """ + + code = 423 + description = "The resource that is being accessed is locked." + + +class FailedDependency(HTTPException): + """*424* `Failed Dependency` + + Used if the method could not be performed on the resource + because the requested action depended on another action and that action failed. + """ + + code = 424 + description = ( + "The method could not be performed on the resource because the" + " requested action depended on another action and that action" + " failed." + ) + + +class PreconditionRequired(HTTPException): + """*428* `Precondition Required` + + The server requires this request to be conditional, typically to prevent + the lost update problem, which is a race condition between two or more + clients attempting to update a resource through PUT or DELETE. By requiring + each client to include a conditional header ("If-Match" or "If-Unmodified- + Since") with the proper value retained from a recent GET request, the + server ensures that each client has at least seen the previous revision of + the resource. + """ + + code = 428 + description = ( + "This request is required to be conditional; try using" + ' "If-Match" or "If-Unmodified-Since".' + ) + + +class _RetryAfter(HTTPException): + """Adds an optional ``retry_after`` parameter which will set the + ``Retry-After`` header. May be an :class:`int` number of seconds or + a :class:`~datetime.datetime`. + """ + + def __init__( + self, + description: t.Optional[str] = None, + response: t.Optional["Response"] = None, + retry_after: t.Optional[t.Union[datetime, int]] = None, + ) -> None: + super().__init__(description, response) + self.retry_after = retry_after + + def get_headers( + self, + environ: t.Optional["WSGIEnvironment"] = None, + scope: t.Optional[dict] = None, + ) -> t.List[t.Tuple[str, str]]: + headers = super().get_headers(environ, scope) + + if self.retry_after: + if isinstance(self.retry_after, datetime): + from .http import http_date + + value = http_date(self.retry_after) + else: + value = str(self.retry_after) + + headers.append(("Retry-After", value)) + + return headers + + +class TooManyRequests(_RetryAfter): + """*429* `Too Many Requests` + + The server is limiting the rate at which this user receives + responses, and this request exceeds that rate. (The server may use + any convenient method to identify users and their request rates). + The server may include a "Retry-After" header to indicate how long + the user should wait before retrying. + + :param retry_after: If given, set the ``Retry-After`` header to this + value. May be an :class:`int` number of seconds or a + :class:`~datetime.datetime`. + + .. versionchanged:: 1.0 + Added ``retry_after`` parameter. + """ + + code = 429 + description = "This user has exceeded an allotted request count. Try again later." + + +class RequestHeaderFieldsTooLarge(HTTPException): + """*431* `Request Header Fields Too Large` + + The server refuses to process the request because the header fields are too + large. One or more individual fields may be too large, or the set of all + headers is too large. + """ + + code = 431 + description = "One or more header fields exceeds the maximum size." + + +class UnavailableForLegalReasons(HTTPException): + """*451* `Unavailable For Legal Reasons` + + This status code indicates that the server is denying access to the + resource as a consequence of a legal demand. + """ + + code = 451 + description = "Unavailable for legal reasons." + + +class InternalServerError(HTTPException): + """*500* `Internal Server Error` + + Raise if an internal server error occurred. This is a good fallback if an + unknown error occurred in the dispatcher. + + .. versionchanged:: 1.0.0 + Added the :attr:`original_exception` attribute. + """ + + code = 500 + description = ( + "The server encountered an internal error and was unable to" + " complete your request. Either the server is overloaded or" + " there is an error in the application." + ) + + def __init__( + self, + description: t.Optional[str] = None, + response: t.Optional["Response"] = None, + original_exception: t.Optional[BaseException] = None, + ) -> None: + #: The original exception that caused this 500 error. Can be + #: used by frameworks to provide context when handling + #: unexpected errors. + self.original_exception = original_exception + super().__init__(description=description, response=response) + + +class NotImplemented(HTTPException): + """*501* `Not Implemented` + + Raise if the application does not support the action requested by the + browser. + """ + + code = 501 + description = "The server does not support the action requested by the browser." + + +class BadGateway(HTTPException): + """*502* `Bad Gateway` + + If you do proxying in your application you should return this status code + if you received an invalid response from the upstream server it accessed + in attempting to fulfill the request. + """ + + code = 502 + description = ( + "The proxy server received an invalid response from an upstream server." + ) + + +class ServiceUnavailable(_RetryAfter): + """*503* `Service Unavailable` + + Status code you should return if a service is temporarily + unavailable. + + :param retry_after: If given, set the ``Retry-After`` header to this + value. May be an :class:`int` number of seconds or a + :class:`~datetime.datetime`. + + .. versionchanged:: 1.0 + Added ``retry_after`` parameter. + """ + + code = 503 + description = ( + "The server is temporarily unable to service your request due" + " to maintenance downtime or capacity problems. Please try" + " again later." + ) + + +class GatewayTimeout(HTTPException): + """*504* `Gateway Timeout` + + Status code you should return if a connection to an upstream server + times out. + """ + + code = 504 + description = "The connection to an upstream server timed out." + + +class HTTPVersionNotSupported(HTTPException): + """*505* `HTTP Version Not Supported` + + The server does not support the HTTP protocol version used in the request. + """ + + code = 505 + description = ( + "The server does not support the HTTP protocol version used in the request." + ) + + +default_exceptions: t.Dict[int, t.Type[HTTPException]] = {} + + +def _find_exceptions() -> None: + for obj in globals().values(): + try: + is_http_exception = issubclass(obj, HTTPException) + except TypeError: + is_http_exception = False + if not is_http_exception or obj.code is None: + continue + old_obj = default_exceptions.get(obj.code, None) + if old_obj is not None and issubclass(obj, old_obj): + continue + default_exceptions[obj.code] = obj + + +_find_exceptions() +del _find_exceptions + + +class Aborter: + """When passed a dict of code -> exception items it can be used as + callable that raises exceptions. If the first argument to the + callable is an integer it will be looked up in the mapping, if it's + a WSGI application it will be raised in a proxy exception. + + The rest of the arguments are forwarded to the exception constructor. + """ + + def __init__( + self, + mapping: t.Optional[t.Dict[int, t.Type[HTTPException]]] = None, + extra: t.Optional[t.Dict[int, t.Type[HTTPException]]] = None, + ) -> None: + if mapping is None: + mapping = default_exceptions + self.mapping = dict(mapping) + if extra is not None: + self.mapping.update(extra) + + def __call__( + self, code: t.Union[int, "Response"], *args: t.Any, **kwargs: t.Any + ) -> "te.NoReturn": + from .sansio.response import Response + + if isinstance(code, Response): + raise HTTPException(response=code) + + if code not in self.mapping: + raise LookupError(f"no exception for {code!r}") + + raise self.mapping[code](*args, **kwargs) + + +def abort( + status: t.Union[int, "Response"], *args: t.Any, **kwargs: t.Any +) -> "te.NoReturn": + """Raises an :py:exc:`HTTPException` for the given status code or WSGI + application. + + If a status code is given, it will be looked up in the list of + exceptions and will raise that exception. If passed a WSGI application, + it will wrap it in a proxy WSGI exception and raise that:: + + abort(404) # 404 Not Found + abort(Response('Hello World')) + + """ + _aborter(status, *args, **kwargs) + + +_aborter: Aborter = Aborter() diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/formparser.py b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/formparser.py new file mode 100644 index 0000000..bebb2fc --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/formparser.py @@ -0,0 +1,465 @@ +import typing as t +from functools import update_wrapper +from io import BytesIO +from itertools import chain +from typing import Union + +from . import exceptions +from .datastructures import FileStorage +from .datastructures import Headers +from .datastructures import MultiDict +from .http import parse_options_header +from .sansio.multipart import Data +from .sansio.multipart import Epilogue +from .sansio.multipart import Field +from .sansio.multipart import File +from .sansio.multipart import MultipartDecoder +from .sansio.multipart import NeedData +from .urls import url_decode_stream +from .wsgi import _make_chunk_iter +from .wsgi import get_content_length +from .wsgi import get_input_stream + +# there are some platforms where SpooledTemporaryFile is not available. +# In that case we need to provide a fallback. +try: + from tempfile import SpooledTemporaryFile +except ImportError: + from tempfile import TemporaryFile + + SpooledTemporaryFile = None # type: ignore + +if t.TYPE_CHECKING: + import typing as te + from _typeshed.wsgi import WSGIEnvironment + + t_parse_result = t.Tuple[t.IO[bytes], MultiDict, MultiDict] + + class TStreamFactory(te.Protocol): + def __call__( + self, + total_content_length: t.Optional[int], + content_type: t.Optional[str], + filename: t.Optional[str], + content_length: t.Optional[int] = None, + ) -> t.IO[bytes]: + ... + + +F = t.TypeVar("F", bound=t.Callable[..., t.Any]) + + +def _exhaust(stream: t.IO[bytes]) -> None: + bts = stream.read(64 * 1024) + while bts: + bts = stream.read(64 * 1024) + + +def default_stream_factory( + total_content_length: t.Optional[int], + content_type: t.Optional[str], + filename: t.Optional[str], + content_length: t.Optional[int] = None, +) -> t.IO[bytes]: + max_size = 1024 * 500 + + if SpooledTemporaryFile is not None: + return t.cast(t.IO[bytes], SpooledTemporaryFile(max_size=max_size, mode="rb+")) + elif total_content_length is None or total_content_length > max_size: + return t.cast(t.IO[bytes], TemporaryFile("rb+")) + + return BytesIO() + + +def parse_form_data( + environ: "WSGIEnvironment", + stream_factory: t.Optional["TStreamFactory"] = None, + charset: str = "utf-8", + errors: str = "replace", + max_form_memory_size: t.Optional[int] = None, + max_content_length: t.Optional[int] = None, + cls: t.Optional[t.Type[MultiDict]] = None, + silent: bool = True, +) -> "t_parse_result": + """Parse the form data in the environ and return it as tuple in the form + ``(stream, form, files)``. You should only call this method if the + transport method is `POST`, `PUT`, or `PATCH`. + + If the mimetype of the data transmitted is `multipart/form-data` the + files multidict will be filled with `FileStorage` objects. If the + mimetype is unknown the input stream is wrapped and returned as first + argument, else the stream is empty. + + This is a shortcut for the common usage of :class:`FormDataParser`. + + Have a look at :doc:`/request_data` for more details. + + .. versionadded:: 0.5 + The `max_form_memory_size`, `max_content_length` and + `cls` parameters were added. + + .. versionadded:: 0.5.1 + The optional `silent` flag was added. + + :param environ: the WSGI environment to be used for parsing. + :param stream_factory: An optional callable that returns a new read and + writeable file descriptor. This callable works + the same as :meth:`Response._get_file_stream`. + :param charset: The character set for URL and url encoded form data. + :param errors: The encoding error behavior. + :param max_form_memory_size: the maximum number of bytes to be accepted for + in-memory stored form data. If the data + exceeds the value specified an + :exc:`~exceptions.RequestEntityTooLarge` + exception is raised. + :param max_content_length: If this is provided and the transmitted data + is longer than this value an + :exc:`~exceptions.RequestEntityTooLarge` + exception is raised. + :param cls: an optional dict class to use. If this is not specified + or `None` the default :class:`MultiDict` is used. + :param silent: If set to False parsing errors will not be caught. + :return: A tuple in the form ``(stream, form, files)``. + """ + return FormDataParser( + stream_factory, + charset, + errors, + max_form_memory_size, + max_content_length, + cls, + silent, + ).parse_from_environ(environ) + + +def exhaust_stream(f: F) -> F: + """Helper decorator for methods that exhausts the stream on return.""" + + def wrapper(self, stream, *args, **kwargs): # type: ignore + try: + return f(self, stream, *args, **kwargs) + finally: + exhaust = getattr(stream, "exhaust", None) + + if exhaust is not None: + exhaust() + else: + while True: + chunk = stream.read(1024 * 64) + + if not chunk: + break + + return update_wrapper(t.cast(F, wrapper), f) + + +class FormDataParser: + """This class implements parsing of form data for Werkzeug. By itself + it can parse multipart and url encoded form data. It can be subclassed + and extended but for most mimetypes it is a better idea to use the + untouched stream and expose it as separate attributes on a request + object. + + .. versionadded:: 0.8 + + :param stream_factory: An optional callable that returns a new read and + writeable file descriptor. This callable works + the same as :meth:`Response._get_file_stream`. + :param charset: The character set for URL and url encoded form data. + :param errors: The encoding error behavior. + :param max_form_memory_size: the maximum number of bytes to be accepted for + in-memory stored form data. If the data + exceeds the value specified an + :exc:`~exceptions.RequestEntityTooLarge` + exception is raised. + :param max_content_length: If this is provided and the transmitted data + is longer than this value an + :exc:`~exceptions.RequestEntityTooLarge` + exception is raised. + :param cls: an optional dict class to use. If this is not specified + or `None` the default :class:`MultiDict` is used. + :param silent: If set to False parsing errors will not be caught. + :param max_form_parts: The maximum number of parts to be parsed. If this is + exceeded, a :exc:`~exceptions.RequestEntityTooLarge` exception is raised. + """ + + def __init__( + self, + stream_factory: t.Optional["TStreamFactory"] = None, + charset: str = "utf-8", + errors: str = "replace", + max_form_memory_size: t.Optional[int] = None, + max_content_length: t.Optional[int] = None, + cls: t.Optional[t.Type[MultiDict]] = None, + silent: bool = True, + *, + max_form_parts: t.Optional[int] = None, + ) -> None: + if stream_factory is None: + stream_factory = default_stream_factory + + self.stream_factory = stream_factory + self.charset = charset + self.errors = errors + self.max_form_memory_size = max_form_memory_size + self.max_content_length = max_content_length + self.max_form_parts = max_form_parts + + if cls is None: + cls = MultiDict + + self.cls = cls + self.silent = silent + + def get_parse_func( + self, mimetype: str, options: t.Dict[str, str] + ) -> t.Optional[ + t.Callable[ + ["FormDataParser", t.IO[bytes], str, t.Optional[int], t.Dict[str, str]], + "t_parse_result", + ] + ]: + return self.parse_functions.get(mimetype) + + def parse_from_environ(self, environ: "WSGIEnvironment") -> "t_parse_result": + """Parses the information from the environment as form data. + + :param environ: the WSGI environment to be used for parsing. + :return: A tuple in the form ``(stream, form, files)``. + """ + content_type = environ.get("CONTENT_TYPE", "") + content_length = get_content_length(environ) + mimetype, options = parse_options_header(content_type) + return self.parse(get_input_stream(environ), mimetype, content_length, options) + + def parse( + self, + stream: t.IO[bytes], + mimetype: str, + content_length: t.Optional[int], + options: t.Optional[t.Dict[str, str]] = None, + ) -> "t_parse_result": + """Parses the information from the given stream, mimetype, + content length and mimetype parameters. + + :param stream: an input stream + :param mimetype: the mimetype of the data + :param content_length: the content length of the incoming data + :param options: optional mimetype parameters (used for + the multipart boundary for instance) + :return: A tuple in the form ``(stream, form, files)``. + """ + if ( + self.max_content_length is not None + and content_length is not None + and content_length > self.max_content_length + ): + # if the input stream is not exhausted, firefox reports Connection Reset + _exhaust(stream) + raise exceptions.RequestEntityTooLarge() + + if options is None: + options = {} + + parse_func = self.get_parse_func(mimetype, options) + + if parse_func is not None: + try: + return parse_func(self, stream, mimetype, content_length, options) + except ValueError: + if not self.silent: + raise + + return stream, self.cls(), self.cls() + + @exhaust_stream + def _parse_multipart( + self, + stream: t.IO[bytes], + mimetype: str, + content_length: t.Optional[int], + options: t.Dict[str, str], + ) -> "t_parse_result": + parser = MultiPartParser( + self.stream_factory, + self.charset, + self.errors, + max_form_memory_size=self.max_form_memory_size, + cls=self.cls, + max_form_parts=self.max_form_parts, + ) + boundary = options.get("boundary", "").encode("ascii") + + if not boundary: + raise ValueError("Missing boundary") + + form, files = parser.parse(stream, boundary, content_length) + return stream, form, files + + @exhaust_stream + def _parse_urlencoded( + self, + stream: t.IO[bytes], + mimetype: str, + content_length: t.Optional[int], + options: t.Dict[str, str], + ) -> "t_parse_result": + if ( + self.max_form_memory_size is not None + and content_length is not None + and content_length > self.max_form_memory_size + ): + # if the input stream is not exhausted, firefox reports Connection Reset + _exhaust(stream) + raise exceptions.RequestEntityTooLarge() + + form = url_decode_stream(stream, self.charset, errors=self.errors, cls=self.cls) + return stream, form, self.cls() + + #: mapping of mimetypes to parsing functions + parse_functions: t.Dict[ + str, + t.Callable[ + ["FormDataParser", t.IO[bytes], str, t.Optional[int], t.Dict[str, str]], + "t_parse_result", + ], + ] = { + "multipart/form-data": _parse_multipart, + "application/x-www-form-urlencoded": _parse_urlencoded, + "application/x-url-encoded": _parse_urlencoded, + } + + +def _line_parse(line: str) -> t.Tuple[str, bool]: + """Removes line ending characters and returns a tuple (`stripped_line`, + `is_terminated`). + """ + if line[-2:] == "\r\n": + return line[:-2], True + + elif line[-1:] in {"\r", "\n"}: + return line[:-1], True + + return line, False + + +class MultiPartParser: + def __init__( + self, + stream_factory: t.Optional["TStreamFactory"] = None, + charset: str = "utf-8", + errors: str = "replace", + max_form_memory_size: t.Optional[int] = None, + cls: t.Optional[t.Type[MultiDict]] = None, + buffer_size: int = 64 * 1024, + max_form_parts: t.Optional[int] = None, + ) -> None: + self.charset = charset + self.errors = errors + self.max_form_memory_size = max_form_memory_size + self.max_form_parts = max_form_parts + + if stream_factory is None: + stream_factory = default_stream_factory + + self.stream_factory = stream_factory + + if cls is None: + cls = MultiDict + + self.cls = cls + + self.buffer_size = buffer_size + + def fail(self, message: str) -> "te.NoReturn": + raise ValueError(message) + + def get_part_charset(self, headers: Headers) -> str: + # Figure out input charset for current part + content_type = headers.get("content-type") + + if content_type: + mimetype, ct_params = parse_options_header(content_type) + return ct_params.get("charset", self.charset) + + return self.charset + + def start_file_streaming( + self, event: File, total_content_length: t.Optional[int] + ) -> t.IO[bytes]: + content_type = event.headers.get("content-type") + + try: + content_length = int(event.headers["content-length"]) + except (KeyError, ValueError): + content_length = 0 + + container = self.stream_factory( + total_content_length=total_content_length, + filename=event.filename, + content_type=content_type, + content_length=content_length, + ) + return container + + def parse( + self, stream: t.IO[bytes], boundary: bytes, content_length: t.Optional[int] + ) -> t.Tuple[MultiDict, MultiDict]: + container: t.Union[t.IO[bytes], t.List[bytes]] + _write: t.Callable[[bytes], t.Any] + + iterator = chain( + _make_chunk_iter( + stream, + limit=content_length, + buffer_size=self.buffer_size, + ), + [None], + ) + + parser = MultipartDecoder( + boundary, self.max_form_memory_size, max_parts=self.max_form_parts + ) + + fields = [] + files = [] + + current_part: Union[Field, File] + for data in iterator: + parser.receive_data(data) + event = parser.next_event() + while not isinstance(event, (Epilogue, NeedData)): + if isinstance(event, Field): + current_part = event + container = [] + _write = container.append + elif isinstance(event, File): + current_part = event + container = self.start_file_streaming(event, content_length) + _write = container.write + elif isinstance(event, Data): + _write(event.data) + if not event.more_data: + if isinstance(current_part, Field): + value = b"".join(container).decode( + self.get_part_charset(current_part.headers), self.errors + ) + fields.append((current_part.name, value)) + else: + container = t.cast(t.IO[bytes], container) + container.seek(0) + files.append( + ( + current_part.name, + FileStorage( + container, + current_part.filename, + current_part.name, + headers=current_part.headers, + ), + ) + ) + + event = parser.next_event() + + return self.cls(fields), self.cls(files) diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/http.py b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/http.py new file mode 100644 index 0000000..0a7bc73 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/http.py @@ -0,0 +1,1327 @@ +import base64 +import email.utils +import re +import typing +import typing as t +import warnings +from datetime import date +from datetime import datetime +from datetime import time +from datetime import timedelta +from datetime import timezone +from enum import Enum +from hashlib import sha1 +from time import mktime +from time import struct_time +from urllib.parse import unquote_to_bytes as _unquote +from urllib.request import parse_http_list as _parse_list_header + +from ._internal import _cookie_quote +from ._internal import _dt_as_utc +from ._internal import _make_cookie_domain +from ._internal import _to_bytes +from ._internal import _to_str +from ._internal import _wsgi_decoding_dance + +if t.TYPE_CHECKING: + from _typeshed.wsgi import WSGIEnvironment + +# for explanation of "media-range", etc. see Sections 5.3.{1,2} of RFC 7231 +_accept_re = re.compile( + r""" + ( # media-range capturing-parenthesis + [^\s;,]+ # type/subtype + (?:[ \t]*;[ \t]* # ";" + (?: # parameter non-capturing-parenthesis + [^\s;,q][^\s;,]* # token that doesn't start with "q" + | # or + q[^\s;,=][^\s;,]* # token that is more than just "q" + ) + )* # zero or more parameters + ) # end of media-range + (?:[ \t]*;[ \t]*q= # weight is a "q" parameter + (\d*(?:\.\d+)?) # qvalue capturing-parentheses + [^,]* # "extension" accept params: who cares? + )? # accept params are optional + """, + re.VERBOSE, +) +_token_chars = frozenset( + "!#$%&'*+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~" +) +_etag_re = re.compile(r'([Ww]/)?(?:"(.*?)"|(.*?))(?:\s*,\s*|$)') +_option_header_piece_re = re.compile( + r""" + ;\s*,?\s* # newlines were replaced with commas + (?P + "[^"\\]*(?:\\.[^"\\]*)*" # quoted string + | + [^\s;,=*]+ # token + ) + (?:\*(?P\d+))? # *1, optional continuation index + \s* + (?: # optionally followed by =value + (?: # equals sign, possibly with encoding + \*\s*=\s* # * indicates extended notation + (?: # optional encoding + (?P[^\s]+?) + '(?P[^\s]*?)' + )? + | + =\s* # basic notation + ) + (?P + "[^"\\]*(?:\\.[^"\\]*)*" # quoted string + | + [^;,]+ # token + )? + )? + \s* + """, + flags=re.VERBOSE, +) +_option_header_start_mime_type = re.compile(r",\s*([^;,\s]+)([;,]\s*.+)?") +_entity_headers = frozenset( + [ + "allow", + "content-encoding", + "content-language", + "content-length", + "content-location", + "content-md5", + "content-range", + "content-type", + "expires", + "last-modified", + ] +) +_hop_by_hop_headers = frozenset( + [ + "connection", + "keep-alive", + "proxy-authenticate", + "proxy-authorization", + "te", + "trailer", + "transfer-encoding", + "upgrade", + ] +) +HTTP_STATUS_CODES = { + 100: "Continue", + 101: "Switching Protocols", + 102: "Processing", + 103: "Early Hints", # see RFC 8297 + 200: "OK", + 201: "Created", + 202: "Accepted", + 203: "Non Authoritative Information", + 204: "No Content", + 205: "Reset Content", + 206: "Partial Content", + 207: "Multi Status", + 208: "Already Reported", # see RFC 5842 + 226: "IM Used", # see RFC 3229 + 300: "Multiple Choices", + 301: "Moved Permanently", + 302: "Found", + 303: "See Other", + 304: "Not Modified", + 305: "Use Proxy", + 306: "Switch Proxy", # unused + 307: "Temporary Redirect", + 308: "Permanent Redirect", + 400: "Bad Request", + 401: "Unauthorized", + 402: "Payment Required", # unused + 403: "Forbidden", + 404: "Not Found", + 405: "Method Not Allowed", + 406: "Not Acceptable", + 407: "Proxy Authentication Required", + 408: "Request Timeout", + 409: "Conflict", + 410: "Gone", + 411: "Length Required", + 412: "Precondition Failed", + 413: "Request Entity Too Large", + 414: "Request URI Too Long", + 415: "Unsupported Media Type", + 416: "Requested Range Not Satisfiable", + 417: "Expectation Failed", + 418: "I'm a teapot", # see RFC 2324 + 421: "Misdirected Request", # see RFC 7540 + 422: "Unprocessable Entity", + 423: "Locked", + 424: "Failed Dependency", + 425: "Too Early", # see RFC 8470 + 426: "Upgrade Required", + 428: "Precondition Required", # see RFC 6585 + 429: "Too Many Requests", + 431: "Request Header Fields Too Large", + 449: "Retry With", # proprietary MS extension + 451: "Unavailable For Legal Reasons", + 500: "Internal Server Error", + 501: "Not Implemented", + 502: "Bad Gateway", + 503: "Service Unavailable", + 504: "Gateway Timeout", + 505: "HTTP Version Not Supported", + 506: "Variant Also Negotiates", # see RFC 2295 + 507: "Insufficient Storage", + 508: "Loop Detected", # see RFC 5842 + 510: "Not Extended", + 511: "Network Authentication Failed", +} + + +class COEP(Enum): + """Cross Origin Embedder Policies""" + + UNSAFE_NONE = "unsafe-none" + REQUIRE_CORP = "require-corp" + + +class COOP(Enum): + """Cross Origin Opener Policies""" + + UNSAFE_NONE = "unsafe-none" + SAME_ORIGIN_ALLOW_POPUPS = "same-origin-allow-popups" + SAME_ORIGIN = "same-origin" + + +def _is_extended_parameter(key: str) -> bool: + """Per RFC 5987/8187, "extended" values may *not* be quoted. + This is in keeping with browser implementations. So we test + using this function to see if the key indicates this parameter + follows the `ext-parameter` syntax (using a trailing '*'). + """ + return key.strip().endswith("*") + + +def quote_header_value( + value: t.Union[str, int], extra_chars: str = "", allow_token: bool = True +) -> str: + """Quote a header value if necessary. + + .. versionadded:: 0.5 + + :param value: the value to quote. + :param extra_chars: a list of extra characters to skip quoting. + :param allow_token: if this is enabled token values are returned + unchanged. + """ + if isinstance(value, bytes): + value = value.decode("latin1") + value = str(value) + if allow_token: + token_chars = _token_chars | set(extra_chars) + if set(value).issubset(token_chars): + return value + value = value.replace("\\", "\\\\").replace('"', '\\"') + return f'"{value}"' + + +def unquote_header_value(value: str, is_filename: bool = False) -> str: + r"""Unquotes a header value. (Reversal of :func:`quote_header_value`). + This does not use the real unquoting but what browsers are actually + using for quoting. + + .. versionadded:: 0.5 + + :param value: the header value to unquote. + :param is_filename: The value represents a filename or path. + """ + if value and value[0] == value[-1] == '"': + # this is not the real unquoting, but fixing this so that the + # RFC is met will result in bugs with internet explorer and + # probably some other browsers as well. IE for example is + # uploading files with "C:\foo\bar.txt" as filename + value = value[1:-1] + + # if this is a filename and the starting characters look like + # a UNC path, then just return the value without quotes. Using the + # replace sequence below on a UNC path has the effect of turning + # the leading double slash into a single slash and then + # _fix_ie_filename() doesn't work correctly. See #458. + if not is_filename or value[:2] != "\\\\": + return value.replace("\\\\", "\\").replace('\\"', '"') + return value + + +def dump_options_header( + header: t.Optional[str], options: t.Mapping[str, t.Optional[t.Union[str, int]]] +) -> str: + """The reverse function to :func:`parse_options_header`. + + :param header: the header to dump + :param options: a dict of options to append. + """ + segments = [] + if header is not None: + segments.append(header) + for key, value in options.items(): + if value is None: + segments.append(key) + elif _is_extended_parameter(key): + segments.append(f"{key}={value}") + else: + segments.append(f"{key}={quote_header_value(value)}") + return "; ".join(segments) + + +def dump_header( + iterable: t.Union[t.Dict[str, t.Union[str, int]], t.Iterable[str]], + allow_token: bool = True, +) -> str: + """Dump an HTTP header again. This is the reversal of + :func:`parse_list_header`, :func:`parse_set_header` and + :func:`parse_dict_header`. This also quotes strings that include an + equals sign unless you pass it as dict of key, value pairs. + + >>> dump_header({'foo': 'bar baz'}) + 'foo="bar baz"' + >>> dump_header(('foo', 'bar baz')) + 'foo, "bar baz"' + + :param iterable: the iterable or dict of values to quote. + :param allow_token: if set to `False` tokens as values are disallowed. + See :func:`quote_header_value` for more details. + """ + if isinstance(iterable, dict): + items = [] + for key, value in iterable.items(): + if value is None: + items.append(key) + elif _is_extended_parameter(key): + items.append(f"{key}={value}") + else: + items.append( + f"{key}={quote_header_value(value, allow_token=allow_token)}" + ) + else: + items = [quote_header_value(x, allow_token=allow_token) for x in iterable] + return ", ".join(items) + + +def dump_csp_header(header: "ds.ContentSecurityPolicy") -> str: + """Dump a Content Security Policy header. + + These are structured into policies such as "default-src 'self'; + script-src 'self'". + + .. versionadded:: 1.0.0 + Support for Content Security Policy headers was added. + + """ + return "; ".join(f"{key} {value}" for key, value in header.items()) + + +def parse_list_header(value: str) -> t.List[str]: + """Parse lists as described by RFC 2068 Section 2. + + In particular, parse comma-separated lists where the elements of + the list may include quoted-strings. A quoted-string could + contain a comma. A non-quoted string could have quotes in the + middle. Quotes are removed automatically after parsing. + + It basically works like :func:`parse_set_header` just that items + may appear multiple times and case sensitivity is preserved. + + The return value is a standard :class:`list`: + + >>> parse_list_header('token, "quoted value"') + ['token', 'quoted value'] + + To create a header from the :class:`list` again, use the + :func:`dump_header` function. + + :param value: a string with a list header. + :return: :class:`list` + """ + result = [] + for item in _parse_list_header(value): + if item[:1] == item[-1:] == '"': + item = unquote_header_value(item[1:-1]) + result.append(item) + return result + + +def parse_dict_header(value: str, cls: t.Type[dict] = dict) -> t.Dict[str, str]: + """Parse lists of key, value pairs as described by RFC 2068 Section 2 and + convert them into a python dict (or any other mapping object created from + the type with a dict like interface provided by the `cls` argument): + + >>> d = parse_dict_header('foo="is a fish", bar="as well"') + >>> type(d) is dict + True + >>> sorted(d.items()) + [('bar', 'as well'), ('foo', 'is a fish')] + + If there is no value for a key it will be `None`: + + >>> parse_dict_header('key_without_value') + {'key_without_value': None} + + To create a header from the :class:`dict` again, use the + :func:`dump_header` function. + + .. versionchanged:: 0.9 + Added support for `cls` argument. + + :param value: a string with a dict header. + :param cls: callable to use for storage of parsed results. + :return: an instance of `cls` + """ + result = cls() + if isinstance(value, bytes): + value = value.decode("latin1") + for item in _parse_list_header(value): + if "=" not in item: + result[item] = None + continue + name, value = item.split("=", 1) + if value[:1] == value[-1:] == '"': + value = unquote_header_value(value[1:-1]) + result[name] = value + return result + + +def parse_options_header(value: t.Optional[str]) -> t.Tuple[str, t.Dict[str, str]]: + """Parse a ``Content-Type``-like header into a tuple with the + value and any options: + + >>> parse_options_header('text/html; charset=utf8') + ('text/html', {'charset': 'utf8'}) + + This should is not for ``Cache-Control``-like headers, which use a + different format. For those, use :func:`parse_dict_header`. + + :param value: The header value to parse. + + .. versionchanged:: 2.2 + Option names are always converted to lowercase. + + .. versionchanged:: 2.1 + The ``multiple`` parameter is deprecated and will be removed in + Werkzeug 2.2. + + .. versionchanged:: 0.15 + :rfc:`2231` parameter continuations are handled. + + .. versionadded:: 0.5 + """ + if not value: + return "", {} + + result: t.List[t.Any] = [] + + value = "," + value.replace("\n", ",") + while value: + match = _option_header_start_mime_type.match(value) + if not match: + break + result.append(match.group(1)) # mimetype + options: t.Dict[str, str] = {} + # Parse options + rest = match.group(2) + encoding: t.Optional[str] + continued_encoding: t.Optional[str] = None + while rest: + optmatch = _option_header_piece_re.match(rest) + if not optmatch: + break + option, count, encoding, language, option_value = optmatch.groups() + # Continuations don't have to supply the encoding after the + # first line. If we're in a continuation, track the current + # encoding to use for subsequent lines. Reset it when the + # continuation ends. + if not count: + continued_encoding = None + else: + if not encoding: + encoding = continued_encoding + continued_encoding = encoding + option = unquote_header_value(option).lower() + + if option_value is not None: + option_value = unquote_header_value(option_value, option == "filename") + + if encoding is not None: + option_value = _unquote(option_value).decode(encoding) + + if count: + # Continuations append to the existing value. For + # simplicity, this ignores the possibility of + # out-of-order indices, which shouldn't happen anyway. + if option_value is not None: + options[option] = options.get(option, "") + option_value + else: + options[option] = option_value # type: ignore[assignment] + + rest = rest[optmatch.end() :] + result.append(options) + return tuple(result) # type: ignore[return-value] + + return tuple(result) if result else ("", {}) # type: ignore[return-value] + + +_TAnyAccept = t.TypeVar("_TAnyAccept", bound="ds.Accept") + + +@typing.overload +def parse_accept_header(value: t.Optional[str]) -> "ds.Accept": + ... + + +@typing.overload +def parse_accept_header( + value: t.Optional[str], cls: t.Type[_TAnyAccept] +) -> _TAnyAccept: + ... + + +def parse_accept_header( + value: t.Optional[str], cls: t.Optional[t.Type[_TAnyAccept]] = None +) -> _TAnyAccept: + """Parses an HTTP Accept-* header. This does not implement a complete + valid algorithm but one that supports at least value and quality + extraction. + + Returns a new :class:`Accept` object (basically a list of ``(value, quality)`` + tuples sorted by the quality with some additional accessor methods). + + The second parameter can be a subclass of :class:`Accept` that is created + with the parsed values and returned. + + :param value: the accept header string to be parsed. + :param cls: the wrapper class for the return value (can be + :class:`Accept` or a subclass thereof) + :return: an instance of `cls`. + """ + if cls is None: + cls = t.cast(t.Type[_TAnyAccept], ds.Accept) + + if not value: + return cls(None) + + result = [] + for match in _accept_re.finditer(value): + quality_match = match.group(2) + if not quality_match: + quality: float = 1 + else: + quality = max(min(float(quality_match), 1), 0) + result.append((match.group(1), quality)) + return cls(result) + + +_TAnyCC = t.TypeVar("_TAnyCC", bound="ds._CacheControl") +_t_cc_update = t.Optional[t.Callable[[_TAnyCC], None]] + + +@typing.overload +def parse_cache_control_header( + value: t.Optional[str], on_update: _t_cc_update, cls: None = None +) -> "ds.RequestCacheControl": + ... + + +@typing.overload +def parse_cache_control_header( + value: t.Optional[str], on_update: _t_cc_update, cls: t.Type[_TAnyCC] +) -> _TAnyCC: + ... + + +def parse_cache_control_header( + value: t.Optional[str], + on_update: _t_cc_update = None, + cls: t.Optional[t.Type[_TAnyCC]] = None, +) -> _TAnyCC: + """Parse a cache control header. The RFC differs between response and + request cache control, this method does not. It's your responsibility + to not use the wrong control statements. + + .. versionadded:: 0.5 + The `cls` was added. If not specified an immutable + :class:`~werkzeug.datastructures.RequestCacheControl` is returned. + + :param value: a cache control header to be parsed. + :param on_update: an optional callable that is called every time a value + on the :class:`~werkzeug.datastructures.CacheControl` + object is changed. + :param cls: the class for the returned object. By default + :class:`~werkzeug.datastructures.RequestCacheControl` is used. + :return: a `cls` object. + """ + if cls is None: + cls = t.cast(t.Type[_TAnyCC], ds.RequestCacheControl) + + if not value: + return cls((), on_update) + + return cls(parse_dict_header(value), on_update) + + +_TAnyCSP = t.TypeVar("_TAnyCSP", bound="ds.ContentSecurityPolicy") +_t_csp_update = t.Optional[t.Callable[[_TAnyCSP], None]] + + +@typing.overload +def parse_csp_header( + value: t.Optional[str], on_update: _t_csp_update, cls: None = None +) -> "ds.ContentSecurityPolicy": + ... + + +@typing.overload +def parse_csp_header( + value: t.Optional[str], on_update: _t_csp_update, cls: t.Type[_TAnyCSP] +) -> _TAnyCSP: + ... + + +def parse_csp_header( + value: t.Optional[str], + on_update: _t_csp_update = None, + cls: t.Optional[t.Type[_TAnyCSP]] = None, +) -> _TAnyCSP: + """Parse a Content Security Policy header. + + .. versionadded:: 1.0.0 + Support for Content Security Policy headers was added. + + :param value: a csp header to be parsed. + :param on_update: an optional callable that is called every time a value + on the object is changed. + :param cls: the class for the returned object. By default + :class:`~werkzeug.datastructures.ContentSecurityPolicy` is used. + :return: a `cls` object. + """ + if cls is None: + cls = t.cast(t.Type[_TAnyCSP], ds.ContentSecurityPolicy) + + if value is None: + return cls((), on_update) + + items = [] + + for policy in value.split(";"): + policy = policy.strip() + + # Ignore badly formatted policies (no space) + if " " in policy: + directive, value = policy.strip().split(" ", 1) + items.append((directive.strip(), value.strip())) + + return cls(items, on_update) + + +def parse_set_header( + value: t.Optional[str], + on_update: t.Optional[t.Callable[["ds.HeaderSet"], None]] = None, +) -> "ds.HeaderSet": + """Parse a set-like header and return a + :class:`~werkzeug.datastructures.HeaderSet` object: + + >>> hs = parse_set_header('token, "quoted value"') + + The return value is an object that treats the items case-insensitively + and keeps the order of the items: + + >>> 'TOKEN' in hs + True + >>> hs.index('quoted value') + 1 + >>> hs + HeaderSet(['token', 'quoted value']) + + To create a header from the :class:`HeaderSet` again, use the + :func:`dump_header` function. + + :param value: a set header to be parsed. + :param on_update: an optional callable that is called every time a + value on the :class:`~werkzeug.datastructures.HeaderSet` + object is changed. + :return: a :class:`~werkzeug.datastructures.HeaderSet` + """ + if not value: + return ds.HeaderSet(None, on_update) + return ds.HeaderSet(parse_list_header(value), on_update) + + +def parse_authorization_header( + value: t.Optional[str], +) -> t.Optional["ds.Authorization"]: + """Parse an HTTP basic/digest authorization header transmitted by the web + browser. The return value is either `None` if the header was invalid or + not given, otherwise an :class:`~werkzeug.datastructures.Authorization` + object. + + :param value: the authorization header to parse. + :return: a :class:`~werkzeug.datastructures.Authorization` object or `None`. + """ + if not value: + return None + value = _wsgi_decoding_dance(value) + try: + auth_type, auth_info = value.split(None, 1) + auth_type = auth_type.lower() + except ValueError: + return None + if auth_type == "basic": + try: + username, password = base64.b64decode(auth_info).split(b":", 1) + except Exception: + return None + try: + return ds.Authorization( + "basic", + { + "username": _to_str(username, "utf-8"), + "password": _to_str(password, "utf-8"), + }, + ) + except UnicodeDecodeError: + return None + elif auth_type == "digest": + auth_map = parse_dict_header(auth_info) + for key in "username", "realm", "nonce", "uri", "response": + if key not in auth_map: + return None + if "qop" in auth_map: + if not auth_map.get("nc") or not auth_map.get("cnonce"): + return None + return ds.Authorization("digest", auth_map) + return None + + +def parse_www_authenticate_header( + value: t.Optional[str], + on_update: t.Optional[t.Callable[["ds.WWWAuthenticate"], None]] = None, +) -> "ds.WWWAuthenticate": + """Parse an HTTP WWW-Authenticate header into a + :class:`~werkzeug.datastructures.WWWAuthenticate` object. + + :param value: a WWW-Authenticate header to parse. + :param on_update: an optional callable that is called every time a value + on the :class:`~werkzeug.datastructures.WWWAuthenticate` + object is changed. + :return: a :class:`~werkzeug.datastructures.WWWAuthenticate` object. + """ + if not value: + return ds.WWWAuthenticate(on_update=on_update) + try: + auth_type, auth_info = value.split(None, 1) + auth_type = auth_type.lower() + except (ValueError, AttributeError): + return ds.WWWAuthenticate(value.strip().lower(), on_update=on_update) + return ds.WWWAuthenticate(auth_type, parse_dict_header(auth_info), on_update) + + +def parse_if_range_header(value: t.Optional[str]) -> "ds.IfRange": + """Parses an if-range header which can be an etag or a date. Returns + a :class:`~werkzeug.datastructures.IfRange` object. + + .. versionchanged:: 2.0 + If the value represents a datetime, it is timezone-aware. + + .. versionadded:: 0.7 + """ + if not value: + return ds.IfRange() + date = parse_date(value) + if date is not None: + return ds.IfRange(date=date) + # drop weakness information + return ds.IfRange(unquote_etag(value)[0]) + + +def parse_range_header( + value: t.Optional[str], make_inclusive: bool = True +) -> t.Optional["ds.Range"]: + """Parses a range header into a :class:`~werkzeug.datastructures.Range` + object. If the header is missing or malformed `None` is returned. + `ranges` is a list of ``(start, stop)`` tuples where the ranges are + non-inclusive. + + .. versionadded:: 0.7 + """ + if not value or "=" not in value: + return None + + ranges = [] + last_end = 0 + units, rng = value.split("=", 1) + units = units.strip().lower() + + for item in rng.split(","): + item = item.strip() + if "-" not in item: + return None + if item.startswith("-"): + if last_end < 0: + return None + try: + begin = int(item) + except ValueError: + return None + end = None + last_end = -1 + elif "-" in item: + begin_str, end_str = item.split("-", 1) + begin_str = begin_str.strip() + end_str = end_str.strip() + + try: + begin = int(begin_str) + except ValueError: + return None + + if begin < last_end or last_end < 0: + return None + if end_str: + try: + end = int(end_str) + 1 + except ValueError: + return None + + if begin >= end: + return None + else: + end = None + last_end = end if end is not None else -1 + ranges.append((begin, end)) + + return ds.Range(units, ranges) + + +def parse_content_range_header( + value: t.Optional[str], + on_update: t.Optional[t.Callable[["ds.ContentRange"], None]] = None, +) -> t.Optional["ds.ContentRange"]: + """Parses a range header into a + :class:`~werkzeug.datastructures.ContentRange` object or `None` if + parsing is not possible. + + .. versionadded:: 0.7 + + :param value: a content range header to be parsed. + :param on_update: an optional callable that is called every time a value + on the :class:`~werkzeug.datastructures.ContentRange` + object is changed. + """ + if value is None: + return None + try: + units, rangedef = (value or "").strip().split(None, 1) + except ValueError: + return None + + if "/" not in rangedef: + return None + rng, length_str = rangedef.split("/", 1) + if length_str == "*": + length = None + else: + try: + length = int(length_str) + except ValueError: + return None + + if rng == "*": + if not is_byte_range_valid(None, None, length): + return None + + return ds.ContentRange(units, None, None, length, on_update=on_update) + elif "-" not in rng: + return None + + start_str, stop_str = rng.split("-", 1) + try: + start = int(start_str) + stop = int(stop_str) + 1 + except ValueError: + return None + + if is_byte_range_valid(start, stop, length): + return ds.ContentRange(units, start, stop, length, on_update=on_update) + + return None + + +def quote_etag(etag: str, weak: bool = False) -> str: + """Quote an etag. + + :param etag: the etag to quote. + :param weak: set to `True` to tag it "weak". + """ + if '"' in etag: + raise ValueError("invalid etag") + etag = f'"{etag}"' + if weak: + etag = f"W/{etag}" + return etag + + +def unquote_etag( + etag: t.Optional[str], +) -> t.Union[t.Tuple[str, bool], t.Tuple[None, None]]: + """Unquote a single etag: + + >>> unquote_etag('W/"bar"') + ('bar', True) + >>> unquote_etag('"bar"') + ('bar', False) + + :param etag: the etag identifier to unquote. + :return: a ``(etag, weak)`` tuple. + """ + if not etag: + return None, None + etag = etag.strip() + weak = False + if etag.startswith(("W/", "w/")): + weak = True + etag = etag[2:] + if etag[:1] == etag[-1:] == '"': + etag = etag[1:-1] + return etag, weak + + +def parse_etags(value: t.Optional[str]) -> "ds.ETags": + """Parse an etag header. + + :param value: the tag header to parse + :return: an :class:`~werkzeug.datastructures.ETags` object. + """ + if not value: + return ds.ETags() + strong = [] + weak = [] + end = len(value) + pos = 0 + while pos < end: + match = _etag_re.match(value, pos) + if match is None: + break + is_weak, quoted, raw = match.groups() + if raw == "*": + return ds.ETags(star_tag=True) + elif quoted: + raw = quoted + if is_weak: + weak.append(raw) + else: + strong.append(raw) + pos = match.end() + return ds.ETags(strong, weak) + + +def generate_etag(data: bytes) -> str: + """Generate an etag for some data. + + .. versionchanged:: 2.0 + Use SHA-1. MD5 may not be available in some environments. + """ + return sha1(data).hexdigest() + + +def parse_date(value: t.Optional[str]) -> t.Optional[datetime]: + """Parse an :rfc:`2822` date into a timezone-aware + :class:`datetime.datetime` object, or ``None`` if parsing fails. + + This is a wrapper for :func:`email.utils.parsedate_to_datetime`. It + returns ``None`` if parsing fails instead of raising an exception, + and always returns a timezone-aware datetime object. If the string + doesn't have timezone information, it is assumed to be UTC. + + :param value: A string with a supported date format. + + .. versionchanged:: 2.0 + Return a timezone-aware datetime object. Use + ``email.utils.parsedate_to_datetime``. + """ + if value is None: + return None + + try: + dt = email.utils.parsedate_to_datetime(value) + except (TypeError, ValueError): + return None + + if dt.tzinfo is None: + return dt.replace(tzinfo=timezone.utc) + + return dt + + +def http_date( + timestamp: t.Optional[t.Union[datetime, date, int, float, struct_time]] = None +) -> str: + """Format a datetime object or timestamp into an :rfc:`2822` date + string. + + This is a wrapper for :func:`email.utils.format_datetime`. It + assumes naive datetime objects are in UTC instead of raising an + exception. + + :param timestamp: The datetime or timestamp to format. Defaults to + the current time. + + .. versionchanged:: 2.0 + Use ``email.utils.format_datetime``. Accept ``date`` objects. + """ + if isinstance(timestamp, date): + if not isinstance(timestamp, datetime): + # Assume plain date is midnight UTC. + timestamp = datetime.combine(timestamp, time(), tzinfo=timezone.utc) + else: + # Ensure datetime is timezone-aware. + timestamp = _dt_as_utc(timestamp) + + return email.utils.format_datetime(timestamp, usegmt=True) + + if isinstance(timestamp, struct_time): + timestamp = mktime(timestamp) + + return email.utils.formatdate(timestamp, usegmt=True) + + +def parse_age(value: t.Optional[str] = None) -> t.Optional[timedelta]: + """Parses a base-10 integer count of seconds into a timedelta. + + If parsing fails, the return value is `None`. + + :param value: a string consisting of an integer represented in base-10 + :return: a :class:`datetime.timedelta` object or `None`. + """ + if not value: + return None + try: + seconds = int(value) + except ValueError: + return None + if seconds < 0: + return None + try: + return timedelta(seconds=seconds) + except OverflowError: + return None + + +def dump_age(age: t.Optional[t.Union[timedelta, int]] = None) -> t.Optional[str]: + """Formats the duration as a base-10 integer. + + :param age: should be an integer number of seconds, + a :class:`datetime.timedelta` object, or, + if the age is unknown, `None` (default). + """ + if age is None: + return None + if isinstance(age, timedelta): + age = int(age.total_seconds()) + else: + age = int(age) + + if age < 0: + raise ValueError("age cannot be negative") + + return str(age) + + +def is_resource_modified( + environ: "WSGIEnvironment", + etag: t.Optional[str] = None, + data: t.Optional[bytes] = None, + last_modified: t.Optional[t.Union[datetime, str]] = None, + ignore_if_range: bool = True, +) -> bool: + """Convenience method for conditional requests. + + :param environ: the WSGI environment of the request to be checked. + :param etag: the etag for the response for comparison. + :param data: or alternatively the data of the response to automatically + generate an etag using :func:`generate_etag`. + :param last_modified: an optional date of the last modification. + :param ignore_if_range: If `False`, `If-Range` header will be taken into + account. + :return: `True` if the resource was modified, otherwise `False`. + + .. versionchanged:: 2.0 + SHA-1 is used to generate an etag value for the data. MD5 may + not be available in some environments. + + .. versionchanged:: 1.0.0 + The check is run for methods other than ``GET`` and ``HEAD``. + """ + return _sansio_http.is_resource_modified( + http_range=environ.get("HTTP_RANGE"), + http_if_range=environ.get("HTTP_IF_RANGE"), + http_if_modified_since=environ.get("HTTP_IF_MODIFIED_SINCE"), + http_if_none_match=environ.get("HTTP_IF_NONE_MATCH"), + http_if_match=environ.get("HTTP_IF_MATCH"), + etag=etag, + data=data, + last_modified=last_modified, + ignore_if_range=ignore_if_range, + ) + + +def remove_entity_headers( + headers: t.Union["ds.Headers", t.List[t.Tuple[str, str]]], + allowed: t.Iterable[str] = ("expires", "content-location"), +) -> None: + """Remove all entity headers from a list or :class:`Headers` object. This + operation works in-place. `Expires` and `Content-Location` headers are + by default not removed. The reason for this is :rfc:`2616` section + 10.3.5 which specifies some entity headers that should be sent. + + .. versionchanged:: 0.5 + added `allowed` parameter. + + :param headers: a list or :class:`Headers` object. + :param allowed: a list of headers that should still be allowed even though + they are entity headers. + """ + allowed = {x.lower() for x in allowed} + headers[:] = [ + (key, value) + for key, value in headers + if not is_entity_header(key) or key.lower() in allowed + ] + + +def remove_hop_by_hop_headers( + headers: t.Union["ds.Headers", t.List[t.Tuple[str, str]]] +) -> None: + """Remove all HTTP/1.1 "Hop-by-Hop" headers from a list or + :class:`Headers` object. This operation works in-place. + + .. versionadded:: 0.5 + + :param headers: a list or :class:`Headers` object. + """ + headers[:] = [ + (key, value) for key, value in headers if not is_hop_by_hop_header(key) + ] + + +def is_entity_header(header: str) -> bool: + """Check if a header is an entity header. + + .. versionadded:: 0.5 + + :param header: the header to test. + :return: `True` if it's an entity header, `False` otherwise. + """ + return header.lower() in _entity_headers + + +def is_hop_by_hop_header(header: str) -> bool: + """Check if a header is an HTTP/1.1 "Hop-by-Hop" header. + + .. versionadded:: 0.5 + + :param header: the header to test. + :return: `True` if it's an HTTP/1.1 "Hop-by-Hop" header, `False` otherwise. + """ + return header.lower() in _hop_by_hop_headers + + +def parse_cookie( + header: t.Union["WSGIEnvironment", str, bytes, None], + charset: str = "utf-8", + errors: str = "replace", + cls: t.Optional[t.Type["ds.MultiDict"]] = None, +) -> "ds.MultiDict[str, str]": + """Parse a cookie from a string or WSGI environ. + + The same key can be provided multiple times, the values are stored + in-order. The default :class:`MultiDict` will have the first value + first, and all values can be retrieved with + :meth:`MultiDict.getlist`. + + :param header: The cookie header as a string, or a WSGI environ dict + with a ``HTTP_COOKIE`` key. + :param charset: The charset for the cookie values. + :param errors: The error behavior for the charset decoding. + :param cls: A dict-like class to store the parsed cookies in. + Defaults to :class:`MultiDict`. + + .. versionchanged:: 1.0.0 + Returns a :class:`MultiDict` instead of a + ``TypeConversionDict``. + + .. versionchanged:: 0.5 + Returns a :class:`TypeConversionDict` instead of a regular dict. + The ``cls`` parameter was added. + """ + if isinstance(header, dict): + cookie = header.get("HTTP_COOKIE", "") + elif header is None: + cookie = "" + else: + cookie = header + + return _sansio_http.parse_cookie( + cookie=cookie, charset=charset, errors=errors, cls=cls + ) + + +def dump_cookie( + key: str, + value: t.Union[bytes, str] = "", + max_age: t.Optional[t.Union[timedelta, int]] = None, + expires: t.Optional[t.Union[str, datetime, int, float]] = None, + path: t.Optional[str] = "/", + domain: t.Optional[str] = None, + secure: bool = False, + httponly: bool = False, + charset: str = "utf-8", + sync_expires: bool = True, + max_size: int = 4093, + samesite: t.Optional[str] = None, +) -> str: + """Create a Set-Cookie header without the ``Set-Cookie`` prefix. + + The return value is usually restricted to ascii as the vast majority + of values are properly escaped, but that is no guarantee. It's + tunneled through latin1 as required by :pep:`3333`. + + The return value is not ASCII safe if the key contains unicode + characters. This is technically against the specification but + happens in the wild. It's strongly recommended to not use + non-ASCII values for the keys. + + :param max_age: should be a number of seconds, or `None` (default) if + the cookie should last only as long as the client's + browser session. Additionally `timedelta` objects + are accepted, too. + :param expires: should be a `datetime` object or unix timestamp. + :param path: limits the cookie to a given path, per default it will + span the whole domain. + :param domain: Use this if you want to set a cross-domain cookie. For + example, ``domain=".example.com"`` will set a cookie + that is readable by the domain ``www.example.com``, + ``foo.example.com`` etc. Otherwise, a cookie will only + be readable by the domain that set it. + :param secure: The cookie will only be available via HTTPS + :param httponly: disallow JavaScript to access the cookie. This is an + extension to the cookie standard and probably not + supported by all browsers. + :param charset: the encoding for string values. + :param sync_expires: automatically set expires if max_age is defined + but expires not. + :param max_size: Warn if the final header value exceeds this size. The + default, 4093, should be safely `supported by most browsers + `_. Set to 0 to disable this check. + :param samesite: Limits the scope of the cookie such that it will + only be attached to requests if those requests are same-site. + + .. _`cookie`: http://browsercookielimits.squawky.net/ + + .. versionchanged:: 1.0.0 + The string ``'None'`` is accepted for ``samesite``. + """ + key = _to_bytes(key, charset) + value = _to_bytes(value, charset) + + if path is not None: + from .urls import iri_to_uri + + path = iri_to_uri(path, charset) + + domain = _make_cookie_domain(domain) + + if isinstance(max_age, timedelta): + max_age = int(max_age.total_seconds()) + + if expires is not None: + if not isinstance(expires, str): + expires = http_date(expires) + elif max_age is not None and sync_expires: + expires = http_date(datetime.now(tz=timezone.utc).timestamp() + max_age) + + if samesite is not None: + samesite = samesite.title() + + if samesite not in {"Strict", "Lax", "None"}: + raise ValueError("SameSite must be 'Strict', 'Lax', or 'None'.") + + buf = [key + b"=" + _cookie_quote(value)] + + # XXX: In theory all of these parameters that are not marked with `None` + # should be quoted. Because stdlib did not quote it before I did not + # want to introduce quoting there now. + for k, v, q in ( + (b"Domain", domain, True), + (b"Expires", expires, False), + (b"Max-Age", max_age, False), + (b"Secure", secure, None), + (b"HttpOnly", httponly, None), + (b"Path", path, False), + (b"SameSite", samesite, False), + ): + if q is None: + if v: + buf.append(k) + continue + + if v is None: + continue + + tmp = bytearray(k) + if not isinstance(v, (bytes, bytearray)): + v = _to_bytes(str(v), charset) + if q: + v = _cookie_quote(v) + tmp += b"=" + v + buf.append(bytes(tmp)) + + # The return value will be an incorrectly encoded latin1 header for + # consistency with the headers object. + rv = b"; ".join(buf) + rv = rv.decode("latin1") + + # Warn if the final value of the cookie is larger than the limit. If the + # cookie is too large, then it may be silently ignored by the browser, + # which can be quite hard to debug. + cookie_size = len(rv) + + if max_size and cookie_size > max_size: + value_size = len(value) + warnings.warn( + f"The {key.decode(charset)!r} cookie is too large: the value was" + f" {value_size} bytes but the" + f" header required {cookie_size - value_size} extra bytes. The final size" + f" was {cookie_size} bytes but the limit is {max_size} bytes. Browsers may" + f" silently ignore cookies larger than this.", + stacklevel=2, + ) + + return rv + + +def is_byte_range_valid( + start: t.Optional[int], stop: t.Optional[int], length: t.Optional[int] +) -> bool: + """Checks if a given byte content range is valid for the given length. + + .. versionadded:: 0.7 + """ + if (start is None) != (stop is None): + return False + elif start is None: + return length is None or length >= 0 + elif length is None: + return 0 <= start < stop # type: ignore + elif start >= stop: # type: ignore + return False + return 0 <= start < length + + +# circular dependencies +from . import datastructures as ds +from .sansio import http as _sansio_http diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/local.py b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/local.py new file mode 100644 index 0000000..9927a0a --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/local.py @@ -0,0 +1,648 @@ +import copy +import math +import operator +import typing as t +from contextvars import ContextVar +from functools import partial +from functools import update_wrapper +from operator import attrgetter + +from .wsgi import ClosingIterator + +if t.TYPE_CHECKING: + from _typeshed.wsgi import StartResponse + from _typeshed.wsgi import WSGIApplication + from _typeshed.wsgi import WSGIEnvironment + +T = t.TypeVar("T") +F = t.TypeVar("F", bound=t.Callable[..., t.Any]) + + +def release_local(local: t.Union["Local", "LocalStack"]) -> None: + """Release the data for the current context in a :class:`Local` or + :class:`LocalStack` without using a :class:`LocalManager`. + + This should not be needed for modern use cases, and may be removed + in the future. + + .. versionadded:: 0.6.1 + """ + local.__release_local__() + + +class Local: + """Create a namespace of context-local data. This wraps a + :class:`ContextVar` containing a :class:`dict` value. + + This may incur a performance penalty compared to using individual + context vars, as it has to copy data to avoid mutating the dict + between nested contexts. + + :param context_var: The :class:`~contextvars.ContextVar` to use as + storage for this local. If not given, one will be created. + Context vars not created at the global scope may interfere with + garbage collection. + + .. versionchanged:: 2.0 + Uses ``ContextVar`` instead of a custom storage implementation. + """ + + __slots__ = ("__storage",) + + def __init__( + self, context_var: t.Optional[ContextVar[t.Dict[str, t.Any]]] = None + ) -> None: + if context_var is None: + # A ContextVar not created at global scope interferes with + # Python's garbage collection. However, a local only makes + # sense defined at the global scope as well, in which case + # the GC issue doesn't seem relevant. + context_var = ContextVar(f"werkzeug.Local<{id(self)}>.storage") + + object.__setattr__(self, "_Local__storage", context_var) + + def __iter__(self) -> t.Iterator[t.Tuple[str, t.Any]]: + return iter(self.__storage.get({}).items()) + + def __call__( + self, name: str, *, unbound_message: t.Optional[str] = None + ) -> "LocalProxy": + """Create a :class:`LocalProxy` that access an attribute on this + local namespace. + + :param name: Proxy this attribute. + :param unbound_message: The error message that the proxy will + show if the attribute isn't set. + """ + return LocalProxy(self, name, unbound_message=unbound_message) + + def __release_local__(self) -> None: + self.__storage.set({}) + + def __getattr__(self, name: str) -> t.Any: + values = self.__storage.get({}) + + if name in values: + return values[name] + + raise AttributeError(name) + + def __setattr__(self, name: str, value: t.Any) -> None: + values = self.__storage.get({}).copy() + values[name] = value + self.__storage.set(values) + + def __delattr__(self, name: str) -> None: + values = self.__storage.get({}) + + if name in values: + values = values.copy() + del values[name] + self.__storage.set(values) + else: + raise AttributeError(name) + + +class LocalStack(t.Generic[T]): + """Create a stack of context-local data. This wraps a + :class:`ContextVar` containing a :class:`list` value. + + This may incur a performance penalty compared to using individual + context vars, as it has to copy data to avoid mutating the list + between nested contexts. + + :param context_var: The :class:`~contextvars.ContextVar` to use as + storage for this local. If not given, one will be created. + Context vars not created at the global scope may interfere with + garbage collection. + + .. versionchanged:: 2.0 + Uses ``ContextVar`` instead of a custom storage implementation. + + .. versionadded:: 0.6.1 + """ + + __slots__ = ("_storage",) + + def __init__(self, context_var: t.Optional[ContextVar[t.List[T]]] = None) -> None: + if context_var is None: + # A ContextVar not created at global scope interferes with + # Python's garbage collection. However, a local only makes + # sense defined at the global scope as well, in which case + # the GC issue doesn't seem relevant. + context_var = ContextVar(f"werkzeug.LocalStack<{id(self)}>.storage") + + self._storage = context_var + + def __release_local__(self) -> None: + self._storage.set([]) + + def push(self, obj: T) -> t.List[T]: + """Add a new item to the top of the stack.""" + stack = self._storage.get([]).copy() + stack.append(obj) + self._storage.set(stack) + return stack + + def pop(self) -> t.Optional[T]: + """Remove the top item from the stack and return it. If the + stack is empty, return ``None``. + """ + stack = self._storage.get([]) + + if len(stack) == 0: + return None + + rv = stack[-1] + self._storage.set(stack[:-1]) + return rv + + @property + def top(self) -> t.Optional[T]: + """The topmost item on the stack. If the stack is empty, + `None` is returned. + """ + stack = self._storage.get([]) + + if len(stack) == 0: + return None + + return stack[-1] + + def __call__( + self, name: t.Optional[str] = None, *, unbound_message: t.Optional[str] = None + ) -> "LocalProxy": + """Create a :class:`LocalProxy` that accesses the top of this + local stack. + + :param name: If given, the proxy access this attribute of the + top item, rather than the item itself. + :param unbound_message: The error message that the proxy will + show if the stack is empty. + """ + return LocalProxy(self, name, unbound_message=unbound_message) + + +class LocalManager: + """Manage releasing the data for the current context in one or more + :class:`Local` and :class:`LocalStack` objects. + + This should not be needed for modern use cases, and may be removed + in the future. + + :param locals: A local or list of locals to manage. + + .. versionchanged:: 2.0 + ``ident_func`` is deprecated and will be removed in Werkzeug + 2.1. + + .. versionchanged:: 0.7 + The ``ident_func`` parameter was added. + + .. versionchanged:: 0.6.1 + The :func:`release_local` function can be used instead of a + manager. + """ + + __slots__ = ("locals",) + + def __init__( + self, + locals: t.Optional[ + t.Union[Local, LocalStack, t.Iterable[t.Union[Local, LocalStack]]] + ] = None, + ) -> None: + if locals is None: + self.locals = [] + elif isinstance(locals, Local): + self.locals = [locals] + else: + self.locals = list(locals) # type: ignore[arg-type] + + def cleanup(self) -> None: + """Release the data in the locals for this context. Call this at + the end of each request or use :meth:`make_middleware`. + """ + for local in self.locals: + release_local(local) + + def make_middleware(self, app: "WSGIApplication") -> "WSGIApplication": + """Wrap a WSGI application so that local data is released + automatically after the response has been sent for a request. + """ + + def application( + environ: "WSGIEnvironment", start_response: "StartResponse" + ) -> t.Iterable[bytes]: + return ClosingIterator(app(environ, start_response), self.cleanup) + + return application + + def middleware(self, func: "WSGIApplication") -> "WSGIApplication": + """Like :meth:`make_middleware` but used as a decorator on the + WSGI application function. + + .. code-block:: python + + @manager.middleware + def application(environ, start_response): + ... + """ + return update_wrapper(self.make_middleware(func), func) + + def __repr__(self) -> str: + return f"<{type(self).__name__} storages: {len(self.locals)}>" + + +class _ProxyLookup: + """Descriptor that handles proxied attribute lookup for + :class:`LocalProxy`. + + :param f: The built-in function this attribute is accessed through. + Instead of looking up the special method, the function call + is redone on the object. + :param fallback: Return this function if the proxy is unbound + instead of raising a :exc:`RuntimeError`. + :param is_attr: This proxied name is an attribute, not a function. + Call the fallback immediately to get the value. + :param class_value: Value to return when accessed from the + ``LocalProxy`` class directly. Used for ``__doc__`` so building + docs still works. + """ + + __slots__ = ("bind_f", "fallback", "is_attr", "class_value", "name") + + def __init__( + self, + f: t.Optional[t.Callable] = None, + fallback: t.Optional[t.Callable] = None, + class_value: t.Optional[t.Any] = None, + is_attr: bool = False, + ) -> None: + bind_f: t.Optional[t.Callable[["LocalProxy", t.Any], t.Callable]] + + if hasattr(f, "__get__"): + # A Python function, can be turned into a bound method. + + def bind_f(instance: "LocalProxy", obj: t.Any) -> t.Callable: + return f.__get__(obj, type(obj)) # type: ignore + + elif f is not None: + # A C function, use partial to bind the first argument. + + def bind_f(instance: "LocalProxy", obj: t.Any) -> t.Callable: + return partial(f, obj) + + else: + # Use getattr, which will produce a bound method. + bind_f = None + + self.bind_f = bind_f + self.fallback = fallback + self.class_value = class_value + self.is_attr = is_attr + + def __set_name__(self, owner: "LocalProxy", name: str) -> None: + self.name = name + + def __get__(self, instance: "LocalProxy", owner: t.Optional[type] = None) -> t.Any: + if instance is None: + if self.class_value is not None: + return self.class_value + + return self + + try: + obj = instance._get_current_object() + except RuntimeError: + if self.fallback is None: + raise + + fallback = self.fallback.__get__(instance, owner) + + if self.is_attr: + # __class__ and __doc__ are attributes, not methods. + # Call the fallback to get the value. + return fallback() + + return fallback + + if self.bind_f is not None: + return self.bind_f(instance, obj) + + return getattr(obj, self.name) + + def __repr__(self) -> str: + return f"proxy {self.name}" + + def __call__(self, instance: "LocalProxy", *args: t.Any, **kwargs: t.Any) -> t.Any: + """Support calling unbound methods from the class. For example, + this happens with ``copy.copy``, which does + ``type(x).__copy__(x)``. ``type(x)`` can't be proxied, so it + returns the proxy type and descriptor. + """ + return self.__get__(instance, type(instance))(*args, **kwargs) + + +class _ProxyIOp(_ProxyLookup): + """Look up an augmented assignment method on a proxied object. The + method is wrapped to return the proxy instead of the object. + """ + + __slots__ = () + + def __init__( + self, f: t.Optional[t.Callable] = None, fallback: t.Optional[t.Callable] = None + ) -> None: + super().__init__(f, fallback) + + def bind_f(instance: "LocalProxy", obj: t.Any) -> t.Callable: + def i_op(self: t.Any, other: t.Any) -> "LocalProxy": + f(self, other) # type: ignore + return instance + + return i_op.__get__(obj, type(obj)) # type: ignore + + self.bind_f = bind_f + + +def _l_to_r_op(op: F) -> F: + """Swap the argument order to turn an l-op into an r-op.""" + + def r_op(obj: t.Any, other: t.Any) -> t.Any: + return op(other, obj) + + return t.cast(F, r_op) + + +def _identity(o: T) -> T: + return o + + +class LocalProxy(t.Generic[T]): + """A proxy to the object bound to a context-local object. All + operations on the proxy are forwarded to the bound object. If no + object is bound, a ``RuntimeError`` is raised. + + :param local: The context-local object that provides the proxied + object. + :param name: Proxy this attribute from the proxied object. + :param unbound_message: The error message to show if the + context-local object is unbound. + + Proxy a :class:`~contextvars.ContextVar` to make it easier to + access. Pass a name to proxy that attribute. + + .. code-block:: python + + _request_var = ContextVar("request") + request = LocalProxy(_request_var) + session = LocalProxy(_request_var, "session") + + Proxy an attribute on a :class:`Local` namespace by calling the + local with the attribute name: + + .. code-block:: python + + data = Local() + user = data("user") + + Proxy the top item on a :class:`LocalStack` by calling the local. + Pass a name to proxy that attribute. + + .. code-block:: + + app_stack = LocalStack() + current_app = app_stack() + g = app_stack("g") + + Pass a function to proxy the return value from that function. This + was previously used to access attributes of local objects before + that was supported directly. + + .. code-block:: python + + session = LocalProxy(lambda: request.session) + + ``__repr__`` and ``__class__`` are proxied, so ``repr(x)`` and + ``isinstance(x, cls)`` will look like the proxied object. Use + ``issubclass(type(x), LocalProxy)`` to check if an object is a + proxy. + + .. code-block:: python + + repr(user) # + isinstance(user, User) # True + issubclass(type(user), LocalProxy) # True + + .. versionchanged:: 2.2.2 + ``__wrapped__`` is set when wrapping an object, not only when + wrapping a function, to prevent doctest from failing. + + .. versionchanged:: 2.2 + Can proxy a ``ContextVar`` or ``LocalStack`` directly. + + .. versionchanged:: 2.2 + The ``name`` parameter can be used with any proxied object, not + only ``Local``. + + .. versionchanged:: 2.2 + Added the ``unbound_message`` parameter. + + .. versionchanged:: 2.0 + Updated proxied attributes and methods to reflect the current + data model. + + .. versionchanged:: 0.6.1 + The class can be instantiated with a callable. + """ + + __slots__ = ("__wrapped", "_get_current_object") + + _get_current_object: t.Callable[[], T] + """Return the current object this proxy is bound to. If the proxy is + unbound, this raises a ``RuntimeError``. + + This should be used if you need to pass the object to something that + doesn't understand the proxy. It can also be useful for performance + if you are accessing the object multiple times in a function, rather + than going through the proxy multiple times. + """ + + def __init__( + self, + local: t.Union[ContextVar[T], Local, LocalStack[T], t.Callable[[], T]], + name: t.Optional[str] = None, + *, + unbound_message: t.Optional[str] = None, + ) -> None: + if name is None: + get_name = _identity + else: + get_name = attrgetter(name) # type: ignore[assignment] + + if unbound_message is None: + unbound_message = "object is not bound" + + if isinstance(local, Local): + if name is None: + raise TypeError("'name' is required when proxying a 'Local' object.") + + def _get_current_object() -> T: + try: + return get_name(local) # type: ignore[return-value] + except AttributeError: + raise RuntimeError(unbound_message) from None + + elif isinstance(local, LocalStack): + + def _get_current_object() -> T: + obj = local.top # type: ignore[union-attr] + + if obj is None: + raise RuntimeError(unbound_message) + + return get_name(obj) + + elif isinstance(local, ContextVar): + + def _get_current_object() -> T: + try: + obj = local.get() # type: ignore[union-attr] + except LookupError: + raise RuntimeError(unbound_message) from None + + return get_name(obj) + + elif callable(local): + + def _get_current_object() -> T: + return get_name(local()) # type: ignore + + else: + raise TypeError(f"Don't know how to proxy '{type(local)}'.") + + object.__setattr__(self, "_LocalProxy__wrapped", local) + object.__setattr__(self, "_get_current_object", _get_current_object) + + __doc__ = _ProxyLookup( # type: ignore + class_value=__doc__, fallback=lambda self: type(self).__doc__, is_attr=True + ) + __wrapped__ = _ProxyLookup( + fallback=lambda self: self._LocalProxy__wrapped, is_attr=True + ) + # __del__ should only delete the proxy + __repr__ = _ProxyLookup( # type: ignore + repr, fallback=lambda self: f"<{type(self).__name__} unbound>" + ) + __str__ = _ProxyLookup(str) # type: ignore + __bytes__ = _ProxyLookup(bytes) + __format__ = _ProxyLookup() # type: ignore + __lt__ = _ProxyLookup(operator.lt) + __le__ = _ProxyLookup(operator.le) + __eq__ = _ProxyLookup(operator.eq) # type: ignore + __ne__ = _ProxyLookup(operator.ne) # type: ignore + __gt__ = _ProxyLookup(operator.gt) + __ge__ = _ProxyLookup(operator.ge) + __hash__ = _ProxyLookup(hash) # type: ignore + __bool__ = _ProxyLookup(bool, fallback=lambda self: False) + __getattr__ = _ProxyLookup(getattr) + # __getattribute__ triggered through __getattr__ + __setattr__ = _ProxyLookup(setattr) # type: ignore + __delattr__ = _ProxyLookup(delattr) # type: ignore + __dir__ = _ProxyLookup(dir, fallback=lambda self: []) # type: ignore + # __get__ (proxying descriptor not supported) + # __set__ (descriptor) + # __delete__ (descriptor) + # __set_name__ (descriptor) + # __objclass__ (descriptor) + # __slots__ used by proxy itself + # __dict__ (__getattr__) + # __weakref__ (__getattr__) + # __init_subclass__ (proxying metaclass not supported) + # __prepare__ (metaclass) + __class__ = _ProxyLookup( + fallback=lambda self: type(self), is_attr=True + ) # type: ignore + __instancecheck__ = _ProxyLookup(lambda self, other: isinstance(other, self)) + __subclasscheck__ = _ProxyLookup(lambda self, other: issubclass(other, self)) + # __class_getitem__ triggered through __getitem__ + __call__ = _ProxyLookup(lambda self, *args, **kwargs: self(*args, **kwargs)) + __len__ = _ProxyLookup(len) + __length_hint__ = _ProxyLookup(operator.length_hint) + __getitem__ = _ProxyLookup(operator.getitem) + __setitem__ = _ProxyLookup(operator.setitem) + __delitem__ = _ProxyLookup(operator.delitem) + # __missing__ triggered through __getitem__ + __iter__ = _ProxyLookup(iter) + __next__ = _ProxyLookup(next) + __reversed__ = _ProxyLookup(reversed) + __contains__ = _ProxyLookup(operator.contains) + __add__ = _ProxyLookup(operator.add) + __sub__ = _ProxyLookup(operator.sub) + __mul__ = _ProxyLookup(operator.mul) + __matmul__ = _ProxyLookup(operator.matmul) + __truediv__ = _ProxyLookup(operator.truediv) + __floordiv__ = _ProxyLookup(operator.floordiv) + __mod__ = _ProxyLookup(operator.mod) + __divmod__ = _ProxyLookup(divmod) + __pow__ = _ProxyLookup(pow) + __lshift__ = _ProxyLookup(operator.lshift) + __rshift__ = _ProxyLookup(operator.rshift) + __and__ = _ProxyLookup(operator.and_) + __xor__ = _ProxyLookup(operator.xor) + __or__ = _ProxyLookup(operator.or_) + __radd__ = _ProxyLookup(_l_to_r_op(operator.add)) + __rsub__ = _ProxyLookup(_l_to_r_op(operator.sub)) + __rmul__ = _ProxyLookup(_l_to_r_op(operator.mul)) + __rmatmul__ = _ProxyLookup(_l_to_r_op(operator.matmul)) + __rtruediv__ = _ProxyLookup(_l_to_r_op(operator.truediv)) + __rfloordiv__ = _ProxyLookup(_l_to_r_op(operator.floordiv)) + __rmod__ = _ProxyLookup(_l_to_r_op(operator.mod)) + __rdivmod__ = _ProxyLookup(_l_to_r_op(divmod)) + __rpow__ = _ProxyLookup(_l_to_r_op(pow)) + __rlshift__ = _ProxyLookup(_l_to_r_op(operator.lshift)) + __rrshift__ = _ProxyLookup(_l_to_r_op(operator.rshift)) + __rand__ = _ProxyLookup(_l_to_r_op(operator.and_)) + __rxor__ = _ProxyLookup(_l_to_r_op(operator.xor)) + __ror__ = _ProxyLookup(_l_to_r_op(operator.or_)) + __iadd__ = _ProxyIOp(operator.iadd) + __isub__ = _ProxyIOp(operator.isub) + __imul__ = _ProxyIOp(operator.imul) + __imatmul__ = _ProxyIOp(operator.imatmul) + __itruediv__ = _ProxyIOp(operator.itruediv) + __ifloordiv__ = _ProxyIOp(operator.ifloordiv) + __imod__ = _ProxyIOp(operator.imod) + __ipow__ = _ProxyIOp(operator.ipow) + __ilshift__ = _ProxyIOp(operator.ilshift) + __irshift__ = _ProxyIOp(operator.irshift) + __iand__ = _ProxyIOp(operator.iand) + __ixor__ = _ProxyIOp(operator.ixor) + __ior__ = _ProxyIOp(operator.ior) + __neg__ = _ProxyLookup(operator.neg) + __pos__ = _ProxyLookup(operator.pos) + __abs__ = _ProxyLookup(abs) + __invert__ = _ProxyLookup(operator.invert) + __complex__ = _ProxyLookup(complex) + __int__ = _ProxyLookup(int) + __float__ = _ProxyLookup(float) + __index__ = _ProxyLookup(operator.index) + __round__ = _ProxyLookup(round) + __trunc__ = _ProxyLookup(math.trunc) + __floor__ = _ProxyLookup(math.floor) + __ceil__ = _ProxyLookup(math.ceil) + __enter__ = _ProxyLookup() + __exit__ = _ProxyLookup() + __await__ = _ProxyLookup() + __aiter__ = _ProxyLookup() + __anext__ = _ProxyLookup() + __aenter__ = _ProxyLookup() + __aexit__ = _ProxyLookup() + __copy__ = _ProxyLookup(copy.copy) + __deepcopy__ = _ProxyLookup(copy.deepcopy) + # __getnewargs_ex__ (pickle through proxy not supported) + # __getnewargs__ (pickle) + # __getstate__ (pickle) + # __setstate__ (pickle) + # __reduce__ (pickle) + # __reduce_ex__ (pickle) diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/middleware/__init__.py b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/middleware/__init__.py new file mode 100644 index 0000000..6ddcf7f --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/middleware/__init__.py @@ -0,0 +1,22 @@ +""" +Middleware +========== + +A WSGI middleware is a WSGI application that wraps another application +in order to observe or change its behavior. Werkzeug provides some +middleware for common use cases. + +.. toctree:: + :maxdepth: 1 + + proxy_fix + shared_data + dispatcher + http_proxy + lint + profiler + +The :doc:`interactive debugger ` is also a middleware that can +be applied manually, although it is typically used automatically with +the :doc:`development server `. +""" diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/middleware/__pycache__/__init__.cpython-311.pyc b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/middleware/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e6318090260a46847b91b8dd7aeae8fd75bede74 GIT binary patch literal 755 zcmY*XO^XyU5ba(UmB1c72p)1>&<=yQW*ra@;z3ziVZlpJO{b`w(GIvb0Le5g)A?M z(bTD6drvpn^X_&R_m)VkDT;0G{M zmb)M+nH+cXh+&14;B61KL#|TfbL?TIT&VaZhUF(5I_`s8DT$FsXOJCGSd6XC72xSmFP)j5pbcF;uY;NIg8@`05%Qg9D^BB-t;+0 zL5)%128`Dj#X>_|iWSaH*?v-V^J9<wbT|-i!N52p|sY1 zWo9XvHU$KQ4?N@`1O_BDDJld=R)0KwRnqo{zo8o5JAg`NF-)T%P|^zAVC{D*BhMPiF6o6WW@kr#8XU3!pTNIxaH@L<1?<0f7x0 z-_VSuwXX7rN^<$+?vh)i)oGcH6~ z!9BRML0Jx*OFQzxqPA1-EZzGs(C_XdE*oHOpax%aDki}z6bB3lE;|fPRaN07Smp)+ zDWiGaHAcZ_LFgiX9SaCt3O(SuxMCAfJ-jq7HgsZvc@R;xy(Lyg#U?l`*lOt`%m~a1 zJt#DVsZh!^!&s{=ckn_%RWUT+i>JDPcdretD}hm36pwNIIJh7#T^1%?F2H3&0|w*W z8$vg+CC1L?R#sJuHg=gqDkXvS@c;#uvAX6#gtLn39 zOsH!*Rk*roKsW&l)YSo5trQEnkEdR&UFmgGp zS3IX=>Kx+o%3!E!q2#z#Cj3>v*WZ5o9lQWRr5;xcP(G;5X#u~#I6E;pF=Of#PSqbo zv6;_GerD0rnRlOZ*MU>8&@W0vS6W6(bM}S-$!9^Xms+JN7*egt^Cv*;Ac~eHD2y>8 zEz4;{GDIeElA!SiiRcp0N}EoScscfZG({bXC?r-^Nb*Sn3Sx?+iAs8i0(aBVVXAgc z!^z$!@#UVNboTkfol@CBtbSKQXvVQt44dM1jRSy21r}h{mDDd+%3J{B`(eF+b5N<; zSqd=*a0R&*G3($;gg;1m1F*~kE>QsuQWioivZf&5V+n{B1OQKzT_GY8C}^V`V5OC@ z?HnUe{Orpfw!}*Tu#7|r4MG!If`ScR!1r{Qhx)mUmW_u{6OLy}SR*3QkVzq%d0z%y znRpX@C1mC+jCvJjm*gl$Fp7sw%%6`zw}Wa@4QW7ytrl}71HlS}CTs*PqHm@7Y>WwD zF%j*o?8iAZ%bgW27=q7U{3*ZYSTrvbU(e5gC7xr`{I4sz?NzJ=%B+gWJI7woNuD?* zo^KUco-ac>t>-tWy^%MKmAqT^)*O2>ckNo98y=l-;pGvpKz@_5^+&X_n(w@-`N$XW zD7aNWrD=w3c$)U;joIiOWgXb%qU}l0nV*C1Q`A(^@R@r2vsB~sg>7>$24qhi{+`1w z@{^#HNHr!>#_xmD!90z%GL#9^k2zRGAaL+JI8iuw4;D@~@uhP7OJhqWv8_0fYw`wS zH*0bYJ!ppkR{FZX9 z(+kr}ceLXC2Os(9g_Qq6xXABWoVj0ISkmUF@6Bc7zPw7k0IHAy9O{V|KsB3U892-a zMK>g_DLnX0@zWw30X6XW3Y7b40Hdb??ks=Ufhum;x~YX{HCwkR7pDO4L1TU53ZSk8 zCI0`QF5JEehxtFD1Jo&K==GiRpP%3T{j<@7!HL7ciMrAlK3z{A^$l+Swr^YrRY{`36a*t1LjxNPsW%%VSAgm{fQ|m4I^TY0cmAON>S6!Y{nS-9 z04A|s=(4@S7s5-L=Bt`!!8e2{=w40xq@tUxousBg0f1%6^HU$WqA2L5AD=Z!o-cEc zv3}s?r-~kB`ie>Y#7Y%DoY@Ghev3kB!tX2@by*LZ9DaIoJ^S!MCAn%912C zVp{DHF literal 0 HcmV?d00001 diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/middleware/__pycache__/http_proxy.cpython-311.pyc b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/middleware/__pycache__/http_proxy.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1a8d5607cc69e2c028b54719531e3d9be3d2cd8d GIT binary patch literal 11320 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 P`T3Wu{i_%BGT!|kvJkU; literal 0 HcmV?d00001 diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/middleware/__pycache__/lint.cpython-311.pyc b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/middleware/__pycache__/lint.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6ccd1be9ab19ea80db12dc60c0635f8f3053108d GIT binary patch literal 21389 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 R0fy{ceoy;<*kzOw^M7Ymzfk}H literal 0 HcmV?d00001 diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/middleware/__pycache__/shared_data.cpython-311.pyc b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/middleware/__pycache__/shared_data.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8278db7e717a4f8c102305e455f448adeb3d31c5 GIT binary patch literal 14441 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 literal 0 HcmV?d00001 diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/middleware/dispatcher.py b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/middleware/dispatcher.py new file mode 100644 index 0000000..ace1c75 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/middleware/dispatcher.py @@ -0,0 +1,78 @@ +""" +Application Dispatcher +====================== + +This middleware creates a single WSGI application that dispatches to +multiple other WSGI applications mounted at different URL paths. + +A common example is writing a Single Page Application, where you have a +backend API and a frontend written in JavaScript that does the routing +in the browser rather than requesting different pages from the server. +The frontend is a single HTML and JS file that should be served for any +path besides "/api". + +This example dispatches to an API app under "/api", an admin app +under "/admin", and an app that serves frontend files for all other +requests:: + + app = DispatcherMiddleware(serve_frontend, { + '/api': api_app, + '/admin': admin_app, + }) + +In production, you might instead handle this at the HTTP server level, +serving files or proxying to application servers based on location. The +API and admin apps would each be deployed with a separate WSGI server, +and the static files would be served directly by the HTTP server. + +.. autoclass:: DispatcherMiddleware + +:copyright: 2007 Pallets +:license: BSD-3-Clause +""" +import typing as t + +if t.TYPE_CHECKING: + from _typeshed.wsgi import StartResponse + from _typeshed.wsgi import WSGIApplication + from _typeshed.wsgi import WSGIEnvironment + + +class DispatcherMiddleware: + """Combine multiple applications as a single WSGI application. + Requests are dispatched to an application based on the path it is + mounted under. + + :param app: The WSGI application to dispatch to if the request + doesn't match a mounted path. + :param mounts: Maps path prefixes to applications for dispatching. + """ + + def __init__( + self, + app: "WSGIApplication", + mounts: t.Optional[t.Dict[str, "WSGIApplication"]] = None, + ) -> None: + self.app = app + self.mounts = mounts or {} + + def __call__( + self, environ: "WSGIEnvironment", start_response: "StartResponse" + ) -> t.Iterable[bytes]: + script = environ.get("PATH_INFO", "") + path_info = "" + + while "/" in script: + if script in self.mounts: + app = self.mounts[script] + break + + script, last_item = script.rsplit("/", 1) + path_info = f"/{last_item}{path_info}" + else: + app = self.mounts.get(script, self.app) + + original_script_name = environ.get("SCRIPT_NAME", "") + environ["SCRIPT_NAME"] = original_script_name + script + environ["PATH_INFO"] = path_info + return app(environ, start_response) diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/middleware/http_proxy.py b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/middleware/http_proxy.py new file mode 100644 index 0000000..1cde458 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/middleware/http_proxy.py @@ -0,0 +1,230 @@ +""" +Basic HTTP Proxy +================ + +.. autoclass:: ProxyMiddleware + +:copyright: 2007 Pallets +:license: BSD-3-Clause +""" +import typing as t +from http import client + +from ..datastructures import EnvironHeaders +from ..http import is_hop_by_hop_header +from ..urls import url_parse +from ..urls import url_quote +from ..wsgi import get_input_stream + +if t.TYPE_CHECKING: + from _typeshed.wsgi import StartResponse + from _typeshed.wsgi import WSGIApplication + from _typeshed.wsgi import WSGIEnvironment + + +class ProxyMiddleware: + """Proxy requests under a path to an external server, routing other + requests to the app. + + This middleware can only proxy HTTP requests, as HTTP is the only + protocol handled by the WSGI server. Other protocols, such as + WebSocket requests, cannot be proxied at this layer. This should + only be used for development, in production a real proxy server + should be used. + + The middleware takes a dict mapping a path prefix to a dict + describing the host to be proxied to:: + + app = ProxyMiddleware(app, { + "/static/": { + "target": "http://127.0.0.1:5001/", + } + }) + + Each host has the following options: + + ``target``: + The target URL to dispatch to. This is required. + ``remove_prefix``: + Whether to remove the prefix from the URL before dispatching it + to the target. The default is ``False``. + ``host``: + ``""`` (default): + The host header is automatically rewritten to the URL of the + target. + ``None``: + The host header is unmodified from the client request. + Any other value: + The host header is overwritten with the value. + ``headers``: + A dictionary of headers to be sent with the request to the + target. The default is ``{}``. + ``ssl_context``: + A :class:`ssl.SSLContext` defining how to verify requests if the + target is HTTPS. The default is ``None``. + + In the example above, everything under ``"/static/"`` is proxied to + the server on port 5001. The host header is rewritten to the target, + and the ``"/static/"`` prefix is removed from the URLs. + + :param app: The WSGI application to wrap. + :param targets: Proxy target configurations. See description above. + :param chunk_size: Size of chunks to read from input stream and + write to target. + :param timeout: Seconds before an operation to a target fails. + + .. versionadded:: 0.14 + """ + + def __init__( + self, + app: "WSGIApplication", + targets: t.Mapping[str, t.Dict[str, t.Any]], + chunk_size: int = 2 << 13, + timeout: int = 10, + ) -> None: + def _set_defaults(opts: t.Dict[str, t.Any]) -> t.Dict[str, t.Any]: + opts.setdefault("remove_prefix", False) + opts.setdefault("host", "") + opts.setdefault("headers", {}) + opts.setdefault("ssl_context", None) + return opts + + self.app = app + self.targets = { + f"/{k.strip('/')}/": _set_defaults(v) for k, v in targets.items() + } + self.chunk_size = chunk_size + self.timeout = timeout + + def proxy_to( + self, opts: t.Dict[str, t.Any], path: str, prefix: str + ) -> "WSGIApplication": + target = url_parse(opts["target"]) + host = t.cast(str, target.ascii_host) + + def application( + environ: "WSGIEnvironment", start_response: "StartResponse" + ) -> t.Iterable[bytes]: + headers = list(EnvironHeaders(environ).items()) + headers[:] = [ + (k, v) + for k, v in headers + if not is_hop_by_hop_header(k) + and k.lower() not in ("content-length", "host") + ] + headers.append(("Connection", "close")) + + if opts["host"] == "": + headers.append(("Host", host)) + elif opts["host"] is None: + headers.append(("Host", environ["HTTP_HOST"])) + else: + headers.append(("Host", opts["host"])) + + headers.extend(opts["headers"].items()) + remote_path = path + + if opts["remove_prefix"]: + remote_path = remote_path[len(prefix) :].lstrip("/") + remote_path = f"{target.path.rstrip('/')}/{remote_path}" + + content_length = environ.get("CONTENT_LENGTH") + chunked = False + + if content_length not in ("", None): + headers.append(("Content-Length", content_length)) # type: ignore + elif content_length is not None: + headers.append(("Transfer-Encoding", "chunked")) + chunked = True + + try: + if target.scheme == "http": + con = client.HTTPConnection( + host, target.port or 80, timeout=self.timeout + ) + elif target.scheme == "https": + con = client.HTTPSConnection( + host, + target.port or 443, + timeout=self.timeout, + context=opts["ssl_context"], + ) + else: + raise RuntimeError( + "Target scheme must be 'http' or 'https', got" + f" {target.scheme!r}." + ) + + con.connect() + remote_url = url_quote(remote_path) + querystring = environ["QUERY_STRING"] + + if querystring: + remote_url = f"{remote_url}?{querystring}" + + con.putrequest(environ["REQUEST_METHOD"], remote_url, skip_host=True) + + for k, v in headers: + if k.lower() == "connection": + v = "close" + + con.putheader(k, v) + + con.endheaders() + stream = get_input_stream(environ) + + while True: + data = stream.read(self.chunk_size) + + if not data: + break + + if chunked: + con.send(b"%x\r\n%s\r\n" % (len(data), data)) + else: + con.send(data) + + resp = con.getresponse() + except OSError: + from ..exceptions import BadGateway + + return BadGateway()(environ, start_response) + + start_response( + f"{resp.status} {resp.reason}", + [ + (k.title(), v) + for k, v in resp.getheaders() + if not is_hop_by_hop_header(k) + ], + ) + + def read() -> t.Iterator[bytes]: + while True: + try: + data = resp.read(self.chunk_size) + except OSError: + break + + if not data: + break + + yield data + + return read() + + return application + + def __call__( + self, environ: "WSGIEnvironment", start_response: "StartResponse" + ) -> t.Iterable[bytes]: + path = environ["PATH_INFO"] + app = self.app + + for prefix, opts in self.targets.items(): + if path.startswith(prefix): + app = self.proxy_to(opts, path, prefix) + break + + return app(environ, start_response) diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/middleware/lint.py b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/middleware/lint.py new file mode 100644 index 0000000..fcf3b41 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/middleware/lint.py @@ -0,0 +1,420 @@ +""" +WSGI Protocol Linter +==================== + +This module provides a middleware that performs sanity checks on the +behavior of the WSGI server and application. It checks that the +:pep:`3333` WSGI spec is properly implemented. It also warns on some +common HTTP errors such as non-empty responses for 304 status codes. + +.. autoclass:: LintMiddleware + +:copyright: 2007 Pallets +:license: BSD-3-Clause +""" +import typing as t +from types import TracebackType +from urllib.parse import urlparse +from warnings import warn + +from ..datastructures import Headers +from ..http import is_entity_header +from ..wsgi import FileWrapper + +if t.TYPE_CHECKING: + from _typeshed.wsgi import StartResponse + from _typeshed.wsgi import WSGIApplication + from _typeshed.wsgi import WSGIEnvironment + + +class WSGIWarning(Warning): + """Warning class for WSGI warnings.""" + + +class HTTPWarning(Warning): + """Warning class for HTTP warnings.""" + + +def check_type(context: str, obj: object, need: t.Type = str) -> None: + if type(obj) is not need: + warn( + f"{context!r} requires {need.__name__!r}, got {type(obj).__name__!r}.", + WSGIWarning, + stacklevel=3, + ) + + +class InputStream: + def __init__(self, stream: t.IO[bytes]) -> None: + self._stream = stream + + def read(self, *args: t.Any) -> bytes: + if len(args) == 0: + warn( + "WSGI does not guarantee an EOF marker on the input stream, thus making" + " calls to 'wsgi.input.read()' unsafe. Conforming servers may never" + " return from this call.", + WSGIWarning, + stacklevel=2, + ) + elif len(args) != 1: + warn( + "Too many parameters passed to 'wsgi.input.read()'.", + WSGIWarning, + stacklevel=2, + ) + return self._stream.read(*args) + + def readline(self, *args: t.Any) -> bytes: + if len(args) == 0: + warn( + "Calls to 'wsgi.input.readline()' without arguments are unsafe. Use" + " 'wsgi.input.read()' instead.", + WSGIWarning, + stacklevel=2, + ) + elif len(args) == 1: + warn( + "'wsgi.input.readline()' was called with a size hint. WSGI does not" + " support this, although it's available on all major servers.", + WSGIWarning, + stacklevel=2, + ) + else: + raise TypeError("Too many arguments passed to 'wsgi.input.readline()'.") + return self._stream.readline(*args) + + def __iter__(self) -> t.Iterator[bytes]: + try: + return iter(self._stream) + except TypeError: + warn("'wsgi.input' is not iterable.", WSGIWarning, stacklevel=2) + return iter(()) + + def close(self) -> None: + warn("The application closed the input stream!", WSGIWarning, stacklevel=2) + self._stream.close() + + +class ErrorStream: + def __init__(self, stream: t.IO[str]) -> None: + self._stream = stream + + def write(self, s: str) -> None: + check_type("wsgi.error.write()", s, str) + self._stream.write(s) + + def flush(self) -> None: + self._stream.flush() + + def writelines(self, seq: t.Iterable[str]) -> None: + for line in seq: + self.write(line) + + def close(self) -> None: + warn("The application closed the error stream!", WSGIWarning, stacklevel=2) + self._stream.close() + + +class GuardedWrite: + def __init__(self, write: t.Callable[[bytes], object], chunks: t.List[int]) -> None: + self._write = write + self._chunks = chunks + + def __call__(self, s: bytes) -> None: + check_type("write()", s, bytes) + self._write(s) + self._chunks.append(len(s)) + + +class GuardedIterator: + def __init__( + self, + iterator: t.Iterable[bytes], + headers_set: t.Tuple[int, Headers], + chunks: t.List[int], + ) -> None: + self._iterator = iterator + self._next = iter(iterator).__next__ + self.closed = False + self.headers_set = headers_set + self.chunks = chunks + + def __iter__(self) -> "GuardedIterator": + return self + + def __next__(self) -> bytes: + if self.closed: + warn("Iterated over closed 'app_iter'.", WSGIWarning, stacklevel=2) + + rv = self._next() + + if not self.headers_set: + warn( + "The application returned before it started the response.", + WSGIWarning, + stacklevel=2, + ) + + check_type("application iterator items", rv, bytes) + self.chunks.append(len(rv)) + return rv + + def close(self) -> None: + self.closed = True + + if hasattr(self._iterator, "close"): + self._iterator.close() + + if self.headers_set: + status_code, headers = self.headers_set + bytes_sent = sum(self.chunks) + content_length = headers.get("content-length", type=int) + + if status_code == 304: + for key, _value in headers: + key = key.lower() + if key not in ("expires", "content-location") and is_entity_header( + key + ): + warn( + f"Entity header {key!r} found in 304 response.", HTTPWarning + ) + if bytes_sent: + warn("304 responses must not have a body.", HTTPWarning) + elif 100 <= status_code < 200 or status_code == 204: + if content_length != 0: + warn( + f"{status_code} responses must have an empty content length.", + HTTPWarning, + ) + if bytes_sent: + warn(f"{status_code} responses must not have a body.", HTTPWarning) + elif content_length is not None and content_length != bytes_sent: + warn( + "Content-Length and the number of bytes sent to the" + " client do not match.", + WSGIWarning, + ) + + def __del__(self) -> None: + if not self.closed: + try: + warn( + "Iterator was garbage collected before it was closed.", WSGIWarning + ) + except Exception: + pass + + +class LintMiddleware: + """Warns about common errors in the WSGI and HTTP behavior of the + server and wrapped application. Some of the issues it checks are: + + - invalid status codes + - non-bytes sent to the WSGI server + - strings returned from the WSGI application + - non-empty conditional responses + - unquoted etags + - relative URLs in the Location header + - unsafe calls to wsgi.input + - unclosed iterators + + Error information is emitted using the :mod:`warnings` module. + + :param app: The WSGI application to wrap. + + .. code-block:: python + + from werkzeug.middleware.lint import LintMiddleware + app = LintMiddleware(app) + """ + + def __init__(self, app: "WSGIApplication") -> None: + self.app = app + + def check_environ(self, environ: "WSGIEnvironment") -> None: + if type(environ) is not dict: + warn( + "WSGI environment is not a standard Python dict.", + WSGIWarning, + stacklevel=4, + ) + for key in ( + "REQUEST_METHOD", + "SERVER_NAME", + "SERVER_PORT", + "wsgi.version", + "wsgi.input", + "wsgi.errors", + "wsgi.multithread", + "wsgi.multiprocess", + "wsgi.run_once", + ): + if key not in environ: + warn( + f"Required environment key {key!r} not found", + WSGIWarning, + stacklevel=3, + ) + if environ["wsgi.version"] != (1, 0): + warn("Environ is not a WSGI 1.0 environ.", WSGIWarning, stacklevel=3) + + script_name = environ.get("SCRIPT_NAME", "") + path_info = environ.get("PATH_INFO", "") + + if script_name and script_name[0] != "/": + warn( + f"'SCRIPT_NAME' does not start with a slash: {script_name!r}", + WSGIWarning, + stacklevel=3, + ) + + if path_info and path_info[0] != "/": + warn( + f"'PATH_INFO' does not start with a slash: {path_info!r}", + WSGIWarning, + stacklevel=3, + ) + + def check_start_response( + self, + status: str, + headers: t.List[t.Tuple[str, str]], + exc_info: t.Optional[ + t.Tuple[t.Type[BaseException], BaseException, TracebackType] + ], + ) -> t.Tuple[int, Headers]: + check_type("status", status, str) + status_code_str = status.split(None, 1)[0] + + if len(status_code_str) != 3 or not status_code_str.isdecimal(): + warn("Status code must be three digits.", WSGIWarning, stacklevel=3) + + if len(status) < 4 or status[3] != " ": + warn( + f"Invalid value for status {status!r}. Valid status strings are three" + " digits, a space and a status explanation.", + WSGIWarning, + stacklevel=3, + ) + + status_code = int(status_code_str) + + if status_code < 100: + warn("Status code < 100 detected.", WSGIWarning, stacklevel=3) + + if type(headers) is not list: + warn("Header list is not a list.", WSGIWarning, stacklevel=3) + + for item in headers: + if type(item) is not tuple or len(item) != 2: + warn("Header items must be 2-item tuples.", WSGIWarning, stacklevel=3) + name, value = item + if type(name) is not str or type(value) is not str: + warn( + "Header keys and values must be strings.", WSGIWarning, stacklevel=3 + ) + if name.lower() == "status": + warn( + "The status header is not supported due to" + " conflicts with the CGI spec.", + WSGIWarning, + stacklevel=3, + ) + + if exc_info is not None and not isinstance(exc_info, tuple): + warn("Invalid value for exc_info.", WSGIWarning, stacklevel=3) + + headers = Headers(headers) + self.check_headers(headers) + + return status_code, headers + + def check_headers(self, headers: Headers) -> None: + etag = headers.get("etag") + + if etag is not None: + if etag.startswith(("W/", "w/")): + if etag.startswith("w/"): + warn( + "Weak etag indicator should be upper case.", + HTTPWarning, + stacklevel=4, + ) + + etag = etag[2:] + + if not (etag[:1] == etag[-1:] == '"'): + warn("Unquoted etag emitted.", HTTPWarning, stacklevel=4) + + location = headers.get("location") + + if location is not None: + if not urlparse(location).netloc: + warn( + "Absolute URLs required for location header.", + HTTPWarning, + stacklevel=4, + ) + + def check_iterator(self, app_iter: t.Iterable[bytes]) -> None: + if isinstance(app_iter, bytes): + warn( + "The application returned a bytestring. The response will send one" + " character at a time to the client, which will kill performance." + " Return a list or iterable instead.", + WSGIWarning, + stacklevel=3, + ) + + def __call__(self, *args: t.Any, **kwargs: t.Any) -> t.Iterable[bytes]: + if len(args) != 2: + warn("A WSGI app takes two arguments.", WSGIWarning, stacklevel=2) + + if kwargs: + warn( + "A WSGI app does not take keyword arguments.", WSGIWarning, stacklevel=2 + ) + + environ: "WSGIEnvironment" = args[0] + start_response: "StartResponse" = args[1] + + self.check_environ(environ) + environ["wsgi.input"] = InputStream(environ["wsgi.input"]) + environ["wsgi.errors"] = ErrorStream(environ["wsgi.errors"]) + + # Hook our own file wrapper in so that applications will always + # iterate to the end and we can check the content length. + environ["wsgi.file_wrapper"] = FileWrapper + + headers_set: t.List[t.Any] = [] + chunks: t.List[int] = [] + + def checking_start_response( + *args: t.Any, **kwargs: t.Any + ) -> t.Callable[[bytes], None]: + if len(args) not in {2, 3}: + warn( + f"Invalid number of arguments: {len(args)}, expected 2 or 3.", + WSGIWarning, + stacklevel=2, + ) + + if kwargs: + warn("'start_response' does not take keyword arguments.", WSGIWarning) + + status: str = args[0] + headers: t.List[t.Tuple[str, str]] = args[1] + exc_info: t.Optional[ + t.Tuple[t.Type[BaseException], BaseException, TracebackType] + ] = (args[2] if len(args) == 3 else None) + + headers_set[:] = self.check_start_response(status, headers, exc_info) + return GuardedWrite(start_response(status, headers, exc_info), chunks) + + app_iter = self.app(environ, t.cast("StartResponse", checking_start_response)) + self.check_iterator(app_iter) + return GuardedIterator( + app_iter, t.cast(t.Tuple[int, Headers], headers_set), chunks + ) diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/middleware/profiler.py b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/middleware/profiler.py new file mode 100644 index 0000000..f91e33b --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/middleware/profiler.py @@ -0,0 +1,139 @@ +""" +Application Profiler +==================== + +This module provides a middleware that profiles each request with the +:mod:`cProfile` module. This can help identify bottlenecks in your code +that may be slowing down your application. + +.. autoclass:: ProfilerMiddleware + +:copyright: 2007 Pallets +:license: BSD-3-Clause +""" +import os.path +import sys +import time +import typing as t +from pstats import Stats + +try: + from cProfile import Profile +except ImportError: + from profile import Profile # type: ignore + +if t.TYPE_CHECKING: + from _typeshed.wsgi import StartResponse + from _typeshed.wsgi import WSGIApplication + from _typeshed.wsgi import WSGIEnvironment + + +class ProfilerMiddleware: + """Wrap a WSGI application and profile the execution of each + request. Responses are buffered so that timings are more exact. + + If ``stream`` is given, :class:`pstats.Stats` are written to it + after each request. If ``profile_dir`` is given, :mod:`cProfile` + data files are saved to that directory, one file per request. + + The filename can be customized by passing ``filename_format``. If + it is a string, it will be formatted using :meth:`str.format` with + the following fields available: + + - ``{method}`` - The request method; GET, POST, etc. + - ``{path}`` - The request path or 'root' should one not exist. + - ``{elapsed}`` - The elapsed time of the request. + - ``{time}`` - The time of the request. + + If it is a callable, it will be called with the WSGI ``environ`` + dict and should return a filename. + + :param app: The WSGI application to wrap. + :param stream: Write stats to this stream. Disable with ``None``. + :param sort_by: A tuple of columns to sort stats by. See + :meth:`pstats.Stats.sort_stats`. + :param restrictions: A tuple of restrictions to filter stats by. See + :meth:`pstats.Stats.print_stats`. + :param profile_dir: Save profile data files to this directory. + :param filename_format: Format string for profile data file names, + or a callable returning a name. See explanation above. + + .. code-block:: python + + from werkzeug.middleware.profiler import ProfilerMiddleware + app = ProfilerMiddleware(app) + + .. versionchanged:: 0.15 + Stats are written even if ``profile_dir`` is given, and can be + disable by passing ``stream=None``. + + .. versionadded:: 0.15 + Added ``filename_format``. + + .. versionadded:: 0.9 + Added ``restrictions`` and ``profile_dir``. + """ + + def __init__( + self, + app: "WSGIApplication", + stream: t.IO[str] = sys.stdout, + sort_by: t.Iterable[str] = ("time", "calls"), + restrictions: t.Iterable[t.Union[str, int, float]] = (), + profile_dir: t.Optional[str] = None, + filename_format: str = "{method}.{path}.{elapsed:.0f}ms.{time:.0f}.prof", + ) -> None: + self._app = app + self._stream = stream + self._sort_by = sort_by + self._restrictions = restrictions + self._profile_dir = profile_dir + self._filename_format = filename_format + + def __call__( + self, environ: "WSGIEnvironment", start_response: "StartResponse" + ) -> t.Iterable[bytes]: + response_body: t.List[bytes] = [] + + def catching_start_response(status, headers, exc_info=None): # type: ignore + start_response(status, headers, exc_info) + return response_body.append + + def runapp() -> None: + app_iter = self._app( + environ, t.cast("StartResponse", catching_start_response) + ) + response_body.extend(app_iter) + + if hasattr(app_iter, "close"): + app_iter.close() + + profile = Profile() + start = time.time() + profile.runcall(runapp) + body = b"".join(response_body) + elapsed = time.time() - start + + if self._profile_dir is not None: + if callable(self._filename_format): + filename = self._filename_format(environ) + else: + filename = self._filename_format.format( + method=environ["REQUEST_METHOD"], + path=environ["PATH_INFO"].strip("/").replace("/", ".") or "root", + elapsed=elapsed * 1000.0, + time=time.time(), + ) + filename = os.path.join(self._profile_dir, filename) + profile.dump_stats(filename) + + if self._stream is not None: + stats = Stats(profile, stream=self._stream) + stats.sort_stats(*self._sort_by) + print("-" * 80, file=self._stream) + path_info = environ.get("PATH_INFO", "") + print(f"PATH: {path_info!r}", file=self._stream) + stats.print_stats(*self._restrictions) + print(f"{'-' * 80}\n", file=self._stream) + + return [body] diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/middleware/proxy_fix.py b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/middleware/proxy_fix.py new file mode 100644 index 0000000..4cef7cc --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/middleware/proxy_fix.py @@ -0,0 +1,187 @@ +""" +X-Forwarded-For Proxy Fix +========================= + +This module provides a middleware that adjusts the WSGI environ based on +``X-Forwarded-`` headers that proxies in front of an application may +set. + +When an application is running behind a proxy server, WSGI may see the +request as coming from that server rather than the real client. Proxies +set various headers to track where the request actually came from. + +This middleware should only be used if the application is actually +behind such a proxy, and should be configured with the number of proxies +that are chained in front of it. Not all proxies set all the headers. +Since incoming headers can be faked, you must set how many proxies are +setting each header so the middleware knows what to trust. + +.. autoclass:: ProxyFix + +:copyright: 2007 Pallets +:license: BSD-3-Clause +""" +import typing as t + +from ..http import parse_list_header + +if t.TYPE_CHECKING: + from _typeshed.wsgi import StartResponse + from _typeshed.wsgi import WSGIApplication + from _typeshed.wsgi import WSGIEnvironment + + +class ProxyFix: + """Adjust the WSGI environ based on ``X-Forwarded-`` that proxies in + front of the application may set. + + - ``X-Forwarded-For`` sets ``REMOTE_ADDR``. + - ``X-Forwarded-Proto`` sets ``wsgi.url_scheme``. + - ``X-Forwarded-Host`` sets ``HTTP_HOST``, ``SERVER_NAME``, and + ``SERVER_PORT``. + - ``X-Forwarded-Port`` sets ``HTTP_HOST`` and ``SERVER_PORT``. + - ``X-Forwarded-Prefix`` sets ``SCRIPT_NAME``. + + You must tell the middleware how many proxies set each header so it + knows what values to trust. It is a security issue to trust values + that came from the client rather than a proxy. + + The original values of the headers are stored in the WSGI + environ as ``werkzeug.proxy_fix.orig``, a dict. + + :param app: The WSGI application to wrap. + :param x_for: Number of values to trust for ``X-Forwarded-For``. + :param x_proto: Number of values to trust for ``X-Forwarded-Proto``. + :param x_host: Number of values to trust for ``X-Forwarded-Host``. + :param x_port: Number of values to trust for ``X-Forwarded-Port``. + :param x_prefix: Number of values to trust for + ``X-Forwarded-Prefix``. + + .. code-block:: python + + from werkzeug.middleware.proxy_fix import ProxyFix + # App is behind one proxy that sets the -For and -Host headers. + app = ProxyFix(app, x_for=1, x_host=1) + + .. versionchanged:: 1.0 + Deprecated code has been removed: + + * The ``num_proxies`` argument and attribute. + * The ``get_remote_addr`` method. + * The environ keys ``orig_remote_addr``, + ``orig_wsgi_url_scheme``, and ``orig_http_host``. + + .. versionchanged:: 0.15 + All headers support multiple values. The ``num_proxies`` + argument is deprecated. Each header is configured with a + separate number of trusted proxies. + + .. versionchanged:: 0.15 + Original WSGI environ values are stored in the + ``werkzeug.proxy_fix.orig`` dict. ``orig_remote_addr``, + ``orig_wsgi_url_scheme``, and ``orig_http_host`` are deprecated + and will be removed in 1.0. + + .. versionchanged:: 0.15 + Support ``X-Forwarded-Port`` and ``X-Forwarded-Prefix``. + + .. versionchanged:: 0.15 + ``X-Forwarded-Host`` and ``X-Forwarded-Port`` modify + ``SERVER_NAME`` and ``SERVER_PORT``. + """ + + def __init__( + self, + app: "WSGIApplication", + x_for: int = 1, + x_proto: int = 1, + x_host: int = 0, + x_port: int = 0, + x_prefix: int = 0, + ) -> None: + self.app = app + self.x_for = x_for + self.x_proto = x_proto + self.x_host = x_host + self.x_port = x_port + self.x_prefix = x_prefix + + def _get_real_value(self, trusted: int, value: t.Optional[str]) -> t.Optional[str]: + """Get the real value from a list header based on the configured + number of trusted proxies. + + :param trusted: Number of values to trust in the header. + :param value: Comma separated list header value to parse. + :return: The real value, or ``None`` if there are fewer values + than the number of trusted proxies. + + .. versionchanged:: 1.0 + Renamed from ``_get_trusted_comma``. + + .. versionadded:: 0.15 + """ + if not (trusted and value): + return None + values = parse_list_header(value) + if len(values) >= trusted: + return values[-trusted] + return None + + def __call__( + self, environ: "WSGIEnvironment", start_response: "StartResponse" + ) -> t.Iterable[bytes]: + """Modify the WSGI environ based on the various ``Forwarded`` + headers before calling the wrapped application. Store the + original environ values in ``werkzeug.proxy_fix.orig_{key}``. + """ + environ_get = environ.get + orig_remote_addr = environ_get("REMOTE_ADDR") + orig_wsgi_url_scheme = environ_get("wsgi.url_scheme") + orig_http_host = environ_get("HTTP_HOST") + environ.update( + { + "werkzeug.proxy_fix.orig": { + "REMOTE_ADDR": orig_remote_addr, + "wsgi.url_scheme": orig_wsgi_url_scheme, + "HTTP_HOST": orig_http_host, + "SERVER_NAME": environ_get("SERVER_NAME"), + "SERVER_PORT": environ_get("SERVER_PORT"), + "SCRIPT_NAME": environ_get("SCRIPT_NAME"), + } + } + ) + + x_for = self._get_real_value(self.x_for, environ_get("HTTP_X_FORWARDED_FOR")) + if x_for: + environ["REMOTE_ADDR"] = x_for + + x_proto = self._get_real_value( + self.x_proto, environ_get("HTTP_X_FORWARDED_PROTO") + ) + if x_proto: + environ["wsgi.url_scheme"] = x_proto + + x_host = self._get_real_value(self.x_host, environ_get("HTTP_X_FORWARDED_HOST")) + if x_host: + environ["HTTP_HOST"] = environ["SERVER_NAME"] = x_host + # "]" to check for IPv6 address without port + if ":" in x_host and not x_host.endswith("]"): + environ["SERVER_NAME"], environ["SERVER_PORT"] = x_host.rsplit(":", 1) + + x_port = self._get_real_value(self.x_port, environ_get("HTTP_X_FORWARDED_PORT")) + if x_port: + host = environ.get("HTTP_HOST") + if host: + # "]" to check for IPv6 address without port + if ":" in host and not host.endswith("]"): + host = host.rsplit(":", 1)[0] + environ["HTTP_HOST"] = f"{host}:{x_port}" + environ["SERVER_PORT"] = x_port + + x_prefix = self._get_real_value( + self.x_prefix, environ_get("HTTP_X_FORWARDED_PREFIX") + ) + if x_prefix: + environ["SCRIPT_NAME"] = x_prefix + + return self.app(environ, start_response) diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/middleware/shared_data.py b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/middleware/shared_data.py new file mode 100644 index 0000000..2ec396c --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/middleware/shared_data.py @@ -0,0 +1,280 @@ +""" +Serve Shared Static Files +========================= + +.. autoclass:: SharedDataMiddleware + :members: is_allowed + +:copyright: 2007 Pallets +:license: BSD-3-Clause +""" +import mimetypes +import os +import pkgutil +import posixpath +import typing as t +from datetime import datetime +from datetime import timezone +from io import BytesIO +from time import time +from zlib import adler32 + +from ..http import http_date +from ..http import is_resource_modified +from ..security import safe_join +from ..utils import get_content_type +from ..wsgi import get_path_info +from ..wsgi import wrap_file + +_TOpener = t.Callable[[], t.Tuple[t.IO[bytes], datetime, int]] +_TLoader = t.Callable[[t.Optional[str]], t.Tuple[t.Optional[str], t.Optional[_TOpener]]] + +if t.TYPE_CHECKING: + from _typeshed.wsgi import StartResponse + from _typeshed.wsgi import WSGIApplication + from _typeshed.wsgi import WSGIEnvironment + + +class SharedDataMiddleware: + + """A WSGI middleware which provides static content for development + environments or simple server setups. Its usage is quite simple:: + + import os + from werkzeug.middleware.shared_data import SharedDataMiddleware + + app = SharedDataMiddleware(app, { + '/shared': os.path.join(os.path.dirname(__file__), 'shared') + }) + + The contents of the folder ``./shared`` will now be available on + ``http://example.com/shared/``. This is pretty useful during development + because a standalone media server is not required. Files can also be + mounted on the root folder and still continue to use the application because + the shared data middleware forwards all unhandled requests to the + application, even if the requests are below one of the shared folders. + + If `pkg_resources` is available you can also tell the middleware to serve + files from package data:: + + app = SharedDataMiddleware(app, { + '/static': ('myapplication', 'static') + }) + + This will then serve the ``static`` folder in the `myapplication` + Python package. + + The optional `disallow` parameter can be a list of :func:`~fnmatch.fnmatch` + rules for files that are not accessible from the web. If `cache` is set to + `False` no caching headers are sent. + + Currently the middleware does not support non-ASCII filenames. If the + encoding on the file system happens to match the encoding of the URI it may + work but this could also be by accident. We strongly suggest using ASCII + only file names for static files. + + The middleware will guess the mimetype using the Python `mimetype` + module. If it's unable to figure out the charset it will fall back + to `fallback_mimetype`. + + :param app: the application to wrap. If you don't want to wrap an + application you can pass it :exc:`NotFound`. + :param exports: a list or dict of exported files and folders. + :param disallow: a list of :func:`~fnmatch.fnmatch` rules. + :param cache: enable or disable caching headers. + :param cache_timeout: the cache timeout in seconds for the headers. + :param fallback_mimetype: The fallback mimetype for unknown files. + + .. versionchanged:: 1.0 + The default ``fallback_mimetype`` is + ``application/octet-stream``. If a filename looks like a text + mimetype, the ``utf-8`` charset is added to it. + + .. versionadded:: 0.6 + Added ``fallback_mimetype``. + + .. versionchanged:: 0.5 + Added ``cache_timeout``. + """ + + def __init__( + self, + app: "WSGIApplication", + exports: t.Union[ + t.Dict[str, t.Union[str, t.Tuple[str, str]]], + t.Iterable[t.Tuple[str, t.Union[str, t.Tuple[str, str]]]], + ], + disallow: None = None, + cache: bool = True, + cache_timeout: int = 60 * 60 * 12, + fallback_mimetype: str = "application/octet-stream", + ) -> None: + self.app = app + self.exports: t.List[t.Tuple[str, _TLoader]] = [] + self.cache = cache + self.cache_timeout = cache_timeout + + if isinstance(exports, dict): + exports = exports.items() + + for key, value in exports: + if isinstance(value, tuple): + loader = self.get_package_loader(*value) + elif isinstance(value, str): + if os.path.isfile(value): + loader = self.get_file_loader(value) + else: + loader = self.get_directory_loader(value) + else: + raise TypeError(f"unknown def {value!r}") + + self.exports.append((key, loader)) + + if disallow is not None: + from fnmatch import fnmatch + + self.is_allowed = lambda x: not fnmatch(x, disallow) + + self.fallback_mimetype = fallback_mimetype + + def is_allowed(self, filename: str) -> bool: + """Subclasses can override this method to disallow the access to + certain files. However by providing `disallow` in the constructor + this method is overwritten. + """ + return True + + def _opener(self, filename: str) -> _TOpener: + return lambda: ( + open(filename, "rb"), + datetime.fromtimestamp(os.path.getmtime(filename), tz=timezone.utc), + int(os.path.getsize(filename)), + ) + + def get_file_loader(self, filename: str) -> _TLoader: + return lambda x: (os.path.basename(filename), self._opener(filename)) + + def get_package_loader(self, package: str, package_path: str) -> _TLoader: + load_time = datetime.now(timezone.utc) + provider = pkgutil.get_loader(package) + reader = provider.get_resource_reader(package) # type: ignore + + def loader( + path: t.Optional[str], + ) -> t.Tuple[t.Optional[str], t.Optional[_TOpener]]: + if path is None: + return None, None + + path = safe_join(package_path, path) + + if path is None: + return None, None + + basename = posixpath.basename(path) + + try: + resource = reader.open_resource(path) + except OSError: + return None, None + + if isinstance(resource, BytesIO): + return ( + basename, + lambda: (resource, load_time, len(resource.getvalue())), + ) + + return ( + basename, + lambda: ( + resource, + datetime.fromtimestamp( + os.path.getmtime(resource.name), tz=timezone.utc + ), + os.path.getsize(resource.name), + ), + ) + + return loader + + def get_directory_loader(self, directory: str) -> _TLoader: + def loader( + path: t.Optional[str], + ) -> t.Tuple[t.Optional[str], t.Optional[_TOpener]]: + if path is not None: + path = safe_join(directory, path) + + if path is None: + return None, None + else: + path = directory + + if os.path.isfile(path): + return os.path.basename(path), self._opener(path) + + return None, None + + return loader + + def generate_etag(self, mtime: datetime, file_size: int, real_filename: str) -> str: + real_filename = os.fsencode(real_filename) + timestamp = mtime.timestamp() + checksum = adler32(real_filename) & 0xFFFFFFFF + return f"wzsdm-{timestamp}-{file_size}-{checksum}" + + def __call__( + self, environ: "WSGIEnvironment", start_response: "StartResponse" + ) -> t.Iterable[bytes]: + path = get_path_info(environ) + file_loader = None + + for search_path, loader in self.exports: + if search_path == path: + real_filename, file_loader = loader(None) + + if file_loader is not None: + break + + if not search_path.endswith("/"): + search_path += "/" + + if path.startswith(search_path): + real_filename, file_loader = loader(path[len(search_path) :]) + + if file_loader is not None: + break + + if file_loader is None or not self.is_allowed(real_filename): # type: ignore + return self.app(environ, start_response) + + guessed_type = mimetypes.guess_type(real_filename) # type: ignore + mime_type = get_content_type(guessed_type[0] or self.fallback_mimetype, "utf-8") + f, mtime, file_size = file_loader() + + headers = [("Date", http_date())] + + if self.cache: + timeout = self.cache_timeout + etag = self.generate_etag(mtime, file_size, real_filename) # type: ignore + headers += [ + ("Etag", f'"{etag}"'), + ("Cache-Control", f"max-age={timeout}, public"), + ] + + if not is_resource_modified(environ, etag, last_modified=mtime): + f.close() + start_response("304 Not Modified", headers) + return [] + + headers.append(("Expires", http_date(time() + timeout))) + else: + headers.append(("Cache-Control", "public")) + + headers.extend( + ( + ("Content-Type", mime_type), + ("Content-Length", str(file_size)), + ("Last-Modified", http_date(mtime)), + ) + ) + start_response("200 OK", headers) + return wrap_file(environ, f) diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/py.typed b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/routing/__init__.py b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/routing/__init__.py new file mode 100644 index 0000000..84b043f --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/routing/__init__.py @@ -0,0 +1,133 @@ +"""When it comes to combining multiple controller or view functions +(however you want to call them) you need a dispatcher. A simple way +would be applying regular expression tests on the ``PATH_INFO`` and +calling registered callback functions that return the value then. + +This module implements a much more powerful system than simple regular +expression matching because it can also convert values in the URLs and +build URLs. + +Here a simple example that creates a URL map for an application with +two subdomains (www and kb) and some URL rules: + +.. code-block:: python + + m = Map([ + # Static URLs + Rule('/', endpoint='static/index'), + Rule('/about', endpoint='static/about'), + Rule('/help', endpoint='static/help'), + # Knowledge Base + Subdomain('kb', [ + Rule('/', endpoint='kb/index'), + Rule('/browse/', endpoint='kb/browse'), + Rule('/browse//', endpoint='kb/browse'), + Rule('/browse//', endpoint='kb/browse') + ]) + ], default_subdomain='www') + +If the application doesn't use subdomains it's perfectly fine to not set +the default subdomain and not use the `Subdomain` rule factory. The +endpoint in the rules can be anything, for example import paths or +unique identifiers. The WSGI application can use those endpoints to get the +handler for that URL. It doesn't have to be a string at all but it's +recommended. + +Now it's possible to create a URL adapter for one of the subdomains and +build URLs: + +.. code-block:: python + + c = m.bind('example.com') + + c.build("kb/browse", dict(id=42)) + 'http://kb.example.com/browse/42/' + + c.build("kb/browse", dict()) + 'http://kb.example.com/browse/' + + c.build("kb/browse", dict(id=42, page=3)) + 'http://kb.example.com/browse/42/3' + + c.build("static/about") + '/about' + + c.build("static/index", force_external=True) + 'http://www.example.com/' + + c = m.bind('example.com', subdomain='kb') + + c.build("static/about") + 'http://www.example.com/about' + +The first argument to bind is the server name *without* the subdomain. +Per default it will assume that the script is mounted on the root, but +often that's not the case so you can provide the real mount point as +second argument: + +.. code-block:: python + + c = m.bind('example.com', '/applications/example') + +The third argument can be the subdomain, if not given the default +subdomain is used. For more details about binding have a look at the +documentation of the `MapAdapter`. + +And here is how you can match URLs: + +.. code-block:: python + + c = m.bind('example.com') + + c.match("/") + ('static/index', {}) + + c.match("/about") + ('static/about', {}) + + c = m.bind('example.com', '/', 'kb') + + c.match("/") + ('kb/index', {}) + + c.match("/browse/42/23") + ('kb/browse', {'id': 42, 'page': 23}) + +If matching fails you get a ``NotFound`` exception, if the rule thinks +it's a good idea to redirect (for example because the URL was defined +to have a slash at the end but the request was missing that slash) it +will raise a ``RequestRedirect`` exception. Both are subclasses of +``HTTPException`` so you can use those errors as responses in the +application. + +If matching succeeded but the URL rule was incompatible to the given +method (for example there were only rules for ``GET`` and ``HEAD`` but +routing tried to match a ``POST`` request) a ``MethodNotAllowed`` +exception is raised. +""" +from .converters import AnyConverter as AnyConverter +from .converters import BaseConverter as BaseConverter +from .converters import FloatConverter as FloatConverter +from .converters import IntegerConverter as IntegerConverter +from .converters import PathConverter as PathConverter +from .converters import UnicodeConverter as UnicodeConverter +from .converters import UUIDConverter as UUIDConverter +from .converters import ValidationError as ValidationError +from .exceptions import BuildError as BuildError +from .exceptions import NoMatch as NoMatch +from .exceptions import RequestAliasRedirect as RequestAliasRedirect +from .exceptions import RequestPath as RequestPath +from .exceptions import RequestRedirect as RequestRedirect +from .exceptions import RoutingException as RoutingException +from .exceptions import WebsocketMismatch as WebsocketMismatch +from .map import Map as Map +from .map import MapAdapter as MapAdapter +from .matcher import StateMachineMatcher as StateMachineMatcher +from .rules import EndpointPrefix as EndpointPrefix +from .rules import parse_converter_args as parse_converter_args +from .rules import Rule as Rule +from .rules import RuleFactory as RuleFactory +from .rules import RuleTemplate as RuleTemplate +from .rules import RuleTemplateFactory as RuleTemplateFactory +from .rules import Subdomain as Subdomain +from .rules import Submount as Submount diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/routing/__pycache__/__init__.cpython-311.pyc b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/routing/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d23279803d19ec5b1e733ccf8b7562845c96e778 GIT binary patch literal 5102 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 G_x}Ok?o%=V literal 0 HcmV?d00001 diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/routing/__pycache__/converters.cpython-311.pyc b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/routing/__pycache__/converters.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..20386df23d5d7bc0b677691b0f761bfa8e2925ae GIT binary patch literal 12515 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 SU^Tdvqs2Hti_wkeZ1I0ZH_mkc literal 0 HcmV?d00001 diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/routing/__pycache__/map.cpython-311.pyc b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/routing/__pycache__/map.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0f07b29ea03a937519f280445ab704747c03a2b4 GIT binary patch literal 44445 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 literal 0 HcmV?d00001 diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/routing/__pycache__/matcher.cpython-311.pyc b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/routing/__pycache__/matcher.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e806c5a44944a6065a88b2e813a469d064570080 GIT binary patch literal 8829 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 WG!Z_1`fc&~$NcSA-x7(T-2WFKP)siX literal 0 HcmV?d00001 diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/routing/converters.py b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/routing/converters.py new file mode 100644 index 0000000..bbad29d --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/routing/converters.py @@ -0,0 +1,257 @@ +import re +import typing as t +import uuid + +from ..urls import _fast_url_quote + +if t.TYPE_CHECKING: + from .map import Map + + +class ValidationError(ValueError): + """Validation error. If a rule converter raises this exception the rule + does not match the current URL and the next URL is tried. + """ + + +class BaseConverter: + """Base class for all converters.""" + + regex = "[^/]+" + weight = 100 + part_isolating = True + + def __init__(self, map: "Map", *args: t.Any, **kwargs: t.Any) -> None: + self.map = map + + def to_python(self, value: str) -> t.Any: + return value + + def to_url(self, value: t.Any) -> str: + if isinstance(value, (bytes, bytearray)): + return _fast_url_quote(value) + return _fast_url_quote(str(value).encode(self.map.charset)) + + +class UnicodeConverter(BaseConverter): + """This converter is the default converter and accepts any string but + only one path segment. Thus the string can not include a slash. + + This is the default validator. + + Example:: + + Rule('/pages/'), + Rule('/') + + :param map: the :class:`Map`. + :param minlength: the minimum length of the string. Must be greater + or equal 1. + :param maxlength: the maximum length of the string. + :param length: the exact length of the string. + """ + + part_isolating = True + + def __init__( + self, + map: "Map", + minlength: int = 1, + maxlength: t.Optional[int] = None, + length: t.Optional[int] = None, + ) -> None: + super().__init__(map) + if length is not None: + length_regex = f"{{{int(length)}}}" + else: + if maxlength is None: + maxlength_value = "" + else: + maxlength_value = str(int(maxlength)) + length_regex = f"{{{int(minlength)},{maxlength_value}}}" + self.regex = f"[^/]{length_regex}" + + +class AnyConverter(BaseConverter): + """Matches one of the items provided. Items can either be Python + identifiers or strings:: + + Rule('/') + + :param map: the :class:`Map`. + :param items: this function accepts the possible items as positional + arguments. + + .. versionchanged:: 2.2 + Value is validated when building a URL. + """ + + part_isolating = True + + def __init__(self, map: "Map", *items: str) -> None: + super().__init__(map) + self.items = set(items) + self.regex = f"(?:{'|'.join([re.escape(x) for x in items])})" + + def to_url(self, value: t.Any) -> str: + if value in self.items: + return str(value) + + valid_values = ", ".join(f"'{item}'" for item in sorted(self.items)) + raise ValueError(f"'{value}' is not one of {valid_values}") + + +class PathConverter(BaseConverter): + """Like the default :class:`UnicodeConverter`, but it also matches + slashes. This is useful for wikis and similar applications:: + + Rule('/') + Rule('//edit') + + :param map: the :class:`Map`. + """ + + regex = "[^/].*?" + weight = 200 + part_isolating = False + + +class NumberConverter(BaseConverter): + """Baseclass for `IntegerConverter` and `FloatConverter`. + + :internal: + """ + + weight = 50 + num_convert: t.Callable = int + part_isolating = True + + def __init__( + self, + map: "Map", + fixed_digits: int = 0, + min: t.Optional[int] = None, + max: t.Optional[int] = None, + signed: bool = False, + ) -> None: + if signed: + self.regex = self.signed_regex + super().__init__(map) + self.fixed_digits = fixed_digits + self.min = min + self.max = max + self.signed = signed + + def to_python(self, value: str) -> t.Any: + if self.fixed_digits and len(value) != self.fixed_digits: + raise ValidationError() + value = self.num_convert(value) + if (self.min is not None and value < self.min) or ( + self.max is not None and value > self.max + ): + raise ValidationError() + return value + + def to_url(self, value: t.Any) -> str: + value = str(self.num_convert(value)) + if self.fixed_digits: + value = value.zfill(self.fixed_digits) + return value + + @property + def signed_regex(self) -> str: + return f"-?{self.regex}" + + +class IntegerConverter(NumberConverter): + """This converter only accepts integer values:: + + Rule("/page/") + + By default it only accepts unsigned, positive values. The ``signed`` + parameter will enable signed, negative values. :: + + Rule("/page/") + + :param map: The :class:`Map`. + :param fixed_digits: The number of fixed digits in the URL. If you + set this to ``4`` for example, the rule will only match if the + URL looks like ``/0001/``. The default is variable length. + :param min: The minimal value. + :param max: The maximal value. + :param signed: Allow signed (negative) values. + + .. versionadded:: 0.15 + The ``signed`` parameter. + """ + + regex = r"\d+" + part_isolating = True + + +class FloatConverter(NumberConverter): + """This converter only accepts floating point values:: + + Rule("/probability/") + + By default it only accepts unsigned, positive values. The ``signed`` + parameter will enable signed, negative values. :: + + Rule("/offset/") + + :param map: The :class:`Map`. + :param min: The minimal value. + :param max: The maximal value. + :param signed: Allow signed (negative) values. + + .. versionadded:: 0.15 + The ``signed`` parameter. + """ + + regex = r"\d+\.\d+" + num_convert = float + part_isolating = True + + def __init__( + self, + map: "Map", + min: t.Optional[float] = None, + max: t.Optional[float] = None, + signed: bool = False, + ) -> None: + super().__init__(map, min=min, max=max, signed=signed) # type: ignore + + +class UUIDConverter(BaseConverter): + """This converter only accepts UUID strings:: + + Rule('/object/') + + .. versionadded:: 0.10 + + :param map: the :class:`Map`. + """ + + regex = ( + r"[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-" + r"[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}" + ) + part_isolating = True + + def to_python(self, value: str) -> uuid.UUID: + return uuid.UUID(value) + + def to_url(self, value: uuid.UUID) -> str: + return str(value) + + +#: the default converter mapping for the map. +DEFAULT_CONVERTERS: t.Mapping[str, t.Type[BaseConverter]] = { + "default": UnicodeConverter, + "string": UnicodeConverter, + "any": AnyConverter, + "path": PathConverter, + "int": IntegerConverter, + "float": FloatConverter, + "uuid": UUIDConverter, +} diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/routing/exceptions.py b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/routing/exceptions.py new file mode 100644 index 0000000..7cbe6e9 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/routing/exceptions.py @@ -0,0 +1,146 @@ +import difflib +import typing as t + +from ..exceptions import BadRequest +from ..exceptions import HTTPException +from ..utils import cached_property +from ..utils import redirect + +if t.TYPE_CHECKING: + from _typeshed.wsgi import WSGIEnvironment + from .map import MapAdapter + from .rules import Rule # noqa: F401 + from ..wrappers.request import Request + from ..wrappers.response import Response + + +class RoutingException(Exception): + """Special exceptions that require the application to redirect, notifying + about missing urls, etc. + + :internal: + """ + + +class RequestRedirect(HTTPException, RoutingException): + """Raise if the map requests a redirect. This is for example the case if + `strict_slashes` are activated and an url that requires a trailing slash. + + The attribute `new_url` contains the absolute destination url. + """ + + code = 308 + + def __init__(self, new_url: str) -> None: + super().__init__(new_url) + self.new_url = new_url + + def get_response( + self, + environ: t.Optional[t.Union["WSGIEnvironment", "Request"]] = None, + scope: t.Optional[dict] = None, + ) -> "Response": + return redirect(self.new_url, self.code) + + +class RequestPath(RoutingException): + """Internal exception.""" + + __slots__ = ("path_info",) + + def __init__(self, path_info: str) -> None: + super().__init__() + self.path_info = path_info + + +class RequestAliasRedirect(RoutingException): # noqa: B903 + """This rule is an alias and wants to redirect to the canonical URL.""" + + def __init__(self, matched_values: t.Mapping[str, t.Any], endpoint: str) -> None: + super().__init__() + self.matched_values = matched_values + self.endpoint = endpoint + + +class BuildError(RoutingException, LookupError): + """Raised if the build system cannot find a URL for an endpoint with the + values provided. + """ + + def __init__( + self, + endpoint: str, + values: t.Mapping[str, t.Any], + method: t.Optional[str], + adapter: t.Optional["MapAdapter"] = None, + ) -> None: + super().__init__(endpoint, values, method) + self.endpoint = endpoint + self.values = values + self.method = method + self.adapter = adapter + + @cached_property + def suggested(self) -> t.Optional["Rule"]: + return self.closest_rule(self.adapter) + + def closest_rule(self, adapter: t.Optional["MapAdapter"]) -> t.Optional["Rule"]: + def _score_rule(rule: "Rule") -> float: + return sum( + [ + 0.98 + * difflib.SequenceMatcher( + None, rule.endpoint, self.endpoint + ).ratio(), + 0.01 * bool(set(self.values or ()).issubset(rule.arguments)), + 0.01 * bool(rule.methods and self.method in rule.methods), + ] + ) + + if adapter and adapter.map._rules: + return max(adapter.map._rules, key=_score_rule) + + return None + + def __str__(self) -> str: + message = [f"Could not build url for endpoint {self.endpoint!r}"] + if self.method: + message.append(f" ({self.method!r})") + if self.values: + message.append(f" with values {sorted(self.values)!r}") + message.append(".") + if self.suggested: + if self.endpoint == self.suggested.endpoint: + if ( + self.method + and self.suggested.methods is not None + and self.method not in self.suggested.methods + ): + message.append( + " Did you mean to use methods" + f" {sorted(self.suggested.methods)!r}?" + ) + missing_values = self.suggested.arguments.union( + set(self.suggested.defaults or ()) + ) - set(self.values.keys()) + if missing_values: + message.append( + f" Did you forget to specify values {sorted(missing_values)!r}?" + ) + else: + message.append(f" Did you mean {self.suggested.endpoint!r} instead?") + return "".join(message) + + +class WebsocketMismatch(BadRequest): + """The only matched rule is either a WebSocket and the request is + HTTP, or the rule is HTTP and the request is a WebSocket. + """ + + +class NoMatch(Exception): + __slots__ = ("have_match_for", "websocket_mismatch") + + def __init__(self, have_match_for: t.Set[str], websocket_mismatch: bool) -> None: + self.have_match_for = have_match_for + self.websocket_mismatch = websocket_mismatch diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/routing/map.py b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/routing/map.py new file mode 100644 index 0000000..daf94b6 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/routing/map.py @@ -0,0 +1,944 @@ +import posixpath +import typing as t +import warnings +from pprint import pformat +from threading import Lock + +from .._internal import _encode_idna +from .._internal import _get_environ +from .._internal import _to_str +from .._internal import _wsgi_decoding_dance +from ..datastructures import ImmutableDict +from ..datastructures import MultiDict +from ..exceptions import BadHost +from ..exceptions import HTTPException +from ..exceptions import MethodNotAllowed +from ..exceptions import NotFound +from ..urls import url_encode +from ..urls import url_join +from ..urls import url_quote +from ..wsgi import get_host +from .converters import DEFAULT_CONVERTERS +from .exceptions import BuildError +from .exceptions import NoMatch +from .exceptions import RequestAliasRedirect +from .exceptions import RequestPath +from .exceptions import RequestRedirect +from .exceptions import WebsocketMismatch +from .matcher import StateMachineMatcher +from .rules import _simple_rule_re +from .rules import Rule + +if t.TYPE_CHECKING: + import typing_extensions as te + from _typeshed.wsgi import WSGIApplication + from _typeshed.wsgi import WSGIEnvironment + from .converters import BaseConverter + from .rules import RuleFactory + from ..wrappers.request import Request + + +class Map: + """The map class stores all the URL rules and some configuration + parameters. Some of the configuration values are only stored on the + `Map` instance since those affect all rules, others are just defaults + and can be overridden for each rule. Note that you have to specify all + arguments besides the `rules` as keyword arguments! + + :param rules: sequence of url rules for this map. + :param default_subdomain: The default subdomain for rules without a + subdomain defined. + :param charset: charset of the url. defaults to ``"utf-8"`` + :param strict_slashes: If a rule ends with a slash but the matched + URL does not, redirect to the URL with a trailing slash. + :param merge_slashes: Merge consecutive slashes when matching or + building URLs. Matches will redirect to the normalized URL. + Slashes in variable parts are not merged. + :param redirect_defaults: This will redirect to the default rule if it + wasn't visited that way. This helps creating + unique URLs. + :param converters: A dict of converters that adds additional converters + to the list of converters. If you redefine one + converter this will override the original one. + :param sort_parameters: If set to `True` the url parameters are sorted. + See `url_encode` for more details. + :param sort_key: The sort key function for `url_encode`. + :param encoding_errors: the error method to use for decoding + :param host_matching: if set to `True` it enables the host matching + feature and disables the subdomain one. If + enabled the `host` parameter to rules is used + instead of the `subdomain` one. + + .. versionchanged:: 1.0 + If ``url_scheme`` is ``ws`` or ``wss``, only WebSocket rules + will match. + + .. versionchanged:: 1.0 + Added ``merge_slashes``. + + .. versionchanged:: 0.7 + Added ``encoding_errors`` and ``host_matching``. + + .. versionchanged:: 0.5 + Added ``sort_parameters`` and ``sort_key``. + """ + + #: A dict of default converters to be used. + default_converters = ImmutableDict(DEFAULT_CONVERTERS) + + #: The type of lock to use when updating. + #: + #: .. versionadded:: 1.0 + lock_class = Lock + + def __init__( + self, + rules: t.Optional[t.Iterable["RuleFactory"]] = None, + default_subdomain: str = "", + charset: str = "utf-8", + strict_slashes: bool = True, + merge_slashes: bool = True, + redirect_defaults: bool = True, + converters: t.Optional[t.Mapping[str, t.Type["BaseConverter"]]] = None, + sort_parameters: bool = False, + sort_key: t.Optional[t.Callable[[t.Any], t.Any]] = None, + encoding_errors: str = "replace", + host_matching: bool = False, + ) -> None: + self._matcher = StateMachineMatcher(merge_slashes) + self._rules_by_endpoint: t.Dict[str, t.List[Rule]] = {} + self._remap = True + self._remap_lock = self.lock_class() + + self.default_subdomain = default_subdomain + self.charset = charset + self.encoding_errors = encoding_errors + self.strict_slashes = strict_slashes + self.merge_slashes = merge_slashes + self.redirect_defaults = redirect_defaults + self.host_matching = host_matching + + self.converters = self.default_converters.copy() + if converters: + self.converters.update(converters) + + self.sort_parameters = sort_parameters + self.sort_key = sort_key + + for rulefactory in rules or (): + self.add(rulefactory) + + def is_endpoint_expecting(self, endpoint: str, *arguments: str) -> bool: + """Iterate over all rules and check if the endpoint expects + the arguments provided. This is for example useful if you have + some URLs that expect a language code and others that do not and + you want to wrap the builder a bit so that the current language + code is automatically added if not provided but endpoints expect + it. + + :param endpoint: the endpoint to check. + :param arguments: this function accepts one or more arguments + as positional arguments. Each one of them is + checked. + """ + self.update() + arguments = set(arguments) + for rule in self._rules_by_endpoint[endpoint]: + if arguments.issubset(rule.arguments): + return True + return False + + @property + def _rules(self) -> t.List[Rule]: + return [rule for rules in self._rules_by_endpoint.values() for rule in rules] + + def iter_rules(self, endpoint: t.Optional[str] = None) -> t.Iterator[Rule]: + """Iterate over all rules or the rules of an endpoint. + + :param endpoint: if provided only the rules for that endpoint + are returned. + :return: an iterator + """ + self.update() + if endpoint is not None: + return iter(self._rules_by_endpoint[endpoint]) + return iter(self._rules) + + def add(self, rulefactory: "RuleFactory") -> None: + """Add a new rule or factory to the map and bind it. Requires that the + rule is not bound to another map. + + :param rulefactory: a :class:`Rule` or :class:`RuleFactory` + """ + for rule in rulefactory.get_rules(self): + rule.bind(self) + if not rule.build_only: + self._matcher.add(rule) + self._rules_by_endpoint.setdefault(rule.endpoint, []).append(rule) + self._remap = True + + def bind( + self, + server_name: str, + script_name: t.Optional[str] = None, + subdomain: t.Optional[str] = None, + url_scheme: str = "http", + default_method: str = "GET", + path_info: t.Optional[str] = None, + query_args: t.Optional[t.Union[t.Mapping[str, t.Any], str]] = None, + ) -> "MapAdapter": + """Return a new :class:`MapAdapter` with the details specified to the + call. Note that `script_name` will default to ``'/'`` if not further + specified or `None`. The `server_name` at least is a requirement + because the HTTP RFC requires absolute URLs for redirects and so all + redirect exceptions raised by Werkzeug will contain the full canonical + URL. + + If no path_info is passed to :meth:`match` it will use the default path + info passed to bind. While this doesn't really make sense for + manual bind calls, it's useful if you bind a map to a WSGI + environment which already contains the path info. + + `subdomain` will default to the `default_subdomain` for this map if + no defined. If there is no `default_subdomain` you cannot use the + subdomain feature. + + .. versionchanged:: 1.0 + If ``url_scheme`` is ``ws`` or ``wss``, only WebSocket rules + will match. + + .. versionchanged:: 0.15 + ``path_info`` defaults to ``'/'`` if ``None``. + + .. versionchanged:: 0.8 + ``query_args`` can be a string. + + .. versionchanged:: 0.7 + Added ``query_args``. + """ + server_name = server_name.lower() + if self.host_matching: + if subdomain is not None: + raise RuntimeError("host matching enabled and a subdomain was provided") + elif subdomain is None: + subdomain = self.default_subdomain + if script_name is None: + script_name = "/" + if path_info is None: + path_info = "/" + + try: + server_name = _encode_idna(server_name) # type: ignore + except UnicodeError as e: + raise BadHost() from e + + return MapAdapter( + self, + server_name, + script_name, + subdomain, + url_scheme, + path_info, + default_method, + query_args, + ) + + def bind_to_environ( + self, + environ: t.Union["WSGIEnvironment", "Request"], + server_name: t.Optional[str] = None, + subdomain: t.Optional[str] = None, + ) -> "MapAdapter": + """Like :meth:`bind` but you can pass it an WSGI environment and it + will fetch the information from that dictionary. Note that because of + limitations in the protocol there is no way to get the current + subdomain and real `server_name` from the environment. If you don't + provide it, Werkzeug will use `SERVER_NAME` and `SERVER_PORT` (or + `HTTP_HOST` if provided) as used `server_name` with disabled subdomain + feature. + + If `subdomain` is `None` but an environment and a server name is + provided it will calculate the current subdomain automatically. + Example: `server_name` is ``'example.com'`` and the `SERVER_NAME` + in the wsgi `environ` is ``'staging.dev.example.com'`` the calculated + subdomain will be ``'staging.dev'``. + + If the object passed as environ has an environ attribute, the value of + this attribute is used instead. This allows you to pass request + objects. Additionally `PATH_INFO` added as a default of the + :class:`MapAdapter` so that you don't have to pass the path info to + the match method. + + .. versionchanged:: 1.0.0 + If the passed server name specifies port 443, it will match + if the incoming scheme is ``https`` without a port. + + .. versionchanged:: 1.0.0 + A warning is shown when the passed server name does not + match the incoming WSGI server name. + + .. versionchanged:: 0.8 + This will no longer raise a ValueError when an unexpected server + name was passed. + + .. versionchanged:: 0.5 + previously this method accepted a bogus `calculate_subdomain` + parameter that did not have any effect. It was removed because + of that. + + :param environ: a WSGI environment. + :param server_name: an optional server name hint (see above). + :param subdomain: optionally the current subdomain (see above). + """ + env = _get_environ(environ) + wsgi_server_name = get_host(env).lower() + scheme = env["wsgi.url_scheme"] + upgrade = any( + v.strip() == "upgrade" + for v in env.get("HTTP_CONNECTION", "").lower().split(",") + ) + + if upgrade and env.get("HTTP_UPGRADE", "").lower() == "websocket": + scheme = "wss" if scheme == "https" else "ws" + + if server_name is None: + server_name = wsgi_server_name + else: + server_name = server_name.lower() + + # strip standard port to match get_host() + if scheme in {"http", "ws"} and server_name.endswith(":80"): + server_name = server_name[:-3] + elif scheme in {"https", "wss"} and server_name.endswith(":443"): + server_name = server_name[:-4] + + if subdomain is None and not self.host_matching: + cur_server_name = wsgi_server_name.split(".") + real_server_name = server_name.split(".") + offset = -len(real_server_name) + + if cur_server_name[offset:] != real_server_name: + # This can happen even with valid configs if the server was + # accessed directly by IP address under some situations. + # Instead of raising an exception like in Werkzeug 0.7 or + # earlier we go by an invalid subdomain which will result + # in a 404 error on matching. + warnings.warn( + f"Current server name {wsgi_server_name!r} doesn't match configured" + f" server name {server_name!r}", + stacklevel=2, + ) + subdomain = "" + else: + subdomain = ".".join(filter(None, cur_server_name[:offset])) + + def _get_wsgi_string(name: str) -> t.Optional[str]: + val = env.get(name) + if val is not None: + return _wsgi_decoding_dance(val, self.charset) + return None + + script_name = _get_wsgi_string("SCRIPT_NAME") + path_info = _get_wsgi_string("PATH_INFO") + query_args = _get_wsgi_string("QUERY_STRING") + return Map.bind( + self, + server_name, + script_name, + subdomain, + scheme, + env["REQUEST_METHOD"], + path_info, + query_args=query_args, + ) + + def update(self) -> None: + """Called before matching and building to keep the compiled rules + in the correct order after things changed. + """ + if not self._remap: + return + + with self._remap_lock: + if not self._remap: + return + + self._matcher.update() + for rules in self._rules_by_endpoint.values(): + rules.sort(key=lambda x: x.build_compare_key()) + self._remap = False + + def __repr__(self) -> str: + rules = self.iter_rules() + return f"{type(self).__name__}({pformat(list(rules))})" + + +class MapAdapter: + + """Returned by :meth:`Map.bind` or :meth:`Map.bind_to_environ` and does + the URL matching and building based on runtime information. + """ + + def __init__( + self, + map: Map, + server_name: str, + script_name: str, + subdomain: t.Optional[str], + url_scheme: str, + path_info: str, + default_method: str, + query_args: t.Optional[t.Union[t.Mapping[str, t.Any], str]] = None, + ): + self.map = map + self.server_name = _to_str(server_name) + script_name = _to_str(script_name) + if not script_name.endswith("/"): + script_name += "/" + self.script_name = script_name + self.subdomain = _to_str(subdomain) + self.url_scheme = _to_str(url_scheme) + self.path_info = _to_str(path_info) + self.default_method = _to_str(default_method) + self.query_args = query_args + self.websocket = self.url_scheme in {"ws", "wss"} + + def dispatch( + self, + view_func: t.Callable[[str, t.Mapping[str, t.Any]], "WSGIApplication"], + path_info: t.Optional[str] = None, + method: t.Optional[str] = None, + catch_http_exceptions: bool = False, + ) -> "WSGIApplication": + """Does the complete dispatching process. `view_func` is called with + the endpoint and a dict with the values for the view. It should + look up the view function, call it, and return a response object + or WSGI application. http exceptions are not caught by default + so that applications can display nicer error messages by just + catching them by hand. If you want to stick with the default + error messages you can pass it ``catch_http_exceptions=True`` and + it will catch the http exceptions. + + Here a small example for the dispatch usage:: + + from werkzeug.wrappers import Request, Response + from werkzeug.wsgi import responder + from werkzeug.routing import Map, Rule + + def on_index(request): + return Response('Hello from the index') + + url_map = Map([Rule('/', endpoint='index')]) + views = {'index': on_index} + + @responder + def application(environ, start_response): + request = Request(environ) + urls = url_map.bind_to_environ(environ) + return urls.dispatch(lambda e, v: views[e](request, **v), + catch_http_exceptions=True) + + Keep in mind that this method might return exception objects, too, so + use :class:`Response.force_type` to get a response object. + + :param view_func: a function that is called with the endpoint as + first argument and the value dict as second. Has + to dispatch to the actual view function with this + information. (see above) + :param path_info: the path info to use for matching. Overrides the + path info specified on binding. + :param method: the HTTP method used for matching. Overrides the + method specified on binding. + :param catch_http_exceptions: set to `True` to catch any of the + werkzeug :class:`HTTPException`\\s. + """ + try: + try: + endpoint, args = self.match(path_info, method) + except RequestRedirect as e: + return e + return view_func(endpoint, args) + except HTTPException as e: + if catch_http_exceptions: + return e + raise + + @t.overload + def match( # type: ignore + self, + path_info: t.Optional[str] = None, + method: t.Optional[str] = None, + return_rule: "te.Literal[False]" = False, + query_args: t.Optional[t.Union[t.Mapping[str, t.Any], str]] = None, + websocket: t.Optional[bool] = None, + ) -> t.Tuple[str, t.Mapping[str, t.Any]]: + ... + + @t.overload + def match( + self, + path_info: t.Optional[str] = None, + method: t.Optional[str] = None, + return_rule: "te.Literal[True]" = True, + query_args: t.Optional[t.Union[t.Mapping[str, t.Any], str]] = None, + websocket: t.Optional[bool] = None, + ) -> t.Tuple[Rule, t.Mapping[str, t.Any]]: + ... + + def match( + self, + path_info: t.Optional[str] = None, + method: t.Optional[str] = None, + return_rule: bool = False, + query_args: t.Optional[t.Union[t.Mapping[str, t.Any], str]] = None, + websocket: t.Optional[bool] = None, + ) -> t.Tuple[t.Union[str, Rule], t.Mapping[str, t.Any]]: + """The usage is simple: you just pass the match method the current + path info as well as the method (which defaults to `GET`). The + following things can then happen: + + - you receive a `NotFound` exception that indicates that no URL is + matching. A `NotFound` exception is also a WSGI application you + can call to get a default page not found page (happens to be the + same object as `werkzeug.exceptions.NotFound`) + + - you receive a `MethodNotAllowed` exception that indicates that there + is a match for this URL but not for the current request method. + This is useful for RESTful applications. + + - you receive a `RequestRedirect` exception with a `new_url` + attribute. This exception is used to notify you about a request + Werkzeug requests from your WSGI application. This is for example the + case if you request ``/foo`` although the correct URL is ``/foo/`` + You can use the `RequestRedirect` instance as response-like object + similar to all other subclasses of `HTTPException`. + + - you receive a ``WebsocketMismatch`` exception if the only + match is a WebSocket rule but the bind is an HTTP request, or + if the match is an HTTP rule but the bind is a WebSocket + request. + + - you get a tuple in the form ``(endpoint, arguments)`` if there is + a match (unless `return_rule` is True, in which case you get a tuple + in the form ``(rule, arguments)``) + + If the path info is not passed to the match method the default path + info of the map is used (defaults to the root URL if not defined + explicitly). + + All of the exceptions raised are subclasses of `HTTPException` so they + can be used as WSGI responses. They will all render generic error or + redirect pages. + + Here is a small example for matching: + + >>> m = Map([ + ... Rule('/', endpoint='index'), + ... Rule('/downloads/', endpoint='downloads/index'), + ... Rule('/downloads/', endpoint='downloads/show') + ... ]) + >>> urls = m.bind("example.com", "/") + >>> urls.match("/", "GET") + ('index', {}) + >>> urls.match("/downloads/42") + ('downloads/show', {'id': 42}) + + And here is what happens on redirect and missing URLs: + + >>> urls.match("/downloads") + Traceback (most recent call last): + ... + RequestRedirect: http://example.com/downloads/ + >>> urls.match("/missing") + Traceback (most recent call last): + ... + NotFound: 404 Not Found + + :param path_info: the path info to use for matching. Overrides the + path info specified on binding. + :param method: the HTTP method used for matching. Overrides the + method specified on binding. + :param return_rule: return the rule that matched instead of just the + endpoint (defaults to `False`). + :param query_args: optional query arguments that are used for + automatic redirects as string or dictionary. It's + currently not possible to use the query arguments + for URL matching. + :param websocket: Match WebSocket instead of HTTP requests. A + websocket request has a ``ws`` or ``wss`` + :attr:`url_scheme`. This overrides that detection. + + .. versionadded:: 1.0 + Added ``websocket``. + + .. versionchanged:: 0.8 + ``query_args`` can be a string. + + .. versionadded:: 0.7 + Added ``query_args``. + + .. versionadded:: 0.6 + Added ``return_rule``. + """ + self.map.update() + if path_info is None: + path_info = self.path_info + else: + path_info = _to_str(path_info, self.map.charset) + if query_args is None: + query_args = self.query_args or {} + method = (method or self.default_method).upper() + + if websocket is None: + websocket = self.websocket + + domain_part = self.server_name if self.map.host_matching else self.subdomain + path_part = f"/{path_info.lstrip('/')}" if path_info else "" + + try: + result = self.map._matcher.match(domain_part, path_part, method, websocket) + except RequestPath as e: + raise RequestRedirect( + self.make_redirect_url( + url_quote(e.path_info, self.map.charset, safe="/:|+"), + query_args, + ) + ) from None + except RequestAliasRedirect as e: + raise RequestRedirect( + self.make_alias_redirect_url( + f"{domain_part}|{path_part}", + e.endpoint, + e.matched_values, + method, + query_args, + ) + ) from None + except NoMatch as e: + if e.have_match_for: + raise MethodNotAllowed(valid_methods=list(e.have_match_for)) from None + + if e.websocket_mismatch: + raise WebsocketMismatch() from None + + raise NotFound() from None + else: + rule, rv = result + + if self.map.redirect_defaults: + redirect_url = self.get_default_redirect(rule, method, rv, query_args) + if redirect_url is not None: + raise RequestRedirect(redirect_url) + + if rule.redirect_to is not None: + if isinstance(rule.redirect_to, str): + + def _handle_match(match: t.Match[str]) -> str: + value = rv[match.group(1)] + return rule._converters[match.group(1)].to_url(value) + + redirect_url = _simple_rule_re.sub(_handle_match, rule.redirect_to) + else: + redirect_url = rule.redirect_to(self, **rv) + + if self.subdomain: + netloc = f"{self.subdomain}.{self.server_name}" + else: + netloc = self.server_name + + raise RequestRedirect( + url_join( + f"{self.url_scheme or 'http'}://{netloc}{self.script_name}", + redirect_url, + ) + ) + + if return_rule: + return rule, rv + else: + return rule.endpoint, rv + + def test( + self, path_info: t.Optional[str] = None, method: t.Optional[str] = None + ) -> bool: + """Test if a rule would match. Works like `match` but returns `True` + if the URL matches, or `False` if it does not exist. + + :param path_info: the path info to use for matching. Overrides the + path info specified on binding. + :param method: the HTTP method used for matching. Overrides the + method specified on binding. + """ + try: + self.match(path_info, method) + except RequestRedirect: + pass + except HTTPException: + return False + return True + + def allowed_methods(self, path_info: t.Optional[str] = None) -> t.Iterable[str]: + """Returns the valid methods that match for a given path. + + .. versionadded:: 0.7 + """ + try: + self.match(path_info, method="--") + except MethodNotAllowed as e: + return e.valid_methods # type: ignore + except HTTPException: + pass + return [] + + def get_host(self, domain_part: t.Optional[str]) -> str: + """Figures out the full host name for the given domain part. The + domain part is a subdomain in case host matching is disabled or + a full host name. + """ + if self.map.host_matching: + if domain_part is None: + return self.server_name + return _to_str(domain_part, "ascii") + subdomain = domain_part + if subdomain is None: + subdomain = self.subdomain + else: + subdomain = _to_str(subdomain, "ascii") + + if subdomain: + return f"{subdomain}.{self.server_name}" + else: + return self.server_name + + def get_default_redirect( + self, + rule: Rule, + method: str, + values: t.MutableMapping[str, t.Any], + query_args: t.Union[t.Mapping[str, t.Any], str], + ) -> t.Optional[str]: + """A helper that returns the URL to redirect to if it finds one. + This is used for default redirecting only. + + :internal: + """ + assert self.map.redirect_defaults + for r in self.map._rules_by_endpoint[rule.endpoint]: + # every rule that comes after this one, including ourself + # has a lower priority for the defaults. We order the ones + # with the highest priority up for building. + if r is rule: + break + if r.provides_defaults_for(rule) and r.suitable_for(values, method): + values.update(r.defaults) # type: ignore + domain_part, path = r.build(values) # type: ignore + return self.make_redirect_url(path, query_args, domain_part=domain_part) + return None + + def encode_query_args(self, query_args: t.Union[t.Mapping[str, t.Any], str]) -> str: + if not isinstance(query_args, str): + return url_encode(query_args, self.map.charset) + return query_args + + def make_redirect_url( + self, + path_info: str, + query_args: t.Optional[t.Union[t.Mapping[str, t.Any], str]] = None, + domain_part: t.Optional[str] = None, + ) -> str: + """Creates a redirect URL. + + :internal: + """ + if query_args: + suffix = f"?{self.encode_query_args(query_args)}" + else: + suffix = "" + + scheme = self.url_scheme or "http" + host = self.get_host(domain_part) + path = posixpath.join(self.script_name.strip("/"), path_info.lstrip("/")) + return f"{scheme}://{host}/{path}{suffix}" + + def make_alias_redirect_url( + self, + path: str, + endpoint: str, + values: t.Mapping[str, t.Any], + method: str, + query_args: t.Union[t.Mapping[str, t.Any], str], + ) -> str: + """Internally called to make an alias redirect URL.""" + url = self.build( + endpoint, values, method, append_unknown=False, force_external=True + ) + if query_args: + url += f"?{self.encode_query_args(query_args)}" + assert url != path, "detected invalid alias setting. No canonical URL found" + return url + + def _partial_build( + self, + endpoint: str, + values: t.Mapping[str, t.Any], + method: t.Optional[str], + append_unknown: bool, + ) -> t.Optional[t.Tuple[str, str, bool]]: + """Helper for :meth:`build`. Returns subdomain and path for the + rule that accepts this endpoint, values and method. + + :internal: + """ + # in case the method is none, try with the default method first + if method is None: + rv = self._partial_build( + endpoint, values, self.default_method, append_unknown + ) + if rv is not None: + return rv + + # Default method did not match or a specific method is passed. + # Check all for first match with matching host. If no matching + # host is found, go with first result. + first_match = None + + for rule in self.map._rules_by_endpoint.get(endpoint, ()): + if rule.suitable_for(values, method): + build_rv = rule.build(values, append_unknown) + + if build_rv is not None: + rv = (build_rv[0], build_rv[1], rule.websocket) + if self.map.host_matching: + if rv[0] == self.server_name: + return rv + elif first_match is None: + first_match = rv + else: + return rv + + return first_match + + def build( + self, + endpoint: str, + values: t.Optional[t.Mapping[str, t.Any]] = None, + method: t.Optional[str] = None, + force_external: bool = False, + append_unknown: bool = True, + url_scheme: t.Optional[str] = None, + ) -> str: + """Building URLs works pretty much the other way round. Instead of + `match` you call `build` and pass it the endpoint and a dict of + arguments for the placeholders. + + The `build` function also accepts an argument called `force_external` + which, if you set it to `True` will force external URLs. Per default + external URLs (include the server name) will only be used if the + target URL is on a different subdomain. + + >>> m = Map([ + ... Rule('/', endpoint='index'), + ... Rule('/downloads/', endpoint='downloads/index'), + ... Rule('/downloads/', endpoint='downloads/show') + ... ]) + >>> urls = m.bind("example.com", "/") + >>> urls.build("index", {}) + '/' + >>> urls.build("downloads/show", {'id': 42}) + '/downloads/42' + >>> urls.build("downloads/show", {'id': 42}, force_external=True) + 'http://example.com/downloads/42' + + Because URLs cannot contain non ASCII data you will always get + bytes back. Non ASCII characters are urlencoded with the + charset defined on the map instance. + + Additional values are converted to strings and appended to the URL as + URL querystring parameters: + + >>> urls.build("index", {'q': 'My Searchstring'}) + '/?q=My+Searchstring' + + When processing those additional values, lists are furthermore + interpreted as multiple values (as per + :py:class:`werkzeug.datastructures.MultiDict`): + + >>> urls.build("index", {'q': ['a', 'b', 'c']}) + '/?q=a&q=b&q=c' + + Passing a ``MultiDict`` will also add multiple values: + + >>> urls.build("index", MultiDict((('p', 'z'), ('q', 'a'), ('q', 'b')))) + '/?p=z&q=a&q=b' + + If a rule does not exist when building a `BuildError` exception is + raised. + + The build method accepts an argument called `method` which allows you + to specify the method you want to have an URL built for if you have + different methods for the same endpoint specified. + + :param endpoint: the endpoint of the URL to build. + :param values: the values for the URL to build. Unhandled values are + appended to the URL as query parameters. + :param method: the HTTP method for the rule if there are different + URLs for different methods on the same endpoint. + :param force_external: enforce full canonical external URLs. If the URL + scheme is not provided, this will generate + a protocol-relative URL. + :param append_unknown: unknown parameters are appended to the generated + URL as query string argument. Disable this + if you want the builder to ignore those. + :param url_scheme: Scheme to use in place of the bound + :attr:`url_scheme`. + + .. versionchanged:: 2.0 + Added the ``url_scheme`` parameter. + + .. versionadded:: 0.6 + Added the ``append_unknown`` parameter. + """ + self.map.update() + + if values: + if isinstance(values, MultiDict): + values = { + k: (v[0] if len(v) == 1 else v) + for k, v in dict.items(values) + if len(v) != 0 + } + else: # plain dict + values = {k: v for k, v in values.items() if v is not None} + else: + values = {} + + rv = self._partial_build(endpoint, values, method, append_unknown) + if rv is None: + raise BuildError(endpoint, values, method, self) + + domain_part, path, websocket = rv + host = self.get_host(domain_part) + + if url_scheme is None: + url_scheme = self.url_scheme + + # Always build WebSocket routes with the scheme (browsers + # require full URLs). If bound to a WebSocket, ensure that HTTP + # routes are built with an HTTP scheme. + secure = url_scheme in {"https", "wss"} + + if websocket: + force_external = True + url_scheme = "wss" if secure else "ws" + elif url_scheme: + url_scheme = "https" if secure else "http" + + # shortcut this. + if not force_external and ( + (self.map.host_matching and host == self.server_name) + or (not self.map.host_matching and domain_part == self.subdomain) + ): + return f"{self.script_name.rstrip('/')}/{path.lstrip('/')}" + + scheme = f"{url_scheme}:" if url_scheme else "" + return f"{scheme}//{host}{self.script_name[:-1]}/{path.lstrip('/')}" diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/routing/matcher.py b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/routing/matcher.py new file mode 100644 index 0000000..05370c3 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/routing/matcher.py @@ -0,0 +1,192 @@ +import re +import typing as t +from dataclasses import dataclass +from dataclasses import field + +from .converters import ValidationError +from .exceptions import NoMatch +from .exceptions import RequestAliasRedirect +from .exceptions import RequestPath +from .rules import Rule +from .rules import RulePart + + +class SlashRequired(Exception): + pass + + +@dataclass +class State: + """A representation of a rule state. + + This includes the *rules* that correspond to the state and the + possible *static* and *dynamic* transitions to the next state. + """ + + dynamic: t.List[t.Tuple[RulePart, "State"]] = field(default_factory=list) + rules: t.List[Rule] = field(default_factory=list) + static: t.Dict[str, "State"] = field(default_factory=dict) + + +class StateMachineMatcher: + def __init__(self, merge_slashes: bool) -> None: + self._root = State() + self.merge_slashes = merge_slashes + + def add(self, rule: Rule) -> None: + state = self._root + for part in rule._parts: + if part.static: + state.static.setdefault(part.content, State()) + state = state.static[part.content] + else: + for test_part, new_state in state.dynamic: + if test_part == part: + state = new_state + break + else: + new_state = State() + state.dynamic.append((part, new_state)) + state = new_state + state.rules.append(rule) + + def update(self) -> None: + # For every state the dynamic transitions should be sorted by + # the weight of the transition + state = self._root + + def _update_state(state: State) -> None: + state.dynamic.sort(key=lambda entry: entry[0].weight) + for new_state in state.static.values(): + _update_state(new_state) + for _, new_state in state.dynamic: + _update_state(new_state) + + _update_state(state) + + def match( + self, domain: str, path: str, method: str, websocket: bool + ) -> t.Tuple[Rule, t.MutableMapping[str, t.Any]]: + # To match to a rule we need to start at the root state and + # try to follow the transitions until we find a match, or find + # there is no transition to follow. + + have_match_for = set() + websocket_mismatch = False + + def _match( + state: State, parts: t.List[str], values: t.List[str] + ) -> t.Optional[t.Tuple[Rule, t.List[str]]]: + # This function is meant to be called recursively, and will attempt + # to match the head part to the state's transitions. + nonlocal have_match_for, websocket_mismatch + + # The base case is when all parts have been matched via + # transitions. Hence if there is a rule with methods & + # websocket that work return it and the dynamic values + # extracted. + if parts == []: + for rule in state.rules: + if rule.methods is not None and method not in rule.methods: + have_match_for.update(rule.methods) + elif rule.websocket != websocket: + websocket_mismatch = True + else: + return rule, values + + # Test if there is a match with this path with a + # trailing slash, if so raise an exception to report + # that matching is possible with an additional slash + if "" in state.static: + for rule in state.static[""].rules: + if websocket == rule.websocket and ( + rule.methods is None or method in rule.methods + ): + if rule.strict_slashes: + raise SlashRequired() + else: + return rule, values + return None + + part = parts[0] + # To match this part try the static transitions first + if part in state.static: + rv = _match(state.static[part], parts[1:], values) + if rv is not None: + return rv + # No match via the static transitions, so try the dynamic + # ones. + for test_part, new_state in state.dynamic: + target = part + remaining = parts[1:] + # A final part indicates a transition that always + # consumes the remaining parts i.e. transitions to a + # final state. + if test_part.final: + target = "/".join(parts) + remaining = [] + match = re.compile(test_part.content).match(target) + if match is not None: + groups = list(match.groups()) + if test_part.suffixed: + # If a part_isolating=False part has a slash suffix, remove the + # suffix from the match and check for the slash redirect next. + suffix = groups.pop() + if suffix == "/": + remaining = [""] + rv = _match(new_state, remaining, values + groups) + if rv is not None: + return rv + + # If there is no match and the only part left is a + # trailing slash ("") consider rules that aren't + # strict-slashes as these should match if there is a final + # slash part. + if parts == [""]: + for rule in state.rules: + if rule.strict_slashes: + continue + if rule.methods is not None and method not in rule.methods: + have_match_for.update(rule.methods) + elif rule.websocket != websocket: + websocket_mismatch = True + else: + return rule, values + + return None + + try: + rv = _match(self._root, [domain, *path.split("/")], []) + except SlashRequired: + raise RequestPath(f"{path}/") from None + + if self.merge_slashes and rv is None: + # Try to match again, but with slashes merged + path = re.sub("/{2,}?", "/", path) + try: + rv = _match(self._root, [domain, *path.split("/")], []) + except SlashRequired: + raise RequestPath(f"{path}/") from None + if rv is None: + raise NoMatch(have_match_for, websocket_mismatch) + else: + raise RequestPath(f"{path}") + elif rv is not None: + rule, values = rv + + result = {} + for name, value in zip(rule._converters.keys(), values): + try: + value = rule._converters[name].to_python(value) + except ValidationError: + raise NoMatch(have_match_for, websocket_mismatch) from None + result[str(name)] = value + if rule.defaults: + result.update(rule.defaults) + + if rule.alias and rule.map.redirect_defaults: + raise RequestAliasRedirect(result, rule.endpoint) + + return rule, result + + raise NoMatch(have_match_for, websocket_mismatch) diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/routing/rules.py b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/routing/rules.py new file mode 100644 index 0000000..7b37890 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/routing/rules.py @@ -0,0 +1,904 @@ +import ast +import re +import typing as t +from dataclasses import dataclass +from string import Template +from types import CodeType + +from .._internal import _to_bytes +from ..urls import url_encode +from ..urls import url_quote +from .converters import ValidationError + +if t.TYPE_CHECKING: + from .converters import BaseConverter + from .map import Map + + +class Weighting(t.NamedTuple): + number_static_weights: int + static_weights: t.List[t.Tuple[int, int]] + number_argument_weights: int + argument_weights: t.List[int] + + +@dataclass +class RulePart: + """A part of a rule. + + Rules can be represented by parts as delimited by `/` with + instances of this class representing those parts. The *content* is + either the raw content if *static* or a regex string to match + against. The *weight* can be used to order parts when matching. + + """ + + content: str + final: bool + static: bool + suffixed: bool + weight: Weighting + + +_part_re = re.compile( + r""" + (?: + (?P\/) # a slash + | + (?P[^<\/]+) # static rule data + | + (?: + < + (?: + (?P[a-zA-Z_][a-zA-Z0-9_]*) # converter name + (?:\((?P.*?)\))? # converter arguments + \: # variable delimiter + )? + (?P[a-zA-Z_][a-zA-Z0-9_]*) # variable name + > + ) + ) + """, + re.VERBOSE, +) + +_simple_rule_re = re.compile(r"<([^>]+)>") +_converter_args_re = re.compile( + r""" + ((?P\w+)\s*=\s*)? + (?P + True|False| + \d+.\d+| + \d+.| + \d+| + [\w\d_.]+| + [urUR]?(?P"[^"]*?"|'[^']*') + )\s*, + """, + re.VERBOSE, +) + + +_PYTHON_CONSTANTS = {"None": None, "True": True, "False": False} + + +def _find(value: str, target: str, pos: int) -> int: + """Find the *target* in *value* after *pos*. + + Returns the *value* length if *target* isn't found. + """ + try: + return value.index(target, pos) + except ValueError: + return len(value) + + +def _pythonize(value: str) -> t.Union[None, bool, int, float, str]: + if value in _PYTHON_CONSTANTS: + return _PYTHON_CONSTANTS[value] + for convert in int, float: + try: + return convert(value) # type: ignore + except ValueError: + pass + if value[:1] == value[-1:] and value[0] in "\"'": + value = value[1:-1] + return str(value) + + +def parse_converter_args(argstr: str) -> t.Tuple[t.Tuple, t.Dict[str, t.Any]]: + argstr += "," + args = [] + kwargs = {} + + for item in _converter_args_re.finditer(argstr): + value = item.group("stringval") + if value is None: + value = item.group("value") + value = _pythonize(value) + if not item.group("name"): + args.append(value) + else: + name = item.group("name") + kwargs[name] = value + + return tuple(args), kwargs + + +class RuleFactory: + """As soon as you have more complex URL setups it's a good idea to use rule + factories to avoid repetitive tasks. Some of them are builtin, others can + be added by subclassing `RuleFactory` and overriding `get_rules`. + """ + + def get_rules(self, map: "Map") -> t.Iterable["Rule"]: + """Subclasses of `RuleFactory` have to override this method and return + an iterable of rules.""" + raise NotImplementedError() + + +class Subdomain(RuleFactory): + """All URLs provided by this factory have the subdomain set to a + specific domain. For example if you want to use the subdomain for + the current language this can be a good setup:: + + url_map = Map([ + Rule('/', endpoint='#select_language'), + Subdomain('', [ + Rule('/', endpoint='index'), + Rule('/about', endpoint='about'), + Rule('/help', endpoint='help') + ]) + ]) + + All the rules except for the ``'#select_language'`` endpoint will now + listen on a two letter long subdomain that holds the language code + for the current request. + """ + + def __init__(self, subdomain: str, rules: t.Iterable[RuleFactory]) -> None: + self.subdomain = subdomain + self.rules = rules + + def get_rules(self, map: "Map") -> t.Iterator["Rule"]: + for rulefactory in self.rules: + for rule in rulefactory.get_rules(map): + rule = rule.empty() + rule.subdomain = self.subdomain + yield rule + + +class Submount(RuleFactory): + """Like `Subdomain` but prefixes the URL rule with a given string:: + + url_map = Map([ + Rule('/', endpoint='index'), + Submount('/blog', [ + Rule('/', endpoint='blog/index'), + Rule('/entry/', endpoint='blog/show') + ]) + ]) + + Now the rule ``'blog/show'`` matches ``/blog/entry/``. + """ + + def __init__(self, path: str, rules: t.Iterable[RuleFactory]) -> None: + self.path = path.rstrip("/") + self.rules = rules + + def get_rules(self, map: "Map") -> t.Iterator["Rule"]: + for rulefactory in self.rules: + for rule in rulefactory.get_rules(map): + rule = rule.empty() + rule.rule = self.path + rule.rule + yield rule + + +class EndpointPrefix(RuleFactory): + """Prefixes all endpoints (which must be strings for this factory) with + another string. This can be useful for sub applications:: + + url_map = Map([ + Rule('/', endpoint='index'), + EndpointPrefix('blog/', [Submount('/blog', [ + Rule('/', endpoint='index'), + Rule('/entry/', endpoint='show') + ])]) + ]) + """ + + def __init__(self, prefix: str, rules: t.Iterable[RuleFactory]) -> None: + self.prefix = prefix + self.rules = rules + + def get_rules(self, map: "Map") -> t.Iterator["Rule"]: + for rulefactory in self.rules: + for rule in rulefactory.get_rules(map): + rule = rule.empty() + rule.endpoint = self.prefix + rule.endpoint + yield rule + + +class RuleTemplate: + """Returns copies of the rules wrapped and expands string templates in + the endpoint, rule, defaults or subdomain sections. + + Here a small example for such a rule template:: + + from werkzeug.routing import Map, Rule, RuleTemplate + + resource = RuleTemplate([ + Rule('/$name/', endpoint='$name.list'), + Rule('/$name/', endpoint='$name.show') + ]) + + url_map = Map([resource(name='user'), resource(name='page')]) + + When a rule template is called the keyword arguments are used to + replace the placeholders in all the string parameters. + """ + + def __init__(self, rules: t.Iterable["Rule"]) -> None: + self.rules = list(rules) + + def __call__(self, *args: t.Any, **kwargs: t.Any) -> "RuleTemplateFactory": + return RuleTemplateFactory(self.rules, dict(*args, **kwargs)) + + +class RuleTemplateFactory(RuleFactory): + """A factory that fills in template variables into rules. Used by + `RuleTemplate` internally. + + :internal: + """ + + def __init__( + self, rules: t.Iterable[RuleFactory], context: t.Dict[str, t.Any] + ) -> None: + self.rules = rules + self.context = context + + def get_rules(self, map: "Map") -> t.Iterator["Rule"]: + for rulefactory in self.rules: + for rule in rulefactory.get_rules(map): + new_defaults = subdomain = None + if rule.defaults: + new_defaults = {} + for key, value in rule.defaults.items(): + if isinstance(value, str): + value = Template(value).substitute(self.context) + new_defaults[key] = value + if rule.subdomain is not None: + subdomain = Template(rule.subdomain).substitute(self.context) + new_endpoint = rule.endpoint + if isinstance(new_endpoint, str): + new_endpoint = Template(new_endpoint).substitute(self.context) + yield Rule( + Template(rule.rule).substitute(self.context), + new_defaults, + subdomain, + rule.methods, + rule.build_only, + new_endpoint, + rule.strict_slashes, + ) + + +def _prefix_names(src: str) -> ast.stmt: + """ast parse and prefix names with `.` to avoid collision with user vars""" + tree = ast.parse(src).body[0] + if isinstance(tree, ast.Expr): + tree = tree.value # type: ignore + for node in ast.walk(tree): + if isinstance(node, ast.Name): + node.id = f".{node.id}" + return tree + + +_CALL_CONVERTER_CODE_FMT = "self._converters[{elem!r}].to_url()" +_IF_KWARGS_URL_ENCODE_CODE = """\ +if kwargs: + params = self._encode_query_vars(kwargs) + q = "?" if params else "" +else: + q = params = "" +""" +_IF_KWARGS_URL_ENCODE_AST = _prefix_names(_IF_KWARGS_URL_ENCODE_CODE) +_URL_ENCODE_AST_NAMES = (_prefix_names("q"), _prefix_names("params")) + + +class Rule(RuleFactory): + """A Rule represents one URL pattern. There are some options for `Rule` + that change the way it behaves and are passed to the `Rule` constructor. + Note that besides the rule-string all arguments *must* be keyword arguments + in order to not break the application on Werkzeug upgrades. + + `string` + Rule strings basically are just normal URL paths with placeholders in + the format ```` where the converter and the + arguments are optional. If no converter is defined the `default` + converter is used which means `string` in the normal configuration. + + URL rules that end with a slash are branch URLs, others are leaves. + If you have `strict_slashes` enabled (which is the default), all + branch URLs that are matched without a trailing slash will trigger a + redirect to the same URL with the missing slash appended. + + The converters are defined on the `Map`. + + `endpoint` + The endpoint for this rule. This can be anything. A reference to a + function, a string, a number etc. The preferred way is using a string + because the endpoint is used for URL generation. + + `defaults` + An optional dict with defaults for other rules with the same endpoint. + This is a bit tricky but useful if you want to have unique URLs:: + + url_map = Map([ + Rule('/all/', defaults={'page': 1}, endpoint='all_entries'), + Rule('/all/page/', endpoint='all_entries') + ]) + + If a user now visits ``http://example.com/all/page/1`` they will be + redirected to ``http://example.com/all/``. If `redirect_defaults` is + disabled on the `Map` instance this will only affect the URL + generation. + + `subdomain` + The subdomain rule string for this rule. If not specified the rule + only matches for the `default_subdomain` of the map. If the map is + not bound to a subdomain this feature is disabled. + + Can be useful if you want to have user profiles on different subdomains + and all subdomains are forwarded to your application:: + + url_map = Map([ + Rule('/', subdomain='', endpoint='user/homepage'), + Rule('/stats', subdomain='', endpoint='user/stats') + ]) + + `methods` + A sequence of http methods this rule applies to. If not specified, all + methods are allowed. For example this can be useful if you want different + endpoints for `POST` and `GET`. If methods are defined and the path + matches but the method matched against is not in this list or in the + list of another rule for that path the error raised is of the type + `MethodNotAllowed` rather than `NotFound`. If `GET` is present in the + list of methods and `HEAD` is not, `HEAD` is added automatically. + + `strict_slashes` + Override the `Map` setting for `strict_slashes` only for this rule. If + not specified the `Map` setting is used. + + `merge_slashes` + Override :attr:`Map.merge_slashes` for this rule. + + `build_only` + Set this to True and the rule will never match but will create a URL + that can be build. This is useful if you have resources on a subdomain + or folder that are not handled by the WSGI application (like static data) + + `redirect_to` + If given this must be either a string or callable. In case of a + callable it's called with the url adapter that triggered the match and + the values of the URL as keyword arguments and has to return the target + for the redirect, otherwise it has to be a string with placeholders in + rule syntax:: + + def foo_with_slug(adapter, id): + # ask the database for the slug for the old id. this of + # course has nothing to do with werkzeug. + return f'foo/{Foo.get_slug_for_id(id)}' + + url_map = Map([ + Rule('/foo/', endpoint='foo'), + Rule('/some/old/url/', redirect_to='foo/'), + Rule('/other/old/url/', redirect_to=foo_with_slug) + ]) + + When the rule is matched the routing system will raise a + `RequestRedirect` exception with the target for the redirect. + + Keep in mind that the URL will be joined against the URL root of the + script so don't use a leading slash on the target URL unless you + really mean root of that domain. + + `alias` + If enabled this rule serves as an alias for another rule with the same + endpoint and arguments. + + `host` + If provided and the URL map has host matching enabled this can be + used to provide a match rule for the whole host. This also means + that the subdomain feature is disabled. + + `websocket` + If ``True``, this rule is only matches for WebSocket (``ws://``, + ``wss://``) requests. By default, rules will only match for HTTP + requests. + + .. versionchanged:: 2.1 + Percent-encoded newlines (``%0a``), which are decoded by WSGI + servers, are considered when routing instead of terminating the + match early. + + .. versionadded:: 1.0 + Added ``websocket``. + + .. versionadded:: 1.0 + Added ``merge_slashes``. + + .. versionadded:: 0.7 + Added ``alias`` and ``host``. + + .. versionchanged:: 0.6.1 + ``HEAD`` is added to ``methods`` if ``GET`` is present. + """ + + def __init__( + self, + string: str, + defaults: t.Optional[t.Mapping[str, t.Any]] = None, + subdomain: t.Optional[str] = None, + methods: t.Optional[t.Iterable[str]] = None, + build_only: bool = False, + endpoint: t.Optional[str] = None, + strict_slashes: t.Optional[bool] = None, + merge_slashes: t.Optional[bool] = None, + redirect_to: t.Optional[t.Union[str, t.Callable[..., str]]] = None, + alias: bool = False, + host: t.Optional[str] = None, + websocket: bool = False, + ) -> None: + if not string.startswith("/"): + raise ValueError("urls must start with a leading slash") + self.rule = string + self.is_leaf = not string.endswith("/") + self.is_branch = string.endswith("/") + + self.map: "Map" = None # type: ignore + self.strict_slashes = strict_slashes + self.merge_slashes = merge_slashes + self.subdomain = subdomain + self.host = host + self.defaults = defaults + self.build_only = build_only + self.alias = alias + self.websocket = websocket + + if methods is not None: + if isinstance(methods, str): + raise TypeError("'methods' should be a list of strings.") + + methods = {x.upper() for x in methods} + + if "HEAD" not in methods and "GET" in methods: + methods.add("HEAD") + + if websocket and methods - {"GET", "HEAD", "OPTIONS"}: + raise ValueError( + "WebSocket rules can only use 'GET', 'HEAD', and 'OPTIONS' methods." + ) + + self.methods = methods + self.endpoint: str = endpoint # type: ignore + self.redirect_to = redirect_to + + if defaults: + self.arguments = set(map(str, defaults)) + else: + self.arguments = set() + + self._converters: t.Dict[str, "BaseConverter"] = {} + self._trace: t.List[t.Tuple[bool, str]] = [] + self._parts: t.List[RulePart] = [] + + def empty(self) -> "Rule": + """ + Return an unbound copy of this rule. + + This can be useful if want to reuse an already bound URL for another + map. See ``get_empty_kwargs`` to override what keyword arguments are + provided to the new copy. + """ + return type(self)(self.rule, **self.get_empty_kwargs()) + + def get_empty_kwargs(self) -> t.Mapping[str, t.Any]: + """ + Provides kwargs for instantiating empty copy with empty() + + Use this method to provide custom keyword arguments to the subclass of + ``Rule`` when calling ``some_rule.empty()``. Helpful when the subclass + has custom keyword arguments that are needed at instantiation. + + Must return a ``dict`` that will be provided as kwargs to the new + instance of ``Rule``, following the initial ``self.rule`` value which + is always provided as the first, required positional argument. + """ + defaults = None + if self.defaults: + defaults = dict(self.defaults) + return dict( + defaults=defaults, + subdomain=self.subdomain, + methods=self.methods, + build_only=self.build_only, + endpoint=self.endpoint, + strict_slashes=self.strict_slashes, + redirect_to=self.redirect_to, + alias=self.alias, + host=self.host, + ) + + def get_rules(self, map: "Map") -> t.Iterator["Rule"]: + yield self + + def refresh(self) -> None: + """Rebinds and refreshes the URL. Call this if you modified the + rule in place. + + :internal: + """ + self.bind(self.map, rebind=True) + + def bind(self, map: "Map", rebind: bool = False) -> None: + """Bind the url to a map and create a regular expression based on + the information from the rule itself and the defaults from the map. + + :internal: + """ + if self.map is not None and not rebind: + raise RuntimeError(f"url rule {self!r} already bound to map {self.map!r}") + self.map = map + if self.strict_slashes is None: + self.strict_slashes = map.strict_slashes + if self.merge_slashes is None: + self.merge_slashes = map.merge_slashes + if self.subdomain is None: + self.subdomain = map.default_subdomain + self.compile() + + def get_converter( + self, + variable_name: str, + converter_name: str, + args: t.Tuple, + kwargs: t.Mapping[str, t.Any], + ) -> "BaseConverter": + """Looks up the converter for the given parameter. + + .. versionadded:: 0.9 + """ + if converter_name not in self.map.converters: + raise LookupError(f"the converter {converter_name!r} does not exist") + return self.map.converters[converter_name](self.map, *args, **kwargs) + + def _encode_query_vars(self, query_vars: t.Mapping[str, t.Any]) -> str: + return url_encode( + query_vars, + charset=self.map.charset, + sort=self.map.sort_parameters, + key=self.map.sort_key, + ) + + def _parse_rule(self, rule: str) -> t.Iterable[RulePart]: + content = "" + static = True + argument_weights = [] + static_weights: t.List[t.Tuple[int, int]] = [] + final = False + + pos = 0 + while pos < len(rule): + match = _part_re.match(rule, pos) + if match is None: + raise ValueError(f"malformed url rule: {rule!r}") + + data = match.groupdict() + if data["static"] is not None: + static_weights.append((len(static_weights), -len(data["static"]))) + self._trace.append((False, data["static"])) + content += data["static"] if static else re.escape(data["static"]) + + if data["variable"] is not None: + if static: + # Switching content to represent regex, hence the need to escape + content = re.escape(content) + static = False + c_args, c_kwargs = parse_converter_args(data["arguments"] or "") + convobj = self.get_converter( + data["variable"], data["converter"] or "default", c_args, c_kwargs + ) + self._converters[data["variable"]] = convobj + self.arguments.add(data["variable"]) + if not convobj.part_isolating: + final = True + content += f"({convobj.regex})" + argument_weights.append(convobj.weight) + self._trace.append((True, data["variable"])) + + if data["slash"] is not None: + self._trace.append((False, "/")) + if final: + content += "/" + else: + if not static: + content += r"\Z" + weight = Weighting( + -len(static_weights), + static_weights, + -len(argument_weights), + argument_weights, + ) + yield RulePart( + content=content, + final=final, + static=static, + suffixed=False, + weight=weight, + ) + content = "" + static = True + argument_weights = [] + static_weights = [] + final = False + + pos = match.end() + + suffixed = False + if final and content[-1] == "/": + # If a converter is part_isolating=False (matches slashes) and ends with a + # slash, augment the regex to support slash redirects. + suffixed = True + content = content[:-1] + "(? None: + """Compiles the regular expression and stores it.""" + assert self.map is not None, "rule not bound" + + if self.map.host_matching: + domain_rule = self.host or "" + else: + domain_rule = self.subdomain or "" + self._parts = [] + self._trace = [] + self._converters = {} + if domain_rule == "": + self._parts = [ + RulePart( + content="", + final=False, + static=True, + suffixed=False, + weight=Weighting(0, [], 0, []), + ) + ] + else: + self._parts.extend(self._parse_rule(domain_rule)) + self._trace.append((False, "|")) + rule = self.rule + if self.merge_slashes: + rule = re.sub("/{2,}?", "/", self.rule) + self._parts.extend(self._parse_rule(rule)) + + self._build: t.Callable[..., t.Tuple[str, str]] + self._build = self._compile_builder(False).__get__(self, None) + self._build_unknown: t.Callable[..., t.Tuple[str, str]] + self._build_unknown = self._compile_builder(True).__get__(self, None) + + @staticmethod + def _get_func_code(code: CodeType, name: str) -> t.Callable[..., t.Tuple[str, str]]: + globs: t.Dict[str, t.Any] = {} + locs: t.Dict[str, t.Any] = {} + exec(code, globs, locs) + return locs[name] # type: ignore + + def _compile_builder( + self, append_unknown: bool = True + ) -> t.Callable[..., t.Tuple[str, str]]: + defaults = self.defaults or {} + dom_ops: t.List[t.Tuple[bool, str]] = [] + url_ops: t.List[t.Tuple[bool, str]] = [] + + opl = dom_ops + for is_dynamic, data in self._trace: + if data == "|" and opl is dom_ops: + opl = url_ops + continue + # this seems like a silly case to ever come up but: + # if a default is given for a value that appears in the rule, + # resolve it to a constant ahead of time + if is_dynamic and data in defaults: + data = self._converters[data].to_url(defaults[data]) + opl.append((False, data)) + elif not is_dynamic: + opl.append( + (False, url_quote(_to_bytes(data, self.map.charset), safe="/:|+")) + ) + else: + opl.append((True, data)) + + def _convert(elem: str) -> ast.stmt: + ret = _prefix_names(_CALL_CONVERTER_CODE_FMT.format(elem=elem)) + ret.args = [ast.Name(str(elem), ast.Load())] # type: ignore # str for py2 + return ret + + def _parts(ops: t.List[t.Tuple[bool, str]]) -> t.List[ast.AST]: + parts = [ + _convert(elem) if is_dynamic else ast.Str(s=elem) + for is_dynamic, elem in ops + ] + parts = parts or [ast.Str("")] + # constant fold + ret = [parts[0]] + for p in parts[1:]: + if isinstance(p, ast.Str) and isinstance(ret[-1], ast.Str): + ret[-1] = ast.Str(ret[-1].s + p.s) + else: + ret.append(p) + return ret + + dom_parts = _parts(dom_ops) + url_parts = _parts(url_ops) + if not append_unknown: + body = [] + else: + body = [_IF_KWARGS_URL_ENCODE_AST] + url_parts.extend(_URL_ENCODE_AST_NAMES) + + def _join(parts: t.List[ast.AST]) -> ast.AST: + if len(parts) == 1: # shortcut + return parts[0] + return ast.JoinedStr(parts) + + body.append( + ast.Return(ast.Tuple([_join(dom_parts), _join(url_parts)], ast.Load())) + ) + + pargs = [ + elem + for is_dynamic, elem in dom_ops + url_ops + if is_dynamic and elem not in defaults + ] + kargs = [str(k) for k in defaults] + + func_ast: ast.FunctionDef = _prefix_names("def _(): pass") # type: ignore + func_ast.name = f"" + func_ast.args.args.append(ast.arg(".self", None)) + for arg in pargs + kargs: + func_ast.args.args.append(ast.arg(arg, None)) + func_ast.args.kwarg = ast.arg(".kwargs", None) + for _ in kargs: + func_ast.args.defaults.append(ast.Str("")) + func_ast.body = body + + # use `ast.parse` instead of `ast.Module` for better portability + # Python 3.8 changes the signature of `ast.Module` + module = ast.parse("") + module.body = [func_ast] + + # mark everything as on line 1, offset 0 + # less error-prone than `ast.fix_missing_locations` + # bad line numbers cause an assert to fail in debug builds + for node in ast.walk(module): + if "lineno" in node._attributes: + node.lineno = 1 + if "end_lineno" in node._attributes: + node.end_lineno = node.lineno # type: ignore[attr-defined] + if "col_offset" in node._attributes: + node.col_offset = 0 + if "end_col_offset" in node._attributes: + node.end_col_offset = node.col_offset # type: ignore[attr-defined] + + code = compile(module, "", "exec") + return self._get_func_code(code, func_ast.name) + + def build( + self, values: t.Mapping[str, t.Any], append_unknown: bool = True + ) -> t.Optional[t.Tuple[str, str]]: + """Assembles the relative url for that rule and the subdomain. + If building doesn't work for some reasons `None` is returned. + + :internal: + """ + try: + if append_unknown: + return self._build_unknown(**values) + else: + return self._build(**values) + except ValidationError: + return None + + def provides_defaults_for(self, rule: "Rule") -> bool: + """Check if this rule has defaults for a given rule. + + :internal: + """ + return bool( + not self.build_only + and self.defaults + and self.endpoint == rule.endpoint + and self != rule + and self.arguments == rule.arguments + ) + + def suitable_for( + self, values: t.Mapping[str, t.Any], method: t.Optional[str] = None + ) -> bool: + """Check if the dict of values has enough data for url generation. + + :internal: + """ + # if a method was given explicitly and that method is not supported + # by this rule, this rule is not suitable. + if ( + method is not None + and self.methods is not None + and method not in self.methods + ): + return False + + defaults = self.defaults or () + + # all arguments required must be either in the defaults dict or + # the value dictionary otherwise it's not suitable + for key in self.arguments: + if key not in defaults and key not in values: + return False + + # in case defaults are given we ensure that either the value was + # skipped or the value is the same as the default value. + if defaults: + for key, value in defaults.items(): + if key in values and value != values[key]: + return False + + return True + + def build_compare_key(self) -> t.Tuple[int, int, int]: + """The build compare key for sorting. + + :internal: + """ + return (1 if self.alias else 0, -len(self.arguments), -len(self.defaults or ())) + + def __eq__(self, other: object) -> bool: + return isinstance(other, type(self)) and self._trace == other._trace + + __hash__ = None # type: ignore + + def __str__(self) -> str: + return self.rule + + def __repr__(self) -> str: + if self.map is None: + return f"<{type(self).__name__} (unbound)>" + parts = [] + for is_dynamic, data in self._trace: + if is_dynamic: + parts.append(f"<{data}>") + else: + parts.append(data) + parts = "".join(parts).lstrip("|") + methods = f" ({', '.join(self.methods)})" if self.methods is not None else "" + return f"<{type(self).__name__} {parts!r}{methods} -> {self.endpoint}>" diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/sansio/__init__.py b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/sansio/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/sansio/__pycache__/__init__.cpython-311.pyc b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/sansio/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d60acddd59445b0d581a4542007902d18d76a3e0 GIT binary patch literal 233 zcmXwzOAbLn5JkHQ5sBD<4H!@nQ!z0YVgWBj7dr3tC;jn@eOQ5rby)z3nUQ`8cW~-f z)g9bhtJP#l&ud4N^HJh2{xk1-G8ZP;f_3@y&Riw!@ACOM!`3JQM{AkG1g#H76U-y( z5Y>#FiUET(dWHgoS$UuXDqE0;b%8#I9qAnuatZbrwnh*8g8_IMsAqAN<0j?y}sh@S=YA{URBQdi~ZrM>GlOW5kXV{ literal 0 HcmV?d00001 diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/sansio/__pycache__/http.cpython-311.pyc b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/sansio/__pycache__/http.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a52bdaffa2db89162151d6b5018910bea5c19799 GIT binary patch literal 5836 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 literal 0 HcmV?d00001 diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/sansio/__pycache__/request.cpython-311.pyc b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/sansio/__pycache__/request.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2d9ddebbaaf5d7ca57ed6a17df9a8992e80c192c GIT binary patch literal 24016 zcmc(Hd2Ae4nqT!rHqUMz;;Ey=Lp($_MctxK>ZYv27AYS+JJW1-70I^xpsT7ysV&)> zStC()9IR$nyJlwOm_f9TCN={7VFU((ATvP%BmoBL7J4J55d&W2k7R-kLc^Y5pnsCz z_p0hu6^oMXovYP){puZGz4v|J`>yvM|5;sKwSdq6`Kyi6f4(RP|D7JpkKf3A`H4pm z-V-E2@}z{Q2fyBwH|>pjJ%Z$u{3&1BAN8|*AQebgL@QXn0{O~lCCgVLAB+ZBK8So( zw2I}ckgtwbvwSu3HPITDuR*>xTFdga$k#>dSiTPV`e;4N*CQW_hFCs?d^j3r`7rVg z(FT@pK)x~B$nuTIH$|IRzA4q5-Voiu^3BM%L|a&X1M;oWR+eu;zAf6u@~z0XN84Gx z4f&2}2g|o3-x=*>`3~eaMmMs2C-R%3n^=A$@|&ZZS$p;mt=*qReE+(CJ>bqvzOzUIhi_7q|-SqK9iEqCsnFz zc7uv^1n{C?Vj&apYjwzpZ1!rB;DqMzjzmsT04atypeRU1^I26(5DS@~MkDj( zKdvB=ATx0g^0l6YtDF7$2EK5uzLAP0~53)m6S3<%EHCXAQV*t{z)h4|-?2t4?Gi*owLi zS6vtH*XgS3=5-rgbv?XpldG%^?cm> zp%2)^fBh33WU%ejaZp#<=7PN41-Vb^UkCD#tKEP!xK6uaSG(=f&^qlR)J__fg7SzI zVf6vb1pkeh>|pgHsBe*WtW!TKotH+zTRS;#?R3p;7pn`tzT1QJ5afbCci?JPX)lImdopZeZsH?{dtVbBo zj=Ad2yP#cgKpS`Uxai{0LMW_aR~2t|m3?5r3U&rVIfC@Q*D zF(f84iBwKXX6C4MwTMqzhHWGbiB~S4N9}BONQ`GBQP;3)q?)yvnop`?A{AFv5t&R@ zL$7#76KAuE2sJG$@syZT<%l?;iRyedmy*P6CUrxck*Qb`7w6>+1C&fJq~tV4OCxw? z^324LIL=Z4iUaPvtmp%dFD#^z31+W}>Vlkzh_A@X)x4aWV{l*$TuVx_iun-~k;Bz{ z#5Hj&ky6K|7mZGlMFkLH*F_B6O&c>C1E%6>0|{f|6i}!mgkjS_o46QP#Wd_faWSdQ z4~c3nF)zkdHWEVN^z@liQ_}!R4+caPMps(aArfl!XCeua3FFtYiEPU87DAN{oI}QY zr>EyNZ9$!$7J;Mb=|vS;=bL2Znylz=ic1o>fl;1b12YO$5p?S(n`cN|$ST^v^z;ya z0csZFG^dN%jEova(p)BaT~xD)tFopJ>93_~)Fx*c^{-~Kiy6ltlx$Xu5hQ&a;K^CQ z(B?r28i<{o;GlFim(jo%xeRP=9S)(6u^Fn-dqGB(Bx+fMzo<3Ph~doSG?NL5%L$`O zHmB*#Oin4$4*nw8u8`A5>kJzKK*4YYAk|m`mDR=>~g8 zA|e<-g;@?5vNSd(?u_hU#LowE+U)TDawTw`iYMf9D_aRN@PK({6*w+2w&Kf;5*(Vf zWy}F^whM!Ql^FWL*k(O1mg_Vnr-F3M;#SKQ`qC;_=!*frj6cg&9PMR45vp8k$9K8X zKw7!RhL3WEB5OG%li>C^v^L3GvTnZoTO{uZH$BVvb$^^TT%U z;?~kl|1y{7W_$XTJRhR}M-~hr#_bZxb2G3EpThlF+bfSIcw42}@@55|g8ZoxPkg2~ zgLq;A4yu;~Xa(=Zr=F34nsOjVVor&mTp6Kc2NK|lSa(4gWe+L+VJB7ZqJ#_{We<{p zsL&*j;Rol-eVxuSdfTZA(2LZ2$502sO z-0_!!?Vq=9MnUc8o!jWKt-!ut;>ALOvj-lC$c#{$%D_Je6c}cT9Zh8F=bB^wcBfz2bNB-tQh62$X&B zsmoQTu2Yx=WG(x(8w)ZKA`?%`u^8i`0iQx{rg9QV*(0ko0&t6Rn4ZxT;zx1POfOkN zy?Px9Xy)}*^{+Ix-0Qz{;?9X*e->(66+Bh_R(;C?9;#0$o%(fd{%vEOurRE z__*gUb}#vVGS-8proxlm7dt=acPvj?&<`8pg|iP+2YfNp+9!Ud0yQ2Z0`+ zgN#H{Mn-B0X=5WIQ0U3OJq2x$&?8N{Duix>4V6nn?vv?63N0d@lxO3J8^M0dGDQHS z=PYdo^)?pQG-YhsfMi-OwB+34eMe+3*X;;*R2rRr>S0t+X~+idYbfMmROijGQ^Z!d zjJ9em5>TLC2sgibvk>f5j-udc6OUbe2eJe7?nff^Ed3^gwz^PZsDxF2ZZPsnpV zN1k&h@|-6>_Bc&x5_a*o0fMj#okjK~DPXR?RI#7bFf$I48HQ4)f@UHE{K^rPjw@FO z42?->J$^%!bfUm0<*i^*f_P)oZ<#_Ug4kIcQ_m%!h-5Y54g*f}&Y--muZZ+YHjQDS zXf~Hg(8`Gzwof9SA)6UgLC{SSXKp~XYh){PI0i59$vXA&H1SYel#;WvG6lUvT9792 zbaHN9fVW%$O_NpB zvJbZQKtS0HTI&ks0&?Y$oIyMfgOBOWlmV(gkHpS~{)I)5(H!Y(K&4xS`_(P z6XfrKZi{gc()8g(`8tBN&rLiuEtM<_kE0j+l|SCcDhsM@YY3U6qBqa=jN&BC=kqSGE(@~ zx^p48YBrZjG2blTU}BdS{RcwrppM!Kp{e!WKp}J<-}mR1rP8*2pEj2cOcXbtD{Vff zmluNP-54PmPP6{98Hp1klII#TPxG%%Pfsa1!-phcqv)oa)3*k9sxl(>mWRywtDgcH zljM!QGC2upO)`i+N9gTkAN=BSfO*OT9^LX?D`~2dYD@=i;ei+Z0zg#S24E}(W%p)b zbEFW86oL`=e7XG=W%K<_v>P~t+iM>pXN5GG3Gp&1IU*KE7pXGtGwOcSkf=NNv(U35IlAzMz#^StElP+c&R{l7x{$~me+LrHlD`20)Qvh4tJfg07&=`Foh}4V zyAkL&5xDmoBhV>~8Pa;!N17fP#cE;E%?MnZHIsDBkCv8`Nz9V}N%L#d=Vxbz*4osq%o$H7n8DUQn`i!R_#dJ{Qv2wm-Jcx% znZI=G@_O>@=|;EV|8;KBGG0lxDE+!WpaHi?m$`#vZzp-3wx;vxmPg}pBp4xN@p3BK z>5Dv%O?~Wh$xna&<#CFmGp~n&fYX-dq2mN_F!VhJs3(2T9abhb`_*((GYQE_&LoNv zt$FG>Sgtr>?9wYQVPXT7W&i8hWQJLcRb}1 z8qzb#3<8YC`w3ZQaG}yD%ZE(0Wfc5hG*aoU(Dgwd|Ip+9L!Z7{ z>_1)VKV95%rnKcuA-M6gQ0GdmNStw=`5+?mKKh>NeLStDa~nX;@D#kS{Qsa8V=oa= zmDFww>rqW{HYsDfB#8(vwmYz+L5M*%uBe>R-!dPU98kC6r;)h6ha-8wq^9$6 zO=n@_$fL$$&F)go?oX>8*Nhix#^ER4-Tis{ru&9EC=8Aj+Ygr758kO?scA3N^dd4@ zx8bwaj-}D}6U&j}=F!sT(PHb)QtQq_c;~M_Yu@}RT=TvMI11viS1}G3y6bVbJY3vwcABjR^1~d)^t3Z!h0ep5w ziyM;9niOnb77I&Y2#BW<)0;>omg0cyh_i(P*Ndvplm&AV3sO{ z6-iwIq5Q>{PoEG+_wU^ee1l9Gk-AGsY<931&DuqZ=nk91LQYx8I;Fe5Cr)?+6zm8N zYuRD^0E-Cn&B=JDCeJC*JcuvKiK}A&g^3dbhF;;Lv5_Sr_Fu&A;~0E$w4~9^WwUzV zTEaFV0+;lvL<0Np87-dTAan+UG6QFO=y(KCT**JBgM@=TaYx+S>)r&A#c2U>6dc2Fz1UyA& z50bMthFH9wh@pqQV7|`a+`}x*fl7@Sn%IBk@`MYgSWnrUl7J&mf#1dXBu)=dCqq6B zqYsOM===-rp!hW(L>FUV%%=++lp@b{PVa8#SfWlksbaLLlq?NI%KnouCi4Gp3a_D@ zc}l}MbJ}UKogE?bn_L|JYC2d8yxWFs@)FtlI8Zy6%%BerEnvA5H_)t9_yrt-DyI=N zP9{J;y~8kj30Q#Ge*ey$>n=4T{%zZ>CTi1%=0gzJ$g(r9g9~Zu^q9w9<8U0IPuZr7 zYA?jEn^8$M|6i%Qy<~lcunw2-d2mT&S-?}5$4{JM9^s`IrzS34oHVQ|j3$c^D{Nqf zj!Z5+BP$#b!mboPO)|YL&S5Hx@f*Z^Kr==Ejp!}wYN}gZaw1%R-SWqinHij+kPLQ= ztZ!+(1EsPyhydTuVN>x4( zMvOF$lEnxWitJ)Hv2s4H(oUDsNrl`XP;SxV8dt8|q=R!n%XgX@f1S!#N$nGq+rnp| zmb+SE)2?D@cPX@cRq$2Sf7X8pYWZI6ld$+WEG}<`B@o_S3h#!$H$XAuf%-cqOSQcP z_FeU%2kYkc(a8#70i1bc$jV*DulwVS-g_uejPdCo`~JfE-k-BK^UJ@mN4gAU%uqb7 z3t2Pf#W#+yOD4|^F?L2QjPs|lAqc5PPKT@|A_I)a<0;0ckd(4QZUr`#5aP$)0^7hh zH;{}Mb7%|**5~xJ2Id_(J9XjwL32m+Fgt%TjV&uI$}|=*Ypi=8GnNFIp6+9P=e2aI z&peewqCmGCeahRw8sm|2fQ`<@DnS#t6{#~zzDGp6r!32?tT;TcgMlDsmSFliX(oE9!3gv@Es(u}9oe5<)u z*Hp|CNqTgW)C$idNg=r1O$Lf*u=Pg)gSE2HT4U?4E4Rn#lx-l2FHS&+E-&JUR64%C zVg<{ybB~bRdLYc=XXi=9Vcot+CSdZxh1YN-%uohwx0ac^Fj}#xo4EmHW%&4feZ^B* zL+cv40taH$1>C`4d&o)JR~|=jWZDU79ilUf%rt+@mSpPsO{68Me#^U zJW>oDErpI2f=As<#iR0n^mk$^ClhJJqQ=LmNZFdto;P-9&2>Ti0Ddx$`r&*Rf~h0| zIKs^k-7^m+Soe-_h?E&k7J?NT4#M@#_^~0DjfhfghOU%CR|>%^ z&%yrlPwzZSh&uV*`NVkqUz!*|q9 zF?7fkZm$`DFwSU;bNS?gp(jne>A?w{gFplt?hOZK>^V4*Y1?$jhsJfXnZv>c(3)kl zRvwuvhL6j&VtCCDiD=zA(P|wlrm*`|vHNtX`*bmMrW8U*`pk1k_PZBRaRDPWDVQ=Y zIj&2=ZzZIh*Cnk*!^vZL`yeDcAGIU0hu6X)oM5plXfH$yj8o6Xu*hAFLmAEGQgO={ zLzk}=ojS#>(U9dyAI=Nru-$8zuA;ftu*nb{Z!U)5HGghf#f|GT#4qZLp;t&b zp2HF|7|1sZxI5P>wvkBl8K0X7$2)Wv%q;6F8JR};MQgk0nYzeO820aFGis|OeZy(Qfz_SHe`Ex?`S_Ibto!DfT z4J>oMb`(E)HmWZbI}epQ4;4d)OQFMs;Njmts+Y*og2PB>d>Omydl%C1Bt(O3@-4*{ zap=_<7PK+5OL6=b0*ejA=Z?{U;ai{2@D0S?MxU5UGsw4_vc^^QDbd9+2XOkjW2xu< z(36hA#~p)(?MI3oM@tOToKcN zzyQ!OcXn0B5%LIPzlR)&roFd3?t$dgu14ut(%7t{dhEmmFeoJz6Yc8KSvka z(~gMv!!G=2C?MKzE*keJ)Z7d^{bWBwBYb0x+M(FhL&S88riZL(k{oY75)>Mw5}PvcDm2HjcBm;m*>NP|xE~k4;ddes;Xrd#2QT zrWiU~3Y{$k&$_vqhx>jGpKJ58;=*i&N%^tYlb4Nn^o8Vggrh06f~y|nnZWgmv7Psr4g(m_-4Y(bX2Em) zRGhH`t+#}+ho}$hD@&35W^&P0)pY)b4W<}AtN$UV{C}C`LFPD}ZJABcJt!8LrZX1q zx9HLW`yj>=VaM$Fk^6*_rTg^^GbRv+EcvF>HeT~h=53D1KJNivxhj>#K|8ubrYsPD zyn!UY=^5y_!2ZlbzVvOep`>o+Ze~J1{aA7oc@PK3Y8(|532Z(Uu z413)2Ac*17&k!yQ;C47xg!{AhY;Cp~Exqn`xOwe!X(@mH2PfF+8skcx87jxZHV=57 zo4j<93C1B&#!Up<;eau-_1LuS4| z^V<4CW6$H-9=d_myAo>n(_4RXYw3H%&`>EfgcEV$-j#6Udo_Pnvvj5y?kR={3*B?j-%p+v2}pB zo*>dS#Ii>#`={sx0v)_7hhnjK27i8pomo=Rg;a=AM+x0tEBh(jscfNVrl06^c{zBA z-Fl9v%05(<15-I%Wmm3IRe+t$ROoo9a)goz*1=HqHp<$rVaoMN;<9WV0!S zVz|mlN~S59p+usD-ATz%ZYz>tCI5gDwny3nttUFoCOP_hmG)yH`2ZQH+9Z%ZJ!Dp0ah2({ckgIk2H z;_XYTEyB)|o`Sz~>81NGFHe*GUST51|B_($!Ii4>PNdi4i&l)JlkIiA6TuTCe=c7>uL?96+&_7SG>V zM`#;R|NZN*Rkc0)}Yz#{pI608eQu&M9v$Z~h7aob7|Z?H9OqNYvM zw5j6u3uqbcx%2jtS_*Zq1RH3q;~sOcVH#|h1{jls9APw*Z#$RU2+p-Fszx;uhJ+fmc1tkyU|y?I~}|D)dzw2uyhjTXqu2DF)a~d1F?g0}ccZEcH^;16E>dcfeSW zfxwZa9JM%NCAMZHr-_X#0^^?L(FbD>=S$uDhydUuTgrQ|3Y`)affJtPmmWkP1xvjc z13lVPTNhO5U4m${1UX8-_LR3}70ql!m4U-|-?*1w?k%Tv6(?8*|+q@{rtn>(w6;p>8R&<7bYWXh5D{Lv8BmU-PV<0c-8N%!_b*P z2nFl08jyTN$?eln6x;SJ$5w*)^DSMgeqY^b4|EPzSX1>jG(_?hB?W&M7H;o>)I~iS^PF>qXnGG(Rj9h^8gT7E@%g91WDMCr7ES?n%iH43;afvxR7k z@-Im7lQB}R!XFO8sRCSMP{{Q!*HhF2CY|wjJ#_O1hJj)7FnflXcFZbb1`IP5nRUr5 zAZ8?yWvLrAWDDV%HU5m7eqMli^K4&|?HI6qG8PtPeDeUPK+CNk($5!2kjT1dGES?k;24 z3m8TYci&&t&lytQ-R$C!!;M{S4bu%QUK?v%Xqs+f@jAqtr<+;49`P;HTUfjS@vYNa zS-cVPmgyE2Z$i9vx|PM75pSDrWAQDBZ=2r6;#(2lKE0jATM!qgMHX*GynVWz#oG|? znC@WlZHRYHce40)#Ji@uSX@NBd%By&+Y#R}y@SO&Vm%AJ)4hoINu6?^)FnHv?$oc+E52?-Wi8QqOCG)ccN85Z=Ro{+k|<9Fs!d&Y#lbc%LKhmBJ}G z6$i%;58SUtx{Hr#kj2v6zYi^o;S^ zcO{itoFVKXjc+lmB;^^Ne=C28P$gK~Pzy7k{#9$ULX9%NR%>~ai{`ru{!S~Zo$WWSVGj|qY;g-3tCX^M?u`1O2(Fy}N!k~MBCN9T-!wH!-6ml-!>l1mci z%2>P0&~8_%#w>N&x$IhYykGtf>i3>eM~qPKc;20qV{>`0PSi>-vPFPd<43Ko!~ajN zBlxz1W88{uU|#^nohTdqfU+GaqYOcKT`=*zW_fSG7hZS1gYw=p%3OBXxe4m&9?w^3 zWCGGFts}u@byTR1&hH4zLbC34$Lr3kMlsg~rNa9By5lH#{v=W=+m?kjqjVz!BJbe; zd&UcU?jv|l`RV_=j`4S$`Ep*vBsg3`Cw`YQfON_Z3W%L1sixxX+n+BFUP&y-gNi&C zy*4-+UQDLI1P7mA3dd7R3m0W&VF`>7yx?M38pKFQ4$7~F7Zzi3a&RsdPF@?lF2}DA z#-f)87jLGnB;rG%J$nX|(Ud%}2$lv8ksQ1sE7#KU()?gD98X3QD2lVK(BjR!FA<+v zTBNa_4$aVD)(2ncNGuTv$CAfFbKsfglaaoM8u1c>&u$C3y0(Jg3LIJMTJL(lU+vhB zD_7rq_xxJDTEBx|xtf-O;0PSabq_x3KJ>8rP^SBEw)^lM_g(+$RHnK^Wmmz4BrM%0 z0FC}uXx*T+Q?Lg?-i6xqxflwSz4Vqa%8)rL-d2VviJNMiZh1;`SRPh)RKDPJ`;NTb zR}gT!?`F5B2S4)*)#94_E&n5N_d{{_{rZf!KP&Ek;L8M0WP>ME?+Ile)x&c=99xoY zEX$4ej6Clk@@)sm(~+!L7EI!N-N}j3I&hX9Daxn)>_bBFFFWk{b#hD@%tJsaMFY!L zqMkz;L=nI^!*fM68I31X;dlfb7!$A3OdmDGA4J?lyl5gG6tp?w@sUbMehC5L9cXMQ z2p-?wTy;GW0jB_jj{{Aso;Q=3Kxa143Bp2(_8luH-#EWopYd-~+4Yn5{`E_l_MvS1 zP|+8L{C6D+5on)}(NEry%6li+lp2oZJ(uDbPs(9@&%0^T&%2^=BsiOrmGI@5ocHR3 z0LWkimOv>7C?H%VsW)atQRL(nuo0RvhL7Ep>aP$`Go9t0QOBL(f|5lFeg{N=AN#X z8}L+xr{HVucTiPxUYl-|8?QF$wU?^pDv4mYrJ4;=5)^`2Tk@@0Zni+FE5@z8AGH$d zOQA?#m=ln#xJH{yjG@*IXzdoAy7aqrRoWu^0NW>RwPDUm9a{-MCM+{1wQQQlC%1s6 zTIbwmYBMIa+VWY(gw(d_mu+&Jv>pG&O;b?s)JyH?;ca^Re1de1wXhW}{HMaMk~#o; zJENF3^mS)RFNy5iCbWqhT_ri%<#ro&_%sUW-XJB>Sozfsog%>lKyfBk0e?qHu1=>Q zcUifP)Fbu2-)EmTr@JYYv{UlRJLDc|7rtY4`yQ=u*>AeH1WLa&Py(foK?wm0_>(kP z0%d0jl-<&v5-7V$QavLLm861Z1FS~-QKOJNC>^)?aLM;)O8UFS)*nYo`eR5Qvh}x>R;*)=Mvu?RVNJLig&Zw~joR*&j!DN$@UV}4 zc~%-J`LdsVIU$Xfd^y0{b`Vr`vZSO#QcM~{AD_Z_q)}HUoyN!*X7p*!Dt3N^5o5Li zbZqb{>5Tc_ng%7G1>R)XWH()`r_(j|FJJ|(^Q{!8y*x%r+U08F1|UteZlUn%)|ri6B;r7xAx z&RGWI%M8X>N?@F0U%$$D+DysU=U55nE$x_;m*K2}3W)i;!ZWU`vY7vx)IUeBkVa2=x#Iq?ec_pzFlf*TpZ^x|a9W>}`)y%VE;__8FlIl0$vTDbo z$rO>=>}>Bf`DVY!Wc|L`SuwRl${8R4eQ+ZhixCY#<9r<&O%MX=6gB|O&gy08h1e>) z0F5~5sClQ*lSq$ZFTjxkn?Kz^cO*cykt?JVMx7J`J|`#EzcOUoWFJ-7-kE3l3s!sT zC>lTn(SR{Ko04Bm4WePaX!k-m1ub(@OhRQPa`0?qf&8i4lYRXRzE&3lh>i!=?}USh zB17fG($3Cm&0+d@bR^5A^_TS%N5k9_P-;U!ahr@%CxCN>4U)@C(U?S3Mvdofol_D- zU^EF6vJt2IKO(Sb$( zR}iWKQlpIL%Q*Gtz52(z-`Xgy!8F#L$U>Z+bc3J390MskWzk12JFVJb`-)caLbof- z7#i*P&Sht^-l|5j%X+r7wsgzIT38FzW%un}%dXqi5-FXGvaUOnay(P5+3%KX+4Wlu zDWCof1(XZljaJ%gPN{FXm)*ZrvFx@&m7JsVuQH763e|`8igJcxv~0_lYowa5(mRp&mvRC&(oAZ-kF(bJer!BN!REMHl)9bQ~Gxi+!lV;EchGm%)fafQ`wWP>>=$}f3Bs2 zG*~O=b2ZJR?CO8q(((I;?j7PUf6~&ib|BNTE8DV*UOcI6QY*LTf=z00``Y+}t`GXv zu2Z;jE!(p#yYG0}m(Is6yVlR#m%bm%w2Wq3M)A6(6Ywc-XKvR4rZC%<@%QE`YgUfs z8n$E`dRI>68e3P~Z&a?}AJ_d@C{Li~GO9d*2w7TebSc=?i&u@D--Qj=+NXGSz3E|k z{LdXn(oXS9Y3Emt^||tH>b5+vNG6||8HRr7@OBdF2%3pz1yg?!n)$1LNoh%42Yqz# z(6{%ly1zMmcQ{+OLk%9n19;48EsI-k#4r?PRjmUCfs%24QN4k9q+=Hzvv>A;} zkYSpG<4X&dWhF#LEm$dORRK#JjFc}@ohAM*`TO; zMON9on_$}X$utC6mFp29Z8C-iV*E!#gX?r1oSdDUoJs$hq?{V@D$|H>T$^uGQeK-U zjaxS-kADK{`20_(E7wq0j5SvIbkG=A99zFnRU?C%hNw4|Rc{N!lS1lg8=8n)Ff=_t zq==_FtUT4KXiphD2i8AB_Xf+I+s?m|!Bi!{YCow+)&321$(jX{o~~3W|7CQEm9rsk zl*+iAb3EO2Hje2lD(P#gurj|yt4Zj&8H-~&ZO`SKDLJXoNL8rhypOoxn4%<lsnVqBAoue6V4=BoiN6I$#sO;in zl1Nkkxe;MK_uoNcPwP2*pD4kUG24`48g@xz{-CcI=~NE;+2P!BE<4vOkn~h!@k39e zrlzk0EUh$BwBNGJ&J!i)HP$ZF42uK(Cl#gohQlY=Y7V`+DKFU}-g3vAI)zk~4oeWa zVA+JE#C#v4#i;!xTT^@NWOgI@Ng^A_qDck6Z3OYbWm|0!`5 zg|L=x7+Y9O-7Kd1v~#!kxkF5Q#Y^MQjZeHZ-dD+o0Z~1te;PpwQEJ|kgg^yTPfU*I zD_$ha92*e|v5>rHiG-uP_c{3{d#lg<%+N(cAn#|{^>ITSvuFvRT*kX}qrs(Y1MO`} zzYp!XEj+GlxFf$g&Fw5SPPRX;hq>#U=kA_c8GBsQyxKsf#Z1jWwq{_ZEZ5Zbo#k&W zubo_P&ouRCoBCJ$KdG!*SFY9sCw1a#*`1?zj;^(%Bhu8D7}f=8!`iyb3kpNi;}FIg8kPQLcuimHQxEU8thQL9my`B?*~H- zCw#&WeKjZATt94ePu%UGJ=Dk>R}wK<WT>g5PNbY803A9K$iUjp85v!6&CI@&DopMEJ zZxj$9b7(OYEJcW$o#nZXc7(K~p-5t(11broSJ1$-v4A>4?GoLz51@9C$iy#RW?De4 zA4T)Md0|z?Yq*AOY)VL@{ zmW~o_aB$u=EJA%VJ3FN;$)p&w&?-ZWu7M0+4@YCvY6A*EyB-ZQVP>)fxXuuV#gow_ z6sCzA;yJ*Wj407X^kgIg`lWs~%PRq)bJAh)JW(M_nvB4$uZRT!idIWo-N1p!n~|x@ z6!~jQpmR;57FJ}jgP;!(qwX*ZPFGCIsew`M{aL|80A&$eR{0tQ2EFH|3A5umM0fd}04#5|A!~e+J{LtH+^EW^CH|479a*Zvy=IwMfY{6oIt}X4k z=GJ@u+?KZNmH|8!n*4#Pj|BvuQt+`S=&SftKv1v;eD!Tv84FZp7yf_pECQPQ)^IQV zEj!>_X_@CRlVRQUo*}&$bJu+==1$5nC?ky#f^l;S^- z^AR@8H)1B7dpMM@V{{<#PTUm69X3mxq;aMZgXTxZ=}l(`rX*o||1)GujvxR>=m$gi z-Qe#8bN*nizJacqhFskix*FPW6>0=u^T&dZhC70SJ>bLLx`d^n&W8J|h}f69q;{1Y zOOC`K?wTmO*thK1WC>g()+Xwak_5SupX9a+wOHbM(%%@pB1f+2D_31{4ik~Y{OOb8 zxygxfk<2yyqRgyO=CIOJ56@aGXoFXii8vNeia5OS_no^~3Uhdlr>l$er~TG9X%EW+ zkH~XqRK5Z$G1yMXbdvYVSZPwMk1JL3&V`g!r;;y^CTFO;bhW|qwKt9Ej^qUZMDJDz zRkd$?WyP5b)~wdA{04kdt1EK;Dz&!jp`RqUKsUbp?ycXswffaeus<8@hg? zPUW{MS5IfEyRy|?g>tvA;we47in8oIy-AN2skMk6lz~lqwaA1ZSseG)h{8&KcC`VV zsojhcc29wy>Rt8)hA&!#^(lnB&mD)^s2$gwS(W{GsmvpQ769hPMWbUPwvJ(4X;I+o z1!KdEkUWj-Ox1{9`SPPc+rvQHYCIDd$OZ;hoKGqPq`Gg;)ir$Q;I|I0j(+p#-J@$Y z*}Be0b$t)(`Z9I9vUR&u|E?!>O?US4{;-Y*H-NK|_bejCYA%S0#wfK5h4^AXksQpm zlAO?;!?=1m5)D+UU!) zl#)ybW|In81r&`T8N9}3g@jdGGnHOREyND%=GddmW=LjXwlILD9rjh`A8^@Rj>FE~ zquHT)hQ%J1r3bcd%c`f;EV(Au&VZc*GzSV4sMJIh&tAgq+Q-2zlLM64kO` zM=HK;Kvv2Mz<{O*#f)$mO<^eg4!ximkCPVn!e`F;>0Jhc)5p>bLvnJ14F!jZ=-L71 zGbqV+9RtnX`Y4>)buqi^qG7O7{k^yt)v=MTEKu1p0t|K1geoEhw1#JcUUyh$I|Zno z+D-w@oD?joRl0ez`aVOIqry7tWG1h5-VSH~br@Ho7RM31~FC!q^Va7IL%E6F>(e-!L*Oax$@`MC6ci z6sXL*M&{*kB`{0PLVS}JVVqr;Z*nCIe12e7vnW|08b;xjUu17QLW#~t<4|NM@C~qxN~$wcPC*+7 z!Ia#P;_^HoT_@=wf{Mk+Nk*QBM?pVYr#US#PXyG?r~-q{6jUL`xzqoZFfz*BJeUnz zB*dm}7In3;AfifQFJ;v9Eg^fV-a}em@@GODc+XP~lN1sPIr6Z!GWVT?sS%@uXU0ZO z0zMo~;I(6v#r;p1kil8nl`z_US%!Lvm2xF487<_+F-e}M2#cr3rb6i|y^-hT_&h+- z0%*$8cK-V3Xg$gBMroR=(9&+!JiSY}wU)3mF*-Fig|4w+#Q^d&>@XyhV8xSKu^(dF&LFg!tBBQ;b`cw zp=mM%@@=DT~`p}TdFiyLpDmVx}dn64;F3OD9&V_`Q*nI?ji*=H<) zg-kH|^g8t4`Mt!W*6G~U=X)c`H7mgL-9Pc(lH=2Ppl&1Tzc=x?`EuX#U*7C)Q2%^RFS(t5-$Z5ObY|4!~kwUljdhgjF&cS zV=YGFWbcKuqq_OQ0Aj&=NbH3_A-wIvB2rTA<`W5tIY3HKAw`(WCMcRsa^aW(f>V$= z<(XrKH-Y*9+peLnE=%L=)tdN;=SM$xt8ms9~I>|MwJ7 zLowaq)IJv&lMA0G$!b7!WYm()hQxlDk0#Yo#}lcy_ymVM;P0SSHbEf?DOwE(NqGSZ z{b&SL;~556A!r_T>A`27u}qBSa+OGSUY0C=bh-wN+Cvan&A5*xA)OxS9Dso^Y*`kC zS-yOX+wqJQgZ8`;@i8lhiHSkp`Z+j38qF~gVXO>LItFeM9gLL|8JlD9=pEq^_&0)# zs4V3sddbx1Q(wm6t;#%$nKqv`s`KFms|V%!^&%!6I1iK5zy;38r1ZyxSlg=&F=Q|? z#v!O94Ae0GL#rnnz1S8(U_M|x*O{X$t$~y%8GArV1`uW^)3__tug3dd18I~ z9uj*evGk2%C4DJwSd;n;P&C`<XoB8Qau&iE?@WKM)+)f z^XlEJE2mdZbEgo8ubTu|9z3pVTa`0)UD>)W)vsOTn9)rhA#luSeq;H~GxyGOG4*?A zAIz)$r!xJgvIv8xRPU)|5S3XA?iTKE8S%S*Q0YKu^T(rRvoZQUA{e8N+jY#N7*G6% zGaATKLVtv>)i1a$zS{2LWgk7a9P!}OMk)6(wv^61ku^_ag)$2K#aG(-hj&`oWyPcI6b`A^&P7 z(3=hPlI9UkMAT>XUD^7sm9dqvpO`*HxgEQh*6u4AZ>P#Gu2yir7*=kkKcU%zVj@=` z6s>UC)y9B$qBHNo|3dNEf7tm(lJBMK*TfrkH?ipkhVaXL<};8GhtDr<0*Yw>d(F)jqyT`@0?LFX z33?ZBL48q+2PRS(Q21&zMEhE(!9Zw=HXbcR=dbW>Nnn_0U=(ekMpEOk8v>{^An4_5 zq7F4*lL0tvwj>SZJtJh+55J(z5A12f*3vc?zK+s}fNKHFX`V#L1l!u8xm4&vo1qNi zOCh3w7fhW}NCd)-TAQnT-e5zMMcVoZ8HL#l1H3IzYtfs!4$HEHW*w|)DQJzFOhlrz zqJ<9z<};p6Q$L9#agn!~%Q;3xc)=GgO-|8lsEK|w--N}zyZ1qaU^)=C?}+7>r9LCC zKCy;u%Ms%B#1lUG4Ftb>eYl;m;iM3hqW?+``6)|(4_FsRX-x`O33gu9#?l0cPFEuG zopa`Tm#Y+^;nIe@j;zFG`6ln`FzwJm88m@gWY%!Wsz4j0#(^}XG>=;RqVfU84qy>w zRoy_vO_*gWZZfm7^HhW+rV=QybtDMvxF5^2K9&3e$NgAy8&iS4@r2iLK&xXqV8gDm z8})#S{JXeqq9X5mK|hYi*4PacqCF*>P*Hzko6C&-~93L_{VhE$)^IHcmm&6ZtTpv>5Q?w`-U9879k?h zf5ys#OoDcc2zhGS?I2den`T~vRM6*2ADqODNF0Sq;rg0{w%LrmPO$x-Sg108OK66f zj2)LldH2{_qw8fTEqLL-sP|-ypCr|GeZ3M8#O_lKQJp39s9b1!lVKed}HK z-I?wCvfKBu>Hfuxw^d~q>(f5x`1r<*=^_9WVZvf?+{A2OB-hy45OCy7e;q3%2*P5R z?}*gZ%Z!I((NCP^2g5C#psQO~Pu@HKsI~uLYybL-nby78 z*1ehFzHD%x>fOiin+}XX*}0e+V8;d~)Bix6cGmW8R`Z<27Fu$_q=m^mpP0xqmMeyh zCK&P*TLvs(A8izN7pt|wn0ZMxX=nt23Ihh;T7$Xs8j2%7JBw6A)nk|>Jod01BR#XT zO!bIoSTbbxh~9w3XMwy`4X~y_rEihk9Xep6EyD=B1_b{4Cq4V9r9B(B>#Vj;oQQ{i zIQOp1zL%a-f>z7Zf5AFymZn53lXw>b8JZH*rZ4`&ZNnN6Mh3diF0@D`KA2FRUuI0u zF@DP23}5pgEIDQFSgwB$^EB9=@%O3hdeXn=k0<_UBGZ2~+kdp^8%Hsw3FBuhF0SL% z7qdMah-u{BWSaHZCsz2hSln~5v7~4V8kkvGdJku2ID~el+#KXgUt% zz50=@Pva#2_mw8aqDzW^C!UkCY+|c#z80S&jht&GL{L0l}bCmKBTq{ zW~z6q?4srN8MfR$qb;}p9m)YWs-?Ocj7aIAw^nXc#-|B8Mj`5N765~GSH!Ez_jB<{wYfYs;xfn>d>{i8fzFyV%AoRjXD;-36+~gfdU;i zJK&m>Em!2&BGcnVVAm$2DlFS@Y{6;}=a?|FGOWe?tR2lxX<3p)l8aAcB){$@Nt3=L z(Hk_d$d2qzH*JC>#ysA2Dt}G`a3_LNN46njP-(z%=qJb43?K0jkNlVhp+_; zL~%M6S8X3w>ksqm$?(yCKk%0We=+#MAmf$!t5D-DX+4lCK5UqS8dzRsR3chk_Bn@AEtGQy5*USe@JE5ll_PPJn^TA z%>L8a{ilmR{`2O|(am`?ABF=k3qTz`K+^6%Cq6y!0t&S0@j zC!k4-avWreCR z$*Hq{xA4P-A0+-J!MXg2O!cVBF3#nDh{`c8uY5qUQl_pWqnN2%tCu#{xSKF|YwBsL z`7M-2%)Zg>d(rV@&g@^*nEi`vCAfLWn7u_UtG1oU_(!w;(GOpNQnreevQ-t3R?7q~ zt^Pm7^M63yv@y^B5kCE5T|wJbiuwPP&i_wvIzd>%|B>c|#^R4<{Kr*xJvnju?*@N3 zm^m?-Ju%7ozxlD0|D$Mbp{hrOj&`z&=GV!Q#F_pGu8_n}=-@WyeU8A;;c zId1qt3Q^zWo-!4iSR9(x@AWH>d)$ zRly^jps{`Cy2rK#(9{fnE|N+X8_|ht;18t?Ff-o3sM)MO2VYyg2y9r7Zql4tbz{^F ztXzp3GnUGvO-l~E1X0H3Nq>{Ny9Nbd;K8)<%>AkFe_0KVsNNCYJ=7)o&rm9S8E?o} z0^9He#m~;w($nBxMmehM(4$KKK%IaMVO)gGM3hn|9z0Nd$+G`C^H%G&8ppkK}#qukcWm{T+c# z3gzbDWcmOvRI`oig)*wd$hO8E~6~C+&cauwxq|Y?DE4Jqdsf=5%8b_zU4z2e2<9eU@sEee=!zzEC`Js zEllYDCnADkla^hRrk#wY(BCwGKA5C5%V7W9dn6WKxGaT_S*&jQt3LrPf5HzQd)vKs z=?^O3s=O_fvQ?@Ws|?9(c0WYD@c)c23I&#;mH&e04NCn}D%Fc`w}my2QDXYLjDIn| zRbpo%nIW--9sZbD@qxcGc1&@$9(Xk+&MS$fMLrZ5y$o;yGnR$;6Eo~Yv1ABdK{zDI z;%g3LKAm!>vu)iHVw|5!=SGA^Cnhe=!j)ucAq17yf20=vCj|L&?VJ&9IN5r13}A60 z?60a%X>I!RqV+FO37INeU2qC+%{sN+=M^U5WB$XCqFz#DiehSME!NaD=bmclW9)+5gSn?XHs z=;H)ohs@FZfX-tSJ5B+)(&d9QGvPQ|%=|o)NKO_OeyHcO6gxpd8G?NI1?-Ce3YAfM z@=Vc*yvp40ZDEm=-=l)ZSQ>a_-!wt$USW9;TeWbxOaB?gnD&Vj8p@9-pf!NfPC+w*d@JYZMXMfdBHrYy zHe=>nHq6UeQ@*X}BPbmcnD&RYlKIA>&vaG`;k}V6Re5QAa^%$5%=pCkSbi%TPBR?r z3{IoLJff-Z6k7b|TWtx5+YwvRlDx}#1Xt_qqGnS-^qKq@6~vd#e+6fw$6F96ct!{| z-aZWxsBz2faoqa$sqXqa;k#GY>h2~~_g-8X_ufLi(A08!0^h3|@EupiT@Ovr(Gdq0 zh=H11S8uMjztGU;!5`W}@T9utZpG@Jdk5Br)?Mqcd$@L z&uC0DZy>L6BmE7cCi+5kw*Z=)`x(|X?54}Z0EBD z0WUv%{%>CTXi|OQ(nl|6$6jQwii0CUV{`7n;oON+Ik7X>u_M>in%lAsbWms*^1#yx z!IMCB!9x#l$*wDuQ_RbHp4Ip4dU9<%+d7o3*<0|@w+i#y=z8=0p$8td>u9$1ShnVP zp^_5#1^lI{tE+WyBnkm~t`cf$K@CH9gM}bHSHriwP(v|vcvGQ{V)bU%vwHEarY}@O zOR(q=8k%kG2yUkY2v|Glp-c$Y7Ro8+HH-Gqa|IP$Nijba9iUj1Su_JxO;lDxUkIor zIN@Mj?(VgcEfL`i(cJ~d zYJ`aGf@2NNY;?DVl&L{as1cvvvvaNHtwxO8$#rkGeNREa8#6GpHgu93{X+Ib!9N;4dlH3oVO4w)iomAEhrKx>s1jYx># zQwqS%8inTmJHNS>%r@=HdAAa@5TG>^v}S_VTn0u4aQ0N5z?Oo?OQ{fiN&%$;N%h+e%Ugwpt~(PsZ&L}x21?jK2^-38UqC+)+os zZyMd!hHj%rYiRTw`><2zO+HUQ`g`m8$or@7pLr1Za7f*Y%@U`x?PrJ*`#*wZ1vfJw zFq~o0>qf!91=6w?#k&G~fnaJY0w9>R75MGGb4%-hXEyABz}A%uId71#3hMC&00057 z8YsIx2BM&*Re37Z?L!3tcQY^=L~li>XFqYv{hTH5Cq|2>2N+cP7fa0RM#A=cJ!|c6 z^{jWUr`G$j9eeMGzd!%rYIgrwwc~7d>p5bY0BH~WLPOu3iM7#eeJ}cjI=H>y@lkgm zz(Gc=I~rM?>piur%LM^9Gcc-5Z~MWHvz{#g;yDIRJiH!$fBt^#!%-EskGIJdW^F;d$1v29`i~YiN|mK5#Or_0+0;fQP#o7zycZuTa*O^>h}T zUJqk!WLxQ#2$gk0j$8*AQzY88VxLZS`VD_O(DBF<* zF=IYG?En>A{jff}{}groX~#!<`P~{)?g1kMxPI5r8V3H3Ty00Lwk_vBRq%PbKoST( zr9gFyU^GRE#x_jUd0=huerFbcq14-*^G_B$u1)|#@F@kVdpl;E?!9YZ3O#yh=K-jj zO6#W5x{;_IrEMEn1(y_awGDLnUqYg7lxQ0z+J;11k*MRq+V=bP*>;rHm8;#Q*6z|m z9PM5$V*UucaE2aFu77PJay3wI_zF}Z@D zH$##S7pz(-)wgDk|Z_0F^kESC+U#{CXTF6C`Nh! zw#J7NhzqR8lz+rKQU@e|;1mQbfgO&VP^;=!PH0o@S5DZb+OLh$w5WpZdLnFB?N?6d zQtekx*rD35jmj#`Rr+SPD%h@^{SjYt!Xedu<%EN({mKbLs{PuiKBd^`PzBqS6S`IV zl@kV3`;`+qRr|Hko4u-FyK?qNe9Z|vRr|FOobVy!Yn4nfG}!KkMr95qN(0#fbd7K0^KjE6t~?@$uq-Bjj@; z6Pe49Tb%5W`HW+ezr}L|zJ-ji>AdA+-_DF{(|ya$zFqL`x#fXxx8jvO3ik<$!i5!;wKpz15rhGenm#v5Uq`S#V@!EQVEg30C>$~+OciZUS zpkEukSMjENa`1ukRu5_lx(+d2dqCe_8?W~H^}VqtWb5hekQ9*nK~f)+Gb|qk+E8nz zGS@yQdEo!-eY0I$@*v3Qm$_vU8F`NEm!3PIjBsWkuBb^(R^n+{m7w0WtR%BEZESx) zsYao}2er7N#nUtmHE)(?;4Ceug>UxAUJ1>q>G%Os7OiLpM~=EnAFnAd?*x z7f~5x%UGZ|-H@oEi+9t;I&{gJA*KzeWir+fQGzc$yDDp&Qd({57>$Z|6so5+Rgz^z zo|_X-N6#~se@Z=I8H#}^dAKB?f=ZQZ?F&=pTf2Fe z7<_9?=K_SCCoH|<=ANT`ie8O4V5{g`nY2Msj|j(<)UjWDwH;g6HkBAvQt6G@g0!U@ znySS9A}gszcJm{JZe|V1fSGM`Y>w7Hd0^jIn*=^L>-ih3uONw3DXwvBa7 zosFJ36Vua%a%xLTZb)lDcUPesJ4$vfrb{Z=ES5FW89lnSZTjM>MmNDxJIc;DE|GXz zk2f3|eJ`UWrHpqR)WW?!Q*Am@fR?NyyBu1WBby|3!Hj1;xc_{9lUBtYEm(I-Jqs(b=y#M zGjLN)W2|DK$@E!^1lNe^QB+ySuZY`nwHH{?UMS4&W|)dwOn9)V8PKz-RQg_=EkM%? zO;)e&h9S5B;h{Y^JlJ6YIts1GUB3Y8?vZ*Q3G~+q=R8si_UE%-xqAyUU+@+8MAbck z&6BmkiNdG(KwS`=laH6{1b)7_$$mPE01r&UR`t8W1p_}=ez{-%e6(sbz%UjSwN%oFR1Br6me6$+c( z>}p0D?9`LEk($pT=1ha5aC1Xk=k5cN>(93(hx+gE_P(5l~u*#09TJMFDM zB9D3CLu3~O3K4LkDb4mPyE~$=A8^7Ud+a!&jsqVr@ELt0A72L_ALHY<^U3k|$8DiU z$no|trH70*%#Z^xGl$&un74EPHkT71Om#~W5XWvX5El&y^JrQzrBPN{OG9W885$@C zMB4^FtgNSXF_or3$C?uPE~*gGx2U2ksxgC*wQFqLki&`turQaI+ao=Fou4~BEnE2OH>vab#o?L zR5P@smTAo3`WD7h0G90;gpqV=8yO&%h6O;SCNgFrKCLlnbE&MFoU^9c3X=&D1d3Ej z>xw8@bjm#-`?MK!qfk|tKs{S>A0(#0-Aq^pL_T8^Z)+(>#wa2{;sZ@PUe^$VeahN+2{WSeLGInUfk)yRgXKP zDI~(vA>hX#hvEi~IH`yY7&z$|7UNKWEEumN0UEObT%I+>h+_vm1s(6gzy5Df0Aigb zf$qI42kySI`&cbFlo#?s%^NIxhigFqiT&-oaNzGPyuE+E>=$cChsvQ7@D{&Wnt$}M z^__>oU4>(1_b9xjD?j_jKYZiq@yf)z)rohZp%x4k778~C3&_c09U3ot@qHrvbJst1 zJxx_kT&SM701Y(Xx2?i2#N5R{G|vee>nO z{DD8X_vaK2w1mz;0Z6WCMjV%%DX=mCJA-b=!3wZrlWjqeAkt&L_Tp3U+2;g8F{J-% z{4NCiy+6)5OVIWDBX@*M&X)7ZLJ9r<+LP-zHrT^1ruC)Q#$bbiIyRo1qtp&=&1TZ6 zk)5}_#=TKeCl{dP_r}^mq9NF+Fr6x03poEX6s6!N2L=+5h2%vNE#^MYcAVDL1>Kd}% zlv)Vf$j1Tl;dGd^(gBP>Hv#t=$k>XvaC4wX;T8ce6W~rE!A_2v3Rnv&LD190`Q?Sh z#Xk(@Szw3Z(1w_IB^@RtgUPk1xU3v%-He@f@H`AIbK*yMGP?)78X1DL1qZn#JCdRG zHnpq@7b0R-g=7_zm_~Af#({1~eYW7l0t7JYL;@pK2To&C1L@(*@--gK0eI0tQVNU> zBM&v|7~UTB8f+j@g8Zh5-Yq7`a_rKWg=hd+u^1dvWAr#gm{;7Toxt4W@e3y>-kzR1 z5t)GtAVHT>3Y^hgFk_ek#Tyk<_(V&q6r&X$X(85bnXU}`jkqkt!stw+Sj4b6jifxT z2po@AX~I?zO%FFvn8Cp^$*9j8nE1WA%lzpRn3kU9=vU{-R@qgF% z9baMgZ-e{6eDGN?TpauH)DNdh<3E^rG*byqRfAJy&(wk7{+kcK{o&tj7sI~|N4^Y4 zo{U$*(P}ta3C60ySVcHp6;79h(>04OuiwA^;QHS61Gj%K_xpMF|9JR^!=>d)I8qHq zD#7V$aJnL#tO_T~!pQ@7s7@Ttd9Fj#M)mK{7yt5UcR6&a61r4{(sQ}$xm@;K{>p!( zu=@Gh_t%Qa?`0llD*o}Rf4nS=TY!fiGDDb5#4#gAdu0@L4WRERYcwbv>>(AlF8C1s z+QA9!&fxC>d;t7$GS|3ka>x#VBy#_p*LFV#w{1w@csj2)>_Kvw!2MPmT(#f3r5tec z#^Y_5Dm3riP+~-o`oY82xx+d>G^Qn5(-L8?LR3n_RM%kf!+9i;z_b!i3n`YMWmV~p zl+H-283ntO>sfUJ_gKRaBD?7XL*N!0RvC>fC)CYLkiYnN+FzsKzzIrG64(l=9jj34aW<)UgP3fM=gDFhkH!)$iZbZB* z+gsLW#6kZAJVoDwLPxhZw^twAUbf{z7oh)pNVp0G?65)NIaUgP>7FjTry)Vh8wc*8 zvU{@b;Jv-iLPra?ACH%hov94HQyqGz5}K`sX3K%um(O~J9xs-IlbEvgwu)MyCw~Pm z4SM0y0MfPmSv>$^_D3S*=e-yBUvn4uh+}Cf!Y@JLSYmM}a>R5Q+go5JlQT?j99>S~ zXvXfzOkeBl#FAoDz&OGJ5eq@|DDXEui!cUPf+?(OT88D&rl)zxGo340xGkfXft2OP zbO66F)|gH}HBDt!`8lz+8r}rG^1QdH$=Qr@kzR)?ELEcaJCratj;oQra`QbP6J`5b zBa>zOTO)6k?Qe~YlfTda zPn|$f3k2&t{2ui7Jsc`7JRGe%vCBoey7xaU+`n=dGsc=$c= zb=95N2p3ho`#Z(){m<%dtb2$*us>Z`-jCJ2Soe`o|HJ9x&1&Dc#U+69y08oRwG}HI ziOtr|%+{U#s0S3a@X(`yQm7=C!qxD}IuA_;BX2$Wv(mzo{?cM~Bx;emh|B%pR)K#I Pue+N=+4zov`Tc(YHbmrn literal 0 HcmV?d00001 diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/sansio/http.py b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/sansio/http.py new file mode 100644 index 0000000..6b22738 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/sansio/http.py @@ -0,0 +1,136 @@ +import re +import typing as t +from datetime import datetime + +from .._internal import _cookie_parse_impl +from .._internal import _dt_as_utc +from .._internal import _to_str +from ..http import generate_etag +from ..http import parse_date +from ..http import parse_etags +from ..http import parse_if_range_header +from ..http import unquote_etag + +_etag_re = re.compile(r'([Ww]/)?(?:"(.*?)"|(.*?))(?:\s*,\s*|$)') + + +def is_resource_modified( + http_range: t.Optional[str] = None, + http_if_range: t.Optional[str] = None, + http_if_modified_since: t.Optional[str] = None, + http_if_none_match: t.Optional[str] = None, + http_if_match: t.Optional[str] = None, + etag: t.Optional[str] = None, + data: t.Optional[bytes] = None, + last_modified: t.Optional[t.Union[datetime, str]] = None, + ignore_if_range: bool = True, +) -> bool: + """Convenience method for conditional requests. + :param http_range: Range HTTP header + :param http_if_range: If-Range HTTP header + :param http_if_modified_since: If-Modified-Since HTTP header + :param http_if_none_match: If-None-Match HTTP header + :param http_if_match: If-Match HTTP header + :param etag: the etag for the response for comparison. + :param data: or alternatively the data of the response to automatically + generate an etag using :func:`generate_etag`. + :param last_modified: an optional date of the last modification. + :param ignore_if_range: If `False`, `If-Range` header will be taken into + account. + :return: `True` if the resource was modified, otherwise `False`. + + .. versionadded:: 2.2 + """ + if etag is None and data is not None: + etag = generate_etag(data) + elif data is not None: + raise TypeError("both data and etag given") + + unmodified = False + if isinstance(last_modified, str): + last_modified = parse_date(last_modified) + + # HTTP doesn't use microsecond, remove it to avoid false positive + # comparisons. Mark naive datetimes as UTC. + if last_modified is not None: + last_modified = _dt_as_utc(last_modified.replace(microsecond=0)) + + if_range = None + if not ignore_if_range and http_range is not None: + # https://tools.ietf.org/html/rfc7233#section-3.2 + # A server MUST ignore an If-Range header field received in a request + # that does not contain a Range header field. + if_range = parse_if_range_header(http_if_range) + + if if_range is not None and if_range.date is not None: + modified_since: t.Optional[datetime] = if_range.date + else: + modified_since = parse_date(http_if_modified_since) + + if modified_since and last_modified and last_modified <= modified_since: + unmodified = True + + if etag: + etag, _ = unquote_etag(etag) + etag = t.cast(str, etag) + + if if_range is not None and if_range.etag is not None: + unmodified = parse_etags(if_range.etag).contains(etag) + else: + if_none_match = parse_etags(http_if_none_match) + if if_none_match: + # https://tools.ietf.org/html/rfc7232#section-3.2 + # "A recipient MUST use the weak comparison function when comparing + # entity-tags for If-None-Match" + unmodified = if_none_match.contains_weak(etag) + + # https://tools.ietf.org/html/rfc7232#section-3.1 + # "Origin server MUST use the strong comparison function when + # comparing entity-tags for If-Match" + if_match = parse_etags(http_if_match) + if if_match: + unmodified = not if_match.is_strong(etag) + + return not unmodified + + +def parse_cookie( + cookie: t.Union[bytes, str, None] = "", + charset: str = "utf-8", + errors: str = "replace", + cls: t.Optional[t.Type["ds.MultiDict"]] = None, +) -> "ds.MultiDict[str, str]": + """Parse a cookie from a string. + + The same key can be provided multiple times, the values are stored + in-order. The default :class:`MultiDict` will have the first value + first, and all values can be retrieved with + :meth:`MultiDict.getlist`. + + :param cookie: The cookie header as a string. + :param charset: The charset for the cookie values. + :param errors: The error behavior for the charset decoding. + :param cls: A dict-like class to store the parsed cookies in. + Defaults to :class:`MultiDict`. + + .. versionadded:: 2.2 + """ + # PEP 3333 sends headers through the environ as latin1 decoded + # strings. Encode strings back to bytes for parsing. + if isinstance(cookie, str): + cookie = cookie.encode("latin1", "replace") + + if cls is None: + cls = ds.MultiDict + + def _parse_pairs() -> t.Iterator[t.Tuple[str, str]]: + for key, val in _cookie_parse_impl(cookie): # type: ignore + key_str = _to_str(key, charset, errors, allow_none_charset=True) + val_str = _to_str(val, charset, errors, allow_none_charset=True) + yield key_str, val_str + + return cls(_parse_pairs()) + + +# circular dependencies +from .. import datastructures as ds diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/sansio/multipart.py b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/sansio/multipart.py new file mode 100644 index 0000000..2684e5d --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/sansio/multipart.py @@ -0,0 +1,287 @@ +import re +from dataclasses import dataclass +from enum import auto +from enum import Enum +from typing import cast +from typing import List +from typing import Optional +from typing import Tuple + +from .._internal import _to_bytes +from .._internal import _to_str +from ..datastructures import Headers +from ..exceptions import RequestEntityTooLarge +from ..http import parse_options_header + + +class Event: + pass + + +@dataclass(frozen=True) +class Preamble(Event): + data: bytes + + +@dataclass(frozen=True) +class Field(Event): + name: str + headers: Headers + + +@dataclass(frozen=True) +class File(Event): + name: str + filename: str + headers: Headers + + +@dataclass(frozen=True) +class Data(Event): + data: bytes + more_data: bool + + +@dataclass(frozen=True) +class Epilogue(Event): + data: bytes + + +class NeedData(Event): + pass + + +NEED_DATA = NeedData() + + +class State(Enum): + PREAMBLE = auto() + PART = auto() + DATA = auto() + EPILOGUE = auto() + COMPLETE = auto() + + +# Multipart line breaks MUST be CRLF (\r\n) by RFC-7578, except that +# many implementations break this and either use CR or LF alone. +LINE_BREAK = b"(?:\r\n|\n|\r)" +BLANK_LINE_RE = re.compile(b"(?:\r\n\r\n|\r\r|\n\n)", re.MULTILINE) +LINE_BREAK_RE = re.compile(LINE_BREAK, re.MULTILINE) +# Header values can be continued via a space or tab after the linebreak, as +# per RFC2231 +HEADER_CONTINUATION_RE = re.compile(b"%s[ \t]" % LINE_BREAK, re.MULTILINE) +# This must be long enough to contain any line breaks plus any +# additional boundary markers (--) such that they will be found in a +# subsequent search +SEARCH_EXTRA_LENGTH = 8 + + +class MultipartDecoder: + """Decodes a multipart message as bytes into Python events. + + The part data is returned as available to allow the caller to save + the data from memory to disk, if desired. + """ + + def __init__( + self, + boundary: bytes, + max_form_memory_size: Optional[int] = None, + *, + max_parts: Optional[int] = None, + ) -> None: + self.buffer = bytearray() + self.complete = False + self.max_form_memory_size = max_form_memory_size + self.max_parts = max_parts + self.state = State.PREAMBLE + self.boundary = boundary + + # Note in the below \h i.e. horizontal whitespace is used + # as [^\S\n\r] as \h isn't supported in python. + + # The preamble must end with a boundary where the boundary is + # prefixed by a line break, RFC2046. Except that many + # implementations including Werkzeug's tests omit the line + # break prefix. In addition the first boundary could be the + # epilogue boundary (for empty form-data) hence the matching + # group to understand if it is an epilogue boundary. + self.preamble_re = re.compile( + rb"%s?--%s(--[^\S\n\r]*%s?|[^\S\n\r]*%s)" + % (LINE_BREAK, re.escape(boundary), LINE_BREAK, LINE_BREAK), + re.MULTILINE, + ) + # A boundary must include a line break prefix and suffix, and + # may include trailing whitespace. In addition the boundary + # could be the epilogue boundary hence the matching group to + # understand if it is an epilogue boundary. + self.boundary_re = re.compile( + rb"%s--%s(--[^\S\n\r]*%s?|[^\S\n\r]*%s)" + % (LINE_BREAK, re.escape(boundary), LINE_BREAK, LINE_BREAK), + re.MULTILINE, + ) + self._search_position = 0 + self._parts_decoded = 0 + + def last_newline(self) -> int: + try: + last_nl = self.buffer.rindex(b"\n") + except ValueError: + last_nl = len(self.buffer) + try: + last_cr = self.buffer.rindex(b"\r") + except ValueError: + last_cr = len(self.buffer) + + return min(last_nl, last_cr) + + def receive_data(self, data: Optional[bytes]) -> None: + if data is None: + self.complete = True + elif ( + self.max_form_memory_size is not None + and len(self.buffer) + len(data) > self.max_form_memory_size + ): + raise RequestEntityTooLarge() + else: + self.buffer.extend(data) + + def next_event(self) -> Event: + event: Event = NEED_DATA + + if self.state == State.PREAMBLE: + match = self.preamble_re.search(self.buffer, self._search_position) + if match is not None: + if match.group(1).startswith(b"--"): + self.state = State.EPILOGUE + else: + self.state = State.PART + data = bytes(self.buffer[: match.start()]) + del self.buffer[: match.end()] + event = Preamble(data=data) + self._search_position = 0 + else: + # Update the search start position to be equal to the + # current buffer length (already searched) minus a + # safe buffer for part of the search target. + self._search_position = max( + 0, len(self.buffer) - len(self.boundary) - SEARCH_EXTRA_LENGTH + ) + + elif self.state == State.PART: + match = BLANK_LINE_RE.search(self.buffer, self._search_position) + if match is not None: + headers = self._parse_headers(self.buffer[: match.start()]) + del self.buffer[: match.end()] + + if "content-disposition" not in headers: + raise ValueError("Missing Content-Disposition header") + + disposition, extra = parse_options_header( + headers["content-disposition"] + ) + name = cast(str, extra.get("name")) + filename = extra.get("filename") + if filename is not None: + event = File( + filename=filename, + headers=headers, + name=name, + ) + else: + event = Field( + headers=headers, + name=name, + ) + self.state = State.DATA + self._search_position = 0 + self._parts_decoded += 1 + + if self.max_parts is not None and self._parts_decoded > self.max_parts: + raise RequestEntityTooLarge() + else: + # Update the search start position to be equal to the + # current buffer length (already searched) minus a + # safe buffer for part of the search target. + self._search_position = max(0, len(self.buffer) - SEARCH_EXTRA_LENGTH) + + elif self.state == State.DATA: + if self.buffer.find(b"--" + self.boundary) == -1: + # No complete boundary in the buffer, but there may be + # a partial boundary at the end. As the boundary + # starts with either a nl or cr find the earliest and + # return up to that as data. + data_length = del_index = self.last_newline() + more_data = True + else: + match = self.boundary_re.search(self.buffer) + if match is not None: + if match.group(1).startswith(b"--"): + self.state = State.EPILOGUE + else: + self.state = State.PART + data_length = match.start() + del_index = match.end() + else: + data_length = del_index = self.last_newline() + more_data = match is None + + data = bytes(self.buffer[:data_length]) + del self.buffer[:del_index] + if data or not more_data: + event = Data(data=data, more_data=more_data) + + elif self.state == State.EPILOGUE and self.complete: + event = Epilogue(data=bytes(self.buffer)) + del self.buffer[:] + self.state = State.COMPLETE + + if self.complete and isinstance(event, NeedData): + raise ValueError(f"Invalid form-data cannot parse beyond {self.state}") + + return event + + def _parse_headers(self, data: bytes) -> Headers: + headers: List[Tuple[str, str]] = [] + # Merge the continued headers into one line + data = HEADER_CONTINUATION_RE.sub(b" ", data) + # Now there is one header per line + for line in data.splitlines(): + if line.strip() != b"": + name, value = _to_str(line).strip().split(":", 1) + headers.append((name.strip(), value.strip())) + return Headers(headers) + + +class MultipartEncoder: + def __init__(self, boundary: bytes) -> None: + self.boundary = boundary + self.state = State.PREAMBLE + + def send_event(self, event: Event) -> bytes: + if isinstance(event, Preamble) and self.state == State.PREAMBLE: + self.state = State.PART + return event.data + elif isinstance(event, (Field, File)) and self.state in { + State.PREAMBLE, + State.PART, + State.DATA, + }: + self.state = State.DATA + data = b"\r\n--" + self.boundary + b"\r\n" + data += b'Content-Disposition: form-data; name="%s"' % _to_bytes(event.name) + if isinstance(event, File): + data += b'; filename="%s"' % _to_bytes(event.filename) + data += b"\r\n" + for name, value in cast(Field, event).headers: + if name.lower() != "content-disposition": + data += _to_bytes(f"{name}: {value}\r\n") + data += b"\r\n" + return data + elif isinstance(event, Data) and self.state == State.DATA: + return event.data + elif isinstance(event, Epilogue): + self.state = State.COMPLETE + return b"\r\n--" + self.boundary + b"--\r\n" + event.data + else: + raise ValueError(f"Cannot generate {event} in state: {self.state}") diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/sansio/request.py b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/sansio/request.py new file mode 100644 index 0000000..8832baa --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/sansio/request.py @@ -0,0 +1,547 @@ +import typing as t +from datetime import datetime + +from .._internal import _to_str +from ..datastructures import Accept +from ..datastructures import Authorization +from ..datastructures import CharsetAccept +from ..datastructures import ETags +from ..datastructures import Headers +from ..datastructures import HeaderSet +from ..datastructures import IfRange +from ..datastructures import ImmutableList +from ..datastructures import ImmutableMultiDict +from ..datastructures import LanguageAccept +from ..datastructures import MIMEAccept +from ..datastructures import MultiDict +from ..datastructures import Range +from ..datastructures import RequestCacheControl +from ..http import parse_accept_header +from ..http import parse_authorization_header +from ..http import parse_cache_control_header +from ..http import parse_date +from ..http import parse_etags +from ..http import parse_if_range_header +from ..http import parse_list_header +from ..http import parse_options_header +from ..http import parse_range_header +from ..http import parse_set_header +from ..urls import url_decode +from ..user_agent import UserAgent +from ..utils import cached_property +from ..utils import header_property +from .http import parse_cookie +from .utils import get_current_url +from .utils import get_host + + +class Request: + """Represents the non-IO parts of a HTTP request, including the + method, URL info, and headers. + + This class is not meant for general use. It should only be used when + implementing WSGI, ASGI, or another HTTP application spec. Werkzeug + provides a WSGI implementation at :cls:`werkzeug.wrappers.Request`. + + :param method: The method the request was made with, such as + ``GET``. + :param scheme: The URL scheme of the protocol the request used, such + as ``https`` or ``wss``. + :param server: The address of the server. ``(host, port)``, + ``(path, None)`` for unix sockets, or ``None`` if not known. + :param root_path: The prefix that the application is mounted under. + This is prepended to generated URLs, but is not part of route + matching. + :param path: The path part of the URL after ``root_path``. + :param query_string: The part of the URL after the "?". + :param headers: The headers received with the request. + :param remote_addr: The address of the client sending the request. + + .. versionadded:: 2.0 + """ + + #: The charset used to decode most data in the request. + charset = "utf-8" + + #: the error handling procedure for errors, defaults to 'replace' + encoding_errors = "replace" + + #: the class to use for `args` and `form`. The default is an + #: :class:`~werkzeug.datastructures.ImmutableMultiDict` which supports + #: multiple values per key. alternatively it makes sense to use an + #: :class:`~werkzeug.datastructures.ImmutableOrderedMultiDict` which + #: preserves order or a :class:`~werkzeug.datastructures.ImmutableDict` + #: which is the fastest but only remembers the last key. It is also + #: possible to use mutable structures, but this is not recommended. + #: + #: .. versionadded:: 0.6 + parameter_storage_class: t.Type[MultiDict] = ImmutableMultiDict + + #: The type to be used for dict values from the incoming WSGI + #: environment. (For example for :attr:`cookies`.) By default an + #: :class:`~werkzeug.datastructures.ImmutableMultiDict` is used. + #: + #: .. versionchanged:: 1.0.0 + #: Changed to ``ImmutableMultiDict`` to support multiple values. + #: + #: .. versionadded:: 0.6 + dict_storage_class: t.Type[MultiDict] = ImmutableMultiDict + + #: the type to be used for list values from the incoming WSGI environment. + #: By default an :class:`~werkzeug.datastructures.ImmutableList` is used + #: (for example for :attr:`access_list`). + #: + #: .. versionadded:: 0.6 + list_storage_class: t.Type[t.List] = ImmutableList + + user_agent_class: t.Type[UserAgent] = UserAgent + """The class used and returned by the :attr:`user_agent` property to + parse the header. Defaults to + :class:`~werkzeug.user_agent.UserAgent`, which does no parsing. An + extension can provide a subclass that uses a parser to provide other + data. + + .. versionadded:: 2.0 + """ + + #: Valid host names when handling requests. By default all hosts are + #: trusted, which means that whatever the client says the host is + #: will be accepted. + #: + #: Because ``Host`` and ``X-Forwarded-Host`` headers can be set to + #: any value by a malicious client, it is recommended to either set + #: this property or implement similar validation in the proxy (if + #: the application is being run behind one). + #: + #: .. versionadded:: 0.9 + trusted_hosts: t.Optional[t.List[str]] = None + + def __init__( + self, + method: str, + scheme: str, + server: t.Optional[t.Tuple[str, t.Optional[int]]], + root_path: str, + path: str, + query_string: bytes, + headers: Headers, + remote_addr: t.Optional[str], + ) -> None: + #: The method the request was made with, such as ``GET``. + self.method = method.upper() + #: The URL scheme of the protocol the request used, such as + #: ``https`` or ``wss``. + self.scheme = scheme + #: The address of the server. ``(host, port)``, ``(path, None)`` + #: for unix sockets, or ``None`` if not known. + self.server = server + #: The prefix that the application is mounted under, without a + #: trailing slash. :attr:`path` comes after this. + self.root_path = root_path.rstrip("/") + #: The path part of the URL after :attr:`root_path`. This is the + #: path used for routing within the application. + self.path = "/" + path.lstrip("/") + #: The part of the URL after the "?". This is the raw value, use + #: :attr:`args` for the parsed values. + self.query_string = query_string + #: The headers received with the request. + self.headers = headers + #: The address of the client sending the request. + self.remote_addr = remote_addr + + def __repr__(self) -> str: + try: + url = self.url + except Exception as e: + url = f"(invalid URL: {e})" + + return f"<{type(self).__name__} {url!r} [{self.method}]>" + + @property + def url_charset(self) -> str: + """The charset that is assumed for URLs. Defaults to the value + of :attr:`charset`. + + .. versionadded:: 0.6 + """ + return self.charset + + @cached_property + def args(self) -> "MultiDict[str, str]": + """The parsed URL parameters (the part in the URL after the question + mark). + + By default an + :class:`~werkzeug.datastructures.ImmutableMultiDict` + is returned from this function. This can be changed by setting + :attr:`parameter_storage_class` to a different type. This might + be necessary if the order of the form data is important. + """ + return url_decode( + self.query_string, + self.url_charset, + errors=self.encoding_errors, + cls=self.parameter_storage_class, + ) + + @cached_property + def access_route(self) -> t.List[str]: + """If a forwarded header exists this is a list of all ip addresses + from the client ip to the last proxy server. + """ + if "X-Forwarded-For" in self.headers: + return self.list_storage_class( + parse_list_header(self.headers["X-Forwarded-For"]) + ) + elif self.remote_addr is not None: + return self.list_storage_class([self.remote_addr]) + return self.list_storage_class() + + @cached_property + def full_path(self) -> str: + """Requested path, including the query string.""" + return f"{self.path}?{_to_str(self.query_string, self.url_charset)}" + + @property + def is_secure(self) -> bool: + """``True`` if the request was made with a secure protocol + (HTTPS or WSS). + """ + return self.scheme in {"https", "wss"} + + @cached_property + def url(self) -> str: + """The full request URL with the scheme, host, root path, path, + and query string.""" + return get_current_url( + self.scheme, self.host, self.root_path, self.path, self.query_string + ) + + @cached_property + def base_url(self) -> str: + """Like :attr:`url` but without the query string.""" + return get_current_url(self.scheme, self.host, self.root_path, self.path) + + @cached_property + def root_url(self) -> str: + """The request URL scheme, host, and root path. This is the root + that the application is accessed from. + """ + return get_current_url(self.scheme, self.host, self.root_path) + + @cached_property + def host_url(self) -> str: + """The request URL scheme and host only.""" + return get_current_url(self.scheme, self.host) + + @cached_property + def host(self) -> str: + """The host name the request was made to, including the port if + it's non-standard. Validated with :attr:`trusted_hosts`. + """ + return get_host( + self.scheme, self.headers.get("host"), self.server, self.trusted_hosts + ) + + @cached_property + def cookies(self) -> "ImmutableMultiDict[str, str]": + """A :class:`dict` with the contents of all cookies transmitted with + the request.""" + wsgi_combined_cookie = ";".join(self.headers.getlist("Cookie")) + return parse_cookie( # type: ignore + wsgi_combined_cookie, + self.charset, + self.encoding_errors, + cls=self.dict_storage_class, + ) + + # Common Descriptors + + content_type = header_property[str]( + "Content-Type", + doc="""The Content-Type entity-header field indicates the media + type of the entity-body sent to the recipient or, in the case of + the HEAD method, the media type that would have been sent had + the request been a GET.""", + read_only=True, + ) + + @cached_property + def content_length(self) -> t.Optional[int]: + """The Content-Length entity-header field indicates the size of the + entity-body in bytes or, in the case of the HEAD method, the size of + the entity-body that would have been sent had the request been a + GET. + """ + if self.headers.get("Transfer-Encoding", "") == "chunked": + return None + + content_length = self.headers.get("Content-Length") + if content_length is not None: + try: + return max(0, int(content_length)) + except (ValueError, TypeError): + pass + + return None + + content_encoding = header_property[str]( + "Content-Encoding", + doc="""The Content-Encoding entity-header field is used as a + modifier to the media-type. When present, its value indicates + what additional content codings have been applied to the + entity-body, and thus what decoding mechanisms must be applied + in order to obtain the media-type referenced by the Content-Type + header field. + + .. versionadded:: 0.9""", + read_only=True, + ) + content_md5 = header_property[str]( + "Content-MD5", + doc="""The Content-MD5 entity-header field, as defined in + RFC 1864, is an MD5 digest of the entity-body for the purpose of + providing an end-to-end message integrity check (MIC) of the + entity-body. (Note: a MIC is good for detecting accidental + modification of the entity-body in transit, but is not proof + against malicious attacks.) + + .. versionadded:: 0.9""", + read_only=True, + ) + referrer = header_property[str]( + "Referer", + doc="""The Referer[sic] request-header field allows the client + to specify, for the server's benefit, the address (URI) of the + resource from which the Request-URI was obtained (the + "referrer", although the header field is misspelled).""", + read_only=True, + ) + date = header_property( + "Date", + None, + parse_date, + doc="""The Date general-header field represents the date and + time at which the message was originated, having the same + semantics as orig-date in RFC 822. + + .. versionchanged:: 2.0 + The datetime object is timezone-aware. + """, + read_only=True, + ) + max_forwards = header_property( + "Max-Forwards", + None, + int, + doc="""The Max-Forwards request-header field provides a + mechanism with the TRACE and OPTIONS methods to limit the number + of proxies or gateways that can forward the request to the next + inbound server.""", + read_only=True, + ) + + def _parse_content_type(self) -> None: + if not hasattr(self, "_parsed_content_type"): + self._parsed_content_type = parse_options_header( + self.headers.get("Content-Type", "") + ) + + @property + def mimetype(self) -> str: + """Like :attr:`content_type`, but without parameters (eg, without + charset, type etc.) and always lowercase. For example if the content + type is ``text/HTML; charset=utf-8`` the mimetype would be + ``'text/html'``. + """ + self._parse_content_type() + return self._parsed_content_type[0].lower() + + @property + def mimetype_params(self) -> t.Dict[str, str]: + """The mimetype parameters as dict. For example if the content + type is ``text/html; charset=utf-8`` the params would be + ``{'charset': 'utf-8'}``. + """ + self._parse_content_type() + return self._parsed_content_type[1] + + @cached_property + def pragma(self) -> HeaderSet: + """The Pragma general-header field is used to include + implementation-specific directives that might apply to any recipient + along the request/response chain. All pragma directives specify + optional behavior from the viewpoint of the protocol; however, some + systems MAY require that behavior be consistent with the directives. + """ + return parse_set_header(self.headers.get("Pragma", "")) + + # Accept + + @cached_property + def accept_mimetypes(self) -> MIMEAccept: + """List of mimetypes this client supports as + :class:`~werkzeug.datastructures.MIMEAccept` object. + """ + return parse_accept_header(self.headers.get("Accept"), MIMEAccept) + + @cached_property + def accept_charsets(self) -> CharsetAccept: + """List of charsets this client supports as + :class:`~werkzeug.datastructures.CharsetAccept` object. + """ + return parse_accept_header(self.headers.get("Accept-Charset"), CharsetAccept) + + @cached_property + def accept_encodings(self) -> Accept: + """List of encodings this client accepts. Encodings in a HTTP term + are compression encodings such as gzip. For charsets have a look at + :attr:`accept_charset`. + """ + return parse_accept_header(self.headers.get("Accept-Encoding")) + + @cached_property + def accept_languages(self) -> LanguageAccept: + """List of languages this client accepts as + :class:`~werkzeug.datastructures.LanguageAccept` object. + + .. versionchanged 0.5 + In previous versions this was a regular + :class:`~werkzeug.datastructures.Accept` object. + """ + return parse_accept_header(self.headers.get("Accept-Language"), LanguageAccept) + + # ETag + + @cached_property + def cache_control(self) -> RequestCacheControl: + """A :class:`~werkzeug.datastructures.RequestCacheControl` object + for the incoming cache control headers. + """ + cache_control = self.headers.get("Cache-Control") + return parse_cache_control_header(cache_control, None, RequestCacheControl) + + @cached_property + def if_match(self) -> ETags: + """An object containing all the etags in the `If-Match` header. + + :rtype: :class:`~werkzeug.datastructures.ETags` + """ + return parse_etags(self.headers.get("If-Match")) + + @cached_property + def if_none_match(self) -> ETags: + """An object containing all the etags in the `If-None-Match` header. + + :rtype: :class:`~werkzeug.datastructures.ETags` + """ + return parse_etags(self.headers.get("If-None-Match")) + + @cached_property + def if_modified_since(self) -> t.Optional[datetime]: + """The parsed `If-Modified-Since` header as a datetime object. + + .. versionchanged:: 2.0 + The datetime object is timezone-aware. + """ + return parse_date(self.headers.get("If-Modified-Since")) + + @cached_property + def if_unmodified_since(self) -> t.Optional[datetime]: + """The parsed `If-Unmodified-Since` header as a datetime object. + + .. versionchanged:: 2.0 + The datetime object is timezone-aware. + """ + return parse_date(self.headers.get("If-Unmodified-Since")) + + @cached_property + def if_range(self) -> IfRange: + """The parsed ``If-Range`` header. + + .. versionchanged:: 2.0 + ``IfRange.date`` is timezone-aware. + + .. versionadded:: 0.7 + """ + return parse_if_range_header(self.headers.get("If-Range")) + + @cached_property + def range(self) -> t.Optional[Range]: + """The parsed `Range` header. + + .. versionadded:: 0.7 + + :rtype: :class:`~werkzeug.datastructures.Range` + """ + return parse_range_header(self.headers.get("Range")) + + # User Agent + + @cached_property + def user_agent(self) -> UserAgent: + """The user agent. Use ``user_agent.string`` to get the header + value. Set :attr:`user_agent_class` to a subclass of + :class:`~werkzeug.user_agent.UserAgent` to provide parsing for + the other properties or other extended data. + + .. versionchanged:: 2.0 + The built in parser is deprecated and will be removed in + Werkzeug 2.1. A ``UserAgent`` subclass must be set to parse + data from the string. + """ + return self.user_agent_class(self.headers.get("User-Agent", "")) + + # Authorization + + @cached_property + def authorization(self) -> t.Optional[Authorization]: + """The `Authorization` object in parsed form.""" + return parse_authorization_header(self.headers.get("Authorization")) + + # CORS + + origin = header_property[str]( + "Origin", + doc=( + "The host that the request originated from. Set" + " :attr:`~CORSResponseMixin.access_control_allow_origin` on" + " the response to indicate which origins are allowed." + ), + read_only=True, + ) + + access_control_request_headers = header_property( + "Access-Control-Request-Headers", + load_func=parse_set_header, + doc=( + "Sent with a preflight request to indicate which headers" + " will be sent with the cross origin request. Set" + " :attr:`~CORSResponseMixin.access_control_allow_headers`" + " on the response to indicate which headers are allowed." + ), + read_only=True, + ) + + access_control_request_method = header_property[str]( + "Access-Control-Request-Method", + doc=( + "Sent with a preflight request to indicate which method" + " will be used for the cross origin request. Set" + " :attr:`~CORSResponseMixin.access_control_allow_methods`" + " on the response to indicate which methods are allowed." + ), + read_only=True, + ) + + @property + def is_json(self) -> bool: + """Check if the mimetype indicates JSON data, either + :mimetype:`application/json` or :mimetype:`application/*+json`. + """ + mt = self.mimetype + return ( + mt == "application/json" + or mt.startswith("application/") + and mt.endswith("+json") + ) diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/sansio/response.py b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/sansio/response.py new file mode 100644 index 0000000..de0bec2 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/sansio/response.py @@ -0,0 +1,704 @@ +import typing as t +from datetime import datetime +from datetime import timedelta +from datetime import timezone +from http import HTTPStatus + +from .._internal import _to_str +from ..datastructures import Headers +from ..datastructures import HeaderSet +from ..http import dump_cookie +from ..http import HTTP_STATUS_CODES +from ..utils import get_content_type +from werkzeug.datastructures import CallbackDict +from werkzeug.datastructures import ContentRange +from werkzeug.datastructures import ContentSecurityPolicy +from werkzeug.datastructures import ResponseCacheControl +from werkzeug.datastructures import WWWAuthenticate +from werkzeug.http import COEP +from werkzeug.http import COOP +from werkzeug.http import dump_age +from werkzeug.http import dump_header +from werkzeug.http import dump_options_header +from werkzeug.http import http_date +from werkzeug.http import parse_age +from werkzeug.http import parse_cache_control_header +from werkzeug.http import parse_content_range_header +from werkzeug.http import parse_csp_header +from werkzeug.http import parse_date +from werkzeug.http import parse_options_header +from werkzeug.http import parse_set_header +from werkzeug.http import parse_www_authenticate_header +from werkzeug.http import quote_etag +from werkzeug.http import unquote_etag +from werkzeug.utils import header_property + + +def _set_property(name: str, doc: t.Optional[str] = None) -> property: + def fget(self: "Response") -> HeaderSet: + def on_update(header_set: HeaderSet) -> None: + if not header_set and name in self.headers: + del self.headers[name] + elif header_set: + self.headers[name] = header_set.to_header() + + return parse_set_header(self.headers.get(name), on_update) + + def fset( + self: "Response", + value: t.Optional[ + t.Union[str, t.Dict[str, t.Union[str, int]], t.Iterable[str]] + ], + ) -> None: + if not value: + del self.headers[name] + elif isinstance(value, str): + self.headers[name] = value + else: + self.headers[name] = dump_header(value) + + return property(fget, fset, doc=doc) + + +class Response: + """Represents the non-IO parts of an HTTP response, specifically the + status and headers but not the body. + + This class is not meant for general use. It should only be used when + implementing WSGI, ASGI, or another HTTP application spec. Werkzeug + provides a WSGI implementation at :cls:`werkzeug.wrappers.Response`. + + :param status: The status code for the response. Either an int, in + which case the default status message is added, or a string in + the form ``{code} {message}``, like ``404 Not Found``. Defaults + to 200. + :param headers: A :class:`~werkzeug.datastructures.Headers` object, + or a list of ``(key, value)`` tuples that will be converted to a + ``Headers`` object. + :param mimetype: The mime type (content type without charset or + other parameters) of the response. If the value starts with + ``text/`` (or matches some other special cases), the charset + will be added to create the ``content_type``. + :param content_type: The full content type of the response. + Overrides building the value from ``mimetype``. + + .. versionadded:: 2.0 + """ + + #: the charset of the response. + charset = "utf-8" + + #: the default status if none is provided. + default_status = 200 + + #: the default mimetype if none is provided. + default_mimetype: t.Optional[str] = "text/plain" + + #: Warn if a cookie header exceeds this size. The default, 4093, should be + #: safely `supported by most browsers `_. A cookie larger than + #: this size will still be sent, but it may be ignored or handled + #: incorrectly by some browsers. Set to 0 to disable this check. + #: + #: .. versionadded:: 0.13 + #: + #: .. _`cookie`: http://browsercookielimits.squawky.net/ + max_cookie_size = 4093 + + # A :class:`Headers` object representing the response headers. + headers: Headers + + def __init__( + self, + status: t.Optional[t.Union[int, str, HTTPStatus]] = None, + headers: t.Optional[ + t.Union[ + t.Mapping[str, t.Union[str, int, t.Iterable[t.Union[str, int]]]], + t.Iterable[t.Tuple[str, t.Union[str, int]]], + ] + ] = None, + mimetype: t.Optional[str] = None, + content_type: t.Optional[str] = None, + ) -> None: + if isinstance(headers, Headers): + self.headers = headers + elif not headers: + self.headers = Headers() + else: + self.headers = Headers(headers) + + if content_type is None: + if mimetype is None and "content-type" not in self.headers: + mimetype = self.default_mimetype + if mimetype is not None: + mimetype = get_content_type(mimetype, self.charset) + content_type = mimetype + if content_type is not None: + self.headers["Content-Type"] = content_type + if status is None: + status = self.default_status + self.status = status # type: ignore + + def __repr__(self) -> str: + return f"<{type(self).__name__} [{self.status}]>" + + @property + def status_code(self) -> int: + """The HTTP status code as a number.""" + return self._status_code + + @status_code.setter + def status_code(self, code: int) -> None: + self.status = code # type: ignore + + @property + def status(self) -> str: + """The HTTP status code as a string.""" + return self._status + + @status.setter + def status(self, value: t.Union[str, int, HTTPStatus]) -> None: + if not isinstance(value, (str, bytes, int, HTTPStatus)): + raise TypeError("Invalid status argument") + + self._status, self._status_code = self._clean_status(value) + + def _clean_status(self, value: t.Union[str, int, HTTPStatus]) -> t.Tuple[str, int]: + if isinstance(value, HTTPStatus): + value = int(value) + status = _to_str(value, self.charset) + split_status = status.split(None, 1) + + if len(split_status) == 0: + raise ValueError("Empty status argument") + + try: + status_code = int(split_status[0]) + except ValueError: + # only message + return f"0 {status}", 0 + + if len(split_status) > 1: + # code and message + return status, status_code + + # only code, look up message + try: + status = f"{status_code} {HTTP_STATUS_CODES[status_code].upper()}" + except KeyError: + status = f"{status_code} UNKNOWN" + + return status, status_code + + def set_cookie( + self, + key: str, + value: str = "", + max_age: t.Optional[t.Union[timedelta, int]] = None, + expires: t.Optional[t.Union[str, datetime, int, float]] = None, + path: t.Optional[str] = "/", + domain: t.Optional[str] = None, + secure: bool = False, + httponly: bool = False, + samesite: t.Optional[str] = None, + ) -> None: + """Sets a cookie. + + A warning is raised if the size of the cookie header exceeds + :attr:`max_cookie_size`, but the header will still be set. + + :param key: the key (name) of the cookie to be set. + :param value: the value of the cookie. + :param max_age: should be a number of seconds, or `None` (default) if + the cookie should last only as long as the client's + browser session. + :param expires: should be a `datetime` object or UNIX timestamp. + :param path: limits the cookie to a given path, per default it will + span the whole domain. + :param domain: if you want to set a cross-domain cookie. For example, + ``domain=".example.com"`` will set a cookie that is + readable by the domain ``www.example.com``, + ``foo.example.com`` etc. Otherwise, a cookie will only + be readable by the domain that set it. + :param secure: If ``True``, the cookie will only be available + via HTTPS. + :param httponly: Disallow JavaScript access to the cookie. + :param samesite: Limit the scope of the cookie to only be + attached to requests that are "same-site". + """ + self.headers.add( + "Set-Cookie", + dump_cookie( + key, + value=value, + max_age=max_age, + expires=expires, + path=path, + domain=domain, + secure=secure, + httponly=httponly, + charset=self.charset, + max_size=self.max_cookie_size, + samesite=samesite, + ), + ) + + def delete_cookie( + self, + key: str, + path: str = "/", + domain: t.Optional[str] = None, + secure: bool = False, + httponly: bool = False, + samesite: t.Optional[str] = None, + ) -> None: + """Delete a cookie. Fails silently if key doesn't exist. + + :param key: the key (name) of the cookie to be deleted. + :param path: if the cookie that should be deleted was limited to a + path, the path has to be defined here. + :param domain: if the cookie that should be deleted was limited to a + domain, that domain has to be defined here. + :param secure: If ``True``, the cookie will only be available + via HTTPS. + :param httponly: Disallow JavaScript access to the cookie. + :param samesite: Limit the scope of the cookie to only be + attached to requests that are "same-site". + """ + self.set_cookie( + key, + expires=0, + max_age=0, + path=path, + domain=domain, + secure=secure, + httponly=httponly, + samesite=samesite, + ) + + @property + def is_json(self) -> bool: + """Check if the mimetype indicates JSON data, either + :mimetype:`application/json` or :mimetype:`application/*+json`. + """ + mt = self.mimetype + return mt is not None and ( + mt == "application/json" + or mt.startswith("application/") + and mt.endswith("+json") + ) + + # Common Descriptors + + @property + def mimetype(self) -> t.Optional[str]: + """The mimetype (content type without charset etc.)""" + ct = self.headers.get("content-type") + + if ct: + return ct.split(";")[0].strip() + else: + return None + + @mimetype.setter + def mimetype(self, value: str) -> None: + self.headers["Content-Type"] = get_content_type(value, self.charset) + + @property + def mimetype_params(self) -> t.Dict[str, str]: + """The mimetype parameters as dict. For example if the + content type is ``text/html; charset=utf-8`` the params would be + ``{'charset': 'utf-8'}``. + + .. versionadded:: 0.5 + """ + + def on_update(d: CallbackDict) -> None: + self.headers["Content-Type"] = dump_options_header(self.mimetype, d) + + d = parse_options_header(self.headers.get("content-type", ""))[1] + return CallbackDict(d, on_update) + + location = header_property[str]( + "Location", + doc="""The Location response-header field is used to redirect + the recipient to a location other than the Request-URI for + completion of the request or identification of a new + resource.""", + ) + age = header_property( + "Age", + None, + parse_age, + dump_age, # type: ignore + doc="""The Age response-header field conveys the sender's + estimate of the amount of time since the response (or its + revalidation) was generated at the origin server. + + Age values are non-negative decimal integers, representing time + in seconds.""", + ) + content_type = header_property[str]( + "Content-Type", + doc="""The Content-Type entity-header field indicates the media + type of the entity-body sent to the recipient or, in the case of + the HEAD method, the media type that would have been sent had + the request been a GET.""", + ) + content_length = header_property( + "Content-Length", + None, + int, + str, + doc="""The Content-Length entity-header field indicates the size + of the entity-body, in decimal number of OCTETs, sent to the + recipient or, in the case of the HEAD method, the size of the + entity-body that would have been sent had the request been a + GET.""", + ) + content_location = header_property[str]( + "Content-Location", + doc="""The Content-Location entity-header field MAY be used to + supply the resource location for the entity enclosed in the + message when that entity is accessible from a location separate + from the requested resource's URI.""", + ) + content_encoding = header_property[str]( + "Content-Encoding", + doc="""The Content-Encoding entity-header field is used as a + modifier to the media-type. When present, its value indicates + what additional content codings have been applied to the + entity-body, and thus what decoding mechanisms must be applied + in order to obtain the media-type referenced by the Content-Type + header field.""", + ) + content_md5 = header_property[str]( + "Content-MD5", + doc="""The Content-MD5 entity-header field, as defined in + RFC 1864, is an MD5 digest of the entity-body for the purpose of + providing an end-to-end message integrity check (MIC) of the + entity-body. (Note: a MIC is good for detecting accidental + modification of the entity-body in transit, but is not proof + against malicious attacks.)""", + ) + date = header_property( + "Date", + None, + parse_date, + http_date, + doc="""The Date general-header field represents the date and + time at which the message was originated, having the same + semantics as orig-date in RFC 822. + + .. versionchanged:: 2.0 + The datetime object is timezone-aware. + """, + ) + expires = header_property( + "Expires", + None, + parse_date, + http_date, + doc="""The Expires entity-header field gives the date/time after + which the response is considered stale. A stale cache entry may + not normally be returned by a cache. + + .. versionchanged:: 2.0 + The datetime object is timezone-aware. + """, + ) + last_modified = header_property( + "Last-Modified", + None, + parse_date, + http_date, + doc="""The Last-Modified entity-header field indicates the date + and time at which the origin server believes the variant was + last modified. + + .. versionchanged:: 2.0 + The datetime object is timezone-aware. + """, + ) + + @property + def retry_after(self) -> t.Optional[datetime]: + """The Retry-After response-header field can be used with a + 503 (Service Unavailable) response to indicate how long the + service is expected to be unavailable to the requesting client. + + Time in seconds until expiration or date. + + .. versionchanged:: 2.0 + The datetime object is timezone-aware. + """ + value = self.headers.get("retry-after") + if value is None: + return None + + try: + seconds = int(value) + except ValueError: + return parse_date(value) + + return datetime.now(timezone.utc) + timedelta(seconds=seconds) + + @retry_after.setter + def retry_after(self, value: t.Optional[t.Union[datetime, int, str]]) -> None: + if value is None: + if "retry-after" in self.headers: + del self.headers["retry-after"] + return + elif isinstance(value, datetime): + value = http_date(value) + else: + value = str(value) + self.headers["Retry-After"] = value + + vary = _set_property( + "Vary", + doc="""The Vary field value indicates the set of request-header + fields that fully determines, while the response is fresh, + whether a cache is permitted to use the response to reply to a + subsequent request without revalidation.""", + ) + content_language = _set_property( + "Content-Language", + doc="""The Content-Language entity-header field describes the + natural language(s) of the intended audience for the enclosed + entity. Note that this might not be equivalent to all the + languages used within the entity-body.""", + ) + allow = _set_property( + "Allow", + doc="""The Allow entity-header field lists the set of methods + supported by the resource identified by the Request-URI. The + purpose of this field is strictly to inform the recipient of + valid methods associated with the resource. An Allow header + field MUST be present in a 405 (Method Not Allowed) + response.""", + ) + + # ETag + + @property + def cache_control(self) -> ResponseCacheControl: + """The Cache-Control general-header field is used to specify + directives that MUST be obeyed by all caching mechanisms along the + request/response chain. + """ + + def on_update(cache_control: ResponseCacheControl) -> None: + if not cache_control and "cache-control" in self.headers: + del self.headers["cache-control"] + elif cache_control: + self.headers["Cache-Control"] = cache_control.to_header() + + return parse_cache_control_header( + self.headers.get("cache-control"), on_update, ResponseCacheControl + ) + + def set_etag(self, etag: str, weak: bool = False) -> None: + """Set the etag, and override the old one if there was one.""" + self.headers["ETag"] = quote_etag(etag, weak) + + def get_etag(self) -> t.Union[t.Tuple[str, bool], t.Tuple[None, None]]: + """Return a tuple in the form ``(etag, is_weak)``. If there is no + ETag the return value is ``(None, None)``. + """ + return unquote_etag(self.headers.get("ETag")) + + accept_ranges = header_property[str]( + "Accept-Ranges", + doc="""The `Accept-Ranges` header. Even though the name would + indicate that multiple values are supported, it must be one + string token only. + + The values ``'bytes'`` and ``'none'`` are common. + + .. versionadded:: 0.7""", + ) + + @property + def content_range(self) -> ContentRange: + """The ``Content-Range`` header as a + :class:`~werkzeug.datastructures.ContentRange` object. Available + even if the header is not set. + + .. versionadded:: 0.7 + """ + + def on_update(rng: ContentRange) -> None: + if not rng: + del self.headers["content-range"] + else: + self.headers["Content-Range"] = rng.to_header() + + rv = parse_content_range_header(self.headers.get("content-range"), on_update) + # always provide a content range object to make the descriptor + # more user friendly. It provides an unset() method that can be + # used to remove the header quickly. + if rv is None: + rv = ContentRange(None, None, None, on_update=on_update) + return rv + + @content_range.setter + def content_range(self, value: t.Optional[t.Union[ContentRange, str]]) -> None: + if not value: + del self.headers["content-range"] + elif isinstance(value, str): + self.headers["Content-Range"] = value + else: + self.headers["Content-Range"] = value.to_header() + + # Authorization + + @property + def www_authenticate(self) -> WWWAuthenticate: + """The ``WWW-Authenticate`` header in a parsed form.""" + + def on_update(www_auth: WWWAuthenticate) -> None: + if not www_auth and "www-authenticate" in self.headers: + del self.headers["www-authenticate"] + elif www_auth: + self.headers["WWW-Authenticate"] = www_auth.to_header() + + header = self.headers.get("www-authenticate") + return parse_www_authenticate_header(header, on_update) + + # CSP + + @property + def content_security_policy(self) -> ContentSecurityPolicy: + """The ``Content-Security-Policy`` header as a + :class:`~werkzeug.datastructures.ContentSecurityPolicy` object. Available + even if the header is not set. + + The Content-Security-Policy header adds an additional layer of + security to help detect and mitigate certain types of attacks. + """ + + def on_update(csp: ContentSecurityPolicy) -> None: + if not csp: + del self.headers["content-security-policy"] + else: + self.headers["Content-Security-Policy"] = csp.to_header() + + rv = parse_csp_header(self.headers.get("content-security-policy"), on_update) + if rv is None: + rv = ContentSecurityPolicy(None, on_update=on_update) + return rv + + @content_security_policy.setter + def content_security_policy( + self, value: t.Optional[t.Union[ContentSecurityPolicy, str]] + ) -> None: + if not value: + del self.headers["content-security-policy"] + elif isinstance(value, str): + self.headers["Content-Security-Policy"] = value + else: + self.headers["Content-Security-Policy"] = value.to_header() + + @property + def content_security_policy_report_only(self) -> ContentSecurityPolicy: + """The ``Content-Security-policy-report-only`` header as a + :class:`~werkzeug.datastructures.ContentSecurityPolicy` object. Available + even if the header is not set. + + The Content-Security-Policy-Report-Only header adds a csp policy + that is not enforced but is reported thereby helping detect + certain types of attacks. + """ + + def on_update(csp: ContentSecurityPolicy) -> None: + if not csp: + del self.headers["content-security-policy-report-only"] + else: + self.headers["Content-Security-policy-report-only"] = csp.to_header() + + rv = parse_csp_header( + self.headers.get("content-security-policy-report-only"), on_update + ) + if rv is None: + rv = ContentSecurityPolicy(None, on_update=on_update) + return rv + + @content_security_policy_report_only.setter + def content_security_policy_report_only( + self, value: t.Optional[t.Union[ContentSecurityPolicy, str]] + ) -> None: + if not value: + del self.headers["content-security-policy-report-only"] + elif isinstance(value, str): + self.headers["Content-Security-policy-report-only"] = value + else: + self.headers["Content-Security-policy-report-only"] = value.to_header() + + # CORS + + @property + def access_control_allow_credentials(self) -> bool: + """Whether credentials can be shared by the browser to + JavaScript code. As part of the preflight request it indicates + whether credentials can be used on the cross origin request. + """ + return "Access-Control-Allow-Credentials" in self.headers + + @access_control_allow_credentials.setter + def access_control_allow_credentials(self, value: t.Optional[bool]) -> None: + if value is True: + self.headers["Access-Control-Allow-Credentials"] = "true" + else: + self.headers.pop("Access-Control-Allow-Credentials", None) + + access_control_allow_headers = header_property( + "Access-Control-Allow-Headers", + load_func=parse_set_header, + dump_func=dump_header, + doc="Which headers can be sent with the cross origin request.", + ) + + access_control_allow_methods = header_property( + "Access-Control-Allow-Methods", + load_func=parse_set_header, + dump_func=dump_header, + doc="Which methods can be used for the cross origin request.", + ) + + access_control_allow_origin = header_property[str]( + "Access-Control-Allow-Origin", + doc="The origin or '*' for any origin that may make cross origin requests.", + ) + + access_control_expose_headers = header_property( + "Access-Control-Expose-Headers", + load_func=parse_set_header, + dump_func=dump_header, + doc="Which headers can be shared by the browser to JavaScript code.", + ) + + access_control_max_age = header_property( + "Access-Control-Max-Age", + load_func=int, + dump_func=str, + doc="The maximum age in seconds the access control settings can be cached for.", + ) + + cross_origin_opener_policy = header_property[COOP]( + "Cross-Origin-Opener-Policy", + load_func=lambda value: COOP(value), + dump_func=lambda value: value.value, + default=COOP.UNSAFE_NONE, + doc="""Allows control over sharing of browsing context group with cross-origin + documents. Values must be a member of the :class:`werkzeug.http.COOP` enum.""", + ) + + cross_origin_embedder_policy = header_property[COEP]( + "Cross-Origin-Embedder-Policy", + load_func=lambda value: COEP(value), + dump_func=lambda value: value.value, + default=COEP.UNSAFE_NONE, + doc="""Prevents a document from loading any cross-origin resources that do not + explicitly grant the document permission. Values must be a member of the + :class:`werkzeug.http.COEP` enum.""", + ) diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/sansio/utils.py b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/sansio/utils.py new file mode 100644 index 0000000..e639dcb --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/sansio/utils.py @@ -0,0 +1,165 @@ +import typing as t + +from .._internal import _encode_idna +from ..exceptions import SecurityError +from ..urls import uri_to_iri +from ..urls import url_quote + + +def host_is_trusted(hostname: str, trusted_list: t.Iterable[str]) -> bool: + """Check if a host matches a list of trusted names. + + :param hostname: The name to check. + :param trusted_list: A list of valid names to match. If a name + starts with a dot it will match all subdomains. + + .. versionadded:: 0.9 + """ + if not hostname: + return False + + if isinstance(trusted_list, str): + trusted_list = [trusted_list] + + def _normalize(hostname: str) -> bytes: + if ":" in hostname: + hostname = hostname.rsplit(":", 1)[0] + + return _encode_idna(hostname) + + try: + hostname_bytes = _normalize(hostname) + except UnicodeError: + return False + + for ref in trusted_list: + if ref.startswith("."): + ref = ref[1:] + suffix_match = True + else: + suffix_match = False + + try: + ref_bytes = _normalize(ref) + except UnicodeError: + return False + + if ref_bytes == hostname_bytes: + return True + + if suffix_match and hostname_bytes.endswith(b"." + ref_bytes): + return True + + return False + + +def get_host( + scheme: str, + host_header: t.Optional[str], + server: t.Optional[t.Tuple[str, t.Optional[int]]] = None, + trusted_hosts: t.Optional[t.Iterable[str]] = None, +) -> str: + """Return the host for the given parameters. + + This first checks the ``host_header``. If it's not present, then + ``server`` is used. The host will only contain the port if it is + different than the standard port for the protocol. + + Optionally, verify that the host is trusted using + :func:`host_is_trusted` and raise a + :exc:`~werkzeug.exceptions.SecurityError` if it is not. + + :param scheme: The protocol the request used, like ``"https"``. + :param host_header: The ``Host`` header value. + :param server: Address of the server. ``(host, port)``, or + ``(path, None)`` for unix sockets. + :param trusted_hosts: A list of trusted host names. + + :return: Host, with port if necessary. + :raise ~werkzeug.exceptions.SecurityError: If the host is not + trusted. + """ + host = "" + + if host_header is not None: + host = host_header + elif server is not None: + host = server[0] + + if server[1] is not None: + host = f"{host}:{server[1]}" + + if scheme in {"http", "ws"} and host.endswith(":80"): + host = host[:-3] + elif scheme in {"https", "wss"} and host.endswith(":443"): + host = host[:-4] + + if trusted_hosts is not None: + if not host_is_trusted(host, trusted_hosts): + raise SecurityError(f"Host {host!r} is not trusted.") + + return host + + +def get_current_url( + scheme: str, + host: str, + root_path: t.Optional[str] = None, + path: t.Optional[str] = None, + query_string: t.Optional[bytes] = None, +) -> str: + """Recreate the URL for a request. If an optional part isn't + provided, it and subsequent parts are not included in the URL. + + The URL is an IRI, not a URI, so it may contain Unicode characters. + Use :func:`~werkzeug.urls.iri_to_uri` to convert it to ASCII. + + :param scheme: The protocol the request used, like ``"https"``. + :param host: The host the request was made to. See :func:`get_host`. + :param root_path: Prefix that the application is mounted under. This + is prepended to ``path``. + :param path: The path part of the URL after ``root_path``. + :param query_string: The portion of the URL after the "?". + """ + url = [scheme, "://", host] + + if root_path is None: + url.append("/") + return uri_to_iri("".join(url)) + + url.append(url_quote(root_path.rstrip("/"))) + url.append("/") + + if path is None: + return uri_to_iri("".join(url)) + + url.append(url_quote(path.lstrip("/"))) + + if query_string: + url.append("?") + url.append(url_quote(query_string, safe=":&%=+$!*'(),")) + + return uri_to_iri("".join(url)) + + +def get_content_length( + http_content_length: t.Union[str, None] = None, + http_transfer_encoding: t.Union[str, None] = "", +) -> t.Optional[int]: + """Returns the content length as an integer or ``None`` if + unavailable or chunked transfer encoding is used. + + :param http_content_length: The Content-Length HTTP header. + :param http_transfer_encoding: The Transfer-Encoding HTTP header. + + .. versionadded:: 2.2 + """ + if http_transfer_encoding == "chunked": + return None + + if http_content_length is not None: + try: + return max(0, int(http_content_length)) + except (ValueError, TypeError): + pass + return None diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/security.py b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/security.py new file mode 100644 index 0000000..4599fb3 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/security.py @@ -0,0 +1,140 @@ +import hashlib +import hmac +import os +import posixpath +import secrets +import typing as t + +if t.TYPE_CHECKING: + pass + +SALT_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" +DEFAULT_PBKDF2_ITERATIONS = 260000 + +_os_alt_seps: t.List[str] = list( + sep for sep in [os.sep, os.path.altsep] if sep is not None and sep != "/" +) + + +def gen_salt(length: int) -> str: + """Generate a random string of SALT_CHARS with specified ``length``.""" + if length <= 0: + raise ValueError("Salt length must be positive") + + return "".join(secrets.choice(SALT_CHARS) for _ in range(length)) + + +def _hash_internal(method: str, salt: str, password: str) -> t.Tuple[str, str]: + """Internal password hash helper. Supports plaintext without salt, + unsalted and salted passwords. In case salted passwords are used + hmac is used. + """ + if method == "plain": + return password, method + + salt = salt.encode("utf-8") + password = password.encode("utf-8") + + if method.startswith("pbkdf2:"): + if not salt: + raise ValueError("Salt is required for PBKDF2") + + args = method[7:].split(":") + + if len(args) not in (1, 2): + raise ValueError("Invalid number of arguments for PBKDF2") + + method = args.pop(0) + iterations = int(args[0] or 0) if args else DEFAULT_PBKDF2_ITERATIONS + return ( + hashlib.pbkdf2_hmac(method, password, salt, iterations).hex(), + f"pbkdf2:{method}:{iterations}", + ) + + if salt: + return hmac.new(salt, password, method).hexdigest(), method + + return hashlib.new(method, password).hexdigest(), method + + +def generate_password_hash( + password: str, method: str = "pbkdf2:sha256", salt_length: int = 16 +) -> str: + """Hash a password with the given method and salt with a string of + the given length. The format of the string returned includes the method + that was used so that :func:`check_password_hash` can check the hash. + + The format for the hashed string looks like this:: + + method$salt$hash + + This method can **not** generate unsalted passwords but it is possible + to set param method='plain' in order to enforce plaintext passwords. + If a salt is used, hmac is used internally to salt the password. + + If PBKDF2 is wanted it can be enabled by setting the method to + ``pbkdf2:method:iterations`` where iterations is optional:: + + pbkdf2:sha256:80000$salt$hash + pbkdf2:sha256$salt$hash + + :param password: the password to hash. + :param method: the hash method to use (one that hashlib supports). Can + optionally be in the format ``pbkdf2:method:iterations`` + to enable PBKDF2. + :param salt_length: the length of the salt in letters. + """ + salt = gen_salt(salt_length) if method != "plain" else "" + h, actual_method = _hash_internal(method, salt, password) + return f"{actual_method}${salt}${h}" + + +def check_password_hash(pwhash: str, password: str) -> bool: + """Check a password against a given salted and hashed password value. + In order to support unsalted legacy passwords this method supports + plain text passwords, md5 and sha1 hashes (both salted and unsalted). + + Returns `True` if the password matched, `False` otherwise. + + :param pwhash: a hashed string like returned by + :func:`generate_password_hash`. + :param password: the plaintext password to compare against the hash. + """ + if pwhash.count("$") < 2: + return False + + method, salt, hashval = pwhash.split("$", 2) + return hmac.compare_digest(_hash_internal(method, salt, password)[0], hashval) + + +def safe_join(directory: str, *pathnames: str) -> t.Optional[str]: + """Safely join zero or more untrusted path components to a base + directory to avoid escaping the base directory. + + :param directory: The trusted base directory. + :param pathnames: The untrusted path components relative to the + base directory. + :return: A safe path, otherwise ``None``. + """ + if not directory: + # Ensure we end up with ./path if directory="" is given, + # otherwise the first untrusted part could become trusted. + directory = "." + + parts = [directory] + + for filename in pathnames: + if filename != "": + filename = posixpath.normpath(filename) + + if ( + any(sep in filename for sep in _os_alt_seps) + or os.path.isabs(filename) + or filename == ".." + or filename.startswith("../") + ): + return None + + parts.append(filename) + + return posixpath.join(*parts) diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/serving.py b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/serving.py new file mode 100644 index 0000000..2a2e74d --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/serving.py @@ -0,0 +1,1069 @@ +"""A WSGI and HTTP server for use **during development only**. This +server is convenient to use, but is not designed to be particularly +stable, secure, or efficient. Use a dedicate WSGI server and HTTP +server when deploying to production. + +It provides features like interactive debugging and code reloading. Use +``run_simple`` to start the server. Put this in a ``run.py`` script: + +.. code-block:: python + + from myapp import create_app + from werkzeug import run_simple +""" +import errno +import io +import os +import socket +import socketserver +import sys +import typing as t +from datetime import datetime as dt +from datetime import timedelta +from datetime import timezone +from http.server import BaseHTTPRequestHandler +from http.server import HTTPServer + +from ._internal import _log +from ._internal import _wsgi_encoding_dance +from .exceptions import InternalServerError +from .urls import uri_to_iri +from .urls import url_parse +from .urls import url_unquote + +try: + import ssl +except ImportError: + + class _SslDummy: + def __getattr__(self, name: str) -> t.Any: + raise RuntimeError( # noqa: B904 + "SSL is unavailable because this Python runtime was not" + " compiled with SSL/TLS support." + ) + + ssl = _SslDummy() # type: ignore + +_log_add_style = True + +if os.name == "nt": + try: + __import__("colorama") + except ImportError: + _log_add_style = False + +can_fork = hasattr(os, "fork") + +if can_fork: + ForkingMixIn = socketserver.ForkingMixIn +else: + + class ForkingMixIn: # type: ignore + pass + + +try: + af_unix = socket.AF_UNIX +except AttributeError: + af_unix = None # type: ignore + +LISTEN_QUEUE = 128 + +_TSSLContextArg = t.Optional[ + t.Union["ssl.SSLContext", t.Tuple[str, t.Optional[str]], "te.Literal['adhoc']"] +] + +if t.TYPE_CHECKING: + import typing_extensions as te # noqa: F401 + from _typeshed.wsgi import WSGIApplication + from _typeshed.wsgi import WSGIEnvironment + from cryptography.hazmat.primitives.asymmetric.rsa import ( + RSAPrivateKeyWithSerialization, + ) + from cryptography.x509 import Certificate + + +class DechunkedInput(io.RawIOBase): + """An input stream that handles Transfer-Encoding 'chunked'""" + + def __init__(self, rfile: t.IO[bytes]) -> None: + self._rfile = rfile + self._done = False + self._len = 0 + + def readable(self) -> bool: + return True + + def read_chunk_len(self) -> int: + try: + line = self._rfile.readline().decode("latin1") + _len = int(line.strip(), 16) + except ValueError as e: + raise OSError("Invalid chunk header") from e + if _len < 0: + raise OSError("Negative chunk length not allowed") + return _len + + def readinto(self, buf: bytearray) -> int: # type: ignore + read = 0 + while not self._done and read < len(buf): + if self._len == 0: + # This is the first chunk or we fully consumed the previous + # one. Read the next length of the next chunk + self._len = self.read_chunk_len() + + if self._len == 0: + # Found the final chunk of size 0. The stream is now exhausted, + # but there is still a final newline that should be consumed + self._done = True + + if self._len > 0: + # There is data (left) in this chunk, so append it to the + # buffer. If this operation fully consumes the chunk, this will + # reset self._len to 0. + n = min(len(buf), self._len) + + # If (read + chunk size) becomes more than len(buf), buf will + # grow beyond the original size and read more data than + # required. So only read as much data as can fit in buf. + if read + n > len(buf): + buf[read:] = self._rfile.read(len(buf) - read) + self._len -= len(buf) - read + read = len(buf) + else: + buf[read : read + n] = self._rfile.read(n) + self._len -= n + read += n + + if self._len == 0: + # Skip the terminating newline of a chunk that has been fully + # consumed. This also applies to the 0-sized final chunk + terminator = self._rfile.readline() + if terminator not in (b"\n", b"\r\n", b"\r"): + raise OSError("Missing chunk terminating newline") + + return read + + +class WSGIRequestHandler(BaseHTTPRequestHandler): + """A request handler that implements WSGI dispatching.""" + + server: "BaseWSGIServer" + + @property + def server_version(self) -> str: # type: ignore + from . import __version__ + + return f"Werkzeug/{__version__}" + + def make_environ(self) -> "WSGIEnvironment": + request_url = url_parse(self.path) + url_scheme = "http" if self.server.ssl_context is None else "https" + + if not self.client_address: + self.client_address = ("", 0) + elif isinstance(self.client_address, str): + self.client_address = (self.client_address, 0) + + # If there was no scheme but the path started with two slashes, + # the first segment may have been incorrectly parsed as the + # netloc, prepend it to the path again. + if not request_url.scheme and request_url.netloc: + path_info = f"/{request_url.netloc}{request_url.path}" + else: + path_info = request_url.path + + path_info = url_unquote(path_info) + + environ: "WSGIEnvironment" = { + "wsgi.version": (1, 0), + "wsgi.url_scheme": url_scheme, + "wsgi.input": self.rfile, + "wsgi.errors": sys.stderr, + "wsgi.multithread": self.server.multithread, + "wsgi.multiprocess": self.server.multiprocess, + "wsgi.run_once": False, + "werkzeug.socket": self.connection, + "SERVER_SOFTWARE": self.server_version, + "REQUEST_METHOD": self.command, + "SCRIPT_NAME": "", + "PATH_INFO": _wsgi_encoding_dance(path_info), + "QUERY_STRING": _wsgi_encoding_dance(request_url.query), + # Non-standard, added by mod_wsgi, uWSGI + "REQUEST_URI": _wsgi_encoding_dance(self.path), + # Non-standard, added by gunicorn + "RAW_URI": _wsgi_encoding_dance(self.path), + "REMOTE_ADDR": self.address_string(), + "REMOTE_PORT": self.port_integer(), + "SERVER_NAME": self.server.server_address[0], + "SERVER_PORT": str(self.server.server_address[1]), + "SERVER_PROTOCOL": self.request_version, + } + + for key, value in self.headers.items(): + key = key.upper().replace("-", "_") + value = value.replace("\r\n", "") + if key not in ("CONTENT_TYPE", "CONTENT_LENGTH"): + key = f"HTTP_{key}" + if key in environ: + value = f"{environ[key]},{value}" + environ[key] = value + + if environ.get("HTTP_TRANSFER_ENCODING", "").strip().lower() == "chunked": + environ["wsgi.input_terminated"] = True + environ["wsgi.input"] = DechunkedInput(environ["wsgi.input"]) + + # Per RFC 2616, if the URL is absolute, use that as the host. + # We're using "has a scheme" to indicate an absolute URL. + if request_url.scheme and request_url.netloc: + environ["HTTP_HOST"] = request_url.netloc + + try: + # binary_form=False gives nicer information, but wouldn't be compatible with + # what Nginx or Apache could return. + peer_cert = self.connection.getpeercert(binary_form=True) + if peer_cert is not None: + # Nginx and Apache use PEM format. + environ["SSL_CLIENT_CERT"] = ssl.DER_cert_to_PEM_cert(peer_cert) + except ValueError: + # SSL handshake hasn't finished. + self.server.log("error", "Cannot fetch SSL peer certificate info") + except AttributeError: + # Not using TLS, the socket will not have getpeercert(). + pass + + return environ + + def run_wsgi(self) -> None: + if self.headers.get("Expect", "").lower().strip() == "100-continue": + self.wfile.write(b"HTTP/1.1 100 Continue\r\n\r\n") + + self.environ = environ = self.make_environ() + status_set: t.Optional[str] = None + headers_set: t.Optional[t.List[t.Tuple[str, str]]] = None + status_sent: t.Optional[str] = None + headers_sent: t.Optional[t.List[t.Tuple[str, str]]] = None + chunk_response: bool = False + + def write(data: bytes) -> None: + nonlocal status_sent, headers_sent, chunk_response + assert status_set is not None, "write() before start_response" + assert headers_set is not None, "write() before start_response" + if status_sent is None: + status_sent = status_set + headers_sent = headers_set + try: + code_str, msg = status_sent.split(None, 1) + except ValueError: + code_str, msg = status_sent, "" + code = int(code_str) + self.send_response(code, msg) + header_keys = set() + for key, value in headers_sent: + self.send_header(key, value) + header_keys.add(key.lower()) + + # Use chunked transfer encoding if there is no content + # length. Do not use for 1xx and 204 responses. 304 + # responses and HEAD requests are also excluded, which + # is the more conservative behavior and matches other + # parts of the code. + # https://httpwg.org/specs/rfc7230.html#rfc.section.3.3.1 + if ( + not ( + "content-length" in header_keys + or environ["REQUEST_METHOD"] == "HEAD" + or (100 <= code < 200) + or code in {204, 304} + ) + and self.protocol_version >= "HTTP/1.1" + ): + chunk_response = True + self.send_header("Transfer-Encoding", "chunked") + + # Always close the connection. This disables HTTP/1.1 + # keep-alive connections. They aren't handled well by + # Python's http.server because it doesn't know how to + # drain the stream before the next request line. + self.send_header("Connection", "close") + self.end_headers() + + assert isinstance(data, bytes), "applications must write bytes" + + if data: + if chunk_response: + self.wfile.write(hex(len(data))[2:].encode()) + self.wfile.write(b"\r\n") + + self.wfile.write(data) + + if chunk_response: + self.wfile.write(b"\r\n") + + self.wfile.flush() + + def start_response(status, headers, exc_info=None): # type: ignore + nonlocal status_set, headers_set + if exc_info: + try: + if headers_sent: + raise exc_info[1].with_traceback(exc_info[2]) + finally: + exc_info = None + elif headers_set: + raise AssertionError("Headers already set") + status_set = status + headers_set = headers + return write + + def execute(app: "WSGIApplication") -> None: + application_iter = app(environ, start_response) + try: + for data in application_iter: + write(data) + if not headers_sent: + write(b"") + if chunk_response: + self.wfile.write(b"0\r\n\r\n") + finally: + if hasattr(application_iter, "close"): + application_iter.close() + + try: + execute(self.server.app) + except (ConnectionError, socket.timeout) as e: + self.connection_dropped(e, environ) + except Exception as e: + if self.server.passthrough_errors: + raise + + if status_sent is not None and chunk_response: + self.close_connection = True + + try: + # if we haven't yet sent the headers but they are set + # we roll back to be able to set them again. + if status_sent is None: + status_set = None + headers_set = None + execute(InternalServerError()) + except Exception: + pass + + from .debug.tbtools import DebugTraceback + + msg = DebugTraceback(e).render_traceback_text() + self.server.log("error", f"Error on request:\n{msg}") + + def handle(self) -> None: + """Handles a request ignoring dropped connections.""" + try: + super().handle() + except (ConnectionError, socket.timeout) as e: + self.connection_dropped(e) + except Exception as e: + if self.server.ssl_context is not None and is_ssl_error(e): + self.log_error("SSL error occurred: %s", e) + else: + raise + + def connection_dropped( + self, error: BaseException, environ: t.Optional["WSGIEnvironment"] = None + ) -> None: + """Called if the connection was closed by the client. By default + nothing happens. + """ + + def __getattr__(self, name: str) -> t.Any: + # All HTTP methods are handled by run_wsgi. + if name.startswith("do_"): + return self.run_wsgi + + # All other attributes are forwarded to the base class. + return getattr(super(), name) + + def address_string(self) -> str: + if getattr(self, "environ", None): + return self.environ["REMOTE_ADDR"] # type: ignore + + if not self.client_address: + return "" + + return self.client_address[0] + + def port_integer(self) -> int: + return self.client_address[1] + + def log_request( + self, code: t.Union[int, str] = "-", size: t.Union[int, str] = "-" + ) -> None: + try: + path = uri_to_iri(self.path) + msg = f"{self.command} {path} {self.request_version}" + except AttributeError: + # path isn't set if the requestline was bad + msg = self.requestline + + code = str(code) + + if code[0] == "1": # 1xx - Informational + msg = _ansi_style(msg, "bold") + elif code == "200": # 2xx - Success + pass + elif code == "304": # 304 - Resource Not Modified + msg = _ansi_style(msg, "cyan") + elif code[0] == "3": # 3xx - Redirection + msg = _ansi_style(msg, "green") + elif code == "404": # 404 - Resource Not Found + msg = _ansi_style(msg, "yellow") + elif code[0] == "4": # 4xx - Client Error + msg = _ansi_style(msg, "bold", "red") + else: # 5xx, or any other response + msg = _ansi_style(msg, "bold", "magenta") + + self.log("info", '"%s" %s %s', msg, code, size) + + def log_error(self, format: str, *args: t.Any) -> None: + self.log("error", format, *args) + + def log_message(self, format: str, *args: t.Any) -> None: + self.log("info", format, *args) + + def log(self, type: str, message: str, *args: t.Any) -> None: + _log( + type, + f"{self.address_string()} - - [{self.log_date_time_string()}] {message}\n", + *args, + ) + + +def _ansi_style(value: str, *styles: str) -> str: + if not _log_add_style: + return value + + codes = { + "bold": 1, + "red": 31, + "green": 32, + "yellow": 33, + "magenta": 35, + "cyan": 36, + } + + for style in styles: + value = f"\x1b[{codes[style]}m{value}" + + return f"{value}\x1b[0m" + + +def generate_adhoc_ssl_pair( + cn: t.Optional[str] = None, +) -> t.Tuple["Certificate", "RSAPrivateKeyWithSerialization"]: + try: + from cryptography import x509 + from cryptography.x509.oid import NameOID + from cryptography.hazmat.backends import default_backend + from cryptography.hazmat.primitives import hashes + from cryptography.hazmat.primitives.asymmetric import rsa + except ImportError: + raise TypeError( + "Using ad-hoc certificates requires the cryptography library." + ) from None + + backend = default_backend() + pkey = rsa.generate_private_key( + public_exponent=65537, key_size=2048, backend=backend + ) + + # pretty damn sure that this is not actually accepted by anyone + if cn is None: + cn = "*" + + subject = x509.Name( + [ + x509.NameAttribute(NameOID.ORGANIZATION_NAME, "Dummy Certificate"), + x509.NameAttribute(NameOID.COMMON_NAME, cn), + ] + ) + + backend = default_backend() + cert = ( + x509.CertificateBuilder() + .subject_name(subject) + .issuer_name(subject) + .public_key(pkey.public_key()) + .serial_number(x509.random_serial_number()) + .not_valid_before(dt.now(timezone.utc)) + .not_valid_after(dt.now(timezone.utc) + timedelta(days=365)) + .add_extension(x509.ExtendedKeyUsage([x509.OID_SERVER_AUTH]), critical=False) + .add_extension(x509.SubjectAlternativeName([x509.DNSName(cn)]), critical=False) + .sign(pkey, hashes.SHA256(), backend) + ) + return cert, pkey + + +def make_ssl_devcert( + base_path: str, host: t.Optional[str] = None, cn: t.Optional[str] = None +) -> t.Tuple[str, str]: + """Creates an SSL key for development. This should be used instead of + the ``'adhoc'`` key which generates a new cert on each server start. + It accepts a path for where it should store the key and cert and + either a host or CN. If a host is given it will use the CN + ``*.host/CN=host``. + + For more information see :func:`run_simple`. + + .. versionadded:: 0.9 + + :param base_path: the path to the certificate and key. The extension + ``.crt`` is added for the certificate, ``.key`` is + added for the key. + :param host: the name of the host. This can be used as an alternative + for the `cn`. + :param cn: the `CN` to use. + """ + + if host is not None: + cn = f"*.{host}/CN={host}" + cert, pkey = generate_adhoc_ssl_pair(cn=cn) + + from cryptography.hazmat.primitives import serialization + + cert_file = f"{base_path}.crt" + pkey_file = f"{base_path}.key" + + with open(cert_file, "wb") as f: + f.write(cert.public_bytes(serialization.Encoding.PEM)) + with open(pkey_file, "wb") as f: + f.write( + pkey.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.TraditionalOpenSSL, + encryption_algorithm=serialization.NoEncryption(), + ) + ) + + return cert_file, pkey_file + + +def generate_adhoc_ssl_context() -> "ssl.SSLContext": + """Generates an adhoc SSL context for the development server.""" + import tempfile + import atexit + + cert, pkey = generate_adhoc_ssl_pair() + + from cryptography.hazmat.primitives import serialization + + cert_handle, cert_file = tempfile.mkstemp() + pkey_handle, pkey_file = tempfile.mkstemp() + atexit.register(os.remove, pkey_file) + atexit.register(os.remove, cert_file) + + os.write(cert_handle, cert.public_bytes(serialization.Encoding.PEM)) + os.write( + pkey_handle, + pkey.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.TraditionalOpenSSL, + encryption_algorithm=serialization.NoEncryption(), + ), + ) + + os.close(cert_handle) + os.close(pkey_handle) + ctx = load_ssl_context(cert_file, pkey_file) + return ctx + + +def load_ssl_context( + cert_file: str, pkey_file: t.Optional[str] = None, protocol: t.Optional[int] = None +) -> "ssl.SSLContext": + """Loads SSL context from cert/private key files and optional protocol. + Many parameters are directly taken from the API of + :py:class:`ssl.SSLContext`. + + :param cert_file: Path of the certificate to use. + :param pkey_file: Path of the private key to use. If not given, the key + will be obtained from the certificate file. + :param protocol: A ``PROTOCOL`` constant from the :mod:`ssl` module. + Defaults to :data:`ssl.PROTOCOL_TLS_SERVER`. + """ + if protocol is None: + protocol = ssl.PROTOCOL_TLS_SERVER + + ctx = ssl.SSLContext(protocol) + ctx.load_cert_chain(cert_file, pkey_file) + return ctx + + +def is_ssl_error(error: t.Optional[Exception] = None) -> bool: + """Checks if the given error (or the current one) is an SSL error.""" + if error is None: + error = t.cast(Exception, sys.exc_info()[1]) + return isinstance(error, ssl.SSLError) + + +def select_address_family(host: str, port: int) -> socket.AddressFamily: + """Return ``AF_INET4``, ``AF_INET6``, or ``AF_UNIX`` depending on + the host and port.""" + if host.startswith("unix://"): + return socket.AF_UNIX + elif ":" in host and hasattr(socket, "AF_INET6"): + return socket.AF_INET6 + return socket.AF_INET + + +def get_sockaddr( + host: str, port: int, family: socket.AddressFamily +) -> t.Union[t.Tuple[str, int], str]: + """Return a fully qualified socket address that can be passed to + :func:`socket.bind`.""" + if family == af_unix: + return host.split("://", 1)[1] + try: + res = socket.getaddrinfo( + host, port, family, socket.SOCK_STREAM, socket.IPPROTO_TCP + ) + except socket.gaierror: + return host, port + return res[0][4] # type: ignore + + +def get_interface_ip(family: socket.AddressFamily) -> str: + """Get the IP address of an external interface. Used when binding to + 0.0.0.0 or ::1 to show a more useful URL. + + :meta private: + """ + # arbitrary private address + host = "fd31:f903:5ab5:1::1" if family == socket.AF_INET6 else "10.253.155.219" + + with socket.socket(family, socket.SOCK_DGRAM) as s: + try: + s.connect((host, 58162)) + except OSError: + return "::1" if family == socket.AF_INET6 else "127.0.0.1" + + return s.getsockname()[0] # type: ignore + + +class BaseWSGIServer(HTTPServer): + """A WSGI server that that handles one request at a time. + + Use :func:`make_server` to create a server instance. + """ + + multithread = False + multiprocess = False + request_queue_size = LISTEN_QUEUE + allow_reuse_address = True + + def __init__( + self, + host: str, + port: int, + app: "WSGIApplication", + handler: t.Optional[t.Type[WSGIRequestHandler]] = None, + passthrough_errors: bool = False, + ssl_context: t.Optional[_TSSLContextArg] = None, + fd: t.Optional[int] = None, + ) -> None: + if handler is None: + handler = WSGIRequestHandler + + # If the handler doesn't directly set a protocol version and + # thread or process workers are used, then allow chunked + # responses and keep-alive connections by enabling HTTP/1.1. + if "protocol_version" not in vars(handler) and ( + self.multithread or self.multiprocess + ): + handler.protocol_version = "HTTP/1.1" + + self.host = host + self.port = port + self.app = app + self.passthrough_errors = passthrough_errors + + self.address_family = address_family = select_address_family(host, port) + server_address = get_sockaddr(host, int(port), address_family) + + # Remove a leftover Unix socket file from a previous run. Don't + # remove a file that was set up by run_simple. + if address_family == af_unix and fd is None: + server_address = t.cast(str, server_address) + + if os.path.exists(server_address): + os.unlink(server_address) + + # Bind and activate will be handled manually, it should only + # happen if we're not using a socket that was already set up. + super().__init__( + server_address, # type: ignore[arg-type] + handler, + bind_and_activate=False, + ) + + if fd is None: + # No existing socket descriptor, do bind_and_activate=True. + try: + self.server_bind() + self.server_activate() + except OSError as e: + # Catch connection issues and show them without the traceback. Show + # extra instructions for address not found, and for macOS. + self.server_close() + print(e.strerror, file=sys.stderr) + + if e.errno == errno.EADDRINUSE: + print( + f"Port {port} is in use by another program. Either identify and" + " stop that program, or start the server with a different" + " port.", + file=sys.stderr, + ) + + if sys.platform == "darwin" and port == 5000: + print( + "On macOS, try disabling the 'AirPlay Receiver' service" + " from System Preferences -> Sharing.", + file=sys.stderr, + ) + + sys.exit(1) + except BaseException: + self.server_close() + raise + else: + # TCPServer automatically opens a socket even if bind_and_activate is False. + # Close it to silence a ResourceWarning. + self.server_close() + + # Use the passed in socket directly. + self.socket = socket.fromfd(fd, address_family, socket.SOCK_STREAM) + self.server_address = self.socket.getsockname() + + if address_family != af_unix: + # If port was 0, this will record the bound port. + self.port = self.server_address[1] + + if ssl_context is not None: + if isinstance(ssl_context, tuple): + ssl_context = load_ssl_context(*ssl_context) + elif ssl_context == "adhoc": + ssl_context = generate_adhoc_ssl_context() + + self.socket = ssl_context.wrap_socket(self.socket, server_side=True) + self.ssl_context: t.Optional["ssl.SSLContext"] = ssl_context + else: + self.ssl_context = None + + def log(self, type: str, message: str, *args: t.Any) -> None: + _log(type, message, *args) + + def serve_forever(self, poll_interval: float = 0.5) -> None: + try: + super().serve_forever(poll_interval=poll_interval) + except KeyboardInterrupt: + pass + finally: + self.server_close() + + def handle_error( + self, request: t.Any, client_address: t.Union[t.Tuple[str, int], str] + ) -> None: + if self.passthrough_errors: + raise + + return super().handle_error(request, client_address) + + def log_startup(self) -> None: + """Show information about the address when starting the server.""" + dev_warning = ( + "WARNING: This is a development server. Do not use it in a production" + " deployment. Use a production WSGI server instead." + ) + dev_warning = _ansi_style(dev_warning, "bold", "red") + messages = [dev_warning] + + if self.address_family == af_unix: + messages.append(f" * Running on {self.host}") + else: + scheme = "http" if self.ssl_context is None else "https" + display_hostname = self.host + + if self.host in {"0.0.0.0", "::"}: + messages.append(f" * Running on all addresses ({self.host})") + + if self.host == "0.0.0.0": + localhost = "127.0.0.1" + display_hostname = get_interface_ip(socket.AF_INET) + else: + localhost = "[::1]" + display_hostname = get_interface_ip(socket.AF_INET6) + + messages.append(f" * Running on {scheme}://{localhost}:{self.port}") + + if ":" in display_hostname: + display_hostname = f"[{display_hostname}]" + + messages.append(f" * Running on {scheme}://{display_hostname}:{self.port}") + + _log("info", "\n".join(messages)) + + +class ThreadedWSGIServer(socketserver.ThreadingMixIn, BaseWSGIServer): + """A WSGI server that handles concurrent requests in separate + threads. + + Use :func:`make_server` to create a server instance. + """ + + multithread = True + daemon_threads = True + + +class ForkingWSGIServer(ForkingMixIn, BaseWSGIServer): + """A WSGI server that handles concurrent requests in separate forked + processes. + + Use :func:`make_server` to create a server instance. + """ + + multiprocess = True + + def __init__( + self, + host: str, + port: int, + app: "WSGIApplication", + processes: int = 40, + handler: t.Optional[t.Type[WSGIRequestHandler]] = None, + passthrough_errors: bool = False, + ssl_context: t.Optional[_TSSLContextArg] = None, + fd: t.Optional[int] = None, + ) -> None: + if not can_fork: + raise ValueError("Your platform does not support forking.") + + super().__init__(host, port, app, handler, passthrough_errors, ssl_context, fd) + self.max_children = processes + + +def make_server( + host: str, + port: int, + app: "WSGIApplication", + threaded: bool = False, + processes: int = 1, + request_handler: t.Optional[t.Type[WSGIRequestHandler]] = None, + passthrough_errors: bool = False, + ssl_context: t.Optional[_TSSLContextArg] = None, + fd: t.Optional[int] = None, +) -> BaseWSGIServer: + """Create an appropriate WSGI server instance based on the value of + ``threaded`` and ``processes``. + + This is called from :func:`run_simple`, but can be used separately + to have access to the server object, such as to run it in a separate + thread. + + See :func:`run_simple` for parameter docs. + """ + if threaded and processes > 1: + raise ValueError("Cannot have a multi-thread and multi-process server.") + + if threaded: + return ThreadedWSGIServer( + host, port, app, request_handler, passthrough_errors, ssl_context, fd=fd + ) + + if processes > 1: + return ForkingWSGIServer( + host, + port, + app, + processes, + request_handler, + passthrough_errors, + ssl_context, + fd=fd, + ) + + return BaseWSGIServer( + host, port, app, request_handler, passthrough_errors, ssl_context, fd=fd + ) + + +def is_running_from_reloader() -> bool: + """Check if the server is running as a subprocess within the + Werkzeug reloader. + + .. versionadded:: 0.10 + """ + return os.environ.get("WERKZEUG_RUN_MAIN") == "true" + + +def run_simple( + hostname: str, + port: int, + application: "WSGIApplication", + use_reloader: bool = False, + use_debugger: bool = False, + use_evalex: bool = True, + extra_files: t.Optional[t.Iterable[str]] = None, + exclude_patterns: t.Optional[t.Iterable[str]] = None, + reloader_interval: int = 1, + reloader_type: str = "auto", + threaded: bool = False, + processes: int = 1, + request_handler: t.Optional[t.Type[WSGIRequestHandler]] = None, + static_files: t.Optional[t.Dict[str, t.Union[str, t.Tuple[str, str]]]] = None, + passthrough_errors: bool = False, + ssl_context: t.Optional[_TSSLContextArg] = None, +) -> None: + """Start a development server for a WSGI application. Various + optional features can be enabled. + + .. warning:: + + Do not use the development server when deploying to production. + It is intended for use only during local development. It is not + designed to be particularly efficient, stable, or secure. + + :param hostname: The host to bind to, for example ``'localhost'``. + Can be a domain, IPv4 or IPv6 address, or file path starting + with ``unix://`` for a Unix socket. + :param port: The port to bind to, for example ``8080``. Using ``0`` + tells the OS to pick a random free port. + :param application: The WSGI application to run. + :param use_reloader: Use a reloader process to restart the server + process when files are changed. + :param use_debugger: Use Werkzeug's debugger, which will show + formatted tracebacks on unhandled exceptions. + :param use_evalex: Make the debugger interactive. A Python terminal + can be opened for any frame in the traceback. Some protection is + provided by requiring a PIN, but this should never be enabled + on a publicly visible server. + :param extra_files: The reloader will watch these files for changes + in addition to Python modules. For example, watch a + configuration file. + :param exclude_patterns: The reloader will ignore changes to any + files matching these :mod:`fnmatch` patterns. For example, + ignore cache files. + :param reloader_interval: How often the reloader tries to check for + changes. + :param reloader_type: The reloader to use. The ``'stat'`` reloader + is built in, but may require significant CPU to watch files. The + ``'watchdog'`` reloader is much more efficient but requires + installing the ``watchdog`` package first. + :param threaded: Handle concurrent requests using threads. Cannot be + used with ``processes``. + :param processes: Handle concurrent requests using up to this number + of processes. Cannot be used with ``threaded``. + :param request_handler: Use a different + :class:`~BaseHTTPServer.BaseHTTPRequestHandler` subclass to + handle requests. + :param static_files: A dict mapping URL prefixes to directories to + serve static files from using + :class:`~werkzeug.middleware.SharedDataMiddleware`. + :param passthrough_errors: Don't catch unhandled exceptions at the + server level, let the serve crash instead. If ``use_debugger`` + is enabled, the debugger will still catch such errors. + :param ssl_context: Configure TLS to serve over HTTPS. Can be an + :class:`ssl.SSLContext` object, a ``(cert_file, key_file)`` + tuple to create a typical context, or the string ``'adhoc'`` to + generate a temporary self-signed certificate. + + .. versionchanged:: 2.1 + Instructions are shown for dealing with an "address already in + use" error. + + .. versionchanged:: 2.1 + Running on ``0.0.0.0`` or ``::`` shows the loopback IP in + addition to a real IP. + + .. versionchanged:: 2.1 + The command-line interface was removed. + + .. versionchanged:: 2.0 + Running on ``0.0.0.0`` or ``::`` shows a real IP address that + was bound as well as a warning not to run the development server + in production. + + .. versionchanged:: 2.0 + The ``exclude_patterns`` parameter was added. + + .. versionchanged:: 0.15 + Bind to a Unix socket by passing a ``hostname`` that starts with + ``unix://``. + + .. versionchanged:: 0.10 + Improved the reloader and added support for changing the backend + through the ``reloader_type`` parameter. + + .. versionchanged:: 0.9 + A command-line interface was added. + + .. versionchanged:: 0.8 + ``ssl_context`` can be a tuple of paths to the certificate and + private key files. + + .. versionchanged:: 0.6 + The ``ssl_context`` parameter was added. + + .. versionchanged:: 0.5 + The ``static_files`` and ``passthrough_errors`` parameters were + added. + """ + if not isinstance(port, int): + raise TypeError("port must be an integer") + + if static_files: + from .middleware.shared_data import SharedDataMiddleware + + application = SharedDataMiddleware(application, static_files) + + if use_debugger: + from .debug import DebuggedApplication + + application = DebuggedApplication(application, evalex=use_evalex) + + if not is_running_from_reloader(): + fd = None + else: + fd = int(os.environ["WERKZEUG_SERVER_FD"]) + + srv = make_server( + hostname, + port, + application, + threaded, + processes, + request_handler, + passthrough_errors, + ssl_context, + fd=fd, + ) + srv.socket.set_inheritable(True) + os.environ["WERKZEUG_SERVER_FD"] = str(srv.fileno()) + + if not is_running_from_reloader(): + srv.log_startup() + _log("info", _ansi_style("Press CTRL+C to quit", "yellow")) + + if use_reloader: + from ._reloader import run_with_reloader + + try: + run_with_reloader( + srv.serve_forever, + extra_files=extra_files, + exclude_patterns=exclude_patterns, + interval=reloader_interval, + reloader_type=reloader_type, + ) + finally: + srv.server_close() + else: + srv.serve_forever() diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/test.py b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/test.py new file mode 100644 index 0000000..996f438 --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/test.py @@ -0,0 +1,1338 @@ +import mimetypes +import sys +import typing as t +from collections import defaultdict +from datetime import datetime +from datetime import timedelta +from http.cookiejar import CookieJar +from io import BytesIO +from itertools import chain +from random import random +from tempfile import TemporaryFile +from time import time +from urllib.request import Request as _UrllibRequest + +from ._internal import _get_environ +from ._internal import _make_encode_wrapper +from ._internal import _wsgi_decoding_dance +from ._internal import _wsgi_encoding_dance +from .datastructures import Authorization +from .datastructures import CallbackDict +from .datastructures import CombinedMultiDict +from .datastructures import EnvironHeaders +from .datastructures import FileMultiDict +from .datastructures import Headers +from .datastructures import MultiDict +from .http import dump_cookie +from .http import dump_options_header +from .http import parse_options_header +from .sansio.multipart import Data +from .sansio.multipart import Epilogue +from .sansio.multipart import Field +from .sansio.multipart import File +from .sansio.multipart import MultipartEncoder +from .sansio.multipart import Preamble +from .urls import iri_to_uri +from .urls import url_encode +from .urls import url_fix +from .urls import url_parse +from .urls import url_unparse +from .urls import url_unquote +from .utils import cached_property +from .utils import get_content_type +from .wrappers.request import Request +from .wrappers.response import Response +from .wsgi import ClosingIterator +from .wsgi import get_current_url + +if t.TYPE_CHECKING: + from _typeshed.wsgi import WSGIApplication + from _typeshed.wsgi import WSGIEnvironment + + +def stream_encode_multipart( + data: t.Mapping[str, t.Any], + use_tempfile: bool = True, + threshold: int = 1024 * 500, + boundary: t.Optional[str] = None, + charset: str = "utf-8", +) -> t.Tuple[t.IO[bytes], int, str]: + """Encode a dict of values (either strings or file descriptors or + :class:`FileStorage` objects.) into a multipart encoded string stored + in a file descriptor. + """ + if boundary is None: + boundary = f"---------------WerkzeugFormPart_{time()}{random()}" + + stream: t.IO[bytes] = BytesIO() + total_length = 0 + on_disk = False + write_binary: t.Callable[[bytes], int] + + if use_tempfile: + + def write_binary(s: bytes) -> int: + nonlocal stream, total_length, on_disk + + if on_disk: + return stream.write(s) + else: + length = len(s) + + if length + total_length <= threshold: + stream.write(s) + else: + new_stream = t.cast(t.IO[bytes], TemporaryFile("wb+")) + new_stream.write(stream.getvalue()) # type: ignore + new_stream.write(s) + stream = new_stream + on_disk = True + + total_length += length + return length + + else: + write_binary = stream.write + + encoder = MultipartEncoder(boundary.encode()) + write_binary(encoder.send_event(Preamble(data=b""))) + for key, value in _iter_data(data): + reader = getattr(value, "read", None) + if reader is not None: + filename = getattr(value, "filename", getattr(value, "name", None)) + content_type = getattr(value, "content_type", None) + if content_type is None: + content_type = ( + filename + and mimetypes.guess_type(filename)[0] + or "application/octet-stream" + ) + headers = value.headers + headers.update([("Content-Type", content_type)]) + if filename is None: + write_binary(encoder.send_event(Field(name=key, headers=headers))) + else: + write_binary( + encoder.send_event( + File(name=key, filename=filename, headers=headers) + ) + ) + while True: + chunk = reader(16384) + + if not chunk: + break + + write_binary(encoder.send_event(Data(data=chunk, more_data=True))) + else: + if not isinstance(value, str): + value = str(value) + write_binary(encoder.send_event(Field(name=key, headers=Headers()))) + write_binary( + encoder.send_event(Data(data=value.encode(charset), more_data=False)) + ) + + write_binary(encoder.send_event(Epilogue(data=b""))) + + length = stream.tell() + stream.seek(0) + return stream, length, boundary + + +def encode_multipart( + values: t.Mapping[str, t.Any], + boundary: t.Optional[str] = None, + charset: str = "utf-8", +) -> t.Tuple[str, bytes]: + """Like `stream_encode_multipart` but returns a tuple in the form + (``boundary``, ``data``) where data is bytes. + """ + stream, length, boundary = stream_encode_multipart( + values, use_tempfile=False, boundary=boundary, charset=charset + ) + return boundary, stream.read() + + +class _TestCookieHeaders: + """A headers adapter for cookielib""" + + def __init__(self, headers: t.Union[Headers, t.List[t.Tuple[str, str]]]) -> None: + self.headers = headers + + def getheaders(self, name: str) -> t.Iterable[str]: + headers = [] + name = name.lower() + for k, v in self.headers: + if k.lower() == name: + headers.append(v) + return headers + + def get_all( + self, name: str, default: t.Optional[t.Iterable[str]] = None + ) -> t.Iterable[str]: + headers = self.getheaders(name) + + if not headers: + return default # type: ignore + + return headers + + +class _TestCookieResponse: + """Something that looks like a httplib.HTTPResponse, but is actually just an + adapter for our test responses to make them available for cookielib. + """ + + def __init__(self, headers: t.Union[Headers, t.List[t.Tuple[str, str]]]) -> None: + self.headers = _TestCookieHeaders(headers) + + def info(self) -> _TestCookieHeaders: + return self.headers + + +class _TestCookieJar(CookieJar): + """A cookielib.CookieJar modified to inject and read cookie headers from + and to wsgi environments, and wsgi application responses. + """ + + def inject_wsgi(self, environ: "WSGIEnvironment") -> None: + """Inject the cookies as client headers into the server's wsgi + environment. + """ + cvals = [f"{c.name}={c.value}" for c in self] + + if cvals: + environ["HTTP_COOKIE"] = "; ".join(cvals) + else: + environ.pop("HTTP_COOKIE", None) + + def extract_wsgi( + self, + environ: "WSGIEnvironment", + headers: t.Union[Headers, t.List[t.Tuple[str, str]]], + ) -> None: + """Extract the server's set-cookie headers as cookies into the + cookie jar. + """ + self.extract_cookies( + _TestCookieResponse(headers), # type: ignore + _UrllibRequest(get_current_url(environ)), + ) + + +def _iter_data(data: t.Mapping[str, t.Any]) -> t.Iterator[t.Tuple[str, t.Any]]: + """Iterate over a mapping that might have a list of values, yielding + all key, value pairs. Almost like iter_multi_items but only allows + lists, not tuples, of values so tuples can be used for files. + """ + if isinstance(data, MultiDict): + yield from data.items(multi=True) + else: + for key, value in data.items(): + if isinstance(value, list): + for v in value: + yield key, v + else: + yield key, value + + +_TAnyMultiDict = t.TypeVar("_TAnyMultiDict", bound=MultiDict) + + +class EnvironBuilder: + """This class can be used to conveniently create a WSGI environment + for testing purposes. It can be used to quickly create WSGI environments + or request objects from arbitrary data. + + The signature of this class is also used in some other places as of + Werkzeug 0.5 (:func:`create_environ`, :meth:`Response.from_values`, + :meth:`Client.open`). Because of this most of the functionality is + available through the constructor alone. + + Files and regular form data can be manipulated independently of each + other with the :attr:`form` and :attr:`files` attributes, but are + passed with the same argument to the constructor: `data`. + + `data` can be any of these values: + + - a `str` or `bytes` object: The object is converted into an + :attr:`input_stream`, the :attr:`content_length` is set and you have to + provide a :attr:`content_type`. + - a `dict` or :class:`MultiDict`: The keys have to be strings. The values + have to be either any of the following objects, or a list of any of the + following objects: + + - a :class:`file`-like object: These are converted into + :class:`FileStorage` objects automatically. + - a `tuple`: The :meth:`~FileMultiDict.add_file` method is called + with the key and the unpacked `tuple` items as positional + arguments. + - a `str`: The string is set as form data for the associated key. + - a file-like object: The object content is loaded in memory and then + handled like a regular `str` or a `bytes`. + + :param path: the path of the request. In the WSGI environment this will + end up as `PATH_INFO`. If the `query_string` is not defined + and there is a question mark in the `path` everything after + it is used as query string. + :param base_url: the base URL is a URL that is used to extract the WSGI + URL scheme, host (server name + server port) and the + script root (`SCRIPT_NAME`). + :param query_string: an optional string or dict with URL parameters. + :param method: the HTTP method to use, defaults to `GET`. + :param input_stream: an optional input stream. Do not specify this and + `data`. As soon as an input stream is set you can't + modify :attr:`args` and :attr:`files` unless you + set the :attr:`input_stream` to `None` again. + :param content_type: The content type for the request. As of 0.5 you + don't have to provide this when specifying files + and form data via `data`. + :param content_length: The content length for the request. You don't + have to specify this when providing data via + `data`. + :param errors_stream: an optional error stream that is used for + `wsgi.errors`. Defaults to :data:`stderr`. + :param multithread: controls `wsgi.multithread`. Defaults to `False`. + :param multiprocess: controls `wsgi.multiprocess`. Defaults to `False`. + :param run_once: controls `wsgi.run_once`. Defaults to `False`. + :param headers: an optional list or :class:`Headers` object of headers. + :param data: a string or dict of form data or a file-object. + See explanation above. + :param json: An object to be serialized and assigned to ``data``. + Defaults the content type to ``"application/json"``. + Serialized with the function assigned to :attr:`json_dumps`. + :param environ_base: an optional dict of environment defaults. + :param environ_overrides: an optional dict of environment overrides. + :param charset: the charset used to encode string data. + :param auth: An authorization object to use for the + ``Authorization`` header value. A ``(username, password)`` tuple + is a shortcut for ``Basic`` authorization. + + .. versionchanged:: 2.1 + ``CONTENT_TYPE`` and ``CONTENT_LENGTH`` are not duplicated as + header keys in the environ. + + .. versionchanged:: 2.0 + ``REQUEST_URI`` and ``RAW_URI`` is the full raw URI including + the query string, not only the path. + + .. versionchanged:: 2.0 + The default :attr:`request_class` is ``Request`` instead of + ``BaseRequest``. + + .. versionadded:: 2.0 + Added the ``auth`` parameter. + + .. versionadded:: 0.15 + The ``json`` param and :meth:`json_dumps` method. + + .. versionadded:: 0.15 + The environ has keys ``REQUEST_URI`` and ``RAW_URI`` containing + the path before percent-decoding. This is not part of the WSGI + PEP, but many WSGI servers include it. + + .. versionchanged:: 0.6 + ``path`` and ``base_url`` can now be unicode strings that are + encoded with :func:`iri_to_uri`. + """ + + #: the server protocol to use. defaults to HTTP/1.1 + server_protocol = "HTTP/1.1" + + #: the wsgi version to use. defaults to (1, 0) + wsgi_version = (1, 0) + + #: The default request class used by :meth:`get_request`. + request_class = Request + + import json + + #: The serialization function used when ``json`` is passed. + json_dumps = staticmethod(json.dumps) + del json + + _args: t.Optional[MultiDict] + _query_string: t.Optional[str] + _input_stream: t.Optional[t.IO[bytes]] + _form: t.Optional[MultiDict] + _files: t.Optional[FileMultiDict] + + def __init__( + self, + path: str = "/", + base_url: t.Optional[str] = None, + query_string: t.Optional[t.Union[t.Mapping[str, str], str]] = None, + method: str = "GET", + input_stream: t.Optional[t.IO[bytes]] = None, + content_type: t.Optional[str] = None, + content_length: t.Optional[int] = None, + errors_stream: t.Optional[t.IO[str]] = None, + multithread: bool = False, + multiprocess: bool = False, + run_once: bool = False, + headers: t.Optional[t.Union[Headers, t.Iterable[t.Tuple[str, str]]]] = None, + data: t.Optional[ + t.Union[t.IO[bytes], str, bytes, t.Mapping[str, t.Any]] + ] = None, + environ_base: t.Optional[t.Mapping[str, t.Any]] = None, + environ_overrides: t.Optional[t.Mapping[str, t.Any]] = None, + charset: str = "utf-8", + mimetype: t.Optional[str] = None, + json: t.Optional[t.Mapping[str, t.Any]] = None, + auth: t.Optional[t.Union[Authorization, t.Tuple[str, str]]] = None, + ) -> None: + path_s = _make_encode_wrapper(path) + if query_string is not None and path_s("?") in path: + raise ValueError("Query string is defined in the path and as an argument") + request_uri = url_parse(path) + if query_string is None and path_s("?") in path: + query_string = request_uri.query + self.charset = charset + self.path = iri_to_uri(request_uri.path) + self.request_uri = path + if base_url is not None: + base_url = url_fix(iri_to_uri(base_url, charset), charset) + self.base_url = base_url # type: ignore + if isinstance(query_string, (bytes, str)): + self.query_string = query_string + else: + if query_string is None: + query_string = MultiDict() + elif not isinstance(query_string, MultiDict): + query_string = MultiDict(query_string) + self.args = query_string + self.method = method + if headers is None: + headers = Headers() + elif not isinstance(headers, Headers): + headers = Headers(headers) + self.headers = headers + if content_type is not None: + self.content_type = content_type + if errors_stream is None: + errors_stream = sys.stderr + self.errors_stream = errors_stream + self.multithread = multithread + self.multiprocess = multiprocess + self.run_once = run_once + self.environ_base = environ_base + self.environ_overrides = environ_overrides + self.input_stream = input_stream + self.content_length = content_length + self.closed = False + + if auth is not None: + if isinstance(auth, tuple): + auth = Authorization( + "basic", {"username": auth[0], "password": auth[1]} + ) + + self.headers.set("Authorization", auth.to_header()) + + if json is not None: + if data is not None: + raise TypeError("can't provide both json and data") + + data = self.json_dumps(json) + + if self.content_type is None: + self.content_type = "application/json" + + if data: + if input_stream is not None: + raise TypeError("can't provide input stream and data") + if hasattr(data, "read"): + data = data.read() + if isinstance(data, str): + data = data.encode(self.charset) + if isinstance(data, bytes): + self.input_stream = BytesIO(data) + if self.content_length is None: + self.content_length = len(data) + else: + for key, value in _iter_data(data): + if isinstance(value, (tuple, dict)) or hasattr(value, "read"): + self._add_file_from_data(key, value) + else: + self.form.setlistdefault(key).append(value) + + if mimetype is not None: + self.mimetype = mimetype + + @classmethod + def from_environ( + cls, environ: "WSGIEnvironment", **kwargs: t.Any + ) -> "EnvironBuilder": + """Turn an environ dict back into a builder. Any extra kwargs + override the args extracted from the environ. + + .. versionchanged:: 2.0 + Path and query values are passed through the WSGI decoding + dance to avoid double encoding. + + .. versionadded:: 0.15 + """ + headers = Headers(EnvironHeaders(environ)) + out = { + "path": _wsgi_decoding_dance(environ["PATH_INFO"]), + "base_url": cls._make_base_url( + environ["wsgi.url_scheme"], + headers.pop("Host"), + _wsgi_decoding_dance(environ["SCRIPT_NAME"]), + ), + "query_string": _wsgi_decoding_dance(environ["QUERY_STRING"]), + "method": environ["REQUEST_METHOD"], + "input_stream": environ["wsgi.input"], + "content_type": headers.pop("Content-Type", None), + "content_length": headers.pop("Content-Length", None), + "errors_stream": environ["wsgi.errors"], + "multithread": environ["wsgi.multithread"], + "multiprocess": environ["wsgi.multiprocess"], + "run_once": environ["wsgi.run_once"], + "headers": headers, + } + out.update(kwargs) + return cls(**out) + + def _add_file_from_data( + self, + key: str, + value: t.Union[ + t.IO[bytes], t.Tuple[t.IO[bytes], str], t.Tuple[t.IO[bytes], str, str] + ], + ) -> None: + """Called in the EnvironBuilder to add files from the data dict.""" + if isinstance(value, tuple): + self.files.add_file(key, *value) + else: + self.files.add_file(key, value) + + @staticmethod + def _make_base_url(scheme: str, host: str, script_root: str) -> str: + return url_unparse((scheme, host, script_root, "", "")).rstrip("/") + "/" + + @property + def base_url(self) -> str: + """The base URL is used to extract the URL scheme, host name, + port, and root path. + """ + return self._make_base_url(self.url_scheme, self.host, self.script_root) + + @base_url.setter + def base_url(self, value: t.Optional[str]) -> None: + if value is None: + scheme = "http" + netloc = "localhost" + script_root = "" + else: + scheme, netloc, script_root, qs, anchor = url_parse(value) + if qs or anchor: + raise ValueError("base url must not contain a query string or fragment") + self.script_root = script_root.rstrip("/") + self.host = netloc + self.url_scheme = scheme + + @property + def content_type(self) -> t.Optional[str]: + """The content type for the request. Reflected from and to + the :attr:`headers`. Do not set if you set :attr:`files` or + :attr:`form` for auto detection. + """ + ct = self.headers.get("Content-Type") + if ct is None and not self._input_stream: + if self._files: + return "multipart/form-data" + if self._form: + return "application/x-www-form-urlencoded" + return None + return ct + + @content_type.setter + def content_type(self, value: t.Optional[str]) -> None: + if value is None: + self.headers.pop("Content-Type", None) + else: + self.headers["Content-Type"] = value + + @property + def mimetype(self) -> t.Optional[str]: + """The mimetype (content type without charset etc.) + + .. versionadded:: 0.14 + """ + ct = self.content_type + return ct.split(";")[0].strip() if ct else None + + @mimetype.setter + def mimetype(self, value: str) -> None: + self.content_type = get_content_type(value, self.charset) + + @property + def mimetype_params(self) -> t.Mapping[str, str]: + """The mimetype parameters as dict. For example if the + content type is ``text/html; charset=utf-8`` the params would be + ``{'charset': 'utf-8'}``. + + .. versionadded:: 0.14 + """ + + def on_update(d: CallbackDict) -> None: + self.headers["Content-Type"] = dump_options_header(self.mimetype, d) + + d = parse_options_header(self.headers.get("content-type", ""))[1] + return CallbackDict(d, on_update) + + @property + def content_length(self) -> t.Optional[int]: + """The content length as integer. Reflected from and to the + :attr:`headers`. Do not set if you set :attr:`files` or + :attr:`form` for auto detection. + """ + return self.headers.get("Content-Length", type=int) + + @content_length.setter + def content_length(self, value: t.Optional[int]) -> None: + if value is None: + self.headers.pop("Content-Length", None) + else: + self.headers["Content-Length"] = str(value) + + def _get_form(self, name: str, storage: t.Type[_TAnyMultiDict]) -> _TAnyMultiDict: + """Common behavior for getting the :attr:`form` and + :attr:`files` properties. + + :param name: Name of the internal cached attribute. + :param storage: Storage class used for the data. + """ + if self.input_stream is not None: + raise AttributeError("an input stream is defined") + + rv = getattr(self, name) + + if rv is None: + rv = storage() + setattr(self, name, rv) + + return rv # type: ignore + + def _set_form(self, name: str, value: MultiDict) -> None: + """Common behavior for setting the :attr:`form` and + :attr:`files` properties. + + :param name: Name of the internal cached attribute. + :param value: Value to assign to the attribute. + """ + self._input_stream = None + setattr(self, name, value) + + @property + def form(self) -> MultiDict: + """A :class:`MultiDict` of form values.""" + return self._get_form("_form", MultiDict) + + @form.setter + def form(self, value: MultiDict) -> None: + self._set_form("_form", value) + + @property + def files(self) -> FileMultiDict: + """A :class:`FileMultiDict` of uploaded files. Use + :meth:`~FileMultiDict.add_file` to add new files. + """ + return self._get_form("_files", FileMultiDict) + + @files.setter + def files(self, value: FileMultiDict) -> None: + self._set_form("_files", value) + + @property + def input_stream(self) -> t.Optional[t.IO[bytes]]: + """An optional input stream. This is mutually exclusive with + setting :attr:`form` and :attr:`files`, setting it will clear + those. Do not provide this if the method is not ``POST`` or + another method that has a body. + """ + return self._input_stream + + @input_stream.setter + def input_stream(self, value: t.Optional[t.IO[bytes]]) -> None: + self._input_stream = value + self._form = None + self._files = None + + @property + def query_string(self) -> str: + """The query string. If you set this to a string + :attr:`args` will no longer be available. + """ + if self._query_string is None: + if self._args is not None: + return url_encode(self._args, charset=self.charset) + return "" + return self._query_string + + @query_string.setter + def query_string(self, value: t.Optional[str]) -> None: + self._query_string = value + self._args = None + + @property + def args(self) -> MultiDict: + """The URL arguments as :class:`MultiDict`.""" + if self._query_string is not None: + raise AttributeError("a query string is defined") + if self._args is None: + self._args = MultiDict() + return self._args + + @args.setter + def args(self, value: t.Optional[MultiDict]) -> None: + self._query_string = None + self._args = value + + @property + def server_name(self) -> str: + """The server name (read-only, use :attr:`host` to set)""" + return self.host.split(":", 1)[0] + + @property + def server_port(self) -> int: + """The server port as integer (read-only, use :attr:`host` to set)""" + pieces = self.host.split(":", 1) + + if len(pieces) == 2: + try: + return int(pieces[1]) + except ValueError: + pass + + if self.url_scheme == "https": + return 443 + return 80 + + def __del__(self) -> None: + try: + self.close() + except Exception: + pass + + def close(self) -> None: + """Closes all files. If you put real :class:`file` objects into the + :attr:`files` dict you can call this method to automatically close + them all in one go. + """ + if self.closed: + return + try: + files = self.files.values() + except AttributeError: + files = () # type: ignore + for f in files: + try: + f.close() + except Exception: + pass + self.closed = True + + def get_environ(self) -> "WSGIEnvironment": + """Return the built environ. + + .. versionchanged:: 0.15 + The content type and length headers are set based on + input stream detection. Previously this only set the WSGI + keys. + """ + input_stream = self.input_stream + content_length = self.content_length + + mimetype = self.mimetype + content_type = self.content_type + + if input_stream is not None: + start_pos = input_stream.tell() + input_stream.seek(0, 2) + end_pos = input_stream.tell() + input_stream.seek(start_pos) + content_length = end_pos - start_pos + elif mimetype == "multipart/form-data": + input_stream, content_length, boundary = stream_encode_multipart( + CombinedMultiDict([self.form, self.files]), charset=self.charset + ) + content_type = f'{mimetype}; boundary="{boundary}"' + elif mimetype == "application/x-www-form-urlencoded": + form_encoded = url_encode(self.form, charset=self.charset).encode("ascii") + content_length = len(form_encoded) + input_stream = BytesIO(form_encoded) + else: + input_stream = BytesIO() + + result: "WSGIEnvironment" = {} + if self.environ_base: + result.update(self.environ_base) + + def _path_encode(x: str) -> str: + return _wsgi_encoding_dance(url_unquote(x, self.charset), self.charset) + + raw_uri = _wsgi_encoding_dance(self.request_uri, self.charset) + result.update( + { + "REQUEST_METHOD": self.method, + "SCRIPT_NAME": _path_encode(self.script_root), + "PATH_INFO": _path_encode(self.path), + "QUERY_STRING": _wsgi_encoding_dance(self.query_string, self.charset), + # Non-standard, added by mod_wsgi, uWSGI + "REQUEST_URI": raw_uri, + # Non-standard, added by gunicorn + "RAW_URI": raw_uri, + "SERVER_NAME": self.server_name, + "SERVER_PORT": str(self.server_port), + "HTTP_HOST": self.host, + "SERVER_PROTOCOL": self.server_protocol, + "wsgi.version": self.wsgi_version, + "wsgi.url_scheme": self.url_scheme, + "wsgi.input": input_stream, + "wsgi.errors": self.errors_stream, + "wsgi.multithread": self.multithread, + "wsgi.multiprocess": self.multiprocess, + "wsgi.run_once": self.run_once, + } + ) + + headers = self.headers.copy() + # Don't send these as headers, they're part of the environ. + headers.remove("Content-Type") + headers.remove("Content-Length") + + if content_type is not None: + result["CONTENT_TYPE"] = content_type + + if content_length is not None: + result["CONTENT_LENGTH"] = str(content_length) + + combined_headers = defaultdict(list) + + for key, value in headers.to_wsgi_list(): + combined_headers[f"HTTP_{key.upper().replace('-', '_')}"].append(value) + + for key, values in combined_headers.items(): + result[key] = ", ".join(values) + + if self.environ_overrides: + result.update(self.environ_overrides) + + return result + + def get_request(self, cls: t.Optional[t.Type[Request]] = None) -> Request: + """Returns a request with the data. If the request class is not + specified :attr:`request_class` is used. + + :param cls: The request wrapper to use. + """ + if cls is None: + cls = self.request_class + + return cls(self.get_environ()) + + +class ClientRedirectError(Exception): + """If a redirect loop is detected when using follow_redirects=True with + the :cls:`Client`, then this exception is raised. + """ + + +class Client: + """This class allows you to send requests to a wrapped application. + + The use_cookies parameter indicates whether cookies should be stored and + sent for subsequent requests. This is True by default, but passing False + will disable this behaviour. + + If you want to request some subdomain of your application you may set + `allow_subdomain_redirects` to `True` as if not no external redirects + are allowed. + + .. versionchanged:: 2.1 + Removed deprecated behavior of treating the response as a + tuple. All data is available as properties on the returned + response object. + + .. versionchanged:: 2.0 + ``response_wrapper`` is always a subclass of + :class:``TestResponse``. + + .. versionchanged:: 0.5 + Added the ``use_cookies`` parameter. + """ + + def __init__( + self, + application: "WSGIApplication", + response_wrapper: t.Optional[t.Type["Response"]] = None, + use_cookies: bool = True, + allow_subdomain_redirects: bool = False, + ) -> None: + self.application = application + + if response_wrapper in {None, Response}: + response_wrapper = TestResponse + elif not isinstance(response_wrapper, TestResponse): + response_wrapper = type( + "WrapperTestResponse", + (TestResponse, response_wrapper), # type: ignore + {}, + ) + + self.response_wrapper = t.cast(t.Type["TestResponse"], response_wrapper) + + if use_cookies: + self.cookie_jar: t.Optional[_TestCookieJar] = _TestCookieJar() + else: + self.cookie_jar = None + + self.allow_subdomain_redirects = allow_subdomain_redirects + + def set_cookie( + self, + server_name: str, + key: str, + value: str = "", + max_age: t.Optional[t.Union[timedelta, int]] = None, + expires: t.Optional[t.Union[str, datetime, int, float]] = None, + path: str = "/", + domain: t.Optional[str] = None, + secure: bool = False, + httponly: bool = False, + samesite: t.Optional[str] = None, + charset: str = "utf-8", + ) -> None: + """Sets a cookie in the client's cookie jar. The server name + is required and has to match the one that is also passed to + the open call. + """ + assert self.cookie_jar is not None, "cookies disabled" + header = dump_cookie( + key, + value, + max_age, + expires, + path, + domain, + secure, + httponly, + charset, + samesite=samesite, + ) + environ = create_environ(path, base_url=f"http://{server_name}") + headers = [("Set-Cookie", header)] + self.cookie_jar.extract_wsgi(environ, headers) + + def delete_cookie( + self, + server_name: str, + key: str, + path: str = "/", + domain: t.Optional[str] = None, + secure: bool = False, + httponly: bool = False, + samesite: t.Optional[str] = None, + ) -> None: + """Deletes a cookie in the test client.""" + self.set_cookie( + server_name, + key, + expires=0, + max_age=0, + path=path, + domain=domain, + secure=secure, + httponly=httponly, + samesite=samesite, + ) + + def run_wsgi_app( + self, environ: "WSGIEnvironment", buffered: bool = False + ) -> t.Tuple[t.Iterable[bytes], str, Headers]: + """Runs the wrapped WSGI app with the given environment. + + :meta private: + """ + if self.cookie_jar is not None: + self.cookie_jar.inject_wsgi(environ) + + rv = run_wsgi_app(self.application, environ, buffered=buffered) + + if self.cookie_jar is not None: + self.cookie_jar.extract_wsgi(environ, rv[2]) + + return rv + + def resolve_redirect( + self, response: "TestResponse", buffered: bool = False + ) -> "TestResponse": + """Perform a new request to the location given by the redirect + response to the previous request. + + :meta private: + """ + scheme, netloc, path, qs, anchor = url_parse(response.location) + builder = EnvironBuilder.from_environ( + response.request.environ, path=path, query_string=qs + ) + + to_name_parts = netloc.split(":", 1)[0].split(".") + from_name_parts = builder.server_name.split(".") + + if to_name_parts != [""]: + # The new location has a host, use it for the base URL. + builder.url_scheme = scheme + builder.host = netloc + else: + # A local redirect with autocorrect_location_header=False + # doesn't have a host, so use the request's host. + to_name_parts = from_name_parts + + # Explain why a redirect to a different server name won't be followed. + if to_name_parts != from_name_parts: + if to_name_parts[-len(from_name_parts) :] == from_name_parts: + if not self.allow_subdomain_redirects: + raise RuntimeError("Following subdomain redirects is not enabled.") + else: + raise RuntimeError("Following external redirects is not supported.") + + path_parts = path.split("/") + root_parts = builder.script_root.split("/") + + if path_parts[: len(root_parts)] == root_parts: + # Strip the script root from the path. + builder.path = path[len(builder.script_root) :] + else: + # The new location is not under the script root, so use the + # whole path and clear the previous root. + builder.path = path + builder.script_root = "" + + # Only 307 and 308 preserve all of the original request. + if response.status_code not in {307, 308}: + # HEAD is preserved, everything else becomes GET. + if builder.method != "HEAD": + builder.method = "GET" + + # Clear the body and the headers that describe it. + + if builder.input_stream is not None: + builder.input_stream.close() + builder.input_stream = None + + builder.content_type = None + builder.content_length = None + builder.headers.pop("Transfer-Encoding", None) + + return self.open(builder, buffered=buffered) + + def open( + self, + *args: t.Any, + buffered: bool = False, + follow_redirects: bool = False, + **kwargs: t.Any, + ) -> "TestResponse": + """Generate an environ dict from the given arguments, make a + request to the application using it, and return the response. + + :param args: Passed to :class:`EnvironBuilder` to create the + environ for the request. If a single arg is passed, it can + be an existing :class:`EnvironBuilder` or an environ dict. + :param buffered: Convert the iterator returned by the app into + a list. If the iterator has a ``close()`` method, it is + called automatically. + :param follow_redirects: Make additional requests to follow HTTP + redirects until a non-redirect status is returned. + :attr:`TestResponse.history` lists the intermediate + responses. + + .. versionchanged:: 2.1 + Removed the ``as_tuple`` parameter. + + .. versionchanged:: 2.0 + ``as_tuple`` is deprecated and will be removed in Werkzeug + 2.1. Use :attr:`TestResponse.request` and + ``request.environ`` instead. + + .. versionchanged:: 2.0 + The request input stream is closed when calling + ``response.close()``. Input streams for redirects are + automatically closed. + + .. versionchanged:: 0.5 + If a dict is provided as file in the dict for the ``data`` + parameter the content type has to be called ``content_type`` + instead of ``mimetype``. This change was made for + consistency with :class:`werkzeug.FileWrapper`. + + .. versionchanged:: 0.5 + Added the ``follow_redirects`` parameter. + """ + request: t.Optional["Request"] = None + + if not kwargs and len(args) == 1: + arg = args[0] + + if isinstance(arg, EnvironBuilder): + request = arg.get_request() + elif isinstance(arg, dict): + request = EnvironBuilder.from_environ(arg).get_request() + elif isinstance(arg, Request): + request = arg + + if request is None: + builder = EnvironBuilder(*args, **kwargs) + + try: + request = builder.get_request() + finally: + builder.close() + + response = self.run_wsgi_app(request.environ, buffered=buffered) + response = self.response_wrapper(*response, request=request) + + redirects = set() + history: t.List["TestResponse"] = [] + + if not follow_redirects: + return response + + while response.status_code in { + 301, + 302, + 303, + 305, + 307, + 308, + }: + # Exhaust intermediate response bodies to ensure middleware + # that returns an iterator runs any cleanup code. + if not buffered: + response.make_sequence() + response.close() + + new_redirect_entry = (response.location, response.status_code) + + if new_redirect_entry in redirects: + raise ClientRedirectError( + f"Loop detected: A {response.status_code} redirect" + f" to {response.location} was already made." + ) + + redirects.add(new_redirect_entry) + response.history = tuple(history) + history.append(response) + response = self.resolve_redirect(response, buffered=buffered) + else: + # This is the final request after redirects. + response.history = tuple(history) + # Close the input stream when closing the response, in case + # the input is an open temporary file. + response.call_on_close(request.input_stream.close) + return response + + def get(self, *args: t.Any, **kw: t.Any) -> "TestResponse": + """Call :meth:`open` with ``method`` set to ``GET``.""" + kw["method"] = "GET" + return self.open(*args, **kw) + + def post(self, *args: t.Any, **kw: t.Any) -> "TestResponse": + """Call :meth:`open` with ``method`` set to ``POST``.""" + kw["method"] = "POST" + return self.open(*args, **kw) + + def put(self, *args: t.Any, **kw: t.Any) -> "TestResponse": + """Call :meth:`open` with ``method`` set to ``PUT``.""" + kw["method"] = "PUT" + return self.open(*args, **kw) + + def delete(self, *args: t.Any, **kw: t.Any) -> "TestResponse": + """Call :meth:`open` with ``method`` set to ``DELETE``.""" + kw["method"] = "DELETE" + return self.open(*args, **kw) + + def patch(self, *args: t.Any, **kw: t.Any) -> "TestResponse": + """Call :meth:`open` with ``method`` set to ``PATCH``.""" + kw["method"] = "PATCH" + return self.open(*args, **kw) + + def options(self, *args: t.Any, **kw: t.Any) -> "TestResponse": + """Call :meth:`open` with ``method`` set to ``OPTIONS``.""" + kw["method"] = "OPTIONS" + return self.open(*args, **kw) + + def head(self, *args: t.Any, **kw: t.Any) -> "TestResponse": + """Call :meth:`open` with ``method`` set to ``HEAD``.""" + kw["method"] = "HEAD" + return self.open(*args, **kw) + + def trace(self, *args: t.Any, **kw: t.Any) -> "TestResponse": + """Call :meth:`open` with ``method`` set to ``TRACE``.""" + kw["method"] = "TRACE" + return self.open(*args, **kw) + + def __repr__(self) -> str: + return f"<{type(self).__name__} {self.application!r}>" + + +def create_environ(*args: t.Any, **kwargs: t.Any) -> "WSGIEnvironment": + """Create a new WSGI environ dict based on the values passed. The first + parameter should be the path of the request which defaults to '/'. The + second one can either be an absolute path (in that case the host is + localhost:80) or a full path to the request with scheme, netloc port and + the path to the script. + + This accepts the same arguments as the :class:`EnvironBuilder` + constructor. + + .. versionchanged:: 0.5 + This function is now a thin wrapper over :class:`EnvironBuilder` which + was added in 0.5. The `headers`, `environ_base`, `environ_overrides` + and `charset` parameters were added. + """ + builder = EnvironBuilder(*args, **kwargs) + + try: + return builder.get_environ() + finally: + builder.close() + + +def run_wsgi_app( + app: "WSGIApplication", environ: "WSGIEnvironment", buffered: bool = False +) -> t.Tuple[t.Iterable[bytes], str, Headers]: + """Return a tuple in the form (app_iter, status, headers) of the + application output. This works best if you pass it an application that + returns an iterator all the time. + + Sometimes applications may use the `write()` callable returned + by the `start_response` function. This tries to resolve such edge + cases automatically. But if you don't get the expected output you + should set `buffered` to `True` which enforces buffering. + + If passed an invalid WSGI application the behavior of this function is + undefined. Never pass non-conforming WSGI applications to this function. + + :param app: the application to execute. + :param buffered: set to `True` to enforce buffering. + :return: tuple in the form ``(app_iter, status, headers)`` + """ + # Copy environ to ensure any mutations by the app (ProxyFix, for + # example) don't affect subsequent requests (such as redirects). + environ = _get_environ(environ).copy() + status: str + response: t.Optional[t.Tuple[str, t.List[t.Tuple[str, str]]]] = None + buffer: t.List[bytes] = [] + + def start_response(status, headers, exc_info=None): # type: ignore + nonlocal response + + if exc_info: + try: + raise exc_info[1].with_traceback(exc_info[2]) + finally: + exc_info = None + + response = (status, headers) + return buffer.append + + app_rv = app(environ, start_response) + close_func = getattr(app_rv, "close", None) + app_iter: t.Iterable[bytes] = iter(app_rv) + + # when buffering we emit the close call early and convert the + # application iterator into a regular list + if buffered: + try: + app_iter = list(app_iter) + finally: + if close_func is not None: + close_func() + + # otherwise we iterate the application iter until we have a response, chain + # the already received data with the already collected data and wrap it in + # a new `ClosingIterator` if we need to restore a `close` callable from the + # original return value. + else: + for item in app_iter: + buffer.append(item) + + if response is not None: + break + + if buffer: + app_iter = chain(buffer, app_iter) + + if close_func is not None and app_iter is not app_rv: + app_iter = ClosingIterator(app_iter, close_func) + + status, headers = response # type: ignore + return app_iter, status, Headers(headers) + + +class TestResponse(Response): + """:class:`~werkzeug.wrappers.Response` subclass that provides extra + information about requests made with the test :class:`Client`. + + Test client requests will always return an instance of this class. + If a custom response class is passed to the client, it is + subclassed along with this to support test information. + + If the test request included large files, or if the application is + serving a file, call :meth:`close` to close any open files and + prevent Python showing a ``ResourceWarning``. + + .. versionchanged:: 2.2 + Set the ``default_mimetype`` to None to prevent a mimetype being + assumed if missing. + + .. versionchanged:: 2.1 + Removed deprecated behavior for treating the response instance + as a tuple. + + .. versionadded:: 2.0 + Test client methods always return instances of this class. + """ + + default_mimetype = None + # Don't assume a mimetype, instead use whatever the response provides + + request: Request + """A request object with the environ used to make the request that + resulted in this response. + """ + + history: t.Tuple["TestResponse", ...] + """A list of intermediate responses. Populated when the test request + is made with ``follow_redirects`` enabled. + """ + + # Tell Pytest to ignore this, it's not a test class. + __test__ = False + + def __init__( + self, + response: t.Iterable[bytes], + status: str, + headers: Headers, + request: Request, + history: t.Tuple["TestResponse"] = (), # type: ignore + **kwargs: t.Any, + ) -> None: + super().__init__(response, status, headers, **kwargs) + self.request = request + self.history = history + self._compat_tuple = response, status, headers + + @cached_property + def text(self) -> str: + """The response data as text. A shortcut for + ``response.get_data(as_text=True)``. + + .. versionadded:: 2.1 + """ + return self.get_data(as_text=True) diff --git a/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/testapp.py b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/testapp.py new file mode 100644 index 0000000..0d7ffbb --- /dev/null +++ b/docs/examples/flask/venv/lib/python3.11/site-packages/werkzeug/testapp.py @@ -0,0 +1,241 @@ +"""A small application that can be used to test a WSGI server and check +it for WSGI compliance. +""" +import base64 +import os +import sys +import typing as t +from textwrap import wrap + +from markupsafe import escape + +from . import __version__ as _werkzeug_version +from .wrappers.request import Request +from .wrappers.response import Response + +if t.TYPE_CHECKING: + from _typeshed.wsgi import StartResponse + from _typeshed.wsgi import WSGIEnvironment + + +logo = Response( + base64.b64decode( + """ +R0lGODlhoACgAOMIAAEDACwpAEpCAGdgAJaKAM28AOnVAP3rAP///////// +//////////////////////yH5BAEKAAgALAAAAACgAKAAAAT+EMlJq704680R+F0ojmRpnuj0rWnrv +nB8rbRs33gu0bzu/0AObxgsGn3D5HHJbCUFyqZ0ukkSDlAidctNFg7gbI9LZlrBaHGtzAae0eloe25 +7w9EDOX2fst/xenyCIn5/gFqDiVVDV4aGeYiKkhSFjnCQY5OTlZaXgZp8nJ2ekaB0SQOjqphrpnOiq +ncEn65UsLGytLVmQ6m4sQazpbtLqL/HwpnER8bHyLrLOc3Oz8PRONPU1crXN9na263dMt/g4SzjMeX +m5yDpLqgG7OzJ4u8lT/P69ej3JPn69kHzN2OIAHkB9RUYSFCFQYQJFTIkCDBiwoXWGnowaLEjRm7+G +p9A7Hhx4rUkAUaSLJlxHMqVMD/aSycSZkyTplCqtGnRAM5NQ1Ly5OmzZc6gO4d6DGAUKA+hSocWYAo +SlM6oUWX2O/o0KdaVU5vuSQLAa0ADwQgMEMB2AIECZhVSnTno6spgbtXmHcBUrQACcc2FrTrWS8wAf +78cMFBgwIBgbN+qvTt3ayikRBk7BoyGAGABAdYyfdzRQGV3l4coxrqQ84GpUBmrdR3xNIDUPAKDBSA +ADIGDhhqTZIWaDcrVX8EsbNzbkvCOxG8bN5w8ly9H8jyTJHC6DFndQydbguh2e/ctZJFXRxMAqqPVA +tQH5E64SPr1f0zz7sQYjAHg0In+JQ11+N2B0XXBeeYZgBZFx4tqBToiTCPv0YBgQv8JqA6BEf6RhXx +w1ENhRBnWV8ctEX4Ul2zc3aVGcQNC2KElyTDYyYUWvShdjDyMOGMuFjqnII45aogPhz/CodUHFwaDx +lTgsaOjNyhGWJQd+lFoAGk8ObghI0kawg+EV5blH3dr+digkYuAGSaQZFHFz2P/cTaLmhF52QeSb45 +Jwxd+uSVGHlqOZpOeJpCFZ5J+rkAkFjQ0N1tah7JJSZUFNsrkeJUJMIBi8jyaEKIhKPomnC91Uo+NB +yyaJ5umnnpInIFh4t6ZSpGaAVmizqjpByDegYl8tPE0phCYrhcMWSv+uAqHfgH88ak5UXZmlKLVJhd +dj78s1Fxnzo6yUCrV6rrDOkluG+QzCAUTbCwf9SrmMLzK6p+OPHx7DF+bsfMRq7Ec61Av9i6GLw23r +idnZ+/OO0a99pbIrJkproCQMA17OPG6suq3cca5ruDfXCCDoS7BEdvmJn5otdqscn+uogRHHXs8cbh +EIfYaDY1AkrC0cqwcZpnM6ludx72x0p7Fo/hZAcpJDjax0UdHavMKAbiKltMWCF3xxh9k25N/Viud8 +ba78iCvUkt+V6BpwMlErmcgc502x+u1nSxJSJP9Mi52awD1V4yB/QHONsnU3L+A/zR4VL/indx/y64 +gqcj+qgTeweM86f0Qy1QVbvmWH1D9h+alqg254QD8HJXHvjQaGOqEqC22M54PcftZVKVSQG9jhkv7C +JyTyDoAJfPdu8v7DRZAxsP/ky9MJ3OL36DJfCFPASC3/aXlfLOOON9vGZZHydGf8LnxYJuuVIbl83y +Az5n/RPz07E+9+zw2A2ahz4HxHo9Kt79HTMx1Q7ma7zAzHgHqYH0SoZWyTuOLMiHwSfZDAQTn0ajk9 +YQqodnUYjByQZhZak9Wu4gYQsMyEpIOAOQKze8CmEF45KuAHTvIDOfHJNipwoHMuGHBnJElUoDmAyX +c2Qm/R8Ah/iILCCJOEokGowdhDYc/yoL+vpRGwyVSCWFYZNljkhEirGXsalWcAgOdeAdoXcktF2udb +qbUhjWyMQxYO01o6KYKOr6iK3fE4MaS+DsvBsGOBaMb0Y6IxADaJhFICaOLmiWTlDAnY1KzDG4ambL +cWBA8mUzjJsN2KjSaSXGqMCVXYpYkj33mcIApyhQf6YqgeNAmNvuC0t4CsDbSshZJkCS1eNisKqlyG +cF8G2JeiDX6tO6Mv0SmjCa3MFb0bJaGPMU0X7c8XcpvMaOQmCajwSeY9G0WqbBmKv34DsMIEztU6Y2 +KiDlFdt6jnCSqx7Dmt6XnqSKaFFHNO5+FmODxMCWBEaco77lNDGXBM0ECYB/+s7nKFdwSF5hgXumQe +EZ7amRg39RHy3zIjyRCykQh8Zo2iviRKyTDn/zx6EefptJj2Cw+Ep2FSc01U5ry4KLPYsTyWnVGnvb +UpyGlhjBUljyjHhWpf8OFaXwhp9O4T1gU9UeyPPa8A2l0p1kNqPXEVRm1AOs1oAGZU596t6SOR2mcB +Oco1srWtkaVrMUzIErrKri85keKqRQYX9VX0/eAUK1hrSu6HMEX3Qh2sCh0q0D2CtnUqS4hj62sE/z +aDs2Sg7MBS6xnQeooc2R2tC9YrKpEi9pLXfYXp20tDCpSP8rKlrD4axprb9u1Df5hSbz9QU0cRpfgn +kiIzwKucd0wsEHlLpe5yHXuc6FrNelOl7pY2+11kTWx7VpRu97dXA3DO1vbkhcb4zyvERYajQgAADs +=""" + ), + mimetype="image/png", +) + + +TEMPLATE = """\ + + +WSGI Information + +
    + +

    WSGI Information

    +

    + This page displays all available information about the WSGI server and + the underlying Python interpreter. +

    Python Interpreter

    + + + + + + +
    Python Version + %(python_version)s +
    Platform + %(platform)s [%(os)s] +
    API Version + %(api_version)s +
    Byteorder + %(byteorder)s +
    Werkzeug Version + %(werkzeug_version)s +
    +

    WSGI Environment

    + %(wsgi_env)s
    +

    Installed Eggs

    +

    + The following python packages were installed on the system as + Python eggs: +

      %(python_eggs)s
    +

    System Path

    +

    + The following paths are the current contents of the load path. The + following entries are looked up for Python packages. Note that not + all items in this path are folders. Gray and underlined items are + entries pointing to invalid resources or used by custom import hooks + such as the zip importer. +

    + Items with a bright background were expanded for display from a relative + path. If you encounter such paths in the output you might want to check + your setup as relative paths are usually problematic in multithreaded + environments. +

      %(sys_path)s
    +
    +""" + + +def iter_sys_path() -> t.Iterator[t.Tuple[str, bool, bool]]: + if os.name == "posix": + + def strip(x: str) -> str: + prefix = os.path.expanduser("~") + if x.startswith(prefix): + x = f"~{x[len(prefix) :]}" + return x + + else: + + def strip(x: str) -> str: + return x + + cwd = os.path.abspath(os.getcwd()) + for item in sys.path: + path = os.path.join(cwd, item or os.path.curdir) + yield strip(os.path.normpath(path)), not os.path.isdir(path), path != item + + +def render_testapp(req: Request) -> bytes: + try: + import pkg_resources + except ImportError: + eggs: t.Iterable[t.Any] = () + else: + eggs = sorted( + pkg_resources.working_set, + key=lambda x: x.project_name.lower(), # type: ignore + ) + python_eggs = [] + for egg in eggs: + try: + version = egg.version + except (ValueError, AttributeError): + version = "unknown" + python_eggs.append( + f"
  • - -
    - - -
    - -
    - - -
    - -
    - - -
    - - - - {% else %} -
    -
    - - -
    - -
    - - -
    - - File Upload - -
    - {% endif %} - -{% endblock %} diff --git a/docs/flask/templates/view.html b/docs/flask/templates/view.html deleted file mode 100644 index 09af7a4..0000000 --- a/docs/flask/templates/view.html +++ /dev/null @@ -1,44 +0,0 @@ -{% extends 'base.html' %} - -{% block title %} QPP Results {% endblock %} -{% block header %} Results {% endblock %} - -{% block header_content %} -

    This is the description for this page

    -{% endblock %} - - -{% block content %} - -
    - - -
    - - -
    - - -
    - -
    - - -
    - - -
    -

    Test: {{ test }}

    -

    Algorithim: {{ algo }}

    - -
    - -{% endblock %} From 017d691e9091091cd9598dce1fe799d84f45e3a2 Mon Sep 17 00:00:00 2001 From: Ahmed Refik Date: Tue, 4 Apr 2023 14:18:47 -0600 Subject: [PATCH 06/46] [MP-144] merging with stashes, adding files in flask --- docs/flask/Client.py | 85 +++++++++ docs/flask/GI_interface.py | 80 +++++++++ docs/flask/README.md | 13 ++ docs/flask/app.py | 108 ++++++++++++ docs/flask/static/style.css | 302 ++++++++++++++++++++++++++++++++ docs/flask/templates/base.html | 41 +++++ docs/flask/templates/calc.html | 81 +++++++++ docs/flask/templates/index.html | 55 ++++++ docs/flask/templates/view.html | 44 +++++ 9 files changed, 809 insertions(+) create mode 100644 docs/flask/Client.py create mode 100644 docs/flask/GI_interface.py create mode 100644 docs/flask/README.md create mode 100755 docs/flask/app.py create mode 100644 docs/flask/static/style.css create mode 100644 docs/flask/templates/base.html create mode 100644 docs/flask/templates/calc.html create mode 100644 docs/flask/templates/index.html create mode 100644 docs/flask/templates/view.html diff --git a/docs/flask/Client.py b/docs/flask/Client.py new file mode 100644 index 0000000..27e78d3 --- /dev/null +++ b/docs/flask/Client.py @@ -0,0 +1,85 @@ +import socket +import queue +import json +import threading +from threading import Lock +import time +""" +Steps: +Get the user interaction +Make sure connection is established between CLI and Core +Send some message +Separate send message and listening into 2 threads +""" + + +class Client: + + def __init__(self, port, host, name): + self.s = socket.socket() + self.port = port + self.host = host + self.name = name + self.recv_queue = queue.Queue() + self.send_queue = queue.Queue() + # self.mutex = Lock() + # self.outgoing_mutex = Lock() + + def connection_setup(self): + self.s.connect((self.host, self.port)) + + # SEND the handshake + # msg = self.string_to_json("REQUEST_HANDSHAKE", "0") + # self.send_queue.put(msg) + # self.send_queue.put('\n') + # # self.connection_send(msg) + # self.connection_send('\n') + + def connection_send(self): + message = self.send_queue.get() + print(message) + self.s.send(message.encode('ascii')) + print("sent") + + + def to_outgoing_queue(self, message): + # self.outgoing_mutex.acquire() + self.send_queue.put(message) + print("OUTGOING QUEUE {}".format(message)) + # self.outgoing_mutex.release() + + + + def connection_recv(self): + msg = self.s.recv(4096) + self.recv_queue.put(msg) + print("Received from server: " + msg.decode('ascii')) + + def print_outgoing_queue(self): + # self.mutex.acquire() + if not self.recv_queue.empty(): + data = self.recv_queue.get() + # self.mutex.release() + return data + + else: + # self.mutex.release() + return None + + + @staticmethod + def string_to_json(api_call, task_id, interface_type="GC", sender_id="0", + payload_total_fragments="0", payload_fragment_number="0", + payload_size="0", payload_content="Hello World"): + msg = {"api_call":api_call, + "task_id":task_id, + "interface_type":interface_type, + "sender_id":sender_id, + "payload_total_fragments": payload_total_fragments, + "payload_fragment_number": payload_fragment_number, + "payload_size": payload_size, + "payload_content": payload_content} + + return json.dumps(msg) + + diff --git a/docs/flask/GI_interface.py b/docs/flask/GI_interface.py new file mode 100644 index 0000000..b8de67d --- /dev/null +++ b/docs/flask/GI_interface.py @@ -0,0 +1,80 @@ +from Client import * +import random +import logging +import sys +LOG_FILE = "out.log" + + +class Logger: + def init(file, level) -> None: + + # to format the log messages that are printed to terminal + logging.basicConfig( + format="%(levelname)s : %(name)s : %(funcName)s[line %(lineno)s]" + + ": %(message)s" + ) + + # create logger instance + log = logging.getLogger(__name__) + + # set log level + log.setLevel(level) + + # format the .log file messages + if file is not None: + file_handler = logging.FileHandler(file) + formatter = logging.Formatter( + "%(asctime)s : %(levelname)s : Module %(module)s" + + ": %(funcName)s[line %(lineno)s] : %(name)s : %(message)s" + ) + file_handler.setFormatter(formatter) + log.addHandler(file_handler) + + return log + + +class Commands: + def __init__(self, logger) -> None: + self.logger = logger + return + + @staticmethod + def random_string(len): + random_string = "" + for ii in range(len): + random_int = random.randint(33, 126) + random_string += chr(random_int) + return random_string + + @staticmethod + def test_vector_gen(args): + vec_len = args[0] + vec_num = args[1] + test_list = [""] * vec_num + for ii in range(vec_num): + test_list[ii] = Commands.random_string(vec_len) + return test_list + + +class GI: + @staticmethod + def Encrypt(task_id, str_list, client): +# logger = Logger.init(LOG_FILE, logging.DEBUG) + for vector in str_list: + msg = client.string_to_json("ENCRYPT", str(task_id), "GI", "0", "0", "0", str(sys.getsizeof(vector)), vector) + client.to_outgoing_queue(msg) + client.connection_send() + return + + @staticmethod + def ReceivedEncrypt(): + return + + @staticmethod + def Decrypt(): + return + + @staticmethod + def ReceivedDecrypt(): + return + diff --git a/docs/flask/README.md b/docs/flask/README.md new file mode 100644 index 0000000..55cd578 --- /dev/null +++ b/docs/flask/README.md @@ -0,0 +1,13 @@ +## Setup to run Flask + +1. In flask directory, create and run virtual enviroment by running these commands +``` +$ python3 -m venv venv +$ . venv/bin/activate +``` + +2. Install Flask using command `pip install Flask` other dependiences may be required +3. Set Flask application with command `export FLASK_APP=app` +4. Then set mode of operation to either `export FLASK_ENV=development` or `export FLASK_ENV=production` +5. Run website with command `flask run` and the website should be at address `http://127.0.0.1:5000/` loaded on the home page + diff --git a/docs/flask/app.py b/docs/flask/app.py new file mode 100755 index 0000000..5b1acfc --- /dev/null +++ b/docs/flask/app.py @@ -0,0 +1,108 @@ +#! /usr/bin/env python3 + +import os +from flask import Flask, flash, render_template, request, redirect, session +from werkzeug.utils import secure_filename +from Client import * +from GI_interface import * + +app = Flask(__name__) +algos = ['AES', 'QPP', 'AES & QPP'] +tests = ['Encryption', 'Decryption', + 'Encrypt & Decrypt', 'M.I.T.M', + 'Brute Force', 'Monte Carlo', + 'Multi-block Message', 'Known Answer' + ] +UPLOAD_FOLDER = "downloads/" +ALLOWED_EXTENSIONS = {'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'} +GI_PORT = 64999 +vector_select = "True" +vector_len = 100 +vector_num = 100 + +client = Client(GI_PORT, "127.0.0.1", "CLI") +client.connection_setup() +task_id = 0 + + +def allowed_file(filename): + return '.' in filename and \ + filename.split('.', 1)[1].lower() in ALLOWED_EXTENSIONS + + +@app.route("/") +@app.route("/index/") +def index(): + return render_template('index.html') + + +@app.route("/api/") +def api(): + return render_template('api.html') + + +@app.route("/research/") +def research(): + return render_template('research.html') + + +@app.route("/demo/") +def demo(): + return render_template('demo.html') + + +@app.route("/calc/", methods=['GET', 'POST']) +def calc(): + global vector_select, algos, tests, vector_num, vector_len + if request.method == "POST": + print("ERROR 1") + vector_select = request.form['vector_select'] + + return render_template('calc.html', + algo_list=algos, + test_list=tests, + vector_select=vector_select) + + +@app.route("/view/", methods=['GET', 'POST']) +def view(): + global vector_select, algos, tests, vector_num, vector_len, task_id + test = algo = "ERROR" + test_vector = [] + + if request.method == 'POST': + algo_select = request.form.get('algo_select') + test_select = request.form.get('test_select') + print("ERROR 2") + print("{}, {}".format(algo_select, test_select)) + if vector_select == "True": + print("ERROR 3") + vector_len = request.form["vector_len"] + vector_num = request.form["vector_num"] + print("{} vectors of size {}".format(vector_num, vector_len)) + test_vector = Commands.test_vector_gen([int(vector_len), int(vector_num)]) + else: + print("ERROR 4") + if 'file' not in request.files: + flash("No File Uploaded") + file = request.files['file'] + if file.filename == '': + flash("No File Selected") + if file and allowed_file(file.filename): + filename = secure_filename(file.filename) + file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename)) + with open(os.path.abspath(UPLOAD_FOLDER + filename), 'r') as fd: + file_contents = fd.read() + print("{}, contents:\n{}".format(filename, file_contents)) + test_vector = [file_contents] + GI.Encrypt(task_id, test_vector, client) + task_id += 1 + + return render_template('view.html', test=test, algo=algo) + + +if __name__ == "__main__": + app.config['UPLOAD_FOLDER'] = os.path.abspath(UPLOAD_FOLDER) + app.secret_key = 'secret_key' + app.config['SESSION_TYPE'] = 'filesystem' + app.run(debug=True, host="127.0.0.1", port=4996) diff --git a/docs/flask/static/style.css b/docs/flask/static/style.css new file mode 100644 index 0000000..efaac31 --- /dev/null +++ b/docs/flask/static/style.css @@ -0,0 +1,302 @@ +:root { + --vt-font-family-base: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", + Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", + "Helvetica Neue", sans-serif; + --vt-font-family-mono: Menlo, Monaco, Consolas, "Courier New", monospace; +} + +:root { + --vt-c-white: #ffffff; + --vt-c-white-soft: #f9f9f9; + --vt-c-white-mute: #f1f1f1; + --vt-c-black: #1a1a1a; + --vt-c-black-pure: #000000; + --vt-c-black-soft: #242424; + --vt-c-black-mute: #2f2f2f; + --vt-c-indigo: #213547; + --vt-c-indigo-soft: #476582; + --vt-c-indigo-light: #aac8e4; + --vt-c-gray: #8e8e8e; + --vt-c-gray-light-1: #aeaeae; + --vt-c-gray-light-2: #c7c7c7; + --vt-c-gray-light-3: #d1d1d1; + --vt-c-gray-light-4: #e5e5e5; + --vt-c-gray-light-5: #f2f2f2; + --vt-c-gray-dark-1: #636363; + --vt-c-gray-dark-2: #484848; + --vt-c-gray-dark-3: #3a3a3a; + --vt-c-gray-dark-4: #282828; + --vt-c-gray-dark-5: #202020; + --vt-c-divider-light-1: rgba(60, 60, 60, 0.29); + --vt-c-divider-light-2: rgba(60, 60, 60, 0.12); + --vt-c-divider-dark-1: rgba(84, 84, 84, 0.65); + --vt-c-divider-dark-2: rgba(84, 84, 84, 0.48); + --vt-c-text-light-1: var(--vt-c-indigo); + --vt-c-text-light-2: rgba(60, 60, 60, 0.7); + --vt-c-text-light-3: rgba( + 60, + 60, + 60, + .--vt-font-family-base: Inter, + -apple-system, + BlinkMacSystemFont, + "Segoe UI", + Roboto, + Oxygen, + Ubuntu, + Cantarell, + "Fira Sans", + "Droid Sans", + "Helvetica Neue", + sans-serif; --vt-font-family-mono: Menlo, + Monaco, + Consolas, + "Courier New", + monospace;33 + ); + --vt-c-text-light-4: rgba(60, 60, 60, 0.18); + --vt-c-text-light-code: var(--vt-c-indigo-soft); + --vt-c-text-dark-1: rgba(255, 255, 255, 0.87); + --vt-c-text-dark-2: rgba(235, 235, 235, 0.6); + --vt-c-text-dark-3: rgba(235, 235, 235, 0.38); + --vt-c-text-dark-4: rgba(235, 235, 235, 0.18); + --vt-c-text-dark-code: var(--vt-c-indigo-light); + --vt-c-green: #42b883; + --vt-c-green-light: #42d392; + --vt-c-green-lighter: #35eb9a; + --vt-c-green-dark: #33a06f; + --vt-c-green-darker: #155f3e; + --vt-c-blue: #3b8eed; + --vt-c-blue-light: #549ced; + --vt-c-blue-lighter: #50a2ff; + --vt-c-blue-dark: #3468a3; + --vt-c-blue-darker: #255489; + --vt-c-yellow: #ffc517; + --vt-c-yellow-light: #ffe417; + --vt-c-yellow-lighter: #ffff17; + --vt-c-yellow-dark: #e0ad15; + --vt-c-yellow-darker: #bc9112; + --vt-c-red: #ed3c50; + --vt-c-red-light: #f43771; + --vt-c-red-lighter: #fd1d7c; + --vt-c-red-dark: #cd2d3f; + --vt-c-red-darker: #ab2131; + --vt-c-purple: #de41e0; + --vt-c-purple-light: #e936eb; + --vt-c-purple-lighter: #f616f8; + --vt-c-purple-dark: #823c83; + --vt-c-purple-darker: #602960; + --header-purple: #4c0ca0; + --header-green: #053f05; + --text-header: #ff88ff; +} + + +html { + word-wrap: break-word; +} + + +* { + box-sizing: border-box; +} + +nav { + position: fixed; + left: 0%; + right: 0%; + top: 0%; + bottom: auto; + height: 70px; + background-color: #ffffff; + display: flex; + flex-direction: row; + justify-content: flex-end; + flex-grow: 1; + min-width: inherit; + border-bottom: 1px solid #000; + /* border-bottom: 1px dotted hsl(0deg 0% 0% / 0.2); */ +} + +a { + text-decoration: none; + color: inherit; +} + +body { + margin: 0px; + font-family: var(--vt-font-family-base); + letter-spacing: 0.2px; +<<<<<<< HEAD +======= + line-height: 24px; +>>>>>>> 8cf1338 (renamed UI folder to docs for github pages) + font-size: 16px; + font-weight: 400; + direction: ltr; + font-synthesis: none; + text-rendering: optimizeLegibility; + display: flex; + flex-direction: column; +} + +.button { + transition: all 300ms ease; + background-color: #000000; + padding: 9px 28px 10px; + align-items: center; + border-radius: 20px; + max-width: 100%; + color: #ffffff; +} + +.header-list { + text-transform: capitalize; + display: flex; + color: inherit; + font-weight: bold; + justify-content: flex-end; + align-items: center; + list-style-type: none; + flex-grow: 1; + gap: 12px; + padding-right: 40px; +} + +.header-logo { + font-size: 25px; + flex-grow: 1; + margin-right: auto; +} + +header { + margin-top: 70px; + border: 0px; + padding: 30px; + min-height: 300px; + display: flex; + flex-direction: column; + align-items: stretch; + flex: 1; + border-bottom: #000 solid 1px; +} + +header div { + display: flex; + flex: 1; +} + +#hero { + align-items: center; + flex-wrap: wrap; + gap: 10px; +} + +.col { + padding: 30px; + border-bottom: 1px solid #000; +} + +.hero-text { + flex-direction: column; + align-self: center; + flex: 1; +} + +h1 { + font-size: 5vw; +} + +p { + font-size: 18px; +} + +.hero-image { + aspect-ratio: 4/3; + max-width: 200px; + width: 100%; + text-align: center; + display: flex; + flex-direction: column; + justify-content: center; + border: 1px solid #000; + flex: 1; + max-width: 100%; +} + +.hero-text button { + align-self: flex-start; +} + +.article-1 { + padding: 30px; + color: #000; + background-color: rgb(78, 226, 65); +} + +.article-2 { + background-color: rgb(233, 247, 47); + border-right: #000 solid 1px; +} + +.article-3 { + background-color: #ff48d7; +} + +.article-interim { + border-bottom: #fff 1px solid; + background-color: #000; + color: #fff; + display: flex; + flex-direction: column; + align-items: center; +} + +.side-view { + flex: 1; + display: flex; +} + +/**************************************************/ +div.hero-text h1 { + font-size: 45px; +} + +.api-section-body { + display: flex; + flex-direction: row; + gap: 10px; + justify-content: center; +} + +.api-section h1 { + font-size: 30px; +} + +.api-group h1 { + font-size: 25px; + min-width: 400px; +} + +.api-filter { + gap: 10px; + justify-content: center; + align-items: baseline; + flex: 1; +} + +#api-filter { + padding: 5px 5px 5px; + border-radius: 10px; + border-width: 1px solid #000; +} + +.api-section { + padding: 20px; +} + +.api-group { + background-color: #DDD; + padding: 10px; + border-radius: 10px; +} diff --git a/docs/flask/templates/base.html b/docs/flask/templates/base.html new file mode 100644 index 0000000..943ef26 --- /dev/null +++ b/docs/flask/templates/base.html @@ -0,0 +1,41 @@ + + + + + + + + {% block title %} {% endblock %} + + +
    + + + +
    +
    +

    {% block header %} {% endblock %}

    + {% block header_content %} {% endblock %} +
    +
    + {% block content %} {% endblock %} + + diff --git a/docs/flask/templates/calc.html b/docs/flask/templates/calc.html new file mode 100644 index 0000000..5995b08 --- /dev/null +++ b/docs/flask/templates/calc.html @@ -0,0 +1,81 @@ + +{% extends 'base.html' %} + +{% block title %} QPP Request {% endblock %} +{% block header %} Interactive Testbed {% endblock %} + +{% block header_content %} +

    This is the description for this page

    +{% endblock %} + + +{% block content %} + +
    + {% if vector_select == "True" %} + + {% else %} + + {% endif %} +
    + +{% if vector_select == "True" %} +
    + + + + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + {% else %} +
    +
    + + +
    + +
    + + +
    + + File Upload + +
    + {% endif %} + +{% endblock %} diff --git a/docs/flask/templates/index.html b/docs/flask/templates/index.html new file mode 100644 index 0000000..ea9e5b4 --- /dev/null +++ b/docs/flask/templates/index.html @@ -0,0 +1,55 @@ +{% extends 'base.html' %} + +{% block title %} QPP Index {% endblock %} +{% block header %} QPP Testbed {% endblock %} + +{% block header_content %} +

    This is the description for this page

    +{% endblock %} + + +{% block content %} +
    +

    Some random thingy

    +

    + Phasellus convallis elit id ullamcorper pulvinar. Duis aliquam turpis + mauris, eu ultricies erat malesuada quis. Aliquam dapibus, lacus eget + hendrerit bibendum, urna est aliquam sem, sit amet imperdiet est velit + quis lorem. +

    +
    +
    +
    +

    Some random thingy

    +

    + Phasellus convallis elit id ullamcorper pulvinar. Duis aliquam turpis + mauris, eu ultricies erat malesuada quis. Aliquam dapibus, lacus eget + hendrerit bibendum, urna est aliquam sem, sit amet imperdiet est velit + quis lorem. +

    +
    + +
    +

    Some random thingy

    +

    + Phasellus convallis elit id ullamcorper pulvinar. Duis aliquam turpis + mauris, eu ultricies erat malesuada quis. Aliquam dapibus, lacus eget + hendrerit bibendum, urna est aliquam sem, sit amet imperdiet est velit + quis lorem. +

    +
    +
    +
    +

    HHHHHHHHHHHHHHH

    +

    oasnfoiasjfniuasjfiasfiuhsafiajsfiusafojsaoijfniosanfo

    +
    + +
    +

    HSOPCMAOISCM

    +

    + Moloch the incomprehensible prison! Moloch the crossbone soulless + jailhouse and Congress of sorrows! Moloch whose buildings are judgment! + Moloch the vast stone of war! Moloch the stunned governments! +

    +
    +{% endblock %} diff --git a/docs/flask/templates/view.html b/docs/flask/templates/view.html new file mode 100644 index 0000000..09af7a4 --- /dev/null +++ b/docs/flask/templates/view.html @@ -0,0 +1,44 @@ +{% extends 'base.html' %} + +{% block title %} QPP Results {% endblock %} +{% block header %} Results {% endblock %} + +{% block header_content %} +

    This is the description for this page

    +{% endblock %} + + +{% block content %} + +
    + + +
    + + +
    + + +
    + +
    + + +
    + + +
    +

    Test: {{ test }}

    +

    Algorithim: {{ algo }}

    + +
    + +{% endblock %} From 5a3aec7ebce2773462a3a1777ce1c1e7ea845fbf Mon Sep 17 00:00:00 2001 From: Ahmed Refik Date: Tue, 4 Apr 2023 23:52:55 -0600 Subject: [PATCH 07/46] [MP-144] merging with main again --- .gitignore | 1 - CLI/cli.py | 19 ++++--------------- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index 389adc6..0c23680 100644 --- a/.gitignore +++ b/.gitignore @@ -25,4 +25,3 @@ # pycache for CLI /CLI/__pycache__/ -======= diff --git a/CLI/cli.py b/CLI/cli.py index 8f2697b..653de1c 100755 --- a/CLI/cli.py +++ b/CLI/cli.py @@ -203,8 +203,8 @@ def UserInput(self) -> bool: outgoing_t.start() print("THREAD START") print_msg_t.start() - print("THREAD START") + # parse the arguments from standard input while True: userinput = input("-> ") @@ -216,21 +216,9 @@ def UserInput(self) -> bool: else: log_level = logging.ERROR -<<<<<<< Updated upstream if not args.logging: LOG_FILE = None -======= - json = \ - { - "task_id": 1, - "sender_id": 1 - } - - if args.verbose: - log_level = logging.DEBUG - else: - log_level = logging.ERROR ->>>>>>> Stashed changes + # configure logging to user preference logger = Logger.init(LOG_FILE, log_level) @@ -247,6 +235,7 @@ def UserInput(self) -> bool: + if args.encryption: # qpp_cipher = QPP_commands.encrypt(test_vectors, "QPP") # if args.AES: @@ -291,4 +280,4 @@ def UserInput(self) -> bool: if __name__ == "__main__": parser = QPP_parser() - parser.UserInput() \ No newline at end of file + parser.UserInput() From 5a4446933e44f3b2bd37258c8557354fea12bb60 Mon Sep 17 00:00:00 2001 From: Ahmed Refik Date: Tue, 4 Apr 2023 14:30:09 -0600 Subject: [PATCH 08/46] [MP-144] merging with CLI mutithreading stuff --- CLI/cli.py | 1 - 1 file changed, 1 deletion(-) diff --git a/CLI/cli.py b/CLI/cli.py index 653de1c..0d261bc 100755 --- a/CLI/cli.py +++ b/CLI/cli.py @@ -219,7 +219,6 @@ def UserInput(self) -> bool: if not args.logging: LOG_FILE = None - # configure logging to user preference logger = Logger.init(LOG_FILE, log_level) From 03027a6095c520dd9930e6112119306b76f33eb9 Mon Sep 17 00:00:00 2001 From: Ahmed Refik Date: Tue, 4 Apr 2023 14:33:37 -0600 Subject: [PATCH 09/46] [MP-144] merging with CLI mutithreading basics --- CLI/Client.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/CLI/Client.py b/CLI/Client.py index 27bfe97..be6b5e1 100644 --- a/CLI/Client.py +++ b/CLI/Client.py @@ -51,8 +51,6 @@ def to_outgoing_queue(self, message): print("OUTGOING QUEUE {}".format(message)) # self.outgoing_mutex.release() - - def connection_recv(self): while True: msg = self.s.recv(4096) From a8d3c607a92217ba1ea74b6a24e0d7ae06bb3552 Mon Sep 17 00:00:00 2001 From: Ahmed Refik Date: Tue, 4 Apr 2023 14:34:29 -0600 Subject: [PATCH 10/46] [MP-144] merging with CLI mutithreading --- CLI/Client.py | 1 + 1 file changed, 1 insertion(+) diff --git a/CLI/Client.py b/CLI/Client.py index be6b5e1..54edb69 100644 --- a/CLI/Client.py +++ b/CLI/Client.py @@ -51,6 +51,7 @@ def to_outgoing_queue(self, message): print("OUTGOING QUEUE {}".format(message)) # self.outgoing_mutex.release() + def connection_recv(self): while True: msg = self.s.recv(4096) From f38272c808669fb7a8188e003495ce11f307ea01 Mon Sep 17 00:00:00 2001 From: Ahmed Refik Date: Tue, 4 Apr 2023 14:18:07 -0600 Subject: [PATCH 11/46] [MP-144] merging with stashes --- .gitignore | 1 + docs/flask/Client.py | 85 --------- docs/flask/GI_interface.py | 80 --------- docs/flask/README.md | 13 -- docs/flask/app.py | 108 ------------ docs/flask/static/style.css | 302 -------------------------------- docs/flask/templates/base.html | 41 ----- docs/flask/templates/calc.html | 81 --------- docs/flask/templates/index.html | 55 ------ docs/flask/templates/view.html | 44 ----- 10 files changed, 1 insertion(+), 809 deletions(-) delete mode 100644 docs/flask/Client.py delete mode 100644 docs/flask/GI_interface.py delete mode 100644 docs/flask/README.md delete mode 100755 docs/flask/app.py delete mode 100644 docs/flask/static/style.css delete mode 100644 docs/flask/templates/base.html delete mode 100644 docs/flask/templates/calc.html delete mode 100644 docs/flask/templates/index.html delete mode 100644 docs/flask/templates/view.html diff --git a/.gitignore b/.gitignore index 0c23680..389adc6 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,4 @@ # pycache for CLI /CLI/__pycache__/ +======= diff --git a/docs/flask/Client.py b/docs/flask/Client.py deleted file mode 100644 index 27e78d3..0000000 --- a/docs/flask/Client.py +++ /dev/null @@ -1,85 +0,0 @@ -import socket -import queue -import json -import threading -from threading import Lock -import time -""" -Steps: -Get the user interaction -Make sure connection is established between CLI and Core -Send some message -Separate send message and listening into 2 threads -""" - - -class Client: - - def __init__(self, port, host, name): - self.s = socket.socket() - self.port = port - self.host = host - self.name = name - self.recv_queue = queue.Queue() - self.send_queue = queue.Queue() - # self.mutex = Lock() - # self.outgoing_mutex = Lock() - - def connection_setup(self): - self.s.connect((self.host, self.port)) - - # SEND the handshake - # msg = self.string_to_json("REQUEST_HANDSHAKE", "0") - # self.send_queue.put(msg) - # self.send_queue.put('\n') - # # self.connection_send(msg) - # self.connection_send('\n') - - def connection_send(self): - message = self.send_queue.get() - print(message) - self.s.send(message.encode('ascii')) - print("sent") - - - def to_outgoing_queue(self, message): - # self.outgoing_mutex.acquire() - self.send_queue.put(message) - print("OUTGOING QUEUE {}".format(message)) - # self.outgoing_mutex.release() - - - - def connection_recv(self): - msg = self.s.recv(4096) - self.recv_queue.put(msg) - print("Received from server: " + msg.decode('ascii')) - - def print_outgoing_queue(self): - # self.mutex.acquire() - if not self.recv_queue.empty(): - data = self.recv_queue.get() - # self.mutex.release() - return data - - else: - # self.mutex.release() - return None - - - @staticmethod - def string_to_json(api_call, task_id, interface_type="GC", sender_id="0", - payload_total_fragments="0", payload_fragment_number="0", - payload_size="0", payload_content="Hello World"): - msg = {"api_call":api_call, - "task_id":task_id, - "interface_type":interface_type, - "sender_id":sender_id, - "payload_total_fragments": payload_total_fragments, - "payload_fragment_number": payload_fragment_number, - "payload_size": payload_size, - "payload_content": payload_content} - - return json.dumps(msg) - - diff --git a/docs/flask/GI_interface.py b/docs/flask/GI_interface.py deleted file mode 100644 index b8de67d..0000000 --- a/docs/flask/GI_interface.py +++ /dev/null @@ -1,80 +0,0 @@ -from Client import * -import random -import logging -import sys -LOG_FILE = "out.log" - - -class Logger: - def init(file, level) -> None: - - # to format the log messages that are printed to terminal - logging.basicConfig( - format="%(levelname)s : %(name)s : %(funcName)s[line %(lineno)s]" - + ": %(message)s" - ) - - # create logger instance - log = logging.getLogger(__name__) - - # set log level - log.setLevel(level) - - # format the .log file messages - if file is not None: - file_handler = logging.FileHandler(file) - formatter = logging.Formatter( - "%(asctime)s : %(levelname)s : Module %(module)s" - + ": %(funcName)s[line %(lineno)s] : %(name)s : %(message)s" - ) - file_handler.setFormatter(formatter) - log.addHandler(file_handler) - - return log - - -class Commands: - def __init__(self, logger) -> None: - self.logger = logger - return - - @staticmethod - def random_string(len): - random_string = "" - for ii in range(len): - random_int = random.randint(33, 126) - random_string += chr(random_int) - return random_string - - @staticmethod - def test_vector_gen(args): - vec_len = args[0] - vec_num = args[1] - test_list = [""] * vec_num - for ii in range(vec_num): - test_list[ii] = Commands.random_string(vec_len) - return test_list - - -class GI: - @staticmethod - def Encrypt(task_id, str_list, client): -# logger = Logger.init(LOG_FILE, logging.DEBUG) - for vector in str_list: - msg = client.string_to_json("ENCRYPT", str(task_id), "GI", "0", "0", "0", str(sys.getsizeof(vector)), vector) - client.to_outgoing_queue(msg) - client.connection_send() - return - - @staticmethod - def ReceivedEncrypt(): - return - - @staticmethod - def Decrypt(): - return - - @staticmethod - def ReceivedDecrypt(): - return - diff --git a/docs/flask/README.md b/docs/flask/README.md deleted file mode 100644 index 55cd578..0000000 --- a/docs/flask/README.md +++ /dev/null @@ -1,13 +0,0 @@ -## Setup to run Flask - -1. In flask directory, create and run virtual enviroment by running these commands -``` -$ python3 -m venv venv -$ . venv/bin/activate -``` - -2. Install Flask using command `pip install Flask` other dependiences may be required -3. Set Flask application with command `export FLASK_APP=app` -4. Then set mode of operation to either `export FLASK_ENV=development` or `export FLASK_ENV=production` -5. Run website with command `flask run` and the website should be at address `http://127.0.0.1:5000/` loaded on the home page - diff --git a/docs/flask/app.py b/docs/flask/app.py deleted file mode 100755 index 5b1acfc..0000000 --- a/docs/flask/app.py +++ /dev/null @@ -1,108 +0,0 @@ -#! /usr/bin/env python3 - -import os -from flask import Flask, flash, render_template, request, redirect, session -from werkzeug.utils import secure_filename -from Client import * -from GI_interface import * - -app = Flask(__name__) -algos = ['AES', 'QPP', 'AES & QPP'] -tests = ['Encryption', 'Decryption', - 'Encrypt & Decrypt', 'M.I.T.M', - 'Brute Force', 'Monte Carlo', - 'Multi-block Message', 'Known Answer' - ] -UPLOAD_FOLDER = "downloads/" -ALLOWED_EXTENSIONS = {'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'} -GI_PORT = 64999 -vector_select = "True" -vector_len = 100 -vector_num = 100 - -client = Client(GI_PORT, "127.0.0.1", "CLI") -client.connection_setup() -task_id = 0 - - -def allowed_file(filename): - return '.' in filename and \ - filename.split('.', 1)[1].lower() in ALLOWED_EXTENSIONS - - -@app.route("/") -@app.route("/index/") -def index(): - return render_template('index.html') - - -@app.route("/api/") -def api(): - return render_template('api.html') - - -@app.route("/research/") -def research(): - return render_template('research.html') - - -@app.route("/demo/") -def demo(): - return render_template('demo.html') - - -@app.route("/calc/", methods=['GET', 'POST']) -def calc(): - global vector_select, algos, tests, vector_num, vector_len - if request.method == "POST": - print("ERROR 1") - vector_select = request.form['vector_select'] - - return render_template('calc.html', - algo_list=algos, - test_list=tests, - vector_select=vector_select) - - -@app.route("/view/", methods=['GET', 'POST']) -def view(): - global vector_select, algos, tests, vector_num, vector_len, task_id - test = algo = "ERROR" - test_vector = [] - - if request.method == 'POST': - algo_select = request.form.get('algo_select') - test_select = request.form.get('test_select') - print("ERROR 2") - print("{}, {}".format(algo_select, test_select)) - if vector_select == "True": - print("ERROR 3") - vector_len = request.form["vector_len"] - vector_num = request.form["vector_num"] - print("{} vectors of size {}".format(vector_num, vector_len)) - test_vector = Commands.test_vector_gen([int(vector_len), int(vector_num)]) - else: - print("ERROR 4") - if 'file' not in request.files: - flash("No File Uploaded") - file = request.files['file'] - if file.filename == '': - flash("No File Selected") - if file and allowed_file(file.filename): - filename = secure_filename(file.filename) - file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename)) - with open(os.path.abspath(UPLOAD_FOLDER + filename), 'r') as fd: - file_contents = fd.read() - print("{}, contents:\n{}".format(filename, file_contents)) - test_vector = [file_contents] - GI.Encrypt(task_id, test_vector, client) - task_id += 1 - - return render_template('view.html', test=test, algo=algo) - - -if __name__ == "__main__": - app.config['UPLOAD_FOLDER'] = os.path.abspath(UPLOAD_FOLDER) - app.secret_key = 'secret_key' - app.config['SESSION_TYPE'] = 'filesystem' - app.run(debug=True, host="127.0.0.1", port=4996) diff --git a/docs/flask/static/style.css b/docs/flask/static/style.css deleted file mode 100644 index efaac31..0000000 --- a/docs/flask/static/style.css +++ /dev/null @@ -1,302 +0,0 @@ -:root { - --vt-font-family-base: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", - Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", - "Helvetica Neue", sans-serif; - --vt-font-family-mono: Menlo, Monaco, Consolas, "Courier New", monospace; -} - -:root { - --vt-c-white: #ffffff; - --vt-c-white-soft: #f9f9f9; - --vt-c-white-mute: #f1f1f1; - --vt-c-black: #1a1a1a; - --vt-c-black-pure: #000000; - --vt-c-black-soft: #242424; - --vt-c-black-mute: #2f2f2f; - --vt-c-indigo: #213547; - --vt-c-indigo-soft: #476582; - --vt-c-indigo-light: #aac8e4; - --vt-c-gray: #8e8e8e; - --vt-c-gray-light-1: #aeaeae; - --vt-c-gray-light-2: #c7c7c7; - --vt-c-gray-light-3: #d1d1d1; - --vt-c-gray-light-4: #e5e5e5; - --vt-c-gray-light-5: #f2f2f2; - --vt-c-gray-dark-1: #636363; - --vt-c-gray-dark-2: #484848; - --vt-c-gray-dark-3: #3a3a3a; - --vt-c-gray-dark-4: #282828; - --vt-c-gray-dark-5: #202020; - --vt-c-divider-light-1: rgba(60, 60, 60, 0.29); - --vt-c-divider-light-2: rgba(60, 60, 60, 0.12); - --vt-c-divider-dark-1: rgba(84, 84, 84, 0.65); - --vt-c-divider-dark-2: rgba(84, 84, 84, 0.48); - --vt-c-text-light-1: var(--vt-c-indigo); - --vt-c-text-light-2: rgba(60, 60, 60, 0.7); - --vt-c-text-light-3: rgba( - 60, - 60, - 60, - .--vt-font-family-base: Inter, - -apple-system, - BlinkMacSystemFont, - "Segoe UI", - Roboto, - Oxygen, - Ubuntu, - Cantarell, - "Fira Sans", - "Droid Sans", - "Helvetica Neue", - sans-serif; --vt-font-family-mono: Menlo, - Monaco, - Consolas, - "Courier New", - monospace;33 - ); - --vt-c-text-light-4: rgba(60, 60, 60, 0.18); - --vt-c-text-light-code: var(--vt-c-indigo-soft); - --vt-c-text-dark-1: rgba(255, 255, 255, 0.87); - --vt-c-text-dark-2: rgba(235, 235, 235, 0.6); - --vt-c-text-dark-3: rgba(235, 235, 235, 0.38); - --vt-c-text-dark-4: rgba(235, 235, 235, 0.18); - --vt-c-text-dark-code: var(--vt-c-indigo-light); - --vt-c-green: #42b883; - --vt-c-green-light: #42d392; - --vt-c-green-lighter: #35eb9a; - --vt-c-green-dark: #33a06f; - --vt-c-green-darker: #155f3e; - --vt-c-blue: #3b8eed; - --vt-c-blue-light: #549ced; - --vt-c-blue-lighter: #50a2ff; - --vt-c-blue-dark: #3468a3; - --vt-c-blue-darker: #255489; - --vt-c-yellow: #ffc517; - --vt-c-yellow-light: #ffe417; - --vt-c-yellow-lighter: #ffff17; - --vt-c-yellow-dark: #e0ad15; - --vt-c-yellow-darker: #bc9112; - --vt-c-red: #ed3c50; - --vt-c-red-light: #f43771; - --vt-c-red-lighter: #fd1d7c; - --vt-c-red-dark: #cd2d3f; - --vt-c-red-darker: #ab2131; - --vt-c-purple: #de41e0; - --vt-c-purple-light: #e936eb; - --vt-c-purple-lighter: #f616f8; - --vt-c-purple-dark: #823c83; - --vt-c-purple-darker: #602960; - --header-purple: #4c0ca0; - --header-green: #053f05; - --text-header: #ff88ff; -} - - -html { - word-wrap: break-word; -} - - -* { - box-sizing: border-box; -} - -nav { - position: fixed; - left: 0%; - right: 0%; - top: 0%; - bottom: auto; - height: 70px; - background-color: #ffffff; - display: flex; - flex-direction: row; - justify-content: flex-end; - flex-grow: 1; - min-width: inherit; - border-bottom: 1px solid #000; - /* border-bottom: 1px dotted hsl(0deg 0% 0% / 0.2); */ -} - -a { - text-decoration: none; - color: inherit; -} - -body { - margin: 0px; - font-family: var(--vt-font-family-base); - letter-spacing: 0.2px; -<<<<<<< HEAD -======= - line-height: 24px; ->>>>>>> 8cf1338 (renamed UI folder to docs for github pages) - font-size: 16px; - font-weight: 400; - direction: ltr; - font-synthesis: none; - text-rendering: optimizeLegibility; - display: flex; - flex-direction: column; -} - -.button { - transition: all 300ms ease; - background-color: #000000; - padding: 9px 28px 10px; - align-items: center; - border-radius: 20px; - max-width: 100%; - color: #ffffff; -} - -.header-list { - text-transform: capitalize; - display: flex; - color: inherit; - font-weight: bold; - justify-content: flex-end; - align-items: center; - list-style-type: none; - flex-grow: 1; - gap: 12px; - padding-right: 40px; -} - -.header-logo { - font-size: 25px; - flex-grow: 1; - margin-right: auto; -} - -header { - margin-top: 70px; - border: 0px; - padding: 30px; - min-height: 300px; - display: flex; - flex-direction: column; - align-items: stretch; - flex: 1; - border-bottom: #000 solid 1px; -} - -header div { - display: flex; - flex: 1; -} - -#hero { - align-items: center; - flex-wrap: wrap; - gap: 10px; -} - -.col { - padding: 30px; - border-bottom: 1px solid #000; -} - -.hero-text { - flex-direction: column; - align-self: center; - flex: 1; -} - -h1 { - font-size: 5vw; -} - -p { - font-size: 18px; -} - -.hero-image { - aspect-ratio: 4/3; - max-width: 200px; - width: 100%; - text-align: center; - display: flex; - flex-direction: column; - justify-content: center; - border: 1px solid #000; - flex: 1; - max-width: 100%; -} - -.hero-text button { - align-self: flex-start; -} - -.article-1 { - padding: 30px; - color: #000; - background-color: rgb(78, 226, 65); -} - -.article-2 { - background-color: rgb(233, 247, 47); - border-right: #000 solid 1px; -} - -.article-3 { - background-color: #ff48d7; -} - -.article-interim { - border-bottom: #fff 1px solid; - background-color: #000; - color: #fff; - display: flex; - flex-direction: column; - align-items: center; -} - -.side-view { - flex: 1; - display: flex; -} - -/**************************************************/ -div.hero-text h1 { - font-size: 45px; -} - -.api-section-body { - display: flex; - flex-direction: row; - gap: 10px; - justify-content: center; -} - -.api-section h1 { - font-size: 30px; -} - -.api-group h1 { - font-size: 25px; - min-width: 400px; -} - -.api-filter { - gap: 10px; - justify-content: center; - align-items: baseline; - flex: 1; -} - -#api-filter { - padding: 5px 5px 5px; - border-radius: 10px; - border-width: 1px solid #000; -} - -.api-section { - padding: 20px; -} - -.api-group { - background-color: #DDD; - padding: 10px; - border-radius: 10px; -} diff --git a/docs/flask/templates/base.html b/docs/flask/templates/base.html deleted file mode 100644 index 943ef26..0000000 --- a/docs/flask/templates/base.html +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - {% block title %} {% endblock %} - - -
    - - - -
    -
    -

    {% block header %} {% endblock %}

    - {% block header_content %} {% endblock %} -
    -
    - {% block content %} {% endblock %} - - diff --git a/docs/flask/templates/calc.html b/docs/flask/templates/calc.html deleted file mode 100644 index 5995b08..0000000 --- a/docs/flask/templates/calc.html +++ /dev/null @@ -1,81 +0,0 @@ - -{% extends 'base.html' %} - -{% block title %} QPP Request {% endblock %} -{% block header %} Interactive Testbed {% endblock %} - -{% block header_content %} -

    This is the description for this page

    -{% endblock %} - - -{% block content %} - -
    - {% if vector_select == "True" %} - - {% else %} - - {% endif %} -
    - -{% if vector_select == "True" %} -
    - - - - -
    - - -
    - -
    - - -
    - -
    - - -
    - -
    - - {% else %} -
    -
    - - -
    - -
    - - -
    - - File Upload - -
    - {% endif %} - -{% endblock %} diff --git a/docs/flask/templates/index.html b/docs/flask/templates/index.html deleted file mode 100644 index ea9e5b4..0000000 --- a/docs/flask/templates/index.html +++ /dev/null @@ -1,55 +0,0 @@ -{% extends 'base.html' %} - -{% block title %} QPP Index {% endblock %} -{% block header %} QPP Testbed {% endblock %} - -{% block header_content %} -

    This is the description for this page

    -{% endblock %} - - -{% block content %} -
    -

    Some random thingy

    -

    - Phasellus convallis elit id ullamcorper pulvinar. Duis aliquam turpis - mauris, eu ultricies erat malesuada quis. Aliquam dapibus, lacus eget - hendrerit bibendum, urna est aliquam sem, sit amet imperdiet est velit - quis lorem. -

    -
    -
    -
    -

    Some random thingy

    -

    - Phasellus convallis elit id ullamcorper pulvinar. Duis aliquam turpis - mauris, eu ultricies erat malesuada quis. Aliquam dapibus, lacus eget - hendrerit bibendum, urna est aliquam sem, sit amet imperdiet est velit - quis lorem. -

    -
    - -
    -

    Some random thingy

    -

    - Phasellus convallis elit id ullamcorper pulvinar. Duis aliquam turpis - mauris, eu ultricies erat malesuada quis. Aliquam dapibus, lacus eget - hendrerit bibendum, urna est aliquam sem, sit amet imperdiet est velit - quis lorem. -

    -
    -
    -
    -

    HHHHHHHHHHHHHHH

    -

    oasnfoiasjfniuasjfiasfiuhsafiajsfiusafojsaoijfniosanfo

    -
    - -
    -

    HSOPCMAOISCM

    -

    - Moloch the incomprehensible prison! Moloch the crossbone soulless - jailhouse and Congress of sorrows! Moloch whose buildings are judgment! - Moloch the vast stone of war! Moloch the stunned governments! -

    -
    -{% endblock %} diff --git a/docs/flask/templates/view.html b/docs/flask/templates/view.html deleted file mode 100644 index 09af7a4..0000000 --- a/docs/flask/templates/view.html +++ /dev/null @@ -1,44 +0,0 @@ -{% extends 'base.html' %} - -{% block title %} QPP Results {% endblock %} -{% block header %} Results {% endblock %} - -{% block header_content %} -

    This is the description for this page

    -{% endblock %} - - -{% block content %} - -
    - - -
    - - -
    - - -
    - -
    - - -
    - - -
    -

    Test: {{ test }}

    -

    Algorithim: {{ algo }}

    - -
    - -{% endblock %} From 9632bc9454cbc316b547460832bf42c576eff09b Mon Sep 17 00:00:00 2001 From: Ahmed Refik Date: Tue, 4 Apr 2023 23:55:45 -0600 Subject: [PATCH 12/46] [MP-144] trying to connect flask to socket, not going well, can't send or recv --- docs/flask/Client.py | 81 +++++++++++++++++++++ docs/flask/GI_interface.py | 105 +++++++++++++++++++++++++++ docs/flask/README.md | 13 ++++ docs/flask/app.py | 115 ++++++++++++++++++++++++++++++ docs/flask/static/scripts/calc.js | 13 ++++ docs/flask/templates/calc.html | 66 +++++++++++++++++ docs/flask/templates/view.html | 23 ++++++ 7 files changed, 416 insertions(+) create mode 100644 docs/flask/Client.py create mode 100644 docs/flask/GI_interface.py create mode 100644 docs/flask/README.md create mode 100755 docs/flask/app.py create mode 100644 docs/flask/static/scripts/calc.js create mode 100644 docs/flask/templates/calc.html create mode 100644 docs/flask/templates/view.html diff --git a/docs/flask/Client.py b/docs/flask/Client.py new file mode 100644 index 0000000..30fb0cf --- /dev/null +++ b/docs/flask/Client.py @@ -0,0 +1,81 @@ +import socket +import queue +import json +import threading +from threading import Lock +import time +""" +Steps: +Get the user interaction +Make sure connection is established between CLI and Core +Send some message +Separate send message and listening into 2 threads +""" + + +class Client: + + def __init__(self, port, host, name): + self.s = socket.socket() + self.port = port + self.host = host + self.name = name + self.recv_queue = queue.Queue() + self.send_queue = queue.Queue() + # self.mutex = Lock() + # self.outgoing_mutex = Lock() + + def connection_setup(self): + self.s.connect((self.host, self.port)) + # SEND the handshake + self.to_outgoing_queue(str(self.port)) + self.connection_send() + + def connection_send(self): + message = self.send_queue.get() + print(message) + self.s.send(message.encode('ascii')) + print("sent to port {}".format(self.port)) + + + def to_outgoing_queue(self, message): + # self.outgoing_mutex.acquire() + self.send_queue.put(message) + print("OUTGOING QUEUE {}".format(message)) + # self.outgoing_mutex.release() + + + + def connection_recv(self): + msg = self.s.recv(1) + self.recv_queue.put(msg) + print("Received from server: " + msg.decode('ascii')) + + def print_outgoing_queue(self): + # self.mutex.acquire() + if not self.recv_queue.empty(): + data = self.recv_queue.get() + # self.mutex.release() + return data + + else: + # self.mutex.release() + return None + + + @staticmethod + def string_to_json(api_call, task_id, interface_type="GC", sender_id="0", + payload_total_fragments="0", payload_fragment_number="0", + payload_size="0", payload_content="Hello World"): + msg = {"api_call":api_call, + "task_id":task_id, + "interface_type":interface_type, + "sender_id":sender_id, + "payload_total_fragments": payload_total_fragments, + "payload_fragment_number": payload_fragment_number, + "payload_size": payload_size, + "payload_content": payload_content} + + return json.dumps(msg) + + diff --git a/docs/flask/GI_interface.py b/docs/flask/GI_interface.py new file mode 100644 index 0000000..eabd89f --- /dev/null +++ b/docs/flask/GI_interface.py @@ -0,0 +1,105 @@ +from Client import * +import random +import logging +import sys +import json +LOG_FILE = "out.log" + + +class Logger: + def init(file, level) -> None: + + # to format the log messages that are printed to terminal + logging.basicConfig( + format="%(levelname)s : %(name)s : %(funcName)s[line %(lineno)s]" + + ": %(message)s" + ) + + # create logger instance + log = logging.getLogger(__name__) + + # set log level + log.setLevel(level) + + # format the .log file messages + if file is not None: + file_handler = logging.FileHandler(file) + formatter = logging.Formatter( + "%(asctime)s : %(levelname)s : Module %(module)s" + + ": %(funcName)s[line %(lineno)s] : %(name)s : %(message)s" + ) + file_handler.setFormatter(formatter) + log.addHandler(file_handler) + + return log + + +class Commands: + def __init__(self, logger) -> None: + self.logger = logger + return + + @staticmethod + def random_string(len): + random_string = "" + for ii in range(len): + random_int = random.randint(33, 126) + random_string += chr(random_int) + return random_string + + @staticmethod + def test_vector_gen(args): + vec_len = args[0] + vec_num = args[1] + test_list = [""] * vec_num + for ii in range(vec_num): + test_list[ii] = Commands.random_string(vec_len) + return test_list + + +class GI: + @staticmethod + def Encrypt(task_id, str_list, client): +# logger = Logger.init(LOG_FILE, logging.DEBUG) + ii = 1 + for vector in str_list: + msg = client.string_to_json("ENCRYPT", str(task_id), "GI", "0", len(str_list), ii, str(sys.getsizeof(str_list)), vector) + client.to_outgoing_queue(msg) + client.connection_send() + ii += 1 + return + + @staticmethod + def ReceivedEncrypt(task_id, client): + ii = 1 + cipher_list = [] + while True: + print("ReceivedEncrypt") + client.connection_recv() + data = client.print_outgoing_queue() + print("data: {}".format(data)) + recv_msg = data.decode('ascii') +# cipher_list.append(recv_msg.decode('ascii')) +# print("\nWe got " + cipher_list[ii] + '\n') + decoded_msg = json.loads(recv_msg) + print(decoded_msg) + if decoded_msg.get('payload_total_fragments') != decoded_msg.get('payload_frag_number'): + print("What up") + ii += 1 + + return cipher_list + + @staticmethod + def Decrypt(task,): + ii = 1 + for vector in str_list: + msg = client.string_to_json("DECRYPT", str(task_id), "GI", "0", len(str_list), ii, str(sys.getsizeof(str_list)), vector) + client.to_outgoing_queue(msg) + client.connection_send() + ii += 1 + return + + @staticmethod + def ReceivedDecrypt(): + return + diff --git a/docs/flask/README.md b/docs/flask/README.md new file mode 100644 index 0000000..9cc20f8 --- /dev/null +++ b/docs/flask/README.md @@ -0,0 +1,13 @@ +## Setup to run Flask + +1. In flask directory, create and run virtual enviroment by running these commands +``` +python3 -m venv venv +. venv/bin/activate +``` + +2. Install Flask using command `pip install Flask` other dependiences may be required +3. Set Flask application with command `export FLASK_APP=app` +4. Then set mode of operation to either `export FLASK_ENV=development` or `export FLASK_ENV=production` +5. Run website with command `./app.py` and the website should be at address `http://127.0.0.1:4996/` loaded on the home page + diff --git a/docs/flask/app.py b/docs/flask/app.py new file mode 100755 index 0000000..d082ac3 --- /dev/null +++ b/docs/flask/app.py @@ -0,0 +1,115 @@ +#! /usr/bin/env python3 + +import os +from flask import Flask, flash, render_template, request, redirect, session +from werkzeug.utils import secure_filename +from Client import * +from GI_interface import * + +app = Flask(__name__) +algos = ['AES', 'QPP', 'AES & QPP'] +tests = ['Encryption', 'Decryption', + 'Encrypt & Decrypt', 'M.I.T.M', + 'Brute Force', 'Monte Carlo', + 'Multi-block Message', 'Known Answer' + ] +DOWNLOAD_FOLDER = "downloads/" +UPLOAD_FOLDER = "uploads/" +ALLOWED_EXTENSIONS = {'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'} +GI_PORT = 64999 + +vector_select = "True" +vector_len = 100 +vector_num = 100 + +client = Client(GI_PORT, "", "CLI") +client.connection_setup() +task_id = 0 + + +def allowed_file(filename): + return '.' in filename and \ + filename.split('.', 1)[1].lower() in ALLOWED_EXTENSIONS + + +@app.route("/") +@app.route("/index/") +def index(): + return render_template('index.html') + + +@app.route("/api/") +def api(): + return render_template('api.html') + + +@app.route("/research/") +def research(): + return render_template('research.html') + + +@app.route("/demo/") +def demo(): + return render_template('demo.html') + + +@app.route("/calc/", methods=['GET', 'POST']) +def calc(): + global vector_select, algos, tests, vector_num, vector_len + if request.method == "POST": + print("ERROR 1") + vector_select = request.form['vector_select'] + + return render_template('calc.html', + algo_list=algos, + test_list=tests, + vector_select=vector_select) + + +@app.route("/view/", methods=['GET', 'POST']) +def view(): + global vector_select, algos, tests, vector_num, vector_len, task_id, client + test = algo = "ERROR" + test_vector = [] + + if request.method == 'POST': + algo_select = request.form.get('algo_select') + test_select = request.form.get('test_select') + print("ERROR 2") + print("{}, {}".format(algo_select, test_select)) + if vector_select == "True": + print("ERROR 3") + vector_len = request.form["vector_len"] + vector_num = request.form["vector_num"] + print("{} vectors of size {}".format(vector_num, vector_len)) + test_vector = Commands.test_vector_gen([int(vector_len), int(vector_num)]) + else: + print("ERROR 4") + if 'file' not in request.files: + flash("No File Uploaded") + file = request.files['file'] + if file.filename == '': + flash("No File Selected") + if file and allowed_file(file.filename): + filename = secure_filename(file.filename) + file.save(os.path.join(app.config['DOWNLOAD_FOLDER'], filename)) + with open(os.path.abspath(DOWNLOAD_FOLDER + filename), 'r') as fd: + file_contents = fd.read() + print("{}, contents:\n{}".format(filename, file_contents)) + test_vector = [file_contents] + GI.Encrypt(task_id, test_vector, client) + print("Going into Recieve") + client.to_outgoing_queue("Please Help") + client.connection_send() + cipherlist = GI.ReceivedEncrypt(task_id, client) + print("cipherlist: \n" + cipherlist) + task_id += 1 + + return render_template('view.html', test=test_select, algo=algo_select) + + +if __name__ == "__main__": + app.config['DOWNLOAD_FOLDER'] = os.path.abspath(DOWNLOAD_FOLDER) + app.secret_key = 'secret_key' + app.config['SESSION_TYPE'] = 'filesystem' + app.run(debug=True, host="127.0.0.1", port=4996) diff --git a/docs/flask/static/scripts/calc.js b/docs/flask/static/scripts/calc.js new file mode 100644 index 0000000..bf5edb2 --- /dev/null +++ b/docs/flask/static/scripts/calc.js @@ -0,0 +1,13 @@ +var vector_radio = document.getElementById('vector_selected'); +var file_radio = document.getElementById('file_selected'); + + +if(vector_radio.checked) { + document.getElementById('testing').innerHTML = "What up"; + document.getElementById("vector_options").style.display = "block"; + document.getElementById("file_options").style.display = "none"; +} else if(file_radio.checked) { + document.getElementById('testing').innerHTML = "Guy"; + document.getElementById("vector_options").style.block = "none"; + document.getElementById("file_options").style.block = "block"; +} diff --git a/docs/flask/templates/calc.html b/docs/flask/templates/calc.html new file mode 100644 index 0000000..e9b7cad --- /dev/null +++ b/docs/flask/templates/calc.html @@ -0,0 +1,66 @@ + +{% extends 'base.html' %} + +{% block title %} QPP Request {% endblock %} +{% block header %} Interactive Testbed {% endblock %} + +{% block header_content %} +

    This is the request page where users can communicate with the CoreComplex + using a interactive and attractive website +

    +{% endblock %} + +{% block content %} + +
    +

    Current Input Type: + + +

    +
    +
    + + +
    + + +
    + +
    + + +
    + {% if vector_select == "True" %} +
    +
    + + +
    + +
    + + +
    +
    + {% else %} +
    + File Upload +
    + {% endif %} + +
    + +{% endblock %} diff --git a/docs/flask/templates/view.html b/docs/flask/templates/view.html new file mode 100644 index 0000000..7c4f37c --- /dev/null +++ b/docs/flask/templates/view.html @@ -0,0 +1,23 @@ +{% extends 'base.html' %} + +{% block title %} QPP Results {% endblock %} +{% block header %} Results {% endblock %} + +{% block header_content %} +

    Here are the final results

    +{% endblock %} + + +{% block content %} + +
    + +

    + CipherText

    +

    Test: {{ test }}

    +

    Algorithim: {{ algo }}

    +

    Time: {{ time }}

    + +
    + +{% endblock %} From bc9564fed402bf6399d97f1e398b6e3f9c1a276e Mon Sep 17 00:00:00 2001 From: Ahmed Refik Date: Wed, 5 Apr 2023 17:25:56 -0600 Subject: [PATCH 13/46] [MP-144] index, base and style were removed for some reason? --- .gitignore | 1 - docs/flask/static/style.css | 302 ++++++++++++++++++++++++++++++++ docs/flask/templates/base.html | 41 +++++ docs/flask/templates/index.html | 55 ++++++ 4 files changed, 398 insertions(+), 1 deletion(-) create mode 100644 docs/flask/static/style.css create mode 100644 docs/flask/templates/base.html create mode 100644 docs/flask/templates/index.html diff --git a/.gitignore b/.gitignore index 389adc6..0c23680 100644 --- a/.gitignore +++ b/.gitignore @@ -25,4 +25,3 @@ # pycache for CLI /CLI/__pycache__/ -======= diff --git a/docs/flask/static/style.css b/docs/flask/static/style.css new file mode 100644 index 0000000..efaac31 --- /dev/null +++ b/docs/flask/static/style.css @@ -0,0 +1,302 @@ +:root { + --vt-font-family-base: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", + Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", + "Helvetica Neue", sans-serif; + --vt-font-family-mono: Menlo, Monaco, Consolas, "Courier New", monospace; +} + +:root { + --vt-c-white: #ffffff; + --vt-c-white-soft: #f9f9f9; + --vt-c-white-mute: #f1f1f1; + --vt-c-black: #1a1a1a; + --vt-c-black-pure: #000000; + --vt-c-black-soft: #242424; + --vt-c-black-mute: #2f2f2f; + --vt-c-indigo: #213547; + --vt-c-indigo-soft: #476582; + --vt-c-indigo-light: #aac8e4; + --vt-c-gray: #8e8e8e; + --vt-c-gray-light-1: #aeaeae; + --vt-c-gray-light-2: #c7c7c7; + --vt-c-gray-light-3: #d1d1d1; + --vt-c-gray-light-4: #e5e5e5; + --vt-c-gray-light-5: #f2f2f2; + --vt-c-gray-dark-1: #636363; + --vt-c-gray-dark-2: #484848; + --vt-c-gray-dark-3: #3a3a3a; + --vt-c-gray-dark-4: #282828; + --vt-c-gray-dark-5: #202020; + --vt-c-divider-light-1: rgba(60, 60, 60, 0.29); + --vt-c-divider-light-2: rgba(60, 60, 60, 0.12); + --vt-c-divider-dark-1: rgba(84, 84, 84, 0.65); + --vt-c-divider-dark-2: rgba(84, 84, 84, 0.48); + --vt-c-text-light-1: var(--vt-c-indigo); + --vt-c-text-light-2: rgba(60, 60, 60, 0.7); + --vt-c-text-light-3: rgba( + 60, + 60, + 60, + .--vt-font-family-base: Inter, + -apple-system, + BlinkMacSystemFont, + "Segoe UI", + Roboto, + Oxygen, + Ubuntu, + Cantarell, + "Fira Sans", + "Droid Sans", + "Helvetica Neue", + sans-serif; --vt-font-family-mono: Menlo, + Monaco, + Consolas, + "Courier New", + monospace;33 + ); + --vt-c-text-light-4: rgba(60, 60, 60, 0.18); + --vt-c-text-light-code: var(--vt-c-indigo-soft); + --vt-c-text-dark-1: rgba(255, 255, 255, 0.87); + --vt-c-text-dark-2: rgba(235, 235, 235, 0.6); + --vt-c-text-dark-3: rgba(235, 235, 235, 0.38); + --vt-c-text-dark-4: rgba(235, 235, 235, 0.18); + --vt-c-text-dark-code: var(--vt-c-indigo-light); + --vt-c-green: #42b883; + --vt-c-green-light: #42d392; + --vt-c-green-lighter: #35eb9a; + --vt-c-green-dark: #33a06f; + --vt-c-green-darker: #155f3e; + --vt-c-blue: #3b8eed; + --vt-c-blue-light: #549ced; + --vt-c-blue-lighter: #50a2ff; + --vt-c-blue-dark: #3468a3; + --vt-c-blue-darker: #255489; + --vt-c-yellow: #ffc517; + --vt-c-yellow-light: #ffe417; + --vt-c-yellow-lighter: #ffff17; + --vt-c-yellow-dark: #e0ad15; + --vt-c-yellow-darker: #bc9112; + --vt-c-red: #ed3c50; + --vt-c-red-light: #f43771; + --vt-c-red-lighter: #fd1d7c; + --vt-c-red-dark: #cd2d3f; + --vt-c-red-darker: #ab2131; + --vt-c-purple: #de41e0; + --vt-c-purple-light: #e936eb; + --vt-c-purple-lighter: #f616f8; + --vt-c-purple-dark: #823c83; + --vt-c-purple-darker: #602960; + --header-purple: #4c0ca0; + --header-green: #053f05; + --text-header: #ff88ff; +} + + +html { + word-wrap: break-word; +} + + +* { + box-sizing: border-box; +} + +nav { + position: fixed; + left: 0%; + right: 0%; + top: 0%; + bottom: auto; + height: 70px; + background-color: #ffffff; + display: flex; + flex-direction: row; + justify-content: flex-end; + flex-grow: 1; + min-width: inherit; + border-bottom: 1px solid #000; + /* border-bottom: 1px dotted hsl(0deg 0% 0% / 0.2); */ +} + +a { + text-decoration: none; + color: inherit; +} + +body { + margin: 0px; + font-family: var(--vt-font-family-base); + letter-spacing: 0.2px; +<<<<<<< HEAD +======= + line-height: 24px; +>>>>>>> 8cf1338 (renamed UI folder to docs for github pages) + font-size: 16px; + font-weight: 400; + direction: ltr; + font-synthesis: none; + text-rendering: optimizeLegibility; + display: flex; + flex-direction: column; +} + +.button { + transition: all 300ms ease; + background-color: #000000; + padding: 9px 28px 10px; + align-items: center; + border-radius: 20px; + max-width: 100%; + color: #ffffff; +} + +.header-list { + text-transform: capitalize; + display: flex; + color: inherit; + font-weight: bold; + justify-content: flex-end; + align-items: center; + list-style-type: none; + flex-grow: 1; + gap: 12px; + padding-right: 40px; +} + +.header-logo { + font-size: 25px; + flex-grow: 1; + margin-right: auto; +} + +header { + margin-top: 70px; + border: 0px; + padding: 30px; + min-height: 300px; + display: flex; + flex-direction: column; + align-items: stretch; + flex: 1; + border-bottom: #000 solid 1px; +} + +header div { + display: flex; + flex: 1; +} + +#hero { + align-items: center; + flex-wrap: wrap; + gap: 10px; +} + +.col { + padding: 30px; + border-bottom: 1px solid #000; +} + +.hero-text { + flex-direction: column; + align-self: center; + flex: 1; +} + +h1 { + font-size: 5vw; +} + +p { + font-size: 18px; +} + +.hero-image { + aspect-ratio: 4/3; + max-width: 200px; + width: 100%; + text-align: center; + display: flex; + flex-direction: column; + justify-content: center; + border: 1px solid #000; + flex: 1; + max-width: 100%; +} + +.hero-text button { + align-self: flex-start; +} + +.article-1 { + padding: 30px; + color: #000; + background-color: rgb(78, 226, 65); +} + +.article-2 { + background-color: rgb(233, 247, 47); + border-right: #000 solid 1px; +} + +.article-3 { + background-color: #ff48d7; +} + +.article-interim { + border-bottom: #fff 1px solid; + background-color: #000; + color: #fff; + display: flex; + flex-direction: column; + align-items: center; +} + +.side-view { + flex: 1; + display: flex; +} + +/**************************************************/ +div.hero-text h1 { + font-size: 45px; +} + +.api-section-body { + display: flex; + flex-direction: row; + gap: 10px; + justify-content: center; +} + +.api-section h1 { + font-size: 30px; +} + +.api-group h1 { + font-size: 25px; + min-width: 400px; +} + +.api-filter { + gap: 10px; + justify-content: center; + align-items: baseline; + flex: 1; +} + +#api-filter { + padding: 5px 5px 5px; + border-radius: 10px; + border-width: 1px solid #000; +} + +.api-section { + padding: 20px; +} + +.api-group { + background-color: #DDD; + padding: 10px; + border-radius: 10px; +} diff --git a/docs/flask/templates/base.html b/docs/flask/templates/base.html new file mode 100644 index 0000000..943ef26 --- /dev/null +++ b/docs/flask/templates/base.html @@ -0,0 +1,41 @@ + + + + + + + + {% block title %} {% endblock %} + + +
    + + + +
    +
    +

    {% block header %} {% endblock %}

    + {% block header_content %} {% endblock %} +
    +
    + {% block content %} {% endblock %} + + diff --git a/docs/flask/templates/index.html b/docs/flask/templates/index.html new file mode 100644 index 0000000..ea9e5b4 --- /dev/null +++ b/docs/flask/templates/index.html @@ -0,0 +1,55 @@ +{% extends 'base.html' %} + +{% block title %} QPP Index {% endblock %} +{% block header %} QPP Testbed {% endblock %} + +{% block header_content %} +

    This is the description for this page

    +{% endblock %} + + +{% block content %} +
    +

    Some random thingy

    +

    + Phasellus convallis elit id ullamcorper pulvinar. Duis aliquam turpis + mauris, eu ultricies erat malesuada quis. Aliquam dapibus, lacus eget + hendrerit bibendum, urna est aliquam sem, sit amet imperdiet est velit + quis lorem. +

    +
    +
    +
    +

    Some random thingy

    +

    + Phasellus convallis elit id ullamcorper pulvinar. Duis aliquam turpis + mauris, eu ultricies erat malesuada quis. Aliquam dapibus, lacus eget + hendrerit bibendum, urna est aliquam sem, sit amet imperdiet est velit + quis lorem. +

    +
    + +
    +

    Some random thingy

    +

    + Phasellus convallis elit id ullamcorper pulvinar. Duis aliquam turpis + mauris, eu ultricies erat malesuada quis. Aliquam dapibus, lacus eget + hendrerit bibendum, urna est aliquam sem, sit amet imperdiet est velit + quis lorem. +

    +
    +
    +
    +

    HHHHHHHHHHHHHHH

    +

    oasnfoiasjfniuasjfiasfiuhsafiajsfiusafojsaoijfniosanfo

    +
    + +
    +

    HSOPCMAOISCM

    +

    + Moloch the incomprehensible prison! Moloch the crossbone soulless + jailhouse and Congress of sorrows! Moloch whose buildings are judgment! + Moloch the vast stone of war! Moloch the stunned governments! +

    +
    +{% endblock %} From 05e68c126d4f9ec09dbd72b3159b8e663f12904d Mon Sep 17 00:00:00 2001 From: Ahmed Refik Date: Wed, 5 Apr 2023 18:11:23 -0600 Subject: [PATCH 14/46] [MP-144] website sockets work and Encrypt works, file contents seperated by line --- .gitignore | 1 + docs/flask/Client.py | 5 +++-- docs/flask/app.py | 19 ++++++++++++------- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index 0c23680..1808456 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ /docs/flask/venv/ /docs/flask/__pycache__/ /docs/flask/downloads/ +/docs/flask/uploads/ # ccls language server cache /.ccls-cache/ diff --git a/docs/flask/Client.py b/docs/flask/Client.py index 30fb0cf..c67329b 100644 --- a/docs/flask/Client.py +++ b/docs/flask/Client.py @@ -26,10 +26,11 @@ def __init__(self, port, host, name): # self.outgoing_mutex = Lock() def connection_setup(self): + print("socket:{}\n host:{}\n port:{}\n".format(self.s, self.host, self.port)) self.s.connect((self.host, self.port)) # SEND the handshake - self.to_outgoing_queue(str(self.port)) - self.connection_send() + # self.to_outgoing_queue(str(self.port)) + # self.connection_send() def connection_send(self): message = self.send_queue.get() diff --git a/docs/flask/app.py b/docs/flask/app.py index d082ac3..74adb9c 100755 --- a/docs/flask/app.py +++ b/docs/flask/app.py @@ -22,8 +22,8 @@ vector_len = 100 vector_num = 100 -client = Client(GI_PORT, "", "CLI") -client.connection_setup() +client = Client(GI_PORT, socket.gethostname(), "CLI") + task_id = 0 @@ -94,12 +94,15 @@ def view(): filename = secure_filename(file.filename) file.save(os.path.join(app.config['DOWNLOAD_FOLDER'], filename)) with open(os.path.abspath(DOWNLOAD_FOLDER + filename), 'r') as fd: - file_contents = fd.read() - print("{}, contents:\n{}".format(filename, file_contents)) - test_vector = [file_contents] + while True: + line = fd.readline() + if not line: + break + test_vector.append(line) + print("{}, contents:\n{}".format(filename, test_vector)) GI.Encrypt(task_id, test_vector, client) print("Going into Recieve") - client.to_outgoing_queue("Please Help") + client.to_outgoing_queue("Please Help\n") client.connection_send() cipherlist = GI.ReceivedEncrypt(task_id, client) print("cipherlist: \n" + cipherlist) @@ -112,4 +115,6 @@ def view(): app.config['DOWNLOAD_FOLDER'] = os.path.abspath(DOWNLOAD_FOLDER) app.secret_key = 'secret_key' app.config['SESSION_TYPE'] = 'filesystem' - app.run(debug=True, host="127.0.0.1", port=4996) + client.connection_setup() + app.run(debug=False, host=socket.gethostname(), port=4996) + client.s.close() From a44f090ee87118218eed5ad42a24d25a2861b5b6 Mon Sep 17 00:00:00 2001 From: Ahmed Refik Date: Wed, 5 Apr 2023 18:40:20 -0600 Subject: [PATCH 15/46] [MP-144] Encryt communications work with files and vectors --- docs/flask/Client.py | 2 +- docs/flask/GI_interface.py | 13 +++++-------- docs/flask/README.md | 2 +- docs/flask/app.py | 11 +++++++---- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/flask/Client.py b/docs/flask/Client.py index c67329b..8545188 100644 --- a/docs/flask/Client.py +++ b/docs/flask/Client.py @@ -48,7 +48,7 @@ def to_outgoing_queue(self, message): def connection_recv(self): - msg = self.s.recv(1) + msg = self.s.recv(4096) self.recv_queue.put(msg) print("Received from server: " + msg.decode('ascii')) diff --git a/docs/flask/GI_interface.py b/docs/flask/GI_interface.py index eabd89f..0bda693 100644 --- a/docs/flask/GI_interface.py +++ b/docs/flask/GI_interface.py @@ -74,20 +74,17 @@ def ReceivedEncrypt(task_id, client): ii = 1 cipher_list = [] while True: - print("ReceivedEncrypt") client.connection_recv() data = client.print_outgoing_queue() - print("data: {}".format(data)) recv_msg = data.decode('ascii') -# cipher_list.append(recv_msg.decode('ascii')) -# print("\nWe got " + cipher_list[ii] + '\n') decoded_msg = json.loads(recv_msg) print(decoded_msg) - if decoded_msg.get('payload_total_fragments') != decoded_msg.get('payload_frag_number'): - print("What up") - ii += 1 + cipher_list.append(decoded_msg.get('payload_content')) - return cipher_list + if decoded_msg.get('payload_total_fragments') == decoded_msg.get('payload_fragment_number'): + return cipher_list + + ii += 1 @staticmethod def Decrypt(task,): diff --git a/docs/flask/README.md b/docs/flask/README.md index 9cc20f8..b475e53 100644 --- a/docs/flask/README.md +++ b/docs/flask/README.md @@ -9,5 +9,5 @@ python3 -m venv venv 2. Install Flask using command `pip install Flask` other dependiences may be required 3. Set Flask application with command `export FLASK_APP=app` 4. Then set mode of operation to either `export FLASK_ENV=development` or `export FLASK_ENV=production` -5. Run website with command `./app.py` and the website should be at address `http://127.0.0.1:4996/` loaded on the home page +5. Run website with command `./app.py` and the website should be at address `http://{hostname}:4996/` loaded on the home page diff --git a/docs/flask/app.py b/docs/flask/app.py index 74adb9c..65632ac 100755 --- a/docs/flask/app.py +++ b/docs/flask/app.py @@ -101,11 +101,14 @@ def view(): test_vector.append(line) print("{}, contents:\n{}".format(filename, test_vector)) GI.Encrypt(task_id, test_vector, client) - print("Going into Recieve") - client.to_outgoing_queue("Please Help\n") - client.connection_send() cipherlist = GI.ReceivedEncrypt(task_id, client) - print("cipherlist: \n" + cipherlist) + print("cipherlist".center(40, '_')) + if not vector_select: + '\n'.join(cipherlist) + print(cipherlist) + else: + for vec in cipherlist: + print(vec) task_id += 1 return render_template('view.html', test=test_select, algo=algo_select) From 11f16db86b32621537c2a59c3080b9d8edd31c96 Mon Sep 17 00:00:00 2001 From: Ahmed Refik Date: Wed, 5 Apr 2023 19:02:45 -0600 Subject: [PATCH 16/46] [MP-144] trying out multithreading --- docs/flask/GI_interface.py | 19 ++++++++++++++----- docs/flask/app.py | 33 ++++++++++++++++++++++++--------- 2 files changed, 38 insertions(+), 14 deletions(-) diff --git a/docs/flask/GI_interface.py b/docs/flask/GI_interface.py index 0bda693..17a015c 100644 --- a/docs/flask/GI_interface.py +++ b/docs/flask/GI_interface.py @@ -78,16 +78,14 @@ def ReceivedEncrypt(task_id, client): data = client.print_outgoing_queue() recv_msg = data.decode('ascii') decoded_msg = json.loads(recv_msg) - print(decoded_msg) cipher_list.append(decoded_msg.get('payload_content')) if decoded_msg.get('payload_total_fragments') == decoded_msg.get('payload_fragment_number'): return cipher_list - ii += 1 @staticmethod - def Decrypt(task,): + def Decrypt(task_id, str_list, client): ii = 1 for vector in str_list: msg = client.string_to_json("DECRYPT", str(task_id), "GI", "0", len(str_list), ii, str(sys.getsizeof(str_list)), vector) @@ -97,6 +95,17 @@ def Decrypt(task,): return @staticmethod - def ReceivedDecrypt(): - return + def ReceivedDecrypt(task_id, client): + ii = 1 + cipher_list = [] + while True: + client.connection_recv() + data = client.print_outgoing_queue() + recv_msg = data.decode('ascii') + decoded_msg = json.loads(recv_msg) + cipher_list.append(decoded_msg.get('payload_content')) + + if decoded_msg.get('payload_total_fragments') == decoded_msg.get('payload_fragment_number'): + return cipher_list + ii += 1 diff --git a/docs/flask/app.py b/docs/flask/app.py index 65632ac..604eb9b 100755 --- a/docs/flask/app.py +++ b/docs/flask/app.py @@ -1,6 +1,10 @@ #! /usr/bin/env python3 import os +import subprocess +import time +import threading +import socket from flask import Flask, flash, render_template, request, redirect, session from werkzeug.utils import secure_filename from Client import * @@ -103,12 +107,8 @@ def view(): GI.Encrypt(task_id, test_vector, client) cipherlist = GI.ReceivedEncrypt(task_id, client) print("cipherlist".center(40, '_')) - if not vector_select: - '\n'.join(cipherlist) - print(cipherlist) - else: - for vec in cipherlist: - print(vec) + '\n'.join(cipherlist) + print(cipherlist) task_id += 1 return render_template('view.html', test=test_select, algo=algo_select) @@ -118,6 +118,21 @@ def view(): app.config['DOWNLOAD_FOLDER'] = os.path.abspath(DOWNLOAD_FOLDER) app.secret_key = 'secret_key' app.config['SESSION_TYPE'] = 'filesystem' - client.connection_setup() - app.run(debug=False, host=socket.gethostname(), port=4996) - client.s.close() + try: + client.connection_setup() + except: + command = "nc -l " + str(GI_PORT) + " &" + subprocess.call(command, shell=True) + time.sleep(2) + client.connection_setup() + finally: + incoming_t = threading.Thread(target=client.connection_recv, args=()) + outgoing_t = threading.Thread(target=client.connection_send, args=()) + incoming_t.start() + outgoing_t.start() + + app.run(debug=False, host=socket.gethostname(), port=4996) + + incoming_t.join() + outgoing_t.join() + client.s.close() From 07e33f984e5357e9ff9b51cfceeaab3eeb65979d Mon Sep 17 00:00:00 2001 From: Ahmed Refik Date: Wed, 5 Apr 2023 19:36:41 -0600 Subject: [PATCH 17/46] [MP-144] multithreading works --- docs/flask/Client.py | 42 +++++++++++++++----------------------- docs/flask/GI_interface.py | 16 +++++++-------- 2 files changed, 24 insertions(+), 34 deletions(-) diff --git a/docs/flask/Client.py b/docs/flask/Client.py index 8545188..89f30ee 100644 --- a/docs/flask/Client.py +++ b/docs/flask/Client.py @@ -33,37 +33,27 @@ def connection_setup(self): # self.connection_send() def connection_send(self): - message = self.send_queue.get() - print(message) - self.s.send(message.encode('ascii')) - print("sent to port {}".format(self.port)) - - - def to_outgoing_queue(self, message): - # self.outgoing_mutex.acquire() + while True: + message = self.send_queue.get() + print(message) + self.s.send(message.encode('ascii')) + print("sent to port {}".format(self.port)) + + def send_to_queue(self, message): self.send_queue.put(message) print("OUTGOING QUEUE {}".format(message)) - # self.outgoing_mutex.release() - - def connection_recv(self): - msg = self.s.recv(4096) - self.recv_queue.put(msg) - print("Received from server: " + msg.decode('ascii')) - - def print_outgoing_queue(self): - # self.mutex.acquire() - if not self.recv_queue.empty(): - data = self.recv_queue.get() - # self.mutex.release() - return data - - else: - # self.mutex.release() - return None - + while True: + msg = self.s.recv(4096) + self.recv_queue.put(msg) + print("Received from server: " + msg.decode('ascii')) + def get_queue(self): + while True: + if not self.recv_queue.empty(): + data = self.recv_queue.get() + return data @staticmethod def string_to_json(api_call, task_id, interface_type="GC", sender_id="0", payload_total_fragments="0", payload_fragment_number="0", diff --git a/docs/flask/GI_interface.py b/docs/flask/GI_interface.py index 17a015c..99ef4ee 100644 --- a/docs/flask/GI_interface.py +++ b/docs/flask/GI_interface.py @@ -3,6 +3,7 @@ import logging import sys import json +import time LOG_FILE = "out.log" @@ -64,8 +65,7 @@ def Encrypt(task_id, str_list, client): ii = 1 for vector in str_list: msg = client.string_to_json("ENCRYPT", str(task_id), "GI", "0", len(str_list), ii, str(sys.getsizeof(str_list)), vector) - client.to_outgoing_queue(msg) - client.connection_send() + client.send_to_queue(msg) ii += 1 return @@ -74,8 +74,7 @@ def ReceivedEncrypt(task_id, client): ii = 1 cipher_list = [] while True: - client.connection_recv() - data = client.print_outgoing_queue() + data = client.get_queue() recv_msg = data.decode('ascii') decoded_msg = json.loads(recv_msg) cipher_list.append(decoded_msg.get('payload_content')) @@ -89,8 +88,7 @@ def Decrypt(task_id, str_list, client): ii = 1 for vector in str_list: msg = client.string_to_json("DECRYPT", str(task_id), "GI", "0", len(str_list), ii, str(sys.getsizeof(str_list)), vector) - client.to_outgoing_queue(msg) - client.connection_send() + client.send_to_queue(msg) ii += 1 return @@ -98,9 +96,11 @@ def Decrypt(task_id, str_list, client): def ReceivedDecrypt(task_id, client): ii = 1 cipher_list = [] + data = None while True: - client.connection_recv() - data = client.print_outgoing_queue() + while data != None: + data = client.get_queue() + print("OUT THE WHILE") recv_msg = data.decode('ascii') decoded_msg = json.loads(recv_msg) cipher_list.append(decoded_msg.get('payload_content')) From 521a83c086f05b58867f0f2587f0b8ca41e66558 Mon Sep 17 00:00:00 2001 From: Ahmed Refik Date: Wed, 5 Apr 2023 21:54:18 -0600 Subject: [PATCH 18/46] [MP-144] end-to-end communication works on website, large amount of data gets corrupted --- docs/flask/Client.py | 44 +++++++++++++++++++------ docs/flask/GI_interface.py | 6 ++-- docs/flask/Makefile | 11 +++++++ docs/flask/README.md | 2 ++ docs/flask/app.py | 60 ++++++++++++++++++++++------------ docs/flask/server.py | 34 +++++++++++++++++++ docs/flask/templates/view.html | 14 +++++--- 7 files changed, 134 insertions(+), 37 deletions(-) create mode 100644 docs/flask/Makefile create mode 100755 docs/flask/server.py diff --git a/docs/flask/Client.py b/docs/flask/Client.py index 89f30ee..5779135 100644 --- a/docs/flask/Client.py +++ b/docs/flask/Client.py @@ -4,6 +4,9 @@ import threading from threading import Lock import time + +PAYLOAD = 4096 + """ Steps: Get the user interaction @@ -22,38 +25,59 @@ def __init__(self, port, host, name): self.name = name self.recv_queue = queue.Queue() self.send_queue = queue.Queue() - # self.mutex = Lock() - # self.outgoing_mutex = Lock() + self.mutex = Lock() + self.outgoing_mutex = Lock() def connection_setup(self): - print("socket:{}\n host:{}\n port:{}\n".format(self.s, self.host, self.port)) self.s.connect((self.host, self.port)) + print("socket:{}\n host:{}\n port:{}\n".format(self.s, self.host, self.port)) # SEND the handshake # self.to_outgoing_queue(str(self.port)) # self.connection_send() def connection_send(self): while True: - message = self.send_queue.get() - print(message) - self.s.send(message.encode('ascii')) - print("sent to port {}".format(self.port)) + if not self.send_queue.empty(): + self.outgoing_mutex.acquire() + message = self.send_queue.get() + self.outgoing_mutex.release() + # print(message) + self.s.send(message.encode('ascii')) + # print("sent to port {}".format(self.port)) def send_to_queue(self, message): + self.outgoing_mutex.acquire() self.send_queue.put(message) - print("OUTGOING QUEUE {}".format(message)) + self.outgoing_mutex.release() + # print("OUTGOING QUEUE {}".format(message)) def connection_recv(self): while True: - msg = self.s.recv(4096) + msg = self.s.recv(PAYLOAD) + self.mutex.acquire() self.recv_queue.put(msg) - print("Received from server: " + msg.decode('ascii')) + self.mutex.release() + # print("Received from server: " + msg.decode('ascii')) def get_queue(self): while True: if not self.recv_queue.empty(): + self.mutex.acquire() data = self.recv_queue.get() + self.mutex.release() return data + + def clear_queue(self): + self.mutex.acquire() + self.outgoing_mutex.acquire() + + self.send_queue.queue.clear() + self.recv_queue.queue.clear() + + self.mutex.release() + self.outgoing_mutex.release() + return + @staticmethod def string_to_json(api_call, task_id, interface_type="GC", sender_id="0", payload_total_fragments="0", payload_fragment_number="0", diff --git a/docs/flask/GI_interface.py b/docs/flask/GI_interface.py index 99ef4ee..dc360e1 100644 --- a/docs/flask/GI_interface.py +++ b/docs/flask/GI_interface.py @@ -76,7 +76,10 @@ def ReceivedEncrypt(task_id, client): while True: data = client.get_queue() recv_msg = data.decode('ascii') - decoded_msg = json.loads(recv_msg) + try: + decoded_msg = json.loads(recv_msg) + except json.decoder.JSONDecodeError: + print('\n' + recv_msg + '\n') cipher_list.append(decoded_msg.get('payload_content')) if decoded_msg.get('payload_total_fragments') == decoded_msg.get('payload_fragment_number'): @@ -100,7 +103,6 @@ def ReceivedDecrypt(task_id, client): while True: while data != None: data = client.get_queue() - print("OUT THE WHILE") recv_msg = data.decode('ascii') decoded_msg = json.loads(recv_msg) cipher_list.append(decoded_msg.get('payload_content')) diff --git a/docs/flask/Makefile b/docs/flask/Makefile new file mode 100644 index 0000000..10df563 --- /dev/null +++ b/docs/flask/Makefile @@ -0,0 +1,11 @@ +CC=@ +PORT=64999 + +all: + $(CC) ./server.py $(PORT) & + $(CC) ./app.py $(PORT) + +# This is to remove any processes on the port +clean: + lsof | grep $(PORT) | awk '{print $$2}' | head -n 1 | xargs -I {} kill -9 {} + rm uploads/*.txt -r diff --git a/docs/flask/README.md b/docs/flask/README.md index b475e53..88432ab 100644 --- a/docs/flask/README.md +++ b/docs/flask/README.md @@ -10,4 +10,6 @@ python3 -m venv venv 3. Set Flask application with command `export FLASK_APP=app` 4. Then set mode of operation to either `export FLASK_ENV=development` or `export FLASK_ENV=production` 5. Run website with command `./app.py` and the website should be at address `http://{hostname}:4996/` loaded on the home page +6. There is a sample server.py program which will send whatever it receives from the app `./server.py $(port)` +7. You can kill any running processes on the port by running the command `lsof | grep $(port) | awk '{print $2}' | xargs -I {} kill -9 {}` diff --git a/docs/flask/app.py b/docs/flask/app.py index 604eb9b..8b18b0e 100755 --- a/docs/flask/app.py +++ b/docs/flask/app.py @@ -1,18 +1,20 @@ #! /usr/bin/env python3 +import sys import os import subprocess import time import threading import socket -from flask import Flask, flash, render_template, request, redirect, session +import logging +from flask import Flask, flash, render_template, request, redirect, send_file from werkzeug.utils import secure_filename from Client import * from GI_interface import * app = Flask(__name__) algos = ['AES', 'QPP', 'AES & QPP'] -tests = ['Encryption', 'Decryption', +tests = ['Encrypt', 'Decrypt', 'Encrypt & Decrypt', 'M.I.T.M', 'Brute Force', 'Monte Carlo', 'Multi-block Message', 'Known Answer' @@ -26,14 +28,14 @@ vector_len = 100 vector_num = 100 +logger = Logger.init(None, logging.ERROR) client = Client(GI_PORT, socket.gethostname(), "CLI") task_id = 0 def allowed_file(filename): - return '.' in filename and \ - filename.split('.', 1)[1].lower() in ALLOWED_EXTENSIONS + return '.' in filename and filename.split('.', 1)[1].lower() #in ALLOWED_EXTENSIONS @app.route("/") @@ -61,7 +63,6 @@ def demo(): def calc(): global vector_select, algos, tests, vector_num, vector_len if request.method == "POST": - print("ERROR 1") vector_select = request.form['vector_select'] return render_template('calc.html', @@ -72,28 +73,26 @@ def calc(): @app.route("/view/", methods=['GET', 'POST']) def view(): - global vector_select, algos, tests, vector_num, vector_len, task_id, client + global vector_select, algos, tests, logger, \ + vector_num, vector_len, task_id, client, cipherpath test = algo = "ERROR" test_vector = [] - + client.clear_queue() if request.method == 'POST': algo_select = request.form.get('algo_select') test_select = request.form.get('test_select') - print("ERROR 2") - print("{}, {}".format(algo_select, test_select)) + logger.info("{}, {}".format(algo_select, test_select)) if vector_select == "True": - print("ERROR 3") vector_len = request.form["vector_len"] vector_num = request.form["vector_num"] - print("{} vectors of size {}".format(vector_num, vector_len)) + logger.info("{} vectors of size {}".format(vector_num, vector_len)) test_vector = Commands.test_vector_gen([int(vector_len), int(vector_num)]) else: - print("ERROR 4") if 'file' not in request.files: - flash("No File Uploaded") + logger.error("FILE NOT UPLOADED") file = request.files['file'] if file.filename == '': - flash("No File Selected") + logger.error("FILE NOT VALID") if file and allowed_file(file.filename): filename = secure_filename(file.filename) file.save(os.path.join(app.config['DOWNLOAD_FOLDER'], filename)) @@ -103,28 +102,49 @@ def view(): if not line: break test_vector.append(line) - print("{}, contents:\n{}".format(filename, test_vector)) + logger.info("plainlist".center(40, '_')) + logger.info(test_vector) + GI.Encrypt(task_id, test_vector, client) cipherlist = GI.ReceivedEncrypt(task_id, client) - print("cipherlist".center(40, '_')) - '\n'.join(cipherlist) - print(cipherlist) + + ciphertext = '\n'.join(cipherlist) + logger.info("cipherlist".center(40, '_')) + logger.info(cipherlist) + cipherpath = str(os.path.abspath(UPLOAD_FOLDER)) + '/' + \ + str(task_id) + '-ciphertext.txt' + + with open(cipherpath, 'w') as fd: + fd.write(ciphertext) task_id += 1 - return render_template('view.html', test=test_select, algo=algo_select) + return render_template('view.html', ciphertext=ciphertext, + test=test_select, + algo=algo_select + ) + + +@app.route("/download/") +def download_cipher(): + return send_file(cipherpath, as_attachment=True) if __name__ == "__main__": + global GI_interface app.config['DOWNLOAD_FOLDER'] = os.path.abspath(DOWNLOAD_FOLDER) app.secret_key = 'secret_key' app.config['SESSION_TYPE'] = 'filesystem' + GI_interface = sys.argv[1] + try: client.connection_setup() + except: command = "nc -l " + str(GI_PORT) + " &" subprocess.call(command, shell=True) - time.sleep(2) + time.sleep(5) client.connection_setup() + finally: incoming_t = threading.Thread(target=client.connection_recv, args=()) outgoing_t = threading.Thread(target=client.connection_send, args=()) diff --git a/docs/flask/server.py b/docs/flask/server.py new file mode 100755 index 0000000..17bfbf3 --- /dev/null +++ b/docs/flask/server.py @@ -0,0 +1,34 @@ +#! /usr/bin/env python3 + +import socket +import sys + + +def server_program(): + # get the hostname + host = socket.gethostname() + port = int(sys.argv[1]) # initiate port no above 1024 + + server_socket = socket.socket() # get instance + # look closely. The bind() function takes tuple as argument + server_socket.bind((host, port)) # bind host address and port together + + print("Listening on port:{}...".format(port)) + # configure how many client the server can listen simultaneously + server_socket.listen(1) + conn, address = server_socket.accept() # accept new connection + print("Connection from: " + str(address)) + while True: + # receive data stream. it won't accept data packet greater than 1024 bytes + data = conn.recv(1024).decode() + if not data: + # if data is not received break + break +# print("from connected user: " + str(data)) + conn.send(data.encode()) + + conn.close() # close the connection + + +if __name__ == '__main__': + server_program() diff --git a/docs/flask/templates/view.html b/docs/flask/templates/view.html index 7c4f37c..b9f643a 100644 --- a/docs/flask/templates/view.html +++ b/docs/flask/templates/view.html @@ -12,11 +12,15 @@
    -

    - CipherText

    -

    Test: {{ test }}

    -

    Algorithim: {{ algo }}

    -

    Time: {{ time }}

    +

    + + CipherText + + : {{ ciphertext }} +

    +

    Test: {{ test }}

    +

    Algorithim: {{ algo }}

    +

    Time: {{ time }}

    From 67cc71f5fabe299c80532073f91e93f02629b8b8 Mon Sep 17 00:00:00 2001 From: Ahmed Refik Date: Wed, 5 Apr 2023 23:01:04 -0600 Subject: [PATCH 19/46] [MP-144] json error recovery implemented --- docs/flask/Client.py | 2 -- docs/flask/GI_interface.py | 20 ++++++++++++++++++-- docs/flask/server.py | 7 ++++++- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/docs/flask/Client.py b/docs/flask/Client.py index 5779135..75ea8d8 100644 --- a/docs/flask/Client.py +++ b/docs/flask/Client.py @@ -41,9 +41,7 @@ def connection_send(self): self.outgoing_mutex.acquire() message = self.send_queue.get() self.outgoing_mutex.release() - # print(message) self.s.send(message.encode('ascii')) - # print("sent to port {}".format(self.port)) def send_to_queue(self, message): self.outgoing_mutex.acquire() diff --git a/docs/flask/GI_interface.py b/docs/flask/GI_interface.py index dc360e1..fb05332 100644 --- a/docs/flask/GI_interface.py +++ b/docs/flask/GI_interface.py @@ -1,3 +1,4 @@ +import re from Client import * import random import logging @@ -79,8 +80,23 @@ def ReceivedEncrypt(task_id, client): try: decoded_msg = json.loads(recv_msg) except json.decoder.JSONDecodeError: - print('\n' + recv_msg + '\n') - cipher_list.append(decoded_msg.get('payload_content')) + print('recv msg'.center(40,'_') + '\n' + recv_msg + '\n') + delim ='.(?={"api_call": "ENCRYPT", "task_id": "\d", "interface_type": "GI", "sender_id": "0",)' + print('delim'.center(40,'_') + '\n' + delim + '\n') + + #recv_msg = [e + delim for e in recv_msg.split(delim) if e] + recv_msg = re.split(delim, recv_msg) + print('last_msg'.center(40,'_') + '\n') + last_msg = recv_msg[-1] + print(recv_msg) + for msg in recv_msg: + if msg != last_msg: + msg = msg + "}" + print('msg'.center(40,'_') + '\n' + msg + '\n') + decoded_msg = json.loads(msg) + cipher_list.append(decoded_msg.get('payload_content')) + else: + cipher_list.append(decoded_msg.get('payload_content')) if decoded_msg.get('payload_total_fragments') == decoded_msg.get('payload_fragment_number'): return cipher_list diff --git a/docs/flask/server.py b/docs/flask/server.py index 17bfbf3..a78872b 100755 --- a/docs/flask/server.py +++ b/docs/flask/server.py @@ -2,6 +2,9 @@ import socket import sys +import time +import threading +from Client import * def server_program(): @@ -20,11 +23,13 @@ def server_program(): print("Connection from: " + str(address)) while True: # receive data stream. it won't accept data packet greater than 1024 bytes - data = conn.recv(1024).decode() + data = conn.recv(4096).decode() + time.sleep(0.1) if not data: # if data is not received break break # print("from connected user: " + str(data)) + time.sleep(0.1) conn.send(data.encode()) conn.close() # close the connection From 93345f7842f9c8cfca38c39b360b596e2fd84873 Mon Sep 17 00:00:00 2001 From: Ahmed Refik Date: Wed, 5 Apr 2023 23:08:16 -0600 Subject: [PATCH 20/46] [MP-144] minor style edits --- docs/flask/GI_interface.py | 52 +++++++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/docs/flask/GI_interface.py b/docs/flask/GI_interface.py index fb05332..6376d9b 100644 --- a/docs/flask/GI_interface.py +++ b/docs/flask/GI_interface.py @@ -65,7 +65,9 @@ def Encrypt(task_id, str_list, client): # logger = Logger.init(LOG_FILE, logging.DEBUG) ii = 1 for vector in str_list: - msg = client.string_to_json("ENCRYPT", str(task_id), "GI", "0", len(str_list), ii, str(sys.getsizeof(str_list)), vector) + msg = client.string_to_json("ENCRYPT", str(task_id), "GI", "0", + len(str_list), ii, + str(sys.getsizeof(str_list)), vector) client.send_to_queue(msg) ii += 1 return @@ -80,25 +82,26 @@ def ReceivedEncrypt(task_id, client): try: decoded_msg = json.loads(recv_msg) except json.decoder.JSONDecodeError: - print('recv msg'.center(40,'_') + '\n' + recv_msg + '\n') - delim ='.(?={"api_call": "ENCRYPT", "task_id": "\d", "interface_type": "GI", "sender_id": "0",)' + print('recv msg'.center(40, '_') + '\n' + recv_msg + '\n') + delim ='.(?={"api_call": "ENCRYPT", "task_id": "\d", \ + "interface_type": "GI", "sender_id": "0",)' print('delim'.center(40,'_') + '\n' + delim + '\n') - - #recv_msg = [e + delim for e in recv_msg.split(delim) if e] + recv_msg = re.split(delim, recv_msg) - print('last_msg'.center(40,'_') + '\n') + print('last_msg'.center(40, '_') + '\n') last_msg = recv_msg[-1] print(recv_msg) for msg in recv_msg: if msg != last_msg: msg = msg + "}" - print('msg'.center(40,'_') + '\n' + msg + '\n') + print('msg'.center(40, '_') + '\n' + msg + '\n') decoded_msg = json.loads(msg) cipher_list.append(decoded_msg.get('payload_content')) else: cipher_list.append(decoded_msg.get('payload_content')) - if decoded_msg.get('payload_total_fragments') == decoded_msg.get('payload_fragment_number'): + if decoded_msg.get('payload_total_fragments') == \ + decoded_msg.get('payload_fragment_number'): return cipher_list ii += 1 @@ -106,7 +109,9 @@ def ReceivedEncrypt(task_id, client): def Decrypt(task_id, str_list, client): ii = 1 for vector in str_list: - msg = client.string_to_json("DECRYPT", str(task_id), "GI", "0", len(str_list), ii, str(sys.getsizeof(str_list)), vector) + msg = client.string_to_json("DECRYPT", str(task_id), "GI", "0", + len(str_list), ii, + str(sys.getsizeof(str_list)), vector) client.send_to_queue(msg) ii += 1 return @@ -115,15 +120,32 @@ def Decrypt(task_id, str_list, client): def ReceivedDecrypt(task_id, client): ii = 1 cipher_list = [] - data = None while True: - while data != None: - data = client.get_queue() + data = client.get_queue() recv_msg = data.decode('ascii') - decoded_msg = json.loads(recv_msg) - cipher_list.append(decoded_msg.get('payload_content')) + try: + decoded_msg = json.loads(recv_msg) + except json.decoder.JSONDecodeError: + print('recv msg'.center(40, '_') + '\n' + recv_msg + '\n') + delim ='.(?={"api_call": "DECRYPT", "task_id": "\d", \ + "interface_type": "GI", "sender_id": "0",)' + print('delim'.center(40,'_') + '\n' + delim + '\n') + + recv_msg = re.split(delim, recv_msg) + print('last_msg'.center(40, '_') + '\n') + last_msg = recv_msg[-1] + print(recv_msg) + for msg in recv_msg: + if msg != last_msg: + msg = msg + "}" + print('msg'.center(40, '_') + '\n' + msg + '\n') + decoded_msg = json.loads(msg) + cipher_list.append(decoded_msg.get('payload_content')) + else: + cipher_list.append(decoded_msg.get('payload_content')) - if decoded_msg.get('payload_total_fragments') == decoded_msg.get('payload_fragment_number'): + if decoded_msg.get('payload_total_fragments') == \ + decoded_msg.get('payload_fragment_number'): return cipher_list ii += 1 From 830d58f3319a47f915adfeb1238a94a6256abab2 Mon Sep 17 00:00:00 2001 From: Ahmed Refik Date: Wed, 5 Apr 2023 23:10:55 -0600 Subject: [PATCH 21/46] [MP-144] fixed delim style --- docs/flask/GI_interface.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/flask/GI_interface.py b/docs/flask/GI_interface.py index 6376d9b..761a09c 100644 --- a/docs/flask/GI_interface.py +++ b/docs/flask/GI_interface.py @@ -83,8 +83,7 @@ def ReceivedEncrypt(task_id, client): decoded_msg = json.loads(recv_msg) except json.decoder.JSONDecodeError: print('recv msg'.center(40, '_') + '\n' + recv_msg + '\n') - delim ='.(?={"api_call": "ENCRYPT", "task_id": "\d", \ - "interface_type": "GI", "sender_id": "0",)' + delim ='.(?={"api_call": "ENCRYPT", "task_id": "\d", "interface_type": "GI", "sender_id": "0",)' print('delim'.center(40,'_') + '\n' + delim + '\n') recv_msg = re.split(delim, recv_msg) @@ -127,8 +126,7 @@ def ReceivedDecrypt(task_id, client): decoded_msg = json.loads(recv_msg) except json.decoder.JSONDecodeError: print('recv msg'.center(40, '_') + '\n' + recv_msg + '\n') - delim ='.(?={"api_call": "DECRYPT", "task_id": "\d", \ - "interface_type": "GI", "sender_id": "0",)' + delim ='.(?={"api_call": "DECRYPT", "task_id": "\d", "interface_type": "GI", "sender_id": "0",)' print('delim'.center(40,'_') + '\n' + delim + '\n') recv_msg = re.split(delim, recv_msg) From bc1705a68c5fda146e1f6f96bfaa0456ccffc707 Mon Sep 17 00:00:00 2001 From: Ahmed Refik Date: Thu, 6 Apr 2023 00:55:29 -0600 Subject: [PATCH 22/46] [MP-144] added json error correction to receiving thread --- docs/flask/Client.py | 68 +++++++++++++++++++++++++++----------- docs/flask/GI_interface.py | 12 ------- 2 files changed, 48 insertions(+), 32 deletions(-) diff --git a/docs/flask/Client.py b/docs/flask/Client.py index 75ea8d8..5bbe21d 100644 --- a/docs/flask/Client.py +++ b/docs/flask/Client.py @@ -1,11 +1,17 @@ import socket +import re import queue import json import threading -from threading import Lock import time +import logging + +from threading import Lock +from GI_interface import Logger +logger = Logger.init(None, logging.DEBUG) PAYLOAD = 4096 +delim ='.(?={"api_call": "ENCRYPT", "task_id": "\d", "interface_type": "GI", "sender_id": "0",)' """ Steps: @@ -30,7 +36,7 @@ def __init__(self, port, host, name): def connection_setup(self): self.s.connect((self.host, self.port)) - print("socket:{}\n host:{}\n port:{}\n".format(self.s, self.host, self.port)) + logger.debug("socket:{}\n host:{}\n port:{}\n".format(self.s, self.host, self.port)) # SEND the handshake # self.to_outgoing_queue(str(self.port)) # self.connection_send() @@ -38,42 +44,64 @@ def connection_setup(self): def connection_send(self): while True: if not self.send_queue.empty(): - self.outgoing_mutex.acquire() + # self.outgoing_mutex.acquire() message = self.send_queue.get() - self.outgoing_mutex.release() + # self.outgoing_mutex.release() self.s.send(message.encode('ascii')) def send_to_queue(self, message): - self.outgoing_mutex.acquire() + # self.outgoing_mutex.acquire() self.send_queue.put(message) - self.outgoing_mutex.release() - # print("OUTGOING QUEUE {}".format(message)) + # self.outgoing_mutex.release() + # logger.debug("OUTGOING QUEUE {}".format(message)) def connection_recv(self): + decoded_msg = None + data = b'' while True: - msg = self.s.recv(PAYLOAD) - self.mutex.acquire() - self.recv_queue.put(msg) - self.mutex.release() - # print("Received from server: " + msg.decode('ascii')) + data += self.s.recv(PAYLOAD) + recv_msg = data.decode('ascii') + try: + decoded_msg = json.loads(recv_msg) + except json.decoder.JSONDecodeError: + logger.debug('recv msg'.center(40, '_') + '\n' + recv_msg + '\n') + delim ='.(?={"api_call": "ENCRYPT", "task_id": "\d", "interface_type": "GI", "sender_id": "0",)' + logger.debug('delim'.center(40,'_') + '\n' + delim + '\n') + + recv_msg = re.split(delim, recv_msg) + logger.debug('last_msg'.center(40, '_') + '\n') + last_msg = recv_msg[-1] + logger.debug(recv_msg) + for msg in recv_msg: + if msg != last_msg: + msg = msg + "}" + logger.debug('msg'.center(40, '_') + '\n' + msg + '\n') + try: + decoded_msg = json.loads(msg) + except json.decoder.JSONDecodeError: + data = msg.encode('ascii') + else: + self.recv_queue.put(msg.encode('ascii')) + data = b'' + else: + self.recv_queue.put(recv_msg.encode('ascii')) + data = b'' def get_queue(self): while True: if not self.recv_queue.empty(): - self.mutex.acquire() + # self.mutex.acquire() data = self.recv_queue.get() - self.mutex.release() + # self.mutex.release() return data def clear_queue(self): - self.mutex.acquire() - self.outgoing_mutex.acquire() - + # self.mutex.acquire() + # self.outgoing_mutex.acquire() self.send_queue.queue.clear() self.recv_queue.queue.clear() - - self.mutex.release() - self.outgoing_mutex.release() + # self.mutex.release() + # self.outgoing_mutex.release() return @staticmethod diff --git a/docs/flask/GI_interface.py b/docs/flask/GI_interface.py index 761a09c..0907df9 100644 --- a/docs/flask/GI_interface.py +++ b/docs/flask/GI_interface.py @@ -82,18 +82,12 @@ def ReceivedEncrypt(task_id, client): try: decoded_msg = json.loads(recv_msg) except json.decoder.JSONDecodeError: - print('recv msg'.center(40, '_') + '\n' + recv_msg + '\n') delim ='.(?={"api_call": "ENCRYPT", "task_id": "\d", "interface_type": "GI", "sender_id": "0",)' - print('delim'.center(40,'_') + '\n' + delim + '\n') - recv_msg = re.split(delim, recv_msg) - print('last_msg'.center(40, '_') + '\n') last_msg = recv_msg[-1] - print(recv_msg) for msg in recv_msg: if msg != last_msg: msg = msg + "}" - print('msg'.center(40, '_') + '\n' + msg + '\n') decoded_msg = json.loads(msg) cipher_list.append(decoded_msg.get('payload_content')) else: @@ -125,18 +119,12 @@ def ReceivedDecrypt(task_id, client): try: decoded_msg = json.loads(recv_msg) except json.decoder.JSONDecodeError: - print('recv msg'.center(40, '_') + '\n' + recv_msg + '\n') delim ='.(?={"api_call": "DECRYPT", "task_id": "\d", "interface_type": "GI", "sender_id": "0",)' - print('delim'.center(40,'_') + '\n' + delim + '\n') - recv_msg = re.split(delim, recv_msg) - print('last_msg'.center(40, '_') + '\n') last_msg = recv_msg[-1] - print(recv_msg) for msg in recv_msg: if msg != last_msg: msg = msg + "}" - print('msg'.center(40, '_') + '\n' + msg + '\n') decoded_msg = json.loads(msg) cipher_list.append(decoded_msg.get('payload_content')) else: From ed22dcd947c3a3cc253f08266bc65791a32f1107 Mon Sep 17 00:00:00 2001 From: Ahmed Refik Date: Thu, 6 Apr 2023 14:51:27 -0600 Subject: [PATCH 23/46] [MP-144] added function to check if user input and decrypted output match --- docs/flask/GI_interface.py | 3 +-- docs/flask/app.py | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/docs/flask/GI_interface.py b/docs/flask/GI_interface.py index 0907df9..a062b8a 100644 --- a/docs/flask/GI_interface.py +++ b/docs/flask/GI_interface.py @@ -78,9 +78,8 @@ def ReceivedEncrypt(task_id, client): cipher_list = [] while True: data = client.get_queue() - recv_msg = data.decode('ascii') try: - decoded_msg = json.loads(recv_msg) + decoded_msg = json.loads(data) except json.decoder.JSONDecodeError: delim ='.(?={"api_call": "ENCRYPT", "task_id": "\d", "interface_type": "GI", "sender_id": "0",)' recv_msg = re.split(delim, recv_msg) diff --git a/docs/flask/app.py b/docs/flask/app.py index 8b18b0e..8ce2244 100755 --- a/docs/flask/app.py +++ b/docs/flask/app.py @@ -28,7 +28,7 @@ vector_len = 100 vector_num = 100 -logger = Logger.init(None, logging.ERROR) +logger = Logger.init(None, logging.INFO) client = Client(GI_PORT, socket.gethostname(), "CLI") task_id = 0 @@ -38,6 +38,19 @@ def allowed_file(filename): return '.' in filename and filename.split('.', 1)[1].lower() #in ALLOWED_EXTENSIONS +def check_equals(a, b): + splitA = set(a.split("\n")) + splitB = set(b.split("\n")) + diff = splitB.difference(splitA) + diff = ", ".join(diff) + result = a==b + logger.info("it is {} that the plain and cipher match".format(a == b)) + if not result: + logger.error(a) + logger.error(b) + return result + + @app.route("/") @app.route("/index/") def index(): @@ -109,8 +122,11 @@ def view(): cipherlist = GI.ReceivedEncrypt(task_id, client) ciphertext = '\n'.join(cipherlist) + plaintext = '\n'.join(test_vector) logger.info("cipherlist".center(40, '_')) logger.info(cipherlist) + check_equals(plaintext, ciphertext) + cipherpath = str(os.path.abspath(UPLOAD_FOLDER)) + '/' + \ str(task_id) + '-ciphertext.txt' From eba763b5b8054bc0cc253aa731d0c2b6e516f4a1 Mon Sep 17 00:00:00 2001 From: Ahmed Refik Date: Fri, 7 Apr 2023 00:33:34 -0600 Subject: [PATCH 24/46] [MP-144] all filetypes are acceptted and users can downloaded final plaintext --- docs/flask/Client.py | 15 ++++---- docs/flask/GI_interface.py | 7 ++-- docs/flask/Makefile | 3 +- docs/flask/app.py | 67 +++++++++++++++++++++++----------- docs/flask/templates/view.html | 6 ++- 5 files changed, 65 insertions(+), 33 deletions(-) diff --git a/docs/flask/Client.py b/docs/flask/Client.py index 5bbe21d..419ce2f 100644 --- a/docs/flask/Client.py +++ b/docs/flask/Client.py @@ -12,6 +12,7 @@ logger = Logger.init(None, logging.DEBUG) PAYLOAD = 4096 delim ='.(?={"api_call": "ENCRYPT", "task_id": "\d", "interface_type": "GI", "sender_id": "0",)' +encoding = 'utf-8' """ Steps: @@ -44,10 +45,10 @@ def connection_setup(self): def connection_send(self): while True: if not self.send_queue.empty(): - # self.outgoing_mutex.acquire() + self.outgoing_mutex.acquire() message = self.send_queue.get() - # self.outgoing_mutex.release() - self.s.send(message.encode('ascii')) + self.outgoing_mutex.release() + self.s.send(message.encode(encoding)) def send_to_queue(self, message): # self.outgoing_mutex.acquire() @@ -60,7 +61,7 @@ def connection_recv(self): data = b'' while True: data += self.s.recv(PAYLOAD) - recv_msg = data.decode('ascii') + recv_msg = data.decode(encoding) try: decoded_msg = json.loads(recv_msg) except json.decoder.JSONDecodeError: @@ -79,12 +80,12 @@ def connection_recv(self): try: decoded_msg = json.loads(msg) except json.decoder.JSONDecodeError: - data = msg.encode('ascii') + data = msg.encode(encoding) else: - self.recv_queue.put(msg.encode('ascii')) + self.recv_queue.put(msg.encode(encoding)) data = b'' else: - self.recv_queue.put(recv_msg.encode('ascii')) + self.recv_queue.put(recv_msg.encode(encoding)) data = b'' def get_queue(self): diff --git a/docs/flask/GI_interface.py b/docs/flask/GI_interface.py index a062b8a..76a34ae 100644 --- a/docs/flask/GI_interface.py +++ b/docs/flask/GI_interface.py @@ -6,7 +6,7 @@ import json import time LOG_FILE = "out.log" - +encoding='utf-8' class Logger: def init(file, level) -> None: @@ -78,8 +78,9 @@ def ReceivedEncrypt(task_id, client): cipher_list = [] while True: data = client.get_queue() + recv_msg = data.decode(encoding) try: - decoded_msg = json.loads(data) + decoded_msg = json.loads(recv_msg) except json.decoder.JSONDecodeError: delim ='.(?={"api_call": "ENCRYPT", "task_id": "\d", "interface_type": "GI", "sender_id": "0",)' recv_msg = re.split(delim, recv_msg) @@ -114,7 +115,7 @@ def ReceivedDecrypt(task_id, client): cipher_list = [] while True: data = client.get_queue() - recv_msg = data.decode('ascii') + recv_msg = data.decode(encoding) try: decoded_msg = json.loads(recv_msg) except json.decoder.JSONDecodeError: diff --git a/docs/flask/Makefile b/docs/flask/Makefile index 10df563..5cc5cc2 100644 --- a/docs/flask/Makefile +++ b/docs/flask/Makefile @@ -8,4 +8,5 @@ all: # This is to remove any processes on the port clean: lsof | grep $(PORT) | awk '{print $$2}' | head -n 1 | xargs -I {} kill -9 {} - rm uploads/*.txt -r + rm uploads/* -r + rm downloads/* -r diff --git a/docs/flask/app.py b/docs/flask/app.py index 8ce2244..95d206e 100755 --- a/docs/flask/app.py +++ b/docs/flask/app.py @@ -7,6 +7,7 @@ import threading import socket import logging +import base64 from flask import Flask, flash, render_template, request, redirect, send_file from werkzeug.utils import secure_filename from Client import * @@ -14,8 +15,7 @@ app = Flask(__name__) algos = ['AES', 'QPP', 'AES & QPP'] -tests = ['Encrypt', 'Decrypt', - 'Encrypt & Decrypt', 'M.I.T.M', +tests = ['Encrypt', 'Encrypt & Decrypt', 'M.I.T.M', 'Brute Force', 'Monte Carlo', 'Multi-block Message', 'Known Answer' ] @@ -87,7 +87,7 @@ def calc(): @app.route("/view/", methods=['GET', 'POST']) def view(): global vector_select, algos, tests, logger, \ - vector_num, vector_len, task_id, client, cipherpath + vector_num, vector_len, task_id, client, cipherpath, plainpath test = algo = "ERROR" test_vector = [] client.clear_queue() @@ -106,45 +106,70 @@ def view(): file = request.files['file'] if file.filename == '': logger.error("FILE NOT VALID") - if file and allowed_file(file.filename): + if file: + file_extension = allowed_file(file.filename) filename = secure_filename(file.filename) file.save(os.path.join(app.config['DOWNLOAD_FOLDER'], filename)) - with open(os.path.abspath(DOWNLOAD_FOLDER + filename), 'r') as fd: - while True: - line = fd.readline() - if not line: - break - test_vector.append(line) - logger.info("plainlist".center(40, '_')) - logger.info(test_vector) - - GI.Encrypt(task_id, test_vector, client) - cipherlist = GI.ReceivedEncrypt(task_id, client) + with open(os.path.abspath(DOWNLOAD_FOLDER + filename), 'rb') as fd: + content = fd.read() + test_vector.append(base64.b64encode(content).decode('ascii')) + logger.debug("plainlist".center(40, '_')) + logger.debug(test_vector) + + if test_select.find('Encrypt') != -1: + logger.info("Encryption Begins") + GI.Encrypt(task_id, test_vector, client) + cipherlist = GI.ReceivedEncrypt(task_id, client) + logger.info("plaintext".center(40, '_')) + logger.info('\n'.join(cipherlist)) + logger.info("Encryption Ends") + if test_select.find('Decrypt') != -1: + logger.info("Decryption Begins") + GI.Decrypt(task_id, cipherlist, client) + plainlist = GI.ReceivedDecrypt(task_id, client) + logger.info("ciphertext".center(40, '_')) + logger.info('\n'.join(plainlist)) + logger.info("Decryption Ends") ciphertext = '\n'.join(cipherlist) plaintext = '\n'.join(test_vector) - logger.info("cipherlist".center(40, '_')) - logger.info(cipherlist) + logger.debug("cipherlist".center(40, '_')) + logger.debug(cipherlist) check_equals(plaintext, ciphertext) - + + ciphertext = base64.b64decode(ciphertext) + plaintext = base64.b64decode(plaintext) cipherpath = str(os.path.abspath(UPLOAD_FOLDER)) + '/' + \ - str(task_id) + '-ciphertext.txt' + str(task_id) + filename.split('.')[0] + '-ciphertext.' + file_extension + + plainpath = str(os.path.abspath(UPLOAD_FOLDER)) + '/' + \ + str(task_id) + filename.split('.')[0] + '-plaintext.' + file_extension - with open(cipherpath, 'w') as fd: + with open(cipherpath, 'wb') as fd: fd.write(ciphertext) + + with open(plainpath, 'wb') as fd: + fd.write(plaintext) + task_id += 1 return render_template('view.html', ciphertext=ciphertext, + plaintext=plaintext, test=test_select, algo=algo_select ) -@app.route("/download/") +@app.route("/download/cipher/") def download_cipher(): return send_file(cipherpath, as_attachment=True) +@app.route("/download/plain/") +def download_plain(): + return send_file(plainpath, as_attachment=True) + + if __name__ == "__main__": global GI_interface app.config['DOWNLOAD_FOLDER'] = os.path.abspath(DOWNLOAD_FOLDER) diff --git a/docs/flask/templates/view.html b/docs/flask/templates/view.html index b9f643a..02c8aa5 100644 --- a/docs/flask/templates/view.html +++ b/docs/flask/templates/view.html @@ -16,7 +16,11 @@ CipherText - : {{ ciphertext }} +

    +

    + + Plaintext +

    Test: {{ test }}

    Algorithim: {{ algo }}

    From 65aaf517b172f533b1a641e95cad1817cf46486c Mon Sep 17 00:00:00 2001 From: Ahmed Refik Date: Fri, 7 Apr 2023 13:12:15 -0600 Subject: [PATCH 25/46] [MP-144] test vector encoding and regex for client recv api_call fixed --- docs/flask/Client.py | 2 +- docs/flask/GI_interface.py | 4 +++- docs/flask/app.py | 10 ++++++++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/docs/flask/Client.py b/docs/flask/Client.py index 419ce2f..8ab459d 100644 --- a/docs/flask/Client.py +++ b/docs/flask/Client.py @@ -66,7 +66,7 @@ def connection_recv(self): decoded_msg = json.loads(recv_msg) except json.decoder.JSONDecodeError: logger.debug('recv msg'.center(40, '_') + '\n' + recv_msg + '\n') - delim ='.(?={"api_call": "ENCRYPT", "task_id": "\d", "interface_type": "GI", "sender_id": "0",)' + delim ='.(?={"api_call": ".*?", "task_id": "\d", "interface_type": "GI", "sender_id": "0",)' logger.debug('delim'.center(40,'_') + '\n' + delim + '\n') recv_msg = re.split(delim, recv_msg) diff --git a/docs/flask/GI_interface.py b/docs/flask/GI_interface.py index 76a34ae..822ad19 100644 --- a/docs/flask/GI_interface.py +++ b/docs/flask/GI_interface.py @@ -115,11 +115,13 @@ def ReceivedDecrypt(task_id, client): cipher_list = [] while True: data = client.get_queue() + print("data".center(40, "_")) + print(data) recv_msg = data.decode(encoding) try: decoded_msg = json.loads(recv_msg) except json.decoder.JSONDecodeError: - delim ='.(?={"api_call": "DECRYPT", "task_id": "\d", "interface_type": "GI", "sender_id": "0",)' + delim ='.(?={"api_call": "DECRYPT", "task_id": "\d", "interface_type": "GI", "sender_id": "0", )' recv_msg = re.split(delim, recv_msg) last_msg = recv_msg[-1] for msg in recv_msg: diff --git a/docs/flask/app.py b/docs/flask/app.py index 95d206e..2a59a46 100755 --- a/docs/flask/app.py +++ b/docs/flask/app.py @@ -28,7 +28,7 @@ vector_len = 100 vector_num = 100 -logger = Logger.init(None, logging.INFO) +logger = Logger.init(None, logging.DEBUG) client = Client(GI_PORT, socket.gethostname(), "CLI") task_id = 0 @@ -100,6 +100,11 @@ def view(): vector_num = request.form["vector_num"] logger.info("{} vectors of size {}".format(vector_num, vector_len)) test_vector = Commands.test_vector_gen([int(vector_len), int(vector_num)]) + logger.info("vectors".center(40, '_')) + print(test_vector) + test_vector = [base64.b64encode(bytes(item, 'utf-8')).decode('ascii') for item in test_vector] + filename = "test-vectors-" + vector_len + "-" + vector_num + file_extension = "txt" else: if 'file' not in request.files: logger.error("FILE NOT UPLOADED") @@ -126,10 +131,11 @@ def view(): if test_select.find('Decrypt') != -1: logger.info("Decryption Begins") GI.Decrypt(task_id, cipherlist, client) + logger.info("Decrypt sent") plainlist = GI.ReceivedDecrypt(task_id, client) + logger.info("Decryption Ends") logger.info("ciphertext".center(40, '_')) logger.info('\n'.join(plainlist)) - logger.info("Decryption Ends") ciphertext = '\n'.join(cipherlist) plaintext = '\n'.join(test_vector) From dea94eda8316164ac7bd7efe3bb5e6531851bd33 Mon Sep 17 00:00:00 2001 From: Ahmed Refik Date: Fri, 7 Apr 2023 16:35:59 -0600 Subject: [PATCH 26/46] [MP-144] CC json fix, times added, encrypt and decrypt funcion seperation --- NodeE | 2 +- docs/flask/Client.py | 6 +-- docs/flask/GI_interface.py | 39 ++++++++-------- docs/flask/app.py | 81 +++++++++++++++++++++------------- docs/flask/templates/view.html | 8 +++- 5 files changed, 81 insertions(+), 55 deletions(-) diff --git a/NodeE b/NodeE index 6739db5..df7ac6d 160000 --- a/NodeE +++ b/NodeE @@ -1 +1 @@ -Subproject commit 6739db58d089b52fa6f0cee96caad657a9a6164f +Subproject commit df7ac6d03842fee928dde7cb05d437e0059aae48 diff --git a/docs/flask/Client.py b/docs/flask/Client.py index 8ab459d..89dfd5b 100644 --- a/docs/flask/Client.py +++ b/docs/flask/Client.py @@ -49,7 +49,7 @@ def connection_send(self): message = self.send_queue.get() self.outgoing_mutex.release() self.s.send(message.encode(encoding)) - + self.s.send('\n'.encode(encoding)) def send_to_queue(self, message): # self.outgoing_mutex.acquire() self.send_queue.put(message) @@ -66,7 +66,7 @@ def connection_recv(self): decoded_msg = json.loads(recv_msg) except json.decoder.JSONDecodeError: logger.debug('recv msg'.center(40, '_') + '\n' + recv_msg + '\n') - delim ='.(?={"api_call": ".*?", "task_id": "\d", "interface_type": "GI", "sender_id": "0",)' + delim ='.\n(?={"api_call": ".*?", "task_id": \d, "interface_type": "GI", "sender_id": 0,)' logger.debug('delim'.center(40,'_') + '\n' + delim + '\n') recv_msg = re.split(delim, recv_msg) @@ -114,7 +114,7 @@ def string_to_json(api_call, task_id, interface_type="GC", sender_id="0", "interface_type":interface_type, "sender_id":sender_id, "payload_total_fragments": payload_total_fragments, - "payload_fragment_number": payload_fragment_number, + "payload_frag_number": payload_fragment_number, "payload_size": payload_size, "payload_content": payload_content} diff --git a/docs/flask/GI_interface.py b/docs/flask/GI_interface.py index 822ad19..2b4a326 100644 --- a/docs/flask/GI_interface.py +++ b/docs/flask/GI_interface.py @@ -65,9 +65,9 @@ def Encrypt(task_id, str_list, client): # logger = Logger.init(LOG_FILE, logging.DEBUG) ii = 1 for vector in str_list: - msg = client.string_to_json("ENCRYPT", str(task_id), "GI", "0", - len(str_list), ii, - str(sys.getsizeof(str_list)), vector) + msg = client.string_to_json("ENCRYPT", task_id, "GI", + 0, len(str_list), ii, + len(vector), vector) client.send_to_queue(msg) ii += 1 return @@ -76,13 +76,14 @@ def Encrypt(task_id, str_list, client): def ReceivedEncrypt(task_id, client): ii = 1 cipher_list = [] + data = client.get_queue() + recv_time = time.time() while True: - data = client.get_queue() - recv_msg = data.decode(encoding) + recv_msg = data.decode(encoding).rstrip() try: decoded_msg = json.loads(recv_msg) except json.decoder.JSONDecodeError: - delim ='.(?={"api_call": "ENCRYPT", "task_id": "\d", "interface_type": "GI", "sender_id": "0",)' + delim ='.(?={"api_call": "ENCRYPT", "task_id": \d, "interface_type": "GI", "sender_id": \d, )' recv_msg = re.split(delim, recv_msg) last_msg = recv_msg[-1] for msg in recv_msg: @@ -94,17 +95,18 @@ def ReceivedEncrypt(task_id, client): cipher_list.append(decoded_msg.get('payload_content')) if decoded_msg.get('payload_total_fragments') == \ - decoded_msg.get('payload_fragment_number'): - return cipher_list + decoded_msg.get('payload_frag_number'): + return cipher_list, recv_time ii += 1 + data = client.get_queue() @staticmethod def Decrypt(task_id, str_list, client): ii = 1 for vector in str_list: - msg = client.string_to_json("DECRYPT", str(task_id), "GI", "0", - len(str_list), ii, - str(sys.getsizeof(str_list)), vector) + msg = client.string_to_json("DECRYPT", task_id, "GI", + 0, len(str_list), ii, + len(vector), vector) client.send_to_queue(msg) ii += 1 return @@ -113,15 +115,14 @@ def Decrypt(task_id, str_list, client): def ReceivedDecrypt(task_id, client): ii = 1 cipher_list = [] + data = client.get_queue() + recv_time = time.time() while True: - data = client.get_queue() - print("data".center(40, "_")) - print(data) - recv_msg = data.decode(encoding) + recv_msg = data.decode(encoding).rstrip() try: decoded_msg = json.loads(recv_msg) except json.decoder.JSONDecodeError: - delim ='.(?={"api_call": "DECRYPT", "task_id": "\d", "interface_type": "GI", "sender_id": "0", )' + delim ='.(?={"api_call": "DECRYPT", "task_id": \d, "interface_type": "GI", "sender_id": \d, )' recv_msg = re.split(delim, recv_msg) last_msg = recv_msg[-1] for msg in recv_msg: @@ -133,7 +134,7 @@ def ReceivedDecrypt(task_id, client): cipher_list.append(decoded_msg.get('payload_content')) if decoded_msg.get('payload_total_fragments') == \ - decoded_msg.get('payload_fragment_number'): - return cipher_list + decoded_msg.get('payload_frag_number'): + return cipher_list, recv_time ii += 1 - + data = client.get_queue() diff --git a/docs/flask/app.py b/docs/flask/app.py index 2a59a46..eb519b0 100755 --- a/docs/flask/app.py +++ b/docs/flask/app.py @@ -28,7 +28,7 @@ vector_len = 100 vector_num = 100 -logger = Logger.init(None, logging.DEBUG) +logger = Logger.init(None, logging.ERROR) client = Client(GI_PORT, socket.gethostname(), "CLI") task_id = 0 @@ -90,6 +90,7 @@ def view(): vector_num, vector_len, task_id, client, cipherpath, plainpath test = algo = "ERROR" test_vector = [] + plaintext = None client.clear_queue() if request.method == 'POST': algo_select = request.form.get('algo_select') @@ -100,8 +101,8 @@ def view(): vector_num = request.form["vector_num"] logger.info("{} vectors of size {}".format(vector_num, vector_len)) test_vector = Commands.test_vector_gen([int(vector_len), int(vector_num)]) - logger.info("vectors".center(40, '_')) - print(test_vector) + logger.debug("vectors".center(40, '_')) + logger.debug(test_vector) test_vector = [base64.b64encode(bytes(item, 'utf-8')).decode('ascii') for item in test_vector] filename = "test-vectors-" + vector_len + "-" + vector_num file_extension = "txt" @@ -121,49 +122,69 @@ def view(): logger.debug("plainlist".center(40, '_')) logger.debug(test_vector) + decrypt_time = encrypt_time = None + if test_select.find('Encrypt') != -1: logger.info("Encryption Begins") + + pre_encrypt_time = time.time() GI.Encrypt(task_id, test_vector, client) - cipherlist = GI.ReceivedEncrypt(task_id, client) + cipherlist, post_encrypt_time = GI.ReceivedEncrypt(task_id, client) + encrypt_time = post_encrypt_time - pre_encrypt_time + logger.info("plaintext".center(40, '_')) logger.info('\n'.join(cipherlist)) logger.info("Encryption Ends") if test_select.find('Decrypt') != -1: logger.info("Decryption Begins") + + pre_decrypt_time = time.time() GI.Decrypt(task_id, cipherlist, client) logger.info("Decrypt sent") - plainlist = GI.ReceivedDecrypt(task_id, client) - logger.info("Decryption Ends") - logger.info("ciphertext".center(40, '_')) - logger.info('\n'.join(plainlist)) - - ciphertext = '\n'.join(cipherlist) - plaintext = '\n'.join(test_vector) - logger.debug("cipherlist".center(40, '_')) - logger.debug(cipherlist) - check_equals(plaintext, ciphertext) - - ciphertext = base64.b64decode(ciphertext) - plaintext = base64.b64decode(plaintext) - cipherpath = str(os.path.abspath(UPLOAD_FOLDER)) + '/' + \ - str(task_id) + filename.split('.')[0] + '-ciphertext.' + file_extension - - plainpath = str(os.path.abspath(UPLOAD_FOLDER)) + '/' + \ - str(task_id) + filename.split('.')[0] + '-plaintext.' + file_extension - with open(cipherpath, 'wb') as fd: - fd.write(ciphertext) + plainlist, post_decrypt_time = GI.ReceivedDecrypt(task_id, client) + decrypt_time = post_decrypt_time - pre_decrypt_time + plainpath = str(os.path.abspath(UPLOAD_FOLDER)) + '/' + \ + str(task_id) + filename.split('.')[0] + '-plaintext.' + file_extension + + if len(plainlist) != 1: + plainlist = [ base64.b64decode(cipher).decode('utf-8') for cipher in plainlist ] + plaintext = '\n'.join(plainlist) + with open(plainpath, 'wb') as fd: + fd.write(bytes(plaintext,'utf-8')) + else: + plainlist = [ base64.b64decode(cipher) for cipher in plainlist ] + plaintext = plainlist[0] + with open(plainpath, 'wb') as fd: + fd.write(plaintext) + + cipherpath = str(os.path.abspath(UPLOAD_FOLDER)) + '/' + \ + str(task_id) + filename.split('.')[0] + '-ciphertext.' + file_extension - with open(plainpath, 'wb') as fd: - fd.write(plaintext) + if len(cipherlist) != 1: + cipherlist = [ base64.b64decode(cipher).decode('utf-8') for cipher in cipherlist ] + ciphertext = '\n'.join(cipherlist) + with open(cipherpath, 'wb') as fd: + fd.write(bytes(ciphertext, 'utf-8')) + else: + cipherlist = [ base64.b64decode(cipher) for cipher in cipherlist ] + ciphertext = cipherlist[0] + with open(cipherpath, 'wb') as fd: + fd.write(ciphertext) + task_id += 1 return render_template('view.html', ciphertext=ciphertext, - plaintext=plaintext, - test=test_select, - algo=algo_select - ) + plaintext=plaintext, + test=test_select, + algo=algo_select, + encrypt_time=encrypt_time, + decrypt_time=decrypt_time + ) + + + @app.route("/download/cipher/") diff --git a/docs/flask/templates/view.html b/docs/flask/templates/view.html index 02c8aa5..5a46825 100644 --- a/docs/flask/templates/view.html +++ b/docs/flask/templates/view.html @@ -17,15 +17,19 @@ CipherText

    + {% if plaintext != None %}

    Plaintext

    + {% endif %}

    Test: {{ test }}

    Algorithim: {{ algo }}

    -

    Time: {{ time }}

    - +

    Encrypt Time: {{ encrypt_time }} (s)

    + {% if decrypt_time != None %} +

    Decrypt Time: {{ decrypt_time }} (s)

    + {% endif %} {% endblock %} From f9bd80e1f992694b3cbca7789d57ea21f0785ff3 Mon Sep 17 00:00:00 2001 From: Ahmed Refik Date: Fri, 7 Apr 2023 19:58:55 -0600 Subject: [PATCH 27/46] [MP-144] beginnings of the demo.html page --- docs/flask/app.py | 10 ++++++---- docs/flask/templates/demo.html | 29 +++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 4 deletions(-) create mode 100644 docs/flask/templates/demo.html diff --git a/docs/flask/app.py b/docs/flask/app.py index eb519b0..578311c 100755 --- a/docs/flask/app.py +++ b/docs/flask/app.py @@ -67,8 +67,13 @@ def research(): return render_template('research.html') -@app.route("/demo/") +@app.route("/demo/", methods=['GET', 'POST']) def demo(): + if request.method == "POST": + print('hi') + + else: + print('hi') return render_template('demo.html') @@ -184,9 +189,6 @@ def view(): ) - - - @app.route("/download/cipher/") def download_cipher(): return send_file(cipherpath, as_attachment=True) diff --git a/docs/flask/templates/demo.html b/docs/flask/templates/demo.html new file mode 100644 index 0000000..be49327 --- /dev/null +++ b/docs/flask/templates/demo.html @@ -0,0 +1,29 @@ +{% extends 'base.html' %} + +{% block title %} QPP Results {% endblock %} +{% block header %} Results {% endblock %} + +{% block header_content %} +

    Here are the final results

    +{% endblock %} + + +{% block content %} + +
    +

    User Input

    + +
    +
    +
    +

    CipherText

    + +
    +

    PlainText

    + +
    + + +
    + +{% endblock %} From 91e8300c60d6e479195a8bd0292cdb0280ce82fe Mon Sep 17 00:00:00 2001 From: Ahmed Refik Date: Sat, 8 Apr 2023 12:44:33 -0600 Subject: [PATCH 28/46] [MP-144] demo page fully funtional with times and everything --- docs/flask/app.py | 35 +++++++++++++++++++++++++++++----- docs/flask/templates/calc.html | 2 +- docs/flask/templates/demo.html | 26 ++++++++++++++++++------- 3 files changed, 50 insertions(+), 13 deletions(-) diff --git a/docs/flask/app.py b/docs/flask/app.py index 578311c..c4f8924 100755 --- a/docs/flask/app.py +++ b/docs/flask/app.py @@ -67,14 +67,38 @@ def research(): return render_template('research.html') + @app.route("/demo/", methods=['GET', 'POST']) def demo(): + global task_id + if len(request.args) == 0: + cipher_list = plain_list = [" "] + decrypt_time = encrypt_time = 0 + if request.method == "POST": - print('hi') + user_input = request.form["user_input"] + + pre_encrypt_time = time.time() + GI.Encrypt(task_id, [user_input], client) + cipher_list, post_encrypt_time = GI.ReceivedEncrypt(task_id, client) + encrypt_time = post_encrypt_time - pre_encrypt_time + task_id += 1 + + print(cipher_list) + print(encrypt_time) + + pre_decrypt_time = time.time() + GI.Decrypt(task_id, cipher_list, client) + plain_list, post_decrypt_time = GI.ReceivedDecrypt(task_id, client) + decrypt_time = post_decrypt_time - pre_decrypt_time + task_id += 1 - else: - print('hi') - return render_template('demo.html') + print(plain_list) + print(decrypt_time) + return render_template('demo.html', ciphertext=cipher_list[0], + plaintext=plain_list[0], + decrypt_time=decrypt_time, + encrypt_time=encrypt_time) @app.route("/calc/", methods=['GET', 'POST']) @@ -221,7 +245,8 @@ def download_plain(): incoming_t.start() outgoing_t.start() - app.run(debug=False, host=socket.gethostname(), port=4996) + app.run(debug=False, host=socket.gethostname(), + port=4996) #ssl_context='adhoc') incoming_t.join() outgoing_t.join() diff --git a/docs/flask/templates/calc.html b/docs/flask/templates/calc.html index e9b7cad..a74b86e 100644 --- a/docs/flask/templates/calc.html +++ b/docs/flask/templates/calc.html @@ -60,7 +60,7 @@ File Upload {% endif %} - + {% endblock %} diff --git a/docs/flask/templates/demo.html b/docs/flask/templates/demo.html index be49327..ea88cf6 100644 --- a/docs/flask/templates/demo.html +++ b/docs/flask/templates/demo.html @@ -1,10 +1,10 @@ {% extends 'base.html' %} -{% block title %} QPP Results {% endblock %} -{% block header %} Results {% endblock %} +{% block title %} QPP Demo {% endblock %} +{% block header %} Interactive Demo {% endblock %} {% block header_content %} -

    Here are the final results

    +

    This page allows users to interact with the embedded system with textual input

    {% endblock %} @@ -12,18 +12,30 @@

    User Input

    - +
    +

    CipherText

    - +

    {{ ciphertext }}

    PlainText

    - +

    {{ plaintext }}

    +
    + +


    +

    Encrypt Time: + {{ encrypt_time }} (s) +

    +
    +
    +

    + + {{ decrypt_time }} (s) +

    -
    {% endblock %} From 5bd6228a70df37a35d7ab3eb4643ed4e4a4221f3 Mon Sep 17 00:00:00 2001 From: Ahmed Refik Date: Sat, 8 Apr 2023 13:04:54 -0600 Subject: [PATCH 29/46] [MP-144] demo page will correctly display newlines --- docs/flask/app.py | 8 ++++++-- docs/flask/templates/demo.html | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/docs/flask/app.py b/docs/flask/app.py index c4f8924..31ead2d 100755 --- a/docs/flask/app.py +++ b/docs/flask/app.py @@ -95,8 +95,12 @@ def demo(): print(plain_list) print(decrypt_time) - return render_template('demo.html', ciphertext=cipher_list[0], - plaintext=plain_list[0], + + cipher_list=cipher_list[0].split('\n') + plain_list=plain_list[0].split('\n') + + return render_template('demo.html', ciphertext=cipher_list, + plaintext=plain_list, decrypt_time=decrypt_time, encrypt_time=encrypt_time) diff --git a/docs/flask/templates/demo.html b/docs/flask/templates/demo.html index ea88cf6..75aa704 100644 --- a/docs/flask/templates/demo.html +++ b/docs/flask/templates/demo.html @@ -18,10 +18,14 @@

    CipherText

    -

    {{ ciphertext }}

    + {% for cipher in ciphertext %} +

    {{ cipher }}

    + {% endfor %}

    PlainText

    -

    {{ plaintext }}

    + {% for plain in plaintext %} +

    {{ plain }}

    + {% endfor %}


    From 114546daa9c50fee41158342c033bfa57bcf78d8 Mon Sep 17 00:00:00 2001 From: Ahmed Refik Date: Mon, 10 Apr 2023 14:36:24 -0600 Subject: [PATCH 30/46] [MP-144] json stirings carry hexadecimal payloads --- docs/flask/README.md | 6 +++--- docs/flask/app.py | 24 ++++++++++++++---------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/docs/flask/README.md b/docs/flask/README.md index 88432ab..bfad150 100644 --- a/docs/flask/README.md +++ b/docs/flask/README.md @@ -10,6 +10,6 @@ python3 -m venv venv 3. Set Flask application with command `export FLASK_APP=app` 4. Then set mode of operation to either `export FLASK_ENV=development` or `export FLASK_ENV=production` 5. Run website with command `./app.py` and the website should be at address `http://{hostname}:4996/` loaded on the home page -6. There is a sample server.py program which will send whatever it receives from the app `./server.py $(port)` -7. You can kill any running processes on the port by running the command `lsof | grep $(port) | awk '{print $2}' | xargs -I {} kill -9 {}` - +### Notes +- Running `make all PORT={port}` will create a server in the background that will echo whatever it receives and the app on the port specified, `PORT=64999` by default +- You can kill any running processes on the port with `make clean` which will run `lsof | grep $(port) | awk '{print $2}' | xargs -I {} kill -9 {}`, although you should be careful not to kill any other programs which might be running diff --git a/docs/flask/app.py b/docs/flask/app.py index 31ead2d..4c6af25 100755 --- a/docs/flask/app.py +++ b/docs/flask/app.py @@ -28,7 +28,7 @@ vector_len = 100 vector_num = 100 -logger = Logger.init(None, logging.ERROR) +logger = Logger.init(None, logging.DEBUG) client = Client(GI_PORT, socket.gethostname(), "CLI") task_id = 0 @@ -82,7 +82,7 @@ def demo(): GI.Encrypt(task_id, [user_input], client) cipher_list, post_encrypt_time = GI.ReceivedEncrypt(task_id, client) encrypt_time = post_encrypt_time - pre_encrypt_time - task_id += 1 + task_id += 1 print(cipher_list) print(encrypt_time) @@ -136,7 +136,7 @@ def view(): test_vector = Commands.test_vector_gen([int(vector_len), int(vector_num)]) logger.debug("vectors".center(40, '_')) logger.debug(test_vector) - test_vector = [base64.b64encode(bytes(item, 'utf-8')).decode('ascii') for item in test_vector] + test_vector = [ bytes(item, 'ascii').hex() for item in test_vector] filename = "test-vectors-" + vector_len + "-" + vector_num file_extension = "txt" else: @@ -151,7 +151,7 @@ def view(): file.save(os.path.join(app.config['DOWNLOAD_FOLDER'], filename)) with open(os.path.abspath(DOWNLOAD_FOLDER + filename), 'rb') as fd: content = fd.read() - test_vector.append(base64.b64encode(content).decode('ascii')) + test_vector.append(content.hex()) logger.debug("plainlist".center(40, '_')) logger.debug(test_vector) @@ -181,12 +181,12 @@ def view(): str(task_id) + filename.split('.')[0] + '-plaintext.' + file_extension if len(plainlist) != 1: - plainlist = [ base64.b64decode(cipher).decode('utf-8') for cipher in plainlist ] + plainlist = [ bytes.fromhex(cipher) for cipher in plainlist ] plaintext = '\n'.join(plainlist) with open(plainpath, 'wb') as fd: - fd.write(bytes(plaintext,'utf-8')) + fd.write(plaintext) else: - plainlist = [ base64.b64decode(cipher) for cipher in plainlist ] + plainlist = [ bytes.fromhex(cipher) for cipher in plainlist ] plaintext = plainlist[0] with open(plainpath, 'wb') as fd: fd.write(plaintext) @@ -195,12 +195,16 @@ def view(): str(task_id) + filename.split('.')[0] + '-ciphertext.' + file_extension if len(cipherlist) != 1: - cipherlist = [ base64.b64decode(cipher).decode('utf-8') for cipher in cipherlist ] + cipherlist = [ bytes.fromhex(cipher).decode('ascii') for cipher in cipherlist ] ciphertext = '\n'.join(cipherlist) + print("cipherlist".center(40, "_")) + print(cipherlist) + print("ciphertex".center(40, "_")) + print(ciphertext) with open(cipherpath, 'wb') as fd: - fd.write(bytes(ciphertext, 'utf-8')) + fd.write(bytes(ciphertext, 'ascii')) else: - cipherlist = [ base64.b64decode(cipher) for cipher in cipherlist ] + cipherlist = [ bytes.fromhex(cipher) for cipher in cipherlist ] ciphertext = cipherlist[0] with open(cipherpath, 'wb') as fd: fd.write(ciphertext) From 0ee860859a24b8301d40461d26e7a4602c6a6c9c Mon Sep 17 00:00:00 2001 From: Ahmed Refik Date: Mon, 10 Apr 2023 14:41:53 -0600 Subject: [PATCH 31/46] [MP-144] test vector hexadecmial conversion fix --- docs/flask/app.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/flask/app.py b/docs/flask/app.py index 4c6af25..d735a4a 100755 --- a/docs/flask/app.py +++ b/docs/flask/app.py @@ -181,10 +181,10 @@ def view(): str(task_id) + filename.split('.')[0] + '-plaintext.' + file_extension if len(plainlist) != 1: - plainlist = [ bytes.fromhex(cipher) for cipher in plainlist ] + plainlist = [ bytes.fromhex(cipher).decode('ascii') for cipher in plainlist ] plaintext = '\n'.join(plainlist) with open(plainpath, 'wb') as fd: - fd.write(plaintext) + fd.write(bytes(plaintext, 'ascii')) else: plainlist = [ bytes.fromhex(cipher) for cipher in plainlist ] plaintext = plainlist[0] From d2a1eda4e56cf19c89308007b49d5d39352559a9 Mon Sep 17 00:00:00 2001 From: Wentao Lu <39390293+lwt0000@users.noreply.github.com> Date: Sat, 8 Apr 2023 15:07:16 -0600 Subject: [PATCH 32/46] [MP-164] CLI decrypted command (#63) --- CLI/Client.py | 4 +++- CLI/cli.py | 9 ++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CLI/Client.py b/CLI/Client.py index 54edb69..58d82c6 100644 --- a/CLI/Client.py +++ b/CLI/Client.py @@ -42,7 +42,9 @@ def connection_send(self): message = self.send_queue.get() # self.outgoing_mutex.release() print(message) + message = message + '\n' self.s.send(message.encode('ascii')) + print("sent") def to_outgoing_queue(self, message): @@ -80,7 +82,7 @@ def string_to_json(api_call, task_id, interface_type = "GC", sender_id = "0", pa "interface_type":interface_type, "sender_id":sender_id, "payload_total_fragments": payload_total_fragments, - "payload_fragment_number": payload_fragment_number, + "payload_frag_number": payload_fragment_number, "payload_size": payload_size, "payload_content": payload_content} diff --git a/CLI/cli.py b/CLI/cli.py index 0d261bc..beec2da 100755 --- a/CLI/cli.py +++ b/CLI/cli.py @@ -242,7 +242,7 @@ def UserInput(self) -> bool: print("Send encryption") #{"api_call":"REQUEST_HANDSHAKE","task_id":"2","interface_type":"T1","sender_id":"1"}\n task_id += 1 - msg = client.string_to_json("ENCRYPT", str(task_id), "GC", "0", "0", "0", "0", "Hello World") + msg = client.string_to_json("ENCRYPT",task_id, "GC", 0, 0, 0, 0, "Hello World") # client.connection_send(msg) # client.connection_send('\n') @@ -264,10 +264,9 @@ def UserInput(self) -> bool: #{"api_call":"REQUEST_HANDSHAKE","task_id":"2","interface_type":"T1","sender_id":"1"}\n task_id += 1 - msg = client.string_to_json("DECRYPT", str(task_id), "GC", "0", "0", "0", "0", "Hello World") - - client.connection_send(msg) - client.connection_send('\n') + msg = client.string_to_json("DECRYPT",task_id, "GC", 0, 0, 0, 0, "Hello World") + + client.to_outgoing_queue(msg) #recieve the decryption data from the CoreComplex From 8175b20cc2aff66df7ce3a0b6b145ada83d1416f Mon Sep 17 00:00:00 2001 From: Ahmed Refik Date: Mon, 10 Apr 2023 15:00:58 -0600 Subject: [PATCH 33/46] [MP-144] demo payload hexadecmial conversion --- docs/flask/app.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/flask/app.py b/docs/flask/app.py index d735a4a..47eb5af 100755 --- a/docs/flask/app.py +++ b/docs/flask/app.py @@ -79,7 +79,7 @@ def demo(): user_input = request.form["user_input"] pre_encrypt_time = time.time() - GI.Encrypt(task_id, [user_input], client) + GI.Encrypt(task_id, [bytes(user_input, 'ascii').hex()], client) cipher_list, post_encrypt_time = GI.ReceivedEncrypt(task_id, client) encrypt_time = post_encrypt_time - pre_encrypt_time task_id += 1 @@ -93,11 +93,13 @@ def demo(): decrypt_time = post_decrypt_time - pre_decrypt_time task_id += 1 - print(plain_list) + print([ bytes.fromhex(text) for text in plain_list ]) print(decrypt_time) - cipher_list=cipher_list[0].split('\n') - plain_list=plain_list[0].split('\n') + cipher_list=bytes.fromhex(cipher_list[0]).decode('ascii').split('\n') + print(cipher_list) + plain_list=bytes.fromhex(plain_list[0]).decode('ascii').split('\n') + print(plain_list) return render_template('demo.html', ciphertext=cipher_list, plaintext=plain_list, From e48cf2900eb6405306e75b4ec291ec797247f5f3 Mon Sep 17 00:00:00 2001 From: Ahmed Refik Date: Mon, 10 Apr 2023 15:18:54 -0600 Subject: [PATCH 34/46] [MP-144] Xceed update --- Xceed | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Xceed b/Xceed index dfb3e23..44ca62d 160000 --- a/Xceed +++ b/Xceed @@ -1 +1 @@ -Subproject commit dfb3e2304552d069fb22d7ff405ea4fed05b7cef +Subproject commit 44ca62d728c2dd9e404a7e6ef89b334ea1df5c3b From a7db4fd2bfef896dc5c5f52edb29ef4e32969b0d Mon Sep 17 00:00:00 2001 From: Ahmed Refik Date: Mon, 10 Apr 2023 17:05:17 -0600 Subject: [PATCH 35/46] [MP-144] dan debugging --- docs/flask/GI_interface.py | 16 +++++------ docs/flask/app.py | 9 +++--- docs/flask/server.py | 6 +--- docs/flask/templates/demo.html | 7 +++-- docs/flask/templates/research.html | 46 ++++++++++++++++++++++++++++++ 5 files changed, 63 insertions(+), 21 deletions(-) create mode 100644 docs/flask/templates/research.html diff --git a/docs/flask/GI_interface.py b/docs/flask/GI_interface.py index 2b4a326..52a151c 100644 --- a/docs/flask/GI_interface.py +++ b/docs/flask/GI_interface.py @@ -63,7 +63,7 @@ class GI: @staticmethod def Encrypt(task_id, str_list, client): # logger = Logger.init(LOG_FILE, logging.DEBUG) - ii = 1 + ii = 0 for vector in str_list: msg = client.string_to_json("ENCRYPT", task_id, "GI", 0, len(str_list), ii, @@ -74,7 +74,7 @@ def Encrypt(task_id, str_list, client): @staticmethod def ReceivedEncrypt(task_id, client): - ii = 1 + ii = 0 cipher_list = [] data = client.get_queue() recv_time = time.time() @@ -83,7 +83,7 @@ def ReceivedEncrypt(task_id, client): try: decoded_msg = json.loads(recv_msg) except json.decoder.JSONDecodeError: - delim ='.(?={"api_call": "ENCRYPT", "task_id": \d, "interface_type": "GI", "sender_id": \d, )' + delim ='.(?={"api_call": "SEND_ENCRYPT", "task_id": \d, "interface_type": "GI", "sender_id": \d, )' recv_msg = re.split(delim, recv_msg) last_msg = recv_msg[-1] for msg in recv_msg: @@ -95,14 +95,14 @@ def ReceivedEncrypt(task_id, client): cipher_list.append(decoded_msg.get('payload_content')) if decoded_msg.get('payload_total_fragments') == \ - decoded_msg.get('payload_frag_number'): + decoded_msg.get('payload_frag_number')+1: return cipher_list, recv_time ii += 1 data = client.get_queue() @staticmethod def Decrypt(task_id, str_list, client): - ii = 1 + ii = 0 for vector in str_list: msg = client.string_to_json("DECRYPT", task_id, "GI", 0, len(str_list), ii, @@ -113,7 +113,7 @@ def Decrypt(task_id, str_list, client): @staticmethod def ReceivedDecrypt(task_id, client): - ii = 1 + ii = 0 cipher_list = [] data = client.get_queue() recv_time = time.time() @@ -122,7 +122,7 @@ def ReceivedDecrypt(task_id, client): try: decoded_msg = json.loads(recv_msg) except json.decoder.JSONDecodeError: - delim ='.(?={"api_call": "DECRYPT", "task_id": \d, "interface_type": "GI", "sender_id": \d, )' + delim ='.(?={"api_call": "SEND_DECRYPTED", "task_id": \d, "interface_type": "GI", "sender_id": \d, )' recv_msg = re.split(delim, recv_msg) last_msg = recv_msg[-1] for msg in recv_msg: @@ -134,7 +134,7 @@ def ReceivedDecrypt(task_id, client): cipher_list.append(decoded_msg.get('payload_content')) if decoded_msg.get('payload_total_fragments') == \ - decoded_msg.get('payload_frag_number'): + decoded_msg.get('payload_frag_number')+1: return cipher_list, recv_time ii += 1 data = client.get_queue() diff --git a/docs/flask/app.py b/docs/flask/app.py index 47eb5af..b484307 100755 --- a/docs/flask/app.py +++ b/docs/flask/app.py @@ -81,7 +81,7 @@ def demo(): pre_encrypt_time = time.time() GI.Encrypt(task_id, [bytes(user_input, 'ascii').hex()], client) cipher_list, post_encrypt_time = GI.ReceivedEncrypt(task_id, client) - encrypt_time = post_encrypt_time - pre_encrypt_time + encrypt_time = round(post_encrypt_time - pre_encrypt_time , 2) task_id += 1 print(cipher_list) @@ -90,7 +90,7 @@ def demo(): pre_decrypt_time = time.time() GI.Decrypt(task_id, cipher_list, client) plain_list, post_decrypt_time = GI.ReceivedDecrypt(task_id, client) - decrypt_time = post_decrypt_time - pre_decrypt_time + decrypt_time = round(post_decrypt_time - pre_decrypt_time, 2) task_id += 1 print([ bytes.fromhex(text) for text in plain_list ]) @@ -165,7 +165,7 @@ def view(): pre_encrypt_time = time.time() GI.Encrypt(task_id, test_vector, client) cipherlist, post_encrypt_time = GI.ReceivedEncrypt(task_id, client) - encrypt_time = post_encrypt_time - pre_encrypt_time + encrypt_time = round(post_encrypt_time - pre_encrypt_time, 2) logger.info("plaintext".center(40, '_')) logger.info('\n'.join(cipherlist)) @@ -178,7 +178,7 @@ def view(): logger.info("Decrypt sent") plainlist, post_decrypt_time = GI.ReceivedDecrypt(task_id, client) - decrypt_time = post_decrypt_time - pre_decrypt_time + decrypt_time = round(post_decrypt_time - pre_decrypt_time, 2) plainpath = str(os.path.abspath(UPLOAD_FOLDER)) + '/' + \ str(task_id) + filename.split('.')[0] + '-plaintext.' + file_extension @@ -211,7 +211,6 @@ def view(): with open(cipherpath, 'wb') as fd: fd.write(ciphertext) - task_id += 1 return render_template('view.html', ciphertext=ciphertext, diff --git a/docs/flask/server.py b/docs/flask/server.py index a78872b..920dc99 100755 --- a/docs/flask/server.py +++ b/docs/flask/server.py @@ -3,8 +3,6 @@ import socket import sys import time -import threading -from Client import * def server_program(): @@ -23,13 +21,11 @@ def server_program(): print("Connection from: " + str(address)) while True: # receive data stream. it won't accept data packet greater than 1024 bytes - data = conn.recv(4096).decode() - time.sleep(0.1) + data = conn.recv(1024).decode() if not data: # if data is not received break break # print("from connected user: " + str(data)) - time.sleep(0.1) conn.send(data.encode()) conn.close() # close the connection diff --git a/docs/flask/templates/demo.html b/docs/flask/templates/demo.html index 75aa704..d2846cc 100644 --- a/docs/flask/templates/demo.html +++ b/docs/flask/templates/demo.html @@ -4,7 +4,8 @@ {% block header %} Interactive Demo {% endblock %} {% block header_content %} -

    This page allows users to interact with the embedded system with textual input

    +

    This page allows users to interact with the embedded system with textual input + which will be Encrypted and Decrypted in QPP. Just hit the Submit button to send the message

    {% endblock %} @@ -17,12 +18,12 @@
    -

    CipherText

    +

    Encrypted CipherText

    {% for cipher in ciphertext %}

    {{ cipher }}

    {% endfor %}
    -

    PlainText

    +

    Decrypted PlainText

    {% for plain in plaintext %}

    {{ plain }}

    {% endfor %} diff --git a/docs/flask/templates/research.html b/docs/flask/templates/research.html new file mode 100644 index 0000000..ac608e5 --- /dev/null +++ b/docs/flask/templates/research.html @@ -0,0 +1,46 @@ +{% extends 'base.html' %} + +{% block title %} QPP Research {% endblock %} +{% block header %} Interactive Research {% endblock %} + +{% block header_content %} +

    This page allows users to interact with the embedded system with textual input + which will be Encrypted and Decrypted in QPP. Just hit the Submit button to send the message

    +{% endblock %} + + +{% block content %} + + +

    User Input

    + +
    + + +
    +

    Encrypted CipherText

    + {% for cipher in ciphertext %} +

    {{ cipher }}

    + {% endfor %} +
    +

    Decrypted PlainText

    + {% for plain in plaintext %} +

    {{ plain }}

    + {% endfor %} +
    + +


    +

    Encrypt Time: + {{ encrypt_time }} (s) +

    +
    +
    +

    + + {{ decrypt_time }} (s) +

    +
    + +
    + +{% endblock %} From 6aaba870d6e204210e210e4adf6588accc315c01 Mon Sep 17 00:00:00 2001 From: Ahmed Refik Date: Mon, 10 Apr 2023 17:37:17 -0600 Subject: [PATCH 36/46] [MP-144] research page basic html and javascript visibility --- docs/flask/app.py | 2 +- docs/flask/templates/research.html | 88 +++++++++++++++++++----------- 2 files changed, 57 insertions(+), 33 deletions(-) diff --git a/docs/flask/app.py b/docs/flask/app.py index b484307..8312632 100755 --- a/docs/flask/app.py +++ b/docs/flask/app.py @@ -254,7 +254,7 @@ def download_plain(): incoming_t.start() outgoing_t.start() - app.run(debug=False, host=socket.gethostname(), + app.run(debug=True, host=socket.gethostname(), port=4996) #ssl_context='adhoc') incoming_t.join() diff --git a/docs/flask/templates/research.html b/docs/flask/templates/research.html index ac608e5..94b294e 100644 --- a/docs/flask/templates/research.html +++ b/docs/flask/templates/research.html @@ -6,41 +6,65 @@ {% block header_content %}

    This page allows users to interact with the embedded system with textual input which will be Encrypted and Decrypted in QPP. Just hit the Submit button to send the message

    -{% endblock %} + +{% endblock %} {% block content %} -
    -

    User Input

    - -
    - -
    -
    -

    Encrypted CipherText

    - {% for cipher in ciphertext %} -

    {{ cipher }}

    - {% endfor %} -
    -

    Decrypted PlainText

    - {% for plain in plaintext %} -

    {{ plain }}

    - {% endfor %} -
    - -


    -

    Encrypt Time: - {{ encrypt_time }} (s) -

    -
    -
    -

    - - {{ decrypt_time }} (s) -

    -
    - -
    +
    Encrypt
    + +
    Decrypt
    +
    + + + other 3 + +
    + other 4 + +
    +
    -