From ba03eea1a7ee40ee86eeb0b2f16642fa77d2b63c Mon Sep 17 00:00:00 2001 From: Thomas Touhey Date: Sat, 2 Dec 2023 16:09:09 +0100 Subject: [PATCH] chore: add demo and docker --- .gitignore | 4 +- docker/Dockerfile | 58 ++ docker/docker-compose.yaml | 13 + docker/nginx.conf | 41 + docker/supervisord.conf | 13 + docs/Makefile | 33 +- .../remove_first_line_in_module_docstrings.py | 75 ++ docs/make.bat | 36 - poetry.lock | 742 +++++++++++++++++- pyproject.toml | 18 +- tests/test_parser.py | 17 + textoutpc/builtin.py | 12 +- textoutpc/demo/__init__.py | 58 ++ textoutpc/demo/content/default.bbcode | 3 + textoutpc/demo/py.typed | 0 textoutpc/demo/static/css/elements.scss | 0 textoutpc/demo/static/css/page.scss | 88 +++ textoutpc/demo/static/favicon.ico | Bin 0 -> 16958 bytes textoutpc/demo/static/favicon.png | Bin 0 -> 13077 bytes textoutpc/demo/static/js/elements.js | 0 textoutpc/demo/templates/default.html | 27 + textoutpc/demo/templates/page.html | 27 + textoutpc/demo/utils.py | 26 + textoutpc/parser.py | 2 +- textoutpc/py.typed | 0 25 files changed, 1221 insertions(+), 72 deletions(-) create mode 100644 docker/Dockerfile create mode 100644 docker/docker-compose.yaml create mode 100644 docker/nginx.conf create mode 100644 docker/supervisord.conf create mode 100644 docs/_ext/remove_first_line_in_module_docstrings.py delete mode 100644 docs/make.bat create mode 100644 textoutpc/demo/__init__.py create mode 100644 textoutpc/demo/content/default.bbcode create mode 100644 textoutpc/demo/py.typed create mode 100644 textoutpc/demo/static/css/elements.scss create mode 100644 textoutpc/demo/static/css/page.scss create mode 100644 textoutpc/demo/static/favicon.ico create mode 100644 textoutpc/demo/static/favicon.png create mode 100644 textoutpc/demo/static/js/elements.js create mode 100644 textoutpc/demo/templates/default.html create mode 100644 textoutpc/demo/templates/page.html create mode 100644 textoutpc/demo/utils.py create mode 100644 textoutpc/py.typed diff --git a/.gitignore b/.gitignore index bf2ad96..fb7d43c 100644 --- a/.gitignore +++ b/.gitignore @@ -424,6 +424,4 @@ terraform.rc # End of https://www.toptal.com/developers/gitignore/api/linux,osx,macos,sublimetext,pycharm,python,kate,kdevelop4,terraform -.terraform.lock.hcl -.envrc -.vault-pass +/textoutpc/demo/static/index.* diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000..39208fb --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,58 @@ +FROM python:3.8-alpine as venv +WORKDIR /opt/app + +RUN \ + apk add --no-cache alpine-sdk=1.0-r1 cmake=3.26.5-r0 linux-headers=6.3-r0 \ + libffi-dev=3.4.4-r2 \ + && pip3 --no-cache-dir install poetry==1.7.1 + +COPY ./pyproject.toml . +COPY ./poetry.lock . + +RUN \ + poetry export --with=deployment --format requirements.txt \ + --output requirements.txt \ + && python -m venv /opt/app/venv \ + && /opt/app/venv/bin/python -m pip install -r requirements.txt + +FROM python:3.8-alpine as builder +WORKDIR /opt/build + +RUN \ + apk add --no-cache alpine-sdk=1.0-r1 cmake=3.26.5-r0 linux-headers=6.3-r0 \ + libffi-dev=3.4.4-r2 \ + && pip3 --no-cache-dir install poetry==1.7.1 + +COPY ./pyproject.toml . +COPY ./poetry.lock . + +RUN \ + poetry export --with dev,docs --format requirements.txt \ + --output requirements.txt \ + && python -m venv /opt/build/venv \ + && /opt/build/venv/bin/python -m pip install -r requirements.txt + +COPY ./docs ./docs +RUN /opt/build/venv/bin/sphinx-build -M html ./docs build + +COPY ./textoutpc ./textoutpc + +FROM python:3.8-alpine as app +ENV PYTHONUNBUFFERED=1, PYTHONDONTWRITEBYTECODE=1 + +RUN addgroup -S app && adduser -S app app +RUN apk add --no-cache supervisor=4.2.5-r2 nginx=1.24.0-r7 \ + && chown -R app /var/lib/nginx/ /var/log/nginx/ /run/nginx/ \ + && chgrp -R app /var/lib/nginx/ /var/log/nginx/ /run/nginx/ + +USER app +COPY --chmod=644 ./docker/supervisord.conf /etc/supervisord.conf +COPY --chmod=644 ./docker/nginx.conf /etc/nginx/nginx.conf + +WORKDIR /opt/app +COPY --from=venv /opt/app/venv ./venv +COPY --from=builder --chown=app:app /opt/build/textoutpc ./textoutpc +COPY --from=builder /opt/build/docs/_build/html /opt/app/docs + +EXPOSE 80 +ENTRYPOINT ["/usr/bin/supervisord"] diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml new file mode 100644 index 0000000..f414bbb --- /dev/null +++ b/docker/docker-compose.yaml @@ -0,0 +1,13 @@ +version: "3.8" +name: textoutpc + +services: + app: + restart: on-failure + image: gitea.touhey.org/touhey-packages/textoutpc-demo + build: + context: .. + dockerfile: docker/Dockerfile + target: app + ports: + - "8080:80" diff --git a/docker/nginx.conf b/docker/nginx.conf new file mode 100644 index 0000000..fb2d7af --- /dev/null +++ b/docker/nginx.conf @@ -0,0 +1,41 @@ +daemon off; +worker_processes auto; +pcre_jit on; +include /etc/nginx/modules/*.conf; +include /etc/nginx/conf.d/*.conf; + +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + server_tokens off; + client_max_body_size 1m; + sendfile on; + tcp_nopush on; + gzip_vary on; + + map $http_upgrade $connection_upgrade { + default upgrade; + '' close; + } + + server { + listen 80 default_server; + + location / { + proxy_pass http://127.0.0.1:3000; + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location /docs/ { + alias /opt/app/docs/; + } + } +} diff --git a/docker/supervisord.conf b/docker/supervisord.conf new file mode 100644 index 0000000..56f6205 --- /dev/null +++ b/docker/supervisord.conf @@ -0,0 +1,13 @@ +[supervisord] +nodaemon = true +logfile = /dev/null +logfile_maxbytes = 0 + +[program:nginx] +command = /usr/sbin/nginx + +[program:textoutpc] +command = /opt/app/venv/bin/python -m gunicorn textoutpc.demo:app -w 1 --bind 0.0.0.0:3000 +stdout_logfile = /dev/fd/1 +stdout_logfile_maxbytes = 0 +redirect_stderr = true diff --git a/docs/Makefile b/docs/Makefile index 737f2fb..c344aee 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -1,34 +1,25 @@ # Minimal makefile for Sphinx documentation # -# You can set these variables from the command line. - -PE = pipenv run -SPHINXOPTS = -SPHINXBUILD = $(PE) sphinx-build -SPHINXWATCH = $(PE) sphinx-autobuild -SOURCEDIR = . -BUILDDIR = _build +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= poetry run sphinx-build +SPHINXAUTOOPTS ?= --ignore "*.kate-swp" --watch ../teal +SPHINXAUTOBUILD ?= poetry run sphinx-autobuild +SOURCEDIR = . +BUILDDIR = _build # Put it first so that "make" without argument is like "make help". help: @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -.PHONY: help Makefile +preview: + @$(SPHINXAUTOBUILD) "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXAUTOOPTS) $(O) + +.PHONY: help preview Makefile # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -# Install everything with pipenv. -prepare: - pipenv install --dev - -.PHONY: prepare - -# Livehtml build. -livehtml: - $(SPHINXWATCH) -b html $(SPHINXOPTS) . $(BUILDDIR)/html - -.PHONY: livehtml diff --git a/docs/_ext/remove_first_line_in_module_docstrings.py b/docs/_ext/remove_first_line_in_module_docstrings.py new file mode 100644 index 0000000..d452422 --- /dev/null +++ b/docs/_ext/remove_first_line_in_module_docstrings.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python +# ***************************************************************************** +# Copyright (C) 2023 Thomas Touhey +# +# This software is governed by the CeCILL 2.1 license under French law and +# abiding by the rules of distribution of free software. You can use, modify +# and/or redistribute the software under the terms of the CeCILL 2.1 license as +# circulated by CEA, CNRS and INRIA at the following URL: https://cecill.info +# +# As a counterpart to the access to the source code and rights to copy, modify +# and redistribute granted by the license, users are provided only with a +# limited warranty and the software's author, the holder of the economic +# rights, and the successive licensors have only limited liability. +# +# In this respect, the user's attention is drawn to the risks associated with +# loading, using, modifying and/or developing or reproducing the software by +# the user in light of its specific status of free software, that may mean that +# it is complicated to manipulate, and that also therefore means that it is +# reserved for developers and experienced professionals having in-depth +# computer knowledge. Users are therefore encouraged to load and test the +# software's suitability as regards their requirements in conditions enabling +# the security of their systems and/or data to be ensured and, more generally, +# to use and operate it in the same conditions as regards security. +# +# The fact that you are presently reading this means that you have had +# knowledge of the CeCILL 2.1 license and that you accept its terms. +# ***************************************************************************** +"""Sphinx plugin for removing the first line in a Python module docstring.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING, Any + + +if TYPE_CHECKING: + from sphinx.application import Sphinx + + +def remove_first_line_in_module_docstring( + app: Sphinx, + what: str, + name: str, + obj: Any, + options: Any, + lines: list[str], +) -> None: + """Remove the first line (and empty line) of the docstring if is a module. + + See `autodoc-process-docstring`_ for more information regarding the + parameters and + + .. _autodoc-process-docstring: + https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html + #event-autodoc-process-docstring + """ + if what != "module" or not lines: + return + + for i in range(1, len(lines)): + if not lines[i]: + lines[: i + 1] = [] + return + + lines[:] = [] + + +def setup(app: Sphinx) -> None: + """Set up the extension. + + :param app: The Sphinx application to set up the extension for. + """ + app.connect( + "autodoc-process-docstring", + remove_first_line_in_module_docstring, + ) diff --git a/docs/make.bat b/docs/make.bat deleted file mode 100644 index 50ebd53..0000000 --- a/docs/make.bat +++ /dev/null @@ -1,36 +0,0 @@ -@ECHO OFF - -pushd %~dp0 - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set SOURCEDIR=. -set BUILDDIR=_build -set SPHINXPROJ=textoutpc - -if "%1" == "" goto help - -%SPHINXBUILD% >NUL 2>NUL -if errorlevel 9009 ( - echo. - echo.The 'sphinx-build' command was not found. Make sure you have Sphinx - echo.installed, then set the SPHINXBUILD environment variable to point - echo.to the full path of the 'sphinx-build' executable. Alternatively you - echo.may add the Sphinx directory to PATH. - echo. - echo.If you don't have Sphinx installed, grab it from - echo.http://sphinx-doc.org/ - exit /b 1 -) - -%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% -goto end - -:help -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% - -:end -popd diff --git a/poetry.lock b/poetry.lock index 992b248..db08d13 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,5 +1,74 @@ # This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +[[package]] +name = "alabaster" +version = "0.7.13" +description = "A configurable sidebar-enabled Sphinx theme" +optional = false +python-versions = ">=3.6" +files = [ + {file = "alabaster-0.7.13-py3-none-any.whl", hash = "sha256:1ee19aca801bbabb5ba3f5f258e4422dfa86f82f3e9cefb0859b283cdd7f62a3"}, + {file = "alabaster-0.7.13.tar.gz", hash = "sha256:a27a4a084d5e690e16e01e03ad2b2e552c61a65469419b907243193de1a84ae2"}, +] + +[[package]] +name = "babel" +version = "2.13.1" +description = "Internationalization utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "Babel-2.13.1-py3-none-any.whl", hash = "sha256:7077a4984b02b6727ac10f1f7294484f737443d7e2e66c5e4380e41a3ae0b4ed"}, + {file = "Babel-2.13.1.tar.gz", hash = "sha256:33e0952d7dd6374af8dbf6768cc4ddf3ccfefc244f9986d4074704f2fbd18900"}, +] + +[package.dependencies] +pytz = {version = ">=2015.7", markers = "python_version < \"3.9\""} +setuptools = {version = "*", markers = "python_version >= \"3.12\""} + +[package.extras] +dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] + +[[package]] +name = "beautifulsoup4" +version = "4.12.2" +description = "Screen-scraping library" +optional = false +python-versions = ">=3.6.0" +files = [ + {file = "beautifulsoup4-4.12.2-py3-none-any.whl", hash = "sha256:bd2520ca0d9d7d12694a53d44ac482d181b4ec1888909b035a3dbf40d0f57d4a"}, + {file = "beautifulsoup4-4.12.2.tar.gz", hash = "sha256:492bbc69dca35d12daac71c4db1bfff0c876c00ef4a2ffacce226d4638eb72da"}, +] + +[package.dependencies] +soupsieve = ">1.2" + +[package.extras] +html5lib = ["html5lib"] +lxml = ["lxml"] + +[[package]] +name = "blinker" +version = "1.7.0" +description = "Fast, simple object-to-object and broadcast signaling" +optional = false +python-versions = ">=3.8" +files = [ + {file = "blinker-1.7.0-py3-none-any.whl", hash = "sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9"}, + {file = "blinker-1.7.0.tar.gz", hash = "sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182"}, +] + +[[package]] +name = "certifi" +version = "2023.11.17" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474"}, + {file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"}, +] + [[package]] name = "cfgv" version = "3.4.0" @@ -11,6 +80,119 @@ files = [ {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, ] +[[package]] +name = "charset-normalizer" +version = "3.3.2" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, + {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, +] + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + [[package]] name = "colorama" version = "0.4.6" @@ -89,6 +271,16 @@ tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.1 [package.extras] toml = ["tomli"] +[[package]] +name = "cssmin" +version = "0.2.0" +description = "A Python port of the YUI CSS compression algorithm." +optional = false +python-versions = "*" +files = [ + {file = "cssmin-0.2.0.tar.gz", hash = "sha256:e012f0cc8401efcf2620332339011564738ae32be8c84b2e43ce8beaec1067b6"}, +] + [[package]] name = "distlib" version = "0.3.7" @@ -141,6 +333,81 @@ docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1 testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] typing = ["typing-extensions (>=4.8)"] +[[package]] +name = "flask" +version = "3.0.0" +description = "A simple framework for building complex web applications." +optional = false +python-versions = ">=3.8" +files = [ + {file = "flask-3.0.0-py3-none-any.whl", hash = "sha256:21128f47e4e3b9d597a3e8521a329bf56909b690fcc3fa3e477725aa81367638"}, + {file = "flask-3.0.0.tar.gz", hash = "sha256:cfadcdb638b609361d29ec22360d6070a77d7463dcb3ab08d2c2f2f168845f58"}, +] + +[package.dependencies] +blinker = ">=1.6.2" +click = ">=8.1.3" +importlib-metadata = {version = ">=3.6.0", markers = "python_version < \"3.10\""} +itsdangerous = ">=2.1.2" +Jinja2 = ">=3.1.2" +Werkzeug = ">=3.0.0" + +[package.extras] +async = ["asgiref (>=3.2)"] +dotenv = ["python-dotenv"] + +[[package]] +name = "flask-assets" +version = "2.1.0" +description = "Asset management for Flask, to compress and merge CSS and Javascript files." +optional = false +python-versions = "*" +files = [ + {file = "Flask-Assets-2.1.0.tar.gz", hash = "sha256:f84d6532ffe59c9ff352885e8740ff4da25c0bcfacd805f0a806815e44354813"}, + {file = "Flask_Assets-2.1.0-py3-none-any.whl", hash = "sha256:a56c476b15f84701712cc3b4b4a001ebbe62b1dcbe81c23f96fbe6f261b75324"}, +] + +[package.dependencies] +Flask = ">=0.8" +webassets = ">=2.0" + +[[package]] +name = "furo" +version = "2023.9.10" +description = "A clean customisable Sphinx documentation theme." +optional = false +python-versions = ">=3.8" +files = [ + {file = "furo-2023.9.10-py3-none-any.whl", hash = "sha256:513092538537dc5c596691da06e3c370714ec99bc438680edc1debffb73e5bfc"}, + {file = "furo-2023.9.10.tar.gz", hash = "sha256:5707530a476d2a63b8cad83b4f961f3739a69f4b058bcf38a03a39fa537195b2"}, +] + +[package.dependencies] +beautifulsoup4 = "*" +pygments = ">=2.7" +sphinx = ">=6.0,<8.0" +sphinx-basic-ng = "*" + +[[package]] +name = "gunicorn" +version = "21.2.0" +description = "WSGI HTTP Server for UNIX" +optional = false +python-versions = ">=3.5" +files = [ + {file = "gunicorn-21.2.0-py3-none-any.whl", hash = "sha256:3213aa5e8c24949e792bcacfc176fef362e7aac80b76c56f6b5122bf350722f0"}, + {file = "gunicorn-21.2.0.tar.gz", hash = "sha256:88ec8bff1d634f98e61b9f65bc4bf3cd918a90806c6f5c48bc5603849ec81033"}, +] + +[package.dependencies] +packaging = "*" + +[package.extras] +eventlet = ["eventlet (>=0.24.1)"] +gevent = ["gevent (>=1.4.0)"] +setproctitle = ["setproctitle"] +tornado = ["tornado (>=0.2)"] + [[package]] name = "identify" version = "2.5.32" @@ -155,6 +422,47 @@ files = [ [package.extras] license = ["ukkonen"] +[[package]] +name = "idna" +version = "3.6" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, + {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, +] + +[[package]] +name = "imagesize" +version = "1.4.1" +description = "Getting image size from png/jpeg/jpeg2000/gif file" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, + {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, +] + +[[package]] +name = "importlib-metadata" +version = "6.9.0" +description = "Read metadata from Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "importlib_metadata-6.9.0-py3-none-any.whl", hash = "sha256:1c8dc6839ddc9771412596926f24cb5a553bbd40624ee2c7e55e531542bed3b8"}, + {file = "importlib_metadata-6.9.0.tar.gz", hash = "sha256:e8acb523c335a91822674e149b46c0399ec4d328c4d1f6e49c273da5ff0201b9"}, +] + +[package.dependencies] +zipp = ">=0.5" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] +perf = ["ipython"] +testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] + [[package]] name = "iniconfig" version = "2.0.0" @@ -166,6 +474,113 @@ files = [ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] +[[package]] +name = "itsdangerous" +version = "2.1.2" +description = "Safely pass data to untrusted environments and back." +optional = false +python-versions = ">=3.7" +files = [ + {file = "itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44"}, + {file = "itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"}, +] + +[[package]] +name = "jinja2" +version = "3.1.2" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +files = [ + {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, + {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "jsmin" +version = "3.0.1" +description = "JavaScript minifier." +optional = false +python-versions = "*" +files = [ + {file = "jsmin-3.0.1.tar.gz", hash = "sha256:c0959a121ef94542e807a674142606f7e90214a2b3d1eb17300244bbb5cc2bfc"}, +] + +[[package]] +name = "markupsafe" +version = "2.1.3" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-win32.whl", hash = "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-win_amd64.whl", hash = "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-win32.whl", hash = "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-win_amd64.whl", hash = "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-win32.whl", hash = "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba"}, + {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"}, +] + [[package]] name = "nodeenv" version = "1.8.0" @@ -239,6 +654,35 @@ nodeenv = ">=0.11.1" pyyaml = ">=5.1" virtualenv = ">=20.10.0" +[[package]] +name = "pygments" +version = "2.17.2" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, + {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, +] + +[package.extras] +plugins = ["importlib-metadata"] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "pyparsing" +version = "3.1.1" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" +optional = false +python-versions = ">=3.6.8" +files = [ + {file = "pyparsing-3.1.1-py3-none-any.whl", hash = "sha256:32c7c0b711493c72ff18a981d24f28aaf9c1fb7ed5e9667c9e84e3db623bdbfb"}, + {file = "pyparsing-3.1.1.tar.gz", hash = "sha256:ede28a1a32462f5a9705e07aea48001a08f7cf81a021585011deba701581a0db"}, +] + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] + [[package]] name = "pytest" version = "7.4.3" @@ -279,6 +723,17 @@ pytest = ">=4.6" [package.extras] testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] +[[package]] +name = "pytz" +version = "2023.3.post1" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2023.3.post1-py2.py3-none-any.whl", hash = "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7"}, + {file = "pytz-2023.3.post1.tar.gz", hash = "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b"}, +] + [[package]] name = "pyyaml" version = "6.0.1" @@ -435,6 +890,41 @@ files = [ {file = "regex-2023.10.3.tar.gz", hash = "sha256:3fef4f844d2290ee0ba57addcec17eec9e3df73f10a2748485dfd6a3a188cc0f"}, ] +[[package]] +name = "requests" +version = "2.31.0" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.7" +files = [ + {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, + {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "scss" +version = "0.8.73" +description = "Python-scss is SCSS compiler for Python. See http://sass-lang.com for more information about scss syntax." +optional = false +python-versions = "*" +files = [ + {file = "scss-0.8.73-py2.py3-none-any.whl", hash = "sha256:116e0baba328ccb9fd5bfe1a23e678fc294ca049f0f8188295487f1dbe416476"}, + {file = "scss-0.8.73.tar.gz", hash = "sha256:f9ea78b523a3e8787fa038837fb70999f78074eedb3bdeace34885d67a6d5418"}, +] + +[package.dependencies] +pyparsing = "*" + [[package]] name = "setuptools" version = "69.0.2" @@ -451,6 +941,180 @@ docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] +[[package]] +name = "snowballstemmer" +version = "2.2.0" +description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." +optional = false +python-versions = "*" +files = [ + {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, + {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, +] + +[[package]] +name = "soupsieve" +version = "2.5" +description = "A modern CSS selector implementation for Beautiful Soup." +optional = false +python-versions = ">=3.8" +files = [ + {file = "soupsieve-2.5-py3-none-any.whl", hash = "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7"}, + {file = "soupsieve-2.5.tar.gz", hash = "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690"}, +] + +[[package]] +name = "sphinx" +version = "7.1.2" +description = "Python documentation generator" +optional = false +python-versions = ">=3.8" +files = [ + {file = "sphinx-7.1.2-py3-none-any.whl", hash = "sha256:d170a81825b2fcacb6dfd5a0d7f578a053e45d3f2b153fecc948c37344eb4cbe"}, + {file = "sphinx-7.1.2.tar.gz", hash = "sha256:780f4d32f1d7d1126576e0e5ecc19dc32ab76cd24e950228dcf7b1f6d3d9e22f"}, +] + +[package.dependencies] +alabaster = ">=0.7,<0.8" +babel = ">=2.9" +colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} +docutils = ">=0.18.1,<0.21" +imagesize = ">=1.3" +importlib-metadata = {version = ">=4.8", markers = "python_version < \"3.10\""} +Jinja2 = ">=3.0" +packaging = ">=21.0" +Pygments = ">=2.13" +requests = ">=2.25.0" +snowballstemmer = ">=2.0" +sphinxcontrib-applehelp = "*" +sphinxcontrib-devhelp = "*" +sphinxcontrib-htmlhelp = ">=2.0.0" +sphinxcontrib-jsmath = "*" +sphinxcontrib-qthelp = "*" +sphinxcontrib-serializinghtml = ">=1.1.5" + +[package.extras] +docs = ["sphinxcontrib-websupport"] +lint = ["docutils-stubs", "flake8 (>=3.5.0)", "flake8-simplify", "isort", "mypy (>=0.990)", "ruff", "sphinx-lint", "types-requests"] +test = ["cython", "filelock", "html5lib", "pytest (>=4.6)"] + +[[package]] +name = "sphinx-basic-ng" +version = "1.0.0b2" +description = "A modern skeleton for Sphinx themes." +optional = false +python-versions = ">=3.7" +files = [ + {file = "sphinx_basic_ng-1.0.0b2-py3-none-any.whl", hash = "sha256:eb09aedbabfb650607e9b4b68c9d240b90b1e1be221d6ad71d61c52e29f7932b"}, + {file = "sphinx_basic_ng-1.0.0b2.tar.gz", hash = "sha256:9ec55a47c90c8c002b5960c57492ec3021f5193cb26cebc2dc4ea226848651c9"}, +] + +[package.dependencies] +sphinx = ">=4.0" + +[package.extras] +docs = ["furo", "ipython", "myst-parser", "sphinx-copybutton", "sphinx-inline-tabs"] + +[[package]] +name = "sphinxcontrib-applehelp" +version = "1.0.4" +description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" +optional = false +python-versions = ">=3.8" +files = [ + {file = "sphinxcontrib-applehelp-1.0.4.tar.gz", hash = "sha256:828f867945bbe39817c210a1abfd1bc4895c8b73fcaade56d45357a348a07d7e"}, + {file = "sphinxcontrib_applehelp-1.0.4-py3-none-any.whl", hash = "sha256:29d341f67fb0f6f586b23ad80e072c8e6ad0b48417db2bde114a4c9746feb228"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-devhelp" +version = "1.0.2" +description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document." +optional = false +python-versions = ">=3.5" +files = [ + {file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"}, + {file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-htmlhelp" +version = "2.0.1" +description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" +optional = false +python-versions = ">=3.8" +files = [ + {file = "sphinxcontrib-htmlhelp-2.0.1.tar.gz", hash = "sha256:0cbdd302815330058422b98a113195c9249825d681e18f11e8b1f78a2f11efff"}, + {file = "sphinxcontrib_htmlhelp-2.0.1-py3-none-any.whl", hash = "sha256:c38cb46dccf316c79de6e5515e1770414b797162b23cd3d06e67020e1d2a6903"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["html5lib", "pytest"] + +[[package]] +name = "sphinxcontrib-jsmath" +version = "1.0.1" +description = "A sphinx extension which renders display math in HTML via JavaScript" +optional = false +python-versions = ">=3.5" +files = [ + {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, + {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, +] + +[package.extras] +test = ["flake8", "mypy", "pytest"] + +[[package]] +name = "sphinxcontrib-mermaid" +version = "0.9.2" +description = "Mermaid diagrams in yours Sphinx powered docs" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sphinxcontrib-mermaid-0.9.2.tar.gz", hash = "sha256:252ef13dd23164b28f16d8b0205cf184b9d8e2b714a302274d9f59eb708e77af"}, + {file = "sphinxcontrib_mermaid-0.9.2-py3-none-any.whl", hash = "sha256:6795a72037ca55e65663d2a2c1a043d636dc3d30d418e56dd6087d1459d98a5d"}, +] + +[[package]] +name = "sphinxcontrib-qthelp" +version = "1.0.3" +description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document." +optional = false +python-versions = ">=3.5" +files = [ + {file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"}, + {file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-serializinghtml" +version = "1.1.5" +description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)." +optional = false +python-versions = ">=3.5" +files = [ + {file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"}, + {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["pytest"] + [[package]] name = "thcolor" version = "0.4" @@ -461,6 +1125,17 @@ files = [ {file = "thcolor-0.4.tar.gz", hash = "sha256:289ec8650393723aeaa34d999427775762d28e6ad53f1705c4d81d6f6d25b38e"}, ] +[[package]] +name = "toml" +version = "0.10.2" +description = "Python Library for Tom's Obvious, Minimal Language" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, +] + [[package]] name = "tomli" version = "2.0.1" @@ -483,15 +1158,31 @@ files = [ {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"}, ] +[[package]] +name = "urllib3" +version = "2.1.0" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.8" +files = [ + {file = "urllib3-2.1.0-py3-none-any.whl", hash = "sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3"}, + {file = "urllib3-2.1.0.tar.gz", hash = "sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + [[package]] name = "virtualenv" -version = "20.24.7" +version = "20.25.0" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.24.7-py3-none-any.whl", hash = "sha256:a18b3fd0314ca59a2e9f4b556819ed07183b3e9a3702ecfe213f593d44f7b3fd"}, - {file = "virtualenv-20.24.7.tar.gz", hash = "sha256:69050ffb42419c91f6c1284a7b24e0475d793447e35929b488bf6a0aade39353"}, + {file = "virtualenv-20.25.0-py3-none-any.whl", hash = "sha256:4238949c5ffe6876362d9c0180fc6c3a824a7b12b80604eeb8085f2ed7460de3"}, + {file = "virtualenv-20.25.0.tar.gz", hash = "sha256:bf51c0d9c7dd63ea8e44086fa1e4fb1093a31e963b86959257378aef020e1f1b"}, ] [package.dependencies] @@ -503,7 +1194,50 @@ platformdirs = ">=3.9.1,<5" docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] +[[package]] +name = "webassets" +version = "2.0" +description = "Media asset management for Python, with glue code for various web frameworks" +optional = false +python-versions = "*" +files = [ + {file = "webassets-2.0-py3-none-any.whl", hash = "sha256:a31a55147752ba1b3dc07dee0ad8c8efff274464e08bbdb88c1fd59ffd552724"}, + {file = "webassets-2.0.tar.gz", hash = "sha256:167132337677c8cedc9705090f6d48da3fb262c8e0b2773b29f3352f050181cd"}, +] + +[[package]] +name = "werkzeug" +version = "3.0.1" +description = "The comprehensive WSGI web application library." +optional = false +python-versions = ">=3.8" +files = [ + {file = "werkzeug-3.0.1-py3-none-any.whl", hash = "sha256:90a285dc0e42ad56b34e696398b8122ee4c681833fb35b8334a095d82c56da10"}, + {file = "werkzeug-3.0.1.tar.gz", hash = "sha256:507e811ecea72b18a404947aded4b3390e1db8f826b494d76550ef45bb3b1dcc"}, +] + +[package.dependencies] +MarkupSafe = ">=2.1.1" + +[package.extras] +watchdog = ["watchdog (>=2.3)"] + +[[package]] +name = "zipp" +version = "3.17.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +optional = false +python-versions = ">=3.8" +files = [ + {file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"}, + {file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"] + [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "a044960cad543cac882e1c301120fbd2fd5c7a3fa6859ae4f5a6f429b2363101" +content-hash = "f53e8a48b99a511dc9abb02f191abf24ee1617baae006fa8128bb25d2d6c8b73" diff --git a/pyproject.toml b/pyproject.toml index f5e7d4b..5caf937 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,8 +24,13 @@ classifiers = [ [tool.poetry.dependencies] python = "^3.8" +cssmin = "^0.2.0" docutils = "^0.20.1" +flask = "^3.0.0" +flask-assets = "^2.1.0" +jsmin = "^3.0.1" regex = "^2023.10.3" +scss = "^0.8.73" thcolor = "^0.4" typing-extensions = "^4.8.0" @@ -34,8 +39,17 @@ pre-commit = "^3.3.3" pytest = "^7.4.3" pytest-cov = "^4.1.0" +[tool.poetry.group.deployment.dependencies] +gunicorn = "^21.2.0" + +[tool.poetry.group.docs.dependencies] +furo = "^2023.9.10" +sphinx = "^7.1.2" +sphinxcontrib-mermaid = "^0.9.2" +toml = "^0.10.2" + [tool.black] -target_version = ['py311'] +target_version = ['py38', 'py39', 'py310', 'py311'] line-length = 79 [tool.commitizen] @@ -100,7 +114,7 @@ lines_after_imports = 2 multi_line_output = 3 no_inline_sort = true profile = "black" -py_version = 311 +py_version = 38 use_parentheses = true combine_as_imports = true diff --git a/tests/test_parser.py b/tests/test_parser.py index 22abf71..8fbae7f 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -17,6 +17,7 @@ from docutils.nodes import ( container, emphasis, literal, + reference, strong, ) from docutils.utils import new_document @@ -170,6 +171,10 @@ def compare_nodes( "the message is: [rot=13]uryyb[/rot] - the - [rot13]jbeyq", [Text("the message is: hello - the - world")], ), + ( + "[rot=25]ibm[/]9000", + [Text("hal9000")], + ), ( "a`[code]`b", [ @@ -205,6 +210,18 @@ def compare_nodes( ), ], ), + ( + "Bonjour [url=https://planet-casio.com/Fr/]le monde[/] !", + [ + Text("Bonjour "), + reference( + "", + Text("le monde"), + refuri="https://planet-casio.com/Fr/", + ), + Text(" !"), + ], + ), ), ) def test_parser(inputstring: str, expected: Sequence[Node]) -> None: diff --git a/textoutpc/builtin.py b/textoutpc/builtin.py index 48641b1..f6c08a4 100644 --- a/textoutpc/builtin.py +++ b/textoutpc/builtin.py @@ -23,6 +23,7 @@ from docutils.nodes import ( literal, reference, strong, + target, ) from thcolor.colors import Color from thcolor.errors import ColorExpressionSyntaxError @@ -649,7 +650,7 @@ class LabelTag(Tag): :param children: The children to process. :return: The produced nodes. """ - yield reference(name=self.value) + yield target(refid=self.value) yield from children @@ -680,8 +681,9 @@ class TargetTag(Tag): if self.value is None: # pragma: no cover raise MissingValue() - yield reference(uri="#" + self.value) - yield from children + el = reference(refid=self.value) + el.extend(children) + yield el class LinkTag(Tag): @@ -732,7 +734,7 @@ class LinkTag(Tag): if url is None: raise ValueError(f"Not a valid URL: {orig_url}") - ref = reference(uri=url) + ref = reference(refuri=url) ref.extend(children) yield ref @@ -1022,7 +1024,7 @@ class SpoilerTag(Tag): [spoiler]This is hidden![/spoiler] - [spoiler=Y a quelque chose de caché !|Ah, bah en fait non :)]:E + [spoiler=Y a quelque chose de caché !|Ah, bah en fait non :)]:E And it's multiline, [big]and formatted[/big], as usual :D[/spoiler] """ diff --git a/textoutpc/demo/__init__.py b/textoutpc/demo/__init__.py new file mode 100644 index 0000000..4b05087 --- /dev/null +++ b/textoutpc/demo/__init__.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python +# ***************************************************************************** +# Copyright (C) 2023 Thomas Touhey +# This file is part of the textoutpc project, which is MIT-licensed. +# ***************************************************************************** +"""Demonstration website for textoutpc.""" + +from __future__ import annotations + +from pathlib import Path + +from flask import Flask, render_template, request +from flask_assets import ( + Bundle as AssetsBundle, + Environment as AssetsEnvironment, +) + +from .utils import to_html + + +__all__ = ["app"] + +STATIC_PATH = Path(__file__).parent +DEFAULT_TEXT = open(STATIC_PATH / "content" / "default.bbcode").read().rstrip() +DEFAULT_TRANSLATED_TEXT = to_html(DEFAULT_TEXT) + +app = Flask( + __name__, + template_folder=STATIC_PATH / "templates", + static_folder=STATIC_PATH / "static", +) + +assets = AssetsEnvironment(app) +assets.register( + "js", + AssetsBundle("js/elements.js"), + filters="jsmin", + output="index.js", +) +assets.register( + "css", + AssetsBundle("css/elements.scss", "css/page.scss"), + filters=("scss", "cssmin"), + output="index.css", +) + + +@app.route("/", methods=("GET", "POST")) +def index() -> str: + """Process input for demonstration purposes.""" + if request.method == "POST" and "text" in request.form: + text = request.form["text"].strip() + result = to_html(text) + else: + text = DEFAULT_TEXT + result = DEFAULT_TRANSLATED_TEXT + + return render_template("page.html", text=text, result=result) diff --git a/textoutpc/demo/content/default.bbcode b/textoutpc/demo/content/default.bbcode new file mode 100644 index 0000000..2378ff7 --- /dev/null +++ b/textoutpc/demo/content/default.bbcode @@ -0,0 +1,3 @@ +[b]Bonjour [url=https://planet-casio.com/Fr/]le monde[/] ![/b] + +Vous connaissez [rot=25]ibm[/]9000 ? diff --git a/textoutpc/demo/py.typed b/textoutpc/demo/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/textoutpc/demo/static/css/elements.scss b/textoutpc/demo/static/css/elements.scss new file mode 100644 index 0000000..e69de29 diff --git a/textoutpc/demo/static/css/page.scss b/textoutpc/demo/static/css/page.scss new file mode 100644 index 0000000..dc25df3 --- /dev/null +++ b/textoutpc/demo/static/css/page.scss @@ -0,0 +1,88 @@ +* { + box-sizing: border-box; + font-family: Georgia; +} + +html, body { + margin: 0; + padding: 0; +} + +body { + margin-top: 50px; + overflow-y: scroll; +} + +.container { + width: 100%; + max-width: 600px; + margin: 0 auto; +} + +.header h1, .header p { + display: inline; +} + +.nav { + list-style-type: none; + padding: 0; + + li { + display: inline; + padding: 5px 10px; + } +} + +.demo { + background-color: #CCC; + padding: 2px 10px; + margin-top: 10px; + margin-bottom: 10px; +} + +.inp { + margin-top: 10px; + + textarea, .inp input[type="submit"] { + width: 100%; + border-radius: 4px; + } + + textarea { + border: 3px solid #CCC; + border-radius: 4px; + min-height: 150px; + padding: 5px 7px; + box-sizing: border-box; + background-color: #f8f8f8; + resize: vertical; + + transition: border 0.2s ease-in-out; + } + textarea:focus { + border: 3px solid #555; + } + + input[type="submit"] { + width: 100%; + background-color: #777; + border: 2px solid #555; + border-radius: 4px; + color: white; + padding: 4px 4px; + text-decoration: none; + margin-top: 5px; + cursor: pointer; + } +} + +a, a:visited, a:hover, a:active { + color: #707070; + text-decoration: none; +} +a:hover { + color: #505050; +} +a:active { + color: #202020; +} diff --git a/textoutpc/demo/static/favicon.ico b/textoutpc/demo/static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..8e0d4fb470aca6a40a413c5222f76ff614ff9ba0 GIT binary patch literal 16958 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 literal 0 HcmV?d00001 diff --git a/textoutpc/demo/static/favicon.png b/textoutpc/demo/static/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..145d4a57c853738f2cec6743fb76885661440bae GIT binary patch literal 13077 zcmc(E2|SeF_y1$dl0sCJs3;^m*-Z$cvSi;&$U0df>yRX}d`OgSl8`-n*0B`HGWKmS zmXKv^!;CT8|MB^JKcC;He|b?&|MJomZxoco-6&ikAjV|Y*J1j{)V002(t z-o9lF);Euci54uc9AAzBD|#38yXpW?k$9Z+fC2n}(ebwNT>uEW3IO3R0N@b375*Cl zo=O71ngakRy#s)=Pcj>fRKN$v9_s7d0;os756uN{z&p%OZd>{S!0}T@L^CuKMgjoN zVBK5lCXZ(^>4AUDn7-{o*7ra|xAv1a{Q`by+z^aleH|Wt{kiamdn~NawQsTCVz}jZ zlXtRH`1xoqx zit)2s=3Cjw=9TH-mKFS$dEz$GV`0XBPBlCL6@dq4JsD@|p4DAA(778QF#qrkoBrdK za1njrwqDy$hq5bH9d|xNrLbgGCD5>^9<=!OD{9<|&|_}v?Tvdy-`WebekI_MPi2?C z-qDVLi!wf5m0z>J(o73_cp`vF}>$E zFBuySIf|9iU(Pt+^C-!}ou7sq0REVN%G&1K>&@_3oka|=(DAwbBD#x-na5|h(Q?(fClUfF30hga})GH^e?r7*$rgd=5&OMk$a#cXWtocFzV0?##a znCPXHt|mk|s^7aP^DrT^Qsj;u4fOTojK_+w{PNe9#{xpq%u$jxwB>c(+WIjL1Dvdv ziq$U(wmSJmNOn4>{$LL*>Px<|a`TH**P|;Rl3Bh0eLpS&92`b3H=-|Q_SBIqV={Kb znYwu8z2&-T)n6p*5_puHpRCmghpYg0;^9yGdQN;cQF5N=ICQWP-7&l!L;K08KkFg= zsi8+_xRg%ZlT$FL!0Im*>u2}IJ z+~64-ew5y~B$^zZ+U;V0J$~e}!*(mnV@4VvSw1Qp5Oe_MyqT__i%K70dGg%v(j|w* z&%ir}lk_Lw9Y;PWvYr3#U>(NNaKX$C<|B4oTrGHrCfa0baXx3ix=c+M<;-hCbgS5amurTii=TXkr3F~t4 z{sS?3%5wDG4l=BCkvWBHi0Q`V7VBsns&K-2mP7|pTQ3qVHbA2hzY3lmv@0GzTMC<3 ze6~9uf0lag<&A3hk|{GCo6Tw`&vy#whdrSMH^jXnxY?zvwC`}qzO7cgqevEvT-_Bo z+cnFSdRkqHE)r+ z7imRL!!`@w;4QhcNlT;VlwaRx{OcF7_Tq%>H_Cd+yot#$Tl9Wu&Xk zHrcY%*~X8V{E&q&XhwdPaG=&WR3sG8MEd!=rvNuw?Lp8v13Boj&L_<^;=Xcb6{ehv zKQmt3dFNXo01!Oo{Lhi1GEcX@2)=YG@{*xFQR9Y3at779!KT3Q6ef1MvpbmGc_`7Rcl)WclfQ{|)8e@2%ypRaCLk2n(RTtKZA?zEOQxk#N7| zzRJEyfD|`%4R267`*q&{iD_D{jE9rki;kypDc?bnd!dxo?DPJ@A8y;A=p!GK1pPlG zMZJpa7NCjQmG?Qt0~Fuat$gJs=oY*wgKyH$Mv&Q%*`ZT$iprg#GS2t1)dGF&CnlAf zq1uv#(^7e!yVcnk$QR*BUvw1l zRo_@YiF^VbjhdGIFFCK-VC8m2#FJZcZn-wtWUU~I5?T}{hQ_Bc)M5b<@U#Yt(?(f*@|`U zv(PX_!ASGX2A4(yUSqlbPa*=6pb>iBcbf4ocX(zm6K%hR)$$)6snjjH0aTscL_TM{ zne<}pYwcsn6p2vPYntEBL>Q_${$Q_vxINo=V>+<-nUig|DC6}Pz+#V$?rFVOw>ER{ ztf{)EL3~GceDJCc-QDNK5mFf_`HkWa)AQUs^I52`K>x#&?5;n2b8r1-=4)E&|7tv@ zytPE|Pz$@gB~ILj!@Ro3JUPGi-H(Y#X{B+6Zk~lyt$XV{^^!U3TKz=e$Eih+o>~Q@ zoMABYN7`@0`i%FeZMAUHr;ORNlhrHXq2uzbG@MxXq7%SxD7<&^YK2D3DUSEN!ZA4o zy@%>3ss^fnjX!9iVrcx{Z7)RmSA6HEQ_vYE$`p5U9=pcR`ge6*v-fcmxw%eKOvZY; zBK&b%3Zt_@y(?Ab(nU*F-UEsztR0x~Ns;E`C#S_cWsemgFTc0oL z+ZRy0&7!b$M^ABYkF$6)#N(b({%u6c4~1CG$IR|^sh;JJY9flo%?*&N)9blL__0(s z`kR384H0e{D5t?9-4pS5R`kXPG|N#B42uFg_=EAb1HrvX@h)jE4OBz~UWpxAu-@Kd z3eUS9yOM1g)RSW`<_{;g(6KRz&O~?*7gRS>qg|fj{DMwrkL(VgHB$-Okg6)>c{SrP zfRxpC>&c#ZNj_XVYb>2)a?+yR`NUoT?K5Ha?vK))hb&Q=kqS>gH7x(WA_H$C=bbM@ z{ZEdn$F}{>Aq%OsuYAPQ$ia zl$$^03RRh|f;tp+-B9D?2R{>YtzJqsrmoNfrR-Szvu9mGm3+RAZ$HZEZ044TIMYK3 z>C*hpAXRnCO>VzH{Ku#o*{mk2`B3heoCo%2?=m|0ntdLqeX)|1Aqjj5A3tXdgaf%n z1NW8MA17z8VJe74$*%e`Ny4$VnR3S^7_;uFW9p>Qczx9i9|)rpQR6dN=%?19PfSa3 zZeySG$mDt<7BgR45nk?upX#pjH1T-`1*&D_aPqNFB`YtCbBxEV+%b<69TpV7y#K=v z_0AGV_$DlT?l)@zyuyO7w&dD)r>5MJ!#3M^Lho-kIX7HXd=73Q;iN=d^-9nSQFWQ? zkwn5ta$wDtE8Cx4 zD=jW_sD#K)GthK@QmrgYvdoi4I<$rrE!7uH)d++v-o59tyjnBEKZ|S`vR80nrgy)T zMSEk>1&da=@`Qj@5&{LMVQ*VItz+4m#k zf^mw8@M3GkKoYnA`Cvy;Q|;_K8D%`zVoR2rre(WF7-SNZTB!JOhJ3z9T9pqx$L)e6 zcT2^tM;gX*Pbi*)l%orQ)2y@>K*MwSTC*S_Ue7mc+ua5Gn+I(z?qwbhW^$=N?Mms^ z?5y$xnJzzJcgb&65UQVQ8%Sd8C-gLjvWcNEZ90`d_bx|0c-0_IOq>!x)*mlpKUK~D z>->1@h-=xCQx`6=Gid^2EIC(M!(TyJRcHZb?mC^C;H~%dTWy6=3xkYiVXK)xy-+FT z^}@#sZoZAY_N|@GNS`j1M#VKJI}$PLmM&G`TW}$Nqak#!C2&9CiDBwGL^*i3q)-5@ z=AK*}aPGH1teman6;adM*fOK>4%z8D*@LNW9LTA z%>xD2$4|%~D=wd|t8V)J#S*SGpXZDGwO-~dws6M{!>TD6z;Y6jwmzWbL}yUYk@-R+ z+F{sRz^RFG2L1Hj7bjL^YU)jeM_LZ8SIfAVE7}uRRB;8JjuNF7BrVQyrMSM=dg#+< zwHU)5|1Me|3&Asfs%)$Nu6QM>$l^SYo#hp8C80K>?xQD_Emf$rKRa% zYxW`x-b|fZSk5>IhOBbkyEf>$E8`LVPUdS>*XljQu;A>-2d%i@iFz4v*xRdqcuo0Nm=G=rLXqZ6t)Z_GwmVjS8D1?XB~Y?ldouR7@i&6kGJkgJHdx5 zEX!y2JjPmEb@g_CUk#f>Qs;Vh(_N9BvAqI?B8_|&`B6~`iI<+iD$nMl4wLoGuTRU& zhCVA4Wg)`r1Ckb&F(k25g7vku)Z;0Mx&lD5O6{JZY{P^b8 zd*SaZ@k9!Qe&Pc$-4OA{U=7W8G?Q{@1UrP35!hJ)o z`<{j;$O_S&J?_!juM&y8Kx* zrT%FY=ShyXL?6HbVEDmD`=s@Pp;+eeE0T>OAHu($WmlyEBTo8?B?~?G_a?P+;rdxO zNAD{{e14;G`F6atBYpYt2}Q@07?XF)o&vLm<~h-1_KR@to55oqbQ-{=63(#-0h-=l z_41FtFAFBE-BpxnaIe3cp2;jo!wFoHyvTGWJa_-?^FjQJS9I-D%WziA&-l5oLw7{x#c=49%mAdpPGIL5o3ylI_!)~^wdX#GoIWq=wz6B&_CouifzmnAz23P^D zn2XVwTygjmpK0G#T}&o6uaY*?_s2!<)+7z7{BO?loi-6hlE38PG0C0TjT&*de2$pB zl$)6vZP`OnSraV6vL2-dch1pQ0Vy{%H2P()hR@00#pmZem^FdUFPHNGB{!^VRqSB) zm5&*?G`P;){OY4Vcjr-Hlg|FZEL9s#_l#{0jyg(oVc>joRKWW5`)3u|S=j)#VDL4PXKjbqv5c8U}y{Z0Uh?{$K5*452U$-Jkc5a)eSev;ZquTY)4Ewz5E`PQdUVu@@Q5X`aj<|Dro+#O$*LQ z2aYrc65YRMKL)ms{agF=zuHIrXlVZ$XBlkM{;O?%0sI4AOn?5U6Vy3?T~AwA8=UBf zO|YP%08N1D*fGXq3`~rSjLghTEUa87SdSlP2}(!LaEy_OndLaxp@tm*SBj37o{oWmo*sM}3O)zu zIT%h|l2Sj$Y4m{cvNxA>L{biufJQ~zY2(3dL79g>&zV`yoaN>@cjc;(u*fx8xf}8d zib|Tdw6t|@>+0P%F*P%{u(Wb~DKk440&iol-YqeBjM_SvrD= zOCu?#qK#QV#(4YmL!UvGGlH`7S9Xp>`%|+2Il-R)Z%Ou#VE-Z46u?SH1AaU@4gdly zKK>X>sKWj6F;*D(|4ejgA7g7=TyV`)plP!1LRIGY$0@yygDJlyV?B0rk76x1m?IS! zhLL=!K)DVMLYAijU68|lzNriS-2T5rL$Lb&|bCM9TR6xc!R=A9F zjW$;J!{2n$E_Q^{MFsMyz`+U?uuI&49YU!<0&IW15v5tbO9dt;;7cTP!h1inf`2&N z`**%;qn}Xvzfl2+1w?Pp$C`g<@jn<>tyYf4`3n(Xrzy_1r`>ju8yS#5gnzIjcTtUN zep!N5%F?}ioMw;Lgg?e&6DwJSKZgHJia{fTRA8x&#CSMn>-Lxm?4l@iWcP-;R%BWa zV%8B$dS9r;^|n3qrt=%wp$W_#F^oylN!L@GpJp)DXViF!(D#(LRnCy5Y~M^G(F{Sl z$U_C*!6`SXz(%pD2J}(jzKo5WysQ2;?3d5^=0eS2^ShAeA@PVE$r<>eVm&KdOzkNHO;&=d{lxDlG>!uhJHs*16qtNL0pvKvF<3xm4pGPfTN zYHpATgu`eI4yFsLfZz+Smt>LP`sDFpGDsMFHd2vZ@i_3hMwy7Hx^gA%num~|o5pyE7n zYBdXOzP9~_IiCX5%(%RH3bw8_YJMC%Tr0ur6d-pZ`%qNi)OANnHWeVUl?FdZUV-fGgPqK3(!RrMLA{H)+V_qM zq?N3I8typNloaLqJDm|X4yU~NMX-h~WpAvgEe=&j1(i<9q;aOqxwic@s>?zXvRemW z9*)bqA?2Qv$fZ@A_^ozyr3W}%KY0=y&c>0O3V2mlBcu48v9N`wjsB3OaXtiQ#C#mG z9TNx>nxYJ6Q|xBG9Ico@?h<(EXwqCJKZ1VqZ5xD?JGKO;7<&+XvJH z+eXJi=C5EO1C%~o2&~7JSm!d~xH-ypppBRZwJQx#P0ppf-$2#vC=$_mzD?JHzW)wp zM>^5Zo1IB>-@=P}1oBYvIbmNEqAe0y~v~fLnX_)eB56Jka7YrjxJ$CxUECtXe)T{6{M{bGHSSu>AP< zAqc^zTt-??;D%rc{7pXl1)pF&&;z#plApsvkUw|h_)};ETp|tD)U`s8Ci<5<9t1xi zCzA7sXyv|LM6{O_V)}4AUTW2DP3O&4x@3%P%eS4=Ivb0+o}!d9DjK_ZOd}+q${F@r z5lID#KANr@l?SEpn#*RmG-_lzl+^M*%ZrszR$1g5ec0YX)a)g1rB(5FWUi=DM%Qtv z{M#{5^41h(G;w|xhauhbZQLvUJm5RJ5jdIc$}3-g;n88}thRYHi5bUM4g1#9i>{+s zIC3M2c6%7;f&tDY3Dj7)eD4fsxs<)*V3nb6{iSor1HBOV9u=r0X9SQc(4eMFaT5`x z52-+V3Kb}fqymBnqLcsE{%a$HyA|cpr4ZM+EVMhMFC>A@Y8+CllDG#G`Z+@iuF4E% z)9ayaz7=w&i`2_c5CSdfFk)Vx3XH(OLBMUIwDOjvbS8HyH^V_hCsleFG;&ExpUfRU zKEo6F!4-*tW-VjDFGci@1NjMccUzq2pTtptZ&?2gD$tV;!3ALpOG#OX8@?DLw-j5P zlIb13hxPZD>x!s=cvqGEZ^U*A0k+ETM7#lu48u&|k0c57{9Z>A$OwZ~2NEzkJZKHntSs68p*R3 zAF|trBvS$TMi~|P9X6s4#(qb1m(&d3-N{^mY!FS^&K`(%6ZuJZWU@eu-9<*@ilcWV zUc%-t5G4-nr&P$`U8M8OSG?l7Ek0buav26%_8dn(dr}P*2%d@(hs}&k-!IsA+HX3K z4-UH!7Rn}J*8i+fy{w?C;Vh0H2Rk%?uLQ!|>bOyxq3+4|ZR9S_i^{A&vG}|oaxH1{ zjnQopch07se~hi0JO6GMg@G(8-uRisLqrxE&HS)4gyFI(e;lgHo##U$|~8AzNpLaGJz!&SX!N8yt9kfc-Ea3sC}L zJrF#3UBTG*ZNjwK16EbW`>+F;=EosF^uevBtyyw8Mi~~}gQ`wMkK(>{pG>gC4BdQ>KZGhw@3k2=Luuf8V-^AoDZpLv_VQ_Q}e8&WsF#Efv<5 zi1u+?*@;6G2oF*b(ffHLa%^7+um&^&@;%OxZ+0&oN~$42N=-Z0#0_Q z?b_iD#1vW$Gi%`CxHV>&k}FdkH-h0pb}5w&P1}_7GeMzS{x*$NAU4{C)JOoWJJf%x z=YnXvQB}6+WEi(KIgMz(GSk?NiE|7Myf6cwz=dJMHgKI%_hW1Zjc@!|E;KFFQaxRY zzj1lENN&s6JRAAtb%XSNA^`+{w3N4LR_R?N5Z6V(^m@vsAhc?ZQdA%?t!GyU#17`n z`!rfyhJO>IgjG6(R7(ZIsen~a>0N$`HLMj@8en$7vr7md_J+aUNPJnfg@*4#HqaQh zX+pM38p)zlJ~((>SW9Cu&vyteSQe~6k}lUB4u$ny0IkVg0Voc-g{9OTK&J6!b)8nx zBmwwC^7x-y^m~=pOiP!lyFhzT0qF|A2N^lpf~-2I+ni|x{T-zS8KT`nbiui7;ykdR z9q&hOmunasz0M4RpVy9CGcSlL#zA4}2m`D_lvRRyoN3hOD;T?=Ah@Hk+WdY32x`S( zZ0lzEX+Ke&zFVe#XoC9z%qn+5g5dw>dfUZ;vS-Th5G!o7ZuQ>0zY8npjUUhZC1a{FKUFO)sIgfME*cg zUN?f^BJ~~REmpg#C=~I7j7+Nv*l|(ps;dcZvg2Q;fR6VMDsZ@8dNL*+y~_V5F3vBN zH?mruV0|R{W@V{ye_zd6S82!iCDQFM#t(W3dhR3KzQljp&)^H}5f%6pm_gc_>y)ZO z&wh2A=XLD!U3;RUd2ZiS=)66$yQM2VDZTO28hTPKU{eJx58c6Z%4~$XC1caw6W(_7 zC^Y8&7B!B4?9=0j3gIt?xsvPfeD-No2iVL_9Ijk`CkO*tRyJ^ll@-j51S4t*ohpgs zLYtFS0l0F3U#Pl(kVA7%%X(vQlUu64Omp3Z@{^!jhTshL%Bus3V2F_2*XU2o+%J2AweflV zq6C?rYsVd{_asPW4I@hhN{fvYV0z^!w!+CVjK@<3t0-4c z{wow~5KcLj>#rMD>_}YxR%nM#Bcz6%`Hmo3XF7IcDCFP`vjZgZ(EKGy*nn^xO9fP5 zxFGxzcb{wYSh@oCsfS5g!LP&<2k_5Fz|1LZy%)U#J0lgJJ-Z({AMc2PxRE<&8X<{l zEu+OdAuh!86ciP}C}H2;!I!y>+BgRXhS4g-UEVw-q2KTSXab`%SZQEDCl3j|0Zrq^ zf})Nq?L5!k+qY{PDvVu@FL96j5gO%mKO48ZyMNVgPij?9q12O2lQISJHlzXQ-FiW85;a%TyBt^(V)sWuK=1b1Q~QHlk2nYZ4^#e zuRUb~jCc39t~T4qZx0eE0ThBn+OI^KgBEXcJQV=9ATG<>$}EeVw-Sc$*#&Xt$)j-D zyzZc9zMOOg1|r~?y2-FU1jQD#{&nP>#Kyxh|CsGWqQDVWUCB7Ys%dw>cEV6>WrXZ2laG24PcSg9wTR7%f4G zI*{PK@;t3T`OefX96vu6blk4P#nI0rT{*)0y-lS!UM%@-4f==a#HS+N@chiHknOn8 zDm>VWsEwSPtn=FPg*P9XtIok#0_~w-7NkD}L2#nrn;#vlfM8;Z3OG;!&E2MQ8H&|r zzdw;puQStB7~A7J^nKyVyV1iMiRfaI8E(TTy``93{EM>ka~BWe+gRc27;3POeiO() zWiP%#u-zdaF5V!H-=7;rq5`QvHjF6esCRed&R)gDvkqoqv1-y_`Hm955K(lwKV%lY zY0gwo8YJ}n4SXY!2rhM{c~*H)roj$RnKr7eV}(%p68u!xVRQCFRiXy}80EKl{1LqG z|4-5bg?9yH*EDT(M%bnI%AL2(nn9gqLm>uRkiP<9yTf4^RP#$E^Mo%2C>wHGKzF0R z@e^>#*NW3{4|TVEDqQ0KuioYdSb{zvOY=E*5m!cJsLh zX_QkiWisam+6A;@V)C0a@CTrFj9(}Xk)!nQLKZxi12YgyDDFZTA2)=PobkfoLlFosCf>)ny>0Hd=F#EwiqxPyBT!{nmf zj_>|K{E1Yc0fe5B9a(3l4DkaBcG7s3A2wj|6F>6blz9+cb3aQ zb_^Ez_XyxX>tV|hn7$?g-@Rb3ewc(_4Cr%T*dxG=LInU+9TquD7VUeAI1O%G0{?G2 zgEx&w>g>ItD(Y(T%Z^yTJlJ;q4!HXle>Toifk-7bu4i4CBVclKvJOV7?f0!Cr_Z1E z&UXFW>}E3#H_OdvRO_O9Uhp#dI;`}Yih1yM=QzfG3ZXXBV(vi&!oEI!fW?QPyfNu` zF*Ov0gI{KaXg^hf0z(pr%}T(k)x5)WeS-iJDWxYH8W$!K7B7PgyKGA?Cdz|wq;D&8 z#mT!!X-Fm8OQBO_O=;}3OW#$bwL}MzMXCTrn#}q;4e|3ol4DSvIh{A}z|3jS5<%ee z$5_!9hvEO0&!tQI2mLyY<%9YlhM0qlH5P}k5<(iu%MeI+ddi^PV*~aLf#HLZZ%o!fN%SkFfojMg`mnw1t7w)AqaP?( z3tW1y?ibR>9fp|>nbmoN0q1PKR`pDC1F}m-z{t{_Qee=4FFzx)W!(UcfmDT{_YAsQjv#(pK~FXG@V^9C2-I)SqYNuru^%>tE7d;eZlpN;HH6vSm2dDGFhq>G*H;fN zIG7#SKBsGF-AG={vKh<8U+TfWRwmj)C>&x`p!rCtNNRR7h?xF>u(!Xmuk1*W1mR9p z;1&^*grHbKXBzoB`28#L-}Iw8rEV8*S&o+X);oSw+UC*AgLoQY^;>4fgFRT9-Hdc& zLw#<9(u1cd>wgFSHFE#NT(;F=^dV!86fEcj6F{~4uhQZ*vao+e{%K|#UX-5Ykc>LV z7FG9_k6vN|OOmDn_nMr9@)1dmTA-(Q1GD)2q+k$Z>fp-B;1}vb9FBHx6inbLh-eD5 z4b1H1{rCEf1dHI<%IB#-2AF?#HGWuysDZ5}QdGfU=aW(`n(%-MfZDxZNEE>_CU%t9 z96)wu{a2c=PKBO+KmJIkZZ<`tSuIvv+(gDx9@?z0b(WFq2MckC0bjVIErR9P7(9s% zGHlt`B-&Hn!4|0i*$q+)`ByYms+vjU)juBvvY37;bakQxiAg;C*My&s07L%2BCWJa z_`|UWQTFa2k8XpO_9*^6&@%IbXAMS2DB})fhYCKC~tbr;2Ikgh71pcO_w(qA$jBFtm zyDA17uq_Jz_C34UHR0TKK6Nm`NCi45%sqp)T3n%yKaP$Y5C^a}#URno9}`Og8F3I0 z4X_Ow@mt*ZIFc=HEG^|JOA?brP`75flZF@UHl2S@=DA;OC^|=;H)df1dY> zNlA%GN}K$3-YX{~DhVF{N)qlIGx*P5@Ps~cbq@TmUyzah>xJLUO|9SymyTv|dgkh^ zbPF8c#MSe!IsS_?<&VGUpMaft{^@)l>g*Trz{d&rx5MZ%S=XZ@Za`P--mNkXhv)wZ D+JG7* literal 0 HcmV?d00001 diff --git a/textoutpc/demo/static/js/elements.js b/textoutpc/demo/static/js/elements.js new file mode 100644 index 0000000..e69de29 diff --git a/textoutpc/demo/templates/default.html b/textoutpc/demo/templates/default.html new file mode 100644 index 0000000..296f3c2 --- /dev/null +++ b/textoutpc/demo/templates/default.html @@ -0,0 +1,27 @@ + + + + + + + + +{% block meta %}{% endblock %} +{% assets "css" %} + +{% endassets %}
+
+

