From 9277169e209a3c2e074640b88a4ca0c8d1abe323 Mon Sep 17 00:00:00 2001 From: Josh Karpel Date: Fri, 25 Nov 2022 19:33:23 -0600 Subject: [PATCH] Switch to Textual (#154) --- .github/workflows/quality-check.yml | 4 +- .pre-commit-config.yaml | 2 +- CHANGELOG.md | 11 + poetry.lock | 1658 ++++++++++++--------------- pyproject.toml | 28 +- spiel/__init__.py | 4 - spiel/__main__.py | 5 +- spiel/app.py | 187 +++ spiel/{main.py => cli.py} | 141 +-- spiel/constants.py | 11 +- spiel/deck.py | 63 +- spiel/demo/__init__.py | 0 spiel/demo/demo.py | 353 +++--- spiel/example.py | 114 -- spiel/exceptions.py | 13 +- spiel/footer.py | 81 -- spiel/help.py | 96 -- spiel/input.py | 424 ------- spiel/load.py | 78 -- spiel/modes.py | 9 - spiel/options.py | 68 -- spiel/present.py | 108 -- spiel/presentable.py | 27 - spiel/reloader.py | 42 - spiel/renderables/__init__.py | 0 spiel/renderables/debug.py | 46 + spiel/{ => renderables}/image.py | 11 +- spiel/repls.py | 30 - spiel/rps.py | 34 - spiel/screens/__init__.py | 0 spiel/screens/deck.py | 24 + spiel/screens/help.py | 40 + spiel/screens/screen.py | 10 + spiel/screens/slide.py | 45 + spiel/slide.py | 32 +- spiel/spiel.css | 0 spiel/state.py | 123 -- spiel/triggers.py | 20 +- spiel/utils.py | 6 +- spiel/widgets/__init__.py | 0 spiel/widgets/bindings.py | 61 + spiel/widgets/footer.py | 55 + spiel/widgets/minislides.py | 70 ++ spiel/widgets/slide.py | 50 + spiel/widgets/widget.py | 21 + tests/conftest.py | 16 +- tests/test_cli.py | 48 +- tests/test_deck.py | 18 +- tests/test_demo.py | 24 +- tests/test_display.py | 39 - tests/test_footer.py | 17 - tests/test_help.py | 8 - tests/test_image.py | 4 +- tests/test_init.py | 12 +- tests/test_input_handlers.py | 93 -- tests/test_input_handling.py | 62 - tests/test_load.py | 49 +- tests/test_options.py | 53 - tests/test_reloader.py | 82 -- tests/test_rps.py | 41 - tests/test_slide.py | 5 + tests/test_state.py | 102 -- tests/test_triggers.py | 56 +- 63 files changed, 1748 insertions(+), 3186 deletions(-) create mode 100644 spiel/app.py rename spiel/{main.py => cli.py} (64%) create mode 100644 spiel/demo/__init__.py delete mode 100644 spiel/example.py delete mode 100644 spiel/footer.py delete mode 100644 spiel/help.py delete mode 100644 spiel/input.py delete mode 100644 spiel/load.py delete mode 100644 spiel/modes.py delete mode 100644 spiel/options.py delete mode 100644 spiel/present.py delete mode 100644 spiel/presentable.py delete mode 100644 spiel/reloader.py create mode 100644 spiel/renderables/__init__.py create mode 100644 spiel/renderables/debug.py rename spiel/{ => renderables}/image.py (89%) delete mode 100644 spiel/repls.py delete mode 100644 spiel/rps.py create mode 100644 spiel/screens/__init__.py create mode 100644 spiel/screens/deck.py create mode 100644 spiel/screens/help.py create mode 100644 spiel/screens/screen.py create mode 100644 spiel/screens/slide.py create mode 100644 spiel/spiel.css delete mode 100644 spiel/state.py create mode 100644 spiel/widgets/__init__.py create mode 100644 spiel/widgets/bindings.py create mode 100644 spiel/widgets/footer.py create mode 100644 spiel/widgets/minislides.py create mode 100644 spiel/widgets/slide.py create mode 100644 spiel/widgets/widget.py delete mode 100644 tests/test_display.py delete mode 100644 tests/test_footer.py delete mode 100644 tests/test_help.py delete mode 100644 tests/test_input_handlers.py delete mode 100644 tests/test_input_handling.py delete mode 100644 tests/test_options.py delete mode 100644 tests/test_reloader.py delete mode 100644 tests/test_rps.py create mode 100644 tests/test_slide.py delete mode 100644 tests/test_state.py diff --git a/.github/workflows/quality-check.yml b/.github/workflows/quality-check.yml index 0dbcb20..1d7a778 100644 --- a/.github/workflows/quality-check.yml +++ b/.github/workflows/quality-check.yml @@ -11,8 +11,8 @@ jobs: strategy: fail-fast: false matrix: - platform: [ubuntu-latest, macos-latest] - python-version: ["3.10"] + platform: [ubuntu-latest, macos-latest, windows-latest] + python-version: ["3.10", "3.11"] defaults: run: shell: bash diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5ceb88b..c64f540 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -27,7 +27,7 @@ repos: - id: python-use-type-annotations - id: python-check-blanket-type-ignore - repo: https://github.com/hadialqattan/pycln - rev: v2.1.1 + rev: v2.1.2 hooks: - id: pycln args: [ --config=pyproject.toml ] diff --git a/CHANGELOG.md b/CHANGELOG.md index a32b044..b398c20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## 0.4.0 + +### Changed + +- [#154](https://github.com/JoshKarpel/spiel/pull/154) Switch to [Textual](https://textual.textualize.io/) as the overall control and rendering engine. + +### Removed + +- [#154](https://github.com/JoshKarpel/spiel/pull/154) Removed library-provided `Example` slides, `Options`, and various other small features + as part of the Textual migration. Some of these features will likely be reintroduced later. + ## 0.3.0 ### Removed diff --git a/poetry.lock b/poetry.lock index 4bd17b1..31b494c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,65 +1,83 @@ [[package]] -name = "appnope" -version = "0.1.3" -description = "Disable App Nap on macOS >= 10.9" -category = "main" +name = "aiohttp" +version = "3.8.3" +description = "Async http client/server framework (asyncio)" +category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.6" + +[package.dependencies] +aiosignal = ">=1.1.2" +async-timeout = ">=4.0.0a3,<5.0" +attrs = ">=17.3.0" +charset-normalizer = ">=2.0,<3.0" +frozenlist = ">=1.1.1" +multidict = ">=4.5,<7.0" +yarl = ">=1.0,<2.0" + +[package.extras] +speedups = ["Brotli", "aiodns", "cchardet"] + +[[package]] +name = "aiosignal" +version = "1.3.1" +description = "aiosignal: a list of registered asynchronous callbacks" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +frozenlist = ">=1.1.0" [[package]] -name = "asttokens" -version = "2.0.5" -description = "Annotate AST trees with source code positions" +name = "anyio" +version = "3.6.2" +description = "High level compatibility layer for multiple asynchronous event loop implementations" category = "main" optional = false -python-versions = "*" +python-versions = ">=3.6.2" [package.dependencies] -six = "*" +idna = ">=2.8" +sniffio = ">=1.1" [package.extras] -test = ["astroid", "pytest"] +doc = ["packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] +test = ["contextlib2", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (<0.15)", "uvloop (>=0.15)"] +trio = ["trio (>=0.16,<0.22)"] [[package]] -name = "atomicwrites" -version = "1.4.1" -description = "Atomic file writes." +name = "async-timeout" +version = "4.0.2" +description = "Timeout context manager for asyncio programs" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.6" [[package]] name = "attrs" -version = "21.4.0" +version = "22.1.0" description = "Classes Without Boilerplate" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.5" [package.extras] -dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] -docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] -tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] -tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"] +dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy (>=0.900,!=0.940)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "sphinx", "sphinx-notfound-page", "zope.interface"] +docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"] +tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "zope.interface"] +tests-no-zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"] [[package]] -name = "backcall" -version = "0.2.0" -description = "Specifications for callback functions passed in to an API" -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "cffi" -version = "1.15.1" -description = "Foreign Function Interface for Python calling C code." -category = "main" +name = "charset-normalizer" +version = "2.1.1" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.6.0" -[package.dependencies] -pycparser = "*" +[package.extras] +unicode-backport = ["unicodedata2"] [[package]] name = "click" @@ -74,11 +92,11 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""} [[package]] name = "colorama" -version = "0.4.5" +version = "0.4.6" description = "Cross-platform colored terminal text." category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" [[package]] name = "commonmark" @@ -93,7 +111,7 @@ test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"] [[package]] name = "coverage" -version = "6.4.2" +version = "6.5.0" description = "Code coverage measurement for Python" category = "dev" optional = false @@ -105,22 +123,6 @@ tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.1 [package.extras] toml = ["tomli"] -[[package]] -name = "debugpy" -version = "1.6.2" -description = "An implementation of the Debug Adapter Protocol for Python" -category = "main" -optional = false -python-versions = ">=3.7" - -[[package]] -name = "decorator" -version = "5.1.1" -description = "Decorators for Humans" -category = "main" -optional = false -python-versions = ">=3.5" - [[package]] name = "docopt" version = "0.6.2" @@ -129,17 +131,9 @@ category = "dev" optional = false python-versions = "*" -[[package]] -name = "entrypoints" -version = "0.4" -description = "Discover and load entry points from installed packages." -category = "main" -optional = false -python-versions = ">=3.6" - [[package]] name = "exceptiongroup" -version = "1.0.0rc8" +version = "1.0.4" description = "Backport of PEP 654 (exception groups)" category = "dev" optional = false @@ -159,29 +153,29 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [package.extras] testing = ["pre-commit"] -[[package]] -name = "executing" -version = "0.8.3" -description = "Get the currently executing AST node of a frame, and other information" -category = "main" -optional = false -python-versions = "*" - [[package]] name = "filelock" -version = "3.7.1" +version = "3.8.0" description = "A platform independent file lock." category = "dev" optional = false python-versions = ">=3.7" [package.extras] -docs = ["furo (>=2021.8.17b43)", "sphinx (>=4.1)", "sphinx-autodoc-typehints (>=1.12)"] -testing = ["covdefaults (>=1.2.0)", "coverage (>=4)", "pytest (>=4)", "pytest-cov", "pytest-timeout (>=1.4.2)"] +docs = ["furo (>=2022.6.21)", "sphinx (>=5.1.1)", "sphinx-autodoc-typehints (>=1.19.1)"] +testing = ["covdefaults (>=2.2)", "coverage (>=6.4.2)", "pytest (>=7.1.2)", "pytest-cov (>=3)", "pytest-timeout (>=2.1)"] + +[[package]] +name = "frozenlist" +version = "1.3.3" +description = "A list-like structure which implements collections.abc.MutableSequence" +category = "dev" +optional = false +python-versions = ">=3.7" [[package]] name = "hypothesis" -version = "6.50.1" +version = "6.58.0" description = "A library for property-based testing" category = "dev" optional = false @@ -189,161 +183,80 @@ python-versions = ">=3.7" [package.dependencies] attrs = ">=19.2.0" -exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +exceptiongroup = {version = ">=1.0.0", markers = "python_version < \"3.11\""} sortedcontainers = ">=2.1.0,<3.0.0" [package.extras] -all = ["black (>=19.10b0)", "click (>=7.0)", "django (>=2.2)", "dpcontracts (>=0.4)", "lark-parser (>=0.6.5)", "libcst (>=0.3.16)", "numpy (>=1.9.0)", "pandas (>=0.25)", "pytest (>=4.6)", "python-dateutil (>=1.4)", "pytz (>=2014.1)", "redis (>=3.0.0)", "rich (>=9.0.0)", "importlib-metadata (>=3.6)", "backports.zoneinfo (>=0.2.1)", "tzdata (>=2022.1)"] -cli = ["click (>=7.0)", "black (>=19.10b0)", "rich (>=9.0.0)"] +all = ["backports.zoneinfo (>=0.2.1)", "black (>=19.10b0)", "click (>=7.0)", "django (>=3.2)", "dpcontracts (>=0.4)", "importlib-metadata (>=3.6)", "lark-parser (>=0.6.5)", "libcst (>=0.3.16)", "numpy (>=1.9.0)", "pandas (>=1.0)", "pytest (>=4.6)", "python-dateutil (>=1.4)", "pytz (>=2014.1)", "redis (>=3.0.0)", "rich (>=9.0.0)", "tzdata (>=2022.6)"] +cli = ["black (>=19.10b0)", "click (>=7.0)", "rich (>=9.0.0)"] codemods = ["libcst (>=0.3.16)"] dateutil = ["python-dateutil (>=1.4)"] -django = ["django (>=2.2)"] +django = ["django (>=3.2)"] dpcontracts = ["dpcontracts (>=0.4)"] ghostwriter = ["black (>=19.10b0)"] lark = ["lark-parser (>=0.6.5)"] numpy = ["numpy (>=1.9.0)"] -pandas = ["pandas (>=0.25)"] +pandas = ["pandas (>=1.0)"] pytest = ["pytest (>=4.6)"] pytz = ["pytz (>=2014.1)"] redis = ["redis (>=3.0.0)"] -zoneinfo = ["backports.zoneinfo (>=0.2.1)", "tzdata (>=2022.1)"] - -[[package]] -name = "iniconfig" -version = "1.1.1" -description = "iniconfig: brain-dead simple config-ini parsing" -category = "dev" -optional = false -python-versions = "*" +zoneinfo = ["backports.zoneinfo (>=0.2.1)", "tzdata (>=2022.6)"] [[package]] -name = "ipykernel" -version = "6.15.1" -description = "IPython Kernel for Jupyter" +name = "idna" +version = "3.4" +description = "Internationalized Domain Names in Applications (IDNA)" category = "main" optional = false -python-versions = ">=3.7" - -[package.dependencies] -appnope = {version = "*", markers = "platform_system == \"Darwin\""} -debugpy = ">=1.0" -ipython = ">=7.23.1" -jupyter-client = ">=6.1.12" -matplotlib-inline = ">=0.1" -nest-asyncio = "*" -packaging = "*" -psutil = "*" -pyzmq = ">=17" -tornado = ">=6.1" -traitlets = ">=5.1.0" - -[package.extras] -test = ["flaky", "ipyparallel", "pre-commit", "pytest-cov", "pytest-timeout", "pytest (>=6.0)"] +python-versions = ">=3.5" [[package]] -name = "ipython" -version = "8.4.0" -description = "IPython: Productive Interactive Computing" +name = "importlib-metadata" +version = "4.13.0" +description = "Read metadata from Python packages" category = "main" optional = false -python-versions = ">=3.8" +python-versions = ">=3.7" [package.dependencies] -appnope = {version = "*", markers = "sys_platform == \"darwin\""} -backcall = "*" -colorama = {version = "*", markers = "sys_platform == \"win32\""} -decorator = "*" -jedi = ">=0.16" -matplotlib-inline = "*" -pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""} -pickleshare = "*" -prompt-toolkit = ">=2.0.0,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.1.0" -pygments = ">=2.4.0" -stack-data = "*" -traitlets = ">=5" +zipp = ">=0.5" [package.extras] -all = ["black", "Sphinx (>=1.3)", "ipykernel", "nbconvert", "nbformat", "ipywidgets", "notebook", "ipyparallel", "qtconsole", "pytest (<7.1)", "pytest-asyncio", "testpath", "curio", "matplotlib (!=3.2.0)", "numpy (>=1.19)", "pandas", "trio"] -black = ["black"] -doc = ["Sphinx (>=1.3)"] -kernel = ["ipykernel"] -nbconvert = ["nbconvert"] -nbformat = ["nbformat"] -notebook = ["ipywidgets", "notebook"] -parallel = ["ipyparallel"] -qtconsole = ["qtconsole"] -test = ["pytest (<7.1)", "pytest-asyncio", "testpath"] -test_extra = ["pytest (<7.1)", "pytest-asyncio", "testpath", "curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.19)", "pandas", "trio"] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] +perf = ["ipython"] +testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] [[package]] -name = "jedi" -version = "0.18.1" -description = "An autocompletion tool for Python that can be used for text editors." -category = "main" +name = "iniconfig" +version = "1.1.1" +description = "iniconfig: brain-dead simple config-ini parsing" +category = "dev" optional = false -python-versions = ">=3.6" - -[package.dependencies] -parso = ">=0.8.0,<0.9.0" - -[package.extras] -qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] -testing = ["Django (<3.1)", "colorama", "docopt", "pytest (<7.0.0)"] +python-versions = "*" [[package]] -name = "jupyter-client" -version = "7.3.4" -description = "Jupyter protocol implementation and client libraries" -category = "main" +name = "msgpack" +version = "1.0.4" +description = "MessagePack serializer" +category = "dev" optional = false -python-versions = ">=3.7" - -[package.dependencies] -entrypoints = "*" -jupyter-core = ">=4.9.2" -nest-asyncio = ">=1.5.4" -python-dateutil = ">=2.8.2" -pyzmq = ">=23.0" -tornado = ">=6.0" -traitlets = "*" - -[package.extras] -doc = ["ipykernel", "myst-parser", "sphinx-rtd-theme", "sphinx (>=1.3.6)", "sphinxcontrib-github-alt"] -test = ["codecov", "coverage", "ipykernel (>=6.5)", "ipython", "mypy", "pre-commit", "pytest", "pytest-asyncio (>=0.18)", "pytest-cov", "pytest-timeout"] +python-versions = "*" [[package]] -name = "jupyter-core" -version = "4.11.1" -description = "Jupyter core package. A base package on which Jupyter projects rely." -category = "main" +name = "multidict" +version = "6.0.2" +description = "multidict implementation" +category = "dev" optional = false python-versions = ">=3.7" -[package.dependencies] -pywin32 = {version = ">=1.0", markers = "sys_platform == \"win32\" and platform_python_implementation != \"PyPy\""} -traitlets = "*" - -[package.extras] -test = ["ipykernel", "pre-commit", "pytest", "pytest-cov", "pytest-timeout"] - -[[package]] -name = "matplotlib-inline" -version = "0.1.3" -description = "Inline Matplotlib backend for Jupyter" -category = "main" -optional = false -python-versions = ">=3.5" - -[package.dependencies] -traitlets = "*" - [[package]] name = "mypy" -version = "0.961" +version = "0.991" description = "Optional static typing for Python" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.dependencies] mypy-extensions = ">=0.4.3" @@ -352,6 +265,7 @@ typing-extensions = ">=3.10" [package.extras] dmypy = ["psutil (>=4.0)"] +install-types = ["pip"] python2 = ["typed-ast (>=1.4.0,<2)"] reports = ["lxml"] @@ -364,70 +278,27 @@ optional = false python-versions = "*" [[package]] -name = "nest-asyncio" -version = "1.5.5" -description = "Patch asyncio to allow nested event loops" +name = "nanoid" +version = "2.0.0" +description = "A tiny, secure, URL-friendly, unique string ID generator for Python" category = "main" optional = false -python-versions = ">=3.5" +python-versions = "*" [[package]] name = "packaging" version = "21.3" description = "Core utilities for Python packages" -category = "main" +category = "dev" optional = false python-versions = ">=3.6" [package.dependencies] pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" -[[package]] -name = "parso" -version = "0.8.3" -description = "A Python Parser" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.extras] -qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] -testing = ["docopt", "pytest (<6.0.0)"] - -[[package]] -name = "pendulum" -version = "2.1.2" -description = "Python datetimes made easy" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - -[package.dependencies] -python-dateutil = ">=2.6,<3.0" -pytzdata = ">=2020.1" - -[[package]] -name = "pexpect" -version = "4.8.0" -description = "Pexpect allows easy control of interactive console applications." -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -ptyprocess = ">=0.5" - -[[package]] -name = "pickleshare" -version = "0.7.5" -description = "Tiny 'shelve'-like database with concurrency support" -category = "main" -optional = false -python-versions = "*" - [[package]] name = "pillow" -version = "9.2.0" +version = "9.3.0" description = "Python Imaging Library (Fork)" category = "main" optional = false @@ -449,106 +320,51 @@ python-versions = ">=3.6" dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] -[[package]] -name = "prompt-toolkit" -version = "3.0.30" -description = "Library for building powerful interactive command lines in Python" -category = "main" -optional = false -python-versions = ">=3.6.2" - -[package.dependencies] -wcwidth = "*" - -[[package]] -name = "psutil" -version = "5.9.1" -description = "Cross-platform lib for process and system monitoring in Python." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[package.extras] -test = ["ipaddress", "mock", "enum34", "pywin32", "wmi"] - -[[package]] -name = "ptyprocess" -version = "0.7.0" -description = "Run a subprocess in a pseudo terminal" -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "pure-eval" -version = "0.2.2" -description = "Safely evaluate AST nodes without side effects" -category = "main" -optional = false -python-versions = "*" - -[package.extras] -tests = ["pytest"] - -[[package]] -name = "py" -version = "1.11.0" -description = "library with cross-python path, ini-parsing, io, code, log facilities" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - -[[package]] -name = "pycparser" -version = "2.21" -description = "C parser in Python" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - [[package]] name = "pygments" -version = "2.12.0" +version = "2.13.0" description = "Pygments is a syntax highlighting package written in Python." category = "main" optional = false python-versions = ">=3.6" +[package.extras] +plugins = ["importlib-metadata"] + [[package]] name = "pyparsing" version = "3.0.9" description = "pyparsing module - Classes and methods to define and execute parsing grammars" -category = "main" +category = "dev" optional = false python-versions = ">=3.6.8" [package.extras] -diagrams = ["railroad-diagrams", "jinja2"] +diagrams = ["jinja2", "railroad-diagrams"] [[package]] name = "pytest" -version = "7.1.2" +version = "7.2.0" description = "pytest: simple powerful testing with Python" category = "dev" optional = false python-versions = ">=3.7" [package.dependencies] -atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} attrs = ">=19.2.0" colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" pluggy = ">=0.12,<2.0" -py = ">=1.8.2" -tomli = ">=1.0.0" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} [package.extras] testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] [[package]] name = "pytest-cov" -version = "3.0.0" +version = "4.0.0" description = "Pytest plugin for measuring coverage." category = "dev" optional = false @@ -559,23 +375,11 @@ coverage = {version = ">=5.2.1", extras = ["toml"]} pytest = ">=4.6" [package.extras] -testing = ["fields", "hunter", "process-tests", "six", "pytest-xdist", "virtualenv"] - -[[package]] -name = "pytest-forked" -version = "1.4.0" -description = "run tests in isolated forked subprocesses" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -py = "*" -pytest = ">=3.10" +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] [[package]] name = "pytest-mock" -version = "3.8.2" +version = "3.10.0" description = "Thin-wrapper around the mock package for easier use with pytest" category = "dev" optional = false @@ -585,20 +389,23 @@ python-versions = ">=3.7" pytest = ">=5.0" [package.extras] -dev = ["pre-commit", "tox", "pytest-asyncio"] +dev = ["pre-commit", "pytest-asyncio", "tox"] [[package]] name = "pytest-mypy" -version = "0.9.1" +version = "0.10.2" description = "Mypy static type checker plugin for Pytest" category = "dev" optional = false -python-versions = ">=3.5" +python-versions = ">=3.6" [package.dependencies] attrs = ">=19.0" filelock = ">=3.0" -mypy = {version = ">=0.780", markers = "python_version >= \"3.9\""} +mypy = [ + {version = ">=0.900", markers = "python_version >= \"3.11\""}, + {version = ">=0.780", markers = "python_version >= \"3.9\" and python_version < \"3.11\""}, +] pytest = {version = ">=6.2", markers = "python_version >= \"3.10\""} [[package]] @@ -617,7 +424,7 @@ watchdog = ">=0.6.0" [[package]] name = "pytest-xdist" -version = "2.5.0" +version = "3.0.2" description = "pytest xdist plugin for distributed testing and loop-on-failing modes" category = "dev" optional = false @@ -626,55 +433,15 @@ python-versions = ">=3.6" [package.dependencies] execnet = ">=1.1" pytest = ">=6.2.0" -pytest-forked = "*" [package.extras] psutil = ["psutil (>=3.0)"] setproctitle = ["setproctitle"] testing = ["filelock"] -[[package]] -name = "python-dateutil" -version = "2.8.2" -description = "Extensions to the standard Python datetime module" -category = "main" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" - -[package.dependencies] -six = ">=1.5" - -[[package]] -name = "pytzdata" -version = "2020.1" -description = "The Olson timezone database for Python." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[[package]] -name = "pywin32" -version = "304" -description = "Python for Window Extensions" -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "pyzmq" -version = "23.2.0" -description = "Python bindings for 0MQ" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -cffi = {version = "*", markers = "implementation_name == \"pypy\""} -py = {version = "*", markers = "implementation_name == \"pypy\""} - [[package]] name = "rich" -version = "12.5.1" +version = "12.6.0" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" category = "main" optional = false @@ -688,12 +455,12 @@ pygments = ">=2.6.0,<3.0.0" jupyter = ["ipywidgets (>=7.5.1,<8.0.0)"] [[package]] -name = "six" -version = "1.16.0" -description = "Python 2 and 3 compatibility utilities" +name = "sniffio" +version = "1.3.0" +description = "Sniff out which async library your code is running under" category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +python-versions = ">=3.7" [[package]] name = "sortedcontainers" @@ -704,59 +471,35 @@ optional = false python-versions = "*" [[package]] -name = "stack-data" -version = "0.3.0" -description = "Extract data from python stack frames and tracebacks for informative displays" +name = "textual" +version = "0.5.0" +description = "Modern Text User Interface framework" category = "main" optional = false -python-versions = "*" +python-versions = ">=3.7,<4.0" [package.dependencies] -asttokens = "*" -executing = "*" -pure-eval = "*" +aiohttp = {version = ">=3.8.1", optional = true, markers = "extra == \"dev\""} +click = {version = ">=8.1.2", optional = true, markers = "extra == \"dev\""} +importlib-metadata = ">=4.11.3,<5.0.0" +msgpack = {version = ">=1.0.3", optional = true, markers = "extra == \"dev\""} +nanoid = ">=2.0.0" +rich = ">=12.6.0,<13.0.0" [package.extras] -tests = ["pytest", "typeguard", "pygments", "littleutils", "cython"] +dev = ["aiohttp (>=3.8.1)", "click (>=8.1.2)", "msgpack (>=1.0.3)"] [[package]] name = "tomli" version = "2.0.1" description = "A lil' TOML parser" -category = "main" -optional = false -python-versions = ">=3.7" - -[[package]] -name = "tomli-w" -version = "1.0.0" -description = "A lil' TOML writer" -category = "main" -optional = false -python-versions = ">=3.7" - -[[package]] -name = "tornado" -version = "6.2" -description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." -category = "main" -optional = false -python-versions = ">= 3.7" - -[[package]] -name = "traitlets" -version = "5.3.0" -description = "" -category = "main" +category = "dev" optional = false python-versions = ">=3.7" -[package.extras] -test = ["pre-commit", "pytest"] - [[package]] name = "typer" -version = "0.6.1" +version = "0.7.0" description = "Typer, build great CLIs. Easy to code. Based on Python type hints." category = "main" optional = false @@ -766,14 +509,14 @@ python-versions = ">=3.6" click = ">=7.1.1,<9.0.0" [package.extras] -all = ["colorama (>=0.4.3,<0.5.0)", "shellingham (>=1.3.0,<2.0.0)", "rich (>=10.11.0,<13.0.0)"] +all = ["colorama (>=0.4.3,<0.5.0)", "rich (>=10.11.0,<13.0.0)", "shellingham (>=1.3.0,<2.0.0)"] dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "pre-commit (>=2.17.0,<3.0.0)"] -doc = ["mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "mdx-include (>=1.4.1,<2.0.0)"] -test = ["shellingham (>=1.3.0,<2.0.0)", "pytest (>=4.4.0,<5.4.0)", "pytest-cov (>=2.10.0,<3.0.0)", "coverage (>=5.2,<6.0)", "pytest-xdist (>=1.32.0,<2.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "mypy (==0.910)", "black (>=22.3.0,<23.0.0)", "isort (>=5.0.6,<6.0.0)", "rich (>=10.11.0,<13.0.0)"] +doc = ["cairosvg (>=2.5.2,<3.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pillow (>=9.3.0,<10.0.0)"] +test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.910)", "pytest (>=4.4.0,<8.0.0)", "pytest-cov (>=2.10.0,<5.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "pytest-xdist (>=1.32.0,<4.0.0)", "rich (>=10.11.0,<13.0.0)", "shellingham (>=1.3.0,<2.0.0)"] [[package]] name = "typing-extensions" -version = "4.3.0" +version = "4.4.0" description = "Backported and Experimental Type Hints for Python 3.7+" category = "dev" optional = false @@ -783,7 +526,7 @@ python-versions = ">=3.7" name = "watchdog" version = "2.1.9" description = "Filesystem events monitoring" -category = "main" +category = "dev" optional = false python-versions = ">=3.6" @@ -791,594 +534,610 @@ python-versions = ">=3.6" watchmedo = ["PyYAML (>=3.10)"] [[package]] -name = "wcwidth" -version = "0.2.5" -description = "Measures the displayed width of unicode strings in a terminal" +name = "watchfiles" +version = "0.18.1" +description = "Simple, modern and high performance file watching and code reload in python." category = "main" optional = false -python-versions = "*" +python-versions = ">=3.7" + +[package.dependencies] +anyio = ">=3.0.0" + +[[package]] +name = "yarl" +version = "1.8.1" +description = "Yet another URL library" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +idna = ">=2.0" +multidict = ">=4.0" + +[[package]] +name = "zipp" +version = "3.11.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] +testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] [metadata] lock-version = "1.1" python-versions = ">=3.10,<4" -content-hash = "64290aa1725d22f6b73924b9c2cd491a09d4055042fa3f08a33bda9165df2e2c" +content-hash = "1098c2e53711d1e14b975cd6c1ef815dcf73df262db73db21e4b9ffc92dff075" [metadata.files] -appnope = [ - {file = "appnope-0.1.3-py2.py3-none-any.whl", hash = "sha256:265a455292d0bd8a72453494fa24df5a11eb18373a60c7c0430889f22548605e"}, - {file = "appnope-0.1.3.tar.gz", hash = "sha256:02bd91c4de869fbb1e1c50aafc4098827a7a54ab2f39d9dcba6c9547ed920e24"}, -] -asttokens = [ - {file = "asttokens-2.0.5-py2.py3-none-any.whl", hash = "sha256:0844691e88552595a6f4a4281a9f7f79b8dd45ca4ccea82e5e05b4bbdb76705c"}, - {file = "asttokens-2.0.5.tar.gz", hash = "sha256:9a54c114f02c7a9480d56550932546a3f1fe71d8a02f1bc7ccd0ee3ee35cf4d5"}, -] -atomicwrites = [ - {file = "atomicwrites-1.4.1.tar.gz", hash = "sha256:81b2c9071a49367a7f770170e5eec8cb66567cfbbc8c73d20ce5ca4a8d71cf11"}, +aiohttp = [ + {file = "aiohttp-3.8.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ba71c9b4dcbb16212f334126cc3d8beb6af377f6703d9dc2d9fb3874fd667ee9"}, + {file = "aiohttp-3.8.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d24b8bb40d5c61ef2d9b6a8f4528c2f17f1c5d2d31fed62ec860f6006142e83e"}, + {file = "aiohttp-3.8.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f88df3a83cf9df566f171adba39d5bd52814ac0b94778d2448652fc77f9eb491"}, + {file = "aiohttp-3.8.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b97decbb3372d4b69e4d4c8117f44632551c692bb1361b356a02b97b69e18a62"}, + {file = "aiohttp-3.8.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:309aa21c1d54b8ef0723181d430347d7452daaff93e8e2363db8e75c72c2fb2d"}, + {file = "aiohttp-3.8.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ad5383a67514e8e76906a06741febd9126fc7c7ff0f599d6fcce3e82b80d026f"}, + {file = "aiohttp-3.8.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20acae4f268317bb975671e375493dbdbc67cddb5f6c71eebdb85b34444ac46b"}, + {file = "aiohttp-3.8.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:05a3c31c6d7cd08c149e50dc7aa2568317f5844acd745621983380597f027a18"}, + {file = "aiohttp-3.8.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d6f76310355e9fae637c3162936e9504b4767d5c52ca268331e2756e54fd4ca5"}, + {file = "aiohttp-3.8.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:256deb4b29fe5e47893fa32e1de2d73c3afe7407738bd3c63829874661d4822d"}, + {file = "aiohttp-3.8.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:5c59fcd80b9049b49acd29bd3598cada4afc8d8d69bd4160cd613246912535d7"}, + {file = "aiohttp-3.8.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:059a91e88f2c00fe40aed9031b3606c3f311414f86a90d696dd982e7aec48142"}, + {file = "aiohttp-3.8.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2feebbb6074cdbd1ac276dbd737b40e890a1361b3cc30b74ac2f5e24aab41f7b"}, + {file = "aiohttp-3.8.3-cp310-cp310-win32.whl", hash = "sha256:5bf651afd22d5f0c4be16cf39d0482ea494f5c88f03e75e5fef3a85177fecdeb"}, + {file = "aiohttp-3.8.3-cp310-cp310-win_amd64.whl", hash = "sha256:653acc3880459f82a65e27bd6526e47ddf19e643457d36a2250b85b41a564715"}, + {file = "aiohttp-3.8.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:86fc24e58ecb32aee09f864cb11bb91bc4c1086615001647dbfc4dc8c32f4008"}, + {file = "aiohttp-3.8.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:75e14eac916f024305db517e00a9252714fce0abcb10ad327fb6dcdc0d060f1d"}, + {file = "aiohttp-3.8.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d1fde0f44029e02d02d3993ad55ce93ead9bb9b15c6b7ccd580f90bd7e3de476"}, + {file = "aiohttp-3.8.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ab94426ddb1ecc6a0b601d832d5d9d421820989b8caa929114811369673235c"}, + {file = "aiohttp-3.8.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:89d2e02167fa95172c017732ed7725bc8523c598757f08d13c5acca308e1a061"}, + {file = "aiohttp-3.8.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:02f9a2c72fc95d59b881cf38a4b2be9381b9527f9d328771e90f72ac76f31ad8"}, + {file = "aiohttp-3.8.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c7149272fb5834fc186328e2c1fa01dda3e1fa940ce18fded6d412e8f2cf76d"}, + {file = "aiohttp-3.8.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:512bd5ab136b8dc0ffe3fdf2dfb0c4b4f49c8577f6cae55dca862cd37a4564e2"}, + {file = "aiohttp-3.8.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7018ecc5fe97027214556afbc7c502fbd718d0740e87eb1217b17efd05b3d276"}, + {file = "aiohttp-3.8.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:88c70ed9da9963d5496d38320160e8eb7e5f1886f9290475a881db12f351ab5d"}, + {file = "aiohttp-3.8.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:da22885266bbfb3f78218dc40205fed2671909fbd0720aedba39b4515c038091"}, + {file = "aiohttp-3.8.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:e65bc19919c910127c06759a63747ebe14f386cda573d95bcc62b427ca1afc73"}, + {file = "aiohttp-3.8.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:08c78317e950e0762c2983f4dd58dc5e6c9ff75c8a0efeae299d363d439c8e34"}, + {file = "aiohttp-3.8.3-cp311-cp311-win32.whl", hash = "sha256:45d88b016c849d74ebc6f2b6e8bc17cabf26e7e40c0661ddd8fae4c00f015697"}, + {file = "aiohttp-3.8.3-cp311-cp311-win_amd64.whl", hash = "sha256:96372fc29471646b9b106ee918c8eeb4cca423fcbf9a34daa1b93767a88a2290"}, + {file = "aiohttp-3.8.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c971bf3786b5fad82ce5ad570dc6ee420f5b12527157929e830f51c55dc8af77"}, + {file = "aiohttp-3.8.3-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ff25f48fc8e623d95eca0670b8cc1469a83783c924a602e0fbd47363bb54aaca"}, + {file = "aiohttp-3.8.3-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e381581b37db1db7597b62a2e6b8b57c3deec95d93b6d6407c5b61ddc98aca6d"}, + {file = "aiohttp-3.8.3-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:db19d60d846283ee275d0416e2a23493f4e6b6028825b51290ac05afc87a6f97"}, + {file = "aiohttp-3.8.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:25892c92bee6d9449ffac82c2fe257f3a6f297792cdb18ad784737d61e7a9a85"}, + {file = "aiohttp-3.8.3-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:398701865e7a9565d49189f6c90868efaca21be65c725fc87fc305906be915da"}, + {file = "aiohttp-3.8.3-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:4a4fbc769ea9b6bd97f4ad0b430a6807f92f0e5eb020f1e42ece59f3ecfc4585"}, + {file = "aiohttp-3.8.3-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:b29bfd650ed8e148f9c515474a6ef0ba1090b7a8faeee26b74a8ff3b33617502"}, + {file = "aiohttp-3.8.3-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:1e56b9cafcd6531bab5d9b2e890bb4937f4165109fe98e2b98ef0dcfcb06ee9d"}, + {file = "aiohttp-3.8.3-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:ec40170327d4a404b0d91855d41bfe1fe4b699222b2b93e3d833a27330a87a6d"}, + {file = "aiohttp-3.8.3-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:2df5f139233060578d8c2c975128fb231a89ca0a462b35d4b5fcf7c501ebdbe1"}, + {file = "aiohttp-3.8.3-cp36-cp36m-win32.whl", hash = "sha256:f973157ffeab5459eefe7b97a804987876dd0a55570b8fa56b4e1954bf11329b"}, + {file = "aiohttp-3.8.3-cp36-cp36m-win_amd64.whl", hash = "sha256:437399385f2abcd634865705bdc180c8314124b98299d54fe1d4c8990f2f9494"}, + {file = "aiohttp-3.8.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:09e28f572b21642128ef31f4e8372adb6888846f32fecb288c8b0457597ba61a"}, + {file = "aiohttp-3.8.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f3553510abdbec67c043ca85727396ceed1272eef029b050677046d3387be8d"}, + {file = "aiohttp-3.8.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e168a7560b7c61342ae0412997b069753f27ac4862ec7867eff74f0fe4ea2ad9"}, + {file = "aiohttp-3.8.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:db4c979b0b3e0fa7e9e69ecd11b2b3174c6963cebadeecfb7ad24532ffcdd11a"}, + {file = "aiohttp-3.8.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e164e0a98e92d06da343d17d4e9c4da4654f4a4588a20d6c73548a29f176abe2"}, + {file = "aiohttp-3.8.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e8a78079d9a39ca9ca99a8b0ac2fdc0c4d25fc80c8a8a82e5c8211509c523363"}, + {file = "aiohttp-3.8.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:21b30885a63c3f4ff5b77a5d6caf008b037cb521a5f33eab445dc566f6d092cc"}, + {file = "aiohttp-3.8.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4b0f30372cef3fdc262f33d06e7b411cd59058ce9174ef159ad938c4a34a89da"}, + {file = "aiohttp-3.8.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:8135fa153a20d82ffb64f70a1b5c2738684afa197839b34cc3e3c72fa88d302c"}, + {file = "aiohttp-3.8.3-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:ad61a9639792fd790523ba072c0555cd6be5a0baf03a49a5dd8cfcf20d56df48"}, + {file = "aiohttp-3.8.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:978b046ca728073070e9abc074b6299ebf3501e8dee5e26efacb13cec2b2dea0"}, + {file = "aiohttp-3.8.3-cp37-cp37m-win32.whl", hash = "sha256:0d2c6d8c6872df4a6ec37d2ede71eff62395b9e337b4e18efd2177de883a5033"}, + {file = "aiohttp-3.8.3-cp37-cp37m-win_amd64.whl", hash = "sha256:21d69797eb951f155026651f7e9362877334508d39c2fc37bd04ff55b2007091"}, + {file = "aiohttp-3.8.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2ca9af5f8f5812d475c5259393f52d712f6d5f0d7fdad9acdb1107dd9e3cb7eb"}, + {file = "aiohttp-3.8.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d90043c1882067f1bd26196d5d2db9aa6d268def3293ed5fb317e13c9413ea4"}, + {file = "aiohttp-3.8.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d737fc67b9a970f3234754974531dc9afeea11c70791dcb7db53b0cf81b79784"}, + {file = "aiohttp-3.8.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebf909ea0a3fc9596e40d55d8000702a85e27fd578ff41a5500f68f20fd32e6c"}, + {file = "aiohttp-3.8.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5835f258ca9f7c455493a57ee707b76d2d9634d84d5d7f62e77be984ea80b849"}, + {file = "aiohttp-3.8.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da37dcfbf4b7f45d80ee386a5f81122501ec75672f475da34784196690762f4b"}, + {file = "aiohttp-3.8.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87f44875f2804bc0511a69ce44a9595d5944837a62caecc8490bbdb0e18b1342"}, + {file = "aiohttp-3.8.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:527b3b87b24844ea7865284aabfab08eb0faf599b385b03c2aa91fc6edd6e4b6"}, + {file = "aiohttp-3.8.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d5ba88df9aa5e2f806650fcbeedbe4f6e8736e92fc0e73b0400538fd25a4dd96"}, + {file = "aiohttp-3.8.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:e7b8813be97cab8cb52b1375f41f8e6804f6507fe4660152e8ca5c48f0436017"}, + {file = "aiohttp-3.8.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:2dea10edfa1a54098703cb7acaa665c07b4e7568472a47f4e64e6319d3821ccf"}, + {file = "aiohttp-3.8.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:713d22cd9643ba9025d33c4af43943c7a1eb8547729228de18d3e02e278472b6"}, + {file = "aiohttp-3.8.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2d252771fc85e0cf8da0b823157962d70639e63cb9b578b1dec9868dd1f4f937"}, + {file = "aiohttp-3.8.3-cp38-cp38-win32.whl", hash = "sha256:66bd5f950344fb2b3dbdd421aaa4e84f4411a1a13fca3aeb2bcbe667f80c9f76"}, + {file = "aiohttp-3.8.3-cp38-cp38-win_amd64.whl", hash = "sha256:84b14f36e85295fe69c6b9789b51a0903b774046d5f7df538176516c3e422446"}, + {file = "aiohttp-3.8.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:16c121ba0b1ec2b44b73e3a8a171c4f999b33929cd2397124a8c7fcfc8cd9e06"}, + {file = "aiohttp-3.8.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8d6aaa4e7155afaf994d7924eb290abbe81a6905b303d8cb61310a2aba1c68ba"}, + {file = "aiohttp-3.8.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:43046a319664a04b146f81b40e1545d4c8ac7b7dd04c47e40bf09f65f2437346"}, + {file = "aiohttp-3.8.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:599418aaaf88a6d02a8c515e656f6faf3d10618d3dd95866eb4436520096c84b"}, + {file = "aiohttp-3.8.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:92a2964319d359f494f16011e23434f6f8ef0434acd3cf154a6b7bec511e2fb7"}, + {file = "aiohttp-3.8.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:73a4131962e6d91109bca6536416aa067cf6c4efb871975df734f8d2fd821b37"}, + {file = "aiohttp-3.8.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:598adde339d2cf7d67beaccda3f2ce7c57b3b412702f29c946708f69cf8222aa"}, + {file = "aiohttp-3.8.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:75880ed07be39beff1881d81e4a907cafb802f306efd6d2d15f2b3c69935f6fb"}, + {file = "aiohttp-3.8.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a0239da9fbafd9ff82fd67c16704a7d1bccf0d107a300e790587ad05547681c8"}, + {file = "aiohttp-3.8.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:4e3a23ec214e95c9fe85a58470b660efe6534b83e6cbe38b3ed52b053d7cb6ad"}, + {file = "aiohttp-3.8.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:47841407cc89a4b80b0c52276f3cc8138bbbfba4b179ee3acbd7d77ae33f7ac4"}, + {file = "aiohttp-3.8.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:54d107c89a3ebcd13228278d68f1436d3f33f2dd2af5415e3feaeb1156e1a62c"}, + {file = "aiohttp-3.8.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c37c5cce780349d4d51739ae682dec63573847a2a8dcb44381b174c3d9c8d403"}, + {file = "aiohttp-3.8.3-cp39-cp39-win32.whl", hash = "sha256:f178d2aadf0166be4df834c4953da2d7eef24719e8aec9a65289483eeea9d618"}, + {file = "aiohttp-3.8.3-cp39-cp39-win_amd64.whl", hash = "sha256:88e5be56c231981428f4f506c68b6a46fa25c4123a2e86d156c58a8369d31ab7"}, + {file = "aiohttp-3.8.3.tar.gz", hash = "sha256:3828fb41b7203176b82fe5d699e0d845435f2374750a44b480ea6b930f6be269"}, +] +aiosignal = [ + {file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"}, + {file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"}, +] +anyio = [ + {file = "anyio-3.6.2-py3-none-any.whl", hash = "sha256:fbbe32bd270d2a2ef3ed1c5d45041250284e31fc0a4df4a5a6071842051a51e3"}, + {file = "anyio-3.6.2.tar.gz", hash = "sha256:25ea0d673ae30af41a0c442f81cf3b38c7e79fdc7b60335a4c14e05eb0947421"}, +] +async-timeout = [ + {file = "async-timeout-4.0.2.tar.gz", hash = "sha256:2163e1640ddb52b7a8c80d0a67a08587e5d245cc9c553a74a847056bc2976b15"}, + {file = "async_timeout-4.0.2-py3-none-any.whl", hash = "sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c"}, ] attrs = [ - {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"}, - {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, -] -backcall = [ - {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"}, - {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, + {file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"}, + {file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"}, ] -cffi = [ - {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, - {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, - {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, - {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, - {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, - {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, - {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, - {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, - {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, - {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, - {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, - {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, - {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, - {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, - {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, - {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, - {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, - {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, - {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, - {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, - {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, - {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, - {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, - {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, - {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, - {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, - {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, - {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, - {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, - {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, - {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, - {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, - {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, - {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, - {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, +charset-normalizer = [ + {file = "charset-normalizer-2.1.1.tar.gz", hash = "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845"}, + {file = "charset_normalizer-2.1.1-py3-none-any.whl", hash = "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"}, ] click = [ {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, ] colorama = [ - {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"}, - {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"}, + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] commonmark = [ {file = "commonmark-0.9.1-py2.py3-none-any.whl", hash = "sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9"}, {file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"}, ] coverage = [ - {file = "coverage-6.4.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a9032f9b7d38bdf882ac9f66ebde3afb8145f0d4c24b2e600bc4c6304aafb87e"}, - {file = "coverage-6.4.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e0524adb49c716ca763dbc1d27bedce36b14f33e6b8af6dba56886476b42957c"}, - {file = "coverage-6.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4548be38a1c810d79e097a38107b6bf2ff42151900e47d49635be69943763d8"}, - {file = "coverage-6.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f23876b018dfa5d3e98e96f5644b109090f16a4acb22064e0f06933663005d39"}, - {file = "coverage-6.4.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fe75dcfcb889b6800f072f2af5a331342d63d0c1b3d2bf0f7b4f6c353e8c9c0"}, - {file = "coverage-6.4.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:2f8553878a24b00d5ab04b7a92a2af50409247ca5c4b7a2bf4eabe94ed20d3ee"}, - {file = "coverage-6.4.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:d774d9e97007b018a651eadc1b3970ed20237395527e22cbeb743d8e73e0563d"}, - {file = "coverage-6.4.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d56f105592188ce7a797b2bd94b4a8cb2e36d5d9b0d8a1d2060ff2a71e6b9bbc"}, - {file = "coverage-6.4.2-cp310-cp310-win32.whl", hash = "sha256:d230d333b0be8042ac34808ad722eabba30036232e7a6fb3e317c49f61c93386"}, - {file = "coverage-6.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:5ef42e1db047ca42827a85e34abe973971c635f83aed49611b7f3ab49d0130f0"}, - {file = "coverage-6.4.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:25b7ec944f114f70803d6529394b64f8749e93cbfac0fe6c5ea1b7e6c14e8a46"}, - {file = "coverage-6.4.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bb00521ab4f99fdce2d5c05a91bddc0280f0afaee0e0a00425e28e209d4af07"}, - {file = "coverage-6.4.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2dff52b3e7f76ada36f82124703f4953186d9029d00d6287f17c68a75e2e6039"}, - {file = "coverage-6.4.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:147605e1702d996279bb3cc3b164f408698850011210d133a2cb96a73a2f7996"}, - {file = "coverage-6.4.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:422fa44070b42fef9fb8dabd5af03861708cdd6deb69463adc2130b7bf81332f"}, - {file = "coverage-6.4.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:8af6c26ba8df6338e57bedbf916d76bdae6308e57fc8f14397f03b5da8622b4e"}, - {file = "coverage-6.4.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:5336e0352c0b12c7e72727d50ff02557005f79a0b8dcad9219c7c4940a930083"}, - {file = "coverage-6.4.2-cp37-cp37m-win32.whl", hash = "sha256:0f211df2cba951ffcae210ee00e54921ab42e2b64e0bf2c0befc977377fb09b7"}, - {file = "coverage-6.4.2-cp37-cp37m-win_amd64.whl", hash = "sha256:a13772c19619118903d65a91f1d5fea84be494d12fd406d06c849b00d31bf120"}, - {file = "coverage-6.4.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f7bd0ffbcd03dc39490a1f40b2669cc414fae0c4e16b77bb26806a4d0b7d1452"}, - {file = "coverage-6.4.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0895ea6e6f7f9939166cc835df8fa4599e2d9b759b02d1521b574e13b859ac32"}, - {file = "coverage-6.4.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4e7ced84a11c10160c0697a6cc0b214a5d7ab21dfec1cd46e89fbf77cc66fae"}, - {file = "coverage-6.4.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80db4a47a199c4563d4a25919ff29c97c87569130375beca3483b41ad5f698e8"}, - {file = "coverage-6.4.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3def6791adf580d66f025223078dc84c64696a26f174131059ce8e91452584e1"}, - {file = "coverage-6.4.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:4f89d8e03c8a3757aae65570d14033e8edf192ee9298303db15955cadcff0c63"}, - {file = "coverage-6.4.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6d0b48aff8e9720bdec315d67723f0babd936a7211dc5df453ddf76f89c59933"}, - {file = "coverage-6.4.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2b20286c2b726f94e766e86a3fddb7b7e37af5d0c635bdfa7e4399bc523563de"}, - {file = "coverage-6.4.2-cp38-cp38-win32.whl", hash = "sha256:d714af0bdba67739598849c9f18efdcc5a0412f4993914a0ec5ce0f1e864d783"}, - {file = "coverage-6.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:5f65e5d3ff2d895dab76b1faca4586b970a99b5d4b24e9aafffc0ce94a6022d6"}, - {file = "coverage-6.4.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a697977157adc052284a7160569b36a8bbec09db3c3220642e6323b47cec090f"}, - {file = "coverage-6.4.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c77943ef768276b61c96a3eb854eba55633c7a3fddf0a79f82805f232326d33f"}, - {file = "coverage-6.4.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54d8d0e073a7f238f0666d3c7c0d37469b2aa43311e4024c925ee14f5d5a1cbe"}, - {file = "coverage-6.4.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f22325010d8824594820d6ce84fa830838f581a7fd86a9235f0d2ed6deb61e29"}, - {file = "coverage-6.4.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24b04d305ea172ccb21bee5bacd559383cba2c6fcdef85b7701cf2de4188aa55"}, - {file = "coverage-6.4.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:866ebf42b4c5dbafd64455b0a1cd5aa7b4837a894809413b930026c91e18090b"}, - {file = "coverage-6.4.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e36750fbbc422c1c46c9d13b937ab437138b998fe74a635ec88989afb57a3978"}, - {file = "coverage-6.4.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:79419370d6a637cb18553ecb25228893966bd7935a9120fa454e7076f13b627c"}, - {file = "coverage-6.4.2-cp39-cp39-win32.whl", hash = "sha256:b5e28db9199dd3833cc8a07fa6cf429a01227b5d429facb56eccd765050c26cd"}, - {file = "coverage-6.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:edfdabe7aa4f97ed2b9dd5dde52d2bb29cb466993bb9d612ddd10d0085a683cf"}, - {file = "coverage-6.4.2-pp36.pp37.pp38-none-any.whl", hash = "sha256:e2618cb2cf5a7cc8d698306e42ebcacd02fb7ef8cfc18485c59394152c70be97"}, - {file = "coverage-6.4.2.tar.gz", hash = "sha256:6c3ccfe89c36f3e5b9837b9ee507472310164f352c9fe332120b764c9d60adbe"}, -] -debugpy = [ - {file = "debugpy-1.6.2-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:77a47d596ce8c69673d5f0c9876a80cb5a6cbc964f3b31b2d44683c7c01b6634"}, - {file = "debugpy-1.6.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:726e5cc0ed5bc63e821dc371d88ddae5cba85e2ad207bf5fefc808b29421cb4c"}, - {file = "debugpy-1.6.2-cp310-cp310-win32.whl", hash = "sha256:9809bd1cdc0026fab711e280e0cb5d8f89ae5f4f74701aba5bda9a20a6afb567"}, - {file = "debugpy-1.6.2-cp310-cp310-win_amd64.whl", hash = "sha256:40741d4bbf59baca1e97a5123514afcc036423caae5f24db23a865c0b4167c34"}, - {file = "debugpy-1.6.2-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:67749e972213c395647a8798cc8377646e581e1fe97d0b1b7607e6b112ae4511"}, - {file = "debugpy-1.6.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4e3c43d650a1e5fa7110af380fb59061bcba1e7348c00237e7473c55ae499b96"}, - {file = "debugpy-1.6.2-cp37-cp37m-win32.whl", hash = "sha256:9e572c2ac3dd93f3f1a038a9226e7cc0d7326b8d345c9b9ce6fbf9cb9822e314"}, - {file = "debugpy-1.6.2-cp37-cp37m-win_amd64.whl", hash = "sha256:ac5d9e625d291a041ff3eaf65bdb816eb79a5b204cf9f1ffaf9617c0eadf96fa"}, - {file = "debugpy-1.6.2-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:9f72435bc9a2026a35a41221beff853dd4b6b17567ba9b9d349ee9512eb71ce6"}, - {file = "debugpy-1.6.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:aaf579de5ecd02634d601d7cf5b6baae5f5bab89a55ef78e0904d766ef477729"}, - {file = "debugpy-1.6.2-cp38-cp38-win32.whl", hash = "sha256:0984086a670f46c75b5046b39a55f34e4120bee78928ac4c3c7f1c7b8be1d8be"}, - {file = "debugpy-1.6.2-cp38-cp38-win_amd64.whl", hash = "sha256:19337bb8ff87da2535ac00ea3877ceaf40ff3c681421d1a96ab4d67dad031a16"}, - {file = "debugpy-1.6.2-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:163f282287ce68b00a51e9dcd7ad461ef288d740dcb3a2f22c01c62f31b62696"}, - {file = "debugpy-1.6.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4909bb2f8e5c8fe33d6ec5b7764100b494289252ebe94ec7838b30467435f1cb"}, - {file = "debugpy-1.6.2-cp39-cp39-win32.whl", hash = "sha256:3b4657d3cd20aa454b62a70040524d3e785efc9a8488d16cd0e6caeb7b2a3f07"}, - {file = "debugpy-1.6.2-cp39-cp39-win_amd64.whl", hash = "sha256:79d9ac34542b830a7954ab111ad8a4c790f1f836b895d03223aea4216b739208"}, - {file = "debugpy-1.6.2-py2.py3-none-any.whl", hash = "sha256:0bfdcf261f97a603d7ef7ab6972cdf7136201fde93d19bf3f917d0d2e43a5694"}, - {file = "debugpy-1.6.2.zip", hash = "sha256:e6047272e97a11aa6898138c1c88c8cf61838deeb2a4f0a74e63bb567f8dafc6"}, -] -decorator = [ - {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"}, - {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, + {file = "coverage-6.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef8674b0ee8cc11e2d574e3e2998aea5df5ab242e012286824ea3c6970580e53"}, + {file = "coverage-6.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:784f53ebc9f3fd0e2a3f6a78b2be1bd1f5575d7863e10c6e12504f240fd06660"}, + {file = "coverage-6.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4a5be1748d538a710f87542f22c2cad22f80545a847ad91ce45e77417293eb4"}, + {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83516205e254a0cb77d2d7bb3632ee019d93d9f4005de31dca0a8c3667d5bc04"}, + {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af4fffaffc4067232253715065e30c5a7ec6faac36f8fc8d6f64263b15f74db0"}, + {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:97117225cdd992a9c2a5515db1f66b59db634f59d0679ca1fa3fe8da32749cae"}, + {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a1170fa54185845505fbfa672f1c1ab175446c887cce8212c44149581cf2d466"}, + {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:11b990d520ea75e7ee8dcab5bc908072aaada194a794db9f6d7d5cfd19661e5a"}, + {file = "coverage-6.5.0-cp310-cp310-win32.whl", hash = "sha256:5dbec3b9095749390c09ab7c89d314727f18800060d8d24e87f01fb9cfb40b32"}, + {file = "coverage-6.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:59f53f1dc5b656cafb1badd0feb428c1e7bc19b867479ff72f7a9dd9b479f10e"}, + {file = "coverage-6.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4a5375e28c5191ac38cca59b38edd33ef4cc914732c916f2929029b4bfb50795"}, + {file = "coverage-6.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4ed2820d919351f4167e52425e096af41bfabacb1857186c1ea32ff9983ed75"}, + {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33a7da4376d5977fbf0a8ed91c4dffaaa8dbf0ddbf4c8eea500a2486d8bc4d7b"}, + {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8fb6cf131ac4070c9c5a3e21de0f7dc5a0fbe8bc77c9456ced896c12fcdad91"}, + {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a6b7d95969b8845250586f269e81e5dfdd8ff828ddeb8567a4a2eaa7313460c4"}, + {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1ef221513e6f68b69ee9e159506d583d31aa3567e0ae84eaad9d6ec1107dddaa"}, + {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cca4435eebea7962a52bdb216dec27215d0df64cf27fc1dd538415f5d2b9da6b"}, + {file = "coverage-6.5.0-cp311-cp311-win32.whl", hash = "sha256:98e8a10b7a314f454d9eff4216a9a94d143a7ee65018dd12442e898ee2310578"}, + {file = "coverage-6.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:bc8ef5e043a2af066fa8cbfc6e708d58017024dc4345a1f9757b329a249f041b"}, + {file = "coverage-6.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4433b90fae13f86fafff0b326453dd42fc9a639a0d9e4eec4d366436d1a41b6d"}, + {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4f05d88d9a80ad3cac6244d36dd89a3c00abc16371769f1340101d3cb899fc3"}, + {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:94e2565443291bd778421856bc975d351738963071e9b8839ca1fc08b42d4bef"}, + {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:027018943386e7b942fa832372ebc120155fd970837489896099f5cfa2890f79"}, + {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:255758a1e3b61db372ec2736c8e2a1fdfaf563977eedbdf131de003ca5779b7d"}, + {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:851cf4ff24062c6aec510a454b2584f6e998cada52d4cb58c5e233d07172e50c"}, + {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:12adf310e4aafddc58afdb04d686795f33f4d7a6fa67a7a9d4ce7d6ae24d949f"}, + {file = "coverage-6.5.0-cp37-cp37m-win32.whl", hash = "sha256:b5604380f3415ba69de87a289a2b56687faa4fe04dbee0754bfcae433489316b"}, + {file = "coverage-6.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:4a8dbc1f0fbb2ae3de73eb0bdbb914180c7abfbf258e90b311dcd4f585d44bd2"}, + {file = "coverage-6.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d900bb429fdfd7f511f868cedd03a6bbb142f3f9118c09b99ef8dc9bf9643c3c"}, + {file = "coverage-6.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2198ea6fc548de52adc826f62cb18554caedfb1d26548c1b7c88d8f7faa8f6ba"}, + {file = "coverage-6.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c4459b3de97b75e3bd6b7d4b7f0db13f17f504f3d13e2a7c623786289dd670e"}, + {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:20c8ac5386253717e5ccc827caad43ed66fea0efe255727b1053a8154d952398"}, + {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b07130585d54fe8dff3d97b93b0e20290de974dc8177c320aeaf23459219c0b"}, + {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dbdb91cd8c048c2b09eb17713b0c12a54fbd587d79adcebad543bc0cd9a3410b"}, + {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:de3001a203182842a4630e7b8d1a2c7c07ec1b45d3084a83d5d227a3806f530f"}, + {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e07f4a4a9b41583d6eabec04f8b68076ab3cd44c20bd29332c6572dda36f372e"}, + {file = "coverage-6.5.0-cp38-cp38-win32.whl", hash = "sha256:6d4817234349a80dbf03640cec6109cd90cba068330703fa65ddf56b60223a6d"}, + {file = "coverage-6.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:7ccf362abd726b0410bf8911c31fbf97f09f8f1061f8c1cf03dfc4b6372848f6"}, + {file = "coverage-6.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:633713d70ad6bfc49b34ead4060531658dc6dfc9b3eb7d8a716d5873377ab745"}, + {file = "coverage-6.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:95203854f974e07af96358c0b261f1048d8e1083f2de9b1c565e1be4a3a48cfc"}, + {file = "coverage-6.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9023e237f4c02ff739581ef35969c3739445fb059b060ca51771e69101efffe"}, + {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:265de0fa6778d07de30bcf4d9dc471c3dc4314a23a3c6603d356a3c9abc2dfcf"}, + {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f830ed581b45b82451a40faabb89c84e1a998124ee4212d440e9c6cf70083e5"}, + {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7b6be138d61e458e18d8e6ddcddd36dd96215edfe5f1168de0b1b32635839b62"}, + {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:42eafe6778551cf006a7c43153af1211c3aaab658d4d66fa5fcc021613d02518"}, + {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:723e8130d4ecc8f56e9a611e73b31219595baa3bb252d539206f7bbbab6ffc1f"}, + {file = "coverage-6.5.0-cp39-cp39-win32.whl", hash = "sha256:d9ecf0829c6a62b9b573c7bb6d4dcd6ba8b6f80be9ba4fc7ed50bf4ac9aecd72"}, + {file = "coverage-6.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc2af30ed0d5ae0b1abdb4ebdce598eafd5b35397d4d75deb341a614d333d987"}, + {file = "coverage-6.5.0-pp36.pp37.pp38-none-any.whl", hash = "sha256:1431986dac3923c5945271f169f59c45b8802a114c8f548d611f2015133df77a"}, + {file = "coverage-6.5.0.tar.gz", hash = "sha256:f642e90754ee3e06b0e7e51bce3379590e76b7f76b708e1a71ff043f87025c84"}, ] docopt = [ {file = "docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491"}, ] -entrypoints = [ - {file = "entrypoints-0.4-py3-none-any.whl", hash = "sha256:f174b5ff827504fd3cd97cc3f8649f3693f51538c7e4bdf3ef002c8429d42f9f"}, - {file = "entrypoints-0.4.tar.gz", hash = "sha256:b706eddaa9218a19ebcd67b56818f05bb27589b1ca9e8d797b74affad4ccacd4"}, -] exceptiongroup = [ - {file = "exceptiongroup-1.0.0rc8-py3-none-any.whl", hash = "sha256:ab0a968e1ef769e55d9a596f4a89f7be9ffedbc9fdefdb77cc68cf5c33ce1035"}, - {file = "exceptiongroup-1.0.0rc8.tar.gz", hash = "sha256:6990c24f06b8d33c8065cfe43e5e8a4bfa384e0358be036af9cc60b6321bd11a"}, + {file = "exceptiongroup-1.0.4-py3-none-any.whl", hash = "sha256:542adf9dea4055530d6e1279602fa5cb11dab2395fa650b8674eaec35fc4a828"}, + {file = "exceptiongroup-1.0.4.tar.gz", hash = "sha256:bd14967b79cd9bdb54d97323216f8fdf533e278df937aa2a90089e7d6e06e5ec"}, ] execnet = [ {file = "execnet-1.9.0-py2.py3-none-any.whl", hash = "sha256:a295f7cc774947aac58dde7fdc85f4aa00c42adf5d8f5468fc630c1acf30a142"}, {file = "execnet-1.9.0.tar.gz", hash = "sha256:8f694f3ba9cc92cab508b152dcfe322153975c29bda272e2fd7f3f00f36e47c5"}, ] -executing = [ - {file = "executing-0.8.3-py2.py3-none-any.whl", hash = "sha256:d1eef132db1b83649a3905ca6dd8897f71ac6f8cac79a7e58a1a09cf137546c9"}, - {file = "executing-0.8.3.tar.gz", hash = "sha256:c6554e21c6b060590a6d3be4b82fb78f8f0194d809de5ea7df1c093763311501"}, -] filelock = [ - {file = "filelock-3.7.1-py3-none-any.whl", hash = "sha256:37def7b658813cda163b56fc564cdc75e86d338246458c4c28ae84cabefa2404"}, - {file = "filelock-3.7.1.tar.gz", hash = "sha256:3a0fd85166ad9dbab54c9aec96737b744106dc5f15c0b09a6744a445299fcf04"}, + {file = "filelock-3.8.0-py3-none-any.whl", hash = "sha256:617eb4e5eedc82fc5f47b6d61e4d11cb837c56cb4544e39081099fa17ad109d4"}, + {file = "filelock-3.8.0.tar.gz", hash = "sha256:55447caa666f2198c5b6b13a26d2084d26fa5b115c00d065664b2124680c4edc"}, +] +frozenlist = [ + {file = "frozenlist-1.3.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff8bf625fe85e119553b5383ba0fb6aa3d0ec2ae980295aaefa552374926b3f4"}, + {file = "frozenlist-1.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dfbac4c2dfcc082fcf8d942d1e49b6aa0766c19d3358bd86e2000bf0fa4a9cf0"}, + {file = "frozenlist-1.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b1c63e8d377d039ac769cd0926558bb7068a1f7abb0f003e3717ee003ad85530"}, + {file = "frozenlist-1.3.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7fdfc24dcfce5b48109867c13b4cb15e4660e7bd7661741a391f821f23dfdca7"}, + {file = "frozenlist-1.3.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2c926450857408e42f0bbc295e84395722ce74bae69a3b2aa2a65fe22cb14b99"}, + {file = "frozenlist-1.3.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1841e200fdafc3d51f974d9d377c079a0694a8f06de2e67b48150328d66d5483"}, + {file = "frozenlist-1.3.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f470c92737afa7d4c3aacc001e335062d582053d4dbe73cda126f2d7031068dd"}, + {file = "frozenlist-1.3.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:783263a4eaad7c49983fe4b2e7b53fa9770c136c270d2d4bbb6d2192bf4d9caf"}, + {file = "frozenlist-1.3.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:924620eef691990dfb56dc4709f280f40baee568c794b5c1885800c3ecc69816"}, + {file = "frozenlist-1.3.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ae4dc05c465a08a866b7a1baf360747078b362e6a6dbeb0c57f234db0ef88ae0"}, + {file = "frozenlist-1.3.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:bed331fe18f58d844d39ceb398b77d6ac0b010d571cba8267c2e7165806b00ce"}, + {file = "frozenlist-1.3.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:02c9ac843e3390826a265e331105efeab489ffaf4dd86384595ee8ce6d35ae7f"}, + {file = "frozenlist-1.3.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9545a33965d0d377b0bc823dcabf26980e77f1b6a7caa368a365a9497fb09420"}, + {file = "frozenlist-1.3.3-cp310-cp310-win32.whl", hash = "sha256:d5cd3ab21acbdb414bb6c31958d7b06b85eeb40f66463c264a9b343a4e238642"}, + {file = "frozenlist-1.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:b756072364347cb6aa5b60f9bc18e94b2f79632de3b0190253ad770c5df17db1"}, + {file = "frozenlist-1.3.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b4395e2f8d83fbe0c627b2b696acce67868793d7d9750e90e39592b3626691b7"}, + {file = "frozenlist-1.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:14143ae966a6229350021384870458e4777d1eae4c28d1a7aa47f24d030e6678"}, + {file = "frozenlist-1.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5d8860749e813a6f65bad8285a0520607c9500caa23fea6ee407e63debcdbef6"}, + {file = "frozenlist-1.3.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23d16d9f477bb55b6154654e0e74557040575d9d19fe78a161bd33d7d76808e8"}, + {file = "frozenlist-1.3.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb82dbba47a8318e75f679690190c10a5e1f447fbf9df41cbc4c3afd726d88cb"}, + {file = "frozenlist-1.3.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9309869032abb23d196cb4e4db574232abe8b8be1339026f489eeb34a4acfd91"}, + {file = "frozenlist-1.3.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a97b4fe50b5890d36300820abd305694cb865ddb7885049587a5678215782a6b"}, + {file = "frozenlist-1.3.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c188512b43542b1e91cadc3c6c915a82a5eb95929134faf7fd109f14f9892ce4"}, + {file = "frozenlist-1.3.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:303e04d422e9b911a09ad499b0368dc551e8c3cd15293c99160c7f1f07b59a48"}, + {file = "frozenlist-1.3.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:0771aed7f596c7d73444c847a1c16288937ef988dc04fb9f7be4b2aa91db609d"}, + {file = "frozenlist-1.3.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:66080ec69883597e4d026f2f71a231a1ee9887835902dbe6b6467d5a89216cf6"}, + {file = "frozenlist-1.3.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:41fe21dc74ad3a779c3d73a2786bdf622ea81234bdd4faf90b8b03cad0c2c0b4"}, + {file = "frozenlist-1.3.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f20380df709d91525e4bee04746ba612a4df0972c1b8f8e1e8af997e678c7b81"}, + {file = "frozenlist-1.3.3-cp311-cp311-win32.whl", hash = "sha256:f30f1928162e189091cf4d9da2eac617bfe78ef907a761614ff577ef4edfb3c8"}, + {file = "frozenlist-1.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:a6394d7dadd3cfe3f4b3b186e54d5d8504d44f2d58dcc89d693698e8b7132b32"}, + {file = "frozenlist-1.3.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8df3de3a9ab8325f94f646609a66cbeeede263910c5c0de0101079ad541af332"}, + {file = "frozenlist-1.3.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0693c609e9742c66ba4870bcee1ad5ff35462d5ffec18710b4ac89337ff16e27"}, + {file = "frozenlist-1.3.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd4210baef299717db0a600d7a3cac81d46ef0e007f88c9335db79f8979c0d3d"}, + {file = "frozenlist-1.3.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:394c9c242113bfb4b9aa36e2b80a05ffa163a30691c7b5a29eba82e937895d5e"}, + {file = "frozenlist-1.3.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6327eb8e419f7d9c38f333cde41b9ae348bec26d840927332f17e887a8dcb70d"}, + {file = "frozenlist-1.3.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e24900aa13212e75e5b366cb9065e78bbf3893d4baab6052d1aca10d46d944c"}, + {file = "frozenlist-1.3.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:3843f84a6c465a36559161e6c59dce2f2ac10943040c2fd021cfb70d58c4ad56"}, + {file = "frozenlist-1.3.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:84610c1502b2461255b4c9b7d5e9c48052601a8957cd0aea6ec7a7a1e1fb9420"}, + {file = "frozenlist-1.3.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:c21b9aa40e08e4f63a2f92ff3748e6b6c84d717d033c7b3438dd3123ee18f70e"}, + {file = "frozenlist-1.3.3-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:efce6ae830831ab6a22b9b4091d411698145cb9b8fc869e1397ccf4b4b6455cb"}, + {file = "frozenlist-1.3.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:40de71985e9042ca00b7953c4f41eabc3dc514a2d1ff534027f091bc74416401"}, + {file = "frozenlist-1.3.3-cp37-cp37m-win32.whl", hash = "sha256:180c00c66bde6146a860cbb81b54ee0df350d2daf13ca85b275123bbf85de18a"}, + {file = "frozenlist-1.3.3-cp37-cp37m-win_amd64.whl", hash = "sha256:9bbbcedd75acdfecf2159663b87f1bb5cfc80e7cd99f7ddd9d66eb98b14a8411"}, + {file = "frozenlist-1.3.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:034a5c08d36649591be1cbb10e09da9f531034acfe29275fc5454a3b101ce41a"}, + {file = "frozenlist-1.3.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ba64dc2b3b7b158c6660d49cdb1d872d1d0bf4e42043ad8d5006099479a194e5"}, + {file = "frozenlist-1.3.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:47df36a9fe24054b950bbc2db630d508cca3aa27ed0566c0baf661225e52c18e"}, + {file = "frozenlist-1.3.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:008a054b75d77c995ea26629ab3a0c0d7281341f2fa7e1e85fa6153ae29ae99c"}, + {file = "frozenlist-1.3.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:841ea19b43d438a80b4de62ac6ab21cfe6827bb8a9dc62b896acc88eaf9cecba"}, + {file = "frozenlist-1.3.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e235688f42b36be2b6b06fc37ac2126a73b75fb8d6bc66dd632aa35286238703"}, + {file = "frozenlist-1.3.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca713d4af15bae6e5d79b15c10c8522859a9a89d3b361a50b817c98c2fb402a2"}, + {file = "frozenlist-1.3.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ac5995f2b408017b0be26d4a1d7c61bce106ff3d9e3324374d66b5964325448"}, + {file = "frozenlist-1.3.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a4ae8135b11652b08a8baf07631d3ebfe65a4c87909dbef5fa0cdde440444ee4"}, + {file = "frozenlist-1.3.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4ea42116ceb6bb16dbb7d526e242cb6747b08b7710d9782aa3d6732bd8d27649"}, + {file = "frozenlist-1.3.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:810860bb4bdce7557bc0febb84bbd88198b9dbc2022d8eebe5b3590b2ad6c842"}, + {file = "frozenlist-1.3.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:ee78feb9d293c323b59a6f2dd441b63339a30edf35abcb51187d2fc26e696d13"}, + {file = "frozenlist-1.3.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0af2e7c87d35b38732e810befb9d797a99279cbb85374d42ea61c1e9d23094b3"}, + {file = "frozenlist-1.3.3-cp38-cp38-win32.whl", hash = "sha256:899c5e1928eec13fd6f6d8dc51be23f0d09c5281e40d9cf4273d188d9feeaf9b"}, + {file = "frozenlist-1.3.3-cp38-cp38-win_amd64.whl", hash = "sha256:7f44e24fa70f6fbc74aeec3e971f60a14dde85da364aa87f15d1be94ae75aeef"}, + {file = "frozenlist-1.3.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2b07ae0c1edaa0a36339ec6cce700f51b14a3fc6545fdd32930d2c83917332cf"}, + {file = "frozenlist-1.3.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ebb86518203e12e96af765ee89034a1dbb0c3c65052d1b0c19bbbd6af8a145e1"}, + {file = "frozenlist-1.3.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5cf820485f1b4c91e0417ea0afd41ce5cf5965011b3c22c400f6d144296ccbc0"}, + {file = "frozenlist-1.3.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c11e43016b9024240212d2a65043b70ed8dfd3b52678a1271972702d990ac6d"}, + {file = "frozenlist-1.3.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8fa3c6e3305aa1146b59a09b32b2e04074945ffcfb2f0931836d103a2c38f936"}, + {file = "frozenlist-1.3.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:352bd4c8c72d508778cf05ab491f6ef36149f4d0cb3c56b1b4302852255d05d5"}, + {file = "frozenlist-1.3.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65a5e4d3aa679610ac6e3569e865425b23b372277f89b5ef06cf2cdaf1ebf22b"}, + {file = "frozenlist-1.3.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1e2c1185858d7e10ff045c496bbf90ae752c28b365fef2c09cf0fa309291669"}, + {file = "frozenlist-1.3.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f163d2fd041c630fed01bc48d28c3ed4a3b003c00acd396900e11ee5316b56bb"}, + {file = "frozenlist-1.3.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:05cdb16d09a0832eedf770cb7bd1fe57d8cf4eaf5aced29c4e41e3f20b30a784"}, + {file = "frozenlist-1.3.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:8bae29d60768bfa8fb92244b74502b18fae55a80eac13c88eb0b496d4268fd2d"}, + {file = "frozenlist-1.3.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:eedab4c310c0299961ac285591acd53dc6723a1ebd90a57207c71f6e0c2153ab"}, + {file = "frozenlist-1.3.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3bbdf44855ed8f0fbcd102ef05ec3012d6a4fd7c7562403f76ce6a52aeffb2b1"}, + {file = "frozenlist-1.3.3-cp39-cp39-win32.whl", hash = "sha256:efa568b885bca461f7c7b9e032655c0c143d305bf01c30caf6db2854a4532b38"}, + {file = "frozenlist-1.3.3-cp39-cp39-win_amd64.whl", hash = "sha256:cfe33efc9cb900a4c46f91a5ceba26d6df370ffddd9ca386eb1d4f0ad97b9ea9"}, + {file = "frozenlist-1.3.3.tar.gz", hash = "sha256:58bcc55721e8a90b88332d6cd441261ebb22342e238296bb330968952fbb3a6a"}, ] hypothesis = [ - {file = "hypothesis-6.50.1-py3-none-any.whl", hash = "sha256:b69c6152dc0e346e228757f76895d6351e8872394b6da3bf1deac26de6cc6956"}, - {file = "hypothesis-6.50.1.tar.gz", hash = "sha256:1a19ade3b27825cab622c95fcf25182a27a42f97589c163173fcbdafb8621d1e"}, + {file = "hypothesis-6.58.0-py3-none-any.whl", hash = "sha256:c2ffa10c94ff1a4a12f4ff515d78a4a3060a7c5b9968331d7b2707576a29570d"}, + {file = "hypothesis-6.58.0.tar.gz", hash = "sha256:89d9aa676dac7275755371d42d65fc81366ff0e0289ab525aa2bb3dcf7c98b9f"}, +] +idna = [ + {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, + {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, +] +importlib-metadata = [ + {file = "importlib_metadata-4.13.0-py3-none-any.whl", hash = "sha256:8a8a81bcf996e74fee46f0d16bd3eaa382a7eb20fd82445c3ad11f4090334116"}, + {file = "importlib_metadata-4.13.0.tar.gz", hash = "sha256:dd0173e8f150d6815e098fd354f6414b0f079af4644ddfe90c71e2fc6174346d"}, ] iniconfig = [ {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, ] -ipykernel = [ - {file = "ipykernel-6.15.1-py3-none-any.whl", hash = "sha256:d8969c5b23b0e453a23166da5a669c954db399789293fcb03fec5cb25367e43c"}, - {file = "ipykernel-6.15.1.tar.gz", hash = "sha256:37acc3254caa8a0dafcddddc8dc863a60ad1b46487b68aee361d9a15bda98112"}, -] -ipython = [ - {file = "ipython-8.4.0-py3-none-any.whl", hash = "sha256:7ca74052a38fa25fe9bedf52da0be7d3fdd2fb027c3b778ea78dfe8c212937d1"}, - {file = "ipython-8.4.0.tar.gz", hash = "sha256:f2db3a10254241d9b447232cec8b424847f338d9d36f9a577a6192c332a46abd"}, -] -jedi = [ - {file = "jedi-0.18.1-py2.py3-none-any.whl", hash = "sha256:637c9635fcf47945ceb91cd7f320234a7be540ded6f3e99a50cb6febdfd1ba8d"}, - {file = "jedi-0.18.1.tar.gz", hash = "sha256:74137626a64a99c8eb6ae5832d99b3bdd7d29a3850fe2aa80a4126b2a7d949ab"}, -] -jupyter-client = [ - {file = "jupyter_client-7.3.4-py3-none-any.whl", hash = "sha256:17d74b0d0a7b24f1c8c527b24fcf4607c56bee542ffe8e3418e50b21e514b621"}, - {file = "jupyter_client-7.3.4.tar.gz", hash = "sha256:aa9a6c32054b290374f95f73bb0cae91455c58dfb84f65c8591912b8f65e6d56"}, -] -jupyter-core = [ - {file = "jupyter_core-4.11.1-py3-none-any.whl", hash = "sha256:715e22bb6cc7db3718fddfac1f69f1c7e899ca00e42bdfd4bf3705452b9fd84a"}, - {file = "jupyter_core-4.11.1.tar.gz", hash = "sha256:2e5f244d44894c4154d06aeae3419dd7f1b0ef4494dc5584929b398c61cfd314"}, -] -matplotlib-inline = [ - {file = "matplotlib-inline-0.1.3.tar.gz", hash = "sha256:a04bfba22e0d1395479f866853ec1ee28eea1485c1d69a6faf00dc3e24ff34ee"}, - {file = "matplotlib_inline-0.1.3-py3-none-any.whl", hash = "sha256:aed605ba3b72462d64d475a21a9296f400a19c4f74a31b59103d2a99ffd5aa5c"}, +msgpack = [ + {file = "msgpack-1.0.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4ab251d229d10498e9a2f3b1e68ef64cb393394ec477e3370c457f9430ce9250"}, + {file = "msgpack-1.0.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:112b0f93202d7c0fef0b7810d465fde23c746a2d482e1e2de2aafd2ce1492c88"}, + {file = "msgpack-1.0.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:002b5c72b6cd9b4bafd790f364b8480e859b4712e91f43014fe01e4f957b8467"}, + {file = "msgpack-1.0.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35bc0faa494b0f1d851fd29129b2575b2e26d41d177caacd4206d81502d4c6a6"}, + {file = "msgpack-1.0.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4733359808c56d5d7756628736061c432ded018e7a1dff2d35a02439043321aa"}, + {file = "msgpack-1.0.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb514ad14edf07a1dbe63761fd30f89ae79b42625731e1ccf5e1f1092950eaa6"}, + {file = "msgpack-1.0.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c23080fdeec4716aede32b4e0ef7e213c7b1093eede9ee010949f2a418ced6ba"}, + {file = "msgpack-1.0.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:49565b0e3d7896d9ea71d9095df15b7f75a035c49be733051c34762ca95bbf7e"}, + {file = "msgpack-1.0.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:aca0f1644d6b5a73eb3e74d4d64d5d8c6c3d577e753a04c9e9c87d07692c58db"}, + {file = "msgpack-1.0.4-cp310-cp310-win32.whl", hash = "sha256:0dfe3947db5fb9ce52aaea6ca28112a170db9eae75adf9339a1aec434dc954ef"}, + {file = "msgpack-1.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:4dea20515f660aa6b7e964433b1808d098dcfcabbebeaaad240d11f909298075"}, + {file = "msgpack-1.0.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e83f80a7fec1a62cf4e6c9a660e39c7f878f603737a0cdac8c13131d11d97f52"}, + {file = "msgpack-1.0.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c11a48cf5e59026ad7cb0dc29e29a01b5a66a3e333dc11c04f7e991fc5510a9"}, + {file = "msgpack-1.0.4-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1276e8f34e139aeff1c77a3cefb295598b504ac5314d32c8c3d54d24fadb94c9"}, + {file = "msgpack-1.0.4-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c9566f2c39ccced0a38d37c26cc3570983b97833c365a6044edef3574a00c08"}, + {file = "msgpack-1.0.4-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:fcb8a47f43acc113e24e910399376f7277cf8508b27e5b88499f053de6b115a8"}, + {file = "msgpack-1.0.4-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:76ee788122de3a68a02ed6f3a16bbcd97bc7c2e39bd4d94be2f1821e7c4a64e6"}, + {file = "msgpack-1.0.4-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:0a68d3ac0104e2d3510de90a1091720157c319ceeb90d74f7b5295a6bee51bae"}, + {file = "msgpack-1.0.4-cp36-cp36m-win32.whl", hash = "sha256:85f279d88d8e833ec015650fd15ae5eddce0791e1e8a59165318f371158efec6"}, + {file = "msgpack-1.0.4-cp36-cp36m-win_amd64.whl", hash = "sha256:c1683841cd4fa45ac427c18854c3ec3cd9b681694caf5bff04edb9387602d661"}, + {file = "msgpack-1.0.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a75dfb03f8b06f4ab093dafe3ddcc2d633259e6c3f74bb1b01996f5d8aa5868c"}, + {file = "msgpack-1.0.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9667bdfdf523c40d2511f0e98a6c9d3603be6b371ae9a238b7ef2dc4e7a427b0"}, + {file = "msgpack-1.0.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11184bc7e56fd74c00ead4f9cc9a3091d62ecb96e97653add7a879a14b003227"}, + {file = "msgpack-1.0.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ac5bd7901487c4a1dd51a8c58f2632b15d838d07ceedaa5e4c080f7190925bff"}, + {file = "msgpack-1.0.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1e91d641d2bfe91ba4c52039adc5bccf27c335356055825c7f88742c8bb900dd"}, + {file = "msgpack-1.0.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2a2df1b55a78eb5f5b7d2a4bb221cd8363913830145fad05374a80bf0877cb1e"}, + {file = "msgpack-1.0.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:545e3cf0cf74f3e48b470f68ed19551ae6f9722814ea969305794645da091236"}, + {file = "msgpack-1.0.4-cp37-cp37m-win32.whl", hash = "sha256:2cc5ca2712ac0003bcb625c96368fd08a0f86bbc1a5578802512d87bc592fe44"}, + {file = "msgpack-1.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:eba96145051ccec0ec86611fe9cf693ce55f2a3ce89c06ed307de0e085730ec1"}, + {file = "msgpack-1.0.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:7760f85956c415578c17edb39eed99f9181a48375b0d4a94076d84148cf67b2d"}, + {file = "msgpack-1.0.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:449e57cc1ff18d3b444eb554e44613cffcccb32805d16726a5494038c3b93dab"}, + {file = "msgpack-1.0.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d603de2b8d2ea3f3bcb2efe286849aa7a81531abc52d8454da12f46235092bcb"}, + {file = "msgpack-1.0.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48f5d88c99f64c456413d74a975bd605a9b0526293218a3b77220a2c15458ba9"}, + {file = "msgpack-1.0.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6916c78f33602ecf0509cc40379271ba0f9ab572b066bd4bdafd7434dee4bc6e"}, + {file = "msgpack-1.0.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:81fc7ba725464651190b196f3cd848e8553d4d510114a954681fd0b9c479d7e1"}, + {file = "msgpack-1.0.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d5b5b962221fa2c5d3a7f8133f9abffc114fe218eb4365e40f17732ade576c8e"}, + {file = "msgpack-1.0.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:77ccd2af37f3db0ea59fb280fa2165bf1b096510ba9fe0cc2bf8fa92a22fdb43"}, + {file = "msgpack-1.0.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b17be2478b622939e39b816e0aa8242611cc8d3583d1cd8ec31b249f04623243"}, + {file = "msgpack-1.0.4-cp38-cp38-win32.whl", hash = "sha256:2bb8cdf50dd623392fa75525cce44a65a12a00c98e1e37bf0fb08ddce2ff60d2"}, + {file = "msgpack-1.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:26b8feaca40a90cbe031b03d82b2898bf560027160d3eae1423f4a67654ec5d6"}, + {file = "msgpack-1.0.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:462497af5fd4e0edbb1559c352ad84f6c577ffbbb708566a0abaaa84acd9f3ae"}, + {file = "msgpack-1.0.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2999623886c5c02deefe156e8f869c3b0aaeba14bfc50aa2486a0415178fce55"}, + {file = "msgpack-1.0.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f0029245c51fd9473dc1aede1160b0a29f4a912e6b1dd353fa6d317085b219da"}, + {file = "msgpack-1.0.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed6f7b854a823ea44cf94919ba3f727e230da29feb4a99711433f25800cf747f"}, + {file = "msgpack-1.0.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0df96d6eaf45ceca04b3f3b4b111b86b33785683d682c655063ef8057d61fd92"}, + {file = "msgpack-1.0.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6a4192b1ab40f8dca3f2877b70e63799d95c62c068c84dc028b40a6cb03ccd0f"}, + {file = "msgpack-1.0.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0e3590f9fb9f7fbc36df366267870e77269c03172d086fa76bb4eba8b2b46624"}, + {file = "msgpack-1.0.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:1576bd97527a93c44fa856770197dec00d223b0b9f36ef03f65bac60197cedf8"}, + {file = "msgpack-1.0.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:63e29d6e8c9ca22b21846234913c3466b7e4ee6e422f205a2988083de3b08cae"}, + {file = "msgpack-1.0.4-cp39-cp39-win32.whl", hash = "sha256:fb62ea4b62bfcb0b380d5680f9a4b3f9a2d166d9394e9bbd9666c0ee09a3645c"}, + {file = "msgpack-1.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:4d5834a2a48965a349da1c5a79760d94a1a0172fbb5ab6b5b33cbf8447e109ce"}, + {file = "msgpack-1.0.4.tar.gz", hash = "sha256:f5d869c18f030202eb412f08b28d2afeea553d6613aee89e200d7aca7ef01f5f"}, +] +multidict = [ + {file = "multidict-6.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b9e95a740109c6047602f4db4da9949e6c5945cefbad34a1299775ddc9a62e2"}, + {file = "multidict-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac0e27844758d7177989ce406acc6a83c16ed4524ebc363c1f748cba184d89d3"}, + {file = "multidict-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:041b81a5f6b38244b34dc18c7b6aba91f9cdaf854d9a39e5ff0b58e2b5773b9c"}, + {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fdda29a3c7e76a064f2477c9aab1ba96fd94e02e386f1e665bca1807fc5386f"}, + {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3368bf2398b0e0fcbf46d85795adc4c259299fec50c1416d0f77c0a843a3eed9"}, + {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4f052ee022928d34fe1f4d2bc743f32609fb79ed9c49a1710a5ad6b2198db20"}, + {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:225383a6603c086e6cef0f2f05564acb4f4d5f019a4e3e983f572b8530f70c88"}, + {file = "multidict-6.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50bd442726e288e884f7be9071016c15a8742eb689a593a0cac49ea093eef0a7"}, + {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:47e6a7e923e9cada7c139531feac59448f1f47727a79076c0b1ee80274cd8eee"}, + {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0556a1d4ea2d949efe5fd76a09b4a82e3a4a30700553a6725535098d8d9fb672"}, + {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:626fe10ac87851f4cffecee161fc6f8f9853f0f6f1035b59337a51d29ff3b4f9"}, + {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:8064b7c6f0af936a741ea1efd18690bacfbae4078c0c385d7c3f611d11f0cf87"}, + {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2d36e929d7f6a16d4eb11b250719c39560dd70545356365b494249e2186bc389"}, + {file = "multidict-6.0.2-cp310-cp310-win32.whl", hash = "sha256:fcb91630817aa8b9bc4a74023e4198480587269c272c58b3279875ed7235c293"}, + {file = "multidict-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:8cbf0132f3de7cc6c6ce00147cc78e6439ea736cee6bca4f068bcf892b0fd658"}, + {file = "multidict-6.0.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:05f6949d6169878a03e607a21e3b862eaf8e356590e8bdae4227eedadacf6e51"}, + {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2c2e459f7050aeb7c1b1276763364884595d47000c1cddb51764c0d8976e608"}, + {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d0509e469d48940147e1235d994cd849a8f8195e0bca65f8f5439c56e17872a3"}, + {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:514fe2b8d750d6cdb4712346a2c5084a80220821a3e91f3f71eec11cf8d28fd4"}, + {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19adcfc2a7197cdc3987044e3f415168fc5dc1f720c932eb1ef4f71a2067e08b"}, + {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b9d153e7f1f9ba0b23ad1568b3b9e17301e23b042c23870f9ee0522dc5cc79e8"}, + {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:aef9cc3d9c7d63d924adac329c33835e0243b5052a6dfcbf7732a921c6e918ba"}, + {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4571f1beddff25f3e925eea34268422622963cd8dc395bb8778eb28418248e43"}, + {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:d48b8ee1d4068561ce8033d2c344cf5232cb29ee1a0206a7b828c79cbc5982b8"}, + {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:45183c96ddf61bf96d2684d9fbaf6f3564d86b34cb125761f9a0ef9e36c1d55b"}, + {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:75bdf08716edde767b09e76829db8c1e5ca9d8bb0a8d4bd94ae1eafe3dac5e15"}, + {file = "multidict-6.0.2-cp37-cp37m-win32.whl", hash = "sha256:a45e1135cb07086833ce969555df39149680e5471c04dfd6a915abd2fc3f6dbc"}, + {file = "multidict-6.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6f3cdef8a247d1eafa649085812f8a310e728bdf3900ff6c434eafb2d443b23a"}, + {file = "multidict-6.0.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0327292e745a880459ef71be14e709aaea2f783f3537588fb4ed09b6c01bca60"}, + {file = "multidict-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e875b6086e325bab7e680e4316d667fc0e5e174bb5611eb16b3ea121c8951b86"}, + {file = "multidict-6.0.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:feea820722e69451743a3d56ad74948b68bf456984d63c1a92e8347b7b88452d"}, + {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cc57c68cb9139c7cd6fc39f211b02198e69fb90ce4bc4a094cf5fe0d20fd8b0"}, + {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:497988d6b6ec6ed6f87030ec03280b696ca47dbf0648045e4e1d28b80346560d"}, + {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:89171b2c769e03a953d5969b2f272efa931426355b6c0cb508022976a17fd376"}, + {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:684133b1e1fe91eda8fa7447f137c9490a064c6b7f392aa857bba83a28cfb693"}, + {file = "multidict-6.0.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd9fc9c4849a07f3635ccffa895d57abce554b467d611a5009ba4f39b78a8849"}, + {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e07c8e79d6e6fd37b42f3250dba122053fddb319e84b55dd3a8d6446e1a7ee49"}, + {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4070613ea2227da2bfb2c35a6041e4371b0af6b0be57f424fe2318b42a748516"}, + {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:47fbeedbf94bed6547d3aa632075d804867a352d86688c04e606971595460227"}, + {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:5774d9218d77befa7b70d836004a768fb9aa4fdb53c97498f4d8d3f67bb9cfa9"}, + {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2957489cba47c2539a8eb7ab32ff49101439ccf78eab724c828c1a54ff3ff98d"}, + {file = "multidict-6.0.2-cp38-cp38-win32.whl", hash = "sha256:e5b20e9599ba74391ca0cfbd7b328fcc20976823ba19bc573983a25b32e92b57"}, + {file = "multidict-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:8004dca28e15b86d1b1372515f32eb6f814bdf6f00952699bdeb541691091f96"}, + {file = "multidict-6.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2e4a0785b84fb59e43c18a015ffc575ba93f7d1dbd272b4cdad9f5134b8a006c"}, + {file = "multidict-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6701bf8a5d03a43375909ac91b6980aea74b0f5402fbe9428fc3f6edf5d9677e"}, + {file = "multidict-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a007b1638e148c3cfb6bf0bdc4f82776cef0ac487191d093cdc316905e504071"}, + {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07a017cfa00c9890011628eab2503bee5872f27144936a52eaab449be5eaf032"}, + {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c207fff63adcdf5a485969131dc70e4b194327666b7e8a87a97fbc4fd80a53b2"}, + {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:373ba9d1d061c76462d74e7de1c0c8e267e9791ee8cfefcf6b0b2495762c370c"}, + {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfba7c6d5d7c9099ba21f84662b037a0ffd4a5e6b26ac07d19e423e6fdf965a9"}, + {file = "multidict-6.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19d9bad105dfb34eb539c97b132057a4e709919ec4dd883ece5838bcbf262b80"}, + {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:de989b195c3d636ba000ee4281cd03bb1234635b124bf4cd89eeee9ca8fcb09d"}, + {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7c40b7bbece294ae3a87c1bc2abff0ff9beef41d14188cda94ada7bcea99b0fb"}, + {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:d16cce709ebfadc91278a1c005e3c17dd5f71f5098bfae1035149785ea6e9c68"}, + {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:a2c34a93e1d2aa35fbf1485e5010337c72c6791407d03aa5f4eed920343dd360"}, + {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:feba80698173761cddd814fa22e88b0661e98cb810f9f986c54aa34d281e4937"}, + {file = "multidict-6.0.2-cp39-cp39-win32.whl", hash = "sha256:23b616fdc3c74c9fe01d76ce0d1ce872d2d396d8fa8e4899398ad64fb5aa214a"}, + {file = "multidict-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:4bae31803d708f6f15fd98be6a6ac0b6958fcf68fda3c77a048a4f9073704aae"}, + {file = "multidict-6.0.2.tar.gz", hash = "sha256:5ff3bd75f38e4c43f1f470f2df7a4d430b821c4ce22be384e1459cb57d6bb013"}, ] mypy = [ - {file = "mypy-0.961-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:697540876638ce349b01b6786bc6094ccdaba88af446a9abb967293ce6eaa2b0"}, - {file = "mypy-0.961-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b117650592e1782819829605a193360a08aa99f1fc23d1d71e1a75a142dc7e15"}, - {file = "mypy-0.961-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bdd5ca340beffb8c44cb9dc26697628d1b88c6bddf5c2f6eb308c46f269bb6f3"}, - {file = "mypy-0.961-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3e09f1f983a71d0672bbc97ae33ee3709d10c779beb613febc36805a6e28bb4e"}, - {file = "mypy-0.961-cp310-cp310-win_amd64.whl", hash = "sha256:e999229b9f3198c0c880d5e269f9f8129c8862451ce53a011326cad38b9ccd24"}, - {file = "mypy-0.961-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b24be97351084b11582fef18d79004b3e4db572219deee0212078f7cf6352723"}, - {file = "mypy-0.961-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f4a21d01fc0ba4e31d82f0fff195682e29f9401a8bdb7173891070eb260aeb3b"}, - {file = "mypy-0.961-cp36-cp36m-win_amd64.whl", hash = "sha256:439c726a3b3da7ca84a0199a8ab444cd8896d95012c4a6c4a0d808e3147abf5d"}, - {file = "mypy-0.961-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5a0b53747f713f490affdceef835d8f0cb7285187a6a44c33821b6d1f46ed813"}, - {file = "mypy-0.961-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0e9f70df36405c25cc530a86eeda1e0867863d9471fe76d1273c783df3d35c2e"}, - {file = "mypy-0.961-cp37-cp37m-win_amd64.whl", hash = "sha256:b88f784e9e35dcaa075519096dc947a388319cb86811b6af621e3523980f1c8a"}, - {file = "mypy-0.961-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:d5aaf1edaa7692490f72bdb9fbd941fbf2e201713523bdb3f4038be0af8846c6"}, - {file = "mypy-0.961-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9f5f5a74085d9a81a1f9c78081d60a0040c3efb3f28e5c9912b900adf59a16e6"}, - {file = "mypy-0.961-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f4b794db44168a4fc886e3450201365c9526a522c46ba089b55e1f11c163750d"}, - {file = "mypy-0.961-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:64759a273d590040a592e0f4186539858c948302c653c2eac840c7a3cd29e51b"}, - {file = "mypy-0.961-cp38-cp38-win_amd64.whl", hash = "sha256:63e85a03770ebf403291ec50097954cc5caf2a9205c888ce3a61bd3f82e17569"}, - {file = "mypy-0.961-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5f1332964963d4832a94bebc10f13d3279be3ce8f6c64da563d6ee6e2eeda932"}, - {file = "mypy-0.961-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:006be38474216b833eca29ff6b73e143386f352e10e9c2fbe76aa8549e5554f5"}, - {file = "mypy-0.961-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9940e6916ed9371809b35b2154baf1f684acba935cd09928952310fbddaba648"}, - {file = "mypy-0.961-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a5ea0875a049de1b63b972456542f04643daf320d27dc592d7c3d9cd5d9bf950"}, - {file = "mypy-0.961-cp39-cp39-win_amd64.whl", hash = "sha256:1ece702f29270ec6af25db8cf6185c04c02311c6bb21a69f423d40e527b75c56"}, - {file = "mypy-0.961-py3-none-any.whl", hash = "sha256:03c6cc893e7563e7b2949b969e63f02c000b32502a1b4d1314cabe391aa87d66"}, - {file = "mypy-0.961.tar.gz", hash = "sha256:f730d56cb924d371c26b8eaddeea3cc07d78ff51c521c6d04899ac6904b75492"}, + {file = "mypy-0.991-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7d17e0a9707d0772f4a7b878f04b4fd11f6f5bcb9b3813975a9b13c9332153ab"}, + {file = "mypy-0.991-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0714258640194d75677e86c786e80ccf294972cc76885d3ebbb560f11db0003d"}, + {file = "mypy-0.991-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0c8f3be99e8a8bd403caa8c03be619544bc2c77a7093685dcf308c6b109426c6"}, + {file = "mypy-0.991-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc9ec663ed6c8f15f4ae9d3c04c989b744436c16d26580eaa760ae9dd5d662eb"}, + {file = "mypy-0.991-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4307270436fd7694b41f913eb09210faff27ea4979ecbcd849e57d2da2f65305"}, + {file = "mypy-0.991-cp310-cp310-win_amd64.whl", hash = "sha256:901c2c269c616e6cb0998b33d4adbb4a6af0ac4ce5cd078afd7bc95830e62c1c"}, + {file = "mypy-0.991-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d13674f3fb73805ba0c45eb6c0c3053d218aa1f7abead6e446d474529aafc372"}, + {file = "mypy-0.991-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1c8cd4fb70e8584ca1ed5805cbc7c017a3d1a29fb450621089ffed3e99d1857f"}, + {file = "mypy-0.991-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:209ee89fbb0deed518605edddd234af80506aec932ad28d73c08f1400ef80a33"}, + {file = "mypy-0.991-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37bd02ebf9d10e05b00d71302d2c2e6ca333e6c2a8584a98c00e038db8121f05"}, + {file = "mypy-0.991-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:26efb2fcc6b67e4d5a55561f39176821d2adf88f2745ddc72751b7890f3194ad"}, + {file = "mypy-0.991-cp311-cp311-win_amd64.whl", hash = "sha256:3a700330b567114b673cf8ee7388e949f843b356a73b5ab22dd7cff4742a5297"}, + {file = "mypy-0.991-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:1f7d1a520373e2272b10796c3ff721ea1a0712288cafaa95931e66aa15798813"}, + {file = "mypy-0.991-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:641411733b127c3e0dab94c45af15fea99e4468f99ac88b39efb1ad677da5711"}, + {file = "mypy-0.991-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:3d80e36b7d7a9259b740be6d8d906221789b0d836201af4234093cae89ced0cd"}, + {file = "mypy-0.991-cp37-cp37m-win_amd64.whl", hash = "sha256:e62ebaad93be3ad1a828a11e90f0e76f15449371ffeecca4a0a0b9adc99abcef"}, + {file = "mypy-0.991-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b86ce2c1866a748c0f6faca5232059f881cda6dda2a893b9a8373353cfe3715a"}, + {file = "mypy-0.991-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ac6e503823143464538efda0e8e356d871557ef60ccd38f8824a4257acc18d93"}, + {file = "mypy-0.991-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0cca5adf694af539aeaa6ac633a7afe9bbd760df9d31be55ab780b77ab5ae8bf"}, + {file = "mypy-0.991-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a12c56bf73cdab116df96e4ff39610b92a348cc99a1307e1da3c3768bbb5b135"}, + {file = "mypy-0.991-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:652b651d42f155033a1967739788c436491b577b6a44e4c39fb340d0ee7f0d70"}, + {file = "mypy-0.991-cp38-cp38-win_amd64.whl", hash = "sha256:4175593dc25d9da12f7de8de873a33f9b2b8bdb4e827a7cae952e5b1a342e243"}, + {file = "mypy-0.991-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:98e781cd35c0acf33eb0295e8b9c55cdbef64fcb35f6d3aa2186f289bed6e80d"}, + {file = "mypy-0.991-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6d7464bac72a85cb3491c7e92b5b62f3dcccb8af26826257760a552a5e244aa5"}, + {file = "mypy-0.991-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c9166b3f81a10cdf9b49f2d594b21b31adadb3d5e9db9b834866c3258b695be3"}, + {file = "mypy-0.991-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8472f736a5bfb159a5e36740847808f6f5b659960115ff29c7cecec1741c648"}, + {file = "mypy-0.991-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5e80e758243b97b618cdf22004beb09e8a2de1af481382e4d84bc52152d1c476"}, + {file = "mypy-0.991-cp39-cp39-win_amd64.whl", hash = "sha256:74e259b5c19f70d35fcc1ad3d56499065c601dfe94ff67ae48b85596b9ec1461"}, + {file = "mypy-0.991-py3-none-any.whl", hash = "sha256:de32edc9b0a7e67c2775e574cb061a537660e51210fbf6006b0b36ea695ae9bb"}, + {file = "mypy-0.991.tar.gz", hash = "sha256:3c0165ba8f354a6d9881809ef29f1a9318a236a6d81c690094c5df32107bde06"}, ] mypy-extensions = [ {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, ] -nest-asyncio = [ - {file = "nest_asyncio-1.5.5-py3-none-any.whl", hash = "sha256:b98e3ec1b246135e4642eceffa5a6c23a3ab12c82ff816a92c612d68205813b2"}, - {file = "nest_asyncio-1.5.5.tar.gz", hash = "sha256:e442291cd942698be619823a17a86a5759eabe1f8613084790de189fe9e16d65"}, +nanoid = [ + {file = "nanoid-2.0.0-py3-none-any.whl", hash = "sha256:90aefa650e328cffb0893bbd4c236cfd44c48bc1f2d0b525ecc53c3187b653bb"}, + {file = "nanoid-2.0.0.tar.gz", hash = "sha256:5a80cad5e9c6e9ae3a41fa2fb34ae189f7cb420b2a5d8f82bd9d23466e4efa68"}, ] packaging = [ {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, ] -parso = [ - {file = "parso-0.8.3-py2.py3-none-any.whl", hash = "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75"}, - {file = "parso-0.8.3.tar.gz", hash = "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0"}, -] -pendulum = [ - {file = "pendulum-2.1.2-cp27-cp27m-macosx_10_15_x86_64.whl", hash = "sha256:b6c352f4bd32dff1ea7066bd31ad0f71f8d8100b9ff709fb343f3b86cee43efe"}, - {file = "pendulum-2.1.2-cp27-cp27m-win_amd64.whl", hash = "sha256:318f72f62e8e23cd6660dbafe1e346950281a9aed144b5c596b2ddabc1d19739"}, - {file = "pendulum-2.1.2-cp35-cp35m-macosx_10_15_x86_64.whl", hash = "sha256:0731f0c661a3cb779d398803655494893c9f581f6488048b3fb629c2342b5394"}, - {file = "pendulum-2.1.2-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:3481fad1dc3f6f6738bd575a951d3c15d4b4ce7c82dce37cf8ac1483fde6e8b0"}, - {file = "pendulum-2.1.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9702069c694306297ed362ce7e3c1ef8404ac8ede39f9b28b7c1a7ad8c3959e3"}, - {file = "pendulum-2.1.2-cp35-cp35m-win_amd64.whl", hash = "sha256:fb53ffa0085002ddd43b6ca61a7b34f2d4d7c3ed66f931fe599e1a531b42af9b"}, - {file = "pendulum-2.1.2-cp36-cp36m-macosx_10_15_x86_64.whl", hash = "sha256:c501749fdd3d6f9e726086bf0cd4437281ed47e7bca132ddb522f86a1645d360"}, - {file = "pendulum-2.1.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:c807a578a532eeb226150d5006f156632df2cc8c5693d778324b43ff8c515dd0"}, - {file = "pendulum-2.1.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:2d1619a721df661e506eff8db8614016f0720ac171fe80dda1333ee44e684087"}, - {file = "pendulum-2.1.2-cp36-cp36m-win_amd64.whl", hash = "sha256:f888f2d2909a414680a29ae74d0592758f2b9fcdee3549887779cd4055e975db"}, - {file = "pendulum-2.1.2-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:e95d329384717c7bf627bf27e204bc3b15c8238fa8d9d9781d93712776c14002"}, - {file = "pendulum-2.1.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:4c9c689747f39d0d02a9f94fcee737b34a5773803a64a5fdb046ee9cac7442c5"}, - {file = "pendulum-2.1.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:1245cd0075a3c6d889f581f6325dd8404aca5884dea7223a5566c38aab94642b"}, - {file = "pendulum-2.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:db0a40d8bcd27b4fb46676e8eb3c732c67a5a5e6bfab8927028224fbced0b40b"}, - {file = "pendulum-2.1.2-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:f5e236e7730cab1644e1b87aca3d2ff3e375a608542e90fe25685dae46310116"}, - {file = "pendulum-2.1.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:de42ea3e2943171a9e95141f2eecf972480636e8e484ccffaf1e833929e9e052"}, - {file = "pendulum-2.1.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7c5ec650cb4bec4c63a89a0242cc8c3cebcec92fcfe937c417ba18277d8560be"}, - {file = "pendulum-2.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:33fb61601083f3eb1d15edeb45274f73c63b3c44a8524703dc143f4212bf3269"}, - {file = "pendulum-2.1.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:29c40a6f2942376185728c9a0347d7c0f07905638c83007e1d262781f1e6953a"}, - {file = "pendulum-2.1.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:94b1fc947bfe38579b28e1cccb36f7e28a15e841f30384b5ad6c5e31055c85d7"}, - {file = "pendulum-2.1.2.tar.gz", hash = "sha256:b06a0ca1bfe41c990bbf0c029f0b6501a7f2ec4e38bfec730712015e8860f207"}, -] -pexpect = [ - {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"}, - {file = "pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"}, -] -pickleshare = [ - {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"}, - {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, -] pillow = [ - {file = "Pillow-9.2.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:a9c9bc489f8ab30906d7a85afac4b4944a572a7432e00698a7239f44a44e6efb"}, - {file = "Pillow-9.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:510cef4a3f401c246cfd8227b300828715dd055463cdca6176c2e4036df8bd4f"}, - {file = "Pillow-9.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7888310f6214f19ab2b6df90f3f06afa3df7ef7355fc025e78a3044737fab1f5"}, - {file = "Pillow-9.2.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:831e648102c82f152e14c1a0938689dbb22480c548c8d4b8b248b3e50967b88c"}, - {file = "Pillow-9.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1cc1d2451e8a3b4bfdb9caf745b58e6c7a77d2e469159b0d527a4554d73694d1"}, - {file = "Pillow-9.2.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:136659638f61a251e8ed3b331fc6ccd124590eeff539de57c5f80ef3a9594e58"}, - {file = "Pillow-9.2.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:6e8c66f70fb539301e064f6478d7453e820d8a2c631da948a23384865cd95544"}, - {file = "Pillow-9.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:37ff6b522a26d0538b753f0b4e8e164fdada12db6c6f00f62145d732d8a3152e"}, - {file = "Pillow-9.2.0-cp310-cp310-win32.whl", hash = "sha256:c79698d4cd9318d9481d89a77e2d3fcaeff5486be641e60a4b49f3d2ecca4e28"}, - {file = "Pillow-9.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:254164c57bab4b459f14c64e93df11eff5ded575192c294a0c49270f22c5d93d"}, - {file = "Pillow-9.2.0-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:408673ed75594933714482501fe97e055a42996087eeca7e5d06e33218d05aa8"}, - {file = "Pillow-9.2.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:727dd1389bc5cb9827cbd1f9d40d2c2a1a0c9b32dd2261db522d22a604a6eec9"}, - {file = "Pillow-9.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50dff9cc21826d2977ef2d2a205504034e3a4563ca6f5db739b0d1026658e004"}, - {file = "Pillow-9.2.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb6259196a589123d755380b65127ddc60f4c64b21fc3bb46ce3a6ea663659b0"}, - {file = "Pillow-9.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b0554af24df2bf96618dac71ddada02420f946be943b181108cac55a7a2dcd4"}, - {file = "Pillow-9.2.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:15928f824870535c85dbf949c09d6ae7d3d6ac2d6efec80f3227f73eefba741c"}, - {file = "Pillow-9.2.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:bdd0de2d64688ecae88dd8935012c4a72681e5df632af903a1dca8c5e7aa871a"}, - {file = "Pillow-9.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d5b87da55a08acb586bad5c3aa3b86505f559b84f39035b233d5bf844b0834b1"}, - {file = "Pillow-9.2.0-cp311-cp311-win32.whl", hash = "sha256:b6d5e92df2b77665e07ddb2e4dbd6d644b78e4c0d2e9272a852627cdba0d75cf"}, - {file = "Pillow-9.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:6bf088c1ce160f50ea40764f825ec9b72ed9da25346216b91361eef8ad1b8f8c"}, - {file = "Pillow-9.2.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:2c58b24e3a63efd22554c676d81b0e57f80e0a7d3a5874a7e14ce90ec40d3069"}, - {file = "Pillow-9.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eef7592281f7c174d3d6cbfbb7ee5984a671fcd77e3fc78e973d492e9bf0eb3f"}, - {file = "Pillow-9.2.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dcd7b9c7139dc8258d164b55696ecd16c04607f1cc33ba7af86613881ffe4ac8"}, - {file = "Pillow-9.2.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a138441e95562b3c078746a22f8fca8ff1c22c014f856278bdbdd89ca36cff1b"}, - {file = "Pillow-9.2.0-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:93689632949aff41199090eff5474f3990b6823404e45d66a5d44304e9cdc467"}, - {file = "Pillow-9.2.0-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:f3fac744f9b540148fa7715a435d2283b71f68bfb6d4aae24482a890aed18b59"}, - {file = "Pillow-9.2.0-cp37-cp37m-win32.whl", hash = "sha256:fa768eff5f9f958270b081bb33581b4b569faabf8774726b283edb06617101dc"}, - {file = "Pillow-9.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:69bd1a15d7ba3694631e00df8de65a8cb031911ca11f44929c97fe05eb9b6c1d"}, - {file = "Pillow-9.2.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:030e3460861488e249731c3e7ab59b07c7853838ff3b8e16aac9561bb345da14"}, - {file = "Pillow-9.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:74a04183e6e64930b667d321524e3c5361094bb4af9083db5c301db64cd341f3"}, - {file = "Pillow-9.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d33a11f601213dcd5718109c09a52c2a1c893e7461f0be2d6febc2879ec2402"}, - {file = "Pillow-9.2.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fd6f5e3c0e4697fa7eb45b6e93996299f3feee73a3175fa451f49a74d092b9f"}, - {file = "Pillow-9.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a647c0d4478b995c5e54615a2e5360ccedd2f85e70ab57fbe817ca613d5e63b8"}, - {file = "Pillow-9.2.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:4134d3f1ba5f15027ff5c04296f13328fecd46921424084516bdb1b2548e66ff"}, - {file = "Pillow-9.2.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:bc431b065722a5ad1dfb4df354fb9333b7a582a5ee39a90e6ffff688d72f27a1"}, - {file = "Pillow-9.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:1536ad017a9f789430fb6b8be8bf99d2f214c76502becc196c6f2d9a75b01b76"}, - {file = "Pillow-9.2.0-cp38-cp38-win32.whl", hash = "sha256:2ad0d4df0f5ef2247e27fc790d5c9b5a0af8ade9ba340db4a73bb1a4a3e5fb4f"}, - {file = "Pillow-9.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:ec52c351b35ca269cb1f8069d610fc45c5bd38c3e91f9ab4cbbf0aebc136d9c8"}, - {file = "Pillow-9.2.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:0ed2c4ef2451de908c90436d6e8092e13a43992f1860275b4d8082667fbb2ffc"}, - {file = "Pillow-9.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ad2f835e0ad81d1689f1b7e3fbac7b01bb8777d5a985c8962bedee0cc6d43da"}, - {file = "Pillow-9.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea98f633d45f7e815db648fd7ff0f19e328302ac36427343e4432c84432e7ff4"}, - {file = "Pillow-9.2.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7761afe0126d046974a01e030ae7529ed0ca6a196de3ec6937c11df0df1bc91c"}, - {file = "Pillow-9.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a54614049a18a2d6fe156e68e188da02a046a4a93cf24f373bffd977e943421"}, - {file = "Pillow-9.2.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:5aed7dde98403cd91d86a1115c78d8145c83078e864c1de1064f52e6feb61b20"}, - {file = "Pillow-9.2.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:13b725463f32df1bfeacbf3dd197fb358ae8ebcd8c5548faa75126ea425ccb60"}, - {file = "Pillow-9.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:808add66ea764ed97d44dda1ac4f2cfec4c1867d9efb16a33d158be79f32b8a4"}, - {file = "Pillow-9.2.0-cp39-cp39-win32.whl", hash = "sha256:337a74fd2f291c607d220c793a8135273c4c2ab001b03e601c36766005f36885"}, - {file = "Pillow-9.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:fac2d65901fb0fdf20363fbd345c01958a742f2dc62a8dd4495af66e3ff502a4"}, - {file = "Pillow-9.2.0-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:ad2277b185ebce47a63f4dc6302e30f05762b688f8dc3de55dbae4651872cdf3"}, - {file = "Pillow-9.2.0-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c7b502bc34f6e32ba022b4a209638f9e097d7a9098104ae420eb8186217ebbb"}, - {file = "Pillow-9.2.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d1f14f5f691f55e1b47f824ca4fdcb4b19b4323fe43cc7bb105988cad7496be"}, - {file = "Pillow-9.2.0-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:dfe4c1fedfde4e2fbc009d5ad420647f7730d719786388b7de0999bf32c0d9fd"}, - {file = "Pillow-9.2.0-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:f07f1f00e22b231dd3d9b9208692042e29792d6bd4f6639415d2f23158a80013"}, - {file = "Pillow-9.2.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1802f34298f5ba11d55e5bb09c31997dc0c6aed919658dfdf0198a2fe75d5490"}, - {file = "Pillow-9.2.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17d4cafe22f050b46d983b71c707162d63d796a1235cdf8b9d7a112e97b15bac"}, - {file = "Pillow-9.2.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:96b5e6874431df16aee0c1ba237574cb6dff1dcb173798faa6a9d8b399a05d0e"}, - {file = "Pillow-9.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:0030fdbd926fb85844b8b92e2f9449ba89607231d3dd597a21ae72dc7fe26927"}, - {file = "Pillow-9.2.0.tar.gz", hash = "sha256:75e636fd3e0fb872693f23ccb8a5ff2cd578801251f3a4f6854c6a5d437d3c04"}, + {file = "Pillow-9.3.0-1-cp37-cp37m-win32.whl", hash = "sha256:e6ea6b856a74d560d9326c0f5895ef8050126acfdc7ca08ad703eb0081e82b74"}, + {file = "Pillow-9.3.0-1-cp37-cp37m-win_amd64.whl", hash = "sha256:32a44128c4bdca7f31de5be641187367fe2a450ad83b833ef78910397db491aa"}, + {file = "Pillow-9.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:0b7257127d646ff8676ec8a15520013a698d1fdc48bc2a79ba4e53df792526f2"}, + {file = "Pillow-9.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b90f7616ea170e92820775ed47e136208e04c967271c9ef615b6fbd08d9af0e3"}, + {file = "Pillow-9.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68943d632f1f9e3dce98908e873b3a090f6cba1cbb1b892a9e8d97c938871fbe"}, + {file = "Pillow-9.3.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be55f8457cd1eac957af0c3f5ece7bc3f033f89b114ef30f710882717670b2a8"}, + {file = "Pillow-9.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d77adcd56a42d00cc1be30843d3426aa4e660cab4a61021dc84467123f7a00c"}, + {file = "Pillow-9.3.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:829f97c8e258593b9daa80638aee3789b7df9da5cf1336035016d76f03b8860c"}, + {file = "Pillow-9.3.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:801ec82e4188e935c7f5e22e006d01611d6b41661bba9fe45b60e7ac1a8f84de"}, + {file = "Pillow-9.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:871b72c3643e516db4ecf20efe735deb27fe30ca17800e661d769faab45a18d7"}, + {file = "Pillow-9.3.0-cp310-cp310-win32.whl", hash = "sha256:655a83b0058ba47c7c52e4e2df5ecf484c1b0b0349805896dd350cbc416bdd91"}, + {file = "Pillow-9.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:9f47eabcd2ded7698106b05c2c338672d16a6f2a485e74481f524e2a23c2794b"}, + {file = "Pillow-9.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:57751894f6618fd4308ed8e0c36c333e2f5469744c34729a27532b3db106ee20"}, + {file = "Pillow-9.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7db8b751ad307d7cf238f02101e8e36a128a6cb199326e867d1398067381bff4"}, + {file = "Pillow-9.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3033fbe1feb1b59394615a1cafaee85e49d01b51d54de0cbf6aa8e64182518a1"}, + {file = "Pillow-9.3.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22b012ea2d065fd163ca096f4e37e47cd8b59cf4b0fd47bfca6abb93df70b34c"}, + {file = "Pillow-9.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9a65733d103311331875c1dca05cb4606997fd33d6acfed695b1232ba1df193"}, + {file = "Pillow-9.3.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:502526a2cbfa431d9fc2a079bdd9061a2397b842bb6bc4239bb176da00993812"}, + {file = "Pillow-9.3.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:90fb88843d3902fe7c9586d439d1e8c05258f41da473952aa8b328d8b907498c"}, + {file = "Pillow-9.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:89dca0ce00a2b49024df6325925555d406b14aa3efc2f752dbb5940c52c56b11"}, + {file = "Pillow-9.3.0-cp311-cp311-win32.whl", hash = "sha256:3168434d303babf495d4ba58fc22d6604f6e2afb97adc6a423e917dab828939c"}, + {file = "Pillow-9.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:18498994b29e1cf86d505edcb7edbe814d133d2232d256db8c7a8ceb34d18cef"}, + {file = "Pillow-9.3.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:772a91fc0e03eaf922c63badeca75e91baa80fe2f5f87bdaed4280662aad25c9"}, + {file = "Pillow-9.3.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa4107d1b306cdf8953edde0534562607fe8811b6c4d9a486298ad31de733b2"}, + {file = "Pillow-9.3.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b4012d06c846dc2b80651b120e2cdd787b013deb39c09f407727ba90015c684f"}, + {file = "Pillow-9.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77ec3e7be99629898c9a6d24a09de089fa5356ee408cdffffe62d67bb75fdd72"}, + {file = "Pillow-9.3.0-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:6c738585d7a9961d8c2821a1eb3dcb978d14e238be3d70f0a706f7fa9316946b"}, + {file = "Pillow-9.3.0-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:828989c45c245518065a110434246c44a56a8b2b2f6347d1409c787e6e4651ee"}, + {file = "Pillow-9.3.0-cp37-cp37m-win32.whl", hash = "sha256:82409ffe29d70fd733ff3c1025a602abb3e67405d41b9403b00b01debc4c9a29"}, + {file = "Pillow-9.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:41e0051336807468be450d52b8edd12ac60bebaa97fe10c8b660f116e50b30e4"}, + {file = "Pillow-9.3.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:b03ae6f1a1878233ac620c98f3459f79fd77c7e3c2b20d460284e1fb370557d4"}, + {file = "Pillow-9.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4390e9ce199fc1951fcfa65795f239a8a4944117b5935a9317fb320e7767b40f"}, + {file = "Pillow-9.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40e1ce476a7804b0fb74bcfa80b0a2206ea6a882938eaba917f7a0f004b42502"}, + {file = "Pillow-9.3.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a0a06a052c5f37b4ed81c613a455a81f9a3a69429b4fd7bb913c3fa98abefc20"}, + {file = "Pillow-9.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03150abd92771742d4a8cd6f2fa6246d847dcd2e332a18d0c15cc75bf6703040"}, + {file = "Pillow-9.3.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:15c42fb9dea42465dfd902fb0ecf584b8848ceb28b41ee2b58f866411be33f07"}, + {file = "Pillow-9.3.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:51e0e543a33ed92db9f5ef69a0356e0b1a7a6b6a71b80df99f1d181ae5875636"}, + {file = "Pillow-9.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:3dd6caf940756101205dffc5367babf288a30043d35f80936f9bfb37f8355b32"}, + {file = "Pillow-9.3.0-cp38-cp38-win32.whl", hash = "sha256:f1ff2ee69f10f13a9596480335f406dd1f70c3650349e2be67ca3139280cade0"}, + {file = "Pillow-9.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:276a5ca930c913f714e372b2591a22c4bd3b81a418c0f6635ba832daec1cbcfc"}, + {file = "Pillow-9.3.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:73bd195e43f3fadecfc50c682f5055ec32ee2c933243cafbfdec69ab1aa87cad"}, + {file = "Pillow-9.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1c7c8ae3864846fc95f4611c78129301e203aaa2af813b703c55d10cc1628535"}, + {file = "Pillow-9.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e0918e03aa0c72ea56edbb00d4d664294815aa11291a11504a377ea018330d3"}, + {file = "Pillow-9.3.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0915e734b33a474d76c28e07292f196cdf2a590a0d25bcc06e64e545f2d146c"}, + {file = "Pillow-9.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af0372acb5d3598f36ec0914deed2a63f6bcdb7b606da04dc19a88d31bf0c05b"}, + {file = "Pillow-9.3.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:ad58d27a5b0262c0c19b47d54c5802db9b34d38bbf886665b626aff83c74bacd"}, + {file = "Pillow-9.3.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:97aabc5c50312afa5e0a2b07c17d4ac5e865b250986f8afe2b02d772567a380c"}, + {file = "Pillow-9.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9aaa107275d8527e9d6e7670b64aabaaa36e5b6bd71a1015ddd21da0d4e06448"}, + {file = "Pillow-9.3.0-cp39-cp39-win32.whl", hash = "sha256:bac18ab8d2d1e6b4ce25e3424f709aceef668347db8637c2296bcf41acb7cf48"}, + {file = "Pillow-9.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:b472b5ea442148d1c3e2209f20f1e0bb0eb556538690fa70b5e1f79fa0ba8dc2"}, + {file = "Pillow-9.3.0-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:ab388aaa3f6ce52ac1cb8e122c4bd46657c15905904b3120a6248b5b8b0bc228"}, + {file = "Pillow-9.3.0-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbb8e7f2abee51cef77673be97760abff1674ed32847ce04b4af90f610144c7b"}, + {file = "Pillow-9.3.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bca31dd6014cb8b0b2db1e46081b0ca7d936f856da3b39744aef499db5d84d02"}, + {file = "Pillow-9.3.0-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c7025dce65566eb6e89f56c9509d4f628fddcedb131d9465cacd3d8bac337e7e"}, + {file = "Pillow-9.3.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:ebf2029c1f464c59b8bdbe5143c79fa2045a581ac53679733d3a91d400ff9efb"}, + {file = "Pillow-9.3.0-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:b59430236b8e58840a0dfb4099a0e8717ffb779c952426a69ae435ca1f57210c"}, + {file = "Pillow-9.3.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12ce4932caf2ddf3e41d17fc9c02d67126935a44b86df6a206cf0d7161548627"}, + {file = "Pillow-9.3.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae5331c23ce118c53b172fa64a4c037eb83c9165aba3a7ba9ddd3ec9fa64a699"}, + {file = "Pillow-9.3.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:0b07fffc13f474264c336298d1b4ce01d9c5a011415b79d4ee5527bb69ae6f65"}, + {file = "Pillow-9.3.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:073adb2ae23431d3b9bcbcff3fe698b62ed47211d0716b067385538a1b0f28b8"}, + {file = "Pillow-9.3.0.tar.gz", hash = "sha256:c935a22a557a560108d780f9a0fc426dd7459940dc54faa49d83249c8d3e760f"}, ] pluggy = [ {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, ] -prompt-toolkit = [ - {file = "prompt_toolkit-3.0.30-py3-none-any.whl", hash = "sha256:d8916d3f62a7b67ab353a952ce4ced6a1d2587dfe9ef8ebc30dd7c386751f289"}, - {file = "prompt_toolkit-3.0.30.tar.gz", hash = "sha256:859b283c50bde45f5f97829f77a4674d1c1fcd88539364f1b28a37805cfd89c0"}, -] -psutil = [ - {file = "psutil-5.9.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:799759d809c31aab5fe4579e50addf84565e71c1dc9f1c31258f159ff70d3f87"}, - {file = "psutil-5.9.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:9272167b5f5fbfe16945be3db475b3ce8d792386907e673a209da686176552af"}, - {file = "psutil-5.9.1-cp27-cp27m-win32.whl", hash = "sha256:0904727e0b0a038830b019551cf3204dd48ef5c6868adc776e06e93d615fc5fc"}, - {file = "psutil-5.9.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e7e10454cb1ab62cc6ce776e1c135a64045a11ec4c6d254d3f7689c16eb3efd2"}, - {file = "psutil-5.9.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:56960b9e8edcca1456f8c86a196f0c3d8e3e361320071c93378d41445ffd28b0"}, - {file = "psutil-5.9.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:44d1826150d49ffd62035785a9e2c56afcea66e55b43b8b630d7706276e87f22"}, - {file = "psutil-5.9.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c7be9d7f5b0d206f0bbc3794b8e16fb7dbc53ec9e40bbe8787c6f2d38efcf6c9"}, - {file = "psutil-5.9.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd9246e4cdd5b554a2ddd97c157e292ac11ef3e7af25ac56b08b455c829dca8"}, - {file = "psutil-5.9.1-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:29a442e25fab1f4d05e2655bb1b8ab6887981838d22effa2396d584b740194de"}, - {file = "psutil-5.9.1-cp310-cp310-win32.whl", hash = "sha256:20b27771b077dcaa0de1de3ad52d22538fe101f9946d6dc7869e6f694f079329"}, - {file = "psutil-5.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:58678bbadae12e0db55186dc58f2888839228ac9f41cc7848853539b70490021"}, - {file = "psutil-5.9.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:3a76ad658641172d9c6e593de6fe248ddde825b5866464c3b2ee26c35da9d237"}, - {file = "psutil-5.9.1-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a6a11e48cb93a5fa606306493f439b4aa7c56cb03fc9ace7f6bfa21aaf07c453"}, - {file = "psutil-5.9.1-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:068935df39055bf27a29824b95c801c7a5130f118b806eee663cad28dca97685"}, - {file = "psutil-5.9.1-cp36-cp36m-win32.whl", hash = "sha256:0f15a19a05f39a09327345bc279c1ba4a8cfb0172cc0d3c7f7d16c813b2e7d36"}, - {file = "psutil-5.9.1-cp36-cp36m-win_amd64.whl", hash = "sha256:db417f0865f90bdc07fa30e1aadc69b6f4cad7f86324b02aa842034efe8d8c4d"}, - {file = "psutil-5.9.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:91c7ff2a40c373d0cc9121d54bc5f31c4fa09c346528e6a08d1845bce5771ffc"}, - {file = "psutil-5.9.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fea896b54f3a4ae6f790ac1d017101252c93f6fe075d0e7571543510f11d2676"}, - {file = "psutil-5.9.1-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3054e923204b8e9c23a55b23b6df73a8089ae1d075cb0bf711d3e9da1724ded4"}, - {file = "psutil-5.9.1-cp37-cp37m-win32.whl", hash = "sha256:d2d006286fbcb60f0b391741f520862e9b69f4019b4d738a2a45728c7e952f1b"}, - {file = "psutil-5.9.1-cp37-cp37m-win_amd64.whl", hash = "sha256:b14ee12da9338f5e5b3a3ef7ca58b3cba30f5b66f7662159762932e6d0b8f680"}, - {file = "psutil-5.9.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:19f36c16012ba9cfc742604df189f2f28d2720e23ff7d1e81602dbe066be9fd1"}, - {file = "psutil-5.9.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:944c4b4b82dc4a1b805329c980f270f170fdc9945464223f2ec8e57563139cf4"}, - {file = "psutil-5.9.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b6750a73a9c4a4e689490ccb862d53c7b976a2a35c4e1846d049dcc3f17d83b"}, - {file = "psutil-5.9.1-cp38-cp38-win32.whl", hash = "sha256:a8746bfe4e8f659528c5c7e9af5090c5a7d252f32b2e859c584ef7d8efb1e689"}, - {file = "psutil-5.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:79c9108d9aa7fa6fba6e668b61b82facc067a6b81517cab34d07a84aa89f3df0"}, - {file = "psutil-5.9.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:28976df6c64ddd6320d281128817f32c29b539a52bdae5e192537bc338a9ec81"}, - {file = "psutil-5.9.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b88f75005586131276634027f4219d06e0561292be8bd6bc7f2f00bdabd63c4e"}, - {file = "psutil-5.9.1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:645bd4f7bb5b8633803e0b6746ff1628724668681a434482546887d22c7a9537"}, - {file = "psutil-5.9.1-cp39-cp39-win32.whl", hash = "sha256:32c52611756096ae91f5d1499fe6c53b86f4a9ada147ee42db4991ba1520e574"}, - {file = "psutil-5.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:f65f9a46d984b8cd9b3750c2bdb419b2996895b005aefa6cbaba9a143b1ce2c5"}, - {file = "psutil-5.9.1.tar.gz", hash = "sha256:57f1819b5d9e95cdfb0c881a8a5b7d542ed0b7c522d575706a80bedc848c8954"}, -] -ptyprocess = [ - {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, - {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, -] -pure-eval = [ - {file = "pure_eval-0.2.2-py3-none-any.whl", hash = "sha256:01eaab343580944bc56080ebe0a674b39ec44a945e6d09ba7db3cb8cec289350"}, - {file = "pure_eval-0.2.2.tar.gz", hash = "sha256:2b45320af6dfaa1750f543d714b6d1c520a1688dec6fd24d339063ce0aaa9ac3"}, -] -py = [ - {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, - {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, -] -pycparser = [ - {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, - {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, -] pygments = [ - {file = "Pygments-2.12.0-py3-none-any.whl", hash = "sha256:dc9c10fb40944260f6ed4c688ece0cd2048414940f1cea51b8b226318411c519"}, - {file = "Pygments-2.12.0.tar.gz", hash = "sha256:5eb116118f9612ff1ee89ac96437bb6b49e8f04d8a13b514ba26f620208e26eb"}, + {file = "Pygments-2.13.0-py3-none-any.whl", hash = "sha256:f643f331ab57ba3c9d89212ee4a2dabc6e94f117cf4eefde99a0574720d14c42"}, + {file = "Pygments-2.13.0.tar.gz", hash = "sha256:56a8508ae95f98e2b9bdf93a6be5ae3f7d8af858b43e02c5a2ff083726be40c1"}, ] pyparsing = [ {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, ] pytest = [ - {file = "pytest-7.1.2-py3-none-any.whl", hash = "sha256:13d0e3ccfc2b6e26be000cb6568c832ba67ba32e719443bfe725814d3c42433c"}, - {file = "pytest-7.1.2.tar.gz", hash = "sha256:a06a0425453864a270bc45e71f783330a7428defb4230fb5e6a731fde06ecd45"}, + {file = "pytest-7.2.0-py3-none-any.whl", hash = "sha256:892f933d339f068883b6fd5a459f03d85bfcb355e4981e146d2c7616c21fef71"}, + {file = "pytest-7.2.0.tar.gz", hash = "sha256:c4014eb40e10f11f355ad4e3c2fb2c6c6d1919c73f3b5a433de4708202cade59"}, ] pytest-cov = [ - {file = "pytest-cov-3.0.0.tar.gz", hash = "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470"}, - {file = "pytest_cov-3.0.0-py3-none-any.whl", hash = "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6"}, -] -pytest-forked = [ - {file = "pytest-forked-1.4.0.tar.gz", hash = "sha256:8b67587c8f98cbbadfdd804539ed5455b6ed03802203485dd2f53c1422d7440e"}, - {file = "pytest_forked-1.4.0-py3-none-any.whl", hash = "sha256:bbbb6717efc886b9d64537b41fb1497cfaf3c9601276be8da2cccfea5a3c8ad8"}, + {file = "pytest-cov-4.0.0.tar.gz", hash = "sha256:996b79efde6433cdbd0088872dbc5fb3ed7fe1578b68cdbba634f14bb8dd0470"}, + {file = "pytest_cov-4.0.0-py3-none-any.whl", hash = "sha256:2feb1b751d66a8bd934e5edfa2e961d11309dc37b73b0eabe73b5945fee20f6b"}, ] pytest-mock = [ - {file = "pytest-mock-3.8.2.tar.gz", hash = "sha256:77f03f4554392558700295e05aed0b1096a20d4a60a4f3ddcde58b0c31c8fca2"}, - {file = "pytest_mock-3.8.2-py3-none-any.whl", hash = "sha256:8a9e226d6c0ef09fcf20c94eb3405c388af438a90f3e39687f84166da82d5948"}, + {file = "pytest-mock-3.10.0.tar.gz", hash = "sha256:fbbdb085ef7c252a326fd8cdcac0aa3b1333d8811f131bdcc701002e1be7ed4f"}, + {file = "pytest_mock-3.10.0-py3-none-any.whl", hash = "sha256:f4c973eeae0282963eb293eb173ce91b091a79c1334455acfac9ddee8a1c784b"}, ] pytest-mypy = [ - {file = "pytest-mypy-0.9.1.tar.gz", hash = "sha256:9ffa3bf405c12c5c6be9e92e22bebb6ab2c91b9c32f45b0f0c93af473269ab5c"}, - {file = "pytest_mypy-0.9.1-py3-none-any.whl", hash = "sha256:a2505fcf61f1c0c51f950d4623ea8ca2daf6fb2101a5603554bad2e130202083"}, + {file = "pytest-mypy-0.10.2.tar.gz", hash = "sha256:efc89a9f54976995a39b98fd3672ccf958d03de8463dad0261aecc166a308b96"}, + {file = "pytest_mypy-0.10.2-py3-none-any.whl", hash = "sha256:486a2b2ca1ebd15794b74f14e7b1bf550ecb23c670dc62df5b25ed98d5c47de4"}, ] pytest-watch = [ {file = "pytest-watch-4.2.0.tar.gz", hash = "sha256:06136f03d5b361718b8d0d234042f7b2f203910d8568f63df2f866b547b3d4b9"}, ] pytest-xdist = [ - {file = "pytest-xdist-2.5.0.tar.gz", hash = "sha256:4580deca3ff04ddb2ac53eba39d76cb5dd5edeac050cb6fbc768b0dd712b4edf"}, - {file = "pytest_xdist-2.5.0-py3-none-any.whl", hash = "sha256:6fe5c74fec98906deb8f2d2b616b5c782022744978e7bd4695d39c8f42d0ce65"}, -] -python-dateutil = [ - {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, - {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, -] -pytzdata = [ - {file = "pytzdata-2020.1-py2.py3-none-any.whl", hash = "sha256:e1e14750bcf95016381e4d472bad004eef710f2d6417240904070b3d6654485f"}, - {file = "pytzdata-2020.1.tar.gz", hash = "sha256:3efa13b335a00a8de1d345ae41ec78dd11c9f8807f522d39850f2dd828681540"}, -] -pywin32 = [ - {file = "pywin32-304-cp310-cp310-win32.whl", hash = "sha256:3c7bacf5e24298c86314f03fa20e16558a4e4138fc34615d7de4070c23e65af3"}, - {file = "pywin32-304-cp310-cp310-win_amd64.whl", hash = "sha256:4f32145913a2447736dad62495199a8e280a77a0ca662daa2332acf849f0be48"}, - {file = "pywin32-304-cp310-cp310-win_arm64.whl", hash = "sha256:d3ee45adff48e0551d1aa60d2ec066fec006083b791f5c3527c40cd8aefac71f"}, - {file = "pywin32-304-cp311-cp311-win32.whl", hash = "sha256:30c53d6ce44c12a316a06c153ea74152d3b1342610f1b99d40ba2795e5af0269"}, - {file = "pywin32-304-cp311-cp311-win_amd64.whl", hash = "sha256:7ffa0c0fa4ae4077e8b8aa73800540ef8c24530057768c3ac57c609f99a14fd4"}, - {file = "pywin32-304-cp311-cp311-win_arm64.whl", hash = "sha256:cbbe34dad39bdbaa2889a424d28752f1b4971939b14b1bb48cbf0182a3bcfc43"}, - {file = "pywin32-304-cp36-cp36m-win32.whl", hash = "sha256:be253e7b14bc601718f014d2832e4c18a5b023cbe72db826da63df76b77507a1"}, - {file = "pywin32-304-cp36-cp36m-win_amd64.whl", hash = "sha256:de9827c23321dcf43d2f288f09f3b6d772fee11e809015bdae9e69fe13213988"}, - {file = "pywin32-304-cp37-cp37m-win32.whl", hash = "sha256:f64c0377cf01b61bd5e76c25e1480ca8ab3b73f0c4add50538d332afdf8f69c5"}, - {file = "pywin32-304-cp37-cp37m-win_amd64.whl", hash = "sha256:bb2ea2aa81e96eee6a6b79d87e1d1648d3f8b87f9a64499e0b92b30d141e76df"}, - {file = "pywin32-304-cp38-cp38-win32.whl", hash = "sha256:94037b5259701988954931333aafd39cf897e990852115656b014ce72e052e96"}, - {file = "pywin32-304-cp38-cp38-win_amd64.whl", hash = "sha256:ead865a2e179b30fb717831f73cf4373401fc62fbc3455a0889a7ddac848f83e"}, - {file = "pywin32-304-cp39-cp39-win32.whl", hash = "sha256:25746d841201fd9f96b648a248f731c1dec851c9a08b8e33da8b56148e4c65cc"}, - {file = "pywin32-304-cp39-cp39-win_amd64.whl", hash = "sha256:d24a3382f013b21aa24a5cfbfad5a2cd9926610c0affde3e8ab5b3d7dbcf4ac9"}, -] -pyzmq = [ - {file = "pyzmq-23.2.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:22ac0243a41798e3eb5d5714b28c2f28e3d10792dffbc8a5fca092f975fdeceb"}, - {file = "pyzmq-23.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f685003d836ad0e5d4f08d1e024ee3ac7816eb2f873b2266306eef858f058133"}, - {file = "pyzmq-23.2.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d4651de7316ec8560afe430fb042c0782ed8ac54c0be43a515944d7c78fddac8"}, - {file = "pyzmq-23.2.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:bcc6953e47bcfc9028ddf9ab2a321a3c51d7cc969db65edec092019bb837959f"}, - {file = "pyzmq-23.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e08671dc202a1880fa522f921f35ca5925ba30da8bc96228d74a8f0643ead9c"}, - {file = "pyzmq-23.2.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de727ea906033b30527b4a99498f19aca3f4d1073230a958679a5b726e2784e0"}, - {file = "pyzmq-23.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5aa9da520e4bb8cee8189f2f541701405e7690745094ded7a37b425d60527ea"}, - {file = "pyzmq-23.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:f3ff6abde52e702397949054cb5b06c1c75b5d6542f6a2ce029e46f71ffbbbf2"}, - {file = "pyzmq-23.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e2e2db5c6ef376e97c912733dfc24406f5949474d03e800d5f07b6aca4d870af"}, - {file = "pyzmq-23.2.0-cp310-cp310-win32.whl", hash = "sha256:e669913cb2179507628419ec4f0e453e48ce6f924de5884d396f18c31836089c"}, - {file = "pyzmq-23.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:a3dc339f7bc185d5fd0fd976242a5baf35de404d467e056484def8a4dd95868b"}, - {file = "pyzmq-23.2.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:30c365e60c39c53f8eea042b37ea28304ffa6558fb7241cf278745095a5757da"}, - {file = "pyzmq-23.2.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c2d8b69a2bf239ae3d987537bf3fbc2b044a405394cf4c258fc684971dd48b2"}, - {file = "pyzmq-23.2.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:602835e5672ca9ca1d78e6c148fb28c4f91b748ebc41fbd2f479d8763d58bc9b"}, - {file = "pyzmq-23.2.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:831da96ba3f36cc892f0afbb4fb89b28b61b387261676e55d55a682addbd29f7"}, - {file = "pyzmq-23.2.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:c8dec8a2f3f0bb462e6439df436cd8c7ec37968e90b4209ac621e7fbc0ed3b00"}, - {file = "pyzmq-23.2.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:814e5aaf0c3be9991a59066eafb2d6e117aed6b413e3e7e9be45d4e55f5e2748"}, - {file = "pyzmq-23.2.0-cp36-cp36m-win32.whl", hash = "sha256:8496a2a5efd055c61ac2c6a18116c768a25c644b6747dcfde43e91620ab3453c"}, - {file = "pyzmq-23.2.0-cp36-cp36m-win_amd64.whl", hash = "sha256:60746a7e8558655420a69441c0a1d47ed225ed3ac355920b96a96d0554ef7e6b"}, - {file = "pyzmq-23.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5cb642e94337b0c76c9c8cb9bfb0f8a78654575847d080d3e1504f312d691fc3"}, - {file = "pyzmq-23.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:444f7d615d5f686d0ef508b9edfa8a286e6d89f449a1ba37b60ef69d869220a3"}, - {file = "pyzmq-23.2.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c9638e0057e3f1a8b7c5ce33c7575349d9183a033a19b5676ad55096ae36820b"}, - {file = "pyzmq-23.2.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:004a431dfa0459123e6f4660d7e3c4ac19217d134ca38bacfffb2e78716fe944"}, - {file = "pyzmq-23.2.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:5592fb4316f895922b1cacb91b04a0fa09d6f6f19bbab4442b4d0a0825177b93"}, - {file = "pyzmq-23.2.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c0a5f987d73fd9b46c3d180891f829afda714ab6bab30a1218724d4a0a63afd8"}, - {file = "pyzmq-23.2.0-cp37-cp37m-win32.whl", hash = "sha256:d11628212fd731b8986f1561d9bb3f8c38d9c15b330c3d8a88963519fbcd553b"}, - {file = "pyzmq-23.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:558f5f636e3e65f261b64925e8b190e8689e334911595394572cc7523879006d"}, - {file = "pyzmq-23.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:61b97f624da42813f74977425a3a6144d604ea21cf065616d36ea3a866d92c1c"}, - {file = "pyzmq-23.2.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:693c96ae4d975eb8efa1639670e9b1fac0c3f98b7845b65c0f369141fb4bb21f"}, - {file = "pyzmq-23.2.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2b054525c9f7e240562185bf21671ca16d56bde92e9bd0f822c07dec7626b704"}, - {file = "pyzmq-23.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:859059caf564f0c9398c9005278055ed3d37af4d73de6b1597821193b04ca09b"}, - {file = "pyzmq-23.2.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8355744fdbdeac5cfadfa4f38b82029b5f2b8cab7472a33453a217a7f3a9dce2"}, - {file = "pyzmq-23.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:420b9abd1a7330687a095373b8280a20cdee04342fbc8ccb3b56d9ec8efd4e62"}, - {file = "pyzmq-23.2.0-cp38-cp38-win32.whl", hash = "sha256:59928dfebe93cf1e203e3cb0fd5d5dd384da56b99c8305f2e1b0a933751710f6"}, - {file = "pyzmq-23.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:c882f1d4f96fbd807e92c334251d8ebd159a1ef89059ccd386ddea83fdb91bd8"}, - {file = "pyzmq-23.2.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:ced12075cdf3c7332ecc1960f77f7439d5ebb8ea20bbd3c34c8299e694f1b0a1"}, - {file = "pyzmq-23.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3a4d87342c2737fbb9eee5c33c792db27b36b04957b4e6b7edd73a5b239a2a13"}, - {file = "pyzmq-23.2.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:99cedf38eaddf263cf7e2a50e405f12c02cedf6d9df00a0d9c5d7b9417b57f76"}, - {file = "pyzmq-23.2.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d1610260cc672975723fcf7705c69a95f3b88802a594c9867781bedd9b13422c"}, - {file = "pyzmq-23.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c223a13555444707a0a7ebc6f9ee63053147c8c082bd1a31fd1207a03e8b0500"}, - {file = "pyzmq-23.2.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f5fdb00d65ec44b10cc6b9b6318ef1363b81647a4aa3270ca39565eadb2d1201"}, - {file = "pyzmq-23.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:984b232802eddf9f0be264a4d57a10b3a1fd7319df14ee6fc7b41c6d155a3e6c"}, - {file = "pyzmq-23.2.0-cp39-cp39-win32.whl", hash = "sha256:f146648941cadaaaf01254a75651a23c08159d009d36c5af42a7cc200a5e53ec"}, - {file = "pyzmq-23.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:83005d8928f8a5cebcfb33af3bfb84b1ad65d882b899141a331cc5d07d89f093"}, - {file = "pyzmq-23.2.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:fee86542dc4ee8229e023003e3939b4d58cc2453922cf127778b69505fc9064b"}, - {file = "pyzmq-23.2.0-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5d57542429df6acff02ff022067aa75b677603cee70e3abb9742787545eec966"}, - {file = "pyzmq-23.2.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:057b154471e096e2dda147f7b057041acc303bb7ca4aa24c3b88c6cecdd78717"}, - {file = "pyzmq-23.2.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:5d92e7cbeab7f70b08cc0f27255b0bb2500afc30f31075bca0b1cb87735d186c"}, - {file = "pyzmq-23.2.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:eb4a573a8499685d62545e806d8fd143c84ac8b3439f925cd92c8763f0ed9bd7"}, - {file = "pyzmq-23.2.0-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:da338e2728410d74ddeb1479ec67cfba73311607037455a40f92b6f5c62bf11d"}, - {file = "pyzmq-23.2.0-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1b2a21f595f8cc549abd6c8de1fcd34c83441e35fb24b8a59bf161889c62a486"}, - {file = "pyzmq-23.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:8c0f4d6f8c985bab83792be26ff3233940ba42e22237610ac50cbcfc10a5c235"}, - {file = "pyzmq-23.2.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:bbabd1df23bf63ae829e81200034c0e433499275a6ed29ca1a912ea7629426d9"}, - {file = "pyzmq-23.2.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:21552624ce69e69f7924f413b802b1fb554f4c0497f837810e429faa1cd4f163"}, - {file = "pyzmq-23.2.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c616893a577e9d6773a3836732fd7e2a729157a108b8fccd31c87512fa01671a"}, - {file = "pyzmq-23.2.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ce4f71e17fa849de41a06109030d3f6815fcc33338bf98dd0dde6d456d33c929"}, - {file = "pyzmq-23.2.0.tar.gz", hash = "sha256:a51f12a8719aad9dcfb55d456022f16b90abc8dde7d3ca93ce3120b40e3fa169"}, + {file = "pytest-xdist-3.0.2.tar.gz", hash = "sha256:688da9b814370e891ba5de650c9327d1a9d861721a524eb917e620eec3e90291"}, + {file = "pytest_xdist-3.0.2-py3-none-any.whl", hash = "sha256:9feb9a18e1790696ea23e1434fa73b325ed4998b0e9fcb221f16fd1945e6df1b"}, ] rich = [ - {file = "rich-12.5.1-py3-none-any.whl", hash = "sha256:2eb4e6894cde1e017976d2975ac210ef515d7548bc595ba20e195fb9628acdeb"}, - {file = "rich-12.5.1.tar.gz", hash = "sha256:63a5c5ce3673d3d5fbbf23cd87e11ab84b6b451436f1b7f19ec54b6bc36ed7ca"}, + {file = "rich-12.6.0-py3-none-any.whl", hash = "sha256:a4eb26484f2c82589bd9a17c73d32a010b1e29d89f1604cd9bf3a2097b81bb5e"}, + {file = "rich-12.6.0.tar.gz", hash = "sha256:ba3a3775974105c221d31141f2c116f4fd65c5ceb0698657a11e9f295ec93fd0"}, ] -six = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +sniffio = [ + {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"}, + {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, ] sortedcontainers = [ {file = "sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0"}, {file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"}, ] -stack-data = [ - {file = "stack_data-0.3.0-py3-none-any.whl", hash = "sha256:aa1d52d14d09c7a9a12bb740e6bdfffe0f5e8f4f9218d85e7c73a8c37f7ae38d"}, - {file = "stack_data-0.3.0.tar.gz", hash = "sha256:77bec1402dcd0987e9022326473fdbcc767304892a533ed8c29888dacb7dddbc"}, +textual = [ + {file = "textual-0.5.0-py3-none-any.whl", hash = "sha256:d1925f9f019858beaabda8a87cb87fb57cbbf321e948fc3b28d7085e83c8f5ab"}, + {file = "textual-0.5.0.tar.gz", hash = "sha256:516bf4db05830e70f9806828610a1a00de08068d984d329dbecc8dbef5d71ab7"}, ] tomli = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] -tomli-w = [ - {file = "tomli_w-1.0.0-py3-none-any.whl", hash = "sha256:9f2a07e8be30a0729e533ec968016807069991ae2fd921a78d42f429ae5f4463"}, - {file = "tomli_w-1.0.0.tar.gz", hash = "sha256:f463434305e0336248cac9c2dc8076b707d8a12d019dd349f5c1e382dd1ae1b9"}, -] -tornado = [ - {file = "tornado-6.2-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:20f638fd8cc85f3cbae3c732326e96addff0a15e22d80f049e00121651e82e72"}, - {file = "tornado-6.2-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:87dcafae3e884462f90c90ecc200defe5e580a7fbbb4365eda7c7c1eb809ebc9"}, - {file = "tornado-6.2-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba09ef14ca9893954244fd872798b4ccb2367c165946ce2dd7376aebdde8e3ac"}, - {file = "tornado-6.2-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8150f721c101abdef99073bf66d3903e292d851bee51910839831caba341a75"}, - {file = "tornado-6.2-cp37-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3a2f5999215a3a06a4fc218026cd84c61b8b2b40ac5296a6db1f1451ef04c1e"}, - {file = "tornado-6.2-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:5f8c52d219d4995388119af7ccaa0bcec289535747620116a58d830e7c25d8a8"}, - {file = "tornado-6.2-cp37-abi3-musllinux_1_1_i686.whl", hash = "sha256:6fdfabffd8dfcb6cf887428849d30cf19a3ea34c2c248461e1f7d718ad30b66b"}, - {file = "tornado-6.2-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:1d54d13ab8414ed44de07efecb97d4ef7c39f7438cf5e976ccd356bebb1b5fca"}, - {file = "tornado-6.2-cp37-abi3-win32.whl", hash = "sha256:5c87076709343557ef8032934ce5f637dbb552efa7b21d08e89ae7619ed0eb23"}, - {file = "tornado-6.2-cp37-abi3-win_amd64.whl", hash = "sha256:e5f923aa6a47e133d1cf87d60700889d7eae68988704e20c75fb2d65677a8e4b"}, - {file = "tornado-6.2.tar.gz", hash = "sha256:9b630419bde84ec666bfd7ea0a4cb2a8a651c2d5cccdbdd1972a0c859dfc3c13"}, -] -traitlets = [ - {file = "traitlets-5.3.0-py3-none-any.whl", hash = "sha256:65fa18961659635933100db8ca120ef6220555286949774b9cfc106f941d1c7a"}, - {file = "traitlets-5.3.0.tar.gz", hash = "sha256:0bb9f1f9f017aa8ec187d8b1b2a7a6626a2a1d877116baba52a129bfa124f8e2"}, -] typer = [ - {file = "typer-0.6.1-py3-none-any.whl", hash = "sha256:54b19e5df18654070a82f8c2aa1da456a4ac16a2a83e6dcd9f170e291c56338e"}, - {file = "typer-0.6.1.tar.gz", hash = "sha256:2d5720a5e63f73eaf31edaa15f6ab87f35f0690f8ca233017d7d23d743a91d73"}, + {file = "typer-0.7.0-py3-none-any.whl", hash = "sha256:b5e704f4e48ec263de1c0b3a2387cd405a13767d2f907f44c1a08cbad96f606d"}, + {file = "typer-0.7.0.tar.gz", hash = "sha256:ff797846578a9f2a201b53442aedeb543319466870fbe1c701eab66dd7681165"}, ] typing-extensions = [ - {file = "typing_extensions-4.3.0-py3-none-any.whl", hash = "sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02"}, - {file = "typing_extensions-4.3.0.tar.gz", hash = "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6"}, + {file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"}, + {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"}, ] watchdog = [ {file = "watchdog-2.1.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a735a990a1095f75ca4f36ea2ef2752c99e6ee997c46b0de507ba40a09bf7330"}, @@ -1407,7 +1166,88 @@ watchdog = [ {file = "watchdog-2.1.9-py3-none-win_ia64.whl", hash = "sha256:ad576a565260d8f99d97f2e64b0f97a48228317095908568a9d5c786c829d428"}, {file = "watchdog-2.1.9.tar.gz", hash = "sha256:43ce20ebb36a51f21fa376f76d1d4692452b2527ccd601950d69ed36b9e21609"}, ] -wcwidth = [ - {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, - {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, +watchfiles = [ + {file = "watchfiles-0.18.1-cp37-abi3-macosx_10_7_x86_64.whl", hash = "sha256:9891d3c94272108bcecf5597a592e61105279def1313521e637f2d5acbe08bc9"}, + {file = "watchfiles-0.18.1-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:7102342d60207fa635e24c02a51c6628bf0472e5fef067f78a612386840407fc"}, + {file = "watchfiles-0.18.1-cp37-abi3-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:00ea0081eca5e8e695cffbc3a726bb90da77f4e3f78ce29b86f0d95db4e70ef7"}, + {file = "watchfiles-0.18.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b8e6db99e49cd7125d8a4c9d33c0735eea7b75a942c6ad68b75be3e91c242fb"}, + {file = "watchfiles-0.18.1-cp37-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bc7c726855f04f22ac79131b51bf0c9f728cb2117419ed830a43828b2c4a5fcb"}, + {file = "watchfiles-0.18.1-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbaff354d12235002e62d9d3fa8bcf326a8490c1179aa5c17195a300a9e5952f"}, + {file = "watchfiles-0.18.1-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:888db233e06907c555eccd10da99b9cd5ed45deca47e41766954292dc9f7b198"}, + {file = "watchfiles-0.18.1-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:dde79930d1b28f15994ad6613aa2865fc7a403d2bb14585a8714a53233b15717"}, + {file = "watchfiles-0.18.1-cp37-abi3-win32.whl", hash = "sha256:e2b2bdd26bf8d6ed90763e6020b475f7634f919dbd1730ea1b6f8cb88e21de5d"}, + {file = "watchfiles-0.18.1-cp37-abi3-win_amd64.whl", hash = "sha256:c541e0f2c3e95e83e4f84561c893284ba984e9d0025352057396d96dceb09f44"}, + {file = "watchfiles-0.18.1-cp37-abi3-win_arm64.whl", hash = "sha256:9a26272ef3e930330fc0c2c148cc29706cc2c40d25760c7ccea8d768a8feef8b"}, + {file = "watchfiles-0.18.1-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:9fb12a5e2b42e0b53769455ff93546e6bc9ab14007fbd436978d827a95ca5bd1"}, + {file = "watchfiles-0.18.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:548d6b42303d40264118178053c78820533b683b20dfbb254a8706ca48467357"}, + {file = "watchfiles-0.18.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e0d8fdfebc50ac7569358f5c75f2b98bb473befccf9498cf23b3e39993bb45a"}, + {file = "watchfiles-0.18.1-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:0f9a22fff1745e2bb930b1e971c4c5b67ea3b38ae17a6adb9019371f80961219"}, + {file = "watchfiles-0.18.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b02e7fa03cd4059dd61ff0600080a5a9e7a893a85cb8e5178943533656eec65e"}, + {file = "watchfiles-0.18.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a868ce2c7565137f852bd4c863a164dc81306cae7378dbdbe4e2aca51ddb8857"}, + {file = "watchfiles-0.18.1.tar.gz", hash = "sha256:4ec0134a5e31797eb3c6c624dbe9354f2a8ee9c720e0b46fc5b7bab472b7c6d4"}, +] +yarl = [ + {file = "yarl-1.8.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:abc06b97407868ef38f3d172762f4069323de52f2b70d133d096a48d72215d28"}, + {file = "yarl-1.8.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:07b21e274de4c637f3e3b7104694e53260b5fc10d51fb3ec5fed1da8e0f754e3"}, + {file = "yarl-1.8.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9de955d98e02fab288c7718662afb33aab64212ecb368c5dc866d9a57bf48880"}, + {file = "yarl-1.8.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ec362167e2c9fd178f82f252b6d97669d7245695dc057ee182118042026da40"}, + {file = "yarl-1.8.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:20df6ff4089bc86e4a66e3b1380460f864df3dd9dccaf88d6b3385d24405893b"}, + {file = "yarl-1.8.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5999c4662631cb798496535afbd837a102859568adc67d75d2045e31ec3ac497"}, + {file = "yarl-1.8.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed19b74e81b10b592084a5ad1e70f845f0aacb57577018d31de064e71ffa267a"}, + {file = "yarl-1.8.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e4808f996ca39a6463f45182e2af2fae55e2560be586d447ce8016f389f626f"}, + {file = "yarl-1.8.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:2d800b9c2eaf0684c08be5f50e52bfa2aa920e7163c2ea43f4f431e829b4f0fd"}, + {file = "yarl-1.8.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6628d750041550c5d9da50bb40b5cf28a2e63b9388bac10fedd4f19236ef4957"}, + {file = "yarl-1.8.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:f5af52738e225fcc526ae64071b7e5342abe03f42e0e8918227b38c9aa711e28"}, + {file = "yarl-1.8.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:76577f13333b4fe345c3704811ac7509b31499132ff0181f25ee26619de2c843"}, + {file = "yarl-1.8.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0c03f456522d1ec815893d85fccb5def01ffaa74c1b16ff30f8aaa03eb21e453"}, + {file = "yarl-1.8.1-cp310-cp310-win32.whl", hash = "sha256:ea30a42dc94d42f2ba4d0f7c0ffb4f4f9baa1b23045910c0c32df9c9902cb272"}, + {file = "yarl-1.8.1-cp310-cp310-win_amd64.whl", hash = "sha256:9130ddf1ae9978abe63808b6b60a897e41fccb834408cde79522feb37fb72fb0"}, + {file = "yarl-1.8.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0ab5a138211c1c366404d912824bdcf5545ccba5b3ff52c42c4af4cbdc2c5035"}, + {file = "yarl-1.8.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0fb2cb4204ddb456a8e32381f9a90000429489a25f64e817e6ff94879d432fc"}, + {file = "yarl-1.8.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:85cba594433915d5c9a0d14b24cfba0339f57a2fff203a5d4fd070e593307d0b"}, + {file = "yarl-1.8.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1ca7e596c55bd675432b11320b4eacc62310c2145d6801a1f8e9ad160685a231"}, + {file = "yarl-1.8.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0f77539733e0ec2475ddcd4e26777d08996f8cd55d2aef82ec4d3896687abda"}, + {file = "yarl-1.8.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:29e256649f42771829974e742061c3501cc50cf16e63f91ed8d1bf98242e5507"}, + {file = "yarl-1.8.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7fce6cbc6c170ede0221cc8c91b285f7f3c8b9fe28283b51885ff621bbe0f8ee"}, + {file = "yarl-1.8.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:59ddd85a1214862ce7c7c66457f05543b6a275b70a65de366030d56159a979f0"}, + {file = "yarl-1.8.1-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:12768232751689c1a89b0376a96a32bc7633c08da45ad985d0c49ede691f5c0d"}, + {file = "yarl-1.8.1-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:b19255dde4b4f4c32e012038f2c169bb72e7f081552bea4641cab4d88bc409dd"}, + {file = "yarl-1.8.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:6c8148e0b52bf9535c40c48faebb00cb294ee577ca069d21bd5c48d302a83780"}, + {file = "yarl-1.8.1-cp37-cp37m-win32.whl", hash = "sha256:de839c3a1826a909fdbfe05f6fe2167c4ab033f1133757b5936efe2f84904c07"}, + {file = "yarl-1.8.1-cp37-cp37m-win_amd64.whl", hash = "sha256:dd032e8422a52e5a4860e062eb84ac94ea08861d334a4bcaf142a63ce8ad4802"}, + {file = "yarl-1.8.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:19cd801d6f983918a3f3a39f3a45b553c015c5aac92ccd1fac619bd74beece4a"}, + {file = "yarl-1.8.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6347f1a58e658b97b0a0d1ff7658a03cb79bdbda0331603bed24dd7054a6dea1"}, + {file = "yarl-1.8.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7c0da7e44d0c9108d8b98469338705e07f4bb7dab96dbd8fa4e91b337db42548"}, + {file = "yarl-1.8.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5587bba41399854703212b87071c6d8638fa6e61656385875f8c6dff92b2e461"}, + {file = "yarl-1.8.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31a9a04ecccd6b03e2b0e12e82131f1488dea5555a13a4d32f064e22a6003cfe"}, + {file = "yarl-1.8.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:205904cffd69ae972a1707a1bd3ea7cded594b1d773a0ce66714edf17833cdae"}, + {file = "yarl-1.8.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea513a25976d21733bff523e0ca836ef1679630ef4ad22d46987d04b372d57fc"}, + {file = "yarl-1.8.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0b51530877d3ad7a8d47b2fff0c8df3b8f3b8deddf057379ba50b13df2a5eae"}, + {file = "yarl-1.8.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d2b8f245dad9e331540c350285910b20dd913dc86d4ee410c11d48523c4fd546"}, + {file = "yarl-1.8.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ab2a60d57ca88e1d4ca34a10e9fb4ab2ac5ad315543351de3a612bbb0560bead"}, + {file = "yarl-1.8.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:449c957ffc6bc2309e1fbe67ab7d2c1efca89d3f4912baeb8ead207bb3cc1cd4"}, + {file = "yarl-1.8.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a165442348c211b5dea67c0206fc61366212d7082ba8118c8c5c1c853ea4d82e"}, + {file = "yarl-1.8.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b3ded839a5c5608eec8b6f9ae9a62cb22cd037ea97c627f38ae0841a48f09eae"}, + {file = "yarl-1.8.1-cp38-cp38-win32.whl", hash = "sha256:c1445a0c562ed561d06d8cbc5c8916c6008a31c60bc3655cdd2de1d3bf5174a0"}, + {file = "yarl-1.8.1-cp38-cp38-win_amd64.whl", hash = "sha256:56c11efb0a89700987d05597b08a1efcd78d74c52febe530126785e1b1a285f4"}, + {file = "yarl-1.8.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e80ed5a9939ceb6fda42811542f31c8602be336b1fb977bccb012e83da7e4936"}, + {file = "yarl-1.8.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6afb336e23a793cd3b6476c30f030a0d4c7539cd81649683b5e0c1b0ab0bf350"}, + {file = "yarl-1.8.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4c322cbaa4ed78a8aac89b2174a6df398faf50e5fc12c4c191c40c59d5e28357"}, + {file = "yarl-1.8.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fae37373155f5ef9b403ab48af5136ae9851151f7aacd9926251ab26b953118b"}, + {file = "yarl-1.8.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5395da939ffa959974577eff2cbfc24b004a2fb6c346918f39966a5786874e54"}, + {file = "yarl-1.8.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:076eede537ab978b605f41db79a56cad2e7efeea2aa6e0fa8f05a26c24a034fb"}, + {file = "yarl-1.8.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d1a50e461615747dd93c099f297c1994d472b0f4d2db8a64e55b1edf704ec1c"}, + {file = "yarl-1.8.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7de89c8456525650ffa2bb56a3eee6af891e98f498babd43ae307bd42dca98f6"}, + {file = "yarl-1.8.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4a88510731cd8d4befaba5fbd734a7dd914de5ab8132a5b3dde0bbd6c9476c64"}, + {file = "yarl-1.8.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2d93a049d29df172f48bcb09acf9226318e712ce67374f893b460b42cc1380ae"}, + {file = "yarl-1.8.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:21ac44b763e0eec15746a3d440f5e09ad2ecc8b5f6dcd3ea8cb4773d6d4703e3"}, + {file = "yarl-1.8.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:d0272228fabe78ce00a3365ffffd6f643f57a91043e119c289aaba202f4095b0"}, + {file = "yarl-1.8.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:99449cd5366fe4608e7226c6cae80873296dfa0cde45d9b498fefa1de315a09e"}, + {file = "yarl-1.8.1-cp39-cp39-win32.whl", hash = "sha256:8b0af1cf36b93cee99a31a545fe91d08223e64390c5ecc5e94c39511832a4bb6"}, + {file = "yarl-1.8.1-cp39-cp39-win_amd64.whl", hash = "sha256:de49d77e968de6626ba7ef4472323f9d2e5a56c1d85b7c0e2a190b2173d3b9be"}, + {file = "yarl-1.8.1.tar.gz", hash = "sha256:af887845b8c2e060eb5605ff72b6f2dd2aab7a761379373fd89d314f4752abbf"}, +] +zipp = [ + {file = "zipp-3.11.0-py3-none-any.whl", hash = "sha256:83a28fcb75844b5c0cdaf5aa4003c2d728c77e05f5aeabe8e95e56727005fbaa"}, + {file = "zipp-3.11.0.tar.gz", hash = "sha256:a7a22e05929290a67401440b39690ae6563279bced5f314609d9d03798f56766"}, ] diff --git a/pyproject.toml b/pyproject.toml index 8bc2962..705231e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "spiel" -version = "0.3.0" +version = "0.4.0" description = "A framework for building and presenting richly-styled presentations in your terminal using Python." readme="README.md" homepage="https://github.com/JoshKarpel/spiel" @@ -20,36 +20,32 @@ classifiers = [ "Typing :: Typed"] authors = ["JoshKarpel "] license = "MIT" -include = ["py.typed", "demo/*"] +include = ["py.typed", "demo/*", "spiel.css"] [tool.poetry.dependencies] python = ">=3.10,<4" rich = ">=12" typer = ">=0.6" -watchdog = ">=2" -pendulum = ">=2" Pillow = ">=8" -tomli = ">=2" -tomli-w = ">=1" -ipython = ">=7" -ipykernel = ">=5" +textual = ">=0.5" +watchfiles = ">=0.18" -[tool.poetry.dev-dependencies] -pytest = ">=6" +[tool.poetry.group.dev.dependencies] +pytest = ">=7" pytest-watch = ">=4" pytest-cov = ">=3" pytest-xdist = ">=2" -mypy = ">=0.961" -pytest-mypy = ">=0.8" +mypy = ">=0.982" +pytest-mypy = ">=0.10" pytest-mock = ">=3" hypothesis = ">=6" +textual = {extras = ["dev"], version = ">=0.5"} [tool.poetry.scripts] -spiel = 'spiel.main:app' +spiel = 'spiel.cli:cli' [tool.black] line-length = 100 -target-version = ["py39", "py310"] include = "\\.pyi?$" [tool.isort] @@ -68,9 +64,7 @@ norecursedirs = ["demo"] pretty = false show_error_codes = true -files = ["spiel/*.py", "tests/*.py"] - -exclude = "demo/" +files = ["spiel/**/*.py", "tests/**/*.py"] check_untyped_defs = true disallow_incomplete_defs = true diff --git a/spiel/__init__.py b/spiel/__init__.py index 2f18949..97b254f 100644 --- a/spiel/__init__.py +++ b/spiel/__init__.py @@ -1,8 +1,4 @@ from spiel.constants import __version__ from spiel.deck import Deck -from spiel.example import Example, example_panels -from spiel.image import Image -from spiel.options import Options -from spiel.repls import repl from spiel.slide import Slide from spiel.triggers import Triggers diff --git a/spiel/__main__.py b/spiel/__main__.py index 2fe1cc9..a59524e 100644 --- a/spiel/__main__.py +++ b/spiel/__main__.py @@ -1,4 +1,3 @@ -from spiel.constants import PACKAGE_NAME -from spiel.main import app +from spiel.cli import cli -app(prog_name=PACKAGE_NAME) +cli() diff --git a/spiel/app.py b/spiel/app.py new file mode 100644 index 0000000..2e90086 --- /dev/null +++ b/spiel/app.py @@ -0,0 +1,187 @@ +from __future__ import annotations + +import asyncio +import code +import datetime +import importlib.util +import sys +from asyncio import wait +from contextlib import contextmanager, redirect_stderr, redirect_stdout +from functools import cached_property, partial +from pathlib import Path +from time import monotonic +from typing import Callable, Iterator + +from rich.style import Style +from rich.text import Text +from textual import log +from textual.app import App +from textual.binding import Binding +from textual.events import Resize +from textual.reactive import reactive +from watchfiles import awatch + +from spiel.constants import DECK, RELOAD_MESSAGE_TIME_FORMAT +from spiel.deck import Deck +from spiel.exceptions import NoDeckFound +from spiel.screens.deck import DeckScreen +from spiel.screens.help import HelpScreen +from spiel.screens.slide import SlideScreen +from spiel.triggers import Triggers +from spiel.utils import clamp +from spiel.widgets.slide import SlideWidget + + +def load_deck(path: Path) -> Deck: + module_name = "__deck" + spec = importlib.util.spec_from_file_location(module_name, path) + + if spec is None: + raise NoDeckFound(f"{path.resolve()} does not appear to be an importable Python module.") + + module = importlib.util.module_from_spec(spec) + sys.modules[module_name] = module + + loader = spec.loader + assert loader is not None + loader.exec_module(module) + + try: + deck = getattr(module, DECK) + except AttributeError: + raise NoDeckFound(f"The module at {path} does not have an attribute named {DECK}.") + + if not isinstance(deck, Deck): + raise NoDeckFound( + f"The module at {path} has an attribute named {DECK}, but it is a {type(deck).__name__}, not a {Deck.__name__}." + ) + + return deck + + +class SpielApp(App[None]): + CSS_PATH = "spiel.css" + BINDINGS = [ + Binding("d", "switch_screen('deck')", "Go to the Deck view."), + Binding("question_mark", "push_screen('help')", "Go to the Help view."), + Binding("i", "repl", "Switch to the REPL."), + ] + SCREENS = {"slide": SlideScreen(), "deck": DeckScreen(), "help": HelpScreen()} + + deck = reactive(Deck(name="New Deck")) + current_slide_idx = reactive(0) + message = reactive(Text("")) + + def __init__(self, deck_path: Path, watch_path: Path): + super().__init__() + + self.deck_path = deck_path + self.watch_path = watch_path + + async def on_mount(self) -> None: + self.deck = load_deck(self.deck_path) + self.reloader = asyncio.create_task(self.reload()) + + await self.push_screen("slide") + + async def reload(self) -> None: + log(f"Watching {self.watch_path} for changes") + async for changes in awatch(self.watch_path): + change_msg = "\n ".join([""] + [f"{k.raw_str()}: {v}" for k, v in changes]) + log(f"Reloading deck from {self.deck_path} due to detected file changes:{change_msg}") + try: + self.deck = load_deck(self.deck_path) + self.current_slide_idx = clamp(self.current_slide_idx, 0, len(self.deck)) + self.set_message_temporarily( + Text( + f"Reloaded deck at {datetime.datetime.now().strftime(RELOAD_MESSAGE_TIME_FORMAT)}", + style=Style(dim=True), + ), + delay=10, + ) + except Exception as e: + self.set_message_temporarily( + Text( + f"Failed to reload deck at {datetime.datetime.now().strftime(RELOAD_MESSAGE_TIME_FORMAT)} due to: {e}", + style=Style(color="red"), + ), + delay=10, + ) + + def on_resize(self, event: Resize) -> None: + self.set_message_temporarily( + message=Text(f"Screen resized to {event.size}", style=Style(dim=True)), delay=2 + ) + + def set_message_temporarily(self, message: Text, delay: float) -> None: + self.message = message + + def clear() -> None: + if self.message is message: + self.message = Text("") + + self.set_timer(delay, clear) + + def action_next_slide(self) -> None: + self.current_slide_idx = clamp(self.current_slide_idx + 1, 0, len(self.deck) - 1) + + def action_prev_slide(self) -> None: + self.current_slide_idx = clamp(self.current_slide_idx - 1, 0, len(self.deck) - 1) + + def action_next_row(self) -> None: + self.current_slide_idx = clamp( + self.current_slide_idx + self.deck_grid_width, 0, len(self.deck) - 1 + ) + + def action_prev_row(self) -> None: + self.current_slide_idx = clamp( + self.current_slide_idx - self.deck_grid_width, 0, len(self.deck) - 1 + ) + + def watch_current_slide_idx(self, new_current_slide_idx: int) -> None: + self.query_one(SlideWidget).triggers = Triggers.new() + + def action_trigger(self) -> None: + now = monotonic() + slide_widget = self.query_one(SlideWidget) + slide_widget.triggers = Triggers(now=now, times=(*slide_widget.triggers.times, now)) + + def action_reset_trigger(self) -> None: + slide_widget = self.query_one(SlideWidget) + slide_widget.triggers = Triggers.new() + + @cached_property + def repl(self) -> Callable[[], None]: + # Lazily enable readline support + import readline # nopycln: import + + self.console.clear() # clear the console the first time we go into the repl + sys.stdout.flush() + + repl = code.InteractiveConsole() + return partial(repl.interact, banner="", exitmsg="") + + def action_repl(self) -> None: + with self.suspend(): + self.repl() + + async def action_quit(self) -> None: + self.reloader.cancel() + await wait([self.reloader], timeout=1) + + await super().action_quit() + + @contextmanager + def suspend(self) -> Iterator[None]: + driver = self._driver + + if driver is not None: + driver.stop_application_mode() + driver.exit_event.clear() # type: ignore[attr-defined] + with redirect_stdout(sys.__stdout__), redirect_stderr(sys.__stderr__): + yield + driver.start_application_mode() + + @property + def deck_grid_width(self) -> int: + return max(self.console.size.width // 35, 1) diff --git a/spiel/main.py b/spiel/cli.py similarity index 64% rename from spiel/main.py rename to spiel/cli.py index 7a6390a..fdc0624 100644 --- a/spiel/main.py +++ b/spiel/cli.py @@ -1,27 +1,29 @@ +import os import shutil -from contextlib import nullcontext from pathlib import Path from textwrap import dedent +import typer.rich_utils as ru +from click.exceptions import Exit from rich.console import Console -from rich.control import Control from rich.style import Style from rich.syntax import Syntax from rich.text import Text -from typer import Argument, Exit, Option, Typer +from typer import Argument, Option, Typer -from spiel.constants import PACKAGE_NAME, __version__ -from spiel.help import version_details -from spiel.load import DeckWatcher -from spiel.modes import Mode -from spiel.present import present_deck -from spiel.reloader import DeckReloader -from spiel.state import State +from spiel.app import SpielApp +from spiel.constants import DEMO_DIR, DEMO_FILE, PACKAGE_DIR, PACKAGE_NAME, __version__ +from spiel.renderables.debug import DebugTable -THIS_DIR = Path(__file__).resolve().parent +ru.STYLE_HELPTEXT = "" +console = Console() -app = Typer( + +cli = Typer( + name=PACKAGE_NAME, + no_args_is_help=True, + rich_markup_mode="rich", help=dedent( f"""\ Display [italic yellow]Rich[/italic yellow]ly-styled presentations using your terminal. @@ -30,73 +32,40 @@ app = Typer( $ spiel demo present - A {PACKAGE_NAME.capitalize()} presentation (a "[italic green]deck[/italic green] of slides") is defined programmatically using a Python script. + A {PACKAGE_NAME.capitalize()} presentation (a "[italic green]deck[/italic green] of slides") + is defined programmatically using a Python script. """ ), - rich_markup_mode="rich", - no_args_is_help=True, ) -@app.command() +@cli.command() def present( path: Path = Argument( ..., dir_okay=False, + exists=True, + readable=True, help="The path to the slide deck file.", ), - mode: Mode = Option( - default=Mode.SLIDE, - help="The mode to start presenting in.", - ), - slide: int = Option( - default=1, - help="The slide number to start the presentation on.", - ), - watch: bool = Option( - default=False, - help="If enabled, reload the deck when the slide deck file changes.", - ), - poll: bool = Option( - default=False, - help="If enabled, poll the filesystem for changes (implies --watch). Use this option on systems that don't support file modification notifications.", + watch: Path = Option( + default=Path.cwd(), ), ) -> None: """ Present a deck. """ - _present(path=path, mode=mode, slide=slide, watch=watch, poll=poll) - - -def _present(path: Path, mode: Mode, slide: int, watch: bool, poll: bool) -> None: - console = Console() - - try: - state = State.from_file(path) - except FileNotFoundError as e: - console.print(Text(f"Error: {e}", style=Style(color="red"))) - raise Exit(code=1) + _present(deck_path=path, watch_path=watch) - state.mode = mode - state.jump_to_slide(slide - 1) - watcher = ( - DeckWatcher(event_handler=DeckReloader(state, path), path=path, poll=poll) - if (watch or poll) - else nullcontext() - ) +def _present(deck_path: Path, watch_path: Path) -> None: + os.environ["TEXTUAL"] = ",".join(sorted(["debug", "devtools"])) - try: - with state, watcher: - present_deck(state) - except KeyboardInterrupt: - raise Exit(code=0) - finally: - state.console.print(Control.clear()) - state.console.print(Control.move_to(0, 0)) + app = SpielApp(deck_path=deck_path, watch_path=watch_path) + app.run() -@app.command() +@cli.command() def init( path: Path = Argument( ..., @@ -110,7 +79,6 @@ def init( This is a good starting point if you already know what you want to do. If you're not so sure, consider taking a look at the demo deck to see what's possible: - $ spiel demo --help """ console = Console() @@ -139,22 +107,19 @@ def init( dedent( f"""\ from textwrap import dedent + from spiel import Deck - from spiel import Deck, Options deck = Deck(name="{name}") - options = Options() @deck.slide(title="Title") def title(): markup = dedent( \"""\\ # {name} - This is your title slide! \""" ) - return Markdown(markup, justify="center") """ ) @@ -171,37 +136,17 @@ def init( console.print(Text(f"Wrote deck template to {path}", style=Style(color="green"))) -@app.command() -def version( - plain: bool = Option( - default=False, - help=f"Print only {PACKAGE_NAME}'s version.", - ) -) -> None: - """ - Display version and debugging information. - """ - console = Console() - - if plain: - print(__version__) - else: - console.print(version_details(console)) - - demo = Typer( name="demo", + no_args_is_help=True, + rich_markup_mode="rich", help=dedent( """\ Use the demonstration deck (present it, display source, etc.). """ ), - rich_markup_mode="rich", - no_args_is_help=True, ) - -DEMO_DIR = THIS_DIR / "demo" -DEMO_SOURCE = THIS_DIR / "demo" / "demo.py" +cli.add_typer(demo) @demo.command(name="present") @@ -209,7 +154,7 @@ def present_demo() -> None: """ Present the demo deck. """ - _present(path=DEMO_SOURCE, mode=Mode.SLIDE, slide=0, watch=False, poll=False) + _present(deck_path=DEMO_FILE, watch_path=PACKAGE_DIR) @demo.command() @@ -220,13 +165,14 @@ def source() -> None: console = Console() with console.pager(styles=True): - console.print(Syntax(DEMO_SOURCE.read_text(), lexer="python")) + console.print(Syntax(DEMO_FILE.read_text(encoding="utf-8"), lexer="python")) @demo.command() def copy( path: Path = Argument( default=..., + exists=False, writable=True, help="The path to copy the demo deck source code and assets to.", ) @@ -235,14 +181,13 @@ def copy( Copy the demo deck source code and assets to a new directory. If you're looking for a more stripped-down starting point, try the init command: - $ spiel init --help """ console = Console() if path.exists(): console.print(Text(f"Error: {path} already exists!", style=Style(color="red"))) - raise Exit(code=2) + raise Exit(code=1) try: shutil.copytree(DEMO_DIR, path) @@ -255,4 +200,18 @@ def copy( ) -app.add_typer(demo) +@cli.command() +def version( + plain: bool = Option( + default=False, + help=f"Print only {PACKAGE_NAME}'s version.", + ) +) -> None: + """ + Display version and debugging information. + """ + + if plain: + console.print(__version__, style=Style()) + else: + console.print(DebugTable()) diff --git a/spiel/constants.py b/spiel/constants.py index d563205..715c319 100644 --- a/spiel/constants.py +++ b/spiel/constants.py @@ -1,15 +1,18 @@ -import os import sys from importlib import metadata +from pathlib import Path PACKAGE_NAME = "spiel" __version__ = metadata.version(PACKAGE_NAME) __rich_version__ = metadata.version("rich") +__textual_version__ = metadata.version("textual") __python_version__ = ".".join(map(str, sys.version_info)) DECK = "deck" -OPTIONS = "options" -TARGET_RPS = 30 +PACKAGE_DIR = Path(__file__).resolve().parent +DEMO_DIR = PACKAGE_DIR / "demo" +DEMO_FILE = PACKAGE_DIR / "demo" / "demo.py" -EDITOR = os.getenv("EDITOR", "not set") +FOOTER_TIME_FORMAT = "%Y-%m-%d %I:%M %p" +RELOAD_MESSAGE_TIME_FORMAT = "%I:%M:%S %p" diff --git a/spiel/deck.py b/spiel/deck.py index 29ca1ab..a3162cc 100644 --- a/spiel/deck.py +++ b/spiel/deck.py @@ -1,75 +1,40 @@ from __future__ import annotations -import dis -import inspect -import sys -from collections.abc import Callable, Collection, Iterator, Sequence from dataclasses import dataclass, field -from textwrap import dedent +from typing import Callable, Iterator, Mapping -from spiel.example import Example -from spiel.presentable import Presentable -from spiel.slide import MakeRenderable, Slide +from spiel.slide import Content, Slide @dataclass -class Deck(Collection[Presentable]): +class Deck: name: str - slides: list[Presentable] = field(default_factory=list) - - def __getitem__(self, idx: int) -> Presentable: - return self.slides[idx] + slides: list[Slide] = field(default_factory=list) def __len__(self) -> int: return len(self.slides) - def __iter__(self) -> Iterator[Presentable]: - return iter(self.slides) - - def __contains__(self, obj: object) -> bool: - return obj in self.slides + def __getitem__(self, item: int) -> Slide: + return self.slides[item] - def add_slides(self, *slides: Presentable) -> Deck: - self.slides.extend(slides) - return self + def __iter__(self) -> Iterator[Slide]: + yield from self.slides def slide( self, title: str = "", - ) -> Callable[[MakeRenderable], Slide]: - def slideify(content: MakeRenderable) -> Slide: + bindings: Mapping[str, Callable[[], None]] | None = None, + ) -> Callable[[Content], Slide]: + def slideify(content: Content) -> Slide: slide = Slide( title=title, content=content, + bindings=bindings or {}, ) self.add_slides(slide) return slide return slideify - def example( - self, - title: str = "", - command: Sequence[str] = (sys.executable,), - name: str = "example.py", - language: str = "python", - ) -> Callable[[Callable[[], None]], Example]: - def exampleify(example: Callable[[], None]) -> Example: - ex = Example( - title=title, - source=get_function_body(example), - command=command, - name=name, - language=language, - ) - self.add_slides(ex) - return ex - - return exampleify - - -def get_function_body(function: Callable[..., object]) -> str: - lines, line_of_def_start = inspect.getsourcelines(function) - line_of_first_instruction = list(dis.Bytecode(function))[0].starts_line or line_of_def_start - offset = line_of_first_instruction - line_of_def_start - return dedent("".join(lines[offset:])) + def add_slides(self, *slides: Slide) -> None: + self.slides.extend(slides) diff --git a/spiel/demo/__init__.py b/spiel/demo/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/spiel/demo/demo.py b/spiel/demo/demo.py index 348d7d6..cd0ec88 100644 --- a/spiel/demo/demo.py +++ b/spiel/demo/demo.py @@ -1,16 +1,17 @@ import inspect -import os import shutil import socket from datetime import datetime from math import cos, floor, pi from pathlib import Path from textwrap import dedent +from typing import Callable, Iterable +from click import edit from rich.align import Align -from rich.box import SQUARE +from rich.box import HEAVY, SQUARE from rich.color import Color, blend_rgb -from rich.console import Group +from rich.console import Group, RenderableType from rich.layout import Layout from rich.markdown import Markdown from rich.padding import Padding @@ -19,10 +20,11 @@ from rich.style import Style from rich.syntax import Syntax from rich.text import Text -from spiel import Deck, Image, Options, Slide, __version__, example_panels +from spiel import Slide, Triggers, __version__ +from spiel.deck import Deck +from spiel.renderables.image import Image deck = Deck(name=f"Spiel Demo Deck (v{__version__})") -options = Options() SPIEL = "[Spiel](https://github.com/JoshKarpel/spiel)" RICH = "[Rich](https://rich.readthedocs.io/)" @@ -32,9 +34,13 @@ WSL = "[Windows Subsystem for Linux](https://docs.microsoft.com/en-us/windows/ws THIS_DIR = Path(__file__).resolve().parent +def pad_markdown(markup: str) -> RenderableType: + return Padding(Markdown(dedent(markup), justify="center"), pad=(0, 5)) + + @deck.slide(title="What is Spiel?") -def what(): - upper_left_markup = dedent( +def what() -> RenderableType: + upper_left = pad_markdown( f"""\ ## What is Spiel? @@ -43,17 +49,17 @@ def what(): Spiel uses {RICH} to render slide content. Anything you can display with Rich, you can display with Spiel (plus some other things)! - Use your right `→` and left `←` arrows keys (or `f` and `b`) to go forwards and backwards through the deck. + Use your right `→` and left `←` arrows keys to go forwards and backwards through the deck. - Press `ctrl-c` or `ctrl-k` to exit. + Press `ctrl-c` to exit. - Press `h` at any time to see the help screen, which describes all of the actions you can take. + Press `?` at any time to see the help screen, which describes all of the actions you can take. To get a copy of the source code for this deck, use the `spiel demo copy` command. """ ) - upper_right_markup = dedent( + upper_right = pad_markdown( """\ ## Why use Spiel? @@ -71,7 +77,7 @@ def what(): """ ) - lower_left_markup = dedent( + lower_left = pad_markdown( f"""\ ## Contributing @@ -83,7 +89,7 @@ def what(): """ ) - lower_right_markup = dedent( + lower_right = pad_markdown( f"""\ ## Inspirations @@ -95,20 +101,17 @@ def what(): """ ) - def pad_markdown(markup): - return Padding(Markdown(markup, justify="center"), pad=(0, 5)) - root = Layout() upper = Layout() lower = Layout() upper.split_row( - Layout(pad_markdown(upper_left_markup)), - Layout(pad_markdown(upper_right_markup)), + Layout(upper_left), + Layout(upper_right), ) lower.split_row( - Layout(pad_markdown(lower_left_markup)), - Layout(pad_markdown(lower_right_markup)), + Layout(lower_left), + Layout(lower_right), ) root.split_column(upper, lower) @@ -116,24 +119,22 @@ def what(): @deck.slide(title="Decks and Slides") -def code(): - markup = dedent( - f"""\ - ## Decks are made of Slides +def code() -> RenderableType: + markup = f"""\ + ## Decks are made of Slides - Here's the code for `Deck` and `Slide`! + Here's the code for `Deck` and `Slide`! - The source code is pulled directly from the definitions via [inspect.getsource](https://docs.python.org/3/library/inspect.html#inspect.getsource). + The source code is pulled directly from the definitions via [inspect.getsource](https://docs.python.org/3/library/inspect.html#inspect.getsource). - ({RICH} supports syntax highlighting, so {SPIEL} does too!) - """ - ) + ({RICH} supports syntax highlighting, so {SPIEL} does too!) + """ root = Layout() - upper = Layout(Markdown(markup, justify="center"), size=len(markup.split("\n")) + 1) + upper = Layout(pad_markdown(markup), size=len(markup.split("\n")) + 1) lower = Layout() root.split_column(upper, lower) - def make_code_panel(obj): + def make_code_panel(obj: type) -> RenderableType: lines, line_number = inspect.getsourcelines(obj) return Panel( Syntax( @@ -156,23 +157,30 @@ def code(): @deck.slide(title="Dynamic Content") -def dynamic(): - home = Path.home() +def dynamic() -> RenderableType: width = shutil.get_terminal_size().columns width_limit = 80 + + home = Path.home() home_dir_contents = list(home.iterdir()) + return Group( Align.center( - Text( - f"Slides can have dynamic content!", - style=Style(color="bright_magenta", bold=True, italic=True), - justify="center", + pad_markdown( + f"""\ + ## Slides can have dynamic content! + + Since slides are created using normal Python code, + any output you can imagine producing via Python can make it into your slides. + + Here are some examples: + """ ), ), Align.center( Panel( Text( - f"Your terminal is {width} cells wide" + f"Your terminal is {width} cells wide (try resizing it or adjusting your font size!)" if width > width_limit else f"Your terminal is only {width} cells wide! Get a bigger monitor!", style=Style(color="green1" if width > width_limit else "red"), @@ -183,7 +191,7 @@ def dynamic(): Align.center( Panel( Text.from_markup( - f"The time on this computer ([bold]{socket.gethostname()}[/bold]) is {datetime.now()}", + f"The local timezone on this computer ([bold]{socket.gethostname()}[/bold]) is [bold underline]{datetime.now().astimezone().tzinfo}[/bold underline]", style="bright_cyan", justify="center", ) @@ -202,25 +210,24 @@ def dynamic(): @deck.slide(title="Triggers") -def triggers(triggers): - info = Markdown( - dedent( - f"""\ - ## Triggers +def triggers(triggers: Triggers) -> RenderableType: + info = pad_markdown( + f"""\ + ## Triggers - Triggers are a mechanism for making dynamic content that depends on *relative* time. + Triggers are a mechanism for making dynamic content that depends on *relative* time. - Triggers can be used to implement effects like fades, motion, and other "animations". + Triggers can be used to implement effects like fades, motion, and other "animations". - Each slide is triggered once when it starts being displayed. - You can trigger it again (as many times as you'd like) by pressing `t`. - You can reset the trigger state by pressing `r`. + Each slide is triggered once when it starts being displayed. - This slide has been triggered {len(triggers)} times. - It was last triggered {triggers.time_since_last_trigger:.2f} seconds ago. - """ - ), - justify="center", + You can trigger it again (as many times as you'd like) by pressing `t`. + You can reset the trigger state by pressing `r`. + + This slide has been triggered {len(triggers)} times. + + It was last triggered {triggers.time_since_last_trigger:.2f} seconds ago. + """ ) bounce_period = 10 @@ -251,7 +258,7 @@ def triggers(triggers): lines = [ Text( - "Triggered!", + f"Triggered at {time - triggers[0]:.3f}!", style=Style( color=( Color.from_triplet( @@ -264,191 +271,153 @@ def triggers(triggers): ) ), ) - for time in triggers.times + for time in triggers ] - fun = Align.center( - Panel( - Text("\n", justify="center").join(lines), - border_style=Style( - color=Color.from_triplet( - blend_rgb( - green.get_truecolor(), - red.get_truecolor(), - cross_fade=min(triggers.time_since_last_trigger / fade_time, 1), - ) + fun = Padding( + Align.center( + Panel( + Text("\n", justify="center").join(lines), + border_style=Style( + color=Color.from_triplet( + blend_rgb( + green.get_truecolor(), + red.get_truecolor(), + cross_fade=min(triggers.time_since_last_trigger / fade_time, 1), + ) + ), ), - ), - title="Trigger Tracker", - ) + title="Trigger Tracker", + ) + ), + pad=(1, 0), ) + return Group(info, fun, ball if len(triggers) > 2 else Text("")) @deck.slide(title="Views") -def grid(): - markup = dedent( +def grid() -> RenderableType: + return pad_markdown( """\ - ## Multiple Views + ## Deck View - Try pressing `d` to go into "deck" view. - Press `s` to go back to "slide" view. + Try pressing `d` to go into "deck" view. + You can still move between slides in deck view. - Press `j`, then enter a slide number (like `3`) to jump to a slide. - """ + Press `enter` to go back to "slide" view (this view), + on the currently-selected slide. + """ ) - return Markdown(markup, justify="center") - - -@deck.slide(title="Watch Mode") -def watch(): - markup = dedent( - f"""\ - ## Developing a Deck - - {SPIEL} can reload your deck as you edit it if you add the `--watch` option to `present`: - `$ spiel present path/to/deck.py --watch` - If you're on a system without inotify support (e.g., {WSL}), you should use the `--poll` option instead. - """ - ) - return Markdown(markup, justify="center") +@deck.slide(title="Displaying Images") +def image() -> RenderableType: + markup = f"""\ + ## Images + {SPIEL} can display images... sort of! -@deck.slide(title="Displaying Images") -def image(): - markup = dedent( - f"""\ - ## Images + Spiel includes an `Image` widget that can render images by interpolating pixel values. - {SPIEL} can display images... sort of! + If you see big chunks of constant color instead of smooth gradients, your terminal is probably not configured for "truecolor" mode. + If your terminal supports truecolor (it probably does), try setting the environment variable `COLORTERM` to `truecolor`. - Spiel includes an `Image` widget that can render images by interpolating pixel values. + For example, for `bash`, you could add - If you see big chunks of constant color instead of smooth gradients, your terminal is probably not configured for "truecolor" mode. - If your terminal supports truecolor (it probably does), try setting the environment variable `COLORTERM` to `truecolor`. - For example, for `bash`, you could add + `export COLORTERM=truecolor` - `export COLORTERM=truecolor` + to your `.bashrc` file, then restart your shell. + """ - to your `.bashrc` file, then restart your shell. - """ - ) + image_path = THIS_DIR / "tree.jpg" root = Layout() root.split_row( - Layout(Padding(Markdown(markup, justify="center"), pad=(0, 2))), - Layout(Image.from_file(THIS_DIR / "tree.jpg")), + Layout(pad_markdown(markup)), + Layout( + Panel.fit( + Image.from_file(image_path), + subtitle=str(image_path), + box=HEAVY, + padding=0, + ) + ), ) return root -@deck.example(title="Examples") -def examples(): - # This is an example that shows how to use random.choice from the standard library. - - # The source code is embedded directly into the demo deck file, - # but you could load it from another file if you wanted to. - - import random - - directions = ["North", "South", "East", "West"] - - print("Which way should we go?") - print(random.choice(directions)) - - -@examples.layout -def _(example, triggers): - root = Layout() - - extra = ( - f""" - ## Example Execution is Cached - - Now that you've triggered the slide, {SPIEL} will execute the example once and display the output. - The result is cached, so the example is not executed on every frame, like code in normal slide content - functions is. - - ## Editing Examples - - Examples can be modified during the talk. - Press `e` to open your `$EDITOR` (`{os.getenv("EDITOR", "not set")}`) on the example code. - Save your changes and exit to come back to the presentation with your updated code. - You can then trigger the example again to run it with the new code. - - ## Layout Customization - - You can customize the example slide's content by providing a custom `layout` function. - If you don't, you'll get the default layout, which looks like just the right half of this slide. - """ - if triggers.triggered - else "" - ) - - markup = dedent( +@deck.slide(title="Watch Mode") +def watch() -> RenderableType: + return pad_markdown( f"""\ - ## Examples - - {SPIEL} can display and execute chunks of example code. + ## Developing a Deck - Example slides are driven by the trigger system. - Press `t` to execute the example code and display the output. + {SPIEL} will reload your deck as you edit it to make development easier. - {extra} - """ + The reload is triggered whenever any files under the path passed to the + `--watch` argument of `spiel present` changes. + That path defaults to your current working directory + (right now it is `{Path.cwd()}`). + """ ) - markdown = Markdown(markup, justify="center") - root.split_row( - Layout(Padding(markdown, pad=(0, 2))), - example_panels(example), - ) - return root +def edit_this_file(suspend: Callable[[], Iterable[None]]) -> None: + with suspend(): + edit(filename=__file__) -@deck.slide(title="Live Coding with the REPL") -def repl(): - markup = dedent( +@deck.slide( + title="Bindings", + bindings={ + "e": edit_this_file, + }, +) +def bindings() -> RenderableType: + return pad_markdown( f"""\ - ## Live Coding: REPL + ## Custom Per-Slide Key Bindings - Sometimes a static example, - or even an example that you're editing and running multiple times, - just isn't interactive enough. + Custom keybindings can be added on a per-slide basis using the `bindings` argument of `@slide`, + which takes a mapping of key names to callables to call when that key is pressed. - To provide a more interactive experience, - {SPIEL} lets you open a REPL on any slide by pressing `i`. + ```python + @deck.slide( + title="Bindings", + bindings={{ + "e": edit_this_file, + }}, + ) + ``` - There are two REPLs available by default: the [builtin Python REPL](https://docs.python.org/3/tutorial/interpreter.html#interactive-mode) and {IPYTHON}. - You can change which REPL to use via `Options`, which will be discussed later. + If the callable takes an argument named `suspend`, + it will be passed a function that, when used as a context manager, + suspends {SPIEL} while inside the `with` block. - When you exit the REPL (by pressing `ctrl-d` or running `exit()`), - you'll be back at the same point in your presentation. + A binding has been registered on this slide that suspends {SPIEL} + and opens your `$EDITOR` on this file. + Try pressing `e`! - The state of the REPL is not persistent between invocations - (it will be completely fresh every time you enter it). + Due to reloading, any changes you make will be reflected in the + presentation you're seeing right now. """ ) - return Markdown(markup, justify="center") -@deck.slide(title="Options") -def options_(): - markup = dedent( - f"""\ - ## Options +class DemoRenderFailure(Exception): + pass + + +@deck.slide(title="Render Failure") +def failure() -> RenderableType: + raise DemoRenderFailure( + f"""Woops! - {SPIEL} has a variety of options that can be adjusted at runtime. - For example, - profiling information can be displayed in the footer to help you debug a slide that is rendering too slowly. + An exception was raised while rendering this slide. - To see your current options, press `p`. - From that mode you can edit your options by pressing `e`. + When this happens, Spiel will display the stack trace to help you debug the problem. - Note that your `Options` are *not* reloaded when running with `--watch`. + Deck reloading will still happen, so you can fix the error without stopping Spiel. """ ) - return Markdown(markup, justify="center") diff --git a/spiel/example.py b/spiel/example.py deleted file mode 100644 index 8361bf1..0000000 --- a/spiel/example.py +++ /dev/null @@ -1,114 +0,0 @@ -from __future__ import annotations - -import shlex -import sys -import tempfile -from collections.abc import Callable, Sequence -from dataclasses import dataclass -from pathlib import Path -from subprocess import PIPE, STDOUT, run - -from rich.align import Align -from rich.console import ConsoleRenderable -from rich.layout import Layout -from rich.panel import Panel -from rich.syntax import Syntax -from rich.text import Text - -from .presentable import Presentable -from .triggers import Triggers - - -@dataclass -class CachedExample: - trigger_number: int - input: str - output: str | None - - -def example_panels(example: Example) -> ConsoleRenderable: - root = Layout() - root.split_column( - Layout( - Align.center( - Panel( - example.input, - title=example.name, - title_align="left", - expand=False, - ) - ) - ), - Layout( - Align.center( - Panel( - example.output, - title=f"$ {example.display_command}", - title_align="left", - expand=False, - ) - if example.output is not None - else Text(" ") - ) - ), - ) - return root - - -ExampleLayout = Callable[["Example"], ConsoleRenderable] - - -@dataclass -class Example(Presentable): - source: str = "" - command: Sequence[str] = (sys.executable,) - name: str = "example.py" - language: str = "python" - _layout: ExampleLayout = example_panels - _cache: CachedExample | None = None - - def layout(self, function: ExampleLayout) -> ExampleLayout: - self._layout = function - return function - - @property - def display_command(self) -> str: - return shlex.join([Path(self.command[0]).stem, *self.command[1:], self.name]) - - def execute(self) -> str: - with tempfile.TemporaryDirectory() as tmpdir: - dir = Path(tmpdir) - file = dir / self.name - file.write_text(self.source) - result = run([*self.command, file], stdout=PIPE, stderr=STDOUT, text=True) - return result.stdout - - @property - def input(self) -> Syntax: - input = (self._cache.input or "") if self._cache is not None else "" - return Syntax( - input.strip(), - lexer=self.language, - code_width=max(len(line) for line in input.splitlines()), - ) - - @property - def output(self) -> Text | None: - return ( - Text(self._cache.output) - if (self._cache is not None and self._cache.output is not None) - else None - ) - - def clear_cache(self) -> None: - self._cache = None - - def render(self, triggers: Triggers) -> ConsoleRenderable: - if self._cache is None: - self._cache = CachedExample(len(triggers), self.source, None) - elif self._cache.trigger_number != len(triggers): - self._cache = CachedExample(len(triggers), self.source, self.execute()) - - return self._layout( - self, **self.get_render_kwargs(function=self._layout, triggers=triggers) - ) diff --git a/spiel/exceptions.py b/spiel/exceptions.py index 439f724..76c0016 100644 --- a/spiel/exceptions.py +++ b/spiel/exceptions.py @@ -1,18 +1,9 @@ -class SpielException(Exception): - pass - - -class DuplicateInputHandler(SpielException): - pass +from __future__ import annotations -class UnknownModeError(SpielException): +class SpielException(Exception): pass class NoDeckFound(SpielException): pass - - -class InvalidOptionValue(SpielException): - pass diff --git a/spiel/footer.py b/spiel/footer.py deleted file mode 100644 index 5cf3eb3..0000000 --- a/spiel/footer.py +++ /dev/null @@ -1,81 +0,0 @@ -from dataclasses import dataclass - -from pendulum import now -from rich.console import ConsoleRenderable -from rich.style import Style -from rich.table import Column, Table -from rich.text import Text - -from spiel.modes import Mode -from spiel.rps import RPSCounter -from spiel.state import State -from spiel.utils import drop_nones, filter_join - - -@dataclass -class Footer: - state: State - rps_counter: RPSCounter - - @property - def longest_slide_number_length(self) -> int: - num_slides = len(self.state.deck) - return len(str(num_slides)) - - def __rich__(self) -> ConsoleRenderable: - grid = Table.grid( - *drop_nones( - Column( - style=Style(dim=True), - justify="left", - ), - Column( - style=Style(bold=True), - justify="center", - ), - Column( - style=Style(dim=True), - justify="right", - ) - if self.state.options.profiling - else None, - Column( - style=Style(dim=True), - justify="right", - ), - Column( - style=Style(dim=True), - justify="right", - ), - ), - expand=True, - padding=1, - ) - grid.add_row( - *drop_nones( - Text( - filter_join( - " | ", - [ - self.state.deck.name, - self.state.current_slide.title - if self.state.mode is Mode.SLIDE - else None, - ], - ) - ), - self.state.message, - Text( - f"Render Time: {self.rps_counter.last_elapsed_render_time() * 1e3:>3.3f} ms | {self.rps_counter.renders_per_second():.2f} RPS" - ) - if self.state.options.profiling - else None, - now().format(self.state.options.footer_time_format), - Text( - f"[{self.state.current_slide_idx + 1:>0{self.longest_slide_number_length}d} / {len(self.state.deck)}]" - ) - if self.state.mode is not Mode.HELP - else Text(Mode.HELP.value, style=Style(italic=True)), - ) - ) - return grid diff --git a/spiel/help.py b/spiel/help.py deleted file mode 100644 index 0ebfb5d..0000000 --- a/spiel/help.py +++ /dev/null @@ -1,96 +0,0 @@ -from dataclasses import dataclass - -from click._termui_impl import Editor -from rich.align import Align -from rich.console import Console, ConsoleRenderable, Group -from rich.padding import Padding -from rich.style import Style -from rich.table import Column, Table -from rich.text import Text - -from spiel.constants import PACKAGE_NAME, __python_version__, __rich_version__, __version__ -from spiel.input import INPUT_HANDLER_HELP, SpecialCharacters -from spiel.modes import Mode -from spiel.state import State - - -@dataclass -class Help: - state: State - - def __rich__(self) -> ConsoleRenderable: - action_table = Table( - Column( - "Action", - style=Style(bold=True), - ), - Column( - "Keys", - style=Style(bold=True), - justify="center", - ), - Column( - "Modes", - justify="center", - ), - Column( - "Description", - ), - show_lines=True, - ) - - for info in INPUT_HANDLER_HELP: - action_table.add_row( - Text(info.name), - Text(" ").join( - Text(c.value if isinstance(c, SpecialCharacters) else c) - for c in info.characters - ), - Text(", ").join(Text(mode.value) for mode in info.modes) - if len(info.modes) != len(list(Mode)) - else Text("any", style=Style(italic=True)), - Text.from_markup(info.help), - ) - - return Padding( - Group( - Align.center(action_table), - Align.center(version_details(self.state.console)), - ), - pad=(0, 1), - ) - - -def version_details(console: Console) -> ConsoleRenderable: - table = Table( - Column(justify="right"), - Column(justify="left"), - show_header=False, - box=None, - ) - - table.add_row(f"{PACKAGE_NAME.capitalize()} Version", __version__) - table.add_row("Rich Version", __rich_version__) - table.add_row("Python Version", __python_version__, end_section=True) - - table.add_row( - "Color System", - Text( - console.color_system or "unknown", - style=Style(color="red" if console.color_system != "truecolor" else "green"), - ), - ) - table.add_row( - "Console Dimensions", - Text(f"{console.width} cells wide, {console.height} cells tall"), - end_section=True, - ) - - editor = Editor().get_editor() - table.add_row( - "Editor", - Text(editor), - end_section=True, - ) - - return table diff --git a/spiel/input.py b/spiel/input.py deleted file mode 100644 index ca6d338..0000000 --- a/spiel/input.py +++ /dev/null @@ -1,424 +0,0 @@ -from __future__ import annotations - -import contextlib -import inspect -import string -import sys -import termios -from collections.abc import Callable, Iterable, Iterator, MutableMapping -from contextlib import contextmanager -from copy import deepcopy -from dataclasses import dataclass -from enum import Enum, unique -from io import UnsupportedOperation -from itertools import product -from pathlib import Path -from textwrap import dedent -from typing import Any, NoReturn, TextIO - -import typer -from rich.control import Control -from rich.text import Text -from tomli import TOMLDecodeError -from typer import Exit - -from spiel.constants import EDITOR, PACKAGE_NAME -from spiel.example import Example -from spiel.exceptions import DuplicateInputHandler, InvalidOptionValue -from spiel.modes import Mode -from spiel.options import Options -from spiel.repls import REPLS -from spiel.state import State - -LFLAG = 3 -CC = 6 - - -try: - ORIGINAL_TCGETATTR: list[Any] | None = termios.tcgetattr(sys.stdin) -except (UnsupportedOperation, termios.error): - ORIGINAL_TCGETATTR = None - - -@contextmanager -def no_echo() -> Iterator[None]: - try: - start_no_echo(sys.stdin) - yield - finally: - reset_tty(sys.stdin) - - -def start_no_echo(stream: TextIO) -> None: - if ORIGINAL_TCGETATTR is None: - return - - mode = deepcopy(ORIGINAL_TCGETATTR) - - mode[LFLAG] = mode[LFLAG] & ~(termios.ECHO | termios.ICANON) - mode[CC][termios.VMIN] = 1 - mode[CC][termios.VTIME] = 0 - - termios.tcsetattr(stream.fileno(), termios.TCSADRAIN, mode) - - -def reset_tty(stream: TextIO) -> None: - if ORIGINAL_TCGETATTR is None: - return - - termios.tcsetattr(stream.fileno(), termios.TCSADRAIN, ORIGINAL_TCGETATTR) - - -@unique -class SpecialCharacters(Enum): - Up = "↑" - Down = "↓" - Right = "→" - Left = "←" - CtrlUp = "ctrl-up" - CtrlDown = "ctrl-down" - CtrlRight = "ctrl-right" - CtrlLeft = "ctrl-left" - CtrlK = "ctrl-k" - CtrlC = "ctrl-c" - ShiftUp = "shift-up" - ShiftDown = "shift-down" - ShiftRight = "shift-right" - ShiftLeft = "shift-left" - CtrlShiftUp = "ctrl-shift-up" - CtrlShiftDown = "ctrl-shift-down" - CtrlShiftRight = "ctrl-shift-right" - CtrlShiftLeft = "ctrl-shift-left" - Backspace = "backspace" - CtrlSpace = "ctrl-space" - Enter = "enter" - - -SPECIAL_CHARACTERS = { - "\x1b[A": SpecialCharacters.Up, - "\x1b[B": SpecialCharacters.Down, - "\x1b[C": SpecialCharacters.Right, - "\x1b[D": SpecialCharacters.Left, - "\x0b": SpecialCharacters.CtrlK, - "\x1b[1;5A": SpecialCharacters.CtrlUp, - "\x1b[1;5B": SpecialCharacters.CtrlDown, - "\x1b[1;5C": SpecialCharacters.CtrlRight, - "\x1b[1;5D": SpecialCharacters.CtrlLeft, - "\x1b[1;2A": SpecialCharacters.ShiftUp, - "\x1b[1;2B": SpecialCharacters.ShiftDown, - "\x1b[1;2C": SpecialCharacters.ShiftRight, - "\x1b[1;2D": SpecialCharacters.ShiftLeft, - "\x1b[1;6A": SpecialCharacters.CtrlShiftUp, - "\x1b[1;6B": SpecialCharacters.CtrlShiftDown, - "\x1b[1;6C": SpecialCharacters.CtrlShiftRight, - "\x1b[1;6D": SpecialCharacters.CtrlShiftLeft, - "\x7f": SpecialCharacters.Backspace, - "\x00": SpecialCharacters.CtrlSpace, - "\n": SpecialCharacters.Enter, -} - - -def get_character(stream: TextIO) -> str | SpecialCharacters: - result = stream.read(1) - - if result == "": # this happens when stdin gets closed; equivalent to a quit - raise Exit(code=0) - - if result[-1] == "\x1b": - result += stream.read(2) - - if len(result) != 1 and result[-1] == "1": - result += stream.read(3) - - return SPECIAL_CHARACTERS.get(result, result) - - -Character = str | SpecialCharacters -InputHandler = Callable[[State], NoReturn | None] -InputHandlerKey = tuple[Character, Mode] -InputHandlerDecorator = Callable[[InputHandler], InputHandler] -InputHandlers = MutableMapping[InputHandlerKey, InputHandler] - -INPUT_HANDLERS: InputHandlers = {} # type: ignore[assignment] - - -@dataclass(frozen=True) -class InputHandlerHelpInfo: - name: str - help: str - characters: tuple[Character, ...] - modes: list[Mode] - - -INPUT_HANDLER_HELP: list[InputHandlerHelpInfo] = [] - - -def handle_input( - state: State, - stream: TextIO, - handlers: InputHandlers = INPUT_HANDLERS, -) -> NoReturn | None: - character = get_character(stream) - - try: - handler = handlers[(character, state.mode)] - except KeyError: - return None - - return handler(state) - - -def normalize_help(help: str) -> str: - return dedent(help).replace("\n", " ").strip() - - -def input_handler( - *characters: Character, - modes: Iterable[Mode] | None = None, - handlers: InputHandlers = INPUT_HANDLERS, - name: str | None = None, - help: str, -) -> InputHandlerDecorator: - target_modes = list(modes or list(Mode)) - - def registrar(func: InputHandler) -> InputHandler: - for character, mode in product(characters, target_modes): - key: InputHandlerKey = (character, mode) - # Don't allow duplicate handlers to be registered inside this module, - # but DO let end-users register them. - if key in handlers and inspect.getmodule(func) == inspect.getmodule(input_handler): - raise DuplicateInputHandler( - f"{character} is already registered as an input handler for mode {mode}" - ) - handlers[key] = func - - INPUT_HANDLER_HELP.append( - InputHandlerHelpInfo( - name=name or " ".join(word.capitalize() for word in func.__name__.split("_")), - help=normalize_help(help), - characters=characters, - modes=target_modes, - ) - ) - - return func - - return registrar - - -NOT_HELP = [Mode.SLIDE, Mode.DECK] - - -@input_handler( - "h", - help=f"Enter {Mode.HELP} mode.", -) -def help_mode(state: State) -> None: - state.mode = Mode.HELP - - -@input_handler( - "s", - help=f"Enter {Mode.SLIDE} mode.", -) -def slide_mode(state: State) -> None: - state.mode = Mode.SLIDE - - -@input_handler( - "d", - help=f"Enter {Mode.DECK} mode.", -) -def deck_mode(state: State) -> None: - state.mode = Mode.DECK - - -@input_handler( - "p", - help=f"Enter {Mode.OPTIONS} mode.", -) -def options_mode(state: State) -> None: - state.mode = Mode.OPTIONS - - -@input_handler( - "e", - modes=[Mode.OPTIONS], - help=f"Open your $EDITOR ([bold]{EDITOR}[/bold]) to edit options (as TOML).", -) -def edit_options(state: State) -> None: - with suspend_live(state): - new_toml = state.options.as_toml() - while True: - new_toml = _clean_toml( - typer.edit(text=new_toml, extension=".toml", require_save=False) or "" - ) - try: - state.options = Options.from_toml(new_toml) - return - except TOMLDecodeError as e: - new_toml = f"{new_toml}\n\n# Parse Error: {e}\n" - except InvalidOptionValue as e: - new_toml = f"{new_toml}\n\n# Invalid Option Value: {e}\n" - except Exception as e: - new_toml = f"{new_toml}\n\n# Error: {e}\n" - - -def _clean_toml(s: str) -> str: - return "\n".join(line for line in s.splitlines() if (line and not line.startswith("#"))) - - -@input_handler( - SpecialCharacters.Right, - "f", - modes=NOT_HELP, - help="Move to the next slide.", -) -def next_slide(state: State) -> None: - state.next_slide() - - -@input_handler( - SpecialCharacters.Left, - "b", - modes=NOT_HELP, - help="Move to the previous slide.", -) -def previous_slide(state: State) -> None: - state.previous_slide() - - -@input_handler( - SpecialCharacters.Up, - modes=[Mode.DECK], - help="Move to the previous deck grid row.", -) -def up_grid_row(state: State) -> None: - state.previous_slide(move=state.deck_grid_width) - - -@input_handler( - SpecialCharacters.Down, - modes=[Mode.DECK], - help="Move to the next deck grid row.", -) -def down_grid_row(state: State) -> None: - state.next_slide(move=state.deck_grid_width) - - -@input_handler( - "j", - modes=NOT_HELP, - help="""\ - Press the action key, then a slide number (e.g., [bold]17[/bold]), then press [bold]enter[/bold], to jump to that slide. - If the slide number is unambiguous, the jump will happen without needing to press [bold]enter[/bold] - (e.g., you enter [bold]3[/bold] and there are only [bold]8[/bold] slides). - """, -) -def jump_to_slide(state: State) -> None: - slide_number = "" - - def display() -> None: - state.set_message(Text(f"Jumping to slide {slide_number}...")) - - def jump() -> None: - state.clear_message() - if slide_number == "": - return - state.jump_to_slide(int(slide_number) - 1) - return - - display() - - while True: - char = get_character(sys.stdin) - - if char is SpecialCharacters.Backspace: - slide_number = slide_number[:-1] - elif char is SpecialCharacters.Enter: - return jump() - elif isinstance(char, SpecialCharacters): - continue - elif char in string.digits: - slide_number += char - - display() - - if len(slide_number) == len(str(len(state.deck))): - return jump() - - -@input_handler( - "t", - modes=[Mode.SLIDE], - help="Trigger the slide: marks the current time and make it available to the slide's content rendering function.", -) -def trigger(state: State) -> None: - state.trigger() - - -@input_handler( - "r", - modes=[Mode.SLIDE], - help="Reset the trigger state to as if the slide just started being displayed.", -) -def reset_trigger(state: State) -> None: - state.reset_trigger() - - -@contextlib.contextmanager -def suspend_live(state: State) -> Iterator[None]: - live = state.console._live - - if live is None: - yield - return - - live.stop() - yield - live.start(refresh=True) - - -@input_handler( - "e", - modes=[Mode.SLIDE], - help=f"Open your $EDITOR ([bold]{EDITOR}[/bold]) on the source of an [bold]Example[/bold] slide. If the current slide is not an [bold]Example[/bold], do nothing.", -) -def edit_example(state: State) -> None: - example = state.current_slide - if isinstance(example, Example): - with suspend_live(state): - example.source = ( - typer.edit( - text=example.source, extension=Path(example.name).suffix, require_save=False - ) - or "" - ) - example.clear_cache() - - -@input_handler( - "i", - name="Start REPL", - modes=NOT_HELP, - help=f"Start an [link=https://ipython.readthedocs.io/en/stable/overview.html]IPython REPL[/link].", -) -def open_repl(state: State) -> None: - with suspend_live(state): - reset_tty(sys.stdin) - state.console.print(Control.clear()) - state.console.print(Control.move_to(0, 0)) - - try: - REPLS[state.options.repl]() - finally: - start_no_echo(sys.stdin) - - -@input_handler( - SpecialCharacters.CtrlK, - SpecialCharacters.CtrlC, - help=f"Exit {PACKAGE_NAME}.", -) -def exit(state: State) -> None: - raise Exit(code=0) diff --git a/spiel/load.py b/spiel/load.py deleted file mode 100644 index 350c905..0000000 --- a/spiel/load.py +++ /dev/null @@ -1,78 +0,0 @@ -from __future__ import annotations - -import importlib.util -import sys -from dataclasses import dataclass -from pathlib import Path -from types import TracebackType -from typing import ContextManager, Type - -from watchdog.events import FileSystemEventHandler -from watchdog.observers import Observer -from watchdog.observers.polling import PollingObserver - -from spiel.constants import DECK, OPTIONS -from spiel.deck import Deck -from spiel.exceptions import NoDeckFound -from spiel.options import Options - - -def load_deck_and_options(path: Path) -> tuple[Deck, Options]: - module_name = "__deck" - spec = importlib.util.spec_from_file_location(module_name, path) - - if spec is None: - raise FileNotFoundError( - f"{path.resolve()} does not appear to be an importable Python module." - ) - - module = importlib.util.module_from_spec(spec) - sys.modules[module_name] = module - - loader = spec.loader - assert loader is not None - loader.exec_module(module) - - try: - deck = getattr(module, DECK) - except AttributeError: - raise NoDeckFound(f"The module at {path} does not have an attribute named {DECK}.") - - if not isinstance(deck, Deck): - raise NoDeckFound( - f"The module at {path} has an attribute named {DECK}, but it is a {type(deck).__name__}, not a {Deck.__name__}." - ) - - options = getattr(module, OPTIONS, Options()) - - if not isinstance(options, Options): - options = Options() - - return deck, options - - -@dataclass -class DeckWatcher(ContextManager["DeckWatcher"]): - event_handler: FileSystemEventHandler - path: Path - poll: bool = False - - def __post_init__(self) -> None: - self.observer = (PollingObserver if self.poll else Observer)(timeout=0.1) - - def __enter__(self) -> DeckWatcher: - self.observer.schedule(self.event_handler, str(self.path)) - self.observer.start() - - return self - - def __exit__( - self, - exc_type: Type[BaseException] | None, - exc_value: BaseException | None, - traceback: TracebackType | None, - ) -> bool | None: - self.observer.stop() - self.observer.join() - - return None diff --git a/spiel/modes.py b/spiel/modes.py deleted file mode 100644 index 0325261..0000000 --- a/spiel/modes.py +++ /dev/null @@ -1,9 +0,0 @@ -from enum import Enum, unique - - -@unique -class Mode(str, Enum): - SLIDE = "slide" - DECK = "deck" - HELP = "help" - OPTIONS = "options" diff --git a/spiel/options.py b/spiel/options.py deleted file mode 100644 index 0450195..0000000 --- a/spiel/options.py +++ /dev/null @@ -1,68 +0,0 @@ -from collections.abc import Mapping -from dataclasses import asdict, dataclass, fields -from pathlib import Path -from typing import Any - -import tomli -import tomli_w -from rich.align import Align -from rich.console import ConsoleRenderable -from rich.padding import Padding -from rich.table import Column, Table - -from spiel.constants import PACKAGE_NAME -from spiel.exceptions import InvalidOptionValue -from spiel.repls import REPLS - - -@dataclass -class Options: - repl: str = "ipython" - footer_time_format: str = "YYYY-MM-DD hh:mm A" - profiling: bool = False - - def __post_init__(self) -> None: - if self.repl not in REPLS: - raise InvalidOptionValue(f"repl must be one of: {set(REPLS.keys())}") - - def as_dict(self) -> Mapping[str, Any]: - return asdict(self) - - @classmethod - def from_dict(cls, d: Mapping[str, Any]) -> "Options": - fields_by_name = {field.name: field for field in fields(cls)} - only_valid = {k: fields_by_name[k].type(v) for k, v in d.items() if k in fields_by_name} - return cls(**only_valid) - - def as_toml(self) -> str: - return tomli_w.dumps({PACKAGE_NAME: self.as_dict()}) - - @classmethod - def from_toml(cls, t: str) -> "Options": - return cls.from_dict(tomli.loads(t).get(PACKAGE_NAME, {})) - - def save(self, path: Path) -> Path: - path.write_text(self.as_toml()) - return path - - @classmethod - def load(cls, path: Path) -> "Options": - return cls.from_toml(path.read_text()) - - def __rich__(self) -> ConsoleRenderable: - table = Table( - Column("Option"), - Column("Type", justify="center"), - Column("Value"), - show_lines=True, - ) - - fields_by_name = {field.name: field for field in fields(self)} - - for key, value in self.as_dict().items(): - table.add_row(key, fields_by_name[key].type.__name__, str(value)) - - return Padding( - Align.center(table), - pad=(0, 1), - ) diff --git a/spiel/present.py b/spiel/present.py deleted file mode 100644 index f65e2e9..0000000 --- a/spiel/present.py +++ /dev/null @@ -1,108 +0,0 @@ -import sys -from itertools import islice -from math import ceil -from time import monotonic - -from rich.console import ConsoleRenderable -from rich.layout import Layout -from rich.live import Live -from rich.padding import Padding -from rich.panel import Panel -from rich.style import Style - -from spiel.constants import TARGET_RPS -from spiel.exceptions import UnknownModeError -from spiel.footer import Footer -from spiel.help import Help -from spiel.input import handle_input, no_echo -from spiel.modes import Mode -from spiel.presentable import Presentable -from spiel.rps import RPSCounter -from spiel.state import State -from spiel.triggers import Triggers -from spiel.utils import clamp, filter_join - - -def render_slide(state: State, slide: Presentable) -> ConsoleRenderable: - return Padding( - slide.render(triggers=Triggers(times=tuple(state.trigger_times))), - pad=1, - ) - - -def split_layout_into_deck_grid(root: Layout, state: State) -> Layout: - grid_width = state.deck_grid_width - row_of_current_slide = state.current_slide_idx // grid_width - num_rows = ceil(len(state.deck) / grid_width) - start_row = clamp( - value=row_of_current_slide - (grid_width // 2), - lower=0, - upper=max(num_rows - grid_width, 0), - ) - start_slide_idx = grid_width * start_row - slides = islice(enumerate(state.deck.slides, start=1), start_slide_idx, None) - - rows = [Layout(name=str(r)) for r in range(grid_width)] - cols = [[Layout(name=f"{r}-{c}") for c in range(grid_width)] for r, _ in enumerate(rows)] - - root.split_column(*rows) - for row, layouts in zip(rows, cols): - for layout in layouts: - slide_number, slide = next(slides, (None, None)) - if slide is None: - layout.update("") - else: - is_active_slide = slide is state.current_slide - layout.update( - Panel( - slide.render(triggers=Triggers(times=(monotonic(),))), - title=filter_join(" | ", [slide_number, slide.title]), - border_style=Style( - color="bright_cyan" if is_active_slide else None, - dim=not is_active_slide, - ), - ) - ) - row.split_row(*layouts) - - return root - - -def present_deck(state: State) -> None: - rps_counter = RPSCounter() - footer = Layout(Footer(state, rps_counter), name="footer", size=1) - help = Layout(Help(state), name="help") - - def get_renderable() -> Layout: - current_slide = state.deck[state.current_slide_idx] - - body = Layout(name="body", ratio=1) - if state.mode is Mode.SLIDE: - body.update(render_slide(state, current_slide)) - elif state.mode is Mode.DECK: - split_layout_into_deck_grid(body, state) - elif state.mode is Mode.HELP: - body.update(help) - elif state.mode is Mode.OPTIONS: - body.update(state.options) - else: # pragma: unreachable - raise UnknownModeError(f"Unrecognized mode: {state.mode!r}") - - root = Layout(name="root") - root.split_column(body, footer) - - rps_counter.mark() - - return root - - with no_echo(), Live( - get_renderable=get_renderable, - console=state.console, - screen=True, - auto_refresh=True, - refresh_per_second=TARGET_RPS, - vertical_overflow="visible", - ) as live: - while True: - handle_input(state, sys.stdin) - live.refresh() diff --git a/spiel/presentable.py b/spiel/presentable.py deleted file mode 100644 index 065e02c..0000000 --- a/spiel/presentable.py +++ /dev/null @@ -1,27 +0,0 @@ -import inspect -from collections.abc import Callable, Mapping -from dataclasses import dataclass -from typing import Any - -from rich.console import ConsoleRenderable - -from spiel.triggers import Triggers - - -@dataclass -class Presentable: # Why not an ABC? https://github.com/python/mypy/issues/5374 - title: str = "" - - def render(self, triggers: Triggers) -> ConsoleRenderable: - raise NotImplementedError - - def get_render_kwargs( - self, function: Callable[..., ConsoleRenderable], triggers: Triggers - ) -> Mapping[str, Any]: - signature = inspect.signature(function) - - kwargs: dict[str, Any] = {} - if "triggers" in signature.parameters: - kwargs["triggers"] = triggers - - return kwargs diff --git a/spiel/reloader.py b/spiel/reloader.py deleted file mode 100644 index 4bf478d..0000000 --- a/spiel/reloader.py +++ /dev/null @@ -1,42 +0,0 @@ -import sys -from dataclasses import dataclass, field -from pathlib import Path - -from pendulum import DateTime, now -from rich.control import Control -from rich.style import Style -from rich.text import Text -from watchdog.events import FileSystemEvent, FileSystemEventHandler - -from spiel.load import load_deck_and_options -from spiel.state import State - - -@dataclass -class DeckReloader(FileSystemEventHandler): - state: State - deck_path: Path - last_reload: DateTime = field(default_factory=now) - - def on_modified(self, event: FileSystemEvent) -> None: - self.last_reload = now() - try: - self.state.deck, _ = load_deck_and_options(self.deck_path) - self.state.reset_trigger() - self.state.set_message( - lambda: Text( - f"Reloaded deck from {self.deck_path} {self.last_reload.diff_for_humans(None, False)}", - style=Style(color="bright_green"), - ) - ) - except Exception: - exc_type, exc_obj, exc_tb = sys.exc_info() - self.state.set_message( - lambda: Text( - f"Error: {self.last_reload.diff_for_humans(None, False)}: {exc_obj!r}{Control.bell()}", - style=Style(color="bright_red"), - ) - ) - - def __hash__(self) -> int: - return hash((type(self), id(self))) diff --git a/spiel/renderables/__init__.py b/spiel/renderables/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/spiel/renderables/debug.py b/spiel/renderables/debug.py new file mode 100644 index 0000000..846813c --- /dev/null +++ b/spiel/renderables/debug.py @@ -0,0 +1,46 @@ +from __future__ import annotations + +from rich.console import Console, ConsoleOptions, RenderResult +from rich.style import Style +from rich.table import Column, Table +from rich.text import Text + +from spiel.constants import ( + PACKAGE_NAME, + __python_version__, + __rich_version__, + __textual_version__, + __version__, +) + + +class DebugTable: + def __rich_console__(self, console: Console, options: ConsoleOptions) -> RenderResult: + table = Table( + Column(justify="right"), + Column(justify="left"), + show_header=False, + title="Debug Information", + ) + + table.add_row(f"{PACKAGE_NAME.capitalize()} Version", __version__) + table.add_row("Rich Version", __rich_version__) + table.add_row("Textual Version", __textual_version__) + table.add_row("Python Version", __python_version__) + + table.add_section() + + table.add_row( + "Color System", + Text( + console.color_system or "unknown", + style=Style(color="red" if console.color_system != "truecolor" else "green"), + ), + ) + table.add_row( + "Console Dimensions", + Text(f"{console.width} cells wide, {console.height} cells tall"), + end_section=True, + ) + + yield table diff --git a/spiel/image.py b/spiel/renderables/image.py similarity index 89% rename from spiel/image.py rename to spiel/renderables/image.py index 974e5eb..c68b0c3 100644 --- a/spiel/image.py +++ b/spiel/renderables/image.py @@ -1,11 +1,10 @@ from __future__ import annotations -from collections.abc import Iterable from dataclasses import dataclass from functools import lru_cache from math import floor from pathlib import Path -from typing import NamedTuple +from typing import Iterable, NamedTuple from PIL import Image as Img from PIL.Image import Resampling @@ -38,8 +37,8 @@ def _pixels_to_segments(pixels: Pixels, size: ImageSize) -> list[Segment]: Segment( text="▀", style=Style.from_color( - color=Color.from_rgb(*top_pixel) if top_pixel else None, - bgcolor=Color.from_rgb(*bottom_pixel) if bottom_pixel else None, + color=Color.from_rgb(*top_pixel[:3]) if top_pixel else None, + bgcolor=Color.from_rgb(*bottom_pixel[:3]) if bottom_pixel else None, ), ) ) @@ -50,7 +49,9 @@ def _pixels_to_segments(pixels: Pixels, size: ImageSize) -> list[Segment]: @lru_cache(maxsize=2**4) def _load_image(path: Path) -> Image: - return Img.open(path) + img = Img.open(path) + img.load() + return img @dataclass(frozen=True) diff --git a/spiel/repls.py b/spiel/repls.py deleted file mode 100644 index ed9b45c..0000000 --- a/spiel/repls.py +++ /dev/null @@ -1,30 +0,0 @@ -import code -from collections.abc import Callable, MutableMapping - -import IPython -from traitlets.config import Config - -REPLExecutor = Callable[[], None] - -REPLS: MutableMapping[str, REPLExecutor] = {} - - -def repl(name: str) -> Callable[[REPLExecutor], REPLExecutor]: - def register(executor: REPLExecutor) -> REPLExecutor: - REPLS[name] = executor - return executor - - return register - - -@repl("builtin") -def builtin() -> None: - code.InteractiveConsole().interact() - - -@repl("ipython") -def ipython() -> None: - c = Config() - c.InteractiveShellEmbed.colors = "Neutral" - - IPython.embed(config=c) diff --git a/spiel/rps.py b/spiel/rps.py deleted file mode 100644 index 0a205ea..0000000 --- a/spiel/rps.py +++ /dev/null @@ -1,34 +0,0 @@ -from __future__ import annotations - -from collections import deque -from time import monotonic -from typing import Deque - -from spiel.constants import TARGET_RPS - - -class RPSCounter: - def __init__(self, render_history_length: int | None = None) -> None: - if render_history_length is None: - render_history_length = 3 * TARGET_RPS - - self.render_time_history: Deque[float] = deque(maxlen=render_history_length) - - @property - def num_samples(self) -> int: - return len(self.render_time_history) - - def mark(self) -> None: - self.render_time_history.append(monotonic()) - - def renders_per_second(self) -> float: - if self.num_samples < 2: - return 0 - - return self.num_samples / (self.render_time_history[-1] - self.render_time_history[0]) - - def last_elapsed_render_time(self) -> float: - if self.num_samples < 2: - return 0 - - return self.render_time_history[-1] - self.render_time_history[-2] diff --git a/spiel/screens/__init__.py b/spiel/screens/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/spiel/screens/deck.py b/spiel/screens/deck.py new file mode 100644 index 0000000..de56f77 --- /dev/null +++ b/spiel/screens/deck.py @@ -0,0 +1,24 @@ +from __future__ import annotations + +from textual.app import ComposeResult +from textual.binding import Binding + +from spiel.screens.screen import SpielScreen +from spiel.widgets.footer import Footer +from spiel.widgets.minislides import MiniSlides + + +class DeckScreen(SpielScreen): + BINDINGS = [ + Binding("right", "next_slide", "Go to next slide."), + Binding("left", "prev_slide", "Go to previous slide."), + Binding("down", "next_row", "Go to next row of slides."), + Binding("up", "prev_row", "Go to previous row of slides."), + Binding( + "escape,enter", "switch_screen('slide')", "Go to Slide view with the selected slide." + ), + ] + + def compose(self) -> ComposeResult: + yield MiniSlides() + yield Footer() diff --git a/spiel/screens/help.py b/spiel/screens/help.py new file mode 100644 index 0000000..025d478 --- /dev/null +++ b/spiel/screens/help.py @@ -0,0 +1,40 @@ +from __future__ import annotations + +from textual.app import ComposeResult +from textual.binding import Binding +from textual.containers import Container + +from spiel.screens.screen import SpielScreen +from spiel.widgets.bindings import AppBindingsTableWidget, ScreenBindingsTableWidget +from spiel.widgets.footer import Footer + + +class HelpScreen(SpielScreen): + DEFAULT_CSS = """ + .h-section { + layout: horizontal; + height: auto; + align: center top; + content-align: center top; + } + """ + + BINDINGS = [ + Binding("escape,enter", "pop_screen", "Return to the previous view."), + ] + + def compose(self) -> ComposeResult: + yield Container( + AppBindingsTableWidget(), + classes="h-section", + ) + yield Container( + ScreenBindingsTableWidget(id="slide"), + ScreenBindingsTableWidget(id="deck"), + classes="h-section", + ) + yield Container( + ScreenBindingsTableWidget(id="help"), + classes="h-section", + ) + yield Footer() diff --git a/spiel/screens/screen.py b/spiel/screens/screen.py new file mode 100644 index 0000000..8bf89e9 --- /dev/null +++ b/spiel/screens/screen.py @@ -0,0 +1,10 @@ +from typing import TYPE_CHECKING + +from textual.screen import Screen + +if TYPE_CHECKING: + from spiel.app import SpielApp + + +class SpielScreen(Screen): + app: "SpielApp" diff --git a/spiel/screens/slide.py b/spiel/screens/slide.py new file mode 100644 index 0000000..02fa68e --- /dev/null +++ b/spiel/screens/slide.py @@ -0,0 +1,45 @@ +from __future__ import annotations + +import inspect + +from textual.app import ComposeResult +from textual.binding import Binding +from textual.events import Key + +from spiel.screens.screen import SpielScreen +from spiel.widgets.footer import Footer +from spiel.widgets.slide import SlideWidget + +SUSPEND = "suspend" + + +class SlideScreen(SpielScreen): + DEFAULT_CSS = """ + Screen { + layout: vertical; + } + """ + + BINDINGS = [ + Binding("right", "next_slide", "Go to next slide."), + Binding("left", "prev_slide", "Go to previous slide."), + Binding("t", "trigger", "Trigger the current slide."), + Binding("r", "reset_trigger", "Reset trigger state."), + ] + + def compose(self) -> ComposeResult: + yield SlideWidget() + yield Footer() + + def on_key(self, event: Key) -> None: + slide = self.app.deck[self.app.current_slide_idx] + bind = slide.bindings.get(event.key) + + if callable(bind): + signature = inspect.signature(bind) + + kwargs: dict[str, object] = {} + if SUSPEND in signature.parameters: + kwargs[SUSPEND] = self.app.suspend + + bind(**kwargs) diff --git a/spiel/slide.py b/spiel/slide.py index ffe3d4b..6714be3 100644 --- a/spiel/slide.py +++ b/spiel/slide.py @@ -1,24 +1,30 @@ from __future__ import annotations -from collections.abc import Callable +import inspect from dataclasses import dataclass, field +from typing import Callable, Mapping -from rich.console import ConsoleRenderable +from rich.console import RenderableType from rich.text import Text -from spiel.presentable import Presentable from spiel.triggers import Triggers -MakeRenderable = Callable[..., ConsoleRenderable] -RenderableLike = MakeRenderable | ConsoleRenderable +TRIGGERS = "triggers" + +Content = Callable[..., RenderableType] @dataclass -class Slide(Presentable): - content: RenderableLike = field(default_factory=Text) - - def render(self, triggers: Triggers) -> ConsoleRenderable: - if callable(self.content): - return self.content(**self.get_render_kwargs(function=self.content, triggers=triggers)) - else: - return self.content +class Slide: + title: str = "" + content: Content = lambda: Text() + bindings: Mapping[str, Callable[[], None]] = field(default_factory=dict) + + def render(self, triggers: Triggers) -> RenderableType: + signature = inspect.signature(self.content) + + kwargs: dict[str, object] = {} + if TRIGGERS in signature.parameters: + kwargs[TRIGGERS] = triggers + + return self.content(**kwargs) diff --git a/spiel/spiel.css b/spiel/spiel.css new file mode 100644 index 0000000..e69de29 diff --git a/spiel/state.py b/spiel/state.py deleted file mode 100644 index b62c57a..0000000 --- a/spiel/state.py +++ /dev/null @@ -1,123 +0,0 @@ -from __future__ import annotations - -from collections.abc import Callable -from dataclasses import dataclass, field -from functools import cached_property -from pathlib import Path -from tempfile import TemporaryDirectory -from time import monotonic -from types import TracebackType -from typing import ContextManager, Type - -from rich.console import Console -from rich.style import Style -from rich.text import Text - -from spiel.constants import PACKAGE_NAME -from spiel.deck import Deck -from spiel.load import load_deck_and_options -from spiel.modes import Mode -from spiel.options import Options -from spiel.presentable import Presentable - -TextLike = Text | Callable[[], Text] - - -@dataclass -class State(ContextManager["State"]): - console: Console - deck: Deck - options: Options - _current_slide_idx: int = 0 - _mode: Mode = Mode.SLIDE - _message: TextLike = Text("") - trigger_times: list[float] = field(default_factory=list) - - @classmethod - def from_file(cls, path: Path, console: Console | None = None) -> State: - deck, options = load_deck_and_options(path) - return cls(console=console or Console(), deck=deck, options=options) - - @property - def mode(self) -> Mode: - return self._mode - - @mode.setter - def mode(self, mode: Mode) -> None: - self._mode = mode - self.reset_trigger() - - @property - def current_slide_idx(self) -> int: - return self._current_slide_idx - - @current_slide_idx.setter - def current_slide_idx(self, idx: int) -> None: - self._current_slide_idx = max(0, min(len(self.deck) - 1, idx)) - self.reset_trigger() - - def next_slide(self, move: int = 1) -> None: - if self.current_slide_idx == len(self.deck) - 1: - return - self.current_slide_idx += move - - def previous_slide(self, move: int = 1) -> None: - if self.current_slide_idx == 0: - return - self.current_slide_idx -= move - - def jump_to_slide(self, idx: int) -> None: - self.current_slide_idx = idx - - @property - def current_slide(self) -> Presentable: - return self.deck[self.current_slide_idx] - - @property - def message(self) -> Text: - if callable(self._message): - try: - return self._message() - except Exception: - return Text( - "Internal Error: failed to display message.", - style=Style(color="bright_red"), - ) - else: - return self._message - - def set_message(self, message: TextLike) -> None: - self._message = message - - def clear_message(self) -> None: - self.set_message(Text("")) - - @property - def deck_grid_width(self) -> int: - return max(self.console.size.width // 30, 1) - - def trigger(self) -> None: - self.trigger_times.append(monotonic()) - - def reset_trigger(self) -> None: - self.trigger_times.clear() - self.trigger() - - @cached_property - def _tmp_dir(self) -> TemporaryDirectory[str]: - return TemporaryDirectory(prefix=f"{PACKAGE_NAME}-") - - @cached_property - def tmp_dir(self) -> Path: - return Path(self._tmp_dir.name) - - def __enter__(self) -> State: - return self - - def __exit__( - self, - exctype: Type[BaseException] | None, - excinst: BaseException | None, - exctb: TracebackType | None, - ) -> None: - self._tmp_dir.cleanup() diff --git a/spiel/triggers.py b/spiel/triggers.py index a7bdaec..16aa6fe 100644 --- a/spiel/triggers.py +++ b/spiel/triggers.py @@ -1,13 +1,27 @@ -from collections.abc import Iterator -from dataclasses import dataclass, field +from __future__ import annotations + +from dataclasses import dataclass from functools import cached_property from time import monotonic +from typing import Iterator @dataclass(frozen=True) class Triggers: times: tuple[float, ...] - now: float = field(default_factory=monotonic) + now: float + + def __post_init__(self) -> None: + if not self.times: + raise ValueError("times must not be empty") + + if self.now < self.times[-1]: + raise ValueError(f"now {self.now} must be later than the last time {self.times[-1]}") + + @classmethod + def new(self) -> Triggers: + now = monotonic() + return Triggers(now=now, times=(now,)) def __len__(self) -> int: return len(self.times) diff --git a/spiel/utils.py b/spiel/utils.py index 419a049..7248e6a 100644 --- a/spiel/utils.py +++ b/spiel/utils.py @@ -1,6 +1,6 @@ from __future__ import annotations -from collections.abc import Iterable, Iterator +from collections.abc import Iterable from itertools import zip_longest from typing import TypeVar @@ -11,10 +11,6 @@ def filter_join(separator: str, items: Iterable[object | None]) -> str: return separator.join(map(str, filter(None, items))) -def drop_nones(*items: T | None) -> Iterator[T]: - yield from (item for item in items if item is not None) - - def clamp(value: int, lower: int, upper: int) -> int: if lower > upper: raise ValueError( diff --git a/spiel/widgets/__init__.py b/spiel/widgets/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/spiel/widgets/bindings.py b/spiel/widgets/bindings.py new file mode 100644 index 0000000..fe1fba4 --- /dev/null +++ b/spiel/widgets/bindings.py @@ -0,0 +1,61 @@ +from __future__ import annotations + +from rich.console import RenderableType +from rich.padding import Padding +from rich.table import Column, Table +from rich.text import Text +from textual.binding import Binding + +from spiel.widgets.widget import SpielWidget + + +class AppBindingsTableWidget(SpielWidget): + DEFAULT_CSS = """ + AppBindingsTableWidget { + width: auto; + height: auto; + } + """ + + def render(self) -> RenderableType: + table = Table( + Column("Key", justify="left"), + Column("Description", justify="left"), + title=f"All Views", + ) + + for binding in self.app.BINDINGS: + if isinstance(binding, Binding): + table.add_row(binding.key, binding.description) + else: + raise TypeError(f"{binding} on {self.app} needs to be a {Binding.__name__}") + + return Padding(table, pad=1) + + +class ScreenBindingsTableWidget(SpielWidget): + DEFAULT_CSS = """ + ScreenBindingsTableWidget { + width: auto; + height: auto; + } + """ + + def render(self) -> RenderableType: + if self.id is None: + return Text("") + + screen = self.app.get_screen(self.id) + table = Table( + Column("Key", justify="left"), + Column("Description", justify="left"), + title=f"{self.id.title()} View", + ) + + for binding in screen.BINDINGS: + if isinstance(binding, Binding): + table.add_row(binding.key, binding.description) + else: + raise TypeError(f"{binding} on {screen} needs to be a {Binding.__name__}") + + return Padding(table, pad=1) diff --git a/spiel/widgets/footer.py b/spiel/widgets/footer.py new file mode 100644 index 0000000..758418c --- /dev/null +++ b/spiel/widgets/footer.py @@ -0,0 +1,55 @@ +from __future__ import annotations + +from datetime import datetime + +from rich.console import Group, RenderableType +from rich.rule import Rule +from rich.style import Style +from rich.table import Column, Table +from rich.text import Text +from textual.reactive import reactive + +from spiel.constants import FOOTER_TIME_FORMAT +from spiel.widgets.widget import SpielWidget + + +class Footer(SpielWidget): + DEFAULT_CSS = """ + Footer { + color: $text; + dock: bottom; + height: 2; + } + """ + + now: datetime = reactive(datetime.now) # type: ignore[arg-type,assignment] + + def on_mount(self) -> None: + super().on_mount() + + self.set_interval(1 / 60, self.update_now) + + def update_now(self) -> None: + self.now = datetime.now() + + @property + def longest_slide_number_length(self) -> int: + num_slides = len(self.app.deck) + return len(str(num_slides)) + + def render(self) -> RenderableType: + grid = Table.grid( + Column(style=Style(dim=True), justify="left"), + Column(style=Style(bold=True), justify="center"), + Column(style=Style(dim=True), justify="right"), + expand=True, + padding=1, + ) + grid.add_row( + Text(f"{self.app.deck.name} | {self.app.deck[self.app.current_slide_idx].title}"), + self.app.message, + Text( + f"{self.now.strftime(FOOTER_TIME_FORMAT)} [{self.app.current_slide_idx + 1:>0{self.longest_slide_number_length}d} / {len(self.app.deck)}]" + ), + ) + return Group(Rule(style=Style(dim=True)), grid) diff --git a/spiel/widgets/minislides.py b/spiel/widgets/minislides.py new file mode 100644 index 0000000..8eacea1 --- /dev/null +++ b/spiel/widgets/minislides.py @@ -0,0 +1,70 @@ +from __future__ import annotations + +from itertools import islice +from math import ceil + +from rich.console import RenderableType +from rich.layout import Layout +from rich.panel import Panel +from rich.style import Style +from rich.text import Text + +from spiel.triggers import Triggers +from spiel.utils import clamp +from spiel.widgets.widget import SpielWidget + + +class MiniSlides(SpielWidget): + def render(self) -> RenderableType: + grid_width = self.app.deck_grid_width + row_of_current_slide = self.app.current_slide_idx // grid_width + num_rows = ceil(len(self.app.deck) / grid_width) + start_row = clamp( + value=row_of_current_slide - (grid_width // 2), + lower=0, + upper=max(num_rows - grid_width, 0), + ) + start_slide_idx = grid_width * start_row + slides = islice(enumerate(self.app.deck.slides), start_slide_idx, None) + + rows = [Layout(name=str(r)) for r in range(grid_width)] + cols = [[Layout(name=f"{r}-{c}") for c in range(grid_width)] for r, _ in enumerate(rows)] + + root = Layout() + root.split_column(*rows) + + for row, layouts in zip(rows, cols): + row.split_row(*layouts) + + for layout in layouts: + slide_idx, slide = next(slides, (None, None)) + if slide_idx is None or slide is None: + layout.update("") + else: + is_active_slide = slide_idx == self.app.current_slide_idx + + try: + content = slide.render(triggers=Triggers.new()) + border_style = Style( + color="bright_cyan" if is_active_slide else None, + dim=not is_active_slide, + ) + except Exception as e: + content = Text( + f"Failed to render slide {slide_idx + 1} due to:\n{e}", + style=Style(color="red"), + ) + border_style = Style( + color="red1", + dim=not is_active_slide, + ) + + layout.update( + Panel( + content, + title=" | ".join((str(slide_idx + 1), slide.title)), + border_style=border_style, + ) + ) + + return root diff --git a/spiel/widgets/slide.py b/spiel/widgets/slide.py new file mode 100644 index 0000000..e8250ae --- /dev/null +++ b/spiel/widgets/slide.py @@ -0,0 +1,50 @@ +from __future__ import annotations + +import sys +from time import monotonic + +from rich.box import HEAVY +from rich.console import RenderableType +from rich.panel import Panel +from rich.style import Style +from rich.traceback import Traceback +from textual.reactive import reactive + +import spiel +from spiel.exceptions import SpielException +from spiel.triggers import Triggers +from spiel.widgets.widget import SpielWidget + + +class SlideWidget(SpielWidget): + triggers: Triggers = reactive(Triggers.new) # type: ignore[assignment,arg-type] + + def on_mount(self) -> None: + super().on_mount() + + self.set_interval(1 / 60, self.update_triggers) + + def update_triggers(self) -> None: + self.triggers = Triggers(now=monotonic(), times=self.triggers.times) + + def render(self) -> RenderableType: + try: + self.remove_class("error") + slide = self.app.deck[self.app.current_slide_idx] + return slide.render(triggers=self.triggers) + except Exception: + self.add_class("error") + et, ev, tr = sys.exc_info() + if et is None or ev is None or tr is None: + raise SpielException("Expected to be handling an exception, but wasn't.") + return Panel( + Traceback.from_exception( + exc_type=et, + exc_value=ev, + traceback=tr, + suppress=(spiel,), + ), + title="Slide failed to render", + border_style=Style(bold=True, color="red1"), + box=HEAVY, + ) diff --git a/spiel/widgets/widget.py b/spiel/widgets/widget.py new file mode 100644 index 0000000..3a74922 --- /dev/null +++ b/spiel/widgets/widget.py @@ -0,0 +1,21 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from textual.reactive import watch +from textual.widget import Widget + +if TYPE_CHECKING: + from spiel.app import SpielApp + + +class SpielWidget(Widget): + app: "SpielApp" + + def on_mount(self) -> None: + watch(self.app, "deck", self.r) + watch(self.app, "current_slide_idx", self.r) + watch(self.app, "message", self.r) + + def r(self, _: object) -> None: + self.refresh() diff --git a/tests/conftest.py b/tests/conftest.py index a1d7147..48dd147 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -8,10 +8,8 @@ from hypothesis import settings from rich.console import Console from typer.testing import CliRunner -from spiel import Deck, Options +from spiel import Deck, Slide from spiel.constants import DECK -from spiel.slide import Slide -from spiel.state import State settings.register_profile("default", deadline=None) settings.load_profile(os.getenv("HYPOTHESIS_PROFILE", "default")) @@ -43,18 +41,6 @@ def console(output: StringIO) -> Console: ) -@pytest.fixture -def three_slide_options() -> Options: - return Options() - - -@pytest.fixture -def three_slide_state( - console: Console, three_slide_deck: Deck, three_slide_options: Options -) -> State: - return State(console=console, deck=three_slide_deck, options=three_slide_options) - - @pytest.fixture def empty_deck_source() -> str: return dedent( diff --git a/tests/test_cli.py b/tests/test_cli.py index fcbd9f7..b4758b3 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -7,13 +7,12 @@ import pytest from pytest_mock import MockFixture from typer.testing import CliRunner -from spiel.constants import PACKAGE_NAME, __version__ -from spiel.main import DEMO_SOURCE, app -from spiel.modes import Mode +from spiel.cli import cli +from spiel.constants import DEMO_FILE, PACKAGE_NAME, __version__ def test_help(runner: CliRunner) -> None: - result = runner.invoke(app, ["--help"]) + result = runner.invoke(cli, ["--help"]) assert result.exit_code == 0 @@ -26,50 +25,40 @@ def test_help_via_main() -> None: def test_version(runner: CliRunner) -> None: - result = runner.invoke(app, ["version"]) + result = runner.invoke(cli, ["version"]) assert result.exit_code == 0 assert __version__ in result.stdout def test_plain_version(runner: CliRunner) -> None: - result = runner.invoke(app, ["version", "--plain"]) + result = runner.invoke(cli, ["version", "--plain"]) assert result.exit_code == 0 assert __version__ in result.stdout -def test_clean_keyboard_interrupt(runner: CliRunner, mocker: MockFixture) -> None: - mock = mocker.patch("spiel.main.present_deck", MagicMock(side_effect=KeyboardInterrupt())) - - result = runner.invoke(app, ["present", str(DEMO_SOURCE)]) - - assert mock.called - assert result.exit_code == 0 - - def test_present_deck_on_missing_file(runner: CliRunner, tmp_path: Path) -> None: - result = runner.invoke(app, ["present", str(tmp_path / "missing.py")]) + result = runner.invoke(cli, ["present", str(tmp_path / "missing.py")]) - assert result.exit_code == 1 + assert result.exit_code == 2 -@pytest.mark.parametrize("mode", list(Mode)) -@pytest.mark.parametrize("stdin", ["", "s", "d", "h", "p"]) -def test_display_demo_deck(runner: CliRunner, mode: Mode, stdin: str) -> None: - result = runner.invoke(app, ["present", str(DEMO_SOURCE), "--mode", mode], input=stdin) +@pytest.mark.parametrize("stdin", [""]) +def test_display_demo_deck(runner: CliRunner, stdin: str) -> None: + result = runner.invoke(cli, ["present", str(DEMO_FILE)], input=stdin) assert result.exit_code == 0 def test_demo_display(runner: CliRunner) -> None: - result = runner.invoke(app, ["demo", "present"]) + result = runner.invoke(cli, ["demo", "present"]) assert result.exit_code == 0 def test_demo_source(runner: CliRunner) -> None: - result = runner.invoke(app, ["demo", "source"]) + result = runner.invoke(cli, ["demo", "source"]) assert result.exit_code == 0 @@ -77,8 +66,7 @@ def test_demo_source(runner: CliRunner) -> None: def test_demo_copy_to_new_path(runner: CliRunner, tmp_path: Path) -> None: target = tmp_path / "new" - result = runner.invoke(app, ["demo", "copy", str(target)]) - print(result.stdout) + result = runner.invoke(cli, ["demo", "copy", str(target)]) assert result.exit_code == 0 @@ -87,18 +75,18 @@ def test_demo_copy_to_existing_file(runner: CliRunner, tmp_path: Path) -> None: target = tmp_path / "new" target.touch() - result = runner.invoke(app, ["demo", "copy", str(target)]) + result = runner.invoke(cli, ["demo", "copy", str(target)]) - assert result.exit_code == 2 + assert result.exit_code == 1 def test_demo_copy_to_existing_dir(runner: CliRunner, tmp_path: Path) -> None: target = tmp_path / "new" target.mkdir(parents=True) - result = runner.invoke(app, ["demo", "copy", str(target)]) + result = runner.invoke(cli, ["demo", "copy", str(target)]) - assert result.exit_code == 2 + assert result.exit_code == 1 def test_demo_copy_error_during_copytree( @@ -110,7 +98,7 @@ def test_demo_copy_error_during_copytree( target = tmp_path / "new" - result = runner.invoke(app, ["demo", "copy", str(target)]) + result = runner.invoke(cli, ["demo", "copy", str(target)]) assert mock.called assert "foobar" in result.stdout diff --git a/tests/test_deck.py b/tests/test_deck.py index 5f42b99..205eb2c 100644 --- a/tests/test_deck.py +++ b/tests/test_deck.py @@ -1,8 +1,4 @@ -import pytest - -from spiel import Deck -from spiel.deck import get_function_body -from spiel.slide import Slide +from spiel import Deck, Slide def test_can_add_slide_to_deck(three_slide_deck: Deck) -> None: @@ -22,15 +18,3 @@ def test_iterate_yields_deck_slides(three_slide_deck: Deck) -> None: def test_deck_contains_its_slides(three_slide_deck: Deck) -> None: for slide in three_slide_deck: assert slide in three_slide_deck - - -def test_get_function_body() -> None: - def foo() -> None: # pragma: never runs - ... - - assert get_function_body(foo) == "...\n" - - -def test_get_function_body_raises_on_function_with_no_source() -> None: - with pytest.raises(TypeError): - get_function_body(sorted) diff --git a/tests/test_demo.py b/tests/test_demo.py index b6b7fb5..9c48ddb 100644 --- a/tests/test_demo.py +++ b/tests/test_demo.py @@ -1,20 +1,10 @@ -import pytest +from spiel import Triggers +from spiel.demo.demo import DemoRenderFailure, deck -from spiel.main import DEMO_SOURCE -from spiel.present import render_slide -from spiel.state import State - - -@pytest.fixture -def state() -> State: - return State.from_file(DEMO_SOURCE) - - -def test_can_render_every_demo_slide(state: State) -> None: - deck = state.deck +def test_can_render_every_demo_slide() -> None: for slide in deck: - for _ in range(10): - state.console.print(render_slide(state, slide)) - state.trigger() - state.reset_trigger() + try: + slide.render(triggers=Triggers.new()) + except DemoRenderFailure: + pass diff --git a/tests/test_display.py b/tests/test_display.py deleted file mode 100644 index 16de112..0000000 --- a/tests/test_display.py +++ /dev/null @@ -1,39 +0,0 @@ -from collections.abc import Callable -from io import StringIO - -import pytest -from rich.console import Console -from rich.layout import Layout -from rich.text import Text - -from spiel import Slide -from spiel.present import render_slide, split_layout_into_deck_grid -from spiel.state import State - - -@pytest.mark.parametrize( - "make_slide", - [ - lambda: Slide(content=Text("foobar")), - lambda: Slide(content=lambda: Text("foobar")), - lambda: Slide(content=lambda triggers: Text("foobar")), - ], -) -def test_can_render_slide( - make_slide: Callable[[], Slide], - console: Console, - output: StringIO, - three_slide_state: State, -) -> None: - renderable = render_slide(state=three_slide_state, slide=make_slide()) - - console.print(renderable) - - result = output.getvalue() - - assert "foobar" in result - - -def test_can_render_deck_grid(three_slide_state: State) -> None: - root = Layout() - split_layout_into_deck_grid(root, three_slide_state) diff --git a/tests/test_footer.py b/tests/test_footer.py deleted file mode 100644 index 8bc1ae1..0000000 --- a/tests/test_footer.py +++ /dev/null @@ -1,17 +0,0 @@ -from io import StringIO - -from rich.console import Console - -from spiel.footer import Footer -from spiel.rps import RPSCounter -from spiel.state import State - - -def test_deck_name_in_footer(console: Console, output: StringIO, three_slide_state: State) -> None: - footer = Footer(state=three_slide_state, rps_counter=RPSCounter()) - - console.print(footer) - - result = output.getvalue() - print(repr(result)) - assert three_slide_state.deck.name in result diff --git a/tests/test_help.py b/tests/test_help.py deleted file mode 100644 index 9fb6d78..0000000 --- a/tests/test_help.py +++ /dev/null @@ -1,8 +0,0 @@ -from rich.console import Console - -from spiel.help import Help -from spiel.state import State - - -def test_can_render_help(console: Console, three_slide_state: State) -> None: - console.print(Help(three_slide_state)) diff --git a/tests/test_image.py b/tests/test_image.py index 1c29e99..6180e89 100644 --- a/tests/test_image.py +++ b/tests/test_image.py @@ -2,8 +2,8 @@ import pytest from PIL import Image as Img from rich.console import Console -from spiel.image import Image, ImageSize -from spiel.main import DEMO_DIR +from spiel.constants import DEMO_DIR +from spiel.renderables.image import Image, ImageSize @pytest.fixture diff --git a/tests/test_init.py b/tests/test_init.py index b347752..9516957 100644 --- a/tests/test_init.py +++ b/tests/test_init.py @@ -3,16 +3,15 @@ from pathlib import Path import pytest from typer.testing import CliRunner -from spiel import Options -from spiel.load import load_deck_and_options -from spiel.main import app +from spiel.app import load_deck +from spiel.cli import cli def test_init_cli_command_fails_if_file_exists(runner: CliRunner, tmp_path: Path) -> None: target = tmp_path / "foo_bar.py" target.touch() - result = runner.invoke(app, ["init", str(target)]) + result = runner.invoke(cli, ["init", str(target)]) assert result.exit_code == 1 @@ -20,7 +19,7 @@ def test_init_cli_command_fails_if_file_exists(runner: CliRunner, tmp_path: Path @pytest.fixture def init_file(runner: CliRunner, tmp_path: Path) -> Path: target = tmp_path / "foo_bar.py" - runner.invoke(app, ["init", str(target)]) + runner.invoke(cli, ["init", str(target)]) return target @@ -30,7 +29,6 @@ def test_title_slide_header_injection(init_file: Path) -> None: def test_can_load_init_file(init_file: Path) -> None: - deck, options = load_deck_and_options(init_file) + deck = load_deck(init_file) assert deck.name == "Foo Bar" - assert options == Options() diff --git a/tests/test_input_handlers.py b/tests/test_input_handlers.py deleted file mode 100644 index 40f09a1..0000000 --- a/tests/test_input_handlers.py +++ /dev/null @@ -1,93 +0,0 @@ -import os -import string -from random import sample - -import pytest -from hypothesis import given, settings -from hypothesis import strategies as st -from rich.console import Console -from rich.text import Text -from typer import Exit - -from spiel import Deck, Options, Slide -from spiel.input import ( - INPUT_HANDLERS, - InputHandler, - deck_mode, - edit_example, - edit_options, - exit, - jump_to_slide, - next_slide, - open_repl, - previous_slide, - slide_mode, -) -from spiel.modes import Mode -from spiel.state import State - - -def test_next_slide_goes_to_next_slide(three_slide_state: State) -> None: - next_slide(three_slide_state) - - assert three_slide_state.current_slide is three_slide_state.deck[1] - - -def test_previous_slide_goes_to_previous_slide(three_slide_state: State) -> None: - three_slide_state.jump_to_slide(2) - - previous_slide(three_slide_state) - - assert three_slide_state.current_slide is three_slide_state.deck[1] - - -def test_enter_deck_mode(three_slide_state: State) -> None: - deck_mode(three_slide_state) - - assert three_slide_state.mode is Mode.DECK - - -def test_enter_slide_mode(three_slide_state: State) -> None: - slide_mode(three_slide_state) - - assert three_slide_state.mode is Mode.SLIDE - - -def test_kill(three_slide_state: State) -> None: - with pytest.raises(Exit): - exit(three_slide_state) - - -TESTABLE_INPUT_HANDLERS = list( - set(INPUT_HANDLERS.values()).difference( - { - exit, - jump_to_slide, - open_repl, - edit_options, - edit_example, - } - ) -) - - -@given(input_handlers=st.lists(st.sampled_from(TESTABLE_INPUT_HANDLERS))) -@settings(max_examples=2_000 if os.getenv("CI") else 200) -def test_input_sequences_dont_crash(input_handlers: list[InputHandler]) -> None: - state = State( - console=Console(), - deck=Deck( - name="deck", - slides=[ - Slide( - content=Text(f"This is slide {n + 1}"), - title="".join(sample(string.ascii_letters, 30)), - ) - for n in range(30) - ], - ), - options=Options(), - ) - - for input_handler in input_handlers: - input_handler(state) diff --git a/tests/test_input_handling.py b/tests/test_input_handling.py deleted file mode 100644 index 04464d9..0000000 --- a/tests/test_input_handling.py +++ /dev/null @@ -1,62 +0,0 @@ -from io import StringIO - -import pytest -from pytest_mock import MockFixture -from rich.console import Console - -from spiel.input import SPECIAL_CHARACTERS, SpecialCharacters, get_character, handle_input -from spiel.modes import Mode -from spiel.state import State - - -@pytest.mark.parametrize("input, expected", SPECIAL_CHARACTERS.items()) -def test_get_character_recognizes_special_characters( - input: str, expected: SpecialCharacters -) -> None: - io = StringIO(input) - - assert get_character(io) == expected - - -def test_handle_input_calls_matching_handler_and_returns_its_return_value( - console: Console, three_slide_state: State, mocker: MockFixture -) -> None: - mock = mocker.MagicMock(return_value="foobar") - - result = handle_input( - state=three_slide_state, - stream=StringIO("a"), - handlers={("a", three_slide_state.mode): mock}, - ) - - assert mock.called - assert result == "foobar" - - -def test_handle_input_returns_none_for_missed_input_based_on_character( - console: Console, three_slide_state: State, mocker: MockFixture -) -> None: - mock = mocker.MagicMock(return_value="foobar") - - result = handle_input( - state=three_slide_state, - stream=StringIO("a"), - handlers={("b", three_slide_state.mode): mock}, - ) - - assert result is None - - -def test_handle_input_returns_none_for_missed_input_based_on_mode( - console: Console, three_slide_state: State, mocker: MockFixture -) -> None: - mock = mocker.MagicMock(return_value="foobar") - three_slide_state.mode = Mode.SLIDE - - result = handle_input( - state=three_slide_state, - stream=StringIO("a"), - handlers={("a", Mode.HELP): mock}, - ) - - assert result is None diff --git a/tests/test_load.py b/tests/test_load.py index dfff109..c29856e 100644 --- a/tests/test_load.py +++ b/tests/test_load.py @@ -3,45 +3,27 @@ from textwrap import dedent import pytest -from spiel import Deck, Options +from spiel import Deck +from spiel.app import load_deck from spiel.constants import DECK from spiel.exceptions import NoDeckFound -from spiel.load import load_deck_and_options def test_loading_from_empty_file_fails(empty_file: Path) -> None: with pytest.raises(NoDeckFound, match=DECK): - load_deck_and_options(empty_file) + load_deck(empty_file) def test_loading_from_missing_file_fails(tmp_path: Path) -> None: missing_file = tmp_path / "no-such-path" - with pytest.raises(FileNotFoundError, match="no-such-path"): - load_deck_and_options(missing_file) + with pytest.raises(NoDeckFound, match="no-such-path"): + load_deck(missing_file) def test_can_load_deck_from_valid_file(file_with_empty_deck: Path) -> None: - deck, options = load_deck_and_options(file_with_empty_deck) + deck = load_deck(file_with_empty_deck) assert isinstance(deck, Deck) - assert isinstance(options, Options) - - -def test_can_load_custom_options(empty_file: Path) -> None: - empty_file.write_text( - dedent( - """\ - from spiel import Deck, Options - - deck = Deck(name="deck") - options = Options(footer_time_format="foobar") - """ - ) - ) - - _, options = load_deck_and_options(empty_file) - - assert options.footer_time_format == "foobar" def test_fails_to_load_not_deck(empty_file: Path) -> None: @@ -56,21 +38,4 @@ def test_fails_to_load_not_deck(empty_file: Path) -> None: ) with pytest.raises(NoDeckFound): - load_deck_and_options(empty_file) - - -def test_can_load_not_options(empty_file: Path) -> None: - empty_file.write_text( - dedent( - """\ - from spiel import Deck - - deck = Deck(name="deck") - options = "not an Options" - """ - ) - ) - - _, options = load_deck_and_options(empty_file) - - assert isinstance(options, Options) + load_deck(empty_file) diff --git a/tests/test_options.py b/tests/test_options.py deleted file mode 100644 index a7187a0..0000000 --- a/tests/test_options.py +++ /dev/null @@ -1,53 +0,0 @@ -from typing import Any - -import pytest -from _pytest.tmpdir import TempPathFactory -from hypothesis import given -from hypothesis import strategies as st -from hypothesis.strategies import SearchStrategy -from rich.console import Console - -from spiel import Options -from spiel.exceptions import InvalidOptionValue -from spiel.repls import REPLS - - -def valid_options() -> SearchStrategy[Options]: - return st.builds( - Options, - profiling=st.booleans(), - repl=st.sampled_from(list(REPLS.keys())), - ) - - -@given(o=valid_options()) -def test_round_trip_to_dict(o: Options) -> None: - assert o == Options.from_dict(o.as_dict()) - - -@given(o=valid_options()) -def test_round_trip_to_toml(o: Options) -> None: - assert o == Options.from_toml(o.as_toml()) - - -@given(o=valid_options()) -def test_round_trip_to_file(o: Options, tmp_path_factory: TempPathFactory) -> None: - dir = tmp_path_factory.mktemp(basename="options-roundtrip") - path = dir / "options.toml" - - assert o == Options.load(o.save(path)) - - -def test_can_render_options(console: Console, three_slide_options: Options) -> None: - console.print(three_slide_options) - - -@pytest.mark.parametrize( - "key, value", - [ - ("repl", "foobar"), - ], -) -def test_reject_invalid_option_values(key: str, value: Any) -> None: - with pytest.raises(InvalidOptionValue): - Options(**{key: value}) diff --git a/tests/test_reloader.py b/tests/test_reloader.py deleted file mode 100644 index 30ec661..0000000 --- a/tests/test_reloader.py +++ /dev/null @@ -1,82 +0,0 @@ -from io import StringIO -from pathlib import Path -from textwrap import dedent -from time import sleep - -from rich.console import Console - -from spiel.constants import DECK -from spiel.load import DeckWatcher -from spiel.reloader import DeckReloader -from spiel.state import State - - -def test_reloader_triggers_when_file_modified( - file_with_empty_deck: Path, - console: Console, - output: StringIO, -) -> None: - state = State.from_file(file_with_empty_deck) - reloader = DeckReloader(state=state, deck_path=file_with_empty_deck) - - with DeckWatcher(event_handler=reloader, path=file_with_empty_deck, poll=True): - sleep(0.01) - - file_with_empty_deck.write_text( - dedent( - f"""\ - from spiel import Deck - - {DECK} = Deck(name="modified") - """ - ) - ) - - sleep(0.01) - - for attempt in range(10): - console.print(state.message) - result = output.getvalue() - if state.deck.name == "modified" and "Reloaded deck" in result: - return # test succeeded - sleep(0.1) - - assert ( - False - ), f"Reloader never triggered, current file contents:\n{file_with_empty_deck.read_text()}" # pragma: debugging - - -def test_reloader_captures_error_in_message( - file_with_empty_deck: Path, - console: Console, - output: StringIO, -) -> None: - state = State.from_file(file_with_empty_deck) - reloader = DeckReloader(state=state, deck_path=file_with_empty_deck) - - with DeckWatcher(event_handler=reloader, path=file_with_empty_deck, poll=True): - sleep(0.01) - - file_with_empty_deck.write_text( - dedent( - f"""\ - from spiel import Deck - - {DECK} = Deck(name="modified") - foobar - """ - ) - ) - - sleep(0.01) - - for attempt in range(10): - console.print(state.message) - result = output.getvalue() - if "NameError" in result and "foobar" in result: - return # test succeeded - sleep(0.1) - - assert ( - False - ), f"Reloader never triggered, current file contents:\n{file_with_empty_deck.read_text()}" # pragma: debugging diff --git a/tests/test_rps.py b/tests/test_rps.py deleted file mode 100644 index 8835743..0000000 --- a/tests/test_rps.py +++ /dev/null @@ -1,41 +0,0 @@ -import pytest - -from spiel.rps import RPSCounter - - -@pytest.fixture -def counter() -> RPSCounter: - return RPSCounter() - - -def test_renders_per_second(counter: RPSCounter) -> None: - # 3 renders in 4 seconds - counter.render_time_history.extend([1, 2, 5]) - - assert counter.renders_per_second() == 3 / 4 - - -def test_not_enough_samples_for_renders_per_second(counter: RPSCounter) -> None: - counter.render_time_history.extend([1]) - - # 1 sample isn't enough - - assert counter.renders_per_second() == 0 - - -def test_last_elapsed_render_time(counter: RPSCounter) -> None: - counter.render_time_history.extend([1, 2, 5]) - - assert counter.last_elapsed_render_time() == 3 - - -def test_not_enough_samples_last_elapsed_render_time(counter: RPSCounter) -> None: - counter.render_time_history.extend([1]) - - # 1 sample isn't enough - - assert counter.last_elapsed_render_time() == 0 - - -def test_custom_length() -> None: - assert RPSCounter(render_history_length=5).render_time_history.maxlen == 5 diff --git a/tests/test_slide.py b/tests/test_slide.py new file mode 100644 index 0000000..526c56d --- /dev/null +++ b/tests/test_slide.py @@ -0,0 +1,5 @@ +from spiel import Slide, Triggers + + +def test_can_render_default_slide() -> None: + Slide().render(triggers=Triggers.new()) diff --git a/tests/test_state.py b/tests/test_state.py deleted file mode 100644 index 3d423af..0000000 --- a/tests/test_state.py +++ /dev/null @@ -1,102 +0,0 @@ -import pytest -from rich.console import Console -from rich.style import Style -from rich.text import Text - -from spiel import Deck, Options -from spiel.state import State, TextLike - - -def test_initial_state_has_first_slide_current(three_slide_state: State) -> None: - assert three_slide_state.current_slide is three_slide_state.deck[0] - - -def test_next_from_first_to_second(three_slide_state: State) -> None: - three_slide_state.next_slide() - assert three_slide_state.current_slide is three_slide_state.deck[1] - - -def test_next_from_first_to_third(three_slide_state: State) -> None: - three_slide_state.next_slide(move=2) - assert three_slide_state.current_slide is three_slide_state.deck[2] - - -def test_jump_to_third_slide(three_slide_state: State) -> None: - three_slide_state.jump_to_slide(2) - assert three_slide_state.current_slide is three_slide_state.deck[2] - - -def test_jump_before_beginning_results_in_beginning(three_slide_state: State) -> None: - three_slide_state.jump_to_slide(-5) - assert three_slide_state.current_slide is three_slide_state.deck[0] - - -def test_jump_past_end_results_in_end(three_slide_state: State) -> None: - three_slide_state.jump_to_slide(len(three_slide_state.deck) + 5) - assert three_slide_state.current_slide is three_slide_state.deck[-1] - - -def test_next_from_last_slide_stays_put(three_slide_state: State) -> None: - three_slide_state.jump_to_slide(2) - - three_slide_state.next_slide() - assert three_slide_state.current_slide is three_slide_state.deck[2] - - -def test_previous_from_first_slide_stays_put(three_slide_state: State) -> None: - three_slide_state.previous_slide() - - assert three_slide_state.current_slide is three_slide_state.deck[0] - - -@pytest.mark.parametrize( - "width, expected", - [ - (20, 1), - (30, 1), - (40, 1), - (60, 2), - (80, 2), - (95, 3), - (120, 4), - ], -) -def test_deck_grid_width(width: int, expected: int) -> None: - console = Console(width=width) - state = State(console=console, deck=Deck(name="deck"), options=Options()) - - assert state.deck_grid_width == expected - - -@pytest.mark.parametrize( - "message, expected", - [ - (Text("foobar"), Text("foobar")), - (lambda: Text("wizbang"), Text("wizbang")), - ( - lambda: 1 / 0, - Text( - "Internal Error: failed to display message.", - style=Style(color="bright_red"), - ), - ), - ], -) -def test_set_message(message: TextLike, expected: Text, three_slide_state: State) -> None: - three_slide_state.set_message(message) - - assert three_slide_state.message == expected - - -def test_clear_message(three_slide_state: State) -> None: - three_slide_state.set_message(Text("foobar")) - - three_slide_state.clear_message() - - assert three_slide_state.message == Text("") - - -def test_tmp_dir_lifecycle(three_slide_state: State) -> None: - with three_slide_state: - assert three_slide_state.tmp_dir.exists() - assert not three_slide_state.tmp_dir.exists() diff --git a/tests/test_triggers.py b/tests/test_triggers.py index ee6fc3f..618e2a6 100644 --- a/tests/test_triggers.py +++ b/tests/test_triggers.py @@ -6,9 +6,9 @@ from spiel import Triggers @pytest.mark.parametrize( "triggers, expected", [ - (Triggers(()), 0), - (Triggers((0, 1)), 2), - (Triggers((0, 1, 2)), 3), + (Triggers(times=(0,), now=0), 1), + (Triggers(times=(0, 1), now=1), 2), + (Triggers(times=(0, 1, 2), now=2), 3), ], ) def test_length(triggers: Triggers, expected: int) -> None: @@ -18,9 +18,9 @@ def test_length(triggers: Triggers, expected: int) -> None: @pytest.mark.parametrize( "triggers, idx, expected", [ - (Triggers((0, 1)), 0, 0), - (Triggers((0, 1)), 1, 1), - (Triggers((0, 1, 2)), -1, 2), + (Triggers(times=(0, 1), now=1), 0, 0), + (Triggers(times=(0, 1), now=1), 1, 1), + (Triggers(times=(0, 1, 2), now=2), -1, 2), ], ) def test_getitem(triggers: Triggers, idx: int, expected: int) -> None: @@ -30,9 +30,9 @@ def test_getitem(triggers: Triggers, idx: int, expected: int) -> None: @pytest.mark.parametrize( "triggers", [ - Triggers(()), - Triggers((0, 1)), - Triggers((0, 1, 2)), + Triggers(times=(0,), now=0), + Triggers(times=(0, 1), now=1), + Triggers(times=(0, 1, 2), now=2), ], ) def test_iter(triggers: Triggers) -> None: @@ -42,9 +42,9 @@ def test_iter(triggers: Triggers) -> None: @pytest.mark.parametrize( "triggers, expected", [ - (Triggers((0, 1), now=5), 4), - (Triggers((0, 1), now=1), 0), - (Triggers((0, 1, 2), now=3), 1), + (Triggers(times=(0, 1), now=5), 4), + (Triggers(times=(0, 1), now=1), 0), + (Triggers(times=(0, 1, 2), now=3), 1), ], ) def test_time_since_last_trigger(triggers: Triggers, expected: float) -> None: @@ -54,11 +54,35 @@ def test_time_since_last_trigger(triggers: Triggers, expected: float) -> None: @pytest.mark.parametrize( "triggers, expected", [ - (Triggers((0, 1), now=5), 5), - (Triggers((0, 1), now=1), 1), - (Triggers((0, 1, 2), now=3), 3), - (Triggers((3, 2), now=4), 1), + (Triggers(times=(0, 1), now=5), 5), + (Triggers(times=(0, 1), now=1), 1), + (Triggers(times=(0, 1, 2), now=3), 3), + (Triggers(times=(3, 2), now=4), 1), ], ) def test_time_since_first_trigger(triggers: Triggers, expected: float) -> None: assert triggers.time_since_first_trigger == expected + + +@pytest.mark.parametrize( + "triggers, expected", + [ + (Triggers(times=(0,), now=0), False), + (Triggers(times=(0, 1), now=1), True), + ], +) +def test_triggered(triggers: Triggers, expected: bool) -> None: + assert triggers.triggered is expected + + +@pytest.mark.parametrize( + "times, now", + [ + ((), 0), # no times + ((0,), -1), # now before last time + ((5,), 4), # now before last time + ], +) +def test_invalid_triggers(times: tuple[float], now: float) -> None: + with pytest.raises(ValueError): + Triggers(times=times, now=now)