diff --git a/.coveragerc b/.coveragerc
deleted file mode 100644
index 398ff08a..00000000
--- a/.coveragerc
+++ /dev/null
@@ -1,2 +0,0 @@
-[run]
-branch = True
diff --git a/.flake8 b/.flake8
new file mode 100644
index 00000000..a2b5078b
--- /dev/null
+++ b/.flake8
@@ -0,0 +1,9 @@
+[flake8]
+select = B,B9,C,D,DAR,E,F,N,RST,S,W
+ignore = E203,E501,RST201,RST203,RST301,W503
+max-line-length = 120
+max-complexity = 10
+docstring-convention = google
+per-file-ignores = tests/*:S101
+rst-roles = class,const,func,meth,mod,ref
+rst-directives = deprecated
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 00000000..6313b56c
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+* text=auto eol=lf
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index 1d39ef13..b86ee533 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -1,10 +1,9 @@
---
name: Bug report
about: Please follow this template to submit bug reports.
-title: ''
+title: ""
labels: bug
-assignees: ''
-
+assignees: ""
---
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 00000000..2beb2698
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,21 @@
+version: 2
+updates:
+ - package-ecosystem: github-actions
+ directory: "/"
+ schedule:
+ interval: daily
+ - package-ecosystem: pip
+ directory: "/.github/workflows"
+ schedule:
+ interval: daily
+ - package-ecosystem: pip
+ directory: "/docs"
+ schedule:
+ interval: daily
+ - package-ecosystem: pip
+ directory: "/"
+ schedule:
+ interval: daily
+ versioning-strategy: lockfile-only
+ allow:
+ - dependency-type: "all"
diff --git a/.github/labels.yml b/.github/labels.yml
new file mode 100644
index 00000000..21a15056
--- /dev/null
+++ b/.github/labels.yml
@@ -0,0 +1,66 @@
+---
+# Labels names are important as they are used by Release Drafter to decide
+# regarding where to record them in changelog or if to skip them.
+#
+# The repository labels will be automatically configured using this file and
+# the GitHub Action https://github.com/marketplace/actions/github-labeler.
+- name: breaking
+ description: Breaking Changes
+ color: bfd4f2
+- name: bug
+ description: Something isn't working
+ color: d73a4a
+- name: build
+ description: Build System and Dependencies
+ color: bfdadc
+- name: ci
+ description: Continuous Integration
+ color: 4a97d6
+- name: dependencies
+ description: Pull requests that update a dependency file
+ color: 0366d6
+- name: documentation
+ description: Improvements or additions to documentation
+ color: 0075ca
+- name: duplicate
+ description: This issue or pull request already exists
+ color: cfd3d7
+- name: enhancement
+ description: New feature or request
+ color: a2eeef
+- name: github_actions
+ description: Pull requests that update Github_actions code
+ color: "000000"
+- name: good first issue
+ description: Good for newcomers
+ color: 7057ff
+- name: help wanted
+ description: Extra attention is needed
+ color: "008672"
+- name: invalid
+ description: This doesn't seem right
+ color: e4e669
+- name: performance
+ description: Performance
+ color: "016175"
+- name: python
+ description: Pull requests that update Python code
+ color: 2b67c6
+- name: question
+ description: Further information is requested
+ color: d876e3
+- name: refactoring
+ description: Refactoring
+ color: ef67c4
+- name: removal
+ description: Removals and Deprecations
+ color: 9ae7ea
+- name: style
+ description: Style
+ color: c120e5
+- name: testing
+ description: Testing
+ color: b1fc6f
+- name: wontfix
+ description: This will not be worked on
+ color: ffffff
diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml
new file mode 100644
index 00000000..7a04410f
--- /dev/null
+++ b/.github/release-drafter.yml
@@ -0,0 +1,29 @@
+categories:
+ - title: ":boom: Breaking Changes"
+ label: "breaking"
+ - title: ":rocket: Features"
+ label: "enhancement"
+ - title: ":fire: Removals and Deprecations"
+ label: "removal"
+ - title: ":beetle: Fixes"
+ label: "bug"
+ - title: ":racehorse: Performance"
+ label: "performance"
+ - title: ":rotating_light: Testing"
+ label: "testing"
+ - title: ":construction_worker: Continuous Integration"
+ label: "ci"
+ - title: ":books: Documentation"
+ label: "documentation"
+ - title: ":hammer: Refactoring"
+ label: "refactoring"
+ - title: ":lipstick: Style"
+ label: "style"
+ - title: ":package: Dependencies"
+ labels:
+ - "dependencies"
+ - "build"
+template: |
+ ## Changes
+
+ $CHANGES
diff --git a/.github/workflows/constraints.txt b/.github/workflows/constraints.txt
new file mode 100644
index 00000000..add18d4e
--- /dev/null
+++ b/.github/workflows/constraints.txt
@@ -0,0 +1,6 @@
+lockfile<0.13.0,>=0.12.2
+pip==23.2.1
+nox==2023.4.22
+nox-poetry==1.0.3
+poetry>=1.2.0
+virtualenv==20.24.5
diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml
new file mode 100644
index 00000000..1717a3d3
--- /dev/null
+++ b/.github/workflows/labeler.yml
@@ -0,0 +1,18 @@
+name: Labeler
+
+on:
+ push:
+ branches:
+ - main
+
+jobs:
+ labeler:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Check out the repository
+ uses: actions/checkout@v4
+
+ - name: Run Labeler
+ uses: crazy-max/ghaction-github-labeler@v5.0.0
+ with:
+ skip-delete: true
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 00000000..3bd2f601
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,70 @@
+name: Release
+
+on:
+ push:
+ branches:
+ - main
+
+jobs:
+ release:
+ name: Release
+ runs-on: ubuntu-latest
+ steps:
+ - name: Check out the repository
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 2
+
+ - name: Set up Python
+ uses: actions/setup-python@v4
+ with:
+ python-version: "3.10"
+
+ - name: Upgrade pip
+ run: |
+ pip install --constraint=${PWD}/.github/workflows/constraints.txt pip
+ pip --version
+
+ - name: Install Poetry
+ run: |
+ pip install --constraint=${PWD}/.github/workflows/constraints.txt poetry
+ poetry --version
+
+ - name: Check if there is a parent commit
+ id: check-parent-commit
+ run: |
+ echo "::set-output name=sha::$(git rev-parse --verify --quiet HEAD^)"
+
+ - name: Detect and tag new version
+ id: check-version
+ if: steps.check-parent-commit.outputs.sha
+ uses: salsify/action-detect-and-tag-new-version@v2.0.1
+ with:
+ version-command: |
+ bash -o pipefail -c "poetry version | awk '{ print \$2 }'"
+
+ - name: Bump version for developmental release
+ if: "! steps.check-version.outputs.tag"
+ run: |
+ poetry version patch &&
+ version=$(poetry version | awk '{ print $2 }') &&
+ poetry version $version.dev.$(date +%s)
+
+ - name: Build package
+ run: |
+ poetry build --ansi
+
+ - name: Publish package on PyPI
+ if: steps.check-version.outputs.tag
+ uses: pypa/gh-action-pypi-publish@v1.8.10
+ with:
+ user: __token__
+ password: ${{ secrets.FLIT_PASSWORD }}
+
+ - name: Publish the release notes
+ uses: release-drafter/release-drafter@v5.24.0
+ with:
+ publish: ${{ steps.check-version.outputs.tag != '' }}
+ tag: ${{ steps.check-version.outputs.tag }}
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 04a7cf8f..b65c03c5 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -1,44 +1,162 @@
-name: tests
+name: Tests
-on: [pull_request]
+on:
+ push:
+ branches: [master]
+ pull_request:
jobs:
- test:
- runs-on: ubuntu-latest
+ tests:
+ name: ${{ matrix.session }} ${{ matrix.python }} / ${{ matrix.os }}
+ runs-on: ${{ matrix.os }}
strategy:
+ fail-fast: false
matrix:
- python-version: [3.6, 3.7, 3.8]
+ include:
+ # - { python: "3.10", os: "ubuntu-latest", session: "pre-commit" }
+ - { python: "3.10", os: "ubuntu-latest", session: "safety" }
+ # - { python: "3.11", os: "ubuntu-latest", session: "mypy" }
+ # - { python: "3.10", os: "ubuntu-latest", session: "mypy" }
+ # - { python: "3.9", os: "ubuntu-latest", session: "mypy" }
+ # - { python: "3.8", os: "ubuntu-latest", session: "mypy" }
+ # - { python: "3.7", os: "ubuntu-latest", session: "mypy" }
+ - { python: "3.11", os: "ubuntu-latest", session: "tests" }
+ - { python: "3.10", os: "ubuntu-latest", session: "tests" }
+ - { python: "3.9", os: "ubuntu-latest", session: "tests" }
+ - { python: "3.8", os: "ubuntu-latest", session: "tests" }
+ - { python: "3.10", os: "windows-latest", session: "tests" }
+ - { python: "3.10", os: "macos-latest", session: "tests" }
+ - { python: "3.10", os: "ubuntu-latest", session: "typeguard" }
+ - { python: "3.10", os: "ubuntu-latest", session: "xdoctest" }
+ - { python: "3.10", os: "ubuntu-latest", session: "docs-build" }
+
+ env:
+ NOXSESSION: ${{ matrix.session }}
+ FORCE_COLOR: "1"
+ PRE_COMMIT_COLOR: "always"
steps:
- - uses: actions/checkout@v2
- - name: Set up Python ${{ matrix.python-version }}
- uses: actions/setup-python@v2
+ - name: Check out the repository
+ uses: actions/checkout@v4
+
+ - name: Set up Python ${{ matrix.python }}
+ uses: actions/setup-python@v4
with:
- python-version: ${{ matrix.python-version }}
- - name: Install camelot with dependencies
+ python-version: ${{ matrix.python }}
+
+ - name: Upgrade pip
run: |
- make install
- - name: Test with pytest
+ pip install --constraint=${PWD}/.github/workflows/constraints.txt pip
+ pip --version
+
+ - name: Upgrade pip in virtual environments
+ shell: python
run: |
- make test
+ import os
+ import pip
- test_latest:
- name: Test on ${{ matrix.os }} with Python 3.9
- runs-on: ${{ matrix.os }}
- strategy:
- matrix:
- os: [ubuntu-latest, macos-latest, windows-latest]
- python-version: [3.9]
+ with open(os.environ["GITHUB_ENV"], mode="a") as io:
+ print(f"VIRTUALENV_PIP={pip.__version__}", file=io)
+
+ - name: Install Poetry
+ run: |
+ pipx install --pip-args=--constraint=${PWD}/.github/workflows/constraints.txt poetry
+ poetry --version
+
+ - name: Install Nox
+ run: |
+ pipx install --pip-args=--constraint=${PWD}/.github/workflows/constraints.txt nox
+ pipx inject --pip-args=--constraint=${PWD}/.github/workflows/constraints.txt nox nox-poetry
+ nox --version
+
+ - name: Install ghostscript
+ if: matrix.os == 'ubuntu-latest'
+ run: |
+ sudo apt update
+ sudo apt install ghostscript
+
+ - name: Compute pre-commit cache key
+ if: matrix.session == 'pre-commit'
+ id: pre-commit-cache
+ shell: python
+ run: |
+ import hashlib
+ import sys
+
+ python = "py{}.{}".format(*sys.version_info[:2])
+ payload = sys.version.encode() + sys.executable.encode()
+ digest = hashlib.sha256(payload).hexdigest()
+ result = "${{ runner.os }}-{}-{}-pre-commit".format(python, digest[:8])
+
+ print("::set-output name=result::{}".format(result))
+
+ - name: Restore pre-commit cache
+ uses: actions/cache@v3
+ if: matrix.session == 'pre-commit'
+ with:
+ path: ~/.cache/pre-commit
+ key: ${{ steps.pre-commit-cache.outputs.result }}-${{ hashFiles('.pre-commit-config.yaml') }}
+ restore-keys: |
+ ${{ steps.pre-commit-cache.outputs.result }}-
+
+ - name: Run Nox
+ run: |
+ nox --python=${{ matrix.python }}
+
+ - name: Upload coverage data
+ if: always() && matrix.session == 'tests'
+ uses: "actions/upload-artifact@v3"
+ with:
+ name: coverage-data
+ path: ".coverage.*"
+ - name: Upload documentation
+ if: matrix.session == 'docs-build'
+ uses: actions/upload-artifact@v3
+ with:
+ name: docs
+ path: docs/_build
+
+ coverage:
+ runs-on: ubuntu-latest
+ needs: tests
steps:
- - uses: actions/checkout@v2
- - name: Set up Python ${{ matrix.python-version }}
- uses: actions/setup-python@v2
+ - name: Check out the repository
+ uses: actions/checkout@v4
+
+ - name: Set up Python
+ uses: actions/setup-python@v4
+ with:
+ python-version: "3.10"
+
+ - name: Upgrade pip
+ run: |
+ pip install --constraint=${PWD}/.github/workflows/constraints.txt pip
+ pip --version
+
+ - name: Install Poetry
+ run: |
+ pipx install --pip-args=--constraint=${PWD}/.github/workflows/constraints.txt poetry
+ poetry --version
+
+ - name: Install Nox
+ run: |
+ pipx install --pip-args=--constraint=${PWD}/.github/workflows/constraints.txt nox
+ pipx inject --pip-args=--constraint=${PWD}/.github/workflows/constraints.txt nox nox-poetry
+ nox --version
+
+ - name: Download coverage data
+ uses: actions/download-artifact@v3
with:
- python-version: ${{ matrix.python-version }}
- - name: Install camelot with dependencies
+ name: coverage-data
+
+ - name: Combine coverage data and display human readable report
run: |
- make install
- - name: Test with pytest
+ nox --session=coverage
+
+ - name: Create coverage report
run: |
- make test
+ nox --session=coverage -- xml -i
+
+ - name: Upload coverage report
+ uses: codecov/codecov-action@v3.1.4
diff --git a/.gitignore b/.gitignore
index e9fc3aeb..04023115 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,20 +1,14 @@
-fontconfig/
+.mypy_cache/
+/.coverage
+/.coverage.*
+/.nox/
+/.python-version
+/.pytype/
+/dist/
+/docs/_build/
+/src/*.egg-info/
__pycache__/*
-*.py[cod]
-*.so
-
-build/
-dist/
-*.egg-info/
-.eggs/
-.coverage
-coverage.xml
-
-.pytest_cache/
-_build/
-
-.venv/
-htmlcov/
+.idea/
# vscode
.vscode
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 00000000..838b6ba6
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,59 @@
+repos:
+ - repo: local
+ hooks:
+ - id: black
+ name: black
+ entry: black
+ language: system
+ types: [python]
+ require_serial: true
+ - id: check-added-large-files
+ name: Check for added large files
+ entry: check-added-large-files
+ language: system
+ - id: check-toml
+ name: Check Toml
+ entry: check-toml
+ language: system
+ types: [toml]
+ - id: check-yaml
+ name: Check Yaml
+ entry: check-yaml
+ language: system
+ types: [yaml]
+ - id: end-of-file-fixer
+ name: Fix End of Files
+ entry: end-of-file-fixer
+ language: system
+ types: [text]
+ stages: [commit, push, manual]
+ - id: flake8
+ name: flake8
+ entry: flake8
+ language: system
+ types: [python]
+ require_serial: true
+ - id: isort
+ name: isort
+ entry: isort
+ require_serial: true
+ language: system
+ types_or: [cython, pyi, python]
+ args: ["--filter-files"]
+ - id: pyupgrade
+ name: pyupgrade
+ description: Automatically upgrade syntax for newer versions.
+ entry: pyupgrade
+ language: system
+ types: [python]
+ args: [--py37-plus]
+ - id: trailing-whitespace
+ name: Trim Trailing Whitespace
+ entry: trailing-whitespace-fixer
+ language: system
+ types: [text]
+ stages: [commit, push, manual]
+ - repo: https://github.com/pre-commit/mirrors-prettier
+ rev: v2.6.0
+ hooks:
+ - id: prettier
diff --git a/.readthedocs.yml b/.readthedocs.yml
index b5a05bd6..66f2a214 100644
--- a/.readthedocs.yml
+++ b/.readthedocs.yml
@@ -1,27 +1,12 @@
-# .readthedocs.yml
-# Read the Docs configuration file
-# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
-
-# Required
version: 2
-
-# Build documentation in the docs/ directory with Sphinx
+build:
+ os: ubuntu-20.04
+ tools:
+ python: "3.10"
sphinx:
configuration: docs/conf.py
-
-# Build documentation with MkDocs
-#mkdocs:
-# configuration: mkdocs.yml
-
-# Optionally build your docs in additional formats such as PDF
-formats:
- - pdf
-
-# Optionally set the version of Python and requirements required to build your docs
+formats: all
python:
- version: 3.8
install:
- - method: pip
- path: .
- extra_requirements:
- - dev
+ - requirements: docs/requirements.txt
+ - path: .
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index 080a0fbe..a25f61fb 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -1,3 +1,136 @@
Be cordial or be on your way. --Kenneth Reitz
https://kennethreitz.org/essays/2013/01/27/be-cordial-or-be-on-your-way
+
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+We as members, contributors, and leaders pledge to make participation in our
+community a harassment-free experience for everyone, regardless of age, body
+size, visible or invisible disability, ethnicity, sex characteristics, gender
+identity and expression, level of experience, education, socio-economic status,
+nationality, personal appearance, race, caste, color, religion, or sexual
+identity and orientation.
+
+We pledge to act and interact in ways that contribute to an open, welcoming,
+diverse, inclusive, and healthy community.
+
+## Our Standards
+
+Examples of behavior that contributes to a positive environment for our
+community include:
+
+- Demonstrating empathy and kindness toward other people
+- Being respectful of differing opinions, viewpoints, and experiences
+- Giving and gracefully accepting constructive feedback
+- Accepting responsibility and apologizing to those affected by our mistakes,
+ and learning from the experience
+- Focusing on what is best not just for us as individuals, but for the overall
+ community
+
+Examples of unacceptable behavior include:
+
+- The use of sexualized language or imagery, and sexual attention or advances of
+ any kind
+- Trolling, insulting or derogatory comments, and personal or political attacks
+- Public or private harassment
+- Publishing others' private information, such as a physical or email address,
+ without their explicit permission
+- Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+## Enforcement Responsibilities
+
+Community leaders are responsible for clarifying and enforcing our standards of
+acceptable behavior and will take appropriate and fair corrective action in
+response to any behavior that they deem inappropriate, threatening, offensive,
+or harmful.
+
+Community leaders have the right and responsibility to remove, edit, or reject
+comments, commits, code, wiki edits, issues, and other contributions that are
+not aligned to this Code of Conduct, and will communicate reasons for moderation
+decisions when appropriate.
+
+## Scope
+
+This Code of Conduct applies within all community spaces, and also applies when
+an individual is officially representing the community in public spaces.
+Examples of representing our community include using an official e-mail address,
+posting via an official social media account, or acting as an appointed
+representative at an online or offline event.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported to the community leaders responsible for enforcement at
+https://github.com/camelot-dev/camelot/issues.
+All complaints will be reviewed and investigated promptly and fairly.
+
+All community leaders are obligated to respect the privacy and security of the
+reporter of any incident.
+
+## Enforcement Guidelines
+
+Community leaders will follow these Community Impact Guidelines in determining
+the consequences for any action they deem in violation of this Code of Conduct:
+
+### 1. Correction
+
+**Community Impact**: Use of inappropriate language or other behavior deemed
+unprofessional or unwelcome in the community.
+
+**Consequence**: A private, written warning from community leaders, providing
+clarity around the nature of the violation and an explanation of why the
+behavior was inappropriate. A public apology may be requested.
+
+### 2. Warning
+
+**Community Impact**: A violation through a single incident or series of
+actions.
+
+**Consequence**: A warning with consequences for continued behavior. No
+interaction with the people involved, including unsolicited interaction with
+those enforcing the Code of Conduct, for a specified period of time. This
+includes avoiding interactions in community spaces as well as external channels
+like social media. Violating these terms may lead to a temporary or permanent
+ban.
+
+### 3. Temporary Ban
+
+**Community Impact**: A serious violation of community standards, including
+sustained inappropriate behavior.
+
+**Consequence**: A temporary ban from any sort of interaction or public
+communication with the community for a specified period of time. No public or
+private interaction with the people involved, including unsolicited interaction
+with those enforcing the Code of Conduct, is allowed during this period.
+Violating these terms may lead to a permanent ban.
+
+### 4. Permanent Ban
+
+**Community Impact**: Demonstrating a pattern of violation of community
+standards, including sustained inappropriate behavior, harassment of an
+individual, or aggression toward or disparagement of classes of individuals.
+
+**Consequence**: A permanent ban from any sort of public interaction within the
+community.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage],
+version 2.1, available at
+[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
+
+Community Impact Guidelines were inspired by
+[Mozilla's code of conduct enforcement ladder][mozilla coc].
+
+For answers to common questions about this code of conduct, see the FAQ at
+[https://www.contributor-covenant.org/faq][faq]. Translations are available at
+[https://www.contributor-covenant.org/translations][translations].
+
+[homepage]: https://www.contributor-covenant.org
+[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
+[mozilla coc]: https://github.com/mozilla/diversity
+[faq]: https://www.contributor-covenant.org/faq
+[translations]: https://www.contributor-covenant.org/translations
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 1bfc8dd8..364a5aee 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,131 +1,117 @@
-# Contributor's Guide
+# Contributor Guide
-If you're reading this, you're probably looking to contributing to Camelot. *Time is the only real currency*, and the fact that you're considering spending some here is *very* generous of you. Thank you very much!
+Thank you for your interest in improving this project.
+This project is open-source under the [MIT license] and
+welcomes contributions in the form of bug reports, feature requests, and pull requests.
-This document will help you get started with contributing documentation, code, testing and filing issues. If you have any questions, feel free to reach out to [Vinayak Mehta](https://vinayak-mehta.github.io), the author and maintainer.
+Kenneth Reitz has also written an [essay](https://www.kennethreitz.org/essays/2013/01/27/be-cordial-or-be-on-your-way) on this topic, which you should read.
-## Code Of Conduct
+Here is a list of important resources for contributors:
-The following quote sums up the **Code Of Conduct**.
+- [Source Code]
+- [Documentation]
+- [Issue Tracker]
+- [Code of Conduct]
- > Be cordial or be on your way. --Kenneth Reitz
+[mit license]: https://opensource.org/licenses/MIT
+[source code]: https://github.com/camelot-dev/camelot
+[documentation]: https://camelot-py.readthedocs.io/
+[issue tracker]: https://github.com/camelot-dev/camelot/issues
-Kenneth Reitz has also written an [essay](https://kennethreitz.org/essays/2013/01/27/be-cordial-or-be-on-your-way) on this topic, which you should read.
+## How to report a bug
-As the [Requests Code Of Conduct](http://docs.python-requests.org/en/master/dev/contributing/#be-cordial) states, **all contributions are welcome**, as long as everyone involved is treated with respect.
+Report bugs on the [Issue Tracker].
-## Your first contribution
+When filing an issue, make sure to answer these questions:
-A great way to start contributing to Camelot is to pick an issue tagged with the [help wanted](https://github.com/camelot-dev/camelot/labels/help%20wanted) tag or the [good first issue](https://github.com/camelot-dev/camelot/labels/good%20first%20issue) tag. If you're unable to find a good first issue, feel free to contact the maintainer.
+- Which operating system and Python version are you using?
+- Which version of this project are you using?
+- What did you do?
+- What did you expect to see?
+- What did you see instead?
-## Setting up a development environment
+The best way to get your bug fixed is to provide a test case,
+and/or steps to reproduce the issue.
-To install the dependencies needed for development, you can use pip:
+## How to request a feature
-
-$ pip install "camelot-py[dev]"
-
+Request features on the [Issue Tracker].
-Alternatively, you can clone the project repository, and install using pip:
+## How to set up your development environment
-
-$ pip install ".[dev]"
-
+You need Python 3.7+ and the following tools:
-## Pull Requests
+- [Poetry]
+- [Nox]
+- [nox-poetry]
-### Submit a pull request
+Install the package with development requirements:
-The preferred workflow for contributing to Camelot is to fork the [project repository](https://github.com/camelot-dev/camelot) on GitHub, clone, develop on a branch and then finally submit a pull request. Here are the steps:
+```console
+$ poetry install
+```
-1. Fork the project repository. Click on the ‘Fork’ button near the top of the page. This creates a copy of the code under your account on the GitHub.
+You can now run an interactive Python session,
+or the command-line interface:
-2. Clone your fork of Camelot from your GitHub account:
+```console
+$ poetry run python
+$ poetry run camelot
+```
-
-$ git clone https://www.github.com/[username]/camelot
-
+[poetry]: https://python-poetry.org/
+[nox]: https://nox.thea.codes/
+[nox-poetry]: https://nox-poetry.readthedocs.io/
-3. Create a branch to hold your changes:
+## How to test the project
-
-$ git checkout -b my-feature
-
+Run the full test suite:
-Always branch out from `master` to work on your contribution. It's good practice to never work on the `master` branch!
+```console
+$ nox
+```
-**Protip: `git stash` is a great way to save the work that you haven't committed yet, to move between branches.**
+List the available Nox sessions:
-4. Work on your contribution. Add changed files using `git add` and then `git commit` them:
+```console
+$ nox --list-sessions
+```
-
-$ git add modified_files
-$ git commit
-
+You can also run a specific Nox session.
+For example, invoke the unit test suite like this:
-5. Finally, push them to your GitHub fork:
+```console
+$ nox --session=tests
+```
-
-$ git push -u origin my-feature
-
+Unit tests are located in the _tests_ directory,
+and are written using the [pytest] testing framework.
-Now it's time to go to the your fork of Camelot and create a pull request! You can [follow these instructions](https://help.github.com/articles/creating-a-pull-request-from-a-fork/) to do this.
+[pytest]: https://pytest.readthedocs.io/
-### Work on your pull request
+## How to submit changes
-We recommend that your pull request complies with the following rules:
+Open a [pull request] to submit changes to this project.
-- Make sure your code follows [pep8](http://pep8.org).
+Your pull request needs to meet the following guidelines for acceptance:
-- In case your pull request contains function docstrings, make sure you follow the [numpydoc](https://numpydoc.readthedocs.io/en/latest/format.html) format. All function docstrings in Camelot follow this format. Moreover, following the format will make sure that the API documentation is generated flawlessly.
+- The Nox test suite must pass without errors and warnings.
+- Include unit tests. This project maintains 100% code coverage.
+- If your changes add functionality, update the documentation accordingly.
-- Make sure your commit messages follow [the seven rules of a great git commit message](https://chris.beams.io/posts/git-commit/):
- - Separate subject from body with a blank line
- - Limit the subject line to 50 characters
- - Capitalize the subject line
- - Do not end the subject line with a period
- - Use the imperative mood in the subject line
- - Wrap the body at 72 characters
- - Use the body to explain what and why vs. how
+Feel free to submit early, though—we can always iterate on this.
-- Please prefix the title of your pull request with [MRG] (Ready for Merge), if the contribution is complete and ready for a detailed review. An incomplete pull request's title should be prefixed with [WIP] (to indicate a work in progress), and changed to [MRG] when it's complete. A good [task list](https://blog.github.com/2013-01-09-task-lists-in-gfm-issues-pulls-comments/) in the PR description will ensure that other people get a better idea of what it proposes to do, which will also increase collaboration.
+To run linting and code formatting checks before committing your change, you can install pre-commit as a Git hook by running the following command:
-- If contributing new functionality, make sure that you add a unit test for it, while making sure that all previous tests pass. Camelot uses [pytest](https://docs.pytest.org/en/latest/) for testing. Tests can be run using:
+```console
+$ nox --session=pre-commit -- install
+```
-
-$ python setup.py test
-
+It is recommended to open an issue before starting work on anything.
+This will allow a chance to talk it over with the owners and validate your approach.
-## Writing Documentation
+[pull request]: https://github.com/camelot-dev/camelot/pulls
-Writing documentation, function docstrings, examples and tutorials is a great way to start contributing to open-source software! The documentation is present inside the `docs/` directory of the project repository.
+
-It is written in [reStructuredText](https://en.wikipedia.org/wiki/ReStructuredText), with [Sphinx](http://www.sphinx-doc.org/en/master/) used to generate these lovely HTML files that you're currently reading (unless you're reading this on GitHub). You can edit the documentation using any text editor and then generate the HTML output by running `make html` in the `docs/` directory.
-
-The function docstrings are written using the [numpydoc](https://numpydoc.readthedocs.io/en/latest/format.html) extension for Sphinx. Make sure you check out its format guidelines before you start writing one.
-
-## Filing Issues
-
-We use [GitHub issues](https://github.com/camelot-dev/camelot/issues) to keep track of all issues and pull requests. Before opening an issue (which asks a question or reports a bug), please use GitHub search to look for existing issues (both open and closed) that may be similar.
-
-### Questions
-
-Please don't use GitHub issues for support questions. A better place for them would be [Stack Overflow](http://stackoverflow.com). Make sure you tag them using the `python-camelot` tag.
-
-### Bug Reports
-
-In bug reports, make sure you include:
-
-- Your operating system type and Python version number, along with the version numbers of NumPy, OpenCV and Camelot. You can use the following code snippet to find this information:
-
-
-import platform; print(platform.platform())
-import sys; print('Python', sys.version)
-import numpy; print('NumPy', numpy.__version__)
-import cv2; print('OpenCV', cv2.__version__)
-import camelot; print('Camelot', camelot.__version__)
-
-
-- The complete traceback. Just adding the exception message or a part of the traceback won't help us fix your issue sooner.
-- Steps to reproduce the bug, using code snippets. See [Creating and highlighting code blocks](https://help.github.com/articles/creating-and-highlighting-code-blocks/).
-- A link to the PDF document that you were trying to extract tables from, telling us what you expected the code to do and what actually happened.
+[code of conduct]: CODE_OF_CONDUCT.md
diff --git a/HISTORY.md b/HISTORY.md
index 9c2f32c1..04e5abd9 100755
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -1,16 +1,21 @@
-Release History
-===============
+# Release History
+
+## master
+
+0.11.0 (2023-02-26)
+------------------
+
+- Replace `PdfFileReader` with `PdfReader` and pin PyPDF to `>=3.0.0`. [#307](https://github.com/camelot-dev/camelot/pull/307) by [Martin Thoma](https://github.com/MartinThoma).
-master
-------
0.10.1 (2021-07-11)
------------------
- Change extra requirements from `cv` to `base`. You can use `pip install "camelot-py[base]"` to install everything required to run camelot.
-0.10.0 (2021-07-11)
-------------------
+ 0.10.0 (2021-07-11)
+
+---
**Improvements**
@@ -21,8 +26,9 @@ master
- Add faq section. [#216](https://github.com/camelot-dev/camelot/pull/216) by [Stefano Fiorucci](https://github.com/anakin87).
-0.9.0 (2021-06-15)
-------------------
+ 0.9.0 (2021-06-15)
+
+---
**Bugfixes**
@@ -46,236 +52,251 @@ master
- [#193](https://github.com/camelot-dev/camelot/issues/193) Add better checks to confirm proper installation of ghostscript. [#196](https://github.com/camelot-dev/camelot/pull/196) by [jimhall](https://github.com/jimhall).
- Update `advanced.rst` plotting examples. [#119](https://github.com/camelot-dev/camelot/pull/119) by [Jens Diemer](https://github.com/jedie).
-0.8.2 (2020-07-27)
-------------------
+ 0.8.2 (2020-07-27)
-* Revert the changes in `0.8.1`.
+---
-0.8.1 (2020-07-21)
-------------------
+- Revert the changes in `0.8.1`.
+
+ 0.8.1 (2020-07-21)
+
+---
**Bugfixes**
-* [#169](https://github.com/camelot-dev/camelot/issues/169) Fix import error caused by `pdfminer.six==20200720`. [#171](https://github.com/camelot-dev/camelot/pull/171) by Vinayak Mehta.
+- [#169](https://github.com/camelot-dev/camelot/issues/169) Fix import error caused by `pdfminer.six==20200720`. [#171](https://github.com/camelot-dev/camelot/pull/171) by Vinayak Mehta.
-0.8.0 (2020-05-24)
-------------------
+ 0.8.0 (2020-05-24)
+
+---
**Improvements**
-* Drop Python 2 support!
- * Remove Python 2.7 and 3.5 support.
- * Replace all instances of `.format` with f-strings.
- * Remove all `__future__` imports.
- * Fix HTTP 403 forbidden exception in read_pdf(url) and remove Python 2 urllib support.
- * Fix test data.
+- Drop Python 2 support!
+ - Remove Python 2.7 and 3.5 support.
+ - Replace all instances of `.format` with f-strings.
+ - Remove all `__future__` imports.
+ - Fix HTTP 403 forbidden exception in read_pdf(url) and remove Python 2 urllib support.
+ - Fix test data.
**Bugfixes**
-* Fix library discovery on Windows. [#32](https://github.com/camelot-dev/camelot/pull/32) by [KOLANICH](https://github.com/KOLANICH).
-* Fix calling convention of callback functions. [#34](https://github.com/camelot-dev/camelot/pull/34) by [KOLANICH](https://github.com/KOLANICH).
+- Fix library discovery on Windows. [#32](https://github.com/camelot-dev/camelot/pull/32) by [KOLANICH](https://github.com/KOLANICH).
+- Fix calling convention of callback functions. [#34](https://github.com/camelot-dev/camelot/pull/34) by [KOLANICH](https://github.com/KOLANICH).
-0.7.3 (2019-07-07)
-------------------
+ 0.7.3 (2019-07-07)
+
+---
**Improvements**
-* Camelot now follows the Black code style! [#1](https://github.com/camelot-dev/camelot/pull/1) and [#3](https://github.com/camelot-dev/camelot/pull/3).
+- Camelot now follows the Black code style! [#1](https://github.com/camelot-dev/camelot/pull/1) and [#3](https://github.com/camelot-dev/camelot/pull/3).
**Bugfixes**
-* Fix Click.HelpFormatter monkey-patch. [#5](https://github.com/camelot-dev/camelot/pull/5) by [Dimiter Naydenov](https://github.com/dimitern).
-* Fix strip_text argument getting ignored. [#4](https://github.com/camelot-dev/camelot/pull/4) by [Dimiter Naydenov](https://github.com/dimitern).
-* [#25](https://github.com/camelot-dev/camelot/issues/25) edge_tol skipped in read_pdf. [#26](https://github.com/camelot-dev/camelot/pull/26) by Vinayak Mehta.
-* Fix pytest deprecation warning. [#2](https://github.com/camelot-dev/camelot/pull/2) by Vinayak Mehta.
-* [#293](https://github.com/socialcopsdev/camelot/issues/293) Split text ignores all text to the right of last cut. [#294](https://github.com/socialcopsdev/camelot/pull/294) by Vinayak Mehta.
-* [#277](https://github.com/socialcopsdev/camelot/issues/277) Sort TableList by order of tables in PDF. [#283](https://github.com/socialcopsdev/camelot/pull/283) by [Sym Roe](https://github.com/symroe).
-* [#312](https://github.com/socialcopsdev/camelot/issues/312) `table_regions` throws `ValueError` when `flavor='stream'`. [#332](https://github.com/socialcopsdev/camelot/pull/332) by Vinayak Mehta.
+- Fix Click.HelpFormatter monkey-patch. [#5](https://github.com/camelot-dev/camelot/pull/5) by [Dimiter Naydenov](https://github.com/dimitern).
+- Fix strip_text argument getting ignored. [#4](https://github.com/camelot-dev/camelot/pull/4) by [Dimiter Naydenov](https://github.com/dimitern).
+- [#25](https://github.com/camelot-dev/camelot/issues/25) edge_tol skipped in read_pdf. [#26](https://github.com/camelot-dev/camelot/pull/26) by Vinayak Mehta.
+- Fix pytest deprecation warning. [#2](https://github.com/camelot-dev/camelot/pull/2) by Vinayak Mehta.
+- [#293](https://github.com/socialcopsdev/camelot/issues/293) Split text ignores all text to the right of last cut. [#294](https://github.com/socialcopsdev/camelot/pull/294) by Vinayak Mehta.
+- [#277](https://github.com/socialcopsdev/camelot/issues/277) Sort TableList by order of tables in PDF. [#283](https://github.com/socialcopsdev/camelot/pull/283) by [Sym Roe](https://github.com/symroe).
+- [#312](https://github.com/socialcopsdev/camelot/issues/312) `table_regions` throws `ValueError` when `flavor='stream'`. [#332](https://github.com/socialcopsdev/camelot/pull/332) by Vinayak Mehta.
-0.7.2 (2019-01-10)
-------------------
+ 0.7.2 (2019-01-10)
+
+---
**Bugfixes**
-* [#245](https://github.com/socialcopsdev/camelot/issues/245) Fix AttributeError for encrypted files. [#251](https://github.com/socialcopsdev/camelot/pull/251) by Yatin Taluja.
+- [#245](https://github.com/socialcopsdev/camelot/issues/245) Fix AttributeError for encrypted files. [#251](https://github.com/socialcopsdev/camelot/pull/251) by Yatin Taluja.
+ 0.7.1 (2019-01-06)
-0.7.1 (2019-01-06)
-------------------
+---
**Bugfixes**
-* Move ghostscript import to inside the function so Anaconda builds don't fail.
+- Move ghostscript import to inside the function so Anaconda builds don't fail.
-0.7.0 (2019-01-05)
-------------------
+ 0.7.0 (2019-01-05)
+
+---
**Improvements**
-* [#240](https://github.com/socialcopsdev/camelot/issues/209) Add support to analyze only certain page regions to look for tables. [#243](https://github.com/socialcopsdev/camelot/pull/243) by Vinayak Mehta.
- * You can use `table_regions` in `read_pdf()` to specify approximate page regions which may contain tables.
- * Kwarg `line_size_scaling` is now called `line_scale`.
-* [#212](https://github.com/socialcopsdev/camelot/issues/212) Add support to export as sqlite database. [#244](https://github.com/socialcopsdev/camelot/pull/244) by Vinayak Mehta.
-* [#239](https://github.com/socialcopsdev/camelot/issues/239) Raise warning if PDF is image-based. [#240](https://github.com/socialcopsdev/camelot/pull/240) by Vinayak Mehta.
+- [#240](https://github.com/socialcopsdev/camelot/issues/209) Add support to analyze only certain page regions to look for tables. [#243](https://github.com/socialcopsdev/camelot/pull/243) by Vinayak Mehta.
+ - You can use `table_regions` in `read_pdf()` to specify approximate page regions which may contain tables.
+ - Kwarg `line_size_scaling` is now called `line_scale`.
+- [#212](https://github.com/socialcopsdev/camelot/issues/212) Add support to export as sqlite database. [#244](https://github.com/socialcopsdev/camelot/pull/244) by Vinayak Mehta.
+- [#239](https://github.com/socialcopsdev/camelot/issues/239) Raise warning if PDF is image-based. [#240](https://github.com/socialcopsdev/camelot/pull/240) by Vinayak Mehta.
**Documentation**
-* Remove mention of old mesh kwarg from docs. [#241](https://github.com/socialcopsdev/camelot/pull/241) by [fte10kso](https://github.com/fte10kso).
+- Remove mention of old mesh kwarg from docs. [#241](https://github.com/socialcopsdev/camelot/pull/241) by [fte10kso](https://github.com/fte10kso).
**Note**: The python wrapper to Ghostscript's C API is now vendorized under the `ext` module. This was done due to unavailability of the [ghostscript](https://pypi.org/project/ghostscript/) package on Anaconda. The code should be removed after we submit a recipe for it to conda-forge. With this release, the user doesn't need to ensure that the Ghostscript executable is available on the PATH variable.
-0.6.0 (2018-12-24)
-------------------
+## 0.6.0 (2018-12-24)
**Improvements**
-* [#91](https://github.com/socialcopsdev/camelot/issues/91) Add support to read from url. [#236](https://github.com/socialcopsdev/camelot/pull/236) by Vinayak Mehta.
-* [#229](https://github.com/socialcopsdev/camelot/issues/229), [#230](https://github.com/socialcopsdev/camelot/issues/230) and [#233](https://github.com/socialcopsdev/camelot/issues/233) New configuration parameters. [#234](https://github.com/socialcopsdev/camelot/pull/234) by Vinayak Mehta.
- * `strip_text`: To define characters that should be stripped from each string.
- * `edge_tol`: Tolerance parameter for extending textedges vertically.
- * `resolution`: Resolution used for PDF to PNG conversion.
- * Check out the [advanced docs](https://camelot-py.readthedocs.io/en/master/user/advanced.html#strip-characters-from-text) for usage details.
-* [#170](https://github.com/socialcopsdev/camelot/issues/170) Add option to pass pdfminer layout kwargs. [#232](https://github.com/socialcopsdev/camelot/pull/232) by Vinayak Mehta.
- * Keyword arguments for [pdfminer.layout.LAParams](https://github.com/euske/pdfminer/blob/master/pdfminer/layout.py#L33) can now be passed using `layout_kwargs` in `read_pdf()`.
- * The `margins` keyword argument in `read_pdf()` is now deprecated.
-
-0.5.0 (2018-12-13)
-------------------
+- [#91](https://github.com/socialcopsdev/camelot/issues/91) Add support to read from url. [#236](https://github.com/socialcopsdev/camelot/pull/236) by Vinayak Mehta.
+- [#229](https://github.com/socialcopsdev/camelot/issues/229), [#230](https://github.com/socialcopsdev/camelot/issues/230) and [#233](https://github.com/socialcopsdev/camelot/issues/233) New configuration parameters. [#234](https://github.com/socialcopsdev/camelot/pull/234) by Vinayak Mehta.
+ - `strip_text`: To define characters that should be stripped from each string.
+ - `edge_tol`: Tolerance parameter for extending textedges vertically.
+ - `resolution`: Resolution used for PDF to PNG conversion.
+ - Check out the [advanced docs](https://camelot-py.readthedocs.io/en/master/user/advanced.html#strip-characters-from-text) for usage details.
+- [#170](https://github.com/socialcopsdev/camelot/issues/170) Add option to pass pdfminer layout kwargs. [#232](https://github.com/socialcopsdev/camelot/pull/232) by Vinayak Mehta.
+ _ Keyword arguments for [pdfminer.layout.LAParams](https://github.com/euske/pdfminer/blob/master/pdfminer/layout.py#L33) can now be passed using `layout_kwargs` in `read_pdf()`.
+ _ The `margins` keyword argument in `read_pdf()` is now deprecated.
+
+ 0.5.0 (2018-12-13)
+
+---
**Improvements**
-* [#207](https://github.com/socialcopsdev/camelot/issues/207) Add a plot type for Stream text edges and detected table areas. [#224](https://github.com/socialcopsdev/camelot/pull/224) by Vinayak Mehta.
-* [#204](https://github.com/socialcopsdev/camelot/issues/204) `suppress_warnings` is now called `suppress_stdout`. [#225](https://github.com/socialcopsdev/camelot/pull/225) by Vinayak Mehta.
+- [#207](https://github.com/socialcopsdev/camelot/issues/207) Add a plot type for Stream text edges and detected table areas. [#224](https://github.com/socialcopsdev/camelot/pull/224) by Vinayak Mehta.
+- [#204](https://github.com/socialcopsdev/camelot/issues/204) `suppress_warnings` is now called `suppress_stdout`. [#225](https://github.com/socialcopsdev/camelot/pull/225) by Vinayak Mehta.
**Bugfixes**
-* [#217](https://github.com/socialcopsdev/camelot/issues/217) Fix IndexError when scale is large.
-* [#105](https://github.com/socialcopsdev/camelot/issues/105), [#192](https://github.com/socialcopsdev/camelot/issues/192) and [#215](https://github.com/socialcopsdev/camelot/issues/215) in [#227](https://github.com/socialcopsdev/camelot/pull/227) by Vinayak Mehta.
+- [#217](https://github.com/socialcopsdev/camelot/issues/217) Fix IndexError when scale is large.
+- [#105](https://github.com/socialcopsdev/camelot/issues/105), [#192](https://github.com/socialcopsdev/camelot/issues/192) and [#215](https://github.com/socialcopsdev/camelot/issues/215) in [#227](https://github.com/socialcopsdev/camelot/pull/227) by Vinayak Mehta.
**Documentation**
-* Add pdfplumber comparison and update Tabula (stream) comparison. Check out the [wiki page](https://github.com/socialcopsdev/camelot/wiki/Comparison-with-other-PDF-Table-Extraction-libraries-and-tools).
+- Add pdfplumber comparison and update Tabula (stream) comparison. Check out the [wiki page](https://github.com/socialcopsdev/camelot/wiki/Comparison-with-other-PDF-Table-Extraction-libraries-and-tools).
-0.4.1 (2018-12-05)
-------------------
+ 0.4.1 (2018-12-05)
+
+---
**Bugfixes**
-* Add chardet to `install_requires` to fix [#210](https://github.com/socialcopsdev/camelot/issues/210). More details in [pdfminer.six#213](https://github.com/pdfminer/pdfminer.six/issues/213).
+- Add chardet to `install_requires` to fix [#210](https://github.com/socialcopsdev/camelot/issues/210). More details in [pdfminer.six#213](https://github.com/pdfminer/pdfminer.six/issues/213).
-0.4.0 (2018-11-23)
-------------------
+ 0.4.0 (2018-11-23)
+
+---
**Improvements**
-* [#102](https://github.com/socialcopsdev/camelot/issues/102) Detect tables automatically when Stream is used. [#206](https://github.com/socialcopsdev/camelot/pull/206) Add implementation of Anssi Nurminen's table detection algorithm by Vinayak Mehta.
+- [#102](https://github.com/socialcopsdev/camelot/issues/102) Detect tables automatically when Stream is used. [#206](https://github.com/socialcopsdev/camelot/pull/206) Add implementation of Anssi Nurminen's table detection algorithm by Vinayak Mehta.
-0.3.2 (2018-11-04)
-------------------
+ 0.3.2 (2018-11-04)
+
+---
**Improvements**
-* [#186](https://github.com/socialcopsdev/camelot/issues/186) Add `_bbox` attribute to table. [#193](https://github.com/socialcopsdev/camelot/pull/193) by Vinayak Mehta.
- * You can use `table._bbox` to get coordinates of the detected table.
+- [#186](https://github.com/socialcopsdev/camelot/issues/186) Add `_bbox` attribute to table. [#193](https://github.com/socialcopsdev/camelot/pull/193) by Vinayak Mehta. \* You can use `table._bbox` to get coordinates of the detected table.
-0.3.1 (2018-11-02)
-------------------
+ 0.3.1 (2018-11-02)
+
+---
**Improvements**
-* Matplotlib is now an optional requirement. [#190](https://github.com/socialcopsdev/camelot/pull/190) by Vinayak Mehta.
- * You can install it using `$ pip install camelot-py[plot]`.
-* [#127](https://github.com/socialcopsdev/camelot/issues/127) Add tests for plotting. Coverage is now at 87%! [#179](https://github.com/socialcopsdev/camelot/pull/179) by [Suyash Behera](https://github.com/Suyash458).
+- Matplotlib is now an optional requirement. [#190](https://github.com/socialcopsdev/camelot/pull/190) by Vinayak Mehta.
+ - You can install it using `$ pip install camelot-py[plot]`.
+- [#127](https://github.com/socialcopsdev/camelot/issues/127) Add tests for plotting. Coverage is now at 87%! [#179](https://github.com/socialcopsdev/camelot/pull/179) by [Suyash Behera](https://github.com/Suyash458).
-0.3.0 (2018-10-28)
-------------------
+ 0.3.0 (2018-10-28)
+
+---
**Improvements**
-* [#162](https://github.com/socialcopsdev/camelot/issues/162) Add password keyword argument. [#180](https://github.com/socialcopsdev/camelot/pull/180) by [rbares](https://github.com/rbares).
- * An encrypted PDF can now be decrypted by passing `password=''` to `read_pdf` or `--password ` to the command-line interface. (Limited encryption algorithm support from PyPDF2.)
-* [#139](https://github.com/socialcopsdev/camelot/issues/139) Add suppress_warnings keyword argument. [#155](https://github.com/socialcopsdev/camelot/pull/155) by [Jonathan Lloyd](https://github.com/jonathanlloyd).
- * Warnings raised by Camelot can now be suppressed by passing `suppress_warnings=True` to `read_pdf` or `--quiet` to the command-line interface.
-* [#154](https://github.com/socialcopsdev/camelot/issues/154) The CLI can now be run using `python -m`. Try `python -m camelot --help`. [#159](https://github.com/socialcopsdev/camelot/pull/159) by [Parth P Panchal](https://github.com/pqrth).
-* [#165](https://github.com/socialcopsdev/camelot/issues/165) Rename `table_area` to `table_areas`. [#171](https://github.com/socialcopsdev/camelot/pull/171) by [Parth P Panchal](https://github.com/pqrth).
+- [#162](https://github.com/socialcopsdev/camelot/issues/162) Add password keyword argument. [#180](https://github.com/socialcopsdev/camelot/pull/180) by [rbares](https://github.com/rbares).
+ - An encrypted PDF can now be decrypted by passing `password=''` to `read_pdf` or `--password ` to the command-line interface. (Limited encryption algorithm support from PyPDF2.)
+- [#139](https://github.com/socialcopsdev/camelot/issues/139) Add suppress_warnings keyword argument. [#155](https://github.com/socialcopsdev/camelot/pull/155) by [Jonathan Lloyd](https://github.com/jonathanlloyd).
+ - Warnings raised by Camelot can now be suppressed by passing `suppress_warnings=True` to `read_pdf` or `--quiet` to the command-line interface.
+- [#154](https://github.com/socialcopsdev/camelot/issues/154) The CLI can now be run using `python -m`. Try `python -m camelot --help`. [#159](https://github.com/socialcopsdev/camelot/pull/159) by [Parth P Panchal](https://github.com/pqrth).
+- [#165](https://github.com/socialcopsdev/camelot/issues/165) Rename `table_area` to `table_areas`. [#171](https://github.com/socialcopsdev/camelot/pull/171) by [Parth P Panchal](https://github.com/pqrth).
**Bugfixes**
-* Raise error if the ghostscript executable is not on the PATH variable. [#166](https://github.com/socialcopsdev/camelot/pull/166) by Vinayak Mehta.
-* Convert filename to lowercase to check for PDF extension. [#169](https://github.com/socialcopsdev/camelot/pull/169) by [Vinicius Mesel](https://github.com/vmesel).
+- Raise error if the ghostscript executable is not on the PATH variable. [#166](https://github.com/socialcopsdev/camelot/pull/166) by Vinayak Mehta.
+- Convert filename to lowercase to check for PDF extension. [#169](https://github.com/socialcopsdev/camelot/pull/169) by [Vinicius Mesel](https://github.com/vmesel).
**Files**
-* [#114](https://github.com/socialcopsdev/camelot/issues/114) Add Makefile and make codecov run only once. [#132](https://github.com/socialcopsdev/camelot/pull/132) by [Vaibhav Mule](https://github.com/vaibhavmule).
-* Add .editorconfig. [#151](https://github.com/socialcopsdev/camelot/pull/151) by [KOLANICH](https://github.com/KOLANICH).
-* Downgrade numpy version from 1.15.2 to 1.13.3.
-* Add requirements.txt for readthedocs.
+- [#114](https://github.com/socialcopsdev/camelot/issues/114) Add Makefile and make codecov run only once. [#132](https://github.com/socialcopsdev/camelot/pull/132) by [Vaibhav Mule](https://github.com/vaibhavmule).
+- Add .editorconfig. [#151](https://github.com/socialcopsdev/camelot/pull/151) by [KOLANICH](https://github.com/KOLANICH).
+- Downgrade numpy version from 1.15.2 to 1.13.3.
+- Add requirements.txt for readthedocs.
**Documentation**
-* Add "Using conda" section to installation instructions.
-* Add readthedocs badge.
+- Add "Using conda" section to installation instructions.
+- Add readthedocs badge.
-0.2.3 (2018-10-08)
-------------------
+ 0.2.3 (2018-10-08)
-* Remove hard dependencies on requirements versions.
+---
-0.2.2 (2018-10-08)
-------------------
+- Remove hard dependencies on requirements versions.
+
+ 0.2.2 (2018-10-08)
+
+---
**Bugfixes**
-* Move opencv-python to extra\_requires. [#134](https://github.com/socialcopsdev/camelot/pull/134) by Vinayak Mehta.
+- Move opencv-python to extra_requires. [#134](https://github.com/socialcopsdev/camelot/pull/134) by Vinayak Mehta.
-0.2.1 (2018-10-05)
-------------------
+ 0.2.1 (2018-10-05)
+
+---
**Bugfixes**
-* [#121](https://github.com/socialcopsdev/camelot/issues/121) Fix ghostscript subprocess call for Windows. [#124](https://github.com/socialcopsdev/camelot/pull/124) by Vinayak Mehta.
+- [#121](https://github.com/socialcopsdev/camelot/issues/121) Fix ghostscript subprocess call for Windows. [#124](https://github.com/socialcopsdev/camelot/pull/124) by Vinayak Mehta.
**Improvements**
-* [#123](https://github.com/socialcopsdev/camelot/issues/123) Make PEP8 compatible. [#125](https://github.com/socialcopsdev/camelot/pull/125) by [Oshawk](https://github.com/Oshawk).
-* [#110](https://github.com/socialcopsdev/camelot/issues/110) Add more tests. Coverage is now at 84%!
- * Add tests for `__repr__`. [#128](https://github.com/socialcopsdev/camelot/pull/128) by [Vaibhav Mule](https://github.com/vaibhavmule).
- * Add tests for CLI. [#122](https://github.com/socialcopsdev/camelot/pull/122) by [Vaibhav Mule](https://github.com/vaibhavmule) and [#117](https://github.com/socialcopsdev/camelot/pull/117) by Vinayak Mehta.
- * Add tests for errors/warnings. [#113](https://github.com/socialcopsdev/camelot/pull/113) by Vinayak Mehta.
- * Add tests for output formats and parser kwargs. [#126](https://github.com/socialcopsdev/camelot/pull/126) by Vinayak Mehta.
-* Add Python 3.5 and 3.7 support. [#119](https://github.com/socialcopsdev/camelot/pull/119) by Vinayak Mehta.
-* Add logging and warnings.
+- [#123](https://github.com/socialcopsdev/camelot/issues/123) Make PEP8 compatible. [#125](https://github.com/socialcopsdev/camelot/pull/125) by [Oshawk](https://github.com/Oshawk).
+- [#110](https://github.com/socialcopsdev/camelot/issues/110) Add more tests. Coverage is now at 84%!
+ - Add tests for `__repr__`. [#128](https://github.com/socialcopsdev/camelot/pull/128) by [Vaibhav Mule](https://github.com/vaibhavmule).
+ - Add tests for CLI. [#122](https://github.com/socialcopsdev/camelot/pull/122) by [Vaibhav Mule](https://github.com/vaibhavmule) and [#117](https://github.com/socialcopsdev/camelot/pull/117) by Vinayak Mehta.
+ - Add tests for errors/warnings. [#113](https://github.com/socialcopsdev/camelot/pull/113) by Vinayak Mehta.
+ - Add tests for output formats and parser kwargs. [#126](https://github.com/socialcopsdev/camelot/pull/126) by Vinayak Mehta.
+- Add Python 3.5 and 3.7 support. [#119](https://github.com/socialcopsdev/camelot/pull/119) by Vinayak Mehta.
+- Add logging and warnings.
**Documentation**
-* Copyedit all documentation. [#112](https://github.com/socialcopsdev/camelot/pull/112) by [Christine Garcia](https://github.com/christinegarcia).
-* [#115](https://github.com/socialcopsdev/camelot/issues/115) Update issue labels in contributor's guide. [#116](https://github.com/socialcopsdev/camelot/pull/116) by [Johnny Metz](https://github.com/johnnymetz).
-* Update installation instructions for Windows. [#124](https://github.com/socialcopsdev/camelot/pull/124) by Vinayak Mehta.
+- Copyedit all documentation. [#112](https://github.com/socialcopsdev/camelot/pull/112) by [Christine Garcia](https://github.com/christinegarcia).
+- [#115](https://github.com/socialcopsdev/camelot/issues/115) Update issue labels in contributor's guide. [#116](https://github.com/socialcopsdev/camelot/pull/116) by [Johnny Metz](https://github.com/johnnymetz).
+- Update installation instructions for Windows. [#124](https://github.com/socialcopsdev/camelot/pull/124) by Vinayak Mehta.
**Note**: This release also bumps the version for numpy from 1.13.3 to 1.15.2 and adds a MANIFEST.in. Also, openpyxl==2.5.8 is a new requirement and pytest-cov==2.6.0 is a new dev requirement.
-0.2.0 (2018-09-28)
-------------------
+## 0.2.0 (2018-09-28)
**Improvements**
-* [#81](https://github.com/socialcopsdev/camelot/issues/81) Add Python 3.6 support. [#109](https://github.com/socialcopsdev/camelot/pull/109) by Vinayak Mehta.
+- [#81](https://github.com/socialcopsdev/camelot/issues/81) Add Python 3.6 support. [#109](https://github.com/socialcopsdev/camelot/pull/109) by Vinayak Mehta.
-0.1.2 (2018-09-25)
-------------------
+ 0.1.2 (2018-09-25)
+
+---
**Improvements**
-* [#85](https://github.com/socialcopsdev/camelot/issues/85) Add Travis and Codecov.
+- [#85](https://github.com/socialcopsdev/camelot/issues/85) Add Travis and Codecov.
-0.1.1 (2018-09-24)
-------------------
+ 0.1.1 (2018-09-24)
+
+---
**Documentation**
-* Add documentation fixes.
+- Add documentation fixes.
-0.1.0 (2018-09-24)
-------------------
+ 0.1.0 (2018-09-24)
+
+---
-* Rebirth!
+- Rebirth!
diff --git a/MANIFEST.in b/MANIFEST.in
deleted file mode 100644
index 19d196dd..00000000
--- a/MANIFEST.in
+++ /dev/null
@@ -1 +0,0 @@
-include MANIFEST.in README.md HISTORY.md LICENSE setup.py setup.cfg
diff --git a/Makefile b/Makefile
deleted file mode 100644
index 383c8018..00000000
--- a/Makefile
+++ /dev/null
@@ -1,28 +0,0 @@
-.PHONY: docs
-INSTALL :=
-UNAME_S := $(shell uname -s)
-ifeq ($(UNAME_S),Linux)
- INSTALL := @sudo apt install python-tk python3-tk ghostscript
-else ifeq ($(UNAME_S),Darwin)
- INSTALL := @brew install tcl-tk ghostscript
-else
- INSTALL := @echo "Please install tk and ghostscript"
-endif
-
-install:
- $(INSTALL)
- pip install --upgrade pip
- pip install ".[dev]"
-
-test:
- pytest --verbose --cov-config .coveragerc --cov-report term --cov-report xml --cov=camelot --mpl
-
-docs:
- cd docs && make html
- @echo "\033[95m\n\nBuild successful! View the docs homepage at docs/_build/html/index.html.\n\033[0m"
-
-publish:
- pip install twine
- python setup.py sdist
- twine upload dist/*
- rm -fr build dist .egg camelot_py.egg-info
diff --git a/README.md b/README.md
index 9876bb79..74662ef5 100644
--- a/README.md
+++ b/README.md
@@ -5,8 +5,8 @@
# Camelot: PDF Table Extraction for Humans
[![tests](https://github.com/camelot-dev/camelot/actions/workflows/tests.yml/badge.svg)](https://github.com/camelot-dev/camelot/actions/workflows/tests.yml) [![Documentation Status](https://readthedocs.org/projects/camelot-py/badge/?version=master)](https://camelot-py.readthedocs.io/en/master/)
- [![codecov.io](https://codecov.io/github/camelot-dev/camelot/badge.svg?branch=master&service=github)](https://codecov.io/github/camelot-dev/camelot?branch=master)
- [![image](https://img.shields.io/pypi/v/camelot-py.svg)](https://pypi.org/project/camelot-py/) [![image](https://img.shields.io/pypi/l/camelot-py.svg)](https://pypi.org/project/camelot-py/) [![image](https://img.shields.io/pypi/pyversions/camelot-py.svg)](https://pypi.org/project/camelot-py/) [![Gitter chat](https://badges.gitter.im/camelot-dev/Lobby.png)](https://gitter.im/camelot-dev/Lobby)
+[![codecov.io](https://codecov.io/github/camelot-dev/camelot/badge.svg?branch=master&service=github)](https://codecov.io/github/camelot-dev/camelot?branch=master)
+[![image](https://img.shields.io/pypi/v/camelot-py.svg)](https://pypi.org/project/camelot-py/) [![image](https://img.shields.io/pypi/l/camelot-py.svg)](https://pypi.org/project/camelot-py/) [![image](https://img.shields.io/pypi/pyversions/camelot-py.svg)](https://pypi.org/project/camelot-py/) [![Gitter chat](https://badges.gitter.im/camelot-dev/Lobby.png)](https://gitter.im/camelot-dev/Lobby)
[![image](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black)
**Camelot** is a Python library that can help you extract tables from PDFs!
@@ -37,7 +37,7 @@
| Cycle Name | KI (1/km) | Distance (mi) | Percent Fuel Savings | | | |
-|------------|-----------|---------------|----------------------|-----------------|-----------------|----------------|
+| ---------- | --------- | ------------- | -------------------- | --------------- | --------------- | -------------- |
| | | | Improved Speed | Decreased Accel | Eliminate Stops | Decreased Idle |
| 2012_2 | 3.30 | 1.3 | 5.9% | 9.5% | 29.2% | 17.4% |
| 2145_1 | 0.68 | 11.2 | 2.4% | 0.1% | 9.5% | 2.7% |
@@ -104,6 +104,10 @@ The documentation is available at [http://camelot-py.readthedocs.io/](http://cam
- [camelot-php](https://github.com/randomstate/camelot-php) provides a [PHP](https://www.php.net/) wrapper on Camelot.
+## Related projects
+
+- [camelot-sharp](https://github.com/BobLd/camelot-sharp) provides a C sharp implementation of pypdf_table_extraction (Camelot).
+
## Contributing
The [Contributor's Guide](https://camelot-py.readthedocs.io/en/master/dev/contributing.html) has detailed information about contributing issues, documentation, code, and tests.
diff --git a/camelot/__init__.py b/camelot/__init__.py
index bc4beb62..79c5331a 100644
--- a/camelot/__init__.py
+++ b/camelot/__init__.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
import logging
from .__version__ import __version__
diff --git a/camelot/__main__.py b/camelot/__main__.py
old mode 100755
new mode 100644
index a0b82a6b..2dc79401
--- a/camelot/__main__.py
+++ b/camelot/__main__.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-
-
__all__ = ("main",)
diff --git a/camelot/__version__.py b/camelot/__version__.py
index 1e903cd3..9395847c 100644
--- a/camelot/__version__.py
+++ b/camelot/__version__.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
-
-VERSION = (0, 10, 1)
+VERSION = (0, 11, 0)
PRERELEASE = None # alpha, beta or rc
REVISION = None
diff --git a/camelot/backends/__init__.py b/camelot/backends/__init__.py
index 8d0b91e9..3bc50d3a 100644
--- a/camelot/backends/__init__.py
+++ b/camelot/backends/__init__.py
@@ -1,3 +1 @@
-# -*- coding: utf-8 -*-
-
from .image_conversion import ImageConversionBackend
diff --git a/camelot/backends/ghostscript_backend.py b/camelot/backends/ghostscript_backend.py
index 1de7da19..7d4fce3c 100644
--- a/camelot/backends/ghostscript_backend.py
+++ b/camelot/backends/ghostscript_backend.py
@@ -1,7 +1,5 @@
-# -*- coding: utf-8 -*-
-
-import sys
import ctypes
+import sys
from ctypes.util import find_library
@@ -17,7 +15,7 @@ def installed_windows():
return library is not None
-class GhostscriptBackend(object):
+class GhostscriptBackend:
def installed(self):
if sys.platform in ["linux", "darwin"]:
return installed_posix()
diff --git a/camelot/backends/image_conversion.py b/camelot/backends/image_conversion.py
index 7d2c4d7a..7ab2f19c 100644
--- a/camelot/backends/image_conversion.py
+++ b/camelot/backends/image_conversion.py
@@ -1,12 +1,11 @@
-# -*- coding: utf-8 -*-
-
-from .poppler_backend import PopplerBackend
from .ghostscript_backend import GhostscriptBackend
+from .poppler_backend import PopplerBackend
+
BACKENDS = {"poppler": PopplerBackend, "ghostscript": GhostscriptBackend}
-class ImageConversionBackend(object):
+class ImageConversionBackend:
def __init__(self, backend="poppler", use_fallback=True):
if backend not in BACKENDS.keys():
raise ValueError(f"Image conversion backend '{backend}' not supported")
diff --git a/camelot/backends/poppler_backend.py b/camelot/backends/poppler_backend.py
index 41033729..ac41d831 100644
--- a/camelot/backends/poppler_backend.py
+++ b/camelot/backends/poppler_backend.py
@@ -1,12 +1,13 @@
-# -*- coding: utf-8 -*-
-
+import os
+import sys
import shutil
import subprocess
+path = os.path.dirname(sys.executable) + os.pathsep + os.environ['PATH']
-class PopplerBackend(object):
+class PopplerBackend:
def convert(self, pdf_path, png_path):
- pdftopng_executable = shutil.which("pdftopng")
+ pdftopng_executable = shutil.which("pdftopng", path=path)
if pdftopng_executable is None:
raise OSError(
"pdftopng is not installed. You can install it using the 'pip install pdftopng' command."
diff --git a/camelot/cli.py b/camelot/cli.py
index 546a32d8..cc349176 100644
--- a/camelot/cli.py
+++ b/camelot/cli.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
-
import logging
import click
+
try:
import matplotlib.pyplot as plt
except ImportError:
@@ -11,14 +10,16 @@
else:
_HAS_MPL = True
-from . import __version__, read_pdf, plot
+from . import __version__
+from . import plot
+from . import read_pdf
logger = logging.getLogger("camelot")
logger.setLevel(logging.INFO)
-class Config(object):
+class Config:
def __init__(self):
self.config = {}
@@ -38,6 +39,12 @@ def set_config(self, key, value):
default="1",
help="Comma-separated page numbers." " Example: 1,3,4 or 1,4-end or all.",
)
+@click.option(
+ "--parallel",
+ is_flag=True,
+ default=False,
+ help="Read pdf pages in parallel using all CPU cores.",
+)
@click.option("-pw", "--password", help="Password for decryption.")
@click.option("-o", "--output", help="Output file path.")
@click.option(
diff --git a/camelot/core.py b/camelot/core.py
index f951f837..cd9859f7 100644
--- a/camelot/core.py
+++ b/camelot/core.py
@@ -1,10 +1,7 @@
-# -*- coding: utf-8 -*-
-
import os
import sqlite3
-import zipfile
import tempfile
-from itertools import chain
+import zipfile
from operator import itemgetter
import numpy as np
@@ -18,7 +15,7 @@
TABLE_AREA_PADDING = 10
-class TextEdge(object):
+class TextEdge:
"""Defines a text edge coordinates relative to a left-bottom
origin. (PDF coordinate space)
@@ -73,7 +70,7 @@ def update_coords(self, x, y0, edge_tol=50):
self.is_valid = True
-class TextEdges(object):
+class TextEdges:
"""Defines a dict of left, right and middle text edges found on
the PDF page. The dict has three keys based on the alignments,
and each key's value is a list of camelot.core.TextEdge objects.
@@ -228,7 +225,7 @@ def pad(area, average_row_height):
return table_areas_padded
-class Cell(object):
+class Cell:
"""Defines a cell in a table with coordinates relative to a
left-bottom origin. (PDF coordinate space)
@@ -308,7 +305,7 @@ def bound(self):
return self.top + self.bottom + self.left + self.right
-class Table(object):
+class Table:
"""Defines a table with coordinates relative to a left-bottom
origin. (PDF coordinate space)
@@ -341,7 +338,7 @@ def __init__(self, cols, rows):
self.cols = cols
self.rows = rows
self.cells = [[Cell(c[0], r[1], c[1], r[0]) for c in cols] for r in rows]
- self.df = None
+ self.df = pd.DataFrame()
self.shape = (0, 0)
self.accuracy = 0
self.whitespace = 0
@@ -665,7 +662,7 @@ def to_sqlite(self, path, **kwargs):
conn.close()
-class TableList(object):
+class TableList:
"""Defines a list of camelot.core.Table objects. Each table can
be accessed using its index.
@@ -748,8 +745,8 @@ def export(self, path, f="csv", compress=False):
writer = pd.ExcelWriter(filepath)
for table in self._tables:
sheet_name = f"page-{table.page}-table-{table.order}"
- table.df.to_excel(writer, sheet_name=sheet_name, encoding="utf-8")
- writer.save()
+ table.df.to_excel(writer, sheet_name=sheet_name)
+ writer.close()
if compress:
zipname = os.path.join(os.path.dirname(path), root) + ".zip"
with zipfile.ZipFile(zipname, "w", allowZip64=True) as z:
diff --git a/camelot/handlers.py b/camelot/handlers.py
index 101793c3..74ddde7a 100644
--- a/camelot/handlers.py
+++ b/camelot/handlers.py
@@ -1,23 +1,25 @@
-# -*- coding: utf-8 -*-
-
+import multiprocessing as mp
import os
import sys
+from pathlib import Path
+from typing import Union
-from PyPDF2 import PdfFileReader, PdfFileWriter
+from pypdf import PdfReader
+from pypdf import PdfWriter
+from pypdf._utils import StrByteType
from .core import TableList
-from .parsers import Stream, Lattice
-from .utils import (
- TemporaryDirectory,
- get_page_layout,
- get_text_objects,
- get_rotation,
- is_url,
- download_url,
-)
-
-
-class PDFHandler(object):
+from .parsers import Lattice
+from .parsers import Stream
+from .utils import TemporaryDirectory
+from .utils import download_url
+from .utils import get_page_layout
+from .utils import get_rotation
+from .utils import get_text_objects
+from .utils import is_url
+
+
+class PDFHandler:
"""Handles all operations like temp directory creation, splitting
file into single page PDFs, parsing each PDF and then removing the
temp directory.
@@ -34,13 +36,16 @@ class PDFHandler(object):
"""
- def __init__(self, filepath, pages="1", password=None):
- self.filepath = download_url(filepath) if is_url(filepath) else filepath
- if not filepath.lower().endswith(".pdf"):
+ def __init__(self, filepath: Union[StrByteType, Path], pages="1", password=None):
+ if is_url(filepath):
+ filepath = download_url(filepath)
+ self.filepath: Union[StrByteType, Path] = filepath
+
+ if isinstance(filepath, str) and not filepath.lower().endswith(".pdf"):
raise NotImplementedError("File format not supported")
if password is None:
- self.password = ""
+ self.password = "" # noqa: S105
else:
self.password = password
if sys.version_info[0] < 3:
@@ -69,30 +74,29 @@ def _get_pages(self, pages):
if pages == "1":
page_numbers.append({"start": 1, "end": 1})
else:
- with open(self.filepath, "rb") as f:
- infile = PdfFileReader(f, strict=False)
-
- if infile.isEncrypted:
- infile.decrypt(self.password)
-
- if pages == "all":
- page_numbers.append({"start": 1, "end": infile.getNumPages()})
- else:
- for r in pages.split(","):
- if "-" in r:
- a, b = r.split("-")
- if b == "end":
- b = infile.getNumPages()
- page_numbers.append({"start": int(a), "end": int(b)})
- else:
- page_numbers.append({"start": int(r), "end": int(r)})
-
- P = []
+ infile = PdfReader(self.filepath, strict=False)
+
+ if infile.is_encrypted:
+ infile.decrypt(self.password)
+
+ if pages == "all":
+ page_numbers.append({"start": 1, "end": len(infile.pages)})
+ else:
+ for r in pages.split(","):
+ if "-" in r:
+ a, b = r.split("-")
+ if b == "end":
+ b = len(infile.pages)
+ page_numbers.append({"start": int(a), "end": int(b)})
+ else:
+ page_numbers.append({"start": int(r), "end": int(r)})
+
+ result = []
for p in page_numbers:
- P.extend(range(p["start"], p["end"] + 1))
- return sorted(set(P))
+ result.extend(range(p["start"], p["end"] + 1))
+ return sorted(set(result))
- def _save_page(self, filepath, page, temp):
+ def _save_page(self, filepath: Union[StrByteType, Path], page, temp):
"""Saves specified page from PDF into a temporary directory.
Parameters
@@ -105,43 +109,47 @@ def _save_page(self, filepath, page, temp):
Tmp directory.
"""
- with open(filepath, "rb") as fileobj:
- infile = PdfFileReader(fileobj, strict=False)
- if infile.isEncrypted:
+ infile = PdfReader(filepath, strict=False)
+ if infile.is_encrypted:
+ infile.decrypt(self.password)
+ fpath = os.path.join(temp, f"page-{page}.pdf")
+ froot, fext = os.path.splitext(fpath)
+ p = infile.pages[page - 1]
+ outfile = PdfWriter()
+ outfile.add_page(p)
+ with open(fpath, "wb") as f:
+ outfile.write(f)
+ layout, dim = get_page_layout(fpath)
+ # fix rotated PDF
+ chars = get_text_objects(layout, ltype="char")
+ horizontal_text = get_text_objects(layout, ltype="horizontal_text")
+ vertical_text = get_text_objects(layout, ltype="vertical_text")
+ rotation = get_rotation(chars, horizontal_text, vertical_text)
+ if rotation != "":
+ fpath_new = "".join([froot.replace("page", "p"), "_rotated", fext])
+ os.rename(fpath, fpath_new)
+ instream = open(fpath_new, "rb")
+ infile = PdfReader(instream, strict=False)
+ if infile.is_encrypted:
infile.decrypt(self.password)
- fpath = os.path.join(temp, f"page-{page}.pdf")
- froot, fext = os.path.splitext(fpath)
- p = infile.getPage(page - 1)
- outfile = PdfFileWriter()
- outfile.addPage(p)
+ outfile = PdfWriter()
+ p = infile.pages[0]
+ if rotation == "anticlockwise":
+ p.rotate(90)
+ elif rotation == "clockwise":
+ p.rotate(-90)
+ outfile.add_page(p)
with open(fpath, "wb") as f:
outfile.write(f)
- layout, dim = get_page_layout(fpath)
- # fix rotated PDF
- chars = get_text_objects(layout, ltype="char")
- horizontal_text = get_text_objects(layout, ltype="horizontal_text")
- vertical_text = get_text_objects(layout, ltype="vertical_text")
- rotation = get_rotation(chars, horizontal_text, vertical_text)
- if rotation != "":
- fpath_new = "".join([froot.replace("page", "p"), "_rotated", fext])
- os.rename(fpath, fpath_new)
- instream = open(fpath_new, "rb")
- infile = PdfFileReader(instream, strict=False)
- if infile.isEncrypted:
- infile.decrypt(self.password)
- outfile = PdfFileWriter()
- p = infile.getPage(0)
- if rotation == "anticlockwise":
- p.rotateClockwise(90)
- elif rotation == "clockwise":
- p.rotateCounterClockwise(90)
- outfile.addPage(p)
- with open(fpath, "wb") as f:
- outfile.write(f)
- instream.close()
+ instream.close()
def parse(
- self, flavor="lattice", suppress_stdout=False, layout_kwargs={}, **kwargs
+ self,
+ flavor="lattice",
+ suppress_stdout=False,
+ parallel=False,
+ layout_kwargs=None,
+ **kwargs
):
"""Extracts tables by calling parser.get_tables on all single
page PDFs.
@@ -151,10 +159,13 @@ def parse(
flavor : str (default: 'lattice')
The parsing method to use ('lattice' or 'stream').
Lattice is used by default.
- suppress_stdout : str (default: False)
+ suppress_stdout : bool (default: False)
Suppress logs and warnings.
+ parallel : bool (default: False)
+ Process pages in parallel using all available cpu cores.
layout_kwargs : dict, optional (default: {})
- A dict of `pdfminer.layout.LAParams `_ kwargs.
+ A dict of `pdfminer.layout.LAParams
+ `_ kwargs.
kwargs : dict
See camelot.read_pdf kwargs.
@@ -164,15 +175,60 @@ def parse(
List of tables found in PDF.
"""
+ if layout_kwargs is None:
+ layout_kwargs = {}
+
tables = []
+ parser = Lattice(**kwargs) if flavor == "lattice" else Stream(**kwargs)
with TemporaryDirectory() as tempdir:
- for p in self.pages:
- self._save_page(self.filepath, p, tempdir)
- pages = [os.path.join(tempdir, f"page-{p}.pdf") for p in self.pages]
- parser = Lattice(**kwargs) if flavor == "lattice" else Stream(**kwargs)
- for p in pages:
- t = parser.extract_tables(
- p, suppress_stdout=suppress_stdout, layout_kwargs=layout_kwargs
- )
- tables.extend(t)
+ cpu_count = mp.cpu_count()
+ # Using multiprocessing only when cpu_count > 1 to prevent a stallness issue
+ # when cpu_count is 1
+ if parallel and len(self.pages) > 1 and cpu_count > 1:
+ with mp.get_context("spawn").Pool(processes=cpu_count) as pool:
+ jobs = []
+ for p in self.pages:
+ j = pool.apply_async(
+ self._parse_page,(p, tempdir, parser, suppress_stdout, layout_kwargs)
+ )
+ jobs.append(j)
+
+ for j in jobs:
+ t = j.get()
+ tables.extend(t)
+ else:
+ for p in self.pages:
+ t = self._parse_page(p, tempdir, parser, suppress_stdout, layout_kwargs)
+ tables.extend(t)
+
return TableList(sorted(tables))
+
+ def _parse_page(
+ self, page, tempdir, parser, suppress_stdout, layout_kwargs
+ ):
+ """Extracts tables by calling parser.get_tables on a single
+ page PDF.
+
+ Parameters
+ ----------
+ page : str
+ Page number to parse
+ parser : Lattice or Stream
+ The parser to use (Lattice or Stream).
+ suppress_stdout : bool
+ Suppress logs and warnings.
+ layout_kwargs : dict, optional (default: {})
+ A dict of `pdfminer.layout.LAParams `_ kwargs.
+
+ Returns
+ -------
+ tables : camelot.core.TableList
+ List of tables found in PDF.
+
+ """
+ self._save_page(self.filepath, page, tempdir)
+ page_path = os.path.join(tempdir, f"page-{page}.pdf")
+ tables = parser.extract_tables(
+ page_path, suppress_stdout=suppress_stdout, layout_kwargs=layout_kwargs
+ )
+ return tables
diff --git a/camelot/image_processing.py b/camelot/image_processing.py
index 08acb23e..a1fe8034 100644
--- a/camelot/image_processing.py
+++ b/camelot/image_processing.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
import cv2
import numpy as np
diff --git a/camelot/io.py b/camelot/io.py
index a27a7c66..12718828 100644
--- a/camelot/io.py
+++ b/camelot/io.py
@@ -1,18 +1,22 @@
-# -*- coding: utf-8 -*-
-
import warnings
+from pathlib import Path
+from typing import Union
+
+from pypdf._utils import StrByteType
from .handlers import PDFHandler
-from .utils import validate_input, remove_extra
+from .utils import remove_extra
+from .utils import validate_input
def read_pdf(
- filepath,
+ filepath: Union[StrByteType, Path],
pages="1",
password=None,
flavor="lattice",
suppress_stdout=False,
- layout_kwargs={},
+ parallel=False,
+ layout_kwargs=None,
**kwargs
):
"""Read PDF and return extracted tables.
@@ -22,7 +26,7 @@ def read_pdf(
Parameters
----------
- filepath : str
+ filepath : str, Path, IO
Filepath or URL of the PDF file.
pages : str, optional (default: '1')
Comma-separated page numbers.
@@ -34,8 +38,11 @@ def read_pdf(
Lattice is used by default.
suppress_stdout : bool, optional (default: True)
Print all logs and warnings.
+ parallel : bool, optional (default: False)
+ Process pages in parallel using all available cpu cores.
layout_kwargs : dict, optional (default: {})
- A dict of `pdfminer.layout.LAParams `_ kwargs.
+ A dict of `pdfminer.layout.LAParams
+ `_ kwargs.
table_areas : list, optional (default: None)
List of table area strings of the form x1,y1,x2,y2
where (x1, y1) -> left-top and (x2, y2) -> right-bottom
@@ -80,16 +87,19 @@ def read_pdf(
Size of a pixel neighborhood that is used to calculate a
threshold value for the pixel: 3, 5, 7, and so on.
- For more information, refer `OpenCV's adaptiveThreshold `_.
+ For more information, refer `OpenCV's adaptiveThreshold
+ `_.
threshold_constant* : int, optional (default: -2)
Constant subtracted from the mean or weighted mean.
Normally, it is positive but may be zero or negative as well.
- For more information, refer `OpenCV's adaptiveThreshold `_.
+ For more information, refer `OpenCV's adaptiveThreshold
+ `_.
iterations* : int, optional (default: 0)
Number of times for erosion/dilation is applied.
- For more information, refer `OpenCV's dilate `_.
+ For more information, refer `OpenCV's dilate
+ `_.
resolution* : int, optional (default: 300)
Resolution used for PDF to PNG conversion.
@@ -98,6 +108,8 @@ def read_pdf(
tables : camelot.core.TableList
"""
+ if layout_kwargs is None:
+ layout_kwargs = {}
if flavor not in ["lattice", "stream"]:
raise NotImplementedError(
"Unknown flavor specified." " Use either 'lattice' or 'stream'"
@@ -113,6 +125,7 @@ def read_pdf(
tables = p.parse(
flavor=flavor,
suppress_stdout=suppress_stdout,
+ parallel=parallel,
layout_kwargs=layout_kwargs,
**kwargs
)
diff --git a/camelot/parsers/__init__.py b/camelot/parsers/__init__.py
index 5cc66051..9019058f 100644
--- a/camelot/parsers/__init__.py
+++ b/camelot/parsers/__init__.py
@@ -1,4 +1,2 @@
-# -*- coding: utf-8 -*-
-
-from .stream import Stream
from .lattice import Lattice
+from .stream import Stream
diff --git a/camelot/parsers/base.py b/camelot/parsers/base.py
index aeba056f..5e4b35f8 100644
--- a/camelot/parsers/base.py
+++ b/camelot/parsers/base.py
@@ -1,11 +1,10 @@
-# -*- coding: utf-8 -*-
-
import os
-from ..utils import get_page_layout, get_text_objects
+from ..utils import get_page_layout
+from ..utils import get_text_objects
-class BaseParser(object):
+class BaseParser:
"""Defines a base parser."""
def _generate_layout(self, filename, layout_kwargs):
diff --git a/camelot/parsers/lattice.py b/camelot/parsers/lattice.py
index d10bb210..5d3c7710 100644
--- a/camelot/parsers/lattice.py
+++ b/camelot/parsers/lattice.py
@@ -1,34 +1,28 @@
-# -*- coding: utf-8 -*-
-
-import os
-import sys
import copy
import locale
import logging
+import os
+import sys
import warnings
import numpy as np
import pandas as pd
-from .base import BaseParser
-from ..core import Table
-from ..utils import (
- scale_image,
- scale_pdf,
- segments_in_bbox,
- text_in_bbox,
- merge_close_lines,
- get_table_index,
- compute_accuracy,
- compute_whitespace,
-)
-from ..image_processing import (
- adaptive_threshold,
- find_lines,
- find_contours,
- find_joints,
-)
from ..backends.image_conversion import BACKENDS
+from ..core import Table
+from ..image_processing import adaptive_threshold
+from ..image_processing import find_contours
+from ..image_processing import find_joints
+from ..image_processing import find_lines
+from ..utils import compute_accuracy
+from ..utils import compute_whitespace
+from ..utils import get_table_index
+from ..utils import merge_close_lines
+from ..utils import scale_image
+from ..utils import scale_pdf
+from ..utils import segments_in_bbox
+from ..utils import text_in_bbox
+from .base import BaseParser
logger = logging.getLogger("camelot")
@@ -340,7 +334,7 @@ def _generate_table(self, table_idx, cols, rows, **kwargs):
v_s = kwargs.get("v_s")
h_s = kwargs.get("h_s")
if v_s is None or h_s is None:
- raise ValueError("No segments found on {}".format(self.rootname))
+ raise ValueError(f"No segments found on {self.rootname}")
table = Table(cols, rows)
# set table edges to True using ver+hor lines
@@ -363,7 +357,7 @@ def _generate_table(self, table_idx, cols, rows, **kwargs):
flag_size=self.flag_size,
strip_text=self.strip_text,
)
- if indices[:2] != (-1, -1):
+ if indices[0][:2] != (-1, -1):
pos_errors.append(error)
indices = Lattice._reduce_index(
table, indices, shift_text=self.shift_text
@@ -400,7 +394,7 @@ def _generate_table(self, table_idx, cols, rows, **kwargs):
def extract_tables(self, filename, suppress_stdout=False, layout_kwargs={}):
self._generate_layout(filename, layout_kwargs)
if not suppress_stdout:
- logger.info("Processing {}".format(os.path.basename(self.rootname)))
+ logger.info(f"Processing {os.path.basename(self.rootname)}")
if not self.horizontal_text:
if self.images:
@@ -409,9 +403,7 @@ def extract_tables(self, filename, suppress_stdout=False, layout_kwargs={}):
" text-based pages.".format(os.path.basename(self.rootname))
)
else:
- warnings.warn(
- "No tables found on {}".format(os.path.basename(self.rootname))
- )
+ warnings.warn(f"No tables found on {os.path.basename(self.rootname)}")
return []
self.backend.convert(self.filename, self.imagename)
diff --git a/camelot/parsers/stream.py b/camelot/parsers/stream.py
index c7b21daf..266a0e95 100644
--- a/camelot/parsers/stream.py
+++ b/camelot/parsers/stream.py
@@ -1,15 +1,17 @@
-# -*- coding: utf-8 -*-
-
-import os
import logging
+import os
import warnings
import numpy as np
import pandas as pd
+from ..core import Table
+from ..core import TextEdges
+from ..utils import compute_accuracy
+from ..utils import compute_whitespace
+from ..utils import get_table_index
+from ..utils import text_in_bbox
from .base import BaseParser
-from ..core import TextEdges, Table
-from ..utils import text_in_bbox, get_table_index, compute_accuracy, compute_whitespace
logger = logging.getLogger("camelot")
@@ -94,10 +96,15 @@ def _text_bbox(t_bbox):
Tuple (x0, y0, x1, y1) in pdf coordinate space.
"""
- xmin = min([t.x0 for direction in t_bbox for t in t_bbox[direction]])
- ymin = min([t.y0 for direction in t_bbox for t in t_bbox[direction]])
- xmax = max([t.x1 for direction in t_bbox for t in t_bbox[direction]])
- ymax = max([t.y1 for direction in t_bbox for t in t_bbox[direction]])
+ xmin = 0
+ ymin = 0
+ xmax = 0
+ ymax = 0
+ if len([t.x0 for direction in t_bbox for t in t_bbox[direction]]) > 0:
+ xmin = min([t.x0 for direction in t_bbox for t in t_bbox[direction]])
+ ymin = min([t.y0 for direction in t_bbox for t in t_bbox[direction]])
+ xmax = max([t.x1 for direction in t_bbox for t in t_bbox[direction]])
+ ymax = max([t.y1 for direction in t_bbox for t in t_bbox[direction]])
text_bbox = (xmin, ymin, xmax, ymax)
return text_bbox
diff --git a/camelot/plotting.py b/camelot/plotting.py
index f5b6afe9..b602cef6 100644
--- a/camelot/plotting.py
+++ b/camelot/plotting.py
@@ -1,15 +1,13 @@
-# -*- coding: utf-8 -*-
-
try:
- import matplotlib.pyplot as plt
import matplotlib.patches as patches
+ import matplotlib.pyplot as plt
except ImportError:
_HAS_MPL = False
else:
_HAS_MPL = True
-class PlotMethods(object):
+class PlotMethods:
def __call__(self, table, kind="text", filename=None):
"""Plot elements found on PDF page based on kind
specified, useful for debugging and playing with different
diff --git a/camelot/py.typed b/camelot/py.typed
new file mode 100644
index 00000000..e69de29b
diff --git a/camelot/utils.py b/camelot/utils.py
index 21f101ab..680ffaec 100644
--- a/camelot/utils.py
+++ b/camelot/utils.py
@@ -1,35 +1,33 @@
-# -*- coding: utf-8 -*-
-
import os
-import re
import random
+import re
import shutil
import string
import tempfile
import warnings
from itertools import groupby
from operator import itemgetter
+from urllib.parse import urlparse as parse_url
+from urllib.parse import uses_netloc
+from urllib.parse import uses_params
+from urllib.parse import uses_relative
+from urllib.request import Request
+from urllib.request import urlopen
import numpy as np
-from pdfminer.pdfparser import PDFParser
+from pdfminer.converter import PDFPageAggregator
+from pdfminer.layout import LAParams
+from pdfminer.layout import LTAnno
+from pdfminer.layout import LTChar
+from pdfminer.layout import LTImage
+from pdfminer.layout import LTTextLineHorizontal
+from pdfminer.layout import LTTextLineVertical
from pdfminer.pdfdocument import PDFDocument
+from pdfminer.pdfinterp import PDFPageInterpreter
+from pdfminer.pdfinterp import PDFResourceManager
from pdfminer.pdfpage import PDFPage
from pdfminer.pdfpage import PDFTextExtractionNotAllowed
-from pdfminer.pdfinterp import PDFResourceManager
-from pdfminer.pdfinterp import PDFPageInterpreter
-from pdfminer.converter import PDFPageAggregator
-from pdfminer.layout import (
- LAParams,
- LTAnno,
- LTChar,
- LTTextLineHorizontal,
- LTTextLineVertical,
- LTImage,
-)
-
-from urllib.request import Request, urlopen
-from urllib.parse import urlparse as parse_url
-from urllib.parse import uses_relative, uses_netloc, uses_params
+from pdfminer.pdfparser import PDFParser
_VALID_URLS = set(uses_relative + uses_netloc + uses_params)
@@ -138,7 +136,7 @@ def remove_extra(kwargs, flavor="lattice"):
# https://stackoverflow.com/a/22726782
-class TemporaryDirectory(object):
+class TemporaryDirectory:
def __enter__(self):
self.name = tempfile.mkdtemp()
return self.name
@@ -375,8 +373,9 @@ def text_in_bbox(bbox, text):
if ba == bb:
continue
if bbox_intersect(ba, bb):
+ ba_area = bbox_area(ba)
# if the intersection is larger than 80% of ba's size, we keep the longest
- if (bbox_intersection_area(ba, bb) / bbox_area(ba)) > 0.8:
+ if ba_area == 0 or (bbox_intersection_area(ba, bb) / ba_area) > 0.8:
if bbox_longer(bb, ba):
rest.discard(ba)
unique_boxes = list(rest)
@@ -504,7 +503,7 @@ def text_strip(text, strip=""):
return text
stripped = re.sub(
- fr"[{''.join(map(re.escape, strip))}]", "", text, flags=re.UNICODE
+ rf"[{''.join(map(re.escape, strip))}]", "", text, flags=re.UNICODE
)
return stripped
@@ -892,12 +891,14 @@ def get_page_layout(
rsrcmgr = PDFResourceManager()
device = PDFPageAggregator(rsrcmgr, laparams=laparams)
interpreter = PDFPageInterpreter(rsrcmgr, device)
- for page in PDFPage.create_pages(document):
- interpreter.process_page(page)
- layout = device.get_result()
- width = layout.bbox[2]
- height = layout.bbox[3]
- dim = (width, height)
+ page = next(PDFPage.create_pages(document), None)
+ if page is None:
+ raise PDFTextExtractionNotAllowed
+ interpreter.process_page(page)
+ layout = device.get_result()
+ width = layout.bbox[2]
+ height = layout.bbox[3]
+ dim = (width, height)
return layout, dim
diff --git a/codecov.yml b/codecov.yml
new file mode 100644
index 00000000..9ac26504
--- /dev/null
+++ b/codecov.yml
@@ -0,0 +1,9 @@
+comment: false
+coverage:
+ status:
+ project:
+ default:
+ target: "100"
+ patch:
+ default:
+ target: "100"
diff --git a/docs/_static/csv/foo.csv b/docs/_static/csv/foo.csv
index 8e956a3a..b8a245c1 100644
--- a/docs/_static/csv/foo.csv
+++ b/docs/_static/csv/foo.csv
@@ -4,4 +4,4 @@
"2145_1","0.68","11.2","2.4%","0.1%","9.5%","2.7%"
"4234_1","0.59","58.7","8.5%","1.3%","8.5%","3.3%"
"2032_2","0.17","57.8","21.7%","0.3%","2.7%","1.2%"
-"4171_1","0.07","173.9","58.1%","1.6%","2.1%","0.5%"
\ No newline at end of file
+"4171_1","0.07","173.9","58.1%","1.6%","2.1%","0.5%"
diff --git a/docs/_templates/hacks.html b/docs/_templates/hacks.html
index d2253041..960e1725 100644
--- a/docs/_templates/hacks.html
+++ b/docs/_templates/hacks.html
@@ -1,16 +1,30 @@
\ No newline at end of file
+
diff --git a/docs/_templates/sidebarintro.html b/docs/_templates/sidebarintro.html
index e1211001..f4050061 100644
--- a/docs/_templates/sidebarintro.html
+++ b/docs/_templates/sidebarintro.html
@@ -1,16 +1,24 @@
-
+
-
+
Useful Links
diff --git a/docs/_templates/sidebarlogo.html b/docs/_templates/sidebarlogo.html
index b68edea1..43cca367 100644
--- a/docs/_templates/sidebarlogo.html
+++ b/docs/_templates/sidebarlogo.html
@@ -1,9 +1,15 @@
-
+
-
+
diff --git a/docs/_themes/.gitignore b/docs/_themes/.gitignore
index 3072e6ff..52e4e611 100644
--- a/docs/_themes/.gitignore
+++ b/docs/_themes/.gitignore
@@ -1,2 +1,2 @@
*.pyc
-*.pyo
\ No newline at end of file
+*.pyo
diff --git a/docs/_themes/LICENSE b/docs/_themes/LICENSE
index 8756c7a6..8daab7ee 100644
--- a/docs/_themes/LICENSE
+++ b/docs/_themes/LICENSE
@@ -34,4 +34,4 @@ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS THEME, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
+POSSIBILITY OF SUCH DAMAGE.
diff --git a/docs/_themes/flask_theme_support.py b/docs/_themes/flask_theme_support.py
index 90005c68..05af9a9b 100644
--- a/docs/_themes/flask_theme_support.py
+++ b/docs/_themes/flask_theme_support.py
@@ -1,19 +1,17 @@
# flasky pygments style based on tango style
from pygments.style import Style
-from pygments.token import (
- Keyword,
- Name,
- Comment,
- String,
- Error,
- Number,
- Operator,
- Generic,
- Whitespace,
- Punctuation,
- Other,
- Literal,
-)
+from pygments.token import Comment
+from pygments.token import Error
+from pygments.token import Generic
+from pygments.token import Keyword
+from pygments.token import Literal
+from pygments.token import Name
+from pygments.token import Number
+from pygments.token import Operator
+from pygments.token import Other
+from pygments.token import Punctuation
+from pygments.token import String
+from pygments.token import Whitespace
class FlaskyStyle(Style):
diff --git a/docs/api.rst b/docs/api.rst
index a1776ee0..93c2ffa8 100644
--- a/docs/api.rst
+++ b/docs/api.rst
@@ -30,4 +30,4 @@ Lower-Lower-Level Classes
.. autoclass:: camelot.core.Table
:inherited-members:
-.. autoclass:: camelot.core.Cell
\ No newline at end of file
+.. autoclass:: camelot.core.Cell
diff --git a/docs/benchmark/lattice/agstat/agstat-data-pdfplumber.csv b/docs/benchmark/lattice/agstat/agstat-data-pdfplumber.csv
index 7733ba8e..2f5fd7c1 100644
--- a/docs/benchmark/lattice/agstat/agstat-data-pdfplumber.csv
+++ b/docs/benchmark/lattice/agstat/agstat-data-pdfplumber.csv
@@ -39,7 +39,7 @@ ot 4(I
T @
(","menteds, age)nes)
uireg sewastton
-qn h
+qn h
Reudis &ak
al cld L
tnen
diff --git a/docs/benchmark/lattice/column_span_1/column_span_1-data-pdfplumber.csv b/docs/benchmark/lattice/column_span_1/column_span_1-data-pdfplumber.csv
index 040edc53..3a2334a9 100644
--- a/docs/benchmark/lattice/column_span_1/column_span_1-data-pdfplumber.csv
+++ b/docs/benchmark/lattice/column_span_1/column_span_1-data-pdfplumber.csv
@@ -1,9 +1,9 @@
"0","1","2","3","4","5","6","7"
"Rate of Accidental Deaths & Suicides and Population Growth During 1967 to 2013","","","","","","",""
-"Sl.
-No.","Year","Population
-(in Lakh)","Accidental Deaths","","Suicides","","Percentage
-Population
+"Sl.
+No.","Year","Population
+(in Lakh)","Accidental Deaths","","Suicides","","Percentage
+Population
growth"
"","","","Incidence","Rate","Incidence","Rate",""
"(1)","(2)","(3)","(4)","(5)","(6)","(7)","(8)"
diff --git a/docs/benchmark/lattice/rotated/rotated-data-pdfplumber.csv b/docs/benchmark/lattice/rotated/rotated-data-pdfplumber.csv
index e274cc59..5142c439 100644
--- a/docs/benchmark/lattice/rotated/rotated-data-pdfplumber.csv
+++ b/docs/benchmark/lattice/rotated/rotated-data-pdfplumber.csv
@@ -5,14 +5,14 @@ a
ail
v
a
-
+
t
o
n
-
+
a
t
a
D
-
+
*",""
diff --git a/docs/benchmark/stream/district_health/district_health-data-pdfplumber.csv b/docs/benchmark/stream/district_health/district_health-data-pdfplumber.csv
index cc079c09..28e2b155 100644
--- a/docs/benchmark/stream/district_health/district_health-data-pdfplumber.csv
+++ b/docs/benchmark/stream/district_health/district_health-data-pdfplumber.csv
@@ -2,17 +2,17 @@
"","DLHS-4 (2012-13)","","DLHS-3 (2007-08)",""
"Indicators","TOTAL","RURAL","TOTAL","RURAL"
"Child feeding practices (based on last-born child in the reference period) (%)","","","",""
-"Children age 0-5 months exclusively breastfed9 .......................................................................... 76.9 80.0
-Children age 6-9 months receiving solid/semi-solid food and breast milk .................................... 78.6 75.0
-Children age 12-23 months receiving breast feeding along with complementary feeding ........... 31.8 24.2
-Children age 6-35 months exclusively breastfed for at least 6 months ........................................ 4.7 3.4
+"Children age 0-5 months exclusively breastfed9 .......................................................................... 76.9 80.0
+Children age 6-9 months receiving solid/semi-solid food and breast milk .................................... 78.6 75.0
+Children age 12-23 months receiving breast feeding along with complementary feeding ........... 31.8 24.2
+Children age 6-35 months exclusively breastfed for at least 6 months ........................................ 4.7 3.4
Children under 3 years breastfed within one hour of birth ............................................................ 42.9 46.5","","","NA","NA"
"","","","85.9","89.3"
"","","","NA","NA"
"","","","30.0","27.7"
"","","","50.6","52.9"
"Birth Weight (%) (age below 36 months)","","","",""
-"Percentage of Children weighed at birth ...................................................................................... 38.8 41.0 NA NA
+"Percentage of Children weighed at birth ...................................................................................... 38.8 41.0 NA NA
Percentage of Children with low birth weight (out of those who weighted) ( below 2.5 kg) ......... 12.8 14.6 NA NA","","","",""
"Awareness about Diarrhoea (%)","","","",""
"Women know about what to do when a child gets diarrhoea ..................................................... 96.3 96.2","","","94.4","94.2"
@@ -22,45 +22,45 @@ Percentage of Children with low birth weight (out of those who weighted) ( below
"","","","",""
"reference period) (%)","","","",""
"","","","",""
-"Prevalence of diarrhoea in last 2 weeks for under 5 years old children ....................................... 1.6 1.3 6.5 7.0
-Children with diarrhoea in the last 2 weeks and received ORS11 ................................................. 100.0 100.0 54.8 53.3
-Children with diarrhoea in the last 2 weeks and sought advice/treatment ................................... 100.0 50.0 72.9 73.3
-Prevalence of ARI in last 2 weeks for under 5 years old children ............................................ 4.3 3.9 3.9 4.2
-Children with acute respiratory infection or fever in last 2 weeks and sought advice/treatment 37.5 33.3 69.8 68.0
+"Prevalence of diarrhoea in last 2 weeks for under 5 years old children ....................................... 1.6 1.3 6.5 7.0
+Children with diarrhoea in the last 2 weeks and received ORS11 ................................................. 100.0 100.0 54.8 53.3
+Children with diarrhoea in the last 2 weeks and sought advice/treatment ................................... 100.0 50.0 72.9 73.3
+Prevalence of ARI in last 2 weeks for under 5 years old children ............................................ 4.3 3.9 3.9 4.2
+Children with acute respiratory infection or fever in last 2 weeks and sought advice/treatment 37.5 33.3 69.8 68.0
Children with diarrhoea in the last 2 weeks given Zinc along with ORS ...................................... 66.6 50.0 NA NA","","","6.5","7.0"
"","","","54.8","53.3"
"","","","72.9","73.3"
"","","","3.9","4.2"
"","","","69.8","68.0"
"Awareness of RTI/STI and HIV/AIDS (%)","","","",""
-"Women who have heard of RTI/STI ............................................................................................. 55.8 57.1
-Women who have heard of HIV/AIDS .......................................................................................... 98.9 99.0
-Women who have any symptoms of RTI/STI .............................................................................. 13.9 13.5
-Women who know the place to go for testing of HIV/AIDS12 ....................................................... 59.9 57.1
+"Women who have heard of RTI/STI ............................................................................................. 55.8 57.1
+Women who have heard of HIV/AIDS .......................................................................................... 98.9 99.0
+Women who have any symptoms of RTI/STI .............................................................................. 13.9 13.5
+Women who know the place to go for testing of HIV/AIDS12 ....................................................... 59.9 57.1
Women underwent test for detecting HIV/AIDS12 ........................................................................ 37.3 36.8","","","34.8","38.2"
"","","","98.3","98.1"
"","","","15.6","16.1"
"","","","48.6","46.3"
"","","","14.1","12.3"
"Utilization of Government Health Services (%)","","","",""
-"Antenatal care .............................................................................................................................. 69.7 66.7 79.0 81.0
-Treatment for pregnancy complications ....................................................................................... 57.1 59.3 88.0 87.8
-Treatment for post-delivery complications ................................................................................... 33.3 33.3 68.4 68.4
-Treatment for vaginal discharge ................................................................................................... 20.0 25.0 73.9 71.4
-Treatment for children with diarrhoea13 ........................................................................................ 50.0 100.0 NA NA
+"Antenatal care .............................................................................................................................. 69.7 66.7 79.0 81.0
+Treatment for pregnancy complications ....................................................................................... 57.1 59.3 88.0 87.8
+Treatment for post-delivery complications ................................................................................... 33.3 33.3 68.4 68.4
+Treatment for vaginal discharge ................................................................................................... 20.0 25.0 73.9 71.4
+Treatment for children with diarrhoea13 ........................................................................................ 50.0 100.0 NA NA
Treatment for children with ARI13 ................................................................................................. NA NA NA NA","","","79.0","81.0"
"","","","88.0","87.8"
"","","","68.4","68.4"
"","","","73.9","71.4"
"Birth Registration (%)","","","",""
-"Children below age 5 years having birth registration done .......................................................... 40.6 44.3 NA NA
+"Children below age 5 years having birth registration done .......................................................... 40.6 44.3 NA NA
Children below age 5 years who received birth certificate (out of those registered) .................... 65.9 63.6 NA NA","","","",""
"Personal Habits (age 15 years and above) (%)","","","",""
-"Men who use any kind of smokeless tobacco ............................................................................. 74.6 74.2 NA NA
-Women who use any kind of smokeless tobacco ........................................................................ 59.5 58.9 NA NA
-Men who smoke ........................................................................................................................... 56.0 56.4 NA NA
-Women who smoke ...................................................................................................................... 18.4 18.0 NA NA
-Men who consume alcohol ........................................................................................................... 58.4 58.2 NA NA
+"Men who use any kind of smokeless tobacco ............................................................................. 74.6 74.2 NA NA
+Women who use any kind of smokeless tobacco ........................................................................ 59.5 58.9 NA NA
+Men who smoke ........................................................................................................................... 56.0 56.4 NA NA
+Women who smoke ...................................................................................................................... 18.4 18.0 NA NA
+Men who consume alcohol ........................................................................................................... 58.4 58.2 NA NA
Women who consume alcohol ..................................................................................................... 10.9 9.3 NA NA","","","",""
"9 Children Who were given nothing but breast milk till the survey date 10Acute Respiratory Infections11Oral Rehydration Solutions/Salts.12Based on","","","",""
"the women who have heard of HIV/AIDS.13 Last two weeks","","","",""
diff --git a/docs/benchmark/stream/health/health-data-pdfplumber.csv b/docs/benchmark/stream/health/health-data-pdfplumber.csv
index 36c67b0c..ffe14768 100644
--- a/docs/benchmark/stream/health/health-data-pdfplumber.csv
+++ b/docs/benchmark/stream/health/health-data-pdfplumber.csv
@@ -2,8 +2,8 @@
"","Table: 5 Public Health Outlay 2012-13 (Budget Estimates) (Rs. in 000)","","","","","","","","","","","","","","","","","","","","","",""
"","States-A","","","Revenue","","","","","","Capital","","","","","","Total","","","Others(1)","","","Total",""
"","","","","","","","","","","","","","","","","Revenue &","","","","","","",""
-"","","","Medical & Family Medical & Family
-Public Welfare Public Welfare
+"","","","Medical & Family Medical & Family
+Public Welfare Public Welfare
Health Health","","","","","","","","","","","","","","","","","","","",""
"","","","","","","","","","","","","","","","","Capital","","","","","","",""
"","","","","","","","","","","","","","","","","","","","","","","",""
diff --git a/docs/benchmark/stream/missing_values/missing_values-data-pdfplumber.csv b/docs/benchmark/stream/missing_values/missing_values-data-pdfplumber.csv
index e8c5eff6..52fec204 100644
--- a/docs/benchmark/stream/missing_values/missing_values-data-pdfplumber.csv
+++ b/docs/benchmark/stream/missing_values/missing_values-data-pdfplumber.csv
@@ -2,39 +2,39 @@
"","DLHS-4 (2012-13)","","DLHS-3 (2007-08)",""
"Indicators","TOTAL","RURAL","TOTAL","RURAL"
"Reported Prevalence of Morbidity","","","",""
-"Any Injury ..................................................................................................................................... 1.9 2.1
-Acute Illness ................................................................................................................................. 4.5 5.6
+"Any Injury ..................................................................................................................................... 1.9 2.1
+Acute Illness ................................................................................................................................. 4.5 5.6
Chronic Illness .............................................................................................................................. 5.1 4.1","","","",""
"","","","",""
"","","","",""
"Reported Prevalence of Chronic Illness during last one year (%)","","","",""
-"Disease of respiratory system ...................................................................................................... 11.7 15.0
-Disease of cardiovascular system ................................................................................................ 8.9 9.3
+"Disease of respiratory system ...................................................................................................... 11.7 15.0
+Disease of cardiovascular system ................................................................................................ 8.9 9.3
Persons suffering from tuberculosis ............................................................................................. 2.2 1.5","","","",""
"","","","",""
"","","","",""
"Anaemia Status by Haemoglobin Level14 (%)","","","",""
-"Children (6-59 months) having anaemia ...................................................................................... 68.5 71.9
-Children (6-59 months) having severe anaemia .......................................................................... 6.7 9.4
-Children (6-9 Years) having anaemia - Male ................................................................................ 67.1 71.4
-Children (6-9 Years) having severe anaemia - Male .................................................................... 4.4 2.4
-Children (6-9 Years) having anaemia - Female ........................................................................... 52.4 48.8
-Children (6-9 Years) having severe anaemia - Female ................................................................ 1.2 0.0
-Children (6-14 years) having anaemia - Male ............................................................................. 50.8 62.5
-Children (6-14 years) having severe anaemia - Male .................................................................. 3.7 3.6
-Children (6-14 years) having anaemia - Female ......................................................................... 48.3 50.0
-Children (6-14 years) having severe anaemia - Female .............................................................. 4.3 6.1
-Children (10-19 Years15) having anaemia - Male ......................................................................... 37.9 51.2
-Children (10-19 Years15) having severe anaemia - Male ............................................................. 3.5 4.0
-Children (10-19 Years15) having anaemia - Female ..................................................................... 46.6 52.1
-Children (10-19 Years15) having severe anaemia - Female ......................................................... 6.4 6.5
-Adolescents (15-19 years) having anaemia ................................................................................ 39.4 46.5
-Adolescents (15-19 years) having severe anaemia ..................................................................... 5.4 5.1
-Pregnant women (15-49 aged) having anaemia .......................................................................... 48.8 51.5
-Pregnant women (15-49 aged) having severe anaemia .............................................................. 7.1 8.8
-Women (15-49 aged) having anaemia ......................................................................................... 45.2 51.7
-Women (15-49 aged) having severe anaemia ............................................................................. 4.8 5.9
-Persons (20 years and above) having anaemia ........................................................................... 37.8 42.1
+"Children (6-59 months) having anaemia ...................................................................................... 68.5 71.9
+Children (6-59 months) having severe anaemia .......................................................................... 6.7 9.4
+Children (6-9 Years) having anaemia - Male ................................................................................ 67.1 71.4
+Children (6-9 Years) having severe anaemia - Male .................................................................... 4.4 2.4
+Children (6-9 Years) having anaemia - Female ........................................................................... 52.4 48.8
+Children (6-9 Years) having severe anaemia - Female ................................................................ 1.2 0.0
+Children (6-14 years) having anaemia - Male ............................................................................. 50.8 62.5
+Children (6-14 years) having severe anaemia - Male .................................................................. 3.7 3.6
+Children (6-14 years) having anaemia - Female ......................................................................... 48.3 50.0
+Children (6-14 years) having severe anaemia - Female .............................................................. 4.3 6.1
+Children (10-19 Years15) having anaemia - Male ......................................................................... 37.9 51.2
+Children (10-19 Years15) having severe anaemia - Male ............................................................. 3.5 4.0
+Children (10-19 Years15) having anaemia - Female ..................................................................... 46.6 52.1
+Children (10-19 Years15) having severe anaemia - Female ......................................................... 6.4 6.5
+Adolescents (15-19 years) having anaemia ................................................................................ 39.4 46.5
+Adolescents (15-19 years) having severe anaemia ..................................................................... 5.4 5.1
+Pregnant women (15-49 aged) having anaemia .......................................................................... 48.8 51.5
+Pregnant women (15-49 aged) having severe anaemia .............................................................. 7.1 8.8
+Women (15-49 aged) having anaemia ......................................................................................... 45.2 51.7
+Women (15-49 aged) having severe anaemia ............................................................................. 4.8 5.9
+Persons (20 years and above) having anaemia ........................................................................... 37.8 42.1
Persons (20 years and above) having Severe anaemia .............................................................. 4.6 4.8","","","",""
"","","","",""
"","","","",""
@@ -58,12 +58,12 @@ Persons (20 years and above) having Severe anaemia .............................
"","","","",""
"","","","",""
"Blood Sugar Level (age 18 years and above) (%)","","","",""
-"Blood Sugar Level >140 mg/dl (high) ........................................................................................... 12.9 11.1
+"Blood Sugar Level >140 mg/dl (high) ........................................................................................... 12.9 11.1
Blood Sugar Level >160 mg/dl (very high) ................................................................................... 7.0 5.1","","","",""
"","","","",""
"Hypertension (age 18 years and above) (%)","","","",""
-"Above Normal Range (Systolic >140 mm of Hg & Diastolic >90 mm of Hg ) .............................. 23.8 22.8
-Moderately High (Systolic >160 mm of Hg & Diastolic >100 mm of Hg ) ..................................... 8.2 7.1
+"Above Normal Range (Systolic >140 mm of Hg & Diastolic >90 mm of Hg ) .............................. 23.8 22.8
+Moderately High (Systolic >160 mm of Hg & Diastolic >100 mm of Hg ) ..................................... 8.2 7.1
Very High (Systolic >180 mm of Hg & Diastolic >110 mm of Hg ) ............................................... 3.7 3.1","","","",""
"","","","",""
"","","","",""
diff --git a/docs/conf.py b/docs/conf.py
index e3384126..368e0187 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# camelot documentation build configuration file, created by
# sphinx-quickstart on Tue Jul 19 13:44:18 2016.
@@ -15,6 +14,9 @@
import os
import sys
+import camelot
+
+
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
@@ -25,8 +27,6 @@
sys.path.insert(0, os.path.abspath(".."))
sys.path.insert(0, os.path.abspath("_themes"))
-import camelot
-
# -- General configuration ------------------------------------------------
@@ -43,6 +43,8 @@
"sphinx.ext.intersphinx",
"sphinx.ext.todo",
"sphinx.ext.viewcode",
+ "sphinx_click",
+ "myst_parser",
]
# Add any paths that contain templates here, relative to this directory.
@@ -62,9 +64,9 @@
master_doc = "index"
# General information about the project.
-project = u"Camelot"
-copyright = u"2021, Camelot Developers"
-author = u"Vinayak Mehta"
+project = "Camelot"
+copyright = "2021, Camelot Developers"
+author = "Vinayak Mehta"
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
@@ -283,7 +285,7 @@
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
- (master_doc, "Camelot.tex", u"Camelot Documentation", u"Vinayak Mehta", "manual"),
+ (master_doc, "Camelot.tex", "Camelot Documentation", "Vinayak Mehta", "manual"),
]
# The name of an image file (relative to this directory) to place at the top of
@@ -323,7 +325,7 @@
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
-man_pages = [(master_doc, "Camelot", u"Camelot Documentation", [author], 1)]
+man_pages = [(master_doc, "Camelot", "Camelot Documentation", [author], 1)]
# If true, show URL addresses after external links.
#
@@ -339,7 +341,7 @@
(
master_doc,
"Camelot",
- u"Camelot Documentation",
+ "Camelot Documentation",
author,
"Camelot",
"One line description of project.",
diff --git a/docs/dev/contributing.rst b/docs/dev/contributing.rst
index 473741f2..fa8778c4 100644
--- a/docs/dev/contributing.rst
+++ b/docs/dev/contributing.rst
@@ -7,7 +7,7 @@ If you're reading this, you're probably looking to contributing to Camelot. *Tim
This document will help you get started with contributing documentation, code, testing and filing issues. If you have any questions, feel free to reach out to `Vinayak Mehta`_, the author and maintainer.
-.. _Vinayak Mehta: https://www.vinayakmehta.com
+.. _Vinayak Mehta: https://vinayak.io
Code Of Conduct
---------------
@@ -18,7 +18,7 @@ The following quote sums up the **Code Of Conduct**.
Kenneth Reitz has also written an `essay`_ on this topic, which you should read.
-.. _essay: https://www.kennethreitz.org/essays/be-cordial-or-be-on-your-way
+.. _essay: https://kennethreitz.org/essays/2013/01/27/be-cordial-or-be-on-your-way
As the `Requests Code Of Conduct`_ states, **all contributions are welcome**, as long as everyone involved is treated with respect.
@@ -162,4 +162,4 @@ In bug reports, make sure you include:
.. _Creating and highlighting code blocks: https://help.github.com/articles/creating-and-highlighting-code-blocks/
-- A link to the PDF document that you were trying to extract tables from, telling us what you expected the code to do and what actually happened.
\ No newline at end of file
+- A link to the PDF document that you were trying to extract tables from, telling us what you expected the code to do and what actually happened.
diff --git a/docs/requirements.txt b/docs/requirements.txt
new file mode 100644
index 00000000..9017c882
--- /dev/null
+++ b/docs/requirements.txt
@@ -0,0 +1,7 @@
+furo==2023.9.10
+sphinx==7.2.6
+sphinx-click==5.0.1
+myst_parser==2.0.0
+ghostscript==0.7
+opencv-python==4.8.1.78
+matplotlib==3.8.0
diff --git a/docs/user/advanced.rst b/docs/user/advanced.rst
index 7447be27..b9a1ea19 100644
--- a/docs/user/advanced.rst
+++ b/docs/user/advanced.rst
@@ -178,7 +178,7 @@ Finally, let's plot all line intersections present on the table's PDF page.
textedge
^^^^^^^^
-You can also visualize the textedges found on a page by specifying ``kind='textedge'``. To know more about what a "textedge" is, you can see pages 20, 35 and 40 of `Anssi Nurminen's master's thesis `_.
+You can also visualize the textedges found on a page by specifying ``kind='textedge'``. To know more about what a "textedge" is, you can see pages 20, 35 and 40 of `Anssi Nurminen's master's thesis `_.
::
@@ -362,7 +362,7 @@ Improve guessed table areas
While using :ref:`Stream `, automatic table detection can fail for PDFs like `this one `_. That's because the text is relatively far apart vertically, which can lead to shorter textedges being calculated.
-.. note:: To know more about how textedges are calculated to guess table areas, you can see pages 20, 35 and 40 of `Anssi Nurminen's master's thesis `_.
+.. note:: To know more about how textedges are calculated to guess table areas, you can see pages 20, 35 and 40 of `Anssi Nurminen's master's thesis `_.
Let's see the table area that is detected by default.
diff --git a/docs/user/intro.rst b/docs/user/intro.rst
index fad2bf4a..bdd1b5a8 100644
--- a/docs/user/intro.rst
+++ b/docs/user/intro.rst
@@ -12,7 +12,7 @@ At a high level, PostScript defines instructions, such as "place this character
Sadly, a lot of today's open data is trapped in PDF tables.
-.. _PostScript: http://www.planetpdf.com/planetpdf/pdfs/warnock_camelot.pdf
+.. _PostScript: https://web.archive.org/web/20210203041543/http://www.planetpdf.com/planetpdf/pdfs/warnock_camelot.pdf
Why another PDF table extraction library?
-----------------------------------------
@@ -36,11 +36,11 @@ As you can already guess, this library is named after `The Camelot Project`_.
Fun fact: In the British comedy film `Monty Python and the Holy Grail`_ (and in the `Arthurian legend`_ depicted in the film), "Camelot" is the name of the castle where Arthur leads his men, the Knights of the Round Table, and then sets off elsewhere after deciding that it is "a silly place". Interestingly, the language in which this library is written (Python) was named after Monty Python.
-.. _The Camelot Project: http://www.planetpdf.com/planetpdf/pdfs/warnock_camelot.pdf
+.. _The Camelot Project: https://web.archive.org/web/20210203041543/http://www.planetpdf.com/planetpdf/pdfs/warnock_camelot.pdf
.. _Monty Python and the Holy Grail: https://en.wikipedia.org/wiki/Monty_Python_and_the_Holy_Grail
.. _Arthurian legend: https://en.wikipedia.org/wiki/King_Arthur
Camelot License
---------------
- .. include:: ../../LICENSE
\ No newline at end of file
+ .. include:: ../../LICENSE
diff --git a/docs/user/quickstart.rst b/docs/user/quickstart.rst
index ec7410c1..c3cff640 100644
--- a/docs/user/quickstart.rst
+++ b/docs/user/quickstart.rst
@@ -99,6 +99,26 @@ By default, Camelot only uses the first page of the PDF to extract tables. To sp
The ``pages`` keyword argument accepts pages as comma-separated string of page numbers. You can also specify page ranges — for example, ``pages=1,4-10,20-30`` or ``pages=1,4-10,20-end``.
+Extract tables in parallel
+--------------------------
+
+Camelot supports extracting tables in parrallel using all the available CPU cores.
+
+::
+
+ >>> tables = camelot.read_pdf('foo.pdf', page='all', parallel=True)
+ >>> tables
+
+
+.. tip::
+ Here's how you can do the same with the :ref:`command-line interface `.
+ ::
+
+ $ camelot --pages all --parallel lattice foo.pdf
+
+.. note:: The reading of the PDF document is parallelized by processing pages by different CPU core.
+ Therefore, a document with a low page count could be slower to process in parallel.
+
Reading encrypted PDFs
----------------------
@@ -116,7 +136,7 @@ To extract tables from encrypted PDF files you must provide a password when call
$ camelot --password userpass lattice foo.pdf
-Currently Camelot only supports PDFs encrypted with ASCII passwords and algorithm `code 1 or 2`_. An exception is thrown if the PDF cannot be read. This may be due to no password being provided, an incorrect password, or an unsupported encryption algorithm.
+Camelot supports PDFs with all encryption types supported by `pypdf`_. This might require installing PyCryptodome. An exception is thrown if the PDF cannot be read. This may be due to no password being provided, an incorrect password, or an unsupported encryption algorithm.
Further encryption support may be added in future, however in the meantime if your PDF files are using unsupported encryption algorithms you are advised to remove encryption before calling :meth:`read_pdf() `. This can been successfully achieved with third-party tools such as `QPDF`_.
@@ -124,7 +144,7 @@ Further encryption support may be added in future, however in the meantime if yo
$ qpdf --password= --decrypt input.pdf output.pdf
-.. _code 1 or 2: https://github.com/mstamy2/PyPDF2/issues/378
+.. _pypdf: https://pypdf.readthedocs.io/en/latest/user/pdf-version-support.html
.. _QPDF: https://www.github.com/qpdf/qpdf
----
diff --git a/noxfile.py b/noxfile.py
new file mode 100644
index 00000000..0f02b6e1
--- /dev/null
+++ b/noxfile.py
@@ -0,0 +1,254 @@
+"""Nox sessions."""
+import os
+import shlex
+import shutil
+import sys
+from pathlib import Path
+from textwrap import dedent
+
+import nox
+
+
+try:
+ from nox_poetry import Session
+ from nox_poetry import session
+except ImportError:
+ message = f"""\
+ Nox failed to import the 'nox-poetry' package.
+
+ Please install it using the following command:
+
+ {sys.executable} -m pip install nox-poetry"""
+ raise SystemExit(dedent(message)) from None
+
+package = "camelot"
+python_versions = ["3.10", "3.9", "3.8", "3.11"]
+nox.needs_version = ">= 2021.6.6"
+nox.options.sessions = (
+ "pre-commit",
+ "safety",
+ "mypy",
+ "tests",
+ "typeguard",
+ "xdoctest",
+ "docs-build",
+)
+
+
+def activate_virtualenv_in_precommit_hooks(session: Session) -> None:
+ """Activate virtualenv in hooks installed by pre-commit.
+
+ This function patches git hooks installed by pre-commit to activate the
+ session's virtual environment. This allows pre-commit to locate hooks in
+ that environment when invoked from git.
+
+ Args:
+ session: The Session object.
+ """
+ assert session.bin is not None # noqa: S101
+
+ # Only patch hooks containing a reference to this session's bindir. Support
+ # quoting rules for Python and bash, but strip the outermost quotes so we
+ # can detect paths within the bindir, like /python.
+ bindirs = [
+ bindir[1:-1] if bindir[0] in "'\"" else bindir
+ for bindir in (repr(session.bin), shlex.quote(session.bin))
+ ]
+
+ virtualenv = session.env.get("VIRTUAL_ENV")
+ if virtualenv is None:
+ return
+
+ headers = {
+ # pre-commit < 2.16.0
+ "python": f"""\
+ import os
+ os.environ["VIRTUAL_ENV"] = {virtualenv!r}
+ os.environ["PATH"] = os.pathsep.join((
+ {session.bin!r},
+ os.environ.get("PATH", ""),
+ ))
+ """,
+ # pre-commit >= 2.16.0
+ "bash": f"""\
+ VIRTUAL_ENV={shlex.quote(virtualenv)}
+ PATH={shlex.quote(session.bin)}"{os.pathsep}$PATH"
+ """,
+ # pre-commit >= 2.17.0 on Windows forces sh shebang
+ "/bin/sh": f"""\
+ VIRTUAL_ENV={shlex.quote(virtualenv)}
+ PATH={shlex.quote(session.bin)}"{os.pathsep}$PATH"
+ """,
+ }
+
+ hookdir = Path(".git") / "hooks"
+ if not hookdir.is_dir():
+ return
+
+ for hook in hookdir.iterdir():
+ if hook.name.endswith(".sample") or not hook.is_file():
+ continue
+
+ if not hook.read_bytes().startswith(b"#!"):
+ continue
+
+ text = hook.read_text()
+
+ if not any(
+ Path("A") == Path("a") and bindir.lower() in text.lower() or bindir in text
+ for bindir in bindirs
+ ):
+ continue
+
+ lines = text.splitlines()
+
+ for executable, header in headers.items():
+ if executable in lines[0].lower():
+ lines.insert(1, dedent(header))
+ hook.write_text("\n".join(lines))
+ break
+
+
+@session(name="pre-commit", python=python_versions[0])
+def precommit(session: Session) -> None:
+ """Lint using pre-commit."""
+ args = session.posargs or [
+ "run",
+ "--all-files",
+ "--hook-stage=manual",
+ "--show-diff-on-failure",
+ ]
+ session.install(
+ "black",
+ "flake8",
+ "flake8-bandit",
+ "flake8-bugbear",
+ "flake8-docstrings",
+ "flake8-rst-docstrings",
+ "isort",
+ "pep8-naming",
+ "pre-commit",
+ "pre-commit-hooks",
+ "pyupgrade",
+ )
+ session.run("pre-commit", *args)
+ if args and args[0] == "install":
+ activate_virtualenv_in_precommit_hooks(session)
+
+
+@session(python=python_versions[0])
+def safety(session: Session) -> None:
+ """Scan dependencies for insecure packages."""
+ requirements = session.poetry.export_requirements()
+ session.install("safety")
+ session.run("safety", "check", "--full-report", f"--file={requirements}")
+
+
+@session(python=python_versions)
+def mypy(session: Session) -> None:
+ """Type-check using mypy."""
+ args = session.posargs or ["camelot", "tests", "docs/conf.py"]
+ session.install(".")
+ session.install("mypy", "pytest")
+ session.run("mypy", *args)
+ if not session.posargs:
+ session.run("mypy", f"--python-executable={sys.executable}", "noxfile.py")
+
+
+base_requires = ["ghostscript>=0.7", "opencv-python>=3.4.2.17"]
+
+plot_requires = [
+ "matplotlib>=2.2.3",
+]
+
+
+@session(python=python_versions)
+def tests(session: Session) -> None:
+ """Run the test suite."""
+ session.install(".")
+
+ session.install(
+ "coverage[toml]", "pytest", "pygments", *base_requires, *plot_requires
+ )
+ try:
+ session.run("coverage", "run", "--parallel", "-m", "pytest", *session.posargs)
+ finally:
+ if session.interactive:
+ session.notify("coverage", posargs=[])
+
+
+@session(python=python_versions[0])
+def coverage(session: Session) -> None:
+ """Produce the coverage report."""
+ args = session.posargs or ["report", "-i"]
+
+ session.install("coverage[toml]")
+
+ if not session.posargs and any(Path().glob(".coverage.*")):
+ session.run("coverage", "combine")
+
+ session.run("coverage", *args)
+
+
+@session(python=python_versions[0])
+def typeguard(session: Session) -> None:
+ """Runtime type checking using Typeguard."""
+ session.install(".")
+ session.install("pytest", "typeguard", "pygments", *base_requires, *plot_requires)
+ session.run("pytest", f"--typeguard-packages={package}", *session.posargs)
+
+
+@session(python=python_versions)
+def xdoctest(session: Session) -> None:
+ """Run examples with xdoctest."""
+ if session.posargs:
+ args = [package, *session.posargs]
+ else:
+ args = [f"--modname={package}", "--command=all"]
+ if "FORCE_COLOR" in os.environ:
+ args.append("--colored=1")
+
+ session.install(".")
+ session.install("xdoctest[colors]")
+ session.run("python", "-m", "xdoctest", *args)
+
+
+@session(name="docs-build", python=python_versions[0])
+def docs_build(session: Session) -> None:
+ """Build the documentation."""
+ args = session.posargs or ["docs", "docs/_build"]
+ if not session.posargs and "FORCE_COLOR" in os.environ:
+ args.insert(0, "--color")
+
+ session.install(".")
+ session.install(
+ "sphinx", "sphinx-click", "furo", "myst-parser", *base_requires, *plot_requires
+ )
+
+ build_dir = Path("docs", "_build")
+ if build_dir.exists():
+ shutil.rmtree(build_dir)
+
+ session.run("sphinx-build", *args)
+
+
+@session(python=python_versions[0])
+def docs(session: Session) -> None:
+ """Build and serve the documentation with live reloading on file changes."""
+ args = session.posargs or ["--open-browser", "docs", "docs/_build"]
+ session.install(".")
+ session.install(
+ "sphinx",
+ "sphinx-autobuild",
+ "sphinx-click",
+ "furo",
+ "myst-parser",
+ *base_requires,
+ *plot_requires,
+ )
+
+ build_dir = Path("docs", "_build")
+ if build_dir.exists():
+ shutil.rmtree(build_dir)
+
+ session.run("sphinx-autobuild", *args)
diff --git a/poetry.lock b/poetry.lock
new file mode 100644
index 00000000..152a22a7
--- /dev/null
+++ b/poetry.lock
@@ -0,0 +1,2643 @@
+# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand.
+
+[[package]]
+name = "alabaster"
+version = "0.7.13"
+description = "A configurable sidebar-enabled Sphinx theme"
+optional = false
+python-versions = ">=3.6"
+files = [
+ {file = "alabaster-0.7.13-py3-none-any.whl", hash = "sha256:1ee19aca801bbabb5ba3f5f258e4422dfa86f82f3e9cefb0859b283cdd7f62a3"},
+ {file = "alabaster-0.7.13.tar.gz", hash = "sha256:a27a4a084d5e690e16e01e03ad2b2e552c61a65469419b907243193de1a84ae2"},
+]
+
+[[package]]
+name = "attrs"
+version = "23.2.0"
+description = "Classes Without Boilerplate"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"},
+ {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"},
+]
+
+[package.extras]
+cov = ["attrs[tests]", "coverage[toml] (>=5.3)"]
+dev = ["attrs[tests]", "pre-commit"]
+docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"]
+tests = ["attrs[tests-no-zope]", "zope-interface"]
+tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"]
+tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"]
+
+[[package]]
+name = "authlib"
+version = "1.3.0"
+description = "The ultimate Python library in building OAuth and OpenID Connect servers and clients."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "Authlib-1.3.0-py2.py3-none-any.whl", hash = "sha256:9637e4de1fb498310a56900b3e2043a206b03cb11c05422014b0302cbc814be3"},
+ {file = "Authlib-1.3.0.tar.gz", hash = "sha256:959ea62a5b7b5123c5059758296122b57cd2585ae2ed1c0622c21b371ffdae06"},
+]
+
+[package.dependencies]
+cryptography = "*"
+
+[[package]]
+name = "babel"
+version = "2.14.0"
+description = "Internationalization utilities"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "Babel-2.14.0-py3-none-any.whl", hash = "sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287"},
+ {file = "Babel-2.14.0.tar.gz", hash = "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363"},
+]
+
+[package.dependencies]
+pytz = {version = ">=2015.7", markers = "python_version < \"3.9\""}
+
+[package.extras]
+dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"]
+
+[[package]]
+name = "bandit"
+version = "1.7.7"
+description = "Security oriented static analyser for python code."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "bandit-1.7.7-py3-none-any.whl", hash = "sha256:17e60786a7ea3c9ec84569fd5aee09936d116cb0cb43151023258340dbffb7ed"},
+ {file = "bandit-1.7.7.tar.gz", hash = "sha256:527906bec6088cb499aae31bc962864b4e77569e9d529ee51df3a93b4b8ab28a"},
+]
+
+[package.dependencies]
+colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""}
+PyYAML = ">=5.3.1"
+rich = "*"
+stevedore = ">=1.20.0"
+
+[package.extras]
+baseline = ["GitPython (>=3.1.30)"]
+test = ["beautifulsoup4 (>=4.8.0)", "coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "pylint (==1.9.4)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)"]
+toml = ["tomli (>=1.1.0)"]
+yaml = ["PyYAML"]
+
+[[package]]
+name = "beautifulsoup4"
+version = "4.12.3"
+description = "Screen-scraping library"
+optional = false
+python-versions = ">=3.6.0"
+files = [
+ {file = "beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"},
+ {file = "beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051"},
+]
+
+[package.dependencies]
+soupsieve = ">1.2"
+
+[package.extras]
+cchardet = ["cchardet"]
+chardet = ["chardet"]
+charset-normalizer = ["charset-normalizer"]
+html5lib = ["html5lib"]
+lxml = ["lxml"]
+
+[[package]]
+name = "black"
+version = "24.2.0"
+description = "The uncompromising code formatter."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "black-24.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6981eae48b3b33399c8757036c7f5d48a535b962a7c2310d19361edeef64ce29"},
+ {file = "black-24.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d533d5e3259720fdbc1b37444491b024003e012c5173f7d06825a77508085430"},
+ {file = "black-24.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61a0391772490ddfb8a693c067df1ef5227257e72b0e4108482b8d41b5aee13f"},
+ {file = "black-24.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:992e451b04667116680cb88f63449267c13e1ad134f30087dec8527242e9862a"},
+ {file = "black-24.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:163baf4ef40e6897a2a9b83890e59141cc8c2a98f2dda5080dc15c00ee1e62cd"},
+ {file = "black-24.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e37c99f89929af50ffaf912454b3e3b47fd64109659026b678c091a4cd450fb2"},
+ {file = "black-24.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9de21bafcba9683853f6c96c2d515e364aee631b178eaa5145fc1c61a3cc92"},
+ {file = "black-24.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:9db528bccb9e8e20c08e716b3b09c6bdd64da0dd129b11e160bf082d4642ac23"},
+ {file = "black-24.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d84f29eb3ee44859052073b7636533ec995bd0f64e2fb43aeceefc70090e752b"},
+ {file = "black-24.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e08fb9a15c914b81dd734ddd7fb10513016e5ce7e6704bdd5e1251ceee51ac9"},
+ {file = "black-24.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:810d445ae6069ce64030c78ff6127cd9cd178a9ac3361435708b907d8a04c693"},
+ {file = "black-24.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:ba15742a13de85e9b8f3239c8f807723991fbfae24bad92d34a2b12e81904982"},
+ {file = "black-24.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7e53a8c630f71db01b28cd9602a1ada68c937cbf2c333e6ed041390d6968faf4"},
+ {file = "black-24.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:93601c2deb321b4bad8f95df408e3fb3943d85012dddb6121336b8e24a0d1218"},
+ {file = "black-24.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0057f800de6acc4407fe75bb147b0c2b5cbb7c3ed110d3e5999cd01184d53b0"},
+ {file = "black-24.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:faf2ee02e6612577ba0181f4347bcbcf591eb122f7841ae5ba233d12c39dcb4d"},
+ {file = "black-24.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:057c3dc602eaa6fdc451069bd027a1b2635028b575a6c3acfd63193ced20d9c8"},
+ {file = "black-24.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:08654d0797e65f2423f850fc8e16a0ce50925f9337fb4a4a176a7aa4026e63f8"},
+ {file = "black-24.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca610d29415ee1a30a3f30fab7a8f4144e9d34c89a235d81292a1edb2b55f540"},
+ {file = "black-24.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:4dd76e9468d5536abd40ffbc7a247f83b2324f0c050556d9c371c2b9a9a95e31"},
+ {file = "black-24.2.0-py3-none-any.whl", hash = "sha256:e8a6ae970537e67830776488bca52000eaa37fa63b9988e8c487458d9cd5ace6"},
+ {file = "black-24.2.0.tar.gz", hash = "sha256:bce4f25c27c3435e4dace4815bcb2008b87e167e3bf4ee47ccdc5ce906eb4894"},
+]
+
+[package.dependencies]
+click = ">=8.0.0"
+mypy-extensions = ">=0.4.3"
+packaging = ">=22.0"
+pathspec = ">=0.9.0"
+platformdirs = ">=2"
+tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
+typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""}
+
+[package.extras]
+colorama = ["colorama (>=0.4.3)"]
+d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"]
+jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
+uvloop = ["uvloop (>=0.15.2)"]
+
+[[package]]
+name = "certifi"
+version = "2024.2.2"
+description = "Python package for providing Mozilla's CA Bundle."
+optional = false
+python-versions = ">=3.6"
+files = [
+ {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"},
+ {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"},
+]
+
+[[package]]
+name = "cffi"
+version = "1.16.0"
+description = "Foreign Function Interface for Python calling C code."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"},
+ {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"},
+ {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"},
+ {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"},
+ {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"},
+ {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"},
+ {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"},
+ {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"},
+ {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"},
+ {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"},
+ {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"},
+ {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"},
+ {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"},
+ {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"},
+ {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"},
+ {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"},
+ {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"},
+ {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"},
+ {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"},
+ {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"},
+ {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"},
+ {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"},
+ {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"},
+ {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"},
+ {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"},
+ {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"},
+ {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"},
+ {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"},
+ {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"},
+ {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"},
+ {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"},
+ {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"},
+ {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"},
+ {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"},
+ {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"},
+ {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"},
+ {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"},
+ {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"},
+ {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"},
+ {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"},
+ {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"},
+ {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"},
+ {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"},
+ {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"},
+ {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"},
+ {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"},
+ {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"},
+ {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"},
+ {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"},
+ {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"},
+ {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"},
+ {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"},
+]
+
+[package.dependencies]
+pycparser = "*"
+
+[[package]]
+name = "cfgv"
+version = "3.4.0"
+description = "Validate configuration and produce human readable error messages."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"},
+ {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"},
+]
+
+[[package]]
+name = "chardet"
+version = "5.2.0"
+description = "Universal encoding detector for Python 3"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970"},
+ {file = "chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7"},
+]
+
+[[package]]
+name = "charset-normalizer"
+version = "3.3.2"
+description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
+optional = false
+python-versions = ">=3.7.0"
+files = [
+ {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"},
+ {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"},
+]
+
+[[package]]
+name = "click"
+version = "8.1.7"
+description = "Composable command line interface toolkit"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"},
+ {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"},
+]
+
+[package.dependencies]
+colorama = {version = "*", markers = "platform_system == \"Windows\""}
+
+[[package]]
+name = "colorama"
+version = "0.4.6"
+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 = "contourpy"
+version = "1.1.0"
+description = "Python library for calculating contours of 2D quadrilateral grids"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "contourpy-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:89f06eff3ce2f4b3eb24c1055a26981bffe4e7264acd86f15b97e40530b794bc"},
+ {file = "contourpy-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dffcc2ddec1782dd2f2ce1ef16f070861af4fb78c69862ce0aab801495dda6a3"},
+ {file = "contourpy-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25ae46595e22f93592d39a7eac3d638cda552c3e1160255258b695f7b58e5655"},
+ {file = "contourpy-1.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:17cfaf5ec9862bc93af1ec1f302457371c34e688fbd381f4035a06cd47324f48"},
+ {file = "contourpy-1.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18a64814ae7bce73925131381603fff0116e2df25230dfc80d6d690aa6e20b37"},
+ {file = "contourpy-1.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90c81f22b4f572f8a2110b0b741bb64e5a6427e0a198b2cdc1fbaf85f352a3aa"},
+ {file = "contourpy-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:53cc3a40635abedbec7f1bde60f8c189c49e84ac180c665f2cd7c162cc454baa"},
+ {file = "contourpy-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:1f795597073b09d631782e7245016a4323cf1cf0b4e06eef7ea6627e06a37ff2"},
+ {file = "contourpy-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0b7b04ed0961647691cfe5d82115dd072af7ce8846d31a5fac6c142dcce8b882"},
+ {file = "contourpy-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:27bc79200c742f9746d7dd51a734ee326a292d77e7d94c8af6e08d1e6c15d545"},
+ {file = "contourpy-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:052cc634bf903c604ef1a00a5aa093c54f81a2612faedaa43295809ffdde885e"},
+ {file = "contourpy-1.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9382a1c0bc46230fb881c36229bfa23d8c303b889b788b939365578d762b5c18"},
+ {file = "contourpy-1.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5cec36c5090e75a9ac9dbd0ff4a8cf7cecd60f1b6dc23a374c7d980a1cd710e"},
+ {file = "contourpy-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f0cbd657e9bde94cd0e33aa7df94fb73c1ab7799378d3b3f902eb8eb2e04a3a"},
+ {file = "contourpy-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:181cbace49874f4358e2929aaf7ba84006acb76694102e88dd15af861996c16e"},
+ {file = "contourpy-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fb3b7d9e6243bfa1efb93ccfe64ec610d85cfe5aec2c25f97fbbd2e58b531256"},
+ {file = "contourpy-1.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bcb41692aa09aeb19c7c213411854402f29f6613845ad2453d30bf421fe68fed"},
+ {file = "contourpy-1.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5d123a5bc63cd34c27ff9c7ac1cd978909e9c71da12e05be0231c608048bb2ae"},
+ {file = "contourpy-1.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62013a2cf68abc80dadfd2307299bfa8f5aa0dcaec5b2954caeb5fa094171103"},
+ {file = "contourpy-1.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0b6616375d7de55797d7a66ee7d087efe27f03d336c27cf1f32c02b8c1a5ac70"},
+ {file = "contourpy-1.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:317267d915490d1e84577924bd61ba71bf8681a30e0d6c545f577363157e5e94"},
+ {file = "contourpy-1.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d551f3a442655f3dcc1285723f9acd646ca5858834efeab4598d706206b09c9f"},
+ {file = "contourpy-1.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e7a117ce7df5a938fe035cad481b0189049e8d92433b4b33aa7fc609344aafa1"},
+ {file = "contourpy-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:d4f26b25b4f86087e7d75e63212756c38546e70f2a92d2be44f80114826e1cd4"},
+ {file = "contourpy-1.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc00bb4225d57bff7ebb634646c0ee2a1298402ec10a5fe7af79df9a51c1bfd9"},
+ {file = "contourpy-1.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:189ceb1525eb0655ab8487a9a9c41f42a73ba52d6789754788d1883fb06b2d8a"},
+ {file = "contourpy-1.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f2931ed4741f98f74b410b16e5213f71dcccee67518970c42f64153ea9313b9"},
+ {file = "contourpy-1.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:30f511c05fab7f12e0b1b7730ebdc2ec8deedcfb505bc27eb570ff47c51a8f15"},
+ {file = "contourpy-1.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:143dde50520a9f90e4a2703f367cf8ec96a73042b72e68fcd184e1279962eb6f"},
+ {file = "contourpy-1.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e94bef2580e25b5fdb183bf98a2faa2adc5b638736b2c0a4da98691da641316a"},
+ {file = "contourpy-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ed614aea8462735e7d70141374bd7650afd1c3f3cb0c2dbbcbe44e14331bf002"},
+ {file = "contourpy-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:438ba416d02f82b692e371858143970ed2eb6337d9cdbbede0d8ad9f3d7dd17d"},
+ {file = "contourpy-1.1.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a698c6a7a432789e587168573a864a7ea374c6be8d4f31f9d87c001d5a843493"},
+ {file = "contourpy-1.1.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:397b0ac8a12880412da3551a8cb5a187d3298a72802b45a3bd1805e204ad8439"},
+ {file = "contourpy-1.1.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:a67259c2b493b00e5a4d0f7bfae51fb4b3371395e47d079a4446e9b0f4d70e76"},
+ {file = "contourpy-1.1.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2b836d22bd2c7bb2700348e4521b25e077255ebb6ab68e351ab5aa91ca27e027"},
+ {file = "contourpy-1.1.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:084eaa568400cfaf7179b847ac871582199b1b44d5699198e9602ecbbb5f6104"},
+ {file = "contourpy-1.1.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:911ff4fd53e26b019f898f32db0d4956c9d227d51338fb3b03ec72ff0084ee5f"},
+ {file = "contourpy-1.1.0.tar.gz", hash = "sha256:e53046c3863828d21d531cc3b53786e6580eb1ba02477e8681009b6aa0870b21"},
+]
+
+[package.dependencies]
+numpy = ">=1.16"
+
+[package.extras]
+bokeh = ["bokeh", "selenium"]
+docs = ["furo", "sphinx-copybutton"]
+mypy = ["contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.2.0)", "types-Pillow"]
+test = ["Pillow", "contourpy[test-no-images]", "matplotlib"]
+test-no-images = ["pytest", "pytest-cov", "wurlitzer"]
+
+[[package]]
+name = "contourpy"
+version = "1.1.1"
+description = "Python library for calculating contours of 2D quadrilateral grids"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "contourpy-1.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:46e24f5412c948d81736509377e255f6040e94216bf1a9b5ea1eaa9d29f6ec1b"},
+ {file = "contourpy-1.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e48694d6a9c5a26ee85b10130c77a011a4fedf50a7279fa0bdaf44bafb4299d"},
+ {file = "contourpy-1.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a66045af6cf00e19d02191ab578a50cb93b2028c3eefed999793698e9ea768ae"},
+ {file = "contourpy-1.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4ebf42695f75ee1a952f98ce9775c873e4971732a87334b099dde90b6af6a916"},
+ {file = "contourpy-1.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f6aec19457617ef468ff091669cca01fa7ea557b12b59a7908b9474bb9674cf0"},
+ {file = "contourpy-1.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:462c59914dc6d81e0b11f37e560b8a7c2dbab6aca4f38be31519d442d6cde1a1"},
+ {file = "contourpy-1.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6d0a8efc258659edc5299f9ef32d8d81de8b53b45d67bf4bfa3067f31366764d"},
+ {file = "contourpy-1.1.1-cp310-cp310-win32.whl", hash = "sha256:d6ab42f223e58b7dac1bb0af32194a7b9311065583cc75ff59dcf301afd8a431"},
+ {file = "contourpy-1.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:549174b0713d49871c6dee90a4b499d3f12f5e5f69641cd23c50a4542e2ca1eb"},
+ {file = "contourpy-1.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:407d864db716a067cc696d61fa1ef6637fedf03606e8417fe2aeed20a061e6b2"},
+ {file = "contourpy-1.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe80c017973e6a4c367e037cb31601044dd55e6bfacd57370674867d15a899b"},
+ {file = "contourpy-1.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e30aaf2b8a2bac57eb7e1650df1b3a4130e8d0c66fc2f861039d507a11760e1b"},
+ {file = "contourpy-1.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3de23ca4f381c3770dee6d10ead6fff524d540c0f662e763ad1530bde5112532"},
+ {file = "contourpy-1.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:566f0e41df06dfef2431defcfaa155f0acfa1ca4acbf8fd80895b1e7e2ada40e"},
+ {file = "contourpy-1.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b04c2f0adaf255bf756cf08ebef1be132d3c7a06fe6f9877d55640c5e60c72c5"},
+ {file = "contourpy-1.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d0c188ae66b772d9d61d43c6030500344c13e3f73a00d1dc241da896f379bb62"},
+ {file = "contourpy-1.1.1-cp311-cp311-win32.whl", hash = "sha256:0683e1ae20dc038075d92e0e0148f09ffcefab120e57f6b4c9c0f477ec171f33"},
+ {file = "contourpy-1.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:8636cd2fc5da0fb102a2504fa2c4bea3cbc149533b345d72cdf0e7a924decc45"},
+ {file = "contourpy-1.1.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:560f1d68a33e89c62da5da4077ba98137a5e4d3a271b29f2f195d0fba2adcb6a"},
+ {file = "contourpy-1.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:24216552104ae8f3b34120ef84825400b16eb6133af2e27a190fdc13529f023e"},
+ {file = "contourpy-1.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56de98a2fb23025882a18b60c7f0ea2d2d70bbbcfcf878f9067234b1c4818442"},
+ {file = "contourpy-1.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:07d6f11dfaf80a84c97f1a5ba50d129d9303c5b4206f776e94037332e298dda8"},
+ {file = "contourpy-1.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1eaac5257a8f8a047248d60e8f9315c6cff58f7803971170d952555ef6344a7"},
+ {file = "contourpy-1.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19557fa407e70f20bfaba7d55b4d97b14f9480856c4fb65812e8a05fe1c6f9bf"},
+ {file = "contourpy-1.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:081f3c0880712e40effc5f4c3b08feca6d064cb8cfbb372ca548105b86fd6c3d"},
+ {file = "contourpy-1.1.1-cp312-cp312-win32.whl", hash = "sha256:059c3d2a94b930f4dafe8105bcdc1b21de99b30b51b5bce74c753686de858cb6"},
+ {file = "contourpy-1.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:f44d78b61740e4e8c71db1cf1fd56d9050a4747681c59ec1094750a658ceb970"},
+ {file = "contourpy-1.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:70e5a10f8093d228bb2b552beeb318b8928b8a94763ef03b858ef3612b29395d"},
+ {file = "contourpy-1.1.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8394e652925a18ef0091115e3cc191fef350ab6dc3cc417f06da66bf98071ae9"},
+ {file = "contourpy-1.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5bd5680f844c3ff0008523a71949a3ff5e4953eb7701b28760805bc9bcff217"},
+ {file = "contourpy-1.1.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:66544f853bfa85c0d07a68f6c648b2ec81dafd30f272565c37ab47a33b220684"},
+ {file = "contourpy-1.1.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0c02b75acfea5cab07585d25069207e478d12309557f90a61b5a3b4f77f46ce"},
+ {file = "contourpy-1.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41339b24471c58dc1499e56783fedc1afa4bb018bcd035cfb0ee2ad2a7501ef8"},
+ {file = "contourpy-1.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f29fb0b3f1217dfe9362ec55440d0743fe868497359f2cf93293f4b2701b8251"},
+ {file = "contourpy-1.1.1-cp38-cp38-win32.whl", hash = "sha256:f9dc7f933975367251c1b34da882c4f0e0b2e24bb35dc906d2f598a40b72bfc7"},
+ {file = "contourpy-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:498e53573e8b94b1caeb9e62d7c2d053c263ebb6aa259c81050766beb50ff8d9"},
+ {file = "contourpy-1.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ba42e3810999a0ddd0439e6e5dbf6d034055cdc72b7c5c839f37a7c274cb4eba"},
+ {file = "contourpy-1.1.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6c06e4c6e234fcc65435223c7b2a90f286b7f1b2733058bdf1345d218cc59e34"},
+ {file = "contourpy-1.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca6fab080484e419528e98624fb5c4282148b847e3602dc8dbe0cb0669469887"},
+ {file = "contourpy-1.1.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:93df44ab351119d14cd1e6b52a5063d3336f0754b72736cc63db59307dabb718"},
+ {file = "contourpy-1.1.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eafbef886566dc1047d7b3d4b14db0d5b7deb99638d8e1be4e23a7c7ac59ff0f"},
+ {file = "contourpy-1.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efe0fab26d598e1ec07d72cf03eaeeba8e42b4ecf6b9ccb5a356fde60ff08b85"},
+ {file = "contourpy-1.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f08e469821a5e4751c97fcd34bcb586bc243c39c2e39321822060ba902eac49e"},
+ {file = "contourpy-1.1.1-cp39-cp39-win32.whl", hash = "sha256:bfc8a5e9238232a45ebc5cb3bfee71f1167064c8d382cadd6076f0d51cff1da0"},
+ {file = "contourpy-1.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:c84fdf3da00c2827d634de4fcf17e3e067490c4aea82833625c4c8e6cdea0887"},
+ {file = "contourpy-1.1.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:229a25f68046c5cf8067d6d6351c8b99e40da11b04d8416bf8d2b1d75922521e"},
+ {file = "contourpy-1.1.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a10dab5ea1bd4401c9483450b5b0ba5416be799bbd50fc7a6cc5e2a15e03e8a3"},
+ {file = "contourpy-1.1.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:4f9147051cb8fdb29a51dc2482d792b3b23e50f8f57e3720ca2e3d438b7adf23"},
+ {file = "contourpy-1.1.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a75cc163a5f4531a256f2c523bd80db509a49fc23721b36dd1ef2f60ff41c3cb"},
+ {file = "contourpy-1.1.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b53d5769aa1f2d4ea407c65f2d1d08002952fac1d9e9d307aa2e1023554a163"},
+ {file = "contourpy-1.1.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:11b836b7dbfb74e049c302bbf74b4b8f6cb9d0b6ca1bf86cfa8ba144aedadd9c"},
+ {file = "contourpy-1.1.1.tar.gz", hash = "sha256:96ba37c2e24b7212a77da85004c38e7c4d155d3e72a45eeaf22c1f03f607e8ab"},
+]
+
+[package.dependencies]
+numpy = {version = ">=1.16,<2.0", markers = "python_version <= \"3.11\""}
+
+[package.extras]
+bokeh = ["bokeh", "selenium"]
+docs = ["furo", "sphinx (>=7.2)", "sphinx-copybutton"]
+mypy = ["contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.4.1)", "types-Pillow"]
+test = ["Pillow", "contourpy[test-no-images]", "matplotlib"]
+test-no-images = ["pytest", "pytest-cov", "wurlitzer"]
+
+[[package]]
+name = "coverage"
+version = "7.4.3"
+description = "Code coverage measurement for Python"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "coverage-7.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8580b827d4746d47294c0e0b92854c85a92c2227927433998f0d3320ae8a71b6"},
+ {file = "coverage-7.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:718187eeb9849fc6cc23e0d9b092bc2348821c5e1a901c9f8975df0bc785bfd4"},
+ {file = "coverage-7.4.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:767b35c3a246bcb55b8044fd3a43b8cd553dd1f9f2c1eeb87a302b1f8daa0524"},
+ {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae7f19afe0cce50039e2c782bff379c7e347cba335429678450b8fe81c4ef96d"},
+ {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba3a8aaed13770e970b3df46980cb068d1c24af1a1968b7818b69af8c4347efb"},
+ {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ee866acc0861caebb4f2ab79f0b94dbfbdbfadc19f82e6e9c93930f74e11d7a0"},
+ {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:506edb1dd49e13a2d4cac6a5173317b82a23c9d6e8df63efb4f0380de0fbccbc"},
+ {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd6545d97c98a192c5ac995d21c894b581f1fd14cf389be90724d21808b657e2"},
+ {file = "coverage-7.4.3-cp310-cp310-win32.whl", hash = "sha256:f6a09b360d67e589236a44f0c39218a8efba2593b6abdccc300a8862cffc2f94"},
+ {file = "coverage-7.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:18d90523ce7553dd0b7e23cbb28865db23cddfd683a38fb224115f7826de78d0"},
+ {file = "coverage-7.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cbbe5e739d45a52f3200a771c6d2c7acf89eb2524890a4a3aa1a7fa0695d2a47"},
+ {file = "coverage-7.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:489763b2d037b164846ebac0cbd368b8a4ca56385c4090807ff9fad817de4113"},
+ {file = "coverage-7.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:451f433ad901b3bb00184d83fd83d135fb682d780b38af7944c9faeecb1e0bfe"},
+ {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fcc66e222cf4c719fe7722a403888b1f5e1682d1679bd780e2b26c18bb648cdc"},
+ {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3ec74cfef2d985e145baae90d9b1b32f85e1741b04cd967aaf9cfa84c1334f3"},
+ {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:abbbd8093c5229c72d4c2926afaee0e6e3140de69d5dcd918b2921f2f0c8baba"},
+ {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:35eb581efdacf7b7422af677b92170da4ef34500467381e805944a3201df2079"},
+ {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8249b1c7334be8f8c3abcaaa996e1e4927b0e5a23b65f5bf6cfe3180d8ca7840"},
+ {file = "coverage-7.4.3-cp311-cp311-win32.whl", hash = "sha256:cf30900aa1ba595312ae41978b95e256e419d8a823af79ce670835409fc02ad3"},
+ {file = "coverage-7.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:18c7320695c949de11a351742ee001849912fd57e62a706d83dfc1581897fa2e"},
+ {file = "coverage-7.4.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b51bfc348925e92a9bd9b2e48dad13431b57011fd1038f08316e6bf1df107d10"},
+ {file = "coverage-7.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d6cdecaedea1ea9e033d8adf6a0ab11107b49571bbb9737175444cea6eb72328"},
+ {file = "coverage-7.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b2eccb883368f9e972e216c7b4c7c06cabda925b5f06dde0650281cb7666a30"},
+ {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c00cdc8fa4e50e1cc1f941a7f2e3e0f26cb2a1233c9696f26963ff58445bac7"},
+ {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9a4a8dd3dcf4cbd3165737358e4d7dfbd9d59902ad11e3b15eebb6393b0446e"},
+ {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:062b0a75d9261e2f9c6d071753f7eef0fc9caf3a2c82d36d76667ba7b6470003"},
+ {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ebe7c9e67a2d15fa97b77ea6571ce5e1e1f6b0db71d1d5e96f8d2bf134303c1d"},
+ {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c0a120238dd71c68484f02562f6d446d736adcc6ca0993712289b102705a9a3a"},
+ {file = "coverage-7.4.3-cp312-cp312-win32.whl", hash = "sha256:37389611ba54fd6d278fde86eb2c013c8e50232e38f5c68235d09d0a3f8aa352"},
+ {file = "coverage-7.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:d25b937a5d9ffa857d41be042b4238dd61db888533b53bc76dc082cb5a15e914"},
+ {file = "coverage-7.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:28ca2098939eabab044ad68850aac8f8db6bf0b29bc7f2887d05889b17346454"},
+ {file = "coverage-7.4.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:280459f0a03cecbe8800786cdc23067a8fc64c0bd51dc614008d9c36e1659d7e"},
+ {file = "coverage-7.4.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c0cdedd3500e0511eac1517bf560149764b7d8e65cb800d8bf1c63ebf39edd2"},
+ {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a9babb9466fe1da12417a4aed923e90124a534736de6201794a3aea9d98484e"},
+ {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dec9de46a33cf2dd87a5254af095a409ea3bf952d85ad339751e7de6d962cde6"},
+ {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:16bae383a9cc5abab9bb05c10a3e5a52e0a788325dc9ba8499e821885928968c"},
+ {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2c854ce44e1ee31bda4e318af1dbcfc929026d12c5ed030095ad98197eeeaed0"},
+ {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ce8c50520f57ec57aa21a63ea4f325c7b657386b3f02ccaedeccf9ebe27686e1"},
+ {file = "coverage-7.4.3-cp38-cp38-win32.whl", hash = "sha256:708a3369dcf055c00ddeeaa2b20f0dd1ce664eeabde6623e516c5228b753654f"},
+ {file = "coverage-7.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:1bf25fbca0c8d121a3e92a2a0555c7e5bc981aee5c3fdaf4bb7809f410f696b9"},
+ {file = "coverage-7.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b253094dbe1b431d3a4ac2f053b6d7ede2664ac559705a704f621742e034f1f"},
+ {file = "coverage-7.4.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77fbfc5720cceac9c200054b9fab50cb2a7d79660609200ab83f5db96162d20c"},
+ {file = "coverage-7.4.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6679060424faa9c11808598504c3ab472de4531c571ab2befa32f4971835788e"},
+ {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4af154d617c875b52651dd8dd17a31270c495082f3d55f6128e7629658d63765"},
+ {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8640f1fde5e1b8e3439fe482cdc2b0bb6c329f4bb161927c28d2e8879c6029ee"},
+ {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:69b9f6f66c0af29642e73a520b6fed25ff9fd69a25975ebe6acb297234eda501"},
+ {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0842571634f39016a6c03e9d4aba502be652a6e4455fadb73cd3a3a49173e38f"},
+ {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a78ed23b08e8ab524551f52953a8a05d61c3a760781762aac49f8de6eede8c45"},
+ {file = "coverage-7.4.3-cp39-cp39-win32.whl", hash = "sha256:c0524de3ff096e15fcbfe8f056fdb4ea0bf497d584454f344d59fce069d3e6e9"},
+ {file = "coverage-7.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:0209a6369ccce576b43bb227dc8322d8ef9e323d089c6f3f26a597b09cb4d2aa"},
+ {file = "coverage-7.4.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:7cbde573904625509a3f37b6fecea974e363460b556a627c60dc2f47e2fffa51"},
+ {file = "coverage-7.4.3.tar.gz", hash = "sha256:276f6077a5c61447a48d133ed13e759c09e62aff0dc84274a68dc18660104d52"},
+]
+
+[package.dependencies]
+tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""}
+
+[package.extras]
+toml = ["tomli"]
+
+[[package]]
+name = "cryptography"
+version = "42.0.5"
+description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:a30596bae9403a342c978fb47d9b0ee277699fa53bbafad14706af51fe543d16"},
+ {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:b7ffe927ee6531c78f81aa17e684e2ff617daeba7f189f911065b2ea2d526dec"},
+ {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2424ff4c4ac7f6b8177b53c17ed5d8fa74ae5955656867f5a8affaca36a27abb"},
+ {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:329906dcc7b20ff3cad13c069a78124ed8247adcac44b10bea1130e36caae0b4"},
+ {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:b03c2ae5d2f0fc05f9a2c0c997e1bc18c8229f392234e8a0194f202169ccd278"},
+ {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f8837fe1d6ac4a8052a9a8ddab256bc006242696f03368a4009be7ee3075cdb7"},
+ {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:0270572b8bd2c833c3981724b8ee9747b3ec96f699a9665470018594301439ee"},
+ {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:b8cac287fafc4ad485b8a9b67d0ee80c66bf3574f655d3b97ef2e1082360faf1"},
+ {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:16a48c23a62a2f4a285699dba2e4ff2d1cff3115b9df052cdd976a18856d8e3d"},
+ {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2bce03af1ce5a5567ab89bd90d11e7bbdff56b8af3acbbec1faded8f44cb06da"},
+ {file = "cryptography-42.0.5-cp37-abi3-win32.whl", hash = "sha256:b6cd2203306b63e41acdf39aa93b86fb566049aeb6dc489b70e34bcd07adca74"},
+ {file = "cryptography-42.0.5-cp37-abi3-win_amd64.whl", hash = "sha256:98d8dc6d012b82287f2c3d26ce1d2dd130ec200c8679b6213b3c73c08b2b7940"},
+ {file = "cryptography-42.0.5-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:5e6275c09d2badf57aea3afa80d975444f4be8d3bc58f7f80d2a484c6f9485c8"},
+ {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4985a790f921508f36f81831817cbc03b102d643b5fcb81cd33df3fa291a1a1"},
+ {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cde5f38e614f55e28d831754e8a3bacf9ace5d1566235e39d91b35502d6936e"},
+ {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:7367d7b2eca6513681127ebad53b2582911d1736dc2ffc19f2c3ae49997496bc"},
+ {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cd2030f6650c089aeb304cf093f3244d34745ce0cfcc39f20c6fbfe030102e2a"},
+ {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a2913c5375154b6ef2e91c10b5720ea6e21007412f6437504ffea2109b5a33d7"},
+ {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:c41fb5e6a5fe9ebcd58ca3abfeb51dffb5d83d6775405305bfa8715b76521922"},
+ {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3eaafe47ec0d0ffcc9349e1708be2aaea4c6dd4978d76bf6eb0cb2c13636c6fc"},
+ {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1b95b98b0d2af784078fa69f637135e3c317091b615cd0905f8b8a087e86fa30"},
+ {file = "cryptography-42.0.5-cp39-abi3-win32.whl", hash = "sha256:1f71c10d1e88467126f0efd484bd44bca5e14c664ec2ede64c32f20875c0d413"},
+ {file = "cryptography-42.0.5-cp39-abi3-win_amd64.whl", hash = "sha256:a011a644f6d7d03736214d38832e030d8268bcff4a41f728e6030325fea3e400"},
+ {file = "cryptography-42.0.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:9481ffe3cf013b71b2428b905c4f7a9a4f76ec03065b05ff499bb5682a8d9ad8"},
+ {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:ba334e6e4b1d92442b75ddacc615c5476d4ad55cc29b15d590cc6b86efa487e2"},
+ {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:ba3e4a42397c25b7ff88cdec6e2a16c2be18720f317506ee25210f6d31925f9c"},
+ {file = "cryptography-42.0.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:111a0d8553afcf8eb02a4fea6ca4f59d48ddb34497aa8706a6cf536f1a5ec576"},
+ {file = "cryptography-42.0.5-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cd65d75953847815962c84a4654a84850b2bb4aed3f26fadcc1c13892e1e29f6"},
+ {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e807b3188f9eb0eaa7bbb579b462c5ace579f1cedb28107ce8b48a9f7ad3679e"},
+ {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f12764b8fffc7a123f641d7d049d382b73f96a34117e0b637b80643169cec8ac"},
+ {file = "cryptography-42.0.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:37dd623507659e08be98eec89323469e8c7b4c1407c85112634ae3dbdb926fdd"},
+ {file = "cryptography-42.0.5.tar.gz", hash = "sha256:6fe07eec95dfd477eb9530aef5bead34fec819b3aaf6c5bd6d20565da607bfe1"},
+]
+
+[package.dependencies]
+cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""}
+
+[package.extras]
+docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"]
+docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"]
+nox = ["nox"]
+pep8test = ["check-sdist", "click", "mypy", "ruff"]
+sdist = ["build"]
+ssh = ["bcrypt (>=3.1.5)"]
+test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"]
+test-randomorder = ["pytest-randomly"]
+
+[[package]]
+name = "cycler"
+version = "0.12.1"
+description = "Composable style cycles"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30"},
+ {file = "cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c"},
+]
+
+[package.extras]
+docs = ["ipython", "matplotlib", "numpydoc", "sphinx"]
+tests = ["pytest", "pytest-cov", "pytest-xdist"]
+
+[[package]]
+name = "distlib"
+version = "0.3.8"
+description = "Distribution utilities"
+optional = false
+python-versions = "*"
+files = [
+ {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"},
+ {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"},
+]
+
+[[package]]
+name = "docutils"
+version = "0.20.1"
+description = "Docutils -- Python Documentation Utilities"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "docutils-0.20.1-py3-none-any.whl", hash = "sha256:96f387a2c5562db4476f09f13bbab2192e764cac08ebbf3a34a95d9b1e4a59d6"},
+ {file = "docutils-0.20.1.tar.gz", hash = "sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b"},
+]
+
+[[package]]
+name = "dparse"
+version = "0.6.4b0"
+description = "A parser for Python dependency files"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "dparse-0.6.4b0-py3-none-any.whl", hash = "sha256:592ff183348b8a5ea0a18442a7965e29445d3a26063654ec2c7e8ef42cd5753c"},
+ {file = "dparse-0.6.4b0.tar.gz", hash = "sha256:f8d49b41a527f3d16a269f854e6665245b325e50e41d2c213810cb984553e5c8"},
+]
+
+[package.dependencies]
+packaging = "*"
+tomli = {version = "*", markers = "python_version < \"3.11\""}
+
+[package.extras]
+all = ["dparse[conda]", "dparse[pipenv]", "dparse[poetry]"]
+conda = ["pyyaml"]
+pipenv = ["pipenv"]
+poetry = ["poetry"]
+
+[[package]]
+name = "et-xmlfile"
+version = "1.1.0"
+description = "An implementation of lxml.xmlfile for the standard library"
+optional = false
+python-versions = ">=3.6"
+files = [
+ {file = "et_xmlfile-1.1.0-py3-none-any.whl", hash = "sha256:a2ba85d1d6a74ef63837eed693bcb89c3f752169b0e3e7ae5b16ca5e1b3deada"},
+ {file = "et_xmlfile-1.1.0.tar.gz", hash = "sha256:8eb9e2bc2f8c97e37a2dc85a09ecdcdec9d8a396530a6d5a33b30b9a92da0c5c"},
+]
+
+[[package]]
+name = "exceptiongroup"
+version = "1.2.0"
+description = "Backport of PEP 654 (exception groups)"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"},
+ {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"},
+]
+
+[package.extras]
+test = ["pytest (>=6)"]
+
+[[package]]
+name = "filelock"
+version = "3.13.1"
+description = "A platform independent file lock."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "filelock-3.13.1-py3-none-any.whl", hash = "sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c"},
+ {file = "filelock-3.13.1.tar.gz", hash = "sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e"},
+]
+
+[package.extras]
+docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.24)"]
+testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"]
+typing = ["typing-extensions (>=4.8)"]
+
+[[package]]
+name = "flake8"
+version = "5.0.4"
+description = "the modular source code checker: pep8 pyflakes and co"
+optional = false
+python-versions = ">=3.6.1"
+files = [
+ {file = "flake8-5.0.4-py2.py3-none-any.whl", hash = "sha256:7a1cf6b73744f5806ab95e526f6f0d8c01c66d7bbe349562d22dfca20610b248"},
+ {file = "flake8-5.0.4.tar.gz", hash = "sha256:6fbe320aad8d6b95cec8b8e47bc933004678dc63095be98528b7bdd2a9f510db"},
+]
+
+[package.dependencies]
+mccabe = ">=0.7.0,<0.8.0"
+pycodestyle = ">=2.9.0,<2.10.0"
+pyflakes = ">=2.5.0,<2.6.0"
+
+[[package]]
+name = "flake8-bandit"
+version = "4.1.1"
+description = "Automated security testing with bandit and flake8."
+optional = false
+python-versions = ">=3.6"
+files = [
+ {file = "flake8_bandit-4.1.1-py3-none-any.whl", hash = "sha256:4c8a53eb48f23d4ef1e59293657181a3c989d0077c9952717e98a0eace43e06d"},
+ {file = "flake8_bandit-4.1.1.tar.gz", hash = "sha256:068e09287189cbfd7f986e92605adea2067630b75380c6b5733dab7d87f9a84e"},
+]
+
+[package.dependencies]
+bandit = ">=1.7.3"
+flake8 = ">=5.0.0"
+
+[[package]]
+name = "flake8-bugbear"
+version = "23.3.12"
+description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle."
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "flake8-bugbear-23.3.12.tar.gz", hash = "sha256:e3e7f74c8a49ad3794a7183353026dabd68c74030d5f46571f84c1fb0eb79363"},
+ {file = "flake8_bugbear-23.3.12-py3-none-any.whl", hash = "sha256:beb5c7efcd7ccc2039ef66a77bb8db925e7be3531ff1cb4d0b7030d0e2113d72"},
+]
+
+[package.dependencies]
+attrs = ">=19.2.0"
+flake8 = ">=3.0.0"
+
+[package.extras]
+dev = ["coverage", "hypothesis", "hypothesmith (>=0.2)", "pre-commit", "pytest", "tox"]
+
+[[package]]
+name = "flake8-rst-docstrings"
+version = "0.3.0"
+description = "Python docstring reStructuredText (RST) validator for flake8"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "flake8-rst-docstrings-0.3.0.tar.gz", hash = "sha256:d1ce22b4bd37b73cd86b8d980e946ef198cfcc18ed82fedb674ceaa2f8d1afa4"},
+ {file = "flake8_rst_docstrings-0.3.0-py3-none-any.whl", hash = "sha256:f8c3c6892ff402292651c31983a38da082480ad3ba253743de52989bdc84ca1c"},
+]
+
+[package.dependencies]
+flake8 = ">=3"
+pygments = "*"
+restructuredtext-lint = "*"
+
+[package.extras]
+develop = ["build", "twine"]
+
+[[package]]
+name = "fonttools"
+version = "4.49.0"
+description = "Tools to manipulate font files"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "fonttools-4.49.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d970ecca0aac90d399e458f0b7a8a597e08f95de021f17785fb68e2dc0b99717"},
+ {file = "fonttools-4.49.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac9a745b7609f489faa65e1dc842168c18530874a5f5b742ac3dd79e26bca8bc"},
+ {file = "fonttools-4.49.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ba0e00620ca28d4ca11fc700806fd69144b463aa3275e1b36e56c7c09915559"},
+ {file = "fonttools-4.49.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdee3ab220283057e7840d5fb768ad4c2ebe65bdba6f75d5d7bf47f4e0ed7d29"},
+ {file = "fonttools-4.49.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ce7033cb61f2bb65d8849658d3786188afd80f53dad8366a7232654804529532"},
+ {file = "fonttools-4.49.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:07bc5ea02bb7bc3aa40a1eb0481ce20e8d9b9642a9536cde0218290dd6085828"},
+ {file = "fonttools-4.49.0-cp310-cp310-win32.whl", hash = "sha256:86eef6aab7fd7c6c8545f3ebd00fd1d6729ca1f63b0cb4d621bccb7d1d1c852b"},
+ {file = "fonttools-4.49.0-cp310-cp310-win_amd64.whl", hash = "sha256:1fac1b7eebfce75ea663e860e7c5b4a8831b858c17acd68263bc156125201abf"},
+ {file = "fonttools-4.49.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:edc0cce355984bb3c1d1e89d6a661934d39586bb32191ebff98c600f8957c63e"},
+ {file = "fonttools-4.49.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:83a0d9336de2cba86d886507dd6e0153df333ac787377325a39a2797ec529814"},
+ {file = "fonttools-4.49.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:36c8865bdb5cfeec88f5028e7e592370a0657b676c6f1d84a2108e0564f90e22"},
+ {file = "fonttools-4.49.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33037d9e56e2562c710c8954d0f20d25b8386b397250d65581e544edc9d6b942"},
+ {file = "fonttools-4.49.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8fb022d799b96df3eaa27263e9eea306bd3d437cc9aa981820850281a02b6c9a"},
+ {file = "fonttools-4.49.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:33c584c0ef7dc54f5dd4f84082eabd8d09d1871a3d8ca2986b0c0c98165f8e86"},
+ {file = "fonttools-4.49.0-cp311-cp311-win32.whl", hash = "sha256:cbe61b158deb09cffdd8540dc4a948d6e8f4d5b4f3bf5cd7db09bd6a61fee64e"},
+ {file = "fonttools-4.49.0-cp311-cp311-win_amd64.whl", hash = "sha256:fc11e5114f3f978d0cea7e9853627935b30d451742eeb4239a81a677bdee6bf6"},
+ {file = "fonttools-4.49.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d647a0e697e5daa98c87993726da8281c7233d9d4ffe410812a4896c7c57c075"},
+ {file = "fonttools-4.49.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f3bbe672df03563d1f3a691ae531f2e31f84061724c319652039e5a70927167e"},
+ {file = "fonttools-4.49.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bebd91041dda0d511b0d303180ed36e31f4f54b106b1259b69fade68413aa7ff"},
+ {file = "fonttools-4.49.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4145f91531fd43c50f9eb893faa08399816bb0b13c425667c48475c9f3a2b9b5"},
+ {file = "fonttools-4.49.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ea329dafb9670ffbdf4dbc3b0e5c264104abcd8441d56de77f06967f032943cb"},
+ {file = "fonttools-4.49.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c076a9e548521ecc13d944b1d261ff3d7825048c338722a4bd126d22316087b7"},
+ {file = "fonttools-4.49.0-cp312-cp312-win32.whl", hash = "sha256:b607ea1e96768d13be26d2b400d10d3ebd1456343eb5eaddd2f47d1c4bd00880"},
+ {file = "fonttools-4.49.0-cp312-cp312-win_amd64.whl", hash = "sha256:a974c49a981e187381b9cc2c07c6b902d0079b88ff01aed34695ec5360767034"},
+ {file = "fonttools-4.49.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b85ec0bdd7bdaa5c1946398cbb541e90a6dfc51df76dfa88e0aaa41b335940cb"},
+ {file = "fonttools-4.49.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:af20acbe198a8a790618ee42db192eb128afcdcc4e96d99993aca0b60d1faeb4"},
+ {file = "fonttools-4.49.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d418b1fee41a1d14931f7ab4b92dc0bc323b490e41d7a333eec82c9f1780c75"},
+ {file = "fonttools-4.49.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b44a52b8e6244b6548851b03b2b377a9702b88ddc21dcaf56a15a0393d425cb9"},
+ {file = "fonttools-4.49.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7c7125068e04a70739dad11857a4d47626f2b0bd54de39e8622e89701836eabd"},
+ {file = "fonttools-4.49.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:29e89d0e1a7f18bc30f197cfadcbef5a13d99806447c7e245f5667579a808036"},
+ {file = "fonttools-4.49.0-cp38-cp38-win32.whl", hash = "sha256:9d95fa0d22bf4f12d2fb7b07a46070cdfc19ef5a7b1c98bc172bfab5bf0d6844"},
+ {file = "fonttools-4.49.0-cp38-cp38-win_amd64.whl", hash = "sha256:768947008b4dc552d02772e5ebd49e71430a466e2373008ce905f953afea755a"},
+ {file = "fonttools-4.49.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:08877e355d3dde1c11973bb58d4acad1981e6d1140711230a4bfb40b2b937ccc"},
+ {file = "fonttools-4.49.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fdb54b076f25d6b0f0298dc706acee5052de20c83530fa165b60d1f2e9cbe3cb"},
+ {file = "fonttools-4.49.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0af65c720520710cc01c293f9c70bd69684365c6015cc3671db2b7d807fe51f2"},
+ {file = "fonttools-4.49.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f255ce8ed7556658f6d23f6afd22a6d9bbc3edb9b96c96682124dc487e1bf42"},
+ {file = "fonttools-4.49.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d00af0884c0e65f60dfaf9340e26658836b935052fdd0439952ae42e44fdd2be"},
+ {file = "fonttools-4.49.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:263832fae27481d48dfafcc43174644b6706639661e242902ceb30553557e16c"},
+ {file = "fonttools-4.49.0-cp39-cp39-win32.whl", hash = "sha256:0404faea044577a01bb82d47a8fa4bc7a54067fa7e324785dd65d200d6dd1133"},
+ {file = "fonttools-4.49.0-cp39-cp39-win_amd64.whl", hash = "sha256:b050d362df50fc6e38ae3954d8c29bf2da52be384649ee8245fdb5186b620836"},
+ {file = "fonttools-4.49.0-py3-none-any.whl", hash = "sha256:af281525e5dd7fa0b39fb1667b8d5ca0e2a9079967e14c4bfe90fd1cd13e0f18"},
+ {file = "fonttools-4.49.0.tar.gz", hash = "sha256:ebf46e7f01b7af7861310417d7c49591a85d99146fc23a5ba82fdb28af156321"},
+]
+
+[package.extras]
+all = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "fs (>=2.2.0,<3)", "lxml (>=4.0)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres", "pycairo", "scipy", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=15.1.0)", "xattr", "zopfli (>=0.1.4)"]
+graphite = ["lz4 (>=1.7.4.2)"]
+interpolatable = ["munkres", "pycairo", "scipy"]
+lxml = ["lxml (>=4.0)"]
+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.1.0)"]
+woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"]
+
+[[package]]
+name = "furo"
+version = "2024.1.29"
+description = "A clean customisable Sphinx documentation theme."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "furo-2024.1.29-py3-none-any.whl", hash = "sha256:3548be2cef45a32f8cdc0272d415fcb3e5fa6a0eb4ddfe21df3ecf1fe45a13cf"},
+ {file = "furo-2024.1.29.tar.gz", hash = "sha256:4d6b2fe3f10a6e36eb9cc24c1e7beb38d7a23fc7b3c382867503b7fcac8a1e02"},
+]
+
+[package.dependencies]
+beautifulsoup4 = "*"
+pygments = ">=2.7"
+sphinx = ">=6.0,<8.0"
+sphinx-basic-ng = "*"
+
+[[package]]
+name = "ghostscript"
+version = "0.7"
+description = "Interface to the Ghostscript C-API, both high- and low-level, based on ctypes"
+optional = false
+python-versions = "*"
+files = [
+ {file = "ghostscript-0.7-py2.py3-none-any.whl", hash = "sha256:97c70e27ba6b1cab4ab1d9b4cc82d89b8b53e57971f608ded4950b8aa20c78a7"},
+ {file = "ghostscript-0.7.tar.gz", hash = "sha256:b7875a87098740eb0be3de2d9662d15db727305ca9a6d4b7534a3cc33a4b965a"},
+]
+
+[package.dependencies]
+setuptools = ">=38.6.0"
+
+[[package]]
+name = "identify"
+version = "2.5.35"
+description = "File identification library for Python"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "identify-2.5.35-py2.py3-none-any.whl", hash = "sha256:c4de0081837b211594f8e877a6b4fad7ca32bbfc1a9307fdd61c28bfe923f13e"},
+ {file = "identify-2.5.35.tar.gz", hash = "sha256:10a7ca245cfcd756a554a7288159f72ff105ad233c7c4b9c6f0f4d108f5f6791"},
+]
+
+[package.extras]
+license = ["ukkonen"]
+
+[[package]]
+name = "idna"
+version = "3.6"
+description = "Internationalized Domain Names in Applications (IDNA)"
+optional = false
+python-versions = ">=3.5"
+files = [
+ {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"},
+ {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"},
+]
+
+[[package]]
+name = "imagesize"
+version = "1.4.1"
+description = "Getting image size from png/jpeg/jpeg2000/gif file"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+files = [
+ {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"},
+ {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"},
+]
+
+[[package]]
+name = "importlib-metadata"
+version = "7.0.1"
+description = "Read metadata from Python packages"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "importlib_metadata-7.0.1-py3-none-any.whl", hash = "sha256:4805911c3a4ec7c3966410053e9ec6a1fecd629117df5adee56dfc9432a1081e"},
+ {file = "importlib_metadata-7.0.1.tar.gz", hash = "sha256:f238736bb06590ae52ac1fab06a3a9ef1d8dce2b7a35b5ab329371d6c8f5d2cc"},
+]
+
+[package.dependencies]
+zipp = ">=0.5"
+
+[package.extras]
+docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"]
+perf = ["ipython"]
+testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"]
+
+[[package]]
+name = "importlib-resources"
+version = "6.1.2"
+description = "Read resources from Python packages"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "importlib_resources-6.1.2-py3-none-any.whl", hash = "sha256:9a0a862501dc38b68adebc82970140c9e4209fc99601782925178f8386339938"},
+ {file = "importlib_resources-6.1.2.tar.gz", hash = "sha256:308abf8474e2dba5f867d279237cd4076482c3de7104a40b41426370e891549b"},
+]
+
+[package.dependencies]
+zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""}
+
+[package.extras]
+docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"]
+testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)", "zipp (>=3.17)"]
+
+[[package]]
+name = "iniconfig"
+version = "2.0.0"
+description = "brain-dead simple config-ini parsing"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"},
+ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
+]
+
+[[package]]
+name = "isort"
+version = "5.13.2"
+description = "A Python utility / library to sort Python imports."
+optional = false
+python-versions = ">=3.8.0"
+files = [
+ {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"},
+ {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"},
+]
+
+[package.extras]
+colors = ["colorama (>=0.4.6)"]
+
+[[package]]
+name = "jinja2"
+version = "3.1.3"
+description = "A very fast and expressive template engine."
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"},
+ {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"},
+]
+
+[package.dependencies]
+MarkupSafe = ">=2.0"
+
+[package.extras]
+i18n = ["Babel (>=2.7)"]
+
+[[package]]
+name = "kiwisolver"
+version = "1.4.5"
+description = "A fast implementation of the Cassowary constraint solver"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "kiwisolver-1.4.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:05703cf211d585109fcd72207a31bb170a0f22144d68298dc5e61b3c946518af"},
+ {file = "kiwisolver-1.4.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:146d14bebb7f1dc4d5fbf74f8a6cb15ac42baadee8912eb84ac0b3b2a3dc6ac3"},
+ {file = "kiwisolver-1.4.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ef7afcd2d281494c0a9101d5c571970708ad911d028137cd558f02b851c08b4"},
+ {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9eaa8b117dc8337728e834b9c6e2611f10c79e38f65157c4c38e9400286f5cb1"},
+ {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ec20916e7b4cbfb1f12380e46486ec4bcbaa91a9c448b97023fde0d5bbf9e4ff"},
+ {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39b42c68602539407884cf70d6a480a469b93b81b7701378ba5e2328660c847a"},
+ {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa12042de0171fad672b6c59df69106d20d5596e4f87b5e8f76df757a7c399aa"},
+ {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a40773c71d7ccdd3798f6489aaac9eee213d566850a9533f8d26332d626b82c"},
+ {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:19df6e621f6d8b4b9c4d45f40a66839294ff2bb235e64d2178f7522d9170ac5b"},
+ {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:83d78376d0d4fd884e2c114d0621624b73d2aba4e2788182d286309ebdeed770"},
+ {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e391b1f0a8a5a10ab3b9bb6afcfd74f2175f24f8975fb87ecae700d1503cdee0"},
+ {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:852542f9481f4a62dbb5dd99e8ab7aedfeb8fb6342349a181d4036877410f525"},
+ {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59edc41b24031bc25108e210c0def6f6c2191210492a972d585a06ff246bb79b"},
+ {file = "kiwisolver-1.4.5-cp310-cp310-win32.whl", hash = "sha256:a6aa6315319a052b4ee378aa171959c898a6183f15c1e541821c5c59beaa0238"},
+ {file = "kiwisolver-1.4.5-cp310-cp310-win_amd64.whl", hash = "sha256:d0ef46024e6a3d79c01ff13801cb19d0cad7fd859b15037aec74315540acc276"},
+ {file = "kiwisolver-1.4.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:11863aa14a51fd6ec28688d76f1735f8f69ab1fabf388851a595d0721af042f5"},
+ {file = "kiwisolver-1.4.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8ab3919a9997ab7ef2fbbed0cc99bb28d3c13e6d4b1ad36e97e482558a91be90"},
+ {file = "kiwisolver-1.4.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fcc700eadbbccbf6bc1bcb9dbe0786b4b1cb91ca0dcda336eef5c2beed37b797"},
+ {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dfdd7c0b105af050eb3d64997809dc21da247cf44e63dc73ff0fd20b96be55a9"},
+ {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76c6a5964640638cdeaa0c359382e5703e9293030fe730018ca06bc2010c4437"},
+ {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbea0db94288e29afcc4c28afbf3a7ccaf2d7e027489c449cf7e8f83c6346eb9"},
+ {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ceec1a6bc6cab1d6ff5d06592a91a692f90ec7505d6463a88a52cc0eb58545da"},
+ {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:040c1aebeda72197ef477a906782b5ab0d387642e93bda547336b8957c61022e"},
+ {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f91de7223d4c7b793867797bacd1ee53bfe7359bd70d27b7b58a04efbb9436c8"},
+ {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:faae4860798c31530dd184046a900e652c95513796ef51a12bc086710c2eec4d"},
+ {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:b0157420efcb803e71d1b28e2c287518b8808b7cf1ab8af36718fd0a2c453eb0"},
+ {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:06f54715b7737c2fecdbf140d1afb11a33d59508a47bf11bb38ecf21dc9ab79f"},
+ {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fdb7adb641a0d13bdcd4ef48e062363d8a9ad4a182ac7647ec88f695e719ae9f"},
+ {file = "kiwisolver-1.4.5-cp311-cp311-win32.whl", hash = "sha256:bb86433b1cfe686da83ce32a9d3a8dd308e85c76b60896d58f082136f10bffac"},
+ {file = "kiwisolver-1.4.5-cp311-cp311-win_amd64.whl", hash = "sha256:6c08e1312a9cf1074d17b17728d3dfce2a5125b2d791527f33ffbe805200a355"},
+ {file = "kiwisolver-1.4.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:32d5cf40c4f7c7b3ca500f8985eb3fb3a7dfc023215e876f207956b5ea26632a"},
+ {file = "kiwisolver-1.4.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f846c260f483d1fd217fe5ed7c173fb109efa6b1fc8381c8b7552c5781756192"},
+ {file = "kiwisolver-1.4.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5ff5cf3571589b6d13bfbfd6bcd7a3f659e42f96b5fd1c4830c4cf21d4f5ef45"},
+ {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7269d9e5f1084a653d575c7ec012ff57f0c042258bf5db0954bf551c158466e7"},
+ {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da802a19d6e15dffe4b0c24b38b3af68e6c1a68e6e1d8f30148c83864f3881db"},
+ {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3aba7311af82e335dd1e36ffff68aaca609ca6290c2cb6d821a39aa075d8e3ff"},
+ {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:763773d53f07244148ccac5b084da5adb90bfaee39c197554f01b286cf869228"},
+ {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2270953c0d8cdab5d422bee7d2007f043473f9d2999631c86a223c9db56cbd16"},
+ {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d099e745a512f7e3bbe7249ca835f4d357c586d78d79ae8f1dcd4d8adeb9bda9"},
+ {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:74db36e14a7d1ce0986fa104f7d5637aea5c82ca6326ed0ec5694280942d1162"},
+ {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e5bab140c309cb3a6ce373a9e71eb7e4873c70c2dda01df6820474f9889d6d4"},
+ {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0f114aa76dc1b8f636d077979c0ac22e7cd8f3493abbab152f20eb8d3cda71f3"},
+ {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:88a2df29d4724b9237fc0c6eaf2a1adae0cdc0b3e9f4d8e7dc54b16812d2d81a"},
+ {file = "kiwisolver-1.4.5-cp312-cp312-win32.whl", hash = "sha256:72d40b33e834371fd330fb1472ca19d9b8327acb79a5821d4008391db8e29f20"},
+ {file = "kiwisolver-1.4.5-cp312-cp312-win_amd64.whl", hash = "sha256:2c5674c4e74d939b9d91dda0fae10597ac7521768fec9e399c70a1f27e2ea2d9"},
+ {file = "kiwisolver-1.4.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3a2b053a0ab7a3960c98725cfb0bf5b48ba82f64ec95fe06f1d06c99b552e130"},
+ {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cd32d6c13807e5c66a7cbb79f90b553642f296ae4518a60d8d76243b0ad2898"},
+ {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59ec7b7c7e1a61061850d53aaf8e93db63dce0c936db1fda2658b70e4a1be709"},
+ {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da4cfb373035def307905d05041c1d06d8936452fe89d464743ae7fb8371078b"},
+ {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2400873bccc260b6ae184b2b8a4fec0e4082d30648eadb7c3d9a13405d861e89"},
+ {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1b04139c4236a0f3aff534479b58f6f849a8b351e1314826c2d230849ed48985"},
+ {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:4e66e81a5779b65ac21764c295087de82235597a2293d18d943f8e9e32746265"},
+ {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:7931d8f1f67c4be9ba1dd9c451fb0eeca1a25b89e4d3f89e828fe12a519b782a"},
+ {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:b3f7e75f3015df442238cca659f8baa5f42ce2a8582727981cbfa15fee0ee205"},
+ {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:bbf1d63eef84b2e8c89011b7f2235b1e0bf7dacc11cac9431fc6468e99ac77fb"},
+ {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:4c380469bd3f970ef677bf2bcba2b6b0b4d5c75e7a020fb863ef75084efad66f"},
+ {file = "kiwisolver-1.4.5-cp37-cp37m-win32.whl", hash = "sha256:9408acf3270c4b6baad483865191e3e582b638b1654a007c62e3efe96f09a9a3"},
+ {file = "kiwisolver-1.4.5-cp37-cp37m-win_amd64.whl", hash = "sha256:5b94529f9b2591b7af5f3e0e730a4e0a41ea174af35a4fd067775f9bdfeee01a"},
+ {file = "kiwisolver-1.4.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:11c7de8f692fc99816e8ac50d1d1aef4f75126eefc33ac79aac02c099fd3db71"},
+ {file = "kiwisolver-1.4.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:53abb58632235cd154176ced1ae8f0d29a6657aa1aa9decf50b899b755bc2b93"},
+ {file = "kiwisolver-1.4.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:88b9f257ca61b838b6f8094a62418421f87ac2a1069f7e896c36a7d86b5d4c29"},
+ {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3195782b26fc03aa9c6913d5bad5aeb864bdc372924c093b0f1cebad603dd712"},
+ {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc579bf0f502e54926519451b920e875f433aceb4624a3646b3252b5caa9e0b6"},
+ {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a580c91d686376f0f7c295357595c5a026e6cbc3d77b7c36e290201e7c11ecb"},
+ {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cfe6ab8da05c01ba6fbea630377b5da2cd9bcbc6338510116b01c1bc939a2c18"},
+ {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:d2e5a98f0ec99beb3c10e13b387f8db39106d53993f498b295f0c914328b1333"},
+ {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a51a263952b1429e429ff236d2f5a21c5125437861baeed77f5e1cc2d2c7c6da"},
+ {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3edd2fa14e68c9be82c5b16689e8d63d89fe927e56debd6e1dbce7a26a17f81b"},
+ {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:74d1b44c6cfc897df648cc9fdaa09bc3e7679926e6f96df05775d4fb3946571c"},
+ {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:76d9289ed3f7501012e05abb8358bbb129149dbd173f1f57a1bf1c22d19ab7cc"},
+ {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:92dea1ffe3714fa8eb6a314d2b3c773208d865a0e0d35e713ec54eea08a66250"},
+ {file = "kiwisolver-1.4.5-cp38-cp38-win32.whl", hash = "sha256:5c90ae8c8d32e472be041e76f9d2f2dbff4d0b0be8bd4041770eddb18cf49a4e"},
+ {file = "kiwisolver-1.4.5-cp38-cp38-win_amd64.whl", hash = "sha256:c7940c1dc63eb37a67721b10d703247552416f719c4188c54e04334321351ced"},
+ {file = "kiwisolver-1.4.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9407b6a5f0d675e8a827ad8742e1d6b49d9c1a1da5d952a67d50ef5f4170b18d"},
+ {file = "kiwisolver-1.4.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15568384086b6df3c65353820a4473575dbad192e35010f622c6ce3eebd57af9"},
+ {file = "kiwisolver-1.4.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0dc9db8e79f0036e8173c466d21ef18e1befc02de8bf8aa8dc0813a6dc8a7046"},
+ {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cdc8a402aaee9a798b50d8b827d7ecf75edc5fb35ea0f91f213ff927c15f4ff0"},
+ {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6c3bd3cde54cafb87d74d8db50b909705c62b17c2099b8f2e25b461882e544ff"},
+ {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:955e8513d07a283056b1396e9a57ceddbd272d9252c14f154d450d227606eb54"},
+ {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:346f5343b9e3f00b8db8ba359350eb124b98c99efd0b408728ac6ebf38173958"},
+ {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b9098e0049e88c6a24ff64545cdfc50807818ba6c1b739cae221bbbcbc58aad3"},
+ {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:00bd361b903dc4bbf4eb165f24d1acbee754fce22ded24c3d56eec268658a5cf"},
+ {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7b8b454bac16428b22560d0a1cf0a09875339cab69df61d7805bf48919415901"},
+ {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:f1d072c2eb0ad60d4c183f3fb44ac6f73fb7a8f16a2694a91f988275cbf352f9"},
+ {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:31a82d498054cac9f6d0b53d02bb85811185bcb477d4b60144f915f3b3126342"},
+ {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6512cb89e334e4700febbffaaa52761b65b4f5a3cf33f960213d5656cea36a77"},
+ {file = "kiwisolver-1.4.5-cp39-cp39-win32.whl", hash = "sha256:9db8ea4c388fdb0f780fe91346fd438657ea602d58348753d9fb265ce1bca67f"},
+ {file = "kiwisolver-1.4.5-cp39-cp39-win_amd64.whl", hash = "sha256:59415f46a37f7f2efeec758353dd2eae1b07640d8ca0f0c42548ec4125492635"},
+ {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5c7b3b3a728dc6faf3fc372ef24f21d1e3cee2ac3e9596691d746e5a536de920"},
+ {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:620ced262a86244e2be10a676b646f29c34537d0d9cc8eb26c08f53d98013390"},
+ {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:378a214a1e3bbf5ac4a8708304318b4f890da88c9e6a07699c4ae7174c09a68d"},
+ {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf7be1207676ac608a50cd08f102f6742dbfc70e8d60c4db1c6897f62f71523"},
+ {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:ba55dce0a9b8ff59495ddd050a0225d58bd0983d09f87cfe2b6aec4f2c1234e4"},
+ {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:fd32ea360bcbb92d28933fc05ed09bffcb1704ba3fc7942e81db0fd4f81a7892"},
+ {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5e7139af55d1688f8b960ee9ad5adafc4ac17c1c473fe07133ac092310d76544"},
+ {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:dced8146011d2bc2e883f9bd68618b8247387f4bbec46d7392b3c3b032640126"},
+ {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9bf3325c47b11b2e51bca0824ea217c7cd84491d8ac4eefd1e409705ef092bd"},
+ {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5794cf59533bc3f1b1c821f7206a3617999db9fbefc345360aafe2e067514929"},
+ {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e368f200bbc2e4f905b8e71eb38b3c04333bddaa6a2464a6355487b02bb7fb09"},
+ {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5d706eba36b4c4d5bc6c6377bb6568098765e990cfc21ee16d13963fab7b3e7"},
+ {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85267bd1aa8880a9c88a8cb71e18d3d64d2751a790e6ca6c27b8ccc724bcd5ad"},
+ {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:210ef2c3a1f03272649aff1ef992df2e724748918c4bc2d5a90352849eb40bea"},
+ {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:11d011a7574eb3b82bcc9c1a1d35c1d7075677fdd15de527d91b46bd35e935ee"},
+ {file = "kiwisolver-1.4.5.tar.gz", hash = "sha256:e57e563a57fb22a142da34f38acc2fc1a5c864bc29ca1517a88abc963e60d6ec"},
+]
+
+[[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 = "markdown-it-py"
+version = "3.0.0"
+description = "Python port of markdown-it. Markdown parsing, done right!"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"},
+ {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"},
+]
+
+[package.dependencies]
+mdurl = ">=0.1,<1.0"
+
+[package.extras]
+benchmarking = ["psutil", "pytest", "pytest-benchmark"]
+code-style = ["pre-commit (>=3.0,<4.0)"]
+compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"]
+linkify = ["linkify-it-py (>=1,<3)"]
+plugins = ["mdit-py-plugins"]
+profiling = ["gprof2dot"]
+rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"]
+testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"]
+
+[[package]]
+name = "markupsafe"
+version = "2.1.5"
+description = "Safely add untrusted strings to HTML/XML markup."
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"},
+ {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"},
+ {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"},
+ {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"},
+ {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"},
+ {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"},
+ {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"},
+ {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"},
+ {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"},
+ {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"},
+ {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"},
+ {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"},
+ {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"},
+ {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"},
+ {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"},
+ {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"},
+ {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"},
+ {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"},
+ {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"},
+ {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"},
+ {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"},
+ {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"},
+ {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"},
+ {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"},
+ {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"},
+ {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"},
+ {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"},
+ {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"},
+ {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"},
+ {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"},
+ {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"},
+ {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"},
+ {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"},
+ {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"},
+ {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"},
+ {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"},
+ {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"},
+ {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"},
+ {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"},
+ {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"},
+]
+
+[[package]]
+name = "marshmallow"
+version = "3.21.0"
+description = "A lightweight library for converting complex datatypes to and from native Python datatypes."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "marshmallow-3.21.0-py3-none-any.whl", hash = "sha256:e7997f83571c7fd476042c2c188e4ee8a78900ca5e74bd9c8097afa56624e9bd"},
+ {file = "marshmallow-3.21.0.tar.gz", hash = "sha256:20f53be28c6e374a711a16165fb22a8dc6003e3f7cda1285e3ca777b9193885b"},
+]
+
+[package.dependencies]
+packaging = ">=17.0"
+
+[package.extras]
+dev = ["marshmallow[tests]", "pre-commit (>=3.5,<4.0)", "tox"]
+docs = ["alabaster (==0.7.16)", "autodocsumm (==0.2.12)", "sphinx (==7.2.6)", "sphinx-issues (==4.0.0)", "sphinx-version-warning (==1.1.2)"]
+tests = ["pytest", "pytz", "simplejson"]
+
+[[package]]
+name = "matplotlib"
+version = "3.7.5"
+description = "Python plotting package"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "matplotlib-3.7.5-cp310-cp310-macosx_10_12_universal2.whl", hash = "sha256:4a87b69cb1cb20943010f63feb0b2901c17a3b435f75349fd9865713bfa63925"},
+ {file = "matplotlib-3.7.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:d3ce45010fefb028359accebb852ca0c21bd77ec0f281952831d235228f15810"},
+ {file = "matplotlib-3.7.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fbea1e762b28400393d71be1a02144aa16692a3c4c676ba0178ce83fc2928fdd"},
+ {file = "matplotlib-3.7.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec0e1adc0ad70ba8227e957551e25a9d2995e319c29f94a97575bb90fa1d4469"},
+ {file = "matplotlib-3.7.5-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6738c89a635ced486c8a20e20111d33f6398a9cbebce1ced59c211e12cd61455"},
+ {file = "matplotlib-3.7.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1210b7919b4ed94b5573870f316bca26de3e3b07ffdb563e79327dc0e6bba515"},
+ {file = "matplotlib-3.7.5-cp310-cp310-win32.whl", hash = "sha256:068ebcc59c072781d9dcdb82f0d3f1458271c2de7ca9c78f5bd672141091e9e1"},
+ {file = "matplotlib-3.7.5-cp310-cp310-win_amd64.whl", hash = "sha256:f098ffbaab9df1e3ef04e5a5586a1e6b1791380698e84938d8640961c79b1fc0"},
+ {file = "matplotlib-3.7.5-cp311-cp311-macosx_10_12_universal2.whl", hash = "sha256:f65342c147572673f02a4abec2d5a23ad9c3898167df9b47c149f32ce61ca078"},
+ {file = "matplotlib-3.7.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4ddf7fc0e0dc553891a117aa083039088d8a07686d4c93fb8a810adca68810af"},
+ {file = "matplotlib-3.7.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0ccb830fc29442360d91be48527809f23a5dcaee8da5f4d9b2d5b867c1b087b8"},
+ {file = "matplotlib-3.7.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efc6bb28178e844d1f408dd4d6341ee8a2e906fc9e0fa3dae497da4e0cab775d"},
+ {file = "matplotlib-3.7.5-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3b15c4c2d374f249f324f46e883340d494c01768dd5287f8bc00b65b625ab56c"},
+ {file = "matplotlib-3.7.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d028555421912307845e59e3de328260b26d055c5dac9b182cc9783854e98fb"},
+ {file = "matplotlib-3.7.5-cp311-cp311-win32.whl", hash = "sha256:fe184b4625b4052fa88ef350b815559dd90cc6cc8e97b62f966e1ca84074aafa"},
+ {file = "matplotlib-3.7.5-cp311-cp311-win_amd64.whl", hash = "sha256:084f1f0f2f1010868c6f1f50b4e1c6f2fb201c58475494f1e5b66fed66093647"},
+ {file = "matplotlib-3.7.5-cp312-cp312-macosx_10_12_universal2.whl", hash = "sha256:34bceb9d8ddb142055ff27cd7135f539f2f01be2ce0bafbace4117abe58f8fe4"},
+ {file = "matplotlib-3.7.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:c5a2134162273eb8cdfd320ae907bf84d171de948e62180fa372a3ca7cf0f433"},
+ {file = "matplotlib-3.7.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:039ad54683a814002ff37bf7981aa1faa40b91f4ff84149beb53d1eb64617980"},
+ {file = "matplotlib-3.7.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d742ccd1b09e863b4ca58291728db645b51dab343eebb08d5d4b31b308296ce"},
+ {file = "matplotlib-3.7.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:743b1c488ca6a2bc7f56079d282e44d236bf375968bfd1b7ba701fd4d0fa32d6"},
+ {file = "matplotlib-3.7.5-cp312-cp312-win_amd64.whl", hash = "sha256:fbf730fca3e1f23713bc1fae0a57db386e39dc81ea57dc305c67f628c1d7a342"},
+ {file = "matplotlib-3.7.5-cp38-cp38-macosx_10_12_universal2.whl", hash = "sha256:cfff9b838531698ee40e40ea1a8a9dc2c01edb400b27d38de6ba44c1f9a8e3d2"},
+ {file = "matplotlib-3.7.5-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:1dbcca4508bca7847fe2d64a05b237a3dcaec1f959aedb756d5b1c67b770c5ee"},
+ {file = "matplotlib-3.7.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4cdf4ef46c2a1609a50411b66940b31778db1e4b73d4ecc2eaa40bd588979b13"},
+ {file = "matplotlib-3.7.5-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:167200ccfefd1674b60e957186dfd9baf58b324562ad1a28e5d0a6b3bea77905"},
+ {file = "matplotlib-3.7.5-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:53e64522934df6e1818b25fd48cf3b645b11740d78e6ef765fbb5fa5ce080d02"},
+ {file = "matplotlib-3.7.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3e3bc79b2d7d615067bd010caff9243ead1fc95cf735c16e4b2583173f717eb"},
+ {file = "matplotlib-3.7.5-cp38-cp38-win32.whl", hash = "sha256:6b641b48c6819726ed47c55835cdd330e53747d4efff574109fd79b2d8a13748"},
+ {file = "matplotlib-3.7.5-cp38-cp38-win_amd64.whl", hash = "sha256:f0b60993ed3488b4532ec6b697059897891927cbfc2b8d458a891b60ec03d9d7"},
+ {file = "matplotlib-3.7.5-cp39-cp39-macosx_10_12_universal2.whl", hash = "sha256:090964d0afaff9c90e4d8de7836757e72ecfb252fb02884016d809239f715651"},
+ {file = "matplotlib-3.7.5-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:9fc6fcfbc55cd719bc0bfa60bde248eb68cf43876d4c22864603bdd23962ba25"},
+ {file = "matplotlib-3.7.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5e7cc3078b019bb863752b8b60e8b269423000f1603cb2299608231996bd9d54"},
+ {file = "matplotlib-3.7.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e4e9a868e8163abaaa8259842d85f949a919e1ead17644fb77a60427c90473c"},
+ {file = "matplotlib-3.7.5-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fa7ebc995a7d747dacf0a717d0eb3aa0f0c6a0e9ea88b0194d3a3cd241a1500f"},
+ {file = "matplotlib-3.7.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3785bfd83b05fc0e0c2ae4c4a90034fe693ef96c679634756c50fe6efcc09856"},
+ {file = "matplotlib-3.7.5-cp39-cp39-win32.whl", hash = "sha256:29b058738c104d0ca8806395f1c9089dfe4d4f0f78ea765c6c704469f3fffc81"},
+ {file = "matplotlib-3.7.5-cp39-cp39-win_amd64.whl", hash = "sha256:fd4028d570fa4b31b7b165d4a685942ae9cdc669f33741e388c01857d9723eab"},
+ {file = "matplotlib-3.7.5-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2a9a3f4d6a7f88a62a6a18c7e6a84aedcaf4faf0708b4ca46d87b19f1b526f88"},
+ {file = "matplotlib-3.7.5-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b9b3fd853d4a7f008a938df909b96db0b454225f935d3917520305b90680579c"},
+ {file = "matplotlib-3.7.5-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0ad550da9f160737d7890217c5eeed4337d07e83ca1b2ca6535078f354e7675"},
+ {file = "matplotlib-3.7.5-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:20da7924a08306a861b3f2d1da0d1aa9a6678e480cf8eacffe18b565af2813e7"},
+ {file = "matplotlib-3.7.5-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b45c9798ea6bb920cb77eb7306409756a7fab9db9b463e462618e0559aecb30e"},
+ {file = "matplotlib-3.7.5-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a99866267da1e561c7776fe12bf4442174b79aac1a47bd7e627c7e4d077ebd83"},
+ {file = "matplotlib-3.7.5-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b6aa62adb6c268fc87d80f963aca39c64615c31830b02697743c95590ce3fbb"},
+ {file = "matplotlib-3.7.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:e530ab6a0afd082d2e9c17eb1eb064a63c5b09bb607b2b74fa41adbe3e162286"},
+ {file = "matplotlib-3.7.5.tar.gz", hash = "sha256:1e5c971558ebc811aa07f54c7b7c677d78aa518ef4c390e14673a09e0860184a"},
+]
+
+[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,<2"
+packaging = ">=20.0"
+pillow = ">=6.2.0"
+pyparsing = ">=2.3.1"
+python-dateutil = ">=2.7"
+
+[[package]]
+name = "mccabe"
+version = "0.7.0"
+description = "McCabe checker, plugin for flake8"
+optional = false
+python-versions = ">=3.6"
+files = [
+ {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"},
+ {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"},
+]
+
+[[package]]
+name = "mdit-py-plugins"
+version = "0.4.0"
+description = "Collection of plugins for markdown-it-py"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "mdit_py_plugins-0.4.0-py3-none-any.whl", hash = "sha256:b51b3bb70691f57f974e257e367107857a93b36f322a9e6d44ca5bf28ec2def9"},
+ {file = "mdit_py_plugins-0.4.0.tar.gz", hash = "sha256:d8ab27e9aed6c38aa716819fedfde15ca275715955f8a185a8e1cf90fb1d2c1b"},
+]
+
+[package.dependencies]
+markdown-it-py = ">=1.0.0,<4.0.0"
+
+[package.extras]
+code-style = ["pre-commit"]
+rtd = ["myst-parser", "sphinx-book-theme"]
+testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"]
+
+[[package]]
+name = "mdurl"
+version = "0.1.2"
+description = "Markdown URL utilities"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"},
+ {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"},
+]
+
+[[package]]
+name = "mypy"
+version = "1.8.0"
+description = "Optional static typing for Python"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "mypy-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:485a8942f671120f76afffff70f259e1cd0f0cfe08f81c05d8816d958d4577d3"},
+ {file = "mypy-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:df9824ac11deaf007443e7ed2a4a26bebff98d2bc43c6da21b2b64185da011c4"},
+ {file = "mypy-1.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2afecd6354bbfb6e0160f4e4ad9ba6e4e003b767dd80d85516e71f2e955ab50d"},
+ {file = "mypy-1.8.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8963b83d53ee733a6e4196954502b33567ad07dfd74851f32be18eb932fb1cb9"},
+ {file = "mypy-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:e46f44b54ebddbeedbd3d5b289a893219065ef805d95094d16a0af6630f5d410"},
+ {file = "mypy-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:855fe27b80375e5c5878492f0729540db47b186509c98dae341254c8f45f42ae"},
+ {file = "mypy-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4c886c6cce2d070bd7df4ec4a05a13ee20c0aa60cb587e8d1265b6c03cf91da3"},
+ {file = "mypy-1.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d19c413b3c07cbecf1f991e2221746b0d2a9410b59cb3f4fb9557f0365a1a817"},
+ {file = "mypy-1.8.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9261ed810972061388918c83c3f5cd46079d875026ba97380f3e3978a72f503d"},
+ {file = "mypy-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:51720c776d148bad2372ca21ca29256ed483aa9a4cdefefcef49006dff2a6835"},
+ {file = "mypy-1.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:52825b01f5c4c1c4eb0db253ec09c7aa17e1a7304d247c48b6f3599ef40db8bd"},
+ {file = "mypy-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f5ac9a4eeb1ec0f1ccdc6f326bcdb464de5f80eb07fb38b5ddd7b0de6bc61e55"},
+ {file = "mypy-1.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afe3fe972c645b4632c563d3f3eff1cdca2fa058f730df2b93a35e3b0c538218"},
+ {file = "mypy-1.8.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:42c6680d256ab35637ef88891c6bd02514ccb7e1122133ac96055ff458f93fc3"},
+ {file = "mypy-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:720a5ca70e136b675af3af63db533c1c8c9181314d207568bbe79051f122669e"},
+ {file = "mypy-1.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:028cf9f2cae89e202d7b6593cd98db6759379f17a319b5faf4f9978d7084cdc6"},
+ {file = "mypy-1.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4e6d97288757e1ddba10dd9549ac27982e3e74a49d8d0179fc14d4365c7add66"},
+ {file = "mypy-1.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f1478736fcebb90f97e40aff11a5f253af890c845ee0c850fe80aa060a267c6"},
+ {file = "mypy-1.8.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42419861b43e6962a649068a61f4a4839205a3ef525b858377a960b9e2de6e0d"},
+ {file = "mypy-1.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:2b5b6c721bd4aabaadead3a5e6fa85c11c6c795e0c81a7215776ef8afc66de02"},
+ {file = "mypy-1.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5c1538c38584029352878a0466f03a8ee7547d7bd9f641f57a0f3017a7c905b8"},
+ {file = "mypy-1.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ef4be7baf08a203170f29e89d79064463b7fc7a0908b9d0d5114e8009c3a259"},
+ {file = "mypy-1.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7178def594014aa6c35a8ff411cf37d682f428b3b5617ca79029d8ae72f5402b"},
+ {file = "mypy-1.8.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ab3c84fa13c04aeeeabb2a7f67a25ef5d77ac9d6486ff33ded762ef353aa5592"},
+ {file = "mypy-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:99b00bc72855812a60d253420d8a2eae839b0afa4938f09f4d2aa9bb4654263a"},
+ {file = "mypy-1.8.0-py3-none-any.whl", hash = "sha256:538fd81bb5e430cc1381a443971c0475582ff9f434c16cd46d2c66763ce85d9d"},
+ {file = "mypy-1.8.0.tar.gz", hash = "sha256:6ff8b244d7085a0b425b56d327b480c3b29cafbd2eff27316a004f9a7391ae07"},
+]
+
+[package.dependencies]
+mypy-extensions = ">=1.0.0"
+tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
+typing-extensions = ">=4.1.0"
+
+[package.extras]
+dmypy = ["psutil (>=4.0)"]
+install-types = ["pip"]
+mypyc = ["setuptools (>=50)"]
+reports = ["lxml"]
+
+[[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 = "myst-parser"
+version = "2.0.0"
+description = "An extended [CommonMark](https://spec.commonmark.org/) compliant parser,"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "myst_parser-2.0.0-py3-none-any.whl", hash = "sha256:7c36344ae39c8e740dad7fdabf5aa6fc4897a813083c6cc9990044eb93656b14"},
+ {file = "myst_parser-2.0.0.tar.gz", hash = "sha256:ea929a67a6a0b1683cdbe19b8d2e724cd7643f8aa3e7bb18dd65beac3483bead"},
+]
+
+[package.dependencies]
+docutils = ">=0.16,<0.21"
+jinja2 = "*"
+markdown-it-py = ">=3.0,<4.0"
+mdit-py-plugins = ">=0.4,<1.0"
+pyyaml = "*"
+sphinx = ">=6,<8"
+
+[package.extras]
+code-style = ["pre-commit (>=3.0,<4.0)"]
+linkify = ["linkify-it-py (>=2.0,<3.0)"]
+rtd = ["ipython", "pydata-sphinx-theme (==v0.13.0rc4)", "sphinx-autodoc2 (>=0.4.2,<0.5.0)", "sphinx-book-theme (==1.0.0rc2)", "sphinx-copybutton", "sphinx-design2", "sphinx-pyscript", "sphinx-tippy (>=0.3.1)", "sphinx-togglebutton", "sphinxext-opengraph (>=0.8.2,<0.9.0)", "sphinxext-rediraffe (>=0.2.7,<0.3.0)"]
+testing = ["beautifulsoup4", "coverage[toml]", "pytest (>=7,<8)", "pytest-cov", "pytest-param-files (>=0.3.4,<0.4.0)", "pytest-regressions", "sphinx-pytest"]
+testing-docutils = ["pygments", "pytest (>=7,<8)", "pytest-param-files (>=0.3.4,<0.4.0)"]
+
+[[package]]
+name = "nodeenv"
+version = "1.8.0"
+description = "Node.js virtual environment builder"
+optional = false
+python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*"
+files = [
+ {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"},
+ {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"},
+]
+
+[package.dependencies]
+setuptools = "*"
+
+[[package]]
+name = "numpy"
+version = "1.24.4"
+description = "Fundamental package for array computing in Python"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "numpy-1.24.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c0bfb52d2169d58c1cdb8cc1f16989101639b34c7d3ce60ed70b19c63eba0b64"},
+ {file = "numpy-1.24.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ed094d4f0c177b1b8e7aa9cba7d6ceed51c0e569a5318ac0ca9a090680a6a1b1"},
+ {file = "numpy-1.24.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79fc682a374c4a8ed08b331bef9c5f582585d1048fa6d80bc6c35bc384eee9b4"},
+ {file = "numpy-1.24.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ffe43c74893dbf38c2b0a1f5428760a1a9c98285553c89e12d70a96a7f3a4d6"},
+ {file = "numpy-1.24.4-cp310-cp310-win32.whl", hash = "sha256:4c21decb6ea94057331e111a5bed9a79d335658c27ce2adb580fb4d54f2ad9bc"},
+ {file = "numpy-1.24.4-cp310-cp310-win_amd64.whl", hash = "sha256:b4bea75e47d9586d31e892a7401f76e909712a0fd510f58f5337bea9572c571e"},
+ {file = "numpy-1.24.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f136bab9c2cfd8da131132c2cf6cc27331dd6fae65f95f69dcd4ae3c3639c810"},
+ {file = "numpy-1.24.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e2926dac25b313635e4d6cf4dc4e51c8c0ebfed60b801c799ffc4c32bf3d1254"},
+ {file = "numpy-1.24.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:222e40d0e2548690405b0b3c7b21d1169117391c2e82c378467ef9ab4c8f0da7"},
+ {file = "numpy-1.24.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7215847ce88a85ce39baf9e89070cb860c98fdddacbaa6c0da3ffb31b3350bd5"},
+ {file = "numpy-1.24.4-cp311-cp311-win32.whl", hash = "sha256:4979217d7de511a8d57f4b4b5b2b965f707768440c17cb70fbf254c4b225238d"},
+ {file = "numpy-1.24.4-cp311-cp311-win_amd64.whl", hash = "sha256:b7b1fc9864d7d39e28f41d089bfd6353cb5f27ecd9905348c24187a768c79694"},
+ {file = "numpy-1.24.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1452241c290f3e2a312c137a9999cdbf63f78864d63c79039bda65ee86943f61"},
+ {file = "numpy-1.24.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:04640dab83f7c6c85abf9cd729c5b65f1ebd0ccf9de90b270cd61935eef0197f"},
+ {file = "numpy-1.24.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5425b114831d1e77e4b5d812b69d11d962e104095a5b9c3b641a218abcc050e"},
+ {file = "numpy-1.24.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd80e219fd4c71fc3699fc1dadac5dcf4fd882bfc6f7ec53d30fa197b8ee22dc"},
+ {file = "numpy-1.24.4-cp38-cp38-win32.whl", hash = "sha256:4602244f345453db537be5314d3983dbf5834a9701b7723ec28923e2889e0bb2"},
+ {file = "numpy-1.24.4-cp38-cp38-win_amd64.whl", hash = "sha256:692f2e0f55794943c5bfff12b3f56f99af76f902fc47487bdfe97856de51a706"},
+ {file = "numpy-1.24.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2541312fbf09977f3b3ad449c4e5f4bb55d0dbf79226d7724211acc905049400"},
+ {file = "numpy-1.24.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9667575fb6d13c95f1b36aca12c5ee3356bf001b714fc354eb5465ce1609e62f"},
+ {file = "numpy-1.24.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3a86ed21e4f87050382c7bc96571755193c4c1392490744ac73d660e8f564a9"},
+ {file = "numpy-1.24.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d11efb4dbecbdf22508d55e48d9c8384db795e1b7b51ea735289ff96613ff74d"},
+ {file = "numpy-1.24.4-cp39-cp39-win32.whl", hash = "sha256:6620c0acd41dbcb368610bb2f4d83145674040025e5536954782467100aa8835"},
+ {file = "numpy-1.24.4-cp39-cp39-win_amd64.whl", hash = "sha256:befe2bf740fd8373cf56149a5c23a0f601e82869598d41f8e188a0e9869926f8"},
+ {file = "numpy-1.24.4-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:31f13e25b4e304632a4619d0e0777662c2ffea99fcae2029556b17d8ff958aef"},
+ {file = "numpy-1.24.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95f7ac6540e95bc440ad77f56e520da5bf877f87dca58bd095288dce8940532a"},
+ {file = "numpy-1.24.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e98f220aa76ca2a977fe435f5b04d7b3470c0a2e6312907b37ba6068f26787f2"},
+ {file = "numpy-1.24.4.tar.gz", hash = "sha256:80f5e3a4e498641401868df4208b74581206afbee7cf7b8329daae82676d9463"},
+]
+
+[[package]]
+name = "opencv-python"
+version = "4.8.1.78"
+description = "Wrapper package for OpenCV python bindings."
+optional = false
+python-versions = ">=3.6"
+files = [
+ {file = "opencv-python-4.8.1.78.tar.gz", hash = "sha256:cc7adbbcd1112877a39274106cb2752e04984bc01a031162952e97450d6117f6"},
+ {file = "opencv_python-4.8.1.78-cp37-abi3-macosx_10_16_x86_64.whl", hash = "sha256:91d5f6f5209dc2635d496f6b8ca6573ecdad051a09e6b5de4c399b8e673c60da"},
+ {file = "opencv_python-4.8.1.78-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:bc31f47e05447da8b3089faa0a07ffe80e114c91ce0b171e6424f9badbd1c5cd"},
+ {file = "opencv_python-4.8.1.78-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9814beca408d3a0eca1bae7e3e5be68b07c17ecceb392b94170881216e09b319"},
+ {file = "opencv_python-4.8.1.78-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4c406bdb41eb21ea51b4e90dfbc989c002786c3f601c236a99c59a54670a394"},
+ {file = "opencv_python-4.8.1.78-cp37-abi3-win32.whl", hash = "sha256:a7aac3900fbacf55b551e7b53626c3dad4c71ce85643645c43e91fcb19045e47"},
+ {file = "opencv_python-4.8.1.78-cp37-abi3-win_amd64.whl", hash = "sha256:b983197f97cfa6fcb74e1da1802c7497a6f94ed561aba6980f1f33123f904956"},
+]
+
+[package.dependencies]
+numpy = {version = ">=1.23.5", markers = "python_version >= \"3.11\""}
+
+[[package]]
+name = "opencv-python"
+version = "4.9.0.80"
+description = "Wrapper package for OpenCV python bindings."
+optional = false
+python-versions = ">=3.6"
+files = [
+ {file = "opencv-python-4.9.0.80.tar.gz", hash = "sha256:1a9f0e6267de3a1a1db0c54213d022c7c8b5b9ca4b580e80bdc58516c922c9e1"},
+ {file = "opencv_python-4.9.0.80-cp37-abi3-macosx_10_16_x86_64.whl", hash = "sha256:7e5f7aa4486651a6ebfa8ed4b594b65bd2d2f41beeb4241a3e4b1b85acbbbadb"},
+ {file = "opencv_python-4.9.0.80-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:71dfb9555ccccdd77305fc3dcca5897fbf0cf28b297c51ee55e079c065d812a3"},
+ {file = "opencv_python-4.9.0.80-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b34a52e9da36dda8c151c6394aed602e4b17fa041df0b9f5b93ae10b0fcca2a"},
+ {file = "opencv_python-4.9.0.80-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4088cab82b66a3b37ffc452976b14a3c599269c247895ae9ceb4066d8188a57"},
+ {file = "opencv_python-4.9.0.80-cp37-abi3-win32.whl", hash = "sha256:dcf000c36dd1651118a2462257e3a9e76db789a78432e1f303c7bac54f63ef6c"},
+ {file = "opencv_python-4.9.0.80-cp37-abi3-win_amd64.whl", hash = "sha256:3f16f08e02b2a2da44259c7cc712e779eff1dd8b55fdb0323e8cab09548086c0"},
+]
+
+[package.dependencies]
+numpy = [
+ {version = ">=1.21.0", markers = "python_version <= \"3.9\" and platform_system == \"Darwin\" and platform_machine == \"arm64\" and python_version >= \"3.8\""},
+ {version = ">=1.19.3", markers = "platform_system == \"Linux\" and platform_machine == \"aarch64\" and python_version >= \"3.8\" and python_version < \"3.10\" or python_version > \"3.9\" and python_version < \"3.10\" or python_version >= \"3.9\" and platform_system != \"Darwin\" and python_version < \"3.10\" or python_version >= \"3.9\" and platform_machine != \"arm64\" and python_version < \"3.10\""},
+ {version = ">=1.17.3", markers = "(platform_system != \"Darwin\" and platform_system != \"Linux\") and python_version >= \"3.8\" and python_version < \"3.9\" or platform_system != \"Darwin\" and python_version >= \"3.8\" and python_version < \"3.9\" and platform_machine != \"aarch64\" or platform_machine != \"arm64\" and python_version >= \"3.8\" and python_version < \"3.9\" and platform_system != \"Linux\" or (platform_machine != \"arm64\" and platform_machine != \"aarch64\") and python_version >= \"3.8\" and python_version < \"3.9\""},
+ {version = ">=1.23.5", markers = "python_version >= \"3.11\" and python_version < \"3.12\""},
+ {version = ">=1.21.4", markers = "python_version >= \"3.10\" and platform_system == \"Darwin\" and python_version < \"3.11\""},
+ {version = ">=1.21.2", markers = "platform_system != \"Darwin\" and python_version >= \"3.10\" and python_version < \"3.11\""},
+]
+
+[[package]]
+name = "openpyxl"
+version = "3.1.2"
+description = "A Python library to read/write Excel 2010 xlsx/xlsm files"
+optional = false
+python-versions = ">=3.6"
+files = [
+ {file = "openpyxl-3.1.2-py2.py3-none-any.whl", hash = "sha256:f91456ead12ab3c6c2e9491cf33ba6d08357d802192379bb482f1033ade496f5"},
+ {file = "openpyxl-3.1.2.tar.gz", hash = "sha256:a6f5977418eff3b2d5500d54d9db50c8277a368436f4e4f8ddb1be3422870184"},
+]
+
+[package.dependencies]
+et-xmlfile = "*"
+
+[[package]]
+name = "packaging"
+version = "23.2"
+description = "Core utilities for Python packages"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"},
+ {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"},
+]
+
+[[package]]
+name = "pandas"
+version = "1.5.3"
+description = "Powerful data structures for data analysis, time series, and statistics"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "pandas-1.5.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3749077d86e3a2f0ed51367f30bf5b82e131cc0f14260c4d3e499186fccc4406"},
+ {file = "pandas-1.5.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:972d8a45395f2a2d26733eb8d0f629b2f90bebe8e8eddbb8829b180c09639572"},
+ {file = "pandas-1.5.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:50869a35cbb0f2e0cd5ec04b191e7b12ed688874bd05dd777c19b28cbea90996"},
+ {file = "pandas-1.5.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3ac844a0fe00bfaeb2c9b51ab1424e5c8744f89860b138434a363b1f620f354"},
+ {file = "pandas-1.5.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a0a56cef15fd1586726dace5616db75ebcfec9179a3a55e78f72c5639fa2a23"},
+ {file = "pandas-1.5.3-cp310-cp310-win_amd64.whl", hash = "sha256:478ff646ca42b20376e4ed3fa2e8d7341e8a63105586efe54fa2508ee087f328"},
+ {file = "pandas-1.5.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6973549c01ca91ec96199e940495219c887ea815b2083722821f1d7abfa2b4dc"},
+ {file = "pandas-1.5.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c39a8da13cede5adcd3be1182883aea1c925476f4e84b2807a46e2775306305d"},
+ {file = "pandas-1.5.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f76d097d12c82a535fda9dfe5e8dd4127952b45fea9b0276cb30cca5ea313fbc"},
+ {file = "pandas-1.5.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e474390e60ed609cec869b0da796ad94f420bb057d86784191eefc62b65819ae"},
+ {file = "pandas-1.5.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f2b952406a1588ad4cad5b3f55f520e82e902388a6d5a4a91baa8d38d23c7f6"},
+ {file = "pandas-1.5.3-cp311-cp311-win_amd64.whl", hash = "sha256:bc4c368f42b551bf72fac35c5128963a171b40dce866fb066540eeaf46faa003"},
+ {file = "pandas-1.5.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:14e45300521902689a81f3f41386dc86f19b8ba8dd5ac5a3c7010ef8d2932813"},
+ {file = "pandas-1.5.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9842b6f4b8479e41968eced654487258ed81df7d1c9b7b870ceea24ed9459b31"},
+ {file = "pandas-1.5.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:26d9c71772c7afb9d5046e6e9cf42d83dd147b5cf5bcb9d97252077118543792"},
+ {file = "pandas-1.5.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fbcb19d6fceb9e946b3e23258757c7b225ba450990d9ed63ccceeb8cae609f7"},
+ {file = "pandas-1.5.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:565fa34a5434d38e9d250af3c12ff931abaf88050551d9fbcdfafca50d62babf"},
+ {file = "pandas-1.5.3-cp38-cp38-win32.whl", hash = "sha256:87bd9c03da1ac870a6d2c8902a0e1fd4267ca00f13bc494c9e5a9020920e1d51"},
+ {file = "pandas-1.5.3-cp38-cp38-win_amd64.whl", hash = "sha256:41179ce559943d83a9b4bbacb736b04c928b095b5f25dd2b7389eda08f46f373"},
+ {file = "pandas-1.5.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c74a62747864ed568f5a82a49a23a8d7fe171d0c69038b38cedf0976831296fa"},
+ {file = "pandas-1.5.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c4c00e0b0597c8e4f59e8d461f797e5d70b4d025880516a8261b2817c47759ee"},
+ {file = "pandas-1.5.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a50d9a4336a9621cab7b8eb3fb11adb82de58f9b91d84c2cd526576b881a0c5a"},
+ {file = "pandas-1.5.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd05f7783b3274aa206a1af06f0ceed3f9b412cf665b7247eacd83be41cf7bf0"},
+ {file = "pandas-1.5.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f69c4029613de47816b1bb30ff5ac778686688751a5e9c99ad8c7031f6508e5"},
+ {file = "pandas-1.5.3-cp39-cp39-win32.whl", hash = "sha256:7cec0bee9f294e5de5bbfc14d0573f65526071029d036b753ee6507d2a21480a"},
+ {file = "pandas-1.5.3-cp39-cp39-win_amd64.whl", hash = "sha256:dfd681c5dc216037e0b0a2c821f5ed99ba9f03ebcf119c7dac0e9a7b960b9ec9"},
+ {file = "pandas-1.5.3.tar.gz", hash = "sha256:74a3fd7e5a7ec052f183273dc7b0acd3a863edf7520f5d3a1765c04ffdb3b0b1"},
+]
+
+[package.dependencies]
+numpy = [
+ {version = ">=1.20.3", markers = "python_version < \"3.10\""},
+ {version = ">=1.23.2", markers = "python_version >= \"3.11\""},
+ {version = ">=1.21.0", markers = "python_version >= \"3.10\" and python_version < \"3.11\""},
+]
+python-dateutil = ">=2.8.1"
+pytz = ">=2020.1"
+
+[package.extras]
+test = ["hypothesis (>=5.5.3)", "pytest (>=6.0)", "pytest-xdist (>=1.31)"]
+
+[[package]]
+name = "pathspec"
+version = "0.12.1"
+description = "Utility library for gitignore style pattern matching of file paths."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"},
+ {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"},
+]
+
+[[package]]
+name = "pbr"
+version = "6.0.0"
+description = "Python Build Reasonableness"
+optional = false
+python-versions = ">=2.6"
+files = [
+ {file = "pbr-6.0.0-py2.py3-none-any.whl", hash = "sha256:4a7317d5e3b17a3dccb6a8cfe67dab65b20551404c52c8ed41279fa4f0cb4cda"},
+ {file = "pbr-6.0.0.tar.gz", hash = "sha256:d1377122a5a00e2f940ee482999518efe16d745d423a670c27773dfbc3c9a7d9"},
+]
+
+[[package]]
+name = "pdfminer-six"
+version = "20221105"
+description = "PDF parser and analyzer"
+optional = false
+python-versions = ">=3.6"
+files = [
+ {file = "pdfminer.six-20221105-py3-none-any.whl", hash = "sha256:1eaddd712d5b2732f8ac8486824533514f8ba12a0787b3d5fe1e686cd826532d"},
+ {file = "pdfminer.six-20221105.tar.gz", hash = "sha256:8448ab7b939d18b64820478ecac5394f482d7a79f5f7eaa7703c6c959c175e1d"},
+]
+
+[package.dependencies]
+charset-normalizer = ">=2.0.0"
+cryptography = ">=36.0.0"
+
+[package.extras]
+dev = ["black", "mypy (==0.931)", "nox", "pytest"]
+docs = ["sphinx", "sphinx-argparse"]
+image = ["Pillow"]
+
+[[package]]
+name = "pep8-naming"
+version = "0.13.3"
+description = "Check PEP-8 naming conventions, plugin for flake8"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "pep8-naming-0.13.3.tar.gz", hash = "sha256:1705f046dfcd851378aac3be1cd1551c7c1e5ff363bacad707d43007877fa971"},
+ {file = "pep8_naming-0.13.3-py3-none-any.whl", hash = "sha256:1a86b8c71a03337c97181917e2b472f0f5e4ccb06844a0d6f0a33522549e7a80"},
+]
+
+[package.dependencies]
+flake8 = ">=5.0.0"
+
+[[package]]
+name = "pillow"
+version = "10.2.0"
+description = "Python Imaging Library (Fork)"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "pillow-10.2.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:7823bdd049099efa16e4246bdf15e5a13dbb18a51b68fa06d6c1d4d8b99a796e"},
+ {file = "pillow-10.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:83b2021f2ade7d1ed556bc50a399127d7fb245e725aa0113ebd05cfe88aaf588"},
+ {file = "pillow-10.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fad5ff2f13d69b7e74ce5b4ecd12cc0ec530fcee76356cac6742785ff71c452"},
+ {file = "pillow-10.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da2b52b37dad6d9ec64e653637a096905b258d2fc2b984c41ae7d08b938a67e4"},
+ {file = "pillow-10.2.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:47c0995fc4e7f79b5cfcab1fc437ff2890b770440f7696a3ba065ee0fd496563"},
+ {file = "pillow-10.2.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:322bdf3c9b556e9ffb18f93462e5f749d3444ce081290352c6070d014c93feb2"},
+ {file = "pillow-10.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:51f1a1bffc50e2e9492e87d8e09a17c5eea8409cda8d3f277eb6edc82813c17c"},
+ {file = "pillow-10.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:69ffdd6120a4737710a9eee73e1d2e37db89b620f702754b8f6e62594471dee0"},
+ {file = "pillow-10.2.0-cp310-cp310-win32.whl", hash = "sha256:c6dafac9e0f2b3c78df97e79af707cdc5ef8e88208d686a4847bab8266870023"},
+ {file = "pillow-10.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:aebb6044806f2e16ecc07b2a2637ee1ef67a11840a66752751714a0d924adf72"},
+ {file = "pillow-10.2.0-cp310-cp310-win_arm64.whl", hash = "sha256:7049e301399273a0136ff39b84c3678e314f2158f50f517bc50285fb5ec847ad"},
+ {file = "pillow-10.2.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:35bb52c37f256f662abdfa49d2dfa6ce5d93281d323a9af377a120e89a9eafb5"},
+ {file = "pillow-10.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9c23f307202661071d94b5e384e1e1dc7dfb972a28a2310e4ee16103e66ddb67"},
+ {file = "pillow-10.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:773efe0603db30c281521a7c0214cad7836c03b8ccff897beae9b47c0b657d61"},
+ {file = "pillow-10.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11fa2e5984b949b0dd6d7a94d967743d87c577ff0b83392f17cb3990d0d2fd6e"},
+ {file = "pillow-10.2.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:716d30ed977be8b37d3ef185fecb9e5a1d62d110dfbdcd1e2a122ab46fddb03f"},
+ {file = "pillow-10.2.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a086c2af425c5f62a65e12fbf385f7c9fcb8f107d0849dba5839461a129cf311"},
+ {file = "pillow-10.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c8de2789052ed501dd829e9cae8d3dcce7acb4777ea4a479c14521c942d395b1"},
+ {file = "pillow-10.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:609448742444d9290fd687940ac0b57fb35e6fd92bdb65386e08e99af60bf757"},
+ {file = "pillow-10.2.0-cp311-cp311-win32.whl", hash = "sha256:823ef7a27cf86df6597fa0671066c1b596f69eba53efa3d1e1cb8b30f3533068"},
+ {file = "pillow-10.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:1da3b2703afd040cf65ec97efea81cfba59cdbed9c11d8efc5ab09df9509fc56"},
+ {file = "pillow-10.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:edca80cbfb2b68d7b56930b84a0e45ae1694aeba0541f798e908a49d66b837f1"},
+ {file = "pillow-10.2.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:1b5e1b74d1bd1b78bc3477528919414874748dd363e6272efd5abf7654e68bef"},
+ {file = "pillow-10.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0eae2073305f451d8ecacb5474997c08569fb4eb4ac231ffa4ad7d342fdc25ac"},
+ {file = "pillow-10.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7c2286c23cd350b80d2fc9d424fc797575fb16f854b831d16fd47ceec078f2c"},
+ {file = "pillow-10.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e23412b5c41e58cec602f1135c57dfcf15482013ce6e5f093a86db69646a5aa"},
+ {file = "pillow-10.2.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:52a50aa3fb3acb9cf7213573ef55d31d6eca37f5709c69e6858fe3bc04a5c2a2"},
+ {file = "pillow-10.2.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:127cee571038f252a552760076407f9cff79761c3d436a12af6000cd182a9d04"},
+ {file = "pillow-10.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:8d12251f02d69d8310b046e82572ed486685c38f02176bd08baf216746eb947f"},
+ {file = "pillow-10.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:54f1852cd531aa981bc0965b7d609f5f6cc8ce8c41b1139f6ed6b3c54ab82bfb"},
+ {file = "pillow-10.2.0-cp312-cp312-win32.whl", hash = "sha256:257d8788df5ca62c980314053197f4d46eefedf4e6175bc9412f14412ec4ea2f"},
+ {file = "pillow-10.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:154e939c5f0053a383de4fd3d3da48d9427a7e985f58af8e94d0b3c9fcfcf4f9"},
+ {file = "pillow-10.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:f379abd2f1e3dddb2b61bc67977a6b5a0a3f7485538bcc6f39ec76163891ee48"},
+ {file = "pillow-10.2.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:8373c6c251f7ef8bda6675dd6d2b3a0fcc31edf1201266b5cf608b62a37407f9"},
+ {file = "pillow-10.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:870ea1ada0899fd0b79643990809323b389d4d1d46c192f97342eeb6ee0b8483"},
+ {file = "pillow-10.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4b6b1e20608493548b1f32bce8cca185bf0480983890403d3b8753e44077129"},
+ {file = "pillow-10.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3031709084b6e7852d00479fd1d310b07d0ba82765f973b543c8af5061cf990e"},
+ {file = "pillow-10.2.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:3ff074fc97dd4e80543a3e91f69d58889baf2002b6be64347ea8cf5533188213"},
+ {file = "pillow-10.2.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:cb4c38abeef13c61d6916f264d4845fab99d7b711be96c326b84df9e3e0ff62d"},
+ {file = "pillow-10.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b1b3020d90c2d8e1dae29cf3ce54f8094f7938460fb5ce8bc5c01450b01fbaf6"},
+ {file = "pillow-10.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:170aeb00224ab3dc54230c797f8404507240dd868cf52066f66a41b33169bdbe"},
+ {file = "pillow-10.2.0-cp38-cp38-win32.whl", hash = "sha256:c4225f5220f46b2fde568c74fca27ae9771536c2e29d7c04f4fb62c83275ac4e"},
+ {file = "pillow-10.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:0689b5a8c5288bc0504d9fcee48f61a6a586b9b98514d7d29b840143d6734f39"},
+ {file = "pillow-10.2.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:b792a349405fbc0163190fde0dc7b3fef3c9268292586cf5645598b48e63dc67"},
+ {file = "pillow-10.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c570f24be1e468e3f0ce7ef56a89a60f0e05b30a3669a459e419c6eac2c35364"},
+ {file = "pillow-10.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8ecd059fdaf60c1963c58ceb8997b32e9dc1b911f5da5307aab614f1ce5c2fb"},
+ {file = "pillow-10.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c365fd1703040de1ec284b176d6af5abe21b427cb3a5ff68e0759e1e313a5e7e"},
+ {file = "pillow-10.2.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:70c61d4c475835a19b3a5aa42492409878bbca7438554a1f89d20d58a7c75c01"},
+ {file = "pillow-10.2.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b6f491cdf80ae540738859d9766783e3b3c8e5bd37f5dfa0b76abdecc5081f13"},
+ {file = "pillow-10.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9d189550615b4948f45252d7f005e53c2040cea1af5b60d6f79491a6e147eef7"},
+ {file = "pillow-10.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:49d9ba1ed0ef3e061088cd1e7538a0759aab559e2e0a80a36f9fd9d8c0c21591"},
+ {file = "pillow-10.2.0-cp39-cp39-win32.whl", hash = "sha256:babf5acfede515f176833ed6028754cbcd0d206f7f614ea3447d67c33be12516"},
+ {file = "pillow-10.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:0304004f8067386b477d20a518b50f3fa658a28d44e4116970abfcd94fac34a8"},
+ {file = "pillow-10.2.0-cp39-cp39-win_arm64.whl", hash = "sha256:0fb3e7fc88a14eacd303e90481ad983fd5b69c761e9e6ef94c983f91025da869"},
+ {file = "pillow-10.2.0-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:322209c642aabdd6207517e9739c704dc9f9db943015535783239022002f054a"},
+ {file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3eedd52442c0a5ff4f887fab0c1c0bb164d8635b32c894bc1faf4c618dd89df2"},
+ {file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb28c753fd5eb3dd859b4ee95de66cc62af91bcff5db5f2571d32a520baf1f04"},
+ {file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:33870dc4653c5017bf4c8873e5488d8f8d5f8935e2f1fb9a2208c47cdd66efd2"},
+ {file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:3c31822339516fb3c82d03f30e22b1d038da87ef27b6a78c9549888f8ceda39a"},
+ {file = "pillow-10.2.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a2b56ba36e05f973d450582fb015594aaa78834fefe8dfb8fcd79b93e64ba4c6"},
+ {file = "pillow-10.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:d8e6aeb9201e655354b3ad049cb77d19813ad4ece0df1249d3c793de3774f8c7"},
+ {file = "pillow-10.2.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:2247178effb34a77c11c0e8ac355c7a741ceca0a732b27bf11e747bbc950722f"},
+ {file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15587643b9e5eb26c48e49a7b33659790d28f190fc514a322d55da2fb5c2950e"},
+ {file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753cd8f2086b2b80180d9b3010dd4ed147efc167c90d3bf593fe2af21265e5a5"},
+ {file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:7c8f97e8e7a9009bcacbe3766a36175056c12f9a44e6e6f2d5caad06dcfbf03b"},
+ {file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d1b35bcd6c5543b9cb547dee3150c93008f8dd0f1fef78fc0cd2b141c5baf58a"},
+ {file = "pillow-10.2.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:fe4c15f6c9285dc54ce6553a3ce908ed37c8f3825b5a51a15c91442bb955b868"},
+ {file = "pillow-10.2.0.tar.gz", hash = "sha256:e87f0b2c78157e12d7686b27d63c070fd65d994e8ddae6f328e0dcf4a0cd007e"},
+]
+
+[package.extras]
+docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-removed-in", "sphinxext-opengraph"]
+fpx = ["olefile"]
+mic = ["olefile"]
+tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"]
+typing = ["typing-extensions"]
+xmp = ["defusedxml"]
+
+[[package]]
+name = "platformdirs"
+version = "4.2.0"
+description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"},
+ {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"},
+]
+
+[package.extras]
+docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"]
+test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"]
+
+[[package]]
+name = "pluggy"
+version = "1.4.0"
+description = "plugin and hook calling mechanisms for python"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"},
+ {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"},
+]
+
+[package.extras]
+dev = ["pre-commit", "tox"]
+testing = ["pytest", "pytest-benchmark"]
+
+[[package]]
+name = "pre-commit"
+version = "3.5.0"
+description = "A framework for managing and maintaining multi-language pre-commit hooks."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "pre_commit-3.5.0-py2.py3-none-any.whl", hash = "sha256:841dc9aef25daba9a0238cd27984041fa0467b4199fc4852e27950664919f660"},
+ {file = "pre_commit-3.5.0.tar.gz", hash = "sha256:5804465c675b659b0862f07907f96295d490822a450c4c40e747d0b1c6ebcb32"},
+]
+
+[package.dependencies]
+cfgv = ">=2.0.0"
+identify = ">=1.0.0"
+nodeenv = ">=0.11.1"
+pyyaml = ">=5.1"
+virtualenv = ">=20.10.0"
+
+[[package]]
+name = "pre-commit-hooks"
+version = "4.5.0"
+description = "Some out-of-the-box hooks for pre-commit."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "pre_commit_hooks-4.5.0-py2.py3-none-any.whl", hash = "sha256:b779d5c44ede9b1fda48e2d96b08e9aa5b1d2fdb8903ca09f0dbaca22d529edb"},
+ {file = "pre_commit_hooks-4.5.0.tar.gz", hash = "sha256:ffbe2af1c85ac9a7695866955680b4dee98822638b748a6f3debefad79748c8a"},
+]
+
+[package.dependencies]
+"ruamel.yaml" = ">=0.15"
+tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
+
+[[package]]
+name = "pycodestyle"
+version = "2.9.1"
+description = "Python style guide checker"
+optional = false
+python-versions = ">=3.6"
+files = [
+ {file = "pycodestyle-2.9.1-py2.py3-none-any.whl", hash = "sha256:d1735fc58b418fd7c5f658d28d943854f8a849b01a5d0a1e6f3f3fdd0166804b"},
+ {file = "pycodestyle-2.9.1.tar.gz", hash = "sha256:2c9607871d58c76354b697b42f5d57e1ada7d261c261efac224b664affdc5785"},
+]
+
+[[package]]
+name = "pycparser"
+version = "2.21"
+description = "C parser in Python"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+files = [
+ {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"},
+ {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"},
+]
+
+[[package]]
+name = "pydantic"
+version = "1.10.14"
+description = "Data validation and settings management using python type hints"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "pydantic-1.10.14-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7f4fcec873f90537c382840f330b90f4715eebc2bc9925f04cb92de593eae054"},
+ {file = "pydantic-1.10.14-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e3a76f571970fcd3c43ad982daf936ae39b3e90b8a2e96c04113a369869dc87"},
+ {file = "pydantic-1.10.14-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82d886bd3c3fbeaa963692ef6b643159ccb4b4cefaf7ff1617720cbead04fd1d"},
+ {file = "pydantic-1.10.14-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:798a3d05ee3b71967844a1164fd5bdb8c22c6d674f26274e78b9f29d81770c4e"},
+ {file = "pydantic-1.10.14-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:23d47a4b57a38e8652bcab15a658fdb13c785b9ce217cc3a729504ab4e1d6bc9"},
+ {file = "pydantic-1.10.14-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f9f674b5c3bebc2eba401de64f29948ae1e646ba2735f884d1594c5f675d6f2a"},
+ {file = "pydantic-1.10.14-cp310-cp310-win_amd64.whl", hash = "sha256:24a7679fab2e0eeedb5a8924fc4a694b3bcaac7d305aeeac72dd7d4e05ecbebf"},
+ {file = "pydantic-1.10.14-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9d578ac4bf7fdf10ce14caba6f734c178379bd35c486c6deb6f49006e1ba78a7"},
+ {file = "pydantic-1.10.14-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fa7790e94c60f809c95602a26d906eba01a0abee9cc24150e4ce2189352deb1b"},
+ {file = "pydantic-1.10.14-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aad4e10efa5474ed1a611b6d7f0d130f4aafadceb73c11d9e72823e8f508e663"},
+ {file = "pydantic-1.10.14-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1245f4f61f467cb3dfeced2b119afef3db386aec3d24a22a1de08c65038b255f"},
+ {file = "pydantic-1.10.14-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:21efacc678a11114c765eb52ec0db62edffa89e9a562a94cbf8fa10b5db5c046"},
+ {file = "pydantic-1.10.14-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:412ab4a3f6dbd2bf18aefa9f79c7cca23744846b31f1d6555c2ee2b05a2e14ca"},
+ {file = "pydantic-1.10.14-cp311-cp311-win_amd64.whl", hash = "sha256:e897c9f35281f7889873a3e6d6b69aa1447ceb024e8495a5f0d02ecd17742a7f"},
+ {file = "pydantic-1.10.14-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d604be0f0b44d473e54fdcb12302495fe0467c56509a2f80483476f3ba92b33c"},
+ {file = "pydantic-1.10.14-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a42c7d17706911199798d4c464b352e640cab4351efe69c2267823d619a937e5"},
+ {file = "pydantic-1.10.14-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:596f12a1085e38dbda5cbb874d0973303e34227b400b6414782bf205cc14940c"},
+ {file = "pydantic-1.10.14-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bfb113860e9288d0886e3b9e49d9cf4a9d48b441f52ded7d96db7819028514cc"},
+ {file = "pydantic-1.10.14-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:bc3ed06ab13660b565eed80887fcfbc0070f0aa0691fbb351657041d3e874efe"},
+ {file = "pydantic-1.10.14-cp37-cp37m-win_amd64.whl", hash = "sha256:ad8c2bc677ae5f6dbd3cf92f2c7dc613507eafe8f71719727cbc0a7dec9a8c01"},
+ {file = "pydantic-1.10.14-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c37c28449752bb1f47975d22ef2882d70513c546f8f37201e0fec3a97b816eee"},
+ {file = "pydantic-1.10.14-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:49a46a0994dd551ec051986806122767cf144b9702e31d47f6d493c336462597"},
+ {file = "pydantic-1.10.14-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53e3819bd20a42470d6dd0fe7fc1c121c92247bca104ce608e609b59bc7a77ee"},
+ {file = "pydantic-1.10.14-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0fbb503bbbbab0c588ed3cd21975a1d0d4163b87e360fec17a792f7d8c4ff29f"},
+ {file = "pydantic-1.10.14-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:336709883c15c050b9c55a63d6c7ff09be883dbc17805d2b063395dd9d9d0022"},
+ {file = "pydantic-1.10.14-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4ae57b4d8e3312d486e2498d42aed3ece7b51848336964e43abbf9671584e67f"},
+ {file = "pydantic-1.10.14-cp38-cp38-win_amd64.whl", hash = "sha256:dba49d52500c35cfec0b28aa8b3ea5c37c9df183ffc7210b10ff2a415c125c4a"},
+ {file = "pydantic-1.10.14-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c66609e138c31cba607d8e2a7b6a5dc38979a06c900815495b2d90ce6ded35b4"},
+ {file = "pydantic-1.10.14-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d986e115e0b39604b9eee3507987368ff8148222da213cd38c359f6f57b3b347"},
+ {file = "pydantic-1.10.14-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:646b2b12df4295b4c3148850c85bff29ef6d0d9621a8d091e98094871a62e5c7"},
+ {file = "pydantic-1.10.14-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:282613a5969c47c83a8710cc8bfd1e70c9223feb76566f74683af889faadc0ea"},
+ {file = "pydantic-1.10.14-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:466669501d08ad8eb3c4fecd991c5e793c4e0bbd62299d05111d4f827cded64f"},
+ {file = "pydantic-1.10.14-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:13e86a19dca96373dcf3190fcb8797d40a6f12f154a244a8d1e8e03b8f280593"},
+ {file = "pydantic-1.10.14-cp39-cp39-win_amd64.whl", hash = "sha256:08b6ec0917c30861e3fe71a93be1648a2aa4f62f866142ba21670b24444d7fd8"},
+ {file = "pydantic-1.10.14-py3-none-any.whl", hash = "sha256:8ee853cd12ac2ddbf0ecbac1c289f95882b2d4482258048079d13be700aa114c"},
+ {file = "pydantic-1.10.14.tar.gz", hash = "sha256:46f17b832fe27de7850896f3afee50ea682220dd218f7e9c88d436788419dca6"},
+]
+
+[package.dependencies]
+typing-extensions = ">=4.2.0"
+
+[package.extras]
+dotenv = ["python-dotenv (>=0.10.4)"]
+email = ["email-validator (>=1.0.3)"]
+
+[[package]]
+name = "pyflakes"
+version = "2.5.0"
+description = "passive checker of Python programs"
+optional = false
+python-versions = ">=3.6"
+files = [
+ {file = "pyflakes-2.5.0-py2.py3-none-any.whl", hash = "sha256:4579f67d887f804e67edb544428f264b7b24f435b263c4614f384135cea553d2"},
+ {file = "pyflakes-2.5.0.tar.gz", hash = "sha256:491feb020dca48ccc562a8c0cbe8df07ee13078df59813b83959cbdada312ea3"},
+]
+
+[[package]]
+name = "pygments"
+version = "2.17.2"
+description = "Pygments is a syntax highlighting package written in Python."
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"},
+ {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"},
+]
+
+[package.extras]
+plugins = ["importlib-metadata"]
+windows-terminal = ["colorama (>=0.4.6)"]
+
+[[package]]
+name = "pyparsing"
+version = "3.1.1"
+description = "pyparsing module - Classes and methods to define and execute parsing grammars"
+optional = false
+python-versions = ">=3.6.8"
+files = [
+ {file = "pyparsing-3.1.1-py3-none-any.whl", hash = "sha256:32c7c0b711493c72ff18a981d24f28aaf9c1fb7ed5e9667c9e84e3db623bdbfb"},
+ {file = "pyparsing-3.1.1.tar.gz", hash = "sha256:ede28a1a32462f5a9705e07aea48001a08f7cf81a021585011deba701581a0db"},
+]
+
+[package.extras]
+diagrams = ["jinja2", "railroad-diagrams"]
+
+[[package]]
+name = "pypdf"
+version = "3.17.4"
+description = "A pure-python PDF library capable of splitting, merging, cropping, and transforming PDF files"
+optional = false
+python-versions = ">=3.6"
+files = [
+ {file = "pypdf-3.17.4-py3-none-any.whl", hash = "sha256:6aa0f61b33779b64486de3f42835d3668badd48dac4a536aeb87da187a5eacd2"},
+ {file = "pypdf-3.17.4.tar.gz", hash = "sha256:ec96e2e4fc9648ac609d19c00d41e9d606e0ae2ce5a0bbe7691426f5f157166a"},
+]
+
+[package.dependencies]
+typing_extensions = {version = ">=3.7.4.3", markers = "python_version < \"3.10\""}
+
+[package.extras]
+crypto = ["PyCryptodome", "cryptography"]
+dev = ["black", "flit", "pip-tools", "pre-commit (<2.18.0)", "pytest-cov", "pytest-socket", "pytest-timeout", "pytest-xdist", "wheel"]
+docs = ["myst_parser", "sphinx", "sphinx_rtd_theme"]
+full = ["Pillow (>=8.0.0)", "PyCryptodome", "cryptography"]
+image = ["Pillow (>=8.0.0)"]
+
+[[package]]
+name = "pytest"
+version = "8.0.2"
+description = "pytest: simple powerful testing with Python"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "pytest-8.0.2-py3-none-any.whl", hash = "sha256:edfaaef32ce5172d5466b5127b42e0d6d35ebbe4453f0e3505d96afd93f6b096"},
+ {file = "pytest-8.0.2.tar.gz", hash = "sha256:d4051d623a2e0b7e51960ba963193b09ce6daeb9759a451844a21e4ddedfc1bd"},
+]
+
+[package.dependencies]
+colorama = {version = "*", markers = "sys_platform == \"win32\""}
+exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""}
+iniconfig = "*"
+packaging = "*"
+pluggy = ">=1.3.0,<2.0"
+tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""}
+
+[package.extras]
+testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
+
+[[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 = "pytz"
+version = "2024.1"
+description = "World timezone definitions, modern and historical"
+optional = false
+python-versions = "*"
+files = [
+ {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"},
+ {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"},
+]
+
+[[package]]
+name = "pyupgrade"
+version = "3.8.0"
+description = "A tool to automatically upgrade syntax for newer versions."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "pyupgrade-3.8.0-py2.py3-none-any.whl", hash = "sha256:08d0e6129f5e9da7e7a581bdbea689e0d49c3c93eeaf156a07ae2fd794f52660"},
+ {file = "pyupgrade-3.8.0.tar.gz", hash = "sha256:1facb0b8407cca468dfcc1d13717e3a85aa37b9e6e7338664ad5bfe5ef50c867"},
+]
+
+[package.dependencies]
+tokenize-rt = ">=3.2.0"
+
+[[package]]
+name = "pyyaml"
+version = "6.0.1"
+description = "YAML parser and emitter for Python"
+optional = false
+python-versions = ">=3.6"
+files = [
+ {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"},
+ {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"},
+ {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"},
+ {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"},
+ {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"},
+ {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"},
+ {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"},
+ {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"},
+ {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"},
+ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"},
+ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"},
+ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"},
+ {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"},
+ {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"},
+ {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"},
+ {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"},
+ {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"},
+ {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"},
+ {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"},
+ {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"},
+ {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"},
+ {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"},
+ {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"},
+ {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"},
+ {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"},
+ {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"},
+ {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"},
+ {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"},
+ {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"},
+ {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"},
+ {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"},
+ {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"},
+ {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"},
+ {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"},
+ {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"},
+ {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"},
+ {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"},
+ {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"},
+ {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"},
+ {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"},
+]
+
+[[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 = "restructuredtext-lint"
+version = "1.4.0"
+description = "reStructuredText linter"
+optional = false
+python-versions = "*"
+files = [
+ {file = "restructuredtext_lint-1.4.0.tar.gz", hash = "sha256:1b235c0c922341ab6c530390892eb9e92f90b9b75046063e047cacfb0f050c45"},
+]
+
+[package.dependencies]
+docutils = ">=0.11,<1.0"
+
+[[package]]
+name = "rich"
+version = "13.7.0"
+description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
+optional = false
+python-versions = ">=3.7.0"
+files = [
+ {file = "rich-13.7.0-py3-none-any.whl", hash = "sha256:6da14c108c4866ee9520bbffa71f6fe3962e193b7da68720583850cd4548e235"},
+ {file = "rich-13.7.0.tar.gz", hash = "sha256:5cb5123b5cf9ee70584244246816e9114227e0b98ad9176eede6ad54bf5403fa"},
+]
+
+[package.dependencies]
+markdown-it-py = ">=2.2.0"
+pygments = ">=2.13.0,<3.0.0"
+typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.9\""}
+
+[package.extras]
+jupyter = ["ipywidgets (>=7.5.1,<9)"]
+
+[[package]]
+name = "ruamel-yaml"
+version = "0.18.6"
+description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "ruamel.yaml-0.18.6-py3-none-any.whl", hash = "sha256:57b53ba33def16c4f3d807c0ccbc00f8a6081827e81ba2491691b76882d0c636"},
+ {file = "ruamel.yaml-0.18.6.tar.gz", hash = "sha256:8b27e6a217e786c6fbe5634d8f3f11bc63e0f80f6a5890f28863d9c45aac311b"},
+]
+
+[package.dependencies]
+"ruamel.yaml.clib" = {version = ">=0.2.7", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.13\""}
+
+[package.extras]
+docs = ["mercurial (>5.7)", "ryd"]
+jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"]
+
+[[package]]
+name = "ruamel-yaml-clib"
+version = "0.2.8"
+description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml"
+optional = false
+python-versions = ">=3.6"
+files = [
+ {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b42169467c42b692c19cf539c38d4602069d8c1505e97b86387fcf7afb766e1d"},
+ {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:07238db9cbdf8fc1e9de2489a4f68474e70dffcb32232db7c08fa61ca0c7c462"},
+ {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:fff3573c2db359f091e1589c3d7c5fc2f86f5bdb6f24252c2d8e539d4e45f412"},
+ {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-win32.whl", hash = "sha256:c69212f63169ec1cfc9bb44723bf2917cbbd8f6191a00ef3410f5a7fe300722d"},
+ {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-win_amd64.whl", hash = "sha256:cabddb8d8ead485e255fe80429f833172b4cadf99274db39abc080e068cbcc31"},
+ {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bef08cd86169d9eafb3ccb0a39edb11d8e25f3dae2b28f5c52fd997521133069"},
+ {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:b16420e621d26fdfa949a8b4b47ade8810c56002f5389970db4ddda51dbff248"},
+ {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:25c515e350e5b739842fc3228d662413ef28f295791af5e5110b543cf0b57d9b"},
+ {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-win32.whl", hash = "sha256:53a300ed9cea38cf5a2a9b069058137c2ca1ce658a874b79baceb8f892f915a7"},
+ {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-win_amd64.whl", hash = "sha256:c2a72e9109ea74e511e29032f3b670835f8a59bbdc9ce692c5b4ed91ccf1eedb"},
+ {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:ebc06178e8821efc9692ea7544aa5644217358490145629914d8020042c24aa1"},
+ {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_13_0_arm64.whl", hash = "sha256:edaef1c1200c4b4cb914583150dcaa3bc30e592e907c01117c08b13a07255ec2"},
+ {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d176b57452ab5b7028ac47e7b3cf644bcfdc8cacfecf7e71759f7f51a59e5c92"},
+ {file = "ruamel.yaml.clib-0.2.8-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a5aa27bad2bb83670b71683aae140a1f52b0857a2deff56ad3f6c13a017a26ed"},
+ {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c58ecd827313af6864893e7af0a3bb85fd529f862b6adbefe14643947cfe2942"},
+ {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-macosx_12_0_arm64.whl", hash = "sha256:f481f16baec5290e45aebdc2a5168ebc6d35189ae6fea7a58787613a25f6e875"},
+ {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7f67a1ee819dc4562d444bbafb135832b0b909f81cc90f7aa00260968c9ca1b3"},
+ {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-win32.whl", hash = "sha256:75e1ed13e1f9de23c5607fe6bd1aeaae21e523b32d83bb33918245361e9cc51b"},
+ {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-win_amd64.whl", hash = "sha256:3f215c5daf6a9d7bbed4a0a4f760f3113b10e82ff4c5c44bec20a68c8014f675"},
+ {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1b617618914cb00bf5c34d4357c37aa15183fa229b24767259657746c9077615"},
+ {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:a6a9ffd280b71ad062eae53ac1659ad86a17f59a0fdc7699fd9be40525153337"},
+ {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:700e4ebb569e59e16a976857c8798aee258dceac7c7d6b50cab63e080058df91"},
+ {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-win32.whl", hash = "sha256:955eae71ac26c1ab35924203fda6220f84dce57d6d7884f189743e2abe3a9fbe"},
+ {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-win_amd64.whl", hash = "sha256:56f4252222c067b4ce51ae12cbac231bce32aee1d33fbfc9d17e5b8d6966c312"},
+ {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:03d1162b6d1df1caa3a4bd27aa51ce17c9afc2046c31b0ad60a0a96ec22f8001"},
+ {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:bba64af9fa9cebe325a62fa398760f5c7206b215201b0ec825005f1b18b9bccf"},
+ {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:da09ad1c359a728e112d60116f626cc9f29730ff3e0e7db72b9a2dbc2e4beed5"},
+ {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-win32.whl", hash = "sha256:84b554931e932c46f94ab306913ad7e11bba988104c5cff26d90d03f68258cd5"},
+ {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-win_amd64.whl", hash = "sha256:25ac8c08322002b06fa1d49d1646181f0b2c72f5cbc15a85e80b4c30a544bb15"},
+ {file = "ruamel.yaml.clib-0.2.8.tar.gz", hash = "sha256:beb2e0404003de9a4cab9753a8805a8fe9320ee6673136ed7f04255fe60bb512"},
+]
+
+[[package]]
+name = "safety"
+version = "3.0.1"
+description = "Checks installed dependencies for known vulnerabilities and licenses."
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "safety-3.0.1-py3-none-any.whl", hash = "sha256:1ed058bc4bef132b974e58d7fcad020fb897cd255328016f8a5a194b94ca91d2"},
+ {file = "safety-3.0.1.tar.gz", hash = "sha256:1f2000f03652f3a0bfc67f8fd1e98bc5723ccb76e15cb1bdd68545c3d803df01"},
+]
+
+[package.dependencies]
+Authlib = ">=1.2.0"
+Click = ">=8.0.2"
+dparse = ">=0.6.4b0"
+jinja2 = ">=3.1.0"
+marshmallow = ">=3.15.0"
+packaging = ">=21.0"
+pydantic = ">=1.10.12,<2.0"
+requests = "*"
+rich = "*"
+"ruamel.yaml" = ">=0.17.21"
+safety-schemas = ">=0.0.1"
+setuptools = ">=65.5.1"
+typer = "*"
+typing-extensions = ">=4.7.1"
+urllib3 = ">=1.26.5"
+
+[package.extras]
+github = ["pygithub (>=1.43.3)"]
+gitlab = ["python-gitlab (>=1.3.0)"]
+spdx = ["spdx-tools (>=0.8.2)"]
+
+[[package]]
+name = "safety-schemas"
+version = "0.0.2"
+description = "Schemas for Safety tools"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "safety_schemas-0.0.2-py3-none-any.whl", hash = "sha256:277c077ce6e53221874a87c29515ffdd2f3773a6db4d035a9f67cc98db3b8c7f"},
+ {file = "safety_schemas-0.0.2.tar.gz", hash = "sha256:7d1b040ec06480f05cff6b45ea7a93e09c8942df864fb0d01ddeb67c323cfa8c"},
+]
+
+[package.dependencies]
+dparse = ">=0.6.4b0"
+packaging = ">=21.0"
+pydantic = "*"
+ruamel-yaml = ">=0.17.21"
+typing-extensions = ">=4.7.1"
+
+[[package]]
+name = "setuptools"
+version = "69.1.1"
+description = "Easily download, build, install, upgrade, and uninstall Python packages"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "setuptools-69.1.1-py3-none-any.whl", hash = "sha256:02fa291a0471b3a18b2b2481ed902af520c69e8ae0919c13da936542754b4c56"},
+ {file = "setuptools-69.1.1.tar.gz", hash = "sha256:5c0806c7d9af348e6dd3777b4f4dbb42c7ad85b190104837488eab9a7c945cf8"},
+]
+
+[package.extras]
+docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
+testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
+testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
+
+[[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 = "snowballstemmer"
+version = "2.2.0"
+description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms."
+optional = false
+python-versions = "*"
+files = [
+ {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"},
+ {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"},
+]
+
+[[package]]
+name = "soupsieve"
+version = "2.5"
+description = "A modern CSS selector implementation for Beautiful Soup."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "soupsieve-2.5-py3-none-any.whl", hash = "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7"},
+ {file = "soupsieve-2.5.tar.gz", hash = "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690"},
+]
+
+[[package]]
+name = "sphinx"
+version = "7.1.2"
+description = "Python documentation generator"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "sphinx-7.1.2-py3-none-any.whl", hash = "sha256:d170a81825b2fcacb6dfd5a0d7f578a053e45d3f2b153fecc948c37344eb4cbe"},
+ {file = "sphinx-7.1.2.tar.gz", hash = "sha256:780f4d32f1d7d1126576e0e5ecc19dc32ab76cd24e950228dcf7b1f6d3d9e22f"},
+]
+
+[package.dependencies]
+alabaster = ">=0.7,<0.8"
+babel = ">=2.9"
+colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""}
+docutils = ">=0.18.1,<0.21"
+imagesize = ">=1.3"
+importlib-metadata = {version = ">=4.8", markers = "python_version < \"3.10\""}
+Jinja2 = ">=3.0"
+packaging = ">=21.0"
+Pygments = ">=2.13"
+requests = ">=2.25.0"
+snowballstemmer = ">=2.0"
+sphinxcontrib-applehelp = "*"
+sphinxcontrib-devhelp = "*"
+sphinxcontrib-htmlhelp = ">=2.0.0"
+sphinxcontrib-jsmath = "*"
+sphinxcontrib-qthelp = "*"
+sphinxcontrib-serializinghtml = ">=1.1.5"
+
+[package.extras]
+docs = ["sphinxcontrib-websupport"]
+lint = ["docutils-stubs", "flake8 (>=3.5.0)", "flake8-simplify", "isort", "mypy (>=0.990)", "ruff", "sphinx-lint", "types-requests"]
+test = ["cython", "filelock", "html5lib", "pytest (>=4.6)"]
+
+[[package]]
+name = "sphinx-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-basic-ng"
+version = "1.0.0b2"
+description = "A modern skeleton for Sphinx themes."
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "sphinx_basic_ng-1.0.0b2-py3-none-any.whl", hash = "sha256:eb09aedbabfb650607e9b4b68c9d240b90b1e1be221d6ad71d61c52e29f7932b"},
+ {file = "sphinx_basic_ng-1.0.0b2.tar.gz", hash = "sha256:9ec55a47c90c8c002b5960c57492ec3021f5193cb26cebc2dc4ea226848651c9"},
+]
+
+[package.dependencies]
+sphinx = ">=4.0"
+
+[package.extras]
+docs = ["furo", "ipython", "myst-parser", "sphinx-copybutton", "sphinx-inline-tabs"]
+
+[[package]]
+name = "sphinx-click"
+version = "5.1.0"
+description = "Sphinx extension that automatically documents click applications"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "sphinx-click-5.1.0.tar.gz", hash = "sha256:6812c2db62d3fae71a4addbe5a8a0a16c97eb491f3cd63fe34b4ed7e07236f33"},
+ {file = "sphinx_click-5.1.0-py3-none-any.whl", hash = "sha256:ae97557a4e9ec646045089326c3b90e026c58a45e083b8f35f17d5d6558d08a0"},
+]
+
+[package.dependencies]
+click = ">=7.0"
+docutils = "*"
+sphinx = ">=2.0"
+
+[[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 = "stevedore"
+version = "5.2.0"
+description = "Manage dynamic plugins for Python applications"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "stevedore-5.2.0-py3-none-any.whl", hash = "sha256:1c15d95766ca0569cad14cb6272d4d31dae66b011a929d7c18219c176ea1b5c9"},
+ {file = "stevedore-5.2.0.tar.gz", hash = "sha256:46b93ca40e1114cea93d738a6c1e365396981bb6bb78c27045b7587c9473544d"},
+]
+
+[package.dependencies]
+pbr = ">=2.0.0,<2.1.0 || >2.1.0"
+
+[[package]]
+name = "tabulate"
+version = "0.9.0"
+description = "Pretty-print tabular data"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"},
+ {file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"},
+]
+
+[package.extras]
+widechars = ["wcwidth"]
+
+[[package]]
+name = "tokenize-rt"
+version = "5.2.0"
+description = "A wrapper around the stdlib `tokenize` which roundtrips."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "tokenize_rt-5.2.0-py2.py3-none-any.whl", hash = "sha256:b79d41a65cfec71285433511b50271b05da3584a1da144a0752e9c621a285289"},
+ {file = "tokenize_rt-5.2.0.tar.gz", hash = "sha256:9fe80f8a5c1edad2d3ede0f37481cc0cc1538a2f442c9c2f9e4feacd2792d054"},
+]
+
+[[package]]
+name = "tomli"
+version = "2.0.1"
+description = "A lil' TOML parser"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
+ {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
+]
+
+[[package]]
+name = "tornado"
+version = "6.4"
+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.4-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:02ccefc7d8211e5a7f9e8bc3f9e5b0ad6262ba2fbb683a6443ecc804e5224ce0"},
+ {file = "tornado-6.4-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:27787de946a9cffd63ce5814c33f734c627a87072ec7eed71f7fc4417bb16263"},
+ {file = "tornado-6.4-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7894c581ecdcf91666a0912f18ce5e757213999e183ebfc2c3fdbf4d5bd764e"},
+ {file = "tornado-6.4-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e43bc2e5370a6a8e413e1e1cd0c91bedc5bd62a74a532371042a18ef19e10579"},
+ {file = "tornado-6.4-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0251554cdd50b4b44362f73ad5ba7126fc5b2c2895cc62b14a1c2d7ea32f212"},
+ {file = "tornado-6.4-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fd03192e287fbd0899dd8f81c6fb9cbbc69194d2074b38f384cb6fa72b80e9c2"},
+ {file = "tornado-6.4-cp38-abi3-musllinux_1_1_i686.whl", hash = "sha256:88b84956273fbd73420e6d4b8d5ccbe913c65d31351b4c004ae362eba06e1f78"},
+ {file = "tornado-6.4-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:71ddfc23a0e03ef2df1c1397d859868d158c8276a0603b96cf86892bff58149f"},
+ {file = "tornado-6.4-cp38-abi3-win32.whl", hash = "sha256:6f8a6c77900f5ae93d8b4ae1196472d0ccc2775cc1dfdc9e7727889145c45052"},
+ {file = "tornado-6.4-cp38-abi3-win_amd64.whl", hash = "sha256:10aeaa8006333433da48dec9fe417877f8bcc21f48dda8d661ae79da357b2a63"},
+ {file = "tornado-6.4.tar.gz", hash = "sha256:72291fa6e6bc84e626589f1c29d90a5a6d593ef5ae68052ee2ef000dfd273dee"},
+]
+
+[[package]]
+name = "typeguard"
+version = "4.1.5"
+description = "Run-time type checker for Python"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "typeguard-4.1.5-py3-none-any.whl", hash = "sha256:8923e55f8873caec136c892c3bed1f676eae7be57cdb94819281b3d3bc9c0953"},
+ {file = "typeguard-4.1.5.tar.gz", hash = "sha256:ea0a113bbc111bcffc90789ebb215625c963411f7096a7e9062d4e4630c155fd"},
+]
+
+[package.dependencies]
+importlib-metadata = {version = ">=3.6", markers = "python_version < \"3.10\""}
+typing-extensions = {version = ">=4.7.0", markers = "python_version < \"3.12\""}
+
+[package.extras]
+doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)"]
+test = ["coverage[toml] (>=7)", "mypy (>=1.2.0)", "pytest (>=7)"]
+
+[[package]]
+name = "typer"
+version = "0.9.0"
+description = "Typer, build great CLIs. Easy to code. Based on Python type hints."
+optional = false
+python-versions = ">=3.6"
+files = [
+ {file = "typer-0.9.0-py3-none-any.whl", hash = "sha256:5d96d986a21493606a358cae4461bd8cdf83cbf33a5aa950ae629ca3b51467ee"},
+ {file = "typer-0.9.0.tar.gz", hash = "sha256:50922fd79aea2f4751a8e0408ff10d2662bd0c8bbfa84755a699f3bada2978b2"},
+]
+
+[package.dependencies]
+click = ">=7.1.1,<9.0.0"
+typing-extensions = ">=3.7.4.3"
+
+[package.extras]
+all = ["colorama (>=0.4.3,<0.5.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"]
+dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "pre-commit (>=2.17.0,<3.0.0)"]
+doc = ["cairosvg (>=2.5.2,<3.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pillow (>=9.3.0,<10.0.0)"]
+test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.910)", "pytest (>=4.4.0,<8.0.0)", "pytest-cov (>=2.10.0,<5.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "pytest-xdist (>=1.32.0,<4.0.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"]
+
+[[package]]
+name = "typing-extensions"
+version = "4.10.0"
+description = "Backported and Experimental Type Hints for Python 3.8+"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"},
+ {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"},
+]
+
+[[package]]
+name = "urllib3"
+version = "2.2.1"
+description = "HTTP library with thread-safe connection pooling, file post, and more."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"},
+ {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"},
+]
+
+[package.extras]
+brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"]
+h2 = ["h2 (>=4,<5)"]
+socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
+zstd = ["zstandard (>=0.18.0)"]
+
+[[package]]
+name = "virtualenv"
+version = "20.25.1"
+description = "Virtual Python Environment builder"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "virtualenv-20.25.1-py3-none-any.whl", hash = "sha256:961c026ac520bac5f69acb8ea063e8a4f071bcc9457b9c1f28f6b085c511583a"},
+ {file = "virtualenv-20.25.1.tar.gz", hash = "sha256:e08e13ecdca7a0bd53798f356d5831434afa5b07b93f0abdf0797b7a06ffe197"},
+]
+
+[package.dependencies]
+distlib = ">=0.3.7,<1"
+filelock = ">=3.12.2,<4"
+platformdirs = ">=3.9.1,<5"
+
+[package.extras]
+docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"]
+test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"]
+
+[[package]]
+name = "xdoctest"
+version = "1.1.3"
+description = "A rewrite of the builtin doctest module"
+optional = false
+python-versions = ">=3.6"
+files = [
+ {file = "xdoctest-1.1.3-py3-none-any.whl", hash = "sha256:9360535bd1a971ffc216d9613898cedceb81d0fd024587cc3c03c74d14c00a31"},
+ {file = "xdoctest-1.1.3.tar.gz", hash = "sha256:84e76a42a11a5926ff66d9d84c616bc101821099672550481ad96549cbdd02ae"},
+]
+
+[package.dependencies]
+colorama = {version = "*", optional = true, markers = "platform_system == \"Windows\" and extra == \"colors\""}
+Pygments = {version = "*", optional = true, markers = "python_version >= \"3.5.0\" and extra == \"colors\""}
+
+[package.extras]
+all = ["IPython (>=7.10.0)", "IPython (>=7.23.1)", "Pygments (>=2.0.0)", "Pygments (>=2.4.1)", "attrs (>=19.2.0)", "colorama (>=0.4.1)", "debugpy (>=1.0.0)", "debugpy (>=1.0.0)", "debugpy (>=1.0.0)", "debugpy (>=1.3.0)", "debugpy (>=1.6.0)", "ipykernel (>=5.2.0)", "ipykernel (>=6.0.0)", "ipykernel (>=6.11.0)", "ipython-genutils (>=0.2.0)", "jedi (>=0.16)", "jinja2 (>=3.0.0)", "jupyter-client (>=6.1.5)", "jupyter-client (>=7.0.0)", "jupyter-core (>=4.7.0)", "nbconvert (>=6.0.0)", "nbconvert (>=6.1.0)", "pyflakes (>=2.2.0)", "pytest (>=4.6.0)", "pytest (>=4.6.0)", "pytest (>=6.2.5)", "pytest-cov (>=3.0.0)", "tomli (>=0.2.0)", "typing (>=3.7.4)"]
+all-strict = ["IPython (==7.10.0)", "IPython (==7.23.1)", "Pygments (==2.0.0)", "Pygments (==2.4.1)", "attrs (==19.2.0)", "colorama (==0.4.1)", "debugpy (==1.0.0)", "debugpy (==1.0.0)", "debugpy (==1.0.0)", "debugpy (==1.3.0)", "debugpy (==1.6.0)", "ipykernel (==5.2.0)", "ipykernel (==6.0.0)", "ipykernel (==6.11.0)", "ipython-genutils (==0.2.0)", "jedi (==0.16)", "jinja2 (==3.0.0)", "jupyter-client (==6.1.5)", "jupyter-client (==7.0.0)", "jupyter-core (==4.7.0)", "nbconvert (==6.0.0)", "nbconvert (==6.1.0)", "pyflakes (==2.2.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==6.2.5)", "pytest-cov (==3.0.0)", "tomli (==0.2.0)", "typing (==3.7.4)"]
+colors = ["Pygments", "Pygments", "colorama"]
+jupyter = ["IPython", "IPython", "attrs", "debugpy", "debugpy", "debugpy", "debugpy", "debugpy", "ipykernel", "ipykernel", "ipykernel", "ipython-genutils", "jedi", "jinja2", "jupyter-client", "jupyter-client", "jupyter-core", "nbconvert", "nbconvert"]
+optional = ["IPython (>=7.10.0)", "IPython (>=7.23.1)", "Pygments (>=2.0.0)", "Pygments (>=2.4.1)", "attrs (>=19.2.0)", "colorama (>=0.4.1)", "debugpy (>=1.0.0)", "debugpy (>=1.0.0)", "debugpy (>=1.0.0)", "debugpy (>=1.3.0)", "debugpy (>=1.6.0)", "ipykernel (>=5.2.0)", "ipykernel (>=6.0.0)", "ipykernel (>=6.11.0)", "ipython-genutils (>=0.2.0)", "jedi (>=0.16)", "jinja2 (>=3.0.0)", "jupyter-client (>=6.1.5)", "jupyter-client (>=7.0.0)", "jupyter-core (>=4.7.0)", "nbconvert (>=6.0.0)", "nbconvert (>=6.1.0)", "pyflakes (>=2.2.0)", "tomli (>=0.2.0)"]
+optional-strict = ["IPython (==7.10.0)", "IPython (==7.23.1)", "Pygments (==2.0.0)", "Pygments (==2.4.1)", "attrs (==19.2.0)", "colorama (==0.4.1)", "debugpy (==1.0.0)", "debugpy (==1.0.0)", "debugpy (==1.0.0)", "debugpy (==1.3.0)", "debugpy (==1.6.0)", "ipykernel (==5.2.0)", "ipykernel (==6.0.0)", "ipykernel (==6.11.0)", "ipython-genutils (==0.2.0)", "jedi (==0.16)", "jinja2 (==3.0.0)", "jupyter-client (==6.1.5)", "jupyter-client (==7.0.0)", "jupyter-core (==4.7.0)", "nbconvert (==6.0.0)", "nbconvert (==6.1.0)", "pyflakes (==2.2.0)", "tomli (==0.2.0)"]
+tests = ["pytest (>=4.6.0)", "pytest (>=4.6.0)", "pytest (>=6.2.5)", "pytest-cov (>=3.0.0)", "typing (>=3.7.4)"]
+tests-binary = ["cmake", "cmake", "ninja", "ninja", "pybind11", "pybind11", "scikit-build", "scikit-build"]
+tests-binary-strict = ["cmake (==3.21.2)", "cmake (==3.25.0)", "ninja (==1.10.2)", "ninja (==1.11.1)", "pybind11 (==2.10.3)", "pybind11 (==2.7.1)", "scikit-build (==0.11.1)", "scikit-build (==0.16.1)"]
+tests-strict = ["pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==6.2.5)", "pytest-cov (==3.0.0)", "typing (==3.7.4)"]
+
+[[package]]
+name = "zipp"
+version = "3.17.0"
+description = "Backport of pathlib-compatible object wrapper for zip files"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"},
+ {file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"},
+]
+
+[package.extras]
+docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"]
+testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"]
+
+[metadata]
+lock-version = "2.0"
+python-versions = "^3.8"
+content-hash = "b48ff439b61279c7de2ac4e0c906c060a39bd009f417dc6c47302bbe513b3f18"
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 00000000..42d4b4d3
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,94 @@
+[tool.poetry]
+name = "pypdf_table_extraction"
+version = "0.0.1"
+description = "PDF Table Extraction for Humans."
+authors = ["Vinayak Mehta "]
+license = "MIT"
+readme = "README.md"
+homepage = "https://github.com/camelot-dev/camelot"
+repository = "https://github.com/camelot-dev/camelot"
+documentation = "https://camelot-py.readthedocs.io"
+packages = [
+ { include = "camelot", from = "." },
+]
+classifiers = [
+ "Development Status :: 5 - Production/Stable",
+]
+
+[tool.poetry.urls]
+Changelog = "https://github.com/camelot-dev/camelot/blob/master/HISTORY.md"
+
+[tool.poetry.dependencies]
+python = "^3.8"
+click = ">=8.0.1"
+chardet = "^5.1.0"
+numpy = "^1.24.2"
+openpyxl = "^3.1.0"
+pandas = "^1.5.3"
+pdfminer-six = "^20221105"
+pypdf = "^3.4.0"
+tabulate = "^0.9.0"
+
+[tool.poetry.dev-dependencies]
+Pygments = ">=2.10.0"
+black = ">=23.1.0"
+coverage = {extras = ["toml"], version = ">=6.2"}
+flake8 = ">=4.0.1"
+flake8-bandit = ">=2.1.2"
+flake8-bugbear = ">=21.9.2"
+flake8-rst-docstrings = ">=0.2.5"
+furo = ">=2021.11.12"
+isort = ">=5.10.1"
+mypy = ">=0.930"
+pep8-naming = ">=0.12.1"
+pre-commit = ">=2.16.0"
+pre-commit-hooks = ">=4.1.0"
+pytest = ">=6.2.5"
+pyupgrade = ">=2.29.1"
+safety = ">=2.2.3"
+sphinx = ">=4.3.2"
+sphinx-autobuild = ">=2021.3.14"
+sphinx-click = ">=3.0.2"
+typeguard = ">=2.13.3"
+xdoctest = {extras = ["colors"], version = ">=0.15.10"}
+myst-parser = {version = ">=0.16.1"}
+
+[tool.poetry.scripts]
+camelot = "camelot.__main__:main"
+
+[tool.poetry.group.base.dependencies]
+ghostscript = "^0.7"
+opencv-python = "^4.7.0.68"
+
+
+[tool.poetry.group.plot.dependencies]
+matplotlib = "^3.6.3"
+
+[tool.coverage.paths]
+source = ["camelot", "*/site-packages"]
+tests = ["tests", "*/tests"]
+
+[tool.coverage.run]
+branch = true
+source = ["camelot", "tests"]
+
+[tool.coverage.report]
+show_missing = true
+fail_under = 90
+
+[tool.isort]
+profile = "black"
+force_single_line = true
+lines_after_imports = 2
+
+[tool.mypy]
+strict = true
+warn_unreachable = true
+pretty = true
+show_column_numbers = true
+show_error_codes = true
+show_error_context = true
+
+[build-system]
+requires = ["poetry-core>=1.0.0"]
+build-backend = "poetry.core.masonry.api"
diff --git a/setup.cfg b/setup.cfg
deleted file mode 100644
index 2c56c090..00000000
--- a/setup.cfg
+++ /dev/null
@@ -1,6 +0,0 @@
-[aliases]
-test=pytest
-
-[tool:pytest]
-addopts = --verbose --cov-config .coveragerc --cov-report term --cov-report xml --cov=camelot --mpl
-python_files = tests/test_*.py
diff --git a/setup.py b/setup.py
deleted file mode 100644
index f79acc30..00000000
--- a/setup.py
+++ /dev/null
@@ -1,91 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import os
-from setuptools import find_packages
-
-
-here = os.path.abspath(os.path.dirname(__file__))
-about = {}
-with open(os.path.join(here, "camelot", "__version__.py"), "r") as f:
- exec(f.read(), about)
-
-with open("README.md", "r") as f:
- readme = f.read()
-
-
-requires = [
- "chardet>=3.0.4",
- "click>=6.7",
- "numpy>=1.13.3",
- "openpyxl>=2.5.8",
- "pandas>=0.23.4",
- "pdfminer.six>=20200726",
- "PyPDF2>=1.26.0",
- "tabulate>=0.8.9",
-]
-
-base_requires = ["ghostscript>=0.7", "opencv-python>=3.4.2.17", "pdftopng>=0.2.3"]
-
-plot_requires = [
- "matplotlib>=2.2.3",
-]
-
-dev_requires = [
- "codecov>=2.0.15",
- "pytest>=5.4.3",
- "pytest-cov>=2.10.0",
- "pytest-mpl>=0.11",
- "pytest-runner>=5.2",
- "Sphinx>=3.1.2",
- "sphinx-autobuild>=2021.3.14",
-]
-
-all_requires = base_requires + plot_requires
-dev_requires = dev_requires + all_requires
-
-
-def setup_package():
- metadata = dict(
- name=about["__title__"],
- version=about["__version__"],
- description=about["__description__"],
- long_description=readme,
- long_description_content_type="text/markdown",
- url=about["__url__"],
- author=about["__author__"],
- author_email=about["__author_email__"],
- license=about["__license__"],
- packages=find_packages(exclude=("tests",)),
- install_requires=requires,
- extras_require={
- "all": all_requires,
- "base": base_requires,
- "cv": base_requires, # deprecate
- "dev": dev_requires,
- "plot": plot_requires,
- },
- entry_points={
- "console_scripts": [
- "camelot = camelot.cli:cli",
- ],
- },
- classifiers=[
- # Trove classifiers
- # Full list: https://pypi.python.org/pypi?%3Aaction=list_classifiers
- "License :: OSI Approved :: MIT License",
- "Programming Language :: Python :: 3.6",
- "Programming Language :: Python :: 3.7",
- "Programming Language :: Python :: 3.8",
- ],
- )
-
- try:
- from setuptools import setup
- except ImportError:
- from distutils.core import setup
-
- setup(**metadata)
-
-
-if __name__ == "__main__":
- setup_package()
diff --git a/tests/__init__.py b/tests/__init__.py
index 96c475ed..461ed5b5 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -1,3 +1,4 @@
import matplotlib
+
matplotlib.use("agg")
diff --git a/tests/conftest.py b/tests/conftest.py
new file mode 100644
index 00000000..17df8c24
--- /dev/null
+++ b/tests/conftest.py
@@ -0,0 +1,24 @@
+import os
+import sys
+
+import pytest
+
+
+skip_on_windows = pytest.mark.skipif(
+ sys.platform.startswith("win"),
+ reason="Ghostscript not installed in Windows test environment",
+)
+
+skip_pdftopng = pytest.mark.skip(
+ reason="Ghostscript not installed in Windows test environment",
+)
+
+
+@pytest.fixture
+def testdir():
+ return os.path.join(os.path.dirname(os.path.abspath(__file__)), "files")
+
+
+@pytest.fixture
+def foo_pdf(testdir):
+ return os.path.join(testdir, "foo.pdf")
diff --git a/tests/data.py b/tests/data.py
old mode 100755
new mode 100644
index 835fbb41..6d743183
--- a/tests/data.py
+++ b/tests/data.py
@@ -1,5 +1,4 @@
-# -*- coding: utf-8 -*-
-
+# noqa
data_stream = [
["States-A", "Revenue", "", "Capital", "", "Total", "Others(1)", "Total"],
diff --git a/tests/files/diesel_engines.pdf b/tests/files/diesel_engines.pdf
new file mode 100644
index 00000000..2f14f161
Binary files /dev/null and b/tests/files/diesel_engines.pdf differ
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-001.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-001.json
index a801a86e..b71f0def 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-001.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-001.json
@@ -1 +1,6 @@
-{"numExpectedTables":7,"numCorrectlyDetectedTables":7,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 7,
+ "numCorrectlyDetectedTables": 7,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-002.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-002.json
index a55497df..db417b53 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-002.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-002.json
@@ -1 +1,6 @@
-{"numExpectedTables":1,"numCorrectlyDetectedTables":1,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 1,
+ "numCorrectlyDetectedTables": 1,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-003.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-003.json
index 71ae6a77..77371fe9 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-003.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-003.json
@@ -1 +1,6 @@
-{"numExpectedTables":3,"numCorrectlyDetectedTables":3,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 3,
+ "numCorrectlyDetectedTables": 3,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-004.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-004.json
index 755a6e46..935ba38a 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-004.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-004.json
@@ -1 +1,6 @@
-{"numExpectedTables":12,"numCorrectlyDetectedTables":12,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 12,
+ "numCorrectlyDetectedTables": 12,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-005.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-005.json
index 1bf5fd29..69f0b666 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-005.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-005.json
@@ -1 +1,6 @@
-{"numExpectedTables":2,"numCorrectlyDetectedTables":2,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 2,
+ "numCorrectlyDetectedTables": 2,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-006.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-006.json
index 11be9878..261d6f3c 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-006.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-006.json
@@ -1 +1,6 @@
-{"numExpectedTables":4,"numCorrectlyDetectedTables":4,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 4,
+ "numCorrectlyDetectedTables": 4,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-007.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-007.json
index 4bed3d03..7bc7116c 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-007.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-007.json
@@ -1 +1,6 @@
-{"numExpectedTables":6,"numCorrectlyDetectedTables":6,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 6,
+ "numCorrectlyDetectedTables": 6,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-008.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-008.json
index a55497df..db417b53 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-008.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-008.json
@@ -1 +1,6 @@
-{"numExpectedTables":1,"numCorrectlyDetectedTables":1,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 1,
+ "numCorrectlyDetectedTables": 1,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-009a-str.xml b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-009a-str.xml
index 6bb1f8c0..9a21fab5 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-009a-str.xml
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-009a-str.xml
@@ -185,7 +185,7 @@ prepared”
3a
-
+
Influence on project
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-009a.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-009a.json
index a55497df..db417b53 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-009a.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-009a.json
@@ -1 +1,6 @@
-{"numExpectedTables":1,"numCorrectlyDetectedTables":1,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 1,
+ "numCorrectlyDetectedTables": 1,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-009b-str.xml b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-009b-str.xml
index 83dc7a29..83555652 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-009b-str.xml
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-009b-str.xml
@@ -178,7 +178,7 @@ prepared”
3a
|
-
+
Influence on project
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-010.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-010.json
index a55497df..db417b53 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-010.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-010.json
@@ -1 +1,6 @@
-{"numExpectedTables":1,"numCorrectlyDetectedTables":1,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 1,
+ "numCorrectlyDetectedTables": 1,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-011.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-011.json
index 492239e1..8cea7c3e 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-011.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-011.json
@@ -1 +1,6 @@
-{"numExpectedTables":1,"numCorrectlyDetectedTables":1,"numErroneouslyDetectedTables":3,"expectedFailure":true}
\ No newline at end of file
+{
+ "numExpectedTables": 1,
+ "numCorrectlyDetectedTables": 1,
+ "numErroneouslyDetectedTables": 3,
+ "expectedFailure": true
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-012.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-012.json
index b1fa6930..850ac646 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-012.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-012.json
@@ -1 +1,6 @@
-{"numExpectedTables":5,"numCorrectlyDetectedTables":5,"numErroneouslyDetectedTables":1,"expectedFailure":true}
\ No newline at end of file
+{
+ "numExpectedTables": 5,
+ "numCorrectlyDetectedTables": 5,
+ "numErroneouslyDetectedTables": 1,
+ "expectedFailure": true
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-013.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-013.json
index 074b6f59..0e0ada23 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-013.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-013.json
@@ -1 +1,6 @@
-{"numExpectedTables":4,"numCorrectlyDetectedTables":4,"numErroneouslyDetectedTables":1,"expectedFailure":true}
\ No newline at end of file
+{
+ "numExpectedTables": 4,
+ "numCorrectlyDetectedTables": 4,
+ "numErroneouslyDetectedTables": 1,
+ "expectedFailure": true
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-014.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-014.json
index e0d0b7bd..b8465b94 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-014.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-014.json
@@ -1 +1,6 @@
-{"numExpectedTables":1,"numCorrectlyDetectedTables":0,"numErroneouslyDetectedTables":1,"expectedFailure":true}
\ No newline at end of file
+{
+ "numExpectedTables": 1,
+ "numCorrectlyDetectedTables": 0,
+ "numErroneouslyDetectedTables": 1,
+ "expectedFailure": true
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-015.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-015.json
index fc890104..90012dd3 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-015.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-015.json
@@ -1 +1,6 @@
-{"numExpectedTables":5,"numCorrectlyDetectedTables":2,"numErroneouslyDetectedTables":2,"expectedFailure":true}
\ No newline at end of file
+{
+ "numExpectedTables": 5,
+ "numCorrectlyDetectedTables": 2,
+ "numErroneouslyDetectedTables": 2,
+ "expectedFailure": true
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-016.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-016.json
index a55497df..db417b53 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-016.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-016.json
@@ -1 +1,6 @@
-{"numExpectedTables":1,"numCorrectlyDetectedTables":1,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 1,
+ "numCorrectlyDetectedTables": 1,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-017.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-017.json
index 16d936d5..17bd403b 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-017.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-017.json
@@ -1 +1,6 @@
-{"numExpectedTables":1,"numCorrectlyDetectedTables":1,"numErroneouslyDetectedTables":1,"expectedFailure":true}
\ No newline at end of file
+{
+ "numExpectedTables": 1,
+ "numCorrectlyDetectedTables": 1,
+ "numErroneouslyDetectedTables": 1,
+ "expectedFailure": true
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-018.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-018.json
index 1bf5fd29..69f0b666 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-018.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-018.json
@@ -1 +1,6 @@
-{"numExpectedTables":2,"numCorrectlyDetectedTables":2,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 2,
+ "numCorrectlyDetectedTables": 2,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-019.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-019.json
index a55497df..db417b53 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-019.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-019.json
@@ -1 +1,6 @@
-{"numExpectedTables":1,"numCorrectlyDetectedTables":1,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 1,
+ "numCorrectlyDetectedTables": 1,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-020.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-020.json
index 71ae6a77..77371fe9 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-020.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-020.json
@@ -1 +1,6 @@
-{"numExpectedTables":3,"numCorrectlyDetectedTables":3,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 3,
+ "numCorrectlyDetectedTables": 3,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-021.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-021.json
index 1bf5fd29..69f0b666 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-021.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-021.json
@@ -1 +1,6 @@
-{"numExpectedTables":2,"numCorrectlyDetectedTables":2,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 2,
+ "numCorrectlyDetectedTables": 2,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-022.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-022.json
index a55497df..db417b53 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-022.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-022.json
@@ -1 +1,6 @@
-{"numExpectedTables":1,"numCorrectlyDetectedTables":1,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 1,
+ "numCorrectlyDetectedTables": 1,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-023.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-023.json
index a55497df..db417b53 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-023.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-023.json
@@ -1 +1,6 @@
-{"numExpectedTables":1,"numCorrectlyDetectedTables":1,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 1,
+ "numCorrectlyDetectedTables": 1,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-024.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-024.json
index a55497df..db417b53 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-024.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-024.json
@@ -1 +1,6 @@
-{"numExpectedTables":1,"numCorrectlyDetectedTables":1,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 1,
+ "numCorrectlyDetectedTables": 1,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-025.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-025.json
index 4d74e0e7..5df8c076 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-025.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-025.json
@@ -1 +1,6 @@
-{"numExpectedTables":5,"numCorrectlyDetectedTables":5,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 5,
+ "numCorrectlyDetectedTables": 5,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-026.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-026.json
index 71ae6a77..77371fe9 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-026.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-026.json
@@ -1 +1,6 @@
-{"numExpectedTables":3,"numCorrectlyDetectedTables":3,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 3,
+ "numCorrectlyDetectedTables": 3,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-027.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-027.json
index a55497df..db417b53 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-027.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-eu/eu-027.json
@@ -1 +1,6 @@
-{"numExpectedTables":1,"numCorrectlyDetectedTables":1,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 1,
+ "numCorrectlyDetectedTables": 1,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-001.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-001.json
index a2697933..b424a76f 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-001.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-001.json
@@ -1 +1,6 @@
-{"numExpectedTables":2,"numCorrectlyDetectedTables":2,"numErroneouslyDetectedTables":2,"expectedFailure":true}
\ No newline at end of file
+{
+ "numExpectedTables": 2,
+ "numCorrectlyDetectedTables": 2,
+ "numErroneouslyDetectedTables": 2,
+ "expectedFailure": true
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-002.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-002.json
index 1bf5fd29..69f0b666 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-002.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-002.json
@@ -1 +1,6 @@
-{"numExpectedTables":2,"numCorrectlyDetectedTables":2,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 2,
+ "numCorrectlyDetectedTables": 2,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-003.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-003.json
index a55497df..db417b53 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-003.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-003.json
@@ -1 +1,6 @@
-{"numExpectedTables":1,"numCorrectlyDetectedTables":1,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 1,
+ "numCorrectlyDetectedTables": 1,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-004.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-004.json
index a55497df..db417b53 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-004.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-004.json
@@ -1 +1,6 @@
-{"numExpectedTables":1,"numCorrectlyDetectedTables":1,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 1,
+ "numCorrectlyDetectedTables": 1,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-005.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-005.json
index a55497df..db417b53 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-005.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-005.json
@@ -1 +1,6 @@
-{"numExpectedTables":1,"numCorrectlyDetectedTables":1,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 1,
+ "numCorrectlyDetectedTables": 1,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-006.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-006.json
index a55497df..db417b53 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-006.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-006.json
@@ -1 +1,6 @@
-{"numExpectedTables":1,"numCorrectlyDetectedTables":1,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 1,
+ "numCorrectlyDetectedTables": 1,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-007.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-007.json
index 1bf5fd29..69f0b666 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-007.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-007.json
@@ -1 +1,6 @@
-{"numExpectedTables":2,"numCorrectlyDetectedTables":2,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 2,
+ "numCorrectlyDetectedTables": 2,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-008.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-008.json
index 1bf5fd29..69f0b666 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-008.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-008.json
@@ -1 +1,6 @@
-{"numExpectedTables":2,"numCorrectlyDetectedTables":2,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 2,
+ "numCorrectlyDetectedTables": 2,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-009.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-009.json
index a55497df..db417b53 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-009.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-009.json
@@ -1 +1,6 @@
-{"numExpectedTables":1,"numCorrectlyDetectedTables":1,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 1,
+ "numCorrectlyDetectedTables": 1,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-010.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-010.json
index a55497df..db417b53 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-010.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-010.json
@@ -1 +1,6 @@
-{"numExpectedTables":1,"numCorrectlyDetectedTables":1,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 1,
+ "numCorrectlyDetectedTables": 1,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-011a.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-011a.json
index 1bf5fd29..69f0b666 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-011a.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-011a.json
@@ -1 +1,6 @@
-{"numExpectedTables":2,"numCorrectlyDetectedTables":2,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 2,
+ "numCorrectlyDetectedTables": 2,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-012.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-012.json
index a55497df..db417b53 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-012.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-012.json
@@ -1 +1,6 @@
-{"numExpectedTables":1,"numCorrectlyDetectedTables":1,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 1,
+ "numCorrectlyDetectedTables": 1,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-013.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-013.json
index a55497df..db417b53 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-013.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-013.json
@@ -1 +1,6 @@
-{"numExpectedTables":1,"numCorrectlyDetectedTables":1,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 1,
+ "numCorrectlyDetectedTables": 1,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-014-str.xml b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-014-str.xml
index b64c9130..326f39ec 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-014-str.xml
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-014-str.xml
@@ -351,4 +351,4 @@ Agreeing
|
-
+
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-014.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-014.json
index 1bf5fd29..69f0b666 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-014.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-014.json
@@ -1 +1,6 @@
-{"numExpectedTables":2,"numCorrectlyDetectedTables":2,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 2,
+ "numCorrectlyDetectedTables": 2,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-015.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-015.json
index 1bf5fd29..69f0b666 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-015.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-015.json
@@ -1 +1,6 @@
-{"numExpectedTables":2,"numCorrectlyDetectedTables":2,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 2,
+ "numCorrectlyDetectedTables": 2,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-016.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-016.json
index a55497df..db417b53 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-016.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-016.json
@@ -1 +1,6 @@
-{"numExpectedTables":1,"numCorrectlyDetectedTables":1,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 1,
+ "numCorrectlyDetectedTables": 1,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-017.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-017.json
index 4bed3d03..7bc7116c 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-017.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-017.json
@@ -1 +1,6 @@
-{"numExpectedTables":6,"numCorrectlyDetectedTables":6,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 6,
+ "numCorrectlyDetectedTables": 6,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-018.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-018.json
index ed2b5b39..0de01ef8 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-018.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-018.json
@@ -1 +1,6 @@
-{"numExpectedTables":7,"numCorrectlyDetectedTables":6,"numErroneouslyDetectedTables":1,"expectedFailure":true}
\ No newline at end of file
+{
+ "numExpectedTables": 7,
+ "numCorrectlyDetectedTables": 6,
+ "numErroneouslyDetectedTables": 1,
+ "expectedFailure": true
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-019.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-019.json
index 4279ae6b..b6eaa91d 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-019.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-019.json
@@ -1 +1,6 @@
-{"numExpectedTables":4,"numCorrectlyDetectedTables":2,"numErroneouslyDetectedTables":2,"expectedFailure":true}
\ No newline at end of file
+{
+ "numExpectedTables": 4,
+ "numCorrectlyDetectedTables": 2,
+ "numErroneouslyDetectedTables": 2,
+ "expectedFailure": true
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-020.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-020.json
index 11be9878..261d6f3c 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-020.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-020.json
@@ -1 +1,6 @@
-{"numExpectedTables":4,"numCorrectlyDetectedTables":4,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 4,
+ "numCorrectlyDetectedTables": 4,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-021.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-021.json
index 1bf5fd29..69f0b666 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-021.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-021.json
@@ -1 +1,6 @@
-{"numExpectedTables":2,"numCorrectlyDetectedTables":2,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 2,
+ "numCorrectlyDetectedTables": 2,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-022.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-022.json
index a55497df..db417b53 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-022.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-022.json
@@ -1 +1,6 @@
-{"numExpectedTables":1,"numCorrectlyDetectedTables":1,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 1,
+ "numCorrectlyDetectedTables": 1,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-023.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-023.json
index 79301b7d..feb114b1 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-023.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-023.json
@@ -1 +1,6 @@
-{"numExpectedTables":1,"numCorrectlyDetectedTables":1,"numErroneouslyDetectedTables":4,"expectedFailure":true}
\ No newline at end of file
+{
+ "numExpectedTables": 1,
+ "numCorrectlyDetectedTables": 1,
+ "numErroneouslyDetectedTables": 4,
+ "expectedFailure": true
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-024.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-024.json
index e700926c..dab08e97 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-024.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-024.json
@@ -1 +1,6 @@
-{"numExpectedTables":4,"numCorrectlyDetectedTables":4,"numErroneouslyDetectedTables":2,"expectedFailure":true}
\ No newline at end of file
+{
+ "numExpectedTables": 4,
+ "numCorrectlyDetectedTables": 4,
+ "numErroneouslyDetectedTables": 2,
+ "expectedFailure": true
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-025-str.xml b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-025-str.xml
index 6fdb985e..7adaf9a0 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-025-str.xml
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-025-str.xml
@@ -1,7 +1,7 @@
-
+
-
+
-
+
@@ -1843,7 +1843,7 @@ group
|
-
+
@@ -2759,7 +2759,7 @@ group
|
-
+
@@ -3271,7 +3271,7 @@ group
|
-
+
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-025.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-025.json
index f783d0e7..c8351c71 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-025.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-025.json
@@ -1 +1,6 @@
-{"numExpectedTables":6,"numCorrectlyDetectedTables":2,"numErroneouslyDetectedTables":3,"expectedFailure":true}
\ No newline at end of file
+{
+ "numExpectedTables": 6,
+ "numCorrectlyDetectedTables": 2,
+ "numErroneouslyDetectedTables": 3,
+ "expectedFailure": true
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-026.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-026.json
index a55497df..db417b53 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-026.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-026.json
@@ -1 +1,6 @@
-{"numExpectedTables":1,"numCorrectlyDetectedTables":1,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 1,
+ "numCorrectlyDetectedTables": 1,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-027.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-027.json
index 1bf5fd29..69f0b666 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-027.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-027.json
@@ -1 +1,6 @@
-{"numExpectedTables":2,"numCorrectlyDetectedTables":2,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 2,
+ "numCorrectlyDetectedTables": 2,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-028.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-028.json
index a2697933..b424a76f 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-028.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-028.json
@@ -1 +1,6 @@
-{"numExpectedTables":2,"numCorrectlyDetectedTables":2,"numErroneouslyDetectedTables":2,"expectedFailure":true}
\ No newline at end of file
+{
+ "numExpectedTables": 2,
+ "numCorrectlyDetectedTables": 2,
+ "numErroneouslyDetectedTables": 2,
+ "expectedFailure": true
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-029.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-029.json
index a55497df..db417b53 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-029.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-029.json
@@ -1 +1,6 @@
-{"numExpectedTables":1,"numCorrectlyDetectedTables":1,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 1,
+ "numCorrectlyDetectedTables": 1,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-030.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-030.json
index a55497df..db417b53 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-030.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-030.json
@@ -1 +1,6 @@
-{"numExpectedTables":1,"numCorrectlyDetectedTables":1,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 1,
+ "numCorrectlyDetectedTables": 1,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-031a.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-031a.json
index a55497df..db417b53 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-031a.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-031a.json
@@ -1 +1,6 @@
-{"numExpectedTables":1,"numCorrectlyDetectedTables":1,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 1,
+ "numCorrectlyDetectedTables": 1,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-032.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-032.json
index a55497df..db417b53 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-032.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-032.json
@@ -1 +1,6 @@
-{"numExpectedTables":1,"numCorrectlyDetectedTables":1,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 1,
+ "numCorrectlyDetectedTables": 1,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-033.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-033.json
index 8bca2b65..6dc747e6 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-033.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-033.json
@@ -1 +1,6 @@
-{"numExpectedTables":3,"numCorrectlyDetectedTables":1,"numErroneouslyDetectedTables":2,"expectedFailure":true}
\ No newline at end of file
+{
+ "numExpectedTables": 3,
+ "numCorrectlyDetectedTables": 1,
+ "numErroneouslyDetectedTables": 2,
+ "expectedFailure": true
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-034.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-034.json
index eb903a1d..50c30d65 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-034.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-034.json
@@ -1 +1,6 @@
-{"numExpectedTables":2,"numCorrectlyDetectedTables":0,"numErroneouslyDetectedTables":1,"expectedFailure":true}
\ No newline at end of file
+{
+ "numExpectedTables": 2,
+ "numCorrectlyDetectedTables": 0,
+ "numErroneouslyDetectedTables": 1,
+ "expectedFailure": true
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-035a.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-035a.json
index 71ae6a77..77371fe9 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-035a.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-035a.json
@@ -1 +1,6 @@
-{"numExpectedTables":3,"numCorrectlyDetectedTables":3,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 3,
+ "numCorrectlyDetectedTables": 3,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-036.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-036.json
index a55497df..db417b53 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-036.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-036.json
@@ -1 +1,6 @@
-{"numExpectedTables":1,"numCorrectlyDetectedTables":1,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 1,
+ "numCorrectlyDetectedTables": 1,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-037.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-037.json
index 8dd87c33..8047a2c2 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-037.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-037.json
@@ -1 +1,6 @@
-{"numExpectedTables":1,"numCorrectlyDetectedTables":0,"numErroneouslyDetectedTables":3,"expectedFailure":true}
\ No newline at end of file
+{
+ "numExpectedTables": 1,
+ "numCorrectlyDetectedTables": 0,
+ "numErroneouslyDetectedTables": 3,
+ "expectedFailure": true
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-038.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-038.json
index a55497df..db417b53 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-038.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-038.json
@@ -1 +1,6 @@
-{"numExpectedTables":1,"numCorrectlyDetectedTables":1,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 1,
+ "numCorrectlyDetectedTables": 1,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-039.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-039.json
index a55497df..db417b53 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-039.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-039.json
@@ -1 +1,6 @@
-{"numExpectedTables":1,"numCorrectlyDetectedTables":1,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 1,
+ "numCorrectlyDetectedTables": 1,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-040.json b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-040.json
index a55497df..db417b53 100644
--- a/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-040.json
+++ b/tests/files/tabula/icdar2013-dataset/competition-dataset-us/us-040.json
@@ -1 +1,6 @@
-{"numExpectedTables":1,"numCorrectlyDetectedTables":1,"numErroneouslyDetectedTables":0,"expectedFailure":false}
\ No newline at end of file
+{
+ "numExpectedTables": 1,
+ "numCorrectlyDetectedTables": 1,
+ "numErroneouslyDetectedTables": 0,
+ "expectedFailure": false
+}
diff --git a/tests/test_cli.py b/tests/test_cli.py
old mode 100755
new mode 100644
index 70b5b7bb..0903dfa3
--- a/tests/test_cli.py
+++ b/tests/test_cli.py
@@ -1,22 +1,10 @@
-# -*- coding: utf-8 -*-
-
import os
-import sys
-import pytest
from click.testing import CliRunner
from camelot.cli import cli
from camelot.utils import TemporaryDirectory
-
-
-testdir = os.path.dirname(os.path.abspath(__file__))
-testdir = os.path.join(testdir, "files")
-
-skip_on_windows = pytest.mark.skipif(
- sys.platform.startswith("win"),
- reason="Ghostscript not installed in Windows test environment",
-)
+from tests.conftest import skip_on_windows
def test_help_output():
@@ -34,7 +22,7 @@ def test_help_output():
@skip_on_windows
-def test_cli_lattice():
+def test_cli_lattice(testdir):
with TemporaryDirectory() as tempdir:
infile = os.path.join(testdir, "foo.pdf")
outfile = os.path.join(tempdir, "foo.csv")
@@ -54,7 +42,7 @@ def test_cli_lattice():
assert format_error in result.output
-def test_cli_stream():
+def test_cli_stream(testdir):
with TemporaryDirectory() as tempdir:
infile = os.path.join(testdir, "budget.pdf")
outfile = os.path.join(tempdir, "budget.csv")
@@ -74,7 +62,31 @@ def test_cli_stream():
assert format_error in result.output
-def test_cli_password():
+@skip_on_windows
+def test_cli_parallel(testdir):
+ with TemporaryDirectory() as tempdir:
+ infile = os.path.join(testdir, "diesel_engines.pdf")
+ outfile = os.path.join(tempdir, "diesel_engines.csv")
+ runner = CliRunner()
+ result = runner.invoke(
+ cli,
+ [
+ "--parallel",
+ "--pages",
+ "1,2,3",
+ "--format",
+ "csv",
+ "--output",
+ outfile,
+ "lattice",
+ infile,
+ ],
+ )
+ assert result.exit_code == 0
+ assert result.output == "Found 2 tables\n"
+
+
+def test_cli_password(testdir):
with TemporaryDirectory() as tempdir:
infile = os.path.join(testdir, "health_protected.pdf")
outfile = os.path.join(tempdir, "health_protected.csv")
@@ -95,7 +107,7 @@ def test_cli_password():
assert result.exit_code == 0
assert result.output == "Found 1 tables\n"
- output_error = "file has not been decrypted"
+ output_error = "File has not been decrypted"
# no password
result = runner.invoke(
cli, ["--format", "csv", "--output", outfile, "stream", infile]
@@ -119,7 +131,7 @@ def test_cli_password():
assert output_error in str(result.exception)
-def test_cli_output_format():
+def test_cli_output_format(testdir):
with TemporaryDirectory() as tempdir:
infile = os.path.join(testdir, "health.pdf")
@@ -174,7 +186,7 @@ def test_cli_output_format():
assert result.exit_code == 0, f"Output: {result.output}"
-def test_cli_quiet():
+def test_cli_quiet(testdir):
with TemporaryDirectory() as tempdir:
infile = os.path.join(testdir, "empty.pdf")
outfile = os.path.join(tempdir, "empty.csv")
@@ -183,7 +195,7 @@ def test_cli_quiet():
result = runner.invoke(
cli, ["--format", "csv", "--output", outfile, "stream", infile]
)
- assert "No tables found on page-1" in result.output
+ assert "Found 0 tables" in result.output
result = runner.invoke(
cli, ["--quiet", "--format", "csv", "--output", outfile, "stream", infile]
diff --git a/tests/test_common.py b/tests/test_common.py
index 5d0054b8..ca9910d0 100644
--- a/tests/test_common.py
+++ b/tests/test_common.py
@@ -1,28 +1,19 @@
-# -*- coding: utf-8 -*-
-
import os
-import sys
+from pathlib import Path
-import pytest
import pandas as pd
from pandas.testing import assert_frame_equal
import camelot
-from camelot.io import PDFHandler
-from camelot.core import Table, TableList
from camelot.__version__ import generate_version
-from camelot.backends import ImageConversionBackend
+from camelot.core import Table
+from camelot.core import TableList
+from camelot.io import PDFHandler
+from .conftest import skip_on_windows
+from .conftest import skip_pdftopng
from .data import *
-testdir = os.path.dirname(os.path.abspath(__file__))
-testdir = os.path.join(testdir, "files")
-
-skip_on_windows = pytest.mark.skipif(
- sys.platform.startswith("win"),
- reason="Ghostscript not installed in Windows test environment",
-)
-
def test_version_generation():
version = (0, 7, 3)
@@ -40,7 +31,7 @@ def test_version_generation_with_prerelease_revision():
@skip_on_windows
-def test_parsing_report():
+def test_parsing_report(testdir):
parsing_report = {"accuracy": 99.02, "whitespace": 12.24, "order": 1, "page": 1}
filename = os.path.join(testdir, "foo.pdf")
@@ -48,7 +39,7 @@ def test_parsing_report():
assert tables[0].parsing_report == parsing_report
-def test_password():
+def test_password(testdir):
df = pd.DataFrame(data_stream)
filename = os.path.join(testdir, "health_protected.pdf")
@@ -59,7 +50,8 @@ def test_password():
assert_frame_equal(df, tables[0].df)
-def test_repr_poppler():
+@skip_pdftopng
+def test_repr_poppler(testdir):
filename = os.path.join(testdir, "foo.pdf")
tables = camelot.read_pdf(filename, backend="poppler")
assert repr(tables) == ""
@@ -68,7 +60,7 @@ def test_repr_poppler():
@skip_on_windows
-def test_repr_ghostscript():
+def test_repr_ghostscript(testdir):
filename = os.path.join(testdir, "foo.pdf")
tables = camelot.read_pdf(filename, backend="ghostscript")
assert repr(tables) == ""
@@ -76,6 +68,7 @@ def test_repr_ghostscript():
assert repr(tables[0].cells[0][0]) == ""
+@skip_pdftopng
def test_url_poppler():
url = "https://camelot-py.readthedocs.io/en/master/_static/pdf/foo.pdf"
tables = camelot.read_pdf(url, backend="poppler")
@@ -85,7 +78,7 @@ def test_url_poppler():
@skip_on_windows
-def test_url_ghostscript():
+def test_url_ghostscript(testdir):
url = "https://camelot-py.readthedocs.io/en/master/_static/pdf/foo.pdf"
tables = camelot.read_pdf(url, backend="ghostscript")
assert repr(tables) == ""
@@ -93,6 +86,7 @@ def test_url_ghostscript():
assert repr(tables[0].cells[0][0]) == ""
+@skip_pdftopng
def test_pages_poppler():
url = "https://camelot-py.readthedocs.io/en/master/_static/pdf/foo.pdf"
tables = camelot.read_pdf(url, backend="poppler")
@@ -155,7 +149,7 @@ def _make_table(page, order):
]
-def test_handler_pages_generator():
+def test_handler_pages_generator(testdir):
filename = os.path.join(testdir, "foo.pdf")
handler = PDFHandler(filename)
@@ -172,3 +166,25 @@ def test_handler_pages_generator():
handler = PDFHandler(filename)
assert handler._get_pages("1,2,5-10") == [1, 2, 5, 6, 7, 8, 9, 10]
+
+ handler = PDFHandler(
+ os.path.join(testdir, "health_protected.pdf"), password="ownerpass"
+ )
+
+ assert handler._get_pages("all") == [1]
+
+
+def test_handler_with_stream(testdir):
+ filename = os.path.join(testdir, "foo.pdf")
+
+ with open(filename, "rb") as f:
+ handler = PDFHandler(f)
+ assert handler._get_pages("1") == [1]
+
+
+def test_handler_with_pathlib(testdir):
+ filename = Path(os.path.join(testdir, "foo.pdf"))
+
+ with open(filename, "rb") as f:
+ handler = PDFHandler(f)
+ assert handler._get_pages("1") == [1]
diff --git a/tests/test_errors.py b/tests/test_errors.py
old mode 100755
new mode 100644
index 1cdb344c..eaa720ec
--- a/tests/test_errors.py
+++ b/tests/test_errors.py
@@ -1,115 +1,103 @@
-# -*- coding: utf-8 -*-
-
import os
-import sys
import warnings
import pytest
import camelot
+from tests.conftest import skip_on_windows
-testdir = os.path.dirname(os.path.abspath(__file__))
-testdir = os.path.join(testdir, "files")
-filename = os.path.join(testdir, "foo.pdf")
-
-skip_on_windows = pytest.mark.skipif(
- sys.platform.startswith("win"),
- reason="Ghostscript not installed in Windows test environment",
-)
-
-
-def test_unknown_flavor():
+def test_unknown_flavor(foo_pdf):
message = "Unknown flavor specified." " Use either 'lattice' or 'stream'"
with pytest.raises(NotImplementedError, match=message):
- tables = camelot.read_pdf(filename, flavor="chocolate")
+ camelot.read_pdf(foo_pdf, flavor="chocolate")
-def test_input_kwargs():
+def test_input_kwargs(foo_pdf):
message = "columns cannot be used with flavor='lattice'"
with pytest.raises(ValueError, match=message):
- tables = camelot.read_pdf(filename, columns=["10,20,30,40"])
+ camelot.read_pdf(foo_pdf, columns=["10,20,30,40"])
-def test_unsupported_format():
+def test_unsupported_format(testdir):
message = "File format not supported"
filename = os.path.join(testdir, "foo.csv")
with pytest.raises(NotImplementedError, match=message):
- tables = camelot.read_pdf(filename)
+ camelot.read_pdf(filename)
@skip_on_windows
-def test_no_tables_found_logs_suppressed():
+def test_no_tables_found_logs_suppressed(testdir):
filename = os.path.join(testdir, "foo.pdf")
with warnings.catch_warnings():
# the test should fail if any warning is thrown
warnings.simplefilter("error")
try:
- tables = camelot.read_pdf(filename, suppress_stdout=True)
+ camelot.read_pdf(filename, suppress_stdout=True)
except Warning as e:
warning_text = str(e)
pytest.fail(f"Unexpected warning: {warning_text}")
-def test_no_tables_found_warnings_suppressed():
+def test_no_tables_found_warnings_suppressed(testdir):
filename = os.path.join(testdir, "empty.pdf")
with warnings.catch_warnings():
# the test should fail if any warning is thrown
warnings.simplefilter("error")
try:
- tables = camelot.read_pdf(filename, suppress_stdout=True)
+ camelot.read_pdf(filename, suppress_stdout=True)
except Warning as e:
warning_text = str(e)
pytest.fail(f"Unexpected warning: {warning_text}")
-def test_no_password():
+def test_no_password(testdir):
filename = os.path.join(testdir, "health_protected.pdf")
- message = "file has not been decrypted"
+ message = "File has not been decrypted"
with pytest.raises(Exception, match=message):
- tables = camelot.read_pdf(filename)
+ camelot.read_pdf(filename)
-def test_bad_password():
+def test_bad_password(testdir):
filename = os.path.join(testdir, "health_protected.pdf")
- message = "file has not been decrypted"
+ message = "File has not been decrypted"
with pytest.raises(Exception, match=message):
- tables = camelot.read_pdf(filename, password="wrongpass")
+ camelot.read_pdf(filename, password="wrongpass")
-def test_stream_equal_length():
+def test_stream_equal_length(foo_pdf):
message = "Length of table_areas and columns" " should be equal"
with pytest.raises(ValueError, match=message):
- tables = camelot.read_pdf(
- filename,
+ camelot.read_pdf(
+ foo_pdf,
flavor="stream",
table_areas=["10,20,30,40"],
columns=["10,20,30,40", "10,20,30,40"],
)
-def test_image_warning():
+def test_image_warning(testdir):
filename = os.path.join(testdir, "image.pdf")
with warnings.catch_warnings():
warnings.simplefilter("error", category=UserWarning)
with pytest.raises(UserWarning) as e:
- tables = camelot.read_pdf(filename)
+ camelot.read_pdf(filename)
assert (
str(e.value)
== "page-1 is image-based, camelot only works on text-based pages."
)
-def test_stream_no_tables_on_page():
+def test_stream_no_tables_on_page(testdir):
filename = os.path.join(testdir, "empty.pdf")
with warnings.catch_warnings():
warnings.simplefilter("error")
with pytest.raises(UserWarning) as e:
- tables = camelot.read_pdf(filename, flavor="stream")
+ camelot.read_pdf(filename, flavor="stream")
assert str(e.value) == "No tables found on page-1"
-def test_stream_no_tables_in_area():
+def test_stream_no_tables_in_area(testdir):
filename = os.path.join(testdir, "only_page_number.pdf")
with warnings.catch_warnings():
warnings.simplefilter("error")
@@ -118,7 +106,7 @@ def test_stream_no_tables_in_area():
assert str(e.value) == "No tables found in table area 1"
-def test_lattice_no_tables_on_page():
+def test_lattice_no_tables_on_page(testdir):
filename = os.path.join(testdir, "empty.pdf")
with warnings.catch_warnings():
warnings.simplefilter("error", category=UserWarning)
@@ -127,22 +115,22 @@ def test_lattice_no_tables_on_page():
assert str(e.value) == "No tables found on page-1"
-def test_lattice_unknown_backend():
+def test_lattice_unknown_backend(foo_pdf):
message = "Unknown backend 'mupdf' specified. Please use either 'poppler' or 'ghostscript'."
with pytest.raises(NotImplementedError, match=message):
- tables = camelot.read_pdf(filename, backend="mupdf")
+ tables = camelot.read_pdf(foo_pdf, backend="mupdf")
-def test_lattice_no_convert_method():
- class ConversionBackend(object):
+def test_lattice_no_convert_method(foo_pdf):
+ class ConversionBackend:
pass
message = "must implement a 'convert' method"
with pytest.raises(NotImplementedError, match=message):
- tables = camelot.read_pdf(filename, backend=ConversionBackend())
+ camelot.read_pdf(foo_pdf, backend=ConversionBackend())
-def test_lattice_ghostscript_deprecation_warning():
+def test_lattice_ghostscript_deprecation_warning(foo_pdf):
ghostscript_deprecation_warning = (
"'ghostscript' will be replaced by 'poppler' as the default image conversion"
" backend in v0.12.0. You can try out 'poppler' with backend='poppler'."
@@ -151,5 +139,5 @@ def test_lattice_ghostscript_deprecation_warning():
with warnings.catch_warnings():
warnings.simplefilter("error")
with pytest.raises(DeprecationWarning) as e:
- tables = camelot.read_pdf(filename)
+ camelot.read_pdf(foo_pdf)
assert str(e.value) == ghostscript_deprecation_warning
diff --git a/tests/test_image_conversion_backend.py b/tests/test_image_conversion_backend.py
index 39f56e69..d1d85b0b 100644
--- a/tests/test_image_conversion_backend.py
+++ b/tests/test_image_conversion_backend.py
@@ -1,34 +1,36 @@
-# -*- coding: utf-8 -*-
-
import pytest
-import camelot.backends.image_conversion
from camelot.backends import ImageConversionBackend
-class PopplerBackendError(object):
+@pytest.fixture
+def patch_backends(monkeypatch):
+ monkeypatch.setattr(
+ "camelot.backends.image_conversion.BACKENDS",
+ {
+ "poppler": PopplerBackendError,
+ "ghostscript": GhostscriptBackendNoError,
+ },
+ raising=True,
+ )
+
+
+class PopplerBackendError:
def convert(self, pdf_path, png_path):
raise ValueError("Image conversion failed")
-class GhostscriptBackendError(object):
+class GhostscriptBackendError:
def convert(self, pdf_path, png_path):
raise ValueError("Image conversion failed")
-class GhostscriptBackendNoError(object):
+class GhostscriptBackendNoError:
def convert(self, pdf_path, png_path):
pass
-def test_poppler_backend_error_when_no_use_fallback(monkeypatch):
- BACKENDS = {
- "poppler": PopplerBackendError,
- "ghostscript": GhostscriptBackendNoError,
- }
- monkeypatch.setattr(
- "camelot.backends.image_conversion.BACKENDS", BACKENDS, raising=True
- )
+def test_poppler_backend_error_when_no_use_fallback(patch_backends):
backend = ImageConversionBackend(use_fallback=False)
message = "Image conversion failed with image conversion backend 'poppler'"
@@ -36,22 +38,16 @@ def test_poppler_backend_error_when_no_use_fallback(monkeypatch):
backend.convert("foo", "bar")
-def test_ghostscript_backend_when_use_fallback(monkeypatch):
- BACKENDS = {
- "poppler": PopplerBackendError,
- "ghostscript": GhostscriptBackendNoError,
- }
- monkeypatch.setattr(
- "camelot.backends.image_conversion.BACKENDS", BACKENDS, raising=True
- )
+def test_ghostscript_backend_when_use_fallback(patch_backends):
backend = ImageConversionBackend()
backend.convert("foo", "bar")
def test_ghostscript_backend_error_when_use_fallback(monkeypatch):
- BACKENDS = {"poppler": PopplerBackendError, "ghostscript": GhostscriptBackendError}
+ backends = {"poppler": PopplerBackendError, "ghostscript": GhostscriptBackendError}
+
monkeypatch.setattr(
- "camelot.backends.image_conversion.BACKENDS", BACKENDS, raising=True
+ "camelot.backends.image_conversion.BACKENDS", backends, raising=True
)
backend = ImageConversionBackend()
diff --git a/tests/test_lattice.py b/tests/test_lattice.py
index 7636b1b2..d2803049 100644
--- a/tests/test_lattice.py
+++ b/tests/test_lattice.py
@@ -1,29 +1,16 @@
-# -*- coding: utf-8 -*-
-
import os
-import sys
-import pytest
import pandas as pd
from pandas.testing import assert_frame_equal
import camelot
-from camelot.core import Table, TableList
-from camelot.__version__ import generate_version
+from .conftest import skip_on_windows
from .data import *
-testdir = os.path.dirname(os.path.abspath(__file__))
-testdir = os.path.join(testdir, "files")
-
-skip_on_windows = pytest.mark.skipif(
- sys.platform.startswith("win"),
- reason="Ghostscript not installed in Windows test environment",
-)
-
@skip_on_windows
-def test_lattice():
+def test_lattice(testdir):
df = pd.DataFrame(data_lattice)
filename = os.path.join(
@@ -34,7 +21,7 @@ def test_lattice():
@skip_on_windows
-def test_lattice_table_rotated():
+def test_lattice_table_rotated(testdir):
df = pd.DataFrame(data_lattice_table_rotated)
filename = os.path.join(testdir, "clockwise_table_1.pdf")
@@ -47,7 +34,7 @@ def test_lattice_table_rotated():
@skip_on_windows
-def test_lattice_two_tables():
+def test_lattice_two_tables(testdir):
df1 = pd.DataFrame(data_lattice_two_tables_1)
df2 = pd.DataFrame(data_lattice_two_tables_2)
@@ -59,7 +46,7 @@ def test_lattice_two_tables():
@skip_on_windows
-def test_lattice_table_regions():
+def test_lattice_table_regions(testdir):
df = pd.DataFrame(data_lattice_table_regions)
filename = os.path.join(testdir, "table_region.pdf")
@@ -68,7 +55,7 @@ def test_lattice_table_regions():
@skip_on_windows
-def test_lattice_table_areas():
+def test_lattice_table_areas(testdir):
df = pd.DataFrame(data_lattice_table_areas)
filename = os.path.join(testdir, "twotables_2.pdf")
@@ -77,7 +64,7 @@ def test_lattice_table_areas():
@skip_on_windows
-def test_lattice_process_background():
+def test_lattice_process_background(testdir):
df = pd.DataFrame(data_lattice_process_background)
filename = os.path.join(testdir, "background_lines_1.pdf")
@@ -86,7 +73,7 @@ def test_lattice_process_background():
@skip_on_windows
-def test_lattice_copy_text():
+def test_lattice_copy_text(testdir):
df = pd.DataFrame(data_lattice_copy_text)
filename = os.path.join(testdir, "row_span_1.pdf")
@@ -95,7 +82,7 @@ def test_lattice_copy_text():
@skip_on_windows
-def test_lattice_shift_text():
+def test_lattice_shift_text(testdir):
df_lt = pd.DataFrame(data_lattice_shift_text_left_top)
df_disable = pd.DataFrame(data_lattice_shift_text_disable)
df_rb = pd.DataFrame(data_lattice_shift_text_right_bottom)
@@ -112,7 +99,7 @@ def test_lattice_shift_text():
@skip_on_windows
-def test_lattice_arabic():
+def test_lattice_arabic(testdir):
df = pd.DataFrame(data_arabic)
filename = os.path.join(testdir, "tabula/arabic.pdf")
diff --git a/tests/test_plotting.py b/tests/test_plotting.py
index 071dfff3..17dc9adf 100644
--- a/tests/test_plotting.py
+++ b/tests/test_plotting.py
@@ -1,38 +1,30 @@
-# -*- coding: utf-8 -*-
-
import os
-import sys
import pytest
import camelot
-
-testdir = os.path.dirname(os.path.abspath(__file__))
-testdir = os.path.join(testdir, "files")
-
-skip_on_windows = pytest.mark.skipif(
- sys.platform.startswith("win"),
- reason="Ghostscript not installed in Windows test environment",
-)
+from tests.conftest import skip_on_windows
+from tests.conftest import skip_pdftopng
@skip_on_windows
@pytest.mark.mpl_image_compare(baseline_dir="files/baseline_plots", remove_text=True)
-def test_text_plot():
+def test_text_plot(testdir):
filename = os.path.join(testdir, "foo.pdf")
tables = camelot.read_pdf(filename)
return camelot.plot(tables[0], kind="text")
@pytest.mark.mpl_image_compare(baseline_dir="files/baseline_plots", remove_text=True)
-def test_textedge_plot():
+def test_textedge_plot(testdir):
filename = os.path.join(testdir, "tabula/12s0324.pdf")
tables = camelot.read_pdf(filename, flavor="stream")
return camelot.plot(tables[0], kind="textedge")
+@skip_pdftopng
@pytest.mark.mpl_image_compare(baseline_dir="files/baseline_plots", remove_text=True)
-def test_lattice_contour_plot_poppler():
+def test_lattice_contour_plot_poppler(testdir):
filename = os.path.join(testdir, "foo.pdf")
tables = camelot.read_pdf(filename, backend="poppler")
return camelot.plot(tables[0], kind="contour")
@@ -40,21 +32,22 @@ def test_lattice_contour_plot_poppler():
@skip_on_windows
@pytest.mark.mpl_image_compare(baseline_dir="files/baseline_plots", remove_text=True)
-def test_lattice_contour_plot_ghostscript():
+def test_lattice_contour_plot_ghostscript(testdir):
filename = os.path.join(testdir, "foo.pdf")
tables = camelot.read_pdf(filename, backend="ghostscript")
return camelot.plot(tables[0], kind="contour")
@pytest.mark.mpl_image_compare(baseline_dir="files/baseline_plots", remove_text=True)
-def test_stream_contour_plot():
+def test_stream_contour_plot(testdir):
filename = os.path.join(testdir, "tabula/12s0324.pdf")
tables = camelot.read_pdf(filename, flavor="stream")
return camelot.plot(tables[0], kind="contour")
+@skip_pdftopng
@pytest.mark.mpl_image_compare(baseline_dir="files/baseline_plots", remove_text=True)
-def test_line_plot_poppler():
+def test_line_plot_poppler(testdir):
filename = os.path.join(testdir, "foo.pdf")
tables = camelot.read_pdf(filename, backend="poppler")
return camelot.plot(tables[0], kind="line")
@@ -62,14 +55,15 @@ def test_line_plot_poppler():
@skip_on_windows
@pytest.mark.mpl_image_compare(baseline_dir="files/baseline_plots", remove_text=True)
-def test_line_plot_ghostscript():
+def test_line_plot_ghostscript(testdir):
filename = os.path.join(testdir, "foo.pdf")
tables = camelot.read_pdf(filename, backend="ghostscript")
return camelot.plot(tables[0], kind="line")
+@skip_pdftopng
@pytest.mark.mpl_image_compare(baseline_dir="files/baseline_plots", remove_text=True)
-def test_joint_plot_poppler():
+def test_joint_plot_poppler(testdir):
filename = os.path.join(testdir, "foo.pdf")
tables = camelot.read_pdf(filename, backend="poppler")
return camelot.plot(tables[0], kind="joint")
@@ -77,14 +71,15 @@ def test_joint_plot_poppler():
@skip_on_windows
@pytest.mark.mpl_image_compare(baseline_dir="files/baseline_plots", remove_text=True)
-def test_joint_plot_ghostscript():
+def test_joint_plot_ghostscript(testdir):
filename = os.path.join(testdir, "foo.pdf")
tables = camelot.read_pdf(filename, backend="ghostscript")
return camelot.plot(tables[0], kind="joint")
+@skip_pdftopng
@pytest.mark.mpl_image_compare(baseline_dir="files/baseline_plots", remove_text=True)
-def test_grid_plot_poppler():
+def test_grid_plot_poppler(testdir):
filename = os.path.join(testdir, "foo.pdf")
tables = camelot.read_pdf(filename, backend="poppler")
return camelot.plot(tables[0], kind="grid")
@@ -92,7 +87,7 @@ def test_grid_plot_poppler():
@skip_on_windows
@pytest.mark.mpl_image_compare(baseline_dir="files/baseline_plots", remove_text=True)
-def test_grid_plot_ghostscript():
+def test_grid_plot_ghostscript(testdir):
filename = os.path.join(testdir, "foo.pdf")
tables = camelot.read_pdf(filename, backend="ghostscript")
return camelot.plot(tables[0], kind="grid")
diff --git a/tests/test_stream.py b/tests/test_stream.py
index 0626ea7b..e86f23b7 100644
--- a/tests/test_stream.py
+++ b/tests/test_stream.py
@@ -1,22 +1,14 @@
-# -*- coding: utf-8 -*-
-
import os
-import pytest
import pandas as pd
from pandas.testing import assert_frame_equal
import camelot
-from camelot.core import Table, TableList
-from camelot.__version__ import generate_version
from .data import *
-testdir = os.path.dirname(os.path.abspath(__file__))
-testdir = os.path.join(testdir, "files")
-
-def test_stream():
+def test_stream(testdir):
df = pd.DataFrame(data_stream)
filename = os.path.join(testdir, "health.pdf")
@@ -24,7 +16,7 @@ def test_stream():
assert_frame_equal(df, tables[0].df)
-def test_stream_table_rotated():
+def test_stream_table_rotated(testdir):
df = pd.DataFrame(data_stream_table_rotated)
filename = os.path.join(testdir, "clockwise_table_2.pdf")
@@ -36,7 +28,7 @@ def test_stream_table_rotated():
assert_frame_equal(df, tables[0].df)
-def test_stream_two_tables():
+def test_stream_two_tables(testdir):
df1 = pd.DataFrame(data_stream_two_tables_1)
df2 = pd.DataFrame(data_stream_two_tables_2)
@@ -48,7 +40,7 @@ def test_stream_two_tables():
assert df2.equals(tables[1].df)
-def test_stream_table_regions():
+def test_stream_table_regions(testdir):
df = pd.DataFrame(data_stream_table_areas)
filename = os.path.join(testdir, "tabula/us-007.pdf")
@@ -58,7 +50,7 @@ def test_stream_table_regions():
assert_frame_equal(df, tables[0].df)
-def test_stream_table_areas():
+def test_stream_table_areas(testdir):
df = pd.DataFrame(data_stream_table_areas)
filename = os.path.join(testdir, "tabula/us-007.pdf")
@@ -68,7 +60,7 @@ def test_stream_table_areas():
assert_frame_equal(df, tables[0].df)
-def test_stream_columns():
+def test_stream_columns(testdir):
df = pd.DataFrame(data_stream_columns)
filename = os.path.join(testdir, "mexican_towns.pdf")
@@ -78,7 +70,7 @@ def test_stream_columns():
assert_frame_equal(df, tables[0].df)
-def test_stream_split_text():
+def test_stream_split_text(testdir):
df = pd.DataFrame(data_stream_split_text)
filename = os.path.join(testdir, "tabula/m27.pdf")
@@ -91,7 +83,7 @@ def test_stream_split_text():
assert_frame_equal(df, tables[0].df)
-def test_stream_flag_size():
+def test_stream_flag_size(testdir):
df = pd.DataFrame(data_stream_flag_size)
filename = os.path.join(testdir, "superscript.pdf")
@@ -99,7 +91,7 @@ def test_stream_flag_size():
assert_frame_equal(df, tables[0].df)
-def test_stream_strip_text():
+def test_stream_strip_text(testdir):
df = pd.DataFrame(data_stream_strip_text)
filename = os.path.join(testdir, "detect_vertical_false.pdf")
@@ -107,7 +99,7 @@ def test_stream_strip_text():
assert_frame_equal(df, tables[0].df)
-def test_stream_edge_tol():
+def test_stream_edge_tol(testdir):
df = pd.DataFrame(data_stream_edge_tol)
filename = os.path.join(testdir, "edge_tol.pdf")
@@ -115,7 +107,7 @@ def test_stream_edge_tol():
assert_frame_equal(df, tables[0].df)
-def test_stream_layout_kwargs():
+def test_stream_layout_kwargs(testdir):
df = pd.DataFrame(data_stream_layout_kwargs)
filename = os.path.join(testdir, "detect_vertical_false.pdf")
@@ -125,7 +117,7 @@ def test_stream_layout_kwargs():
assert_frame_equal(df, tables[0].df)
-def test_stream_duplicated_text():
+def test_stream_duplicated_text(testdir):
df = pd.DataFrame(data_stream_duplicated_text)
filename = os.path.join(testdir, "birdisland.pdf")
diff --git a/tests/test_utils.py b/tests/test_utils.py
new file mode 100644
index 00000000..9a68f386
--- /dev/null
+++ b/tests/test_utils.py
@@ -0,0 +1,45 @@
+"Test to check intersection logic when no intersection area returned"
+import os
+
+from pdfminer.converter import PDFPageAggregator
+from pdfminer.layout import (
+ LAParams,
+ LTTextBoxHorizontal
+)
+from pdfminer.pdfinterp import PDFPageInterpreter, PDFResourceManager
+from pdfminer.pdfpage import PDFPage
+
+from camelot.utils import bbox_intersection_area
+
+
+def get_text_from_pdf(filename):
+ "Method to extract text object from pdf"
+ # https://stackoverflow.com/questions/22898145/how-to-extract-text-and-text-coordinates-from-a-pdf-file
+ # https://pdfminer-docs.readthedocs.io/programming.html#performing-layout-analysis
+ document = open(filename, 'rb')
+ # Create resource manager
+ rsrcmgr = PDFResourceManager()
+ # Set parameters for analysis.
+ laparams = LAParams()
+ # Create a PDF page aggregator object.
+ device = PDFPageAggregator(rsrcmgr, laparams=laparams)
+ interpreter = PDFPageInterpreter(rsrcmgr, device)
+ for page in PDFPage.get_pages(document):
+ interpreter.process_page(page)
+ # receive the LTPage object for the page.
+ layout = device.get_result()
+ for element in layout:
+ if isinstance(element, LTTextBoxHorizontal):
+ return element
+
+
+def test_bbox_intersection_text(testdir):
+ """
+ Test to check area of intersection between both boxes when no intersection area returned
+ """
+ filename1 = os.path.join(testdir, "foo.pdf")
+ pdftextelement1 = get_text_from_pdf(filename1)
+ filename2 = os.path.join(testdir, "tabula/12s0324.pdf")
+ pdftextelement2 = get_text_from_pdf(filename2)
+
+ assert bbox_intersection_area(pdftextelement1, pdftextelement2) == 0.0
| | |