├── .flake8 ├── .github ├── dependabot.yml ├── labels.yml ├── release-drafter.yml └── workflows │ ├── constraints.txt │ ├── labeler.yml │ ├── release.yml │ └── tests.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .readthedocs.yml ├── CODE_OF_CONDUCT.rst ├── CONTRIBUTING.rst ├── LICENSE ├── README.rst ├── bandit.yml ├── codecov.yml ├── docs ├── codeofconduct.rst ├── conf.py ├── contributing.rst ├── index.rst ├── license.rst └── requirements.txt ├── mypy.ini ├── noxfile.py ├── poetry.lock ├── pyproject.toml ├── pytest.ini ├── src └── django_pagination_bootstrap │ ├── __init__.py │ ├── middleware.py │ ├── paginator.py │ ├── templates │ └── pagination.html │ └── templatetags │ ├── __init__.py │ └── pagination_tags.py └── tests ├── __init__.py ├── manage.py ├── settings.py ├── test_http_request.py ├── test_middleware.py ├── test_pagination_tags.py └── test_paginator.py /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | select = B,B9,C,DAR,E,F,N,RST,W 3 | ignore = E203,E501,W503,C901,B950 4 | max-line-length = 80 5 | max-complexity = 10 6 | docstring-convention = google 7 | ; darglint 8 | strictness = short 9 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | - package-ecosystem: pip 8 | directory: "/.github/workflows" 9 | schedule: 10 | interval: daily 11 | - package-ecosystem: pip 12 | directory: "/docs" 13 | schedule: 14 | interval: daily 15 | - package-ecosystem: pip 16 | directory: "/" 17 | schedule: 18 | interval: daily 19 | -------------------------------------------------------------------------------- /.github/labels.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Labels names are important as they are used by Release Drafter to decide 3 | # regarding where to record them in changelog or if to skip them. 4 | # 5 | # The repository labels will be automatically configured using this file and 6 | # the GitHub Action https://github.com/marketplace/actions/github-labeler. 7 | - name: breaking 8 | description: Breaking Changes 9 | color: bfd4f2 10 | - name: bug 11 | description: Something isn't working 12 | color: d73a4a 13 | - name: build 14 | description: Build System and Dependencies 15 | color: bfdadc 16 | - name: ci 17 | description: Continuous Integration 18 | color: 4a97d6 19 | - name: dependencies 20 | description: Pull requests that update a dependency file 21 | color: 0366d6 22 | - name: documentation 23 | description: Improvements or additions to documentation 24 | color: 0075ca 25 | - name: duplicate 26 | description: This issue or pull request already exists 27 | color: cfd3d7 28 | - name: enhancement 29 | description: New feature or request 30 | color: a2eeef 31 | - name: github_actions 32 | description: Pull requests that update Github_actions code 33 | color: "000000" 34 | - name: good first issue 35 | description: Good for newcomers 36 | color: 7057ff 37 | - name: help wanted 38 | description: Extra attention is needed 39 | color: 008672 40 | - name: invalid 41 | description: This doesn't seem right 42 | color: e4e669 43 | - name: performance 44 | description: Performance 45 | color: "016175" 46 | - name: python 47 | description: Pull requests that update Python code 48 | color: 2b67c6 49 | - name: question 50 | description: Further information is requested 51 | color: d876e3 52 | - name: refactoring 53 | description: Refactoring 54 | color: ef67c4 55 | - name: removal 56 | description: Removals and Deprecations 57 | color: 9ae7ea 58 | - name: style 59 | description: Style 60 | color: c120e5 61 | - name: testing 62 | description: Testing 63 | color: b1fc6f 64 | - name: wontfix 65 | description: This will not be worked on 66 | color: ffffff 67 | -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | categories: 2 | - title: ":boom: Breaking Changes" 3 | label: "breaking" 4 | - title: ":rocket: Features" 5 | label: "enhancement" 6 | - title: ":fire: Removals and Deprecations" 7 | label: "removal" 8 | - title: ":beetle: Fixes" 9 | label: "bug" 10 | - title: ":raising_hand: Help wanted" 11 | label: "help wanted" 12 | - title: ":racehorse: Performance" 13 | label: "performance" 14 | - title: ":rotating_light: Testing" 15 | label: "testing" 16 | - title: ":construction_worker: Continuous Integration" 17 | label: "ci" 18 | - title: ":books: Documentation" 19 | label: "documentation" 20 | - title: ":hammer: Refactoring" 21 | label: "refactoring" 22 | - title: ":lipstick: Style" 23 | label: "style" 24 | - title: ":package: Dependencies" 25 | labels: 26 | - "dependencies" 27 | - "build" 28 | template: | 29 | ## Changes 30 | 31 | $CHANGES 32 | -------------------------------------------------------------------------------- /.github/workflows/constraints.txt: -------------------------------------------------------------------------------- 1 | pip==22.3.1 2 | nox==2022.8.7 3 | nox-poetry==1.0.2 4 | poetry==1.2.2 5 | virtualenv==20.16.7 6 | -------------------------------------------------------------------------------- /.github/workflows/labeler.yml: -------------------------------------------------------------------------------- 1 | name: Labeler 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | labeler: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Check out the repository 13 | uses: actions/checkout@v2.3.4 14 | 15 | - name: Run Labeler 16 | uses: crazy-max/ghaction-github-labeler@v4.1.0 17 | with: 18 | skip-delete: true 19 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | release: 10 | name: Release 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Check out the repository 14 | uses: actions/checkout@v3 15 | with: 16 | fetch-depth: 2 17 | 18 | - name: Set up Python 19 | uses: actions/setup-python@v4 20 | with: 21 | python-version: "3.9" 22 | 23 | - name: Upgrade pip 24 | run: | 25 | pip install --constraint=.github/workflows/constraints.txt pip 26 | pip --version 27 | 28 | - name: Install Poetry 29 | run: | 30 | pip install --constraint=.github/workflows/constraints.txt poetry 31 | poetry --version 32 | 33 | - name: Check if there is a parent commit 34 | id: check-parent-commit 35 | run: | 36 | echo "::set-output name=sha::$(git rev-parse --verify --quiet HEAD^)" 37 | 38 | - name: Detect and tag new version 39 | id: check-version 40 | if: steps.check-parent-commit.outputs.sha 41 | uses: salsify/action-detect-and-tag-new-version@v2.0.3 42 | with: 43 | version-command: | 44 | bash -o pipefail -c "poetry version | awk '{ print \$2 }'" 45 | 46 | - name: Bump version for developmental release 47 | if: "! steps.check-version.outputs.tag" 48 | run: | 49 | poetry version patch && 50 | version=$(poetry version | awk '{ print $2 }') && 51 | poetry version $version.dev.$(date +%s) 52 | 53 | - name: Build package 54 | run: | 55 | poetry build --ansi 56 | 57 | - name: Publish package on PyPI 58 | if: steps.check-version.outputs.tag 59 | uses: pypa/gh-action-pypi-publish@v1.6.5 60 | with: 61 | user: __token__ 62 | password: ${{ secrets.PYPI_TOKEN }} 63 | 64 | - name: Publish package on TestPyPI 65 | if: "! steps.check-version.outputs.tag" 66 | uses: pypa/gh-action-pypi-publish@v1.6.5 67 | with: 68 | user: __token__ 69 | password: ${{ secrets.TEST_PYPI_TOKEN }} 70 | repository_url: https://test.pypi.org/legacy/ 71 | 72 | - name: Publish the release notes 73 | uses: release-drafter/release-drafter@v5.21.1 74 | with: 75 | publish: ${{ steps.check-version.outputs.tag != '' }} 76 | tag: ${{ steps.check-version.outputs.tag }} 77 | env: 78 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 79 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | - push 5 | - pull_request 6 | 7 | jobs: 8 | tests: 9 | name: ${{ matrix.session }} ${{ matrix.python-version }} / ${{ matrix.os }} 10 | runs-on: ${{ matrix.os }} 11 | strategy: 12 | fail-fast: false 13 | matrix: 14 | include: 15 | - { python: "3.9", os: ubuntu-latest, session: "pre-commit" } 16 | - { python: "3.9", os: ubuntu-latest, session: "safety" } 17 | - { python: "3.9", os: ubuntu-latest, session: "mypy" } 18 | - { python: "3.8", os: ubuntu-latest, session: "mypy" } 19 | - { python: "3.7", os: ubuntu-latest, session: "mypy" } 20 | - { python: "3.9", os: ubuntu-latest, session: "tests" } 21 | - { python: "3.8", os: ubuntu-latest, session: "tests" } 22 | - { python: "3.7", os: ubuntu-latest, session: "tests" } 23 | - { python: "3.9", os: windows-latest, session: "tests" } 24 | - { python: "3.9", os: macos-latest, session: "tests" } 25 | # - { python: "3.9", os: ubuntu-latest, session: "typeguard" } 26 | - { python: "3.9", os: ubuntu-latest, session: "xdoctest" } 27 | - { python: "3.9", os: ubuntu-latest, session: "docs-build" } 28 | 29 | env: 30 | NOXSESSION: ${{ matrix.session }} 31 | 32 | steps: 33 | - name: Check out the repository 34 | uses: actions/checkout@v3 35 | 36 | - name: Set up Python ${{ matrix.python }} 37 | uses: actions/setup-python@v4 38 | with: 39 | python-version: ${{ matrix.python }} 40 | 41 | - name: Upgrade pip 42 | run: | 43 | pip install --constraint=.github/workflows/constraints.txt pip 44 | pip --version 45 | - name: Install Poetry 46 | run: | 47 | pipx install --pip-args=--constraint=.github/workflows/constraints.txt poetry 48 | poetry --version 49 | - name: Install Nox 50 | run: | 51 | pipx install --pip-args=--constraint=.github/workflows/constraints.txt nox 52 | pipx inject --pip-args=--constraint=.github/workflows/constraints.txt nox nox-poetry 53 | nox --version 54 | - name: Compute pre-commit cache key 55 | if: matrix.session == 'pre-commit' 56 | id: pre-commit-cache 57 | shell: python 58 | run: | 59 | import hashlib 60 | import sys 61 | python = "py{}.{}".format(*sys.version_info[:2]) 62 | payload = sys.version.encode() + sys.executable.encode() 63 | digest = hashlib.sha256(payload).hexdigest() 64 | result = "${{ runner.os }}-{}-{}-pre-commit".format(python, digest[:8]) 65 | print("::set-output name=result::{}".format(result)) 66 | - name: Restore pre-commit cache 67 | uses: actions/cache@v3 68 | if: matrix.session == 'pre-commit' 69 | with: 70 | path: ~/.cache/pre-commit 71 | key: ${{ steps.pre-commit-cache.outputs.result }}-${{ hashFiles('.pre-commit-config.yaml') }} 72 | restore-keys: | 73 | ${{ steps.pre-commit-cache.outputs.result }}- 74 | - name: Run Nox 75 | run: | 76 | nox --force-color --python=${{ matrix.python }} 77 | - name: Upload coverage data 78 | if: always() && matrix.session == 'tests' 79 | uses: "actions/upload-artifact@v3" 80 | with: 81 | name: coverage-data 82 | path: ".coverage.*" 83 | 84 | - name: Upload documentation 85 | if: matrix.session == 'docs-build' 86 | uses: actions/upload-artifact@v3 87 | with: 88 | name: docs 89 | path: docs/_build 90 | 91 | coverage: 92 | runs-on: ubuntu-latest 93 | needs: tests 94 | steps: 95 | - name: Check out the repository 96 | uses: actions/checkout@v3 97 | 98 | - name: Set up Python 99 | uses: actions/setup-python@v4 100 | with: 101 | python-version: "3.9" 102 | 103 | - name: Upgrade pip 104 | run: | 105 | pip install --constraint=.github/workflows/constraints.txt pip 106 | pip --version 107 | - name: Install Poetry 108 | run: | 109 | pipx install --pip-args=--constraint=.github/workflows/constraints.txt poetry 110 | poetry --version 111 | - name: Install Nox 112 | run: | 113 | pipx install --pip-args=--constraint=.github/workflows/constraints.txt nox 114 | pipx inject --pip-args=--constraint=.github/workflows/constraints.txt nox nox-poetry 115 | nox --version 116 | - name: Download coverage data 117 | uses: actions/download-artifact@v3 118 | with: 119 | name: coverage-data 120 | 121 | - name: Combine coverage data and display human readable report 122 | run: | 123 | nox --force-color --session=coverage 124 | - name: Create coverage report 125 | run: | 126 | nox --force-color --session=coverage -- xml 127 | - name: Upload coverage report 128 | uses: codecov/codecov-action@v3 129 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | 131 | # Pytype 132 | .pytype/ 133 | 134 | # Pycharm 135 | .idea/ 136 | 137 | # Visual Studio Code 138 | .vscode/ 139 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: local 3 | hooks: 4 | - id: bandit 5 | name: bandit 6 | entry: bandit 7 | language: system 8 | types: [python] 9 | require_serial: true 10 | args: ["-c", "bandit.yml"] 11 | - id: black 12 | name: black 13 | entry: black 14 | language: system 15 | types: [python] 16 | require_serial: true 17 | - id: check-added-large-files 18 | name: Check for added large files 19 | entry: check-added-large-files 20 | language: system 21 | - id: check-toml 22 | name: Check Toml 23 | entry: check-toml 24 | language: system 25 | types: [toml] 26 | - id: check-yaml 27 | name: Check Yaml 28 | entry: check-yaml 29 | language: system 30 | types: [yaml] 31 | - id: end-of-file-fixer 32 | name: Fix End of Files 33 | entry: end-of-file-fixer 34 | language: system 35 | types: [text] 36 | stages: [commit, push, manual] 37 | - id: flake8 38 | name: flake8 39 | entry: flake8 40 | language: system 41 | types: [python] 42 | require_serial: true 43 | - id: trailing-whitespace 44 | name: Trim Trailing Whitespace 45 | entry: trailing-whitespace-fixer 46 | language: system 47 | types: [text] 48 | stages: [commit, push, manual] 49 | # - repo: https://github.com/prettier/pre-commit 50 | # rev: v2.1.2 51 | # hooks: 52 | # - id: prettier 53 | -------------------------------------------------------------------------------- /.readthedocs.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | sphinx: 3 | configuration: docs/conf.py 4 | formats: all 5 | python: 6 | version: 3.8 7 | install: 8 | - requirements: docs/requirements.txt 9 | - path: . 10 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.rst: -------------------------------------------------------------------------------- 1 | Contributor Covenant Code of Conduct 2 | ==================================== 3 | 4 | Our Pledge 5 | ---------- 6 | 7 | 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, religion, or sexual identity and orientation. 8 | 9 | We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. 10 | 11 | 12 | Our Standards 13 | ------------- 14 | 15 | Examples of behavior that contributes to a positive environment for our community include: 16 | 17 | - Demonstrating empathy and kindness toward other people 18 | - Being respectful of differing opinions, viewpoints, and experiences 19 | - Giving and gracefully accepting constructive feedback 20 | - Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience 21 | - Focusing on what is best not just for us as individuals, but for the overall community 22 | 23 | Examples of unacceptable behavior include: 24 | 25 | - The use of sexualized language or imagery, and sexual attention or 26 | advances of any kind 27 | - Trolling, insulting or derogatory comments, and personal or political attacks 28 | - Public or private harassment 29 | - Publishing others' private information, such as a physical or email 30 | address, without their explicit permission 31 | - Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | Enforcement Responsibilities 35 | ---------------------------- 36 | 37 | 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. 38 | 39 | 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. 40 | 41 | 42 | Scope 43 | ----- 44 | 45 | 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. 46 | 47 | 48 | Enforcement 49 | ----------- 50 | 51 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at thiagocavila@gmail.com. All complaints will be reviewed and investigated promptly and fairly. 52 | 53 | All community leaders are obligated to respect the privacy and security of the reporter of any incident. 54 | 55 | 56 | Enforcement Guidelines 57 | ---------------------- 58 | 59 | Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: 60 | 61 | 62 | 1. Correction 63 | ~~~~~~~~~~~~~ 64 | 65 | **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. 66 | 67 | **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. 68 | 69 | 70 | 2. Warning 71 | ~~~~~~~~~~ 72 | 73 | **Community Impact**: A violation through a single incident or series of actions. 74 | 75 | **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. 76 | 77 | 78 | 3. Temporary Ban 79 | ~~~~~~~~~~~~~~~~ 80 | 81 | **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. 82 | 83 | **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. 84 | 85 | 86 | 4. Permanent Ban 87 | ~~~~~~~~~~~~~~~~ 88 | 89 | **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. 90 | 91 | **Consequence**: A permanent ban from any sort of public interaction within the community. 92 | 93 | 94 | Attribution 95 | ----------- 96 | 97 | This Code of Conduct is adapted from the `Contributor Covenant `__, version 2.0, 98 | available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 99 | 100 | Community Impact Guidelines were inspired by `Mozilla’s code of conduct enforcement ladder `__. 101 | 102 | .. _homepage: https://www.contributor-covenant.org 103 | 104 | For answers to common questions about this code of conduct, see the FAQ at 105 | https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations. 106 | -------------------------------------------------------------------------------- /CONTRIBUTING.rst: -------------------------------------------------------------------------------- 1 | Contributor Guide 2 | ================= 3 | 4 | Thank you for your interest in improving this project. 5 | This project is open-source under the `MIT license`_ and 6 | welcomes contributions in the form of bug reports, feature requests, and pull requests. 7 | 8 | Here is a list of important resources for contributors: 9 | 10 | - `Source Code`_ 11 | - `Documentation`_ 12 | - `Issue Tracker`_ 13 | - `Code of Conduct`_ 14 | 15 | .. _MIT license: https://opensource.org/licenses/MIT 16 | .. _Source Code: https://github.com/staticdev/django-pagination-bootstrap 17 | .. _Documentation: https://django-pagination-bootstrap.readthedocs.io/ 18 | .. _Issue Tracker: https://github.com/staticdev/django-pagination-bootstrap/issues 19 | 20 | How to report a bug 21 | ------------------- 22 | 23 | Report bugs on the `Issue Tracker`_. 24 | 25 | When filing an issue, make sure to answer these questions: 26 | 27 | - Which operating system and Python version are you using? 28 | - Which version of this project are you using? 29 | - What did you do? 30 | - What did you expect to see? 31 | - What did you see instead? 32 | 33 | The best way to get your bug fixed is to provide a test case, 34 | and/or steps to reproduce the issue. 35 | 36 | 37 | How to request a feature 38 | ------------------------ 39 | 40 | Request features on the `Issue Tracker`_. 41 | 42 | 43 | How to set up your development environment 44 | ------------------------------------------ 45 | 46 | You need Python 3.6+ and the following tools: 47 | 48 | - Poetry_ 49 | - Nox_ 50 | 51 | Install the package with development requirements: 52 | 53 | .. code:: console 54 | 55 | $ poetry install 56 | 57 | You can now run an interactive Python session, 58 | or the command-line interface: 59 | 60 | .. code:: console 61 | 62 | $ poetry run python 63 | $ poetry run django-pagination-bootstrap 64 | 65 | .. _Poetry: https://python-poetry.org/ 66 | .. _Nox: https://nox.thea.codes/ 67 | 68 | 69 | How to test the project 70 | ----------------------- 71 | 72 | Run the full test suite: 73 | 74 | .. code:: console 75 | 76 | $ nox 77 | 78 | List the available Nox sessions: 79 | 80 | .. code:: console 81 | 82 | $ nox --list-sessions 83 | 84 | You can also run a specific Nox session. 85 | For example, invoke the unit test suite like this: 86 | 87 | .. code:: console 88 | 89 | $ nox --session=tests 90 | 91 | Unit tests are located in the ``tests`` directory, 92 | and are written using the pytest_ testing framework. 93 | 94 | .. _pytest: https://pytest.readthedocs.io/ 95 | 96 | 97 | How to submit changes 98 | --------------------- 99 | 100 | Open a `pull request`_ to submit changes to this project. 101 | 102 | Your pull request needs to meet the following guidelines for acceptance: 103 | 104 | - The Nox test suite must pass without errors and warnings. 105 | - Include unit tests. This project maintains 100% code coverage. 106 | - If your changes add functionality, update the documentation accordingly. 107 | 108 | Feel free to submit early, though—we can always iterate on this. 109 | 110 | To run linting and code formatting checks before commiting your change, you can install pre-commit as a Git hook by running the following command: 111 | 112 | .. code:: console 113 | 114 | $ nox --session=pre-commit -- install 115 | 116 | It is recommended to open an issue before starting work on anything. 117 | This will allow a chance to talk it over with the owners and validate your approach. 118 | 119 | .. _pull request: https://github.com/staticdev/django-pagination-bootstrap/pulls 120 | .. github-only 121 | .. _Code of Conduct: CODE_OF_CONDUCT.rst 122 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2022 by staticdev 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Django Pagination Bootstrap 2 | =========================== 3 | 4 | **MAINTAINER NEEDED: this project is complete but won't be updated until further notice. If you have interest in improving it, please contact me by creating an** `issue here`_ **.** 5 | 6 | .. badges-begin 7 | 8 | |PyPI| |Python Version| |License| 9 | 10 | |Tests| |Codecov| 11 | 12 | |Black| |pre-commit| 13 | 14 | .. |PyPi| image:: https://badge.fury.io/py/django-pagination-bootstrap.svg 15 | :target: https://badge.fury.io/py/django-pagination-bootstrap 16 | :alt: PyPI 17 | .. |Python Version| image:: https://img.shields.io/pypi/pyversions/django-pagination-bootstrap 18 | :target: https://pypi.org/project/django-pagination-bootstrap 19 | :alt: Python Version 20 | .. |License| image:: https://img.shields.io/pypi/l/django-pagination-bootstrap 21 | :target: https://opensource.org/licenses/MIT 22 | :alt: License 23 | .. |Tests| image:: https://github.com/staticdev/django-pagination-bootstrap/workflows/Tests/badge.svg 24 | :target: https://github.com/staticdev/django-pagination-bootstrap/actions?workflow=Tests 25 | :alt: Tests 26 | .. |Codecov| image:: https://codecov.io/gh/staticdev/django-pagination-bootstrap/branch/master/graph/badge.svg 27 | :target: https://codecov.io/gh/staticdev/django-pagination-bootstrap 28 | :alt: Codecov 29 | .. |Black| image:: https://img.shields.io/badge/code%20style-black-000000.svg 30 | :target: https://github.com/psf/black 31 | :alt: Black 32 | .. |pre-commit| image:: https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white 33 | :target: https://github.com/pre-commit/pre-commit 34 | :alt: pre-commit 35 | 36 | Django-pagination-bootstrap is an app to easy add pagination in Django_, using `Bootstrap`_'s layout. 37 | 38 | Note: This library currently works with Python 3.6+, Django 2.0+ and Bootstrap 3+. 39 | 40 | Installation 41 | ------------ 42 | 43 | To install ``django-pagination-bootstrap`` simply run: 44 | 45 | .. code:: console 46 | 47 | pip install django-pagination-bootstrap 48 | 49 | Configuration 50 | ------------- 51 | 52 | We need to hook ``django-pagination-bootstrap`` into our project. 53 | 54 | 1. Put ``django-pagination-bootstrap`` into your ``INSTALLED_APPS`` at settings module: 55 | 56 | .. code-block:: python 57 | 58 | INSTALLED_APPS = ( 59 | # other apps 60 | "django_pagination_bootstrap", 61 | ) 62 | 63 | 2. Install the pagination middleware. Your settings file might look something like: 64 | 65 | .. code-block:: python 66 | 67 | MIDDLEWARE_CLASSES = ( 68 | # other middleware 69 | "django_pagination_bootstrap.middleware.PaginationMiddleware", 70 | ) 71 | 72 | 3. Guarantee you have ``django.template.context_processors.request`` on settings.py: 73 | 74 | .. code-block:: python 75 | 76 | TEMPLATES = [ 77 | { 78 | # ... 79 | "OPTIONS": { 80 | "context_processors": [ 81 | # ... 82 | "django.template.context_processors.request" 83 | # ... 84 | ], 85 | }, 86 | }, 87 | ] 88 | 89 | 4. Add this line at the top of your template to load the pagination tags: 90 | 91 | .. code-block:: python 92 | 93 | {% load pagination_tags %} 94 | 95 | 5. Decide on a variable that you would like to paginate, and use the autopaginate tag on that variable before iterating over it. This could take one of two forms (using the canonical object_list as an example variable): 96 | 97 | .. code-block:: python 98 | 99 | {% autopaginate object_list %} 100 | 101 | 102 | This assumes that you would like to have the default 20 results per page. If you would like to specify your own amount of results per page, you can specify that like so: 103 | 104 | .. code-block:: python 105 | 106 | {% autopaginate object_list 10 %} 107 | 108 | Note that this replaces object_list with the list for the current page, so you can iterate over the object_list like you normally would. 109 | 110 | 6. Now you want to display the current page and the available pages, so somewhere after having used autopaginate. If you are using Bootstrap 3, use the paginate inclusion tag: 111 | 112 | .. code-block:: python 113 | 114 | {% paginate %} 115 | 116 | This does not take any arguments, but does assume that you have already called autopaginate, so make sure to do so first. 117 | 118 | That's it! You have now paginated object_list and given users of the site a way to navigate between the different pages--all without touching your views. 119 | 120 | Side effects 121 | ------------ 122 | 123 | A django-paginator_ instance will be injected in the template context as ``paginator``. You can access it as usual: 124 | 125 | .. code-block:: python 126 | 127 | page {{ page }} of {{ paginator.num_pages }} 128 | 129 | Optional Settings 130 | ----------------- 131 | 132 | In django-pagination, there are no required settings. There are, however, a small set of optional settings useful for changing the default behavior of the pagination tags. Here's an overview: 133 | 134 | * PAGINATION_DEFAULT_PAGINATION 135 | 136 | The default amount of items to show on a page if no number is specified. 137 | 138 | * PAGINATION_DEFAULT_WINDOW 139 | 140 | The number of items to the left and to the right of the current page to display (accounting for ellipses). 141 | 142 | * PAGINATION_DEFAULT_ORPHANS 143 | 144 | The number of orphans allowed. According to the Django documentation, orphans are defined as: 145 | 146 | The minimum number of items allowed on the last page, defaults to zero. 147 | 148 | * PAGINATION_INVALID_PAGE_RAISES_404 149 | 150 | Determines whether an invalid page raises an Http404 or just sets the invalid_page context variable. True does the former and False does the latter. 151 | 152 | Credits 153 | ------- 154 | 155 | This is based on Eric Florenzano's django-pagination 1.0.7 and is an updated version of https://github.com/tgdn/django-bootstrap-pagination for Django 1.7 or higher. 156 | 157 | .. _issue here: https://github.com/staticdev/staticdev/issues 158 | .. _Django: https://www.djangoproject.com/ 159 | .. _Bootstrap: http://getbootstrap.com/ 160 | .. _django-pagination: https://pypi.python.org/pypi/django-pagination 161 | .. _django-paginator: https://docs.djangoproject.com/en/dev/topics/pagination/#paginator-objects 162 | -------------------------------------------------------------------------------- /bandit.yml: -------------------------------------------------------------------------------- 1 | assert_used: 2 | skips: ["*/test_*.py"] 3 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | comment: false 2 | coverage: 3 | status: 4 | project: 5 | default: 6 | target: "80" 7 | patch: 8 | default: 9 | target: "0" 10 | -------------------------------------------------------------------------------- /docs/codeofconduct.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../CODE_OF_CONDUCT.rst 2 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | """Sphinx configuration.""" 2 | from datetime import datetime 3 | 4 | 5 | project = "Django Pagination Bootstrap" 6 | author = "Thiago Carvalho D'Ávila" 7 | copyright = f"{datetime.now().year}, {author}" 8 | extensions = ["sphinx.ext.autodoc", "sphinx.ext.napoleon"] 9 | autodoc_typehints = "description" 10 | -------------------------------------------------------------------------------- /docs/contributing.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../CONTRIBUTING.rst 2 | :end-before: github-only 3 | 4 | .. _Code of Conduct: codeofconduct.html 5 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../README.rst 2 | :end-before: github-only 3 | 4 | .. _Contributor Guide: contributing.html 5 | 6 | .. toctree:: 7 | :hidden: 8 | :maxdepth: 1 9 | 10 | contributing 11 | Code of Conduct 12 | License 13 | Changelog 14 | -------------------------------------------------------------------------------- /docs/license.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../LICENSE 2 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx==5.3.0 2 | -------------------------------------------------------------------------------- /mypy.ini: -------------------------------------------------------------------------------- 1 | [mypy] 2 | 3 | [mypy-django.*] 4 | ignore_missing_imports = True 5 | -------------------------------------------------------------------------------- /noxfile.py: -------------------------------------------------------------------------------- 1 | """Nox sessions.""" 2 | import os 3 | import shlex 4 | import shutil 5 | import sys 6 | from pathlib import Path 7 | from textwrap import dedent 8 | 9 | import nox 10 | 11 | 12 | try: 13 | from nox_poetry import Session 14 | from nox_poetry import session 15 | except ImportError: 16 | message = f"""\ 17 | Nox failed to import the 'nox-poetry' package. 18 | Please install it using the following command: 19 | {sys.executable} -m pip install nox-poetry""" 20 | raise SystemExit(dedent(message)) from None 21 | 22 | 23 | package = "django_pagination_bootstrap" 24 | python_versions = ["3.9", "3.8", "3.7"] 25 | nox.needs_version = ">= 2021.6.6" 26 | nox.options.sessions = ( 27 | "pre-commit", 28 | "safety", 29 | "mypy", 30 | "tests", 31 | "typeguard", 32 | "xdoctest", 33 | "docs-build", 34 | ) 35 | 36 | 37 | def activate_virtualenv_in_precommit_hooks(session: Session) -> None: 38 | """Activate virtualenv in hooks installed by pre-commit. 39 | 40 | This function patches git hooks installed by pre-commit to activate the 41 | session's virtual environment. This allows pre-commit to locate hooks in 42 | that environment when invoked from git. 43 | 44 | Args: 45 | session: The Session object. 46 | """ 47 | assert session.bin is not None # nosec 48 | 49 | # Only patch hooks containing a reference to this session's bindir. Support 50 | # quoting rules for Python and bash, but strip the outermost quotes so we 51 | # can detect paths within the bindir, like /python. 52 | bindirs = [ 53 | bindir[1:-1] if bindir[0] in "'\"" else bindir 54 | for bindir in (repr(session.bin), shlex.quote(session.bin)) 55 | ] 56 | 57 | virtualenv = session.env.get("VIRTUAL_ENV") 58 | if virtualenv is None: 59 | return 60 | 61 | headers = { 62 | # pre-commit < 2.16.0 63 | "python": f"""\ 64 | import os 65 | os.environ["VIRTUAL_ENV"] = {virtualenv!r} 66 | os.environ["PATH"] = os.pathsep.join(( 67 | {session.bin!r}, 68 | os.environ.get("PATH", ""), 69 | )) 70 | """, 71 | # pre-commit >= 2.16.0 72 | "bash": f"""\ 73 | VIRTUAL_ENV={shlex.quote(virtualenv)} 74 | PATH={shlex.quote(session.bin)}"{os.pathsep}$PATH" 75 | """, 76 | # pre-commit >= 2.17.0 on Windows forces sh shebang 77 | "/bin/sh": f"""\ 78 | VIRTUAL_ENV={shlex.quote(virtualenv)} 79 | PATH={shlex.quote(session.bin)}"{os.pathsep}$PATH" 80 | """, 81 | } 82 | 83 | hookdir = Path(".git") / "hooks" 84 | if not hookdir.is_dir(): 85 | return 86 | 87 | for hook in hookdir.iterdir(): 88 | if hook.name.endswith(".sample") or not hook.is_file(): 89 | continue 90 | 91 | if not hook.read_bytes().startswith(b"#!"): 92 | continue 93 | 94 | text = hook.read_text() 95 | 96 | if not any( 97 | Path("A") == Path("a") and bindir.lower() in text.lower() or bindir in text 98 | for bindir in bindirs 99 | ): 100 | continue 101 | 102 | lines = text.splitlines() 103 | 104 | for executable, header in headers.items(): 105 | if executable in lines[0].lower(): 106 | lines.insert(1, dedent(header)) 107 | hook.write_text("\n".join(lines)) 108 | break 109 | 110 | 111 | @session(name="pre-commit", python=python_versions[0]) 112 | def precommit(session: Session) -> None: 113 | """Lint using pre-commit.""" 114 | args = session.posargs or [ 115 | "run", 116 | "--all-files", 117 | "--hook-stage=manual", 118 | "--show-diff-on-failure", 119 | ] 120 | session.install( 121 | "bandit[toml]", 122 | "black", 123 | "darglint", 124 | "flake8", 125 | "flake8-bugbear", 126 | "flake8-docstrings", 127 | "flake8-rst-docstrings", 128 | "pep8-naming", 129 | "pre-commit", 130 | "pre-commit-hooks", 131 | ) 132 | session.run("pre-commit", *args) 133 | if args and args[0] == "install": 134 | activate_virtualenv_in_precommit_hooks(session) 135 | 136 | 137 | @session(python=python_versions[0]) 138 | def safety(session: Session) -> None: 139 | """Scan dependencies for insecure packages.""" 140 | requirements = session.poetry.export_requirements() 141 | session.install("safety") 142 | session.run("safety", "check", f"--file={requirements}", "--bare") 143 | 144 | 145 | @session(python=python_versions) 146 | def mypy(session: Session) -> None: 147 | """Type-check using mypy.""" 148 | args = session.posargs or ["src", "tests", "docs/conf.py"] 149 | session.install(".") 150 | session.install("mypy") 151 | session.run("mypy", *args) 152 | if not session.posargs: 153 | session.run("mypy", f"--python-executable={sys.executable}", "noxfile.py") 154 | 155 | 156 | @session(python=python_versions) 157 | def tests(session: Session) -> None: 158 | """Run the test suite.""" 159 | session.install(".") 160 | session.install("coverage[toml]", "pygments", "pytest", "pytest-django") 161 | try: 162 | session.run("coverage", "run", "--parallel", "-m", "pytest", *session.posargs) 163 | finally: 164 | if session.interactive: 165 | session.notify("coverage") 166 | 167 | 168 | @session 169 | def coverage(session: Session) -> None: 170 | """Produce the coverage report.""" 171 | # Do not use session.posargs unless this is the only session. 172 | has_args = session.posargs and len(session._runner.manifest) == 1 173 | args = session.posargs if has_args else ["report"] 174 | 175 | session.install("coverage[toml]") 176 | 177 | if not has_args and any(Path().glob(".coverage.*")): 178 | session.run("coverage", "combine") 179 | 180 | session.run("coverage", *args) 181 | 182 | 183 | @session(python=python_versions) 184 | def typeguard(session: Session) -> None: 185 | """Runtime type checking using Typeguard.""" 186 | session.install(".") 187 | session.install("pygments", "pytest", "pytest-django", "typeguard") 188 | session.run("pytest", f"--typeguard-packages={package}", *session.posargs) 189 | 190 | 191 | @session(python=python_versions) 192 | def xdoctest(session: Session) -> None: 193 | """Run examples with xdoctest.""" 194 | args = session.posargs or ["all"] 195 | session.install(".") 196 | session.install("xdoctest") 197 | session.run("python", "-m", "xdoctest", package, *args) 198 | 199 | 200 | @session(name="docs-build", python=python_versions[0]) 201 | def docs_build(session: Session) -> None: 202 | """Build the documentation.""" 203 | args = session.posargs or ["docs", "docs/_build"] 204 | session.install(".") 205 | session.install("sphinx") 206 | 207 | build_dir = Path("docs", "_build") 208 | if build_dir.exists(): 209 | shutil.rmtree(build_dir) 210 | 211 | session.run("sphinx-build", *args) 212 | 213 | 214 | @session(python=python_versions[0]) 215 | def docs(session: Session) -> None: 216 | """Build and serve the documentation with live reloading on file changes.""" 217 | args = session.posargs or ["--open-browser", "docs", "docs/_build"] 218 | session.install(".") 219 | session.install("sphinx", "sphinx-autobuild") 220 | 221 | build_dir = Path("docs", "_build") 222 | if build_dir.exists(): 223 | shutil.rmtree(build_dir) 224 | 225 | session.run("sphinx-autobuild", *args) 226 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Poetry and should not be changed by hand. 2 | 3 | [[package]] 4 | name = "alabaster" 5 | version = "0.7.12" 6 | description = "A configurable sidebar-enabled Sphinx theme" 7 | category = "dev" 8 | optional = false 9 | python-versions = "*" 10 | files = [ 11 | {file = "alabaster-0.7.12-py2.py3-none-any.whl", hash = "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359"}, 12 | {file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"}, 13 | ] 14 | 15 | [[package]] 16 | name = "asgiref" 17 | version = "3.5.2" 18 | description = "ASGI specs, helper code, and adapters" 19 | category = "main" 20 | optional = false 21 | python-versions = ">=3.7" 22 | files = [ 23 | {file = "asgiref-3.5.2-py3-none-any.whl", hash = "sha256:1d2880b792ae8757289136f1db2b7b99100ce959b2aa57fd69dab783d05afac4"}, 24 | {file = "asgiref-3.5.2.tar.gz", hash = "sha256:4a29362a6acebe09bf1d6640db38c1dc3d9217c68e6f9f6204d72667fc19a424"}, 25 | ] 26 | 27 | [package.dependencies] 28 | typing-extensions = {version = "*", markers = "python_version < \"3.8\""} 29 | 30 | [package.extras] 31 | tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"] 32 | 33 | [[package]] 34 | name = "attrs" 35 | version = "22.1.0" 36 | description = "Classes Without Boilerplate" 37 | category = "dev" 38 | optional = false 39 | python-versions = ">=3.5" 40 | files = [ 41 | {file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"}, 42 | {file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"}, 43 | ] 44 | 45 | [package.extras] 46 | dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy (>=0.900,!=0.940)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "sphinx", "sphinx-notfound-page", "zope.interface"] 47 | docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"] 48 | tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "zope.interface"] 49 | tests-no-zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"] 50 | 51 | [[package]] 52 | name = "babel" 53 | version = "2.11.0" 54 | description = "Internationalization utilities" 55 | category = "dev" 56 | optional = false 57 | python-versions = ">=3.6" 58 | files = [ 59 | {file = "Babel-2.11.0-py3-none-any.whl", hash = "sha256:1ad3eca1c885218f6dce2ab67291178944f810a10a9b5f3cb8382a5a232b64fe"}, 60 | {file = "Babel-2.11.0.tar.gz", hash = "sha256:5ef4b3226b0180dedded4229651c8b0e1a3a6a2837d45a073272f313e4cf97f6"}, 61 | ] 62 | 63 | [package.dependencies] 64 | pytz = ">=2015.7" 65 | 66 | [[package]] 67 | name = "bandit" 68 | version = "1.7.4" 69 | description = "Security oriented static analyser for python code." 70 | category = "dev" 71 | optional = false 72 | python-versions = ">=3.7" 73 | files = [ 74 | {file = "bandit-1.7.4-py3-none-any.whl", hash = "sha256:412d3f259dab4077d0e7f0c11f50f650cc7d10db905d98f6520a95a18049658a"}, 75 | {file = "bandit-1.7.4.tar.gz", hash = "sha256:2d63a8c573417bae338962d4b9b06fbc6080f74ecd955a092849e1e65c717bd2"}, 76 | ] 77 | 78 | [package.dependencies] 79 | colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""} 80 | GitPython = ">=1.0.1" 81 | PyYAML = ">=5.3.1" 82 | stevedore = ">=1.20.0" 83 | 84 | [package.extras] 85 | 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"] 86 | toml = ["toml"] 87 | yaml = ["PyYAML"] 88 | 89 | [[package]] 90 | name = "black" 91 | version = "22.10.0" 92 | description = "The uncompromising code formatter." 93 | category = "dev" 94 | optional = false 95 | python-versions = ">=3.7" 96 | files = [ 97 | {file = "black-22.10.0-1fixedarch-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:5cc42ca67989e9c3cf859e84c2bf014f6633db63d1cbdf8fdb666dcd9e77e3fa"}, 98 | {file = "black-22.10.0-1fixedarch-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:5d8f74030e67087b219b032aa33a919fae8806d49c867846bfacde57f43972ef"}, 99 | {file = "black-22.10.0-1fixedarch-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:197df8509263b0b8614e1df1756b1dd41be6738eed2ba9e9769f3880c2b9d7b6"}, 100 | {file = "black-22.10.0-1fixedarch-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:2644b5d63633702bc2c5f3754b1b475378fbbfb481f62319388235d0cd104c2d"}, 101 | {file = "black-22.10.0-1fixedarch-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:e41a86c6c650bcecc6633ee3180d80a025db041a8e2398dcc059b3afa8382cd4"}, 102 | {file = "black-22.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2039230db3c6c639bd84efe3292ec7b06e9214a2992cd9beb293d639c6402edb"}, 103 | {file = "black-22.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14ff67aec0a47c424bc99b71005202045dc09270da44a27848d534600ac64fc7"}, 104 | {file = "black-22.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:819dc789f4498ecc91438a7de64427c73b45035e2e3680c92e18795a839ebb66"}, 105 | {file = "black-22.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5b9b29da4f564ba8787c119f37d174f2b69cdfdf9015b7d8c5c16121ddc054ae"}, 106 | {file = "black-22.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8b49776299fece66bffaafe357d929ca9451450f5466e997a7285ab0fe28e3b"}, 107 | {file = "black-22.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:21199526696b8f09c3997e2b4db8d0b108d801a348414264d2eb8eb2532e540d"}, 108 | {file = "black-22.10.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e464456d24e23d11fced2bc8c47ef66d471f845c7b7a42f3bd77bf3d1789650"}, 109 | {file = "black-22.10.0-cp37-cp37m-win_amd64.whl", hash = "sha256:9311e99228ae10023300ecac05be5a296f60d2fd10fff31cf5c1fa4ca4b1988d"}, 110 | {file = "black-22.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fba8a281e570adafb79f7755ac8721b6cf1bbf691186a287e990c7929c7692ff"}, 111 | {file = "black-22.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:915ace4ff03fdfff953962fa672d44be269deb2eaf88499a0f8805221bc68c87"}, 112 | {file = "black-22.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:444ebfb4e441254e87bad00c661fe32df9969b2bf224373a448d8aca2132b395"}, 113 | {file = "black-22.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:974308c58d057a651d182208a484ce80a26dac0caef2895836a92dd6ebd725e0"}, 114 | {file = "black-22.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72ef3925f30e12a184889aac03d77d031056860ccae8a1e519f6cbb742736383"}, 115 | {file = "black-22.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:432247333090c8c5366e69627ccb363bc58514ae3e63f7fc75c54b1ea80fa7de"}, 116 | {file = "black-22.10.0-py3-none-any.whl", hash = "sha256:c957b2b4ea88587b46cf49d1dc17681c1e672864fd7af32fc1e9664d572b3458"}, 117 | {file = "black-22.10.0.tar.gz", hash = "sha256:f513588da599943e0cde4e32cc9879e825d58720d6557062d1098c5ad80080e1"}, 118 | ] 119 | 120 | [package.dependencies] 121 | click = ">=8.0.0" 122 | mypy-extensions = ">=0.4.3" 123 | pathspec = ">=0.9.0" 124 | platformdirs = ">=2" 125 | tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""} 126 | typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\" and implementation_name == \"cpython\""} 127 | typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} 128 | 129 | [package.extras] 130 | colorama = ["colorama (>=0.4.3)"] 131 | d = ["aiohttp (>=3.7.4)"] 132 | jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] 133 | uvloop = ["uvloop (>=0.15.2)"] 134 | 135 | [[package]] 136 | name = "certifi" 137 | version = "2022.12.7" 138 | description = "Python package for providing Mozilla's CA Bundle." 139 | category = "dev" 140 | optional = false 141 | python-versions = ">=3.6" 142 | files = [ 143 | {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"}, 144 | {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, 145 | ] 146 | 147 | [[package]] 148 | name = "cfgv" 149 | version = "3.3.1" 150 | description = "Validate configuration and produce human readable error messages." 151 | category = "dev" 152 | optional = false 153 | python-versions = ">=3.6.1" 154 | files = [ 155 | {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, 156 | {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, 157 | ] 158 | 159 | [[package]] 160 | name = "charset-normalizer" 161 | version = "2.1.1" 162 | description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." 163 | category = "dev" 164 | optional = false 165 | python-versions = ">=3.6.0" 166 | files = [ 167 | {file = "charset-normalizer-2.1.1.tar.gz", hash = "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845"}, 168 | {file = "charset_normalizer-2.1.1-py3-none-any.whl", hash = "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"}, 169 | ] 170 | 171 | [package.extras] 172 | unicode-backport = ["unicodedata2"] 173 | 174 | [[package]] 175 | name = "click" 176 | version = "8.1.3" 177 | description = "Composable command line interface toolkit" 178 | category = "dev" 179 | optional = false 180 | python-versions = ">=3.7" 181 | files = [ 182 | {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, 183 | {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, 184 | ] 185 | 186 | [package.dependencies] 187 | colorama = {version = "*", markers = "platform_system == \"Windows\""} 188 | importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} 189 | 190 | [[package]] 191 | name = "colorama" 192 | version = "0.4.6" 193 | description = "Cross-platform colored terminal text." 194 | category = "dev" 195 | optional = false 196 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" 197 | files = [ 198 | {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, 199 | {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, 200 | ] 201 | 202 | [[package]] 203 | name = "coverage" 204 | version = "6.5.0" 205 | description = "Code coverage measurement for Python" 206 | category = "dev" 207 | optional = false 208 | python-versions = ">=3.7" 209 | files = [ 210 | {file = "coverage-6.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef8674b0ee8cc11e2d574e3e2998aea5df5ab242e012286824ea3c6970580e53"}, 211 | {file = "coverage-6.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:784f53ebc9f3fd0e2a3f6a78b2be1bd1f5575d7863e10c6e12504f240fd06660"}, 212 | {file = "coverage-6.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4a5be1748d538a710f87542f22c2cad22f80545a847ad91ce45e77417293eb4"}, 213 | {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83516205e254a0cb77d2d7bb3632ee019d93d9f4005de31dca0a8c3667d5bc04"}, 214 | {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af4fffaffc4067232253715065e30c5a7ec6faac36f8fc8d6f64263b15f74db0"}, 215 | {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:97117225cdd992a9c2a5515db1f66b59db634f59d0679ca1fa3fe8da32749cae"}, 216 | {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a1170fa54185845505fbfa672f1c1ab175446c887cce8212c44149581cf2d466"}, 217 | {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:11b990d520ea75e7ee8dcab5bc908072aaada194a794db9f6d7d5cfd19661e5a"}, 218 | {file = "coverage-6.5.0-cp310-cp310-win32.whl", hash = "sha256:5dbec3b9095749390c09ab7c89d314727f18800060d8d24e87f01fb9cfb40b32"}, 219 | {file = "coverage-6.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:59f53f1dc5b656cafb1badd0feb428c1e7bc19b867479ff72f7a9dd9b479f10e"}, 220 | {file = "coverage-6.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4a5375e28c5191ac38cca59b38edd33ef4cc914732c916f2929029b4bfb50795"}, 221 | {file = "coverage-6.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4ed2820d919351f4167e52425e096af41bfabacb1857186c1ea32ff9983ed75"}, 222 | {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33a7da4376d5977fbf0a8ed91c4dffaaa8dbf0ddbf4c8eea500a2486d8bc4d7b"}, 223 | {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8fb6cf131ac4070c9c5a3e21de0f7dc5a0fbe8bc77c9456ced896c12fcdad91"}, 224 | {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a6b7d95969b8845250586f269e81e5dfdd8ff828ddeb8567a4a2eaa7313460c4"}, 225 | {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1ef221513e6f68b69ee9e159506d583d31aa3567e0ae84eaad9d6ec1107dddaa"}, 226 | {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cca4435eebea7962a52bdb216dec27215d0df64cf27fc1dd538415f5d2b9da6b"}, 227 | {file = "coverage-6.5.0-cp311-cp311-win32.whl", hash = "sha256:98e8a10b7a314f454d9eff4216a9a94d143a7ee65018dd12442e898ee2310578"}, 228 | {file = "coverage-6.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:bc8ef5e043a2af066fa8cbfc6e708d58017024dc4345a1f9757b329a249f041b"}, 229 | {file = "coverage-6.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4433b90fae13f86fafff0b326453dd42fc9a639a0d9e4eec4d366436d1a41b6d"}, 230 | {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4f05d88d9a80ad3cac6244d36dd89a3c00abc16371769f1340101d3cb899fc3"}, 231 | {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:94e2565443291bd778421856bc975d351738963071e9b8839ca1fc08b42d4bef"}, 232 | {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:027018943386e7b942fa832372ebc120155fd970837489896099f5cfa2890f79"}, 233 | {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:255758a1e3b61db372ec2736c8e2a1fdfaf563977eedbdf131de003ca5779b7d"}, 234 | {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:851cf4ff24062c6aec510a454b2584f6e998cada52d4cb58c5e233d07172e50c"}, 235 | {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:12adf310e4aafddc58afdb04d686795f33f4d7a6fa67a7a9d4ce7d6ae24d949f"}, 236 | {file = "coverage-6.5.0-cp37-cp37m-win32.whl", hash = "sha256:b5604380f3415ba69de87a289a2b56687faa4fe04dbee0754bfcae433489316b"}, 237 | {file = "coverage-6.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:4a8dbc1f0fbb2ae3de73eb0bdbb914180c7abfbf258e90b311dcd4f585d44bd2"}, 238 | {file = "coverage-6.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d900bb429fdfd7f511f868cedd03a6bbb142f3f9118c09b99ef8dc9bf9643c3c"}, 239 | {file = "coverage-6.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2198ea6fc548de52adc826f62cb18554caedfb1d26548c1b7c88d8f7faa8f6ba"}, 240 | {file = "coverage-6.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c4459b3de97b75e3bd6b7d4b7f0db13f17f504f3d13e2a7c623786289dd670e"}, 241 | {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:20c8ac5386253717e5ccc827caad43ed66fea0efe255727b1053a8154d952398"}, 242 | {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b07130585d54fe8dff3d97b93b0e20290de974dc8177c320aeaf23459219c0b"}, 243 | {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dbdb91cd8c048c2b09eb17713b0c12a54fbd587d79adcebad543bc0cd9a3410b"}, 244 | {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:de3001a203182842a4630e7b8d1a2c7c07ec1b45d3084a83d5d227a3806f530f"}, 245 | {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e07f4a4a9b41583d6eabec04f8b68076ab3cd44c20bd29332c6572dda36f372e"}, 246 | {file = "coverage-6.5.0-cp38-cp38-win32.whl", hash = "sha256:6d4817234349a80dbf03640cec6109cd90cba068330703fa65ddf56b60223a6d"}, 247 | {file = "coverage-6.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:7ccf362abd726b0410bf8911c31fbf97f09f8f1061f8c1cf03dfc4b6372848f6"}, 248 | {file = "coverage-6.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:633713d70ad6bfc49b34ead4060531658dc6dfc9b3eb7d8a716d5873377ab745"}, 249 | {file = "coverage-6.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:95203854f974e07af96358c0b261f1048d8e1083f2de9b1c565e1be4a3a48cfc"}, 250 | {file = "coverage-6.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9023e237f4c02ff739581ef35969c3739445fb059b060ca51771e69101efffe"}, 251 | {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:265de0fa6778d07de30bcf4d9dc471c3dc4314a23a3c6603d356a3c9abc2dfcf"}, 252 | {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f830ed581b45b82451a40faabb89c84e1a998124ee4212d440e9c6cf70083e5"}, 253 | {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7b6be138d61e458e18d8e6ddcddd36dd96215edfe5f1168de0b1b32635839b62"}, 254 | {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:42eafe6778551cf006a7c43153af1211c3aaab658d4d66fa5fcc021613d02518"}, 255 | {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:723e8130d4ecc8f56e9a611e73b31219595baa3bb252d539206f7bbbab6ffc1f"}, 256 | {file = "coverage-6.5.0-cp39-cp39-win32.whl", hash = "sha256:d9ecf0829c6a62b9b573c7bb6d4dcd6ba8b6f80be9ba4fc7ed50bf4ac9aecd72"}, 257 | {file = "coverage-6.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc2af30ed0d5ae0b1abdb4ebdce598eafd5b35397d4d75deb341a614d333d987"}, 258 | {file = "coverage-6.5.0-pp36.pp37.pp38-none-any.whl", hash = "sha256:1431986dac3923c5945271f169f59c45b8802a114c8f548d611f2015133df77a"}, 259 | {file = "coverage-6.5.0.tar.gz", hash = "sha256:f642e90754ee3e06b0e7e51bce3379590e76b7f76b708e1a71ff043f87025c84"}, 260 | ] 261 | 262 | [package.dependencies] 263 | tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} 264 | 265 | [package.extras] 266 | toml = ["tomli"] 267 | 268 | [[package]] 269 | name = "darglint" 270 | version = "1.8.1" 271 | description = "A utility for ensuring Google-style docstrings stay up to date with the source code." 272 | category = "dev" 273 | optional = false 274 | python-versions = ">=3.6,<4.0" 275 | files = [ 276 | {file = "darglint-1.8.1-py3-none-any.whl", hash = "sha256:5ae11c259c17b0701618a20c3da343a3eb98b3bc4b5a83d31cdd94f5ebdced8d"}, 277 | {file = "darglint-1.8.1.tar.gz", hash = "sha256:080d5106df149b199822e7ee7deb9c012b49891538f14a11be681044f0bb20da"}, 278 | ] 279 | 280 | [[package]] 281 | name = "distlib" 282 | version = "0.3.6" 283 | description = "Distribution utilities" 284 | category = "dev" 285 | optional = false 286 | python-versions = "*" 287 | files = [ 288 | {file = "distlib-0.3.6-py2.py3-none-any.whl", hash = "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"}, 289 | {file = "distlib-0.3.6.tar.gz", hash = "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46"}, 290 | ] 291 | 292 | [[package]] 293 | name = "django" 294 | version = "3.2.19" 295 | description = "A high-level Python Web framework that encourages rapid development and clean, pragmatic design." 296 | category = "main" 297 | optional = false 298 | python-versions = ">=3.6" 299 | files = [ 300 | {file = "Django-3.2.19-py3-none-any.whl", hash = "sha256:21cc991466245d659ab79cb01204f9515690f8dae00e5eabde307f14d24d4d7d"}, 301 | {file = "Django-3.2.19.tar.gz", hash = "sha256:031365bae96814da19c10706218c44dff3b654cc4de20a98bd2d29b9bde469f0"}, 302 | ] 303 | 304 | [package.dependencies] 305 | asgiref = ">=3.3.2,<4" 306 | pytz = "*" 307 | sqlparse = ">=0.2.2" 308 | 309 | [package.extras] 310 | argon2 = ["argon2-cffi (>=19.1.0)"] 311 | bcrypt = ["bcrypt"] 312 | 313 | [[package]] 314 | name = "docutils" 315 | version = "0.17.1" 316 | description = "Docutils -- Python Documentation Utilities" 317 | category = "dev" 318 | optional = false 319 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 320 | files = [ 321 | {file = "docutils-0.17.1-py2.py3-none-any.whl", hash = "sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61"}, 322 | {file = "docutils-0.17.1.tar.gz", hash = "sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125"}, 323 | ] 324 | 325 | [[package]] 326 | name = "dparse" 327 | version = "0.6.2" 328 | description = "A parser for Python dependency files" 329 | category = "dev" 330 | optional = false 331 | python-versions = ">=3.5" 332 | files = [ 333 | {file = "dparse-0.6.2-py3-none-any.whl", hash = "sha256:8097076f1dd26c377f30d4745e6ec18fef42f3bf493933b842ac5bafad8c345f"}, 334 | {file = "dparse-0.6.2.tar.gz", hash = "sha256:d45255bda21f998bc7ddf2afd5e62505ba6134756ba2d42a84c56b0826614dfe"}, 335 | ] 336 | 337 | [package.dependencies] 338 | packaging = "*" 339 | toml = "*" 340 | 341 | [package.extras] 342 | conda = ["pyyaml"] 343 | pipenv = ["pipenv"] 344 | 345 | [[package]] 346 | name = "exceptiongroup" 347 | version = "1.0.1" 348 | description = "Backport of PEP 654 (exception groups)" 349 | category = "dev" 350 | optional = false 351 | python-versions = ">=3.7" 352 | files = [ 353 | {file = "exceptiongroup-1.0.1-py3-none-any.whl", hash = "sha256:4d6c0aa6dd825810941c792f53d7b8d71da26f5e5f84f20f9508e8f2d33b140a"}, 354 | {file = "exceptiongroup-1.0.1.tar.gz", hash = "sha256:73866f7f842ede6cb1daa42c4af078e2035e5f7607f0e2c762cc51bb31bbe7b2"}, 355 | ] 356 | 357 | [package.extras] 358 | test = ["pytest (>=6)"] 359 | 360 | [[package]] 361 | name = "filelock" 362 | version = "3.8.0" 363 | description = "A platform independent file lock." 364 | category = "dev" 365 | optional = false 366 | python-versions = ">=3.7" 367 | files = [ 368 | {file = "filelock-3.8.0-py3-none-any.whl", hash = "sha256:617eb4e5eedc82fc5f47b6d61e4d11cb837c56cb4544e39081099fa17ad109d4"}, 369 | {file = "filelock-3.8.0.tar.gz", hash = "sha256:55447caa666f2198c5b6b13a26d2084d26fa5b115c00d065664b2124680c4edc"}, 370 | ] 371 | 372 | [package.extras] 373 | docs = ["furo (>=2022.6.21)", "sphinx (>=5.1.1)", "sphinx-autodoc-typehints (>=1.19.1)"] 374 | testing = ["covdefaults (>=2.2)", "coverage (>=6.4.2)", "pytest (>=7.1.2)", "pytest-cov (>=3)", "pytest-timeout (>=2.1)"] 375 | 376 | [[package]] 377 | name = "flake8" 378 | version = "5.0.4" 379 | description = "the modular source code checker: pep8 pyflakes and co" 380 | category = "dev" 381 | optional = false 382 | python-versions = ">=3.6.1" 383 | files = [ 384 | {file = "flake8-5.0.4-py2.py3-none-any.whl", hash = "sha256:7a1cf6b73744f5806ab95e526f6f0d8c01c66d7bbe349562d22dfca20610b248"}, 385 | {file = "flake8-5.0.4.tar.gz", hash = "sha256:6fbe320aad8d6b95cec8b8e47bc933004678dc63095be98528b7bdd2a9f510db"}, 386 | ] 387 | 388 | [package.dependencies] 389 | importlib-metadata = {version = ">=1.1.0,<4.3", markers = "python_version < \"3.8\""} 390 | mccabe = ">=0.7.0,<0.8.0" 391 | pycodestyle = ">=2.9.0,<2.10.0" 392 | pyflakes = ">=2.5.0,<2.6.0" 393 | 394 | [[package]] 395 | name = "flake8-bugbear" 396 | version = "22.10.27" 397 | description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle." 398 | category = "dev" 399 | optional = false 400 | python-versions = ">=3.7" 401 | files = [ 402 | {file = "flake8-bugbear-22.10.27.tar.gz", hash = "sha256:a6708608965c9e0de5fff13904fed82e0ba21ac929fe4896459226a797e11cd5"}, 403 | {file = "flake8_bugbear-22.10.27-py3-none-any.whl", hash = "sha256:6ad0ab754507319060695e2f2be80e6d8977cfcea082293089a9226276bd825d"}, 404 | ] 405 | 406 | [package.dependencies] 407 | attrs = ">=19.2.0" 408 | flake8 = ">=3.0.0" 409 | 410 | [package.extras] 411 | dev = ["coverage", "hypothesis", "hypothesmith (>=0.2)", "pre-commit", "tox"] 412 | 413 | [[package]] 414 | name = "flake8-docstrings" 415 | version = "1.6.0" 416 | description = "Extension for flake8 which uses pydocstyle to check docstrings" 417 | category = "dev" 418 | optional = false 419 | python-versions = "*" 420 | files = [ 421 | {file = "flake8-docstrings-1.6.0.tar.gz", hash = "sha256:9fe7c6a306064af8e62a055c2f61e9eb1da55f84bb39caef2b84ce53708ac34b"}, 422 | {file = "flake8_docstrings-1.6.0-py2.py3-none-any.whl", hash = "sha256:99cac583d6c7e32dd28bbfbef120a7c0d1b6dde4adb5a9fd441c4227a6534bde"}, 423 | ] 424 | 425 | [package.dependencies] 426 | flake8 = ">=3" 427 | pydocstyle = ">=2.1" 428 | 429 | [[package]] 430 | name = "flake8-rst-docstrings" 431 | version = "0.2.7" 432 | description = "Python docstring reStructuredText (RST) validator" 433 | category = "dev" 434 | optional = false 435 | python-versions = ">=3.7" 436 | files = [ 437 | {file = "flake8-rst-docstrings-0.2.7.tar.gz", hash = "sha256:2740067ab9237559dd45a3434d8c987792c7b259ca563621a3b95efe201f5382"}, 438 | {file = "flake8_rst_docstrings-0.2.7-py3-none-any.whl", hash = "sha256:5d56075dce360bcc9c6775bfe7cb431aa395de600ca7e8d40580a28d50b2a803"}, 439 | ] 440 | 441 | [package.dependencies] 442 | flake8 = ">=3.0.0" 443 | pygments = "*" 444 | restructuredtext-lint = "*" 445 | 446 | [[package]] 447 | name = "gitdb" 448 | version = "4.0.9" 449 | description = "Git Object Database" 450 | category = "dev" 451 | optional = false 452 | python-versions = ">=3.6" 453 | files = [ 454 | {file = "gitdb-4.0.9-py3-none-any.whl", hash = "sha256:8033ad4e853066ba6ca92050b9df2f89301b8fc8bf7e9324d412a63f8bf1a8fd"}, 455 | {file = "gitdb-4.0.9.tar.gz", hash = "sha256:bac2fd45c0a1c9cf619e63a90d62bdc63892ef92387424b855792a6cabe789aa"}, 456 | ] 457 | 458 | [package.dependencies] 459 | smmap = ">=3.0.1,<6" 460 | 461 | [[package]] 462 | name = "gitpython" 463 | version = "3.1.30" 464 | description = "GitPython is a python library used to interact with Git repositories" 465 | category = "dev" 466 | optional = false 467 | python-versions = ">=3.7" 468 | files = [ 469 | {file = "GitPython-3.1.30-py3-none-any.whl", hash = "sha256:cd455b0000615c60e286208ba540271af9fe531fa6a87cc590a7298785ab2882"}, 470 | {file = "GitPython-3.1.30.tar.gz", hash = "sha256:769c2d83e13f5d938b7688479da374c4e3d49f71549aaf462b646db9602ea6f8"}, 471 | ] 472 | 473 | [package.dependencies] 474 | gitdb = ">=4.0.1,<5" 475 | typing-extensions = {version = ">=3.7.4.3", markers = "python_version < \"3.8\""} 476 | 477 | [[package]] 478 | name = "identify" 479 | version = "2.5.8" 480 | description = "File identification library for Python" 481 | category = "dev" 482 | optional = false 483 | python-versions = ">=3.7" 484 | files = [ 485 | {file = "identify-2.5.8-py2.py3-none-any.whl", hash = "sha256:48b7925fe122720088aeb7a6c34f17b27e706b72c61070f27fe3789094233440"}, 486 | {file = "identify-2.5.8.tar.gz", hash = "sha256:7a214a10313b9489a0d61467db2856ae8d0b8306fc923e03a9effa53d8aedc58"}, 487 | ] 488 | 489 | [package.extras] 490 | license = ["ukkonen"] 491 | 492 | [[package]] 493 | name = "idna" 494 | version = "3.4" 495 | description = "Internationalized Domain Names in Applications (IDNA)" 496 | category = "dev" 497 | optional = false 498 | python-versions = ">=3.5" 499 | files = [ 500 | {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, 501 | {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, 502 | ] 503 | 504 | [[package]] 505 | name = "imagesize" 506 | version = "1.4.1" 507 | description = "Getting image size from png/jpeg/jpeg2000/gif file" 508 | category = "dev" 509 | optional = false 510 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 511 | files = [ 512 | {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, 513 | {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, 514 | ] 515 | 516 | [[package]] 517 | name = "importlib-metadata" 518 | version = "4.2.0" 519 | description = "Read metadata from Python packages" 520 | category = "dev" 521 | optional = false 522 | python-versions = ">=3.6" 523 | files = [ 524 | {file = "importlib_metadata-4.2.0-py3-none-any.whl", hash = "sha256:057e92c15bc8d9e8109738a48db0ccb31b4d9d5cfbee5a8670879a30be66304b"}, 525 | {file = "importlib_metadata-4.2.0.tar.gz", hash = "sha256:b7e52a1f8dec14a75ea73e0891f3060099ca1d8e6a462a4dff11c3e119ea1b31"}, 526 | ] 527 | 528 | [package.dependencies] 529 | typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} 530 | zipp = ">=0.5" 531 | 532 | [package.extras] 533 | docs = ["jaraco.packaging (>=8.2)", "rst.linker (>=1.9)", "sphinx"] 534 | testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pep517", "pyfakefs", "pytest (>=4.6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-flake8", "pytest-mypy"] 535 | 536 | [[package]] 537 | name = "iniconfig" 538 | version = "1.1.1" 539 | description = "iniconfig: brain-dead simple config-ini parsing" 540 | category = "dev" 541 | optional = false 542 | python-versions = "*" 543 | files = [ 544 | {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, 545 | {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, 546 | ] 547 | 548 | [[package]] 549 | name = "jinja2" 550 | version = "3.1.2" 551 | description = "A very fast and expressive template engine." 552 | category = "dev" 553 | optional = false 554 | python-versions = ">=3.7" 555 | files = [ 556 | {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, 557 | {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, 558 | ] 559 | 560 | [package.dependencies] 561 | MarkupSafe = ">=2.0" 562 | 563 | [package.extras] 564 | i18n = ["Babel (>=2.7)"] 565 | 566 | [[package]] 567 | name = "livereload" 568 | version = "2.6.3" 569 | description = "Python LiveReload is an awesome tool for web developers" 570 | category = "dev" 571 | optional = false 572 | python-versions = "*" 573 | files = [ 574 | {file = "livereload-2.6.3-py2.py3-none-any.whl", hash = "sha256:ad4ac6f53b2d62bb6ce1a5e6e96f1f00976a32348afedcb4b6d68df2a1d346e4"}, 575 | {file = "livereload-2.6.3.tar.gz", hash = "sha256:776f2f865e59fde56490a56bcc6773b6917366bce0c267c60ee8aaf1a0959869"}, 576 | ] 577 | 578 | [package.dependencies] 579 | six = "*" 580 | tornado = {version = "*", markers = "python_version > \"2.7\""} 581 | 582 | [[package]] 583 | name = "markupsafe" 584 | version = "2.1.1" 585 | description = "Safely add untrusted strings to HTML/XML markup." 586 | category = "dev" 587 | optional = false 588 | python-versions = ">=3.7" 589 | files = [ 590 | {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"}, 591 | {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"}, 592 | {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"}, 593 | {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5"}, 594 | {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4"}, 595 | {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f"}, 596 | {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e"}, 597 | {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933"}, 598 | {file = "MarkupSafe-2.1.1-cp310-cp310-win32.whl", hash = "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6"}, 599 | {file = "MarkupSafe-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417"}, 600 | {file = "MarkupSafe-2.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02"}, 601 | {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a"}, 602 | {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37"}, 603 | {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980"}, 604 | {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a"}, 605 | {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3"}, 606 | {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a"}, 607 | {file = "MarkupSafe-2.1.1-cp37-cp37m-win32.whl", hash = "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff"}, 608 | {file = "MarkupSafe-2.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a"}, 609 | {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452"}, 610 | {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003"}, 611 | {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1"}, 612 | {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601"}, 613 | {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925"}, 614 | {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f"}, 615 | {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88"}, 616 | {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63"}, 617 | {file = "MarkupSafe-2.1.1-cp38-cp38-win32.whl", hash = "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1"}, 618 | {file = "MarkupSafe-2.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"}, 619 | {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a"}, 620 | {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f"}, 621 | {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6"}, 622 | {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77"}, 623 | {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603"}, 624 | {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7"}, 625 | {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135"}, 626 | {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96"}, 627 | {file = "MarkupSafe-2.1.1-cp39-cp39-win32.whl", hash = "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c"}, 628 | {file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"}, 629 | {file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"}, 630 | ] 631 | 632 | [[package]] 633 | name = "mccabe" 634 | version = "0.7.0" 635 | description = "McCabe checker, plugin for flake8" 636 | category = "dev" 637 | optional = false 638 | python-versions = ">=3.6" 639 | files = [ 640 | {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, 641 | {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, 642 | ] 643 | 644 | [[package]] 645 | name = "mypy" 646 | version = "0.991" 647 | description = "Optional static typing for Python" 648 | category = "dev" 649 | optional = false 650 | python-versions = ">=3.7" 651 | files = [ 652 | {file = "mypy-0.991-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7d17e0a9707d0772f4a7b878f04b4fd11f6f5bcb9b3813975a9b13c9332153ab"}, 653 | {file = "mypy-0.991-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0714258640194d75677e86c786e80ccf294972cc76885d3ebbb560f11db0003d"}, 654 | {file = "mypy-0.991-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0c8f3be99e8a8bd403caa8c03be619544bc2c77a7093685dcf308c6b109426c6"}, 655 | {file = "mypy-0.991-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc9ec663ed6c8f15f4ae9d3c04c989b744436c16d26580eaa760ae9dd5d662eb"}, 656 | {file = "mypy-0.991-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4307270436fd7694b41f913eb09210faff27ea4979ecbcd849e57d2da2f65305"}, 657 | {file = "mypy-0.991-cp310-cp310-win_amd64.whl", hash = "sha256:901c2c269c616e6cb0998b33d4adbb4a6af0ac4ce5cd078afd7bc95830e62c1c"}, 658 | {file = "mypy-0.991-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d13674f3fb73805ba0c45eb6c0c3053d218aa1f7abead6e446d474529aafc372"}, 659 | {file = "mypy-0.991-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1c8cd4fb70e8584ca1ed5805cbc7c017a3d1a29fb450621089ffed3e99d1857f"}, 660 | {file = "mypy-0.991-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:209ee89fbb0deed518605edddd234af80506aec932ad28d73c08f1400ef80a33"}, 661 | {file = "mypy-0.991-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37bd02ebf9d10e05b00d71302d2c2e6ca333e6c2a8584a98c00e038db8121f05"}, 662 | {file = "mypy-0.991-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:26efb2fcc6b67e4d5a55561f39176821d2adf88f2745ddc72751b7890f3194ad"}, 663 | {file = "mypy-0.991-cp311-cp311-win_amd64.whl", hash = "sha256:3a700330b567114b673cf8ee7388e949f843b356a73b5ab22dd7cff4742a5297"}, 664 | {file = "mypy-0.991-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:1f7d1a520373e2272b10796c3ff721ea1a0712288cafaa95931e66aa15798813"}, 665 | {file = "mypy-0.991-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:641411733b127c3e0dab94c45af15fea99e4468f99ac88b39efb1ad677da5711"}, 666 | {file = "mypy-0.991-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:3d80e36b7d7a9259b740be6d8d906221789b0d836201af4234093cae89ced0cd"}, 667 | {file = "mypy-0.991-cp37-cp37m-win_amd64.whl", hash = "sha256:e62ebaad93be3ad1a828a11e90f0e76f15449371ffeecca4a0a0b9adc99abcef"}, 668 | {file = "mypy-0.991-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b86ce2c1866a748c0f6faca5232059f881cda6dda2a893b9a8373353cfe3715a"}, 669 | {file = "mypy-0.991-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ac6e503823143464538efda0e8e356d871557ef60ccd38f8824a4257acc18d93"}, 670 | {file = "mypy-0.991-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0cca5adf694af539aeaa6ac633a7afe9bbd760df9d31be55ab780b77ab5ae8bf"}, 671 | {file = "mypy-0.991-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a12c56bf73cdab116df96e4ff39610b92a348cc99a1307e1da3c3768bbb5b135"}, 672 | {file = "mypy-0.991-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:652b651d42f155033a1967739788c436491b577b6a44e4c39fb340d0ee7f0d70"}, 673 | {file = "mypy-0.991-cp38-cp38-win_amd64.whl", hash = "sha256:4175593dc25d9da12f7de8de873a33f9b2b8bdb4e827a7cae952e5b1a342e243"}, 674 | {file = "mypy-0.991-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:98e781cd35c0acf33eb0295e8b9c55cdbef64fcb35f6d3aa2186f289bed6e80d"}, 675 | {file = "mypy-0.991-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6d7464bac72a85cb3491c7e92b5b62f3dcccb8af26826257760a552a5e244aa5"}, 676 | {file = "mypy-0.991-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c9166b3f81a10cdf9b49f2d594b21b31adadb3d5e9db9b834866c3258b695be3"}, 677 | {file = "mypy-0.991-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8472f736a5bfb159a5e36740847808f6f5b659960115ff29c7cecec1741c648"}, 678 | {file = "mypy-0.991-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5e80e758243b97b618cdf22004beb09e8a2de1af481382e4d84bc52152d1c476"}, 679 | {file = "mypy-0.991-cp39-cp39-win_amd64.whl", hash = "sha256:74e259b5c19f70d35fcc1ad3d56499065c601dfe94ff67ae48b85596b9ec1461"}, 680 | {file = "mypy-0.991-py3-none-any.whl", hash = "sha256:de32edc9b0a7e67c2775e574cb061a537660e51210fbf6006b0b36ea695ae9bb"}, 681 | {file = "mypy-0.991.tar.gz", hash = "sha256:3c0165ba8f354a6d9881809ef29f1a9318a236a6d81c690094c5df32107bde06"}, 682 | ] 683 | 684 | [package.dependencies] 685 | mypy-extensions = ">=0.4.3" 686 | tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} 687 | typed-ast = {version = ">=1.4.0,<2", markers = "python_version < \"3.8\""} 688 | typing-extensions = ">=3.10" 689 | 690 | [package.extras] 691 | dmypy = ["psutil (>=4.0)"] 692 | install-types = ["pip"] 693 | python2 = ["typed-ast (>=1.4.0,<2)"] 694 | reports = ["lxml"] 695 | 696 | [[package]] 697 | name = "mypy-extensions" 698 | version = "0.4.3" 699 | description = "Experimental type system extensions for programs checked with the mypy typechecker." 700 | category = "dev" 701 | optional = false 702 | python-versions = "*" 703 | files = [ 704 | {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, 705 | {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, 706 | ] 707 | 708 | [[package]] 709 | name = "nodeenv" 710 | version = "1.7.0" 711 | description = "Node.js virtual environment builder" 712 | category = "dev" 713 | optional = false 714 | python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" 715 | files = [ 716 | {file = "nodeenv-1.7.0-py2.py3-none-any.whl", hash = "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e"}, 717 | {file = "nodeenv-1.7.0.tar.gz", hash = "sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b"}, 718 | ] 719 | 720 | [package.dependencies] 721 | setuptools = "*" 722 | 723 | [[package]] 724 | name = "packaging" 725 | version = "21.3" 726 | description = "Core utilities for Python packages" 727 | category = "dev" 728 | optional = false 729 | python-versions = ">=3.6" 730 | files = [ 731 | {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, 732 | {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, 733 | ] 734 | 735 | [package.dependencies] 736 | pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" 737 | 738 | [[package]] 739 | name = "pathspec" 740 | version = "0.10.1" 741 | description = "Utility library for gitignore style pattern matching of file paths." 742 | category = "dev" 743 | optional = false 744 | python-versions = ">=3.7" 745 | files = [ 746 | {file = "pathspec-0.10.1-py3-none-any.whl", hash = "sha256:46846318467efc4556ccfd27816e004270a9eeeeb4d062ce5e6fc7a87c573f93"}, 747 | {file = "pathspec-0.10.1.tar.gz", hash = "sha256:7ace6161b621d31e7902eb6b5ae148d12cfd23f4a249b9ffb6b9fee12084323d"}, 748 | ] 749 | 750 | [[package]] 751 | name = "pbr" 752 | version = "5.11.0" 753 | description = "Python Build Reasonableness" 754 | category = "dev" 755 | optional = false 756 | python-versions = ">=2.6" 757 | files = [ 758 | {file = "pbr-5.11.0-py2.py3-none-any.whl", hash = "sha256:db2317ff07c84c4c63648c9064a79fe9d9f5c7ce85a9099d4b6258b3db83225a"}, 759 | {file = "pbr-5.11.0.tar.gz", hash = "sha256:b97bc6695b2aff02144133c2e7399d5885223d42b7912ffaec2ca3898e673bfe"}, 760 | ] 761 | 762 | [[package]] 763 | name = "pep8-naming" 764 | version = "0.13.2" 765 | description = "Check PEP-8 naming conventions, plugin for flake8" 766 | category = "dev" 767 | optional = false 768 | python-versions = ">=3.7" 769 | files = [ 770 | {file = "pep8-naming-0.13.2.tar.gz", hash = "sha256:93eef62f525fd12a6f8c98f4dcc17fa70baae2f37fa1f73bec00e3e44392fa48"}, 771 | {file = "pep8_naming-0.13.2-py3-none-any.whl", hash = "sha256:59e29e55c478db69cffbe14ab24b5bd2cd615c0413edf790d47d3fb7ba9a4e23"}, 772 | ] 773 | 774 | [package.dependencies] 775 | flake8 = ">=3.9.1" 776 | 777 | [[package]] 778 | name = "platformdirs" 779 | version = "2.5.2" 780 | description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." 781 | category = "dev" 782 | optional = false 783 | python-versions = ">=3.7" 784 | files = [ 785 | {file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"}, 786 | {file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"}, 787 | ] 788 | 789 | [package.extras] 790 | docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx (>=4)", "sphinx-autodoc-typehints (>=1.12)"] 791 | test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] 792 | 793 | [[package]] 794 | name = "pluggy" 795 | version = "1.0.0" 796 | description = "plugin and hook calling mechanisms for python" 797 | category = "dev" 798 | optional = false 799 | python-versions = ">=3.6" 800 | files = [ 801 | {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, 802 | {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, 803 | ] 804 | 805 | [package.dependencies] 806 | importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} 807 | 808 | [package.extras] 809 | dev = ["pre-commit", "tox"] 810 | testing = ["pytest", "pytest-benchmark"] 811 | 812 | [[package]] 813 | name = "pre-commit" 814 | version = "2.20.0" 815 | description = "A framework for managing and maintaining multi-language pre-commit hooks." 816 | category = "dev" 817 | optional = false 818 | python-versions = ">=3.7" 819 | files = [ 820 | {file = "pre_commit-2.20.0-py2.py3-none-any.whl", hash = "sha256:51a5ba7c480ae8072ecdb6933df22d2f812dc897d5fe848778116129a681aac7"}, 821 | {file = "pre_commit-2.20.0.tar.gz", hash = "sha256:a978dac7bc9ec0bcee55c18a277d553b0f419d259dadb4b9418ff2d00eb43959"}, 822 | ] 823 | 824 | [package.dependencies] 825 | cfgv = ">=2.0.0" 826 | identify = ">=1.0.0" 827 | importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} 828 | nodeenv = ">=0.11.1" 829 | pyyaml = ">=5.1" 830 | toml = "*" 831 | virtualenv = ">=20.0.8" 832 | 833 | [[package]] 834 | name = "pre-commit-hooks" 835 | version = "4.3.0" 836 | description = "Some out-of-the-box hooks for pre-commit." 837 | category = "dev" 838 | optional = false 839 | python-versions = ">=3.7" 840 | files = [ 841 | {file = "pre_commit_hooks-4.3.0-py2.py3-none-any.whl", hash = "sha256:9ccaf7c98794659d345080ee1ea0256a55ae059675045eebdbbc17c0be8c7e4b"}, 842 | {file = "pre_commit_hooks-4.3.0.tar.gz", hash = "sha256:fda598a4c834d030727e6a615722718b47510f4bed72df4c949f95ba9f5aaf88"}, 843 | ] 844 | 845 | [package.dependencies] 846 | "ruamel.yaml" = ">=0.15" 847 | tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} 848 | 849 | [[package]] 850 | name = "pycodestyle" 851 | version = "2.9.1" 852 | description = "Python style guide checker" 853 | category = "dev" 854 | optional = false 855 | python-versions = ">=3.6" 856 | files = [ 857 | {file = "pycodestyle-2.9.1-py2.py3-none-any.whl", hash = "sha256:d1735fc58b418fd7c5f658d28d943854f8a849b01a5d0a1e6f3f3fdd0166804b"}, 858 | {file = "pycodestyle-2.9.1.tar.gz", hash = "sha256:2c9607871d58c76354b697b42f5d57e1ada7d261c261efac224b664affdc5785"}, 859 | ] 860 | 861 | [[package]] 862 | name = "pydocstyle" 863 | version = "6.1.1" 864 | description = "Python docstring style checker" 865 | category = "dev" 866 | optional = false 867 | python-versions = ">=3.6" 868 | files = [ 869 | {file = "pydocstyle-6.1.1-py3-none-any.whl", hash = "sha256:6987826d6775056839940041beef5c08cc7e3d71d63149b48e36727f70144dc4"}, 870 | {file = "pydocstyle-6.1.1.tar.gz", hash = "sha256:1d41b7c459ba0ee6c345f2eb9ae827cab14a7533a88c5c6f7e94923f72df92dc"}, 871 | ] 872 | 873 | [package.dependencies] 874 | snowballstemmer = "*" 875 | 876 | [package.extras] 877 | toml = ["toml"] 878 | 879 | [[package]] 880 | name = "pyflakes" 881 | version = "2.5.0" 882 | description = "passive checker of Python programs" 883 | category = "dev" 884 | optional = false 885 | python-versions = ">=3.6" 886 | files = [ 887 | {file = "pyflakes-2.5.0-py2.py3-none-any.whl", hash = "sha256:4579f67d887f804e67edb544428f264b7b24f435b263c4614f384135cea553d2"}, 888 | {file = "pyflakes-2.5.0.tar.gz", hash = "sha256:491feb020dca48ccc562a8c0cbe8df07ee13078df59813b83959cbdada312ea3"}, 889 | ] 890 | 891 | [[package]] 892 | name = "pygments" 893 | version = "2.13.0" 894 | description = "Pygments is a syntax highlighting package written in Python." 895 | category = "dev" 896 | optional = false 897 | python-versions = ">=3.6" 898 | files = [ 899 | {file = "Pygments-2.13.0-py3-none-any.whl", hash = "sha256:f643f331ab57ba3c9d89212ee4a2dabc6e94f117cf4eefde99a0574720d14c42"}, 900 | {file = "Pygments-2.13.0.tar.gz", hash = "sha256:56a8508ae95f98e2b9bdf93a6be5ae3f7d8af858b43e02c5a2ff083726be40c1"}, 901 | ] 902 | 903 | [package.extras] 904 | plugins = ["importlib-metadata"] 905 | 906 | [[package]] 907 | name = "pyparsing" 908 | version = "3.0.9" 909 | description = "pyparsing module - Classes and methods to define and execute parsing grammars" 910 | category = "dev" 911 | optional = false 912 | python-versions = ">=3.6.8" 913 | files = [ 914 | {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, 915 | {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, 916 | ] 917 | 918 | [package.extras] 919 | diagrams = ["jinja2", "railroad-diagrams"] 920 | 921 | [[package]] 922 | name = "pytest" 923 | version = "7.2.0" 924 | description = "pytest: simple powerful testing with Python" 925 | category = "dev" 926 | optional = false 927 | python-versions = ">=3.7" 928 | files = [ 929 | {file = "pytest-7.2.0-py3-none-any.whl", hash = "sha256:892f933d339f068883b6fd5a459f03d85bfcb355e4981e146d2c7616c21fef71"}, 930 | {file = "pytest-7.2.0.tar.gz", hash = "sha256:c4014eb40e10f11f355ad4e3c2fb2c6c6d1919c73f3b5a433de4708202cade59"}, 931 | ] 932 | 933 | [package.dependencies] 934 | attrs = ">=19.2.0" 935 | colorama = {version = "*", markers = "sys_platform == \"win32\""} 936 | exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} 937 | importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} 938 | iniconfig = "*" 939 | packaging = "*" 940 | pluggy = ">=0.12,<2.0" 941 | tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} 942 | 943 | [package.extras] 944 | testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] 945 | 946 | [[package]] 947 | name = "pytest-django" 948 | version = "4.5.2" 949 | description = "A Django plugin for pytest." 950 | category = "dev" 951 | optional = false 952 | python-versions = ">=3.5" 953 | files = [ 954 | {file = "pytest-django-4.5.2.tar.gz", hash = "sha256:d9076f759bb7c36939dbdd5ae6633c18edfc2902d1a69fdbefd2426b970ce6c2"}, 955 | {file = "pytest_django-4.5.2-py3-none-any.whl", hash = "sha256:c60834861933773109334fe5a53e83d1ef4828f2203a1d6a0fa9972f4f75ab3e"}, 956 | ] 957 | 958 | [package.dependencies] 959 | pytest = ">=5.4.0" 960 | 961 | [package.extras] 962 | docs = ["sphinx", "sphinx-rtd-theme"] 963 | testing = ["Django", "django-configurations (>=2.0)"] 964 | 965 | [[package]] 966 | name = "pytz" 967 | version = "2022.6" 968 | description = "World timezone definitions, modern and historical" 969 | category = "main" 970 | optional = false 971 | python-versions = "*" 972 | files = [ 973 | {file = "pytz-2022.6-py2.py3-none-any.whl", hash = "sha256:222439474e9c98fced559f1709d89e6c9cbf8d79c794ff3eb9f8800064291427"}, 974 | {file = "pytz-2022.6.tar.gz", hash = "sha256:e89512406b793ca39f5971bc999cc538ce125c0e51c27941bef4568b460095e2"}, 975 | ] 976 | 977 | [[package]] 978 | name = "pyyaml" 979 | version = "6.0" 980 | description = "YAML parser and emitter for Python" 981 | category = "dev" 982 | optional = false 983 | python-versions = ">=3.6" 984 | files = [ 985 | {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, 986 | {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, 987 | {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, 988 | {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, 989 | {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, 990 | {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, 991 | {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, 992 | {file = "PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358"}, 993 | {file = "PyYAML-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1"}, 994 | {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d"}, 995 | {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f"}, 996 | {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782"}, 997 | {file = "PyYAML-6.0-cp311-cp311-win32.whl", hash = "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7"}, 998 | {file = "PyYAML-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf"}, 999 | {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, 1000 | {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, 1001 | {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, 1002 | {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, 1003 | {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, 1004 | {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, 1005 | {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, 1006 | {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, 1007 | {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, 1008 | {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, 1009 | {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, 1010 | {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, 1011 | {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, 1012 | {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, 1013 | {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, 1014 | {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, 1015 | {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, 1016 | {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, 1017 | {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, 1018 | {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, 1019 | {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, 1020 | {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, 1021 | {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, 1022 | {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, 1023 | {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, 1024 | {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, 1025 | ] 1026 | 1027 | [[package]] 1028 | name = "requests" 1029 | version = "2.31.0" 1030 | description = "Python HTTP for Humans." 1031 | category = "dev" 1032 | optional = false 1033 | python-versions = ">=3.7" 1034 | files = [ 1035 | {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, 1036 | {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, 1037 | ] 1038 | 1039 | [package.dependencies] 1040 | certifi = ">=2017.4.17" 1041 | charset-normalizer = ">=2,<4" 1042 | idna = ">=2.5,<4" 1043 | urllib3 = ">=1.21.1,<3" 1044 | 1045 | [package.extras] 1046 | socks = ["PySocks (>=1.5.6,!=1.5.7)"] 1047 | use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] 1048 | 1049 | [[package]] 1050 | name = "restructuredtext-lint" 1051 | version = "1.4.0" 1052 | description = "reStructuredText linter" 1053 | category = "dev" 1054 | optional = false 1055 | python-versions = "*" 1056 | files = [ 1057 | {file = "restructuredtext_lint-1.4.0.tar.gz", hash = "sha256:1b235c0c922341ab6c530390892eb9e92f90b9b75046063e047cacfb0f050c45"}, 1058 | ] 1059 | 1060 | [package.dependencies] 1061 | docutils = ">=0.11,<1.0" 1062 | 1063 | [[package]] 1064 | name = "ruamel-yaml" 1065 | version = "0.17.21" 1066 | description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" 1067 | category = "dev" 1068 | optional = false 1069 | python-versions = ">=3" 1070 | files = [ 1071 | {file = "ruamel.yaml-0.17.21-py3-none-any.whl", hash = "sha256:742b35d3d665023981bd6d16b3d24248ce5df75fdb4e2924e93a05c1f8b61ca7"}, 1072 | {file = "ruamel.yaml-0.17.21.tar.gz", hash = "sha256:8b7ce697a2f212752a35c1ac414471dc16c424c9573be4926b56ff3f5d23b7af"}, 1073 | ] 1074 | 1075 | [package.dependencies] 1076 | "ruamel.yaml.clib" = {version = ">=0.2.6", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.11\""} 1077 | 1078 | [package.extras] 1079 | docs = ["ryd"] 1080 | jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"] 1081 | 1082 | [[package]] 1083 | name = "ruamel-yaml-clib" 1084 | version = "0.2.7" 1085 | description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml" 1086 | category = "dev" 1087 | optional = false 1088 | python-versions = ">=3.5" 1089 | files = [ 1090 | {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d5859983f26d8cd7bb5c287ef452e8aacc86501487634573d260968f753e1d71"}, 1091 | {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:debc87a9516b237d0466a711b18b6ebeb17ba9f391eb7f91c649c5c4ec5006c7"}, 1092 | {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:df5828871e6648db72d1c19b4bd24819b80a755c4541d3409f0f7acd0f335c80"}, 1093 | {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:efa08d63ef03d079dcae1dfe334f6c8847ba8b645d08df286358b1f5293d24ab"}, 1094 | {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-win32.whl", hash = "sha256:763d65baa3b952479c4e972669f679fe490eee058d5aa85da483ebae2009d231"}, 1095 | {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-win_amd64.whl", hash = "sha256:d000f258cf42fec2b1bbf2863c61d7b8918d31ffee905da62dede869254d3b8a"}, 1096 | {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:045e0626baf1c52e5527bd5db361bc83180faaba2ff586e763d3d5982a876a9e"}, 1097 | {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-macosx_12_6_arm64.whl", hash = "sha256:721bc4ba4525f53f6a611ec0967bdcee61b31df5a56801281027a3a6d1c2daf5"}, 1098 | {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:41d0f1fa4c6830176eef5b276af04c89320ea616655d01327d5ce65e50575c94"}, 1099 | {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-win32.whl", hash = "sha256:f6d3d39611ac2e4f62c3128a9eed45f19a6608670c5a2f4f07f24e8de3441d38"}, 1100 | {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-win_amd64.whl", hash = "sha256:da538167284de58a52109a9b89b8f6a53ff8437dd6dc26d33b57bf6699153122"}, 1101 | {file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:4b3a93bb9bc662fc1f99c5c3ea8e623d8b23ad22f861eb6fce9377ac07ad6072"}, 1102 | {file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-macosx_12_0_arm64.whl", hash = "sha256:a234a20ae07e8469da311e182e70ef6b199d0fbeb6c6cc2901204dd87fb867e8"}, 1103 | {file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:15910ef4f3e537eea7fe45f8a5d19997479940d9196f357152a09031c5be59f3"}, 1104 | {file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:370445fd795706fd291ab00c9df38a0caed0f17a6fb46b0f607668ecb16ce763"}, 1105 | {file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-win32.whl", hash = "sha256:ecdf1a604009bd35c674b9225a8fa609e0282d9b896c03dd441a91e5f53b534e"}, 1106 | {file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-win_amd64.whl", hash = "sha256:f34019dced51047d6f70cb9383b2ae2853b7fc4dce65129a5acd49f4f9256646"}, 1107 | {file = "ruamel.yaml.clib-0.2.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2aa261c29a5545adfef9296b7e33941f46aa5bbd21164228e833412af4c9c75f"}, 1108 | {file = "ruamel.yaml.clib-0.2.7-cp37-cp37m-macosx_12_0_arm64.whl", hash = "sha256:f01da5790e95815eb5a8a138508c01c758e5f5bc0ce4286c4f7028b8dd7ac3d0"}, 1109 | {file = "ruamel.yaml.clib-0.2.7-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:40d030e2329ce5286d6b231b8726959ebbe0404c92f0a578c0e2482182e38282"}, 1110 | {file = "ruamel.yaml.clib-0.2.7-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:c3ca1fbba4ae962521e5eb66d72998b51f0f4d0f608d3c0347a48e1af262efa7"}, 1111 | {file = "ruamel.yaml.clib-0.2.7-cp37-cp37m-win32.whl", hash = "sha256:7bdb4c06b063f6fd55e472e201317a3bb6cdeeee5d5a38512ea5c01e1acbdd93"}, 1112 | {file = "ruamel.yaml.clib-0.2.7-cp37-cp37m-win_amd64.whl", hash = "sha256:be2a7ad8fd8f7442b24323d24ba0b56c51219513cfa45b9ada3b87b76c374d4b"}, 1113 | {file = "ruamel.yaml.clib-0.2.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:91a789b4aa0097b78c93e3dc4b40040ba55bef518f84a40d4442f713b4094acb"}, 1114 | {file = "ruamel.yaml.clib-0.2.7-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:99e77daab5d13a48a4054803d052ff40780278240a902b880dd37a51ba01a307"}, 1115 | {file = "ruamel.yaml.clib-0.2.7-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:3243f48ecd450eddadc2d11b5feb08aca941b5cd98c9b1db14b2fd128be8c697"}, 1116 | {file = "ruamel.yaml.clib-0.2.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:8831a2cedcd0f0927f788c5bdf6567d9dc9cc235646a434986a852af1cb54b4b"}, 1117 | {file = "ruamel.yaml.clib-0.2.7-cp38-cp38-win32.whl", hash = "sha256:3110a99e0f94a4a3470ff67fc20d3f96c25b13d24c6980ff841e82bafe827cac"}, 1118 | {file = "ruamel.yaml.clib-0.2.7-cp38-cp38-win_amd64.whl", hash = "sha256:92460ce908546ab69770b2e576e4f99fbb4ce6ab4b245345a3869a0a0410488f"}, 1119 | {file = "ruamel.yaml.clib-0.2.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5bc0667c1eb8f83a3752b71b9c4ba55ef7c7058ae57022dd9b29065186a113d9"}, 1120 | {file = "ruamel.yaml.clib-0.2.7-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:4a4d8d417868d68b979076a9be6a38c676eca060785abaa6709c7b31593c35d1"}, 1121 | {file = "ruamel.yaml.clib-0.2.7-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:bf9a6bc4a0221538b1a7de3ed7bca4c93c02346853f44e1cd764be0023cd3640"}, 1122 | {file = "ruamel.yaml.clib-0.2.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:a7b301ff08055d73223058b5c46c55638917f04d21577c95e00e0c4d79201a6b"}, 1123 | {file = "ruamel.yaml.clib-0.2.7-cp39-cp39-win32.whl", hash = "sha256:d5e51e2901ec2366b79f16c2299a03e74ba4531ddcfacc1416639c557aef0ad8"}, 1124 | {file = "ruamel.yaml.clib-0.2.7-cp39-cp39-win_amd64.whl", hash = "sha256:184faeaec61dbaa3cace407cffc5819f7b977e75360e8d5ca19461cd851a5fc5"}, 1125 | {file = "ruamel.yaml.clib-0.2.7.tar.gz", hash = "sha256:1f08fd5a2bea9c4180db71678e850b995d2a5f4537be0e94557668cf0f5f9497"}, 1126 | ] 1127 | 1128 | [[package]] 1129 | name = "safety" 1130 | version = "2.3.5" 1131 | description = "Checks installed dependencies for known vulnerabilities and licenses." 1132 | category = "dev" 1133 | optional = false 1134 | python-versions = "*" 1135 | files = [ 1136 | {file = "safety-2.3.5-py3-none-any.whl", hash = "sha256:2227fcac1b22b53c1615af78872b48348661691450aa25d6704a5504dbd1f7e2"}, 1137 | {file = "safety-2.3.5.tar.gz", hash = "sha256:a60c11f8952f412cbb165d70cb1f673a3b43a2ba9a93ce11f97e6a4de834aa3a"}, 1138 | ] 1139 | 1140 | [package.dependencies] 1141 | Click = ">=8.0.2" 1142 | dparse = ">=0.6.2" 1143 | packaging = ">=21.0,<22.0" 1144 | requests = "*" 1145 | "ruamel.yaml" = ">=0.17.21" 1146 | setuptools = ">=19.3" 1147 | 1148 | [package.extras] 1149 | github = ["jinja2 (>=3.1.0)", "pygithub (>=1.43.3)"] 1150 | gitlab = ["python-gitlab (>=1.3.0)"] 1151 | 1152 | [[package]] 1153 | name = "setuptools" 1154 | version = "65.5.1" 1155 | description = "Easily download, build, install, upgrade, and uninstall Python packages" 1156 | category = "dev" 1157 | optional = false 1158 | python-versions = ">=3.7" 1159 | files = [ 1160 | {file = "setuptools-65.5.1-py3-none-any.whl", hash = "sha256:d0b9a8433464d5800cbe05094acf5c6d52a91bfac9b52bcfc4d41382be5d5d31"}, 1161 | {file = "setuptools-65.5.1.tar.gz", hash = "sha256:e197a19aa8ec9722928f2206f8de752def0e4c9fc6953527360d1c36d94ddb2f"}, 1162 | ] 1163 | 1164 | [package.extras] 1165 | docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] 1166 | testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] 1167 | testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] 1168 | 1169 | [[package]] 1170 | name = "six" 1171 | version = "1.16.0" 1172 | description = "Python 2 and 3 compatibility utilities" 1173 | category = "dev" 1174 | optional = false 1175 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" 1176 | files = [ 1177 | {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, 1178 | {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, 1179 | ] 1180 | 1181 | [[package]] 1182 | name = "smmap" 1183 | version = "5.0.0" 1184 | description = "A pure Python implementation of a sliding window memory map manager" 1185 | category = "dev" 1186 | optional = false 1187 | python-versions = ">=3.6" 1188 | files = [ 1189 | {file = "smmap-5.0.0-py3-none-any.whl", hash = "sha256:2aba19d6a040e78d8b09de5c57e96207b09ed71d8e55ce0959eeee6c8e190d94"}, 1190 | {file = "smmap-5.0.0.tar.gz", hash = "sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936"}, 1191 | ] 1192 | 1193 | [[package]] 1194 | name = "snowballstemmer" 1195 | version = "2.2.0" 1196 | description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." 1197 | category = "dev" 1198 | optional = false 1199 | python-versions = "*" 1200 | files = [ 1201 | {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, 1202 | {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, 1203 | ] 1204 | 1205 | [[package]] 1206 | name = "sphinx" 1207 | version = "4.3.2" 1208 | description = "Python documentation generator" 1209 | category = "dev" 1210 | optional = false 1211 | python-versions = ">=3.6" 1212 | files = [ 1213 | {file = "Sphinx-4.3.2-py3-none-any.whl", hash = "sha256:6a11ea5dd0bdb197f9c2abc2e0ce73e01340464feaece525e64036546d24c851"}, 1214 | {file = "Sphinx-4.3.2.tar.gz", hash = "sha256:0a8836751a68306b3fe97ecbe44db786f8479c3bf4b80e3a7f5c838657b4698c"}, 1215 | ] 1216 | 1217 | [package.dependencies] 1218 | alabaster = ">=0.7,<0.8" 1219 | babel = ">=1.3" 1220 | colorama = {version = ">=0.3.5", markers = "sys_platform == \"win32\""} 1221 | docutils = ">=0.14,<0.18" 1222 | imagesize = "*" 1223 | Jinja2 = ">=2.3" 1224 | packaging = "*" 1225 | Pygments = ">=2.0" 1226 | requests = ">=2.5.0" 1227 | setuptools = "*" 1228 | snowballstemmer = ">=1.1" 1229 | sphinxcontrib-applehelp = "*" 1230 | sphinxcontrib-devhelp = "*" 1231 | sphinxcontrib-htmlhelp = ">=2.0.0" 1232 | sphinxcontrib-jsmath = "*" 1233 | sphinxcontrib-qthelp = "*" 1234 | sphinxcontrib-serializinghtml = ">=1.1.5" 1235 | 1236 | [package.extras] 1237 | docs = ["sphinxcontrib-websupport"] 1238 | lint = ["docutils-stubs", "flake8 (>=3.5.0)", "isort", "mypy (>=0.920)", "types-pkg-resources", "types-requests", "types-typed-ast"] 1239 | test = ["cython", "html5lib", "pytest", "pytest-cov", "typed-ast"] 1240 | 1241 | [[package]] 1242 | name = "sphinx-autobuild" 1243 | version = "2021.3.14" 1244 | description = "Rebuild Sphinx documentation on changes, with live-reload in the browser." 1245 | category = "dev" 1246 | optional = false 1247 | python-versions = ">=3.6" 1248 | files = [ 1249 | {file = "sphinx-autobuild-2021.3.14.tar.gz", hash = "sha256:de1ca3b66e271d2b5b5140c35034c89e47f263f2cd5db302c9217065f7443f05"}, 1250 | {file = "sphinx_autobuild-2021.3.14-py3-none-any.whl", hash = "sha256:8fe8cbfdb75db04475232f05187c776f46f6e9e04cacf1e49ce81bdac649ccac"}, 1251 | ] 1252 | 1253 | [package.dependencies] 1254 | colorama = "*" 1255 | livereload = "*" 1256 | sphinx = "*" 1257 | 1258 | [package.extras] 1259 | test = ["pytest", "pytest-cov"] 1260 | 1261 | [[package]] 1262 | name = "sphinxcontrib-applehelp" 1263 | version = "1.0.2" 1264 | description = "sphinxcontrib-applehelp is a sphinx extension which outputs Apple help books" 1265 | category = "dev" 1266 | optional = false 1267 | python-versions = ">=3.5" 1268 | files = [ 1269 | {file = "sphinxcontrib-applehelp-1.0.2.tar.gz", hash = "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"}, 1270 | {file = "sphinxcontrib_applehelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a"}, 1271 | ] 1272 | 1273 | [package.extras] 1274 | lint = ["docutils-stubs", "flake8", "mypy"] 1275 | test = ["pytest"] 1276 | 1277 | [[package]] 1278 | name = "sphinxcontrib-devhelp" 1279 | version = "1.0.2" 1280 | description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document." 1281 | category = "dev" 1282 | optional = false 1283 | python-versions = ">=3.5" 1284 | files = [ 1285 | {file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"}, 1286 | {file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"}, 1287 | ] 1288 | 1289 | [package.extras] 1290 | lint = ["docutils-stubs", "flake8", "mypy"] 1291 | test = ["pytest"] 1292 | 1293 | [[package]] 1294 | name = "sphinxcontrib-htmlhelp" 1295 | version = "2.0.0" 1296 | description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" 1297 | category = "dev" 1298 | optional = false 1299 | python-versions = ">=3.6" 1300 | files = [ 1301 | {file = "sphinxcontrib-htmlhelp-2.0.0.tar.gz", hash = "sha256:f5f8bb2d0d629f398bf47d0d69c07bc13b65f75a81ad9e2f71a63d4b7a2f6db2"}, 1302 | {file = "sphinxcontrib_htmlhelp-2.0.0-py2.py3-none-any.whl", hash = "sha256:d412243dfb797ae3ec2b59eca0e52dac12e75a241bf0e4eb861e450d06c6ed07"}, 1303 | ] 1304 | 1305 | [package.extras] 1306 | lint = ["docutils-stubs", "flake8", "mypy"] 1307 | test = ["html5lib", "pytest"] 1308 | 1309 | [[package]] 1310 | name = "sphinxcontrib-jsmath" 1311 | version = "1.0.1" 1312 | description = "A sphinx extension which renders display math in HTML via JavaScript" 1313 | category = "dev" 1314 | optional = false 1315 | python-versions = ">=3.5" 1316 | files = [ 1317 | {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, 1318 | {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, 1319 | ] 1320 | 1321 | [package.extras] 1322 | test = ["flake8", "mypy", "pytest"] 1323 | 1324 | [[package]] 1325 | name = "sphinxcontrib-qthelp" 1326 | version = "1.0.3" 1327 | description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document." 1328 | category = "dev" 1329 | optional = false 1330 | python-versions = ">=3.5" 1331 | files = [ 1332 | {file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"}, 1333 | {file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"}, 1334 | ] 1335 | 1336 | [package.extras] 1337 | lint = ["docutils-stubs", "flake8", "mypy"] 1338 | test = ["pytest"] 1339 | 1340 | [[package]] 1341 | name = "sphinxcontrib-serializinghtml" 1342 | version = "1.1.5" 1343 | description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)." 1344 | category = "dev" 1345 | optional = false 1346 | python-versions = ">=3.5" 1347 | files = [ 1348 | {file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"}, 1349 | {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, 1350 | ] 1351 | 1352 | [package.extras] 1353 | lint = ["docutils-stubs", "flake8", "mypy"] 1354 | test = ["pytest"] 1355 | 1356 | [[package]] 1357 | name = "sqlparse" 1358 | version = "0.4.4" 1359 | description = "A non-validating SQL parser." 1360 | category = "main" 1361 | optional = false 1362 | python-versions = ">=3.5" 1363 | files = [ 1364 | {file = "sqlparse-0.4.4-py3-none-any.whl", hash = "sha256:5430a4fe2ac7d0f93e66f1efc6e1338a41884b7ddf2a350cedd20ccc4d9d28f3"}, 1365 | {file = "sqlparse-0.4.4.tar.gz", hash = "sha256:d446183e84b8349fa3061f0fe7f06ca94ba65b426946ffebe6e3e8295332420c"}, 1366 | ] 1367 | 1368 | [package.extras] 1369 | dev = ["build", "flake8"] 1370 | doc = ["sphinx"] 1371 | test = ["pytest", "pytest-cov"] 1372 | 1373 | [[package]] 1374 | name = "stevedore" 1375 | version = "3.5.2" 1376 | description = "Manage dynamic plugins for Python applications" 1377 | category = "dev" 1378 | optional = false 1379 | python-versions = ">=3.6" 1380 | files = [ 1381 | {file = "stevedore-3.5.2-py3-none-any.whl", hash = "sha256:fa2630e3d0ad3e22d4914aff2501445815b9a4467a6edc49387c667a38faf5bf"}, 1382 | {file = "stevedore-3.5.2.tar.gz", hash = "sha256:cf99f41fc0d5a4f185ca4d3d42b03be9011b0a1ec1a4ea1a282be1b4b306dcc2"}, 1383 | ] 1384 | 1385 | [package.dependencies] 1386 | importlib-metadata = {version = ">=1.7.0", markers = "python_version < \"3.8\""} 1387 | pbr = ">=2.0.0,<2.1.0 || >2.1.0" 1388 | 1389 | [[package]] 1390 | name = "toml" 1391 | version = "0.10.2" 1392 | description = "Python Library for Tom's Obvious, Minimal Language" 1393 | category = "dev" 1394 | optional = false 1395 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" 1396 | files = [ 1397 | {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, 1398 | {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, 1399 | ] 1400 | 1401 | [[package]] 1402 | name = "tomli" 1403 | version = "2.0.1" 1404 | description = "A lil' TOML parser" 1405 | category = "dev" 1406 | optional = false 1407 | python-versions = ">=3.7" 1408 | files = [ 1409 | {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, 1410 | {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, 1411 | ] 1412 | 1413 | [[package]] 1414 | name = "tornado" 1415 | version = "6.2" 1416 | description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." 1417 | category = "dev" 1418 | optional = false 1419 | python-versions = ">= 3.7" 1420 | files = [ 1421 | {file = "tornado-6.2-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:20f638fd8cc85f3cbae3c732326e96addff0a15e22d80f049e00121651e82e72"}, 1422 | {file = "tornado-6.2-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:87dcafae3e884462f90c90ecc200defe5e580a7fbbb4365eda7c7c1eb809ebc9"}, 1423 | {file = "tornado-6.2-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba09ef14ca9893954244fd872798b4ccb2367c165946ce2dd7376aebdde8e3ac"}, 1424 | {file = "tornado-6.2-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8150f721c101abdef99073bf66d3903e292d851bee51910839831caba341a75"}, 1425 | {file = "tornado-6.2-cp37-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3a2f5999215a3a06a4fc218026cd84c61b8b2b40ac5296a6db1f1451ef04c1e"}, 1426 | {file = "tornado-6.2-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:5f8c52d219d4995388119af7ccaa0bcec289535747620116a58d830e7c25d8a8"}, 1427 | {file = "tornado-6.2-cp37-abi3-musllinux_1_1_i686.whl", hash = "sha256:6fdfabffd8dfcb6cf887428849d30cf19a3ea34c2c248461e1f7d718ad30b66b"}, 1428 | {file = "tornado-6.2-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:1d54d13ab8414ed44de07efecb97d4ef7c39f7438cf5e976ccd356bebb1b5fca"}, 1429 | {file = "tornado-6.2-cp37-abi3-win32.whl", hash = "sha256:5c87076709343557ef8032934ce5f637dbb552efa7b21d08e89ae7619ed0eb23"}, 1430 | {file = "tornado-6.2-cp37-abi3-win_amd64.whl", hash = "sha256:e5f923aa6a47e133d1cf87d60700889d7eae68988704e20c75fb2d65677a8e4b"}, 1431 | {file = "tornado-6.2.tar.gz", hash = "sha256:9b630419bde84ec666bfd7ea0a4cb2a8a651c2d5cccdbdd1972a0c859dfc3c13"}, 1432 | ] 1433 | 1434 | [[package]] 1435 | name = "typed-ast" 1436 | version = "1.5.4" 1437 | description = "a fork of Python 2 and 3 ast modules with type comment support" 1438 | category = "dev" 1439 | optional = false 1440 | python-versions = ">=3.6" 1441 | files = [ 1442 | {file = "typed_ast-1.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:669dd0c4167f6f2cd9f57041e03c3c2ebf9063d0757dc89f79ba1daa2bfca9d4"}, 1443 | {file = "typed_ast-1.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:211260621ab1cd7324e0798d6be953d00b74e0428382991adfddb352252f1d62"}, 1444 | {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:267e3f78697a6c00c689c03db4876dd1efdfea2f251a5ad6555e82a26847b4ac"}, 1445 | {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c542eeda69212fa10a7ada75e668876fdec5f856cd3d06829e6aa64ad17c8dfe"}, 1446 | {file = "typed_ast-1.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:a9916d2bb8865f973824fb47436fa45e1ebf2efd920f2b9f99342cb7fab93f72"}, 1447 | {file = "typed_ast-1.5.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:79b1e0869db7c830ba6a981d58711c88b6677506e648496b1f64ac7d15633aec"}, 1448 | {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a94d55d142c9265f4ea46fab70977a1944ecae359ae867397757d836ea5a3f47"}, 1449 | {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:183afdf0ec5b1b211724dfef3d2cad2d767cbefac291f24d69b00546c1837fb6"}, 1450 | {file = "typed_ast-1.5.4-cp36-cp36m-win_amd64.whl", hash = "sha256:639c5f0b21776605dd6c9dbe592d5228f021404dafd377e2b7ac046b0349b1a1"}, 1451 | {file = "typed_ast-1.5.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cf4afcfac006ece570e32d6fa90ab74a17245b83dfd6655a6f68568098345ff6"}, 1452 | {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed855bbe3eb3715fca349c80174cfcfd699c2f9de574d40527b8429acae23a66"}, 1453 | {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6778e1b2f81dfc7bc58e4b259363b83d2e509a65198e85d5700dfae4c6c8ff1c"}, 1454 | {file = "typed_ast-1.5.4-cp37-cp37m-win_amd64.whl", hash = "sha256:0261195c2062caf107831e92a76764c81227dae162c4f75192c0d489faf751a2"}, 1455 | {file = "typed_ast-1.5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2efae9db7a8c05ad5547d522e7dbe62c83d838d3906a3716d1478b6c1d61388d"}, 1456 | {file = "typed_ast-1.5.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7d5d014b7daa8b0bf2eaef684295acae12b036d79f54178b92a2b6a56f92278f"}, 1457 | {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:370788a63915e82fd6f212865a596a0fefcbb7d408bbbb13dea723d971ed8bdc"}, 1458 | {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4e964b4ff86550a7a7d56345c7864b18f403f5bd7380edf44a3c1fb4ee7ac6c6"}, 1459 | {file = "typed_ast-1.5.4-cp38-cp38-win_amd64.whl", hash = "sha256:683407d92dc953c8a7347119596f0b0e6c55eb98ebebd9b23437501b28dcbb8e"}, 1460 | {file = "typed_ast-1.5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4879da6c9b73443f97e731b617184a596ac1235fe91f98d279a7af36c796da35"}, 1461 | {file = "typed_ast-1.5.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3e123d878ba170397916557d31c8f589951e353cc95fb7f24f6bb69adc1a8a97"}, 1462 | {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebd9d7f80ccf7a82ac5f88c521115cc55d84e35bf8b446fcd7836eb6b98929a3"}, 1463 | {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98f80dee3c03455e92796b58b98ff6ca0b2a6f652120c263efdba4d6c5e58f72"}, 1464 | {file = "typed_ast-1.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:0fdbcf2fef0ca421a3f5912555804296f0b0960f0418c440f5d6d3abb549f3e1"}, 1465 | {file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"}, 1466 | ] 1467 | 1468 | [[package]] 1469 | name = "typeguard" 1470 | version = "2.13.3" 1471 | description = "Run-time type checker for Python" 1472 | category = "dev" 1473 | optional = false 1474 | python-versions = ">=3.5.3" 1475 | files = [ 1476 | {file = "typeguard-2.13.3-py3-none-any.whl", hash = "sha256:5e3e3be01e887e7eafae5af63d1f36c849aaa94e3a0112097312aabfa16284f1"}, 1477 | {file = "typeguard-2.13.3.tar.gz", hash = "sha256:00edaa8da3a133674796cf5ea87d9f4b4c367d77476e185e80251cc13dfbb8c4"}, 1478 | ] 1479 | 1480 | [package.extras] 1481 | doc = ["sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] 1482 | test = ["mypy", "pytest", "typing-extensions"] 1483 | 1484 | [[package]] 1485 | name = "typing-extensions" 1486 | version = "4.4.0" 1487 | description = "Backported and Experimental Type Hints for Python 3.7+" 1488 | category = "main" 1489 | optional = false 1490 | python-versions = ">=3.7" 1491 | files = [ 1492 | {file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"}, 1493 | {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"}, 1494 | ] 1495 | 1496 | [[package]] 1497 | name = "urllib3" 1498 | version = "1.26.12" 1499 | description = "HTTP library with thread-safe connection pooling, file post, and more." 1500 | category = "dev" 1501 | optional = false 1502 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4" 1503 | files = [ 1504 | {file = "urllib3-1.26.12-py2.py3-none-any.whl", hash = "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997"}, 1505 | {file = "urllib3-1.26.12.tar.gz", hash = "sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e"}, 1506 | ] 1507 | 1508 | [package.extras] 1509 | brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] 1510 | secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] 1511 | socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] 1512 | 1513 | [[package]] 1514 | name = "virtualenv" 1515 | version = "20.16.2" 1516 | description = "Virtual Python Environment builder" 1517 | category = "dev" 1518 | optional = false 1519 | python-versions = ">=3.6" 1520 | files = [ 1521 | {file = "virtualenv-20.16.2-py2.py3-none-any.whl", hash = "sha256:635b272a8e2f77cb051946f46c60a54ace3cb5e25568228bd6b57fc70eca9ff3"}, 1522 | {file = "virtualenv-20.16.2.tar.gz", hash = "sha256:0ef5be6d07181946891f5abc8047fda8bc2f0b4b9bf222c64e6e8963baee76db"}, 1523 | ] 1524 | 1525 | [package.dependencies] 1526 | distlib = ">=0.3.1,<1" 1527 | filelock = ">=3.2,<4" 1528 | importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} 1529 | platformdirs = ">=2,<3" 1530 | 1531 | [package.extras] 1532 | docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=21.3)"] 1533 | testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "packaging (>=20.0)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)"] 1534 | 1535 | [[package]] 1536 | name = "xdoctest" 1537 | version = "1.1.0" 1538 | description = "A rewrite of the builtin doctest module" 1539 | category = "dev" 1540 | optional = false 1541 | python-versions = ">=3.6" 1542 | files = [ 1543 | {file = "xdoctest-1.1.0-py3-none-any.whl", hash = "sha256:da330c4dacee51f3c785820bc743188fb6f7c64c5fa1c54bff8836b3cf23d69b"}, 1544 | {file = "xdoctest-1.1.0.tar.gz", hash = "sha256:0fd4fad7932f0a2f082dfdfb857dd6ca41603757586c39b1e5b4d333fc389f8a"}, 1545 | ] 1546 | 1547 | [package.dependencies] 1548 | six = "*" 1549 | 1550 | [package.extras] 1551 | all = ["IPython", "IPython", "Pygments", "Pygments", "attrs", "cmake", "codecov", "colorama", "debugpy", "debugpy", "debugpy", "debugpy", "debugpy", "ipykernel", "ipykernel", "ipython-genutils", "jedi", "jinja2", "jupyter-client", "jupyter-client", "jupyter-core", "nbconvert", "ninja", "pybind11", "pytest", "pytest", "pytest", "pytest", "pytest", "pytest", "pytest-cov", "pytest-cov", "pytest-cov", "pytest-cov", "scikit-build", "six", "typing"] 1552 | all-strict = ["IPython (==7.10.0)", "IPython (==7.23.1)", "Pygments (==2.0.0)", "Pygments (==2.4.1)", "attrs (==19.2.0)", "cmake (==3.21.2)", "codecov (==2.0.15)", "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)", "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)", "ninja (==1.10.2)", "pybind11 (==2.7.1)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==6.2.5)", "pytest-cov (==2.8.1)", "pytest-cov (==2.8.1)", "pytest-cov (==2.9.0)", "pytest-cov (==3.0.0)", "scikit-build (==0.11.1)", "six (==1.11.0)", "typing (==3.7.4)"] 1553 | colors = ["Pygments", "Pygments", "colorama"] 1554 | jupyter = ["IPython", "IPython", "attrs", "debugpy", "debugpy", "debugpy", "debugpy", "debugpy", "ipykernel", "ipykernel", "ipython-genutils", "jedi", "jinja2", "jupyter-client", "jupyter-client", "jupyter-core", "nbconvert"] 1555 | optional = ["IPython", "IPython", "Pygments", "Pygments", "attrs", "colorama", "debugpy", "debugpy", "debugpy", "debugpy", "debugpy", "ipykernel", "ipykernel", "ipython-genutils", "jedi", "jinja2", "jupyter-client", "jupyter-client", "jupyter-core", "nbconvert", "pyflakes", "tomli"] 1556 | 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)", "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)", "pyflakes (==2.2.0)", "tomli (==0.2.0)"] 1557 | runtime-strict = ["six (==1.11.0)"] 1558 | tests = ["cmake", "codecov", "ninja", "pybind11", "pytest", "pytest", "pytest", "pytest", "pytest", "pytest", "pytest-cov", "pytest-cov", "pytest-cov", "pytest-cov", "scikit-build", "typing"] 1559 | tests-strict = ["cmake (==3.21.2)", "codecov (==2.0.15)", "ninja (==1.10.2)", "pybind11 (==2.7.1)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==6.2.5)", "pytest-cov (==2.8.1)", "pytest-cov (==2.8.1)", "pytest-cov (==2.9.0)", "pytest-cov (==3.0.0)", "scikit-build (==0.11.1)", "typing (==3.7.4)"] 1560 | 1561 | [[package]] 1562 | name = "zipp" 1563 | version = "3.10.0" 1564 | description = "Backport of pathlib-compatible object wrapper for zip files" 1565 | category = "dev" 1566 | optional = false 1567 | python-versions = ">=3.7" 1568 | files = [ 1569 | {file = "zipp-3.10.0-py3-none-any.whl", hash = "sha256:4fcb6f278987a6605757302a6e40e896257570d11c51628968ccb2a47e80c6c1"}, 1570 | {file = "zipp-3.10.0.tar.gz", hash = "sha256:7a7262fd930bd3e36c50b9a64897aec3fafff3dfdeec9623ae22b40e93f99bb8"}, 1571 | ] 1572 | 1573 | [package.extras] 1574 | docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] 1575 | testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] 1576 | 1577 | [metadata] 1578 | lock-version = "2.0" 1579 | python-versions = "^3.7.0" 1580 | content-hash = "df7600c26cc2d11f68faee3e0dd26cbe4aa1b3469917f0ae6c61938e4ec3a83b" 1581 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "django-pagination-bootstrap" 3 | version = "2.4.6" 4 | description = "Easy add pagination in Django, using Bootstrap's layout." 5 | authors = ["staticdev "] 6 | license = "MIT" 7 | readme = "README.rst" 8 | repository = "https://github.com/staticdev/django-pagination-bootstrap" 9 | homepage = "https://github.com/staticdev/django-pagination-bootstrap" 10 | documentation = "https://django-pagination-bootstrap.readthedocs.io" 11 | classifiers = [ 12 | "Environment :: Web Environment", 13 | "Framework :: Django", 14 | "Framework :: Django :: 2.0", 15 | "Framework :: Django :: 2.1", 16 | "Framework :: Django :: 2.2", 17 | "Framework :: Django :: 3.0", 18 | "Operating System :: OS Independent", 19 | "Development Status :: 7 - Inactive", 20 | "Programming Language :: Python", 21 | "Programming Language :: Python :: 3.6", 22 | "Programming Language :: Python :: 3.7", 23 | "Programming Language :: Python :: 3.8", 24 | "Programming Language :: Python :: 3.9", 25 | "Intended Audience :: Developers", 26 | "License :: OSI Approved :: MIT License", 27 | "Topic :: Internet :: WWW/HTTP", 28 | "Topic :: Internet :: WWW/HTTP :: Dynamic Content", 29 | "Topic :: Software Development :: Libraries :: Python Modules", 30 | ] 31 | 32 | [tool.poetry.urls] 33 | Changelog = "https://github.com/staticdev/django-pagination-bootstrap/releases" 34 | 35 | [tool.poetry.dependencies] 36 | python = "^3.7.0" 37 | django = ">=2.0" 38 | 39 | [tool.poetry.dev-dependencies] 40 | bandit = ">=1.7.4" 41 | black = ">=22.1" 42 | coverage = {extras = ["toml"], version = ">=6.3.2"} 43 | darglint = ">=1.8.1" 44 | flake8 = ">=5.0.4" 45 | flake8-bugbear = ">=21.9.2" 46 | flake8-docstrings = ">=1.6.0" 47 | flake8-rst-docstrings = ">=0.2.5" 48 | mypy = ">=0.941" 49 | pep8-naming = ">=0.12.1" 50 | pre-commit = ">=2.17.0" 51 | pre-commit-hooks = ">=4.1.0" 52 | pytest = ">=7.0.0" 53 | pytest-django = ">=4.1.0" 54 | pygments = ">=2.11.2" 55 | safety = ">=2.3.1" 56 | sphinx = ">=4.3.2" 57 | sphinx-autobuild = ">=2021.3.14" 58 | typeguard = ">=2.13.3" 59 | xdoctest = ">=0.15.2" 60 | 61 | [tool.coverage.paths] 62 | source = ["src", "*/site-packages"] 63 | 64 | [tool.coverage.run] 65 | branch = true 66 | source = ["django_pagination_bootstrap"] 67 | 68 | [tool.coverage.report] 69 | show_missing = true 70 | fail_under = 80 71 | 72 | [build-system] 73 | requires = ["poetry-core>=1.0.0"] 74 | build-backend = "poetry.core.masonry.api" 75 | -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | DJANGO_SETTINGS_MODULE = tests.settings 3 | -------------------------------------------------------------------------------- /src/django_pagination_bootstrap/__init__.py: -------------------------------------------------------------------------------- 1 | """django-pagination-bootstrap lib.""" 2 | -------------------------------------------------------------------------------- /src/django_pagination_bootstrap/middleware.py: -------------------------------------------------------------------------------- 1 | """django-pagination-bootstrap middleware.""" 2 | 3 | 4 | def get_page(self) -> int: 5 | """A function which will be monkeypatched onto the request to get the current integer representing the current page.""" 6 | try: 7 | return int(self.GET.get("page")) 8 | except (KeyError, ValueError, TypeError): 9 | return 1 10 | 11 | 12 | class PaginationMiddleware: 13 | """Pagination Middleware class. 14 | 15 | Inserts a variable representing the current page onto the request object if 16 | it exists in either **GET** or **POST** portions of the request. 17 | """ 18 | 19 | def __init__(self, get_response) -> None: 20 | """Constructor.""" 21 | self.get_response = get_response 22 | 23 | def __call__(self, request) -> None: 24 | request.__class__.page = property(get_page) 25 | response = self.get_response(request) 26 | return response 27 | -------------------------------------------------------------------------------- /src/django_pagination_bootstrap/paginator.py: -------------------------------------------------------------------------------- 1 | """paginator classes.""" 2 | from django.core.paginator import EmptyPage 3 | from django.core.paginator import Page 4 | from django.core.paginator import PageNotAnInteger 5 | from django.core.paginator import Paginator 6 | 7 | 8 | class InfinitePaginator(Paginator): 9 | """Paginator designed for when it's not important to know how many total pages. 10 | 11 | This is useful for any object_list that has no count() method or can 12 | be used to improve performance for MySQL by removing counts. 13 | 14 | The orphans parameter has been removed for simplicity and there's a link 15 | template string for creating the links to the next and previous pages. 16 | """ 17 | 18 | def __init__( 19 | self, 20 | object_list, 21 | per_page, 22 | allow_empty_first_page=True, 23 | link_template="/page/%d/", 24 | ): 25 | """Constructor.""" 26 | orphans = 0 # no orphans 27 | super(InfinitePaginator, self).__init__( 28 | object_list, per_page, orphans, allow_empty_first_page 29 | ) 30 | try: 31 | # no count or num pages 32 | del self._num_pages, self._count 33 | except AttributeError: 34 | pass 35 | # bonus links 36 | self.link_template = link_template 37 | 38 | def validate_number(self, number): 39 | """Validate the given 1-based page number.""" 40 | try: 41 | number = int(number) 42 | except ValueError: 43 | raise PageNotAnInteger("That page number is not an integer") from None 44 | if number < 1: 45 | raise EmptyPage("That page number is less than 1") 46 | return number 47 | 48 | def page(self, number): 49 | """ 50 | Return a Page object for the given 1-based page number. 51 | """ 52 | number = self.validate_number(number) 53 | bottom = (number - 1) * self.per_page 54 | top = bottom + self.per_page 55 | page_items = self.object_list[bottom:top] 56 | # check moved from validate_number 57 | if not page_items: 58 | if number == 1 and self.allow_empty_first_page: 59 | pass 60 | else: 61 | raise EmptyPage("That page contains no results") 62 | return InfinitePage(page_items, number, self) 63 | 64 | def _get_count(self): 65 | """Return the total number of objects, across all pages.""" 66 | raise NotImplementedError 67 | 68 | count = property(_get_count) 69 | 70 | def _get_num_pages(self): 71 | """Return the total number of pages.""" 72 | raise NotImplementedError 73 | 74 | num_pages = property(_get_num_pages) 75 | 76 | def _get_page_range(self): 77 | """Return a 1-based range of pages. 78 | 79 | It is used for iterating through within a template for loop. 80 | 81 | Raises: 82 | NotImplementedError: to be implemented. 83 | """ 84 | raise NotImplementedError 85 | 86 | page_range = property(_get_page_range) 87 | 88 | 89 | class InfinitePage(Page): 90 | def __repr__(self): 91 | return "" % self.number 92 | 93 | def has_next(self): 94 | """Check for one more item than last on this page.""" 95 | try: 96 | self.paginator.object_list[self.number * self.paginator.per_page] 97 | except IndexError: 98 | return False 99 | return True 100 | 101 | def end_index(self) -> int: 102 | """Return the 1-based index of the last object on this page. 103 | 104 | This index is relative to total objects found (hits). 105 | 106 | Returns: 107 | int: end index. 108 | """ 109 | return (self.number - 1) * self.paginator.per_page + len(self.object_list) 110 | 111 | # Bonus methods for creating links 112 | def next_link(self): 113 | if self.has_next(): 114 | return self.paginator.link_template % (self.number + 1) 115 | return None 116 | 117 | def previous_link(self): 118 | if self.has_previous(): 119 | return self.paginator.link_template % (self.number - 1) 120 | return None 121 | 122 | 123 | class FinitePaginator(InfinitePaginator): 124 | """Paginator for cases when the list of items is already finite. 125 | 126 | A good example is a list generated from an API call. This is a subclass 127 | of InfinitePaginator because we have no idea how many items exist in the 128 | full collection. 129 | 130 | To accurately determine if the next page exists, a FinitePaginator MUST be 131 | created with an object_list_plus that may contain more items than the 132 | per_page count. Typically, you'll have an object_list_plus with one extra 133 | item (if there's a next page). You'll also need to supply the offset from 134 | the full collection in order to get the page start_index. 135 | 136 | This is a very silly class but useful if you love the Django pagination 137 | conventions. 138 | """ 139 | 140 | def __init__( 141 | self, 142 | object_list_plus, 143 | per_page, 144 | offset=None, 145 | allow_empty_first_page=True, 146 | link_template="/page/%d/", 147 | ): 148 | """Constructor.""" 149 | super(FinitePaginator, self).__init__( 150 | object_list_plus, per_page, allow_empty_first_page, link_template 151 | ) 152 | self.offset = offset 153 | 154 | def validate_number(self, number): 155 | super(FinitePaginator, self).validate_number(number) 156 | # check for an empty list to see if the page exists 157 | if not self.object_list: 158 | if number == 1 and self.allow_empty_first_page: 159 | pass 160 | else: 161 | raise EmptyPage("That page contains no results") 162 | return number 163 | 164 | def page(self, number): 165 | """Return a Page object for the given 1-based page number.""" 166 | number = self.validate_number(number) 167 | # remove the extra item(s) when creating the page 168 | page_items = self.object_list[: self.per_page] 169 | return FinitePage(page_items, number, self) 170 | 171 | 172 | class FinitePage(InfinitePage): 173 | def has_next(self): 174 | """Check for one more item than last on this page.""" 175 | try: 176 | self.paginator.object_list[self.paginator.per_page] 177 | except IndexError: 178 | return False 179 | return True 180 | 181 | def start_index(self): 182 | """Return the 1-based index of the first object on this page. 183 | 184 | This index is relative to total objects in the paginator. 185 | 186 | Returns: 187 | paginator offset. 188 | """ 189 | # TODO should this holler if you haven't defined the offset? 190 | return self.paginator.offset 191 | -------------------------------------------------------------------------------- /src/django_pagination_bootstrap/templates/pagination.html: -------------------------------------------------------------------------------- 1 | {% if is_paginated %} 2 | {% load i18n %} 3 | 4 | 30 | -------------------------------------------------------------------------------- /src/django_pagination_bootstrap/templatetags/__init__.py: -------------------------------------------------------------------------------- 1 | """django-pagination-bootstrap templatetags.""" 2 | -------------------------------------------------------------------------------- /src/django_pagination_bootstrap/templatetags/pagination_tags.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """django-bootstrap-pagination tags.""" 3 | from django import template 4 | from django.conf import settings 5 | from django.core.paginator import EmptyPage 6 | from django.core.paginator import InvalidPage 7 | from django.core.paginator import Paginator 8 | from django.http import Http404 9 | 10 | register = template.Library() 11 | 12 | DEFAULT_PAGINATION = getattr(settings, "PAGINATION_DEFAULT_PAGINATION", 20) 13 | DEFAULT_WINDOW = getattr(settings, "PAGINATION_DEFAULT_WINDOW", 4) 14 | DEFAULT_ORPHANS = getattr(settings, "PAGINATION_DEFAULT_ORPHANS", 0) 15 | INVALID_PAGE_RAISES_404 = getattr(settings, "PAGINATION_INVALID_PAGE_RAISES_404", False) 16 | INVALID_PAGE_FIXUP = getattr(settings, "PAGINATION_INVALID_PAGE_FIXUP", False) 17 | 18 | 19 | @register.tag(name="autopaginate") 20 | def do_autopaginate(parser, token): 21 | """Split the arguments to the autopaginate tag and formats them correctly.""" 22 | split = token.split_contents() 23 | as_index = None 24 | context_var = None 25 | for i, bit in enumerate(split): 26 | if bit == "as": 27 | as_index = i 28 | break 29 | if as_index is not None: 30 | try: 31 | context_var = split[as_index + 1] 32 | except IndexError: 33 | raise template.TemplateSyntaxError( 34 | ( 35 | f"Context variable assignment must take the form of {{% " 36 | f"{split[0]} object.example_set.all ... as context_var_name %}}" 37 | ) 38 | ) from None 39 | del split[as_index : as_index + 2] 40 | if len(split) == 2: 41 | return AutoPaginateNode(split[1]) 42 | if len(split) == 3: 43 | return AutoPaginateNode(split[1], paginate_by=split[2], context_var=context_var) 44 | if len(split) == 4: 45 | try: 46 | orphans = int(split[3]) 47 | except ValueError: 48 | raise template.TemplateSyntaxError( 49 | f"Got {split[3]}, but expected integer." 50 | ) from None 51 | return AutoPaginateNode( 52 | split[1], paginate_by=split[2], orphans=orphans, context_var=context_var 53 | ) 54 | raise template.TemplateSyntaxError( 55 | f"{split[0]} tag takes one required argument and one optional argument" 56 | ) 57 | 58 | 59 | class AutoPaginateNode(template.Node): 60 | """Emit the required objects to allow for Digg-style pagination. 61 | 62 | First, it looks in the current context for the variable specified, and using 63 | that object, it emits a simple ``Paginator`` and the current page object 64 | into the context names ``paginator`` and ``page_obj``, respectively. 65 | 66 | It will then replace the variable specified with only the objects for the 67 | current page. 68 | 69 | .. note:: 70 | 71 | It is recommended to use *{% paginate %}* after using the autopaginate 72 | tag. If you choose not to use *{% paginate %}*, make sure to display the 73 | list of available pages, or else the application may seem to be buggy. 74 | """ 75 | 76 | def __init__( 77 | self, 78 | queryset_var, 79 | paginate_by=DEFAULT_PAGINATION, 80 | orphans=DEFAULT_ORPHANS, 81 | context_var=None, 82 | ): 83 | """Constructor.""" 84 | self.queryset_var = template.Variable(queryset_var) 85 | if isinstance(paginate_by, int): 86 | self.paginate_by = paginate_by 87 | else: 88 | self.paginate_by = template.Variable(paginate_by) 89 | self.orphans = orphans 90 | self.context_var = context_var 91 | 92 | def render(self, context): 93 | key = self.queryset_var.var 94 | value = self.queryset_var.resolve(context) 95 | page_obj = None 96 | 97 | if isinstance(self.paginate_by, int): 98 | paginate_by = self.paginate_by 99 | else: 100 | paginate_by = self.paginate_by.resolve(context) 101 | paginator = Paginator(value, paginate_by, self.orphans) 102 | 103 | try: 104 | page_obj = paginator.page(context["request"].page) 105 | except InvalidPage as e: 106 | if INVALID_PAGE_RAISES_404: 107 | raise Http404( 108 | "Invalid page requested. If DEBUG were set to " 109 | + "False, an HTTP 404 page would have been shown instead." 110 | ) from None 111 | 112 | if INVALID_PAGE_FIXUP: 113 | # if we're fixing up an invalid page in the request then we 114 | # assume that we'll goto the first page 115 | default_page = 1 116 | 117 | if type(e) == EmptyPage: 118 | # page requested is out of range, as long as the page 119 | # requested is greater than 1 then we show the last page 120 | # otherwise just use the default (first page) 121 | if int(context["request"].page) > 1: 122 | # deliver the last page 123 | default_page = paginator.num_pages 124 | 125 | # get our page_obj 126 | page_obj = paginator.page(default_page) 127 | 128 | # if we don't have a page_obj from the Paginator then bail out 129 | if page_obj is None: 130 | context[key] = [] 131 | context["invalid_page"] = True 132 | return "" 133 | 134 | if self.context_var is not None: 135 | context[self.context_var] = page_obj.object_list 136 | else: 137 | context[key] = page_obj.object_list 138 | 139 | context["paginator"] = paginator 140 | context["page_obj"] = page_obj 141 | return "" 142 | 143 | 144 | def paginate(context, window=DEFAULT_WINDOW, hashtag=""): 145 | """Render the pagination.html template. 146 | 147 | Args: 148 | context: 149 | Dictionary-like data structure and must contain the following keys 150 | paginator - a ``Paginator`` or ``QuerySetPaginator`` object. 151 | page_obj - the result of calling the page method on the 152 | aforementioned ``Paginator`` or ``QuerySetPaginator`` object, given 153 | the current page. 154 | getvars (optional) - a dictionary of all of the **GET** parameters in the current request. 155 | This is useful to maintain certain types of state, even when requesting 156 | a different page. 157 | window: 158 | Optional. Defaults to DEFAULT_WINDOW. 159 | hashtag: 160 | Optional. Defaults to "". 161 | 162 | Returns: 163 | A Digg-like display of the available pages, given the current page. 164 | If there are too many pages to be displayed before and after the current page, then 165 | elipses will be used to indicate the undisplayed gap between page numbers. 166 | """ 167 | try: 168 | paginator = context["paginator"] 169 | page_obj = context["page_obj"] 170 | page_range = list(paginator.page_range) 171 | # Calculate the record range in the current page for display. 172 | records = {"first": 1 + (page_obj.number - 1) * paginator.per_page} 173 | records["last"] = records["first"] + paginator.per_page - 1 174 | if records["last"] + paginator.orphans >= paginator.count: 175 | records["last"] = paginator.count 176 | # First and last are simply the first *n* pages and the last *n* pages, 177 | # where *n* is the current window size. 178 | first = set(page_range[:window]) 179 | last = set(page_range[-window:]) 180 | # Now we look around our current page, making sure that we don't wrap 181 | # around. 182 | current_start = page_obj.number - 1 - window 183 | if current_start < 0: 184 | current_start = 0 185 | current_end = page_obj.number - 1 + window 186 | if current_end < 0: 187 | current_end = 0 188 | current = set(page_range[current_start:current_end]) 189 | pages = [] 190 | # If there's no overlap between the first set of pages and the current 191 | # set of pages, then there's a possible need for elusion. 192 | if len(first.intersection(current)) == 0: 193 | first_list = list(first) 194 | first_list.sort() 195 | second_list = list(current) 196 | second_list.sort() 197 | pages.extend(first_list) 198 | diff = second_list[0] - first_list[-1] 199 | # If there is a gap of two, between the last page of the first 200 | # set and the first page of the current set, then we're missing a 201 | # page. 202 | if diff == 2: 203 | pages.append(second_list[0] - 1) 204 | # If the difference is just one, then there's nothing to be done, 205 | # as the pages need no elusion and are correct. 206 | elif diff == 1: 207 | pass 208 | # Otherwise, there's a bigger gap which needs to be signaled for 209 | # elusion, by pushing a None value to the page list. 210 | else: 211 | pages.append(None) 212 | pages.extend(second_list) 213 | else: 214 | unioned = list(first.union(current)) 215 | unioned.sort() 216 | pages.extend(unioned) 217 | # If there's no overlap between the current set of pages and the last 218 | # set of pages, then there's a possible need for elusion. 219 | if len(current.intersection(last)) == 0: 220 | second_list = list(last) 221 | second_list.sort() 222 | diff = second_list[0] - pages[-1] 223 | # If there is a gap of two, between the last page of the current 224 | # set and the first page of the last set, then we're missing a 225 | # page. 226 | if diff == 2: 227 | pages.append(second_list[0] - 1) 228 | # If the difference is just one, then there's nothing to be done, 229 | # as the pages need no elusion and are correct. 230 | elif diff == 1: 231 | pass 232 | # Otherwise, there's a bigger gap which needs to be signaled for 233 | # elusion, by pushing a None value to the page list. 234 | else: 235 | pages.append(None) 236 | pages.extend(second_list) 237 | else: 238 | differenced = list(last.difference(current)) 239 | differenced.sort() 240 | pages.extend(differenced) 241 | to_return = { 242 | "MEDIA_URL": settings.MEDIA_URL, 243 | "pages": pages, 244 | "records": records, 245 | "page_obj": page_obj, 246 | "paginator": paginator, 247 | "hashtag": hashtag, 248 | "is_paginated": paginator.count > paginator.per_page, 249 | } 250 | if "request" in context: 251 | getvars = context["request"].GET.copy() 252 | if "page" in getvars: 253 | del getvars["page"] 254 | if len(list(getvars.keys())) > 0: 255 | to_return["getvars"] = "&%s" % getvars.urlencode() 256 | else: 257 | to_return["getvars"] = "" 258 | return to_return 259 | except KeyError: 260 | return {} 261 | 262 | 263 | # registers the tag paginate 264 | register.inclusion_tag("pagination.html", takes_context=True, name="paginate")(paginate) 265 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | """Test suite for the django-pagination-bootstrap package.""" 2 | -------------------------------------------------------------------------------- /tests/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main() -> None: 8 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "tests.settings") 9 | try: 10 | from django.core.management import execute_from_command_line 11 | except ImportError as exc: 12 | raise ImportError( 13 | "Couldn't import Django. Are you sure it's installed and " 14 | "available on your PYTHONPATH environment variable? Did you " 15 | "forget to activate a virtual environment?" 16 | ) from exc 17 | execute_from_command_line(sys.argv) 18 | 19 | 20 | if __name__ == "__main__": 21 | main() 22 | -------------------------------------------------------------------------------- /tests/settings.py: -------------------------------------------------------------------------------- 1 | """Django test settings.""" 2 | import os 3 | 4 | SECRET_KEY = "$%ffv#zpca%a#bdxtl&)&=5k20egnwcwjdg665r-lsr+s5zdw#" # nosec 5 | 6 | APP_NAME = "django_pagination_bootstrap" 7 | 8 | DEBUG = True 9 | 10 | DATABASES = {"default": {"ENGINE": "django.db.backends.sqlite3"}} 11 | 12 | USE_TZ = True 13 | 14 | MIDDLEWARE = ["pagination_bootstrap.middleware.PaginationMiddleware"] 15 | 16 | SITE_ID = 1 17 | 18 | INSTALLED_APPS = ( 19 | "django.contrib.auth", 20 | "django.contrib.contenttypes", 21 | "django.contrib.sessions", 22 | "django.contrib.sites", 23 | APP_NAME, 24 | ) 25 | 26 | TEMPLATES = [ 27 | { 28 | "BACKEND": "django.template.backends.django.DjangoTemplates", 29 | "DIRS": [os.path.join(os.path.dirname(__file__), "templates")], 30 | "OPTIONS": { 31 | "context_processors": [ 32 | "django.template.context_processors.debug", 33 | "django.template.context_processors.request", 34 | "django.contrib.auth.context_processors.auth", 35 | "django.template.context_processors.i18n", 36 | "django.template.context_processors.media", 37 | ], 38 | "loaders": [ 39 | "django.template.loaders.filesystem.Loader", 40 | "django.template.loaders.app_directories.Loader", 41 | ], 42 | }, 43 | } 44 | ] 45 | -------------------------------------------------------------------------------- /tests/test_http_request.py: -------------------------------------------------------------------------------- 1 | from django.http import HttpRequest 2 | 3 | 4 | class TestHttpRequest(HttpRequest): 5 | """Test helper class.""" 6 | 7 | __test__ = False 8 | page = 1 9 | -------------------------------------------------------------------------------- /tests/test_middleware.py: -------------------------------------------------------------------------------- 1 | from django.http import HttpRequest 2 | 3 | from .test_http_request import TestHttpRequest 4 | from django_pagination_bootstrap import middleware 5 | 6 | 7 | def test_get_page() -> None: 8 | obj = TestHttpRequest() 9 | assert middleware.get_page(obj) == 1 10 | 11 | 12 | def test_get_page_invalid() -> None: 13 | assert middleware.get_page(HttpRequest()) == 1 14 | 15 | 16 | def test_init() -> None: 17 | pagination_middleware = middleware.PaginationMiddleware("response") 18 | assert (pagination_middleware.get_response) == "response" 19 | 20 | 21 | # TODO redo testing after closed issue https://github.com/staticdev/django-pagination-bootstrap/issues/246 22 | # def test_pagination_middleware(mocker: MockFixture) -> None: 23 | # request = mocker.Mock() 24 | # request.__class__.return_value = 5 25 | # pagination_middleware = middleware.PaginationMiddleware(mocker.Mock()) 26 | # # CALL MIDDLEWARE ON REQUEST HERE 27 | # pagination_middleware(request) 28 | # assert request.__class__.page == 5 29 | -------------------------------------------------------------------------------- /tests/test_pagination_tags.py: -------------------------------------------------------------------------------- 1 | from django import template 2 | from django.test import TestCase 3 | 4 | from .test_http_request import TestHttpRequest 5 | 6 | 7 | class TestTemplatePaginateTags(TestCase): 8 | def test_render_range_by_two(self) -> None: 9 | t = template.Template( 10 | "{% load pagination_tags %}{% autopaginate var 2 %}{% paginate %}" 11 | ) 12 | c = template.Context({"var": range(21), "request": TestHttpRequest()}) 13 | self.assertEqual( 14 | t.render(c), 15 | '\n\n\n\n', 16 | ) 17 | 18 | def test_render_range_by_two_one_orphan(self) -> None: 19 | t = template.Template( 20 | "{% load pagination_tags %}{% autopaginate var 2 1 %}{% paginate %}" 21 | ) 22 | c = template.Context({"var": range(20), "request": TestHttpRequest()}) 23 | self.assertEqual( 24 | t.render(c), 25 | '\n\n\n\n', 26 | ) 27 | 28 | def test_render_range_by_one(self) -> None: 29 | t = template.Template( 30 | "{% load pagination_tags %}{% autopaginate var %}{% paginate %}" 31 | ) 32 | c = template.Context({"var": range(21), "request": TestHttpRequest()}) 33 | self.assertEqual( 34 | t.render(c), 35 | '\n\n\n\n', 36 | ) 37 | 38 | def test_render_range_by_twenty(self) -> None: 39 | t = template.Template( 40 | "{% load pagination_tags %}{% autopaginate var 20 %}{% paginate %}" 41 | ) 42 | c = template.Context({"var": range(21), "request": TestHttpRequest()}) 43 | self.assertEqual( 44 | t.render(c), 45 | '\n\n\n\n', 46 | ) 47 | 48 | def test_render_range_by_var(self) -> None: 49 | t = template.Template( 50 | "{% load pagination_tags %}{% autopaginate var by %}{% paginate %}" 51 | ) 52 | c = template.Context({"var": range(21), "by": 20, "request": TestHttpRequest()}) 53 | self.assertEqual( 54 | t.render(c), 55 | '\n\n\n\n', 56 | ) 57 | 58 | def test_render_range_by_var_as_name(self) -> None: 59 | t = template.Template( 60 | "{% load pagination_tags %}{% autopaginate var by as foo %}{{ foo }}" 61 | ) 62 | c = template.Context( 63 | {"var": list(range(21)), "by": 20, "request": TestHttpRequest()} 64 | ) 65 | self.assertEqual( 66 | t.render(c), 67 | "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]", 68 | ) 69 | 70 | def test_render_invalid_arguments(self) -> None: 71 | with self.assertRaisesMessage( 72 | template.TemplateSyntaxError, 73 | "autopaginate tag takes one required argument and one optional argument", 74 | ): 75 | template.Template("{% load pagination_tags %}{% autopaginate %}") 76 | 77 | def test_render_invalid_orphans(self) -> None: 78 | with self.assertRaisesMessage( 79 | template.TemplateSyntaxError, "Got a, but expected integer." 80 | ): 81 | template.Template("{% load pagination_tags %}{% autopaginate var 2 a %}") 82 | 83 | def test_render_range_by_var_as_index_error(self) -> None: 84 | with self.assertRaisesMessage( 85 | template.TemplateSyntaxError, 86 | "Context variable assignment must take the form of {% autopaginate object.example_set.all ... as context_var_name %}", 87 | ): 88 | template.Template("{% load pagination_tags %}{% autopaginate var by as %}") 89 | -------------------------------------------------------------------------------- /tests/test_paginator.py: -------------------------------------------------------------------------------- 1 | from django.core.paginator import Paginator 2 | from django.test import TestCase 3 | 4 | from django_pagination_bootstrap import paginator 5 | from django_pagination_bootstrap.templatetags import pagination_tags 6 | 7 | 8 | class TestPaginator(TestCase): 9 | def test_page_obj_one(self) -> None: 10 | p = Paginator(range(15), 2) 11 | pg = pagination_tags.paginate({"paginator": p, "page_obj": p.page(1)}) 12 | self.assertEqual(pg["pages"], [1, 2, 3, 4, 5, 6, 7, 8]) 13 | self.assertEqual(pg["records"]["first"], 1) 14 | self.assertEqual(pg["records"]["last"], 2) 15 | 16 | def test_page_obj_eight(self) -> None: 17 | p = Paginator(range(15), 2) 18 | pg = pagination_tags.paginate({"paginator": p, "page_obj": p.page(8)}) 19 | self.assertEqual(pg["pages"], [1, 2, 3, 4, 5, 6, 7, 8]) 20 | self.assertEqual(pg["records"]["first"], 15) 21 | self.assertEqual(pg["records"]["last"], 15) 22 | 23 | def test_pages_list(self) -> None: 24 | p = Paginator(range(17), 2) 25 | pages = pagination_tags.paginate({"paginator": p, "page_obj": p.page(1)})[ 26 | "pages" 27 | ] 28 | self.assertEqual(pages, [1, 2, 3, 4, 5, 6, 7, 8, 9]) 29 | 30 | def test_truncated_pages_list(self) -> None: 31 | p = Paginator(range(19), 2) 32 | pages = pagination_tags.paginate({"paginator": p, "page_obj": p.page(1)})[ 33 | "pages" 34 | ] 35 | self.assertEqual(pages, [1, 2, 3, 4, None, 7, 8, 9, 10]) 36 | 37 | def test_longer_truncated_pages_list(self) -> None: 38 | p = Paginator(range(21), 2) 39 | pages = pagination_tags.paginate({"paginator": p, "page_obj": p.page(1)})[ 40 | "pages" 41 | ] 42 | self.assertEqual(pages, [1, 2, 3, 4, None, 8, 9, 10, 11]) 43 | 44 | def test_orphaned_page_list(self) -> None: 45 | p = Paginator(range(5), 2, 1) 46 | pages = pagination_tags.paginate({"paginator": p, "page_obj": p.page(1)})[ 47 | "pages" 48 | ] 49 | self.assertEqual(pages, [1, 2]) 50 | 51 | def test_orphaned_page_obj_one(self) -> None: 52 | p = Paginator(range(5), 2, 1) 53 | pg = pagination_tags.paginate({"paginator": p, "page_obj": p.page(1)}) 54 | self.assertTrue(pg["pages"], [1, 2, 3, 4, None, 7, 8, 9, 10]) 55 | self.assertTrue(pg["records"]["first"], 1) 56 | self.assertTrue(pg["records"]["last"], 2) 57 | 58 | def test_orphaned_page_obj_ten(self) -> None: 59 | p = Paginator(range(21), 2, 1) 60 | pg = pagination_tags.paginate({"paginator": p, "page_obj": p.page(10)}) 61 | self.assertTrue(pg["pages"], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) 62 | self.assertTrue(pg["records"]["first"], 19) 63 | self.assertTrue(pg["records"]["last"], 21) 64 | 65 | 66 | class TestInfinitePaginator(TestCase): 67 | def test_paginate_range_by_two(self) -> None: 68 | pg = paginator.InfinitePaginator(range(20), 2, link_template="/bacon/page/%d") 69 | self.assertEqual(pg.validate_number(2), 2) 70 | self.assertEqual(pg.orphans, 0) 71 | p3 = pg.page(3) 72 | self.assertIsInstance(p3, paginator.InfinitePage) 73 | self.assertEqual(p3.end_index(), 6) 74 | self.assertTrue(p3.has_next()) 75 | self.assertTrue(p3.has_previous()) 76 | self.assertEqual(p3.next_link(), "/bacon/page/4") 77 | self.assertEqual(p3.previous_link(), "/bacon/page/2") 78 | p10 = pg.page(10) 79 | self.assertFalse(p10.has_next()) 80 | self.assertTrue(p10.has_previous()) 81 | 82 | 83 | class TestFinitePaginator(TestCase): 84 | def test_paginate_range_by_two_offset_ten(self) -> None: 85 | pg = paginator.FinitePaginator( 86 | range(20), 2, offset=10, link_template="/bacon/page/%d" 87 | ) 88 | self.assertEqual(pg.validate_number(2), 2) 89 | self.assertEqual(pg.orphans, 0) 90 | p3 = pg.page(3) 91 | self.assertIsInstance(p3, paginator.FinitePage) 92 | self.assertEqual(p3.start_index(), 10) 93 | self.assertEqual(p3.end_index(), 6) 94 | self.assertTrue(p3.has_next()) 95 | self.assertTrue(p3.has_previous()) 96 | self.assertEqual(p3.next_link(), "/bacon/page/4") 97 | self.assertEqual(p3.previous_link(), "/bacon/page/2") 98 | 99 | def test_paginate_range_by_twenty_offset_ten(self) -> None: 100 | pg = paginator.FinitePaginator( 101 | range(20), 20, offset=10, link_template="/bacon/page/%d" 102 | ) 103 | self.assertEqual(pg.validate_number(2), 2) 104 | self.assertEqual(pg.orphans, 0) 105 | p2 = pg.page(2) 106 | self.assertIsInstance(p2, paginator.FinitePage) 107 | self.assertEqual(p2.start_index(), 10) 108 | self.assertEqual(p2.end_index(), 40) 109 | self.assertFalse(p2.has_next()) 110 | self.assertTrue(p2.has_previous()) 111 | self.assertIsNone(p2.next_link()) 112 | self.assertEqual(p2.previous_link(), "/bacon/page/1") 113 | --------------------------------------------------------------------------------