textoutpc

— moteur de traduction BBcode

+ +
+ +{% block content %}{% endblock %} + +
+ +{% assets "js" %} + +{% endassets %} + diff --git a/textoutpc/demo/templates/page.html b/textoutpc/demo/templates/page.html new file mode 100644 index 0000000..6d1161a --- /dev/null +++ b/textoutpc/demo/templates/page.html @@ -0,0 +1,27 @@ +{% extends "default.html" %} +{% block meta %}textoutpc — Planète Casio's flavour of BBcode + + + +{% +endblock %}{% block content %}{% if result != None +%} + +
+{% autoescape false %}{{ result }}{% endautoescape %} +
+ +{% endif +%} + +
+
+ + +
+
+ +
+

textoutpc est un projet de traducteur d'un langage de formattage, le BBcode (adapté par et au site Planète Casio), en d'autres dont le HTML. Ce site, dont la source est disponible ici, est la démonstration de ce traducteur.

+

Pour tester le traducteur, entrez du texte dans la zone d'écriture et cliquez sur « Voir le résultat ». Pour parler au sujet de ce traducteur, il y a ce sujet sur Planète Casio. Pour signaler des failles de sécurité (XSS, RCE) dans le traducteur, merci de me contacter par e-mail sur thomas@touhey.fr.

