diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 000000000..3fc786dff --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +@maintainers diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index efa7d4918..21f192233 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,5 +1,5 @@ blank_issues_enabled: false contact_links: - name: Start a Discussion - url: https://github.com/idom-team/idom/discussions + url: https://github.com/reactive-python/reactpy/discussions about: Report issues, request features, ask questions, and share ideas diff --git a/.github/ISSUE_TEMPLATE/issue-form.yml b/.github/ISSUE_TEMPLATE/issue-form.yml index 342316ad2..3cad28d11 100644 --- a/.github/ISSUE_TEMPLATE/issue-form.yml +++ b/.github/ISSUE_TEMPLATE/issue-form.yml @@ -1,6 +1,6 @@ name: Plan a Task description: Create a detailed plan of action (ONLY START AFTER DISCUSSION PLEASE 🙏). -labels: ["flag: triage"] +labels: ["flag-triage"] body: - type: textarea attributes: @@ -14,10 +14,3 @@ body: description: Describe what ought to be done, and why that will address the reasons for action mentioned above. validations: required: false -- type: textarea - attributes: - label: Work Items - description: | - An itemized list or detailed description of the work to be done to based on the proposed actions above. - validations: - required: false diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..a55532008 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,14 @@ +## Description + + + +## Checklist + +Please update this checklist as you complete each item: + +- [ ] Tests have been developed for bug fixes or new functionality. +- [ ] The changelog has been updated, if necessary. +- [ ] Documentation has been updated, if necessary. +- [ ] GitHub Issues closed by this PR have been linked. + +By submitting this pull request I agree that all contributions comply with this project's open source license(s). diff --git a/.github/workflows/.hatch-run.yml b/.github/workflows/.hatch-run.yml new file mode 100644 index 000000000..1b21e4202 --- /dev/null +++ b/.github/workflows/.hatch-run.yml @@ -0,0 +1,59 @@ +name: hatch-run + +on: + workflow_call: + inputs: + job-name: + required: true + type: string + hatch-run: + required: true + type: string + runs-on-array: + required: false + type: string + default: '["ubuntu-latest"]' + python-version-array: + required: false + type: string + default: '["3.x"]' + node-registry-url: + required: false + type: string + default: "" + secrets: + node-auth-token: + required: false + pypi-username: + required: false + pypi-password: + required: false + +jobs: + hatch: + name: ${{ format(inputs.job-name, matrix.python-version, matrix.runs-on) }} + strategy: + matrix: + python-version: ${{ fromJson(inputs.python-version-array) }} + runs-on: ${{ fromJson(inputs.runs-on-array) }} + runs-on: ${{ matrix.runs-on }} + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + with: + node-version: "14.x" + registry-url: ${{ inputs.node-registry-url }} + - name: Pin NPM Version + run: npm install -g npm@8.19.3 + - name: Use Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install Python Dependencies + run: pip install hatch poetry + - name: Run Scripts + env: + NODE_AUTH_TOKEN: ${{ secrets.node-auth-token }} + PYPI_USERNAME: ${{ secrets.pypi-username }} + PYPI_PASSWORD: ${{ secrets.pypi-password }} + run: hatch run ${{ inputs.hatch-run }} diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml new file mode 100644 index 000000000..d370ea129 --- /dev/null +++ b/.github/workflows/check.yml @@ -0,0 +1,48 @@ +name: check + +on: + push: + branches: + - main + pull_request: + branches: + - main + schedule: + - cron: "0 0 * * 0" + +jobs: + test-py-cov: + uses: ./.github/workflows/.hatch-run.yml + with: + job-name: "python-{0}" + hatch-run: "test-py" + lint-py: + uses: ./.github/workflows/.hatch-run.yml + with: + job-name: "python-{0}" + hatch-run: "lint-py" + test-py-matrix: + uses: ./.github/workflows/.hatch-run.yml + with: + job-name: "python-{0} {1}" + hatch-run: "test-py --no-cov" + runs-on-array: '["ubuntu-latest", "macos-latest", "windows-latest"]' + python-version-array: '["3.9", "3.10", "3.11"]' + test-docs: + uses: ./.github/workflows/.hatch-run.yml + with: + job-name: "python-{0}" + hatch-run: "test-docs" + # as of Dec 2023 lxml does have wheels for 3.12 + # https://bugs.launchpad.net/lxml/+bug/2040440 + python-version-array: '["3.11"]' + test-js: + uses: ./.github/workflows/.hatch-run.yml + with: + job-name: "{1}" + hatch-run: "test-js" + lint-js: + uses: ./.github/workflows/.hatch-run.yml + with: + job-name: "{1}" + hatch-run: "lint-js" diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 830092f76..b4f77ee00 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -9,16 +9,16 @@ # the `language` matrix defined below to confirm you have the correct set of # supported CodeQL languages. # -name: "CodeQL" +name: codeql on: push: - branches: [ main ] + branches: [main] pull_request: # The branches below must be a subset of the branches above - branches: [ main ] + branches: [main] schedule: - - cron: '43 3 * * 3' + - cron: "43 3 * * 3" jobs: analyze: @@ -32,40 +32,40 @@ jobs: strategy: fail-fast: false matrix: - language: [ 'javascript', 'python' ] + language: ["javascript", "python"] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] # Learn more: # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed steps: - - name: Checkout repository - uses: actions/checkout@v2 + - name: Checkout repository + uses: actions/checkout@v2 - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v1 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - # queries: ./path/to/local/query, your-org/your-repo/queries@main + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v1 + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v1 - # â„šī¸ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl + # â„šī¸ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl - # âœī¸ If the Autobuild fails above, remove it and uncomment the following three lines - # and modify them (or add more) to build your code if your project - # uses a compiled language + # âœī¸ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language - #- run: | - # make bootstrap - # make release + #- run: | + # make bootstrap + # make release - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index 3de3cff17..7337f505b 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -1,7 +1,7 @@ # This workflows will upload a Python Package using Twine when a release is created # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries -name: Deploy Documentation +name: deploy-docs on: push: diff --git a/.github/workflows/publish-js.yml b/.github/workflows/publish-js.yml deleted file mode 100644 index 8b9f1369e..000000000 --- a/.github/workflows/publish-js.yml +++ /dev/null @@ -1,31 +0,0 @@ -# This workflows will upload a Javscript Package using NPM to npmjs.org when a release is created -# For more information see: https://docs.github.com/en/actions/guides/publishing-nodejs-packages - -name: Publish Javascript - -on: - release: - types: [published] - -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - # Setup .npmrc file to publish to npm - - uses: actions/setup-node@v2 - with: - node-version: "14.x" - registry-url: "https://registry.npmjs.org" - - name: Install Specific NPM Version - run: npm install -g npm@8.3 - - name: Prepare Release - working-directory: ./src/client - run: | - npm install -g npm@7.22.0 - npm install - - name: Publish Release - working-directory: ./src/client - run: npm run publish - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTOMATION_TOKEN }} diff --git a/.github/workflows/publish-py.yml b/.github/workflows/publish-py.yml deleted file mode 100644 index 799f5d060..000000000 --- a/.github/workflows/publish-py.yml +++ /dev/null @@ -1,38 +0,0 @@ -# This workflows will upload a Python Package using Twine when a release is created -# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries - -name: Publish Python - -on: - release: - types: [published] - -jobs: - release-package: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2 - with: - node-version: "14.x" - - name: Install Specific NPM Version - run: npm install -g npm@8.3 - - name: Set up Python - uses: actions/setup-python@v1 - with: - python-version: "3.x" - - name: Install latest NPM - run: | - npm install -g npm@7.22.0 - npm --version - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -r requirements/build-pkg.txt - - name: Build and publish - env: - TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} - TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} - run: | - python -m build --sdist --wheel --outdir dist . - twine upload dist/* diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 000000000..e9271cbd5 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,20 @@ +# This workflows will upload a Javscript Package using NPM to npmjs.org when a release is created +# For more information see: https://docs.github.com/en/actions/guides/publishing-nodejs-packages + +name: publish + +on: + release: + types: [published] + +jobs: + publish: + uses: ./.github/workflows/.hatch-run.yml + with: + job-name: "publish" + hatch-run: "publish" + node-registry-url: "https://registry.npmjs.org" + secrets: + node-auth-token: ${{ secrets.NODE_AUTH_TOKEN }} + pypi-username: ${{ secrets.PYPI_USERNAME }} + pypi-password: ${{ secrets.PYPI_PASSWORD }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index 239281f79..000000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,87 +0,0 @@ -name: Test - -on: - push: - branches: - - main - pull_request: - branches: - - main - schedule: - - cron: "0 0 * * *" - -jobs: - test-python-coverage: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: nanasess/setup-chromedriver@master - - uses: actions/setup-node@v2 - with: - node-version: "14.x" - - name: Install Specific NPM Version - run: npm install -g npm@8.3 - - name: Use Latest Python - uses: actions/setup-python@v2 - with: - python-version: "3.9" - - name: Install Python Dependencies - run: pip install -r requirements/nox-deps.txt - - name: Run Tests - env: { "CI": "true" } - run: nox -s test_python_suite -- --headless --maxfail=3 - test-python-environments: - runs-on: ${{ matrix.os }} - strategy: - matrix: - python-version: ["3.7", "3.8", "3.9"] - os: [ubuntu-latest, macos-latest, windows-latest] - steps: - - uses: actions/checkout@v2 - - uses: nanasess/setup-chromedriver@master - - uses: actions/setup-node@v2 - with: - node-version: "14.x" - - name: Install Specific NPM Version - run: npm install -g npm@8.3 - - name: Use Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - - name: Install Python Dependencies - run: pip install -r requirements/nox-deps.txt - - name: Run Tests - env: { "CI": "true" } - run: nox -s test_python --stop-on-first-error -- --headless --maxfail=3 --no-cov - test-docs: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2 - with: - node-version: "14.x" - - name: Install Specific NPM Version - run: npm install -g npm@8.3 - - name: Use Latest Python - uses: actions/setup-python@v2 - with: - python-version: "3.9" - - name: Install Python Dependencies - run: pip install -r requirements/nox-deps.txt - - name: Run Tests - env: { "CI": "true" } - run: nox -s test_docs - test-javascript: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2 - with: - node-version: "14.x" - - name: Install Specific NPM Version - run: npm install -g npm@8.3 - - name: Install Python Dependencies - run: pip install -r requirements/nox-deps.txt - - name: Run Tests - env: { "CI": "true" } - run: nox -s test_javascript diff --git a/.gitignore b/.gitignore index 75485ddbf..946bff43f 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ .jupyter # --- Python --- +.hatch .venv venv MANIFEST @@ -37,6 +38,5 @@ pip-wheel-metadata .vscode # --- JS --- - node_modules -src/idom/client + diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f602709ee..0383cbb1d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,18 +1,34 @@ repos: - - repo: https://github.com/ambv/black - rev: 22.1.0 + - repo: local hooks: - - id: black - - repo: https://github.com/PyCQA/flake8 - rev: 3.7.9 + - id: lint-py-fix + name: Fix Python Lint + entry: hatch run lint-py + language: system + args: [--fix] + pass_filenames: false + files: \.py$ + - repo: local hooks: - - id: flake8 - - repo: https://github.com/pycqa/isort - rev: 5.6.3 + - id: lint-js-fix + name: Fix JS Lint + entry: hatch run lint-js --fix + language: system + pass_filenames: false + files: \.(js|jsx|ts|tsx)$ + - repo: local hooks: - - id: isort - name: isort - - repo: https://github.com/pre-commit/mirrors-prettier - rev: "v2.5.1" + - id: lint-py-check + name: Check Python Lint + entry: hatch run lint-py + language: system + pass_filenames: false + files: \.py$ + - repo: local hooks: - - id: prettier + - id: lint-js-check + name: Check JS Lint + entry: hatch run lint-py + language: system + pass_filenames: false + files: \.(js|jsx|ts|tsx)$ diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 000000000..7471953dc --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,12 @@ +{ + "recommendations": [ + "wholroyd.jinja", + "esbenp.prettier-vscode", + "ms-python.vscode-pylance", + "ms-python.python", + "charliermarsh.ruff", + "dbaeumer.vscode-eslint", + "ms-python.black-formatter", + "ms-python.mypy-type-checker" + ] +} diff --git a/LICENSE b/LICENSE index 5caf76c93..060079c01 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2019-2022 Ryan S. Morshead +Copyright (c) 2019 Ryan S. Morshead Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 39ea6e477..000000000 --- a/MANIFEST.in +++ /dev/null @@ -1,3 +0,0 @@ -recursive-include src/idom/client * -recursive-include src/idom/web/templates * -include src/idom/py.typed diff --git a/README.md b/README.md index c4a3de93c..83241e19a 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,72 @@ -# IDOM · [![Tests](https://github.com/idom-team/idom/workflows/Test/badge.svg?event=push)](https://github.com/idom-team/idom/actions?query=workflow%3ATest) [![PyPI Version](https://img.shields.io/pypi/v/idom.svg)](https://pypi.python.org/pypi/idom) [![License](https://img.shields.io/badge/License-MIT-purple.svg)](https://github.com/idom-team/idom/blob/main/LICENSE) +# ReactPy -IDOM is a Python web framework for building **interactive websites without needing a -single line of Javascript**. These sites are built from small elements of functionality -like buttons text and images. IDOM allows you to combine these elements into reusable -"components" that can be composed together to create complex views. +

+ + + + + + + + + + + + + + + +

-Ecosystem independence is also a core feature of IDOM. It can be added to existing -applications built on a variety of sync and async web servers, as well as integrated -with other frameworks like Django, Jupyter, and Plotly Dash. Not only does this mean -you're free to choose what technology stack to run on, but on top of that, you can run -the exact same components wherever you need them. For example, you can take a component -originally developed in a Jupyter Notebook and embed it in your production application -without changing anything about the component itself. + +[ReactPy](https://reactpy.dev/) is a library for building user interfaces in Python without Javascript. ReactPy interfaces are made from components that look and behave similar to those found in [ReactJS](https://reactjs.org/). Designed with simplicity in mind, ReactPy can be used by those without web development experience while also being powerful enough to grow with your ambitions. + + + + + + + + + + + + + + + + +
Supported Backends
Built-inExternal
+ + Flask, FastAPI, Sanic, Tornado + + + Django, + Jupyter, + Plotly-Dash +
# At a Glance -To get a rough idea of how to write apps in IDOM, take a look at the tiny "hello -world" application below: +To get a rough idea of how to write apps in ReactPy, take a look at this tiny _Hello World_ application. ```python -from idom import component, html, run +from reactpy import component, html, run @component -def App(): +def hello_world(): return html.h1("Hello, World!") -run(App) +run(hello_world) ``` # Resources -Follow the links below to find out more about this project +Follow the links below to find out more about this project. -- [Try it Now](https://mybinder.org/v2/gh/idom-team/idom-jupyter/main?urlpath=lab/tree/notebooks/introduction.ipynb) - check out IDOM in a Jupyter Notebook. -- [Documentation](https://idom-docs.herokuapp.com/) - learn how to install, run, and use IDOM. -- [Community Forum](https://github.com/idom-team/idom/discussions) - ask questions, share ideas, and show off projects. -- [Contributor Guide](https://idom-docs.herokuapp.com/docs/developing-idom/contributor-guide.html) - see how you can help develop this project. -- [Code of Conduct](https://github.com/idom-team/idom/blob/main/CODE_OF_CONDUCT.md) - standards for interacting with this community. +- [Try ReactPy (Jupyter Notebook)](https://mybinder.org/v2/gh/reactive-python/reactpy-jupyter/main?urlpath=lab/tree/notebooks/introduction.ipynb) +- [Documentation](https://reactpy.dev/) +- [GitHub Discussions](https://github.com/reactive-python/reactpy/discussions) +- [Discord](https://discord.gg/uNb5P4hA9X) +- [Contributor Guide](https://reactpy.dev/docs/about/contributor-guide.html) +- [Code of Conduct](https://github.com/reactive-python/reactpy/blob/main/CODE_OF_CONDUCT.md) diff --git a/VERSION b/VERSION deleted file mode 100644 index 93d4c1ef0..000000000 --- a/VERSION +++ /dev/null @@ -1 +0,0 @@ -0.36.0 diff --git a/branding/ico/idom-logo-black-square-small.ico b/branding/ico/idom-logo-black-square-small.ico deleted file mode 100644 index 7005d2938..000000000 Binary files a/branding/ico/idom-logo-black-square-small.ico and /dev/null differ diff --git a/branding/ico/idom-logo-white-square-small.ico b/branding/ico/idom-logo-white-square-small.ico deleted file mode 100644 index 6a5c139fb..000000000 Binary files a/branding/ico/idom-logo-white-square-small.ico and /dev/null differ diff --git a/branding/ico/reactpy-logo.ico b/branding/ico/reactpy-logo.ico new file mode 100644 index 000000000..62be5f5ba Binary files /dev/null and b/branding/ico/reactpy-logo.ico differ diff --git a/branding/png/idom-logo-black-square-small.png b/branding/png/idom-logo-black-square-small.png deleted file mode 100644 index 7971eaa68..000000000 Binary files a/branding/png/idom-logo-black-square-small.png and /dev/null differ diff --git a/branding/png/idom-logo-black-square.png b/branding/png/idom-logo-black-square.png deleted file mode 100644 index 049f6fd55..000000000 Binary files a/branding/png/idom-logo-black-square.png and /dev/null differ diff --git a/branding/png/idom-logo-black.png b/branding/png/idom-logo-black.png deleted file mode 100644 index 9a78e48fc..000000000 Binary files a/branding/png/idom-logo-black.png and /dev/null differ diff --git a/branding/png/idom-logo-white-square-small.png b/branding/png/idom-logo-white-square-small.png deleted file mode 100644 index 957c58bfa..000000000 Binary files a/branding/png/idom-logo-white-square-small.png and /dev/null differ diff --git a/branding/png/idom-logo-white-square.png b/branding/png/idom-logo-white-square.png deleted file mode 100644 index f4dbb4db1..000000000 Binary files a/branding/png/idom-logo-white-square.png and /dev/null differ diff --git a/branding/png/idom-logo-white.png b/branding/png/idom-logo-white.png deleted file mode 100644 index 2ef265474..000000000 Binary files a/branding/png/idom-logo-white.png and /dev/null differ diff --git a/branding/png/reactpy-logo-landscape-padded.png b/branding/png/reactpy-logo-landscape-padded.png new file mode 100644 index 000000000..b2ba7a6d4 Binary files /dev/null and b/branding/png/reactpy-logo-landscape-padded.png differ diff --git a/branding/png/reactpy-logo-landscape.png b/branding/png/reactpy-logo-landscape.png new file mode 100644 index 000000000..1152fde84 Binary files /dev/null and b/branding/png/reactpy-logo-landscape.png differ diff --git a/branding/png/reactpy-logo-padded.png b/branding/png/reactpy-logo-padded.png new file mode 100644 index 000000000..46b8b8a07 Binary files /dev/null and b/branding/png/reactpy-logo-padded.png differ diff --git a/branding/svg/idom-logo-backed-square.svg b/branding/svg/idom-logo-backed-square.svg deleted file mode 100644 index 9e34b6808..000000000 --- a/branding/svg/idom-logo-backed-square.svg +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - diff --git a/branding/svg/idom-logo-backed.svg b/branding/svg/idom-logo-backed.svg deleted file mode 100644 index 290fc7ed0..000000000 --- a/branding/svg/idom-logo-backed.svg +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - diff --git a/branding/svg/idom-logo-square-small.svg b/branding/svg/idom-logo-square-small.svg deleted file mode 100644 index eb36c7b11..000000000 --- a/branding/svg/idom-logo-square-small.svg +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - diff --git a/branding/svg/idom-logo-square.svg b/branding/svg/idom-logo-square.svg deleted file mode 100644 index 354f961e4..000000000 --- a/branding/svg/idom-logo-square.svg +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - diff --git a/branding/svg/idom-logo.svg b/branding/svg/idom-logo.svg deleted file mode 100644 index ffcee8d96..000000000 --- a/branding/svg/idom-logo.svg +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - diff --git a/branding/svg/reactpy-logo-landscape-padded.svg b/branding/svg/reactpy-logo-landscape-padded.svg new file mode 100644 index 000000000..f9889b5fb --- /dev/null +++ b/branding/svg/reactpy-logo-landscape-padded.svg @@ -0,0 +1,174 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ReactPy + + diff --git a/branding/svg/reactpy-logo-landscape.svg b/branding/svg/reactpy-logo-landscape.svg new file mode 100644 index 000000000..d4997a83d --- /dev/null +++ b/branding/svg/reactpy-logo-landscape.svg @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ReactPy + diff --git a/branding/svg/reactpy-logo-square-padded.svg b/branding/svg/reactpy-logo-square-padded.svg new file mode 100644 index 000000000..601707bce --- /dev/null +++ b/branding/svg/reactpy-logo-square-padded.svg @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/branding/svg/reactpy-logo-square.svg b/branding/svg/reactpy-logo-square.svg new file mode 100644 index 000000000..312fb87a5 --- /dev/null +++ b/branding/svg/reactpy-logo-square.svg @@ -0,0 +1,160 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/Dockerfile b/docs/Dockerfile index 0a6cc87f2..7a5d49b7b 100644 --- a/docs/Dockerfile +++ b/docs/Dockerfile @@ -1,50 +1,42 @@ FROM python:3.9 - WORKDIR /app/ +RUN apt-get update + # Install NodeJS # -------------- -RUN curl -sL https://deb.nodesource.com/setup_14.x | bash - -RUN apt-get install -yq nodejs build-essential -RUN npm install -g npm@7.13.0 +RUN curl -SLO https://deb.nodesource.com/nsolid_setup_deb.sh +RUN chmod 500 nsolid_setup_deb.sh +RUN ./nsolid_setup_deb.sh 20 +RUN apt-get install nodejs -y + +# Install Poetry +# -------------- +RUN pip install poetry -# Create Python Venv -# ------------------ +# Create/Activate Python Venv +# --------------------------- ENV VIRTUAL_ENV=/opt/venv RUN python3 -m venv $VIRTUAL_ENV ENV PATH="$VIRTUAL_ENV/bin:$PATH" RUN pip install --upgrade pip -# Install IDOM -# ------------ -ADD requirements ./requirements -ADD src ./src -ADD scripts ./scripts -ADD setup.py ./ -ADD setup.cfg ./ -ADD MANIFEST.in ./ -ADD README.md ./ - -RUN pip install -e .[all] - -# Add License -# ----------- -Add LICENSE /app/ - -# Build the Docs -# -------------- -ADD docs/__init__.py ./docs/ -ADD docs/app.py ./docs/ -ADD docs/examples.py ./docs/ -ADD docs/source ./docs/source -ADD branding ./branding +# Copy Files +# ---------- +COPY LICENSE ./ +COPY src ./src +COPY docs ./docs +COPY branding ./branding -RUN pip install -r requirements/build-docs.txt -RUN sphinx-build -W -b html docs/source docs/build +# Install and Build Docs +# ---------------------- +WORKDIR /app/docs +RUN poetry install +RUN sphinx-build -v -W -b html source build # Define Entrypoint # ----------------- ENV PORT 5000 -ENV IDOM_DEBUG_MODE=1 -ENV IDOM_CHECK_VDOM_SPEC=0 -CMD python scripts/run_docs.py +ENV REACTPY_DEBUG_MODE=1 +ENV REACTPY_CHECK_VDOM_SPEC=0 +CMD python main.py diff --git a/docs/Makefile b/docs/Makefile deleted file mode 100644 index 69fe55ecf..000000000 --- a/docs/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -SOURCEDIR = source -BUILDDIR = build - -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: help Makefile - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) \ No newline at end of file diff --git a/docs/README.md b/docs/README.md index 067adc0b8..1360bc825 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,14 +1,16 @@ -# IDOM's Documentation +# ReactPy's Documentation -We provide two main ways to run the docs. Both use `nox` which has a `noxfile.py` at the -root of the repository. Running the docs with `nox -s docs` will start up an iteractive -session which will rebuild the docs any time a file is modified. Using `nox -s -docs_in_docker` on the other hand, will build a docker image and run the docs from -there. The latter command mimics how the docs will behave in production. As such, if any -changes to the core of the documentation are made (i.e. to non-`*.rst` files), then you -should run a manual test of the documentation using the `docs_in_docker` session. +We provide two main ways to run the docs. Both use +[`nox`](https://pypi.org/project/nox/): -If you with to build and run the docs by hand you need to perform two commands, each +- `nox -s docs` - displays the docs and rebuilds when files are modified. +- `nox -s docs-in-docker` - builds a docker image and runs the docs from there. + +If any changes to the core of the documentation are made (i.e. to non-`*.rst` files), +then you should run a manual test of the documentation using the `docs_in_docker` +session. + +If you wish to build and run the docs by hand you need to perform two commands, each being run from the root of the repository: - `sphinx-build -b html docs/source docs/build` diff --git a/docs/__init__.py b/docs/__init__.py deleted file mode 100644 index a6074c96f..000000000 --- a/docs/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from .app import run - - -__all__ = ["run"] diff --git a/docs/app.py b/docs/app.py deleted file mode 100644 index b065dd09e..000000000 --- a/docs/app.py +++ /dev/null @@ -1,60 +0,0 @@ -import os -from logging import getLogger -from pathlib import Path - -from sanic import Sanic, response - -from idom.server.sanic import PerClientStateServer -from idom.widgets import multiview - -from .examples import load_examples - - -HERE = Path(__file__).parent -IDOM_MODEL_SERVER_URL_PREFIX = "/_idom" - -logger = getLogger(__name__) - - -IDOM_MODEL_SERVER_URL_PREFIX = "/_idom" - - -def run(): - app = make_app() - - PerClientStateServer( - make_examples_component(), - { - "redirect_root_to_index": False, - "url_prefix": IDOM_MODEL_SERVER_URL_PREFIX, - }, - app, - ) - - app.run( - host="0.0.0.0", - port=int(os.environ.get("PORT", 5000)), - workers=int(os.environ.get("WEB_CONCURRENCY", 1)), - debug=bool(int(os.environ.get("DEBUG", "0"))), - ) - - -def make_app(): - app = Sanic(__name__) - - app.static("/docs", str(HERE / "build")) - - @app.route("/") - async def forward_to_index(request): - return response.redirect("/docs/index.html") - - return app - - -def make_examples_component(): - mount, component = multiview() - - for example_name, example_component in load_examples(): - mount.add(example_name, example_component) - - return component diff --git a/scripts/common/__init__.py b/docs/docs_app/__init__.py similarity index 100% rename from scripts/common/__init__.py rename to docs/docs_app/__init__.py diff --git a/docs/docs_app/app.py b/docs/docs_app/app.py new file mode 100644 index 000000000..3fe4669ff --- /dev/null +++ b/docs/docs_app/app.py @@ -0,0 +1,59 @@ +from logging import getLogger +from pathlib import Path + +from sanic import Sanic, response + +from docs_app.examples import get_normalized_example_name, load_examples +from reactpy import component +from reactpy.backend.sanic import Options, configure, use_request +from reactpy.core.types import ComponentConstructor + +THIS_DIR = Path(__file__).parent +DOCS_DIR = THIS_DIR.parent +DOCS_BUILD_DIR = DOCS_DIR / "build" + +REACTPY_MODEL_SERVER_URL_PREFIX = "/_reactpy" + +logger = getLogger(__name__) + + +REACTPY_MODEL_SERVER_URL_PREFIX = "/_reactpy" + + +@component +def Example(): + raw_view_id = use_request().get_args().get("view_id") + view_id = get_normalized_example_name(raw_view_id) + return _get_examples()[view_id]() + + +def _get_examples(): + if not _EXAMPLES: + _EXAMPLES.update(load_examples()) + return _EXAMPLES + + +def reload_examples(): + _EXAMPLES.clear() + _EXAMPLES.update(load_examples()) + + +_EXAMPLES: dict[str, ComponentConstructor] = {} + + +def make_app(name: str): + app = Sanic(name) + + app.static("/docs", str(DOCS_BUILD_DIR)) + + @app.route("/") + async def forward_to_index(_): + return response.redirect("/docs/index.html") + + configure( + app, + Example, + Options(url_prefix=REACTPY_MODEL_SERVER_URL_PREFIX), + ) + + return app diff --git a/docs/docs_app/dev.py b/docs/docs_app/dev.py new file mode 100644 index 000000000..5d661924d --- /dev/null +++ b/docs/docs_app/dev.py @@ -0,0 +1,104 @@ +import asyncio +import os +import threading +import time +import webbrowser + +from sphinx_autobuild.cli import ( + Server, + _get_build_args, + _get_ignore_handler, + find_free_port, + get_builder, + get_parser, +) + +from docs_app.app import make_app, reload_examples +from reactpy.backend.sanic import serve_development_app +from reactpy.testing import clear_reactpy_web_modules_dir + +# these environment variable are used in custom Sphinx extensions +os.environ["REACTPY_DOC_EXAMPLE_SERVER_HOST"] = "127.0.0.1:5555" +os.environ["REACTPY_DOC_STATIC_SERVER_HOST"] = "" + + +def wrap_builder(old_builder): + # This is the bit that we're injecting to get the example components to reload too + + app = make_app("docs_dev_app") + + thread_started = threading.Event() + + def run_in_thread(): + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + + server_started = asyncio.Event() + + async def set_thread_event_when_started(): + await server_started.wait() + thread_started.set() + + loop.run_until_complete( + asyncio.gather( + serve_development_app(app, "127.0.0.1", 5555, server_started), + set_thread_event_when_started(), + ) + ) + + threading.Thread(target=run_in_thread, daemon=True).start() + + thread_started.wait() + + def new_builder(): + clear_reactpy_web_modules_dir() + reload_examples() + old_builder() + + return new_builder + + +def main(): + # Mostly copied from https://github.com/executablebooks/sphinx-autobuild/blob/b54fb08afc5112bfcda1d844a700c5a20cd6ba5e/src/sphinx_autobuild/cli.py + parser = get_parser() + args = parser.parse_args() + + srcdir = os.path.realpath(args.sourcedir) + outdir = os.path.realpath(args.outdir) + if not os.path.exists(outdir): + os.makedirs(outdir) + + server = Server() + + build_args, pre_build_commands = _get_build_args(args) + builder = wrap_builder( + get_builder( + server.watcher, + build_args, + host=args.host, + port=args.port, + pre_build_commands=pre_build_commands, + ) + ) + + ignore_handler = _get_ignore_handler(args) + server.watch(srcdir, builder, ignore=ignore_handler) + for dirpath in args.additional_watched_dirs: + real_dirpath = os.path.realpath(dirpath) + server.watch(real_dirpath, builder, ignore=ignore_handler) + server.watch(outdir, ignore=ignore_handler) + + if not args.no_initial_build: + builder() + + # Find the free port + portn = args.port or find_free_port() + if args.openbrowser is True: + + def opener(): + time.sleep(args.delay) + webbrowser.open(f"http://{args.host}:{args.port}/index.html") + + threading.Thread(target=opener, daemon=True).start() + + server.serve(port=portn, host=args.host, root=outdir) diff --git a/docs/examples.py b/docs/docs_app/examples.py similarity index 82% rename from docs/examples.py rename to docs/docs_app/examples.py index aee0ef2d5..a71a0b111 100644 --- a/docs/examples.py +++ b/docs/docs_app/examples.py @@ -1,18 +1,18 @@ from __future__ import annotations +from collections.abc import Iterator from io import StringIO from pathlib import Path from traceback import format_exc -from typing import Callable, Iterator - -import idom -from idom import ComponentType +from typing import Callable +import reactpy +from reactpy.types import ComponentType HERE = Path(__file__) -SOURCE_DIR = HERE.parent / "source" +SOURCE_DIR = HERE.parent.parent / "source" CONF_FILE = SOURCE_DIR / "conf.py" -RUN_IDOM = idom.run +RUN_ReactPy = reactpy.run def load_examples() -> Iterator[tuple[str, Callable[[], ComponentType]]]: @@ -23,7 +23,7 @@ def load_examples() -> Iterator[tuple[str, Callable[[], ComponentType]]]: def all_example_names() -> set[str]: names = set() for file in _iter_example_files(SOURCE_DIR): - path = file.parent if file.name == "app.py" else file + path = file.parent if file.name == "main.py" else file names.add("/".join(path.relative_to(SOURCE_DIR).with_suffix("").parts)) return names @@ -48,7 +48,7 @@ def get_main_example_file_by_name( ) -> Path: path = _get_root_example_path_by_name(name, relative_to) if path.is_dir(): - return path / "app.py" + return path / "main.py" else: return path.with_suffix(".py") @@ -95,7 +95,7 @@ def capture_component(component_constructor): nonlocal captured_component_constructor captured_component_constructor = component_constructor - idom.run = capture_component + reactpy.run = capture_component try: code = compile(file.read_text(), str(file), "exec") exec( @@ -109,20 +109,24 @@ def capture_component(component_constructor): except Exception: return _make_error_display(format_exc()) finally: - idom.run = RUN_IDOM + reactpy.run = RUN_ReactPy if captured_component_constructor is None: return _make_example_did_not_run(str(file)) - @idom.component + @reactpy.component def Wrapper(): - return idom.html.div(captured_component_constructor(), PrintView()) + return reactpy.html.div(captured_component_constructor(), PrintView()) - @idom.component + @reactpy.component def PrintView(): - text, set_text = idom.hooks.use_state(print_buffer.getvalue()) + text, set_text = reactpy.hooks.use_state(print_buffer.getvalue()) print_buffer.set_callback(set_text) - return idom.html.pre({"class": "printout"}, text) if text else idom.html.div() + return ( + reactpy.html.pre({"class_name": "printout"}, text) + if text + else reactpy.html.div() + ) return Wrapper() @@ -133,7 +137,7 @@ def _get_root_example_path_by_name(name: str, relative_to: str | Path | None) -> rel_path = rel_path.parent if rel_path.is_file() else rel_path else: rel_path = SOURCE_DIR - return rel_path.joinpath(*name.split("/")) + return rel_path.joinpath(*name.split("/")).resolve() class _PrintBuffer: @@ -144,7 +148,6 @@ def __init__(self, max_lines: int = 10): def set_callback(self, function: Callable[[str], None]) -> None: self._callback = function - return None def getvalue(self) -> str: return "".join(self._lines) @@ -159,16 +162,16 @@ def write(self, text: str) -> None: def _make_example_did_not_run(example_name): - @idom.component + @reactpy.component def ExampleDidNotRun(): - return idom.html.code(f"Example {example_name} did not run") + return reactpy.html.code(f"Example {example_name} did not run") return ExampleDidNotRun() def _make_error_display(message): - @idom.component + @reactpy.component def ShowError(): - return idom.html.pre(message) + return reactpy.html.pre(message) return ShowError() diff --git a/docs/docs_app/prod.py b/docs/docs_app/prod.py new file mode 100644 index 000000000..0acf12432 --- /dev/null +++ b/docs/docs_app/prod.py @@ -0,0 +1,14 @@ +import os + +from docs_app.app import make_app + +app = make_app("docs_prod_app") + + +def main() -> None: + app.run( + host="0.0.0.0", # noqa: S104 + port=int(os.environ.get("PORT", 5000)), + workers=int(os.environ.get("WEB_CONCURRENCY", 1)), + debug=bool(int(os.environ.get("DEBUG", "0"))), + ) diff --git a/docs/main.py b/docs/main.py new file mode 100644 index 000000000..e3181f393 --- /dev/null +++ b/docs/main.py @@ -0,0 +1,9 @@ +import sys + +from docs_app import dev, prod + +if __name__ == "__main__": + if len(sys.argv) == 1: + prod.main() + else: + dev.main() diff --git a/docs/make.bat b/docs/make.bat deleted file mode 100644 index 543c6b13b..000000000 --- a/docs/make.bat +++ /dev/null @@ -1,35 +0,0 @@ -@ECHO OFF - -pushd %~dp0 - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set SOURCEDIR=source -set BUILDDIR=build - -if "%1" == "" goto help - -%SPHINXBUILD% >NUL 2>NUL -if errorlevel 9009 ( - echo. - echo.The 'sphinx-build' command was not found. Make sure you have Sphinx - echo.installed, then set the SPHINXBUILD environment variable to point - echo.to the full path of the 'sphinx-build' executable. Alternatively you - echo.may add the Sphinx directory to PATH. - echo. - echo.If you don't have Sphinx installed, grab it from - echo.http://sphinx-doc.org/ - exit /b 1 -) - -%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% -goto end - -:help -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% - -:end -popd diff --git a/docs/poetry.lock b/docs/poetry.lock new file mode 100644 index 000000000..8e1daef24 --- /dev/null +++ b/docs/poetry.lock @@ -0,0 +1,2269 @@ +# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. + +[[package]] +name = "aiofiles" +version = "23.1.0" +description = "File support for asyncio." +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "aiofiles-23.1.0-py3-none-any.whl", hash = "sha256:9312414ae06472eb6f1d163f555e466a23aed1c8f60c30cccf7121dba2e53eb2"}, + {file = "aiofiles-23.1.0.tar.gz", hash = "sha256:edd247df9a19e0db16534d4baaf536d6609a43e1de5401d7a4c1c148753a1635"}, +] + +[[package]] +name = "alabaster" +version = "0.7.13" +description = "A configurable sidebar-enabled Sphinx theme" +optional = false +python-versions = ">=3.6" +files = [ + {file = "alabaster-0.7.13-py3-none-any.whl", hash = "sha256:1ee19aca801bbabb5ba3f5f258e4422dfa86f82f3e9cefb0859b283cdd7f62a3"}, + {file = "alabaster-0.7.13.tar.gz", hash = "sha256:a27a4a084d5e690e16e01e03ad2b2e552c61a65469419b907243193de1a84ae2"}, +] + +[[package]] +name = "anyio" +version = "3.7.0" +description = "High level compatibility layer for multiple asynchronous event loop implementations" +optional = false +python-versions = ">=3.7" +files = [ + {file = "anyio-3.7.0-py3-none-any.whl", hash = "sha256:eddca883c4175f14df8aedce21054bfca3adb70ffe76a9f607aef9d7fa2ea7f0"}, + {file = "anyio-3.7.0.tar.gz", hash = "sha256:275d9973793619a5374e1c89a4f4ad3f4b0a5510a2b5b939444bee8f4c4d37ce"}, +] + +[package.dependencies] +exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} +idna = ">=2.8" +sniffio = ">=1.1" + +[package.extras] +doc = ["Sphinx (>=6.1.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme", "sphinxcontrib-jquery"] +test = ["anyio[trio]", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] +trio = ["trio (<0.22)"] + +[[package]] +name = "asgiref" +version = "3.7.2" +description = "ASGI specs, helper code, and adapters" +optional = false +python-versions = ">=3.7" +files = [ + {file = "asgiref-3.7.2-py3-none-any.whl", hash = "sha256:89b2ef2247e3b562a16eef663bc0e2e703ec6468e2fa8a5cd61cd449786d4f6e"}, + {file = "asgiref-3.7.2.tar.gz", hash = "sha256:9e0ce3aa93a819ba5b45120216b23878cf6e8525eb3848653452b4192b92afed"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4", markers = "python_version < \"3.11\""} + +[package.extras] +tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"] + +[[package]] +name = "babel" +version = "2.12.1" +description = "Internationalization utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "Babel-2.12.1-py3-none-any.whl", hash = "sha256:b4246fb7677d3b98f501a39d43396d3cafdc8eadb045f4a31be01863f655c610"}, + {file = "Babel-2.12.1.tar.gz", hash = "sha256:cc2d99999cd01d44420ae725a21c9e3711b3aadc7976d6147f622d8581963455"}, +] + +[[package]] +name = "beautifulsoup4" +version = "4.12.2" +description = "Screen-scraping library" +optional = false +python-versions = ">=3.6.0" +files = [ + {file = "beautifulsoup4-4.12.2-py3-none-any.whl", hash = "sha256:bd2520ca0d9d7d12694a53d44ac482d181b4ec1888909b035a3dbf40d0f57d4a"}, + {file = "beautifulsoup4-4.12.2.tar.gz", hash = "sha256:492bbc69dca35d12daac71c4db1bfff0c876c00ef4a2ffacce226d4638eb72da"}, +] + +[package.dependencies] +soupsieve = ">1.2" + +[package.extras] +html5lib = ["html5lib"] +lxml = ["lxml"] + +[[package]] +name = "certifi" +version = "2023.5.7" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2023.5.7-py3-none-any.whl", hash = "sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716"}, + {file = "certifi-2023.5.7.tar.gz", hash = "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.1.0" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.1.0.tar.gz", hash = "sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-win32.whl", hash = "sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-win32.whl", hash = "sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-win32.whl", hash = "sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-win32.whl", hash = "sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-win32.whl", hash = "sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b"}, + {file = "charset_normalizer-3.1.0-py3-none-any.whl", hash = "sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d"}, +] + +[[package]] +name = "click" +version = "8.1.3" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, + {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "colorlog" +version = "6.7.0" +description = "Add colours to the output of Python's logging module." +optional = false +python-versions = ">=3.6" +files = [ + {file = "colorlog-6.7.0-py2.py3-none-any.whl", hash = "sha256:0d33ca236784a1ba3ff9c532d4964126d8a2c44f1f0cb1d2b0728196f512f662"}, + {file = "colorlog-6.7.0.tar.gz", hash = "sha256:bd94bd21c1e13fac7bd3153f4bc3a7dc0eb0974b8bc2fdf1a989e474f6e582e5"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} + +[package.extras] +development = ["black", "flake8", "mypy", "pytest", "types-colorama"] + +[[package]] +name = "contourpy" +version = "1.0.7" +description = "Python library for calculating contours of 2D quadrilateral grids" +optional = false +python-versions = ">=3.8" +files = [ + {file = "contourpy-1.0.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:95c3acddf921944f241b6773b767f1cbce71d03307270e2d769fd584d5d1092d"}, + {file = "contourpy-1.0.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:fc1464c97579da9f3ab16763c32e5c5d5bb5fa1ec7ce509a4ca6108b61b84fab"}, + {file = "contourpy-1.0.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8acf74b5d383414401926c1598ed77825cd530ac7b463ebc2e4f46638f56cce6"}, + {file = "contourpy-1.0.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c71fdd8f1c0f84ffd58fca37d00ca4ebaa9e502fb49825484da075ac0b0b803"}, + {file = "contourpy-1.0.7-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f99e9486bf1bb979d95d5cffed40689cb595abb2b841f2991fc894b3452290e8"}, + {file = "contourpy-1.0.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87f4d8941a9564cda3f7fa6a6cd9b32ec575830780677932abdec7bcb61717b0"}, + {file = "contourpy-1.0.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9e20e5a1908e18aaa60d9077a6d8753090e3f85ca25da6e25d30dc0a9e84c2c6"}, + {file = "contourpy-1.0.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a877ada905f7d69b2a31796c4b66e31a8068b37aa9b78832d41c82fc3e056ddd"}, + {file = "contourpy-1.0.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6381fa66866b0ea35e15d197fc06ac3840a9b2643a6475c8fff267db8b9f1e69"}, + {file = "contourpy-1.0.7-cp310-cp310-win32.whl", hash = "sha256:3c184ad2433635f216645fdf0493011a4667e8d46b34082f5a3de702b6ec42e3"}, + {file = "contourpy-1.0.7-cp310-cp310-win_amd64.whl", hash = "sha256:3caea6365b13119626ee996711ab63e0c9d7496f65641f4459c60a009a1f3e80"}, + {file = "contourpy-1.0.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ed33433fc3820263a6368e532f19ddb4c5990855e4886088ad84fd7c4e561c71"}, + {file = "contourpy-1.0.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:38e2e577f0f092b8e6774459317c05a69935a1755ecfb621c0a98f0e3c09c9a5"}, + {file = "contourpy-1.0.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ae90d5a8590e5310c32a7630b4b8618cef7563cebf649011da80874d0aa8f414"}, + {file = "contourpy-1.0.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:130230b7e49825c98edf0b428b7aa1125503d91732735ef897786fe5452b1ec2"}, + {file = "contourpy-1.0.7-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58569c491e7f7e874f11519ef46737cea1d6eda1b514e4eb5ac7dab6aa864d02"}, + {file = "contourpy-1.0.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54d43960d809c4c12508a60b66cb936e7ed57d51fb5e30b513934a4a23874fae"}, + {file = "contourpy-1.0.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:152fd8f730c31fd67fe0ffebe1df38ab6a669403da93df218801a893645c6ccc"}, + {file = "contourpy-1.0.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:9056c5310eb1daa33fc234ef39ebfb8c8e2533f088bbf0bc7350f70a29bde1ac"}, + {file = "contourpy-1.0.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a9d7587d2fdc820cc9177139b56795c39fb8560f540bba9ceea215f1f66e1566"}, + {file = "contourpy-1.0.7-cp311-cp311-win32.whl", hash = "sha256:4ee3ee247f795a69e53cd91d927146fb16c4e803c7ac86c84104940c7d2cabf0"}, + {file = "contourpy-1.0.7-cp311-cp311-win_amd64.whl", hash = "sha256:5caeacc68642e5f19d707471890f037a13007feba8427eb7f2a60811a1fc1350"}, + {file = "contourpy-1.0.7-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:fd7dc0e6812b799a34f6d12fcb1000539098c249c8da54f3566c6a6461d0dbad"}, + {file = "contourpy-1.0.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0f9d350b639db6c2c233d92c7f213d94d2e444d8e8fc5ca44c9706cf72193772"}, + {file = "contourpy-1.0.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e96a08b62bb8de960d3a6afbc5ed8421bf1a2d9c85cc4ea73f4bc81b4910500f"}, + {file = "contourpy-1.0.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:031154ed61f7328ad7f97662e48660a150ef84ee1bc8876b6472af88bf5a9b98"}, + {file = "contourpy-1.0.7-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e9ebb4425fc1b658e13bace354c48a933b842d53c458f02c86f371cecbedecc"}, + {file = "contourpy-1.0.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efb8f6d08ca7998cf59eaf50c9d60717f29a1a0a09caa46460d33b2924839dbd"}, + {file = "contourpy-1.0.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6c180d89a28787e4b73b07e9b0e2dac7741261dbdca95f2b489c4f8f887dd810"}, + {file = "contourpy-1.0.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b8d587cc39057d0afd4166083d289bdeff221ac6d3ee5046aef2d480dc4b503c"}, + {file = "contourpy-1.0.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:769eef00437edf115e24d87f8926955f00f7704bede656ce605097584f9966dc"}, + {file = "contourpy-1.0.7-cp38-cp38-win32.whl", hash = "sha256:62398c80ef57589bdbe1eb8537127321c1abcfdf8c5f14f479dbbe27d0322e66"}, + {file = "contourpy-1.0.7-cp38-cp38-win_amd64.whl", hash = "sha256:57119b0116e3f408acbdccf9eb6ef19d7fe7baf0d1e9aaa5381489bc1aa56556"}, + {file = "contourpy-1.0.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:30676ca45084ee61e9c3da589042c24a57592e375d4b138bd84d8709893a1ba4"}, + {file = "contourpy-1.0.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3e927b3868bd1e12acee7cc8f3747d815b4ab3e445a28d2e5373a7f4a6e76ba1"}, + {file = "contourpy-1.0.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:366a0cf0fc079af5204801786ad7a1c007714ee3909e364dbac1729f5b0849e5"}, + {file = "contourpy-1.0.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89ba9bb365446a22411f0673abf6ee1fea3b2cf47b37533b970904880ceb72f3"}, + {file = "contourpy-1.0.7-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:71b0bf0c30d432278793d2141362ac853859e87de0a7dee24a1cea35231f0d50"}, + {file = "contourpy-1.0.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7281244c99fd7c6f27c1c6bfafba878517b0b62925a09b586d88ce750a016d2"}, + {file = "contourpy-1.0.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b6d0f9e1d39dbfb3977f9dd79f156c86eb03e57a7face96f199e02b18e58d32a"}, + {file = "contourpy-1.0.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7f6979d20ee5693a1057ab53e043adffa1e7418d734c1532e2d9e915b08d8ec2"}, + {file = "contourpy-1.0.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5dd34c1ae752515318224cba7fc62b53130c45ac6a1040c8b7c1a223c46e8967"}, + {file = "contourpy-1.0.7-cp39-cp39-win32.whl", hash = "sha256:c5210e5d5117e9aec8c47d9156d1d3835570dd909a899171b9535cb4a3f32693"}, + {file = "contourpy-1.0.7-cp39-cp39-win_amd64.whl", hash = "sha256:60835badb5ed5f4e194a6f21c09283dd6e007664a86101431bf870d9e86266c4"}, + {file = "contourpy-1.0.7-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ce41676b3d0dd16dbcfabcc1dc46090aaf4688fd6e819ef343dbda5a57ef0161"}, + {file = "contourpy-1.0.7-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a011cf354107b47c58ea932d13b04d93c6d1d69b8b6dce885e642531f847566"}, + {file = "contourpy-1.0.7-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:31a55dccc8426e71817e3fe09b37d6d48ae40aae4ecbc8c7ad59d6893569c436"}, + {file = "contourpy-1.0.7-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:69f8ff4db108815addd900a74df665e135dbbd6547a8a69333a68e1f6e368ac2"}, + {file = "contourpy-1.0.7-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:efe99298ba37e37787f6a2ea868265465410822f7bea163edcc1bd3903354ea9"}, + {file = "contourpy-1.0.7-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a1e97b86f73715e8670ef45292d7cc033548266f07d54e2183ecb3c87598888f"}, + {file = "contourpy-1.0.7-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc331c13902d0f50845099434cd936d49d7a2ca76cb654b39691974cb1e4812d"}, + {file = "contourpy-1.0.7-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:24847601071f740837aefb730e01bd169fbcaa610209779a78db7ebb6e6a7051"}, + {file = "contourpy-1.0.7-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abf298af1e7ad44eeb93501e40eb5a67abbf93b5d90e468d01fc0c4451971afa"}, + {file = "contourpy-1.0.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:64757f6460fc55d7e16ed4f1de193f362104285c667c112b50a804d482777edd"}, + {file = "contourpy-1.0.7.tar.gz", hash = "sha256:d8165a088d31798b59e91117d1f5fc3df8168d8b48c4acc10fc0df0d0bdbcc5e"}, +] + +[package.dependencies] +numpy = ">=1.16" + +[package.extras] +bokeh = ["bokeh", "chromedriver", "selenium"] +docs = ["furo", "sphinx-copybutton"] +mypy = ["contourpy[bokeh]", "docutils-stubs", "mypy (==0.991)", "types-Pillow"] +test = ["Pillow", "matplotlib", "pytest"] +test-no-images = ["pytest"] + +[[package]] +name = "cycler" +version = "0.11.0" +description = "Composable style cycles" +optional = false +python-versions = ">=3.6" +files = [ + {file = "cycler-0.11.0-py3-none-any.whl", hash = "sha256:3a27e95f763a428a739d2add979fa7494c912a32c17c4c38c4d5f082cad165a3"}, + {file = "cycler-0.11.0.tar.gz", hash = "sha256:9c87405839a19696e837b3b818fed3f5f69f16f1eec1a1ad77e043dcea9c772f"}, +] + +[[package]] +name = "docutils" +version = "0.17.1" +description = "Docutils -- Python Documentation Utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "docutils-0.17.1-py2.py3-none-any.whl", hash = "sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61"}, + {file = "docutils-0.17.1.tar.gz", hash = "sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125"}, +] + +[[package]] +name = "exceptiongroup" +version = "1.1.1" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.1.1-py3-none-any.whl", hash = "sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e"}, + {file = "exceptiongroup-1.1.1.tar.gz", hash = "sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "fastapi" +version = "0.96.0" +description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" +optional = false +python-versions = ">=3.7" +files = [ + {file = "fastapi-0.96.0-py3-none-any.whl", hash = "sha256:b8e11fe81e81eab4e1504209917338e0b80f783878a42c2b99467e5e1019a1e9"}, + {file = "fastapi-0.96.0.tar.gz", hash = "sha256:71232d47c2787446991c81c41c249f8a16238d52d779c0e6b43927d3773dbe3c"}, +] + +[package.dependencies] +pydantic = ">=1.6.2,<1.7 || >1.7,<1.7.1 || >1.7.1,<1.7.2 || >1.7.2,<1.7.3 || >1.7.3,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0" +starlette = ">=0.27.0,<0.28.0" + +[package.extras] +all = ["email-validator (>=1.1.1)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "python-multipart (>=0.0.5)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] +dev = ["pre-commit (>=2.17.0,<3.0.0)", "ruff (==0.0.138)", "uvicorn[standard] (>=0.12.0,<0.21.0)"] +doc = ["mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.3.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pyyaml (>=5.3.1,<7.0.0)", "typer-cli (>=0.0.13,<0.0.14)", "typer[all] (>=0.6.1,<0.8.0)"] +test = ["anyio[trio] (>=3.2.1,<4.0.0)", "black (==23.1.0)", "coverage[toml] (>=6.5.0,<8.0)", "databases[sqlite] (>=0.3.2,<0.7.0)", "email-validator (>=1.1.1,<2.0.0)", "flask (>=1.1.2,<3.0.0)", "httpx (>=0.23.0,<0.24.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.982)", "orjson (>=3.2.1,<4.0.0)", "passlib[bcrypt] (>=1.7.2,<2.0.0)", "peewee (>=3.13.3,<4.0.0)", "pytest (>=7.1.3,<8.0.0)", "python-jose[cryptography] (>=3.3.0,<4.0.0)", "python-multipart (>=0.0.5,<0.0.7)", "pyyaml (>=5.3.1,<7.0.0)", "ruff (==0.0.138)", "sqlalchemy (>=1.3.18,<1.4.43)", "types-orjson (==3.6.2)", "types-ujson (==5.7.0.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,<6.0.0)"] + +[[package]] +name = "fastjsonschema" +version = "2.17.1" +description = "Fastest Python implementation of JSON schema" +optional = false +python-versions = "*" +files = [ + {file = "fastjsonschema-2.17.1-py3-none-any.whl", hash = "sha256:4b90b252628ca695280924d863fe37234eebadc29c5360d322571233dc9746e0"}, + {file = "fastjsonschema-2.17.1.tar.gz", hash = "sha256:f4eeb8a77cef54861dbf7424ac8ce71306f12cbb086c45131bcba2c6a4f726e3"}, +] + +[package.extras] +devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benchmark", "pytest-cache", "validictory"] + +[[package]] +name = "flask" +version = "2.1.3" +description = "A simple framework for building complex web applications." +optional = false +python-versions = ">=3.7" +files = [ + {file = "Flask-2.1.3-py3-none-any.whl", hash = "sha256:9013281a7402ad527f8fd56375164f3aa021ecfaff89bfe3825346c24f87e04c"}, + {file = "Flask-2.1.3.tar.gz", hash = "sha256:15972e5017df0575c3d6c090ba168b6db90259e620ac8d7ea813a396bad5b6cb"}, +] + +[package.dependencies] +click = ">=8.0" +importlib-metadata = {version = ">=3.6.0", markers = "python_version < \"3.10\""} +itsdangerous = ">=2.0" +Jinja2 = ">=3.0" +Werkzeug = ">=2.0" + +[package.extras] +async = ["asgiref (>=3.2)"] +dotenv = ["python-dotenv"] + +[[package]] +name = "flask-cors" +version = "3.0.10" +description = "A Flask extension adding a decorator for CORS support" +optional = false +python-versions = "*" +files = [ + {file = "Flask-Cors-3.0.10.tar.gz", hash = "sha256:b60839393f3b84a0f3746f6cdca56c1ad7426aa738b70d6c61375857823181de"}, + {file = "Flask_Cors-3.0.10-py2.py3-none-any.whl", hash = "sha256:74efc975af1194fc7891ff5cd85b0f7478be4f7f59fe158102e91abb72bb4438"}, +] + +[package.dependencies] +Flask = ">=0.9" +Six = "*" + +[[package]] +name = "flask-sock" +version = "0.6.0" +description = "WebSocket support for Flask" +optional = false +python-versions = ">=3.6" +files = [ + {file = "flask-sock-0.6.0.tar.gz", hash = "sha256:435cf81bb497ac7622cd1dda554fbfa3e369e629daea0a1d21b73a24f1bd6229"}, + {file = "flask_sock-0.6.0-py3-none-any.whl", hash = "sha256:593fffb186928080a5b5b03d717efc56dac2d5ed690ce6bfff333b3597a2f518"}, +] + +[package.dependencies] +flask = ">=2" +simple-websocket = ">=0.5.1" + +[[package]] +name = "fonttools" +version = "4.39.4" +description = "Tools to manipulate font files" +optional = false +python-versions = ">=3.8" +files = [ + {file = "fonttools-4.39.4-py3-none-any.whl", hash = "sha256:106caf6167c4597556b31a8d9175a3fdc0356fdcd70ab19973c3b0d4c893c461"}, + {file = "fonttools-4.39.4.zip", hash = "sha256:dba8d7cdb8e2bac1b3da28c5ed5960de09e59a2fe7e63bb73f5a59e57b0430d2"}, +] + +[package.extras] +all = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "fs (>=2.2.0,<3)", "lxml (>=4.0,<5)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres", "scipy", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=15.0.0)", "xattr", "zopfli (>=0.1.4)"] +graphite = ["lz4 (>=1.7.4.2)"] +interpolatable = ["munkres", "scipy"] +lxml = ["lxml (>=4.0,<5)"] +pathops = ["skia-pathops (>=0.5.0)"] +plot = ["matplotlib"] +repacker = ["uharfbuzz (>=0.23.0)"] +symfont = ["sympy"] +type1 = ["xattr"] +ufo = ["fs (>=2.2.0,<3)"] +unicode = ["unicodedata2 (>=15.0.0)"] +woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"] + +[[package]] +name = "furo" +version = "2022.4.7" +description = "A clean customisable Sphinx documentation theme." +optional = false +python-versions = ">=3.6" +files = [ + {file = "furo-2022.4.7-py3-none-any.whl", hash = "sha256:7f3e3d2fb977483590f8ecb2c2cd511bd82661b79c18efb24de9558bc9cdf2d7"}, + {file = "furo-2022.4.7.tar.gz", hash = "sha256:96204ab7cd047e4b6c523996e0279c4c629a8fc31f4f109b2efd470c17f49c80"}, +] + +[package.dependencies] +beautifulsoup4 = "*" +pygments = ">=2.7,<3.0" +sphinx = ">=4.0,<5.0" + +[[package]] +name = "greenlet" +version = "2.0.2" +description = "Lightweight in-process concurrent programming" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" +files = [ + {file = "greenlet-2.0.2-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:bdfea8c661e80d3c1c99ad7c3ff74e6e87184895bbaca6ee8cc61209f8b9b85d"}, + {file = "greenlet-2.0.2-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:9d14b83fab60d5e8abe587d51c75b252bcc21683f24699ada8fb275d7712f5a9"}, + {file = "greenlet-2.0.2-cp27-cp27m-win32.whl", hash = "sha256:6c3acb79b0bfd4fe733dff8bc62695283b57949ebcca05ae5c129eb606ff2d74"}, + {file = "greenlet-2.0.2-cp27-cp27m-win_amd64.whl", hash = "sha256:283737e0da3f08bd637b5ad058507e578dd462db259f7f6e4c5c365ba4ee9343"}, + {file = "greenlet-2.0.2-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:d27ec7509b9c18b6d73f2f5ede2622441de812e7b1a80bbd446cb0633bd3d5ae"}, + {file = "greenlet-2.0.2-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:30bcf80dda7f15ac77ba5af2b961bdd9dbc77fd4ac6105cee85b0d0a5fcf74df"}, + {file = "greenlet-2.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26fbfce90728d82bc9e6c38ea4d038cba20b7faf8a0ca53a9c07b67318d46088"}, + {file = "greenlet-2.0.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9190f09060ea4debddd24665d6804b995a9c122ef5917ab26e1566dcc712ceeb"}, + {file = "greenlet-2.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d75209eed723105f9596807495d58d10b3470fa6732dd6756595e89925ce2470"}, + {file = "greenlet-2.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3a51c9751078733d88e013587b108f1b7a1fb106d402fb390740f002b6f6551a"}, + {file = "greenlet-2.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:76ae285c8104046b3a7f06b42f29c7b73f77683df18c49ab5af7983994c2dd91"}, + {file = "greenlet-2.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:2d4686f195e32d36b4d7cf2d166857dbd0ee9f3d20ae349b6bf8afc8485b3645"}, + {file = "greenlet-2.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c4302695ad8027363e96311df24ee28978162cdcdd2006476c43970b384a244c"}, + {file = "greenlet-2.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c48f54ef8e05f04d6eff74b8233f6063cb1ed960243eacc474ee73a2ea8573ca"}, + {file = "greenlet-2.0.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a1846f1b999e78e13837c93c778dcfc3365902cfb8d1bdb7dd73ead37059f0d0"}, + {file = "greenlet-2.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a06ad5312349fec0ab944664b01d26f8d1f05009566339ac6f63f56589bc1a2"}, + {file = "greenlet-2.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:eff4eb9b7eb3e4d0cae3d28c283dc16d9bed6b193c2e1ace3ed86ce48ea8df19"}, + {file = "greenlet-2.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5454276c07d27a740c5892f4907c86327b632127dd9abec42ee62e12427ff7e3"}, + {file = "greenlet-2.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:7cafd1208fdbe93b67c7086876f061f660cfddc44f404279c1585bbf3cdc64c5"}, + {file = "greenlet-2.0.2-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:910841381caba4f744a44bf81bfd573c94e10b3045ee00de0cbf436fe50673a6"}, + {file = "greenlet-2.0.2-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:18a7f18b82b52ee85322d7a7874e676f34ab319b9f8cce5de06067384aa8ff43"}, + {file = "greenlet-2.0.2-cp35-cp35m-win32.whl", hash = "sha256:03a8f4f3430c3b3ff8d10a2a86028c660355ab637cee9333d63d66b56f09d52a"}, + {file = "greenlet-2.0.2-cp35-cp35m-win_amd64.whl", hash = "sha256:4b58adb399c4d61d912c4c331984d60eb66565175cdf4a34792cd9600f21b394"}, + {file = "greenlet-2.0.2-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:703f18f3fda276b9a916f0934d2fb6d989bf0b4fb5a64825260eb9bfd52d78f0"}, + {file = "greenlet-2.0.2-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:32e5b64b148966d9cccc2c8d35a671409e45f195864560829f395a54226408d3"}, + {file = "greenlet-2.0.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2dd11f291565a81d71dab10b7033395b7a3a5456e637cf997a6f33ebdf06f8db"}, + {file = "greenlet-2.0.2-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e0f72c9ddb8cd28532185f54cc1453f2c16fb417a08b53a855c4e6a418edd099"}, + {file = "greenlet-2.0.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd021c754b162c0fb55ad5d6b9d960db667faad0fa2ff25bb6e1301b0b6e6a75"}, + {file = "greenlet-2.0.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:3c9b12575734155d0c09d6c3e10dbd81665d5c18e1a7c6597df72fd05990c8cf"}, + {file = "greenlet-2.0.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:b9ec052b06a0524f0e35bd8790686a1da006bd911dd1ef7d50b77bfbad74e292"}, + {file = "greenlet-2.0.2-cp36-cp36m-win32.whl", hash = "sha256:dbfcfc0218093a19c252ca8eb9aee3d29cfdcb586df21049b9d777fd32c14fd9"}, + {file = "greenlet-2.0.2-cp36-cp36m-win_amd64.whl", hash = "sha256:9f35ec95538f50292f6d8f2c9c9f8a3c6540bbfec21c9e5b4b751e0a7c20864f"}, + {file = "greenlet-2.0.2-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:d5508f0b173e6aa47273bdc0a0b5ba055b59662ba7c7ee5119528f466585526b"}, + {file = "greenlet-2.0.2-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:f82d4d717d8ef19188687aa32b8363e96062911e63ba22a0cff7802a8e58e5f1"}, + {file = "greenlet-2.0.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9c59a2120b55788e800d82dfa99b9e156ff8f2227f07c5e3012a45a399620b7"}, + {file = "greenlet-2.0.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2780572ec463d44c1d3ae850239508dbeb9fed38e294c68d19a24d925d9223ca"}, + {file = "greenlet-2.0.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:937e9020b514ceedb9c830c55d5c9872abc90f4b5862f89c0887033ae33c6f73"}, + {file = "greenlet-2.0.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:36abbf031e1c0f79dd5d596bfaf8e921c41df2bdf54ee1eed921ce1f52999a86"}, + {file = "greenlet-2.0.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:18e98fb3de7dba1c0a852731c3070cf022d14f0d68b4c87a19cc1016f3bb8b33"}, + {file = "greenlet-2.0.2-cp37-cp37m-win32.whl", hash = "sha256:3f6ea9bd35eb450837a3d80e77b517ea5bc56b4647f5502cd28de13675ee12f7"}, + {file = "greenlet-2.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:7492e2b7bd7c9b9916388d9df23fa49d9b88ac0640db0a5b4ecc2b653bf451e3"}, + {file = "greenlet-2.0.2-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:b864ba53912b6c3ab6bcb2beb19f19edd01a6bfcbdfe1f37ddd1778abfe75a30"}, + {file = "greenlet-2.0.2-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:ba2956617f1c42598a308a84c6cf021a90ff3862eddafd20c3333d50f0edb45b"}, + {file = "greenlet-2.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc3a569657468b6f3fb60587e48356fe512c1754ca05a564f11366ac9e306526"}, + {file = "greenlet-2.0.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8eab883b3b2a38cc1e050819ef06a7e6344d4a990d24d45bc6f2cf959045a45b"}, + {file = "greenlet-2.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acd2162a36d3de67ee896c43effcd5ee3de247eb00354db411feb025aa319857"}, + {file = "greenlet-2.0.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0bf60faf0bc2468089bdc5edd10555bab6e85152191df713e2ab1fcc86382b5a"}, + {file = "greenlet-2.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b0ef99cdbe2b682b9ccbb964743a6aca37905fda5e0452e5ee239b1654d37f2a"}, + {file = "greenlet-2.0.2-cp38-cp38-win32.whl", hash = "sha256:b80f600eddddce72320dbbc8e3784d16bd3fb7b517e82476d8da921f27d4b249"}, + {file = "greenlet-2.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:4d2e11331fc0c02b6e84b0d28ece3a36e0548ee1a1ce9ddde03752d9b79bba40"}, + {file = "greenlet-2.0.2-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:88d9ab96491d38a5ab7c56dd7a3cc37d83336ecc564e4e8816dbed12e5aaefc8"}, + {file = "greenlet-2.0.2-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:561091a7be172ab497a3527602d467e2b3fbe75f9e783d8b8ce403fa414f71a6"}, + {file = "greenlet-2.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:971ce5e14dc5e73715755d0ca2975ac88cfdaefcaab078a284fea6cfabf866df"}, + {file = "greenlet-2.0.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be4ed120b52ae4d974aa40215fcdfde9194d63541c7ded40ee12eb4dda57b76b"}, + {file = "greenlet-2.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94c817e84245513926588caf1152e3b559ff794d505555211ca041f032abbb6b"}, + {file = "greenlet-2.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1a819eef4b0e0b96bb0d98d797bef17dc1b4a10e8d7446be32d1da33e095dbb8"}, + {file = "greenlet-2.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7efde645ca1cc441d6dc4b48c0f7101e8d86b54c8530141b09fd31cef5149ec9"}, + {file = "greenlet-2.0.2-cp39-cp39-win32.whl", hash = "sha256:ea9872c80c132f4663822dd2a08d404073a5a9b5ba6155bea72fb2a79d1093b5"}, + {file = "greenlet-2.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:db1a39669102a1d8d12b57de2bb7e2ec9066a6f2b3da35ae511ff93b01b5d564"}, + {file = "greenlet-2.0.2.tar.gz", hash = "sha256:e7c8dc13af7db097bed64a051d2dd49e9f0af495c26995c00a9ee842690d34c0"}, +] + +[package.extras] +docs = ["Sphinx", "docutils (<0.18)"] +test = ["objgraph", "psutil"] + +[[package]] +name = "h11" +version = "0.14.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +optional = false +python-versions = ">=3.7" +files = [ + {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, + {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, +] + +[[package]] +name = "html5tagger" +version = "1.3.0" +description = "Pythonic HTML generation/templating (no template files)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "html5tagger-1.3.0-py3-none-any.whl", hash = "sha256:ce14313515edffec8ed8a36c5890d023922641171b4e6e5774ad1a74998f5351"}, + {file = "html5tagger-1.3.0.tar.gz", hash = "sha256:84fa3dfb49e5c83b79bbd856ab7b1de8e2311c3bb46a8be925f119e3880a8da9"}, +] + +[[package]] +name = "httptools" +version = "0.5.0" +description = "A collection of framework independent HTTP protocol utils." +optional = false +python-versions = ">=3.5.0" +files = [ + {file = "httptools-0.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8f470c79061599a126d74385623ff4744c4e0f4a0997a353a44923c0b561ee51"}, + {file = "httptools-0.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e90491a4d77d0cb82e0e7a9cb35d86284c677402e4ce7ba6b448ccc7325c5421"}, + {file = "httptools-0.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1d2357f791b12d86faced7b5736dea9ef4f5ecdc6c3f253e445ee82da579449"}, + {file = "httptools-0.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f90cd6fd97c9a1b7fe9215e60c3bd97336742a0857f00a4cb31547bc22560c2"}, + {file = "httptools-0.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5230a99e724a1bdbbf236a1b58d6e8504b912b0552721c7c6b8570925ee0ccde"}, + {file = "httptools-0.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3a47a34f6015dd52c9eb629c0f5a8a5193e47bf2a12d9a3194d231eaf1bc451a"}, + {file = "httptools-0.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:24bb4bb8ac3882f90aa95403a1cb48465de877e2d5298ad6ddcfdebec060787d"}, + {file = "httptools-0.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e67d4f8734f8054d2c4858570cc4b233bf753f56e85217de4dfb2495904cf02e"}, + {file = "httptools-0.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7e5eefc58d20e4c2da82c78d91b2906f1a947ef42bd668db05f4ab4201a99f49"}, + {file = "httptools-0.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0297822cea9f90a38df29f48e40b42ac3d48a28637368f3ec6d15eebefd182f9"}, + {file = "httptools-0.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:557be7fbf2bfa4a2ec65192c254e151684545ebab45eca5d50477d562c40f986"}, + {file = "httptools-0.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:54465401dbbec9a6a42cf737627fb0f014d50dc7365a6b6cd57753f151a86ff0"}, + {file = "httptools-0.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4d9ebac23d2de960726ce45f49d70eb5466725c0087a078866043dad115f850f"}, + {file = "httptools-0.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:e8a34e4c0ab7b1ca17b8763613783e2458e77938092c18ac919420ab8655c8c1"}, + {file = "httptools-0.5.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f659d7a48401158c59933904040085c200b4be631cb5f23a7d561fbae593ec1f"}, + {file = "httptools-0.5.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef1616b3ba965cd68e6f759eeb5d34fbf596a79e84215eeceebf34ba3f61fdc7"}, + {file = "httptools-0.5.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3625a55886257755cb15194efbf209584754e31d336e09e2ffe0685a76cb4b60"}, + {file = "httptools-0.5.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:72ad589ba5e4a87e1d404cc1cb1b5780bfcb16e2aec957b88ce15fe879cc08ca"}, + {file = "httptools-0.5.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:850fec36c48df5a790aa735417dca8ce7d4b48d59b3ebd6f83e88a8125cde324"}, + {file = "httptools-0.5.0-cp36-cp36m-win_amd64.whl", hash = "sha256:f222e1e9d3f13b68ff8a835574eda02e67277d51631d69d7cf7f8e07df678c86"}, + {file = "httptools-0.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3cb8acf8f951363b617a8420768a9f249099b92e703c052f9a51b66342eea89b"}, + {file = "httptools-0.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:550059885dc9c19a072ca6d6735739d879be3b5959ec218ba3e013fd2255a11b"}, + {file = "httptools-0.5.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a04fe458a4597aa559b79c7f48fe3dceabef0f69f562daf5c5e926b153817281"}, + {file = "httptools-0.5.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7d0c1044bce274ec6711f0770fd2d5544fe392591d204c68328e60a46f88843b"}, + {file = "httptools-0.5.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c6eeefd4435055a8ebb6c5cc36111b8591c192c56a95b45fe2af22d9881eee25"}, + {file = "httptools-0.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:5b65be160adcd9de7a7e6413a4966665756e263f0d5ddeffde277ffeee0576a5"}, + {file = "httptools-0.5.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:fe9c766a0c35b7e3d6b6939393c8dfdd5da3ac5dec7f971ec9134f284c6c36d6"}, + {file = "httptools-0.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:85b392aba273566c3d5596a0a490978c085b79700814fb22bfd537d381dd230c"}, + {file = "httptools-0.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5e3088f4ed33947e16fd865b8200f9cfae1144f41b64a8cf19b599508e096bc"}, + {file = "httptools-0.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c2a56b6aad7cc8f5551d8e04ff5a319d203f9d870398b94702300de50190f63"}, + {file = "httptools-0.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9b571b281a19762adb3f48a7731f6842f920fa71108aff9be49888320ac3e24d"}, + {file = "httptools-0.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa47ffcf70ba6f7848349b8a6f9b481ee0f7637931d91a9860a1838bfc586901"}, + {file = "httptools-0.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:bede7ee075e54b9a5bde695b4fc8f569f30185891796b2e4e09e2226801d09bd"}, + {file = "httptools-0.5.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:64eba6f168803a7469866a9c9b5263a7463fa8b7a25b35e547492aa7322036b6"}, + {file = "httptools-0.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4b098e4bb1174096a93f48f6193e7d9aa7071506a5877da09a783509ca5fff42"}, + {file = "httptools-0.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9423a2de923820c7e82e18980b937893f4aa8251c43684fa1772e341f6e06887"}, + {file = "httptools-0.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca1b7becf7d9d3ccdbb2f038f665c0f4857e08e1d8481cbcc1a86a0afcfb62b2"}, + {file = "httptools-0.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:50d4613025f15f4b11f1c54bbed4761c0020f7f921b95143ad6d58c151198142"}, + {file = "httptools-0.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8ffce9d81c825ac1deaa13bc9694c0562e2840a48ba21cfc9f3b4c922c16f372"}, + {file = "httptools-0.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:1af91b3650ce518d226466f30bbba5b6376dbd3ddb1b2be8b0658c6799dd450b"}, + {file = "httptools-0.5.0.tar.gz", hash = "sha256:295874861c173f9101960bba332429bb77ed4dcd8cdf5cee9922eb00e4f6bc09"}, +] + +[package.extras] +test = ["Cython (>=0.29.24,<0.30.0)"] + +[[package]] +name = "idna" +version = "3.4" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, + {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, +] + +[[package]] +name = "imagesize" +version = "1.4.1" +description = "Getting image size from png/jpeg/jpeg2000/gif file" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, + {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, +] + +[[package]] +name = "importlib-metadata" +version = "6.6.0" +description = "Read metadata from Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "importlib_metadata-6.6.0-py3-none-any.whl", hash = "sha256:43dd286a2cd8995d5eaef7fee2066340423b818ed3fd70adf0bad5f1fac53fed"}, + {file = "importlib_metadata-6.6.0.tar.gz", hash = "sha256:92501cdf9cc66ebd3e612f1b4f0c0765dfa42f0fa38ffb319b6bd84dd675d705"}, +] + +[package.dependencies] +zipp = ">=0.5" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +perf = ["ipython"] +testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] + +[[package]] +name = "importlib-resources" +version = "5.12.0" +description = "Read resources from Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "importlib_resources-5.12.0-py3-none-any.whl", hash = "sha256:7b1deeebbf351c7578e09bf2f63fa2ce8b5ffec296e0d349139d43cca061a81a"}, + {file = "importlib_resources-5.12.0.tar.gz", hash = "sha256:4be82589bf5c1d7999aedf2a45159d10cb3ca4f19b2271f8792bc8e6da7b22f6"}, +] + +[package.dependencies] +zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["flake8 (<5)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] + +[[package]] +name = "itsdangerous" +version = "2.1.2" +description = "Safely pass data to untrusted environments and back." +optional = false +python-versions = ">=3.7" +files = [ + {file = "itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44"}, + {file = "itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"}, +] + +[[package]] +name = "jinja2" +version = "3.1.2" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +files = [ + {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, + {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "jsonpatch" +version = "1.32" +description = "Apply JSON-Patches (RFC 6902)" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "jsonpatch-1.32-py2.py3-none-any.whl", hash = "sha256:26ac385719ac9f54df8a2f0827bb8253aa3ea8ab7b3368457bcdb8c14595a397"}, + {file = "jsonpatch-1.32.tar.gz", hash = "sha256:b6ddfe6c3db30d81a96aaeceb6baf916094ffa23d7dd5fa2c13e13f8b6e600c2"}, +] + +[package.dependencies] +jsonpointer = ">=1.9" + +[[package]] +name = "jsonpointer" +version = "2.3" +description = "Identify specific nodes in a JSON document (RFC 6901)" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "jsonpointer-2.3-py2.py3-none-any.whl", hash = "sha256:51801e558539b4e9cd268638c078c6c5746c9ac96bc38152d443400e4f3793e9"}, + {file = "jsonpointer-2.3.tar.gz", hash = "sha256:97cba51526c829282218feb99dab1b1e6bdf8efd1c43dc9d57be093c0d69c99a"}, +] + +[[package]] +name = "kiwisolver" +version = "1.4.4" +description = "A fast implementation of the Cassowary constraint solver" +optional = false +python-versions = ">=3.7" +files = [ + {file = "kiwisolver-1.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2f5e60fabb7343a836360c4f0919b8cd0d6dbf08ad2ca6b9cf90bf0c76a3c4f6"}, + {file = "kiwisolver-1.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:10ee06759482c78bdb864f4109886dff7b8a56529bc1609d4f1112b93fe6423c"}, + {file = "kiwisolver-1.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c79ebe8f3676a4c6630fd3f777f3cfecf9289666c84e775a67d1d358578dc2e3"}, + {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:abbe9fa13da955feb8202e215c4018f4bb57469b1b78c7a4c5c7b93001699938"}, + {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7577c1987baa3adc4b3c62c33bd1118c3ef5c8ddef36f0f2c950ae0b199e100d"}, + {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8ad8285b01b0d4695102546b342b493b3ccc6781fc28c8c6a1bb63e95d22f09"}, + {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ed58b8acf29798b036d347791141767ccf65eee7f26bde03a71c944449e53de"}, + {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a68b62a02953b9841730db7797422f983935aeefceb1679f0fc85cbfbd311c32"}, + {file = "kiwisolver-1.4.4-cp310-cp310-win32.whl", hash = "sha256:e92a513161077b53447160b9bd8f522edfbed4bd9759e4c18ab05d7ef7e49408"}, + {file = "kiwisolver-1.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:3fe20f63c9ecee44560d0e7f116b3a747a5d7203376abeea292ab3152334d004"}, + {file = "kiwisolver-1.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e0ea21f66820452a3f5d1655f8704a60d66ba1191359b96541eaf457710a5fc6"}, + {file = "kiwisolver-1.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bc9db8a3efb3e403e4ecc6cd9489ea2bac94244f80c78e27c31dcc00d2790ac2"}, + {file = "kiwisolver-1.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d5b61785a9ce44e5a4b880272baa7cf6c8f48a5180c3e81c59553ba0cb0821ca"}, + {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c2dbb44c3f7e6c4d3487b31037b1bdbf424d97687c1747ce4ff2895795c9bf69"}, + {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6295ecd49304dcf3bfbfa45d9a081c96509e95f4b9d0eb7ee4ec0530c4a96514"}, + {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4bd472dbe5e136f96a4b18f295d159d7f26fd399136f5b17b08c4e5f498cd494"}, + {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf7d9fce9bcc4752ca4a1b80aabd38f6d19009ea5cbda0e0856983cf6d0023f5"}, + {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78d6601aed50c74e0ef02f4204da1816147a6d3fbdc8b3872d263338a9052c51"}, + {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:877272cf6b4b7e94c9614f9b10140e198d2186363728ed0f701c6eee1baec1da"}, + {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:db608a6757adabb32f1cfe6066e39b3706d8c3aa69bbc353a5b61edad36a5cb4"}, + {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:5853eb494c71e267912275e5586fe281444eb5e722de4e131cddf9d442615626"}, + {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:f0a1dbdb5ecbef0d34eb77e56fcb3e95bbd7e50835d9782a45df81cc46949750"}, + {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:283dffbf061a4ec60391d51e6155e372a1f7a4f5b15d59c8505339454f8989e4"}, + {file = "kiwisolver-1.4.4-cp311-cp311-win32.whl", hash = "sha256:d06adcfa62a4431d404c31216f0f8ac97397d799cd53800e9d3efc2fbb3cf14e"}, + {file = "kiwisolver-1.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:e7da3fec7408813a7cebc9e4ec55afed2d0fd65c4754bc376bf03498d4e92686"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:62ac9cc684da4cf1778d07a89bf5f81b35834cb96ca523d3a7fb32509380cbf6"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41dae968a94b1ef1897cb322b39360a0812661dba7c682aa45098eb8e193dbdf"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:02f79693ec433cb4b5f51694e8477ae83b3205768a6fb48ffba60549080e295b"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d0611a0a2a518464c05ddd5a3a1a0e856ccc10e67079bb17f265ad19ab3c7597"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:db5283d90da4174865d520e7366801a93777201e91e79bacbac6e6927cbceede"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1041feb4cda8708ce73bb4dcb9ce1ccf49d553bf87c3954bdfa46f0c3f77252c"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-win32.whl", hash = "sha256:a553dadda40fef6bfa1456dc4be49b113aa92c2a9a9e8711e955618cd69622e3"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-win_amd64.whl", hash = "sha256:03baab2d6b4a54ddbb43bba1a3a2d1627e82d205c5cf8f4c924dc49284b87166"}, + {file = "kiwisolver-1.4.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:841293b17ad704d70c578f1f0013c890e219952169ce8a24ebc063eecf775454"}, + {file = "kiwisolver-1.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f4f270de01dd3e129a72efad823da90cc4d6aafb64c410c9033aba70db9f1ff0"}, + {file = "kiwisolver-1.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f9f39e2f049db33a908319cf46624a569b36983c7c78318e9726a4cb8923b26c"}, + {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c97528e64cb9ebeff9701e7938653a9951922f2a38bd847787d4a8e498cc83ae"}, + {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d1573129aa0fd901076e2bfb4275a35f5b7aa60fbfb984499d661ec950320b0"}, + {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ad881edc7ccb9d65b0224f4e4d05a1e85cf62d73aab798943df6d48ab0cd79a1"}, + {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b428ef021242344340460fa4c9185d0b1f66fbdbfecc6c63eff4b7c29fad429d"}, + {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:2e407cb4bd5a13984a6c2c0fe1845e4e41e96f183e5e5cd4d77a857d9693494c"}, + {file = "kiwisolver-1.4.4-cp38-cp38-win32.whl", hash = "sha256:75facbe9606748f43428fc91a43edb46c7ff68889b91fa31f53b58894503a191"}, + {file = "kiwisolver-1.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:5bce61af018b0cb2055e0e72e7d65290d822d3feee430b7b8203d8a855e78766"}, + {file = "kiwisolver-1.4.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8c808594c88a025d4e322d5bb549282c93c8e1ba71b790f539567932722d7bd8"}, + {file = "kiwisolver-1.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f0a71d85ecdd570ded8ac3d1c0f480842f49a40beb423bb8014539a9f32a5897"}, + {file = "kiwisolver-1.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b533558eae785e33e8c148a8d9921692a9fe5aa516efbdff8606e7d87b9d5824"}, + {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:efda5fc8cc1c61e4f639b8067d118e742b812c930f708e6667a5ce0d13499e29"}, + {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7c43e1e1206cd421cd92e6b3280d4385d41d7166b3ed577ac20444b6995a445f"}, + {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc8d3bd6c72b2dd9decf16ce70e20abcb3274ba01b4e1c96031e0c4067d1e7cd"}, + {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4ea39b0ccc4f5d803e3337dd46bcce60b702be4d86fd0b3d7531ef10fd99a1ac"}, + {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:968f44fdbf6dd757d12920d63b566eeb4d5b395fd2d00d29d7ef00a00582aac9"}, + {file = "kiwisolver-1.4.4-cp39-cp39-win32.whl", hash = "sha256:da7e547706e69e45d95e116e6939488d62174e033b763ab1496b4c29b76fabea"}, + {file = "kiwisolver-1.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:ba59c92039ec0a66103b1d5fe588fa546373587a7d68f5c96f743c3396afc04b"}, + {file = "kiwisolver-1.4.4-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:91672bacaa030f92fc2f43b620d7b337fd9a5af28b0d6ed3f77afc43c4a64b5a"}, + {file = "kiwisolver-1.4.4-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:787518a6789009c159453da4d6b683f468ef7a65bbde796bcea803ccf191058d"}, + {file = "kiwisolver-1.4.4-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da152d8cdcab0e56e4f45eb08b9aea6455845ec83172092f09b0e077ece2cf7a"}, + {file = "kiwisolver-1.4.4-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:ecb1fa0db7bf4cff9dac752abb19505a233c7f16684c5826d1f11ebd9472b871"}, + {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:28bc5b299f48150b5f822ce68624e445040595a4ac3d59251703779836eceff9"}, + {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:81e38381b782cc7e1e46c4e14cd997ee6040768101aefc8fa3c24a4cc58e98f8"}, + {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2a66fdfb34e05b705620dd567f5a03f239a088d5a3f321e7b6ac3239d22aa286"}, + {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:872b8ca05c40d309ed13eb2e582cab0c5a05e81e987ab9c521bf05ad1d5cf5cb"}, + {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:70e7c2e7b750585569564e2e5ca9845acfaa5da56ac46df68414f29fea97be9f"}, + {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9f85003f5dfa867e86d53fac6f7e6f30c045673fa27b603c397753bebadc3008"}, + {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e307eb9bd99801f82789b44bb45e9f541961831c7311521b13a6c85afc09767"}, + {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1792d939ec70abe76f5054d3f36ed5656021dcad1322d1cc996d4e54165cef9"}, + {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6cb459eea32a4e2cf18ba5fcece2dbdf496384413bc1bae15583f19e567f3b2"}, + {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:36dafec3d6d6088d34e2de6b85f9d8e2324eb734162fba59d2ba9ed7a2043d5b"}, + {file = "kiwisolver-1.4.4.tar.gz", hash = "sha256:d41997519fcba4a1e46eb4a2fe31bc12f0ff957b2b81bac28db24744f333e955"}, +] + +[[package]] +name = "livereload" +version = "2.6.3" +description = "Python LiveReload is an awesome tool for web developers" +optional = false +python-versions = "*" +files = [ + {file = "livereload-2.6.3-py2.py3-none-any.whl", hash = "sha256:ad4ac6f53b2d62bb6ce1a5e6e96f1f00976a32348afedcb4b6d68df2a1d346e4"}, + {file = "livereload-2.6.3.tar.gz", hash = "sha256:776f2f865e59fde56490a56bcc6773b6917366bce0c267c60ee8aaf1a0959869"}, +] + +[package.dependencies] +six = "*" +tornado = {version = "*", markers = "python_version > \"2.7\""} + +[[package]] +name = "lxml" +version = "4.9.2" +description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*" +files = [ + {file = "lxml-4.9.2-cp27-cp27m-macosx_10_15_x86_64.whl", hash = "sha256:76cf573e5a365e790396a5cc2b909812633409306c6531a6877c59061e42c4f2"}, + {file = "lxml-4.9.2-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b1f42b6921d0e81b1bcb5e395bc091a70f41c4d4e55ba99c6da2b31626c44892"}, + {file = "lxml-4.9.2-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:9f102706d0ca011de571de32c3247c6476b55bb6bc65a20f682f000b07a4852a"}, + {file = "lxml-4.9.2-cp27-cp27m-win32.whl", hash = "sha256:8d0b4612b66ff5d62d03bcaa043bb018f74dfea51184e53f067e6fdcba4bd8de"}, + {file = "lxml-4.9.2-cp27-cp27m-win_amd64.whl", hash = "sha256:4c8f293f14abc8fd3e8e01c5bd86e6ed0b6ef71936ded5bf10fe7a5efefbaca3"}, + {file = "lxml-4.9.2-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2899456259589aa38bfb018c364d6ae7b53c5c22d8e27d0ec7609c2a1ff78b50"}, + {file = "lxml-4.9.2-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6749649eecd6a9871cae297bffa4ee76f90b4504a2a2ab528d9ebe912b101975"}, + {file = "lxml-4.9.2-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:a08cff61517ee26cb56f1e949cca38caabe9ea9fbb4b1e10a805dc39844b7d5c"}, + {file = "lxml-4.9.2-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:85cabf64adec449132e55616e7ca3e1000ab449d1d0f9d7f83146ed5bdcb6d8a"}, + {file = "lxml-4.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:8340225bd5e7a701c0fa98284c849c9b9fc9238abf53a0ebd90900f25d39a4e4"}, + {file = "lxml-4.9.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:1ab8f1f932e8f82355e75dda5413a57612c6ea448069d4fb2e217e9a4bed13d4"}, + {file = "lxml-4.9.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:699a9af7dffaf67deeae27b2112aa06b41c370d5e7633e0ee0aea2e0b6c211f7"}, + {file = "lxml-4.9.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b9cc34af337a97d470040f99ba4282f6e6bac88407d021688a5d585e44a23184"}, + {file = "lxml-4.9.2-cp310-cp310-win32.whl", hash = "sha256:d02a5399126a53492415d4906ab0ad0375a5456cc05c3fc0fc4ca11771745cda"}, + {file = "lxml-4.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:a38486985ca49cfa574a507e7a2215c0c780fd1778bb6290c21193b7211702ab"}, + {file = "lxml-4.9.2-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:c83203addf554215463b59f6399835201999b5e48019dc17f182ed5ad87205c9"}, + {file = "lxml-4.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:2a87fa548561d2f4643c99cd13131acb607ddabb70682dcf1dff5f71f781a4bf"}, + {file = "lxml-4.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:d6b430a9938a5a5d85fc107d852262ddcd48602c120e3dbb02137c83d212b380"}, + {file = "lxml-4.9.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3efea981d956a6f7173b4659849f55081867cf897e719f57383698af6f618a92"}, + {file = "lxml-4.9.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:df0623dcf9668ad0445e0558a21211d4e9a149ea8f5666917c8eeec515f0a6d1"}, + {file = "lxml-4.9.2-cp311-cp311-win32.whl", hash = "sha256:da248f93f0418a9e9d94b0080d7ebc407a9a5e6d0b57bb30db9b5cc28de1ad33"}, + {file = "lxml-4.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:3818b8e2c4b5148567e1b09ce739006acfaa44ce3156f8cbbc11062994b8e8dd"}, + {file = "lxml-4.9.2-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ca989b91cf3a3ba28930a9fc1e9aeafc2a395448641df1f387a2d394638943b0"}, + {file = "lxml-4.9.2-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:822068f85e12a6e292803e112ab876bc03ed1f03dddb80154c395f891ca6b31e"}, + {file = "lxml-4.9.2-cp35-cp35m-win32.whl", hash = "sha256:be7292c55101e22f2a3d4d8913944cbea71eea90792bf914add27454a13905df"}, + {file = "lxml-4.9.2-cp35-cp35m-win_amd64.whl", hash = "sha256:998c7c41910666d2976928c38ea96a70d1aa43be6fe502f21a651e17483a43c5"}, + {file = "lxml-4.9.2-cp36-cp36m-macosx_10_15_x86_64.whl", hash = "sha256:b26a29f0b7fc6f0897f043ca366142d2b609dc60756ee6e4e90b5f762c6adc53"}, + {file = "lxml-4.9.2-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:ab323679b8b3030000f2be63e22cdeea5b47ee0abd2d6a1dc0c8103ddaa56cd7"}, + {file = "lxml-4.9.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:689bb688a1db722485e4610a503e3e9210dcc20c520b45ac8f7533c837be76fe"}, + {file = "lxml-4.9.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:f49e52d174375a7def9915c9f06ec4e569d235ad428f70751765f48d5926678c"}, + {file = "lxml-4.9.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:36c3c175d34652a35475a73762b545f4527aec044910a651d2bf50de9c3352b1"}, + {file = "lxml-4.9.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a35f8b7fa99f90dd2f5dc5a9fa12332642f087a7641289ca6c40d6e1a2637d8e"}, + {file = "lxml-4.9.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:58bfa3aa19ca4c0f28c5dde0ff56c520fbac6f0daf4fac66ed4c8d2fb7f22e74"}, + {file = "lxml-4.9.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:bc718cd47b765e790eecb74d044cc8d37d58562f6c314ee9484df26276d36a38"}, + {file = "lxml-4.9.2-cp36-cp36m-win32.whl", hash = "sha256:d5bf6545cd27aaa8a13033ce56354ed9e25ab0e4ac3b5392b763d8d04b08e0c5"}, + {file = "lxml-4.9.2-cp36-cp36m-win_amd64.whl", hash = "sha256:3ab9fa9d6dc2a7f29d7affdf3edebf6ece6fb28a6d80b14c3b2fb9d39b9322c3"}, + {file = "lxml-4.9.2-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:05ca3f6abf5cf78fe053da9b1166e062ade3fa5d4f92b4ed688127ea7d7b1d03"}, + {file = "lxml-4.9.2-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:a5da296eb617d18e497bcf0a5c528f5d3b18dadb3619fbdadf4ed2356ef8d941"}, + {file = "lxml-4.9.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:04876580c050a8c5341d706dd464ff04fd597095cc8c023252566a8826505726"}, + {file = "lxml-4.9.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:c9ec3eaf616d67db0764b3bb983962b4f385a1f08304fd30c7283954e6a7869b"}, + {file = "lxml-4.9.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2a29ba94d065945944016b6b74e538bdb1751a1db6ffb80c9d3c2e40d6fa9894"}, + {file = "lxml-4.9.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a82d05da00a58b8e4c0008edbc8a4b6ec5a4bc1e2ee0fb6ed157cf634ed7fa45"}, + {file = "lxml-4.9.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:223f4232855ade399bd409331e6ca70fb5578efef22cf4069a6090acc0f53c0e"}, + {file = "lxml-4.9.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d17bc7c2ccf49c478c5bdd447594e82692c74222698cfc9b5daae7ae7e90743b"}, + {file = "lxml-4.9.2-cp37-cp37m-win32.whl", hash = "sha256:b64d891da92e232c36976c80ed7ebb383e3f148489796d8d31a5b6a677825efe"}, + {file = "lxml-4.9.2-cp37-cp37m-win_amd64.whl", hash = "sha256:a0a336d6d3e8b234a3aae3c674873d8f0e720b76bc1d9416866c41cd9500ffb9"}, + {file = "lxml-4.9.2-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:da4dd7c9c50c059aba52b3524f84d7de956f7fef88f0bafcf4ad7dde94a064e8"}, + {file = "lxml-4.9.2-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:821b7f59b99551c69c85a6039c65b75f5683bdc63270fec660f75da67469ca24"}, + {file = "lxml-4.9.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:e5168986b90a8d1f2f9dc1b841467c74221bd752537b99761a93d2d981e04889"}, + {file = "lxml-4.9.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:8e20cb5a47247e383cf4ff523205060991021233ebd6f924bca927fcf25cf86f"}, + {file = "lxml-4.9.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:13598ecfbd2e86ea7ae45ec28a2a54fb87ee9b9fdb0f6d343297d8e548392c03"}, + {file = "lxml-4.9.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:880bbbcbe2fca64e2f4d8e04db47bcdf504936fa2b33933efd945e1b429bea8c"}, + {file = "lxml-4.9.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7d2278d59425777cfcb19735018d897ca8303abe67cc735f9f97177ceff8027f"}, + {file = "lxml-4.9.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5344a43228767f53a9df6e5b253f8cdca7dfc7b7aeae52551958192f56d98457"}, + {file = "lxml-4.9.2-cp38-cp38-win32.whl", hash = "sha256:925073b2fe14ab9b87e73f9a5fde6ce6392da430f3004d8b72cc86f746f5163b"}, + {file = "lxml-4.9.2-cp38-cp38-win_amd64.whl", hash = "sha256:9b22c5c66f67ae00c0199f6055705bc3eb3fcb08d03d2ec4059a2b1b25ed48d7"}, + {file = "lxml-4.9.2-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:5f50a1c177e2fa3ee0667a5ab79fdc6b23086bc8b589d90b93b4bd17eb0e64d1"}, + {file = "lxml-4.9.2-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:090c6543d3696cbe15b4ac6e175e576bcc3f1ccfbba970061b7300b0c15a2140"}, + {file = "lxml-4.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:63da2ccc0857c311d764e7d3d90f429c252e83b52d1f8f1d1fe55be26827d1f4"}, + {file = "lxml-4.9.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:5b4545b8a40478183ac06c073e81a5ce4cf01bf1734962577cf2bb569a5b3bbf"}, + {file = "lxml-4.9.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2e430cd2824f05f2d4f687701144556646bae8f249fd60aa1e4c768ba7018947"}, + {file = "lxml-4.9.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6804daeb7ef69e7b36f76caddb85cccd63d0c56dedb47555d2fc969e2af6a1a5"}, + {file = "lxml-4.9.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a6e441a86553c310258aca15d1c05903aaf4965b23f3bc2d55f200804e005ee5"}, + {file = "lxml-4.9.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ca34efc80a29351897e18888c71c6aca4a359247c87e0b1c7ada14f0ab0c0fb2"}, + {file = "lxml-4.9.2-cp39-cp39-win32.whl", hash = "sha256:6b418afe5df18233fc6b6093deb82a32895b6bb0b1155c2cdb05203f583053f1"}, + {file = "lxml-4.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:f1496ea22ca2c830cbcbd473de8f114a320da308438ae65abad6bab7867fe38f"}, + {file = "lxml-4.9.2-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:b264171e3143d842ded311b7dccd46ff9ef34247129ff5bf5066123c55c2431c"}, + {file = "lxml-4.9.2-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:0dc313ef231edf866912e9d8f5a042ddab56c752619e92dfd3a2c277e6a7299a"}, + {file = "lxml-4.9.2-pp38-pypy38_pp73-macosx_10_15_x86_64.whl", hash = "sha256:16efd54337136e8cd72fb9485c368d91d77a47ee2d42b057564aae201257d419"}, + {file = "lxml-4.9.2-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:0f2b1e0d79180f344ff9f321327b005ca043a50ece8713de61d1cb383fb8ac05"}, + {file = "lxml-4.9.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:7b770ed79542ed52c519119473898198761d78beb24b107acf3ad65deae61f1f"}, + {file = "lxml-4.9.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:efa29c2fe6b4fdd32e8ef81c1528506895eca86e1d8c4657fda04c9b3786ddf9"}, + {file = "lxml-4.9.2-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7e91ee82f4199af8c43d8158024cbdff3d931df350252288f0d4ce656df7f3b5"}, + {file = "lxml-4.9.2-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:b23e19989c355ca854276178a0463951a653309fb8e57ce674497f2d9f208746"}, + {file = "lxml-4.9.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:01d36c05f4afb8f7c20fd9ed5badca32a2029b93b1750f571ccc0b142531caf7"}, + {file = "lxml-4.9.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7b515674acfdcadb0eb5d00d8a709868173acece5cb0be3dd165950cbfdf5409"}, + {file = "lxml-4.9.2.tar.gz", hash = "sha256:2455cfaeb7ac70338b3257f41e21f0724f4b5b0c0e7702da67ee6c3640835b67"}, +] + +[package.extras] +cssselect = ["cssselect (>=0.7)"] +html5 = ["html5lib"] +htmlsoup = ["BeautifulSoup4"] +source = ["Cython (>=0.29.7)"] + +[[package]] +name = "markupsafe" +version = "2.0.1" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.6" +files = [ + {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4dc8f9fb58f7364b63fd9f85013b780ef83c11857ae79f2feda41e270468dd9b"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:20dca64a3ef2d6e4d5d615a3fd418ad3bde77a47ec8a23d984a12b5b4c74491a"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cdfba22ea2f0029c9261a4bd07e830a8da012291fbe44dc794e488b6c9bb353a"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-win32.whl", hash = "sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:deb993cacb280823246a026e3b2d81c493c53de6acfd5e6bfe31ab3402bb37dd"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:63f3268ba69ace99cab4e3e3b5840b03340efed0948ab8f78d2fd87ee5442a4f"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:8d206346619592c6200148b01a2142798c989edcb9c896f9ac9722a99d4e77e6"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d6c7ebd4e944c85e2c3421e612a7057a2f48d478d79e61800d81468a8d842207"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f0567c4dc99f264f49fe27da5f735f414c4e7e7dd850cfd8e69f0862d7c74ea9"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:89c687013cb1cd489a0f0ac24febe8c7a666e6e221b783e53ac50ebf68e45d86"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:aca6377c0cb8a8253e493c6b451565ac77e98c2951c45f913e0b52facdcff83f"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:04635854b943835a6ea959e948d19dcd311762c5c0c6e1f0e16ee57022669194"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6300b8454aa6930a24b9618fbb54b5a68135092bc666f7b06901f897fa5c2fee"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-win32.whl", hash = "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4296f2b1ce8c86a6aea78613c34bb1a672ea0e3de9c6ba08a960efe0b0a09047"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f02365d4e99430a12647f09b6cc8bab61a6564363f313126f775eb4f6ef798e"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5b6d930f030f8ed98e3e6c98ffa0652bdb82601e7a016ec2ab5d7ff23baa78d1"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-win32.whl", hash = "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"}, + {file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"}, +] + +[[package]] +name = "matplotlib" +version = "3.7.1" +description = "Python plotting package" +optional = false +python-versions = ">=3.8" +files = [ + {file = "matplotlib-3.7.1-cp310-cp310-macosx_10_12_universal2.whl", hash = "sha256:95cbc13c1fc6844ab8812a525bbc237fa1470863ff3dace7352e910519e194b1"}, + {file = "matplotlib-3.7.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:08308bae9e91aca1ec6fd6dda66237eef9f6294ddb17f0d0b3c863169bf82353"}, + {file = "matplotlib-3.7.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:544764ba51900da4639c0f983b323d288f94f65f4024dc40ecb1542d74dc0500"}, + {file = "matplotlib-3.7.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56d94989191de3fcc4e002f93f7f1be5da476385dde410ddafbb70686acf00ea"}, + {file = "matplotlib-3.7.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e99bc9e65901bb9a7ce5e7bb24af03675cbd7c70b30ac670aa263240635999a4"}, + {file = "matplotlib-3.7.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb7d248c34a341cd4c31a06fd34d64306624c8cd8d0def7abb08792a5abfd556"}, + {file = "matplotlib-3.7.1-cp310-cp310-win32.whl", hash = "sha256:ce463ce590f3825b52e9fe5c19a3c6a69fd7675a39d589e8b5fbe772272b3a24"}, + {file = "matplotlib-3.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:3d7bc90727351fb841e4d8ae620d2d86d8ed92b50473cd2b42ce9186104ecbba"}, + {file = "matplotlib-3.7.1-cp311-cp311-macosx_10_12_universal2.whl", hash = "sha256:770a205966d641627fd5cf9d3cb4b6280a716522cd36b8b284a8eb1581310f61"}, + {file = "matplotlib-3.7.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:f67bfdb83a8232cb7a92b869f9355d677bce24485c460b19d01970b64b2ed476"}, + {file = "matplotlib-3.7.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2bf092f9210e105f414a043b92af583c98f50050559616930d884387d0772aba"}, + {file = "matplotlib-3.7.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89768d84187f31717349c6bfadc0e0d8c321e8eb34522acec8a67b1236a66332"}, + {file = "matplotlib-3.7.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83111e6388dec67822e2534e13b243cc644c7494a4bb60584edbff91585a83c6"}, + {file = "matplotlib-3.7.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a867bf73a7eb808ef2afbca03bcdb785dae09595fbe550e1bab0cd023eba3de0"}, + {file = "matplotlib-3.7.1-cp311-cp311-win32.whl", hash = "sha256:fbdeeb58c0cf0595efe89c05c224e0a502d1aa6a8696e68a73c3efc6bc354304"}, + {file = "matplotlib-3.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:c0bd19c72ae53e6ab979f0ac6a3fafceb02d2ecafa023c5cca47acd934d10be7"}, + {file = "matplotlib-3.7.1-cp38-cp38-macosx_10_12_universal2.whl", hash = "sha256:6eb88d87cb2c49af00d3bbc33a003f89fd9f78d318848da029383bfc08ecfbfb"}, + {file = "matplotlib-3.7.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:cf0e4f727534b7b1457898c4f4ae838af1ef87c359b76dcd5330fa31893a3ac7"}, + {file = "matplotlib-3.7.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:46a561d23b91f30bccfd25429c3c706afe7d73a5cc64ef2dfaf2b2ac47c1a5dc"}, + {file = "matplotlib-3.7.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8704726d33e9aa8a6d5215044b8d00804561971163563e6e6591f9dcf64340cc"}, + {file = "matplotlib-3.7.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4cf327e98ecf08fcbb82685acaf1939d3338548620ab8dfa02828706402c34de"}, + {file = "matplotlib-3.7.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:617f14ae9d53292ece33f45cba8503494ee199a75b44de7717964f70637a36aa"}, + {file = "matplotlib-3.7.1-cp38-cp38-win32.whl", hash = "sha256:7c9a4b2da6fac77bcc41b1ea95fadb314e92508bf5493ceff058e727e7ecf5b0"}, + {file = "matplotlib-3.7.1-cp38-cp38-win_amd64.whl", hash = "sha256:14645aad967684e92fc349493fa10c08a6da514b3d03a5931a1bac26e6792bd1"}, + {file = "matplotlib-3.7.1-cp39-cp39-macosx_10_12_universal2.whl", hash = "sha256:81a6b377ea444336538638d31fdb39af6be1a043ca5e343fe18d0f17e098770b"}, + {file = "matplotlib-3.7.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:28506a03bd7f3fe59cd3cd4ceb2a8d8a2b1db41afede01f66c42561b9be7b4b7"}, + {file = "matplotlib-3.7.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8c587963b85ce41e0a8af53b9b2de8dddbf5ece4c34553f7bd9d066148dc719c"}, + {file = "matplotlib-3.7.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8bf26ade3ff0f27668989d98c8435ce9327d24cffb7f07d24ef609e33d582439"}, + {file = "matplotlib-3.7.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:def58098f96a05f90af7e92fd127d21a287068202aa43b2a93476170ebd99e87"}, + {file = "matplotlib-3.7.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f883a22a56a84dba3b588696a2b8a1ab0d2c3d41be53264115c71b0a942d8fdb"}, + {file = "matplotlib-3.7.1-cp39-cp39-win32.whl", hash = "sha256:4f99e1b234c30c1e9714610eb0c6d2f11809c9c78c984a613ae539ea2ad2eb4b"}, + {file = "matplotlib-3.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:3ba2af245e36990facf67fde840a760128ddd71210b2ab6406e640188d69d136"}, + {file = "matplotlib-3.7.1-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3032884084f541163f295db8a6536e0abb0db464008fadca6c98aaf84ccf4717"}, + {file = "matplotlib-3.7.1-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a2cb34336110e0ed8bb4f650e817eed61fa064acbefeb3591f1b33e3a84fd96"}, + {file = "matplotlib-3.7.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b867e2f952ed592237a1828f027d332d8ee219ad722345b79a001f49df0936eb"}, + {file = "matplotlib-3.7.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:57bfb8c8ea253be947ccb2bc2d1bb3862c2bccc662ad1b4626e1f5e004557042"}, + {file = "matplotlib-3.7.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:438196cdf5dc8d39b50a45cb6e3f6274edbcf2254f85fa9b895bf85851c3a613"}, + {file = "matplotlib-3.7.1-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:21e9cff1a58d42e74d01153360de92b326708fb205250150018a52c70f43c290"}, + {file = "matplotlib-3.7.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75d4725d70b7c03e082bbb8a34639ede17f333d7247f56caceb3801cb6ff703d"}, + {file = "matplotlib-3.7.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:97cc368a7268141afb5690760921765ed34867ffb9655dd325ed207af85c7529"}, + {file = "matplotlib-3.7.1.tar.gz", hash = "sha256:7b73305f25eab4541bd7ee0b96d87e53ae9c9f1823be5659b806cd85786fe882"}, +] + +[package.dependencies] +contourpy = ">=1.0.1" +cycler = ">=0.10" +fonttools = ">=4.22.0" +importlib-resources = {version = ">=3.2.0", markers = "python_version < \"3.10\""} +kiwisolver = ">=1.0.1" +numpy = ">=1.20" +packaging = ">=20.0" +pillow = ">=6.2.0" +pyparsing = ">=2.3.1" +python-dateutil = ">=2.7" + +[[package]] +name = "multidict" +version = "6.0.4" +description = "multidict implementation" +optional = false +python-versions = ">=3.7" +files = [ + {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b1a97283e0c85772d613878028fec909f003993e1007eafa715b24b377cb9b8"}, + {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eeb6dcc05e911516ae3d1f207d4b0520d07f54484c49dfc294d6e7d63b734171"}, + {file = "multidict-6.0.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d6d635d5209b82a3492508cf5b365f3446afb65ae7ebd755e70e18f287b0adf7"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c048099e4c9e9d615545e2001d3d8a4380bd403e1a0578734e0d31703d1b0c0b"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ea20853c6dbbb53ed34cb4d080382169b6f4554d394015f1bef35e881bf83547"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16d232d4e5396c2efbbf4f6d4df89bfa905eb0d4dc5b3549d872ab898451f569"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36c63aaa167f6c6b04ef2c85704e93af16c11d20de1d133e39de6a0e84582a93"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:64bdf1086b6043bf519869678f5f2757f473dee970d7abf6da91ec00acb9cb98"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:43644e38f42e3af682690876cff722d301ac585c5b9e1eacc013b7a3f7b696a0"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7582a1d1030e15422262de9f58711774e02fa80df0d1578995c76214f6954988"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ddff9c4e225a63a5afab9dd15590432c22e8057e1a9a13d28ed128ecf047bbdc"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ee2a1ece51b9b9e7752e742cfb661d2a29e7bcdba2d27e66e28a99f1890e4fa0"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a2e4369eb3d47d2034032a26c7a80fcb21a2cb22e1173d761a162f11e562caa5"}, + {file = "multidict-6.0.4-cp310-cp310-win32.whl", hash = "sha256:574b7eae1ab267e5f8285f0fe881f17efe4b98c39a40858247720935b893bba8"}, + {file = "multidict-6.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:4dcbb0906e38440fa3e325df2359ac6cb043df8e58c965bb45f4e406ecb162cc"}, + {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0dfad7a5a1e39c53ed00d2dd0c2e36aed4650936dc18fd9a1826a5ae1cad6f03"}, + {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:64da238a09d6039e3bd39bb3aee9c21a5e34f28bfa5aa22518581f910ff94af3"}, + {file = "multidict-6.0.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ff959bee35038c4624250473988b24f846cbeb2c6639de3602c073f10410ceba"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01a3a55bd90018c9c080fbb0b9f4891db37d148a0a18722b42f94694f8b6d4c9"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c5cb09abb18c1ea940fb99360ea0396f34d46566f157122c92dfa069d3e0e982"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:666daae833559deb2d609afa4490b85830ab0dfca811a98b70a205621a6109fe"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11bdf3f5e1518b24530b8241529d2050014c884cf18b6fc69c0c2b30ca248710"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d18748f2d30f94f498e852c67d61261c643b349b9d2a581131725595c45ec6c"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:458f37be2d9e4c95e2d8866a851663cbc76e865b78395090786f6cd9b3bbf4f4"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b1a2eeedcead3a41694130495593a559a668f382eee0727352b9a41e1c45759a"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7d6ae9d593ef8641544d6263c7fa6408cc90370c8cb2bbb65f8d43e5b0351d9c"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:5979b5632c3e3534e42ca6ff856bb24b2e3071b37861c2c727ce220d80eee9ed"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dcfe792765fab89c365123c81046ad4103fcabbc4f56d1c1997e6715e8015461"}, + {file = "multidict-6.0.4-cp311-cp311-win32.whl", hash = "sha256:3601a3cece3819534b11d4efc1eb76047488fddd0c85a3948099d5da4d504636"}, + {file = "multidict-6.0.4-cp311-cp311-win_amd64.whl", hash = "sha256:81a4f0b34bd92df3da93315c6a59034df95866014ac08535fc819f043bfd51f0"}, + {file = "multidict-6.0.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:67040058f37a2a51ed8ea8f6b0e6ee5bd78ca67f169ce6122f3e2ec80dfe9b78"}, + {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:853888594621e6604c978ce2a0444a1e6e70c8d253ab65ba11657659dcc9100f"}, + {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:39ff62e7d0f26c248b15e364517a72932a611a9b75f35b45be078d81bdb86603"}, + {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af048912e045a2dc732847d33821a9d84ba553f5c5f028adbd364dd4765092ac"}, + {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1e8b901e607795ec06c9e42530788c45ac21ef3aaa11dbd0c69de543bfb79a9"}, + {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62501642008a8b9871ddfccbf83e4222cf8ac0d5aeedf73da36153ef2ec222d2"}, + {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:99b76c052e9f1bc0721f7541e5e8c05db3941eb9ebe7b8553c625ef88d6eefde"}, + {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:509eac6cf09c794aa27bcacfd4d62c885cce62bef7b2c3e8b2e49d365b5003fe"}, + {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:21a12c4eb6ddc9952c415f24eef97e3e55ba3af61f67c7bc388dcdec1404a067"}, + {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:5cad9430ab3e2e4fa4a2ef4450f548768400a2ac635841bc2a56a2052cdbeb87"}, + {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ab55edc2e84460694295f401215f4a58597f8f7c9466faec545093045476327d"}, + {file = "multidict-6.0.4-cp37-cp37m-win32.whl", hash = "sha256:5a4dcf02b908c3b8b17a45fb0f15b695bf117a67b76b7ad18b73cf8e92608775"}, + {file = "multidict-6.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:6ed5f161328b7df384d71b07317f4d8656434e34591f20552c7bcef27b0ab88e"}, + {file = "multidict-6.0.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5fc1b16f586f049820c5c5b17bb4ee7583092fa0d1c4e28b5239181ff9532e0c"}, + {file = "multidict-6.0.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1502e24330eb681bdaa3eb70d6358e818e8e8f908a22a1851dfd4e15bc2f8161"}, + {file = "multidict-6.0.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b692f419760c0e65d060959df05f2a531945af31fda0c8a3b3195d4efd06de11"}, + {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45e1ecb0379bfaab5eef059f50115b54571acfbe422a14f668fc8c27ba410e7e"}, + {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ddd3915998d93fbcd2566ddf9cf62cdb35c9e093075f862935573d265cf8f65d"}, + {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:59d43b61c59d82f2effb39a93c48b845efe23a3852d201ed2d24ba830d0b4cf2"}, + {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc8e1d0c705233c5dd0c5e6460fbad7827d5d36f310a0fadfd45cc3029762258"}, + {file = "multidict-6.0.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6aa0418fcc838522256761b3415822626f866758ee0bc6632c9486b179d0b52"}, + {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6748717bb10339c4760c1e63da040f5f29f5ed6e59d76daee30305894069a660"}, + {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4d1a3d7ef5e96b1c9e92f973e43aa5e5b96c659c9bc3124acbbd81b0b9c8a951"}, + {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4372381634485bec7e46718edc71528024fcdc6f835baefe517b34a33c731d60"}, + {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:fc35cb4676846ef752816d5be2193a1e8367b4c1397b74a565a9d0389c433a1d"}, + {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4b9d9e4e2b37daddb5c23ea33a3417901fa7c7b3dee2d855f63ee67a0b21e5b1"}, + {file = "multidict-6.0.4-cp38-cp38-win32.whl", hash = "sha256:e41b7e2b59679edfa309e8db64fdf22399eec4b0b24694e1b2104fb789207779"}, + {file = "multidict-6.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:d6c254ba6e45d8e72739281ebc46ea5eb5f101234f3ce171f0e9f5cc86991480"}, + {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:16ab77bbeb596e14212e7bab8429f24c1579234a3a462105cda4a66904998664"}, + {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc779e9e6f7fda81b3f9aa58e3a6091d49ad528b11ed19f6621408806204ad35"}, + {file = "multidict-6.0.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ceef517eca3e03c1cceb22030a3e39cb399ac86bff4e426d4fc6ae49052cc60"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:281af09f488903fde97923c7744bb001a9b23b039a909460d0f14edc7bf59706"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:52f2dffc8acaba9a2f27174c41c9e57f60b907bb9f096b36b1a1f3be71c6284d"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b41156839806aecb3641f3208c0dafd3ac7775b9c4c422d82ee2a45c34ba81ca"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5e3fc56f88cc98ef8139255cf8cd63eb2c586531e43310ff859d6bb3a6b51f1"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8316a77808c501004802f9beebde51c9f857054a0c871bd6da8280e718444449"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f70b98cd94886b49d91170ef23ec5c0e8ebb6f242d734ed7ed677b24d50c82cf"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bf6774e60d67a9efe02b3616fee22441d86fab4c6d335f9d2051d19d90a40063"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:e69924bfcdda39b722ef4d9aa762b2dd38e4632b3641b1d9a57ca9cd18f2f83a"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:6b181d8c23da913d4ff585afd1155a0e1194c0b50c54fcfe286f70cdaf2b7176"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:52509b5be062d9eafc8170e53026fbc54cf3b32759a23d07fd935fb04fc22d95"}, + {file = "multidict-6.0.4-cp39-cp39-win32.whl", hash = "sha256:27c523fbfbdfd19c6867af7346332b62b586eed663887392cff78d614f9ec313"}, + {file = "multidict-6.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:33029f5734336aa0d4c0384525da0387ef89148dc7191aae00ca5fb23d7aafc2"}, + {file = "multidict-6.0.4.tar.gz", hash = "sha256:3666906492efb76453c0e7b97f2cf459b0682e7402c0489a95484965dbc1da49"}, +] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + +[[package]] +name = "numpy" +version = "1.24.3" +description = "Fundamental package for array computing in Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "numpy-1.24.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3c1104d3c036fb81ab923f507536daedc718d0ad5a8707c6061cdfd6d184e570"}, + {file = "numpy-1.24.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:202de8f38fc4a45a3eea4b63e2f376e5f2dc64ef0fa692838e31a808520efaf7"}, + {file = "numpy-1.24.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8535303847b89aa6b0f00aa1dc62867b5a32923e4d1681a35b5eef2d9591a463"}, + {file = "numpy-1.24.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d926b52ba1367f9acb76b0df6ed21f0b16a1ad87c6720a1121674e5cf63e2b6"}, + {file = "numpy-1.24.3-cp310-cp310-win32.whl", hash = "sha256:f21c442fdd2805e91799fbe044a7b999b8571bb0ab0f7850d0cb9641a687092b"}, + {file = "numpy-1.24.3-cp310-cp310-win_amd64.whl", hash = "sha256:ab5f23af8c16022663a652d3b25dcdc272ac3f83c3af4c02eb8b824e6b3ab9d7"}, + {file = "numpy-1.24.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9a7721ec204d3a237225db3e194c25268faf92e19338a35f3a224469cb6039a3"}, + {file = "numpy-1.24.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d6cc757de514c00b24ae8cf5c876af2a7c3df189028d68c0cb4eaa9cd5afc2bf"}, + {file = "numpy-1.24.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76e3f4e85fc5d4fd311f6e9b794d0c00e7002ec122be271f2019d63376f1d385"}, + {file = "numpy-1.24.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1d3c026f57ceaad42f8231305d4653d5f05dc6332a730ae5c0bea3513de0950"}, + {file = "numpy-1.24.3-cp311-cp311-win32.whl", hash = "sha256:c91c4afd8abc3908e00a44b2672718905b8611503f7ff87390cc0ac3423fb096"}, + {file = "numpy-1.24.3-cp311-cp311-win_amd64.whl", hash = "sha256:5342cf6aad47943286afa6f1609cad9b4266a05e7f2ec408e2cf7aea7ff69d80"}, + {file = "numpy-1.24.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7776ea65423ca6a15255ba1872d82d207bd1e09f6d0894ee4a64678dd2204078"}, + {file = "numpy-1.24.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ae8d0be48d1b6ed82588934aaaa179875e7dc4f3d84da18d7eae6eb3f06c242c"}, + {file = "numpy-1.24.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ecde0f8adef7dfdec993fd54b0f78183051b6580f606111a6d789cd14c61ea0c"}, + {file = "numpy-1.24.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4749e053a29364d3452c034827102ee100986903263e89884922ef01a0a6fd2f"}, + {file = "numpy-1.24.3-cp38-cp38-win32.whl", hash = "sha256:d933fabd8f6a319e8530d0de4fcc2e6a61917e0b0c271fded460032db42a0fe4"}, + {file = "numpy-1.24.3-cp38-cp38-win_amd64.whl", hash = "sha256:56e48aec79ae238f6e4395886b5eaed058abb7231fb3361ddd7bfdf4eed54289"}, + {file = "numpy-1.24.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4719d5aefb5189f50887773699eaf94e7d1e02bf36c1a9d353d9f46703758ca4"}, + {file = "numpy-1.24.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0ec87a7084caa559c36e0a2309e4ecb1baa03b687201d0a847c8b0ed476a7187"}, + {file = "numpy-1.24.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea8282b9bcfe2b5e7d491d0bf7f3e2da29700cec05b49e64d6246923329f2b02"}, + {file = "numpy-1.24.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:210461d87fb02a84ef243cac5e814aad2b7f4be953b32cb53327bb49fd77fbb4"}, + {file = "numpy-1.24.3-cp39-cp39-win32.whl", hash = "sha256:784c6da1a07818491b0ffd63c6bbe5a33deaa0e25a20e1b3ea20cf0e43f8046c"}, + {file = "numpy-1.24.3-cp39-cp39-win_amd64.whl", hash = "sha256:d5036197ecae68d7f491fcdb4df90082b0d4960ca6599ba2659957aafced7c17"}, + {file = "numpy-1.24.3-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:352ee00c7f8387b44d19f4cada524586f07379c0d49270f87233983bc5087ca0"}, + {file = "numpy-1.24.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a7d6acc2e7524c9955e5c903160aa4ea083736fde7e91276b0e5d98e6332812"}, + {file = "numpy-1.24.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:35400e6a8d102fd07c71ed7dcadd9eb62ee9a6e84ec159bd48c28235bbb0f8e4"}, + {file = "numpy-1.24.3.tar.gz", hash = "sha256:ab344f1bf21f140adab8e47fdbc7c35a477dc01408791f8ba00d018dd0bc5155"}, +] + +[[package]] +name = "packaging" +version = "23.1" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, + {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, +] + +[[package]] +name = "pillow" +version = "9.5.0" +description = "Python Imaging Library (Fork)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "Pillow-9.5.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:ace6ca218308447b9077c14ea4ef381ba0b67ee78d64046b3f19cf4e1139ad16"}, + {file = "Pillow-9.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d3d403753c9d5adc04d4694d35cf0391f0f3d57c8e0030aac09d7678fa8030aa"}, + {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ba1b81ee69573fe7124881762bb4cd2e4b6ed9dd28c9c60a632902fe8db8b38"}, + {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe7e1c262d3392afcf5071df9afa574544f28eac825284596ac6db56e6d11062"}, + {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f36397bf3f7d7c6a3abdea815ecf6fd14e7fcd4418ab24bae01008d8d8ca15e"}, + {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:252a03f1bdddce077eff2354c3861bf437c892fb1832f75ce813ee94347aa9b5"}, + {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:85ec677246533e27770b0de5cf0f9d6e4ec0c212a1f89dfc941b64b21226009d"}, + {file = "Pillow-9.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b416f03d37d27290cb93597335a2f85ed446731200705b22bb927405320de903"}, + {file = "Pillow-9.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1781a624c229cb35a2ac31cc4a77e28cafc8900733a864870c49bfeedacd106a"}, + {file = "Pillow-9.5.0-cp310-cp310-win32.whl", hash = "sha256:8507eda3cd0608a1f94f58c64817e83ec12fa93a9436938b191b80d9e4c0fc44"}, + {file = "Pillow-9.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:d3c6b54e304c60c4181da1c9dadf83e4a54fd266a99c70ba646a9baa626819eb"}, + {file = "Pillow-9.5.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:7ec6f6ce99dab90b52da21cf0dc519e21095e332ff3b399a357c187b1a5eee32"}, + {file = "Pillow-9.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:560737e70cb9c6255d6dcba3de6578a9e2ec4b573659943a5e7e4af13f298f5c"}, + {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96e88745a55b88a7c64fa49bceff363a1a27d9a64e04019c2281049444a571e3"}, + {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d9c206c29b46cfd343ea7cdfe1232443072bbb270d6a46f59c259460db76779a"}, + {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cfcc2c53c06f2ccb8976fb5c71d448bdd0a07d26d8e07e321c103416444c7ad1"}, + {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:a0f9bb6c80e6efcde93ffc51256d5cfb2155ff8f78292f074f60f9e70b942d99"}, + {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:8d935f924bbab8f0a9a28404422da8af4904e36d5c33fc6f677e4c4485515625"}, + {file = "Pillow-9.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fed1e1cf6a42577953abbe8e6cf2fe2f566daebde7c34724ec8803c4c0cda579"}, + {file = "Pillow-9.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c1170d6b195555644f0616fd6ed929dfcf6333b8675fcca044ae5ab110ded296"}, + {file = "Pillow-9.5.0-cp311-cp311-win32.whl", hash = "sha256:54f7102ad31a3de5666827526e248c3530b3a33539dbda27c6843d19d72644ec"}, + {file = "Pillow-9.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:cfa4561277f677ecf651e2b22dc43e8f5368b74a25a8f7d1d4a3a243e573f2d4"}, + {file = "Pillow-9.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:965e4a05ef364e7b973dd17fc765f42233415974d773e82144c9bbaaaea5d089"}, + {file = "Pillow-9.5.0-cp312-cp312-win32.whl", hash = "sha256:22baf0c3cf0c7f26e82d6e1adf118027afb325e703922c8dfc1d5d0156bb2eeb"}, + {file = "Pillow-9.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:432b975c009cf649420615388561c0ce7cc31ce9b2e374db659ee4f7d57a1f8b"}, + {file = "Pillow-9.5.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:5d4ebf8e1db4441a55c509c4baa7a0587a0210f7cd25fcfe74dbbce7a4bd1906"}, + {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:375f6e5ee9620a271acb6820b3d1e94ffa8e741c0601db4c0c4d3cb0a9c224bf"}, + {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99eb6cafb6ba90e436684e08dad8be1637efb71c4f2180ee6b8f940739406e78"}, + {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dfaaf10b6172697b9bceb9a3bd7b951819d1ca339a5ef294d1f1ac6d7f63270"}, + {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:763782b2e03e45e2c77d7779875f4432e25121ef002a41829d8868700d119392"}, + {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:35f6e77122a0c0762268216315bf239cf52b88865bba522999dc38f1c52b9b47"}, + {file = "Pillow-9.5.0-cp37-cp37m-win32.whl", hash = "sha256:aca1c196f407ec7cf04dcbb15d19a43c507a81f7ffc45b690899d6a76ac9fda7"}, + {file = "Pillow-9.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:322724c0032af6692456cd6ed554bb85f8149214d97398bb80613b04e33769f6"}, + {file = "Pillow-9.5.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:a0aa9417994d91301056f3d0038af1199eb7adc86e646a36b9e050b06f526597"}, + {file = "Pillow-9.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f8286396b351785801a976b1e85ea88e937712ee2c3ac653710a4a57a8da5d9c"}, + {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c830a02caeb789633863b466b9de10c015bded434deb3ec87c768e53752ad22a"}, + {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fbd359831c1657d69bb81f0db962905ee05e5e9451913b18b831febfe0519082"}, + {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8fc330c3370a81bbf3f88557097d1ea26cd8b019d6433aa59f71195f5ddebbf"}, + {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:7002d0797a3e4193c7cdee3198d7c14f92c0836d6b4a3f3046a64bd1ce8df2bf"}, + {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:229e2c79c00e85989a34b5981a2b67aa079fd08c903f0aaead522a1d68d79e51"}, + {file = "Pillow-9.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9adf58f5d64e474bed00d69bcd86ec4bcaa4123bfa70a65ce72e424bfb88ed96"}, + {file = "Pillow-9.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:662da1f3f89a302cc22faa9f14a262c2e3951f9dbc9617609a47521c69dd9f8f"}, + {file = "Pillow-9.5.0-cp38-cp38-win32.whl", hash = "sha256:6608ff3bf781eee0cd14d0901a2b9cc3d3834516532e3bd673a0a204dc8615fc"}, + {file = "Pillow-9.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:e49eb4e95ff6fd7c0c402508894b1ef0e01b99a44320ba7d8ecbabefddcc5569"}, + {file = "Pillow-9.5.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:482877592e927fd263028c105b36272398e3e1be3269efda09f6ba21fd83ec66"}, + {file = "Pillow-9.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3ded42b9ad70e5f1754fb7c2e2d6465a9c842e41d178f262e08b8c85ed8a1d8e"}, + {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c446d2245ba29820d405315083d55299a796695d747efceb5717a8b450324115"}, + {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8aca1152d93dcc27dc55395604dcfc55bed5f25ef4c98716a928bacba90d33a3"}, + {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:608488bdcbdb4ba7837461442b90ea6f3079397ddc968c31265c1e056964f1ef"}, + {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:60037a8db8750e474af7ffc9faa9b5859e6c6d0a50e55c45576bf28be7419705"}, + {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:07999f5834bdc404c442146942a2ecadd1cb6292f5229f4ed3b31e0a108746b1"}, + {file = "Pillow-9.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a127ae76092974abfbfa38ca2d12cbeddcdeac0fb71f9627cc1135bedaf9d51a"}, + {file = "Pillow-9.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:489f8389261e5ed43ac8ff7b453162af39c3e8abd730af8363587ba64bb2e865"}, + {file = "Pillow-9.5.0-cp39-cp39-win32.whl", hash = "sha256:9b1af95c3a967bf1da94f253e56b6286b50af23392a886720f563c547e48e964"}, + {file = "Pillow-9.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:77165c4a5e7d5a284f10a6efaa39a0ae8ba839da344f20b111d62cc932fa4e5d"}, + {file = "Pillow-9.5.0-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:833b86a98e0ede388fa29363159c9b1a294b0905b5128baf01db683672f230f5"}, + {file = "Pillow-9.5.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aaf305d6d40bd9632198c766fb64f0c1a83ca5b667f16c1e79e1661ab5060140"}, + {file = "Pillow-9.5.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0852ddb76d85f127c135b6dd1f0bb88dbb9ee990d2cd9aa9e28526c93e794fba"}, + {file = "Pillow-9.5.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:91ec6fe47b5eb5a9968c79ad9ed78c342b1f97a091677ba0e012701add857829"}, + {file = "Pillow-9.5.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:cb841572862f629b99725ebaec3287fc6d275be9b14443ea746c1dd325053cbd"}, + {file = "Pillow-9.5.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:c380b27d041209b849ed246b111b7c166ba36d7933ec6e41175fd15ab9eb1572"}, + {file = "Pillow-9.5.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c9af5a3b406a50e313467e3565fc99929717f780164fe6fbb7704edba0cebbe"}, + {file = "Pillow-9.5.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5671583eab84af046a397d6d0ba25343c00cd50bce03787948e0fff01d4fd9b1"}, + {file = "Pillow-9.5.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:84a6f19ce086c1bf894644b43cd129702f781ba5751ca8572f08aa40ef0ab7b7"}, + {file = "Pillow-9.5.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:1e7723bd90ef94eda669a3c2c19d549874dd5badaeefabefd26053304abe5799"}, + {file = "Pillow-9.5.0.tar.gz", hash = "sha256:bf548479d336726d7a0eceb6e767e179fbde37833ae42794602631a070d630f1"}, +] + +[package.extras] +docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-removed-in", "sphinxext-opengraph"] +tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] + +[[package]] +name = "playwright" +version = "1.34.0" +description = "A high-level API to automate web browsers" +optional = false +python-versions = ">=3.7" +files = [ + {file = "playwright-1.34.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:69bb9b3296e366a23a99277b4c7673cb54ce71a3f5d630f114f7701b61f98f25"}, + {file = "playwright-1.34.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:402d946631c8458436e099d7731bbf54cf79c9e62e3acae0ea8421e72616926b"}, + {file = "playwright-1.34.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:462251cda0fcbb273497d357dbe14b11e43ebceb0bac9b892beda041ff209aa9"}, + {file = "playwright-1.34.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:a8ba124ea302596a03a66993cd500484fb255cbc10fe0757fa4d49f974267a80"}, + {file = "playwright-1.34.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf0cb6aac49d24335fe361868aea72b11f276a95e7809f1a5d1c69b4120c46ac"}, + {file = "playwright-1.34.0-py3-none-win32.whl", hash = "sha256:c50fef189d87243cc09ae0feb8e417fbe434359ccbcc863fb19ba06d46d31c33"}, + {file = "playwright-1.34.0-py3-none-win_amd64.whl", hash = "sha256:42e16c930e1e910461f4c551a72fc1b900f37124431bf2b6a6d9ddae70042db4"}, +] + +[package.dependencies] +greenlet = "2.0.2" +pyee = "9.0.4" + +[[package]] +name = "pydantic" +version = "1.10.8" +description = "Data validation and settings management using python type hints" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pydantic-1.10.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1243d28e9b05003a89d72e7915fdb26ffd1d39bdd39b00b7dbe4afae4b557f9d"}, + {file = "pydantic-1.10.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c0ab53b609c11dfc0c060d94335993cc2b95b2150e25583bec37a49b2d6c6c3f"}, + {file = "pydantic-1.10.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9613fadad06b4f3bc5db2653ce2f22e0de84a7c6c293909b48f6ed37b83c61f"}, + {file = "pydantic-1.10.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:df7800cb1984d8f6e249351139667a8c50a379009271ee6236138a22a0c0f319"}, + {file = "pydantic-1.10.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0c6fafa0965b539d7aab0a673a046466d23b86e4b0e8019d25fd53f4df62c277"}, + {file = "pydantic-1.10.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e82d4566fcd527eae8b244fa952d99f2ca3172b7e97add0b43e2d97ee77f81ab"}, + {file = "pydantic-1.10.8-cp310-cp310-win_amd64.whl", hash = "sha256:ab523c31e22943713d80d8d342d23b6f6ac4b792a1e54064a8d0cf78fd64e800"}, + {file = "pydantic-1.10.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:666bdf6066bf6dbc107b30d034615d2627e2121506c555f73f90b54a463d1f33"}, + {file = "pydantic-1.10.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:35db5301b82e8661fa9c505c800d0990bc14e9f36f98932bb1d248c0ac5cada5"}, + {file = "pydantic-1.10.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f90c1e29f447557e9e26afb1c4dbf8768a10cc676e3781b6a577841ade126b85"}, + {file = "pydantic-1.10.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93e766b4a8226e0708ef243e843105bf124e21331694367f95f4e3b4a92bbb3f"}, + {file = "pydantic-1.10.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:88f195f582851e8db960b4a94c3e3ad25692c1c1539e2552f3df7a9e972ef60e"}, + {file = "pydantic-1.10.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:34d327c81e68a1ecb52fe9c8d50c8a9b3e90d3c8ad991bfc8f953fb477d42fb4"}, + {file = "pydantic-1.10.8-cp311-cp311-win_amd64.whl", hash = "sha256:d532bf00f381bd6bc62cabc7d1372096b75a33bc197a312b03f5838b4fb84edd"}, + {file = "pydantic-1.10.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7d5b8641c24886d764a74ec541d2fc2c7fb19f6da2a4001e6d580ba4a38f7878"}, + {file = "pydantic-1.10.8-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b1f6cb446470b7ddf86c2e57cd119a24959af2b01e552f60705910663af09a4"}, + {file = "pydantic-1.10.8-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c33b60054b2136aef8cf190cd4c52a3daa20b2263917c49adad20eaf381e823b"}, + {file = "pydantic-1.10.8-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1952526ba40b220b912cdc43c1c32bcf4a58e3f192fa313ee665916b26befb68"}, + {file = "pydantic-1.10.8-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:bb14388ec45a7a0dc429e87def6396f9e73c8c77818c927b6a60706603d5f2ea"}, + {file = "pydantic-1.10.8-cp37-cp37m-win_amd64.whl", hash = "sha256:16f8c3e33af1e9bb16c7a91fc7d5fa9fe27298e9f299cff6cb744d89d573d62c"}, + {file = "pydantic-1.10.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1ced8375969673929809d7f36ad322934c35de4af3b5e5b09ec967c21f9f7887"}, + {file = "pydantic-1.10.8-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:93e6bcfccbd831894a6a434b0aeb1947f9e70b7468f274154d03d71fabb1d7c6"}, + {file = "pydantic-1.10.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:191ba419b605f897ede9892f6c56fb182f40a15d309ef0142212200a10af4c18"}, + {file = "pydantic-1.10.8-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:052d8654cb65174d6f9490cc9b9a200083a82cf5c3c5d3985db765757eb3b375"}, + {file = "pydantic-1.10.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ceb6a23bf1ba4b837d0cfe378329ad3f351b5897c8d4914ce95b85fba96da5a1"}, + {file = "pydantic-1.10.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6f2e754d5566f050954727c77f094e01793bcb5725b663bf628fa6743a5a9108"}, + {file = "pydantic-1.10.8-cp38-cp38-win_amd64.whl", hash = "sha256:6a82d6cda82258efca32b40040228ecf43a548671cb174a1e81477195ed3ed56"}, + {file = "pydantic-1.10.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3e59417ba8a17265e632af99cc5f35ec309de5980c440c255ab1ca3ae96a3e0e"}, + {file = "pydantic-1.10.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:84d80219c3f8d4cad44575e18404099c76851bc924ce5ab1c4c8bb5e2a2227d0"}, + {file = "pydantic-1.10.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e4148e635994d57d834be1182a44bdb07dd867fa3c2d1b37002000646cc5459"}, + {file = "pydantic-1.10.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12f7b0bf8553e310e530e9f3a2f5734c68699f42218bf3568ef49cd9b0e44df4"}, + {file = "pydantic-1.10.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:42aa0c4b5c3025483240a25b09f3c09a189481ddda2ea3a831a9d25f444e03c1"}, + {file = "pydantic-1.10.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:17aef11cc1b997f9d574b91909fed40761e13fac438d72b81f902226a69dac01"}, + {file = "pydantic-1.10.8-cp39-cp39-win_amd64.whl", hash = "sha256:66a703d1983c675a6e0fed8953b0971c44dba48a929a2000a493c3772eb61a5a"}, + {file = "pydantic-1.10.8-py3-none-any.whl", hash = "sha256:7456eb22ed9aaa24ff3e7b4757da20d9e5ce2a81018c1b3ebd81a0b88a18f3b2"}, + {file = "pydantic-1.10.8.tar.gz", hash = "sha256:1410275520dfa70effadf4c21811d755e7ef9bb1f1d077a21958153a92c8d9ca"}, +] + +[package.dependencies] +typing-extensions = ">=4.2.0" + +[package.extras] +dotenv = ["python-dotenv (>=0.10.4)"] +email = ["email-validator (>=1.0.3)"] + +[[package]] +name = "pyee" +version = "9.0.4" +description = "A port of node.js's EventEmitter to python." +optional = false +python-versions = "*" +files = [ + {file = "pyee-9.0.4-py2.py3-none-any.whl", hash = "sha256:9f066570130c554e9cc12de5a9d86f57c7ee47fece163bbdaa3e9c933cfbdfa5"}, + {file = "pyee-9.0.4.tar.gz", hash = "sha256:2770c4928abc721f46b705e6a72b0c59480c4a69c9a83ca0b00bb994f1ea4b32"}, +] + +[package.dependencies] +typing-extensions = "*" + +[[package]] +name = "pygments" +version = "2.15.1" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.7" +files = [ + {file = "Pygments-2.15.1-py3-none-any.whl", hash = "sha256:db2db3deb4b4179f399a09054b023b6a586b76499d36965813c71aa8ed7b5fd1"}, + {file = "Pygments-2.15.1.tar.gz", hash = "sha256:8ace4d3c1dd481894b2005f560ead0f9f19ee64fe983366be1a21e171d12775c"}, +] + +[package.extras] +plugins = ["importlib-metadata"] + +[[package]] +name = "pyparsing" +version = "3.0.9" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" +optional = false +python-versions = ">=3.6.8" +files = [ + {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, + {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, +] + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] + +[[package]] +name = "python-dateutil" +version = "2.8.2" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {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"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "python-dotenv" +version = "1.0.0" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python-dotenv-1.0.0.tar.gz", hash = "sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba"}, + {file = "python_dotenv-1.0.0-py3-none-any.whl", hash = "sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + +[[package]] +name = "pyyaml" +version = "6.0" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.6" +files = [ + {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, + {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, + {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, + {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, + {file = "PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358"}, + {file = "PyYAML-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1"}, + {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d"}, + {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f"}, + {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782"}, + {file = "PyYAML-6.0-cp311-cp311-win32.whl", hash = "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7"}, + {file = "PyYAML-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf"}, + {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, + {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, + {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, + {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, + {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, + {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, + {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, + {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, + {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, + {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, + {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, + {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, + {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, + {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, +] + +[[package]] +name = "reactpy" +version = "1.0.0" +description = "Reactive user interfaces with pure Python" +optional = false +python-versions = ">=3.9" +files = [] +develop = false + +[package.dependencies] +anyio = ">=3" +asgiref = ">=3" +colorlog = ">=6" +fastapi = {version = ">=0.63.0", optional = true, markers = "extra == \"fastapi\""} +fastjsonschema = ">=2.14.5" +flask = {version = "*", optional = true, markers = "extra == \"flask\""} +flask-cors = {version = "*", optional = true, markers = "extra == \"flask\""} +flask-sock = {version = "*", optional = true, markers = "extra == \"flask\""} +jsonpatch = ">=1.32" +lxml = ">=4" +markupsafe = {version = ">=1.1.1,<2.1", optional = true, markers = "extra == \"flask\""} +mypy-extensions = ">=0.4.3" +playwright = {version = "*", optional = true, markers = "extra == \"testing\""} +requests = ">=2" +sanic = {version = ">=21", optional = true, markers = "extra == \"sanic\""} +sanic-cors = {version = "*", optional = true, markers = "extra == \"sanic\""} +starlette = {version = ">=0.13.6", optional = true, markers = "extra == \"starlette\""} +tornado = {version = "*", optional = true, markers = "extra == \"tornado\""} +typing-extensions = ">=3.10" +uvicorn = {version = ">=0.19.0", extras = ["standard"], optional = true, markers = "extra == \"fastapi\" or extra == \"sanic\" or extra == \"starlette\""} + +[package.extras] +all = ["reactpy[fastapi,flask,sanic,starlette,testing,tornado]"] +fastapi = ["fastapi (>=0.63.0)", "uvicorn[standard] (>=0.19.0)"] +flask = ["flask", "flask-cors", "flask-sock", "markupsafe (>=1.1.1,<2.1)"] +sanic = ["sanic (>=21)", "sanic-cors", "uvicorn[standard] (>=0.19.0)"] +starlette = ["starlette (>=0.13.6)", "uvicorn[standard] (>=0.19.0)"] +testing = ["playwright"] +tornado = ["tornado"] + +[package.source] +type = "directory" +url = "../src/py/reactpy" + +[[package]] +name = "requests" +version = "2.31.0" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.7" +files = [ + {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, + {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "sanic" +version = "23.3.0" +description = "A web server and web framework that's written to go fast. Build fast. Run fast." +optional = false +python-versions = ">=3.7" +files = [ + {file = "sanic-23.3.0-py3-none-any.whl", hash = "sha256:7cafbd63da9c6c6d8aeb8cb4304addf8a274352ab812014386c63e55f474fbee"}, + {file = "sanic-23.3.0.tar.gz", hash = "sha256:b80ebc5c38c983cb45ae5ecc7a669a54c823ec1dff297fbd5f817b1e9e9e49af"}, +] + +[package.dependencies] +aiofiles = ">=0.6.0" +html5tagger = ">=1.2.1" +httptools = ">=0.0.10" +multidict = ">=5.0,<7.0" +sanic-routing = ">=22.8.0" +tracerite = ">=1.0.0" +ujson = {version = ">=1.35", markers = "sys_platform != \"win32\" and implementation_name == \"cpython\""} +uvloop = {version = ">=0.15.0", markers = "sys_platform != \"win32\" and implementation_name == \"cpython\""} +websockets = ">=10.0" + +[package.extras] +all = ["bandit", "beautifulsoup4", "black", "chardet (==3.*)", "coverage", "cryptography", "docutils", "enum-tools[sphinx]", "flake8", "isort (>=5.0.0)", "m2r2", "mistune (<2.0.0)", "mypy (>=0.901,<0.910)", "pygments", "pytest (==7.1.*)", "pytest-benchmark", "pytest-sanic", "sanic-testing (>=23.3.0)", "slotscheck (>=0.8.0,<1)", "sphinx (>=2.1.2)", "sphinx-rtd-theme (>=0.4.3)", "towncrier", "tox", "types-ujson", "uvicorn (<0.15.0)"] +dev = ["bandit", "beautifulsoup4", "black", "chardet (==3.*)", "coverage", "cryptography", "docutils", "flake8", "isort (>=5.0.0)", "mypy (>=0.901,<0.910)", "pygments", "pytest (==7.1.*)", "pytest-benchmark", "pytest-sanic", "sanic-testing (>=23.3.0)", "slotscheck (>=0.8.0,<1)", "towncrier", "tox", "types-ujson", "uvicorn (<0.15.0)"] +docs = ["docutils", "enum-tools[sphinx]", "m2r2", "mistune (<2.0.0)", "pygments", "sphinx (>=2.1.2)", "sphinx-rtd-theme (>=0.4.3)"] +ext = ["sanic-ext"] +http3 = ["aioquic"] +test = ["bandit", "beautifulsoup4", "black", "chardet (==3.*)", "coverage", "docutils", "flake8", "isort (>=5.0.0)", "mypy (>=0.901,<0.910)", "pygments", "pytest (==7.1.*)", "pytest-benchmark", "pytest-sanic", "sanic-testing (>=23.3.0)", "slotscheck (>=0.8.0,<1)", "types-ujson", "uvicorn (<0.15.0)"] + +[[package]] +name = "sanic-cors" +version = "2.2.0" +description = "A Sanic extension adding a decorator for CORS support. Based on flask-cors by Cory Dolphin." +optional = false +python-versions = "*" +files = [ + {file = "Sanic-Cors-2.2.0.tar.gz", hash = "sha256:f8d7515da4c8b837871d422c66314c4b5704396a78894b59c50e26aa72a95873"}, + {file = "Sanic_Cors-2.2.0-py2.py3-none-any.whl", hash = "sha256:c3b133ff1f0bb609a53db35f727f5c371dc4ebeb6be4cc2c37c19dd8b9301115"}, +] + +[package.dependencies] +packaging = ">=21.3" +sanic = ">=21.9.3" + +[[package]] +name = "sanic-routing" +version = "22.8.0" +description = "Core routing component for Sanic" +optional = false +python-versions = "*" +files = [ + {file = "sanic-routing-22.8.0.tar.gz", hash = "sha256:305729b4e0bf01f074044a2a315ff401fa7eeffb009eec1d2c81d35e1038ddfc"}, + {file = "sanic_routing-22.8.0-py3-none-any.whl", hash = "sha256:9a928ed9e19a36bc019223be90a5da0ab88cdd76b101e032510b6a7073c017e9"}, +] + +[[package]] +name = "simple-websocket" +version = "0.10.0" +description = "Simple WebSocket server and client for Python" +optional = false +python-versions = ">=3.6" +files = [ + {file = "simple-websocket-0.10.0.tar.gz", hash = "sha256:82c0b0b1006d5490f09ff66392394d90dd758285635edad241e093e9a8abd3eb"}, + {file = "simple_websocket-0.10.0-py3-none-any.whl", hash = "sha256:fc1bc56c393a187e7268f8ab99da1a8e8da9b5dfb7769a2f3b8dada00067745b"}, +] + +[package.dependencies] +wsproto = "*" + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "sniffio" +version = "1.3.0" +description = "Sniff out which async library your code is running under" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"}, + {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, +] + +[[package]] +name = "snowballstemmer" +version = "2.2.0" +description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." +optional = false +python-versions = "*" +files = [ + {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, + {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, +] + +[[package]] +name = "soupsieve" +version = "2.4.1" +description = "A modern CSS selector implementation for Beautiful Soup." +optional = false +python-versions = ">=3.7" +files = [ + {file = "soupsieve-2.4.1-py3-none-any.whl", hash = "sha256:1c1bfee6819544a3447586c889157365a27e10d88cde3ad3da0cf0ddf646feb8"}, + {file = "soupsieve-2.4.1.tar.gz", hash = "sha256:89d12b2d5dfcd2c9e8c22326da9d9aa9cb3dfab0a83a024f05704076ee8d35ea"}, +] + +[[package]] +name = "sphinx" +version = "4.5.0" +description = "Python documentation generator" +optional = false +python-versions = ">=3.6" +files = [ + {file = "Sphinx-4.5.0-py3-none-any.whl", hash = "sha256:ebf612653238bcc8f4359627a9b7ce44ede6fdd75d9d30f68255c7383d3a6226"}, + {file = "Sphinx-4.5.0.tar.gz", hash = "sha256:7bf8ca9637a4ee15af412d1a1d9689fec70523a68ca9bb9127c2f3eeb344e2e6"}, +] + +[package.dependencies] +alabaster = ">=0.7,<0.8" +babel = ">=1.3" +colorama = {version = ">=0.3.5", markers = "sys_platform == \"win32\""} +docutils = ">=0.14,<0.18" +imagesize = "*" +importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} +Jinja2 = ">=2.3" +packaging = "*" +Pygments = ">=2.0" +requests = ">=2.5.0" +snowballstemmer = ">=1.1" +sphinxcontrib-applehelp = "*" +sphinxcontrib-devhelp = "*" +sphinxcontrib-htmlhelp = ">=2.0.0" +sphinxcontrib-jsmath = "*" +sphinxcontrib-qthelp = "*" +sphinxcontrib-serializinghtml = ">=1.1.5" + +[package.extras] +docs = ["sphinxcontrib-websupport"] +lint = ["docutils-stubs", "flake8 (>=3.5.0)", "isort", "mypy (>=0.931)", "types-requests", "types-typed-ast"] +test = ["cython", "html5lib", "pytest", "pytest-cov", "typed-ast"] + +[[package]] +name = "sphinx-autobuild" +version = "2021.3.14" +description = "Rebuild Sphinx documentation on changes, with live-reload in the browser." +optional = false +python-versions = ">=3.6" +files = [ + {file = "sphinx-autobuild-2021.3.14.tar.gz", hash = "sha256:de1ca3b66e271d2b5b5140c35034c89e47f263f2cd5db302c9217065f7443f05"}, + {file = "sphinx_autobuild-2021.3.14-py3-none-any.whl", hash = "sha256:8fe8cbfdb75db04475232f05187c776f46f6e9e04cacf1e49ce81bdac649ccac"}, +] + +[package.dependencies] +colorama = "*" +livereload = "*" +sphinx = "*" + +[package.extras] +test = ["pytest", "pytest-cov"] + +[[package]] +name = "sphinx-autodoc-typehints" +version = "1.19.1" +description = "Type hints (PEP 484) support for the Sphinx autodoc extension" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sphinx_autodoc_typehints-1.19.1-py3-none-any.whl", hash = "sha256:9be46aeeb1b315eb5df1f3a7cb262149895d16c7d7dcd77b92513c3c3a1e85e6"}, + {file = "sphinx_autodoc_typehints-1.19.1.tar.gz", hash = "sha256:6c841db55e0e9be0483ff3962a2152b60e79306f4288d8c4e7e86ac84486a5ea"}, +] + +[package.dependencies] +Sphinx = ">=4.5" + +[package.extras] +testing = ["covdefaults (>=2.2)", "coverage (>=6.3)", "diff-cover (>=6.4)", "nptyping (>=2.1.2)", "pytest (>=7.1)", "pytest-cov (>=3)", "sphobjinv (>=2)", "typing-extensions (>=4.1)"] +type-comments = ["typed-ast (>=1.5.2)"] + +[[package]] +name = "sphinx-copybutton" +version = "0.5.2" +description = "Add a copy button to each of your code cells." +optional = false +python-versions = ">=3.7" +files = [ + {file = "sphinx-copybutton-0.5.2.tar.gz", hash = "sha256:4cf17c82fb9646d1bc9ca92ac280813a3b605d8c421225fd9913154103ee1fbd"}, + {file = "sphinx_copybutton-0.5.2-py3-none-any.whl", hash = "sha256:fb543fd386d917746c9a2c50360c7905b605726b9355cd26e9974857afeae06e"}, +] + +[package.dependencies] +sphinx = ">=1.8" + +[package.extras] +code-style = ["pre-commit (==2.12.1)"] +rtd = ["ipython", "myst-nb", "sphinx", "sphinx-book-theme", "sphinx-examples"] + +[[package]] +name = "sphinx-design" +version = "0.4.1" +description = "A sphinx extension for designing beautiful, view size responsive web components." +optional = false +python-versions = ">=3.7" +files = [ + {file = "sphinx_design-0.4.1-py3-none-any.whl", hash = "sha256:23bf5705eb31296d4451f68b0222a698a8a84396ffe8378dfd9319ba7ab8efd9"}, + {file = "sphinx_design-0.4.1.tar.gz", hash = "sha256:5b6418ba4a2dc3d83592ea0ff61a52a891fe72195a4c3a18b2fa1c7668ce4708"}, +] + +[package.dependencies] +sphinx = ">=4,<7" + +[package.extras] +code-style = ["pre-commit (>=2.12,<3.0)"] +rtd = ["myst-parser (>=0.18.0,<2)"] +testing = ["myst-parser (>=0.18.0,<2)", "pytest (>=7.1,<8.0)", "pytest-cov", "pytest-regressions"] +theme-furo = ["furo (>=2022.06.04,<2022.07)"] +theme-pydata = ["pydata-sphinx-theme (>=0.9.0,<0.10.0)"] +theme-rtd = ["sphinx-rtd-theme (>=1.0,<2.0)"] +theme-sbt = ["sphinx-book-theme (>=0.3.0,<0.4.0)"] + +[[package]] +name = "sphinx-reredirects" +version = "0.1.2" +description = "Handles redirects for moved pages in Sphinx documentation projects" +optional = false +python-versions = ">=3.5" +files = [ + {file = "sphinx_reredirects-0.1.2-py3-none-any.whl", hash = "sha256:3a22161771aadd448bb608a4fe7277252182a337af53c18372b7104531d71489"}, + {file = "sphinx_reredirects-0.1.2.tar.gz", hash = "sha256:a0e7213304759b01edc22f032f1715a1c61176fc8f167164e7a52b9feec9ac64"}, +] + +[package.dependencies] +sphinx = "*" + +[[package]] +name = "sphinx-resolve-py-references" +version = "0.1.0" +description = "Better python object resolution in Sphinx" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sphinx_resolve_py_references-0.1.0-py2.py3-none-any.whl", hash = "sha256:ccf44a6b62d75c3a568285f4e1815734088c1a7cab7bbb7935bb22fbf0d78bc2"}, + {file = "sphinx_resolve_py_references-0.1.0.tar.gz", hash = "sha256:0f87c06b29ec128964aee2e40d170d1d3c0e5f4955b2618a89ca724f42385372"}, +] + +[package.dependencies] +sphinx = "*" + +[[package]] +name = "sphinxcontrib-applehelp" +version = "1.0.4" +description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" +optional = false +python-versions = ">=3.8" +files = [ + {file = "sphinxcontrib-applehelp-1.0.4.tar.gz", hash = "sha256:828f867945bbe39817c210a1abfd1bc4895c8b73fcaade56d45357a348a07d7e"}, + {file = "sphinxcontrib_applehelp-1.0.4-py3-none-any.whl", hash = "sha256:29d341f67fb0f6f586b23ad80e072c8e6ad0b48417db2bde114a4c9746feb228"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-devhelp" +version = "1.0.2" +description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document." +optional = false +python-versions = ">=3.5" +files = [ + {file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"}, + {file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-htmlhelp" +version = "2.0.1" +description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" +optional = false +python-versions = ">=3.8" +files = [ + {file = "sphinxcontrib-htmlhelp-2.0.1.tar.gz", hash = "sha256:0cbdd302815330058422b98a113195c9249825d681e18f11e8b1f78a2f11efff"}, + {file = "sphinxcontrib_htmlhelp-2.0.1-py3-none-any.whl", hash = "sha256:c38cb46dccf316c79de6e5515e1770414b797162b23cd3d06e67020e1d2a6903"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["html5lib", "pytest"] + +[[package]] +name = "sphinxcontrib-jsmath" +version = "1.0.1" +description = "A sphinx extension which renders display math in HTML via JavaScript" +optional = false +python-versions = ">=3.5" +files = [ + {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, + {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, +] + +[package.extras] +test = ["flake8", "mypy", "pytest"] + +[[package]] +name = "sphinxcontrib-qthelp" +version = "1.0.3" +description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document." +optional = false +python-versions = ">=3.5" +files = [ + {file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"}, + {file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-serializinghtml" +version = "1.1.5" +description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)." +optional = false +python-versions = ">=3.5" +files = [ + {file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"}, + {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["pytest"] + +[[package]] +name = "sphinxext-opengraph" +version = "0.8.2" +description = "Sphinx Extension to enable OGP support" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sphinxext-opengraph-0.8.2.tar.gz", hash = "sha256:45a693b6704052c426576f0a1f630649c55b4188bc49eb63e9587e24a923db39"}, + {file = "sphinxext_opengraph-0.8.2-py3-none-any.whl", hash = "sha256:6a05bdfe5176d9dd0a1d58a504f17118362ab976631213cd36fb44c4c40544c9"}, +] + +[package.dependencies] +matplotlib = "*" +sphinx = ">=4.0" + +[[package]] +name = "starlette" +version = "0.27.0" +description = "The little ASGI library that shines." +optional = false +python-versions = ">=3.7" +files = [ + {file = "starlette-0.27.0-py3-none-any.whl", hash = "sha256:918416370e846586541235ccd38a474c08b80443ed31c578a418e2209b3eef91"}, + {file = "starlette-0.27.0.tar.gz", hash = "sha256:6a6b0d042acb8d469a01eba54e9cda6cbd24ac602c4cd016723117d6a7e73b75"}, +] + +[package.dependencies] +anyio = ">=3.4.0,<5" +typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} + +[package.extras] +full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart", "pyyaml"] + +[[package]] +name = "tornado" +version = "6.3.2" +description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." +optional = false +python-versions = ">= 3.8" +files = [ + {file = "tornado-6.3.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:c367ab6c0393d71171123ca5515c61ff62fe09024fa6bf299cd1339dc9456829"}, + {file = "tornado-6.3.2-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:b46a6ab20f5c7c1cb949c72c1994a4585d2eaa0be4853f50a03b5031e964fc7c"}, + {file = "tornado-6.3.2-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c2de14066c4a38b4ecbbcd55c5cc4b5340eb04f1c5e81da7451ef555859c833f"}, + {file = "tornado-6.3.2-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:05615096845cf50a895026f749195bf0b10b8909f9be672f50b0fe69cba368e4"}, + {file = "tornado-6.3.2-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5b17b1cf5f8354efa3d37c6e28fdfd9c1c1e5122f2cb56dac121ac61baa47cbe"}, + {file = "tornado-6.3.2-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:29e71c847a35f6e10ca3b5c2990a52ce38b233019d8e858b755ea6ce4dcdd19d"}, + {file = "tornado-6.3.2-cp38-abi3-musllinux_1_1_i686.whl", hash = "sha256:834ae7540ad3a83199a8da8f9f2d383e3c3d5130a328889e4cc991acc81e87a0"}, + {file = "tornado-6.3.2-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:6a0848f1aea0d196a7c4f6772197cbe2abc4266f836b0aac76947872cd29b411"}, + {file = "tornado-6.3.2-cp38-abi3-win32.whl", hash = "sha256:7efcbcc30b7c654eb6a8c9c9da787a851c18f8ccd4a5a3a95b05c7accfa068d2"}, + {file = "tornado-6.3.2-cp38-abi3-win_amd64.whl", hash = "sha256:0c325e66c8123c606eea33084976c832aa4e766b7dff8aedd7587ea44a604cdf"}, + {file = "tornado-6.3.2.tar.gz", hash = "sha256:4b927c4f19b71e627b13f3db2324e4ae660527143f9e1f2e2fb404f3a187e2ba"}, +] + +[[package]] +name = "tracerite" +version = "1.1.0" +description = "Human-readable HTML tracebacks for Python exceptions" +optional = false +python-versions = "*" +files = [ + {file = "tracerite-1.1.0-py3-none-any.whl", hash = "sha256:4cccac04db05eeeabda45e72b57199e147fa2f73cf64d89cfd625df321bd2ab6"}, + {file = "tracerite-1.1.0.tar.gz", hash = "sha256:041dab8fd4bb405f73506293ac7438a2d311e5f9044378ba7d9a6540392f9e4b"}, +] + +[package.dependencies] +html5tagger = ">=1.2.1" + +[[package]] +name = "typing-extensions" +version = "4.6.3" +description = "Backported and Experimental Type Hints for Python 3.7+" +optional = false +python-versions = ">=3.7" +files = [ + {file = "typing_extensions-4.6.3-py3-none-any.whl", hash = "sha256:88a4153d8505aabbb4e13aacb7c486c2b4a33ca3b3f807914a9b4c844c471c26"}, + {file = "typing_extensions-4.6.3.tar.gz", hash = "sha256:d91d5919357fe7f681a9f2b5b4cb2a5f1ef0a1e9f59c4d8ff0d3491e05c0ffd5"}, +] + +[[package]] +name = "ujson" +version = "5.7.0" +description = "Ultra fast JSON encoder and decoder for Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "ujson-5.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5eba5e69e4361ac3a311cf44fa71bc619361b6e0626768a494771aacd1c2f09b"}, + {file = "ujson-5.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aae4d9e1b4c7b61780f0a006c897a4a1904f862fdab1abb3ea8f45bd11aa58f3"}, + {file = "ujson-5.7.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2e43ccdba1cb5c6d3448eadf6fc0dae7be6c77e357a3abc968d1b44e265866d"}, + {file = "ujson-5.7.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54384ce4920a6d35fa9ea8e580bc6d359e3eb961fa7e43f46c78e3ed162d56ff"}, + {file = "ujson-5.7.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:24ad1aa7fc4e4caa41d3d343512ce68e41411fb92adf7f434a4d4b3749dc8f58"}, + {file = "ujson-5.7.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:afff311e9f065a8f03c3753db7011bae7beb73a66189c7ea5fcb0456b7041ea4"}, + {file = "ujson-5.7.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6e80f0d03e7e8646fc3d79ed2d875cebd4c83846e129737fdc4c2532dbd43d9e"}, + {file = "ujson-5.7.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:137831d8a0db302fb6828ee21c67ad63ac537bddc4376e1aab1c8573756ee21c"}, + {file = "ujson-5.7.0-cp310-cp310-win32.whl", hash = "sha256:7df3fd35ebc14dafeea031038a99232b32f53fa4c3ecddb8bed132a43eefb8ad"}, + {file = "ujson-5.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:af4639f684f425177d09ae409c07602c4096a6287027469157bfb6f83e01448b"}, + {file = "ujson-5.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9b0f2680ce8a70f77f5d70aaf3f013d53e6af6d7058727a35d8ceb4a71cdd4e9"}, + {file = "ujson-5.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:67a19fd8e7d8cc58a169bea99fed5666023adf707a536d8f7b0a3c51dd498abf"}, + {file = "ujson-5.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6abb8e6d8f1ae72f0ed18287245f5b6d40094e2656d1eab6d99d666361514074"}, + {file = "ujson-5.7.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8cd622c069368d5074bd93817b31bdb02f8d818e57c29e206f10a1f9c6337dd"}, + {file = "ujson-5.7.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:14f9082669f90e18e64792b3fd0bf19f2b15e7fe467534a35ea4b53f3bf4b755"}, + {file = "ujson-5.7.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d7ff6ebb43bc81b057724e89550b13c9a30eda0f29c2f506f8b009895438f5a6"}, + {file = "ujson-5.7.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:f7f241488879d91a136b299e0c4ce091996c684a53775e63bb442d1a8e9ae22a"}, + {file = "ujson-5.7.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5593263a7fcfb934107444bcfba9dde8145b282de0ee9f61e285e59a916dda0f"}, + {file = "ujson-5.7.0-cp311-cp311-win32.whl", hash = "sha256:26c2b32b489c393106e9cb68d0a02e1a7b9d05a07429d875c46b94ee8405bdb7"}, + {file = "ujson-5.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:ed24406454bb5a31df18f0a423ae14beb27b28cdfa34f6268e7ebddf23da807e"}, + {file = "ujson-5.7.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:18679484e3bf9926342b1c43a3bd640f93a9eeeba19ef3d21993af7b0c44785d"}, + {file = "ujson-5.7.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ee295761e1c6c30400641f0a20d381633d7622633cdf83a194f3c876a0e4b7e"}, + {file = "ujson-5.7.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b738282e12a05f400b291966630a98d622da0938caa4bc93cf65adb5f4281c60"}, + {file = "ujson-5.7.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00343501dbaa5172e78ef0e37f9ebd08040110e11c12420ff7c1f9f0332d939e"}, + {file = "ujson-5.7.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c0d1f7c3908357ee100aa64c4d1cf91edf99c40ac0069422a4fd5fd23b263263"}, + {file = "ujson-5.7.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a5d2f44331cf04689eafac7a6596c71d6657967c07ac700b0ae1c921178645da"}, + {file = "ujson-5.7.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:16b2254a77b310f118717715259a196662baa6b1f63b1a642d12ab1ff998c3d7"}, + {file = "ujson-5.7.0-cp37-cp37m-win32.whl", hash = "sha256:6faf46fa100b2b89e4db47206cf8a1ffb41542cdd34dde615b2fc2288954f194"}, + {file = "ujson-5.7.0-cp37-cp37m-win_amd64.whl", hash = "sha256:ff0004c3f5a9a6574689a553d1b7819d1a496b4f005a7451f339dc2d9f4cf98c"}, + {file = "ujson-5.7.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:75204a1dd7ec6158c8db85a2f14a68d2143503f4bafb9a00b63fe09d35762a5e"}, + {file = "ujson-5.7.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7312731c7826e6c99cdd3ac503cd9acd300598e7a80bcf41f604fee5f49f566c"}, + {file = "ujson-5.7.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b9dc5a90e2149643df7f23634fe202fed5ebc787a2a1be95cf23632b4d90651"}, + {file = "ujson-5.7.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6a6961fc48821d84b1198a09516e396d56551e910d489692126e90bf4887d29"}, + {file = "ujson-5.7.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b01a9af52a0d5c46b2c68e3f258fdef2eacaa0ce6ae3e9eb97983f5b1166edb6"}, + {file = "ujson-5.7.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b7316d3edeba8a403686cdcad4af737b8415493101e7462a70ff73dd0609eafc"}, + {file = "ujson-5.7.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4ee997799a23227e2319a3f8817ce0b058923dbd31904761b788dc8f53bd3e30"}, + {file = "ujson-5.7.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:dda9aa4c33435147262cd2ea87c6b7a1ca83ba9b3933ff7df34e69fee9fced0c"}, + {file = "ujson-5.7.0-cp38-cp38-win32.whl", hash = "sha256:bea8d30e362180aafecabbdcbe0e1f0b32c9fa9e39c38e4af037b9d3ca36f50c"}, + {file = "ujson-5.7.0-cp38-cp38-win_amd64.whl", hash = "sha256:c96e3b872bf883090ddf32cc41957edf819c5336ab0007d0cf3854e61841726d"}, + {file = "ujson-5.7.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6411aea4c94a8e93c2baac096fbf697af35ba2b2ed410b8b360b3c0957a952d3"}, + {file = "ujson-5.7.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3d3b3499c55911f70d4e074c626acdb79a56f54262c3c83325ffb210fb03e44d"}, + {file = "ujson-5.7.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:341f891d45dd3814d31764626c55d7ab3fd21af61fbc99d070e9c10c1190680b"}, + {file = "ujson-5.7.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2f242eec917bafdc3f73a1021617db85f9958df80f267db69c76d766058f7b19"}, + {file = "ujson-5.7.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c3af9f9f22a67a8c9466a32115d9073c72a33ae627b11de6f592df0ee09b98b6"}, + {file = "ujson-5.7.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4a3d794afbf134df3056a813e5c8a935208cddeae975bd4bc0ef7e89c52f0ce0"}, + {file = "ujson-5.7.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:800bf998e78dae655008dd10b22ca8dc93bdcfcc82f620d754a411592da4bbf2"}, + {file = "ujson-5.7.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b5ac3d5c5825e30b438ea92845380e812a476d6c2a1872b76026f2e9d8060fc2"}, + {file = "ujson-5.7.0-cp39-cp39-win32.whl", hash = "sha256:cd90027e6d93e8982f7d0d23acf88c896d18deff1903dd96140613389b25c0dd"}, + {file = "ujson-5.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:523ee146cdb2122bbd827f4dcc2a8e66607b3f665186bce9e4f78c9710b6d8ab"}, + {file = "ujson-5.7.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e87cec407ec004cf1b04c0ed7219a68c12860123dfb8902ef880d3d87a71c172"}, + {file = "ujson-5.7.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bab10165db6a7994e67001733f7f2caf3400b3e11538409d8756bc9b1c64f7e8"}, + {file = "ujson-5.7.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b522be14a28e6ac1cf818599aeff1004a28b42df4ed4d7bc819887b9dac915fc"}, + {file = "ujson-5.7.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7592f40175c723c032cdbe9fe5165b3b5903604f774ab0849363386e99e1f253"}, + {file = "ujson-5.7.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:ed22f9665327a981f288a4f758a432824dc0314e4195a0eaeb0da56a477da94d"}, + {file = "ujson-5.7.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:adf445a49d9a97a5a4c9bb1d652a1528de09dd1c48b29f79f3d66cea9f826bf6"}, + {file = "ujson-5.7.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64772a53f3c4b6122ed930ae145184ebaed38534c60f3d859d8c3f00911eb122"}, + {file = "ujson-5.7.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35209cb2c13fcb9d76d249286105b4897b75a5e7f0efb0c0f4b90f222ce48910"}, + {file = "ujson-5.7.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:90712dfc775b2c7a07d4d8e059dd58636bd6ff1776d79857776152e693bddea6"}, + {file = "ujson-5.7.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:0e4e8981c6e7e9e637e637ad8ffe948a09e5434bc5f52ecbb82b4b4cfc092bfb"}, + {file = "ujson-5.7.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:581c945b811a3d67c27566539bfcb9705ea09cb27c4be0002f7a553c8886b817"}, + {file = "ujson-5.7.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d36a807a24c7d44f71686685ae6fbc8793d784bca1adf4c89f5f780b835b6243"}, + {file = "ujson-5.7.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b4257307e3662aa65e2644a277ca68783c5d51190ed9c49efebdd3cbfd5fa44"}, + {file = "ujson-5.7.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea7423d8a2f9e160c5e011119741682414c5b8dce4ae56590a966316a07a4618"}, + {file = "ujson-5.7.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4c592eb91a5968058a561d358d0fef59099ed152cfb3e1cd14eee51a7a93879e"}, + {file = "ujson-5.7.0.tar.gz", hash = "sha256:e788e5d5dcae8f6118ac9b45d0b891a0d55f7ac480eddcb7f07263f2bcf37b23"}, +] + +[[package]] +name = "urllib3" +version = "2.0.2" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.7" +files = [ + {file = "urllib3-2.0.2-py3-none-any.whl", hash = "sha256:d055c2f9d38dc53c808f6fdc8eab7360b6fdbbde02340ed25cfbcd817c62469e"}, + {file = "urllib3-2.0.2.tar.gz", hash = "sha256:61717a1095d7e155cdb737ac7bb2f4324a858a1e2e6466f6d03ff630ca68d3cc"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "uvicorn" +version = "0.22.0" +description = "The lightning-fast ASGI server." +optional = false +python-versions = ">=3.7" +files = [ + {file = "uvicorn-0.22.0-py3-none-any.whl", hash = "sha256:e9434d3bbf05f310e762147f769c9f21235ee118ba2d2bf1155a7196448bd996"}, + {file = "uvicorn-0.22.0.tar.gz", hash = "sha256:79277ae03db57ce7d9aa0567830bbb51d7a612f54d6e1e3e92da3ef24c2c8ed8"}, +] + +[package.dependencies] +click = ">=7.0" +colorama = {version = ">=0.4", optional = true, markers = "sys_platform == \"win32\" and extra == \"standard\""} +h11 = ">=0.8" +httptools = {version = ">=0.5.0", optional = true, markers = "extra == \"standard\""} +python-dotenv = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} +pyyaml = {version = ">=5.1", optional = true, markers = "extra == \"standard\""} +uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "(sys_platform != \"win32\" and sys_platform != \"cygwin\") and platform_python_implementation != \"PyPy\" and extra == \"standard\""} +watchfiles = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} +websockets = {version = ">=10.4", optional = true, markers = "extra == \"standard\""} + +[package.extras] +standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] + +[[package]] +name = "uvloop" +version = "0.17.0" +description = "Fast implementation of asyncio event loop on top of libuv" +optional = false +python-versions = ">=3.7" +files = [ + {file = "uvloop-0.17.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ce9f61938d7155f79d3cb2ffa663147d4a76d16e08f65e2c66b77bd41b356718"}, + {file = "uvloop-0.17.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:68532f4349fd3900b839f588972b3392ee56042e440dd5873dfbbcd2cc67617c"}, + {file = "uvloop-0.17.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0949caf774b9fcefc7c5756bacbbbd3fc4c05a6b7eebc7c7ad6f825b23998d6d"}, + {file = "uvloop-0.17.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff3d00b70ce95adce264462c930fbaecb29718ba6563db354608f37e49e09024"}, + {file = "uvloop-0.17.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:a5abddb3558d3f0a78949c750644a67be31e47936042d4f6c888dd6f3c95f4aa"}, + {file = "uvloop-0.17.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8efcadc5a0003d3a6e887ccc1fb44dec25594f117a94e3127954c05cf144d811"}, + {file = "uvloop-0.17.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3378eb62c63bf336ae2070599e49089005771cc651c8769aaad72d1bd9385a7c"}, + {file = "uvloop-0.17.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6aafa5a78b9e62493539456f8b646f85abc7093dd997f4976bb105537cf2635e"}, + {file = "uvloop-0.17.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c686a47d57ca910a2572fddfe9912819880b8765e2f01dc0dd12a9bf8573e539"}, + {file = "uvloop-0.17.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:864e1197139d651a76c81757db5eb199db8866e13acb0dfe96e6fc5d1cf45fc4"}, + {file = "uvloop-0.17.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2a6149e1defac0faf505406259561bc14b034cdf1d4711a3ddcdfbaa8d825a05"}, + {file = "uvloop-0.17.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6708f30db9117f115eadc4f125c2a10c1a50d711461699a0cbfaa45b9a78e376"}, + {file = "uvloop-0.17.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:23609ca361a7fc587031429fa25ad2ed7242941adec948f9d10c045bfecab06b"}, + {file = "uvloop-0.17.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2deae0b0fb00a6af41fe60a675cec079615b01d68beb4cc7b722424406b126a8"}, + {file = "uvloop-0.17.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45cea33b208971e87a31c17622e4b440cac231766ec11e5d22c76fab3bf9df62"}, + {file = "uvloop-0.17.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:9b09e0f0ac29eee0451d71798878eae5a4e6a91aa275e114037b27f7db72702d"}, + {file = "uvloop-0.17.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:dbbaf9da2ee98ee2531e0c780455f2841e4675ff580ecf93fe5c48fe733b5667"}, + {file = "uvloop-0.17.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a4aee22ece20958888eedbad20e4dbb03c37533e010fb824161b4f05e641f738"}, + {file = "uvloop-0.17.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:307958f9fc5c8bb01fad752d1345168c0abc5d62c1b72a4a8c6c06f042b45b20"}, + {file = "uvloop-0.17.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ebeeec6a6641d0adb2ea71dcfb76017602ee2bfd8213e3fcc18d8f699c5104f"}, + {file = "uvloop-0.17.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1436c8673c1563422213ac6907789ecb2b070f5939b9cbff9ef7113f2b531595"}, + {file = "uvloop-0.17.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8887d675a64cfc59f4ecd34382e5b4f0ef4ae1da37ed665adba0c2badf0d6578"}, + {file = "uvloop-0.17.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:3db8de10ed684995a7f34a001f15b374c230f7655ae840964d51496e2f8a8474"}, + {file = "uvloop-0.17.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7d37dccc7ae63e61f7b96ee2e19c40f153ba6ce730d8ba4d3b4e9738c1dccc1b"}, + {file = "uvloop-0.17.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cbbe908fda687e39afd6ea2a2f14c2c3e43f2ca88e3a11964b297822358d0e6c"}, + {file = "uvloop-0.17.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d97672dc709fa4447ab83276f344a165075fd9f366a97b712bdd3fee05efae8"}, + {file = "uvloop-0.17.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1e507c9ee39c61bfddd79714e4f85900656db1aec4d40c6de55648e85c2799c"}, + {file = "uvloop-0.17.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c092a2c1e736086d59ac8e41f9c98f26bbf9b9222a76f21af9dfe949b99b2eb9"}, + {file = "uvloop-0.17.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:30babd84706115626ea78ea5dbc7dd8d0d01a2e9f9b306d24ca4ed5796c66ded"}, + {file = "uvloop-0.17.0.tar.gz", hash = "sha256:0ddf6baf9cf11a1a22c71487f39f15b2cf78eb5bde7e5b45fbb99e8a9d91b9e1"}, +] + +[package.extras] +dev = ["Cython (>=0.29.32,<0.30.0)", "Sphinx (>=4.1.2,<4.2.0)", "aiohttp", "flake8 (>=3.9.2,<3.10.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=22.0.0,<22.1.0)", "pycodestyle (>=2.7.0,<2.8.0)", "pytest (>=3.6.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] +docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] +test = ["Cython (>=0.29.32,<0.30.0)", "aiohttp", "flake8 (>=3.9.2,<3.10.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=22.0.0,<22.1.0)", "pycodestyle (>=2.7.0,<2.8.0)"] + +[[package]] +name = "watchfiles" +version = "0.19.0" +description = "Simple, modern and high performance file watching and code reload in python." +optional = false +python-versions = ">=3.7" +files = [ + {file = "watchfiles-0.19.0-cp37-abi3-macosx_10_7_x86_64.whl", hash = "sha256:91633e64712df3051ca454ca7d1b976baf842d7a3640b87622b323c55f3345e7"}, + {file = "watchfiles-0.19.0-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:b6577b8c6c8701ba8642ea9335a129836347894b666dd1ec2226830e263909d3"}, + {file = "watchfiles-0.19.0-cp37-abi3-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:18b28f6ad871b82df9542ff958d0c86bb0d8310bb09eb8e87d97318a3b5273af"}, + {file = "watchfiles-0.19.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fac19dc9cbc34052394dbe81e149411a62e71999c0a19e1e09ce537867f95ae0"}, + {file = "watchfiles-0.19.0-cp37-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:09ea3397aecbc81c19ed7f025e051a7387feefdb789cf768ff994c1228182fda"}, + {file = "watchfiles-0.19.0-cp37-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c0376deac92377817e4fb8f347bf559b7d44ff556d9bc6f6208dd3f79f104aaf"}, + {file = "watchfiles-0.19.0-cp37-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c75eff897786ee262c9f17a48886f4e98e6cfd335e011c591c305e5d083c056"}, + {file = "watchfiles-0.19.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb5d45c4143c1dd60f98a16187fd123eda7248f84ef22244818c18d531a249d1"}, + {file = "watchfiles-0.19.0-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:79c533ff593db861ae23436541f481ec896ee3da4e5db8962429b441bbaae16e"}, + {file = "watchfiles-0.19.0-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:3d7d267d27aceeeaa3de0dd161a0d64f0a282264d592e335fff7958cc0cbae7c"}, + {file = "watchfiles-0.19.0-cp37-abi3-win32.whl", hash = "sha256:176a9a7641ec2c97b24455135d58012a5be5c6217fc4d5fef0b2b9f75dbf5154"}, + {file = "watchfiles-0.19.0-cp37-abi3-win_amd64.whl", hash = "sha256:945be0baa3e2440151eb3718fd8846751e8b51d8de7b884c90b17d271d34cae8"}, + {file = "watchfiles-0.19.0-cp37-abi3-win_arm64.whl", hash = "sha256:0089c6dc24d436b373c3c57657bf4f9a453b13767150d17284fc6162b2791911"}, + {file = "watchfiles-0.19.0-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:cae3dde0b4b2078f31527acff6f486e23abed307ba4d3932466ba7cdd5ecec79"}, + {file = "watchfiles-0.19.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:7f3920b1285a7d3ce898e303d84791b7bf40d57b7695ad549dc04e6a44c9f120"}, + {file = "watchfiles-0.19.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9afd0d69429172c796164fd7fe8e821ade9be983f51c659a38da3faaaaac44dc"}, + {file = "watchfiles-0.19.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68dce92b29575dda0f8d30c11742a8e2b9b8ec768ae414b54f7453f27bdf9545"}, + {file = "watchfiles-0.19.0-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:5569fc7f967429d4bc87e355cdfdcee6aabe4b620801e2cf5805ea245c06097c"}, + {file = "watchfiles-0.19.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:5471582658ea56fca122c0f0d0116a36807c63fefd6fdc92c71ca9a4491b6b48"}, + {file = "watchfiles-0.19.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b538014a87f94d92f98f34d3e6d2635478e6be6423a9ea53e4dd96210065e193"}, + {file = "watchfiles-0.19.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20b44221764955b1e703f012c74015306fb7e79a00c15370785f309b1ed9aa8d"}, + {file = "watchfiles-0.19.0.tar.gz", hash = "sha256:d9b073073e048081e502b6c6b0b88714c026a1a4c890569238d04aca5f9ca74b"}, +] + +[package.dependencies] +anyio = ">=3.0.0" + +[[package]] +name = "websockets" +version = "11.0.3" +description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "websockets-11.0.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3ccc8a0c387629aec40f2fc9fdcb4b9d5431954f934da3eaf16cdc94f67dbfac"}, + {file = "websockets-11.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d67ac60a307f760c6e65dad586f556dde58e683fab03323221a4e530ead6f74d"}, + {file = "websockets-11.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84d27a4832cc1a0ee07cdcf2b0629a8a72db73f4cf6de6f0904f6661227f256f"}, + {file = "websockets-11.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffd7dcaf744f25f82190856bc26ed81721508fc5cbf2a330751e135ff1283564"}, + {file = "websockets-11.0.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7622a89d696fc87af8e8d280d9b421db5133ef5b29d3f7a1ce9f1a7bf7fcfa11"}, + {file = "websockets-11.0.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bceab846bac555aff6427d060f2fcfff71042dba6f5fca7dc4f75cac815e57ca"}, + {file = "websockets-11.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:54c6e5b3d3a8936a4ab6870d46bdd6ec500ad62bde9e44462c32d18f1e9a8e54"}, + {file = "websockets-11.0.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:41f696ba95cd92dc047e46b41b26dd24518384749ed0d99bea0a941ca87404c4"}, + {file = "websockets-11.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:86d2a77fd490ae3ff6fae1c6ceaecad063d3cc2320b44377efdde79880e11526"}, + {file = "websockets-11.0.3-cp310-cp310-win32.whl", hash = "sha256:2d903ad4419f5b472de90cd2d40384573b25da71e33519a67797de17ef849b69"}, + {file = "websockets-11.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:1d2256283fa4b7f4c7d7d3e84dc2ece74d341bce57d5b9bf385df109c2a1a82f"}, + {file = "websockets-11.0.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e848f46a58b9fcf3d06061d17be388caf70ea5b8cc3466251963c8345e13f7eb"}, + {file = "websockets-11.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aa5003845cdd21ac0dc6c9bf661c5beddd01116f6eb9eb3c8e272353d45b3288"}, + {file = "websockets-11.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b58cbf0697721120866820b89f93659abc31c1e876bf20d0b3d03cef14faf84d"}, + {file = "websockets-11.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:660e2d9068d2bedc0912af508f30bbeb505bbbf9774d98def45f68278cea20d3"}, + {file = "websockets-11.0.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c1f0524f203e3bd35149f12157438f406eff2e4fb30f71221c8a5eceb3617b6b"}, + {file = "websockets-11.0.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:def07915168ac8f7853812cc593c71185a16216e9e4fa886358a17ed0fd9fcf6"}, + {file = "websockets-11.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b30c6590146e53149f04e85a6e4fcae068df4289e31e4aee1fdf56a0dead8f97"}, + {file = "websockets-11.0.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:619d9f06372b3a42bc29d0cd0354c9bb9fb39c2cbc1a9c5025b4538738dbffaf"}, + {file = "websockets-11.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:01f5567d9cf6f502d655151645d4e8b72b453413d3819d2b6f1185abc23e82dd"}, + {file = "websockets-11.0.3-cp311-cp311-win32.whl", hash = "sha256:e1459677e5d12be8bbc7584c35b992eea142911a6236a3278b9b5ce3326f282c"}, + {file = "websockets-11.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:e7837cb169eca3b3ae94cc5787c4fed99eef74c0ab9506756eea335e0d6f3ed8"}, + {file = "websockets-11.0.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:9f59a3c656fef341a99e3d63189852be7084c0e54b75734cde571182c087b152"}, + {file = "websockets-11.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2529338a6ff0eb0b50c7be33dc3d0e456381157a31eefc561771ee431134a97f"}, + {file = "websockets-11.0.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:34fd59a4ac42dff6d4681d8843217137f6bc85ed29722f2f7222bd619d15e95b"}, + {file = "websockets-11.0.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:332d126167ddddec94597c2365537baf9ff62dfcc9db4266f263d455f2f031cb"}, + {file = "websockets-11.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6505c1b31274723ccaf5f515c1824a4ad2f0d191cec942666b3d0f3aa4cb4007"}, + {file = "websockets-11.0.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f467ba0050b7de85016b43f5a22b46383ef004c4f672148a8abf32bc999a87f0"}, + {file = "websockets-11.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:9d9acd80072abcc98bd2c86c3c9cd4ac2347b5a5a0cae7ed5c0ee5675f86d9af"}, + {file = "websockets-11.0.3-cp37-cp37m-win32.whl", hash = "sha256:e590228200fcfc7e9109509e4d9125eace2042fd52b595dd22bbc34bb282307f"}, + {file = "websockets-11.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:b16fff62b45eccb9c7abb18e60e7e446998093cdcb50fed33134b9b6878836de"}, + {file = "websockets-11.0.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:fb06eea71a00a7af0ae6aefbb932fb8a7df3cb390cc217d51a9ad7343de1b8d0"}, + {file = "websockets-11.0.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8a34e13a62a59c871064dfd8ffb150867e54291e46d4a7cf11d02c94a5275bae"}, + {file = "websockets-11.0.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4841ed00f1026dfbced6fca7d963c4e7043aa832648671b5138008dc5a8f6d99"}, + {file = "websockets-11.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a073fc9ab1c8aff37c99f11f1641e16da517770e31a37265d2755282a5d28aa"}, + {file = "websockets-11.0.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:68b977f21ce443d6d378dbd5ca38621755f2063d6fdb3335bda981d552cfff86"}, + {file = "websockets-11.0.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1a99a7a71631f0efe727c10edfba09ea6bee4166a6f9c19aafb6c0b5917d09c"}, + {file = "websockets-11.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:bee9fcb41db2a23bed96c6b6ead6489702c12334ea20a297aa095ce6d31370d0"}, + {file = "websockets-11.0.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4b253869ea05a5a073ebfdcb5cb3b0266a57c3764cf6fe114e4cd90f4bfa5f5e"}, + {file = "websockets-11.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:1553cb82942b2a74dd9b15a018dce645d4e68674de2ca31ff13ebc2d9f283788"}, + {file = "websockets-11.0.3-cp38-cp38-win32.whl", hash = "sha256:f61bdb1df43dc9c131791fbc2355535f9024b9a04398d3bd0684fc16ab07df74"}, + {file = "websockets-11.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:03aae4edc0b1c68498f41a6772d80ac7c1e33c06c6ffa2ac1c27a07653e79d6f"}, + {file = "websockets-11.0.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:777354ee16f02f643a4c7f2b3eff8027a33c9861edc691a2003531f5da4f6bc8"}, + {file = "websockets-11.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8c82f11964f010053e13daafdc7154ce7385ecc538989a354ccc7067fd7028fd"}, + {file = "websockets-11.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3580dd9c1ad0701169e4d6fc41e878ffe05e6bdcaf3c412f9d559389d0c9e016"}, + {file = "websockets-11.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f1a3f10f836fab6ca6efa97bb952300b20ae56b409414ca85bff2ad241d2a61"}, + {file = "websockets-11.0.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:df41b9bc27c2c25b486bae7cf42fccdc52ff181c8c387bfd026624a491c2671b"}, + {file = "websockets-11.0.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:279e5de4671e79a9ac877427f4ac4ce93751b8823f276b681d04b2156713b9dd"}, + {file = "websockets-11.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1fdf26fa8a6a592f8f9235285b8affa72748dc12e964a5518c6c5e8f916716f7"}, + {file = "websockets-11.0.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:69269f3a0b472e91125b503d3c0b3566bda26da0a3261c49f0027eb6075086d1"}, + {file = "websockets-11.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:97b52894d948d2f6ea480171a27122d77af14ced35f62e5c892ca2fae9344311"}, + {file = "websockets-11.0.3-cp39-cp39-win32.whl", hash = "sha256:c7f3cb904cce8e1be667c7e6fef4516b98d1a6a0635a58a57528d577ac18a128"}, + {file = "websockets-11.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c792ea4eabc0159535608fc5658a74d1a81020eb35195dd63214dcf07556f67e"}, + {file = "websockets-11.0.3-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f2e58f2c36cc52d41f2659e4c0cbf7353e28c8c9e63e30d8c6d3494dc9fdedcf"}, + {file = "websockets-11.0.3-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de36fe9c02995c7e6ae6efe2e205816f5f00c22fd1fbf343d4d18c3d5ceac2f5"}, + {file = "websockets-11.0.3-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0ac56b661e60edd453585f4bd68eb6a29ae25b5184fd5ba51e97652580458998"}, + {file = "websockets-11.0.3-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e052b8467dd07d4943936009f46ae5ce7b908ddcac3fda581656b1b19c083d9b"}, + {file = "websockets-11.0.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:42cc5452a54a8e46a032521d7365da775823e21bfba2895fb7b77633cce031bb"}, + {file = "websockets-11.0.3-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e6316827e3e79b7b8e7d8e3b08f4e331af91a48e794d5d8b099928b6f0b85f20"}, + {file = "websockets-11.0.3-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8531fdcad636d82c517b26a448dcfe62f720e1922b33c81ce695d0edb91eb931"}, + {file = "websockets-11.0.3-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c114e8da9b475739dde229fd3bc6b05a6537a88a578358bc8eb29b4030fac9c9"}, + {file = "websockets-11.0.3-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e063b1865974611313a3849d43f2c3f5368093691349cf3c7c8f8f75ad7cb280"}, + {file = "websockets-11.0.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:92b2065d642bf8c0a82d59e59053dd2fdde64d4ed44efe4870fa816c1232647b"}, + {file = "websockets-11.0.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0ee68fe502f9031f19d495dae2c268830df2760c0524cbac5d759921ba8c8e82"}, + {file = "websockets-11.0.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcacf2c7a6c3a84e720d1bb2b543c675bf6c40e460300b628bab1b1efc7c034c"}, + {file = "websockets-11.0.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b67c6f5e5a401fc56394f191f00f9b3811fe843ee93f4a70df3c389d1adf857d"}, + {file = "websockets-11.0.3-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d5023a4b6a5b183dc838808087033ec5df77580485fc533e7dab2567851b0a4"}, + {file = "websockets-11.0.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ed058398f55163a79bb9f06a90ef9ccc063b204bb346c4de78efc5d15abfe602"}, + {file = "websockets-11.0.3-py3-none-any.whl", hash = "sha256:6681ba9e7f8f3b19440921e99efbb40fc89f26cd71bf539e45d8c8a25c976dc6"}, + {file = "websockets-11.0.3.tar.gz", hash = "sha256:88fc51d9a26b10fc331be344f1781224a375b78488fc343620184e95a4b27016"}, +] + +[[package]] +name = "werkzeug" +version = "2.1.2" +description = "The comprehensive WSGI web application library." +optional = false +python-versions = ">=3.7" +files = [ + {file = "Werkzeug-2.1.2-py3-none-any.whl", hash = "sha256:72a4b735692dd3135217911cbeaa1be5fa3f62bffb8745c5215420a03dc55255"}, + {file = "Werkzeug-2.1.2.tar.gz", hash = "sha256:1ce08e8093ed67d638d63879fd1ba3735817f7a80de3674d293f5984f25fb6e6"}, +] + +[package.extras] +watchdog = ["watchdog"] + +[[package]] +name = "wsproto" +version = "1.2.0" +description = "WebSockets state-machine based protocol implementation" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "wsproto-1.2.0-py3-none-any.whl", hash = "sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736"}, + {file = "wsproto-1.2.0.tar.gz", hash = "sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065"}, +] + +[package.dependencies] +h11 = ">=0.9.0,<1" + +[[package]] +name = "zipp" +version = "3.15.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +optional = false +python-versions = ">=3.7" +files = [ + {file = "zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"}, + {file = "zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] + +[metadata] +lock-version = "2.0" +python-versions = "^3.9" +content-hash = "629118cfac10f1dab4c39c6ccd50bd69ca68a7fc05dd2baf1d020082d6b19e4e" diff --git a/docs/pyproject.toml b/docs/pyproject.toml new file mode 100644 index 000000000..f47b0e944 --- /dev/null +++ b/docs/pyproject.toml @@ -0,0 +1,23 @@ +[tool.poetry] +name = "docs_app" +version = "0.0.0" +description = "docs" +authors = ["rmorshea "] +readme = "README.md" + +[tool.poetry.dependencies] +python = "^3.9" +reactpy = { path = "../src/py/reactpy", extras = ["starlette", "sanic", "fastapi", "flask", "tornado", "testing"], develop = false } +furo = "2022.04.07" +sphinx = "*" +sphinx-autodoc-typehints = "*" +sphinx-copybutton = "*" +sphinx-autobuild = "*" +sphinx-reredirects = "*" +sphinx-design = "*" +sphinx-resolve-py-references = "*" +sphinxext-opengraph = "*" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/docs/source/_custom_js/README.md b/docs/source/_custom_js/README.md index 8c77d450b..4d5d75dc2 100644 --- a/docs/source/_custom_js/README.md +++ b/docs/source/_custom_js/README.md @@ -1,4 +1,4 @@ -# Custom Javascript for IDOM's Docs +# Custom Javascript for ReactPy's Docs Build the javascript with diff --git a/docs/source/_custom_js/package-lock.json b/docs/source/_custom_js/package-lock.json index 49364d871..98cbb7014 100644 --- a/docs/source/_custom_js/package-lock.json +++ b/docs/source/_custom_js/package-lock.json @@ -1,14 +1,14 @@ { - "name": "idom-docs-example-loader", + "name": "reactpy-docs-example-loader", "version": "1.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "idom-docs-example-loader", + "name": "reactpy-docs-example-loader", "version": "1.0.0", "dependencies": { - "idom-client-react": "file:../../../src/client/packages/idom-client-react" + "@reactpy/client": "file:../../../src/js/packages/@reactpy/client" }, "devDependencies": { "@rollup/plugin-commonjs": "^21.0.1", @@ -18,28 +18,74 @@ "rollup": "^2.35.1" } }, - "../../../src/client/packages/idom-client-react": { - "version": "0.34.0", + "../../../src/client/packages/@reactpy/client": { + "version": "0.3.1", + "integrity": "sha512-pIK5eNwFSHKXg7ClpASWFVKyZDYxz59MSFpVaX/OqJFkrJaAxBuhKGXNTMXmuyWOL5Iyvb/ErwwDRxQRzMNkfQ==", + "extraneous": true, "license": "MIT", "dependencies": { - "fast-json-patch": "^3.0.0-1", - "htm": "^3.0.3" + "event-to-object": "^0.1.2", + "json-pointer": "^0.6.2" }, "devDependencies": { - "jsdom": "16.3.0", - "lodash": "^4.17.21", - "prettier": "^2.5.1", - "uvu": "^0.5.1" + "@types/json-pointer": "^1.0.31", + "@types/react": "^17.0", + "@types/react-dom": "^17.0", + "typescript": "^4.9.5" }, "peerDependencies": { - "react": ">=16", - "react-dom": ">=16" + "react": ">=16 <18", + "react-dom": ">=16 <18" } }, + "../../../src/client/packages/client": { + "name": "@reactpy/client", + "version": "0.2.0", + "extraneous": true, + "license": "MIT", + "dependencies": { + "event-to-object": "^0.1.0", + "json-pointer": "^0.6.2" + }, + "devDependencies": { + "@types/json-pointer": "^1.0.31", + "@types/react": "^17.0", + "@types/react-dom": "^17.0", + "prettier": "^3.0.0-alpha.6", + "typescript": "^4.9.5" + }, + "peerDependencies": { + "react": ">=16 <18", + "react-dom": ">=16 <18" + } + }, + "../../../src/js/packages/@reactpy/client": { + "version": "0.3.1", + "license": "MIT", + "dependencies": { + "event-to-object": "^0.1.2", + "json-pointer": "^0.6.2" + }, + "devDependencies": { + "@types/json-pointer": "^1.0.31", + "@types/react": "^17.0", + "@types/react-dom": "^17.0", + "typescript": "^4.9.5" + }, + "peerDependencies": { + "react": ">=16 <18", + "react-dom": ">=16 <18" + } + }, + "node_modules/@reactpy/client": { + "resolved": "../../../src/js/packages/@reactpy/client", + "link": true + }, "node_modules/@rollup/plugin-commonjs": { "version": "21.0.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-21.0.1.tgz", + "integrity": "sha512-EA+g22lbNJ8p5kuZJUYyhhDK7WgJckW5g4pNN7n4mAFUM96VuwUnNT3xr2Db2iCZPI1pJPbGyfT5mS9T1dHfMg==", "dev": true, - "license": "MIT", "dependencies": { "@rollup/pluginutils": "^3.1.0", "commondir": "^1.0.1", @@ -58,13 +104,15 @@ }, "node_modules/@rollup/plugin-commonjs/node_modules/estree-walker": { "version": "2.0.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true }, "node_modules/@rollup/plugin-node-resolve": { "version": "13.1.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.1.1.tgz", + "integrity": "sha512-6QKtRevXLrmEig9UiMYt2fSvee9TyltGRfw+qSs6xjUnxwjOzTOqy+/Lpxsgjb8mJn1EQNbCDAvt89O4uzL5kw==", "dev": true, - "license": "MIT", "dependencies": { "@rollup/pluginutils": "^3.1.0", "@types/resolve": "1.17.1", @@ -82,16 +130,18 @@ }, "node_modules/@rollup/plugin-node-resolve/node_modules/@types/resolve": { "version": "1.17.1", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", + "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", "dev": true, - "license": "MIT", "dependencies": { "@types/node": "*" } }, "node_modules/@rollup/plugin-replace": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-3.0.0.tgz", + "integrity": "sha512-3c7JCbMuYXM4PbPWT4+m/4Y6U60SgsnDT/cCyAyUKwFHg7pTSfsSQzIpETha3a3ig6OdOKzZz87D9ZXIK3qsDg==", "dev": true, - "license": "MIT", "dependencies": { "@rollup/pluginutils": "^3.1.0", "magic-string": "^0.25.7" @@ -102,8 +152,9 @@ }, "node_modules/@rollup/pluginutils": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", + "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", "dev": true, - "license": "MIT", "dependencies": { "@types/estree": "0.0.39", "estree-walker": "^1.0.1", @@ -118,33 +169,39 @@ }, "node_modules/@rollup/pluginutils/node_modules/@types/estree": { "version": "0.0.39", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", + "dev": true }, "node_modules/@rollup/pluginutils/node_modules/estree-walker": { "version": "1.0.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", + "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", + "dev": true }, "node_modules/@types/estree": { "version": "0.0.48", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.48.tgz", + "integrity": "sha512-LfZwXoGUDo0C3me81HXgkBg5CTQYb6xzEl+fNmbO4JdRiSKQ8A0GD1OBBvKAIsbCUgoyAty7m99GqqMQe784ew==", + "dev": true }, "node_modules/@types/node": { "version": "15.12.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.2.tgz", + "integrity": "sha512-zjQ69G564OCIWIOHSXyQEEDpdpGl+G348RAKY0XXy9Z5kU9Vzv1GMNnkar/ZJ8dzXB3COzD9Mo9NtRZ4xfgUww==", + "dev": true }, "node_modules/balanced-match": { "version": "1.0.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true }, "node_modules/brace-expansion": { "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -152,8 +209,9 @@ }, "node_modules/builtin-modules": { "version": "3.2.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.2.0.tgz", + "integrity": "sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" }, @@ -163,36 +221,56 @@ }, "node_modules/commondir": { "version": "1.0.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true }, "node_modules/concat-map": { "version": "0.0.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true }, "node_modules/deepmerge": { "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/fs.realpath": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, - "license": "ISC" + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } }, "node_modules/function-bind": { "version": "1.1.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true }, "node_modules/glob": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, - "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -210,8 +288,9 @@ }, "node_modules/has": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dev": true, - "license": "MIT", "dependencies": { "function-bind": "^1.1.1" }, @@ -219,14 +298,11 @@ "node": ">= 0.4.0" } }, - "node_modules/idom-client-react": { - "resolved": "../../../src/client/packages/idom-client-react", - "link": true - }, "node_modules/inflight": { "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, - "license": "ISC", "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -234,13 +310,15 @@ }, "node_modules/inherits": { "version": "2.0.4", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true }, "node_modules/is-core-module": { "version": "2.4.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz", + "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==", "dev": true, - "license": "MIT", "dependencies": { "has": "^1.0.3" }, @@ -250,29 +328,33 @@ }, "node_modules/is-module": { "version": "1.0.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=", + "dev": true }, "node_modules/is-reference": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", + "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", "dev": true, - "license": "MIT", "dependencies": { "@types/estree": "*" } }, "node_modules/magic-string": { "version": "0.25.7", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", + "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", "dev": true, - "license": "MIT", "dependencies": { "sourcemap-codec": "^1.4.4" } }, "node_modules/minimatch": { "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -282,29 +364,33 @@ }, "node_modules/once": { "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, - "license": "ISC", "dependencies": { "wrappy": "1" } }, "node_modules/path-is-absolute": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/path-parse": { "version": "1.0.7", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true }, "node_modules/picomatch": { "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", "dev": true, - "license": "MIT", "engines": { "node": ">=8.6" }, @@ -314,8 +400,9 @@ }, "node_modules/prettier": { "version": "2.3.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.1.tgz", + "integrity": "sha512-p+vNbgpLjif/+D+DwAZAbndtRrR0md0MwfmOVN9N+2RgyACMT+7tfaRnT+WDPkqnuVwleyuBIG2XBxKDme3hPA==", "dev": true, - "license": "MIT", "bin": { "prettier": "bin-prettier.js" }, @@ -325,8 +412,9 @@ }, "node_modules/resolve": { "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", "dev": true, - "license": "MIT", "dependencies": { "is-core-module": "^2.2.0", "path-parse": "^1.0.6" @@ -337,8 +425,9 @@ }, "node_modules/rollup": { "version": "2.52.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.52.1.tgz", + "integrity": "sha512-/SPqz8UGnp4P1hq6wc9gdTqA2bXQXGx13TtoL03GBm6qGRI6Hm3p4Io7GeiHNLl0BsQAne1JNYY+q/apcY933w==", "dev": true, - "license": "MIT", "bin": { "rollup": "dist/bin/rollup" }, @@ -351,18 +440,33 @@ }, "node_modules/sourcemap-codec": { "version": "1.4.8", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "dev": true }, "node_modules/wrappy": { "version": "1.0.2", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true } }, "dependencies": { + "@reactpy/client": { + "version": "file:../../../src/js/packages/@reactpy/client", + "requires": { + "@types/json-pointer": "^1.0.31", + "@types/react": "^17.0", + "@types/react-dom": "^17.0", + "event-to-object": "^0.1.2", + "json-pointer": "^0.6.2", + "typescript": "^4.9.5" + } + }, "@rollup/plugin-commonjs": { "version": "21.0.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-21.0.1.tgz", + "integrity": "sha512-EA+g22lbNJ8p5kuZJUYyhhDK7WgJckW5g4pNN7n4mAFUM96VuwUnNT3xr2Db2iCZPI1pJPbGyfT5mS9T1dHfMg==", "dev": true, "requires": { "@rollup/pluginutils": "^3.1.0", @@ -376,12 +480,16 @@ "dependencies": { "estree-walker": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", "dev": true } } }, "@rollup/plugin-node-resolve": { "version": "13.1.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.1.1.tgz", + "integrity": "sha512-6QKtRevXLrmEig9UiMYt2fSvee9TyltGRfw+qSs6xjUnxwjOzTOqy+/Lpxsgjb8mJn1EQNbCDAvt89O4uzL5kw==", "dev": true, "requires": { "@rollup/pluginutils": "^3.1.0", @@ -394,6 +502,8 @@ "dependencies": { "@types/resolve": { "version": "1.17.1", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", + "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", "dev": true, "requires": { "@types/node": "*" @@ -403,6 +513,8 @@ }, "@rollup/plugin-replace": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-3.0.0.tgz", + "integrity": "sha512-3c7JCbMuYXM4PbPWT4+m/4Y6U60SgsnDT/cCyAyUKwFHg7pTSfsSQzIpETha3a3ig6OdOKzZz87D9ZXIK3qsDg==", "dev": true, "requires": { "@rollup/pluginutils": "^3.1.0", @@ -411,6 +523,8 @@ }, "@rollup/pluginutils": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", + "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", "dev": true, "requires": { "@types/estree": "0.0.39", @@ -420,28 +534,40 @@ "dependencies": { "@types/estree": { "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", "dev": true }, "estree-walker": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", + "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", "dev": true } } }, "@types/estree": { "version": "0.0.48", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.48.tgz", + "integrity": "sha512-LfZwXoGUDo0C3me81HXgkBg5CTQYb6xzEl+fNmbO4JdRiSKQ8A0GD1OBBvKAIsbCUgoyAty7m99GqqMQe784ew==", "dev": true }, "@types/node": { "version": "15.12.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.2.tgz", + "integrity": "sha512-zjQ69G564OCIWIOHSXyQEEDpdpGl+G348RAKY0XXy9Z5kU9Vzv1GMNnkar/ZJ8dzXB3COzD9Mo9NtRZ4xfgUww==", "dev": true }, "balanced-match": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, "brace-expansion": { "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "requires": { "balanced-match": "^1.0.0", @@ -450,30 +576,51 @@ }, "builtin-modules": { "version": "3.2.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.2.0.tgz", + "integrity": "sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA==", "dev": true }, "commondir": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", "dev": true }, "concat-map": { "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, "deepmerge": { "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", "dev": true }, "fs.realpath": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, "function-bind": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, "glob": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -486,24 +633,17 @@ }, "has": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dev": true, "requires": { "function-bind": "^1.1.1" } }, - "idom-client-react": { - "version": "file:../../../src/client/packages/idom-client-react", - "requires": { - "fast-json-patch": "^3.0.0-1", - "htm": "^3.0.3", - "jsdom": "16.3.0", - "lodash": "^4.17.21", - "prettier": "^2.5.1", - "uvu": "^0.5.1" - } - }, "inflight": { "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "requires": { "once": "^1.3.0", @@ -512,10 +652,14 @@ }, "inherits": { "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, "is-core-module": { "version": "2.4.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz", + "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==", "dev": true, "requires": { "has": "^1.0.3" @@ -523,10 +667,14 @@ }, "is-module": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=", "dev": true }, "is-reference": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", + "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", "dev": true, "requires": { "@types/estree": "*" @@ -534,6 +682,8 @@ }, "magic-string": { "version": "0.25.7", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", + "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", "dev": true, "requires": { "sourcemap-codec": "^1.4.4" @@ -541,6 +691,8 @@ }, "minimatch": { "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -548,6 +700,8 @@ }, "once": { "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { "wrappy": "1" @@ -555,22 +709,32 @@ }, "path-is-absolute": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, "path-parse": { "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, "picomatch": { "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", "dev": true }, "prettier": { "version": "2.3.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.1.tgz", + "integrity": "sha512-p+vNbgpLjif/+D+DwAZAbndtRrR0md0MwfmOVN9N+2RgyACMT+7tfaRnT+WDPkqnuVwleyuBIG2XBxKDme3hPA==", "dev": true }, "resolve": { "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", "dev": true, "requires": { "is-core-module": "^2.2.0", @@ -579,6 +743,8 @@ }, "rollup": { "version": "2.52.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.52.1.tgz", + "integrity": "sha512-/SPqz8UGnp4P1hq6wc9gdTqA2bXQXGx13TtoL03GBm6qGRI6Hm3p4Io7GeiHNLl0BsQAne1JNYY+q/apcY933w==", "dev": true, "requires": { "fsevents": "~2.3.2" @@ -586,10 +752,14 @@ }, "sourcemap-codec": { "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", "dev": true }, "wrappy": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true } } diff --git a/docs/source/_custom_js/package.json b/docs/source/_custom_js/package.json index 633a43739..78d72b961 100644 --- a/docs/source/_custom_js/package.json +++ b/docs/source/_custom_js/package.json @@ -1,7 +1,7 @@ { - "name": "idom-docs-example-loader", + "name": "reactpy-docs-example-loader", "version": "1.0.0", - "description": "simple javascript client for IDOM's documentation", + "description": "simple javascript client for ReactPy's documentation", "main": "index.js", "scripts": { "build": "rollup --config", @@ -15,6 +15,6 @@ "rollup": "^2.35.1" }, "dependencies": { - "idom-client-react": "file:../../../src/client/packages/idom-client-react" + "@reactpy/client": "file:../../../src/js/packages/@reactpy/client" } } diff --git a/docs/source/_custom_js/src/index.js b/docs/source/_custom_js/src/index.js index 1aa401123..505adedd0 100644 --- a/docs/source/_custom_js/src/index.js +++ b/docs/source/_custom_js/src/index.js @@ -1,42 +1,42 @@ -import { mountWithLayoutServer, LayoutServerInfo } from "idom-client-react"; +import { SimpleReactPyClient, mount } from "@reactpy/client"; let didMountDebug = false; export function mountWidgetExample( mountID, viewID, - idomServerHost, - useActivateButton + reactpyServerHost, + useActivateButton, ) { - let idomHost, idomPort; - if (idomServerHost) { - [idomHost, idomPort] = idomServerHost.split(":", 2); + let reactpyHost, reactpyPort; + if (reactpyServerHost) { + [reactpyHost, reactpyPort] = reactpyServerHost.split(":", 2); } else { - idomHost = window.location.hostname; - idomPort = window.location.port; + reactpyHost = window.location.hostname; + reactpyPort = window.location.port; } - const serverInfo = new LayoutServerInfo({ - host: idomHost, - port: idomPort, - path: "/_idom/", - query: `view_id=${viewID}`, - secure: window.location.protocol == "https:", + const client = new SimpleReactPyClient({ + serverLocation: { + url: `${window.location.protocol}//${reactpyHost}:${reactpyPort}`, + route: "/", + query: `?view_id=${viewID}`, + }, }); const mountEl = document.getElementById(mountID); let isMounted = false; triggerIfInViewport(mountEl, () => { if (!isMounted) { - activateView(mountEl, serverInfo, useActivateButton); + activateView(mountEl, client, useActivateButton); isMounted = true; } }); } -function activateView(mountEl, serverInfo, useActivateButton) { +function activateView(mountEl, client, useActivateButton) { if (!useActivateButton) { - mountWithLayoutServer(mountEl, serverInfo); + mount(mountEl, client); return; } @@ -51,7 +51,7 @@ function activateView(mountEl, serverInfo, useActivateButton) { mountEl.setAttribute("class", "interactive widget-container"); mountWithLayoutServer(mountEl, serverInfo); } - }) + }), ); function fadeOutElementThenCallback(element, callback) { @@ -86,8 +86,8 @@ function triggerIfInViewport(element, callback) { }, { root: null, - threshold: 0.1, // set offset 0.1 means trigger if atleast 10% of element in viewport - } + threshold: 0.1, // set offset 0.1 means trigger if at least 10% of element in viewport + }, ); observer.observe(element); diff --git a/docs/source/_exts/async_doctest.py b/docs/source/_exts/async_doctest.py index 7db4116d9..96024d488 100644 --- a/docs/source/_exts/async_doctest.py +++ b/docs/source/_exts/async_doctest.py @@ -6,18 +6,16 @@ from sphinx.ext.doctest import DocTestBuilder from sphinx.ext.doctest import setup as doctest_setup - test_template = """ -import asyncio as __asyncio +import asyncio as __test_template_asyncio -async def __run(): +async def __test_template__main(): {test} globals().update(locals()) -loop = __asyncio.get_event_loop() -loop.run_until_complete(__run()) +__test_template_asyncio.run(__test_template__main()) """ @@ -42,10 +40,8 @@ def test_runner(self) -> DocTestRunner: @test_runner.setter def test_runner(self, value: DocTestRunner) -> None: self._test_runner = TestRunnerWrapper(value) - return None def setup(app: Sphinx) -> None: doctest_setup(app) app.add_builder(AsyncDoctestBuilder, override=True) - return None diff --git a/docs/source/_exts/autogen_api_docs.py b/docs/source/_exts/autogen_api_docs.py index 96ee6e876..b95d85a99 100644 --- a/docs/source/_exts/autogen_api_docs.py +++ b/docs/source/_exts/autogen_api_docs.py @@ -1,15 +1,14 @@ from __future__ import annotations import sys +from collections.abc import Collection, Iterator from pathlib import Path -from typing import Collection, Iterator from sphinx.application import Sphinx - HERE = Path(__file__).parent SRC = HERE.parent.parent.parent / "src" -PYTHON_PACKAGE = SRC / "idom" +PYTHON_PACKAGE = SRC / "py" / "reactpy" / "reactpy" AUTO_DIR = HERE.parent / "_auto" AUTO_DIR.mkdir(exist_ok=True) @@ -22,10 +21,12 @@ AUTODOC_TEMPLATE_WITH_MEMBERS = """\ .. automodule:: {module} :members: + :ignore-module-all: """ AUTODOC_TEMPLATE_WITHOUT_MEMBERS = """\ .. automodule:: {module} + :ignore-module-all: """ TITLE = """\ @@ -80,9 +81,12 @@ def get_module_name(path: Path) -> str: def get_section_symbol(path: Path) -> str: - rel_path_parts = path.relative_to(PYTHON_PACKAGE).parts - assert len(rel_path_parts) < len(SECTION_SYMBOLS), "package structure is too deep" - return SECTION_SYMBOLS[len(rel_path_parts)] + rel_path = path.relative_to(PYTHON_PACKAGE) + rel_path_parts = rel_path.parts + if len(rel_path_parts) > len(SECTION_SYMBOLS): + msg = f"package structure is too deep - ran out of section symbols: {rel_path}" + raise RuntimeError(msg) + return SECTION_SYMBOLS[len(rel_path_parts) - 1] def walk_python_files(root: Path, ignore_dirs: Collection[str]) -> Iterator[Path]: diff --git a/docs/source/_exts/build_custom_js.py b/docs/source/_exts/build_custom_js.py index b84378353..97857ba74 100644 --- a/docs/source/_exts/build_custom_js.py +++ b/docs/source/_exts/build_custom_js.py @@ -3,11 +3,10 @@ from sphinx.application import Sphinx - SOURCE_DIR = Path(__file__).parent.parent CUSTOM_JS_DIR = SOURCE_DIR / "_custom_js" def setup(app: Sphinx) -> None: - subprocess.run("npm install", cwd=CUSTOM_JS_DIR, shell=True) - subprocess.run("npm run build", cwd=CUSTOM_JS_DIR, shell=True) + subprocess.run("npm install", cwd=CUSTOM_JS_DIR, shell=True) # noqa S607 + subprocess.run("npm run build", cwd=CUSTOM_JS_DIR, shell=True) # noqa S607 diff --git a/docs/source/_exts/copy_vdom_json_schema.py b/docs/source/_exts/copy_vdom_json_schema.py index f78a163c2..38fc171ac 100644 --- a/docs/source/_exts/copy_vdom_json_schema.py +++ b/docs/source/_exts/copy_vdom_json_schema.py @@ -3,7 +3,7 @@ from sphinx.application import Sphinx -from idom.core.vdom import VDOM_JSON_SCHEMA +from reactpy.core.vdom import VDOM_JSON_SCHEMA def setup(app: Sphinx) -> None: diff --git a/docs/source/_exts/custom_autosectionlabel.py b/docs/source/_exts/custom_autosectionlabel.py index 573bc35dd..92ff5e2df 100644 --- a/docs/source/_exts/custom_autosectionlabel.py +++ b/docs/source/_exts/custom_autosectionlabel.py @@ -4,8 +4,10 @@ https://github.com/sphinx-doc/sphinx/blob/f9968594206e538f13fa1c27c065027f10d4ea27/LICENSE """ +from __future__ import annotations + from fnmatch import fnmatch -from typing import Any, Dict, cast +from typing import Any, cast from docutils import nodes from docutils.nodes import Node @@ -15,7 +17,6 @@ from sphinx.util import logging from sphinx.util.nodes import clean_astext - logger = logging.getLogger(__name__) @@ -30,7 +31,6 @@ def get_node_depth(node: Node) -> int: def register_sections_as_label(app: Sphinx, document: Node) -> None: docname = app.env.docname - print(docname) for pattern in app.config.autosectionlabel_skip_docs: if fnmatch(docname, pattern): @@ -67,7 +67,7 @@ def register_sections_as_label(app: Sphinx, document: Node) -> None: domain.labels[name] = docname, labelid, sectname -def setup(app: Sphinx) -> Dict[str, Any]: +def setup(app: Sphinx) -> dict[str, Any]: app.add_config_value("autosectionlabel_prefix_document", False, "env") app.add_config_value("autosectionlabel_maxdepth", None, "env") app.add_config_value("autosectionlabel_skip_docs", [], "env") diff --git a/docs/source/_exts/idom_example.py b/docs/source/_exts/reactpy_example.py similarity index 86% rename from docs/source/_exts/idom_example.py rename to docs/source/_exts/reactpy_example.py index 07c0a74a2..1171d32e0 100644 --- a/docs/source/_exts/idom_example.py +++ b/docs/source/_exts/reactpy_example.py @@ -2,28 +2,26 @@ import re from pathlib import Path -from typing import Any +from typing import Any, ClassVar +from docs_app.examples import ( + SOURCE_DIR, + get_example_files_by_name, + get_normalized_example_name, +) from docutils.parsers.rst import directives from docutils.statemachine import StringList from sphinx.application import Sphinx from sphinx.util.docutils import SphinxDirective from sphinx_design.tabs import TabSetDirective -from docs.examples import ( - SOURCE_DIR, - get_example_files_by_name, - get_normalized_example_name, -) - class WidgetExample(SphinxDirective): - has_content = False required_arguments = 1 _next_id = 0 - option_spec = { + option_spec: ClassVar[dict[str, Any]] = { "result-is-default-tab": directives.flag, "activate-button": directives.flag, } @@ -42,16 +40,14 @@ def run(self): ex_files = get_example_files_by_name(example_name) if not ex_files: src_file, line_num = self.get_source_info() - raise ValueError( - f"Missing example named {example_name!r} " - f"referenced by document {src_file}:{line_num}" - ) + msg = f"Missing example named {example_name!r} referenced by document {src_file}:{line_num}" + raise ValueError(msg) labeled_tab_items: list[tuple[str, Any]] = [] if len(ex_files) == 1: labeled_tab_items.append( ( - "app.py", + "main.py", _literal_include( path=ex_files[0], linenos=show_linenos, @@ -59,7 +55,9 @@ def run(self): ) ) else: - for path in sorted(ex_files, key=lambda p: p.name): + for path in sorted( + ex_files, key=lambda p: "" if p.name == "main.py" else p.name + ): labeled_tab_items.append( ( path.name, @@ -71,7 +69,7 @@ def run(self): ) result_tab_item = ( - "â–ļī¸ result", + "🚀 result", _interactive_widget( name=example_name, with_activate_button=not activate_result, @@ -87,7 +85,7 @@ def run(self): [], {}, _make_tab_items(labeled_tab_items), - self.lineno - 1, + self.lineno - 2, self.content_offset, "", self.state, @@ -113,7 +111,8 @@ def _literal_include(path: Path, linenos: bool): ".json": "json", }[path.suffix] except KeyError: - raise ValueError(f"Unknown extension type {path.suffix!r}") + msg = f"Unknown extension type {path.suffix!r}" + raise ValueError(msg) from None return _literal_include_template.format( name=str(path.relative_to(SOURCE_DIR)), @@ -161,7 +160,7 @@ def _interactive_widget(name, with_activate_button): _interactive_widget_template = """ -.. idom-view:: {name} +.. reactpy-view:: {name} {activate_button_opt} """ @@ -178,4 +177,4 @@ def _string_to_nested_lines(content): def setup(app: Sphinx) -> None: - app.add_directive("idom", WidgetExample) + app.add_directive("reactpy", WidgetExample) diff --git a/docs/source/_exts/idom_view.py b/docs/source/_exts/reactpy_view.py similarity index 69% rename from docs/source/_exts/idom_view.py rename to docs/source/_exts/reactpy_view.py index 995640301..6a583998f 100644 --- a/docs/source/_exts/idom_view.py +++ b/docs/source/_exts/reactpy_view.py @@ -1,31 +1,31 @@ import os +from typing import Any, ClassVar +from docs_app.examples import get_normalized_example_name from docutils.nodes import raw from docutils.parsers.rst import directives from sphinx.application import Sphinx from sphinx.util.docutils import SphinxDirective -from docs.examples import get_normalized_example_name - - -_IDOM_EXAMPLE_HOST = os.environ.get("IDOM_DOC_EXAMPLE_SERVER_HOST", "") -_IDOM_STATIC_HOST = os.environ.get("IDOM_DOC_STATIC_SERVER_HOST", "/docs").rstrip("/") +_REACTPY_EXAMPLE_HOST = os.environ.get("REACTPY_DOC_EXAMPLE_SERVER_HOST", "") +_REACTPY_STATIC_HOST = os.environ.get("REACTPY_DOC_STATIC_SERVER_HOST", "/docs").rstrip( + "/" +) class IteractiveWidget(SphinxDirective): - has_content = False required_arguments = 1 _next_id = 0 - option_spec = { + option_spec: ClassVar[dict[str, Any]] = { "activate-button": directives.flag, "margin": float, } def run(self): IteractiveWidget._next_id += 1 - container_id = f"idom-widget-{IteractiveWidget._next_id}" + container_id = f"reactpy-widget-{IteractiveWidget._next_id}" view_id = get_normalized_example_name( self.arguments[0], # only used if example name starts with "/" @@ -38,15 +38,15 @@ def run(self):
@@ -58,4 +58,4 @@ def run(self): def setup(app: Sphinx) -> None: - app.add_directive("idom-view", IteractiveWidget) + app.add_directive("reactpy-view", IteractiveWidget) diff --git a/docs/source/_static/css/furo-theme-overrides.css b/docs/source/_static/css/furo-theme-overrides.css index f23c23168..a258e025e 100644 --- a/docs/source/_static/css/furo-theme-overrides.css +++ b/docs/source/_static/css/furo-theme-overrides.css @@ -1,4 +1,6 @@ -body { - --admonition-title-font-size: 1rem !important; - --admonition-font-size: 1rem !important; +.sidebar-container { + width: 18em; +} +.sidebar-brand-text { + display: none; } diff --git a/docs/source/_static/css/idom-view.css b/docs/source/_static/css/reactpy-view.css similarity index 91% rename from docs/source/_static/css/idom-view.css rename to docs/source/_static/css/reactpy-view.css index 9abe24198..56df74970 100644 --- a/docs/source/_static/css/idom-view.css +++ b/docs/source/_static/css/reactpy-view.css @@ -21,11 +21,6 @@ width: 100%; } -.center-content { - display: flex; - align-items: center; - justify-content: center; -} .enable-widget-button { padding: 10px; color: #ffffff !important; diff --git a/docs/source/_static/css/set-color-scheme.css b/docs/source/_static/css/set-color-scheme.css deleted file mode 100644 index 4b9521c7d..000000000 --- a/docs/source/_static/css/set-color-scheme.css +++ /dev/null @@ -1,3 +0,0 @@ -:root { - color-scheme: light dark; -} diff --git a/docs/source/_static/css/sphinx-design-overrides.css b/docs/source/_static/css/sphinx-design-overrides.css index cc9b285c0..767d9d16c 100644 --- a/docs/source/_static/css/sphinx-design-overrides.css +++ b/docs/source/_static/css/sphinx-design-overrides.css @@ -1,12 +1,3 @@ -body { - --sd-color-info: var(--color-admonition-title-background--note); - --sd-color-warning: var(--color-admonition-title-background--warning); - --sd-color-danger: var(--color-admonition-title-background--danger); - --sd-color-info-text: var(--color-admonition-title--note); - --sd-color-warning-text: var(--color-admonition-title--warning); - --sd-color-danger-text: var(--color-admonition-title--danger); -} - .sd-card-body { display: flex; flex-direction: column; diff --git a/docs/source/_static/install-and-run-idom.gif b/docs/source/_static/install-and-run-idom.gif deleted file mode 100644 index 67d226a12..000000000 Binary files a/docs/source/_static/install-and-run-idom.gif and /dev/null differ diff --git a/docs/source/_static/install-and-run-reactpy.gif b/docs/source/_static/install-and-run-reactpy.gif new file mode 100644 index 000000000..49d431341 Binary files /dev/null and b/docs/source/_static/install-and-run-reactpy.gif differ diff --git a/docs/source/about/changelog.rst b/docs/source/about/changelog.rst new file mode 100644 index 000000000..48a229eee --- /dev/null +++ b/docs/source/about/changelog.rst @@ -0,0 +1,1171 @@ +Changelog +========= + +.. note:: + + All notable changes to this project will be recorded in this document. The style of + which is based on `Keep a Changelog `__. The versioning + scheme for the project adheres to `Semantic Versioning `__. For + more info, see the :ref:`Contributor Guide `. + + +.. INSTRUCTIONS FOR CHANGELOG CONTRIBUTORS +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +.. If you're adding a changelog entry, be sure to read the "Creating a Changelog Entry" +.. section of the documentation before doing so for instructions on how to adhere to the +.. "Keep a Changelog" style guide (https://keepachangelog.com). + +Unreleased +---------- + +**Fixed** + +- :pull:`1118` - `module_from_template` is broken with a recent release of `requests` +- :pull:`1131` - `module_from_template` did not work when using Flask backend +- :pull:`1200` - Fixed `UnicodeDecodeError` when using `reactpy.web.export` + +**Added** + +- :pull:`1165` - Allow asynchronously rendering discrete component tree - enable this + experimental feature by setting `REACTPY_ASYNC_RENDERING=true`. This should improve + the overall responsiveness of your app, particularly when handling larger renders + that would otherwise block faster renders from being processed. + +**Changed** + +- :pull:`1171` - Previously ``None``, when present in an HTML element, would render as + the string ``"None"``. Now ``None`` will not render at all. This is consistent with + how ``None`` is handled when returned from components. It also makes it easier to + conditionally render elements. For example, previously you would have needed to use a + fragment to conditionally render an element by writing + ``something if condition else html._()``. Now you can simply write + ``something if condition else None``. + +**Deprecated** + +- :pull:`1171` - The ``Stop`` exception. Recent releases of ``anyio`` have made this + exception difficult to use since it now raises an ``ExceptionGroup``. This exception + was primarily used for internal testing purposes and so is now deprecated. + + +v1.0.2 +------ + +**Fixed** + +- :issue:`1086` - fix rendering bug when children change positions (via :pull:`1085`) + + +v1.0.1 +------ + +**Changed** + +- :pull:`1050` - Warn and attempt to fix missing mime types, which can result in ``reactpy.run`` not working as expected. +- :pull:`1051` - Rename ``reactpy.backend.BackendImplementation`` to ``reactpy.backend.BackendType`` +- :pull:`1051` - Allow ``reactpy.run`` to fail in more predictable ways + +**Fixed** + +- :issue:`930` - better traceback for JSON serialization errors (via :pull:`1008`) +- :issue:`437` - explain that JS component attributes must be JSON (via :pull:`1008`) +- :pull:`1051` - Fix ``reactpy.run`` port assignment sometimes attaching to in-use ports on Windows +- :pull:`1051` - Fix ``reactpy.run`` not recognizing ``fastapi`` + + +v1.0.0 +------ +:octicon:`milestone` *released on 2023-03-14* + +No changes. + + +v1.0.0-a6 +--------- +:octicon:`milestone` *released on 2023-02-23* + +**Fixed** + +- :pull:`936` - remaining issues from :pull:`934` + + +v1.0.0-a5 +--------- +:octicon:`milestone` *released on 2023-02-21* + +**Fixed** + +- :pull:`934` - minor issues with camelCase rewrite CLI utility + + +v1.0.0-a4 +--------- +:octicon:`milestone` *released on 2023-02-21* + +**Changed** + +- :pull:`919` - Reverts :pull:`841` as per the conclusion in :discussion:`916`. but + preserves the ability to declare attributes with snake_case. + +**Deprecated** + +- :pull:`919` - Declaration of keys via keyword arguments in standard elements. A script + has been added to automatically convert old usages where possible. + + +v1.0.0-a3 +--------- +:octicon:`milestone` *released on 2023-02-02* + +**Fixed** + +- :pull:`908` - minor type hint issue with ``VdomDictConstructor`` + +**Removed** + +- :pull:`907` - accidental import of reactpy.testing + + +v1.0.0-a2 +--------- +:octicon:`milestone` *released on 2023-01-31* + +**Reverted** + +- :pull:`901` - reverts :pull:`886` due to :issue:`896` + +**Fixed** + +- :issue:`896` - Stale event handlers after disconnect/reconnect cycle +- :issue:`898` - Fixed CLI not registered as entry point + + +v1.0.0-a1 +--------- +:octicon:`milestone` *released on 2023-01-28* + +**Changed** + +- :pull:`841` - Revamped element constructor interface. Now instead of passing a + dictionary of attributes to element constructors, attributes are declared using + keyword arguments. For example, instead of writing: + + .. code-block:: + + html.div({"className": "some-class"}, "some", "text") + + You now should write: + + .. code-block:: + + html.div("some", "text", class_name="some-class") + + .. note:: + + All attributes are written using ``snake_case``. + + In conjunction, with these changes, ReactPy now supplies a command line utility that + makes a "best effort" attempt to automatically convert code to the new API. Usage of + this utility is as follows: + + .. code-block:: bash + + reactpy update-html-usages [PATHS] + + Where ``[PATHS]`` is any number of directories or files that should be rewritten. + + .. warning:: + + After running this utility, code comments and formatting may have been altered. It's + recommended that you run a code formatting tool like `Black + `__ and manually review and replace any comments that + may have been moved. + +**Fixed** + +- :issue:`755` - unification of component and VDOM constructor interfaces. See above. + + +v0.44.0 +------- +:octicon:`milestone` *released on 2023-01-27* + +**Deprecated** + +- :pull:`876` - ``reactpy.widgets.hotswap``. The function has no clear uses outside of some + internal applications. For this reason it has been deprecated. + +**Removed** + +- :pull:`886` - Ability to access element value from events via `event['value']` key. + Instead element value should be accessed via `event['target']['value']`. Originally + deprecated in :ref:`v0.34.0`. +- :pull:`886` - old misspelled option ``reactpy.config.REACTPY_WED_MODULES_DIR``. Originally + deprecated in :ref:`v0.36.1`. + + +v0.43.0 +------- +:octicon:`milestone` *released on 2023-01-09* + +**Deprecated** + +- :pull:`870` - ``ComponentType.should_render()``. This method was implemented based on + reading the React/Preact source code. As it turns out though it seems like it's mostly + a vestige from the fact that both these libraries still support class-based + components. The ability for components to not render also caused several bugs. + +**Fixed** + +- :issue:`846` - Nested context does no update value if outer context should not render. +- :issue:`847` - Detached model state on render of context consumer if unmounted and + context value does not change. + + +v0.42.0 +------- +:octicon:`milestone` *released on 2022-12-02* + +**Added** + +- :pull:`835` - Ability to customize the ```` element of ReactPy's built-in client. +- :pull:`835` - ``vdom_to_html`` utility function. +- :pull:`843` - Ability to subscribe to changes that are made to mutable options. +- :pull:`832` - ``del_html_head_body_transform`` to remove ````, ````, and ```` while preserving children. +- :pull:`699` - Support for form element serialization + +**Fixed** + +- :issue:`582` - ``REACTPY_DEBUG_MODE`` is now mutable and can be changed at runtime +- :pull:`832` - Fix ``html_to_vdom`` improperly removing ````, ````, and ```` nodes. + +**Removed** + +- :pull:`832` - Removed ``reactpy.html.body`` as it is currently unusable due to technological limitations, and thus not needed. +- :pull:`840` - remove ``REACTPY_FEATURE_INDEX_AS_DEFAULT_KEY`` option +- :pull:`835` - ``serve_static_files`` option from backend configuration + +**Deprecated** + +- :commit:`8f3785b` - Deprecated ``module_from_template`` + +v0.41.0 +------- +:octicon:`milestone` *released on 2022-11-01* + +**Changed** + +- :pull:`823` - The hooks ``use_location`` and ``use_scope`` are no longer + implementation specific and are now available as top-level imports. Instead of each + backend defining these hooks, backends establish a ``ConnectionContext`` with this + information. +- :pull:`824` - ReactPy's built-in backend server now expose the following routes: + + - ``/_reactpy/assets/`` + - ``/_reactpy/stream/`` + - ``/_reactpy/modules/`` + - ``//`` + + This should allow the browser to cache static resources. Even if your ``url_prefix`` + is ``/_reactpy``, your app should still work as expected. Though if you're using + ``reactpy-router``, ReactPy's server routes will always take priority. +- :pull:`824` - Backend implementations now strip any URL prefix in the pathname for + ``use_location``. +- :pull:`827` - ``use_state`` now returns a named tuple with ``value`` and ``set_value`` + fields. This is convenient for adding type annotations if the initial state value is + not the same as the values you might pass to the state setter. Where previously you + might have to do something like: + + .. code-block:: + + value: int | None = None + value, set_value = use_state(value) + + Now you can annotate your state using the ``State`` class: + + .. code-block:: + + state: State[int | None] = use_state(None) + + # access value and setter + state.value + state.set_value + + # can still destructure if you need to + value, set_value = state + +**Added** + +- :pull:`823` - There is a new ``use_connection`` hook which returns a ``Connection`` + object. This ``Connection`` object contains a ``location`` and ``scope``, along with + a ``carrier`` which is unique to each backend implementation. + + +v0.40.2 +------- +:octicon:`milestone` *released on 2022-09-13* + +**Changed** + +- :pull:`809` - Avoid the use of JSON patch for diffing models. + + +v0.40.1 +------- +:octicon:`milestone` *released on 2022-09-11* + +**Fixed** + +- :issue:`806` - Child models after a component fail to render + + +v0.40.0 (yanked) +---------------- +:octicon:`milestone` *released on 2022-08-13* + +**Fixed** + +- :issue:`777` - Fix edge cases where ``html_to_vdom`` can fail to convert HTML +- :issue:`789` - Conditionally rendered components cannot use contexts +- :issue:`773` - Use strict equality check for text, numeric, and binary types in hooks +- :issue:`801` - Accidental mutation of old model causes invalid JSON Patch + +**Changed** + +- :pull:`123` - set default timeout on playwright page for testing +- :pull:`787` - Track contexts in hooks as state +- :pull:`787` - remove non-standard ``name`` argument from ``create_context`` + +**Added** + +- :pull:`123` - ``asgiref`` as a dependency +- :pull:`795` - ``lxml`` as a dependency + + +v0.39.0 +------- +:octicon:`milestone` *released on 2022-06-20* + +**Fixed** + +- :pull:`763` - ``No module named 'reactpy.server'`` from ``reactpy.run`` +- :pull:`749` - Setting appropriate MIME type for web modules in `sanic` server implementation + +**Changed** + +- :pull:`763` - renamed various: + + - ``reactpy.testing.server -> reactpy.testing.backend`` + - ``ServerFixture -> BackendFixture`` + - ``DisplayFixture.server -> DisplayFixture.backend`` + +- :pull:`765` - ``exports_default`` parameter is removed from ``module_from_template``. + +**Added** + +- :pull:`765` - ability to specify versions with module templates (e.g. + ``module_from_template("react@^17.0.0", ...)``). + + +v0.38.1 +------- +:octicon:`milestone` *released on 2022-04-15* + +**Fixed** + +- `reactive-python/reactpy-jupyter#22 `__ - + a missing file extension was causing a problem with WebPack. + + +v0.38.0 +------- +:octicon:`milestone` *released on 2022-04-15* + +No changes. + + +v0.38.0-a4 +---------- +:octicon:`milestone` *released on 2022-04-15* + +**Added** + +- :pull:`733` - ``use_debug_value`` hook + +**Changed** + +- :pull:`733` - renamed ``assert_reactpy_logged`` testing util to ``assert_reactpy_did_log`` + + +v0.38.0-a3 +---------- +:octicon:`milestone` *released on 2022-04-15* + +**Changed** + +- :pull:`730` - Layout context management is not async + + +v0.38.0-a2 +---------- +:octicon:`milestone` *released on 2022-04-14* + +**Added** + +- :pull:`721` - Implement ``use_location()`` hook. Navigating to any route below the + root of the application will be reflected in the ``location.pathname``. This operates + in concert with how ReactPy's configured routes have changed. This will ultimately work + towards resolving :issue:`569`. + +**Changed** + +- :pull:`721` - The routes ReactPy configures on apps have changed + + .. code-block:: text + + prefix/_api/modules/* web modules + prefix/_api/stream websocket endpoint + prefix/* client react app + + This means that ReactPy's client app is available at any route below the configured + ``url_prefix`` besides ``prefix/_api``. The ``_api`` route will likely remain a route + which is reserved by ReactPy. The route navigated to below the ``prefix`` will be shown + in ``use_location``. + +- :pull:`721` - ReactPy's client now uses Preact instead of React + +- :pull:`726` - Renamed ``reactpy.server`` to ``reactpy.backend``. Other references to "server + implementations" have been renamed to "backend implementations" throughout the + documentation and code. + +**Removed** + +- :pull:`721` - ``redirect_root`` server option + + +v0.38.0-a1 +---------- +:octicon:`milestone` *released on 2022-03-27* + +**Changed** + +- :pull:`703` - How ReactPy integrates with servers. ``reactpy.run`` no longer accepts an app + instance to discourage use outside of testing. ReactPy's server implementations now + provide ``configure()`` functions instead. ``reactpy.testing`` has been completely + reworked in order to support async web drivers +- :pull:`703` - ``PerClientStateServer`` has been functionally replaced by ``configure`` + +**Added** + +- :issue:`669` - Access to underlying server requests via contexts + +**Removed** + +- :issue:`669` - Removed ``reactpy.widgets.multiview`` since basic routing view ``use_scope`` is + now possible as well as all ``SharedClientStateServer`` implementations. + +**Fixed** + +- :issue:`591` - ReactPy's test suite no longer uses sync web drivers +- :issue:`678` - Updated Sanic requirement to ``>=21`` +- :issue:`657` - How we advertise ``reactpy.run`` + + +v0.37.2 +------- +:octicon:`milestone` *released on 2022-03-27* + +**Changed** + +- :pull:`701` - The name of ``proto`` modules to ``types`` and added a top level + ``reactpy.types`` module + +**Fixed** + +- :pull:`716` - A typo caused ReactPy to use the insecure ``ws`` web-socket protocol on + pages loaded with ``https`` instead of the secure ``wss`` protocol + + +v0.37.1 +------- +:octicon:`milestone` *released on 2022-03-05* + +No changes. + + +v0.37.1-a2 +---------- +:octicon:`milestone` *released on 2022-03-02* + +**Fixed:** + +- :issue:`684` - Revert :pull:`694` and by making ``value`` uncontrolled client-side + + +v0.37.1-a1 +---------- +:octicon:`milestone` *released on 2022-02-28* + +**Fixed:** + +- :issue:`684` - ``onChange`` event for inputs missing key strokes + + +v0.37.0 +------- +:octicon:`milestone` *released on 2022-02-27* + +**Added:** + +- :issue:`682` - Support for keys in HTML fragments +- :pull:`585` - Use Context Hook + +**Fixed:** + +- :issue:`690` - React warning about set state in unmounted component +- :pull:`688` - Missing reset of schedule_render_later flag + +---- + +Releases below do not use the "Keep a Changelog" style guidelines. + +---- + +v0.36.3 +------- +:octicon:`milestone` *released on 2022-02-18* + +Misc bug fixes along with a minor improvement that allows components to return ``None`` +to render nothing. + +**Closed Issues** + +- All child states wiped upon any child key change - :issue:`652` +- Allow NoneType returns within components - :issue:`538` + +**Merged Pull Requests** + +- fix #652 - :pull:`672` +- Fix 663 - :pull:`667` + + +v0.36.2 +------- +:octicon:`milestone` *released on 2022-02-02* + +Hot fix for newly introduced ``DeprecatedOption``: + +- :commit:`c146dfb264cbc3d2256a62efdfe9ccf62c795b01` + + +v0.36.1 +------- +:octicon:`milestone` *released on 2022-02-02* + +Includes bug fixes and renames the configuration option ``REACTPY_WED_MODULES_DIR`` to +``REACTPY_WEB_MODULES_DIR`` with a corresponding deprecation warning. + +**Closed Issues** + +- Fix Key Error When Cleaning Up Event Handlers - :issue:`640` +- Update Script Tag Behavior - :issue:`628` + +**Merged Pull Requests** + +- mark old state as None if unmounting - :pull:`641` +- rename REACTPY_WED_MODULES_DIR to REACTPY_WEB_MODULES_DIR - :pull:`638` + + +v0.36.0 +------- +:octicon:`milestone` *released on 2022-01-30* + +This release includes an important fix for errors produced after :pull:`623` was merged. +In addition there is not a new ``http.script`` element which can behave similarly to a +standard HTML `` diff --git a/docs/source/getting-started/_static/embed-idom-view/index.html b/docs/source/getting-started/_static/embed-idom-view/index.html deleted file mode 100644 index 3080e1d5d..000000000 --- a/docs/source/getting-started/_static/embed-idom-view/index.html +++ /dev/null @@ -1,39 +0,0 @@ - - - - - Example App - - -

This is an Example App

-

Just below is an embedded IDOM view...

-
- - - diff --git a/docs/source/getting-started/_static/embed-idom-view/main.py b/docs/source/getting-started/_static/embed-idom-view/main.py deleted file mode 100644 index 0c0cb5ac0..000000000 --- a/docs/source/getting-started/_static/embed-idom-view/main.py +++ /dev/null @@ -1,23 +0,0 @@ -from sanic import Sanic -from sanic.response import file - -from idom import component, html -from idom.server.sanic import Config, PerClientStateServer - - -app = Sanic(__name__) - - -@app.route("/") -async def index(request): - return await file("index.html") - - -@component -def IdomView(): - return html.code("This text came from an IDOM App") - - -PerClientStateServer(IdomView, app=app, config=Config(url_prefix="/_idom")) - -app.run(host="127.0.0.1", port=5000) diff --git a/docs/source/getting-started/index.rst b/docs/source/getting-started/index.rst deleted file mode 100644 index 0a5ed4daf..000000000 --- a/docs/source/getting-started/index.rst +++ /dev/null @@ -1,108 +0,0 @@ -Getting Started -=============== - -.. toctree:: - :hidden: - - installing-idom - running-idom - -.. dropdown:: :octicon:`bookmark-fill;2em` What You'll Learn - :color: info - :animate: fade-in - :open: - - .. grid:: 1 2 2 2 - - .. grid-item-card:: :octicon:`tools` Installing IDOM - :link: installing-idom - :link-type: doc - - Learn how IDOM can be installed in a variety of different ways - with different web - servers and even in different frameworks. - - .. grid-item-card:: :octicon:`play` Running IDOM - :link: running-idom - :link-type: doc - - See the ways that IDOM can be run with servers or be embedded in existing - applications. - -The fastest way to get started with IDOM is to try it out in a `Juptyer Notebook -`__. -If you want to use a Notebook to work through the examples shown in this documentation, -you'll need to replace calls to ``idom.run(App)`` with a line at the end of each cell -that constructs the ``App()`` in question. If that doesn't make sense, the introductory -notebook linked below will demonstrate how to do this: - -.. card:: - :link: https://mybinder.org/v2/gh/idom-team/idom-jupyter/main?urlpath=lab/tree/notebooks/introduction.ipynb - - .. image:: _static/idom-in-jupyterlab.gif - :scale: 72% - :align: center - - -Section 1: Installing IDOM --------------------------- - -The next fastest option is to install IDOM with ``pip``: - -.. code-block:: bash - - pip install "idom[stable]" - -To check that everything is working you can run the sample application: - -.. code-block:: bash - - python -c "import idom; idom.run_sample_app(open_browser=True)" - -This should automatically open up a browser window to a page that looks like this: - -.. card:: - - .. idom-view:: _examples/sample_app - -If you get a ``RuntimeError`` similar to the following: - -.. code-block:: text - - Found none of the following builtin server implementations... - -Then be sure you installed ``"idom[stable]"`` and not just ``idom``. - -For anything else, report your issue in IDOM's :discussion-type:`discussion forum -`. - -.. card:: - :link: installing-idom - :link-type: doc - - :octicon:`book` Read More - ^^^^^^^^^^^^^^^^^^^^^^^^^ - - Learn how IDOM can be installed in a variety of different ways - with different web - servers and even in different frameworks. - - -Section 2: Running IDOM ------------------------ - -Once you've :ref:`installed IDOM `. The simplest way to run IDOM is -with the :func:`~idom.server.prefab.run` function. By default this will execute your -application using one of the builtin server implementations whose dependencies have all -been installed. Running a tiny "hello world" application just requires the following -code: - -.. idom:: _examples/hello_world - -.. card:: - :link: running-idom - :link-type: doc - - :octicon:`book` Read More - ^^^^^^^^^^^^^^^^^^^^^^^^^ - - See the ways that IDOM can be run with servers or be embedded in existing - applications. diff --git a/docs/source/getting-started/installing-idom.rst b/docs/source/getting-started/installing-idom.rst deleted file mode 100644 index a9082972d..000000000 --- a/docs/source/getting-started/installing-idom.rst +++ /dev/null @@ -1,114 +0,0 @@ -Installing IDOM -=============== - -The easiest way to ``pip`` install idom is to do so using the ``stable`` option: - -.. code-block:: bash - - pip install "idom[stable]" - -This includes a set of default dependencies for one of the builtin web server -implementation. If you want to install IDOM without these dependencies you may simply -``pip install idom``. - - -Installing Other Servers ------------------------- - -IDOM includes built-in support for a variety web server implementations. To install the -required dependencies for each you should substitute ``stable`` from the ``pip install`` -command above with one of the options below: - -- ``fastapi`` - https://fastapi.tiangolo.com -- ``flask`` - https://palletsprojects.com/p/flask/ -- ``sanic`` - https://sanicframework.org -- ``tornado`` - https://www.tornadoweb.org/en/stable/ - -If you need to, you can install more than one option by separating them with commas: - -.. code-block:: bash - - pip install idom[fastapi,flask,sanic,tornado] - -Once this is complete you should be able to :ref:`run IDOM ` with your -:ref:`chosen server implementation `. - - -Installing In Other Frameworks ------------------------------- - -While IDOM can run in a variety of contexts, sometimes web frameworks require extra work -in order to integrate with them. In these cases, the IDOM team distributes bindings for -various frameworks as separate Python packages. For documentation on how to install and -run IDOM in the supported frameworks, follow the links below: - -.. raw:: html - - - -.. role:: transparent-text-color - -.. We add transparent-text-color to the text so it's not visible, but it's still -.. searchable. - -.. grid:: 3 - - .. grid-item-card:: - :link: https://github.com/idom-team/django-idom - :img-background: _static/logo-django.svg - :class-card: card-logo-image - - :transparent-text-color:`Django` - - .. grid-item-card:: - :link: https://github.com/idom-team/idom-jupyter - :img-background: _static/logo-jupyter.svg - :class-card: card-logo-image - - :transparent-text-color:`Jupyter` - - .. grid-item-card:: - :link: https://github.com/idom-team/idom-dash - :img-background: _static/logo-plotly.svg - :class-card: card-logo-image - - :transparent-text-color:`Plotly Dash` - - -Installing for Development --------------------------- - -If you want to contribute to the development of IDOM or modify it, you'll want to -install a development version of IDOM. This involves cloning the repository where IDOM's -source is maintained, and setting up a :ref:`development environment`. From there you'll -be able to modifying IDOM's source code and :ref:`run its tests ` to -ensure the modifications you've made are backwards compatible. If you want to add a new -feature to IDOM you should write your own test that validates its behavior. - -If you have questions about how to modify IDOM or help with its development, be sure to -`start a discussion -`__. The IDOM team -are always excited to :ref:`welcome ` new contributions and -contributors of all kinds - -.. card:: - :link: /developing-idom/index - :link-type: doc - - :octicon:`book` Read More - ^^^^^^^^^^^^^^^^^^^^^^^^^ - - Learn more about how to contribute to the development of IDOM. diff --git a/docs/source/getting-started/running-idom.rst b/docs/source/getting-started/running-idom.rst deleted file mode 100644 index 3b180f6e5..000000000 --- a/docs/source/getting-started/running-idom.rst +++ /dev/null @@ -1,290 +0,0 @@ -Running IDOM -============ - -The simplest way to run IDOM is with the :func:`~idom.server.prefab.run` function. By -default this will execute your application using one of the builtin server -implementations whose dependencies have all been installed. Running a tiny "hello world" -application just requires the following code: - -.. idom:: _examples/hello_world - -.. note:: - - Try clicking the **â–ļī¸ Result** tab to see what this displays! - - -Running IDOM in Debug Mode --------------------------- - -IDOM provides a debug mode that is turned off by default. This can be enabled when you -run your application by setting the ``IDOM_DEBUG_MODE`` environment variable. - -.. tab-set:: - - .. tab-item:: Unix Shell - - .. code-block:: - - export IDOM_DEBUG_MODE=1 - python my_idom_app.py - - .. tab-item:: Command Prompt - - .. code-block:: text - - set IDOM_DEBUG_MODE=1 - python my_idom_app.py - - .. tab-item:: PowerShell - - .. code-block:: powershell - - $env:IDOM_DEBUG_MODE = "1" - python my_idom_app.py - -.. danger:: - - Leave debug mode off in production! - -Among other things, running in this mode: - -- Turns on debug log messages -- Adds checks to ensure the :ref:`VDOM` spec is adhered to -- Displays error messages that occur within your app - -Errors will be displayed where the uppermost component is located in the view: - -.. idom:: _examples/debug_error_example - - -Choosing a Server Implementation --------------------------------- - -Without extra care, running an IDOM app with the ``run()`` function can be somewhat -inpredictable since the kind of server being used by default depends on what gets -discovered first. To be more explicit about which server implementation you want to run -with you can import your chosen server class and pass it to the ``server_type`` -parameter of ``run()``: - -.. code-block:: - - from idom import component, html, run - from idom.server.sanic import PerClientStateServer - - - @component - def App(): - return html.h1(f"Hello, World!") - - - run(App, server_type=PerClientStateServer) - -Presently IDOM's core library supports the following server implementations: - -- :mod:`idom.server.fastapi` -- :mod:`idom.server.sanic` -- :mod:`idom.server.flask` -- :mod:`idom.server.tornado` - -.. hint:: - - To install them, see the :ref:`Installing Other Servers` section. - - -Available Server Types ----------------------- - -Some of server implementations have more than one server type available. The server type -which exists for all implementations is the ``PerClientStateServer``. This server type -displays a unique view to each user who visits the site. For those that support it, -there may also be a ``SharedClientStateServer`` available. This server type presents the -same view to all users who visit the site. For example, if you were to run the following -code: - -.. code-block:: - - from idom import component, hooks, html, run - from idom.server.sanic import SharedClientStateServer - - - @component - def Slider(): - value, set_value = hooks.use_state(50) - return html.input({"type": "range", "min": 1, "max": 100, "value": value}) - - - run(Slider, server_type=SharedClientStateServer) - -Two clients could see the slider and see a synchronized view of it. That is, when one -client moved the slider, the other would see the slider update without their action. -This might look similar to the video below: - -.. image:: _static/shared-client-state-server-slider.gif - -Presently the following server implementations support the ``SharedClientStateServer``: - -- :func:`idom.server.fastapi.SharedClientStateServer` -- :func:`idom.server.sanic.SharedClientStateServer` - -.. note:: - - If you need to, your can :ref:`write your own server implementation `. - -Common Server Settings ----------------------- - -Each server implementation has its own high-level settings that are defined by its -respective ``Config`` (a typed dictionary). As a general rule, these ``Config`` types -expose the same options across implementations. These configuration dictionaries can -then be passed to the ``run()`` function via the ``config`` parameter: - -.. code-block:: - - from idom import run, component, html - from idom.server.sanic import PerClientStateServer, Config - - - @component - def App(): - return html.h1(f"Hello, World!") - - - server_config = Config( - cors=False, - url_prefix="", - serve_static_files=True, - redirect_root_to_index=True, - ) - - run(App, server_type=PerClientStateServer, config=server_config) - -Here's the list of available configuration types: - -- :class:`idom.server.fastapi.Config` -- :class:`idom.server.sanic.Config` -- :class:`idom.server.flask.Config` -- :class:`idom.server.tornado.Config` - - -Specific Server Settings ------------------------- - -The ``Config`` :ref:`described above ` is meant to be an -implementation agnostic - all ``Config`` objects support a similar set of options. -However, there are inevitably cases where you need to set up your chosen server using -implementation specific details. For instance, you might want to add an extra route to -the server your using in order to provide extra resources to your application. - -Doing this kind of set up can be achieved by passing in an instance of your chosen -server implementation into the ``app`` parameter of the ``run()`` function. To -illustrate, if I'm making my application with ``sanic`` and I want to add an extra route -I would do the following: - -.. code-block:: - - from sanic import Sanic - from idom import component, html, run - from idom.server.sanic import PerClientStateServer - - app = Sanic(__name__) - - # these are implementation specific settings only known to `sanic` servers - app.config.REQUEST_TIMEOUT = 60 - app.config.RESPONSE_TIMEOUT = 60 - - - @component - def SomeView(): - return html.form({"action": }) - - - run(SomeView, server_type=PerClientStateServer, app=app) - - -Add to an Existing Web Server ------------------------------ - -If you're already serving an application with one of the supported web servers listed -above, you can add an IDOM to them as a server extension. Instead of using the ``run()`` -function, you'll instantiate one of IDOM's server implementations by passing it an -instance of your existing application: - -.. code-block:: - - from sanic import Sanic - - from idom import component, html - from idom.server.sanic import PerClientStateServer, Config - - existing_app = Sanic(__name__) - - - @component - def IdomView(): - return html.h1("This is an IDOM App") - - - PerClientStateServer(IdomView, app=existing_app, config=Config(url_prefix="app")) - - existing_app.run(host="127.0.0.1", port=8000) - -To test that everything is working, you should be able to navigate to -``https://127.0.0.1:8000/app`` where you should see the results from ``IdomView``. - - -Embed in an Existing Webpage ----------------------------- - -IDOM provides a Javascript client called ``idom-client-react`` that can be used to embed -IDOM views within an existing applications. This is actually how the interactive -examples throughout this documentation have been created. You can try this out by -embedding one the examples from this documentation into your own webpage: - -.. tab-set:: - - .. tab-item:: HTML - - .. literalinclude:: _static/embed-doc-ex.html - :language: html - - .. tab-item:: â–ļī¸ Result - - .. raw:: html - :file: _static/embed-doc-ex.html - -.. note:: - - For more information on how to use the client see the :ref:`Javascript API` - reference. Or if you need to, your can :ref:`write your own server implementation - `. - -As mentioned though, this is connecting to the server that is hosting this -documentation. If you want to connect to a view from your own server, you'll need to -change the URL above to one you provide. One way to do this might be to :ref:`add to an -existing web server`. Another would be to run IDOM in an adjacent web server instance -that you coordinate with something like `NGINX `__. For the sake -of simplicity, we'll assume you do something similar to the following in an existing -Python app: - -.. tab-set:: - - .. tab-item:: main.py - - .. literalinclude:: _static/embed-idom-view/main.py - :language: python - - .. tab-item:: index.html - - .. literalinclude:: _static/embed-idom-view/index.html - :language: html - -After running ``python main.py``, you should be able to navigate to -``http://127.0.0.1:8000/index.html`` and see: - -.. card:: - :text-align: center - - .. image:: _static/embed-idom-view/screenshot.png - :width: 500px - diff --git a/docs/source/adding-interactivity/components-with-state/_examples/adding_state_variable/data.json b/docs/source/guides/adding-interactivity/components-with-state/_examples/adding_state_variable/data.json similarity index 97% rename from docs/source/adding-interactivity/components-with-state/_examples/adding_state_variable/data.json rename to docs/source/guides/adding-interactivity/components-with-state/_examples/adding_state_variable/data.json index b2a2ba966..b1315912d 100644 --- a/docs/source/adding-interactivity/components-with-state/_examples/adding_state_variable/data.json +++ b/docs/source/guides/adding-interactivity/components-with-state/_examples/adding_state_variable/data.json @@ -44,7 +44,7 @@ { "name": "Terracotta Army", "artist": "Unknown Artist", - "description": "The Terracotta Army is a collection of terracotta sculptures depicting the armies of Qin Shi Huang, the first Emperor of China. The army consited of more than 8,000 soldiers, 130 chariots with 520 horses, and 150 cavalry horses.", + "description": "The Terracotta Army is a collection of terracotta sculptures depicting the armies of Qin Shi Huang, the first Emperor of China. The army consisted of more than 8,000 soldiers, 130 chariots with 520 horses, and 150 cavalry horses.", "url": "https://upload.wikimedia.org/wikipedia/commons/thumb/9/9a/2015-09-22-081415_-_Terrakotta-Armee%2C_Grosse_Halle.jpg/1920px-2015-09-22-081415_-_Terrakotta-Armee%2C_Grosse_Halle.jpg", "alt": "12 terracotta sculptures of solemn warriors, each with a unique facial expression and armor." }, diff --git a/docs/source/adding-interactivity/components-with-state/_examples/adding_state_variable/app.py b/docs/source/guides/adding-interactivity/components-with-state/_examples/adding_state_variable/main.py similarity index 81% rename from docs/source/adding-interactivity/components-with-state/_examples/adding_state_variable/app.py rename to docs/source/guides/adding-interactivity/components-with-state/_examples/adding_state_variable/main.py index 5a96d2f03..a919a2354 100644 --- a/docs/source/adding-interactivity/components-with-state/_examples/adding_state_variable/app.py +++ b/docs/source/guides/adding-interactivity/components-with-state/_examples/adding_state_variable/main.py @@ -1,8 +1,7 @@ import json from pathlib import Path -from idom import component, hooks, html, run - +from reactpy import component, hooks, html, run HERE = Path(__file__) DATA_PATH = HERE.parent / "data.json" @@ -25,9 +24,9 @@ def handle_click(event): url = sculpture["url"] return html.div( - html.button({"onClick": handle_click}, "Next"), + html.button({"on_click": handle_click}, "Next"), html.h2(name, " by ", artist), - html.p(f"({bounded_index + 1} or {len(sculpture_data)})"), + html.p(f"({bounded_index + 1} of {len(sculpture_data)})"), html.img({"src": url, "alt": alt, "style": {"height": "200px"}}), html.p(description), ) diff --git a/docs/source/adding-interactivity/components-with-state/_examples/isolated_state/data.json b/docs/source/guides/adding-interactivity/components-with-state/_examples/isolated_state/data.json similarity index 97% rename from docs/source/adding-interactivity/components-with-state/_examples/isolated_state/data.json rename to docs/source/guides/adding-interactivity/components-with-state/_examples/isolated_state/data.json index b2a2ba966..b1315912d 100644 --- a/docs/source/adding-interactivity/components-with-state/_examples/isolated_state/data.json +++ b/docs/source/guides/adding-interactivity/components-with-state/_examples/isolated_state/data.json @@ -44,7 +44,7 @@ { "name": "Terracotta Army", "artist": "Unknown Artist", - "description": "The Terracotta Army is a collection of terracotta sculptures depicting the armies of Qin Shi Huang, the first Emperor of China. The army consited of more than 8,000 soldiers, 130 chariots with 520 horses, and 150 cavalry horses.", + "description": "The Terracotta Army is a collection of terracotta sculptures depicting the armies of Qin Shi Huang, the first Emperor of China. The army consisted of more than 8,000 soldiers, 130 chariots with 520 horses, and 150 cavalry horses.", "url": "https://upload.wikimedia.org/wikipedia/commons/thumb/9/9a/2015-09-22-081415_-_Terrakotta-Armee%2C_Grosse_Halle.jpg/1920px-2015-09-22-081415_-_Terrakotta-Armee%2C_Grosse_Halle.jpg", "alt": "12 terracotta sculptures of solemn warriors, each with a unique facial expression and armor." }, diff --git a/docs/source/adding-interactivity/components-with-state/_examples/isolated_state/app.py b/docs/source/guides/adding-interactivity/components-with-state/_examples/isolated_state/main.py similarity index 84% rename from docs/source/adding-interactivity/components-with-state/_examples/isolated_state/app.py rename to docs/source/guides/adding-interactivity/components-with-state/_examples/isolated_state/main.py index 08a53d1c6..d07b87140 100644 --- a/docs/source/adding-interactivity/components-with-state/_examples/isolated_state/app.py +++ b/docs/source/guides/adding-interactivity/components-with-state/_examples/isolated_state/main.py @@ -1,8 +1,7 @@ import json from pathlib import Path -from idom import component, hooks, html, run - +from reactpy import component, hooks, html, run HERE = Path(__file__) DATA_PATH = HERE.parent / "data.json" @@ -29,14 +28,14 @@ def handle_more_click(event): url = sculpture["url"] return html.div( - html.button({"onClick": handle_next_click}, "Next"), + html.button({"on_click": handle_next_click}, "Next"), html.h2(name, " by ", artist), html.p(f"({bounded_index + 1} or {len(sculpture_data)})"), html.img({"src": url, "alt": alt, "style": {"height": "200px"}}), html.div( html.button( - {"onClick": handle_more_click}, - f"{'Show' if show_more else 'Hide'} details", + {"on_click": handle_more_click}, + f"{('Show' if show_more else 'Hide')} details", ), (html.p(description) if show_more else ""), ), diff --git a/docs/source/adding-interactivity/components-with-state/_examples/multiple_state_variables/data.json b/docs/source/guides/adding-interactivity/components-with-state/_examples/multiple_state_variables/data.json similarity index 97% rename from docs/source/adding-interactivity/components-with-state/_examples/multiple_state_variables/data.json rename to docs/source/guides/adding-interactivity/components-with-state/_examples/multiple_state_variables/data.json index b2a2ba966..b1315912d 100644 --- a/docs/source/adding-interactivity/components-with-state/_examples/multiple_state_variables/data.json +++ b/docs/source/guides/adding-interactivity/components-with-state/_examples/multiple_state_variables/data.json @@ -44,7 +44,7 @@ { "name": "Terracotta Army", "artist": "Unknown Artist", - "description": "The Terracotta Army is a collection of terracotta sculptures depicting the armies of Qin Shi Huang, the first Emperor of China. The army consited of more than 8,000 soldiers, 130 chariots with 520 horses, and 150 cavalry horses.", + "description": "The Terracotta Army is a collection of terracotta sculptures depicting the armies of Qin Shi Huang, the first Emperor of China. The army consisted of more than 8,000 soldiers, 130 chariots with 520 horses, and 150 cavalry horses.", "url": "https://upload.wikimedia.org/wikipedia/commons/thumb/9/9a/2015-09-22-081415_-_Terrakotta-Armee%2C_Grosse_Halle.jpg/1920px-2015-09-22-081415_-_Terrakotta-Armee%2C_Grosse_Halle.jpg", "alt": "12 terracotta sculptures of solemn warriors, each with a unique facial expression and armor." }, diff --git a/docs/source/adding-interactivity/components-with-state/_examples/multiple_state_variables/app.py b/docs/source/guides/adding-interactivity/components-with-state/_examples/multiple_state_variables/main.py similarity index 81% rename from docs/source/adding-interactivity/components-with-state/_examples/multiple_state_variables/app.py rename to docs/source/guides/adding-interactivity/components-with-state/_examples/multiple_state_variables/main.py index 3e7f7bde4..87f9651be 100644 --- a/docs/source/adding-interactivity/components-with-state/_examples/multiple_state_variables/app.py +++ b/docs/source/guides/adding-interactivity/components-with-state/_examples/multiple_state_variables/main.py @@ -1,8 +1,7 @@ import json from pathlib import Path -from idom import component, hooks, html, run - +from reactpy import component, hooks, html, run HERE = Path(__file__) DATA_PATH = HERE.parent / "data.json" @@ -29,14 +28,14 @@ def handle_more_click(event): url = sculpture["url"] return html.div( - html.button({"onClick": handle_next_click}, "Next"), + html.button({"on_click": handle_next_click}, "Next"), html.h2(name, " by ", artist), html.p(f"({bounded_index + 1} or {len(sculpture_data)})"), html.img({"src": url, "alt": alt, "style": {"height": "200px"}}), html.div( html.button( - {"onClick": handle_more_click}, - f"{'Show' if show_more else 'Hide'} details", + {"on_click": handle_more_click}, + f"{('Show' if show_more else 'Hide')} details", ), (html.p(description) if show_more else ""), ), diff --git a/docs/source/adding-interactivity/components-with-state/_examples/when_variables_arent_enough/data.json b/docs/source/guides/adding-interactivity/components-with-state/_examples/when_variables_are_not_enough/data.json similarity index 97% rename from docs/source/adding-interactivity/components-with-state/_examples/when_variables_arent_enough/data.json rename to docs/source/guides/adding-interactivity/components-with-state/_examples/when_variables_are_not_enough/data.json index b2a2ba966..b1315912d 100644 --- a/docs/source/adding-interactivity/components-with-state/_examples/when_variables_arent_enough/data.json +++ b/docs/source/guides/adding-interactivity/components-with-state/_examples/when_variables_are_not_enough/data.json @@ -44,7 +44,7 @@ { "name": "Terracotta Army", "artist": "Unknown Artist", - "description": "The Terracotta Army is a collection of terracotta sculptures depicting the armies of Qin Shi Huang, the first Emperor of China. The army consited of more than 8,000 soldiers, 130 chariots with 520 horses, and 150 cavalry horses.", + "description": "The Terracotta Army is a collection of terracotta sculptures depicting the armies of Qin Shi Huang, the first Emperor of China. The army consisted of more than 8,000 soldiers, 130 chariots with 520 horses, and 150 cavalry horses.", "url": "https://upload.wikimedia.org/wikipedia/commons/thumb/9/9a/2015-09-22-081415_-_Terrakotta-Armee%2C_Grosse_Halle.jpg/1920px-2015-09-22-081415_-_Terrakotta-Armee%2C_Grosse_Halle.jpg", "alt": "12 terracotta sculptures of solemn warriors, each with a unique facial expression and armor." }, diff --git a/docs/source/adding-interactivity/components-with-state/_examples/when_variables_arent_enough/app.py b/docs/source/guides/adding-interactivity/components-with-state/_examples/when_variables_are_not_enough/main.py similarity index 89% rename from docs/source/adding-interactivity/components-with-state/_examples/when_variables_arent_enough/app.py rename to docs/source/guides/adding-interactivity/components-with-state/_examples/when_variables_are_not_enough/main.py index f8679cbfc..c617586de 100644 --- a/docs/source/adding-interactivity/components-with-state/_examples/when_variables_arent_enough/app.py +++ b/docs/source/guides/adding-interactivity/components-with-state/_examples/when_variables_are_not_enough/main.py @@ -7,7 +7,7 @@ import json from pathlib import Path -from idom import component, html, run +from reactpy import component, html, run HERE = Path(__file__) @@ -31,7 +31,7 @@ def handle_click(event): url = sculpture["url"] return html.div( - html.button({"onClick": handle_click}, "Next"), + html.button({"on_click": handle_click}, "Next"), html.h2(name, " by ", artist), html.p(f"({bounded_index + 1} or {len(sculpture_data)})"), html.img({"src": url, "alt": alt, "style": {"height": "200px"}}), diff --git a/docs/source/adding-interactivity/components-with-state/index.rst b/docs/source/guides/adding-interactivity/components-with-state/index.rst similarity index 81% rename from docs/source/adding-interactivity/components-with-state/index.rst rename to docs/source/guides/adding-interactivity/components-with-state/index.rst index 2542d97bb..f8235ac0d 100644 --- a/docs/source/adding-interactivity/components-with-state/index.rst +++ b/docs/source/guides/adding-interactivity/components-with-state/index.rst @@ -5,7 +5,7 @@ Components often need to change what’s on the screen as a result of an interac example, typing into the form should update the input field, clicking “next” on an image carousel should change which image is displayed, clicking “buy” should put a product in the shopping cart. Components need to “remember” things like the current input value, -the current image, the shopping cart. In IDOM, this kind of component-specific memory is +the current image, the shopping cart. In ReactPy, this kind of component-specific memory is called state. @@ -16,13 +16,13 @@ Below is a gallery of images about sculpture. Clicking the "Next" button should increment the ``index`` and, as a result, change what image is displayed. However, this does not work: -.. idom:: _examples/when_variables_arent_enough +.. reactpy:: _examples/when_variables_are_not_enough .. note:: Try clicking the button to see that it does not cause a change. -After clicking "Next", if you check the server logs, you'll discover an +After clicking "Next", if you check your web server's logs, you'll discover an ``UnboundLocalError`` error. It turns out that in this case, the ``index = index + 1`` statement is similar to `trying to set global variables `__. @@ -36,13 +36,13 @@ that still wouldn't fix the underlying problems: destroyed when it updates. 2. **Changes to local variables do not cause components to re-render** - there's no way - for IDOM to observe when these variables change. Thus IDOM is not aware that + for ReactPy to observe when these variables change. Thus ReactPy is not aware that something has changed and that a re-render should take place. -To address these problems, IDOM provides the :func:`~idom.core.hooks.use_state` "hook" +To address these problems, ReactPy provides the :func:`~reactpy.core.hooks.use_state` "hook" which provides: -1. A **state variable** whose data is retained aross renders. +1. A **state variable** whose data is retained across renders. 2. A **state setter** function that can be used to update that variable and trigger a render. @@ -51,12 +51,12 @@ which provides: Adding State to Components -------------------------- -To create a state variable and state setter with :func:`~idom.core.hooks.use_state` hook +To create a state variable and state setter with :func:`~reactpy.core.hooks.use_state` hook as described above, we'll begin by importing it: .. testcode:: - from idom import use_state + from reactpy import use_state Then we'll make the following changes to our code :ref:`from before `: @@ -84,16 +84,16 @@ After making those changes we should get: We'll talk more about what this is doing :ref:`shortly `, but for now let's just verify that this does in fact fix the problems from before: -.. idom:: _examples/adding_state_variable +.. reactpy:: _examples/adding_state_variable Your First Hook --------------- -In IDOM, ``use_state``, as well as any other function whose name starts with ``use``, is -called a "hook". These are special functions that should only be called while IDOM is +In ReactPy, ``use_state``, as well as any other function whose name starts with ``use``, is +called a "hook". These are special functions that should only be called while ReactPy is :ref:`rendering `. They let you "hook into" the different -capabilities of IDOM's components of which ``use_state`` is just one (well get into the +capabilities of ReactPy's components of which ``use_state`` is just one (well get into the other :ref:`later `). While hooks are just normal functions, but it's helpful to think of them as @@ -107,7 +107,7 @@ words, you'll "use" hooks at the top of your component in the same way you might Introduction to ``use_state`` ----------------------------- -When you call :func:`~idom.core.hooks.use_state` inside the body of a component's render +When you call :func:`~reactpy.core.hooks.use_state` inside the body of a component's render function, you're declaring that this component needs to remember something. That "something" which needs to be remembered, is known as **state**. So when we look at an assignment expression like the one below @@ -130,7 +130,7 @@ Thus, in this example: of the component. The convention is that, if you name your state variable ``thing``, your state setter -should be named ``set_thing``. While you could name them anything you want, adhereing to +should be named ``set_thing``. While you could name them anything you want, adhering to the convention makes things easier to understand across projects. ---- @@ -151,12 +151,12 @@ below highlights a line of code where something of interest occurs:

Initial render

- .. literalinclude:: _examples/adding_state_variable/app.py + .. literalinclude:: _examples/adding_state_variable/main.py :lines: 12-33 :emphasize-lines: 2 At this point, we've just begun to render the ``Gallery`` component. As yet, - IDOM is not aware that this component has any state or what view it will + ReactPy is not aware that this component has any state or what view it will display. This will change in a moment though when we move to the next line... .. tab-item:: 2 @@ -165,11 +165,11 @@ below highlights a line of code where something of interest occurs:

Initial state declaration

- .. literalinclude:: _examples/adding_state_variable/app.py + .. literalinclude:: _examples/adding_state_variable/main.py :lines: 12-33 :emphasize-lines: 3 - The ``Gallery`` component has just declared some state. IDOM now knows that it + The ``Gallery`` component has just declared some state. ReactPy now knows that it must remember the ``index`` and trigger an update of this component when ``set_index`` is called. Currently the value of ``index`` is ``0`` as per the default value given to ``use_state``. Thus, the resulting view will display @@ -181,7 +181,7 @@ below highlights a line of code where something of interest occurs:

Define event handler

- .. literalinclude:: _examples/adding_state_variable/app.py + .. literalinclude:: _examples/adding_state_variable/main.py :lines: 12-33 :emphasize-lines: 5 @@ -196,7 +196,7 @@ below highlights a line of code where something of interest occurs:

Return the view

- .. literalinclude:: _examples/adding_state_variable/app.py + .. literalinclude:: _examples/adding_state_variable/main.py :lines: 12-33 :emphasize-lines: 16 @@ -212,11 +212,11 @@ below highlights a line of code where something of interest occurs:

User interaction

- .. literalinclude:: _examples/adding_state_variable/app.py + .. literalinclude:: _examples/adding_state_variable/main.py :lines: 12-33 :emphasize-lines: 5 - A user has just clicked the button đŸ–ąī¸! IDOM has sent information about the event + A user has just clicked the button đŸ–ąī¸! ReactPy has sent information about the event to the ``handle_click`` function and it is about to execute. In a moment we will update the state of this component and schedule a re-render. @@ -226,11 +226,11 @@ below highlights a line of code where something of interest occurs:

New state is set

- .. literalinclude:: _examples/adding_state_variable/app.py + .. literalinclude:: _examples/adding_state_variable/main.py :lines: 12-33 :emphasize-lines: 6 - We've just now told IDOM that we want to update the state of our ``Gallery`` and + We've just now told ReactPy that we want to update the state of our ``Gallery`` and that it needs to be re-rendered. More specifically, we are incrementing its ``index``, and once ``Gallery`` re-renders the index *will* be ``1``. Importantly, at this point, the value of ``index`` is still ``0``! This will @@ -242,11 +242,11 @@ below highlights a line of code where something of interest occurs:

Next render begins

- .. literalinclude:: _examples/adding_state_variable/app.py + .. literalinclude:: _examples/adding_state_variable/main.py :lines: 12-33 :emphasize-lines: 2 - The scheduled re-render of ``Gallery`` has just begun. IDOM has now updated its + The scheduled re-render of ``Gallery`` has just begun. ReactPy has now updated its internal state store such that, the next time we call ``use_state`` we will get back the updated value of ``index``. @@ -256,12 +256,12 @@ below highlights a line of code where something of interest occurs:

Next state is acquired

- .. literalinclude:: _examples/adding_state_variable/app.py + .. literalinclude:: _examples/adding_state_variable/main.py :lines: 12-33 :emphasize-lines: 3 - With IDOM's state store updated, as we call ``use_state``, instead of returning - ``0`` for the value of ``index`` as it did before, IDOM now returns the value + With ReactPy's state store updated, as we call ``use_state``, instead of returning + ``0`` for the value of ``index`` as it did before, ReactPy now returns the value ``1``. With this change the view we display will be altered - instead of displaying data for the first item in our ``sculpture_data`` list we will now display information about the second. @@ -272,7 +272,7 @@ below highlights a line of code where something of interest occurs:

Repeat...

- .. literalinclude:: _examples/adding_state_variable/app.py + .. literalinclude:: _examples/adding_state_variable/main.py :lines: 12-33 From this point on, the steps remain the same. The only difference being the @@ -293,14 +293,14 @@ below highlights a line of code where something of interest occurs: Multiple State Declarations --------------------------- -The powerful thing about hooks like :func:`~idom.core.hooks.use_state` is that you're +The powerful thing about hooks like :func:`~reactpy.core.hooks.use_state` is that you're not limited to just one state declaration. You can call ``use_state()`` as many times as you need to in one component. For example, in the example below we've added a ``show_more`` state variable along with a few other modifications (e.g. renaming ``handle_click``) to make the description for each sculpture optionally displayed. Only when the user clicks the "Show details" button is this description shown: -.. idom:: _examples/multiple_state_variables +.. reactpy:: _examples/multiple_state_variables It's generally a good idea to define separate state variables if the data they represent is unrelated. In this case, ``index`` corresponds to what sculpture information is being @@ -308,7 +308,7 @@ displayed and ``show_more`` is solely concerned with whether the description for sculpture is shown. Put other way ``index`` is concerned with *what* information is displayed while ``show_more`` is concerned with *how* it is displayed. Conversely though, if you have a form with many fields, it probably makes sense to have a single -objec that holds the data for all the fields rather than an object per-field. +object that holds the data for all the fields rather than an object per-field. .. note:: @@ -326,7 +326,7 @@ In this example, the ``Gallery`` component from earlier is rendered twice with n changes to its logic. Try clicking the buttons inside each of the galleries. Notice that their state is independent: -.. idom:: _examples/isolated_state +.. reactpy:: _examples/isolated_state :result-is-default-tab: This is what makes state different from regular variables that you might declare at the @@ -340,12 +340,12 @@ it. The parent component can’t change it. This lets you add state to any compo remove it without impacting the rest of the components. .. card:: - :link: /managing-state/shared-component-state + :link: /guides/managing-state/sharing-component-state/index :link-type: doc :octicon:`book` Read More ^^^^^^^^^^^^^^^^^^^^^^^^^ What if you wanted both galleries to keep their states in sync? The right way to do - it in IDOM is to remove state from child components and add it to their closest + it in ReactPy is to remove state from child components and add it to their closest shared parent. diff --git a/docs/source/adding-interactivity/dangers-of-mutability/_examples/dict_remove.py b/docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/dict_remove.py similarity index 77% rename from docs/source/adding-interactivity/dangers-of-mutability/_examples/dict_remove.py rename to docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/dict_remove.py index cf1955301..6c3c783da 100644 --- a/docs/source/adding-interactivity/dangers-of-mutability/_examples/dict_remove.py +++ b/docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/dict_remove.py @@ -1,4 +1,4 @@ -from idom import component, html, run, use_state +from reactpy import component, html, run, use_state @component @@ -26,29 +26,29 @@ def handle_click(event): return handle_click return html.div( - html.button({"onClick": handle_add_click}, "add term"), + html.button({"on_click": handle_add_click}, "add term"), html.label( "Term: ", - html.input({"value": term_to_add, "onChange": handle_term_to_add_change}), + html.input({"value": term_to_add, "on_change": handle_term_to_add_change}), ), html.label( "Definition: ", html.input( { "value": definition_to_add, - "onChange": handle_definition_to_add_change, + "on_change": handle_definition_to_add_change, } ), ), html.hr(), [ html.div( + {"key": term}, html.button( - {"onClick": make_delete_click_handler(term)}, "delete term" + {"on_click": make_delete_click_handler(term)}, "delete term" ), html.dt(term), html.dd(definition), - key=term, ) for term, definition in all_terms.items() ], diff --git a/docs/source/adding-interactivity/dangers-of-mutability/_examples/dict_update.py b/docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/dict_update.py similarity index 68% rename from docs/source/adding-interactivity/dangers-of-mutability/_examples/dict_update.py rename to docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/dict_update.py index 9a2aad5e6..32dd4073a 100644 --- a/docs/source/adding-interactivity/dangers-of-mutability/_examples/dict_update.py +++ b/docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/dict_update.py @@ -1,4 +1,4 @@ -from idom import component, html, run, use_state +from reactpy import component, html, run, use_state @component @@ -24,20 +24,18 @@ def handle_email_change(event): html.label( "First name: ", html.input( - {"value": person["first_name"], "onChange": handle_first_name_change}, + {"value": person["first_name"], "on_change": handle_first_name_change} ), ), html.label( - "First name: ", + "Last name: ", html.input( - {"value": person["last_name"], "onChange": handle_last_name_change}, + {"value": person["last_name"], "on_change": handle_last_name_change} ), ), html.label( - "First name: ", - html.input( - {"value": person["email"], "onChange": handle_email_change}, - ), + "Email: ", + html.input({"value": person["email"], "on_change": handle_email_change}), ), html.p(f"{person['first_name']} {person['last_name']} {person['email']}"), ) diff --git a/docs/source/adding-interactivity/dangers-of-mutability/_examples/list_insert.py b/docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/list_insert.py similarity index 65% rename from docs/source/adding-interactivity/dangers-of-mutability/_examples/list_insert.py rename to docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/list_insert.py index 374198f45..1f4072e0b 100644 --- a/docs/source/adding-interactivity/dangers-of-mutability/_examples/list_insert.py +++ b/docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/list_insert.py @@ -1,4 +1,4 @@ -from idom import component, html, run, use_state +from reactpy import component, html, run, use_state @component @@ -16,9 +16,9 @@ def handle_click(event): return html.div( html.h1("Inspiring sculptors:"), - html.input({"value": artist_to_add, "onChange": handle_change}), - html.button({"onClick": handle_click}, "add"), - html.ul([html.li(name, key=name) for name in artists]), + html.input({"value": artist_to_add, "on_change": handle_change}), + html.button({"on_click": handle_click}, "add"), + html.ul([html.li({"key": name}, name) for name in artists]), ) diff --git a/docs/source/adding-interactivity/dangers-of-mutability/_examples/list_re_order.py b/docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/list_re_order.py similarity index 55% rename from docs/source/adding-interactivity/dangers-of-mutability/_examples/list_re_order.py rename to docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/list_re_order.py index 32a0e47c0..3bd2fd601 100644 --- a/docs/source/adding-interactivity/dangers-of-mutability/_examples/list_re_order.py +++ b/docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/list_re_order.py @@ -1,4 +1,4 @@ -from idom import component, html, run, use_state +from reactpy import component, html, run, use_state @component @@ -8,16 +8,16 @@ def ArtistList(): ) def handle_sort_click(event): - set_artists(list(sorted(artists))) + set_artists(sorted(artists)) def handle_reverse_click(event): set_artists(list(reversed(artists))) return html.div( html.h1("Inspiring sculptors:"), - html.button({"onClick": handle_sort_click}, "sort"), - html.button({"onClick": handle_reverse_click}, "reverse"), - html.ul([html.li(name, key=name) for name in artists]), + html.button({"on_click": handle_sort_click}, "sort"), + html.button({"on_click": handle_reverse_click}, "reverse"), + html.ul([html.li({"key": name}, name) for name in artists]), ) diff --git a/docs/source/adding-interactivity/dangers-of-mutability/_examples/list_remove.py b/docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/list_remove.py similarity index 71% rename from docs/source/adding-interactivity/dangers-of-mutability/_examples/list_remove.py rename to docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/list_remove.py index 170deb2f4..6223284f6 100644 --- a/docs/source/adding-interactivity/dangers-of-mutability/_examples/list_remove.py +++ b/docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/list_remove.py @@ -1,4 +1,4 @@ -from idom import component, html, run, use_state +from reactpy import component, html, run, use_state @component @@ -24,14 +24,16 @@ def handle_click(event): return html.div( html.h1("Inspiring sculptors:"), - html.input({"value": artist_to_add, "onChange": handle_change}), - html.button({"onClick": handle_add_click}, "add"), + html.input({"value": artist_to_add, "on_change": handle_change}), + html.button({"on_click": handle_add_click}, "add"), html.ul( [ html.li( + {"key": name}, name, - html.button({"onClick": make_handle_delete_click(index)}, "delete"), - key=name, + html.button( + {"on_click": make_handle_delete_click(index)}, "delete" + ), ) for index, name in enumerate(artists) ] diff --git a/docs/source/adding-interactivity/dangers-of-mutability/_examples/list_replace.py b/docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/list_replace.py similarity index 74% rename from docs/source/adding-interactivity/dangers-of-mutability/_examples/list_replace.py rename to docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/list_replace.py index 2fafcfde8..4952b9597 100644 --- a/docs/source/adding-interactivity/dangers-of-mutability/_examples/list_replace.py +++ b/docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/list_replace.py @@ -1,4 +1,4 @@ -from idom import component, html, run, use_state +from reactpy import component, html, run, use_state @component @@ -15,9 +15,9 @@ def handle_click(event): return html.ul( [ html.li( + {"key": index}, count, - html.button({"onClick": make_increment_click_handler(index)}, "+1"), - key=index, + html.button({"on_click": make_increment_click_handler(index)}, "+1"), ) for index, count in enumerate(counters) ] diff --git a/docs/source/adding-interactivity/dangers-of-mutability/_examples/moving_dot.py b/docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/moving_dot.py similarity index 79% rename from docs/source/adding-interactivity/dangers-of-mutability/_examples/moving_dot.py rename to docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/moving_dot.py index d3edb8590..e5ab54dca 100644 --- a/docs/source/adding-interactivity/dangers-of-mutability/_examples/moving_dot.py +++ b/docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/moving_dot.py @@ -1,4 +1,4 @@ -from idom import component, html, run, use_state +from reactpy import component, html, run, use_state @component @@ -17,26 +17,26 @@ async def handle_pointer_move(event): return html.div( { - "onPointerMove": handle_pointer_move, + "on_pointer_move": handle_pointer_move, "style": { "position": "relative", "height": "200px", "width": "100%", - "backgroundColor": "white", + "background_color": "white", }, }, html.div( { "style": { "position": "absolute", - "backgroundColor": "red", - "borderRadius": "50%", + "background_color": "red", + "border_radius": "50%", "width": "20px", "height": "20px", "left": "-10px", "top": "-10px", "transform": f"translate({position['x']}px, {position['y']}px)", - }, + } } ), ) diff --git a/docs/source/adding-interactivity/dangers-of-mutability/_examples/moving_dot_broken.py b/docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/moving_dot_broken.py similarity index 78% rename from docs/source/adding-interactivity/dangers-of-mutability/_examples/moving_dot_broken.py rename to docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/moving_dot_broken.py index 90885e7fe..8972ce74e 100644 --- a/docs/source/adding-interactivity/dangers-of-mutability/_examples/moving_dot_broken.py +++ b/docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/moving_dot_broken.py @@ -1,6 +1,6 @@ # :linenos: -from idom import component, html, run, use_state +from reactpy import component, html, run, use_state @component @@ -15,26 +15,26 @@ def handle_pointer_move(event): return html.div( { - "onPointerMove": handle_pointer_move, + "on_pointer_move": handle_pointer_move, "style": { "position": "relative", "height": "200px", "width": "100%", - "backgroundColor": "white", + "background_color": "white", }, }, html.div( { "style": { "position": "absolute", - "backgroundColor": "red", - "borderRadius": "50%", + "background_color": "red", + "border_radius": "50%", "width": "20px", "height": "20px", "left": "-10px", "top": "-10px", "transform": f"translate({position['x']}px, {position['y']}px)", - }, + } } ), ) diff --git a/docs/source/adding-interactivity/dangers-of-mutability/_examples/set_remove.py b/docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/set_remove.py similarity index 82% rename from docs/source/adding-interactivity/dangers-of-mutability/_examples/set_remove.py rename to docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/set_remove.py index 4c4905350..abe55a918 100644 --- a/docs/source/adding-interactivity/dangers-of-mutability/_examples/set_remove.py +++ b/docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/set_remove.py @@ -1,4 +1,4 @@ -from idom import component, html, run, use_state +from reactpy import component, html, run, use_state @component @@ -20,18 +20,18 @@ def handle_click(event): [ html.div( { - "onClick": make_handle_click(index), + "on_click": make_handle_click(index), "style": { "height": "30px", "width": "30px", - "backgroundColor": ( + "background_color": ( "black" if index in selected_indices else "white" ), "outline": "1px solid grey", "cursor": "pointer", }, - }, - key=index, + "key": index, + } ) for index in range(line_size) ], diff --git a/docs/source/adding-interactivity/dangers-of-mutability/_examples/set_update.py b/docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/set_update.py similarity index 79% rename from docs/source/adding-interactivity/dangers-of-mutability/_examples/set_update.py rename to docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/set_update.py index 9e1077cb0..27f170a42 100644 --- a/docs/source/adding-interactivity/dangers-of-mutability/_examples/set_update.py +++ b/docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/set_update.py @@ -1,4 +1,4 @@ -from idom import component, html, run, use_state +from reactpy import component, html, run, use_state @component @@ -17,18 +17,18 @@ def handle_click(event): [ html.div( { - "onClick": make_handle_click(index), + "on_click": make_handle_click(index), "style": { "height": "30px", "width": "30px", - "backgroundColor": ( + "background_color": ( "black" if index in selected_indices else "white" ), "outline": "1px solid grey", "cursor": "pointer", }, - }, - key=index, + "key": index, + } ) for index in range(line_size) ], diff --git a/docs/source/adding-interactivity/dangers-of-mutability/index.rst b/docs/source/guides/adding-interactivity/dangers-of-mutability/index.rst similarity index 94% rename from docs/source/adding-interactivity/dangers-of-mutability/index.rst rename to docs/source/guides/adding-interactivity/dangers-of-mutability/index.rst index 80a4e6a04..bcac79e18 100644 --- a/docs/source/adding-interactivity/dangers-of-mutability/index.rst +++ b/docs/source/guides/adding-interactivity/dangers-of-mutability/index.rst @@ -2,7 +2,7 @@ Dangers of Mutability ===================== While state can hold any type of value, you should be careful to avoid directly -modifying objects that you declare as state with IDOM. In other words, you must not +modifying objects that you declare as state with ReactPy. In other words, you must not :ref:`"mutate" ` values which are held as state. Rather, to change these values you should use new ones or create copies. @@ -42,8 +42,8 @@ facilitate change: values are equivalent with the ``==`` or ``!=`` operators. Thus far, all the values we've been working with have been immutable. These include -:class:`int`, :class:`float`, :class:`str`, and :class:`bool` values. As a result, we've -have not had to consider the consiquences of mutations. +:class:`int`, :class:`float`, :class:`str`, and :class:`bool` values. As a result, we +have not had to consider the consequences of mutations. .. _Why Avoid Mutation: @@ -51,13 +51,13 @@ have not had to consider the consiquences of mutations. Why Avoid Mutation? ------------------- -Unfortunately, IDOM does not understand that when a value is mutated, it may have +Unfortunately, ReactPy does not understand that when a value is mutated, it may have changed. As a result, mutating values will not trigger re-renders. Thus, you must be -careful to avoid mutation whenever you want IDOM to re-render a component. For example, +careful to avoid mutation whenever you want ReactPy to re-render a component. For example, the intention of the code below is to make the red dot move when you touch or hover over the preview area. However it doesn't - the dot remains stationary: -.. idom:: _examples/moving_dot_broken +.. reactpy:: _examples/moving_dot_broken The problem is with this section of code: @@ -68,7 +68,7 @@ The problem is with this section of code: :lineno-start: 13 This code mutates the ``position`` dictionary from the prior render instead of using the -state variable's associated state setter. Without calling setter IDOM has no idea that +state variable's associated state setter. Without calling setter ReactPy has no idea that the variable's data has been modified. While it can be possible to get away with mutating state variables, it's highly dicsouraged. Doing so can cause strange and unpredictable behavior. As a result, you should always treat the data within a state @@ -80,7 +80,7 @@ call it by passing a *new* dictionary with the values for the next render. Notic by making these alterations to the code, that the dot now follows your pointer when you touch or hover over the preview: -.. idom:: _examples/moving_dot +.. reactpy:: _examples/moving_dot .. dropdown:: Local mutation can be alright @@ -208,7 +208,7 @@ updating user information with a preview of the currently entered data. We can accomplish this using `"unpacking" `__ with the ``**`` syntax: -.. idom:: _examples/dict_update +.. reactpy:: _examples/dict_update .. _removing-dictionary-items: @@ -249,7 +249,7 @@ set of keys. One way to do this is with a dictionary comprehension. The example shows an interface where you're able to enter a new term and definition. Once added, you can click a delete button to remove the term and definition: -.. idom:: _examples/dict_remove +.. reactpy:: _examples/dict_remove Working with Lists @@ -349,7 +349,7 @@ the items we want to append instead. There are several ways to do this for one o values however it's often simplest to use `"unpacking" `__ with the ``*`` syntax. -.. idom:: _examples/list_insert +.. reactpy:: _examples/list_insert .. _removing-list-items: @@ -378,7 +378,7 @@ not quite as clean. You must select the portion the list prior to the item which be removed (``l[:index]``) and the portion after the item (``l[index + 1:]``) and add them together: -.. idom:: _examples/list_remove +.. reactpy:: _examples/list_remove .. _replacing-list-items: @@ -409,7 +409,7 @@ must select the portion before and after the item in question. But this time, in of adding those two selections together, you must insert that values you want to replace between them: -.. idom:: _examples/list_replace +.. reactpy:: _examples/list_replace .. _re-ordering-list-items: @@ -441,7 +441,7 @@ list object, you should use the builtin functions :func:`sorted` and :func:`reve and pass the resulting iterator into the :class:`list` constructor to create a sorted or reversed copy of the given list: -.. idom:: _examples/list_re_order +.. reactpy:: _examples/list_re_order Working with Sets @@ -512,7 +512,7 @@ or operator ``|`` serves as a succinct way to compute the union of two sets. How you should be careful to not use an in-place assignment with this operator as that will (counterintuitively) mutate the original set rather than creating a new one. -.. idom:: _examples/set_update +.. reactpy:: _examples/set_update .. _removing-set-items: @@ -531,7 +531,7 @@ Removing Set Items s.difference_update(values) s -= values # "in-place" operators mutate! - s.symetric_difference_update(values) + s.symmetric_difference_update(values) s ^= values # "in-place" operators mutate! s.intersection_update(values) @@ -547,7 +547,7 @@ Removing Set Items s.difference(values) s - values - s.symetric_difference(values) + s.symmetric_difference(values) s ^ values s.intersection(values) @@ -558,7 +558,7 @@ methods to return new sets without mutating them. As before when :ref:`adding se you need to avoid using the inline assignment operators since that will (counterintuitively) mutate the original set rather than given you a new one: -.. idom:: _examples/set_remove +.. reactpy:: _examples/set_remove Useful Packages diff --git a/docs/source/adding-interactivity/index.rst b/docs/source/guides/adding-interactivity/index.rst similarity index 81% rename from docs/source/adding-interactivity/index.rst rename to docs/source/guides/adding-interactivity/index.rst index a09db3f51..af84fab88 100644 --- a/docs/source/adding-interactivity/index.rst +++ b/docs/source/guides/adding-interactivity/index.rst @@ -56,15 +56,15 @@ Adding Interactivity Section 1: Responding to Events ------------------------------- -IDOM lets you add event handlers to your parts of the interface. This means that you can +ReactPy lets you add event handlers to your parts of the interface. This means that you can define synchronous or asynchronous functions that are triggered when a particular user -interaction occurs like clicking, hovering, of focusing on form inputs, and more. +interaction occurs like clicking, hovering, focusing on form inputs, and more. -.. idom:: responding-to-events/_examples/button_prints_message +.. reactpy:: responding-to-events/_examples/button_prints_message It may feel weird to define a function within a function like this, but doing so allows the ``handle_event`` function to access information from within the scope of the -component. That's important if you want to use any arguments that may have beend passed +component. That's important if you want to use any arguments that may have been passed your component in the handler. .. card:: @@ -85,16 +85,16 @@ Components often need to change what’s on the screen as a result of an interac example, typing into the form should update the input field, clicking a “Comment” button should bring up a text input field, clicking “Buy” should put a product in the shopping cart. Components need to “remember” things like the current input value, the current -image, the shopping cart. In IDOM, this kind of component-specific memory is created and +image, the shopping cart. In ReactPy, this kind of component-specific memory is created and updated with a "hook" called ``use_state()`` that creates a **state variable** and **state setter** respectively: -.. idom:: components-with-state/_examples/adding_state_variable +.. reactpy:: components-with-state/_examples/adding_state_variable -In IDOM, ``use_state``, as well as any other function whose name starts with ``use``, is -called a "hook". These are special functions that should only be called while IDOM is +In ReactPy, ``use_state``, as well as any other function whose name starts with ``use``, is +called a "hook". These are special functions that should only be called while ReactPy is :ref:`rendering `. They let you "hook into" the different -capabilities of IDOM's components of which ``use_state`` is just one (well get into the +capabilities of ReactPy's components of which ``use_state`` is just one (well get into the other :ref:`later `). .. card:: @@ -111,7 +111,7 @@ Section 3: State as a Snapshot ------------------------------ As we :ref:`learned earlier `, state setters behave a little -differently than you might exepct at first glance. Instead of updating your current +differently than you might expect at first glance. Instead of updating your current handle on the setter's corresponding variable, it schedules a re-render of the component which owns the state. @@ -122,7 +122,7 @@ which owns the state. set_count(count + 1) # schedule a re-render where count is 1 print(count) # still prints: 0 -This behavior of IDOM means that each render of a component is like taking a snapshot of +This behavior of ReactPy means that each render of a component is like taking a snapshot of the UI based on the component's state at that time. Treating state in this way can help reduce subtle bugs. For instance, in the code below there's a simple chat app with a message input and recipient selector. The catch is that the message actually gets sent 5 @@ -130,15 +130,15 @@ seconds after the "Send" button is clicked. So what would happen if we changed t recipient between the time the "Send" button was clicked and the moment the message is actually sent? -.. idom:: state-as-a-snapshot/_examples/print_chat_message +.. reactpy:: state-as-a-snapshot/_examples/print_chat_message As it turns out, changing the message recipient after pressing send does not change -where the message ulitmately goes. However, one could imagine a bug where the recipient +where the message ultimately goes. However, one could imagine a bug where the recipient of a message is determined at the time the message is sent rather than at the time the "Send" button it clicked. Thus changing the recipient after pressing send would change where the message got sent. -In many cases, IDOM avoids this class of bug entirely because it treats state as a +In many cases, ReactPy avoids this class of bug entirely because it treats state as a snapshot. .. card:: @@ -159,10 +159,10 @@ changes to state only take effect in the next render, not in the current one. Fu changes to state are batched, calling a particular state setter 3 times won't trigger 3 renders, it will only trigger 1. This means that multiple state assignments are batched - so long as the event handler is synchronous (i.e. the event handler is not an -``async`` function), IDOM waits until all the code in an event handler has run before +``async`` function), ReactPy waits until all the code in an event handler has run before processing state and starting the next render: -.. idom:: multiple-state-updates/_examples/set_color_3_times +.. reactpy:: multiple-state-updates/_examples/set_color_3_times Sometimes though, you need to update a state variable more than once before the next render. In these cases, instead of having updates batched, you instead want them to be @@ -172,7 +172,7 @@ To accomplish this, instead of passing the next state value directly (e.g. ``compute_new_state(old_state)`` to the state setter (e.g. ``set_state(compute_new_state)``): -.. idom:: multiple-state-updates/_examples/set_state_function +.. reactpy:: multiple-state-updates/_examples/set_state_function .. card:: :link: multiple-state-updates/index @@ -188,17 +188,17 @@ Section 5: Dangers of Mutability -------------------------------- While state can hold any type of value, you should be careful to avoid directly -modifying objects that you declare as state with IDOM. In other words, you must not +modifying objects that you declare as state with ReactPy. In other words, you must not :ref:`"mutate" ` values which are held as state. Rather, to change these values you should use new ones or create copies. -This is because IDOM does not understand that when a value is mutated, it may have +This is because ReactPy does not understand that when a value is mutated, it may have changed. As a result, mutating values will not trigger re-renders. Thus, you must be -careful to avoid mutation whenever you want IDOM to re-render a component. For example, +careful to avoid mutation whenever you want ReactPy to re-render a component. For example, instead of mutating dictionaries to update their items you should instead create a copy that contains the desired changes: -.. idom:: dangers-of-mutability/_examples/dict_update +.. reactpy:: dangers-of-mutability/_examples/dict_update .. card:: :link: dangers-of-mutability/index diff --git a/docs/source/adding-interactivity/multiple-state-updates/_examples/delay_before_count_updater.py b/docs/source/guides/adding-interactivity/multiple-state-updates/_examples/delay_before_count_updater.py similarity index 70% rename from docs/source/adding-interactivity/multiple-state-updates/_examples/delay_before_count_updater.py rename to docs/source/guides/adding-interactivity/multiple-state-updates/_examples/delay_before_count_updater.py index 0c000477e..e53c5b1ad 100644 --- a/docs/source/adding-interactivity/multiple-state-updates/_examples/delay_before_count_updater.py +++ b/docs/source/guides/adding-interactivity/multiple-state-updates/_examples/delay_before_count_updater.py @@ -1,6 +1,6 @@ import asyncio -from idom import component, html, run, use_state +from reactpy import component, html, run, use_state @component @@ -13,7 +13,7 @@ async def handle_click(event): return html.div( html.h1(number), - html.button({"onClick": handle_click}, "Increment"), + html.button({"on_click": handle_click}, "Increment"), ) diff --git a/docs/source/adding-interactivity/multiple-state-updates/_examples/delay_before_set_count.py b/docs/source/guides/adding-interactivity/multiple-state-updates/_examples/delay_before_set_count.py similarity index 68% rename from docs/source/adding-interactivity/multiple-state-updates/_examples/delay_before_set_count.py rename to docs/source/guides/adding-interactivity/multiple-state-updates/_examples/delay_before_set_count.py index 024df12e7..bb64724f1 100644 --- a/docs/source/adding-interactivity/multiple-state-updates/_examples/delay_before_set_count.py +++ b/docs/source/guides/adding-interactivity/multiple-state-updates/_examples/delay_before_set_count.py @@ -1,6 +1,6 @@ import asyncio -from idom import component, html, run, use_state +from reactpy import component, html, run, use_state @component @@ -13,7 +13,7 @@ async def handle_click(event): return html.div( html.h1(number), - html.button({"onClick": handle_click}, "Increment"), + html.button({"on_click": handle_click}, "Increment"), ) diff --git a/docs/source/adding-interactivity/multiple-state-updates/_examples/set_color_3_times.py b/docs/source/guides/adding-interactivity/multiple-state-updates/_examples/set_color_3_times.py similarity index 59% rename from docs/source/adding-interactivity/multiple-state-updates/_examples/set_color_3_times.py rename to docs/source/guides/adding-interactivity/multiple-state-updates/_examples/set_color_3_times.py index e755c35b9..59d7d0f20 100644 --- a/docs/source/adding-interactivity/multiple-state-updates/_examples/set_color_3_times.py +++ b/docs/source/guides/adding-interactivity/multiple-state-updates/_examples/set_color_3_times.py @@ -1,4 +1,4 @@ -from idom import component, html, run, use_state +from reactpy import component, html, run, use_state @component @@ -15,10 +15,11 @@ def handle_reset(event): return html.div( html.button( - {"onClick": handle_click, "style": {"backgroundColor": color}}, "Set Color" + {"on_click": handle_click, "style": {"background_color": color}}, + "Set Color", ), html.button( - {"onClick": handle_reset, "style": {"backgroundColor": color}}, "Reset" + {"on_click": handle_reset, "style": {"background_color": color}}, "Reset" ), ) diff --git a/docs/source/adding-interactivity/multiple-state-updates/_examples/set_state_function.py b/docs/source/guides/adding-interactivity/multiple-state-updates/_examples/set_state_function.py similarity index 74% rename from docs/source/adding-interactivity/multiple-state-updates/_examples/set_state_function.py rename to docs/source/guides/adding-interactivity/multiple-state-updates/_examples/set_state_function.py index ec3193de9..56bbe80e3 100644 --- a/docs/source/adding-interactivity/multiple-state-updates/_examples/set_state_function.py +++ b/docs/source/guides/adding-interactivity/multiple-state-updates/_examples/set_state_function.py @@ -1,4 +1,4 @@ -from idom import component, html, run, use_state +from reactpy import component, html, run, use_state def increment(old_number): @@ -17,7 +17,7 @@ def handle_click(event): return html.div( html.h1(number), - html.button({"onClick": handle_click}, "Increment"), + html.button({"on_click": handle_click}, "Increment"), ) diff --git a/docs/source/adding-interactivity/multiple-state-updates/index.rst b/docs/source/guides/adding-interactivity/multiple-state-updates/index.rst similarity index 87% rename from docs/source/adding-interactivity/multiple-state-updates/index.rst rename to docs/source/guides/adding-interactivity/multiple-state-updates/index.rst index 73524e173..bc3245d7e 100644 --- a/docs/source/adding-interactivity/multiple-state-updates/index.rst +++ b/docs/source/guides/adding-interactivity/multiple-state-updates/index.rst @@ -10,21 +10,21 @@ Batched Updates --------------- As we learned :ref:`previously `, state variables remain fixed -inside each render as if state were a snapshot taken at the begining of each render. +inside each render as if state were a snapshot taken at the beginning of each render. This is why, in the example below, even though it might seem like clicking the "Increment" button would cause the ``number`` to increase by ``3``, it only does by ``1``: -.. idom:: ../state-as-a-snapshot/_examples/set_counter_3_times +.. reactpy:: ../state-as-a-snapshot/_examples/set_counter_3_times The reason this happens is because, so long as the event handler is synchronous (i.e. -the event handler is not an ``async`` function), IDOM waits until all the code in an +the event handler is not an ``async`` function), ReactPy waits until all the code in an event handler has run before processing state and starting the next render. Thus, it's the last call to a given state setter that matters. In the example below, even though we set the color of the button to ``"orange"`` and then ``"pink"`` before ``"blue"``, -the color does not quickly flash orange and pink before blue - it alway remains blue: +the color does not quickly flash orange and pink before blue - it always remains blue: -.. idom:: _examples/set_color_3_times +.. reactpy:: _examples/set_color_3_times This behavior let's you make multiple state changes without triggering unnecessary renders or renders with inconsistent state where only some of the variables have been @@ -33,13 +33,13 @@ handlers have finished running. .. note:: - For asynchronous event handlers, IDOM will not render until you ``await`` something. + For asynchronous event handlers, ReactPy will not render until you ``await`` something. As we saw in :ref:`prior examples `, if you introduce an asynchronous delay to an event handler after changing state, renders may take place before the remainder of the event handler completes. However, state variables within handlers, even async ones, always remains static. -This behavior of IDOM to "batch" state changes that take place inside a single event +This behavior of ReactPy to "batch" state changes that take place inside a single event handler, do not extend across event handlers. In other words, distinct events will always produce distinct renders. To give an example, if clicking a button increments a counter by one, no matter how fast the user clicks, the view will never jump from 1 to 3 @@ -77,9 +77,9 @@ In our case, ``new_state = old_state + 1``. So we might define: Which we can use to replace ``set_number(number + 1)`` with ``set_number(increment)``: -.. idom:: _examples/set_state_function +.. reactpy:: _examples/set_state_function -The way to think about how IDOM runs though this series of ``set_state(increment)`` +The way to think about how ReactPy runs though this series of ``set_state(increment)`` calls is to imagine that each one updates the internally managed state with its return value, then that return value is being passed to the next updater function. Ultimately, this is functionally equivalent to the following: @@ -94,14 +94,14 @@ introduced a delay before ``set_number(number + 1)``. What would happen if we cl the "Increment" button more than once before the delay in the first triggered event completed? -.. idom:: _examples/delay_before_set_count +.. reactpy:: _examples/delay_before_set_count From an :ref:`earlier lesson `, we learned that introducing delays do not change the fact that state variables do not change until the next render. As a result, despite clicking many times before the delay completes, the ``number`` only increments by one. To solve this we can use updater functions: -.. idom:: _examples/delay_before_count_updater +.. reactpy:: _examples/delay_before_count_updater Now when you click the "Increment" button, each click, though delayed, corresponds to ``number`` being increased. This is because the ``old_number`` in the updater function diff --git a/docs/source/guides/adding-interactivity/responding-to-events/_examples/audio_player.py b/docs/source/guides/adding-interactivity/responding-to-events/_examples/audio_player.py new file mode 100644 index 000000000..82826c50d --- /dev/null +++ b/docs/source/guides/adding-interactivity/responding-to-events/_examples/audio_player.py @@ -0,0 +1,21 @@ +import json + +import reactpy + + +@reactpy.component +def PlayDinosaurSound(): + event, set_event = reactpy.hooks.use_state(None) + return reactpy.html.div( + reactpy.html.audio( + { + "controls": True, + "on_time_update": lambda e: set_event(e), + "src": "https://interactive-examples.mdn.mozilla.net/media/cc0-audio/t-rex-roar.mp3", + } + ), + reactpy.html.pre(json.dumps(event, indent=2)), + ) + + +reactpy.run(PlayDinosaurSound) diff --git a/docs/source/adding-interactivity/responding-to-events/_examples/button_async_handlers.py b/docs/source/guides/adding-interactivity/responding-to-events/_examples/button_async_handlers.py similarity index 76% rename from docs/source/adding-interactivity/responding-to-events/_examples/button_async_handlers.py rename to docs/source/guides/adding-interactivity/responding-to-events/_examples/button_async_handlers.py index a355f6142..992641e00 100644 --- a/docs/source/adding-interactivity/responding-to-events/_examples/button_async_handlers.py +++ b/docs/source/guides/adding-interactivity/responding-to-events/_examples/button_async_handlers.py @@ -1,6 +1,6 @@ import asyncio -from idom import component, html, run +from reactpy import component, html, run @component @@ -9,7 +9,7 @@ async def handle_event(event): await asyncio.sleep(delay) print(message) - return html.button({"onClick": handle_event}, message) + return html.button({"on_click": handle_event}, message) @component diff --git a/docs/source/adding-interactivity/responding-to-events/_examples/button_does_nothing.py b/docs/source/guides/adding-interactivity/responding-to-events/_examples/button_does_nothing.py similarity index 68% rename from docs/source/adding-interactivity/responding-to-events/_examples/button_does_nothing.py rename to docs/source/guides/adding-interactivity/responding-to-events/_examples/button_does_nothing.py index e26e770d2..ea8313263 100644 --- a/docs/source/adding-interactivity/responding-to-events/_examples/button_does_nothing.py +++ b/docs/source/guides/adding-interactivity/responding-to-events/_examples/button_does_nothing.py @@ -1,4 +1,4 @@ -from idom import component, html, run +from reactpy import component, html, run @component diff --git a/docs/source/adding-interactivity/responding-to-events/_examples/button_handler_as_arg.py b/docs/source/guides/adding-interactivity/responding-to-events/_examples/button_handler_as_arg.py similarity index 83% rename from docs/source/adding-interactivity/responding-to-events/_examples/button_handler_as_arg.py rename to docs/source/guides/adding-interactivity/responding-to-events/_examples/button_handler_as_arg.py index 4de22a024..e5276bef3 100644 --- a/docs/source/adding-interactivity/responding-to-events/_examples/button_handler_as_arg.py +++ b/docs/source/guides/adding-interactivity/responding-to-events/_examples/button_handler_as_arg.py @@ -1,9 +1,9 @@ -from idom import component, html, run +from reactpy import component, html, run @component def Button(display_text, on_click): - return html.button({"onClick": on_click}, display_text) + return html.button({"on_click": on_click}, display_text) @component diff --git a/docs/source/guides/adding-interactivity/responding-to-events/_examples/button_prints_event.py b/docs/source/guides/adding-interactivity/responding-to-events/_examples/button_prints_event.py new file mode 100644 index 000000000..38638db4b --- /dev/null +++ b/docs/source/guides/adding-interactivity/responding-to-events/_examples/button_prints_event.py @@ -0,0 +1,12 @@ +from reactpy import component, html, run + + +@component +def Button(): + def handle_event(event): + print(event) + + return html.button({"on_click": handle_event}, "Click me!") + + +run(Button) diff --git a/docs/source/adding-interactivity/responding-to-events/_examples/button_prints_message.py b/docs/source/guides/adding-interactivity/responding-to-events/_examples/button_prints_message.py similarity index 70% rename from docs/source/adding-interactivity/responding-to-events/_examples/button_prints_message.py rename to docs/source/guides/adding-interactivity/responding-to-events/_examples/button_prints_message.py index f5ee69f80..56118a57f 100644 --- a/docs/source/adding-interactivity/responding-to-events/_examples/button_prints_message.py +++ b/docs/source/guides/adding-interactivity/responding-to-events/_examples/button_prints_message.py @@ -1,4 +1,4 @@ -from idom import component, html, run +from reactpy import component, html, run @component @@ -6,7 +6,7 @@ def PrintButton(display_text, message_text): def handle_event(event): print(message_text) - return html.button({"onClick": handle_event}, display_text) + return html.button({"on_click": handle_event}, display_text) @component diff --git a/docs/source/adding-interactivity/responding-to-events/_examples/prevent_default_event_actions.py b/docs/source/guides/adding-interactivity/responding-to-events/_examples/prevent_default_event_actions.py similarity index 70% rename from docs/source/adding-interactivity/responding-to-events/_examples/prevent_default_event_actions.py rename to docs/source/guides/adding-interactivity/responding-to-events/_examples/prevent_default_event_actions.py index 7e8ef9938..d3f0941bd 100644 --- a/docs/source/adding-interactivity/responding-to-events/_examples/prevent_default_event_actions.py +++ b/docs/source/guides/adding-interactivity/responding-to-events/_examples/prevent_default_event_actions.py @@ -1,4 +1,4 @@ -from idom import component, event, html, run +from reactpy import component, event, html, run @component @@ -7,7 +7,7 @@ def DoNotChangePages(): html.p("Normally clicking this link would take you to a new page"), html.a( { - "onClick": event(lambda event: None, prevent_default=True), + "on_click": event(lambda event: None, prevent_default=True), "href": "https://google.com", }, "https://google.com", diff --git a/docs/source/adding-interactivity/responding-to-events/_examples/stop_event_propagation.py b/docs/source/guides/adding-interactivity/responding-to-events/_examples/stop_event_propagation.py similarity index 61% rename from docs/source/adding-interactivity/responding-to-events/_examples/stop_event_propagation.py rename to docs/source/guides/adding-interactivity/responding-to-events/_examples/stop_event_propagation.py index e87bae026..41c575042 100644 --- a/docs/source/adding-interactivity/responding-to-events/_examples/stop_event_propagation.py +++ b/docs/source/guides/adding-interactivity/responding-to-events/_examples/stop_event_propagation.py @@ -1,4 +1,4 @@ -from idom import component, event, hooks, html, run +from reactpy import component, event, hooks, html, run @component @@ -9,24 +9,28 @@ def DivInDiv(): div_in_div = html.div( { - "onClick": lambda event: set_outer_count(outer_count + 1), - "style": {"height": "100px", "width": "100px", "backgroundColor": "red"}, + "on_click": lambda event: set_outer_count(outer_count + 1), + "style": {"height": "100px", "width": "100px", "background_color": "red"}, }, html.div( { - "onClick": event( + "on_click": event( lambda event: set_inner_count(inner_count + 1), stop_propagation=stop_propagatation, ), - "style": {"height": "50px", "width": "50px", "backgroundColor": "blue"}, - }, + "style": { + "height": "50px", + "width": "50px", + "background_color": "blue", + }, + } ), ) return html.div( html.button( - {"onClick": lambda event: set_stop_propagatation(not stop_propagatation)}, - "Toggle Propogation", + {"on_click": lambda event: set_stop_propagatation(not stop_propagatation)}, + "Toggle Propagation", ), html.pre(f"Will propagate: {not stop_propagatation}"), html.pre(f"Inner click count: {inner_count}"), diff --git a/docs/source/adding-interactivity/responding-to-events/index.rst b/docs/source/guides/adding-interactivity/responding-to-events/index.rst similarity index 77% rename from docs/source/adding-interactivity/responding-to-events/index.rst rename to docs/source/guides/adding-interactivity/responding-to-events/index.rst index 3b3033bf6..89f567ca0 100644 --- a/docs/source/adding-interactivity/responding-to-events/index.rst +++ b/docs/source/guides/adding-interactivity/responding-to-events/index.rst @@ -1,7 +1,7 @@ Responding to Events ==================== -IDOM lets you add event handlers to your parts of the interface. These events handlers +ReactPy lets you add event handlers to your parts of the interface. These events handlers are functions which can be assigned to a part of a UI such that, when a user iteracts with the interface, those functions get triggered. Examples of interaction include clicking, hovering, of focusing on form inputs, and more. @@ -12,7 +12,7 @@ Adding Event Handlers To start out we'll just display a button that, for the moment, doesn't do anything: -.. idom:: _examples/button_does_nothing +.. reactpy:: _examples/button_does_nothing To add an event handler to this button we'll do three things: @@ -20,19 +20,19 @@ To add an event handler to this button we'll do three things: 2. Add logic to ``handle_event`` that will print the ``event`` it receives to the console. 3. Add an ``"onClick": handle_event`` attribute to the ``
-What this shows is that you can recreate the same HTML layouts with IDOM using functions -from the :mod:`idom.html` module. These function share the same names as their +What this shows is that you can recreate the same HTML layouts with ReactPy using functions +from the :mod:`reactpy.html` module. These function share the same names as their corresponding HTML tags. For instance, the ``

`` element above has a similarly named -:func:`~idom.html.h1` function. With that said, while the code above looks similar, it's +:func:`~reactpy.html.h1` function. With that said, while the code above looks similar, it's not very useful because we haven't captured the results from these function calls in a variable. To do this we need to wrap up the layout above into a single -:func:`~idom.html.div` and assign it to a variable: +:func:`~reactpy.html.div` and assign it to a variable: .. testcode:: @@ -73,49 +71,61 @@ Adding HTML Attributes ---------------------- That's all well and good, but there's more to HTML than just text. What if we wanted to -display an image? In HTMl we'd use the `` element and add attributes to it order +display an image? In HTMl we'd use the ```` element and add attributes to it order to specify a URL to its ``src`` and use some ``style`` to modify and position it: .. code-block:: html Billie Holiday -In IDOM we add these attributes to elements using dictionaries. There are some notable -differences though. The biggest being the fact that all names in IDOM use ``camelCase`` -instead of dash-sepearted words. For example, ``margin-left`` becomes ``marginLeft``. -Additionally, instead of specifying ``style`` using a string, we use a dictionary: +In ReactPy we add these attributes to elements using a dictionary: .. testcode:: html.img( { "src": "https://picsum.photos/id/237/500/300", - "style": {"width": "50%", "marginLeft": "25%"}, + "class_name": "img-fluid", + "style": {"width": "50%", "margin_left": "25%"}, "alt": "Billie Holiday", } ) .. raw:: html + Billie Holiday +There are some notable differences. First, all names in ReactPy use ``snake_case`` instead +of dash-separated words. For example, ``tabindex`` and ``margin-left`` become +``tab_index`` and ``margin_left`` respectively. Second, instead of using a string to +specify the ``style`` attribute, we use a dictionary to describe the CSS properties we +want to apply to an element. This is done to avoid having to escape quotes and other +characters in the string. Finally, the ``class`` attribute is renamed to ``class_name`` +to avoid conflicting with the ``class`` keyword in Python. + +For full list of supported attributes and differences from HTML, see the +:ref:`HTML Attributes` reference. ---------- .. card:: - :link: /understanding-idom/representing-html + :link: /guides/understanding-reactpy/representing-html :link-type: doc :octicon:`book` Read More ^^^^^^^^^^^^^^^^^^^^^^^^^ - Dive into the data structures IDOM uses to represent HTML + Dive into the data structures ReactPy uses to represent HTML diff --git a/docs/source/creating-interfaces/index.rst b/docs/source/guides/creating-interfaces/index.rst similarity index 72% rename from docs/source/creating-interfaces/index.rst rename to docs/source/guides/creating-interfaces/index.rst index 77f9fd4fc..78466eaef 100644 --- a/docs/source/creating-interfaces/index.rst +++ b/docs/source/guides/creating-interfaces/index.rst @@ -4,9 +4,9 @@ Creating Interfaces .. toctree:: :hidden: - html-with-idom - your-first-components - rendering-data + html-with-reactpy/index + your-first-components/index + rendering-data/index .. dropdown:: :octicon:`bookmark-fill;2em` What You'll Learn :color: info @@ -15,39 +15,39 @@ Creating Interfaces .. grid:: 1 2 2 2 - .. grid-item-card:: :octicon:`code-square` HTML with IDOM - :link: html-with-idom + .. grid-item-card:: :octicon:`code-square` HTML with ReactPy + :link: html-with-reactpy/index :link-type: doc Construct HTML layouts from the basic units of user interface functionality. .. grid-item-card:: :octicon:`package` Your First Components - :link: your-first-components + :link: your-first-components/index :link-type: doc Define reusable building blocks that it easier to construct complex interfaces. .. grid-item-card:: :octicon:`database` Rendering Data - :link: rendering-data + :link: rendering-data/index :link-type: doc Use data to organize and render HTML elements and components. -IDOM is a Python package for making user interfaces (UI). These interfaces are built -from small elements of functionality like buttons text and images. IDOM allows you to +ReactPy is a Python package for making user interfaces (UI). These interfaces are built +from small elements of functionality like buttons text and images. ReactPy allows you to combine these elements into reusable, nestable :ref:`"components" `. In the sections that follow you'll learn how these UI elements are created and organized into components. Then, you'll use components to customize and conditionally display more complex UIs. -Section 1: HTML with IDOM -------------------------- +Section 1: HTML with ReactPy +---------------------------- -In a typical Python-base web application the resonsibility of defining the view along +In a typical Python-base web application the responsibility of defining the view along with its backing data and logic are distributed between a client and server -respectively. With IDOM, both these tasks are centralized in a single place. The most +respectively. With ReactPy, both these tasks are centralized in a single place. The most foundational pilar of this capability is formed by allowing HTML interfaces to be constructed in Python. Let's consider the HTML sample below: @@ -59,11 +59,11 @@ constructed in Python. Let's consider the HTML sample below:
  • Share it with the world!
  • -To recreate the same thing in IDOM you would write: +To recreate the same thing in ReactPy you would write: .. code-block:: - from idom import html + from reactpy import html html.div( html.h1("My Todo List"), @@ -75,7 +75,7 @@ To recreate the same thing in IDOM you would write: ) .. card:: - :link: html-with-idom + :link: html-with-reactpy/index :link-type: doc :octicon:`book` Read More @@ -87,17 +87,17 @@ To recreate the same thing in IDOM you would write: Section 2: Your First Components -------------------------------- -The next building block in our journey with IDOM are components. At their core, -components are just a normal Python functions that return :ref:`HTML `. +The next building block in our journey with ReactPy are components. At their core, +components are just a normal Python functions that return :ref:`HTML `. The one special thing about them that we'll concern ourselves with now, is that to create them we need to add an ``@component`` `decorator `__. To see what this looks like in practice we'll quickly make a ``Photo`` component: -.. idom:: _examples/simple_photo +.. reactpy:: your-first-components/_examples/simple_photo .. card:: - :link: your-first-components + :link: your-first-components/index :link-type: doc :octicon:`book` Read More @@ -112,14 +112,14 @@ Section 3: Rendering Data The last pillar of knowledge you need before you can start making :ref:`interactive interfaces ` is the ability to render sections of the UI given a collection of data. This will require you to understand how elements which are derived -from data in this way must be orgnized with :ref:`"keys" `. +from data in this way must be organized with :ref:`"keys" `. One case where we might want to do this is if items in a todo list come from a list of data that we want to sort and filter: -.. idom:: _examples/todo_list_with_keys +.. reactpy:: rendering-data/_examples/todo_list_with_keys .. card:: - :link: rendering-data + :link: rendering-data/index :link-type: doc :octicon:`book` Read More diff --git a/docs/source/creating-interfaces/_examples/sorted_and_filtered_todo_list.py b/docs/source/guides/creating-interfaces/rendering-data/_examples/sorted_and_filtered_todo_list.py similarity index 89% rename from docs/source/creating-interfaces/_examples/sorted_and_filtered_todo_list.py rename to docs/source/guides/creating-interfaces/rendering-data/_examples/sorted_and_filtered_todo_list.py index 28708416e..8be2b5f30 100644 --- a/docs/source/creating-interfaces/_examples/sorted_and_filtered_todo_list.py +++ b/docs/source/guides/creating-interfaces/rendering-data/_examples/sorted_and_filtered_todo_list.py @@ -1,4 +1,4 @@ -from idom import component, html, run +from reactpy import component, html, run @component @@ -6,7 +6,7 @@ def DataList(items, filter_by_priority=None, sort_by_priority=False): if filter_by_priority is not None: items = [i for i in items if i["priority"] <= filter_by_priority] if sort_by_priority: - items = list(sorted(items, key=lambda i: i["priority"])) + items = sorted(items, key=lambda i: i["priority"]) list_item_elements = [html.li(i["text"]) for i in items] return html.ul(list_item_elements) diff --git a/docs/source/creating-interfaces/_examples/todo_from_list.py b/docs/source/guides/creating-interfaces/rendering-data/_examples/todo_from_list.py similarity index 92% rename from docs/source/creating-interfaces/_examples/todo_from_list.py rename to docs/source/guides/creating-interfaces/rendering-data/_examples/todo_from_list.py index 474c78770..85ba1d79d 100644 --- a/docs/source/creating-interfaces/_examples/todo_from_list.py +++ b/docs/source/guides/creating-interfaces/rendering-data/_examples/todo_from_list.py @@ -1,4 +1,4 @@ -from idom import component, html, run +from reactpy import component, html, run @component diff --git a/docs/source/creating-interfaces/_examples/todo_list_with_keys.py b/docs/source/guides/creating-interfaces/rendering-data/_examples/todo_list_with_keys.py similarity index 83% rename from docs/source/creating-interfaces/_examples/todo_list_with_keys.py rename to docs/source/guides/creating-interfaces/rendering-data/_examples/todo_list_with_keys.py index 5a173e1e0..8afd2ae55 100644 --- a/docs/source/creating-interfaces/_examples/todo_list_with_keys.py +++ b/docs/source/guides/creating-interfaces/rendering-data/_examples/todo_list_with_keys.py @@ -1,4 +1,4 @@ -from idom import component, html, run +from reactpy import component, html, run @component @@ -6,8 +6,8 @@ def DataList(items, filter_by_priority=None, sort_by_priority=False): if filter_by_priority is not None: items = [i for i in items if i["priority"] <= filter_by_priority] if sort_by_priority: - items = list(sorted(items, key=lambda i: i["priority"])) - list_item_elements = [html.li(i["text"], key=i["id"]) for i in items] + items = sorted(items, key=lambda i: i["priority"]) + list_item_elements = [html.li({"key": i["id"]}, i["text"]) for i in items] return html.ul(list_item_elements) diff --git a/docs/source/creating-interfaces/rendering-data.rst b/docs/source/guides/creating-interfaces/rendering-data/index.rst similarity index 89% rename from docs/source/creating-interfaces/rendering-data.rst rename to docs/source/guides/creating-interfaces/rendering-data/index.rst index d10aed9c3..620426142 100644 --- a/docs/source/creating-interfaces/rendering-data.rst +++ b/docs/source/guides/creating-interfaces/rendering-data/index.rst @@ -40,7 +40,7 @@ We could then take this list and "render" it into a series of ``
  • `` elements: .. testcode:: - from idom import html + from reactpy import html list_item_elements = [html.li(text) for text in tasks] @@ -52,7 +52,7 @@ This list of elements can then be passed into a parent ``
      `` element: The last thing we have to do is return this from a component: -.. idom:: _examples/todo_from_list +.. reactpy:: _examples/todo_from_list Filtering and Sorting Elements @@ -76,14 +76,10 @@ priority. Thus, we need to change the data structure we're using to represent ou ] With this we can now imaging writing some filtering and sorting logic using Python's -:func:`filter` and :func:`sorted` functions respecitvely. We'll do this by only +:func:`filter` and :func:`sorted` functions respectively. We'll do this by only displaying items whose ``priority`` is less than or equal to some ``filter_by_priority`` and then ordering the elements based on the ``priority``: -.. testcode:: - - x = 1 - .. testcode:: filter_by_priority = 1 @@ -105,13 +101,18 @@ and then ordering the elements based on the ``priority``: We could then add this code to our ``DataList`` component: -.. idom:: _examples/sorted_and_filtered_todo_list +.. warning:: + + The code below produces a bunch of warnings! Be sure to read the + :ref:`next section ` to find out why. + +.. reactpy:: _examples/sorted_and_filtered_todo_list Organizing Items With Keys -------------------------- -If you run the examples above :ref:`in debug mode ` you'll +If you run the examples above :ref:`in debug mode ` you'll see the server log a bunch of errors that look something like: .. code-block:: text @@ -135,20 +136,19 @@ structure even further to include a unique ID for each item in our todo list: {"id": 7, "text": "Read a book", "priority": 1}, ] -Then, as we're constructing our ``
    • `` elements we'll pass in a ``key`` argument to -the element constructor: +Then, as we're constructing our ``
    • `` elements we'll declare a ``key`` attribute: .. code-block:: - list_item_elements = [html.li(t["text"], key=t["id"]) for t in tasks] + list_item_elements = [html.li({"key": t["id"]}, t["text"]) for t in tasks] -This ``key`` tells IDOM which ``
    • `` element corresponds to which item of data in our +This ``key`` tells ReactPy which ``
    • `` element corresponds to which item of data in our ``tasks`` list. This becomes important if the order or number of items in your list can change. In our case, if we decided to change whether we want to ``filter_by_priority`` or ``sort_by_priority`` the items in our ``
        `` element would change. Given this, here's how we'd change our component: -.. idom:: _examples/todo_list_with_keys +.. reactpy:: _examples/todo_list_with_keys Keys for Components @@ -161,7 +161,7 @@ exact same way that it does for standard HTML elements: .. testcode:: - from idom import component + from reactpy import component @component @@ -189,7 +189,7 @@ exact same way that it does for standard HTML elements: .. testcode:: - from idom import component + from reactpy import component @component def FunctionWithKeyParam(key): @@ -231,7 +231,7 @@ with the correct UI element. html.ul([html.li(data["text"], key=data["id"]) for data in data_2]), ) -.. dropdown:: Keys must be unique amonst siblings +.. dropdown:: Keys must be unique amongst siblings :color: danger Keys must be unique among siblings. @@ -288,10 +288,10 @@ use for each ``key``? .. card:: - :link: /understanding-idom/why-idom-needs-keys + :link: /guides/understanding-reactpy/why-reactpy-needs-keys :link-type: doc :octicon:`book` Read More ^^^^^^^^^^^^^^^^^^^^^^^^^ - Learn about why IDOM needs keys in the first place. + Learn about why ReactPy needs keys in the first place. diff --git a/docs/source/creating-interfaces/_examples/bad_conditional_todo_list.py b/docs/source/guides/creating-interfaces/your-first-components/_examples/bad_conditional_todo_list.py similarity index 91% rename from docs/source/creating-interfaces/_examples/bad_conditional_todo_list.py rename to docs/source/guides/creating-interfaces/your-first-components/_examples/bad_conditional_todo_list.py index a5de11f77..1ed3268b6 100644 --- a/docs/source/creating-interfaces/_examples/bad_conditional_todo_list.py +++ b/docs/source/guides/creating-interfaces/your-first-components/_examples/bad_conditional_todo_list.py @@ -1,4 +1,4 @@ -from idom import component, html, run +from reactpy import component, html, run @component diff --git a/docs/source/creating-interfaces/_examples/good_conditional_todo_list.py b/docs/source/guides/creating-interfaces/your-first-components/_examples/good_conditional_todo_list.py similarity index 79% rename from docs/source/creating-interfaces/_examples/good_conditional_todo_list.py rename to docs/source/guides/creating-interfaces/your-first-components/_examples/good_conditional_todo_list.py index 64d6f6813..cd9ab6fc0 100644 --- a/docs/source/creating-interfaces/_examples/good_conditional_todo_list.py +++ b/docs/source/guides/creating-interfaces/your-first-components/_examples/good_conditional_todo_list.py @@ -1,9 +1,9 @@ -from idom import component, html, run +from reactpy import component, html, run @component def Item(name, done): - return html.li(name, "" if done else " ✔") + return html.li(name, " ✔" if done else "") @component diff --git a/docs/source/creating-interfaces/_examples/nested_photos.py b/docs/source/guides/creating-interfaces/your-first-components/_examples/nested_photos.py similarity index 89% rename from docs/source/creating-interfaces/_examples/nested_photos.py rename to docs/source/guides/creating-interfaces/your-first-components/_examples/nested_photos.py index 4c512b7e6..96f8531d3 100644 --- a/docs/source/creating-interfaces/_examples/nested_photos.py +++ b/docs/source/guides/creating-interfaces/your-first-components/_examples/nested_photos.py @@ -1,4 +1,4 @@ -from idom import component, html, run +from reactpy import component, html, run @component diff --git a/docs/source/creating-interfaces/_examples/parametrized_photos.py b/docs/source/guides/creating-interfaces/your-first-components/_examples/parametrized_photos.py similarity index 78% rename from docs/source/creating-interfaces/_examples/parametrized_photos.py rename to docs/source/guides/creating-interfaces/your-first-components/_examples/parametrized_photos.py index aeea41589..665dd8c86 100644 --- a/docs/source/creating-interfaces/_examples/parametrized_photos.py +++ b/docs/source/guides/creating-interfaces/your-first-components/_examples/parametrized_photos.py @@ -1,11 +1,11 @@ -from idom import component, html, run +from reactpy import component, html, run @component def Photo(alt_text, image_id): return html.img( { - "src": f"https://picsum.photos/id/{image_id}/500/300", + "src": f"https://picsum.photos/id/{image_id}/500/200", "style": {"width": "50%"}, "alt": alt_text, } diff --git a/docs/source/creating-interfaces/_examples/simple_photo.py b/docs/source/guides/creating-interfaces/your-first-components/_examples/simple_photo.py similarity index 83% rename from docs/source/creating-interfaces/_examples/simple_photo.py rename to docs/source/guides/creating-interfaces/your-first-components/_examples/simple_photo.py index c6b92c652..94fa6633f 100644 --- a/docs/source/creating-interfaces/_examples/simple_photo.py +++ b/docs/source/guides/creating-interfaces/your-first-components/_examples/simple_photo.py @@ -1,4 +1,4 @@ -from idom import component, html, run +from reactpy import component, html, run @component diff --git a/docs/source/creating-interfaces/_examples/todo_list.py b/docs/source/guides/creating-interfaces/your-first-components/_examples/todo_list.py similarity index 90% rename from docs/source/creating-interfaces/_examples/todo_list.py rename to docs/source/guides/creating-interfaces/your-first-components/_examples/todo_list.py index 4f0053c4b..2ffd09261 100644 --- a/docs/source/creating-interfaces/_examples/todo_list.py +++ b/docs/source/guides/creating-interfaces/your-first-components/_examples/todo_list.py @@ -1,4 +1,4 @@ -from idom import component, html, run +from reactpy import component, html, run @component diff --git a/docs/source/guides/creating-interfaces/your-first-components/_examples/wrap_in_div.py b/docs/source/guides/creating-interfaces/your-first-components/_examples/wrap_in_div.py new file mode 100644 index 000000000..58ed79dd8 --- /dev/null +++ b/docs/source/guides/creating-interfaces/your-first-components/_examples/wrap_in_div.py @@ -0,0 +1,13 @@ +from reactpy import component, html, run + + +@component +def MyTodoList(): + return html.div( + html.h1("My Todo List"), + html.img({"src": "https://picsum.photos/id/0/500/300"}), + html.ul(html.li("The first thing I need to do is...")), + ) + + +run(MyTodoList) diff --git a/docs/source/guides/creating-interfaces/your-first-components/_examples/wrap_in_fragment.py b/docs/source/guides/creating-interfaces/your-first-components/_examples/wrap_in_fragment.py new file mode 100644 index 000000000..cc54d8b71 --- /dev/null +++ b/docs/source/guides/creating-interfaces/your-first-components/_examples/wrap_in_fragment.py @@ -0,0 +1,13 @@ +from reactpy import component, html, run + + +@component +def MyTodoList(): + return html._( + html.h1("My Todo List"), + html.img({"src": "https://picsum.photos/id/0/500/200"}), + html.ul(html.li("The first thing I need to do is...")), + ) + + +run(MyTodoList) diff --git a/docs/source/creating-interfaces/your-first-components.rst b/docs/source/guides/creating-interfaces/your-first-components/index.rst similarity index 55% rename from docs/source/creating-interfaces/your-first-components.rst rename to docs/source/guides/creating-interfaces/your-first-components/index.rst index eeee7fa31..00b53d1b7 100644 --- a/docs/source/creating-interfaces/your-first-components.rst +++ b/docs/source/guides/creating-interfaces/your-first-components/index.rst @@ -1,10 +1,10 @@ Your First Components ===================== -As we learned :ref:`earlier ` we can use IDOM to make rich structured +As we learned :ref:`earlier ` we can use ReactPy to make rich structured documents out of standard HTML elements. As these documents become larger and more complex though, working with these tiny UI elements can become difficult. When this -happens, IDOM allows you to group these elements together info "components". These +happens, ReactPy allows you to group these elements together info "components". These components can then be reused throughout your application. @@ -16,9 +16,9 @@ component you just need to add a ``@component`` `decorator `__ to a function. Functions decorator in this way are known as **render function** and, by convention, we name them like classes - with ``CamelCase``. So consider what we would do if we wanted to write, -and then :ref:`display ` a ``Photo`` component: +and then :ref:`display ` a ``Photo`` component: -.. idom:: _examples/simple_photo +.. reactpy:: _examples/simple_photo .. warning:: @@ -39,7 +39,64 @@ can define a "parent" ``Gallery`` component that returns one or more ``Profile`` components. This is part of what makes components so powerful - you can define a component once and use it wherever and however you need to: -.. idom:: _examples/nested_photos +.. reactpy:: _examples/nested_photos + + +Return a Single Root Element +---------------------------- + +Components must return a "single root element". That one root element may have children, +but you cannot for example, return a list of element from a component and expect it to +be rendered correctly. If you want to return multiple elements you must wrap them in +something like a :func:`html.div `: + +.. reactpy:: _examples/wrap_in_div + +If don't want to add an extra ``div`` you can use a "fragment" instead with the +:func:`html._ ` function: + +.. reactpy:: _examples/wrap_in_fragment + +Fragments allow you to group elements together without leaving any trace in the UI. For +example, the first code sample written with ReactPy will produce the second HTML code +block: + +.. grid:: 1 2 2 2 + :margin: 0 + :padding: 0 + + .. grid-item:: + + .. testcode:: + + from reactpy import html + + html.ul( + html._( + html.li("Group 1 Item 1"), + html.li("Group 1 Item 2"), + html.li("Group 1 Item 3"), + ), + html._( + html.li("Group 2 Item 1"), + html.li("Group 2 Item 2"), + html.li("Group 2 Item 3"), + ) + ) + + .. grid-item:: + + .. code-block:: html + +
          +
        • Group 1 Item 1
        • +
        • Group 1 Item 2
        • +
        • Group 1 Item 3
        • +
        • Group 2 Item 1
        • +
        • Group 2 Item 2
        • +
        • Group 2 Item 3
        • +
        + Parametrizing Components @@ -50,7 +107,7 @@ parent components to pass information to child components. Where standard HTML e are parametrized by dictionaries, since components behave like typical functions you can give them positional and keyword arguments as you would normally: -.. idom:: _examples/parametrized_photos +.. reactpy:: _examples/parametrized_photos Conditional Rendering @@ -61,17 +118,17 @@ conditions. Let's imagine that we had a basic todo list where only some of the i have been completed. Below we have a basic implementation for such a list except that the ``Item`` component doesn't change based on whether it's ``done``: -.. idom:: _examples/todo_list +.. reactpy:: _examples/todo_list Let's imagine that we want to add a ✔ to the items which have been marked ``done=True``. One way to do this might be to write an ``if`` statement where we return one ``li`` element if the item is ``done`` and a different one if it's not: -.. idom:: _examples/bad_conditional_todo_list +.. reactpy:: _examples/bad_conditional_todo_list As you can see this accomplishes our goal! However, notice how similar ``html.li(name, " ✔")`` and ``html.li(name)`` are. While in this case it isn't especially harmful, we could make our code a little easier to read and maintain by using an "inline" ``if`` statement. -.. idom:: _examples/good_conditional_todo_list +.. reactpy:: _examples/good_conditional_todo_list diff --git a/docs/source/guides/escape-hatches/_examples/material_ui_button_no_action.py b/docs/source/guides/escape-hatches/_examples/material_ui_button_no_action.py new file mode 100644 index 000000000..3ad4dac5b --- /dev/null +++ b/docs/source/guides/escape-hatches/_examples/material_ui_button_no_action.py @@ -0,0 +1,16 @@ +from reactpy import component, run, web + +mui = web.module_from_template( + "react@^17.0.0", + "@material-ui/core@4.12.4", + fallback="⌛", +) +Button = web.export(mui, "Button") + + +@component +def HelloWorld(): + return Button({"color": "primary", "variant": "contained"}, "Hello World!") + + +run(HelloWorld) diff --git a/docs/source/guides/escape-hatches/_examples/material_ui_button_on_click.py b/docs/source/guides/escape-hatches/_examples/material_ui_button_on_click.py new file mode 100644 index 000000000..3fc684005 --- /dev/null +++ b/docs/source/guides/escape-hatches/_examples/material_ui_button_on_click.py @@ -0,0 +1,30 @@ +import json + +import reactpy + +mui = reactpy.web.module_from_template( + "react@^17.0.0", + "@material-ui/core@4.12.4", + fallback="⌛", +) +Button = reactpy.web.export(mui, "Button") + + +@reactpy.component +def ViewButtonEvents(): + event, set_event = reactpy.hooks.use_state(None) + + return reactpy.html.div( + Button( + { + "color": "primary", + "variant": "contained", + "onClick": lambda event: set_event(event), + }, + "Click Me!", + ), + reactpy.html.pre(json.dumps(event, indent=2)), + ) + + +reactpy.run(ViewButtonEvents) diff --git a/docs/source/escape-hatches/_examples/super_simple_chart/app.py b/docs/source/guides/escape-hatches/_examples/super_simple_chart/main.py similarity index 94% rename from docs/source/escape-hatches/_examples/super_simple_chart/app.py rename to docs/source/guides/escape-hatches/_examples/super_simple_chart/main.py index 2f2e17556..4640785f8 100644 --- a/docs/source/escape-hatches/_examples/super_simple_chart/app.py +++ b/docs/source/guides/escape-hatches/_examples/super_simple_chart/main.py @@ -1,7 +1,6 @@ from pathlib import Path -from idom import component, run, web - +from reactpy import component, run, web file = Path(__file__).parent / "super-simple-chart.js" ssc = web.module_from_file("super-simple-chart", file, fallback="⌛") diff --git a/docs/source/escape-hatches/_examples/super_simple_chart/super-simple-chart.js b/docs/source/guides/escape-hatches/_examples/super_simple_chart/super-simple-chart.js similarity index 91% rename from docs/source/escape-hatches/_examples/super_simple_chart/super-simple-chart.js rename to docs/source/guides/escape-hatches/_examples/super_simple_chart/super-simple-chart.js index f14103d78..486e5c363 100644 --- a/docs/source/escape-hatches/_examples/super_simple_chart/super-simple-chart.js +++ b/docs/source/guides/escape-hatches/_examples/super_simple_chart/super-simple-chart.js @@ -8,7 +8,7 @@ export function bind(node, config) { create: (component, props, children) => h(component, props, ...children), render: (element) => render(element, node), unmount: () => render(null, node), - } + }; } export function SuperSimpleChart(props) { @@ -48,10 +48,9 @@ function makePath(props, domain, data, options) { const getSvgX = (x) => ((x - xMin) / (xMax - xMin)) * width; const getSvgY = (y) => height - ((y - yMin) / (yMax - yMin)) * height; - let pathD = "M " + getSvgX(data[0].x) + " " + getSvgY(data[0].y) + " "; - pathD += data.map((point, i) => { - return "L " + getSvgX(point.x) + " " + getSvgY(point.y) + " "; - }); + let pathD = + `M ${getSvgX(data[0].x)} ${getSvgY(data[0].y)} ` + + data.map(({ x, y }, i) => `L ${getSvgX(x)} ${getSvgY(y)}`).join(" "); return html`=40.8.0", "wheel"] build-backend = "setuptools.build_meta" -Then, we can creat the ``setup.py`` file which uses Setuptools. This will differ +Then, we can create the ``setup.py`` file which uses Setuptools. This will differ substantially from a normal ``setup.py`` file since, as part of the build process we'll need to use NPM to bundle our Javascript. This requires customizing some of the build commands in Setuptools like ``build``, ``sdist``, and ``develop``: @@ -230,10 +230,10 @@ commands in Setuptools like ``build``, ``sdist``, and ``develop``: name=PACKAGE_NAME, version="0.0.1", packages=find_packages(exclude=["tests*"]), - classifiers=["Framework :: IDOM", ...], - keywords=["IDOM", "components", ...], - # install IDOM with this package - install_requires=["idom"], + classifiers=["Framework :: ReactPy", ...], + keywords=["ReactPy", "components", ...], + # install ReactPy with this package + install_requires=["reactpy"], # required in order to include static files like bundle.js using MANIFEST.in include_package_data=True, # we need access to the file system, so cannot be run from a zip file @@ -297,7 +297,7 @@ up and running as quickly as possible. .. _install NPM: https://www.npmjs.com/get-npm .. _CDN: https://en.wikipedia.org/wiki/Content_delivery_network .. _PyPI: https://pypi.org/ -.. _template repository: https://github.com/idom-team/idom-react-component-cookiecutter +.. _template repository: https://github.com/reactive-python/reactpy-js-component-template .. _web module: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules .. _Rollup: https://rollupjs.org/guide/en/ .. _Webpack: https://webpack.js.org/ diff --git a/docs/source/escape-hatches/index.rst b/docs/source/guides/escape-hatches/index.rst similarity index 66% rename from docs/source/escape-hatches/index.rst rename to docs/source/guides/escape-hatches/index.rst index ddf0be60e..3ef1b7122 100644 --- a/docs/source/escape-hatches/index.rst +++ b/docs/source/guides/escape-hatches/index.rst @@ -4,11 +4,10 @@ Escape Hatches .. toctree:: :hidden: - class-components javascript-components distributing-javascript - writing-your-own-server - writing-your-own-client + using-a-custom-backend + using-a-custom-client .. note:: diff --git a/docs/source/escape-hatches/javascript-components.rst b/docs/source/guides/escape-hatches/javascript-components.rst similarity index 87% rename from docs/source/escape-hatches/javascript-components.rst rename to docs/source/guides/escape-hatches/javascript-components.rst index 4e0177dcf..f0a71b6b7 100644 --- a/docs/source/escape-hatches/javascript-components.rst +++ b/docs/source/guides/escape-hatches/javascript-components.rst @@ -3,12 +3,12 @@ Javascript Components ===================== -While IDOM is a great tool for displaying HTML and responding to browser events with +While ReactPy is a great tool for displaying HTML and responding to browser events with pure Python, there are other projects which already allow you to do this inside `Jupyter Notebooks `__ or in standard `web apps `__. -The real power of IDOM comes from its ability to seamlessly leverage the existing +The real power of ReactPy comes from its ability to seamlessly leverage the existing Javascript ecosystem. This can be accomplished in different ways for different reasons: .. list-table:: @@ -18,7 +18,7 @@ Javascript ecosystem. This can be accomplished in different ways for different r - Use Case * - :ref:`Dynamically Loaded Components` - - You want to **quickly experiment** with IDOM and the Javascript ecosystem. + - You want to **quickly experiment** with ReactPy and the Javascript ecosystem. * - :ref:`Custom Javascript Components` - You want to create polished software that can be **easily shared** with others. @@ -35,18 +35,18 @@ Dynamically Loaded Components Javascript` for more info. Instead, it's best used during exploratory phases of development. -IDOM makes it easy to draft your code when you're in the early stages of development by +ReactPy makes it easy to draft your code when you're in the early stages of development by using a CDN_ to dynamically load Javascript packages on the fly. In this example we'll be using the ubiquitous React-based UI framework `Material UI`_. -.. idom:: _examples/material_ui_button_no_action +.. reactpy:: _examples/material_ui_button_no_action So now that we can display a Material UI Button we probably want to make it do something. Thankfully there's nothing new to learn here, you can pass event handlers to the button just as you did when :ref:`getting started `. Thus, all we need to do is add an ``onClick`` handler to the component: -.. idom:: _examples/material_ui_button_on_click +.. reactpy:: _examples/material_ui_button_on_click .. _Custom Javascript Component: @@ -55,7 +55,7 @@ Custom Javascript Components ---------------------------- For projects that will be shared with others, we recommend bundling your Javascript with -Rollup_ or Webpack_ into a `web module`_. IDOM also provides a `template repository`_ +Rollup_ or Webpack_ into a `web module`_. ReactPy also provides a `template repository`_ that can be used as a blueprint to build a library of React components. To work as intended, the Javascript bundle must export a function ``bind()`` that @@ -132,7 +132,7 @@ Javascript that can run directly in the browser. This means we can't use fancy s like `JSX `__ and instead will use `htm `__ to simulate JSX in plain Javascript. -.. idom:: _examples/super_simple_chart +.. reactpy:: _examples/super_simple_chart .. Links @@ -140,7 +140,7 @@ like `JSX `__ and instead will us .. _Material UI: https://material-ui.com/ .. _CDN: https://en.wikipedia.org/wiki/Content_delivery_network -.. _template repository: https://github.com/idom-team/idom-react-component-cookiecutter +.. _template repository: https://github.com/reactive-python/reactpy-js-component-template .. _web module: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules .. _Rollup: https://rollupjs.org/guide/en/ .. _Webpack: https://webpack.js.org/ diff --git a/docs/source/guides/escape-hatches/using-a-custom-backend.rst b/docs/source/guides/escape-hatches/using-a-custom-backend.rst new file mode 100644 index 000000000..f9d21208a --- /dev/null +++ b/docs/source/guides/escape-hatches/using-a-custom-backend.rst @@ -0,0 +1,9 @@ +.. _Writing Your Own Backend: +.. _Using a Custom Backend: + +Using a Custom Backend 🚧 +========================= + +.. note:: + + Under construction 🚧 diff --git a/docs/source/guides/escape-hatches/using-a-custom-client.rst b/docs/source/guides/escape-hatches/using-a-custom-client.rst new file mode 100644 index 000000000..95de23e59 --- /dev/null +++ b/docs/source/guides/escape-hatches/using-a-custom-client.rst @@ -0,0 +1,9 @@ +.. _Writing Your Own Client: +.. _Using a Custom Client: + +Using a Custom Client 🚧 +======================== + +.. note:: + + Under construction 🚧 diff --git a/docs/source/guides/getting-started/_examples/debug_error_example.py b/docs/source/guides/getting-started/_examples/debug_error_example.py new file mode 100644 index 000000000..dd0b212ab --- /dev/null +++ b/docs/source/guides/getting-started/_examples/debug_error_example.py @@ -0,0 +1,20 @@ +from reactpy import component, html, run + + +@component +def App(): + return html.div(GoodComponent(), BadComponent()) + + +@component +def GoodComponent(): + return html.p("This component rendered successfully") + + +@component +def BadComponent(): + msg = "This component raised an error" + raise RuntimeError(msg) + + +run(App) diff --git a/docs/source/guides/getting-started/_examples/hello_world.py b/docs/source/guides/getting-started/_examples/hello_world.py new file mode 100644 index 000000000..f38d9e38f --- /dev/null +++ b/docs/source/guides/getting-started/_examples/hello_world.py @@ -0,0 +1,9 @@ +from reactpy import component, html, run + + +@component +def App(): + return html.h1("Hello, world!") + + +run(App) diff --git a/docs/source/guides/getting-started/_examples/run_fastapi.py b/docs/source/guides/getting-started/_examples/run_fastapi.py new file mode 100644 index 000000000..bb02e9d6a --- /dev/null +++ b/docs/source/guides/getting-started/_examples/run_fastapi.py @@ -0,0 +1,22 @@ +# :lines: 11- + +from reactpy import run +from reactpy.backend import fastapi as fastapi_server + +# the run() function is the entry point for examples +fastapi_server.configure = lambda _, cmpt: run(cmpt) + + +from fastapi import FastAPI + +from reactpy import component, html +from reactpy.backend.fastapi import configure + + +@component +def HelloWorld(): + return html.h1("Hello, world!") + + +app = FastAPI() +configure(app, HelloWorld) diff --git a/docs/source/guides/getting-started/_examples/run_flask.py b/docs/source/guides/getting-started/_examples/run_flask.py new file mode 100644 index 000000000..f98753784 --- /dev/null +++ b/docs/source/guides/getting-started/_examples/run_flask.py @@ -0,0 +1,22 @@ +# :lines: 11- + +from reactpy import run +from reactpy.backend import flask as flask_server + +# the run() function is the entry point for examples +flask_server.configure = lambda _, cmpt: run(cmpt) + + +from flask import Flask + +from reactpy import component, html +from reactpy.backend.flask import configure + + +@component +def HelloWorld(): + return html.h1("Hello, world!") + + +app = Flask(__name__) +configure(app, HelloWorld) diff --git a/docs/source/guides/getting-started/_examples/run_sanic.py b/docs/source/guides/getting-started/_examples/run_sanic.py new file mode 100644 index 000000000..1dae9f6e0 --- /dev/null +++ b/docs/source/guides/getting-started/_examples/run_sanic.py @@ -0,0 +1,26 @@ +# :lines: 11- + +from reactpy import run +from reactpy.backend import sanic as sanic_server + +# the run() function is the entry point for examples +sanic_server.configure = lambda _, cmpt: run(cmpt) + + +from sanic import Sanic + +from reactpy import component, html +from reactpy.backend.sanic import configure + + +@component +def HelloWorld(): + return html.h1("Hello, world!") + + +app = Sanic("MyApp") +configure(app, HelloWorld) + + +if __name__ == "__main__": + app.run(port=8000) diff --git a/docs/source/guides/getting-started/_examples/run_starlette.py b/docs/source/guides/getting-started/_examples/run_starlette.py new file mode 100644 index 000000000..966b9ef77 --- /dev/null +++ b/docs/source/guides/getting-started/_examples/run_starlette.py @@ -0,0 +1,22 @@ +# :lines: 11- + +from reactpy import run +from reactpy.backend import starlette as starlette_server + +# the run() function is the entry point for examples +starlette_server.configure = lambda _, cmpt: run(cmpt) + + +from starlette.applications import Starlette + +from reactpy import component, html +from reactpy.backend.starlette import configure + + +@component +def HelloWorld(): + return html.h1("Hello, world!") + + +app = Starlette() +configure(app, HelloWorld) diff --git a/docs/source/guides/getting-started/_examples/run_tornado.py b/docs/source/guides/getting-started/_examples/run_tornado.py new file mode 100644 index 000000000..b86126e63 --- /dev/null +++ b/docs/source/guides/getting-started/_examples/run_tornado.py @@ -0,0 +1,31 @@ +# :lines: 11- + +from reactpy import run +from reactpy.backend import tornado as tornado_server + +# the run() function is the entry point for examples +tornado_server.configure = lambda _, cmpt: run(cmpt) + + +import tornado.ioloop +import tornado.web + +from reactpy import component, html +from reactpy.backend.tornado import configure + + +@component +def HelloWorld(): + return html.h1("Hello, world!") + + +def make_app(): + app = tornado.web.Application() + configure(app, HelloWorld) + return app + + +if __name__ == "__main__": + app = make_app() + app.listen(8000) + tornado.ioloop.IOLoop.current().start() diff --git a/docs/source/guides/getting-started/_examples/sample_app.py b/docs/source/guides/getting-started/_examples/sample_app.py new file mode 100644 index 000000000..a1cc34e6d --- /dev/null +++ b/docs/source/guides/getting-started/_examples/sample_app.py @@ -0,0 +1,3 @@ +import reactpy + +reactpy.run(reactpy.sample.SampleApp) diff --git a/docs/source/guides/getting-started/_static/embed-doc-ex.html b/docs/source/guides/getting-started/_static/embed-doc-ex.html new file mode 100644 index 000000000..589cb5d80 --- /dev/null +++ b/docs/source/guides/getting-started/_static/embed-doc-ex.html @@ -0,0 +1,8 @@ +
        + diff --git a/docs/source/guides/getting-started/_static/embed-reactpy-view/index.html b/docs/source/guides/getting-started/_static/embed-reactpy-view/index.html new file mode 100644 index 000000000..146d715e4 --- /dev/null +++ b/docs/source/guides/getting-started/_static/embed-reactpy-view/index.html @@ -0,0 +1,31 @@ + + + + + Example App + + +

        This is an Example App

        +

        Just below is an embedded ReactPy view...

        +
        + + + diff --git a/docs/source/guides/getting-started/_static/embed-reactpy-view/main.py b/docs/source/guides/getting-started/_static/embed-reactpy-view/main.py new file mode 100644 index 000000000..6e3687f27 --- /dev/null +++ b/docs/source/guides/getting-started/_static/embed-reactpy-view/main.py @@ -0,0 +1,22 @@ +from sanic import Sanic +from sanic.response import file + +from reactpy import component, html +from reactpy.backend.sanic import Options, configure + +app = Sanic("MyApp") + + +@app.route("/") +async def index(request): + return await file("index.html") + + +@component +def ReactPyView(): + return html.code("This text came from an ReactPy App") + + +configure(app, ReactPyView, Options(url_prefix="/_reactpy")) + +app.run(host="127.0.0.1", port=5000) diff --git a/docs/source/getting-started/_static/embed-idom-view/screenshot.png b/docs/source/guides/getting-started/_static/embed-reactpy-view/screenshot.png similarity index 100% rename from docs/source/getting-started/_static/embed-idom-view/screenshot.png rename to docs/source/guides/getting-started/_static/embed-reactpy-view/screenshot.png diff --git a/docs/source/getting-started/_static/logo-django.svg b/docs/source/guides/getting-started/_static/logo-django.svg similarity index 100% rename from docs/source/getting-started/_static/logo-django.svg rename to docs/source/guides/getting-started/_static/logo-django.svg diff --git a/docs/source/getting-started/_static/logo-jupyter.svg b/docs/source/guides/getting-started/_static/logo-jupyter.svg similarity index 100% rename from docs/source/getting-started/_static/logo-jupyter.svg rename to docs/source/guides/getting-started/_static/logo-jupyter.svg diff --git a/docs/source/getting-started/_static/logo-plotly.svg b/docs/source/guides/getting-started/_static/logo-plotly.svg similarity index 100% rename from docs/source/getting-started/_static/logo-plotly.svg rename to docs/source/guides/getting-started/_static/logo-plotly.svg diff --git a/docs/source/getting-started/_static/idom-in-jupyterlab.gif b/docs/source/guides/getting-started/_static/reactpy-in-jupyterlab.gif similarity index 100% rename from docs/source/getting-started/_static/idom-in-jupyterlab.gif rename to docs/source/guides/getting-started/_static/reactpy-in-jupyterlab.gif diff --git a/docs/source/getting-started/_static/shared-client-state-server-slider.gif b/docs/source/guides/getting-started/_static/shared-client-state-server-slider.gif similarity index 100% rename from docs/source/getting-started/_static/shared-client-state-server-slider.gif rename to docs/source/guides/getting-started/_static/shared-client-state-server-slider.gif diff --git a/docs/source/guides/getting-started/index.rst b/docs/source/guides/getting-started/index.rst new file mode 100644 index 000000000..dd210be60 --- /dev/null +++ b/docs/source/guides/getting-started/index.rst @@ -0,0 +1,123 @@ +Getting Started +=============== + +.. toctree:: + :hidden: + + installing-reactpy + running-reactpy + +.. dropdown:: :octicon:`bookmark-fill;2em` What You'll Learn + :color: info + :animate: fade-in + :open: + + .. grid:: 1 2 2 2 + + .. grid-item-card:: :octicon:`tools` Installing ReactPy + :link: installing-reactpy + :link-type: doc + + Learn how ReactPy can be installed in a variety of different ways - with + different web servers and even in different frameworks. + + .. grid-item-card:: :octicon:`play` Running ReactPy + :link: running-reactpy + :link-type: doc + + See how ReactPy can be run with a variety of different production servers or be + added to existing applications. + +The fastest way to get started with ReactPy is to try it out in a `Juptyer Notebook +`__. +If you want to use a Notebook to work through the examples shown in this documentation, +you'll need to replace calls to ``reactpy.run(App)`` with a line at the end of each cell +that constructs the ``App()`` in question. If that doesn't make sense, the introductory +notebook linked below will demonstrate how to do this: + +.. card:: + :link: https://mybinder.org/v2/gh/reactive-python/reactpy-jupyter/main?urlpath=lab/tree/notebooks/introduction.ipynb + + .. image:: _static/reactpy-in-jupyterlab.gif + :scale: 72% + :align: center + + +Section 1: Installing ReactPy +----------------------------- + +The next fastest option is to install ReactPy along with a supported server (like +``starlette``) with ``pip``: + +.. code-block:: bash + + pip install "reactpy[starlette]" + +To check that everything is working you can run the sample application: + +.. code-block:: bash + + python -c "import reactpy; reactpy.run(reactpy.sample.SampleApp)" + +.. note:: + + This launches a simple development server which is good enough for testing, but + probably not what you want to use in production. When deploying in production, + there's a number of different ways of :ref:`running ReactPy
        `. + +You should then see a few log messages: + +.. code-block:: text + + 2022-03-27T11:58:59-0700 | WARNING | You are running a development server. Change this before deploying in production! + 2022-03-27T11:58:59-0700 | INFO | Running with 'Starlette' at http://127.0.0.1:8000 + +The second log message includes a URL indicating where you should go to view the app. +That will usually be http://127.0.0.1:8000. Once you go to that URL you should see +something like this: + +.. card:: + + .. reactpy-view:: _examples/sample_app + +If you get a ``RuntimeError`` similar to the following: + +.. code-block:: text + + Found none of the following builtin server implementations... + +Then be sure you run ``pip install "reactpy[starlette]"`` instead of just ``reactpy``. For +anything else, report your issue in ReactPy's :discussion-type:`discussion forum +`. + +.. card:: + :link: installing-reactpy + :link-type: doc + + :octicon:`book` Read More + ^^^^^^^^^^^^^^^^^^^^^^^^^ + + Learn how ReactPy can be installed in a variety of different ways - with different web + servers and even in different frameworks. + + +Section 2: Running ReactPy +-------------------------- + +Once you've :ref:`installed ReactPy `, you'll want to learn how to run an +application. Throughout most of the examples in this documentation, you'll see the +:func:`~reactpy.backend.utils.run` function used. While it's convenient tool for +development it shouldn't be used in production settings - it's slow, and could leak +secrets through debug log messages. + +.. reactpy:: _examples/hello_world + +.. card:: + :link: running-reactpy + :link-type: doc + + :octicon:`book` Read More + ^^^^^^^^^^^^^^^^^^^^^^^^^ + + See how ReactPy can be run with a variety of different production servers or be + added to existing applications. diff --git a/docs/source/guides/getting-started/installing-reactpy.rst b/docs/source/guides/getting-started/installing-reactpy.rst new file mode 100644 index 000000000..0b2ffc28a --- /dev/null +++ b/docs/source/guides/getting-started/installing-reactpy.rst @@ -0,0 +1,121 @@ +Installing ReactPy +================== + +You will typically ``pip`` install ReactPy to alongside one of it's natively supported +backends. For example, if we want to run ReactPy using the `Starlette +`__ backend you would run + +.. code-block:: bash + + pip install "reactpy[starlette]" + +If you want to install a "pure" version of ReactPy **without a backend implementation** +you can do so without any installation extras. You might do this if you wanted to +:ref:`use a custom backend ` or if you wanted to manually pin +the dependencies for your chosen backend: + +.. code-block:: bash + + pip install reactpy + + +Native Backends +--------------- + +ReactPy includes built-in support for a variety backend implementations. To install the +required dependencies for each you should substitute ``starlette`` from the ``pip +install`` command above with one of the options below: + +- ``fastapi`` - https://fastapi.tiangolo.com +- ``flask`` - https://palletsprojects.com/p/flask/ +- ``sanic`` - https://sanicframework.org +- ``starlette`` - https://www.starlette.io/ +- ``tornado`` - https://www.tornadoweb.org/en/stable/ + +If you need to, you can install more than one option by separating them with commas: + +.. code-block:: bash + + pip install "reactpy[fastapi,flask,sanic,starlette,tornado]" + +Once this is complete you should be able to :ref:`run ReactPy ` with your +chosen implementation. + + +Other Backends +-------------- + +While ReactPy can run in a variety of contexts, sometimes frameworks require extra work in +order to integrate with them. In these cases, the ReactPy team distributes bindings for +those frameworks as separate Python packages. For documentation on how to install and +run ReactPy in these supported frameworks, follow the links below: + +.. raw:: html + + + +.. role:: transparent-text-color + +.. We add transparent-text-color to the text so it's not visible, but it's still +.. searchable. + +.. grid:: 3 + + .. grid-item-card:: + :link: https://github.com/reactive-python/reactpy-django + :img-background: _static/logo-django.svg + :class-card: card-logo-image + + :transparent-text-color:`Django` + + .. grid-item-card:: + :link: https://github.com/reactive-python/reactpy-jupyter + :img-background: _static/logo-jupyter.svg + :class-card: card-logo-image + + :transparent-text-color:`Jupyter` + + .. grid-item-card:: + :link: https://github.com/reactive-python/reactpy-dash + :img-background: _static/logo-plotly.svg + :class-card: card-logo-image + + :transparent-text-color:`Plotly Dash` + + +For Development +--------------- + +If you want to contribute to the development of ReactPy or modify it, you'll want to +install a development version of ReactPy. This involves cloning the repository where ReactPy's +source is maintained, and setting up a :ref:`development environment`. From there you'll +be able to modifying ReactPy's source code and :ref:`run its tests ` to +ensure the modifications you've made are backwards compatible. If you want to add a new +feature to ReactPy you should write your own test that validates its behavior. + +If you have questions about how to modify ReactPy or help with its development, be sure to +:discussion:`start a discussion `. The ReactPy team are always +excited to :ref:`welcome ` new contributions and contributors +of all kinds + +.. card:: + :link: /about/contributor-guide + :link-type: doc + + :octicon:`book` Read More + ^^^^^^^^^^^^^^^^^^^^^^^^^ + + Learn more about how to contribute to the development of ReactPy. diff --git a/docs/source/guides/getting-started/running-reactpy.rst b/docs/source/guides/getting-started/running-reactpy.rst new file mode 100644 index 000000000..8abbd574f --- /dev/null +++ b/docs/source/guides/getting-started/running-reactpy.rst @@ -0,0 +1,221 @@ +Running ReactPy +=============== + +The simplest way to run ReactPy is with the :func:`~reactpy.backend.utils.run` function. This +is the method you'll see used throughout this documentation. However, this executes your +application using a development server which is great for testing, but probably not what +if you're :ref:`deploying in production `. Below are some +more robust and performant ways of running ReactPy with various supported servers. + + +Running ReactPy in Production +----------------------------- + +The first thing you'll need to do if you want to run ReactPy in production is choose a +backend implementation and follow its documentation on how to create and run an +application. This is the backend :ref:`you probably chose ` when +installing ReactPy. Then you'll need to configure that application with an ReactPy view. We +show the basics of how to set up, and then run, each supported backend below, but all +implementations will follow a pattern similar to the following: + +.. code-block:: + + from my_chosen_backend import Application + + from reactpy import component, html + from reactpy.backend.my_chosen_backend import configure + + + @component + def HelloWorld(): + return html.h1("Hello, world!") + + + app = Application() + configure(app, HelloWorld) + +You'll then run this ``app`` using an `ASGI `__ +or `WSGI `__ server from the command line. + + +Running with `FastAPI `__ +....................................................... + +.. reactpy:: _examples/run_fastapi + +Then assuming you put this in ``main.py``, you can run the ``app`` using the `Uvicorn +`__ ASGI server: + +.. code-block:: bash + + uvicorn main:app + + +Running with `Flask `__ +............................................................. + +.. reactpy:: _examples/run_flask + +Then assuming you put this in ``main.py``, you can run the ``app`` using the `Gunicorn +`__ WSGI server: + +.. code-block:: bash + + gunicorn main:app + + +Running with `Sanic `__ +................................................... + +.. reactpy:: _examples/run_sanic + +Then assuming you put this in ``main.py``, you can run the ``app`` using Sanic's builtin +server: + +.. code-block:: bash + + sanic main.app + + +Running with `Starlette `__ +...................................................... + +.. reactpy:: _examples/run_starlette + +Then assuming you put this in ``main.py``, you can run the application using the +`Uvicorn `__ ASGI server: + +.. code-block:: bash + + uvicorn main:app + + +Running with `Tornado `__ +................................................................ + +.. reactpy:: _examples/run_tornado + +Tornado is run using it's own builtin server rather than an external WSGI or ASGI +server. + + +Running ReactPy in Debug Mode +----------------------------- + +ReactPy provides a debug mode that is turned off by default. This can be enabled when you +run your application by setting the ``REACTPY_DEBUG_MODE`` environment variable. + +.. tab-set:: + + .. tab-item:: Unix Shell + + .. code-block:: + + export REACTPY_DEBUG_MODE=1 + python my_reactpy_app.py + + .. tab-item:: Command Prompt + + .. code-block:: text + + set REACTPY_DEBUG_MODE=1 + python my_reactpy_app.py + + .. tab-item:: PowerShell + + .. code-block:: powershell + + $env:REACTPY_DEBUG_MODE = "1" + python my_reactpy_app.py + +.. danger:: + + Leave debug mode off in production! + +Among other things, running in this mode: + +- Turns on debug log messages +- Adds checks to ensure the :ref:`VDOM` spec is adhered to +- Displays error messages that occur within your app + +Errors will be displayed where the uppermost component is located in the view: + +.. reactpy:: _examples/debug_error_example + + +Backend Configuration Options +----------------------------- + +ReactPy's various backend implementations come with ``Options`` that can be passed to their +respective ``configure()`` functions in the following way: + +.. code-block:: + + from reactpy.backend. import configure, Options + + configure(app, MyComponent, Options(...)) + +To learn more read about the options for your chosen backend ````: + +- :class:`reactpy.backend.fastapi.Options` +- :class:`reactpy.backend.flask.Options` +- :class:`reactpy.backend.sanic.Options` +- :class:`reactpy.backend.starlette.Options` +- :class:`reactpy.backend.tornado.Options` + + +Embed in an Existing Webpage +---------------------------- + +ReactPy provides a Javascript client called ``@reactpy/client`` that can be used to embed +ReactPy views within an existing applications. This is actually how the interactive +examples throughout this documentation have been created. You can try this out by +embedding one the examples from this documentation into your own webpage: + +.. tab-set:: + + .. tab-item:: HTML + + .. literalinclude:: _static/embed-doc-ex.html + :language: html + + .. tab-item:: â–ļī¸ Result + + .. raw:: html + :file: _static/embed-doc-ex.html + +.. note:: + + For more information on how to use the client see the :ref:`Javascript API` + reference. Or if you need to, your can :ref:`write your own backend implementation + `. + +As mentioned though, this is connecting to the server that is hosting this +documentation. If you want to connect to a view from your own server, you'll need to +change the URL above to one you provide. One way to do this might be to add to an +existing application. Another would be to run ReactPy in an adjacent web server instance +that you coordinate with something like `NGINX `__. For the sake +of simplicity, we'll assume you do something similar to the following in an existing +Python app: + +.. tab-set:: + + .. tab-item:: main.py + + .. literalinclude:: _static/embed-reactpy-view/main.py + :language: python + + .. tab-item:: index.html + + .. literalinclude:: _static/embed-reactpy-view/index.html + :language: html + +After running ``python main.py``, you should be able to navigate to +``http://127.0.0.1:8000/index.html`` and see: + +.. card:: + :text-align: center + + .. image:: _static/embed-reactpy-view/screenshot.png + :width: 500px + diff --git a/docs/source/guides/managing-state/combining-contexts-and-reducers/index.rst b/docs/source/guides/managing-state/combining-contexts-and-reducers/index.rst new file mode 100644 index 000000000..b9f274f0a --- /dev/null +++ b/docs/source/guides/managing-state/combining-contexts-and-reducers/index.rst @@ -0,0 +1,6 @@ +Combining Contexts and Reducers 🚧 +================================== + +.. note:: + + Under construction 🚧 diff --git a/docs/source/guides/managing-state/deeply-sharing-state-with-contexts/index.rst b/docs/source/guides/managing-state/deeply-sharing-state-with-contexts/index.rst new file mode 100644 index 000000000..4a00caa48 --- /dev/null +++ b/docs/source/guides/managing-state/deeply-sharing-state-with-contexts/index.rst @@ -0,0 +1,6 @@ +Deeply Sharing State with Contexts 🚧 +===================================== + +.. note:: + + Under construction 🚧 diff --git a/docs/source/managing-state/structuring-your-state.rst b/docs/source/guides/managing-state/how-to-structure-state/index.rst similarity index 77% rename from docs/source/managing-state/structuring-your-state.rst rename to docs/source/guides/managing-state/how-to-structure-state/index.rst index 68209cccf..5092370a5 100644 --- a/docs/source/managing-state/structuring-your-state.rst +++ b/docs/source/guides/managing-state/how-to-structure-state/index.rst @@ -1,6 +1,6 @@ .. _Structuring Your State: -Structuring Your State 🚧 +How to Structure State 🚧 ========================= .. note:: diff --git a/docs/source/guides/managing-state/index.rst b/docs/source/guides/managing-state/index.rst new file mode 100644 index 000000000..0578bafdd --- /dev/null +++ b/docs/source/guides/managing-state/index.rst @@ -0,0 +1,127 @@ +Managing State +============== + +.. toctree:: + :hidden: + + how-to-structure-state/index + sharing-component-state/index + when-and-how-to-reset-state/index + simplifying-updates-with-reducers/index + deeply-sharing-state-with-contexts/index + combining-contexts-and-reducers/index + +.. dropdown:: :octicon:`bookmark-fill;2em` What You'll Learn + :color: info + :animate: fade-in + :open: + + .. grid:: 1 2 2 2 + + .. grid-item-card:: :octicon:`organization` How to Structure State + :link: how-to-structure-state/index + :link-type: doc + + Make it easy to reason about your application with strategies for organizing + state. + + .. grid-item-card:: :octicon:`link` Sharing Component State + :link: sharing-component-state/index + :link-type: doc + + Allow components to vary vary together, by lifting state into common + parents. + + .. grid-item-card:: :octicon:`light-bulb` When and How to Reset State + :link: when-and-how-to-reset-state/index + :link-type: doc + + Control if and how state is preserved by understanding it's relationship to + the "UI tree". + + .. grid-item-card:: :octicon:`plug` Simplifying Updates with Reducers + :link: simplifying-updates-with-reducers/index + :link-type: doc + + Consolidate state update logic outside your component in a single function, + called a “reducer". + + .. grid-item-card:: :octicon:`broadcast` Deeply Sharing State with Contexts + :link: deeply-sharing-state-with-contexts/index + :link-type: doc + + Instead of passing shared state down deep component trees, bring state into + "contexts" instead. + + .. grid-item-card:: :octicon:`rocket` Combining Contexts and Reducers + :link: combining-contexts-and-reducers/index + :link-type: doc + + You can combine reducers and context together to manage state of a complex + screen. + + +Section 1: How to Structure State +--------------------------------- + +.. note:: + + Under construction 🚧 + + +Section 2: Shared Component State +--------------------------------- + +Sometimes, you want the state of two components to always change together. To do it, +remove state from both of them, move it to their closest common parent, and then pass it +down to them via props. This is known as “lifting state up”, and it’s one of the most +common things you will do writing code with ReactPy. + +In the example below the search input and the list of elements below share the same +state, the state represents the food name. Note how the component ``Table`` gets called +at each change of state. The component is observing the state and reacting to state +changes automatically, just like it would do in React. + +.. reactpy:: sharing-component-state/_examples/synced_inputs + +.. card:: + :link: sharing-component-state/index + :link-type: doc + + :octicon:`book` Read More + ^^^^^^^^^^^^^^^^^^^^^^^^^ + + Allow components to vary vary together, by lifting state into common parents. + + +Section 3: When and How to Reset State +-------------------------------------- + +.. note:: + + Under construction 🚧 + + +Section 4: Simplifying Updates with Reducers +-------------------------------------------- + +.. note:: + + Under construction 🚧 + + +Section 5: Deeply Sharing State with Contexts +--------------------------------------------- + +.. note:: + + Under construction 🚧 + + + +Section 6: Combining Contexts and Reducers +------------------------------------------ + +.. note:: + + Under construction 🚧 diff --git a/docs/source/guides/managing-state/sharing-component-state/_examples/filterable_list/data.json b/docs/source/guides/managing-state/sharing-component-state/_examples/filterable_list/data.json new file mode 100644 index 000000000..f977fe9a7 --- /dev/null +++ b/docs/source/guides/managing-state/sharing-component-state/_examples/filterable_list/data.json @@ -0,0 +1,22 @@ +[ + { + "name": "Sushi", + "description": "Sushi is a traditional Japanese dish of prepared vinegared rice" + }, + { + "name": "Dal", + "description": "The most common way of preparing dal is in the form of a soup to which onions, tomatoes and various spices may be added" + }, + { + "name": "Pierogi", + "description": "Pierogi are filled dumplings made by wrapping unleavened dough around a savoury or sweet filling and cooking in boiling water" + }, + { + "name": "Shish Kebab", + "description": "Shish kebab is a popular meal of skewered and grilled cubes of meat" + }, + { + "name": "Dim sum", + "description": "Dim sum is a large range of small dishes that Cantonese people traditionally enjoy in restaurants for breakfast and lunch" + } +] diff --git a/docs/source/guides/managing-state/sharing-component-state/_examples/filterable_list/main.py b/docs/source/guides/managing-state/sharing-component-state/_examples/filterable_list/main.py new file mode 100644 index 000000000..ca68aedcb --- /dev/null +++ b/docs/source/guides/managing-state/sharing-component-state/_examples/filterable_list/main.py @@ -0,0 +1,44 @@ +import json +from pathlib import Path + +from reactpy import component, hooks, html, run + +HERE = Path(__file__) +DATA_PATH = HERE.parent / "data.json" +food_data = json.loads(DATA_PATH.read_text()) + + +@component +def FilterableList(): + value, set_value = hooks.use_state("") + return html.p(Search(value, set_value), html.hr(), Table(value, set_value)) + + +@component +def Search(value, set_value): + def handle_change(event): + set_value(event["target"]["value"]) + + return html.label( + "Search by Food Name: ", + html.input({"value": value, "on_change": handle_change}), + ) + + +@component +def Table(value, set_value): + rows = [] + for row in food_data: + name = html.td(row["name"]) + descr = html.td(row["description"]) + tr = html.tr(name, descr, value) + if not value: + rows.append(tr) + elif value.lower() in row["name"].lower(): + rows.append(tr) + headers = html.tr(html.td(html.b("name")), html.td(html.b("description"))) + table = html.table(html.thead(headers), html.tbody(rows)) + return table + + +run(FilterableList) diff --git a/docs/source/guides/managing-state/sharing-component-state/_examples/synced_inputs/main.py b/docs/source/guides/managing-state/sharing-component-state/_examples/synced_inputs/main.py new file mode 100644 index 000000000..e8bcdf333 --- /dev/null +++ b/docs/source/guides/managing-state/sharing-component-state/_examples/synced_inputs/main.py @@ -0,0 +1,23 @@ +from reactpy import component, hooks, html, run + + +@component +def SyncedInputs(): + value, set_value = hooks.use_state("") + return html.p( + Input("First input", value, set_value), + Input("Second input", value, set_value), + ) + + +@component +def Input(label, value, set_value): + def handle_change(event): + set_value(event["target"]["value"]) + + return html.label( + label + " ", html.input({"value": value, "on_change": handle_change}) + ) + + +run(SyncedInputs) diff --git a/docs/source/guides/managing-state/sharing-component-state/index.rst b/docs/source/guides/managing-state/sharing-component-state/index.rst new file mode 100644 index 000000000..54b61335a --- /dev/null +++ b/docs/source/guides/managing-state/sharing-component-state/index.rst @@ -0,0 +1,38 @@ +Sharing Component State +======================= + +.. note:: + + Parts of this document are still under construction 🚧 + +Sometimes, you want the state of two components to always change together. To do it, +remove state from both of them, move it to their closest common parent, and then pass it +down to them via props. This is known as “lifting state up”, and it’s one of the most +common things you will do writing code with ReactPy. + + +Synced Inputs +------------- + +In the code below the two input boxes are synchronized, this happens because they share +state. The state is shared via the parent component ``SyncedInputs``. Check the ``value`` +and ``set_value`` variables. + +.. reactpy:: _examples/synced_inputs + + +Filterable List +---------------- + +In the example below the search input and the list of elements below share the +same state, the state represents the food name. + +Note how the component ``Table`` gets called at each change of state. The +component is observing the state and reacting to state changes automatically, +just like it would do in React. + +.. reactpy:: _examples/filterable_list + +.. note:: + + Try typing a food name in the search bar. diff --git a/docs/source/guides/managing-state/simplifying-updates-with-reducers/index.rst b/docs/source/guides/managing-state/simplifying-updates-with-reducers/index.rst new file mode 100644 index 000000000..08fce5a69 --- /dev/null +++ b/docs/source/guides/managing-state/simplifying-updates-with-reducers/index.rst @@ -0,0 +1,6 @@ +Simplifying Updates with Reducers 🚧 +==================================== + +.. note:: + + Under construction 🚧 diff --git a/docs/source/managing-state/when-to-reset-state.rst b/docs/source/guides/managing-state/when-and-how-to-reset-state/index.rst similarity index 50% rename from docs/source/managing-state/when-to-reset-state.rst rename to docs/source/guides/managing-state/when-and-how-to-reset-state/index.rst index 2a0b8c3ae..6a96f4b30 100644 --- a/docs/source/managing-state/when-to-reset-state.rst +++ b/docs/source/guides/managing-state/when-and-how-to-reset-state/index.rst @@ -1,7 +1,7 @@ .. _When to Reset State: -When to Reset State 🚧 -====================== +When and How to Reset State 🚧 +============================== .. note:: diff --git a/docs/source/understanding-idom/_static/idom-flow-diagram.svg b/docs/source/guides/understanding-reactpy/_static/idom-flow-diagram.svg similarity index 99% rename from docs/source/understanding-idom/_static/idom-flow-diagram.svg rename to docs/source/guides/understanding-reactpy/_static/idom-flow-diagram.svg index 27c78b0b2..9077913ca 100644 --- a/docs/source/understanding-idom/_static/idom-flow-diagram.svg +++ b/docs/source/guides/understanding-reactpy/_static/idom-flow-diagram.svg @@ -13,7 +13,7 @@ viewBox="-0.5 -0.5 680 580" content="<mxfile host="app.diagrams.net" modified="2020-09-07T18:34:20.858Z" agent="5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36" etag="IvUE9xI9CxZQnD7O0sJm" version="13.6.6" type="device"><diagram id="3GUrj3vU2Wc3lj3yRkW7" name="Page-1">7Zpdb9owFIZ/DZetkrgEuCy027R1XSWkddqdiU8Sqw5mjvnar5+d2CEhFEpbPkZBlUqO7WP7+D0PdpIG6iWzzwKP4u+cAGt4Dpk10E3D89wrz2voP4fMc0sLdXJDJCgxlRaGPv0Lxtg01jElkFYqSs6ZpKOqMeDDIQSyYsNC8Gm1WshZtdcRjqBm6AeY1a2PlMg4t7abzsL+BWgU255dx5Qk2FY2hjTGhE9LJnTbQD3Bucy/JbMeMB08G5e83adnSouBCRjKlzTAv+WFQ760viXebfT49/4J/cIXfu5lgtnYTLjh+Uz564ZcuVUBw0Fe4P8Z65F2e3wsKAhVdA/ThVlPUM5Zta72cZFma3qtKqjRzsot/Mj8zzocWMMdnvOxtGY1ocFyVWXLh2fNXqV7T/DxkICet6uKpzGV0B/lE5kqmSpbLBNmikPKWI8zLrK2iGBoh4Gyp1LwJyiV+EEbBmFRYuWAihFMQEiYPbs8brHoKluAJyDFXFUxDTzX6MQkim8upwvVFckUlxRn62Ej9KjwvNCC+mLksIU0WkcojRuajrAMYt3Jx5ZHcX0wfbSPUB+PoL46fRCTs0LQwQnSOUKF9BiF4dofl4H4iGppHpwn7iqgLEUahuRab+rUVcBwmtKgGtxqqJr1cINLmtBaFe6O30LYL4ILpLYv3BjaUujs/rAcOWsTwLCkk6r7VeE0PTxwmmXLbGml5naVllYkVUkUgGlV3hBucFRcW0cSiwhkzVG2usW037DgG/mwddJvQZQ8l7/2f9yrsge9qXhpumtp3OGBOuhUtIcZjYZamEomqkPU1flJ1Uni2hQklBDtoytAzQAPMn+Ouh7pCGcxb3YbzRtlYdp9FwdPUcaWkkw/ZZ91DDAnIeN/cf4oC3hNAj5LDOfSayNUUYz7NkEbLxduq9qEh2EKO5GcHdyZMdsxxmu9E2PQ8s/Hjhlj1/fMmNcwRg/R3JLxnJ0wZ8MuZYfMQXtjDjoz5zXMcd9rX+PteV/jXR2eOT9vfnw/s2ZVIp42a1Ztqd+bNWEIfrDyyEpanYHj/AesQWgDIl7KmmVHNWjtmDW2+8Pub1TJ7aR6W+VMnCId90+c/QEH7eNAdYLAqZ2DXguc2sls18A5igPVGTjr0vGkgbOP09QJAqd2c/e1wKndbt41cI7gNHUGzvp0PGXgXG3cYW+rpkcYpDx4gkJM2z9xzNb+gadUUq5lI3J+FPpiEMqVoEtjPNI+klmk39e6TNRAxqPLBAv9LxgLNu+KbD4VYNpXl7SICE7j7GGns/J2U/bR1ahQisqHp1ijR/Cukn35o07fvku2TLANJO5s/6RTXS5e6MoVuHgtDt3+Aw==</diagram></mxfile>" id="svg4818" - sodipodi:docname="idom-flow-diagram.svg" + sodipodi:docname="reactpy-flow-diagram.svg" inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"> diff --git a/docs/source/understanding-idom/_static/live-examples-in-docs.gif b/docs/source/guides/understanding-reactpy/_static/live-examples-in-docs.gif similarity index 100% rename from docs/source/understanding-idom/_static/live-examples-in-docs.gif rename to docs/source/guides/understanding-reactpy/_static/live-examples-in-docs.gif diff --git a/docs/source/understanding-idom/_static/mvc-flow-diagram.svg b/docs/source/guides/understanding-reactpy/_static/mvc-flow-diagram.svg similarity index 100% rename from docs/source/understanding-idom/_static/mvc-flow-diagram.svg rename to docs/source/guides/understanding-reactpy/_static/mvc-flow-diagram.svg diff --git a/docs/source/understanding-idom/_static/npm-download-trends.png b/docs/source/guides/understanding-reactpy/_static/npm-download-trends.png similarity index 100% rename from docs/source/understanding-idom/_static/npm-download-trends.png rename to docs/source/guides/understanding-reactpy/_static/npm-download-trends.png diff --git a/docs/source/understanding-idom/index.rst b/docs/source/guides/understanding-reactpy/index.rst similarity index 54% rename from docs/source/understanding-idom/index.rst rename to docs/source/guides/understanding-reactpy/index.rst index 5c1b94231..3e0b2ab72 100644 --- a/docs/source/understanding-idom/index.rst +++ b/docs/source/guides/understanding-reactpy/index.rst @@ -1,5 +1,5 @@ -Understanding IDOM -================== +Understanding ReactPy +===================== .. toctree:: :hidden: @@ -7,6 +7,11 @@ Understanding IDOM representing-html what-are-components the-rendering-pipeline - why-idom-needs-keys + why-reactpy-needs-keys the-rendering-process layout-render-servers + writing-tests + +.. note:: + + Under construction 🚧 diff --git a/docs/source/understanding-idom/layout-render-servers.rst b/docs/source/guides/understanding-reactpy/layout-render-servers.rst similarity index 100% rename from docs/source/understanding-idom/layout-render-servers.rst rename to docs/source/guides/understanding-reactpy/layout-render-servers.rst diff --git a/docs/source/understanding-idom/representing-html.rst b/docs/source/guides/understanding-reactpy/representing-html.rst similarity index 89% rename from docs/source/understanding-idom/representing-html.rst rename to docs/source/guides/understanding-reactpy/representing-html.rst index ffae0d331..c2f32ebd9 100644 --- a/docs/source/understanding-idom/representing-html.rst +++ b/docs/source/guides/understanding-reactpy/representing-html.rst @@ -7,10 +7,10 @@ Representing HTML 🚧 Under construction 🚧 -We've already discussed how to contruct HTML with IDOM in a :ref:`previous section `, but we skimmed over the question of the data structure we use to represent +We've already discussed how to construct HTML with ReactPy in a :ref:`previous section `, but we skimmed over the question of the data structure we use to represent it. Let's reconsider the examples from before - on the top is some HTML and on the -bottom is the corresponding code to create it in IDOM: +bottom is the corresponding code to create it in ReactPy: .. code-block:: html @@ -24,7 +24,7 @@ bottom is the corresponding code to create it in IDOM: .. testcode:: - from idom import html + from reactpy import html layout = html.div( html.h1("My Todo List"), diff --git a/docs/source/understanding-idom/the-rendering-pipeline.rst b/docs/source/guides/understanding-reactpy/the-rendering-pipeline.rst similarity index 100% rename from docs/source/understanding-idom/the-rendering-pipeline.rst rename to docs/source/guides/understanding-reactpy/the-rendering-pipeline.rst diff --git a/docs/source/understanding-idom/the-rendering-process.rst b/docs/source/guides/understanding-reactpy/the-rendering-process.rst similarity index 100% rename from docs/source/understanding-idom/the-rendering-process.rst rename to docs/source/guides/understanding-reactpy/the-rendering-process.rst diff --git a/docs/source/understanding-idom/what-are-components.rst b/docs/source/guides/understanding-reactpy/what-are-components.rst similarity index 100% rename from docs/source/understanding-idom/what-are-components.rst rename to docs/source/guides/understanding-reactpy/what-are-components.rst diff --git a/docs/source/managing-state/shared-component-state.rst b/docs/source/guides/understanding-reactpy/why-reactpy-needs-keys.rst similarity index 54% rename from docs/source/managing-state/shared-component-state.rst rename to docs/source/guides/understanding-reactpy/why-reactpy-needs-keys.rst index 3c9f66617..e570b8f41 100644 --- a/docs/source/managing-state/shared-component-state.rst +++ b/docs/source/guides/understanding-reactpy/why-reactpy-needs-keys.rst @@ -1,6 +1,6 @@ -.. _Shared Component State: +.. _Why ReactPy Needs Keys: -Shared Component State 🚧 +Why ReactPy Needs Keys 🚧 ========================= .. note:: diff --git a/docs/source/managing-state/writing-tests.rst b/docs/source/guides/understanding-reactpy/writing-tests.rst similarity index 100% rename from docs/source/managing-state/writing-tests.rst rename to docs/source/guides/understanding-reactpy/writing-tests.rst diff --git a/docs/source/index.rst b/docs/source/index.rst index e7ecbc18d..8b21160f6 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -1,80 +1,81 @@ .. card:: This documentation is still under construction 🚧. We welcome your `feedback - `__! + `__! -What is IDOM? -============= +ReactPy +======= .. toctree:: :hidden: - :caption: User Guide + :caption: Guides - getting-started/index - creating-interfaces/index - adding-interactivity/index - managing-state/index - escape-hatches/index - understanding-idom/index + guides/getting-started/index + guides/creating-interfaces/index + guides/adding-interactivity/index + guides/managing-state/index + guides/escape-hatches/index + guides/understanding-reactpy/index .. toctree:: :hidden: - :caption: Other Resources + :caption: Reference - developing-idom/index - reference-material/index - credits-and-licenses + reference/browser-events + reference/html-attributes + reference/hooks-api + _auto/apis + reference/javascript-api + reference/specifications .. toctree:: :hidden: - :caption: External Links + :caption: About - Source Code - Community - Issues + about/changelog + about/contributor-guide + about/credits-and-licenses + Source Code + Community - -IDOM is a Python web framework for building **interactive websites without needing a -single line of Javascript**. This is accomplished by breaking down complex applications -into nestable and reusable chunks of code called :ref:`"components" ` that allow you to focus on what your application does rather than how it -does it. - -Ecosystem independence is also a core feature of IDOM. It can be added to existing -applications built on a variety of sync and async web servers, as well as integrated -with other frameworks like Django, Jupyter, and Plotly Dash. Not only does this mean -you're free to choose what technology stack to run on, but on top of that, you can run -the exact same components wherever you need them. For example, you can take a component -originally developed in a Jupyter Notebook and embed it in your production application -without changing anything about the component itself. +ReactPy is a library for building user interfaces in Python without Javascript. ReactPy +interfaces are made from :ref:`components ` which look and behave +similarly to those found in `ReactJS `__. Designed with simplicity +in mind, ReactPy can be used by those without web development experience while also +being powerful enough to grow with your ambitions. At a Glance ----------- -To get a rough idea of how to write apps in IDOM, take a look at the tiny `"hello world" +To get a rough idea of how to write apps in ReactPy, take a look at the tiny `"hello world" `__ application below: -.. idom:: getting-started/_examples/hello_world +.. reactpy:: guides/getting-started/_examples/hello_world .. hint:: - Try clicking the **â–ļī¸ result** tab to see what this displays! + Try clicking the **🚀 result** tab to see what this displays! -So what exactly does this code do? First, it imports a few tools from ``idom`` that will +So what exactly does this code do? First, it imports a few tools from ``reactpy`` that will get used to describe and execute an application. Then, we create an ``App`` function which will define the content the application displays. Specifically, it displays a kind of HTML element called an ``h1`` `section heading `__. Importantly though, a ``@component`` decorator has been applied to the ``App`` function to turn it into a :ref:`component `. Finally, we :ref:`run -` an application server by passing the ``App`` component to the ``run()`` -function. +` a development web server by passing the ``App`` component to the +``run()`` function. + +.. note:: + + See :ref:`Running ReactPy in Production` to learn how to use a production-grade server + to run ReactPy. -Learning IDOM -------------- +Learning ReactPy +---------------- This documentation is broken up into chapters and sections that introduce you to concepts step by step with detailed explanations and lots of examples. You should feel @@ -88,45 +89,45 @@ Chapter 1 - :ref:`Getting Started` ----------------------------------- If you want to follow along with examples in the sections that follow, you'll want to -start here so you can :ref:`install IDOM `. This section also contains -more detailed information about how to :ref:`run IDOM ` in different -contexts. For example, if you want to embed IDOM into an existing application, or run -IDOM within a Jupyter Notebook, this is where you can learn how to do those things. +start here so you can :ref:`install ReactPy `. This section also contains +more detailed information about how to :ref:`run ReactPy ` in different +contexts. For example, if you want to embed ReactPy into an existing application, or run +ReactPy within a Jupyter Notebook, this is where you can learn how to do those things. .. grid:: 1 2 2 2 .. grid-item:: - .. image:: _static/install-and-run-idom.gif + .. image:: _static/install-and-run-reactpy.gif .. grid-item:: - .. image:: getting-started/_static/idom-in-jupyterlab.gif + .. image:: guides/getting-started/_static/reactpy-in-jupyterlab.gif .. card:: - :link: getting-started/index + :link: guides/getting-started/index :link-type: doc :octicon:`book` Read More ^^^^^^^^^^^^^^^^^^^^^^^^^ - Install IDOM and run it in a variety of different ways - with different web servers - and frameworks. You'll even embed IDOM into an existing app. + Install ReactPy and run it in a variety of different ways - with different web servers + and frameworks. You'll even embed ReactPy into an existing app. Chapter 2 - :ref:`Creating Interfaces` -------------------------------------- -IDOM is a Python package for making user interfaces (UI). These interfaces are built -from small elements of functionality like buttons text and images. IDOM allows you to +ReactPy is a Python package for making user interfaces (UI). These interfaces are built +from small elements of functionality like buttons text and images. ReactPy allows you to combine these elements into reusable :ref:`"components" `. In the sections that follow you'll learn how these UI elements are created and organized into components. Then, you'll use this knowledge to create interfaces from raw data: -.. idom:: creating-interfaces/_examples/todo_list_with_keys +.. reactpy:: guides/creating-interfaces/rendering-data/_examples/todo_list_with_keys .. card:: - :link: creating-interfaces/index + :link: guides/creating-interfaces/index :link-type: doc :octicon:`book` Read More @@ -142,20 +143,20 @@ Components often need to change what’s on the screen as a result of an interac example, typing into the form should update the input field, clicking a “Comment” button should bring up a text input field, clicking “Buy” should put a product in the shopping cart. Components need to “remember” things like the current input value, the current -image, the shopping cart. In IDOM, this kind of component-specific memory is created and +image, the shopping cart. In ReactPy, this kind of component-specific memory is created and updated with a "hook" called ``use_state()`` that creates a **state variable** and **state setter** respectively: -.. idom:: adding-interactivity/components-with-state/_examples/adding_state_variable +.. reactpy:: guides/adding-interactivity/components-with-state/_examples/adding_state_variable -In IDOM, ``use_state``, as well as any other function whose name starts with ``use``, is -called a "hook". These are special functions that should only be called while IDOM is +In ReactPy, ``use_state``, as well as any other function whose name starts with ``use``, is +called a "hook". These are special functions that should only be called while ReactPy is :ref:`rendering `. They let you "hook into" the different -capabilities of IDOM's components of which ``use_state`` is just one (well get into the +capabilities of ReactPy's components of which ``use_state`` is just one (well get into the other :ref:`later `). .. card:: - :link: adding-interactivity/index + :link: guides/adding-interactivity/index :link-type: doc :octicon:`book` Read More @@ -168,7 +169,7 @@ Chapter 4 - :ref:`Managing State` --------------------------------- .. card:: - :link: managing-state/index + :link: guides/managing-state/index :link-type: doc :octicon:`book` Read More @@ -182,7 +183,7 @@ Chapter 5 - :ref:`Escape Hatches` --------------------------------- .. card:: - :link: escape-hatches/index + :link: guides/escape-hatches/index :link-type: doc :octicon:`book` Read More @@ -191,11 +192,11 @@ Chapter 5 - :ref:`Escape Hatches` Under construction 🚧 -Chapter 6 - :ref:`Understanding IDOM` -------------------------------------- +Chapter 6 - :ref:`Understanding ReactPy` +---------------------------------------- .. card:: - :link: escape-hatches/index + :link: guides/escape-hatches/index :link-type: doc :octicon:`book` Read More diff --git a/docs/source/managing-state/index.rst b/docs/source/managing-state/index.rst deleted file mode 100644 index 4ef9850ac..000000000 --- a/docs/source/managing-state/index.rst +++ /dev/null @@ -1,14 +0,0 @@ -Managing State -============== - -.. toctree:: - :hidden: - - keeping-components-pure - logical-flow-of-state - structuring-your-state - shared-component-state - when-to-reset-state - writing-tests - -Under construction 🚧 diff --git a/docs/source/managing-state/keeping-components-pure.rst b/docs/source/managing-state/keeping-components-pure.rst deleted file mode 100644 index a2fc1a15b..000000000 --- a/docs/source/managing-state/keeping-components-pure.rst +++ /dev/null @@ -1,8 +0,0 @@ -.. _Keeping Components Pure: - -Keeping Components Pure 🚧 -========================== - -.. note:: - - Under construction 🚧 diff --git a/docs/source/managing-state/logical-flow-of-state.rst b/docs/source/managing-state/logical-flow-of-state.rst deleted file mode 100644 index 53bf0cff9..000000000 --- a/docs/source/managing-state/logical-flow-of-state.rst +++ /dev/null @@ -1,8 +0,0 @@ -.. _Logical Flow of State: - -Logical Flow of State 🚧 -======================== - -.. note:: - - Under construction 🚧 diff --git a/docs/source/reference-material/_examples/click_count.py b/docs/source/reference-material/_examples/click_count.py deleted file mode 100644 index 6f30ce517..000000000 --- a/docs/source/reference-material/_examples/click_count.py +++ /dev/null @@ -1,14 +0,0 @@ -import idom - - -@idom.component -def ClickCount(): - count, set_count = idom.hooks.use_state(0) - - return idom.html.button( - {"onClick": lambda event: set_count(count + 1)}, - [f"Click count: {count}"], - ) - - -idom.run(ClickCount) diff --git a/docs/source/reference-material/_examples/material_ui_switch.py b/docs/source/reference-material/_examples/material_ui_switch.py deleted file mode 100644 index 9e4363b74..000000000 --- a/docs/source/reference-material/_examples/material_ui_switch.py +++ /dev/null @@ -1,23 +0,0 @@ -import idom - - -mui = idom.web.module_from_template("react", "@material-ui/core@^5.0", fallback="⌛") -Switch = idom.web.export(mui, "Switch") - - -@idom.component -def DayNightSwitch(): - checked, set_checked = idom.hooks.use_state(False) - - return idom.html.div( - Switch( - { - "checked": checked, - "onChange": lambda event, checked: set_checked(checked), - } - ), - "🌞" if checked else "🌚", - ) - - -idom.run(DayNightSwitch) diff --git a/docs/source/reference-material/_examples/todo.py b/docs/source/reference-material/_examples/todo.py deleted file mode 100644 index 7b1f6f675..000000000 --- a/docs/source/reference-material/_examples/todo.py +++ /dev/null @@ -1,33 +0,0 @@ -import idom - - -@idom.component -def Todo(): - items, set_items = idom.hooks.use_state([]) - - async def add_new_task(event): - if event["key"] == "Enter": - set_items(items + [event["target"]["value"]]) - - tasks = [] - - for index, text in enumerate(items): - - async def remove_task(event, index=index): - set_items(items[:index] + items[index + 1 :]) - - task_text = idom.html.td(idom.html.p(text)) - delete_button = idom.html.td({"onClick": remove_task}, idom.html.button(["x"])) - tasks.append(idom.html.tr(task_text, delete_button)) - - task_input = idom.html.input({"onKeyDown": add_new_task}) - task_table = idom.html.table(tasks) - - return idom.html.div( - idom.html.p("press enter to add a task:"), - task_input, - task_table, - ) - - -idom.run(Todo) diff --git a/docs/source/reference-material/_examples/use_reducer_counter.py b/docs/source/reference-material/_examples/use_reducer_counter.py deleted file mode 100644 index ea1b780a0..000000000 --- a/docs/source/reference-material/_examples/use_reducer_counter.py +++ /dev/null @@ -1,26 +0,0 @@ -import idom - - -def reducer(count, action): - if action == "increment": - return count + 1 - elif action == "decrement": - return count - 1 - elif action == "reset": - return 0 - else: - raise ValueError(f"Unknown action '{action}'") - - -@idom.component -def Counter(): - count, dispatch = idom.hooks.use_reducer(reducer, 0) - return idom.html.div( - f"Count: {count}", - idom.html.button({"onClick": lambda event: dispatch("reset")}, "Reset"), - idom.html.button({"onClick": lambda event: dispatch("increment")}, "+"), - idom.html.button({"onClick": lambda event: dispatch("decrement")}, "-"), - ) - - -idom.run(Counter) diff --git a/docs/source/reference-material/_examples/use_state_counter.py b/docs/source/reference-material/_examples/use_state_counter.py deleted file mode 100644 index 8626a60b9..000000000 --- a/docs/source/reference-material/_examples/use_state_counter.py +++ /dev/null @@ -1,24 +0,0 @@ -import idom - - -def increment(last_count): - return last_count + 1 - - -def decrement(last_count): - return last_count - 1 - - -@idom.component -def Counter(): - initial_count = 0 - count, set_count = idom.hooks.use_state(initial_count) - return idom.html.div( - f"Count: {count}", - idom.html.button({"onClick": lambda event: set_count(initial_count)}, "Reset"), - idom.html.button({"onClick": lambda event: set_count(increment)}, "+"), - idom.html.button({"onClick": lambda event: set_count(decrement)}, "-"), - ) - - -idom.run(Counter) diff --git a/docs/source/reference-material/_examples/victory_chart.py b/docs/source/reference-material/_examples/victory_chart.py deleted file mode 100644 index e2c48d34e..000000000 --- a/docs/source/reference-material/_examples/victory_chart.py +++ /dev/null @@ -1,8 +0,0 @@ -import idom - - -victory = idom.web.module_from_template("react", "victory-bar", fallback="⌛") -VictoryBar = idom.web.export(victory, "VictoryBar") - -bar_style = {"parent": {"width": "500px"}, "data": {"fill": "royalblue"}} -idom.run(idom.component(lambda: VictoryBar({"style": bar_style}))) diff --git a/docs/source/reference-material/examples.rst b/docs/source/reference-material/examples.rst deleted file mode 100644 index 694864e53..000000000 --- a/docs/source/reference-material/examples.rst +++ /dev/null @@ -1,106 +0,0 @@ -Examples -======== - -Slideshow ---------- - -Try clicking the image đŸ–ąī¸ - -.. idom:: _examples/slideshow - :result-is-default-tab: - - -Click Counter -------------- - -.. idom:: _examples/click_count - :result-is-default-tab: - - -To Do List ----------- - -Try typing in the text box and pressing 'Enter' 📋 - -.. idom:: _examples/todo - :result-is-default-tab: - - -Simple Image Movement ---------------------- - -.. idom:: _examples/character_movement - :result-is-default-tab: - - -The Game Snake --------------- - -Click to start playing and use the arrow keys to move 🎮 - -Slow internet may cause inconsistent frame pacing 😅 - -.. idom:: _examples/snake_game - :result-is-default-tab: - - -Matplotlib Plot ---------------- - -Pick the polynomial coefficients (separate each coefficient by a space) đŸ”ĸ: - -.. idom:: _examples/matplotlib_plot - :result-is-default-tab: - - -Simple Dashboard ----------------- - -Try interacting with the sliders 📈 - -.. idom:: _examples/simple_dashboard - :result-is-default-tab: - - -Dynamically Loaded React Components ------------------------------------ - -This method is not recommended for use in production applications, but it's great while -you're experimenting: - -.. idom:: _examples/victory_chart - :result-is-default-tab: - - -Material UI Button ------------------- - -Click the button to change the indicator 👇 - -.. idom:: _examples/material_ui_switch - :result-is-default-tab: - - -Pigeon Maps ------------ - -Click the map to create pinned location 📍: - -.. idom:: _examples/pigeon_maps - :result-is-default-tab: - - -Cytoscape Network Graph ------------------------ - -You can move the nodes in the graph đŸ•¸ī¸: - -.. idom:: _examples/network_graph - :result-is-default-tab: - - -.. Links -.. ===== - -.. |launch-binder| image:: https://mybinder.org/badge_logo.svg - :target: https://mybinder.org/v2/gh/idom-team/idom-jupyter/main?filepath=examples%2Fintroduction.ipynb diff --git a/docs/source/reference-material/faq.rst b/docs/source/reference-material/faq.rst deleted file mode 100644 index df1628dcd..000000000 --- a/docs/source/reference-material/faq.rst +++ /dev/null @@ -1,64 +0,0 @@ -FAQ -=== - -See our :discussion-type:`Discussion Forum ` for more questions and answers. - - -Do UI components run client-side? ---------------------------------- - -No. The layout is constructed, and components are executed, server-side in Python. Only -rendering occurs client-side. This means you can access files, databases, and all your -favorite Python packages with IDOM. - - -Does IDOM transpile Python to Javascript? ------------------------------------------ - -No. As in the answer to :ref:`Do UI components run client-side?`, IDOM runs almost -everything server-side and in Python. This was an explicit design choice to keep things -simple and one which allows you to do everything you normally would in Python. - - -Does IDOM support any React component? --------------------------------------- - -If you use :ref:`Dynamically Loaded Components`, then the answer is no. Only components -whose props are JSON serializable, or which expect basic callback functions similar to -those of standard event handlers (e.g. ``onClick``) will operate as expected. - -However, if you import a :ref:`Custom Javascript Component ` -then, so long as the bundle has be defined appropriately, any component can be made to -work, even those that don't rely on React. - - -How does IDOM communicate with the client? ------------------------------------------- - -IDOM sends diffs of a Virtual Document Object Model (:ref:`VDOM`) to the -client. For more details, see the description in -`this article `__. - - -Can I use Javascript components from a CDN? -------------------------------------------- - -Yes, but with some restrictions: - -1. The Javascript in question must be distributed as an ECMAScript Module - (`ESM `__) -2. The module must export the :ref:`required interface `. - -These restrictions apply because the Javascript from the CDN must be able to run -natively in the browser and the module must be able to run in isolation from the main -application. - -See :ref:`Distributing Javascript via CDN_` for more info. - - -What props can I pass to Javascript components? ------------------------------------------------ - -You can only pass JSON serializable props to components implemented in Javascript. It is -possible to create a :ref:`Custom Javascript Component ` -which undestands how to deserialise JSON data into native Javascript objects though. diff --git a/docs/source/reference-material/index.rst b/docs/source/reference-material/index.rst deleted file mode 100644 index fb4021aca..000000000 --- a/docs/source/reference-material/index.rst +++ /dev/null @@ -1,17 +0,0 @@ -Reference Material -================== - -.. toctree:: - :hidden: - - examples - hooks-api - /_auto/apis - javascript-api - browser-events - specifications - faq - -.. note:: - - Under construction 🚧 diff --git a/docs/source/reference-material/_examples/character_movement/app.py b/docs/source/reference/_examples/character_movement/main.py similarity index 64% rename from docs/source/reference-material/_examples/character_movement/app.py rename to docs/source/reference/_examples/character_movement/main.py index fbf257a32..9545b0c0a 100644 --- a/docs/source/reference-material/_examples/character_movement/app.py +++ b/docs/source/reference/_examples/character_movement/main.py @@ -1,9 +1,8 @@ from pathlib import Path from typing import NamedTuple -from idom import component, html, run, use_state -from idom.widgets import image - +from reactpy import component, html, run, use_state +from reactpy.widgets import image HERE = Path(__file__) CHARACTER_IMAGE = (HERE.parent / "static" / "bunny.png").read_bytes() @@ -42,7 +41,7 @@ def Scene(): "style": { "width": "200px", "height": "200px", - "backgroundColor": "slategray", + "background_color": "slategray", } }, image( @@ -58,12 +57,16 @@ def Scene(): }, ), ), - html.button({"onClick": lambda e: set_position(translate(x=-10))}, "Move Left"), - html.button({"onClick": lambda e: set_position(translate(x=10))}, "Move Right"), - html.button({"onClick": lambda e: set_position(translate(y=-10))}, "Move Up"), - html.button({"onClick": lambda e: set_position(translate(y=10))}, "Move Down"), - html.button({"onClick": lambda e: set_position(rotate(-30))}, "Rotate Left"), - html.button({"onClick": lambda e: set_position(rotate(30))}, "Rotate Right"), + html.button( + {"on_click": lambda e: set_position(translate(x=-10))}, "Move Left" + ), + html.button( + {"on_click": lambda e: set_position(translate(x=10))}, "Move Right" + ), + html.button({"on_click": lambda e: set_position(translate(y=-10))}, "Move Up"), + html.button({"on_click": lambda e: set_position(translate(y=10))}, "Move Down"), + html.button({"on_click": lambda e: set_position(rotate(-30))}, "Rotate Left"), + html.button({"on_click": lambda e: set_position(rotate(30))}, "Rotate Right"), ) diff --git a/docs/source/reference-material/_examples/character_movement/static/bunny.png b/docs/source/reference/_examples/character_movement/static/bunny.png similarity index 100% rename from docs/source/reference-material/_examples/character_movement/static/bunny.png rename to docs/source/reference/_examples/character_movement/static/bunny.png diff --git a/docs/source/reference/_examples/click_count.py b/docs/source/reference/_examples/click_count.py new file mode 100644 index 000000000..3ee2c89c5 --- /dev/null +++ b/docs/source/reference/_examples/click_count.py @@ -0,0 +1,13 @@ +import reactpy + + +@reactpy.component +def ClickCount(): + count, set_count = reactpy.hooks.use_state(0) + + return reactpy.html.button( + {"on_click": lambda event: set_count(count + 1)}, [f"Click count: {count}"] + ) + + +reactpy.run(ClickCount) diff --git a/docs/source/reference/_examples/material_ui_switch.py b/docs/source/reference/_examples/material_ui_switch.py new file mode 100644 index 000000000..704ae3145 --- /dev/null +++ b/docs/source/reference/_examples/material_ui_switch.py @@ -0,0 +1,22 @@ +import reactpy + +mui = reactpy.web.module_from_template("react", "@material-ui/core@^5.0", fallback="⌛") +Switch = reactpy.web.export(mui, "Switch") + + +@reactpy.component +def DayNightSwitch(): + checked, set_checked = reactpy.hooks.use_state(False) + + return reactpy.html.div( + Switch( + { + "checked": checked, + "onChange": lambda event, checked: set_checked(checked), + } + ), + "🌞" if checked else "🌚", + ) + + +reactpy.run(DayNightSwitch) diff --git a/docs/source/reference-material/_examples/matplotlib_plot.py b/docs/source/reference/_examples/matplotlib_plot.py similarity index 62% rename from docs/source/reference-material/_examples/matplotlib_plot.py rename to docs/source/reference/_examples/matplotlib_plot.py index 6dffb79db..5c4d616fe 100644 --- a/docs/source/reference-material/_examples/matplotlib_plot.py +++ b/docs/source/reference/_examples/matplotlib_plot.py @@ -2,24 +2,24 @@ import matplotlib.pyplot as plt -import idom -from idom.widgets import image +import reactpy +from reactpy.widgets import image -@idom.component +@reactpy.component def PolynomialPlot(): - coefficients, set_coefficients = idom.hooks.use_state([0]) + coefficients, set_coefficients = reactpy.hooks.use_state([0]) - x = [n for n in linspace(-1, 1, 50)] + x = list(linspace(-1, 1, 50)) y = [polynomial(value, coefficients) for value in x] - return idom.html.div( + return reactpy.html.div( plot(f"{len(coefficients)} Term Polynomial", x, y), ExpandableNumberInputs(coefficients, set_coefficients), ) -@idom.component +@reactpy.component def ExpandableNumberInputs(values, set_values): inputs = [] for i in range(len(values)): @@ -31,16 +31,16 @@ def set_value_at_index(event, index=i): inputs.append(poly_coef_input(i + 1, set_value_at_index)) def add_input(): - set_values(values + [0]) + set_values([*values, 0]) def del_input(): set_values(values[:-1]) - return idom.html.div( - idom.html.div( + return reactpy.html.div( + reactpy.html.div( "add/remove term:", - idom.html.button({"onClick": lambda event: add_input()}, "+"), - idom.html.button({"onClick": lambda event: del_input()}, "-"), + reactpy.html.button({"on_click": lambda event: add_input()}, "+"), + reactpy.html.button({"on_click": lambda event: del_input()}, "-"), ), inputs, ) @@ -57,21 +57,15 @@ def plot(title, x, y): def poly_coef_input(index, callback): - return idom.html.div( - {"style": {"margin-top": "5px"}}, - idom.html.label( + return reactpy.html.div( + {"style": {"margin-top": "5px"}, "key": index}, + reactpy.html.label( "C", - idom.html.sub(index), - " × X", - idom.html.sup(index), + reactpy.html.sub(index), + " x X", + reactpy.html.sup(index), ), - idom.html.input( - { - "type": "number", - "onChange": callback, - }, - ), - key=index, + reactpy.html.input({"type": "number", "on_change": callback}), ) @@ -88,4 +82,4 @@ def linspace(start, stop, n): yield start + h * i -idom.run(PolynomialPlot) +reactpy.run(PolynomialPlot) diff --git a/docs/source/reference-material/_examples/network_graph.py b/docs/source/reference/_examples/network_graph.py similarity index 72% rename from docs/source/reference-material/_examples/network_graph.py rename to docs/source/reference/_examples/network_graph.py index 3dfb9ae87..79b1092f3 100644 --- a/docs/source/reference-material/_examples/network_graph.py +++ b/docs/source/reference/_examples/network_graph.py @@ -1,19 +1,16 @@ import random -import idom +import reactpy - -react_cytoscapejs = idom.web.module_from_template( - # we need to use this template because react-cytoscapejs uses a default export +react_cytoscapejs = reactpy.web.module_from_template( "react", "react-cytoscapejs", - exports_default=True, fallback="⌛", ) -Cytoscape = idom.web.export(react_cytoscapejs, "default") +Cytoscape = reactpy.web.export(react_cytoscapejs, "default") -@idom.component +@reactpy.component def RandomNetworkGraph(): return Cytoscape( { @@ -40,4 +37,4 @@ def random_network(number_of_nodes): return nodes + conns -idom.run(RandomNetworkGraph) +reactpy.run(RandomNetworkGraph) diff --git a/docs/source/reference-material/_examples/pigeon_maps.py b/docs/source/reference/_examples/pigeon_maps.py similarity index 52% rename from docs/source/reference-material/_examples/pigeon_maps.py rename to docs/source/reference/_examples/pigeon_maps.py index ab0157f73..1ddf04fdc 100644 --- a/docs/source/reference-material/_examples/pigeon_maps.py +++ b/docs/source/reference/_examples/pigeon_maps.py @@ -1,26 +1,23 @@ -import idom +import reactpy +pigeon_maps = reactpy.web.module_from_template("react", "pigeon-maps", fallback="⌛") +Map, Marker = reactpy.web.export(pigeon_maps, ["Map", "Marker"]) -pigeon_maps = idom.web.module_from_template("react", "pigeon-maps", fallback="⌛") -Map, Marker = idom.web.export(pigeon_maps, ["Map", "Marker"]) - -@idom.component +@reactpy.component def MapWithMarkers(): marker_anchor, add_marker_anchor, remove_marker_anchor = use_set() - markers = list( - map( - lambda anchor: Marker( - { - "anchor": anchor, - "onClick": lambda: remove_marker_anchor(anchor), - }, - key=str(anchor), - ), - marker_anchor, + markers = [ + Marker( + { + "anchor": anchor, + "onClick": lambda event, a=anchor: remove_marker_anchor(a), + }, + key=str(anchor), ) - ) + for anchor in marker_anchor + ] return Map( { @@ -35,7 +32,7 @@ def MapWithMarkers(): def use_set(initial_value=None): - values, set_values = idom.hooks.use_state(initial_value or set()) + values, set_values = reactpy.hooks.use_state(initial_value or set()) def add_value(lat_lon): set_values(values.union({lat_lon})) @@ -46,4 +43,4 @@ def remove_value(lat_lon): return values, add_value, remove_value -idom.run(MapWithMarkers) +reactpy.run(MapWithMarkers) diff --git a/docs/source/reference-material/_examples/simple_dashboard.py b/docs/source/reference/_examples/simple_dashboard.py similarity index 73% rename from docs/source/reference-material/_examples/simple_dashboard.py rename to docs/source/reference/_examples/simple_dashboard.py index 540082f58..66913fc84 100644 --- a/docs/source/reference-material/_examples/simple_dashboard.py +++ b/docs/source/reference/_examples/simple_dashboard.py @@ -2,28 +2,27 @@ import random import time -import idom -from idom.widgets import Input +import reactpy +from reactpy.widgets import Input - -victory = idom.web.module_from_template( +victory = reactpy.web.module_from_template( "react", "victory-line", fallback="⌛", # not usually required (see issue #461 for more info) unmount_before_update=True, ) -VictoryLine = idom.web.export(victory, "VictoryLine") +VictoryLine = reactpy.web.export(victory, "VictoryLine") -@idom.component +@reactpy.component def RandomWalk(): - mu = idom.hooks.use_ref(0) - sigma = idom.hooks.use_ref(1) + mu = reactpy.hooks.use_ref(0) + sigma = reactpy.hooks.use_ref(1) - return idom.html.div( + return reactpy.html.div( RandomWalkGraph(mu, sigma), - idom.html.style( + reactpy.html.style( """ .number-input-container {margin-bottom: 20px} .number-input-container input {width: 48%;float: left} @@ -45,12 +44,12 @@ def RandomWalk(): ) -@idom.component +@reactpy.component def RandomWalkGraph(mu, sigma): interval = use_interval(0.5) - data, set_data = idom.hooks.use_state([{"x": 0, "y": 0}] * 50) + data, set_data = reactpy.hooks.use_state([{"x": 0, "y": 0}] * 50) - @idom.hooks.use_effect + @reactpy.hooks.use_effect async def animate(): await interval last_data_point = data[-1] @@ -71,27 +70,27 @@ async def animate(): ) -@idom.component +@reactpy.component def NumberInput(label, value, set_value_callback, domain): minimum, maximum, step = domain attrs = {"min": minimum, "max": maximum, "step": step} - value, set_value = idom.hooks.use_state(value) + value, set_value = reactpy.hooks.use_state(value) def update_value(value): set_value(value) set_value_callback(value) - return idom.html.fieldset( - {"class": "number-input-container"}, - idom.html.legend({"style": {"font-size": "medium"}}, label), + return reactpy.html.fieldset( + {"class_name": "number-input-container"}, + reactpy.html.legend({"style": {"font-size": "medium"}}, label), Input(update_value, "number", value, attributes=attrs, cast=float), Input(update_value, "range", value, attributes=attrs, cast=float), ) def use_interval(rate): - usage_time = idom.hooks.use_ref(time.time()) + usage_time = reactpy.hooks.use_ref(time.time()) async def interval() -> None: await asyncio.sleep(rate - (time.time() - usage_time.current)) @@ -100,4 +99,4 @@ async def interval() -> None: return asyncio.ensure_future(interval()) -idom.run(RandomWalk) +reactpy.run(RandomWalk) diff --git a/docs/source/reference-material/_examples/slideshow.py b/docs/source/reference/_examples/slideshow.py similarity index 55% rename from docs/source/reference-material/_examples/slideshow.py rename to docs/source/reference/_examples/slideshow.py index 0d3116ac4..b490b3feb 100644 --- a/docs/source/reference-material/_examples/slideshow.py +++ b/docs/source/reference/_examples/slideshow.py @@ -1,20 +1,20 @@ -import idom +import reactpy -@idom.component +@reactpy.component def Slideshow(): - index, set_index = idom.hooks.use_state(0) + index, set_index = reactpy.hooks.use_state(0) def next_image(event): set_index(index + 1) - return idom.html.img( + return reactpy.html.img( { "src": f"https://picsum.photos/id/{index}/800/300", "style": {"cursor": "pointer"}, - "onClick": next_image, + "on_click": next_image, } ) -idom.run(Slideshow) +reactpy.run(Slideshow) diff --git a/docs/source/reference-material/_examples/snake_game.py b/docs/source/reference/_examples/snake_game.py similarity index 74% rename from docs/source/reference-material/_examples/snake_game.py rename to docs/source/reference/_examples/snake_game.py index 92fe054f0..36916410e 100644 --- a/docs/source/reference-material/_examples/snake_game.py +++ b/docs/source/reference/_examples/snake_game.py @@ -3,7 +3,7 @@ import random import time -import idom +import reactpy class GameState(enum.Enum): @@ -13,26 +13,25 @@ class GameState(enum.Enum): play = 3 -@idom.component +@reactpy.component def GameView(): - game_state, set_game_state = idom.hooks.use_state(GameState.init) + game_state, set_game_state = reactpy.hooks.use_state(GameState.init) if game_state == GameState.play: return GameLoop(grid_size=6, block_scale=50, set_game_state=set_game_state) - start_button = idom.html.button( - {"onClick": lambda event: set_game_state(GameState.play)}, - "Start", + start_button = reactpy.html.button( + {"on_click": lambda event: set_game_state(GameState.play)}, "Start" ) if game_state == GameState.won: - menu = idom.html.div(idom.html.h3("You won!"), start_button) + menu = reactpy.html.div(reactpy.html.h3("You won!"), start_button) elif game_state == GameState.lost: - menu = idom.html.div(idom.html.h3("You lost"), start_button) + menu = reactpy.html.div(reactpy.html.h3("You lost"), start_button) else: - menu = idom.html.div(idom.html.h3("Click to play"), start_button) + menu = reactpy.html.div(reactpy.html.h3("Click to play"), start_button) - menu_style = idom.html.style( + menu_style = reactpy.html.style( """ .snake-game-menu h3 { margin-top: 0px !important; @@ -40,7 +39,7 @@ def GameView(): """ ) - return idom.html.div({"className": "snake-game-menu"}, menu_style, menu) + return reactpy.html.div({"class_name": "snake-game-menu"}, menu_style, menu) class Direction(enum.Enum): @@ -50,19 +49,21 @@ class Direction(enum.Enum): ArrowRight = (1, 0) -@idom.component +@reactpy.component def GameLoop(grid_size, block_scale, set_game_state): # we `use_ref` here to capture the latest direction press without any delay - direction = idom.hooks.use_ref(Direction.ArrowRight.value) + direction = reactpy.hooks.use_ref(Direction.ArrowRight.value) # capture the last direction of travel that was rendered last_direction = direction.current - snake, set_snake = idom.hooks.use_state([(grid_size // 2 - 1, grid_size // 2 - 1)]) + snake, set_snake = reactpy.hooks.use_state( + [(grid_size // 2 - 1, grid_size // 2 - 1)] + ) food, set_food = use_snake_food(grid_size, snake) grid = create_grid(grid_size, block_scale) - @idom.event(prevent_default=True) + @reactpy.event(prevent_default=True) def on_direction_change(event): if hasattr(Direction, event["key"]): maybe_new_direction = Direction[event["key"]].value @@ -72,7 +73,7 @@ def on_direction_change(event): if direction_vector_sum != (0, 0): direction.current = maybe_new_direction - grid_wrapper = idom.html.div({"onKeyDown": on_direction_change}, grid) + grid_wrapper = reactpy.html.div({"on_key_down": on_direction_change}, grid) assign_grid_block_color(grid, food, "blue") @@ -89,7 +90,7 @@ def on_direction_change(event): interval = use_interval(0.5) - @idom.hooks.use_effect + @reactpy.hooks.use_effect async def animate(): if new_game_state is not None: await asyncio.sleep(1) @@ -106,7 +107,7 @@ async def animate(): if snake[-1] == food: set_food() - new_snake = snake + [new_snake_head] + new_snake = [*snake, new_snake_head] else: new_snake = snake[1:] + [new_snake_head] @@ -119,7 +120,7 @@ def use_snake_food(grid_size, current_snake): grid_points = {(x, y) for x in range(grid_size) for y in range(grid_size)} points_not_in_snake = grid_points.difference(current_snake) - food, _set_food = idom.hooks.use_state(current_snake[-1]) + food, _set_food = reactpy.hooks.use_state(current_snake[-1]) def set_food(): _set_food(random.choice(list(points_not_in_snake))) @@ -128,7 +129,7 @@ def set_food(): def use_interval(rate): - usage_time = idom.hooks.use_ref(time.time()) + usage_time = reactpy.hooks.use_ref(time.time()) async def interval() -> None: await asyncio.sleep(rate - (time.time() - usage_time.current)) @@ -138,7 +139,7 @@ async def interval() -> None: def create_grid(grid_size, block_scale): - return idom.html.div( + return reactpy.html.div( { "style": { "height": f"{block_scale * grid_size}px", @@ -149,16 +150,15 @@ def create_grid(grid_size, block_scale): "grid-template-columns": f"repeat({grid_size}, {block_scale}px)", "grid-template-rows": f"repeat({grid_size}, {block_scale}px)", }, - "tabIndex": -1, + "tab_index": -1, }, [ - idom.html.div( - {"style": {"height": f"{block_scale}px"}}, + reactpy.html.div( + {"style": {"height": f"{block_scale}px"}, "key": i}, [ create_grid_block("black", block_scale, key=i) for i in range(grid_size) ], - key=i, ) for i in range(grid_size) ], @@ -166,16 +166,16 @@ def create_grid(grid_size, block_scale): def create_grid_block(color, block_scale, key): - return idom.html.div( + return reactpy.html.div( { "style": { "height": f"{block_scale}px", "width": f"{block_scale}px", - "backgroundColor": color, + "background_color": color, "outline": "1px solid grey", - } - }, - key=key, + }, + "key": key, + } ) @@ -185,4 +185,4 @@ def assign_grid_block_color(grid, point, color): block["attributes"]["style"]["backgroundColor"] = color -idom.run(GameView) +reactpy.run(GameView) diff --git a/docs/source/reference/_examples/todo.py b/docs/source/reference/_examples/todo.py new file mode 100644 index 000000000..104ea59a9 --- /dev/null +++ b/docs/source/reference/_examples/todo.py @@ -0,0 +1,35 @@ +import reactpy + + +@reactpy.component +def Todo(): + items, set_items = reactpy.hooks.use_state([]) + + async def add_new_task(event): + if event["key"] == "Enter": + set_items([*items, event["target"]["value"]]) + + tasks = [] + + for index, text in enumerate(items): + + async def remove_task(event, index=index): + set_items(items[:index] + items[index + 1 :]) + + task_text = reactpy.html.td(reactpy.html.p(text)) + delete_button = reactpy.html.td( + {"on_click": remove_task}, reactpy.html.button(["x"]) + ) + tasks.append(reactpy.html.tr(task_text, delete_button)) + + task_input = reactpy.html.input({"on_key_down": add_new_task}) + task_table = reactpy.html.table(tasks) + + return reactpy.html.div( + reactpy.html.p("press enter to add a task:"), + task_input, + task_table, + ) + + +reactpy.run(Todo) diff --git a/docs/source/reference/_examples/use_reducer_counter.py b/docs/source/reference/_examples/use_reducer_counter.py new file mode 100644 index 000000000..6f9581dfd --- /dev/null +++ b/docs/source/reference/_examples/use_reducer_counter.py @@ -0,0 +1,27 @@ +import reactpy + + +def reducer(count, action): + if action == "increment": + return count + 1 + elif action == "decrement": + return count - 1 + elif action == "reset": + return 0 + else: + msg = f"Unknown action '{action}'" + raise ValueError(msg) + + +@reactpy.component +def Counter(): + count, dispatch = reactpy.hooks.use_reducer(reducer, 0) + return reactpy.html.div( + f"Count: {count}", + reactpy.html.button({"on_click": lambda event: dispatch("reset")}, "Reset"), + reactpy.html.button({"on_click": lambda event: dispatch("increment")}, "+"), + reactpy.html.button({"on_click": lambda event: dispatch("decrement")}, "-"), + ) + + +reactpy.run(Counter) diff --git a/docs/source/reference/_examples/use_state_counter.py b/docs/source/reference/_examples/use_state_counter.py new file mode 100644 index 000000000..b2d8c84a9 --- /dev/null +++ b/docs/source/reference/_examples/use_state_counter.py @@ -0,0 +1,26 @@ +import reactpy + + +def increment(last_count): + return last_count + 1 + + +def decrement(last_count): + return last_count - 1 + + +@reactpy.component +def Counter(): + initial_count = 0 + count, set_count = reactpy.hooks.use_state(initial_count) + return reactpy.html.div( + f"Count: {count}", + reactpy.html.button( + {"on_click": lambda event: set_count(initial_count)}, "Reset" + ), + reactpy.html.button({"on_click": lambda event: set_count(increment)}, "+"), + reactpy.html.button({"on_click": lambda event: set_count(decrement)}, "-"), + ) + + +reactpy.run(Counter) diff --git a/docs/source/reference/_examples/victory_chart.py b/docs/source/reference/_examples/victory_chart.py new file mode 100644 index 000000000..ce37c522f --- /dev/null +++ b/docs/source/reference/_examples/victory_chart.py @@ -0,0 +1,7 @@ +import reactpy + +victory = reactpy.web.module_from_template("react", "victory-bar", fallback="⌛") +VictoryBar = reactpy.web.export(victory, "VictoryBar") + +bar_style = {"parent": {"width": "500px"}, "data": {"fill": "royalblue"}} +reactpy.run(reactpy.component(lambda: VictoryBar({"style": bar_style}))) diff --git a/docs/source/reference-material/_static/vdom-json-schema.json b/docs/source/reference/_static/vdom-json-schema.json similarity index 83% rename from docs/source/reference-material/_static/vdom-json-schema.json rename to docs/source/reference/_static/vdom-json-schema.json index d3ab5bd6d..b1005d2ed 100644 --- a/docs/source/reference-material/_static/vdom-json-schema.json +++ b/docs/source/reference/_static/vdom-json-schema.json @@ -35,9 +35,7 @@ "type": "string" } }, - "required": [ - "tagName" - ], + "required": ["tagName"], "type": "object" }, "elementChildren": { @@ -61,12 +59,9 @@ "then": { "$ref": "#/definitions/element" }, - "type": [ - "object", - "string" - ] + "type": ["object", "string"] }, - "eventHander": { + "eventHandler": { "properties": { "preventDefault": { "type": "boolean" @@ -78,9 +73,7 @@ "type": "string" } }, - "required": [ - "target" - ], + "required": ["target"], "type": "object" }, "importSource": { @@ -94,29 +87,20 @@ "then": { "$ref": "#/definitions/elementOrString" }, - "type": [ - "object", - "string", - "null" - ] + "type": ["object", "string", "null"] }, "source": { "type": "string" }, "sourceType": { - "enum": [ - "URL", - "NAME" - ] + "enum": ["URL", "NAME"] }, "unmountBeforeUpdate": { "type": "boolean" } }, - "required": [ - "source" - ], + "required": ["source"], "type": "object" } } -} \ No newline at end of file +} diff --git a/docs/source/reference-material/browser-events.rst b/docs/source/reference/browser-events.rst similarity index 100% rename from docs/source/reference-material/browser-events.rst rename to docs/source/reference/browser-events.rst diff --git a/docs/source/reference-material/hooks-api.rst b/docs/source/reference/hooks-api.rst similarity index 85% rename from docs/source/reference-material/hooks-api.rst rename to docs/source/reference/hooks-api.rst index f2967376e..ca8123e85 100644 --- a/docs/source/reference-material/hooks-api.rst +++ b/docs/source/reference/hooks-api.rst @@ -7,12 +7,6 @@ Components. Their usage should always follow the :ref:`Rules of Hooks`. For most cases the :ref:`Basic Hooks` should be enough, however the remaining :ref:`Supplementary Hooks` should fulfill less common scenarios. -.. note:: - - Not all of React's built-in hooks have been implemented. - `In the future `_ they will be - added, but if you have a particular need for a missing hook post an issue. - Basic Hooks =========== @@ -55,7 +49,7 @@ accepts a single argument (the previous state) and returns the next state. Consi simply use case of a counter where we've pulled out logic for increment and decremented the count: -.. idom:: _examples/use_state_counter +.. reactpy:: _examples/use_state_counter We use the functional form for the "+" and "-" buttons since the next ``count`` depends on the previous value, while for the "Reset" button we simple assign the @@ -108,7 +102,7 @@ component render functions. .. note:: Normally in React the ``did_render`` function is called once an update has been - committed to the screen. Since no such action is performed by IDOM, and the time + committed to the screen. Since no such action is performed by ReactPy, and the time at which the update is displayed cannot be known we are unable to achieve parity with this behavior. @@ -159,16 +153,16 @@ Here, a new connection will be established whenever a new ``url`` is set. Async Effects ............. -A behavior unique to IDOM's implementation of ``use_effect`` is that it natively +A behavior unique to ReactPy's implementation of ``use_effect`` is that it natively supports ``async`` functions: .. code-block:: - async def nonblocking_effect(): + async def non_blocking_effect(): resource = await do_something_asynchronously() return lambda: blocking_close(resource) - use_effect(nonblocking_effect) + use_effect(non_blocking_effect) There are **three important subtleties** to note about using asynchronous effects: @@ -186,9 +180,9 @@ There are **three important subtleties** to note about using asynchronous effect Manual Effect Conditions ........................ -In some cases, you may want to explicitely declare when an effect should be triggered. -You can do this by passing ``dependencies`` to ``use_effect``. Each of the following values -produce different effect behaviors: +In some cases, you may want to explicitly declare when an effect should be triggered. +You can do this by passing ``dependencies`` to ``use_effect``. Each of the following +values produce different effect behaviors: - ``use_effect(..., dependencies=None)`` - triggers and cleans up on every render. - ``use_effect(..., dependencies=[])`` - only triggers on the first and cleans up after @@ -197,6 +191,24 @@ produce different effect behaviors: ``x`` or ``y`` have changed. +Use Context +----------- + +.. code-block:: + + value = use_context(MyContext) + +Accepts a context object (the value returned from +:func:`reactpy.core.hooks.create_context`) and returns the current context value for that +context. The current context value is determined by the ``value`` argument passed to the +nearest ``MyContext`` in the tree. + +When the nearest above the component updates, this Hook will +trigger a rerender with the latest context value passed to that MyContext provider. Even +if an ancestor uses React.memo or shouldComponentUpdate, a rerender will still happen +starting at the component itself using useContext. + + Supplementary Hooks =================== @@ -223,7 +235,7 @@ may be slightly more performant as well as being preferable since there is only We can rework the :ref:`Functional Updates` counter example to use ``use_reducer``: -.. idom:: _examples/use_reducer_counter +.. reactpy:: _examples/use_reducer_counter .. note:: @@ -286,14 +298,13 @@ Use Ref ref_container = use_ref(initial_value) -Returns a mutable :class:`~idom.utils.Ref` object that has a single -:attr:`~idom.utils.Ref.current` attribute that at first contains the ``initial_state``. +Returns a mutable :class:`~reactpy.utils.Ref` object that has a single +:attr:`~reactpy.utils.Ref.current` attribute that at first contains the ``initial_state``. The identity of the ``Ref`` object will be preserved for the lifetime of the component. A ``Ref`` is most useful if you need to incur side effects since updating its ``.current`` attribute doesn't trigger a re-render of the component. You'll often use this hook alongside :ref:`Use Effect` or in response to component event handlers. -:ref:`The Game Snake` provides a good use case for ``use_ref``. .. links @@ -318,7 +329,7 @@ Only call outside flow controls **Don't call hooks inside loops, conditions, or nested functions.** Instead you must always call hooks at the top level of your functions. By adhering to this rule you ensure that hooks are always called in the exact same order. This fact is what allows -IDOM to preserve the state of hooks between multiple calls to ``useState`` and +ReactPy to preserve the state of hooks between multiple calls to ``useState`` and ``useEffect`` calls. @@ -331,33 +342,33 @@ Only call in render functions - ✅ Call Hooks from another custom hook -Following this rule ensures stateful logic for IDOM component is always clearly +Following this rule ensures stateful logic for ReactPy component is always clearly separated from the rest of your codebase. Flake8 Plugin ------------- -We provide a Flake8 plugin called `flake8-idom-hooks `_ that helps +We provide a Flake8 plugin called `flake8-reactpy-hooks `_ that helps to enforce the two rules described above. You can ``pip`` install it directly, or with -the ``lint`` extra for IDOM: +the ``lint`` extra for ReactPy: .. code-block:: bash - pip install flake8-idom-hooks + pip install flake8-reactpy-hooks Once installed running, ``flake8`` on your code will start catching errors. For example: .. code-block:: bash - flake8 my_idom_components.py + flake8 my_reactpy_components.py Might produce something like the following output: .. code-block:: text - ./my_idom_components:10:8 ROH102 hook 'use_effect' used inside if statement - ./my_idom_components:23:4 ROH102 hook 'use_state' used outside component or hook definition + ./my_reactpy_components:10:8 ROH102 hook 'use_effect' used inside if statement + ./my_reactpy_components:23:4 ROH102 hook 'use_state' used outside component or hook definition See the Flake8 docs for `more info `__. @@ -365,4 +376,4 @@ See the Flake8 docs for .. links .. ===== -.. _Flake8 Linter Plugin: https://github.com/idom-team/flake8-idom-hooks +.. _Flake8 Linter Plugin: https://github.com/reactive-python/flake8-reactpy-hooks diff --git a/docs/source/reference/html-attributes.rst b/docs/source/reference/html-attributes.rst new file mode 100644 index 000000000..91813c355 --- /dev/null +++ b/docs/source/reference/html-attributes.rst @@ -0,0 +1,197 @@ +.. testcode:: + + from reactpy import html + + +HTML Attributes +=============== + +In ReactPy, HTML attributes are specified using snake_case instead of dash-separated +words. For example, ``tabindex`` and ``margin-left`` become ``tab_index`` and +``margin_left`` respectively. + + +Notable Attributes +------------------- + +Some attributes in ReactPy are renamed, have special meaning, or are used differently +than in HTML. + +``style`` +......... + +As mentioned above, instead of using a string to specify the ``style`` attribute, we use +a dictionary to describe the CSS properties we want to apply to an element. For example, +the following HTML: + +.. code-block:: html + +
        +

        My Todo List

        +
          +
        • Build a cool new app
        • +
        • Share it with the world!
        • +
        +
        + +Would be written in ReactPy as: + +.. testcode:: + + html.div( + { + "style": { + "width": "50%", + "margin_left": "25%", + }, + }, + html.h1( + { + "style": { + "margin_top": "0px", + }, + }, + "My Todo List", + ), + html.ul( + html.li("Build a cool new app"), + html.li("Share it with the world!"), + ), + ) + +``class`` vs ``class_name`` +........................... + +In HTML, the ``class`` attribute is used to specify a CSS class for an element. In +ReactPy, this attribute is renamed to ``class_name`` to avoid conflicting with the +``class`` keyword in Python. For example, the following HTML: + +.. code-block:: html + +
        +

        My Todo List

        +
          +
        • Build a cool new app
        • +
        • Share it with the world!
        • +
        +
        + +Would be written in ReactPy as: + +.. testcode:: + + html.div( + {"class_name": "container"}, + html.h1({"class_name": "title"}, "My Todo List"), + html.ul( + {"class_name": "list"}, + html.li({"class_name": "item"}, "Build a cool new app"), + html.li({"class_name": "item"}, "Share it with the world!"), + ), + ) + +``for`` vs ``html_for`` +....................... + +In HTML, the ``for`` attribute is used to specify the ``id`` of the element it's +associated with. In ReactPy, this attribute is renamed to ``html_for`` to avoid +conflicting with the ``for`` keyword in Python. For example, the following HTML: + +.. code-block:: html + +
        + + +
        + +Would be written in ReactPy as: + +.. testcode:: + + html.div( + html.label({"html_for": "todo"}, "Todo:"), + html.input({"id": "todo", "type": "text"}), + ) + +``dangerously_set_inner_HTML`` +.............................. + +This is used to set the ``innerHTML`` property of an element and should be provided a +dictionary with a single key ``__html`` whose value is the HTML to be set. It should be +used with **extreme caution** as it can lead to XSS attacks if the HTML inside isn't +trusted (for example if it comes from user input). + + +All Attributes +-------------- + +`access_key `__ + A string. Specifies a keyboard shortcut for the element. Not generally recommended. + +`aria_* `__ + ARIA attributes let you specify the accessibility tree information for this element. + See ARIA attributes for a complete reference. In ReactPy, all ARIA attribute names are + exactly the same as in HTML. + +`auto_capitalize `__ + A string. Specifies whether and how the user input should be capitalized. + +`content_editable `__ + A boolean. If true, the browser lets the user edit the rendered element directly. This + is used to implement rich text input libraries like Lexical. ReactPy warns if you try + to pass children to an element with ``content_editable = True`` because ReactPy will + not be able to update its content after user edits. + +`data_* `__ + Data attributes let you attach some string data to the element, for example + data-fruit="banana". In ReactPy, they are not commonly used because you would usually + read data from props or state instead. + +`dir `__ + Either ``"ltr"`` or ``"rtl"``. Specifies the text direction of the element. + +`draggable `__ + A boolean. Specifies whether the element is draggable. Part of HTML Drag and Drop API. + +`enter_key_hint `__ + A string. Specifies which action to present for the enter key on virtual keyboards. + +`hidden