+
{% endblock %} diff --git a/textoutpc/demo/utils.py b/textoutpc/demo/utils.py new file mode 100644 index 0000000..92c03e2 --- /dev/null +++ b/textoutpc/demo/utils.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python +# ***************************************************************************** +# Copyright (C) 2023 Thomas Touhey +# This file is part of the textoutpc project, which is MIT-licensed. +# ***************************************************************************** +"""Utilities for the demonstration website for textoutpc.""" + +from __future__ import annotations + +from docutils.core import publish_parts + + +def to_html(inputstring: str) -> str: + """Convert textout-style BBCode to HTML. + + :param inputstring: The input string to convert. + :return: The produced HTML. + """ + parts = publish_parts( + inputstring, + source_path="", + destination_path="", + parser_name="textoutpc", + writer_name="textoutpc.html", + ) + return parts["body"] diff --git a/textoutpc/parser.py b/textoutpc/parser.py index 4529572..4266b65 100644 --- a/textoutpc/parser.py +++ b/textoutpc/parser.py @@ -263,7 +263,7 @@ class StateMachine: if isinstance(entity, CloseTagEntity): ent_name = f"[{entity.name}]" if self.stack and self.stack[0].is_raw: - if self.stack[0].name == ent_name: + if ent_name in ("[]", self.stack[0].name): # We are indeed closing the current raw tag! self.close_multiple(1) else: diff --git a/textoutpc/py.typed b/textoutpc/py.typed new file mode 100644 index 0000000..e69de29