diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml
index 799c989..e012e48 100644
--- a/.github/workflows/coverage.yml
+++ b/.github/workflows/coverage.yml
@@ -13,79 +13,30 @@ on: # Runs on all push events to master branch and any push related to a pull r
jobs:
coverage:
- name: ${{ matrix.os }} / ${{ matrix.python-version }}
- runs-on: ${{ matrix.os }}
- strategy:
- matrix: # only lowest supported python on ubuntu-latest
- os: [ubuntu-latest]
- python-version: [3.7]
+ runs-on: ubuntu-latest
+ env:
+ python-version: 3.12
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v4
- - name: Set up Python ${{ matrix.python-version }}
- uses: actions/setup-python@v2
+ - name: Set up Python ${{ env.python-version }}
+ uses: actions/setup-python@v5
with:
- python-version: ${{ matrix.python-version }}
+ python-version: ${{ env.python-version }}
+ cache: 'pip'
+ cache-dependency-path: '**/pyproject.toml'
- - name: Get full Python version
- id: full-python-version
- run: echo ::set-output name=version::$(python -c "import sys; print('-'.join(str(v) for v in sys.version_info))")
+ - name: Upgrade pip
+ run: python -m pip install --upgrade pip
- - name: Install poetry
- uses: abatilo/actions-poetry@v2.1.0
- with:
- poetry-version: 1.1.4
-
- - name: Configure Poetry
- run: |
- echo "PATH=$HOME/.poetry/bin:$PATH" >> $GITHUB_ENV
- poetry config virtualenvs.in-project true
-
- - name: Set up cache
- uses: actions/cache@v2
- id: cache
- with:
- path: .venv
- key: venv-${{ runner.os }}-${{ steps.full-python-version.outputs.version }}-${{ hashFiles('**/poetry.lock') }}
-
- - name: Ensure cache is healthy
- if: steps.cache.outputs.cache-hit == 'true'
- run: poetry run pip --version >/dev/null 2>&1 || rm -rf .venv
-
- - name: Upgrade pip, setuptools and wheel
- run: poetry run python -m pip install --upgrade pip setuptools wheel
-
- - name: Install dependencies
- run: poetry install -v -E dataframe
-
- - name: Set up env for CodeClimate (push)
- run: |
- echo "GIT_BRANCH=$GITHUB_REF" >> $GITHUB_ENV
- echo "GIT_COMMIT_SHA=$GITHUB_SHA" >> $GITHUB_ENV
- if: github.event_name == 'push'
-
- - name: Set up env for CodeClimate (pull_request)
- env:
- PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
- run: |
- echo "GIT_BRANCH=$GITHUB_HEAD_REF" >> $GITHUB_ENV
- echo "GIT_COMMIT_SHA=$PR_HEAD_SHA" >> $GITHUB_ENV
- if: github.event_name == 'pull_request'
-
- - name: Prepare CodeClimate binary
- env:
- CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
- run: |
- curl -LSs 'https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64' >./cc-test-reporter;
- chmod +x ./cc-test-reporter
- ./cc-test-reporter before-build
+ - name: Install package with test dependencies
+ run: python -m pip install ".[test]"
- name: Run all tests
- run: poetry run python -m pytest
+ run: python -m pytest
- - name: Push Coverage to CodeClimate
- if: ${{ success() }} # only if tests were successful
- env:
- CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
- run: ./cc-test-reporter after-build
+ - name: Upload Coverage to Codecov
+ uses: codecov/codecov-action@v4
+ with:
+ token: ${{ secrets.CODECOV_TOKEN }}
diff --git a/.github/workflows/cron.yml b/.github/workflows/cron.yml
index c1733da..9ee2157 100644
--- a/.github/workflows/cron.yml
+++ b/.github/workflows/cron.yml
@@ -15,47 +15,25 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
- os: [ubuntu-18.04, ubuntu-20.04, macos-latest, windows-latest]
- python-version: [3.7, 3.8, 3.9] # crons should always run latest python hence 3.x
+ os: [ubuntu-22.04, ubuntu-24.04, macos-latest, windows-latest]
+ python-version: ["3.10", 3.11, 3.12, 3.x] # crons should always run latest python hence 3.x
+ fail-fast: false
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
- uses: actions/setup-python@v2
+ uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
+ cache: 'pip'
+ cache-dependency-path: '**/pyproject.toml'
- - name: Get full Python version
- id: full-python-version
- run: echo ::set-output name=version::$(python -c "import sys; print('-'.join(str(v) for v in sys.version_info))")
+ - name: Upgrade pip
+ run: python -m pip install --upgrade pip
- - name: Set up cache
- uses: actions/cache@v2
- id: cache
- with:
- path: .venv
- key: venv-${{ runner.os }}-${{ steps.full-python-version.outputs.version }}-${{ hashFiles('**/poetry.lock') }}
-
- - name: Ensure cache is healthy
- if: steps.cache.outputs.cache-hit == 'true'
- run: pip --version >/dev/null 2>&1 || rm -rf .venv
-
- - name: Install poetry
- uses: abatilo/actions-poetry@v2.1.0
- with:
- poetry-version: 1.1.4
-
- - name: Configure Poetry
- run: |
- echo "PATH=$HOME/.poetry/bin:$PATH" >> $GITHUB_ENV
- poetry config virtualenvs.in-project true
-
- - name: Upgrade pip, setuptools and wheel
- run: poetry run python -m pip install --upgrade pip setuptools wheel
-
- - name: Install dependencies
- run: poetry install -v -E dataframe
+ - name: Install package with test dependencies
+ run: python -m pip install ".[test]"
- name: Run Tests
- run: poetry run python -m pytest
+ run: python -m pytest
diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml
index 52f5976..5c4aff7 100644
--- a/.github/workflows/documentation.yml
+++ b/.github/workflows/documentation.yml
@@ -12,59 +12,43 @@ on: # Runs on any push event to master
jobs:
documentation:
- name: ${{ matrix.os }} / ${{ matrix.python-version }}
- runs-on: ${{ matrix.os }}
- strategy:
- matrix: # only lowest supported python on ubuntu-latest
- os: [ubuntu-latest]
- python-version: [3.7]
+ runs-on: ubuntu-latest
+ env:
+ python-version: 3.12
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v4
- - name: Set up Python ${{ matrix.python-version }}
- uses: actions/setup-python@v2
+ - name: Set up Python ${{ env.python-version }}
+ uses: actions/setup-python@v5
with:
- python-version: ${{ matrix.python-version }}
+ python-version: ${{ env.python-version }}
+ cache: 'pip'
+ cache-dependency-path: '**/pyproject.toml'
- - name: Get full Python version
- id: full-python-version
- run: echo ::set-output name=version::$(python -c "import sys; print('-'.join(str(v) for v in sys.version_info))")
+ - name: Upgrade pip
+ run: python -m pip install --upgrade pip
- - name: Install poetry
- uses: abatilo/actions-poetry@v2.1.0
- with:
- poetry-version: 1.1.4
+ - name: Install package with doc dependencies
+ run: python -m pip install ".[docs]"
- - name: Configure Poetry
- run: |
- echo "PATH=$HOME/.poetry/bin:$PATH" >> $GITHUB_ENV
- poetry config virtualenvs.in-project true
+ - name: Build documentation
+ run: portray as_html -o doc_build
- - name: Set up cache
- uses: actions/cache@v2
- id: cache
+ # Upload artifacts if in PR so reviewers can have a quick look without building documentation from the branch locally
+ - name: Upload build artifacts
+ uses: actions/upload-artifact@v4
+ if: success() && github.event_name == 'pull_request' # only for pushes in PR
with:
- path: .venv
- key: venv-${{ runner.os }}-${{ steps.full-python-version.outputs.version }}-${{ hashFiles('**/poetry.lock') }}
-
- - name: Ensure cache is healthy
- if: steps.cache.outputs.cache-hit == 'true'
- run: pip --version >/dev/null 2>&1 || rm -rf .venv
-
- - name: Upgrade pip, setuptools and wheel
- run: poetry run python -m pip install --upgrade pip setuptools wheel
-
- - name: Install dependencies
- run: poetry install -v -E docs -E dataframe
-
- - name: Build documentation
- run: poetry run portray as_html -o doc_build
+ name: site-build
+ path: doc_build
+ retention-days: 5
+ # Upload the doc to github pages branch and publish if from a push to master
- name: Upload documentation to gh-pages
- if: ${{ success() }}
- uses: JamesIves/github-pages-deploy-action@3.7.1
+ if: success() && github.ref == 'refs/heads/master' # only for pushes to master
+ uses: JamesIves/github-pages-deploy-action@v4
with:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- BRANCH: gh-pages
- FOLDER: doc_build
+ token: ${{ secrets.GITHUB_TOKEN }}
+ branch: gh-pages
+ folder: doc_build
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index fe9990a..f7152c5 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -11,56 +11,22 @@ on: # Runs everytime a release is added to the repository
jobs:
deploy:
- name: ${{ matrix.os }} / ${{ matrix.python-version }}
- runs-on: ${{ matrix.os }}
- strategy:
- matrix: # only lowest supported python on ubuntu-latest
- os: [ubuntu-latest]
- python-version: [3.7]
-
+ runs-on: ubuntu-latest
+ env:
+ python-version: 3.12
steps:
- - uses: actions/checkout@v2
-
- - name: Set up Python ${{ matrix.python-version }}
- uses: actions/setup-python@v2
- with:
- python-version: ${{ matrix.python-version }}
-
- - name: Get full Python version
- id: full-python-version
- run: echo ::set-output name=version::$(python -c "import sys; print('-'.join(str(v) for v in sys.version_info))")
-
- - name: Install poetry
- uses: abatilo/actions-poetry@v2.1.0
- with:
- poetry-version: 1.1.4
+ - uses: actions/checkout@v4
- - name: Configure Poetry
- run: |
- echo "PATH=$HOME/.poetry/bin:$PATH" >> $GITHUB_ENV
- poetry config virtualenvs.in-project true
-
- - name: Set up cache
- uses: actions/cache@v2
- id: cache
- with:
- path: .venv
- key: venv-${{ runner.os }}-${{ steps.full-python-version.outputs.version }}-${{ hashFiles('**/poetry.lock') }}
-
- - name: Ensure cache is healthy
- if: steps.cache.outputs.cache-hit == 'true'
- run: pip --version >/dev/null 2>&1 || rm -rf .venv
-
- - name: Upgrade pip, setuptools and wheel
- run: poetry run python -m pip install --upgrade pip setuptools wheel
+ - name: Install hatch
+ run: pipx install hatch
- name: Build wheels and sdist
- run: poetry build
+ run: hatch build
- - name: Publish package to PyPI
- if: ${{ success() }}
- env:
- PYPI_USERNAME: ${{ secrets.PYPI_USERNAME }}
- PYPI_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
- run: poetry publish -u "$PYPI_USERNAME" -p "$PYPI_PASSWORD"
+ - name: Publish Python distribution to PyPI
+ uses: pypa/gh-action-pypi-publish@release/v1
+ with:
+ verbose: true
+ print-hash: true
+ password: ${{ secrets.PYPI_API_TOKEN }}
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 905e156..5e358bd 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -1,10 +1,15 @@
-name: Tests
+# Runs all tests
+name: All Tests
defaults:
run:
shell: bash
-on: [push]
+on: # Runs on any push event to any branch except master (the coverage workflow takes care of that)
+ push:
+ branches-ignore:
+ - 'master'
+
jobs:
tests:
@@ -12,47 +17,25 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
- os: [ubuntu-18.04, ubuntu-20.04, macos-latest, windows-latest]
- python-version: [3.7, 3.8, 3.9]
+ os: [ubuntu-22.04, ubuntu-24.04, macos-latest, windows-latest]
+ python-version: ["3.10", 3.11, 3.12]
+ fail-fast: false
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
- uses: actions/setup-python@v2
+ uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
+ cache: 'pip'
+ cache-dependency-path: '**/pyproject.toml'
- - name: Get full Python version
- id: full-python-version
- run: echo ::set-output name=version::$(python -c "import sys; print('-'.join(str(v) for v in sys.version_info))")
-
- - name: Install poetry
- uses: abatilo/actions-poetry@v2.1.0
- with:
- poetry-version: 1.1.4
-
- - name: Configure Poetry
- run: |
- echo "PATH=$HOME/.poetry/bin:$PATH" >> $GITHUB_ENV
- poetry config virtualenvs.in-project true
-
- - name: Set up cache
- uses: actions/cache@v2
- id: cache
- with:
- path: .venv
- key: venv-${{ runner.os }}-${{ steps.full-python-version.outputs.version }}-${{ hashFiles('**/poetry.lock') }}
-
- - name: Ensure cache is healthy
- if: steps.cache.outputs.cache-hit == 'true'
- run: poetry run pip --version >/dev/null 2>&1 || rm -rf .venv
-
- - name: Upgrade pip, setuptools and wheel
- run: poetry run python -m pip install --upgrade pip setuptools wheel
+ - name: Upgrade pip
+ run: python -m pip install --upgrade pip
- - name: Install dependencies
- run: poetry install -v -E dataframe
+ - name: Install package with test dependencies
+ run: python -m pip install ".[test]"
- - name: Run Tests
- run: poetry run python -m pytest
+ - name: Run all tests
+ run: python -m pytest
diff --git a/Makefile b/Makefile
index b07df07..bcc40d3 100644
--- a/Makefile
+++ b/Makefile
@@ -19,13 +19,18 @@ all: install
help:
@echo "Please use 'make $(R)$(E)' where $(R)$(E) is one of:"
- @echo " $(R) clean $(E) \t to recursively remove build, run, and bitecode files/dirs."
- @echo " $(R) format $(E) \t to recursively apply PEP8 formatting through the $(P)Black$(E) cli tool."
- @echo " $(R) install $(E) \t to $(D)poetry install$(E) this package into the project's virtual environment."
- @echo " $(R) lines $(E) \t to count lines of code with the $(P)tokei$(E) tool."
- @echo " $(R) lint $(E) \t to lint the code though $(P)Pylint$(E)."
+ @echo " $(R) build $(E) \t to build wheel and source distribution with $(P)Hatch$(E)."
+ @echo " $(R) clean $(E) \t to recursively remove build, run and bitecode files/dirs."
+ @echo " $(R) format $(E) \t to check and format code with $(P)Ruff$(E) through $(P)Hatch$(E)."
+ @echo " $(R) install $(E) \t to $(C)pip install$(E) this package into the current environment."
+ @echo " $(R) lint $(E) \t to lint-check the code with $(P)Ruff$(E)."
@echo " $(R) tests $(E) \t to run tests with the $(P)pytest$(E) package."
- @echo " $(R) type $(E) \t to run type checking with the $(P)mypy$(E) package."
+
+build:
+ @echo "Re-building wheel and dist"
+ @rm -rf dist
+ @hatch build --clean
+ @echo "Created build is located in the $(C)dist$(E) folder."
clean:
@echo "Cleaning up distutils remains."
@@ -44,30 +49,24 @@ clean:
@echo "All cleaned up!\n"
format:
- @echo "Sorting imports and formatting code to PEP8, default line length is 100 characters."
- @poetry run isort . && black .
+ @echo "Checking code with Ruff through Hatch."
+ @hatch fmt
install: format clean
- @echo "Installing through $(D)poetry$(E), with dev dependencies but no extras."
- @poetry install
-
-lines: format
- @tokei .
+ @echo "Installing (editable) with $(D)pip$(E) in the current environment."
+ @python -m pip install --editable . -v
-lint: format
- @echo "Linting code"
- @poetry run pylint aoe2netwrapper/
-
-tests: format clean
- @poetry run pytest
- @make clean
+lint:
+ @echo "Checking code with Ruff through Hatch."
+ @hatch fmt
-type: format
- @echo "Checking code typing with mypy"
- @poetry run mypy --pretty --no-strict-optional --show-error-codes --warn-redundant-casts --ignore-missing-imports --follow-imports skip aoe2netwrapper
+tests: clean
+ @python -m pytest
@make clean
# Catch-all unknow targets without returning an error. This is a POSIX-compliant syntax.
.DEFAULT:
- @echo "Make caught an invalid target! See help output below for available targets."
+ @echo "Make caught an invalid target."
+ @echo "See help output below for available targets."
+ @echo ""
@make help
diff --git a/README.md b/README.md
index d557603..733172a 100644
--- a/README.md
+++ b/README.md
@@ -16,8 +16,8 @@
-
-
+
+
@@ -26,20 +26,19 @@
-
-
-
+
+
-
-
+
+
@@ -54,7 +53,7 @@
- A simple, efficient and typed wrapper to query the https://aoe2.net APIs with Python 3.7+
+ A simple, efficient and typed Python wrapper to query the https://aoe2.net APIs
@@ -75,7 +74,5 @@ Link to [source code][package_source].
MIT © 2021 Felix Soubelet
-
-
[package_doc]: https://fsoubelet.github.io/AoE2NetAPIWrapper/
[package_source]: https://github.com/fsoubelet/AoE2NetAPIWrapper
diff --git a/aoe2netwrapper/__init__.py b/aoe2netwrapper/__init__.py
index 3df5563..0539272 100644
--- a/aoe2netwrapper/__init__.py
+++ b/aoe2netwrapper/__init__.py
@@ -9,5 +9,9 @@
:license: MIT, see LICENSE file for more details.
"""
-from .api import AoE2NetAPI
-from .nightbot import AoE2NightbotAPI
+from .api import AoE2NetAPI # noqa: TID252
+from .nightbot import AoE2NightbotAPI # noqa: TID252
+
+__version__ = "0.4.0"
+
+__all__ = ["AoE2NetAPI", "AoE2NightbotAPI"]
diff --git a/aoe2netwrapper/api.py b/aoe2netwrapper/api.py
index f012dc6..17f4b14 100644
--- a/aoe2netwrapper/api.py
+++ b/aoe2netwrapper/api.py
@@ -5,14 +5,16 @@
This module implements a high-level client to query the API at https://aoe2.net/#api.
"""
-from typing import Any, Dict, List, Tuple, Union
+from __future__ import annotations
+
+from typing import Any
import requests
from loguru import logger
-from pydantic import parse_obj_as
+from pydantic import TypeAdapter
-from aoe2netwrapper.exceptions import Aoe2NetException
+from aoe2netwrapper.exceptions import Aoe2NetError, RemovedApiEndpointError
from aoe2netwrapper.models import (
LastMatchResponse,
LeaderBoardResponse,
@@ -22,6 +24,15 @@
StringsResponse,
)
+_MAX_LEADERBOARD_COUNT: int = 10_000
+_MAX_MATCH_HISTORY_COUNT: int = 1_000
+_MAX_RATING_HISTORY_COUNT: int = 10_000
+_MAX_MATCHES_COUNT: int = 1_000
+_OK_STATUS_CODE: int = 200
+
+_LIST_MATCHLOBBY_ADAPTER = TypeAdapter(list[MatchLobby])
+_LIST_RATINGTIMEPOINT_ADAPTER = TypeAdapter(list[RatingTimePoint])
+
class AoE2NetAPI:
"""
@@ -30,24 +41,24 @@ class AoE2NetAPI:
parsing and validating the response before returning it.
"""
- API_BASE_URL: str = "https://aoe2.net/api"
- STRINGS_ENDPOINT: str = API_BASE_URL + "/strings"
- LEADERBOARD_ENDPOINT: str = API_BASE_URL + "/leaderboard"
- LOBBIES_ENDPOINT: str = API_BASE_URL + "/lobbies"
- LAST_MATCH_ENDPOINT: str = API_BASE_URL + "/player/lastmatch"
- MATCH_HISTORY_ENDPOINT: str = API_BASE_URL + "/player/matches"
- RATING_HISTORY_ENDPOINT: str = API_BASE_URL + "/player/ratinghistory"
- MATCHES_ENDPOINT: str = API_BASE_URL + "/matches"
- MATCH_ENDPOINT: str = API_BASE_URL + "/match"
- NUMBER_ONLINE_ENDPOINT: str = API_BASE_URL + "/stats/players"
-
- def __init__(self, timeout: Union[float, Tuple[float, float]] = 5):
+ _API_BASE_URL: str = "https://aoe2.net/api"
+ _STRINGS_ENDPOINT: str = _API_BASE_URL + "/strings"
+ _LEADERBOARD_ENDPOINT: str = _API_BASE_URL + "/leaderboard"
+ _LOBBIES_ENDPOINT: str = _API_BASE_URL + "/lobbies"
+ _LAST_MATCH_ENDPOINT: str = _API_BASE_URL + "/player/lastmatch"
+ _MATCH_HISTORY_ENDPOINT: str = _API_BASE_URL + "/player/matches"
+ _RATING_HISTORY_ENDPOINT: str = _API_BASE_URL + "/player/ratinghistory"
+ _MATCHES_ENDPOINT: str = _API_BASE_URL + "/matches"
+ _MATCH_ENDPOINT: str = _API_BASE_URL + "/match"
+ _NUMBER_ONLINE_ENDPOINT: str = _API_BASE_URL + "/stats/players"
+
+ def __init__(self, timeout: float | tuple[float, float] = 5):
"""Creating a Session for connection pooling since we're always querying the same host."""
self.session = requests.Session()
self.timeout = timeout
def __repr__(self) -> str:
- return f"Client for <{self.API_BASE_URL}>"
+ return f"Client for <{self._API_BASE_URL}>"
def strings(self, game: str = "aoe2de") -> StringsResponse:
"""
@@ -66,11 +77,11 @@ def strings(self, game: str = "aoe2de") -> StringsResponse:
processed_response = _get_request_response_json(
session=self.session,
- url=self.STRINGS_ENDPOINT,
+ url=self._STRINGS_ENDPOINT,
params=query_params,
timeout=self.timeout,
)
- logger.trace(f"Validating response from '{self.STRINGS_ENDPOINT}'")
+ logger.trace(f"Validating response from '{self._STRINGS_ENDPOINT}'")
return StringsResponse(**processed_response)
def leaderboard(
@@ -79,9 +90,9 @@ def leaderboard(
leaderboard_id: int = 3,
start: int = 1,
count: int = 10,
- search: str = None,
- steam_id: int = None,
- profile_id: int = None,
+ search: str | None = None,
+ steam_id: int | None = None,
+ profile_id: int | None = None,
) -> LeaderBoardResponse:
"""
Request the current leaderboards.
@@ -104,16 +115,17 @@ def leaderboard(
profile ID (ex: 459658).
Raises:
- Aoe2NetException: if the 'count' parameter exceeds 10 000.
+ Aoe2NetError: if the 'count' parameter exceeds 10 000.
Returns:
A LeaderBoardResponse validated object with the different parameters used for the
query, the total amount of hits, and the leaderboard as a list profile entries for
each ranking.
"""
- if count > 10_000:
+ if count > _MAX_LEADERBOARD_COUNT:
logger.error(f"'count' has to be 10000 or less, but {count} was provided.")
- raise Aoe2NetException("Invalid value for parameter 'count'.")
+ msg = "Invalid value for parameter 'count'."
+ raise Aoe2NetError(msg)
logger.debug("Preparing parameters for leaderboard query")
query_params = {
@@ -128,14 +140,14 @@ def leaderboard(
processed_response = _get_request_response_json(
session=self.session,
- url=self.LEADERBOARD_ENDPOINT,
+ url=self._LEADERBOARD_ENDPOINT,
params=query_params,
timeout=self.timeout,
)
- logger.trace(f"Validating response from '{self.LEADERBOARD_ENDPOINT}'")
+ logger.trace(f"Validating response from '{self._LEADERBOARD_ENDPOINT}'")
return LeaderBoardResponse(**processed_response)
- def lobbies(self, game: str = "aoe2de") -> List[MatchLobby]:
+ def lobbies(self, game: str = "aoe2de") -> list[MatchLobby]:
"""
Request all open lobbies.
@@ -148,20 +160,22 @@ def lobbies(self, game: str = "aoe2de") -> List[MatchLobby]:
A list of MatchLobby valideted objects, each one encapsulating the data for a currently
open lobby.
"""
- logger.debug("Preparing parameters for open lobbies query")
- query_params = {"game": game}
-
- processed_response = _get_request_response_json(
- session=self.session,
- url=self.LOBBIES_ENDPOINT,
- params=query_params,
- timeout=self.timeout,
- )
- logger.trace(f"Validating response from '{self.LOBBIES_ENDPOINT}'")
- return parse_obj_as(List[MatchLobby], processed_response)
+ # logger.debug("Preparing parameters for open lobbies query")
+ # query_params = {"game": game}
+
+ # processed_response = _get_request_response_json(
+ # session=self.session,
+ # url=self._LOBBIES_ENDPOINT,
+ # params=query_params,
+ # timeout=self.timeout,
+ # )
+ # logger.trace(f"Validating response from '{self._LOBBIES_ENDPOINT}'")
+ # return _LIST_MATCHLOBBY_ADAPTER.validate_python(processed_response)
+ logger.error(f"Tried to query {self._LOBBIES_ENDPOINT} endpoint, which was removed by aoe2.net")
+ raise RemovedApiEndpointError(self._LOBBIES_ENDPOINT)
def last_match(
- self, game: str = "aoe2de", steam_id: int = None, profile_id: int = None
+ self, game: str = "aoe2de", steam_id: int | None = None, profile_id: int | None = None
) -> LastMatchResponse:
"""
Request the last match the player started playing, this will be the current match if they
@@ -175,37 +189,40 @@ def last_match(
profile_id (int): The player's profile ID (ex: 459658).
Raises:
- Aoe2NetException: if the not one of 'steam_id' or 'profile_id' are provided.
+ Aoe2NetError: if the not one of 'steam_id' or 'profile_id' are provided.
Returns:
A LastMatchResponse validated object with the information of the game, including the
following attributes: 'profile_id', 'steam_id', 'name', 'clan', 'country' and
'last_match'.
"""
- if not steam_id and not profile_id:
- logger.error("Missing one of 'steam_id', 'profile_id'.")
- raise Aoe2NetException("Either 'steam_id' or 'profile_id' required, please provide one.")
-
- logger.debug("Preparing parameters for last match query")
- query_params = {"game": game, "steam_id": steam_id, "profile_id": profile_id}
-
- processed_response = _get_request_response_json(
- session=self.session,
- url=self.LAST_MATCH_ENDPOINT,
- params=query_params,
- timeout=self.timeout,
- )
- logger.trace(f"Validating response from '{self.LAST_MATCH_ENDPOINT}'")
- return LastMatchResponse(**processed_response)
+ # if not steam_id and not profile_id:
+ # logger.error("Missing one of 'steam_id', 'profile_id'.")
+ # msg = "Either 'steam_id' or 'profile_id' required, please provide one."
+ # raise Aoe2NetError(msg)
+
+ # logger.debug("Preparing parameters for last match query")
+ # query_params = {"game": game, "steam_id": steam_id, "profile_id": profile_id}
+
+ # processed_response = _get_request_response_json(
+ # session=self.session,
+ # url=self._LAST_MATCH_ENDPOINT,
+ # params=query_params,
+ # timeout=self.timeout,
+ # )
+ # logger.trace(f"Validating response from '{self._LAST_MATCH_ENDPOINT}'")
+ # return LastMatchResponse(**processed_response)
+ logger.error(f"Tried to query {self._LAST_MATCH_ENDPOINT} endpoint, which was removed by aoe2.net")
+ raise RemovedApiEndpointError(self._LAST_MATCH_ENDPOINT)
def match_history(
self,
game: str = "aoe2de",
start: int = 0,
count: int = 10,
- steam_id: int = None,
- profile_id: int = None,
- ) -> List[MatchLobby]:
+ steam_id: int | None = None,
+ profile_id: int | None = None,
+ ) -> list[MatchLobby]:
"""
Request the match history for a player. Either 'steam_id' or 'profile_id' required.
@@ -219,20 +236,22 @@ def match_history(
profile_id (int): The player's profile ID (ex: 459658).
Raises:
- Aoe2NetException: if the 'count' parameter exceeds 1000.
- Aoe2NetException: if the not one of 'steam_id' or 'profile_id' are provided.
+ Aoe2NetError: if the 'count' parameter exceeds 1000.
+ Aoe2NetError: if the not one of 'steam_id' or 'profile_id' are provided.
Returns:
A list of MatchLobby validated objects, each one encapsulating the data for one of the
player's previous matches.
"""
- if count > 1_000:
+ if count > _MAX_MATCH_HISTORY_COUNT:
logger.error(f"'count' has to be 1000 or less, but {count} was provided.")
- raise Aoe2NetException("Invalid value for parameter 'count'.")
+ msg = "Invalid value for parameter 'count'."
+ raise Aoe2NetError(msg)
if not steam_id and not profile_id:
logger.error("Missing one of 'steam_id', 'profile_id'.")
- raise Aoe2NetException("Either 'steam_id' or 'profile_id' required, please provide one.")
+ msg = "Either 'steam_id' or 'profile_id' required, please provide one."
+ raise Aoe2NetError(msg)
logger.debug("Preparing parameters for match history query")
query_params = {
@@ -245,12 +264,12 @@ def match_history(
processed_response = _get_request_response_json(
session=self.session,
- url=self.MATCH_HISTORY_ENDPOINT,
+ url=self._MATCH_HISTORY_ENDPOINT,
params=query_params,
timeout=self.timeout,
)
- logger.trace(f"Validating response from '{self.MATCH_HISTORY_ENDPOINT}'")
- return parse_obj_as(List[MatchLobby], processed_response)
+ logger.trace(f"Validating response from '{self._MATCH_HISTORY_ENDPOINT}'")
+ return _LIST_MATCHLOBBY_ADAPTER.validate_python(processed_response)
def rating_history(
self,
@@ -258,9 +277,9 @@ def rating_history(
leaderboard_id: int = 3,
start: int = 0,
count: int = 20,
- steam_id: int = None,
- profile_id: int = None,
- ) -> List[RatingTimePoint]:
+ steam_id: int | None = None,
+ profile_id: int | None = None,
+ ) -> list[RatingTimePoint]:
"""
Requests the rating history for a player. Either 'steam_id' or 'profile_id' required.
@@ -278,21 +297,23 @@ def rating_history(
profile_id (int): The player's profile ID (ex: 459658).
Raises:
- Aoe2NetException: if the 'count' parameter exceeds 10 000.
- Aoe2NetException: if the not one of 'steam_id' or 'profile_id' are provided.
+ Aoe2NetError: if the 'count' parameter exceeds 10 000.
+ Aoe2NetError: if the not one of 'steam_id' or 'profile_id' are provided.
Returns:
A list of RatingTimePoint validated objects, each one encapsulating data at a certain
point in time corresponding to a match played by the player, including the rating,
timestamp of the match, streaks etc.
"""
- if count > 10_000:
+ if count > _MAX_RATING_HISTORY_COUNT:
logger.error(f"'count' has to be 10 000 or less, but {count} was provided.")
- raise Aoe2NetException("Invalid value for parameter 'count'.")
+ msg = "Invalid value for parameter 'count'."
+ raise Aoe2NetError(msg)
if not steam_id and not profile_id:
logger.error("Missing one of 'steam_id', 'profile_id'.")
- raise Aoe2NetException("Either 'steam_id' or 'profile_id' required, please provide one.")
+ msg = "Either 'steam_id' or 'profile_id' required, please provide one."
+ raise Aoe2NetError(msg)
logger.debug("Preparing parameters for rating history query")
query_params = {
@@ -306,14 +327,14 @@ def rating_history(
processed_response = _get_request_response_json(
session=self.session,
- url=self.RATING_HISTORY_ENDPOINT,
+ url=self._RATING_HISTORY_ENDPOINT,
params=query_params,
timeout=self.timeout,
)
- logger.trace(f"Validating response from '{self.RATING_HISTORY_ENDPOINT}'")
- return parse_obj_as(List[RatingTimePoint], processed_response)
+ logger.trace(f"Validating response from '{self._RATING_HISTORY_ENDPOINT}'")
+ return _LIST_RATINGTIMEPOINT_ADAPTER.validate_python(processed_response)
- def matches(self, game: str = "aoe2de", count: int = 10, since: int = None) -> List[MatchLobby]:
+ def matches(self, game: str = "aoe2de", count: int = 10, since: int | None = None) -> list[MatchLobby]:
"""
Request matches after a specific time: the match history in an optionally given time
window.
@@ -329,33 +350,36 @@ def matches(self, game: str = "aoe2de", count: int = 10, since: int = None) -> L
since (int): only show matches starting after 'since' timestamp (epoch).
Raises:
- Aoe2NetException: if the 'count' parameter exceeds 1000.
+ Aoe2NetError: if the 'count' parameter exceeds 1000.
Returns:
A list of MatchLobby validated objects, each one encapsulating the data for one of the
played matches during the time window queried for.
"""
- if count > 1000:
- logger.error(f"'count' has to be 1000 or less, but {count} was provided.")
- raise Aoe2NetException("Invalid value for parameter 'count'.")
-
- logger.debug("Preparing parameters for matches query")
- query_params = {
- "game": game,
- "count": count,
- "since": since,
- }
-
- processed_response = _get_request_response_json(
- session=self.session,
- url=self.MATCHES_ENDPOINT,
- params=query_params,
- timeout=self.timeout,
- )
- logger.trace(f"Validating response from '{self.MATCHES_ENDPOINT}'")
- return parse_obj_as(List[MatchLobby], processed_response)
-
- def match(self, game: str = "aoe2de", uuid: str = None, match_id: int = None) -> MatchLobby:
+ # if count > _MAX_MATCHES_COUNT:
+ # logger.error(f"'count' has to be 1000 or less, but {count} was provided.")
+ # msg = "Invalid value for parameter 'count'."
+ # raise Aoe2NetError(msg)
+
+ # logger.debug("Preparing parameters for matches query")
+ # query_params = {
+ # "game": game,
+ # "count": count,
+ # "since": since,
+ # }
+
+ # processed_response = _get_request_response_json(
+ # session=self.session,
+ # url=self._MATCHES_ENDPOINT,
+ # params=query_params,
+ # timeout=self.timeout,
+ # )
+ # logger.trace(f"Validating response from '{self._MATCHES_ENDPOINT}'")
+ # return _LIST_MATCHLOBBY_ADAPTER.validate_python(processed_response)
+ logger.error(f"Tried to query {self._MATCHES_ENDPOINT} endpoint, which was removed by aoe2.net")
+ raise RemovedApiEndpointError(self._MATCHES_ENDPOINT)
+
+ def match(self, game: str = "aoe2de", uuid: str | None = None, match_id: int | None = None) -> MatchLobby:
"""
Request details about a match. Either 'uuid' or 'match_id' required.
@@ -367,30 +391,33 @@ def match(self, game: str = "aoe2de", uuid: str = None, match_id: int = None) ->
match_id (int): match ID.
Raises:
- Aoe2NetException: if the not one of 'uuid' or 'match_id' are provided.
+ Aoe2NetError: if the not one of 'uuid' or 'match_id' are provided.
Returns:
A MatchLobby validated object with the information of the specific match, including.
"""
- if not uuid and not match_id:
- logger.error("Missing one of 'uuid', 'match_id'.")
- raise Aoe2NetException("Either 'uuid' or 'match_id' required, please provide one.")
-
- logger.debug("Preparing parameters for single match query")
- query_params = {
- "game": game,
- "uuid": uuid,
- "match_id": match_id,
- }
-
- processed_response = _get_request_response_json(
- session=self.session,
- url=self.MATCH_ENDPOINT,
- params=query_params,
- timeout=self.timeout,
- )
- logger.trace(f"Validating response from '{self.MATCH_ENDPOINT}'")
- return MatchLobby(**processed_response)
+ # if not uuid and not match_id:
+ # logger.error("Missing one of 'uuid', 'match_id'.")
+ # msg = "Either 'uuid' or 'match_id' required, please provide one."
+ # raise Aoe2NetError(msg)
+
+ # logger.debug("Preparing parameters for single match query")
+ # query_params = {
+ # "game": game,
+ # "uuid": uuid,
+ # "match_id": match_id,
+ # }
+
+ # processed_response = _get_request_response_json(
+ # session=self.session,
+ # url=self._MATCH_ENDPOINT,
+ # params=query_params,
+ # timeout=self.timeout,
+ # )
+ # logger.trace(f"Validating response from '{self._MATCH_ENDPOINT}'")
+ # return MatchLobby(**processed_response)
+ logger.error(f"Tried to query {self._MATCH_ENDPOINT} endpoint, which was removed by aoe2.net")
+ raise RemovedApiEndpointError(self._MATCH_ENDPOINT)
def num_online(self, game: str = "aoe2de") -> NumOnlineResponse:
"""
@@ -406,17 +433,19 @@ def num_online(self, game: str = "aoe2de") -> NumOnlineResponse:
validated objects encapsulating estimated metrics at different timestamps ('steam',
'multiplayer', 'looking', 'in_game', 'multiplayer_1h' & 'multiplayer_24h').
"""
- logger.debug("Preparing parameters for number of online players query")
- query_params = {"game": game}
+ # logger.debug("Preparing parameters for number of online players query")
+ # query_params = {"game": game}
- processed_response = _get_request_response_json(
- session=self.session,
- url=self.NUMBER_ONLINE_ENDPOINT,
- params=query_params,
- timeout=self.timeout,
- )
- logger.trace(f"Validating response from '{self.NUMBER_ONLINE_ENDPOINT}'")
- return NumOnlineResponse(**processed_response)
+ # processed_response = _get_request_response_json(
+ # session=self.session,
+ # url=self._NUMBER_ONLINE_ENDPOINT,
+ # params=query_params,
+ # timeout=self.timeout,
+ # )
+ # logger.trace(f"Validating response from '{self._NUMBER_ONLINE_ENDPOINT}'")
+ # return NumOnlineResponse(**processed_response)
+ logger.error(f"Tried to query {self._NUMBER_ONLINE_ENDPOINT} endpoint, which was removed by aoe2.net")
+ raise RemovedApiEndpointError(self._NUMBER_ONLINE_ENDPOINT)
# ----- Helpers ----- #
@@ -425,8 +454,8 @@ def num_online(self, game: str = "aoe2de") -> NumOnlineResponse:
def _get_request_response_json(
session: requests.Session,
url: str,
- params: Dict[str, Any] = None,
- timeout: Union[float, Tuple[float, float]] = None,
+ params: dict[str, Any] | None = None,
+ timeout: float | tuple[float, float] | None = None,
) -> dict:
"""
Helper function to handle a GET request to an endpoint and return the response JSON content
@@ -438,17 +467,18 @@ def _get_request_response_json(
params (dict): A dictionary of parameters for the GET request.
Raises:
- Aoe2NetException: if the status code returned is not 200.
+ Aoe2NetError: if the status code returned is not 200.
Returns:
The request's JSON response as a dictionary.
"""
default_headers = {"content-type": "application/json;charset=UTF-8"}
logger.debug(f"Sending GET request at '{url}'")
- logger.trace(f"Parameters are: {str(params)}")
+ logger.trace(f"Parameters are: {params!s}")
response = session.get(url, params=params, headers=default_headers, timeout=timeout)
- if response.status_code != 200:
+ if response.status_code != _OK_STATUS_CODE:
logger.error(f"GET request at '{response.url}' returned a {response.status_code} status code")
- raise Aoe2NetException(f"Expected status code 200 - got {response.status_code} instead.")
+ msg = f"Expected status code 200 - got {response.status_code} instead"
+ raise Aoe2NetError(msg)
return response.json()
diff --git a/aoe2netwrapper/converters.py b/aoe2netwrapper/converters.py
index 9f0db9b..6069335 100644
--- a/aoe2netwrapper/converters.py
+++ b/aoe2netwrapper/converters.py
@@ -5,15 +5,12 @@
This module implements a high-level class with static methods to convert result of AoENetAPI methods to
pandas DataFrames.
"""
-from typing import List
from loguru import logger
-from aoe2netwrapper.models import (
- LastMatchResponse,
+from aoe2netwrapper.models import ( # LastMatchResponse, NumOnlineResponse,
LeaderBoardResponse,
MatchLobby,
- NumOnlineResponse,
RatingTimePoint,
StringsResponse,
)
@@ -21,13 +18,9 @@
try:
import pandas as pd
except ImportError as error:
- logger.error(
- "User tried to use the 'converters' submodule without havinig installed the 'pandas' library."
- )
- raise NotImplementedError(
- "The 'aoe2netwrapper.converters' module exports results to 'pandas.DataFrame' objects and "
- "needs the 'pandas' library installed to function."
- ) from error
+ logger.error("User tried to use the 'converters' submodule without the 'pandas' library.")
+ msg = "The 'converters' submodule requires the 'pandas' library to function."
+ raise NotImplementedError(msg) from error
class Convert:
@@ -53,7 +46,8 @@ def strings(strings_response: StringsResponse) -> pd.DataFrame:
"""
if not isinstance(strings_response, StringsResponse):
logger.error("Tried to use method with a parameter of type != StringsResponse")
- raise TypeError("Provided parameter should be an instance of 'StringsResponse'")
+ msg = "Provided parameter should be an instance of 'StringsResponse'"
+ raise TypeError(msg)
logger.debug("Converting StringsResponse to DataFrame")
dframe = pd.DataFrame(strings_response).transpose()
@@ -88,7 +82,8 @@ def leaderboard(leaderboard_response: LeaderBoardResponse) -> pd.DataFrame:
"""
if not isinstance(leaderboard_response, LeaderBoardResponse):
logger.error("Tried to use method with a parameter of type != LeaderBoardResponse")
- raise TypeError("Provided parameter should be an instance of 'LeaderBoardResponse'")
+ msg = "Provided parameter should be an instance of 'LeaderBoardResponse'"
+ raise TypeError(msg)
logger.debug("Converting LeaderBoardResponse leaderboard to DataFrame")
dframe = pd.DataFrame(leaderboard_response.leaderboard)
@@ -105,65 +100,67 @@ def leaderboard(leaderboard_response: LeaderBoardResponse) -> pd.DataFrame:
dframe["last_match_time"] = pd.to_datetime(dframe["last_match_time"], unit="s")
return dframe
- @staticmethod
- def lobbies(lobbies_response: List[MatchLobby]) -> pd.DataFrame:
- """
- Convert the result given by a call to AoE2NetAPI().lobbies to a pandas DataFrame. The resulting
- DataFrame will contain several rows for each lobby, namely as many as there are players in said
- lobby. All global attributes of each lobby are broadcasted to arrays, making them duplicates.
-
- To isolate a specific lobby, either call the AoE2NetAPI().match method with the lobby's UUID or
- make use of the groupby functionality of pandas DataFrames.
-
- Args:
- lobbies_response (List[MatchLobby]): the response directly returned by your AoE2NetAPI
- client.
-
- Returns:
- A pandas DataFrame from the list of MatchLobby elements..
- """
- if not isinstance(lobbies_response, list): # move list to List[MatchLobby] when supporting > 3.9
- logger.error("Tried to use method with a parameter of type != List[MatchLobby]")
- raise TypeError("Provided parameter should be an instance of 'List[MatchLobby]'")
-
- logger.debug("Converting Lobbies response to DataFrame")
- unfolded_lobbies = [_unfold_match_lobby_to_dataframe(match_lobby) for match_lobby in lobbies_response]
- return pd.concat(unfolded_lobbies).reset_index(drop=True)
+ # @staticmethod
+ # def lobbies(lobbies_response: list[MatchLobby]) -> pd.DataFrame:
+ # """
+ # Convert the result given by a call to AoE2NetAPI().lobbies to a pandas DataFrame. The resulting
+ # DataFrame will contain several rows for each lobby, namely as many as there are players in said
+ # lobby. All global attributes of each lobby are broadcasted to arrays, making them duplicates.
+
+ # To isolate a specific lobby, either call the AoE2NetAPI().match method with the lobby's UUID or
+ # make use of the groupby functionality of pandas DataFrames.
+
+ # Args:
+ # lobbies_response (list[MatchLobby]): the response directly returned by your AoE2NetAPI
+ # client.
+
+ # Returns:
+ # A pandas DataFrame from the list of MatchLobby elements..
+ # """
+ # if not isinstance(lobbies_response, list): # move list to list[MatchLobby] when supporting > 3.9
+ # logger.error("Tried to use method with a parameter of type != list[MatchLobby]")
+ # msg = "Provided parameter should be an instance of 'list[MatchLobby]'"
+ # raise TypeError(msg)
+
+ # logger.debug("Converting Lobbies response to DataFrame")
+ # unfolded_lobbies = [_unfold_match_lobby_to_dataframe(match_lobby) for match_lobby in lobbies_response]
+ # return pd.concat(unfolded_lobbies).reset_index(drop=True)
+
+ # @staticmethod
+ # def last_match(last_match_response: LastMatchResponse) -> pd.DataFrame:
+ # """
+ # Convert the result given by a call to AoE2NetAPI().last_match to a pandas DataFrame. There is not
+ # much use to this as the DataFrame will only have one row, but the method is provided nonetheless in
+ # case users want to concatenate several of these results in a DataFrame.
+
+ # Args:
+ # last_match_response (LastMatchResponse): the response directly returned by your AoE2NetAPI
+ # client.
+
+ # Returns:
+ # A pandas DataFrame from the list of LastMatchResponse attributes. Beware: the 'players'
+ # column is directly the content of the 'LastMatchResponse.last_match.players' attribute and as
+ # such holds a list of LobbyMember objects.
+ # """
+ # if not isinstance(last_match_response, LastMatchResponse):
+ # logger.error("Tried to use method with a parameter of type != LastMatchResponse")
+ # msg = "Provided parameter should be an instance of 'LastMatchResponse'"
+ # raise TypeError(msg)
+
+ # logger.debug("Converting LastMatchResponse last_match to DataFrame")
+ # dframe = pd.DataFrame(last_match_response.last_match).transpose()
+ # dframe.columns = dframe.iloc[0]
+ # dframe = dframe.drop(0).reset_index()
+
+ # logger.trace("Inserting LastMatchResponse attributes as columns")
+ # dframe["profile_id"] = last_match_response.profile_id
+ # dframe["steam_id"] = last_match_response.steam_id
+ # dframe["name"] = last_match_response.name
+ # dframe["country"] = last_match_response.country
+ # return dframe
@staticmethod
- def last_match(last_match_response: LastMatchResponse) -> pd.DataFrame:
- """
- Convert the result given by a call to AoE2NetAPI().last_match to a pandas DataFrame. There is not
- much use to this as the DataFrame will only have one row, but the method is provided nonetheless in
- case users want to concatenate several of these results in a DataFrame.
-
- Args:
- last_match_response (LastMatchResponse): the response directly returned by your AoE2NetAPI
- client.
-
- Returns:
- A pandas DataFrame from the list of LastMatchResponse attributes. Beware: the 'players'
- column is directly the content of the 'LastMatchResponse.last_match.players' attribute and as
- such holds a list of LobbyMember objects.
- """
- if not isinstance(last_match_response, LastMatchResponse):
- logger.error("Tried to use method with a parameter of type != LastMatchResponse")
- raise TypeError("Provided parameter should be an instance of 'LastMatchResponse'")
-
- logger.debug("Converting LastMatchResponse last_match to DataFrame")
- dframe = pd.DataFrame(last_match_response.last_match).transpose()
- dframe.columns = dframe.iloc[0]
- dframe = dframe.drop(0).reset_index()
-
- logger.trace("Inserting LastMatchResponse attributes as columns")
- dframe["profile_id"] = last_match_response.profile_id
- dframe["steam_id"] = last_match_response.steam_id
- dframe["name"] = last_match_response.name
- dframe["country"] = last_match_response.country
- return dframe
-
- @staticmethod
- def match_history(match_history_response: List[MatchLobby]) -> pd.DataFrame:
+ def match_history(match_history_response: list[MatchLobby]) -> pd.DataFrame:
"""
Convert the result given by a call to AoE2NetAPI().match_history to a pandas DataFrame. The resulting
DataFrame will contain several rows for each lobby, namely as many as there are players in said
@@ -173,16 +170,17 @@ def match_history(match_history_response: List[MatchLobby]) -> pd.DataFrame:
make use of the groupby functionality of pandas DataFrames.
Args:
- match_history_response (List[MatchLobby]): the response directly returned by your AoE2NetAPI
+ match_history_response (list[MatchLobby]): the response directly returned by your AoE2NetAPI
client.
Returns:
A pandas DataFrame from the list of MatchLobby elements.
"""
- # move list to List[MatchLobby] when supporting > 3.9
+ # move list to list[MatchLobby] when supporting > 3.9
if not isinstance(match_history_response, list):
- logger.error("Tried to use method with a parameter of type != List[MatchLobby]")
- raise TypeError("Provided parameter should be an instance of 'List[MatchLobby]'")
+ logger.error("Tried to use method with a parameter of type != list[MatchLobby]")
+ msg = "Provided parameter should be an instance of 'list[MatchLobby]'"
+ raise TypeError(msg)
logger.debug("Converting Match History response to DataFrame")
unfolded_lobbies = [
@@ -191,22 +189,23 @@ def match_history(match_history_response: List[MatchLobby]) -> pd.DataFrame:
return pd.concat(unfolded_lobbies).reset_index(drop=True)
@staticmethod
- def rating_history(rating_history_response: List[RatingTimePoint]) -> pd.DataFrame:
+ def rating_history(rating_history_response: list[RatingTimePoint]) -> pd.DataFrame:
"""
Convert the result given by a call to AoE2NetAPI().leaderboard to a pandas DataFrame.
Args:
- rating_history_response (List[RatingTimePoint]): the response directly returned by your AoE2NetAPI
+ rating_history_response (list[RatingTimePoint]): the response directly returned by your AoE2NetAPI
client.
Returns:
A pandas DataFrame from the list of RatingTimePoint elements, each row being the information from
one RatingTimePoint in the list. Timestamps are converted to datetime objects.
"""
- # move list to List[RatingTimePoint] when supporting > 3.9
+ # move list to list[RatingTimePoint] when supporting > 3.9
if not isinstance(rating_history_response, list):
- logger.error("Tried to use method with a parameter of type != List[RatingTimePoint]")
- raise TypeError("Provided parameter should be an instance of 'List[RatingTimePoint]'")
+ logger.error("Tried to use method with a parameter of type != list[RatingTimePoint]")
+ msg = "Provided parameter should be an instance of 'list[RatingTimePoint]'"
+ raise TypeError(msg)
logger.debug("Converting Rating History rsponse to DataFrame")
dframe = pd.DataFrame(rating_history_response)
@@ -214,83 +213,83 @@ def rating_history(rating_history_response: List[RatingTimePoint]) -> pd.DataFra
logger.trace("Converting timestamps to datetime objects")
dframe["time"] = pd.to_datetime(dframe["timestamp"], unit="s")
- dframe = dframe.drop(columns=["timestamp"])
- return dframe
-
- @staticmethod
- def matches(matches_response: List[MatchLobby]) -> pd.DataFrame:
- """
- Convert the result given by a call to AoE2NetAPI().match_history to a pandas DataFrame. The resulting
- DataFrame will contain several rows for each lobby, namely as many as there are players in said
- lobby. All global attributes of each lobby are broadcasted to arrays, making them duplicates.
-
- To isolate a specific lobby, either call the AoE2NetAPI().match method with the lobby's UUID or
- make use of the groupby functionality of pandas DataFrames.
-
- Args:
- matches_response (List[MatchLobby]): the response directly returned by your AoE2NetAPI
- client.
-
- Returns:
- A pandas DataFrame from the list of MatchLobby elements.
- """
- if not isinstance(matches_response, list): # move list to List[MatchLobby] when supporting > 3.9
- logger.error("Tried to use method with a parameter of type != List[MatchLobby]")
- raise TypeError("Provided parameter should be an instance of 'List[MatchLobby]'")
-
- logger.debug("Converting Match History response to DataFrame")
- unfolded_lobbies = [_unfold_match_lobby_to_dataframe(match_lobby) for match_lobby in matches_response]
- return pd.concat(unfolded_lobbies).reset_index(drop=True)
-
- @staticmethod
- def match(match_response: MatchLobby) -> pd.DataFrame:
- """
- Convert the content of a MatchLobby to a pandas DataFrame. The resulting DataFrame will have as many
- rows as there are players in the lobby, and all global attributes will be broadcasted to columns of
- the same length, making them duplicates.
-
- Args:
- match_response (MatchLobby): a MatchLobby object.
-
- Returns:
- A pandas DataFrame from the MatchLobby attributes, each row being global information from the
- MatchLobby as well as one of the players in the lobby.
- """
- return _unfold_match_lobby_to_dataframe(match_response)
-
- @staticmethod
- def num_online(num_online_response: NumOnlineResponse) -> pd.DataFrame:
- """
- Convert the result given by a call to AoE2NetAPI().num_online to a pandas DataFrame.
-
- Args:
- num_online_response (NumOnlineResponse): the response directly returned by your AoE2NetAPI
- client.
-
- Returns:
- A pandas DataFrame from the NumOnlineResponse, each row being an entry in the leaderboard.
- Top level attributes such as 'app_id' are broadcast to an entire array the size of the
- dataframe, and timestamps are converted to datetime objects.
- """
- if not isinstance(num_online_response, NumOnlineResponse):
- logger.error("Tried to use method with a parameter of type != NumOnlineResponse")
- raise TypeError("Provided parameter should be an instance of 'NumOnlineResponse'")
-
- logger.debug("Converting NumOnlineResponse to DataFrame")
- dframe = pd.DataFrame(num_online_response.dict())
-
- logger.trace("Exporting 'player_stats' attribute contents to columns")
- dframe["time"] = dframe.player_stats.apply(lambda x: x["time"]).apply(pd.to_datetime)
- dframe["steam"] = dframe.player_stats.apply(lambda x: x["num_players"]["steam"])
- dframe["looking"] = dframe.player_stats.apply(lambda x: x["num_players"]["looking"])
- dframe["in_game"] = dframe.player_stats.apply(lambda x: x["num_players"]["in_game"])
- dframe["multiplayer"] = dframe.player_stats.apply(lambda x: x["num_players"]["multiplayer"])
- dframe["multiplayer_1h"] = dframe.player_stats.apply(lambda x: x["num_players"]["multiplayer_1h"])
- dframe["multiplayer_24h"] = dframe.player_stats.apply(lambda x: x["num_players"]["multiplayer_24h"])
-
- logger.trace("Removing 'player_stats' column to avoid nested & duplicate data")
- dframe = dframe.drop(columns=["player_stats"])
- return dframe
+ return dframe.drop(columns=["timestamp"])
+
+ # @staticmethod
+ # def matches(matches_response: list[MatchLobby]) -> pd.DataFrame:
+ # """
+ # Convert the result given by a call to AoE2NetAPI().match_history to a pandas DataFrame. The resulting
+ # DataFrame will contain several rows for each lobby, namely as many as there are players in said
+ # lobby. All global attributes of each lobby are broadcasted to arrays, making them duplicates.
+
+ # To isolate a specific lobby, either call the AoE2NetAPI().match method with the lobby's UUID or
+ # make use of the groupby functionality of pandas DataFrames.
+
+ # Args:
+ # matches_response (list[MatchLobby]): the response directly returned by your AoE2NetAPI
+ # client.
+
+ # Returns:
+ # A pandas DataFrame from the list of MatchLobby elements.
+ # """
+ # if not isinstance(matches_response, list): # move list to list[MatchLobby] when supporting > 3.9
+ # logger.error("Tried to use method with a parameter of type != list[MatchLobby]")
+ # msg = "Provided parameter should be an instance of 'list[MatchLobby]'"
+ # raise TypeError(msg)
+
+ # logger.debug("Converting Match History response to DataFrame")
+ # unfolded_lobbies = [_unfold_match_lobby_to_dataframe(match_lobby) for match_lobby in matches_response]
+ # return pd.concat(unfolded_lobbies).reset_index(drop=True)
+
+ # @staticmethod
+ # def match(match_response: MatchLobby) -> pd.DataFrame:
+ # """
+ # Convert the content of a MatchLobby to a pandas DataFrame. The resulting DataFrame will have as many
+ # rows as there are players in the lobby, and all global attributes will be broadcasted to columns of
+ # the same length, making them duplicates.
+
+ # Args:
+ # match_response (MatchLobby): a MatchLobby object.
+
+ # Returns:
+ # A pandas DataFrame from the MatchLobby attributes, each row being global information from the
+ # MatchLobby as well as one of the players in the lobby.
+ # """
+ # return _unfold_match_lobby_to_dataframe(match_response)
+
+ # @staticmethod
+ # def num_online(num_online_response: NumOnlineResponse) -> pd.DataFrame:
+ # """
+ # Convert the result given by a call to AoE2NetAPI().num_online to a pandas DataFrame.
+
+ # Args:
+ # num_online_response (NumOnlineResponse): the response directly returned by your AoE2NetAPI
+ # client.
+
+ # Returns:
+ # A pandas DataFrame from the NumOnlineResponse, each row being an entry in the leaderboard.
+ # Top level attributes such as 'app_id' are broadcast to an entire array the size of the
+ # dataframe, and timestamps are converted to datetime objects.
+ # """
+ # if not isinstance(num_online_response, NumOnlineResponse):
+ # logger.error("Tried to use method with a parameter of type != NumOnlineResponse")
+ # msg = "Provided parameter should be an instance of 'NumOnlineResponse'"
+ # raise TypeError(msg)
+
+ # logger.debug("Converting NumOnlineResponse to DataFrame")
+ # dframe = pd.DataFrame(num_online_response.dict())
+
+ # logger.trace("Exporting 'player_stats' attribute contents to columns")
+ # dframe["time"] = dframe.player_stats.apply(lambda x: x["time"]).apply(pd.to_datetime)
+ # dframe["steam"] = dframe.player_stats.apply(lambda x: x["num_players"]["steam"])
+ # dframe["looking"] = dframe.player_stats.apply(lambda x: x["num_players"]["looking"])
+ # dframe["in_game"] = dframe.player_stats.apply(lambda x: x["num_players"]["in_game"])
+ # dframe["multiplayer"] = dframe.player_stats.apply(lambda x: x["num_players"]["multiplayer"])
+ # dframe["multiplayer_1h"] = dframe.player_stats.apply(lambda x: x["num_players"]["multiplayer_1h"])
+ # dframe["multiplayer_24h"] = dframe.player_stats.apply(lambda x: x["num_players"]["multiplayer_24h"])
+
+ # logger.trace("Removing 'player_stats' column to avoid nested & duplicate data")
+ # return dframe.drop(columns=["player_stats"])
# ----- Helpers ----- #
@@ -332,7 +331,8 @@ def _unfold_match_lobby_to_dataframe(match_lobby: MatchLobby) -> pd.DataFrame:
"""
if not isinstance(match_lobby, MatchLobby):
logger.error("Tried to use method with a parameter of type != MatchLobby")
- raise TypeError("Provided parameter should be an instance of 'MatchLobby'")
+ msg = "Provided parameter should be an instance of 'MatchLobby'"
+ raise TypeError(msg)
logger.trace("Unfolding MatchLobby.players contents to DataFrame")
dframe = pd.DataFrame(match_lobby.players)
diff --git a/aoe2netwrapper/exceptions.py b/aoe2netwrapper/exceptions.py
index eb2e1c0..a485255 100644
--- a/aoe2netwrapper/exceptions.py
+++ b/aoe2netwrapper/exceptions.py
@@ -5,9 +5,17 @@
"""
-class Aoe2NetException(Exception):
+class Aoe2NetError(Exception):
"""Default exception for AoE2.net API interaction."""
-class NightBotException(Exception):
+class RemovedApiEndpointError(Exception):
+ """Exception raised when an API endpoint is removed from AoE2.net."""
+
+ def __init__(self, endpoint: str) -> None:
+ msg = f"The API endpoint for '{endpoint}' has been removed from AoE2.net."
+ super().__init__(msg)
+
+
+class NightBotError(Exception):
"""Default exception for AoE2.net Nightbot API interaction."""
diff --git a/aoe2netwrapper/models/__init__.py b/aoe2netwrapper/models/__init__.py
index b3f2fdc..6860398 100644
--- a/aoe2netwrapper/models/__init__.py
+++ b/aoe2netwrapper/models/__init__.py
@@ -5,12 +5,19 @@
This subpackage contains the model objects used to encapsulate responses from the API.
Each module therein contains the models for a specific API endpoint.
"""
-from .last_match import LastMatchResponse
-from .leaderboard import LeaderBoardResponse
-from .lobbies import MatchLobby
-from .match import MatchLobby
-from .match_history import MatchLobby
-from .matches import MatchLobby
-from .num_online import NumOnlineResponse
-from .rating_history import RatingTimePoint
-from .strings import StringsResponse
+
+from .last_match import LastMatchResponse # noqa: TID252
+from .leaderboard import LeaderBoardResponse # noqa: TID252
+from .lobbies import MatchLobby # noqa: TID252
+from .num_online import NumOnlineResponse # noqa: TID252
+from .rating_history import RatingTimePoint # noqa: TID252
+from .strings import StringsResponse # noqa: TID252
+
+__all__ = [
+ "LastMatchResponse",
+ "LeaderBoardResponse",
+ "MatchLobby",
+ "NumOnlineResponse",
+ "RatingTimePoint",
+ "StringsResponse",
+]
diff --git a/aoe2netwrapper/models/last_match.py b/aoe2netwrapper/models/last_match.py
index 02ebf51..6be12ef 100644
--- a/aoe2netwrapper/models/last_match.py
+++ b/aoe2netwrapper/models/last_match.py
@@ -5,7 +5,8 @@
This module contains the model objects to encapsulate the responses from the endpoint at
https://aoe2.net/api/lobbies
"""
-from typing import Optional
+
+from __future__ import annotations
from pydantic import BaseModel, Field
@@ -15,8 +16,8 @@
class LastMatchResponse(BaseModel):
"""An object to encapsulate the response from the last_match API."""
- profile_id: Optional[int] = Field(None, description="The ID attributed to the player by AoE II")
- steam_id: Optional[int] = Field(None, description="ID of the player on the Steam platform")
- name: Optional[str] = Field(None, description="Name of the player the query was made for")
- country: Optional[str] = Field(None, description="Country the player connected from")
- last_match: Optional[MatchLobby] = Field(None, description="MatchLobby of the last match")
+ profile_id: int | None = Field(None, description="The ID attributed to the player by AoE II")
+ steam_id: int | None = Field(None, description="ID of the player on the Steam platform")
+ name: str | None = Field(None, description="Name of the player the query was made for")
+ country: str | None = Field(None, description="Country the player connected from")
+ last_match: MatchLobby | None = Field(None, description="MatchLobby of the last match")
diff --git a/aoe2netwrapper/models/leaderboard.py b/aoe2netwrapper/models/leaderboard.py
index 9f6d239..e4e79d0 100644
--- a/aoe2netwrapper/models/leaderboard.py
+++ b/aoe2netwrapper/models/leaderboard.py
@@ -5,7 +5,10 @@
This module contains the model objects to encapsulate the responses from the endpoint at
https://aoe2.net/api/leaderboard
"""
-from typing import Any, List, Optional
+
+from __future__ import annotations
+
+from typing import Any
from pydantic import BaseModel, Field
@@ -13,32 +16,32 @@
class LeaderBoardSpot(BaseModel):
"""An object to encapsulate any entry in the leaderboard ranking."""
- profile_id: Optional[int] = Field(None, description="The ID attributed to the player by AoE II")
- rank: Optional[int] = Field(None, description="The player's rank on the ladder")
- rating: Optional[int] = Field(None, description="The player's rating in the ELO system")
- steam_id: Optional[int] = Field(None, description="ID of the player on the Steam platform")
- icon: Optional[Any] = Field(None, description="The player's icon")
- name: Optional[str] = Field(None, description="The player's in-game name")
- clan: Optional[str] = Field(None, description="The player's clan / team")
- country: Optional[str] = Field(None, description="Country the player connected from")
- previous_rating: Optional[int] = Field(None, description="Player's rating at their last match")
- highest_rating: Optional[int] = Field(None, description="Highest rating achieved by the player")
- streak: Optional[int] = Field(None, description="Current number of consecutive wins")
- lowest_streak: Optional[int] = Field(None, description="Lowest streak achieved by this player")
- highest_streak: Optional[int] = Field(None, description="Highest streak achieved by this player")
- games: Optional[int] = Field(None, description="The total amount of games played by the player")
- wins: Optional[int] = Field(None, description="Total amount of wins")
- losses: Optional[int] = Field(None, description="Total amount of losses")
- drops: Optional[int] = Field(None, description="Number of games the player dropped out of")
- last_match: Optional[int] = Field(None, description="Timestamp of the last game played")
- last_match_time: Optional[int] = Field(None, description="Timestamp of the last game played")
+ profile_id: int | None = Field(None, description="The ID attributed to the player by AoE II")
+ rank: int | None = Field(None, description="The player's rank on the ladder")
+ rating: int | None = Field(None, description="The player's rating in the ELO system")
+ steam_id: int | None = Field(None, description="ID of the player on the Steam platform")
+ icon: Any | None = Field(None, description="The player's icon")
+ name: str | None = Field(None, description="The player's in-game name")
+ clan: str | None = Field(None, description="The player's clan / team")
+ country: str | None = Field(None, description="Country the player connected from")
+ previous_rating: int | None = Field(None, description="Player's rating at their last match")
+ highest_rating: int | None = Field(None, description="Highest rating achieved by the player")
+ streak: int | None = Field(None, description="Current number of consecutive wins")
+ lowest_streak: int | None = Field(None, description="Lowest streak achieved by this player")
+ highest_streak: int | None = Field(None, description="Highest streak achieved by this player")
+ games: int | None = Field(None, description="The total amount of games played by the player")
+ wins: int | None = Field(None, description="Total amount of wins")
+ losses: int | None = Field(None, description="Total amount of losses")
+ drops: int | None = Field(None, description="Number of games the player dropped out of")
+ last_match: int | None = Field(None, description="Timestamp of the last game played")
+ last_match_time: int | None = Field(None, description="Timestamp of the last game played")
class LeaderBoardResponse(BaseModel):
"""An object to encapsulate the response from the leaderboard API."""
- total: Optional[int] = Field(None, description="Total number of entries in the leaderboard")
- leaderboard_id: Optional[int] = Field(None, description="ID of the leaderboard queried, aka game type")
- start: Optional[int] = Field(None, description="Starting rank of the first entry in the response")
- count: Optional[int] = Field(None, description="Number of entries returned")
- leaderboard: Optional[List[LeaderBoardSpot]] = Field(None, description="List of LeaderBoardSport entries")
+ total: int | None = Field(None, description="Total number of entries in the leaderboard")
+ leaderboard_id: int | None = Field(None, description="ID of the leaderboard queried, aka game type")
+ start: int | None = Field(None, description="Starting rank of the first entry in the response")
+ count: int | None = Field(None, description="Number of entries returned")
+ leaderboard: list[LeaderBoardSpot] | None = Field(None, description="List of LeaderBoardSport entries")
diff --git a/aoe2netwrapper/models/lobbies.py b/aoe2netwrapper/models/lobbies.py
index 0388db7..775c357 100644
--- a/aoe2netwrapper/models/lobbies.py
+++ b/aoe2netwrapper/models/lobbies.py
@@ -5,7 +5,10 @@
This module contains the model objects to encapsulate the responses from the endpoint at
https://aoe2.net/api/lobbies
"""
-from typing import Any, List, Optional
+
+from __future__ import annotations
+
+from typing import Any
from pydantic import BaseModel, Field
@@ -13,66 +16,66 @@
class LobbyMember(BaseModel):
"""An object to encapsulate any entry in the leaderboard ranking."""
- profile_id: Optional[int] = Field(None, description="The ID attributed to the member by AoE II")
- steam_id: Optional[int] = Field(None, description="ID of the member on the Steam platform")
- name: Optional[str] = Field(None, description="The member's in-game name")
- clan: Optional[str] = Field(None, description="The member's clan / team")
- country: Optional[str] = Field(None, description="Country the player connected from")
- slot: Optional[int] = Field(None, description="Slot number of the member in the lobby")
- slot_type: Optional[int] = Field(None, description="ID of the role of the member in the lobby")
- rating: Optional[int] = Field(None, description="The member's rating in the ELO system")
- rating_change: Optional[Any] = Field(None, description="The difference to the member's previous rating")
- games: Optional[int] = Field(None, description="The total amount of games played by the member")
- wins: Optional[int] = Field(None, description="Total amount of wins of the member")
- streak: Optional[int] = Field(None, description="Current number of consecutive wins of the member")
- drops: Optional[int] = Field(None, description="Number of games the member dropped out of")
- color: Optional[str] = Field(None, description="The member's in-game color")
- team: Optional[str] = Field(None, description="The member's team number for the game")
- civ: Optional[int] = Field(None, description="The member's civilization pick for the game")
- won: Optional[int] = Field(None, description="Unclear")
+ profile_id: int | None = Field(None, description="The ID attributed to the member by AoE II")
+ steam_id: int | None = Field(None, description="ID of the member on the Steam platform")
+ name: str | None = Field(None, description="The member's in-game name")
+ clan: str | None = Field(None, description="The member's clan / team")
+ country: str | None = Field(None, description="Country the player connected from")
+ slot: int | None = Field(None, description="Slot number of the member in the lobby")
+ slot_type: int | None = Field(None, description="ID of the role of the member in the lobby")
+ rating: int | None = Field(None, description="The member's rating in the ELO system")
+ rating_change: Any | None = Field(None, description="The difference to the member's previous rating")
+ games: int | None = Field(None, description="The total amount of games played by the member")
+ wins: int | None = Field(None, description="Total amount of wins of the member")
+ streak: int | None = Field(None, description="Current number of consecutive wins of the member")
+ drops: int | None = Field(None, description="Number of games the member dropped out of")
+ color: str | int | None = Field(None, description="The member's in-game color")
+ team: int | None = Field(None, description="The member's team number for the game")
+ civ: int | None = Field(None, description="The member's civilization pick for the game")
+ won: int | None = Field(None, description="Unclear")
class MatchLobby(BaseModel):
"""An object to encapsulate any entry in the list of returned lobbies."""
- match_id: Optional[int] = Field(None, description="ID attributed to the match this lobby is for")
- lobby_id: Optional[int] = Field(None, description="ID attributed to the lobby itself")
- match_uuid: Optional[str] = Field(None, description="UUID attributed to the match this lobby is for")
- version: Optional[int] = Field(None, description="Version number of the game patch")
- name: Optional[str] = Field(None, description="Name given to the lobby")
- num_players: Optional[int] = Field(None, description="Number of players in the lobby")
- num_slots: Optional[int] = Field(None, description="Number of player slots in the lobby")
- average_rating: Optional[int] = Field(None, description="Average rating of the members in the lobby")
- cheats: Optional[bool] = Field(None, description="Whether cheats are enabled")
- full_tech_tree: Optional[bool] = Field(None, description="Whether the full tech tree is set unlocked")
- ending_age: Optional[int] = Field(None, description="The last attainable age for the game")
- expansion: Optional[str] = Field(None, description="The expansion patch enabled")
- game_type: Optional[int] = Field(None, description="ID of the game type, same a leaderboard IDs")
- has_custom_content: Optional[bool] = Field(None, description="Whether the game has custom content")
- has_password: Optional[bool] = Field(None, description="Whether the lobby is password-protected")
- lock_speed: Optional[bool] = Field(None, description="Whether the game speed setting is locked")
- lock_teams: Optional[bool] = Field(None, description="Whether the player teams are locked")
- map_size: Optional[int] = Field(None, description="The game's map size setting")
- map_type: Optional[int] = Field(None, description="ID of the game's map type")
- pop: Optional[int] = Field(None, description="The max population setting for the game")
- ranked: Optional[bool] = Field(None, description="Whether the lobby is for a ranked game")
- leaderboard_id: Optional[int] = Field(None, description="Leaderboard ID for the game type")
- rating_type: Optional[int] = Field(None, description="The rating ID for the game")
- resources: Optional[int] = Field(None, description="The setting for players' starting resources")
- rms: Optional[str] = Field(None, description="Unclear")
- scenario: Optional[str] = Field(None, description="The activated scenario for the game")
- server: Optional[str] = Field(None, description="The server hosting the game")
- shared_exploration: Optional[bool] = Field(None, description="Whether the map exploration is shared")
- speed: Optional[int] = Field(None, description="The game speed")
- starting_age: Optional[int] = Field(None, description="The starting age for the game")
- team_together: Optional[bool] = Field(None, description="Whether players can team up")
- team_positions: Optional[bool] = Field(None, description="Whether players start with team positions")
- treaty_length: Optional[int] = Field(None, description="Duration of the 'no attack' treaty in minutes")
- turbo: Optional[bool] = Field(None, description="Whether the game will be played in turbo mode")
- victory: Optional[int] = Field(None, description="ID of the game's victory condition")
- victory_time: Optional[int] = Field(None, description="Setting of the victory time limit")
- visibility: Optional[int] = Field(None, description="ID of the visibility setting")
- opened: Optional[int] = Field(None, description="Timestamp of the lobby's creation")
- started: Optional[Any] = Field(None, description="Timestamp of the game's start")
- finished: Optional[Any] = Field(None, description="Timestamp of the game's end")
- players: Optional[List[LobbyMember]] = Field(None, description="List of members in the lobby")
+ match_id: int | None = Field(None, description="ID attributed to the match this lobby is for")
+ lobby_id: int | None = Field(None, description="ID attributed to the lobby itself")
+ match_uuid: str | None = Field(None, description="UUID attributed to the match this lobby is for")
+ version: int | None = Field(None, description="Version number of the game patch")
+ name: str | None = Field(None, description="Name given to the lobby")
+ num_players: int | None = Field(None, description="Number of players in the lobby")
+ num_slots: int | None = Field(None, description="Number of player slots in the lobby")
+ average_rating: int | None = Field(None, description="Average rating of the members in the lobby")
+ cheats: bool | None = Field(None, description="Whether cheats are enabled")
+ full_tech_tree: bool | None = Field(None, description="Whether the full tech tree is set unlocked")
+ ending_age: int | None = Field(None, description="The last attainable age for the game")
+ expansion: str | None = Field(None, description="The expansion patch enabled")
+ game_type: int | None = Field(None, description="ID of the game type, same a leaderboard IDs")
+ has_custom_content: bool | None = Field(None, description="Whether the game has custom content")
+ has_password: bool | None = Field(None, description="Whether the lobby is password-protected")
+ lock_speed: bool | None = Field(None, description="Whether the game speed setting is locked")
+ lock_teams: bool | None = Field(None, description="Whether the player teams are locked")
+ map_size: int | None = Field(None, description="The game's map size setting")
+ map_type: int | None = Field(None, description="ID of the game's map type")
+ pop: int | None = Field(None, description="The max population setting for the game")
+ ranked: bool | None = Field(None, description="Whether the lobby is for a ranked game")
+ leaderboard_id: int | None = Field(None, description="Leaderboard ID for the game type")
+ rating_type: int | None = Field(None, description="The rating ID for the game")
+ resources: int | None = Field(None, description="The setting for players' starting resources")
+ rms: str | None = Field(None, description="Unclear")
+ scenario: str | None = Field(None, description="The activated scenario for the game")
+ server: str | None = Field(None, description="The server hosting the game")
+ shared_exploration: bool | None = Field(None, description="Whether the map exploration is shared")
+ speed: int | None = Field(None, description="The game speed")
+ starting_age: int | None = Field(None, description="The starting age for the game")
+ team_together: bool | None = Field(None, description="Whether players can team up")
+ team_positions: bool | None = Field(None, description="Whether players start with team positions")
+ treaty_length: int | None = Field(None, description="Duration of the 'no attack' treaty in minutes")
+ turbo: bool | None = Field(None, description="Whether the game will be played in turbo mode")
+ victory: int | None = Field(None, description="ID of the game's victory condition")
+ victory_time: int | None = Field(None, description="Setting of the victory time limit")
+ visibility: int | None = Field(None, description="ID of the visibility setting")
+ opened: int | None = Field(None, description="Timestamp of the lobby's creation")
+ started: Any | None = Field(None, description="Timestamp of the game's start")
+ finished: Any | None = Field(None, description="Timestamp of the game's end")
+ players: list[LobbyMember] | None = Field(None, description="List of members in the lobby")
diff --git a/aoe2netwrapper/models/match.py b/aoe2netwrapper/models/match.py
index ee76713..b18f2fc 100644
--- a/aoe2netwrapper/models/match.py
+++ b/aoe2netwrapper/models/match.py
@@ -5,5 +5,6 @@
This module contains the model objects to encapsulate the responses from the endpoint at
https://aoe2.net/api/match
"""
+
# pylint: disable=unused-import
from aoe2netwrapper.models.lobbies import MatchLobby as MatchLobby # noqa
diff --git a/aoe2netwrapper/models/match_history.py b/aoe2netwrapper/models/match_history.py
index 840c0ba..f917a90 100644
--- a/aoe2netwrapper/models/match_history.py
+++ b/aoe2netwrapper/models/match_history.py
@@ -5,5 +5,6 @@
This module contains the model objects to encapsulate the responses from the endpoint at
https://aoe2.net/api/player/matches
"""
+
# pylint: disable=unused-import
from aoe2netwrapper.models.lobbies import MatchLobby as MatchLobby # noqa
diff --git a/aoe2netwrapper/models/matches.py b/aoe2netwrapper/models/matches.py
index 6478487..f753411 100644
--- a/aoe2netwrapper/models/matches.py
+++ b/aoe2netwrapper/models/matches.py
@@ -5,5 +5,6 @@
This module contains the model objects to encapsulate the responses from the endpoint at
https://aoe2.net/api/matches
"""
+
# pylint: disable=unused-import
from aoe2netwrapper.models.lobbies import MatchLobby as MatchLobby # noqa
diff --git a/aoe2netwrapper/models/num_online.py b/aoe2netwrapper/models/num_online.py
index 8d52c46..a787a41 100644
--- a/aoe2netwrapper/models/num_online.py
+++ b/aoe2netwrapper/models/num_online.py
@@ -5,7 +5,8 @@
This module contains the model objects to encapsulate the responses from the endpoint at
https://aoe2.net/api/stats/players
"""
-from typing import List, Optional
+
+from __future__ import annotations
from pydantic import BaseModel, Field
@@ -13,25 +14,25 @@
class NumPlayers(BaseModel):
"""A model to encapsulate the metrics from the num_online API."""
- steam: Optional[int] = Field(None, description="Number of players from Steam")
- multiplayer: Optional[int] = Field(None, description="Number of people playing multiplayer")
- looking: Optional[int] = Field(None, description="Number of players currently looking for a game")
- in_game: Optional[int] = Field(None, description="Number of players currently playing")
- multiplayer_1h: Optional[int] = Field(None, description="Number of multiplayer players the past hour")
- multiplayer_24h: Optional[int] = Field(None, description="Number of multiplayer players the past 24 hour")
+ steam: int | None = Field(None, description="Number of players from Steam")
+ multiplayer: int | None = Field(None, description="Number of people playing multiplayer")
+ looking: int | None = Field(None, description="Number of players currently looking for a game")
+ in_game: int | None = Field(None, description="Number of players currently playing")
+ multiplayer_1h: int | None = Field(None, description="Number of multiplayer players the past hour")
+ multiplayer_24h: int | None = Field(None, description="Number of multiplayer players the past 24 hour")
class PlayerCountTimePoint(BaseModel):
"""A model to encapsulate a timestamp datapoint of the num_online API metrics."""
- time: Optional[int] = Field(None, description="Timestamp of the metrics data point")
- num_players: Optional[NumPlayers] = Field(None, description="A NumPlayer object with the metrics")
+ time: int | None = Field(None, description="Timestamp of the metrics data point")
+ num_players: NumPlayers | None = Field(None, description="A NumPlayer object with the metrics")
class NumOnlineResponse(BaseModel):
"""A model to encapsulate the response from the num_online API."""
- app_id: Optional[int] = Field(None, description="Unclear")
- player_stats: Optional[List[PlayerCountTimePoint]] = Field(
+ app_id: int | None = Field(None, description="Unclear")
+ player_stats: list[PlayerCountTimePoint] | None = Field(
None, description="List of metrics at different points in time"
)
diff --git a/aoe2netwrapper/models/rating_history.py b/aoe2netwrapper/models/rating_history.py
index 0c18ee3..ad8aa61 100644
--- a/aoe2netwrapper/models/rating_history.py
+++ b/aoe2netwrapper/models/rating_history.py
@@ -5,7 +5,8 @@
This module contains the model objects to encapsulate the responses from the endpoint at
https://aoe2.net/api/player/ratinghistory
"""
-from typing import Optional
+
+from __future__ import annotations
from pydantic import BaseModel, Field
@@ -13,9 +14,9 @@
class RatingTimePoint(BaseModel):
"""An object to encapsulate any entry in the list of returned ranking timestamped data points."""
- rating: Optional[int] = Field(None, description="The player's rating in the ELO system")
- num_wins: Optional[int] = Field(None, description="Total amount of wins")
- num_losses: Optional[int] = Field(None, description="Total amount of losses")
- streak: Optional[int] = Field(None, description="Current number of consecutive wins")
- drops: Optional[int] = Field(None, description="Number of games dropped out of")
- timestamp: Optional[int] = Field(None, description="Timestamp of the metrics")
+ rating: int | None = Field(None, description="The player's rating in the ELO system")
+ num_wins: int | None = Field(None, description="Total amount of wins")
+ num_losses: int | None = Field(None, description="Total amount of losses")
+ streak: int | None = Field(None, description="Current number of consecutive wins")
+ drops: int | None = Field(None, description="Number of games dropped out of")
+ timestamp: int | None = Field(None, description="Timestamp of the metrics")
diff --git a/aoe2netwrapper/models/strings.py b/aoe2netwrapper/models/strings.py
index 07f5399..f99c6fe 100644
--- a/aoe2netwrapper/models/strings.py
+++ b/aoe2netwrapper/models/strings.py
@@ -8,7 +8,8 @@
Any confusing with a specific attribute being an INTEGER in the models can be cleared by checking the
corresponding string the API / system attributes to this ID.
"""
-from typing import List, Optional
+
+from __future__ import annotations
from pydantic import BaseModel, Field
@@ -16,112 +17,112 @@
class AgeString(BaseModel):
"""An object to encapsulate any entry for the available age strings and their respective IDs."""
- id: Optional[int] = Field(None, description="ID for this specific string value")
- string: Optional[str] = Field(None, description="String value for this specific 'age' ID")
+ id: int | None = Field(None, description="ID for this specific string value")
+ string: str | None = Field(None, description="String value for this specific 'age' ID")
class CivilizationString(BaseModel):
"""An object to encapsulate any entry for the available civilization strings and their respective IDs."""
- id: Optional[int] = Field(None, description="ID for this specific string value")
- string: Optional[str] = Field(None, description="String value for this specific 'civ' ID")
+ id: int | None = Field(None, description="ID for this specific string value")
+ string: str | None = Field(None, description="String value for this specific 'civ' ID")
class GameTypeString(BaseModel):
"""An object to encapsulate any entry for the available game type strings and their respective IDs."""
- id: Optional[int] = Field(None, description="ID for this specific string value")
- string: Optional[str] = Field(None, description="String value for this specific 'game_type' ID")
+ id: int | None = Field(None, description="ID for this specific string value")
+ string: str | None = Field(None, description="String value for this specific 'game_type' ID")
class LeaderBoardString(BaseModel):
"""An object to encapsulate any entry for the available leaderboard strings and their respective IDs."""
- id: Optional[int] = Field(None, description="ID for this specific string value")
- string: Optional[str] = Field(None, description="String value for this specific 'leaderboard' ID")
+ id: int | None = Field(None, description="ID for this specific string value")
+ string: str | None = Field(None, description="String value for this specific 'leaderboard' ID")
class MapSizeString(BaseModel):
"""An object to encapsulate any entry for the available map size strings and their respective IDs."""
- id: Optional[int] = Field(None, description="ID for this specific string value")
- string: Optional[str] = Field(None, description="String value for this specific 'map_size' ID")
+ id: int | None = Field(None, description="ID for this specific string value")
+ string: str | None = Field(None, description="String value for this specific 'map_size' ID")
class MapTypeString(BaseModel):
"""An object to encapsulate any entry for the available map type strings and their respective IDs."""
- id: Optional[int] = Field(None, description="ID for this specific string value")
- string: Optional[str] = Field(None, description="String value for this specific 'map_type' ID")
+ id: int | None = Field(None, description="ID for this specific string value")
+ string: str | None = Field(None, description="String value for this specific 'map_type' ID")
class RatingTypeString(BaseModel):
"""An object to encapsulate any entry for the available rating type strings and their respective IDs."""
- id: Optional[int] = Field(None, description="ID for this specific string value")
- string: Optional[str] = Field(None, description="String value for this specific 'rating_type' ID")
+ id: int | None = Field(None, description="ID for this specific string value")
+ string: str | None = Field(None, description="String value for this specific 'rating_type' ID")
class ResourcesString(BaseModel):
"""An object to encapsulate any entry for the available resources strings and their respective IDs."""
- id: Optional[int] = Field(None, description="ID for this specific string value")
- string: Optional[str] = Field(None, description="String value for this specific 'resources' ID")
+ id: int | None = Field(None, description="ID for this specific string value")
+ string: str | None = Field(None, description="String value for this specific 'resources' ID")
class SpeedString(BaseModel):
"""An object to encapsulate any entry for the available speed strings and their respective IDs."""
- id: Optional[int] = Field(None, description="ID for this specific string value")
- string: Optional[str] = Field(None, description="String value for this specific 'speed' ID")
+ id: int | None = Field(None, description="ID for this specific string value")
+ string: str | None = Field(None, description="String value for this specific 'speed' ID")
class VictoryString(BaseModel):
"""An object to encapsulate any entry for the available victory strings and their respective IDs."""
- id: Optional[int] = Field(None, description="ID for this specific string value")
- string: Optional[str] = Field(None, description="String value for this specific 'victory' ID")
+ id: int | None = Field(None, description="ID for this specific string value")
+ string: str | None = Field(None, description="String value for this specific 'victory' ID")
class VisibilityString(BaseModel):
"""An object to encapsulate any entry for the available visibiliity strings and their respective IDs."""
- id: Optional[int] = Field(None, description="ID for this specific string value")
- string: Optional[str] = Field(None, description="String value for this specific 'visibility' ID")
+ id: int | None = Field(None, description="ID for this specific string value")
+ string: str | None = Field(None, description="String value for this specific 'visibility' ID")
class StringsResponse(BaseModel):
"""An object to encapsulate the response from the strings API endpoint."""
- language: Optional[str] = Field(None, description="Language of the returned strings")
- age: Optional[List[AgeString]] = Field(None, description="List of all strings and their IDs for ages")
- civ: Optional[List[CivilizationString]] = Field(
+ language: str | None = Field(None, description="Language of the returned strings")
+ age: list[AgeString] | None = Field(None, description="List of all strings and their IDs for ages")
+ civ: list[CivilizationString] | None = Field(
None, description="List of all strings and their IDs for civilizations"
)
- game_type: Optional[List[GameTypeString]] = Field(
+ game_type: list[GameTypeString] | None = Field(
None, description="List of all strings and their IDs for game types"
)
- leaderboard: Optional[List[LeaderBoardString]] = Field(
+ leaderboard: list[LeaderBoardString] | None = Field(
None, description="List of all strings and their IDs for leaderboards"
)
- map_size: Optional[List[MapSizeString]] = Field(
+ map_size: list[MapSizeString] | None = Field(
None, description="List of all strings and their IDs for map sizes"
)
- map_type: Optional[List[MapTypeString]] = Field(
+ map_type: list[MapTypeString] | None = Field(
None, description="List of all strings and their IDs for map types"
)
- rating_type: Optional[List[RatingTypeString]] = Field(
+ rating_type: list[RatingTypeString] | None = Field(
None, description="List of all strings and their IDs for rating types"
)
- resources: Optional[List[ResourcesString]] = Field(
+ resources: list[ResourcesString] | None = Field(
None, description="List of all strings and their IDs for resources"
)
- speed: Optional[List[SpeedString]] = Field(
+ speed: list[SpeedString] | None = Field(
None, description="List of all strings and their IDs for game speeds"
)
- victory: Optional[List[VictoryString]] = Field(
+ victory: list[VictoryString] | None = Field(
None, description="List of all strings and their IDs for victory types"
)
- visibility: Optional[List[VisibilityString]] = Field(
+ visibility: list[VisibilityString] | None = Field(
None, description="List of all strings and their IDs for visibility"
)
diff --git a/aoe2netwrapper/nightbot.py b/aoe2netwrapper/nightbot.py
index 1212a1b..7e27516 100644
--- a/aoe2netwrapper/nightbot.py
+++ b/aoe2netwrapper/nightbot.py
@@ -5,13 +5,17 @@
This module implements a high-level client to query the API at https://aoe2.net/#nightbot.
"""
-from typing import Any, Dict, Tuple, Union
+from __future__ import annotations
+
+from typing import Any
import requests
from loguru import logger
-from aoe2netwrapper.exceptions import NightBotException
+from aoe2netwrapper.exceptions import NightBotError
+
+_OK_STATUS_CODE: int = 200
class AoE2NightbotAPI:
@@ -27,7 +31,7 @@ class AoE2NightbotAPI:
CURRENT_CIVS_ENDPOINT = NIGHTBOT_BASE_URL + "/civs"
CURRENT_MAP_ENDPOINT = NIGHTBOT_BASE_URL + "/map"
- def __init__(self, timeout: Union[float, Tuple[float, float]] = 5):
+ def __init__(self, timeout: float | tuple[float, float] = 5):
"""Creating a Session for connection pooling since we're always querying the same host."""
self.session = requests.Session()
self.timeout = timeout
@@ -41,9 +45,9 @@ def rank(
leaderboard_id: int = 3,
language: str = "en",
flag: str = "true",
- search: str = None,
- steam_id: int = None,
- profile_id: int = None,
+ search: str | None = None,
+ steam_id: int | None = None,
+ profile_id: int | None = None,
) -> str:
"""
Request rank details about a player. Either 'search', 'steam_id' or 'profile_id' required.
@@ -68,7 +72,7 @@ def rank(
(ex: 459658).
Raises:
- NightBotException: if the not one of 'search', 'steam_id' or 'profile_id' are provided.
+ NightBotError: if the not one of 'search', 'steam_id' or 'profile_id' are provided.
Returns:
The text content of the response, as a decoded unicode string, with a quick sentence
@@ -76,9 +80,8 @@ def rank(
"""
if not any((search, steam_id, profile_id)):
logger.error("Missing one of 'search', 'steam_id', 'profile_id'.")
- raise NightBotException(
- "Either 'search', 'steam_id' or 'profile_id' required, please provide one."
- )
+ msg = "Either 'search', 'steam_id' or 'profile_id' required, please provide one."
+ raise NightBotError(msg)
logger.debug("Preparing parameters for rank details query")
query_params = {
@@ -104,9 +107,9 @@ def opponent(
leaderboard_id: int = 3,
language: str = "en",
flag: str = "true",
- search: str = None,
- steam_id: int = None,
- profile_id: int = None,
+ search: str | None = None,
+ steam_id: int | None = None,
+ profile_id: int | None = None,
) -> str:
"""
Request rank details about a player's most recent opponent (1v1 only). Either 'search',
@@ -132,7 +135,7 @@ def opponent(
(ex: 459658).
Raises:
- NightBotException: if the not one of 'search', 'steam_id' or 'profile_id' are provided.
+ NightBotError: if the not one of 'search', 'steam_id' or 'profile_id' are provided.
Returns:
The text content of the response, as a decoded unicode string, with a quick sentence
@@ -140,9 +143,8 @@ def opponent(
"""
if not any((search, steam_id, profile_id)):
logger.error("Missing one of 'search', 'steam_id', 'profile_id'.")
- raise NightBotException(
- "Either 'search', 'steam_id' or 'profile_id' required, please provide one."
- )
+ msg = "Either 'search', 'steam_id' or 'profile_id' required, please provide one."
+ raise NightBotError(msg)
logger.debug("Preparing parameters for opponent details query")
query_params = {
@@ -169,9 +171,9 @@ def match(
language: str = "en",
color: str = "true",
flag: str = "true",
- search: str = None,
- steam_id: int = None,
- profile_id: int = None,
+ search: str | None = None,
+ steam_id: int | None = None,
+ profile_id: int | None = None,
) -> str:
"""
Request details about the current or last match. Either 'search', 'steam_id' or
@@ -199,7 +201,7 @@ def match(
(ex: 459658).
Raises:
- NightBotException: if the not one of 'search', 'steam_id' or 'profile_id' are provided.
+ NightBotError: if the not one of 'search', 'steam_id' or 'profile_id' are provided.
Returns:
The text content of the response, as a decoded unicode string, with a quick sentence
@@ -207,9 +209,8 @@ def match(
"""
if not any((search, steam_id, profile_id)):
logger.error("Missing one of 'search', 'steam_id', 'profile_id'.")
- raise NightBotException(
- "Either 'search', 'steam_id' or 'profile_id' required, please provide one."
- )
+ msg = "Either 'search', 'steam_id' or 'profile_id' required, please provide one."
+ raise NightBotError(msg)
logger.debug("Preparing parameters for match details query")
query_params = {
@@ -235,9 +236,9 @@ def civs(
game: str = "aoe2de",
leaderboard_id: int = 3,
language: str = "en",
- search: str = None,
- steam_id: int = None,
- profile_id: int = None,
+ search: str | None = None,
+ steam_id: int | None = None,
+ profile_id: int | None = None,
) -> str:
"""
Request civilisations from the current or last match. Either 'search', 'steam_id' or
@@ -261,7 +262,7 @@ def civs(
(ex: 459658).
Raises:
- NightBotException: if the not one of 'search', 'steam_id' or 'profile_id' are provided.
+ NightBotError: if the not one of 'search', 'steam_id' or 'profile_id' are provided.
Returns:
The text content of the response, as a decoded unicode string, with a quick sentence
@@ -269,9 +270,8 @@ def civs(
"""
if not any((search, steam_id, profile_id)):
logger.error("Missing one of 'search', 'steam_id', 'profile_id'.")
- raise NightBotException(
- "Either 'search', 'steam_id' or 'profile_id' required, please provide one."
- )
+ msg = "Either 'search', 'steam_id' or 'profile_id' required, please provide one."
+ raise NightBotError(msg)
logger.debug("Preparing parameters for civilisations details query")
query_params = {
@@ -295,9 +295,9 @@ def map(
game: str = "aoe2de",
leaderboard_id: int = 3,
language: str = "en",
- search: str = None,
- steam_id: int = None,
- profile_id: int = None,
+ search: str | None = None,
+ steam_id: int | None = None,
+ profile_id: int | None = None,
) -> str:
"""
Request civilisations from the current or last match. Either 'search', 'steam_id' or
@@ -321,7 +321,7 @@ def map(
(ex: 459658).
Raises:
- NightBotException: if the not one of 'search', 'steam_id' or 'profile_id' are provided.
+ NightBotError: if the not one of 'search', 'steam_id' or 'profile_id' are provided.
Returns:
The text content of the response, as a decoded unicode string, with a quick sentence
@@ -329,9 +329,8 @@ def map(
"""
if not any((search, steam_id, profile_id)):
logger.error("Missing one of 'search', 'steam_id', 'profile_id'.")
- raise NightBotException(
- "Either 'search', 'steam_id' or 'profile_id' required, please provide one."
- )
+ msg = "Either 'search', 'steam_id' or 'profile_id' required, please provide one."
+ raise NightBotError(msg)
logger.debug("Preparing parameters for civilisations details query")
query_params = {
@@ -357,8 +356,8 @@ def map(
def _get_request_text_response_decoded(
session: requests.Session,
url: str,
- params: Dict[str, Any] = None,
- timeout: Union[float, Tuple[float, float]] = None,
+ params: dict[str, Any] | None = None,
+ timeout: float | tuple[float, float] | None = None,
) -> str:
"""
Helper function to handle a GET request to an endpoint and return the response JSON content
@@ -370,17 +369,18 @@ def _get_request_text_response_decoded(
params (dict): A dictionary of parameters for the GET request.
Raises:
- Aoe2NetException: if the status code returned is not 200.
+ NightBotError: if the status code returned is not 200.
Returns:
The request's JSON response as a dictionary.
"""
default_headers = {"content-type": "application/json;charset=UTF-8"}
logger.debug(f"Sending GET request at '{url}'")
- logger.trace(f"Parameters are: {str(params)}")
+ logger.trace(f"Parameters are: {params!s}")
response = session.get(url, params=params, headers=default_headers, timeout=timeout)
- if response.status_code != 200:
+ if response.status_code != _OK_STATUS_CODE:
logger.error(f"GET request at '{response.url}' returned a {response.status_code} status code")
- raise NightBotException(f"Expected status code 200 - got {response.status_code} instead.")
+ msg = f"Expected status code 200 - got {response.status_code} instead."
+ raise NightBotError(msg)
return response.text
diff --git a/docs/Features.md b/docs/Features.md
index 1a2713c..ca3ad04 100644
--- a/docs/Features.md
+++ b/docs/Features.md
@@ -25,7 +25,7 @@ As for example below, the `profile_id` key inside a `LeaderBoardSpot` inside a `
All the validation is handled by the well-established and robust [pydantic][pydantic_repo]{target=_blank}.
??? tip "Models Integration"
- All the other goodies from the returned pydantic models are available to you: exporting options, deep copies, ORM integration, etc.
+ All the other goodies from the returned `pydantic` models are available to you: exporting options, deep copies, ORM integration, etc.
## Built-In Results Conversion to Pandas DataFrames
@@ -37,6 +37,5 @@ The class, `Convert`, provides static methods taking in the direct output given
* 100% test coverage.
* A codebase making use of the amazing logging from [loguru][loguru_repo]{target=_blank}, which can be integrated and extended.
-
[pydantic_repo]: https://github.com/samuelcolvin/pydantic
-[loguru_repo]: https://github.com/delgan/loguru
\ No newline at end of file
+[loguru_repo]: https://github.com/delgan/loguru
diff --git a/docs/Getting_Started.md b/docs/Getting_Started.md
index 634c59f..d6e44b9 100644
--- a/docs/Getting_Started.md
+++ b/docs/Getting_Started.md
@@ -1,12 +1,17 @@
# Getting Started
+!!! Warning "Supported API Endpoints"
+ Between versions `0.3.1` and `0.4.0` of this package, several API endpoints were removed from `aoe2.net`.
+ As a consequence, the respective methods in this package were deprecated, and now raise an error when called.
+ Should you notice code crashing after updating: please know it is a reflection of `aoe2.net`'s API changes.
+
## Installation
-This code is compatible with `Python 3.7+`.
-This package is installable from PyPI with [pip]{target=_blank} in a virtual environment with:
+This package is compatible with all currently supported Python versions.
+It is installable from `PyPI` with [pip]{target=_blank} in your virtual environment with:
```bash
-pip install aoe2netwrapper
+python -m pip install aoe2netwrapper
```
The package is also accessible from the `conda-forge` channel, and can be installed in a `conda` environment with:
@@ -22,30 +27,30 @@ conda install -c conda-forge aoe2netwrapper
??? info "Extra Dependencies"
It is also possible to install with extra dependencies to access export functionnality from the package, with:
```bash
- pip install aoe2netwrapper[dataframe]
+ python -m pip install aoe2netwrapper[dataframe]
```
??? question "How about a development environment?"
-
- Sure thing. This repository uses [Poetry]{target=_blank} as a packaging and build tool.
+ Sure thing. This repository uses [Hatch](https://github.com/pypa/hatch/){target=_blank} as a packaging and build tool, though it is not strictly necessary.
To set yourself up, get a local copy through VCS and run:
-
+
```bash
- poetry install
+ python -m pip install --editable ".[all]"
```
- This repository follows the `Google` docstring format, uses [Black][black_formatter] as a code formatter with a default enforced line length of 110 characters, and [Pylint][pylint_ref] as a linter.
- You can format the code with `make format` and lint it (which will format first) with `make lint`.
+ The package will be installed in editable mode, alongside all dependencies (tests, docs).
+ This repository follows the `Google` docstring format, uses [Black](https://github.com/psf/black/){target=_blank} as a code formatter with a default enforced line length of 110 characters, and [Ruff](https://github.com/astral-sh/ruff/){target=_blank} as a linter.
+ A Makefile is included with some useful commands, which one can list with `make help`.
Testing builds are ensured after each commit through Github Actions.
- You can run tests locally with the predefined `make tests`, or through `poetry run pytest ` for customized options.
+ You can run tests locally with the predefined `make tests`.
## Quick Start
-The package provides a simple, fully-typed high-level object to interact with each API provided by `aoe2.net`.
+The package provides a simple, fully-typed high-level object to interact with each API provided by [aoe2.net](https://aoe2.net/){target=_blank}.
Each exposed endpoint from the APIs can be queried with a dedicated method named after it:
-* __Complete Data API__
+* **Complete Data API**
```python
from aoe2netwrapper import AoE2NetAPI
@@ -62,7 +67,7 @@ top_accounts = client.leaderboard(
open_lobbies = client.lobbies(game="aoe2de")
```
-* __Nightbot API__
+* **Nightbot API**
```python
from aoe2netwrapper import AoE2NightbotAPI
@@ -76,7 +81,7 @@ print(viper_details)
# '🇳🇴 GL.TheViper (2501) Rank #1, has played 762 games with a 69% winrate, -1 streak, and 2 drops'
```
-* __Converting Results to Pandas DataFrames__
+* **Converting Results to Pandas DataFrames**
```python
from aoe2netwrapper import AoE2NetAPI
@@ -92,9 +97,5 @@ lobbies_dframe = Convert.lobbies(open_lobbies)
The full documentation for the API endpoints can be found at https://aoe2.net/#api and https://aoe2.net/#nightbot.
For convenience, it is also included in the methods' docstrings, and can be accessed in the `Reference` section.
-
-[virtual_env_primer]: https://realpython.com/python-virtual-environments-a-primer/
-[black_formatter]: https://github.com/psf/black
[pip]: https://pip.pypa.io/en/stable/
-[Poetry]: https://python-poetry.org/
-[pylint_ref]: https://www.pylint.org/
+[virtual_env_primer]: https://realpython.com/python-virtual-environments-a-primer/
\ No newline at end of file
diff --git a/poetry.lock b/poetry.lock
deleted file mode 100644
index 4f9a40b..0000000
--- a/poetry.lock
+++ /dev/null
@@ -1,1682 +0,0 @@
-# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
-
-[[package]]
-name = "appdirs"
-version = "1.4.4"
-description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
-optional = false
-python-versions = "*"
-files = [
- {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"},
- {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"},
-]
-
-[[package]]
-name = "astroid"
-version = "2.11.7"
-description = "An abstract syntax tree for Python with inference support."
-optional = false
-python-versions = ">=3.6.2"
-files = [
- {file = "astroid-2.11.7-py3-none-any.whl", hash = "sha256:86b0a340a512c65abf4368b80252754cda17c02cdbbd3f587dddf98112233e7b"},
- {file = "astroid-2.11.7.tar.gz", hash = "sha256:bb24615c77f4837c707669d16907331374ae8a964650a66999da3f5ca68dc946"},
-]
-
-[package.dependencies]
-lazy-object-proxy = ">=1.4.0"
-setuptools = ">=20.0"
-typed-ast = {version = ">=1.4.0,<2.0", markers = "implementation_name == \"cpython\" and python_version < \"3.8\""}
-typing-extensions = {version = ">=3.10", markers = "python_version < \"3.10\""}
-wrapt = ">=1.11,<2"
-
-[[package]]
-name = "atomicwrites"
-version = "1.4.1"
-description = "Atomic file writes."
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
-files = [
- {file = "atomicwrites-1.4.1.tar.gz", hash = "sha256:81b2c9071a49367a7f770170e5eec8cb66567cfbbc8c73d20ce5ca4a8d71cf11"},
-]
-
-[[package]]
-name = "attrs"
-version = "23.2.0"
-description = "Classes Without Boilerplate"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"},
- {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"},
-]
-
-[package.dependencies]
-importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
-
-[package.extras]
-cov = ["attrs[tests]", "coverage[toml] (>=5.3)"]
-dev = ["attrs[tests]", "pre-commit"]
-docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"]
-tests = ["attrs[tests-no-zope]", "zope-interface"]
-tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"]
-tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"]
-
-[[package]]
-name = "black"
-version = "20.8b1"
-description = "The uncompromising code formatter."
-optional = false
-python-versions = ">=3.6"
-files = [
- {file = "black-20.8b1.tar.gz", hash = "sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea"},
-]
-
-[package.dependencies]
-appdirs = "*"
-click = ">=7.1.2"
-mypy-extensions = ">=0.4.3"
-pathspec = ">=0.6,<1"
-regex = ">=2020.1.8"
-toml = ">=0.10.1"
-typed-ast = ">=1.4.0"
-typing-extensions = ">=3.7.4"
-
-[package.extras]
-colorama = ["colorama (>=0.4.3)"]
-d = ["aiohttp (>=3.3.2)", "aiohttp-cors"]
-
-[[package]]
-name = "certifi"
-version = "2024.6.2"
-description = "Python package for providing Mozilla's CA Bundle."
-optional = false
-python-versions = ">=3.6"
-files = [
- {file = "certifi-2024.6.2-py3-none-any.whl", hash = "sha256:ddc6c8ce995e6987e7faf5e3f1b02b302836a0e5d98ece18392cb1a36c72ad56"},
- {file = "certifi-2024.6.2.tar.gz", hash = "sha256:3cd43f1c6fa7dedc5899d69d3ad0398fd018ad1a17fba83ddaf78aa46c747516"},
-]
-
-[[package]]
-name = "charset-normalizer"
-version = "3.3.2"
-description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
-optional = false
-python-versions = ">=3.7.0"
-files = [
- {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"},
- {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"},
- {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"},
- {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"},
- {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"},
- {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"},
- {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"},
- {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"},
- {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"},
- {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"},
- {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"},
- {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"},
- {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"},
- {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"},
- {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"},
-]
-
-[[package]]
-name = "click"
-version = "8.1.7"
-description = "Composable command line interface toolkit"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"},
- {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"},
-]
-
-[package.dependencies]
-colorama = {version = "*", markers = "platform_system == \"Windows\""}
-importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
-
-[[package]]
-name = "colorama"
-version = "0.4.6"
-description = "Cross-platform colored terminal text."
-optional = false
-python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
-files = [
- {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
- {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
-]
-
-[[package]]
-name = "coverage"
-version = "7.2.7"
-description = "Code coverage measurement for Python"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "coverage-7.2.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d39b5b4f2a66ccae8b7263ac3c8170994b65266797fb96cbbfd3fb5b23921db8"},
- {file = "coverage-7.2.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6d040ef7c9859bb11dfeb056ff5b3872436e3b5e401817d87a31e1750b9ae2fb"},
- {file = "coverage-7.2.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba90a9563ba44a72fda2e85302c3abc71c5589cea608ca16c22b9804262aaeb6"},
- {file = "coverage-7.2.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7d9405291c6928619403db1d10bd07888888ec1abcbd9748fdaa971d7d661b2"},
- {file = "coverage-7.2.7-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31563e97dae5598556600466ad9beea39fb04e0229e61c12eaa206e0aa202063"},
- {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ebba1cd308ef115925421d3e6a586e655ca5a77b5bf41e02eb0e4562a111f2d1"},
- {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cb017fd1b2603ef59e374ba2063f593abe0fc45f2ad9abdde5b4d83bd922a353"},
- {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62a5c7dad11015c66fbb9d881bc4caa5b12f16292f857842d9d1871595f4495"},
- {file = "coverage-7.2.7-cp310-cp310-win32.whl", hash = "sha256:ee57190f24fba796e36bb6d3aa8a8783c643d8fa9760c89f7a98ab5455fbf818"},
- {file = "coverage-7.2.7-cp310-cp310-win_amd64.whl", hash = "sha256:f75f7168ab25dd93110c8a8117a22450c19976afbc44234cbf71481094c1b850"},
- {file = "coverage-7.2.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:06a9a2be0b5b576c3f18f1a241f0473575c4a26021b52b2a85263a00f034d51f"},
- {file = "coverage-7.2.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5baa06420f837184130752b7c5ea0808762083bf3487b5038d68b012e5937dbe"},
- {file = "coverage-7.2.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdec9e8cbf13a5bf63290fc6013d216a4c7232efb51548594ca3631a7f13c3a3"},
- {file = "coverage-7.2.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:52edc1a60c0d34afa421c9c37078817b2e67a392cab17d97283b64c5833f427f"},
- {file = "coverage-7.2.7-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63426706118b7f5cf6bb6c895dc215d8a418d5952544042c8a2d9fe87fcf09cb"},
- {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:afb17f84d56068a7c29f5fa37bfd38d5aba69e3304af08ee94da8ed5b0865833"},
- {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:48c19d2159d433ccc99e729ceae7d5293fbffa0bdb94952d3579983d1c8c9d97"},
- {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0e1f928eaf5469c11e886fe0885ad2bf1ec606434e79842a879277895a50942a"},
- {file = "coverage-7.2.7-cp311-cp311-win32.whl", hash = "sha256:33d6d3ea29d5b3a1a632b3c4e4f4ecae24ef170b0b9ee493883f2df10039959a"},
- {file = "coverage-7.2.7-cp311-cp311-win_amd64.whl", hash = "sha256:5b7540161790b2f28143191f5f8ec02fb132660ff175b7747b95dcb77ac26562"},
- {file = "coverage-7.2.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f2f67fe12b22cd130d34d0ef79206061bfb5eda52feb6ce0dba0644e20a03cf4"},
- {file = "coverage-7.2.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a342242fe22407f3c17f4b499276a02b01e80f861f1682ad1d95b04018e0c0d4"},
- {file = "coverage-7.2.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:171717c7cb6b453aebac9a2ef603699da237f341b38eebfee9be75d27dc38e01"},
- {file = "coverage-7.2.7-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49969a9f7ffa086d973d91cec8d2e31080436ef0fb4a359cae927e742abfaaa6"},
- {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b46517c02ccd08092f4fa99f24c3b83d8f92f739b4657b0f146246a0ca6a831d"},
- {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:a3d33a6b3eae87ceaefa91ffdc130b5e8536182cd6dfdbfc1aa56b46ff8c86de"},
- {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:976b9c42fb2a43ebf304fa7d4a310e5f16cc99992f33eced91ef6f908bd8f33d"},
- {file = "coverage-7.2.7-cp312-cp312-win32.whl", hash = "sha256:8de8bb0e5ad103888d65abef8bca41ab93721647590a3f740100cd65c3b00511"},
- {file = "coverage-7.2.7-cp312-cp312-win_amd64.whl", hash = "sha256:9e31cb64d7de6b6f09702bb27c02d1904b3aebfca610c12772452c4e6c21a0d3"},
- {file = "coverage-7.2.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:58c2ccc2f00ecb51253cbe5d8d7122a34590fac9646a960d1430d5b15321d95f"},
- {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d22656368f0e6189e24722214ed8d66b8022db19d182927b9a248a2a8a2f67eb"},
- {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a895fcc7b15c3fc72beb43cdcbdf0ddb7d2ebc959edac9cef390b0d14f39f8a9"},
- {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e84606b74eb7de6ff581a7915e2dab7a28a0517fbe1c9239eb227e1354064dcd"},
- {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:0a5f9e1dbd7fbe30196578ca36f3fba75376fb99888c395c5880b355e2875f8a"},
- {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:419bfd2caae268623dd469eff96d510a920c90928b60f2073d79f8fe2bbc5959"},
- {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2aee274c46590717f38ae5e4650988d1af340fe06167546cc32fe2f58ed05b02"},
- {file = "coverage-7.2.7-cp37-cp37m-win32.whl", hash = "sha256:61b9a528fb348373c433e8966535074b802c7a5d7f23c4f421e6c6e2f1697a6f"},
- {file = "coverage-7.2.7-cp37-cp37m-win_amd64.whl", hash = "sha256:b1c546aca0ca4d028901d825015dc8e4d56aac4b541877690eb76490f1dc8ed0"},
- {file = "coverage-7.2.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:54b896376ab563bd38453cecb813c295cf347cf5906e8b41d340b0321a5433e5"},
- {file = "coverage-7.2.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3d376df58cc111dc8e21e3b6e24606b5bb5dee6024f46a5abca99124b2229ef5"},
- {file = "coverage-7.2.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e330fc79bd7207e46c7d7fd2bb4af2963f5f635703925543a70b99574b0fea9"},
- {file = "coverage-7.2.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e9d683426464e4a252bf70c3498756055016f99ddaec3774bf368e76bbe02b6"},
- {file = "coverage-7.2.7-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d13c64ee2d33eccf7437961b6ea7ad8673e2be040b4f7fd4fd4d4d28d9ccb1e"},
- {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b7aa5f8a41217360e600da646004f878250a0d6738bcdc11a0a39928d7dc2050"},
- {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8fa03bce9bfbeeef9f3b160a8bed39a221d82308b4152b27d82d8daa7041fee5"},
- {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:245167dd26180ab4c91d5e1496a30be4cd721a5cf2abf52974f965f10f11419f"},
- {file = "coverage-7.2.7-cp38-cp38-win32.whl", hash = "sha256:d2c2db7fd82e9b72937969bceac4d6ca89660db0a0967614ce2481e81a0b771e"},
- {file = "coverage-7.2.7-cp38-cp38-win_amd64.whl", hash = "sha256:2e07b54284e381531c87f785f613b833569c14ecacdcb85d56b25c4622c16c3c"},
- {file = "coverage-7.2.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:537891ae8ce59ef63d0123f7ac9e2ae0fc8b72c7ccbe5296fec45fd68967b6c9"},
- {file = "coverage-7.2.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:06fb182e69f33f6cd1d39a6c597294cff3143554b64b9825d1dc69d18cc2fff2"},
- {file = "coverage-7.2.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:201e7389591af40950a6480bd9edfa8ed04346ff80002cec1a66cac4549c1ad7"},
- {file = "coverage-7.2.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f6951407391b639504e3b3be51b7ba5f3528adbf1a8ac3302b687ecababf929e"},
- {file = "coverage-7.2.7-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f48351d66575f535669306aa7d6d6f71bc43372473b54a832222803eb956fd1"},
- {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b29019c76039dc3c0fd815c41392a044ce555d9bcdd38b0fb60fb4cd8e475ba9"},
- {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:81c13a1fc7468c40f13420732805a4c38a105d89848b7c10af65a90beff25250"},
- {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:975d70ab7e3c80a3fe86001d8751f6778905ec723f5b110aed1e450da9d4b7f2"},
- {file = "coverage-7.2.7-cp39-cp39-win32.whl", hash = "sha256:7ee7d9d4822c8acc74a5e26c50604dff824710bc8de424904c0982e25c39c6cb"},
- {file = "coverage-7.2.7-cp39-cp39-win_amd64.whl", hash = "sha256:eb393e5ebc85245347950143969b241d08b52b88a3dc39479822e073a1a8eb27"},
- {file = "coverage-7.2.7-pp37.pp38.pp39-none-any.whl", hash = "sha256:b7b4c971f05e6ae490fef852c218b0e79d4e52f79ef0c8475566584a8fb3e01d"},
- {file = "coverage-7.2.7.tar.gz", hash = "sha256:924d94291ca674905fe9481f12294eb11f2d3d3fd1adb20314ba89e94f44ed59"},
-]
-
-[package.extras]
-toml = ["tomli"]
-
-[[package]]
-name = "dill"
-version = "0.3.7"
-description = "serialize all of Python"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "dill-0.3.7-py3-none-any.whl", hash = "sha256:76b122c08ef4ce2eedcd4d1abd8e641114bfc6c2867f49f3c41facf65bf19f5e"},
- {file = "dill-0.3.7.tar.gz", hash = "sha256:cc1c8b182eb3013e24bd475ff2e9295af86c1a38eb1aff128dac8962a9ce3c03"},
-]
-
-[package.extras]
-graph = ["objgraph (>=1.7.2)"]
-
-[[package]]
-name = "docstring-parser"
-version = "0.16"
-description = "Parse Python docstrings in reST, Google and Numpydoc format"
-optional = true
-python-versions = ">=3.6,<4.0"
-files = [
- {file = "docstring_parser-0.16-py3-none-any.whl", hash = "sha256:bf0a1387354d3691d102edef7ec124f219ef639982d096e26e3b60aeffa90637"},
- {file = "docstring_parser-0.16.tar.gz", hash = "sha256:538beabd0af1e2db0146b6bd3caa526c35a34d61af9fd2887f3a8a27a739aa6e"},
-]
-
-[[package]]
-name = "falcon"
-version = "2.0.0"
-description = "An unladen web framework for building APIs and app backends."
-optional = true
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
-files = [
- {file = "falcon-2.0.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:733033ec80c896e30a43ab3e776856096836787197a44eb21022320a61311983"},
- {file = "falcon-2.0.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:f93351459f110b4c1ee28556aef9a791832df6f910bea7b3f616109d534df06b"},
- {file = "falcon-2.0.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:e9efa0791b5d9f9dd9689015ea6bce0a27fcd5ecbcd30e6d940bffa4f7f03389"},
- {file = "falcon-2.0.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:59d1e8c993b9a37ea06df9d72cf907a46cc8063b30717cdac2f34d1658b6f936"},
- {file = "falcon-2.0.0-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:a5ebb22a04c9cc65081938ee7651b4e3b4d2a28522ea8ec04c7bdd2b3e9e8cd8"},
- {file = "falcon-2.0.0-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:95bf6ce986c1119aef12c9b348f4dee9c6dcc58391bdd0bc2b0bf353c2b15986"},
- {file = "falcon-2.0.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:aa184895d1ad4573fbfaaf803563d02f019ebdf4790e41cc568a330607eae439"},
- {file = "falcon-2.0.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:74cf1d18207381c665b9e6292d65100ce146d958707793174b03869dc6e614f4"},
- {file = "falcon-2.0.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:24adcd2b29a8ffa9d552dc79638cd21736a3fb04eda7d102c6cebafdaadb88ad"},
- {file = "falcon-2.0.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:18157af2a4fc3feedf2b5dcc6196f448639acf01c68bc33d4d5a04c3ef87f494"},
- {file = "falcon-2.0.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:e3782b7b92fefd46a6ad1fd8fe63fe6c6f1b7740a95ca56957f48d1aee34b357"},
- {file = "falcon-2.0.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:9712975adcf8c6e12876239085ad757b8fdeba223d46d23daef82b47658f83a9"},
- {file = "falcon-2.0.0-py2.py3-none-any.whl", hash = "sha256:54f2cb4b687035b2a03206dbfc538055cc48b59a953187b0458aa1b574d47b53"},
- {file = "falcon-2.0.0.tar.gz", hash = "sha256:eea593cf466b9c126ce667f6d30503624ef24459f118c75594a69353b6c3d5fc"},
-]
-
-[[package]]
-name = "ghp-import"
-version = "2.1.0"
-description = "Copy your docs directly to the gh-pages branch."
-optional = true
-python-versions = "*"
-files = [
- {file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"},
- {file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"},
-]
-
-[package.dependencies]
-python-dateutil = ">=2.8.1"
-
-[package.extras]
-dev = ["flake8", "markdown", "twine", "wheel"]
-
-[[package]]
-name = "gitdb"
-version = "4.0.11"
-description = "Git Object Database"
-optional = true
-python-versions = ">=3.7"
-files = [
- {file = "gitdb-4.0.11-py3-none-any.whl", hash = "sha256:81a3407ddd2ee8df444cbacea00e2d038e40150acfa3001696fe0dcf1d3adfa4"},
- {file = "gitdb-4.0.11.tar.gz", hash = "sha256:bf5421126136d6d0af55bc1e7c1af1c397a34f5b7bd79e776cd3e89785c2b04b"},
-]
-
-[package.dependencies]
-smmap = ">=3.0.1,<6"
-
-[[package]]
-name = "gitpython"
-version = "3.1.43"
-description = "GitPython is a Python library used to interact with Git repositories"
-optional = true
-python-versions = ">=3.7"
-files = [
- {file = "GitPython-3.1.43-py3-none-any.whl", hash = "sha256:eec7ec56b92aad751f9912a73404bc02ba212a23adb2c7098ee668417051a1ff"},
- {file = "GitPython-3.1.43.tar.gz", hash = "sha256:35f314a9f878467f5453cc1fee295c3e18e52f1b99f10f6cf5b1682e968a9e7c"},
-]
-
-[package.dependencies]
-gitdb = ">=4.0.1,<5"
-typing-extensions = {version = ">=3.7.4.3", markers = "python_version < \"3.8\""}
-
-[package.extras]
-doc = ["sphinx (==4.3.2)", "sphinx-autodoc-typehints", "sphinx-rtd-theme", "sphinxcontrib-applehelp (>=1.0.2,<=1.0.4)", "sphinxcontrib-devhelp (==1.0.2)", "sphinxcontrib-htmlhelp (>=2.0.0,<=2.0.1)", "sphinxcontrib-qthelp (==1.0.3)", "sphinxcontrib-serializinghtml (==1.1.5)"]
-test = ["coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock", "mypy", "pre-commit", "pytest (>=7.3.1)", "pytest-cov", "pytest-instafail", "pytest-mock", "pytest-sugar", "typing-extensions"]
-
-[[package]]
-name = "hug"
-version = "2.6.1"
-description = "A Python framework that makes developing APIs as simple as possible, but no simpler."
-optional = true
-python-versions = ">=3.5"
-files = [
- {file = "hug-2.6.1-py2.py3-none-any.whl", hash = "sha256:31c8fc284f81377278629a4b94cbb619ae9ce829cdc2da9564ccc66a121046b4"},
- {file = "hug-2.6.1.tar.gz", hash = "sha256:b0edace2acb618873779c9ce6ecf9165db54fef95c22262f5700fcdd9febaec9"},
-]
-
-[package.dependencies]
-falcon = "2.0.0"
-requests = "*"
-
-[[package]]
-name = "idna"
-version = "3.7"
-description = "Internationalized Domain Names in Applications (IDNA)"
-optional = false
-python-versions = ">=3.5"
-files = [
- {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"},
- {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"},
-]
-
-[[package]]
-name = "importlib-metadata"
-version = "6.7.0"
-description = "Read metadata from Python packages"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "importlib_metadata-6.7.0-py3-none-any.whl", hash = "sha256:cb52082e659e97afc5dac71e79de97d8681de3aa07ff18578330904a9d18e5b5"},
- {file = "importlib_metadata-6.7.0.tar.gz", hash = "sha256:1aaf550d4f73e5d6783e7acb77aec43d49da8017410afae93822cc9cca98c4d4"},
-]
-
-[package.dependencies]
-typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""}
-zipp = ">=0.5"
-
-[package.extras]
-docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
-perf = ["ipython"]
-testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"]
-
-[[package]]
-name = "iniconfig"
-version = "2.0.0"
-description = "brain-dead simple config-ini parsing"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"},
- {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
-]
-
-[[package]]
-name = "isort"
-version = "5.11.5"
-description = "A Python utility / library to sort Python imports."
-optional = false
-python-versions = ">=3.7.0"
-files = [
- {file = "isort-5.11.5-py3-none-any.whl", hash = "sha256:ba1d72fb2595a01c7895a5128f9585a5cc4b6d395f1c8d514989b9a7eb2a8746"},
- {file = "isort-5.11.5.tar.gz", hash = "sha256:6be1f76a507cb2ecf16c7cf14a37e41609ca082330be4e3436a18ef74add55db"},
-]
-
-[package.extras]
-colors = ["colorama (>=0.4.3,<0.5.0)"]
-pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"]
-plugins = ["setuptools"]
-requirements-deprecated-finder = ["pip-api", "pipreqs"]
-
-[[package]]
-name = "jinja2"
-version = "3.1.4"
-description = "A very fast and expressive template engine."
-optional = true
-python-versions = ">=3.7"
-files = [
- {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"},
- {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"},
-]
-
-[package.dependencies]
-MarkupSafe = ">=2.0"
-
-[package.extras]
-i18n = ["Babel (>=2.7)"]
-
-[[package]]
-name = "lazy-object-proxy"
-version = "1.9.0"
-description = "A fast and thorough lazy object proxy."
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "lazy-object-proxy-1.9.0.tar.gz", hash = "sha256:659fb5809fa4629b8a1ac5106f669cfc7bef26fbb389dda53b3e010d1ac4ebae"},
- {file = "lazy_object_proxy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b40387277b0ed2d0602b8293b94d7257e17d1479e257b4de114ea11a8cb7f2d7"},
- {file = "lazy_object_proxy-1.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8c6cfb338b133fbdbc5cfaa10fe3c6aeea827db80c978dbd13bc9dd8526b7d4"},
- {file = "lazy_object_proxy-1.9.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:721532711daa7db0d8b779b0bb0318fa87af1c10d7fe5e52ef30f8eff254d0cd"},
- {file = "lazy_object_proxy-1.9.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:66a3de4a3ec06cd8af3f61b8e1ec67614fbb7c995d02fa224813cb7afefee701"},
- {file = "lazy_object_proxy-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1aa3de4088c89a1b69f8ec0dcc169aa725b0ff017899ac568fe44ddc1396df46"},
- {file = "lazy_object_proxy-1.9.0-cp310-cp310-win32.whl", hash = "sha256:f0705c376533ed2a9e5e97aacdbfe04cecd71e0aa84c7c0595d02ef93b6e4455"},
- {file = "lazy_object_proxy-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:ea806fd4c37bf7e7ad82537b0757999264d5f70c45468447bb2b91afdbe73a6e"},
- {file = "lazy_object_proxy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:946d27deaff6cf8452ed0dba83ba38839a87f4f7a9732e8f9fd4107b21e6ff07"},
- {file = "lazy_object_proxy-1.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79a31b086e7e68b24b99b23d57723ef7e2c6d81ed21007b6281ebcd1688acb0a"},
- {file = "lazy_object_proxy-1.9.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f699ac1c768270c9e384e4cbd268d6e67aebcfae6cd623b4d7c3bfde5a35db59"},
- {file = "lazy_object_proxy-1.9.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bfb38f9ffb53b942f2b5954e0f610f1e721ccebe9cce9025a38c8ccf4a5183a4"},
- {file = "lazy_object_proxy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:189bbd5d41ae7a498397287c408617fe5c48633e7755287b21d741f7db2706a9"},
- {file = "lazy_object_proxy-1.9.0-cp311-cp311-win32.whl", hash = "sha256:81fc4d08b062b535d95c9ea70dbe8a335c45c04029878e62d744bdced5141586"},
- {file = "lazy_object_proxy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:f2457189d8257dd41ae9b434ba33298aec198e30adf2dcdaaa3a28b9994f6adb"},
- {file = "lazy_object_proxy-1.9.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d9e25ef10a39e8afe59a5c348a4dbf29b4868ab76269f81ce1674494e2565a6e"},
- {file = "lazy_object_proxy-1.9.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cbf9b082426036e19c6924a9ce90c740a9861e2bdc27a4834fd0a910742ac1e8"},
- {file = "lazy_object_proxy-1.9.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f5fa4a61ce2438267163891961cfd5e32ec97a2c444e5b842d574251ade27d2"},
- {file = "lazy_object_proxy-1.9.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:8fa02eaab317b1e9e03f69aab1f91e120e7899b392c4fc19807a8278a07a97e8"},
- {file = "lazy_object_proxy-1.9.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e7c21c95cae3c05c14aafffe2865bbd5e377cfc1348c4f7751d9dc9a48ca4bda"},
- {file = "lazy_object_proxy-1.9.0-cp37-cp37m-win32.whl", hash = "sha256:f12ad7126ae0c98d601a7ee504c1122bcef553d1d5e0c3bfa77b16b3968d2734"},
- {file = "lazy_object_proxy-1.9.0-cp37-cp37m-win_amd64.whl", hash = "sha256:edd20c5a55acb67c7ed471fa2b5fb66cb17f61430b7a6b9c3b4a1e40293b1671"},
- {file = "lazy_object_proxy-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2d0daa332786cf3bb49e10dc6a17a52f6a8f9601b4cf5c295a4f85854d61de63"},
- {file = "lazy_object_proxy-1.9.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cd077f3d04a58e83d04b20e334f678c2b0ff9879b9375ed107d5d07ff160171"},
- {file = "lazy_object_proxy-1.9.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:660c94ea760b3ce47d1855a30984c78327500493d396eac4dfd8bd82041b22be"},
- {file = "lazy_object_proxy-1.9.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:212774e4dfa851e74d393a2370871e174d7ff0ebc980907723bb67d25c8a7c30"},
- {file = "lazy_object_proxy-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f0117049dd1d5635bbff65444496c90e0baa48ea405125c088e93d9cf4525b11"},
- {file = "lazy_object_proxy-1.9.0-cp38-cp38-win32.whl", hash = "sha256:0a891e4e41b54fd5b8313b96399f8b0e173bbbfc03c7631f01efbe29bb0bcf82"},
- {file = "lazy_object_proxy-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:9990d8e71b9f6488e91ad25f322898c136b008d87bf852ff65391b004da5e17b"},
- {file = "lazy_object_proxy-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9e7551208b2aded9c1447453ee366f1c4070602b3d932ace044715d89666899b"},
- {file = "lazy_object_proxy-1.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f83ac4d83ef0ab017683d715ed356e30dd48a93746309c8f3517e1287523ef4"},
- {file = "lazy_object_proxy-1.9.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7322c3d6f1766d4ef1e51a465f47955f1e8123caee67dd641e67d539a534d006"},
- {file = "lazy_object_proxy-1.9.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:18b78ec83edbbeb69efdc0e9c1cb41a3b1b1ed11ddd8ded602464c3fc6020494"},
- {file = "lazy_object_proxy-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:09763491ce220c0299688940f8dc2c5d05fd1f45af1e42e636b2e8b2303e4382"},
- {file = "lazy_object_proxy-1.9.0-cp39-cp39-win32.whl", hash = "sha256:9090d8e53235aa280fc9239a86ae3ea8ac58eff66a705fa6aa2ec4968b95c821"},
- {file = "lazy_object_proxy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:db1c1722726f47e10e0b5fdbf15ac3b8adb58c091d12b3ab713965795036985f"},
-]
-
-[[package]]
-name = "livereload"
-version = "2.7.0"
-description = "Python LiveReload is an awesome tool for web developers"
-optional = true
-python-versions = ">=3.7"
-files = [
- {file = "livereload-2.7.0-py3-none-any.whl", hash = "sha256:19bee55aff51d5ade6ede0dc709189a0f904d3b906d3ea71641ed548acff3246"},
- {file = "livereload-2.7.0.tar.gz", hash = "sha256:f4ba199ef93248902841e298670eebfe1aa9e148e19b343bc57dbf1b74de0513"},
-]
-
-[package.dependencies]
-tornado = "*"
-
-[[package]]
-name = "loguru"
-version = "0.5.3"
-description = "Python logging made (stupidly) simple"
-optional = false
-python-versions = ">=3.5"
-files = [
- {file = "loguru-0.5.3-py3-none-any.whl", hash = "sha256:f8087ac396b5ee5f67c963b495d615ebbceac2796379599820e324419d53667c"},
- {file = "loguru-0.5.3.tar.gz", hash = "sha256:b28e72ac7a98be3d28ad28570299a393dfcd32e5e3f6a353dec94675767b6319"},
-]
-
-[package.dependencies]
-colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""}
-win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""}
-
-[package.extras]
-dev = ["Sphinx (>=2.2.1)", "black (>=19.10b0)", "codecov (>=2.0.15)", "colorama (>=0.3.4)", "flake8 (>=3.7.7)", "isort (>=5.1.1)", "pytest (>=4.6.2)", "pytest-cov (>=2.7.1)", "sphinx-autobuild (>=0.7.1)", "sphinx-rtd-theme (>=0.4.3)", "tox (>=3.9.0)", "tox-travis (>=0.12)"]
-
-[[package]]
-name = "mako"
-version = "1.2.4"
-description = "A super-fast templating language that borrows the best ideas from the existing templating languages."
-optional = true
-python-versions = ">=3.7"
-files = [
- {file = "Mako-1.2.4-py3-none-any.whl", hash = "sha256:c97c79c018b9165ac9922ae4f32da095ffd3c4e6872b45eded42926deea46818"},
- {file = "Mako-1.2.4.tar.gz", hash = "sha256:d60a3903dc3bb01a18ad6a89cdbe2e4eadc69c0bc8ef1e3773ba53d44c3f7a34"},
-]
-
-[package.dependencies]
-importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
-MarkupSafe = ">=0.9.2"
-
-[package.extras]
-babel = ["Babel"]
-lingua = ["lingua"]
-testing = ["pytest"]
-
-[[package]]
-name = "markdown"
-version = "3.4.4"
-description = "Python implementation of John Gruber's Markdown."
-optional = true
-python-versions = ">=3.7"
-files = [
- {file = "Markdown-3.4.4-py3-none-any.whl", hash = "sha256:a4c1b65c0957b4bd9e7d86ddc7b3c9868fb9670660f6f99f6d1bca8954d5a941"},
- {file = "Markdown-3.4.4.tar.gz", hash = "sha256:225c6123522495d4119a90b3a3ba31a1e87a70369e03f14799ea9c0d7183a3d6"},
-]
-
-[package.dependencies]
-importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""}
-
-[package.extras]
-docs = ["mdx-gh-links (>=0.2)", "mkdocs (>=1.0)", "mkdocs-nature (>=0.4)"]
-testing = ["coverage", "pyyaml"]
-
-[[package]]
-name = "markupsafe"
-version = "2.1.5"
-description = "Safely add untrusted strings to HTML/XML markup."
-optional = true
-python-versions = ">=3.7"
-files = [
- {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"},
- {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"},
- {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"},
- {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"},
- {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"},
- {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"},
- {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"},
- {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"},
- {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"},
- {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"},
- {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"},
- {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"},
- {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"},
- {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"},
- {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"},
- {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"},
- {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"},
- {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"},
- {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"},
- {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"},
- {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"},
- {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"},
- {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"},
- {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"},
- {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"},
- {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"},
- {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"},
- {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"},
- {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"},
- {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"},
- {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"},
- {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"},
- {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"},
- {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"},
- {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"},
- {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"},
- {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"},
- {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"},
- {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"},
- {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"},
- {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"},
- {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"},
- {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"},
- {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"},
- {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"},
- {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"},
- {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"},
- {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"},
- {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"},
- {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"},
- {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"},
- {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"},
- {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"},
- {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"},
- {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"},
- {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"},
- {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"},
- {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"},
- {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"},
- {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"},
-]
-
-[[package]]
-name = "mccabe"
-version = "0.7.0"
-description = "McCabe checker, plugin for flake8"
-optional = false
-python-versions = ">=3.6"
-files = [
- {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"},
- {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"},
-]
-
-[[package]]
-name = "mergedeep"
-version = "1.3.4"
-description = "A deep merge function for 🐍."
-optional = true
-python-versions = ">=3.6"
-files = [
- {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"},
- {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"},
-]
-
-[[package]]
-name = "mkdocs"
-version = "1.2.4"
-description = "Project documentation with Markdown."
-optional = true
-python-versions = ">=3.6"
-files = [
- {file = "mkdocs-1.2.4-py3-none-any.whl", hash = "sha256:f108e7ab5a7ed3e30826dbf82f37638f0d90d11161644616cc4f01a1e2ab3940"},
- {file = "mkdocs-1.2.4.tar.gz", hash = "sha256:8e7970a26183487fe2a1041940c6fd03aa0dbe5549e50c3e7194f565cb3c678a"},
-]
-
-[package.dependencies]
-click = ">=3.3"
-ghp-import = ">=1.0"
-importlib-metadata = ">=3.10"
-Jinja2 = ">=2.10.1"
-Markdown = ">=3.2.1"
-mergedeep = ">=1.3.4"
-packaging = ">=20.5"
-PyYAML = ">=3.10"
-pyyaml-env-tag = ">=0.1"
-watchdog = ">=2.0"
-
-[package.extras]
-i18n = ["babel (>=2.9.0)"]
-
-[[package]]
-name = "mkdocs-material"
-version = "7.3.0"
-description = "A Material Design theme for MkDocs"
-optional = true
-python-versions = "*"
-files = [
- {file = "mkdocs-material-7.3.0.tar.gz", hash = "sha256:07db0580fa96c3473aee99ec3fb4606a1a5a1e4f4467e64c0cd1ba8da5b6476e"},
- {file = "mkdocs_material-7.3.0-py2.py3-none-any.whl", hash = "sha256:b183c27dc0f44e631bbc32c51057f61a3e2ba8b3c1080e59f944167eeba9ff1d"},
-]
-
-[package.dependencies]
-markdown = ">=3.2"
-mkdocs = ">=1.2.2"
-mkdocs-material-extensions = ">=1.0"
-Pygments = ">=2.4"
-pymdown-extensions = ">=7.0"
-
-[[package]]
-name = "mkdocs-material-extensions"
-version = "1.2"
-description = "Extension pack for Python Markdown and MkDocs Material."
-optional = true
-python-versions = ">=3.7"
-files = [
- {file = "mkdocs_material_extensions-1.2-py3-none-any.whl", hash = "sha256:c767bd6d6305f6420a50f0b541b0c9966d52068839af97029be14443849fb8a1"},
- {file = "mkdocs_material_extensions-1.2.tar.gz", hash = "sha256:27e2d1ed2d031426a6e10d5ea06989d67e90bb02acd588bc5673106b5ee5eedf"},
-]
-
-[[package]]
-name = "mypy"
-version = "0.790"
-description = "Optional static typing for Python"
-optional = false
-python-versions = ">=3.5"
-files = [
- {file = "mypy-0.790-cp35-cp35m-macosx_10_6_x86_64.whl", hash = "sha256:bd03b3cf666bff8d710d633d1c56ab7facbdc204d567715cb3b9f85c6e94f669"},
- {file = "mypy-0.790-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:2170492030f6faa537647d29945786d297e4862765f0b4ac5930ff62e300d802"},
- {file = "mypy-0.790-cp35-cp35m-win_amd64.whl", hash = "sha256:e86bdace26c5fe9cf8cb735e7cedfe7850ad92b327ac5d797c656717d2ca66de"},
- {file = "mypy-0.790-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e97e9c13d67fbe524be17e4d8025d51a7dca38f90de2e462243ab8ed8a9178d1"},
- {file = "mypy-0.790-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0d34d6b122597d48a36d6c59e35341f410d4abfa771d96d04ae2c468dd201abc"},
- {file = "mypy-0.790-cp36-cp36m-win_amd64.whl", hash = "sha256:72060bf64f290fb629bd4a67c707a66fd88ca26e413a91384b18db3876e57ed7"},
- {file = "mypy-0.790-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:eea260feb1830a627fb526d22fbb426b750d9f5a47b624e8d5e7e004359b219c"},
- {file = "mypy-0.790-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:c614194e01c85bb2e551c421397e49afb2872c88b5830e3554f0519f9fb1c178"},
- {file = "mypy-0.790-cp37-cp37m-win_amd64.whl", hash = "sha256:0a0d102247c16ce93c97066443d11e2d36e6cc2a32d8ccc1f705268970479324"},
- {file = "mypy-0.790-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cf4e7bf7f1214826cf7333627cb2547c0db7e3078723227820d0a2490f117a01"},
- {file = "mypy-0.790-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:af4e9ff1834e565f1baa74ccf7ae2564ae38c8df2a85b057af1dbbc958eb6666"},
- {file = "mypy-0.790-cp38-cp38-win_amd64.whl", hash = "sha256:da56dedcd7cd502ccd3c5dddc656cb36113dd793ad466e894574125945653cea"},
- {file = "mypy-0.790-py3-none-any.whl", hash = "sha256:2842d4fbd1b12ab422346376aad03ff5d0805b706102e475e962370f874a5122"},
- {file = "mypy-0.790.tar.gz", hash = "sha256:2b21ba45ad9ef2e2eb88ce4aeadd0112d0f5026418324176fd494a6824b74975"},
-]
-
-[package.dependencies]
-mypy-extensions = ">=0.4.3,<0.5.0"
-typed-ast = ">=1.4.0,<1.5.0"
-typing-extensions = ">=3.7.4"
-
-[package.extras]
-dmypy = ["psutil (>=4.0)"]
-
-[[package]]
-name = "mypy-extensions"
-version = "0.4.4"
-description = "Experimental type system extensions for programs checked with the mypy typechecker."
-optional = false
-python-versions = ">=2.7"
-files = [
- {file = "mypy_extensions-0.4.4.tar.gz", hash = "sha256:c8b707883a96efe9b4bb3aaf0dcc07e7e217d7d8368eec4db4049ee9e142f4fd"},
-]
-
-[[package]]
-name = "numpy"
-version = "1.21.1"
-description = "NumPy is the fundamental package for array computing with Python."
-optional = true
-python-versions = ">=3.7"
-files = [
- {file = "numpy-1.21.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:38e8648f9449a549a7dfe8d8755a5979b45b3538520d1e735637ef28e8c2dc50"},
- {file = "numpy-1.21.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:fd7d7409fa643a91d0a05c7554dd68aa9c9bb16e186f6ccfe40d6e003156e33a"},
- {file = "numpy-1.21.1-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a75b4498b1e93d8b700282dc8e655b8bd559c0904b3910b144646dbbbc03e062"},
- {file = "numpy-1.21.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1412aa0aec3e00bc23fbb8664d76552b4efde98fb71f60737c83efbac24112f1"},
- {file = "numpy-1.21.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e46ceaff65609b5399163de5893d8f2a82d3c77d5e56d976c8b5fb01faa6b671"},
- {file = "numpy-1.21.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:c6a2324085dd52f96498419ba95b5777e40b6bcbc20088fddb9e8cbb58885e8e"},
- {file = "numpy-1.21.1-cp37-cp37m-win32.whl", hash = "sha256:73101b2a1fef16602696d133db402a7e7586654682244344b8329cdcbbb82172"},
- {file = "numpy-1.21.1-cp37-cp37m-win_amd64.whl", hash = "sha256:7a708a79c9a9d26904d1cca8d383bf869edf6f8e7650d85dbc77b041e8c5a0f8"},
- {file = "numpy-1.21.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:95b995d0c413f5d0428b3f880e8fe1660ff9396dcd1f9eedbc311f37b5652e16"},
- {file = "numpy-1.21.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:635e6bd31c9fb3d475c8f44a089569070d10a9ef18ed13738b03049280281267"},
- {file = "numpy-1.21.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4a3d5fb89bfe21be2ef47c0614b9c9c707b7362386c9a3ff1feae63e0267ccb6"},
- {file = "numpy-1.21.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8a326af80e86d0e9ce92bcc1e65c8ff88297de4fa14ee936cb2293d414c9ec63"},
- {file = "numpy-1.21.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:791492091744b0fe390a6ce85cc1bf5149968ac7d5f0477288f78c89b385d9af"},
- {file = "numpy-1.21.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0318c465786c1f63ac05d7c4dbcecd4d2d7e13f0959b01b534ea1e92202235c5"},
- {file = "numpy-1.21.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9a513bd9c1551894ee3d31369f9b07460ef223694098cf27d399513415855b68"},
- {file = "numpy-1.21.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:91c6f5fc58df1e0a3cc0c3a717bb3308ff850abdaa6d2d802573ee2b11f674a8"},
- {file = "numpy-1.21.1-cp38-cp38-win32.whl", hash = "sha256:978010b68e17150db8765355d1ccdd450f9fc916824e8c4e35ee620590e234cd"},
- {file = "numpy-1.21.1-cp38-cp38-win_amd64.whl", hash = "sha256:9749a40a5b22333467f02fe11edc98f022133ee1bfa8ab99bda5e5437b831214"},
- {file = "numpy-1.21.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d7a4aeac3b94af92a9373d6e77b37691b86411f9745190d2c351f410ab3a791f"},
- {file = "numpy-1.21.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d9e7912a56108aba9b31df688a4c4f5cb0d9d3787386b87d504762b6754fbb1b"},
- {file = "numpy-1.21.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:25b40b98ebdd272bc3020935427a4530b7d60dfbe1ab9381a39147834e985eac"},
- {file = "numpy-1.21.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8a92c5aea763d14ba9d6475803fc7904bda7decc2a0a68153f587ad82941fec1"},
- {file = "numpy-1.21.1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:05a0f648eb28bae4bcb204e6fd14603de2908de982e761a2fc78efe0f19e96e1"},
- {file = "numpy-1.21.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f01f28075a92eede918b965e86e8f0ba7b7797a95aa8d35e1cc8821f5fc3ad6a"},
- {file = "numpy-1.21.1-cp39-cp39-win32.whl", hash = "sha256:88c0b89ad1cc24a5efbb99ff9ab5db0f9a86e9cc50240177a571fbe9c2860ac2"},
- {file = "numpy-1.21.1-cp39-cp39-win_amd64.whl", hash = "sha256:01721eefe70544d548425a07c80be8377096a54118070b8a62476866d5208e33"},
- {file = "numpy-1.21.1-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2d4d1de6e6fb3d28781c73fbde702ac97f03d79e4ffd6598b880b2d95d62ead4"},
- {file = "numpy-1.21.1.zip", hash = "sha256:dff4af63638afcc57a3dfb9e4b26d434a7a602d225b42d746ea7fe2edf1342fd"},
-]
-
-[[package]]
-name = "packaging"
-version = "24.0"
-description = "Core utilities for Python packages"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"},
- {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"},
-]
-
-[[package]]
-name = "pandas"
-version = "1.1.5"
-description = "Powerful data structures for data analysis, time series, and statistics"
-optional = true
-python-versions = ">=3.6.1"
-files = [
- {file = "pandas-1.1.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:bf23a3b54d128b50f4f9d4675b3c1857a688cc6731a32f931837d72effb2698d"},
- {file = "pandas-1.1.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:5a780260afc88268a9d3ac3511d8f494fdcf637eece62fb9eb656a63d53eb7ca"},
- {file = "pandas-1.1.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:b61080750d19a0122469ab59b087380721d6b72a4e7d962e4d7e63e0c4504814"},
- {file = "pandas-1.1.5-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:0de3ddb414d30798cbf56e642d82cac30a80223ad6fe484d66c0ce01a84d6f2f"},
- {file = "pandas-1.1.5-cp36-cp36m-win32.whl", hash = "sha256:70865f96bb38fec46f7ebd66d4b5cfd0aa6b842073f298d621385ae3898d28b5"},
- {file = "pandas-1.1.5-cp36-cp36m-win_amd64.whl", hash = "sha256:19a2148a1d02791352e9fa637899a78e371a3516ac6da5c4edc718f60cbae648"},
- {file = "pandas-1.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:26fa92d3ac743a149a31b21d6f4337b0594b6302ea5575b37af9ca9611e8981a"},
- {file = "pandas-1.1.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:c16d59c15d946111d2716856dd5479221c9e4f2f5c7bc2d617f39d870031e086"},
- {file = "pandas-1.1.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:3be7a7a0ca71a2640e81d9276f526bca63505850add10206d0da2e8a0a325dae"},
- {file = "pandas-1.1.5-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:573fba5b05bf2c69271a32e52399c8de599e4a15ab7cec47d3b9c904125ab788"},
- {file = "pandas-1.1.5-cp37-cp37m-win32.whl", hash = "sha256:21b5a2b033380adbdd36b3116faaf9a4663e375325831dac1b519a44f9e439bb"},
- {file = "pandas-1.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:24c7f8d4aee71bfa6401faeba367dd654f696a77151a8a28bc2013f7ced4af98"},
- {file = "pandas-1.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2860a97cbb25444ffc0088b457da0a79dc79f9c601238a3e0644312fcc14bf11"},
- {file = "pandas-1.1.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:5008374ebb990dad9ed48b0f5d0038124c73748f5384cc8c46904dace27082d9"},
- {file = "pandas-1.1.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:2c2f7c670ea4e60318e4b7e474d56447cf0c7d83b3c2a5405a0dbb2600b9c48e"},
- {file = "pandas-1.1.5-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:0a643bae4283a37732ddfcecab3f62dd082996021b980f580903f4e8e01b3c5b"},
- {file = "pandas-1.1.5-cp38-cp38-win32.whl", hash = "sha256:5447ea7af4005b0daf695a316a423b96374c9c73ffbd4533209c5ddc369e644b"},
- {file = "pandas-1.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:4c62e94d5d49db116bef1bd5c2486723a292d79409fc9abd51adf9e05329101d"},
- {file = "pandas-1.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:731568be71fba1e13cae212c362f3d2ca8932e83cb1b85e3f1b4dd77d019254a"},
- {file = "pandas-1.1.5-cp39-cp39-manylinux1_i686.whl", hash = "sha256:c61c043aafb69329d0f961b19faa30b1dab709dd34c9388143fc55680059e55a"},
- {file = "pandas-1.1.5-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:2b1c6cd28a0dfda75c7b5957363333f01d370936e4c6276b7b8e696dd500582a"},
- {file = "pandas-1.1.5-cp39-cp39-win32.whl", hash = "sha256:c94ff2780a1fd89f190390130d6d36173ca59fcfb3fe0ff596f9a56518191ccb"},
- {file = "pandas-1.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:edda9bacc3843dfbeebaf7a701763e68e741b08fccb889c003b0a52f0ee95782"},
- {file = "pandas-1.1.5.tar.gz", hash = "sha256:f10fc41ee3c75a474d3bdf68d396f10782d013d7f67db99c0efbfd0acb99701b"},
-]
-
-[package.dependencies]
-numpy = ">=1.15.4"
-python-dateutil = ">=2.7.3"
-pytz = ">=2017.2"
-
-[package.extras]
-test = ["hypothesis (>=3.58)", "pytest (>=4.0.2)", "pytest-xdist"]
-
-[[package]]
-name = "pathspec"
-version = "0.11.2"
-description = "Utility library for gitignore style pattern matching of file paths."
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"},
- {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"},
-]
-
-[[package]]
-name = "pdocs"
-version = "1.2.0"
-description = "A simple program and library to auto generate API documentation for Python modules."
-optional = true
-python-versions = ">=3.7"
-files = [
- {file = "pdocs-1.2.0-py3-none-any.whl", hash = "sha256:caca2829415e62d31eaab46c53cd77a2a14f132c73e48e25187331c3a9fc2459"},
- {file = "pdocs-1.2.0.tar.gz", hash = "sha256:996ad4d5039b59a9a112d29abfb3995ec4ed8d8415ddce6947c3a5248adb428b"},
-]
-
-[package.dependencies]
-docstring_parser = ">=0.7.2"
-hug = ">=2.6"
-Mako = ">1.2.2"
-Markdown = ">=3.0.0"
-
-[[package]]
-name = "platformdirs"
-version = "4.0.0"
-description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "platformdirs-4.0.0-py3-none-any.whl", hash = "sha256:118c954d7e949b35437270383a3f2531e99dd93cf7ce4dc8340d3356d30f173b"},
- {file = "platformdirs-4.0.0.tar.gz", hash = "sha256:cb633b2bcf10c51af60beb0ab06d2f1d69064b43abf4c185ca6b28865f3f9731"},
-]
-
-[package.dependencies]
-typing-extensions = {version = ">=4.7.1", markers = "python_version < \"3.8\""}
-
-[package.extras]
-docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"]
-test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"]
-
-[[package]]
-name = "pluggy"
-version = "1.2.0"
-description = "plugin and hook calling mechanisms for python"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "pluggy-1.2.0-py3-none-any.whl", hash = "sha256:c2fd55a7d7a3863cba1a013e4e2414658b1d07b6bc57b3919e0c63c9abb99849"},
- {file = "pluggy-1.2.0.tar.gz", hash = "sha256:d12f0c4b579b15f5e054301bb226ee85eeeba08ffec228092f8defbaa3a4c4b3"},
-]
-
-[package.dependencies]
-importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""}
-
-[package.extras]
-dev = ["pre-commit", "tox"]
-testing = ["pytest", "pytest-benchmark"]
-
-[[package]]
-name = "portray"
-version = "1.7.0"
-description = "Your Project with Great Documentation"
-optional = true
-python-versions = ">=3.6,<4.0"
-files = [
- {file = "portray-1.7.0-py3-none-any.whl", hash = "sha256:fb4467105d948fabf0ec35b11af50f3c0c4f2aabaa31d5dcd657fadb1c6132e1"},
- {file = "portray-1.7.0.tar.gz", hash = "sha256:8f3c0a6a6841969329e4dd1e94e180220658c3ad0367a5bad81dd964a75ae1fe"},
-]
-
-[package.dependencies]
-GitPython = ">=3.0,<4.0"
-hug = ">=2.6,<3.0"
-livereload = ">=2.6.3,<3.0.0"
-mkdocs = ">=1.2,<1.3"
-mkdocs-material = ">=7.0,<8.0"
-pdocs = ">=1.1.1,<2.0.0"
-pymdown-extensions = ">=7.0,<8.0"
-toml = ">=0.10.0,<0.11.0"
-yaspin = ">=0.15.0,<0.16.0"
-
-[[package]]
-name = "py"
-version = "1.11.0"
-description = "library with cross-python path, ini-parsing, io, code, log facilities"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
-files = [
- {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"},
- {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"},
-]
-
-[[package]]
-name = "pydantic"
-version = "1.10.17"
-description = "Data validation and settings management using python type hints"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "pydantic-1.10.17-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0fa51175313cc30097660b10eec8ca55ed08bfa07acbfe02f7a42f6c242e9a4b"},
- {file = "pydantic-1.10.17-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c7e8988bb16988890c985bd2093df9dd731bfb9d5e0860db054c23034fab8f7a"},
- {file = "pydantic-1.10.17-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:371dcf1831f87c9e217e2b6a0c66842879a14873114ebb9d0861ab22e3b5bb1e"},
- {file = "pydantic-1.10.17-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4866a1579c0c3ca2c40575398a24d805d4db6cb353ee74df75ddeee3c657f9a7"},
- {file = "pydantic-1.10.17-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:543da3c6914795b37785703ffc74ba4d660418620cc273490d42c53949eeeca6"},
- {file = "pydantic-1.10.17-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7623b59876f49e61c2e283551cc3647616d2fbdc0b4d36d3d638aae8547ea681"},
- {file = "pydantic-1.10.17-cp310-cp310-win_amd64.whl", hash = "sha256:409b2b36d7d7d19cd8310b97a4ce6b1755ef8bd45b9a2ec5ec2b124db0a0d8f3"},
- {file = "pydantic-1.10.17-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fa43f362b46741df8f201bf3e7dff3569fa92069bcc7b4a740dea3602e27ab7a"},
- {file = "pydantic-1.10.17-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2a72d2a5ff86a3075ed81ca031eac86923d44bc5d42e719d585a8eb547bf0c9b"},
- {file = "pydantic-1.10.17-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4ad32aed3bf5eea5ca5decc3d1bbc3d0ec5d4fbcd72a03cdad849458decbc63"},
- {file = "pydantic-1.10.17-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aeb4e741782e236ee7dc1fb11ad94dc56aabaf02d21df0e79e0c21fe07c95741"},
- {file = "pydantic-1.10.17-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d2f89a719411cb234105735a520b7c077158a81e0fe1cb05a79c01fc5eb59d3c"},
- {file = "pydantic-1.10.17-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db3b48d9283d80a314f7a682f7acae8422386de659fffaba454b77a083c3937d"},
- {file = "pydantic-1.10.17-cp311-cp311-win_amd64.whl", hash = "sha256:9c803a5113cfab7bbb912f75faa4fc1e4acff43e452c82560349fff64f852e1b"},
- {file = "pydantic-1.10.17-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:820ae12a390c9cbb26bb44913c87fa2ff431a029a785642c1ff11fed0a095fcb"},
- {file = "pydantic-1.10.17-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c1e51d1af306641b7d1574d6d3307eaa10a4991542ca324f0feb134fee259815"},
- {file = "pydantic-1.10.17-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e53fb834aae96e7b0dadd6e92c66e7dd9cdf08965340ed04c16813102a47fab"},
- {file = "pydantic-1.10.17-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e2495309b1266e81d259a570dd199916ff34f7f51f1b549a0d37a6d9b17b4dc"},
- {file = "pydantic-1.10.17-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:098ad8de840c92ea586bf8efd9e2e90c6339d33ab5c1cfbb85be66e4ecf8213f"},
- {file = "pydantic-1.10.17-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:525bbef620dac93c430d5d6bdbc91bdb5521698d434adf4434a7ef6ffd5c4b7f"},
- {file = "pydantic-1.10.17-cp312-cp312-win_amd64.whl", hash = "sha256:6654028d1144df451e1da69a670083c27117d493f16cf83da81e1e50edce72ad"},
- {file = "pydantic-1.10.17-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c87cedb4680d1614f1d59d13fea353faf3afd41ba5c906a266f3f2e8c245d655"},
- {file = "pydantic-1.10.17-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11289fa895bcbc8f18704efa1d8020bb9a86314da435348f59745473eb042e6b"},
- {file = "pydantic-1.10.17-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:94833612d6fd18b57c359a127cbfd932d9150c1b72fea7c86ab58c2a77edd7c7"},
- {file = "pydantic-1.10.17-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:d4ecb515fa7cb0e46e163ecd9d52f9147ba57bc3633dca0e586cdb7a232db9e3"},
- {file = "pydantic-1.10.17-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:7017971ffa7fd7808146880aa41b266e06c1e6e12261768a28b8b41ba55c8076"},
- {file = "pydantic-1.10.17-cp37-cp37m-win_amd64.whl", hash = "sha256:e840e6b2026920fc3f250ea8ebfdedf6ea7a25b77bf04c6576178e681942ae0f"},
- {file = "pydantic-1.10.17-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bfbb18b616abc4df70591b8c1ff1b3eabd234ddcddb86b7cac82657ab9017e33"},
- {file = "pydantic-1.10.17-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ebb249096d873593e014535ab07145498957091aa6ae92759a32d40cb9998e2e"},
- {file = "pydantic-1.10.17-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8c209af63ccd7b22fba94b9024e8b7fd07feffee0001efae50dd99316b27768"},
- {file = "pydantic-1.10.17-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d4b40c9e13a0b61583e5599e7950490c700297b4a375b55b2b592774332798b7"},
- {file = "pydantic-1.10.17-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c31d281c7485223caf6474fc2b7cf21456289dbaa31401844069b77160cab9c7"},
- {file = "pydantic-1.10.17-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ae5184e99a060a5c80010a2d53c99aee76a3b0ad683d493e5f0620b5d86eeb75"},
- {file = "pydantic-1.10.17-cp38-cp38-win_amd64.whl", hash = "sha256:ad1e33dc6b9787a6f0f3fd132859aa75626528b49cc1f9e429cdacb2608ad5f0"},
- {file = "pydantic-1.10.17-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7e17c0ee7192e54a10943f245dc79e36d9fe282418ea05b886e1c666063a7b54"},
- {file = "pydantic-1.10.17-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cafb9c938f61d1b182dfc7d44a7021326547b7b9cf695db5b68ec7b590214773"},
- {file = "pydantic-1.10.17-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95ef534e3c22e5abbdbdd6f66b6ea9dac3ca3e34c5c632894f8625d13d084cbe"},
- {file = "pydantic-1.10.17-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62d96b8799ae3d782df7ec9615cb59fc32c32e1ed6afa1b231b0595f6516e8ab"},
- {file = "pydantic-1.10.17-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ab2f976336808fd5d539fdc26eb51f9aafc1f4b638e212ef6b6f05e753c8011d"},
- {file = "pydantic-1.10.17-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b8ad363330557beac73159acfbeed220d5f1bfcd6b930302a987a375e02f74fd"},
- {file = "pydantic-1.10.17-cp39-cp39-win_amd64.whl", hash = "sha256:48db882e48575ce4b39659558b2f9f37c25b8d348e37a2b4e32971dd5a7d6227"},
- {file = "pydantic-1.10.17-py3-none-any.whl", hash = "sha256:e41b5b973e5c64f674b3b4720286ded184dcc26a691dd55f34391c62c6934688"},
- {file = "pydantic-1.10.17.tar.gz", hash = "sha256:f434160fb14b353caf634149baaf847206406471ba70e64657c1e8330277a991"},
-]
-
-[package.dependencies]
-typing-extensions = ">=4.2.0"
-
-[package.extras]
-dotenv = ["python-dotenv (>=0.10.4)"]
-email = ["email-validator (>=1.0.3)"]
-
-[[package]]
-name = "pygments"
-version = "2.17.2"
-description = "Pygments is a syntax highlighting package written in Python."
-optional = true
-python-versions = ">=3.7"
-files = [
- {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"},
- {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"},
-]
-
-[package.extras]
-plugins = ["importlib-metadata"]
-windows-terminal = ["colorama (>=0.4.6)"]
-
-[[package]]
-name = "pylint"
-version = "2.13.9"
-description = "python code static checker"
-optional = false
-python-versions = ">=3.6.2"
-files = [
- {file = "pylint-2.13.9-py3-none-any.whl", hash = "sha256:705c620d388035bdd9ff8b44c5bcdd235bfb49d276d488dd2c8ff1736aa42526"},
- {file = "pylint-2.13.9.tar.gz", hash = "sha256:095567c96e19e6f57b5b907e67d265ff535e588fe26b12b5ebe1fc5645b2c731"},
-]
-
-[package.dependencies]
-astroid = ">=2.11.5,<=2.12.0-dev0"
-colorama = {version = "*", markers = "sys_platform == \"win32\""}
-dill = ">=0.2"
-isort = ">=4.2.5,<6"
-mccabe = ">=0.6,<0.8"
-platformdirs = ">=2.2.0"
-tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
-typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""}
-
-[package.extras]
-testutil = ["gitpython (>3)"]
-
-[[package]]
-name = "pymdown-extensions"
-version = "7.1"
-description = "Extension pack for Python Markdown."
-optional = true
-python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*"
-files = [
- {file = "pymdown-extensions-7.1.tar.gz", hash = "sha256:5bf93d1ccd8281948cd7c559eb363e59b179b5373478e8a7195cf4b78e3c11b6"},
- {file = "pymdown_extensions-7.1-py2.py3-none-any.whl", hash = "sha256:8f415b21ee86d80bb2c3676f4478b274d0a8ccb13af672a4c86b9ffd22bd005c"},
-]
-
-[package.dependencies]
-Markdown = ">=3.2"
-
-[[package]]
-name = "pytest"
-version = "6.2.5"
-description = "pytest: simple powerful testing with Python"
-optional = false
-python-versions = ">=3.6"
-files = [
- {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"},
- {file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"},
-]
-
-[package.dependencies]
-atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""}
-attrs = ">=19.2.0"
-colorama = {version = "*", markers = "sys_platform == \"win32\""}
-importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""}
-iniconfig = "*"
-packaging = "*"
-pluggy = ">=0.12,<2.0"
-py = ">=1.8.2"
-toml = "*"
-
-[package.extras]
-testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"]
-
-[[package]]
-name = "pytest-cov"
-version = "2.12.1"
-description = "Pytest plugin for measuring coverage."
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
-files = [
- {file = "pytest-cov-2.12.1.tar.gz", hash = "sha256:261ceeb8c227b726249b376b8526b600f38667ee314f910353fa318caa01f4d7"},
- {file = "pytest_cov-2.12.1-py2.py3-none-any.whl", hash = "sha256:261bb9e47e65bd099c89c3edf92972865210c36813f80ede5277dceb77a4a62a"},
-]
-
-[package.dependencies]
-coverage = ">=5.2.1"
-pytest = ">=4.6"
-toml = "*"
-
-[package.extras]
-testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"]
-
-[[package]]
-name = "python-dateutil"
-version = "2.9.0.post0"
-description = "Extensions to the standard Python datetime module"
-optional = true
-python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
-files = [
- {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"},
- {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"},
-]
-
-[package.dependencies]
-six = ">=1.5"
-
-[[package]]
-name = "pytz"
-version = "2024.1"
-description = "World timezone definitions, modern and historical"
-optional = true
-python-versions = "*"
-files = [
- {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"},
- {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"},
-]
-
-[[package]]
-name = "pyyaml"
-version = "6.0.1"
-description = "YAML parser and emitter for Python"
-optional = true
-python-versions = ">=3.6"
-files = [
- {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"},
- {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"},
- {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"},
- {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"},
- {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"},
- {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"},
- {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"},
- {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"},
- {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"},
- {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"},
- {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"},
- {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"},
- {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"},
- {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"},
- {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"},
- {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"},
- {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"},
- {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"},
- {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"},
- {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"},
- {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"},
- {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"},
- {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"},
- {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"},
- {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"},
- {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"},
- {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"},
- {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"},
- {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"},
- {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"},
- {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"},
- {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"},
- {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"},
- {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"},
- {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"},
- {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"},
- {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"},
- {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"},
- {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"},
- {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"},
- {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"},
- {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"},
- {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"},
- {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"},
- {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"},
- {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"},
- {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"},
- {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"},
- {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"},
- {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"},
- {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"},
-]
-
-[[package]]
-name = "pyyaml-env-tag"
-version = "0.1"
-description = "A custom YAML tag for referencing environment variables in YAML files. "
-optional = true
-python-versions = ">=3.6"
-files = [
- {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"},
- {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"},
-]
-
-[package.dependencies]
-pyyaml = "*"
-
-[[package]]
-name = "regex"
-version = "2024.4.16"
-description = "Alternative regular expression module, to replace re."
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "regex-2024.4.16-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fb83cc090eac63c006871fd24db5e30a1f282faa46328572661c0a24a2323a08"},
- {file = "regex-2024.4.16-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8c91e1763696c0eb66340c4df98623c2d4e77d0746b8f8f2bee2c6883fd1fe18"},
- {file = "regex-2024.4.16-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:10188fe732dec829c7acca7422cdd1bf57d853c7199d5a9e96bb4d40db239c73"},
- {file = "regex-2024.4.16-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:956b58d692f235cfbf5b4f3abd6d99bf102f161ccfe20d2fd0904f51c72c4c66"},
- {file = "regex-2024.4.16-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a70b51f55fd954d1f194271695821dd62054d949efd6368d8be64edd37f55c86"},
- {file = "regex-2024.4.16-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c02fcd2bf45162280613d2e4a1ca3ac558ff921ae4e308ecb307650d3a6ee51"},
- {file = "regex-2024.4.16-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4ed75ea6892a56896d78f11006161eea52c45a14994794bcfa1654430984b22"},
- {file = "regex-2024.4.16-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd727ad276bb91928879f3aa6396c9a1d34e5e180dce40578421a691eeb77f47"},
- {file = "regex-2024.4.16-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7cbc5d9e8a1781e7be17da67b92580d6ce4dcef5819c1b1b89f49d9678cc278c"},
- {file = "regex-2024.4.16-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:78fddb22b9ef810b63ef341c9fcf6455232d97cfe03938cbc29e2672c436670e"},
- {file = "regex-2024.4.16-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:445ca8d3c5a01309633a0c9db57150312a181146315693273e35d936472df912"},
- {file = "regex-2024.4.16-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:95399831a206211d6bc40224af1c635cb8790ddd5c7493e0bd03b85711076a53"},
- {file = "regex-2024.4.16-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:7731728b6568fc286d86745f27f07266de49603a6fdc4d19c87e8c247be452af"},
- {file = "regex-2024.4.16-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4facc913e10bdba42ec0aee76d029aedda628161a7ce4116b16680a0413f658a"},
- {file = "regex-2024.4.16-cp310-cp310-win32.whl", hash = "sha256:911742856ce98d879acbea33fcc03c1d8dc1106234c5e7d068932c945db209c0"},
- {file = "regex-2024.4.16-cp310-cp310-win_amd64.whl", hash = "sha256:e0a2df336d1135a0b3a67f3bbf78a75f69562c1199ed9935372b82215cddd6e2"},
- {file = "regex-2024.4.16-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1210365faba7c2150451eb78ec5687871c796b0f1fa701bfd2a4a25420482d26"},
- {file = "regex-2024.4.16-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9ab40412f8cd6f615bfedea40c8bf0407d41bf83b96f6fc9ff34976d6b7037fd"},
- {file = "regex-2024.4.16-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fd80d1280d473500d8086d104962a82d77bfbf2b118053824b7be28cd5a79ea5"},
- {file = "regex-2024.4.16-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bb966fdd9217e53abf824f437a5a2d643a38d4fd5fd0ca711b9da683d452969"},
- {file = "regex-2024.4.16-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:20b7a68444f536365af42a75ccecb7ab41a896a04acf58432db9e206f4e525d6"},
- {file = "regex-2024.4.16-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b74586dd0b039c62416034f811d7ee62810174bb70dffcca6439f5236249eb09"},
- {file = "regex-2024.4.16-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c8290b44d8b0af4e77048646c10c6e3aa583c1ca67f3b5ffb6e06cf0c6f0f89"},
- {file = "regex-2024.4.16-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2d80a6749724b37853ece57988b39c4e79d2b5fe2869a86e8aeae3bbeef9eb0"},
- {file = "regex-2024.4.16-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3a1018e97aeb24e4f939afcd88211ace472ba566efc5bdf53fd8fd7f41fa7170"},
- {file = "regex-2024.4.16-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:8d015604ee6204e76569d2f44e5a210728fa917115bef0d102f4107e622b08d5"},
- {file = "regex-2024.4.16-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:3d5ac5234fb5053850d79dd8eb1015cb0d7d9ed951fa37aa9e6249a19aa4f336"},
- {file = "regex-2024.4.16-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:0a38d151e2cdd66d16dab550c22f9521ba79761423b87c01dae0a6e9add79c0d"},
- {file = "regex-2024.4.16-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:159dc4e59a159cb8e4e8f8961eb1fa5d58f93cb1acd1701d8aff38d45e1a84a6"},
- {file = "regex-2024.4.16-cp311-cp311-win32.whl", hash = "sha256:ba2336d6548dee3117520545cfe44dc28a250aa091f8281d28804aa8d707d93d"},
- {file = "regex-2024.4.16-cp311-cp311-win_amd64.whl", hash = "sha256:8f83b6fd3dc3ba94d2b22717f9c8b8512354fd95221ac661784df2769ea9bba9"},
- {file = "regex-2024.4.16-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:80b696e8972b81edf0af2a259e1b2a4a661f818fae22e5fa4fa1a995fb4a40fd"},
- {file = "regex-2024.4.16-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d61ae114d2a2311f61d90c2ef1358518e8f05eafda76eaf9c772a077e0b465ec"},
- {file = "regex-2024.4.16-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8ba6745440b9a27336443b0c285d705ce73adb9ec90e2f2004c64d95ab5a7598"},
- {file = "regex-2024.4.16-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6295004b2dd37b0835ea5c14a33e00e8cfa3c4add4d587b77287825f3418d310"},
- {file = "regex-2024.4.16-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4aba818dcc7263852aabb172ec27b71d2abca02a593b95fa79351b2774eb1d2b"},
- {file = "regex-2024.4.16-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d0800631e565c47520aaa04ae38b96abc5196fe8b4aa9bd864445bd2b5848a7a"},
- {file = "regex-2024.4.16-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08dea89f859c3df48a440dbdcd7b7155bc675f2fa2ec8c521d02dc69e877db70"},
- {file = "regex-2024.4.16-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eeaa0b5328b785abc344acc6241cffde50dc394a0644a968add75fcefe15b9d4"},
- {file = "regex-2024.4.16-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4e819a806420bc010489f4e741b3036071aba209f2e0989d4750b08b12a9343f"},
- {file = "regex-2024.4.16-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:c2d0e7cbb6341e830adcbfa2479fdeebbfbb328f11edd6b5675674e7a1e37730"},
- {file = "regex-2024.4.16-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:91797b98f5e34b6a49f54be33f72e2fb658018ae532be2f79f7c63b4ae225145"},
- {file = "regex-2024.4.16-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:d2da13568eff02b30fd54fccd1e042a70fe920d816616fda4bf54ec705668d81"},
- {file = "regex-2024.4.16-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:370c68dc5570b394cbaadff50e64d705f64debed30573e5c313c360689b6aadc"},
- {file = "regex-2024.4.16-cp312-cp312-win32.whl", hash = "sha256:904c883cf10a975b02ab3478bce652f0f5346a2c28d0a8521d97bb23c323cc8b"},
- {file = "regex-2024.4.16-cp312-cp312-win_amd64.whl", hash = "sha256:785c071c982dce54d44ea0b79cd6dfafddeccdd98cfa5f7b86ef69b381b457d9"},
- {file = "regex-2024.4.16-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e2f142b45c6fed48166faeb4303b4b58c9fcd827da63f4cf0a123c3480ae11fb"},
- {file = "regex-2024.4.16-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e87ab229332ceb127a165612d839ab87795972102cb9830e5f12b8c9a5c1b508"},
- {file = "regex-2024.4.16-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:81500ed5af2090b4a9157a59dbc89873a25c33db1bb9a8cf123837dcc9765047"},
- {file = "regex-2024.4.16-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b340cccad138ecb363324aa26893963dcabb02bb25e440ebdf42e30963f1a4e0"},
- {file = "regex-2024.4.16-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c72608e70f053643437bd2be0608f7f1c46d4022e4104d76826f0839199347a"},
- {file = "regex-2024.4.16-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a01fe2305e6232ef3e8f40bfc0f0f3a04def9aab514910fa4203bafbc0bb4682"},
- {file = "regex-2024.4.16-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:03576e3a423d19dda13e55598f0fd507b5d660d42c51b02df4e0d97824fdcae3"},
- {file = "regex-2024.4.16-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:549c3584993772e25f02d0656ac48abdda73169fe347263948cf2b1cead622f3"},
- {file = "regex-2024.4.16-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:34422d5a69a60b7e9a07a690094e824b66f5ddc662a5fc600d65b7c174a05f04"},
- {file = "regex-2024.4.16-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:5f580c651a72b75c39e311343fe6875d6f58cf51c471a97f15a938d9fe4e0d37"},
- {file = "regex-2024.4.16-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:3399dd8a7495bbb2bacd59b84840eef9057826c664472e86c91d675d007137f5"},
- {file = "regex-2024.4.16-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8d1f86f3f4e2388aa3310b50694ac44daefbd1681def26b4519bd050a398dc5a"},
- {file = "regex-2024.4.16-cp37-cp37m-win32.whl", hash = "sha256:dd5acc0a7d38fdc7a3a6fd3ad14c880819008ecb3379626e56b163165162cc46"},
- {file = "regex-2024.4.16-cp37-cp37m-win_amd64.whl", hash = "sha256:ba8122e3bb94ecda29a8de4cf889f600171424ea586847aa92c334772d200331"},
- {file = "regex-2024.4.16-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:743deffdf3b3481da32e8a96887e2aa945ec6685af1cfe2bcc292638c9ba2f48"},
- {file = "regex-2024.4.16-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7571f19f4a3fd00af9341c7801d1ad1967fc9c3f5e62402683047e7166b9f2b4"},
- {file = "regex-2024.4.16-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:df79012ebf6f4efb8d307b1328226aef24ca446b3ff8d0e30202d7ebcb977a8c"},
- {file = "regex-2024.4.16-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e757d475953269fbf4b441207bb7dbdd1c43180711b6208e129b637792ac0b93"},
- {file = "regex-2024.4.16-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4313ab9bf6a81206c8ac28fdfcddc0435299dc88cad12cc6305fd0e78b81f9e4"},
- {file = "regex-2024.4.16-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d83c2bc678453646f1a18f8db1e927a2d3f4935031b9ad8a76e56760461105dd"},
- {file = "regex-2024.4.16-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9df1bfef97db938469ef0a7354b2d591a2d438bc497b2c489471bec0e6baf7c4"},
- {file = "regex-2024.4.16-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62120ed0de69b3649cc68e2965376048793f466c5a6c4370fb27c16c1beac22d"},
- {file = "regex-2024.4.16-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c2ef6f7990b6e8758fe48ad08f7e2f66c8f11dc66e24093304b87cae9037bb4a"},
- {file = "regex-2024.4.16-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8fc6976a3395fe4d1fbeb984adaa8ec652a1e12f36b56ec8c236e5117b585427"},
- {file = "regex-2024.4.16-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:03e68f44340528111067cecf12721c3df4811c67268b897fbe695c95f860ac42"},
- {file = "regex-2024.4.16-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:ec7e0043b91115f427998febaa2beb82c82df708168b35ece3accb610b91fac1"},
- {file = "regex-2024.4.16-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:c21fc21a4c7480479d12fd8e679b699f744f76bb05f53a1d14182b31f55aac76"},
- {file = "regex-2024.4.16-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:12f6a3f2f58bb7344751919a1876ee1b976fe08b9ffccb4bbea66f26af6017b9"},
- {file = "regex-2024.4.16-cp38-cp38-win32.whl", hash = "sha256:479595a4fbe9ed8f8f72c59717e8cf222da2e4c07b6ae5b65411e6302af9708e"},
- {file = "regex-2024.4.16-cp38-cp38-win_amd64.whl", hash = "sha256:0534b034fba6101611968fae8e856c1698da97ce2efb5c2b895fc8b9e23a5834"},
- {file = "regex-2024.4.16-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a7ccdd1c4a3472a7533b0a7aa9ee34c9a2bef859ba86deec07aff2ad7e0c3b94"},
- {file = "regex-2024.4.16-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6f2f017c5be19984fbbf55f8af6caba25e62c71293213f044da3ada7091a4455"},
- {file = "regex-2024.4.16-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:803b8905b52de78b173d3c1e83df0efb929621e7b7c5766c0843704d5332682f"},
- {file = "regex-2024.4.16-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:684008ec44ad275832a5a152f6e764bbe1914bea10968017b6feaecdad5736e0"},
- {file = "regex-2024.4.16-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65436dce9fdc0aeeb0a0effe0839cb3d6a05f45aa45a4d9f9c60989beca78b9c"},
- {file = "regex-2024.4.16-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea355eb43b11764cf799dda62c658c4d2fdb16af41f59bb1ccfec517b60bcb07"},
- {file = "regex-2024.4.16-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98c1165f3809ce7774f05cb74e5408cd3aa93ee8573ae959a97a53db3ca3180d"},
- {file = "regex-2024.4.16-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cccc79a9be9b64c881f18305a7c715ba199e471a3973faeb7ba84172abb3f317"},
- {file = "regex-2024.4.16-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:00169caa125f35d1bca6045d65a662af0202704489fada95346cfa092ec23f39"},
- {file = "regex-2024.4.16-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6cc38067209354e16c5609b66285af17a2863a47585bcf75285cab33d4c3b8df"},
- {file = "regex-2024.4.16-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:23cff1b267038501b179ccbbd74a821ac4a7192a1852d1d558e562b507d46013"},
- {file = "regex-2024.4.16-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:b9d320b3bf82a39f248769fc7f188e00f93526cc0fe739cfa197868633d44701"},
- {file = "regex-2024.4.16-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:89ec7f2c08937421bbbb8b48c54096fa4f88347946d4747021ad85f1b3021b3c"},
- {file = "regex-2024.4.16-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4918fd5f8b43aa7ec031e0fef1ee02deb80b6afd49c85f0790be1dc4ce34cb50"},
- {file = "regex-2024.4.16-cp39-cp39-win32.whl", hash = "sha256:684e52023aec43bdf0250e843e1fdd6febbe831bd9d52da72333fa201aaa2335"},
- {file = "regex-2024.4.16-cp39-cp39-win_amd64.whl", hash = "sha256:e697e1c0238133589e00c244a8b676bc2cfc3ab4961318d902040d099fec7483"},
- {file = "regex-2024.4.16.tar.gz", hash = "sha256:fa454d26f2e87ad661c4f0c5a5fe4cf6aab1e307d1b94f16ffdfcb089ba685c0"},
-]
-
-[[package]]
-name = "requests"
-version = "2.31.0"
-description = "Python HTTP for Humans."
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"},
- {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"},
-]
-
-[package.dependencies]
-certifi = ">=2017.4.17"
-charset-normalizer = ">=2,<4"
-idna = ">=2.5,<4"
-urllib3 = ">=1.21.1,<3"
-
-[package.extras]
-socks = ["PySocks (>=1.5.6,!=1.5.7)"]
-use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
-
-[[package]]
-name = "responses"
-version = "0.12.1"
-description = "A utility library for mocking out the `requests` Python library."
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
-files = [
- {file = "responses-0.12.1-py2.py3-none-any.whl", hash = "sha256:ef265bd3200bdef5ec17912fc64a23570ba23597fd54ca75c18650fa1699213d"},
- {file = "responses-0.12.1.tar.gz", hash = "sha256:2e5764325c6b624e42b428688f2111fea166af46623cb0127c05f6afb14d3457"},
-]
-
-[package.dependencies]
-requests = ">=2.0"
-six = "*"
-urllib3 = ">=1.25.10"
-
-[package.extras]
-tests = ["coverage (>=3.7.1,<6.0.0)", "flake8", "pytest (>=4.6)", "pytest (>=4.6,<5.0)", "pytest-cov", "pytest-localserver"]
-
-[[package]]
-name = "setuptools"
-version = "68.0.0"
-description = "Easily download, build, install, upgrade, and uninstall Python packages"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "setuptools-68.0.0-py3-none-any.whl", hash = "sha256:11e52c67415a381d10d6b462ced9cfb97066179f0e871399e006c4ab101fc85f"},
- {file = "setuptools-68.0.0.tar.gz", hash = "sha256:baf1fdb41c6da4cd2eae722e135500da913332ab3f2f5c7d33af9b492acb5235"},
-]
-
-[package.extras]
-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-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
-testing = ["build[virtualenv]", "filelock (>=3.4.0)", "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-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
-testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
-
-[[package]]
-name = "six"
-version = "1.16.0"
-description = "Python 2 and 3 compatibility utilities"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
-files = [
- {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
- {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
-]
-
-[[package]]
-name = "smmap"
-version = "5.0.1"
-description = "A pure Python implementation of a sliding window memory map manager"
-optional = true
-python-versions = ">=3.7"
-files = [
- {file = "smmap-5.0.1-py3-none-any.whl", hash = "sha256:e6d8668fa5f93e706934a62d7b4db19c8d9eb8cf2adbb75ef1b675aa332b69da"},
- {file = "smmap-5.0.1.tar.gz", hash = "sha256:dceeb6c0028fdb6734471eb07c0cd2aae706ccaecab45965ee83f11c8d3b1f62"},
-]
-
-[[package]]
-name = "toml"
-version = "0.10.2"
-description = "Python Library for Tom's Obvious, Minimal Language"
-optional = false
-python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
-files = [
- {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
- {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
-]
-
-[[package]]
-name = "tomli"
-version = "2.0.1"
-description = "A lil' TOML parser"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
- {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
-]
-
-[[package]]
-name = "tornado"
-version = "6.2"
-description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed."
-optional = true
-python-versions = ">= 3.7"
-files = [
- {file = "tornado-6.2-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:20f638fd8cc85f3cbae3c732326e96addff0a15e22d80f049e00121651e82e72"},
- {file = "tornado-6.2-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:87dcafae3e884462f90c90ecc200defe5e580a7fbbb4365eda7c7c1eb809ebc9"},
- {file = "tornado-6.2-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba09ef14ca9893954244fd872798b4ccb2367c165946ce2dd7376aebdde8e3ac"},
- {file = "tornado-6.2-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8150f721c101abdef99073bf66d3903e292d851bee51910839831caba341a75"},
- {file = "tornado-6.2-cp37-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3a2f5999215a3a06a4fc218026cd84c61b8b2b40ac5296a6db1f1451ef04c1e"},
- {file = "tornado-6.2-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:5f8c52d219d4995388119af7ccaa0bcec289535747620116a58d830e7c25d8a8"},
- {file = "tornado-6.2-cp37-abi3-musllinux_1_1_i686.whl", hash = "sha256:6fdfabffd8dfcb6cf887428849d30cf19a3ea34c2c248461e1f7d718ad30b66b"},
- {file = "tornado-6.2-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:1d54d13ab8414ed44de07efecb97d4ef7c39f7438cf5e976ccd356bebb1b5fca"},
- {file = "tornado-6.2-cp37-abi3-win32.whl", hash = "sha256:5c87076709343557ef8032934ce5f637dbb552efa7b21d08e89ae7619ed0eb23"},
- {file = "tornado-6.2-cp37-abi3-win_amd64.whl", hash = "sha256:e5f923aa6a47e133d1cf87d60700889d7eae68988704e20c75fb2d65677a8e4b"},
- {file = "tornado-6.2.tar.gz", hash = "sha256:9b630419bde84ec666bfd7ea0a4cb2a8a651c2d5cccdbdd1972a0c859dfc3c13"},
-]
-
-[[package]]
-name = "typed-ast"
-version = "1.4.3"
-description = "a fork of Python 2 and 3 ast modules with type comment support"
-optional = false
-python-versions = "*"
-files = [
- {file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:2068531575a125b87a41802130fa7e29f26c09a2833fea68d9a40cf33902eba6"},
- {file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:c907f561b1e83e93fad565bac5ba9c22d96a54e7ea0267c708bffe863cbe4075"},
- {file = "typed_ast-1.4.3-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:1b3ead4a96c9101bef08f9f7d1217c096f31667617b58de957f690c92378b528"},
- {file = "typed_ast-1.4.3-cp35-cp35m-win32.whl", hash = "sha256:dde816ca9dac1d9c01dd504ea5967821606f02e510438120091b84e852367428"},
- {file = "typed_ast-1.4.3-cp35-cp35m-win_amd64.whl", hash = "sha256:777a26c84bea6cd934422ac2e3b78863a37017618b6e5c08f92ef69853e765d3"},
- {file = "typed_ast-1.4.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f8afcf15cc511ada719a88e013cec87c11aff7b91f019295eb4530f96fe5ef2f"},
- {file = "typed_ast-1.4.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:52b1eb8c83f178ab787f3a4283f68258525f8d70f778a2f6dd54d3b5e5fb4341"},
- {file = "typed_ast-1.4.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:01ae5f73431d21eead5015997ab41afa53aa1fbe252f9da060be5dad2c730ace"},
- {file = "typed_ast-1.4.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:c190f0899e9f9f8b6b7863debfb739abcb21a5c054f911ca3596d12b8a4c4c7f"},
- {file = "typed_ast-1.4.3-cp36-cp36m-win32.whl", hash = "sha256:398e44cd480f4d2b7ee8d98385ca104e35c81525dd98c519acff1b79bdaac363"},
- {file = "typed_ast-1.4.3-cp36-cp36m-win_amd64.whl", hash = "sha256:bff6ad71c81b3bba8fa35f0f1921fb24ff4476235a6e94a26ada2e54370e6da7"},
- {file = "typed_ast-1.4.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0fb71b8c643187d7492c1f8352f2c15b4c4af3f6338f21681d3681b3dc31a266"},
- {file = "typed_ast-1.4.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:760ad187b1041a154f0e4d0f6aae3e40fdb51d6de16e5c99aedadd9246450e9e"},
- {file = "typed_ast-1.4.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5feca99c17af94057417d744607b82dd0a664fd5e4ca98061480fd8b14b18d04"},
- {file = "typed_ast-1.4.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:95431a26309a21874005845c21118c83991c63ea800dd44843e42a916aec5899"},
- {file = "typed_ast-1.4.3-cp37-cp37m-win32.whl", hash = "sha256:aee0c1256be6c07bd3e1263ff920c325b59849dc95392a05f258bb9b259cf39c"},
- {file = "typed_ast-1.4.3-cp37-cp37m-win_amd64.whl", hash = "sha256:9ad2c92ec681e02baf81fdfa056fe0d818645efa9af1f1cd5fd6f1bd2bdfd805"},
- {file = "typed_ast-1.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b36b4f3920103a25e1d5d024d155c504080959582b928e91cb608a65c3a49e1a"},
- {file = "typed_ast-1.4.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:067a74454df670dcaa4e59349a2e5c81e567d8d65458d480a5b3dfecec08c5ff"},
- {file = "typed_ast-1.4.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7538e495704e2ccda9b234b82423a4038f324f3a10c43bc088a1636180f11a41"},
- {file = "typed_ast-1.4.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:af3d4a73793725138d6b334d9d247ce7e5f084d96284ed23f22ee626a7b88e39"},
- {file = "typed_ast-1.4.3-cp38-cp38-win32.whl", hash = "sha256:f2362f3cb0f3172c42938946dbc5b7843c2a28aec307c49100c8b38764eb6927"},
- {file = "typed_ast-1.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:dd4a21253f42b8d2b48410cb31fe501d32f8b9fbeb1f55063ad102fe9c425e40"},
- {file = "typed_ast-1.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f328adcfebed9f11301eaedfa48e15bdece9b519fb27e6a8c01aa52a17ec31b3"},
- {file = "typed_ast-1.4.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:2c726c276d09fc5c414693a2de063f521052d9ea7c240ce553316f70656c84d4"},
- {file = "typed_ast-1.4.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:cae53c389825d3b46fb37538441f75d6aecc4174f615d048321b716df2757fb0"},
- {file = "typed_ast-1.4.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b9574c6f03f685070d859e75c7f9eeca02d6933273b5e69572e5ff9d5e3931c3"},
- {file = "typed_ast-1.4.3-cp39-cp39-win32.whl", hash = "sha256:209596a4ec71d990d71d5e0d312ac935d86930e6eecff6ccc7007fe54d703808"},
- {file = "typed_ast-1.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:9c6d1a54552b5330bc657b7ef0eae25d00ba7ffe85d9ea8ae6540d2197a3788c"},
- {file = "typed_ast-1.4.3.tar.gz", hash = "sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65"},
-]
-
-[[package]]
-name = "typing-extensions"
-version = "4.7.1"
-description = "Backported and Experimental Type Hints for Python 3.7+"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"},
- {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"},
-]
-
-[[package]]
-name = "urllib3"
-version = "2.0.7"
-description = "HTTP library with thread-safe connection pooling, file post, and more."
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "urllib3-2.0.7-py3-none-any.whl", hash = "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e"},
- {file = "urllib3-2.0.7.tar.gz", hash = "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84"},
-]
-
-[package.extras]
-brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"]
-secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"]
-socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
-zstd = ["zstandard (>=0.18.0)"]
-
-[[package]]
-name = "watchdog"
-version = "3.0.0"
-description = "Filesystem events monitoring"
-optional = true
-python-versions = ">=3.7"
-files = [
- {file = "watchdog-3.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:336adfc6f5cc4e037d52db31194f7581ff744b67382eb6021c868322e32eef41"},
- {file = "watchdog-3.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a70a8dcde91be523c35b2bf96196edc5730edb347e374c7de7cd20c43ed95397"},
- {file = "watchdog-3.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:adfdeab2da79ea2f76f87eb42a3ab1966a5313e5a69a0213a3cc06ef692b0e96"},
- {file = "watchdog-3.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2b57a1e730af3156d13b7fdddfc23dea6487fceca29fc75c5a868beed29177ae"},
- {file = "watchdog-3.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7ade88d0d778b1b222adebcc0927428f883db07017618a5e684fd03b83342bd9"},
- {file = "watchdog-3.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7e447d172af52ad204d19982739aa2346245cc5ba6f579d16dac4bfec226d2e7"},
- {file = "watchdog-3.0.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:9fac43a7466eb73e64a9940ac9ed6369baa39b3bf221ae23493a9ec4d0022674"},
- {file = "watchdog-3.0.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8ae9cda41fa114e28faf86cb137d751a17ffd0316d1c34ccf2235e8a84365c7f"},
- {file = "watchdog-3.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:25f70b4aa53bd743729c7475d7ec41093a580528b100e9a8c5b5efe8899592fc"},
- {file = "watchdog-3.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4f94069eb16657d2c6faada4624c39464f65c05606af50bb7902e036e3219be3"},
- {file = "watchdog-3.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7c5f84b5194c24dd573fa6472685b2a27cc5a17fe5f7b6fd40345378ca6812e3"},
- {file = "watchdog-3.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3aa7f6a12e831ddfe78cdd4f8996af9cf334fd6346531b16cec61c3b3c0d8da0"},
- {file = "watchdog-3.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:233b5817932685d39a7896b1090353fc8efc1ef99c9c054e46c8002561252fb8"},
- {file = "watchdog-3.0.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:13bbbb462ee42ec3c5723e1205be8ced776f05b100e4737518c67c8325cf6100"},
- {file = "watchdog-3.0.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:8f3ceecd20d71067c7fd4c9e832d4e22584318983cabc013dbf3f70ea95de346"},
- {file = "watchdog-3.0.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c9d8c8ec7efb887333cf71e328e39cffbf771d8f8f95d308ea4125bf5f90ba64"},
- {file = "watchdog-3.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:0e06ab8858a76e1219e68c7573dfeba9dd1c0219476c5a44d5333b01d7e1743a"},
- {file = "watchdog-3.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:d00e6be486affb5781468457b21a6cbe848c33ef43f9ea4a73b4882e5f188a44"},
- {file = "watchdog-3.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:c07253088265c363d1ddf4b3cdb808d59a0468ecd017770ed716991620b8f77a"},
- {file = "watchdog-3.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:5113334cf8cf0ac8cd45e1f8309a603291b614191c9add34d33075727a967709"},
- {file = "watchdog-3.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:51f90f73b4697bac9c9a78394c3acbbd331ccd3655c11be1a15ae6fe289a8c83"},
- {file = "watchdog-3.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:ba07e92756c97e3aca0912b5cbc4e5ad802f4557212788e72a72a47ff376950d"},
- {file = "watchdog-3.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:d429c2430c93b7903914e4db9a966c7f2b068dd2ebdd2fa9b9ce094c7d459f33"},
- {file = "watchdog-3.0.0-py3-none-win32.whl", hash = "sha256:3ed7c71a9dccfe838c2f0b6314ed0d9b22e77d268c67e015450a29036a81f60f"},
- {file = "watchdog-3.0.0-py3-none-win_amd64.whl", hash = "sha256:4c9956d27be0bb08fc5f30d9d0179a855436e655f046d288e2bcc11adfae893c"},
- {file = "watchdog-3.0.0-py3-none-win_ia64.whl", hash = "sha256:5d9f3a10e02d7371cd929b5d8f11e87d4bad890212ed3901f9b4d68767bee759"},
- {file = "watchdog-3.0.0.tar.gz", hash = "sha256:4d98a320595da7a7c5a18fc48cb633c2e73cda78f93cac2ef42d42bf609a33f9"},
-]
-
-[package.extras]
-watchmedo = ["PyYAML (>=3.10)"]
-
-[[package]]
-name = "win32-setctime"
-version = "1.1.0"
-description = "A small Python utility to set file creation time on Windows"
-optional = false
-python-versions = ">=3.5"
-files = [
- {file = "win32_setctime-1.1.0-py3-none-any.whl", hash = "sha256:231db239e959c2fe7eb1d7dc129f11172354f98361c4fa2d6d2d7e278baa8aad"},
- {file = "win32_setctime-1.1.0.tar.gz", hash = "sha256:15cf5750465118d6929ae4de4eb46e8edae9a5634350c01ba582df868e932cb2"},
-]
-
-[package.extras]
-dev = ["black (>=19.3b0)", "pytest (>=4.6.2)"]
-
-[[package]]
-name = "wrapt"
-version = "1.16.0"
-description = "Module for decorators, wrappers and monkey patching."
-optional = false
-python-versions = ">=3.6"
-files = [
- {file = "wrapt-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4"},
- {file = "wrapt-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020"},
- {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440"},
- {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487"},
- {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf"},
- {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72"},
- {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0"},
- {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136"},
- {file = "wrapt-1.16.0-cp310-cp310-win32.whl", hash = "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d"},
- {file = "wrapt-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2"},
- {file = "wrapt-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09"},
- {file = "wrapt-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d"},
- {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389"},
- {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060"},
- {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1"},
- {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3"},
- {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956"},
- {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d"},
- {file = "wrapt-1.16.0-cp311-cp311-win32.whl", hash = "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362"},
- {file = "wrapt-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89"},
- {file = "wrapt-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b"},
- {file = "wrapt-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36"},
- {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73"},
- {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809"},
- {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b"},
- {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81"},
- {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9"},
- {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c"},
- {file = "wrapt-1.16.0-cp312-cp312-win32.whl", hash = "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc"},
- {file = "wrapt-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8"},
- {file = "wrapt-1.16.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8"},
- {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39"},
- {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c"},
- {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40"},
- {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc"},
- {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e"},
- {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465"},
- {file = "wrapt-1.16.0-cp36-cp36m-win32.whl", hash = "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e"},
- {file = "wrapt-1.16.0-cp36-cp36m-win_amd64.whl", hash = "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966"},
- {file = "wrapt-1.16.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593"},
- {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292"},
- {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5"},
- {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf"},
- {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228"},
- {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f"},
- {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c"},
- {file = "wrapt-1.16.0-cp37-cp37m-win32.whl", hash = "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c"},
- {file = "wrapt-1.16.0-cp37-cp37m-win_amd64.whl", hash = "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00"},
- {file = "wrapt-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0"},
- {file = "wrapt-1.16.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202"},
- {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0"},
- {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e"},
- {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f"},
- {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267"},
- {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca"},
- {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6"},
- {file = "wrapt-1.16.0-cp38-cp38-win32.whl", hash = "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b"},
- {file = "wrapt-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41"},
- {file = "wrapt-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2"},
- {file = "wrapt-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb"},
- {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8"},
- {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c"},
- {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a"},
- {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664"},
- {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f"},
- {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537"},
- {file = "wrapt-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3"},
- {file = "wrapt-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35"},
- {file = "wrapt-1.16.0-py3-none-any.whl", hash = "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1"},
- {file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"},
-]
-
-[[package]]
-name = "yaspin"
-version = "0.15.0"
-description = "Yet Another Terminal Spinner"
-optional = true
-python-versions = "*"
-files = [
- {file = "yaspin-0.15.0-py2.py3-none-any.whl", hash = "sha256:0ee4668936d0053de752c9a4963929faa3a832bd0ba823877d27855592dc80aa"},
- {file = "yaspin-0.15.0.tar.gz", hash = "sha256:5a938bdc7bab353fd8942d0619d56c6b5159a80997dc1c387a479b39e6dc9391"},
-]
-
-[[package]]
-name = "zipp"
-version = "3.15.0"
-description = "Backport of pathlib-compatible object wrapper for zip files"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"},
- {file = "zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b"},
-]
-
-[package.extras]
-docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
-testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"]
-
-[extras]
-dataframe = ["pandas"]
-docs = ["portray"]
-
-[metadata]
-lock-version = "2.0"
-python-versions = "^3.7"
-content-hash = "088d86be89668f485756887e5b1a3f4b31bff18b9feb093a101fb2ff3fffcaa6"
diff --git a/pyproject.toml b/pyproject.toml
index 4aadc60..7526a2a 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,14 +1,34 @@
-[tool.poetry]
+[build-system]
+requires = ["hatchling"]
+build-backend = "hatchling.build"
+
+[tool.hatch.version]
+path = "aoe2netwrapper/__init__.py"
+
+[tool.hatch.build.targets.sdist]
+exclude = [
+ "/.github",
+ "/docs",
+ "/tests",
+]
+
+[tool.hatch.build.targets.wheel]
+packages = ["aoe2netwrapper"]
+
+[project]
name = "aoe2netwrapper"
-version = "0.3.1"
+readme = "README.md"
description = "My Python wrapper for the aoe2.net data API"
-authors = ["Felix Soubelet "]
+authors = [{name = "Felix Soubelet", email = "felix.soubelet@cern.ch"},]
license = "MIT"
+dynamic = ["version"]
+requires-python = ">=3.10"
-readme = "README.md"
-repository = "https://github.com/fsoubelet/AoE2NetAPIWrapper"
-
-keywords = ["API", "AoE2", "Utility",]
+keywords = [
+ "API",
+ "AoE2",
+ "Utility",
+]
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
@@ -17,45 +37,47 @@ classifiers = [
"Natural Language :: English",
"Programming Language :: Python",
"Programming Language :: Python :: 3 :: Only",
- "Programming Language :: Python :: 3.7",
- "Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
+ "Programming Language :: Python :: 3.11",
+ "Programming Language :: Python :: 3.12",
"Programming Language :: Python :: Implementation :: CPython",
"Topic :: Games/Entertainment",
"Topic :: Utilities",
"Typing :: Typed",
]
-[tool.poetry.dependencies]
-python = "^3.7"
-requests = "^2.25"
-loguru = "^0.5"
-pydantic = "^1.7"
-
-# Defining optional dependencies for extras
-portray = { version = "^1.4", optional = true }
-pandas = { version = "^1.0", optional = true }
-
-# Defining the extras
-[tool.poetry.extras]
-docs = ["portray"]
-dataframe = ["pandas"]
-
-[tool.poetry.dev-dependencies]
-pytest = "^6.2"
-pytest-cov = "^2.10"
-responses = "^0.12"
-mypy = "^0.790"
-pylint = "^2.6"
-isort = "^5.7"
-black = "^20.8b1"
-tomli = "^2.0" # for coverage-py to support pyproject.toml
+dependencies = [
+ "requests >= 2.30",
+ "loguru < 1.0",
+ "pydantic >= 2.5",
+]
-[build-system]
-requires = ["poetry-core>=1.0.0"]
-build-backend = "poetry.core.masonry.api"
+[project.optional-dependencies]
+dataframe = [
+ "pandas >= 2.0",
+]
+test = [
+ "aoe2netwrapper[dataframe]",
+ "pytest >= 7.0",
+ "pytest-cov >= 2.9",
+ "responses >= 0.20",
+]
+docs = [
+ "numpy < 2.0", # portray needs hug which does not have compatibility
+ "portray >= 1.4",
+]
+all = [
+ "aoe2netwrapper[test]",
+ "aoe2netwrapper[docs]",
+ "aoe2netwrapper[dataframe]",
+]
+
+[project.urls]
+homepage = "https://github.com/fsoubelet/AoE2NetAPIWrapper"
+repository = "https://github.com/fsoubelet/AoE2NetAPIWrapper"
+documentation = "https://fsoubelet.github.io/AoE2NetAPIWrapper/"
# ----- Testing Configuration ----- #
@@ -68,7 +90,11 @@ source = ["aoe2netwrapper/"]
[tool.coverage.report]
ignore_errors = true
-
+omit = [
+ "aoe2netwrapper/models/match.py",
+ "aoe2netwrapper/models/match_history.py",
+ "aoe2netwrapper/models/matches.py",
+]
# ----- Documentation Configuration ----- #
@@ -104,7 +130,6 @@ extra_css = ["stylesheets/extra.css"]
[tool.portray.mkdocs.theme]
name = "material"
palette = {scheme = "slate", primary = "blue grey", accent = "amber"}
-font = {text = "Ubuntu Mono", code = "Fira Code"}
[[tool.portray.mkdocs.nav]]
Home = "README.md"
@@ -150,68 +175,12 @@ exclude = '''
)/
'''
-[tool.pylint]
-[tool.pylint.master]
-jobs="1" # Auto-detect the number of processors
-ignore-patterns='''
- \.eggs
- |\.git
- |build
- |dist
- |conf.py
-'''
-persistent="no"
-load-plugins=""
-limit-inference-results="100"
-suggestion-mode="yes"
-unsafe-load-any-extension="no"
-extension-pkg-whitelist=""
-exit-zero="no"
-from-stdin="no"
-extension_pkg_whitelist="pydantic"
-
-[tool.pylint.messages_control]
-disable = [
- "attribute-defined-outside-init",
- "bad-whitespace",
- "bad-continuation",
- "expression-not-assigned",
- "invalid-name",
- "import-error",
- "logging-format-interpolation",
- "no-name-in-module",
- "protected-access",
- "reimported",
- "too-few-public-methods",
- "too-many-instance-attributes",
- "format", # handled by black
-]
+[tool.ruff]
+line-length = 110
+# Assume Python 3.9+.
+target-version = "py39"
+include = ["aoe2netwrapper"]
-[tool.pylint.reports]
-output-format="text"
-msg-template="{path}:{line}:{column}: Pylint: {msg}. [{msg_id}: {symbol}]"
-
-[tool.pylint.basic]
-argument-naming-style="snake_case"
-attr-naming-style="snake_case"
-class-attribute-naming-style="snake_case"
-class-naming-style="PascalCase"
-const-naming-style="UPPER_CASE"
-function-naming-style="snake_case"
-inlinevar-naming-style="snake_case"
-method-naming-style="snake_case"
-module-naming-style="snake_case"
-variable-naming-style="snake_case"
-
-[tool.pylint.design]
-max-args="10"
-max-attributes="10"
-max-locals="15"
-max-public-methods="20"
-max-returns="5"
-
-[tool.pylint.'SIMILARITIES']
-ignore-comments=['yes']
-ignore-docstrings=['yes']
-ignore-imports=['no']
-min-similarity-lines=4
+[tool.ruff.lint]
+# Allow unused variables when underscore-prefixed.
+dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
diff --git a/tests/conftest.py b/tests/conftest.py
new file mode 100644
index 0000000..221aa98
--- /dev/null
+++ b/tests/conftest.py
@@ -0,0 +1,187 @@
+import json
+import pathlib
+
+import pandas as pd
+import pytest
+
+CURRENT_DIR = pathlib.Path(__file__).parent
+INPUTS_DIR = CURRENT_DIR / "inputs"
+
+
+# ----- Fixtures for AoE2NetAPI ----- #
+
+
+@pytest.fixture(scope="session")
+def strings_defaults_payload() -> dict:
+ strings_response_file = INPUTS_DIR / "strings.json"
+ with strings_response_file.open("r") as fileobj:
+ return json.load(fileobj)
+
+
+@pytest.fixture(scope="session")
+def leaderboard_defaults_payload() -> dict:
+ leaderboard_defaults_file = INPUTS_DIR / "leaderboard_defaults.json"
+ with leaderboard_defaults_file.open("r") as fileobj:
+ return json.load(fileobj)
+
+
+@pytest.fixture(scope="session")
+def leaderboard_search_payload() -> dict:
+ leaderboard_search_file = INPUTS_DIR / "leaderboard_search.json"
+ with leaderboard_search_file.open("r") as fileobj:
+ return json.load(fileobj)
+
+
+@pytest.fixture(scope="session")
+def leaderboard_steamid_payload() -> dict:
+ leaderboard_steamid_file = INPUTS_DIR / "leaderboard_steamid.json"
+ with leaderboard_steamid_file.open("r") as fileobj:
+ return json.load(fileobj)
+
+
+@pytest.fixture(scope="session")
+def leaderboard_profileid_payload() -> dict:
+ leaderboard_profileid_file = INPUTS_DIR / "leaderboard_profileid.json"
+ with leaderboard_profileid_file.open("r") as fileobj:
+ return json.load(fileobj)
+
+
+@pytest.fixture(scope="session")
+def lobbies_defaults_payload() -> dict:
+ lobbies_defaults_file = INPUTS_DIR / "lobbies.json"
+ with lobbies_defaults_file.open("r") as fileobj:
+ return json.load(fileobj)
+
+
+@pytest.fixture(scope="session")
+def last_match_steamid_payload() -> dict:
+ last_match_steamid_file = INPUTS_DIR / "last_match_steamid.json"
+ with last_match_steamid_file.open("r") as fileobj:
+ return json.load(fileobj)
+
+
+@pytest.fixture(scope="session")
+def last_match_profileid_payload() -> dict:
+ last_match_profileid_file = INPUTS_DIR / "last_match_profileid.json"
+ with last_match_profileid_file.open("r") as fileobj:
+ return json.load(fileobj)
+
+
+@pytest.fixture(scope="session")
+def match_history_steamid_payload() -> dict:
+ match_history_steamid_file = INPUTS_DIR / "match_history_steamid.json"
+ with match_history_steamid_file.open("r") as fileobj:
+ return json.load(fileobj)
+
+
+@pytest.fixture(scope="session")
+def match_history_profileid_payload() -> dict:
+ match_history_profileid_file = INPUTS_DIR / "match_history_profileid.json"
+ with match_history_profileid_file.open("r") as fileobj:
+ return json.load(fileobj)
+
+
+@pytest.fixture(scope="session")
+def rating_history_steamid_payload() -> dict:
+ rating_history_steamid_file = INPUTS_DIR / "rating_history_steamid.json"
+ with rating_history_steamid_file.open("r") as fileobj:
+ return json.load(fileobj)
+
+
+@pytest.fixture(scope="session")
+def rating_history_profileid_payload() -> dict:
+ rating_history_profileid_file = INPUTS_DIR / "rating_history_profileid.json"
+ with rating_history_profileid_file.open("r") as fileobj:
+ return json.load(fileobj)
+
+
+@pytest.fixture(scope="session")
+def matches_defaults_payload() -> dict:
+ matches_defaults_file = INPUTS_DIR / "matches_defaults.json"
+ with matches_defaults_file.open("r") as fileobj:
+ return json.load(fileobj)
+
+
+@pytest.fixture(scope="session")
+def matches_since_payload() -> dict:
+ matches_since_file = INPUTS_DIR / "matches_since.json"
+ with matches_since_file.open("r") as fileobj:
+ return json.load(fileobj)
+
+
+@pytest.fixture(scope="session")
+def match_uuid_payload() -> dict:
+ match_uuid_file = INPUTS_DIR / "match_uuid.json"
+ with match_uuid_file.open("r") as fileobj:
+ return json.load(fileobj)
+
+
+@pytest.fixture(scope="session")
+def match_matchid_payload() -> dict:
+ match_match_id_file = INPUTS_DIR / "match_matchid.json"
+ with match_match_id_file.open("r") as fileobj:
+ return json.load(fileobj)
+
+
+@pytest.fixture(scope="session")
+def num_online_defaults_payload() -> dict:
+ num_online_defaults_file = INPUTS_DIR / "num_online.json"
+ with num_online_defaults_file.open("r") as fileobj:
+ return json.load(fileobj)
+
+
+# ----- Fixtures for Converters ----- #
+
+
+@pytest.fixture(scope="session")
+def strings_converted() -> pd.DataFrame:
+ strings_converted_file = INPUTS_DIR / "convert_strings.pkl"
+ return pd.read_pickle(strings_converted_file)
+
+
+@pytest.fixture(scope="session")
+def leaderboard_converted() -> pd.DataFrame:
+ leaderboard_converted_file = INPUTS_DIR / "convert_leaderboard.pkl"
+ return pd.read_pickle(leaderboard_converted_file)
+
+
+@pytest.fixture(scope="session")
+def lobbies_converted() -> pd.DataFrame:
+ lobbies_converted_file = INPUTS_DIR / "convert_lobbies.pkl"
+ return pd.read_pickle(lobbies_converted_file)
+
+
+@pytest.fixture(scope="session")
+def last_match_converted() -> pd.DataFrame:
+ last_match_converted_file = INPUTS_DIR / "convert_last_match.pkl"
+ return pd.read_pickle(last_match_converted_file)
+
+
+@pytest.fixture(scope="session")
+def match_history_converted() -> pd.DataFrame:
+ match_history_converted_file = INPUTS_DIR / "convert_match_history.pkl"
+ return pd.read_pickle(match_history_converted_file)
+
+
+@pytest.fixture(scope="session")
+def rating_history_converted() -> pd.DataFrame:
+ rating_history_converted_file = INPUTS_DIR / "convert_rating_history.pkl"
+ return pd.read_pickle(rating_history_converted_file)
+
+
+@pytest.fixture(scope="session")
+def matches_converted() -> pd.DataFrame:
+ matches_converted_file = INPUTS_DIR / "convert_matches.pkl"
+ return pd.read_pickle(matches_converted_file)
+
+
+@pytest.fixture(scope="session")
+def match_converted() -> pd.DataFrame:
+ match_converted_file = INPUTS_DIR / "convert_match.pkl"
+ return pd.read_pickle(match_converted_file)
+
+
+@pytest.fixture(scope="session")
+def num_online_converted() -> pd.DataFrame:
+ num_online_converted_file = INPUTS_DIR / "convert_num_online.pkl"
+ return pd.read_pickle(num_online_converted_file)
diff --git a/tests/inputs/convert_match_history.pkl b/tests/inputs/convert_match_history.pkl
index f4f3c8f..e3de79a 100644
Binary files a/tests/inputs/convert_match_history.pkl and b/tests/inputs/convert_match_history.pkl differ
diff --git a/tests/test_aoe2netapi.py b/tests/test_aoe2netapi.py
index ada4896..24d51eb 100644
--- a/tests/test_aoe2netapi.py
+++ b/tests/test_aoe2netapi.py
@@ -1,16 +1,13 @@
-import json
import pathlib
import pytest
import responses
from aoe2netwrapper.api import AoE2NetAPI, _get_request_response_json
-from aoe2netwrapper.exceptions import Aoe2NetException
-from aoe2netwrapper.models import (
- LastMatchResponse,
+from aoe2netwrapper.exceptions import Aoe2NetError, RemovedApiEndpointError
+from aoe2netwrapper.models import ( # LastMatchResponse, NumOnlineResponse,
LeaderBoardResponse,
MatchLobby,
- NumOnlineResponse,
RatingTimePoint,
StringsResponse,
)
@@ -23,23 +20,23 @@ class TestExceptions:
client = AoE2NetAPI()
def test_leaderboard_invalid_count_parameter(self, caplog):
- with pytest.raises(Aoe2NetException):
+ with pytest.raises(Aoe2NetError):
self.client.leaderboard(count=11_000)
for record in caplog.records:
assert record.levelname == "ERROR"
assert "'count' has to be 10000 or less, but 11000 was provided" in caplog.text
- def test_last_match_misses_required_param(self, caplog):
- with pytest.raises(Aoe2NetException):
- self.client.last_match()
+ # def test_last_match_misses_required_param(self, caplog):
+ # with pytest.raises(Aoe2NetError):
+ # self.client.last_match()
- for record in caplog.records:
- assert record.levelname == "ERROR"
- assert "Missing one of 'steam_id', 'profile_id'" in caplog.text
+ # for record in caplog.records:
+ # assert record.levelname == "ERROR"
+ # assert "Missing one of 'steam_id', 'profile_id'" in caplog.text
def test_match_history_invalid_count_parameter(self, caplog):
- with pytest.raises(Aoe2NetException):
+ with pytest.raises(Aoe2NetError):
self.client.match_history(count=1500, steam_id=76561199003184910)
for record in caplog.records:
@@ -47,7 +44,7 @@ def test_match_history_invalid_count_parameter(self, caplog):
assert "'count' has to be 1000 or less, but 1500 was provided" in caplog.text
def test_match_history_misses_required_param(self, caplog):
- with pytest.raises(Aoe2NetException):
+ with pytest.raises(Aoe2NetError):
self.client.match_history()
for record in caplog.records:
@@ -55,7 +52,7 @@ def test_match_history_misses_required_param(self, caplog):
assert "Missing one of 'steam_id', 'profile_id'" in caplog.text
def test_rating_history_invalid_count_parameter(self, caplog):
- with pytest.raises(Aoe2NetException):
+ with pytest.raises(Aoe2NetError):
self.client.rating_history(count=12_000, steam_id=76561199003184910)
for record in caplog.records:
@@ -63,28 +60,28 @@ def test_rating_history_invalid_count_parameter(self, caplog):
assert "'count' has to be 10 000 or less, but 12000 was provided" in caplog.text
def test_rating_history_misses_required_param(self, caplog):
- with pytest.raises(Aoe2NetException):
+ with pytest.raises(Aoe2NetError):
self.client.rating_history()
for record in caplog.records:
assert record.levelname == "ERROR"
assert "Missing one of 'steam_id', 'profile_id'" in caplog.text
- def test_matches_invalid_count_parameter(self, caplog):
- with pytest.raises(Aoe2NetException):
- self.client.matches(count=2000)
+ # def test_matches_invalid_count_parameter(self, caplog):
+ # with pytest.raises(Aoe2NetError):
+ # self.client.matches(count=2000)
- for record in caplog.records:
- assert record.levelname == "ERROR"
- assert "'count' has to be 1000 or less, but 2000 was provided." in caplog.text
+ # for record in caplog.records:
+ # assert record.levelname == "ERROR"
+ # assert "'count' has to be 1000 or less, but 2000 was provided." in caplog.text
- def test_match_misses_required_param(self, caplog):
- with pytest.raises(Aoe2NetException):
- self.client.match()
+ # def test_match_misses_required_param(self, caplog):
+ # with pytest.raises(Aoe2NetError):
+ # self.client.match()
- for record in caplog.records:
- assert record.levelname == "ERROR"
- assert "Missing one of 'uuid', 'match_id'" in caplog.text
+ # for record in caplog.records:
+ # assert record.levelname == "ERROR"
+ # assert "Missing one of 'uuid', 'match_id'" in caplog.text
@responses.activate
def test_raise_on_invalid_status_codes(self, caplog):
@@ -95,7 +92,7 @@ def test_raise_on_invalid_status_codes(self, caplog):
status=404,
)
- with pytest.raises(Aoe2NetException):
+ with pytest.raises(Aoe2NetError):
_ = _get_request_response_json(self.client.session, url="https://local/test/endpoint")
for record in caplog.records:
@@ -241,69 +238,81 @@ def test_leaderboard_endpoint_with_profileid(self, leaderboard_profileid_payload
"profile_id=459658"
)
- @responses.activate
+ # @responses.activate
def test_lobbies_endpoint(self, lobbies_defaults_payload):
- responses.add(
- responses.GET,
- "https://aoe2.net/api/lobbies",
- json=lobbies_defaults_payload,
- status=200,
- )
-
- result = self.client.lobbies()
- assert isinstance(result, list)
- assert isinstance(result[0], MatchLobby)
- assert result == [MatchLobby(**lobby) for lobby in lobbies_defaults_payload]
-
- assert len(responses.calls) == 1
- assert responses.calls[0].request.params == {"game": "aoe2de"}
- assert responses.calls[0].request.url == "https://aoe2.net/api/lobbies?game=aoe2de"
-
- @responses.activate
+ # This was removed from the aoe2.net API, just test we raise
+ with pytest.raises(RemovedApiEndpointError):
+ self.client.lobbies()
+
+ # responses.add(
+ # responses.GET,
+ # "https://aoe2.net/api/lobbies",
+ # json=lobbies_defaults_payload,
+ # status=200,
+ # )
+
+ # result = self.client.lobbies()
+ # assert isinstance(result, list)
+ # assert isinstance(result[0], MatchLobby)
+ # assert result == [MatchLobby(**lobby) for lobby in lobbies_defaults_payload]
+
+ # assert len(responses.calls) == 1
+ # assert responses.calls[0].request.params == {"game": "aoe2de"}
+ # assert responses.calls[0].request.url == "https://aoe2.net/api/lobbies?game=aoe2de"
+
+ # @responses.activate
def test_last_match_endpoint_with_steamid(self, last_match_steamid_payload):
- responses.add(
- responses.GET,
- "https://aoe2.net/api/player/lastmatch",
- json=last_match_steamid_payload,
- status=200,
- )
-
- result = self.client.last_match(steam_id=76561199003184910)
- assert isinstance(result, LastMatchResponse)
- assert result == LastMatchResponse(**last_match_steamid_payload)
-
- assert len(responses.calls) == 1
- assert responses.calls[0].request.params == {
- "game": "aoe2de",
- "steam_id": "76561199003184910",
- }
- assert (
- responses.calls[0].request.url == "https://aoe2.net/api/player/lastmatch?"
- "game=aoe2de&steam_id=76561199003184910"
- )
-
- @responses.activate
+ # This was removed from the aoe2.net API, just test we raise
+ with pytest.raises(RemovedApiEndpointError):
+ self.client.last_match(steam_id=76561199003184910)
+
+ # responses.add(
+ # responses.GET,
+ # "https://aoe2.net/api/player/lastmatch",
+ # json=last_match_steamid_payload,
+ # status=200,
+ # )
+
+ # result = self.client.last_match(steam_id=76561199003184910)
+ # assert isinstance(result, LastMatchResponse)
+ # assert result == LastMatchResponse(**last_match_steamid_payload)
+
+ # assert len(responses.calls) == 1
+ # assert responses.calls[0].request.params == {
+ # "game": "aoe2de",
+ # "steam_id": "76561199003184910",
+ # }
+ # assert (
+ # responses.calls[0].request.url == "https://aoe2.net/api/player/lastmatch?"
+ # "game=aoe2de&steam_id=76561199003184910"
+ # )
+
+ # @responses.activate
def test_last_match_endpoint_with_profileid(self, last_match_profileid_payload):
- responses.add(
- responses.GET,
- "https://aoe2.net/api/player/lastmatch",
- json=last_match_profileid_payload,
- status=200,
- )
-
- result = self.client.last_match(profile_id=459658)
- assert isinstance(result, LastMatchResponse)
- assert result == LastMatchResponse(**last_match_profileid_payload)
-
- assert len(responses.calls) == 1
- assert responses.calls[0].request.params == {
- "game": "aoe2de",
- "profile_id": "459658",
- }
- assert (
- responses.calls[0].request.url == "https://aoe2.net/api/player/lastmatch?"
- "game=aoe2de&profile_id=459658"
- )
+ # This was removed from the aoe2.net API, just test we raise
+ with pytest.raises(RemovedApiEndpointError):
+ self.client.last_match(profile_id=459658)
+
+ # responses.add(
+ # responses.GET,
+ # "https://aoe2.net/api/player/lastmatch",
+ # json=last_match_profileid_payload,
+ # status=200,
+ # )
+
+ # result = self.client.last_match(profile_id=459658)
+ # assert isinstance(result, LastMatchResponse)
+ # assert result == LastMatchResponse(**last_match_profileid_payload)
+
+ # assert len(responses.calls) == 1
+ # assert responses.calls[0].request.params == {
+ # "game": "aoe2de",
+ # "profile_id": "459658",
+ # }
+ # assert (
+ # responses.calls[0].request.url == "https://aoe2.net/api/player/lastmatch?"
+ # "game=aoe2de&profile_id=459658"
+ # )
@responses.activate
def test_match_history_endpoint_with_steamid(self, match_history_steamid_payload):
@@ -414,247 +423,128 @@ def test_rating_history_endpoint_with_profileid(self, rating_history_profileid_p
"count=20&profile_id=459658"
)
- @responses.activate
+ # @responses.activate
def test_matches_endpoint_defaults(self, matches_defaults_payload):
- responses.add(
- responses.GET,
- "https://aoe2.net/api/matches",
- json=matches_defaults_payload,
- status=200,
- )
-
- result = self.client.matches()
- assert isinstance(result, list)
- assert isinstance(result[0], MatchLobby)
- assert result == [MatchLobby(**lobby) for lobby in matches_defaults_payload]
-
- assert len(responses.calls) == 1
- assert responses.calls[0].request.params == {
- "game": "aoe2de",
- "count": "10",
- }
- assert responses.calls[0].request.url == "https://aoe2.net/api/matches?game=aoe2de&count=10"
-
- @responses.activate
+ # This was removed from the aoe2.net API, just test we raise
+ with pytest.raises(RemovedApiEndpointError):
+ self.client.matches()
+
+ # responses.add(
+ # responses.GET,
+ # "https://aoe2.net/api/matches",
+ # json=matches_defaults_payload,
+ # status=200,
+ # )
+
+ # result = self.client.matches()
+ # assert isinstance(result, list)
+ # assert isinstance(result[0], MatchLobby)
+ # assert result == [MatchLobby(**lobby) for lobby in matches_defaults_payload]
+
+ # assert len(responses.calls) == 1
+ # assert responses.calls[0].request.params == {
+ # "game": "aoe2de",
+ # "count": "10",
+ # }
+ # assert responses.calls[0].request.url == "https://aoe2.net/api/matches?game=aoe2de&count=10"
+
+ # @responses.activate
def test_matches_endpoint_with_since(self, matches_since_payload):
- responses.add(
- responses.GET,
- "https://aoe2.net/api/matches",
- json=matches_since_payload,
- status=200,
- )
-
- result = self.client.matches(since=1596775000)
- assert isinstance(result, list)
- assert isinstance(result[0], MatchLobby)
- assert result == [MatchLobby(**lobby) for lobby in matches_since_payload]
-
- assert len(responses.calls) == 1
- assert responses.calls[0].request.params == {
- "game": "aoe2de",
- "count": "10",
- "since": "1596775000",
- }
- assert (
- responses.calls[0].request.url
- == "https://aoe2.net/api/matches?game=aoe2de&count=10&since=1596775000"
- )
-
- @responses.activate
+ # This was removed from the aoe2.net API, just test we raise
+ with pytest.raises(RemovedApiEndpointError):
+ self.client.matches()
+
+ # responses.add(
+ # responses.GET,
+ # "https://aoe2.net/api/matches",
+ # json=matches_since_payload,
+ # status=200,
+ # )
+
+ # result = self.client.matches(since=1596775000)
+ # assert isinstance(result, list)
+ # assert isinstance(result[0], MatchLobby)
+ # assert result == [MatchLobby(**lobby) for lobby in matches_since_payload]
+
+ # assert len(responses.calls) == 1
+ # assert responses.calls[0].request.params == {
+ # "game": "aoe2de",
+ # "count": "10",
+ # "since": "1596775000",
+ # }
+ # assert (
+ # responses.calls[0].request.url
+ # == "https://aoe2.net/api/matches?game=aoe2de&count=10&since=1596775000"
+ # )
+
+ # @responses.activate
def test_match_endpoint_with_uuid(self, match_uuid_payload):
- responses.add(
- responses.GET,
- "https://aoe2.net/api/match",
- json=match_uuid_payload,
- status=200,
- )
-
- result = self.client.match(uuid="66ec2575-5ee4-d241-a1fc-d7ffeffb48b6")
- assert isinstance(result, MatchLobby)
- assert result == MatchLobby(**match_uuid_payload)
-
- assert len(responses.calls) == 1
- assert responses.calls[0].request.params == {
- "game": "aoe2de",
- "uuid": "66ec2575-5ee4-d241-a1fc-d7ffeffb48b6",
- }
- assert (
- responses.calls[0].request.url
- == "https://aoe2.net/api/match?game=aoe2de&uuid=66ec2575-5ee4-d241-a1fc-d7ffeffb48b6"
- )
-
- @responses.activate
+ # This was removed from the aoe2.net API, just test we raise
+ with pytest.raises(RemovedApiEndpointError):
+ self.client.match(uuid="66ec2575-5ee4-d241-a1fc-d7ffeffb48b6")
+
+ # responses.add(
+ # responses.GET,
+ # "https://aoe2.net/api/match",
+ # json=match_uuid_payload,
+ # status=200,
+ # )
+
+ # result = self.client.match(uuid="66ec2575-5ee4-d241-a1fc-d7ffeffb48b6")
+ # assert isinstance(result, MatchLobby)
+ # assert result == MatchLobby(**match_uuid_payload)
+
+ # assert len(responses.calls) == 1
+ # assert responses.calls[0].request.params == {
+ # "game": "aoe2de",
+ # "uuid": "66ec2575-5ee4-d241-a1fc-d7ffeffb48b6",
+ # }
+ # assert (
+ # responses.calls[0].request.url
+ # == "https://aoe2.net/api/match?game=aoe2de&uuid=66ec2575-5ee4-d241-a1fc-d7ffeffb48b6"
+ # )
+
+ # @responses.activate
def test_match_endpoint_with_matchid(self, match_matchid_payload):
- responses.add(
- responses.GET,
- "https://aoe2.net/api/match",
- json=match_matchid_payload,
- status=200,
- )
-
- result = self.client.match(match_id=32435313)
- assert isinstance(result, MatchLobby)
- assert result == MatchLobby(**match_matchid_payload)
-
- assert len(responses.calls) == 1
- assert responses.calls[0].request.params == {
- "game": "aoe2de",
- "match_id": "32435313",
- }
- assert responses.calls[0].request.url == "https://aoe2.net/api/match?game=aoe2de&match_id=32435313"
-
- @responses.activate
+ # This was removed from the aoe2.net API, just test we raise
+ with pytest.raises(RemovedApiEndpointError):
+ self.client.match(uuid="66ec2575-5ee4-d241-a1fc-d7ffeffb48b6")
+
+ # responses.add(
+ # responses.GET,
+ # "https://aoe2.net/api/match",
+ # json=match_matchid_payload,
+ # status=200,
+ # )
+
+ # result = self.client.match(match_id=32435313)
+ # assert isinstance(result, MatchLobby)
+ # assert result == MatchLobby(**match_matchid_payload)
+
+ # assert len(responses.calls) == 1
+ # assert responses.calls[0].request.params == {
+ # "game": "aoe2de",
+ # "match_id": "32435313",
+ # }
+ # assert responses.calls[0].request.url == "https://aoe2.net/api/match?game=aoe2de&match_id=32435313"
+
+ # @responses.activate
def test_num_online_endpoint(self, num_online_defaults_payload):
- responses.add(
- responses.GET,
- "https://aoe2.net/api/stats/players",
- json=num_online_defaults_payload,
- status=200,
- )
-
- result = self.client.num_online()
- assert isinstance(result, NumOnlineResponse)
- assert result == NumOnlineResponse(**num_online_defaults_payload)
-
- assert len(responses.calls) == 1
- assert responses.calls[0].request.params == {"game": "aoe2de"}
- assert responses.calls[0].request.url == "https://aoe2.net/api/stats/players?game=aoe2de"
-
-
-# ----- Fixtures ----- #
-
-
-@pytest.fixture()
-def strings_defaults_payload() -> dict:
- strings_response_file = INPUTS_DIR / "strings.json"
- with strings_response_file.open("r") as fileobj:
- payload = json.load(fileobj)
- return payload
-
-
-@pytest.fixture()
-def leaderboard_defaults_payload() -> dict:
- leaderboard_defaults_file = INPUTS_DIR / "leaderboard_defaults.json"
- with leaderboard_defaults_file.open("r") as fileobj:
- payload = json.load(fileobj)
- return payload
-
-
-@pytest.fixture()
-def leaderboard_search_payload() -> dict:
- leaderboard_search_file = INPUTS_DIR / "leaderboard_search.json"
- with leaderboard_search_file.open("r") as fileobj:
- payload = json.load(fileobj)
- return payload
-
-
-@pytest.fixture()
-def leaderboard_steamid_payload() -> dict:
- leaderboard_steamid_file = INPUTS_DIR / "leaderboard_steamid.json"
- with leaderboard_steamid_file.open("r") as fileobj:
- payload = json.load(fileobj)
- return payload
-
-
-@pytest.fixture()
-def leaderboard_profileid_payload() -> dict:
- leaderboard_profileid_file = INPUTS_DIR / "leaderboard_profileid.json"
- with leaderboard_profileid_file.open("r") as fileobj:
- payload = json.load(fileobj)
- return payload
-
-
-@pytest.fixture()
-def lobbies_defaults_payload() -> dict:
- lobbies_defaults_file = INPUTS_DIR / "lobbies.json"
- with lobbies_defaults_file.open("r") as fileobj:
- payload = json.load(fileobj)
- return payload
-
-
-@pytest.fixture()
-def last_match_steamid_payload() -> dict:
- last_match_steamid_file = INPUTS_DIR / "last_match_steamid.json"
- with last_match_steamid_file.open("r") as fileobj:
- payload = json.load(fileobj)
- return payload
-
-
-@pytest.fixture()
-def last_match_profileid_payload() -> dict:
- last_match_profileid_file = INPUTS_DIR / "last_match_profileid.json"
- with last_match_profileid_file.open("r") as fileobj:
- payload = json.load(fileobj)
- return payload
-
-
-@pytest.fixture()
-def match_history_steamid_payload() -> dict:
- match_history_steamid_file = INPUTS_DIR / "match_history_steamid.json"
- with match_history_steamid_file.open("r") as fileobj:
- payload = json.load(fileobj)
- return payload
-
-
-@pytest.fixture()
-def match_history_profileid_payload() -> dict:
- match_history_profileid_file = INPUTS_DIR / "match_history_profileid.json"
- with match_history_profileid_file.open("r") as fileobj:
- payload = json.load(fileobj)
- return payload
-
-
-@pytest.fixture()
-def rating_history_steamid_payload() -> dict:
- rating_history_steamid_file = INPUTS_DIR / "rating_history_steamid.json"
- with rating_history_steamid_file.open("r") as fileobj:
- payload = json.load(fileobj)
- return payload
-
-
-@pytest.fixture()
-def rating_history_profileid_payload() -> dict:
- rating_history_profileid_file = INPUTS_DIR / "rating_history_profileid.json"
- with rating_history_profileid_file.open("r") as fileobj:
- payload = json.load(fileobj)
- return payload
-
-
-@pytest.fixture()
-def matches_defaults_payload() -> dict:
- matches_defaults_file = INPUTS_DIR / "matches_defaults.json"
- with matches_defaults_file.open("r") as fileobj:
- payload = json.load(fileobj)
- return payload
-
-
-@pytest.fixture()
-def matches_since_payload() -> dict:
- matches_since_file = INPUTS_DIR / "matches_since.json"
- with matches_since_file.open("r") as fileobj:
- payload = json.load(fileobj)
- return payload
-
-
-@pytest.fixture()
-def match_uuid_payload() -> dict:
- match_uuid_file = INPUTS_DIR / "match_uuid.json"
- with match_uuid_file.open("r") as fileobj:
- payload = json.load(fileobj)
- return payload
-
-
-@pytest.fixture()
-def match_matchid_payload() -> dict:
- match_match_id_file = INPUTS_DIR / "match_matchid.json"
- with match_match_id_file.open("r") as fileobj:
- payload = json.load(fileobj)
- return payload
-
-
-@pytest.fixture()
-def num_online_defaults_payload() -> dict:
- num_online_defaults_file = INPUTS_DIR / "num_online.json"
- with num_online_defaults_file.open("r") as fileobj:
- payload = json.load(fileobj)
- return payload
+ # This was removed from the aoe2.net API, just test we raise
+ with pytest.raises(RemovedApiEndpointError):
+ self.client.num_online()
+
+ # responses.add(
+ # responses.GET,
+ # "https://aoe2.net/api/stats/players",
+ # json=num_online_defaults_payload,
+ # status=200,
+ # )
+
+ # result = self.client.num_online()
+ # assert isinstance(result, NumOnlineResponse)
+ # assert result == NumOnlineResponse(**num_online_defaults_payload)
+
+ # assert len(responses.calls) == 1
+ # assert responses.calls[0].request.params == {"game": "aoe2de"}
+ # assert responses.calls[0].request.url == "https://aoe2.net/api/stats/players?game=aoe2de"
diff --git a/tests/test_aoe2nightbotapi.py b/tests/test_aoe2nightbotapi.py
index ec97163..2599279 100644
--- a/tests/test_aoe2nightbotapi.py
+++ b/tests/test_aoe2nightbotapi.py
@@ -3,7 +3,7 @@
import pytest
import responses
-from aoe2netwrapper.exceptions import NightBotException
+from aoe2netwrapper.exceptions import NightBotError
from aoe2netwrapper.nightbot import AoE2NightbotAPI, _get_request_text_response_decoded
CURRENT_DIR = pathlib.Path(__file__).parent
@@ -53,7 +53,7 @@ class TestExceptions:
client = AoE2NightbotAPI()
def test_rank_misses_required_param(self, caplog):
- with pytest.raises(NightBotException):
+ with pytest.raises(NightBotError):
self.client.rank()
for record in caplog.records:
@@ -61,7 +61,7 @@ def test_rank_misses_required_param(self, caplog):
assert "Missing one of 'search', 'steam_id', 'profile_id'" in caplog.text
def test_opponent_misses_required_param(self, caplog):
- with pytest.raises(NightBotException):
+ with pytest.raises(NightBotError):
self.client.opponent()
for record in caplog.records:
@@ -69,7 +69,7 @@ def test_opponent_misses_required_param(self, caplog):
assert "Missing one of 'search', 'steam_id', 'profile_id'" in caplog.text
def test_match_misses_required_param(self, caplog):
- with pytest.raises(NightBotException):
+ with pytest.raises(NightBotError):
self.client.match()
for record in caplog.records:
@@ -77,7 +77,7 @@ def test_match_misses_required_param(self, caplog):
assert "Missing one of 'search', 'steam_id', 'profile_id'" in caplog.text
def test_civs_misses_required_param(self, caplog):
- with pytest.raises(NightBotException):
+ with pytest.raises(NightBotError):
self.client.civs()
for record in caplog.records:
@@ -85,7 +85,7 @@ def test_civs_misses_required_param(self, caplog):
assert "Missing one of 'search', 'steam_id', 'profile_id'" in caplog.text
def test_ma_misses_required_param(self, caplog):
- with pytest.raises(NightBotException):
+ with pytest.raises(NightBotError):
self.client.map()
for record in caplog.records:
@@ -101,7 +101,7 @@ def test_raise_on_invalid_status_codes(self, caplog):
status=404,
)
- with pytest.raises(NightBotException):
+ with pytest.raises(NightBotError):
_ = _get_request_text_response_decoded(self.client.session, url="https://local/test/endpoint")
for record in caplog.records:
@@ -125,12 +125,12 @@ class TestMethods:
@responses.activate
@pytest.mark.parametrize(
- "flag, search, steam_id, profile_id, returned_text",
+ ("flag", "search", "steam_id", "profile_id", "returned_text"),
[
- ["true", "GL.TheViper", None, None, RANKS["viper_flag"]],
- ["false", "GL.TheViper", None, None, RANKS["viper"]],
- ["true", None, 76561199003184910, None, RANKS["hoang_flag"]],
- ["false", None, None, 459658, RANKS["hoang"]],
+ ("true", "GL.TheViper", None, None, RANKS["viper_flag"]),
+ ("false", "GL.TheViper", None, None, RANKS["viper"]),
+ ("true", None, 76561199003184910, None, RANKS["hoang_flag"]),
+ ("false", None, None, 459658, RANKS["hoang"]),
],
)
def test_rank_endpoint(self, flag, search, steam_id, profile_id, returned_text):
@@ -152,12 +152,12 @@ def test_rank_endpoint(self, flag, search, steam_id, profile_id, returned_text):
@responses.activate
@pytest.mark.parametrize(
- "flag, search, steam_id, profile_id, returned_text",
+ ("flag", "search", "steam_id", "profile_id", "returned_text"),
[
- ["true", "GL.TheViper", None, None, OPPONENTS["viper_flag"]],
- ["false", "GL.TheViper", None, None, OPPONENTS["viper"]],
- ["true", None, 76561199003184910, None, OPPONENTS["hoang_flag"]],
- ["false", None, None, 459658, OPPONENTS["hoang"]],
+ ("true", "GL.TheViper", None, None, OPPONENTS["viper_flag"]),
+ ("false", "GL.TheViper", None, None, OPPONENTS["viper"]),
+ ("true", None, 76561199003184910, None, OPPONENTS["hoang_flag"]),
+ ("false", None, None, 459658, OPPONENTS["hoang"]),
],
)
def test_opponent_endpoint(self, flag, search, steam_id, profile_id, returned_text):
@@ -179,12 +179,12 @@ def test_opponent_endpoint(self, flag, search, steam_id, profile_id, returned_te
@responses.activate
@pytest.mark.parametrize(
- "flag, search, steam_id, profile_id, returned_text",
+ ("flag", "search", "steam_id", "profile_id", "returned_text"),
[
- ["true", "GL.TheViper", None, None, MATCH["viper_flag"]],
- ["false", "GL.TheViper", None, None, MATCH["viper"]],
- ["true", None, 76561199003184910, None, MATCH["hoang_flag"]],
- ["false", None, None, 459658, MATCH["hoang"]],
+ ("true", "GL.TheViper", None, None, MATCH["viper_flag"]),
+ ("false", "GL.TheViper", None, None, MATCH["viper"]),
+ ("true", None, 76561199003184910, None, MATCH["hoang_flag"]),
+ ("false", None, None, 459658, MATCH["hoang"]),
],
)
def test_match_endpoint(self, flag, search, steam_id, profile_id, returned_text):
@@ -206,11 +206,11 @@ def test_match_endpoint(self, flag, search, steam_id, profile_id, returned_text)
@responses.activate
@pytest.mark.parametrize(
- "search, steam_id, profile_id, returned_text",
+ ("search", "steam_id", "profile_id", "returned_text"),
[
- ["GL.TheViper", None, None, CIVS["viper"]],
- [None, 76561199003184910, None, CIVS["hoang"]],
- [None, None, 459658, CIVS["hoang"]],
+ ("GL.TheViper", None, None, CIVS["viper"]),
+ (None, 76561199003184910, None, CIVS["hoang"]),
+ (None, None, 459658, CIVS["hoang"]),
],
)
def test_civs_endpoint(self, search, steam_id, profile_id, returned_text):
@@ -231,11 +231,11 @@ def test_civs_endpoint(self, search, steam_id, profile_id, returned_text):
@responses.activate
@pytest.mark.parametrize(
- "search, steam_id, profile_id, returned_text",
+ ("search", "steam_id", "profile_id", "returned_text"),
[
- ["GL.TheViper", None, None, MAP["viper"]],
- [None, 76561199003184910, None, MAP["hoang"]],
- [None, None, 459658, MAP["hoang"]],
+ ("GL.TheViper", None, None, MAP["viper"]),
+ (None, 76561199003184910, None, MAP["hoang"]),
+ (None, None, 459658, MAP["hoang"]),
],
)
def test_map_endpoint(self, search, steam_id, profile_id, returned_text):
diff --git a/tests/test_converters.py b/tests/test_converters.py
index 92585f3..d9c6c10 100644
--- a/tests/test_converters.py
+++ b/tests/test_converters.py
@@ -1,15 +1,9 @@
-import json
-import pathlib
-
import pandas as pd
import pytest
import responses
from aoe2netwrapper import AoE2NetAPI
-from aoe2netwrapper.converters import Convert
-
-CURRENT_DIR = pathlib.Path(__file__).parent
-INPUTS_DIR = CURRENT_DIR / "inputs"
+from aoe2netwrapper.converters import Convert, _unfold_match_lobby_to_dataframe
class TestExceptions:
@@ -29,21 +23,23 @@ def test_leaderboard_fail_on_wrong_type(self, caplog):
assert record.levelname == "ERROR"
assert "Tried to use method with a parameter of type != 'LeaderBoardResponse'" in caplog.text
- def test_lobbies_fail_on_wrong_type(self, caplog):
- with pytest.raises(TypeError):
- _ = Convert.lobbies(None)
+ # def test_lobbies_fail_on_wrong_type(self, caplog):
+ # # No longer tested as endpoint and method have been removed
+ # with pytest.raises(TypeError):
+ # _ = Convert.lobbies(None)
- for record in caplog.records:
- assert record.levelname == "ERROR"
- assert "Tried to use method with a parameter of type != 'List[MatchLobby]'" in caplog.text
+ # for record in caplog.records:
+ # assert record.levelname == "ERROR"
+ # assert "Tried to use method with a parameter of type != 'List[MatchLobby]'" in caplog.text
- def test_last_match_fail_on_wrong_type(self, caplog):
- with pytest.raises(TypeError):
- _ = Convert.last_match(json)
+ # def test_last_match_fail_on_wrong_type(self, caplog):
+ # # No longer tested as endpoint and method have been removed
+ # with pytest.raises(TypeError):
+ # _ = Convert.last_match({"a": 1})
- for record in caplog.records:
- assert record.levelname == "ERROR"
- assert "Tried to use method with a parameter of type != 'LastMatchResponse'" in caplog.text
+ # for record in caplog.records:
+ # assert record.levelname == "ERROR"
+ # assert "Tried to use method with a parameter of type != 'LastMatchResponse'" in caplog.text
def test_match_history_fail_on_wrong_type(self, caplog):
with pytest.raises(TypeError):
@@ -61,30 +57,40 @@ def test_rating_history_fail_on_wrong_type(self, caplog):
assert record.levelname == "ERROR"
assert "Tried to use method with a parameter of type != 'List[RatingTimePoint]'" in caplog.text
- def test_matches_fail_on_wrong_type(self, caplog):
- with pytest.raises(TypeError):
- _ = Convert.matches(TypeError)
+ # def test_matches_fail_on_wrong_type(self, caplog):
+ # # No longer tested as endpoint and method have been removed
+ # with pytest.raises(TypeError):
+ # _ = Convert.matches(TypeError)
- for record in caplog.records:
- assert record.levelname == "ERROR"
- assert "Tried to use method with a parameter of type != 'List[MatchLobby]'" in caplog.text
+ # for record in caplog.records:
+ # assert record.levelname == "ERROR"
+ # assert "Tried to use method with a parameter of type != 'List[MatchLobby]'" in caplog.text
- def test_match_fail_on_wrong_type(self, caplog):
- with pytest.raises(TypeError):
- _ = Convert.match(CURRENT_DIR)
+ # def test_match_fail_on_wrong_type(self, caplog):
+ # # No longer tested as endpoint and method have been removed
+ # with pytest.raises(TypeError):
+ # _ = Convert.match({"a": 1})
- for record in caplog.records:
- assert record.levelname == "ERROR"
- assert "Tried to use method with a parameter of type != 'MatchLobby'" in caplog.text
+ # for record in caplog.records:
+ # assert record.levelname == "ERROR"
+ # assert "Tried to use method with a parameter of type != 'MatchLobby'" in caplog.text
- def test_num_online_fail_on_wrong_type(self, caplog):
+ # def test_num_online_fail_on_wrong_type(self, caplog):
+ # # No longer tested as endpoint and method have been removed
+ # with pytest.raises(TypeError):
+ # _ = Convert.num_online(self)
+
+ # for record in caplog.records:
+ # assert record.levelname == "ERROR"
+ # assert "Tried to use method with a parameter of type != 'NumOnlineResponse'" in caplog.text
+
+ def test_match_lobby_unfolding_raises_on_wrong_type(self, caplog):
with pytest.raises(TypeError):
- _ = Convert.num_online(self)
+ _ = _unfold_match_lobby_to_dataframe(10)
for record in caplog.records:
assert record.levelname == "ERROR"
- assert "Tried to use method with a parameter of type != 'NumOnlineResponse'" in caplog.text
-
+ assert "Tried to use method with a parameter of type != 'MatchLobby'" in caplog.text
class TestConvert:
client = AoE2NetAPI()
@@ -123,39 +129,41 @@ def test_leaderboard(self, leaderboard_defaults_payload, leaderboard_converted):
assert dframe.shape == (10, 23)
pd.testing.assert_frame_equal(dframe, leaderboard_converted)
- @responses.activate
- def test_lobbies(self, lobbies_defaults_payload, lobbies_converted):
- responses.add(
- responses.GET,
- "https://aoe2.net/api/lobbies",
- json=lobbies_defaults_payload,
- status=200,
- )
-
- result = self.client.lobbies()
- dframe = Convert.lobbies(result)
-
- assert isinstance(dframe, pd.DataFrame)
- assert dframe.size == 34086
- assert dframe.shape == (598, 57)
- pd.testing.assert_frame_equal(dframe, lobbies_converted)
-
- @responses.activate
- def test_last_match_endpoint_with_steamid(self, last_match_steamid_payload, last_match_converted):
- responses.add(
- responses.GET,
- "https://aoe2.net/api/player/lastmatch",
- json=last_match_steamid_payload,
- status=200,
- )
-
- result = self.client.last_match(steam_id=76561199003184910)
- dframe = Convert.last_match(result)
-
- assert isinstance(dframe, pd.DataFrame)
- assert dframe.size == 45
- assert dframe.shape == (1, 45)
- pd.testing.assert_frame_equal(dframe, last_match_converted)
+ # @responses.activate
+ # def test_lobbies(self, lobbies_defaults_payload, lobbies_converted):
+ # # No longer tested as endpoint and method have been removed
+ # responses.add(
+ # responses.GET,
+ # "https://aoe2.net/api/lobbies",
+ # json=lobbies_defaults_payload,
+ # status=200,
+ # )
+
+ # result = self.client.lobbies()
+ # dframe = Convert.lobbies(result)
+
+ # assert isinstance(dframe, pd.DataFrame)
+ # assert dframe.size == 34086
+ # assert dframe.shape == (598, 57)
+ # pd.testing.assert_frame_equal(dframe, lobbies_converted)
+
+ # @responses.activate
+ # def test_last_match_endpoint_with_steamid(self, last_match_steamid_payload, last_match_converted):
+ # # No longer tested as endpoint and method have been removed
+ # responses.add(
+ # responses.GET,
+ # "https://aoe2.net/api/player/lastmatch",
+ # json=last_match_steamid_payload,
+ # status=200,
+ # )
+
+ # result = self.client.last_match(steam_id=76561199003184910)
+ # dframe = Convert.last_match(result)
+
+ # assert isinstance(dframe, pd.DataFrame)
+ # assert dframe.size == 45
+ # assert dframe.shape == (1, 45)
+ # pd.testing.assert_frame_equal(dframe, last_match_converted)
@responses.activate
def test_match_history(self, match_history_steamid_payload, match_history_converted):
@@ -191,182 +199,56 @@ def test_rating_history(self, rating_history_profileid_payload, rating_history_c
assert dframe.shape == (100, 6)
pd.testing.assert_frame_equal(dframe, rating_history_converted)
- @responses.activate
- def test_matches(self, matches_defaults_payload, matches_converted):
- responses.add(
- responses.GET,
- "https://aoe2.net/api/matches",
- json=matches_defaults_payload,
- status=200,
- )
-
- result = self.client.matches()
- dframe = Convert.matches(result)
-
- assert isinstance(dframe, pd.DataFrame)
- assert dframe.size == 2622
- assert dframe.shape == (46, 57)
- pd.testing.assert_frame_equal(dframe, matches_converted)
-
- @responses.activate
- def test_match(self, match_uuid_payload, match_converted):
- responses.add(
- responses.GET,
- "https://aoe2.net/api/match",
- json=match_uuid_payload,
- status=200,
- )
-
- result = self.client.match(uuid="66ec2575-5ee4-d241-a1fc-d7ffeffb48b6")
- dframe = Convert.match(result)
-
- assert isinstance(dframe, pd.DataFrame)
- assert dframe.size == 456
- assert dframe.shape == (8, 57)
- pd.testing.assert_frame_equal(dframe, match_converted)
-
- @responses.activate
- def test_num_online(self, num_online_defaults_payload, num_online_converted):
- responses.add(
- responses.GET,
- "https://aoe2.net/api/stats/players",
- json=num_online_defaults_payload,
- status=200,
- )
-
- result = self.client.num_online()
- dframe = Convert.num_online(result)
-
- assert isinstance(dframe, pd.DataFrame)
- assert dframe.size == 5752
- assert dframe.shape == (719, 8)
- pd.testing.assert_frame_equal(dframe, num_online_converted)
-
-
-# ----- Fixtures ----- #
-
-
-@pytest.fixture()
-def strings_defaults_payload() -> dict:
- strings_response_file = INPUTS_DIR / "strings.json"
- with strings_response_file.open("r") as fileobj:
- payload = json.load(fileobj)
- return payload
-
-
-@pytest.fixture()
-def strings_converted() -> pd.DataFrame:
- strings_converted_file = INPUTS_DIR / "convert_strings.pkl"
- return pd.read_pickle(strings_converted_file)
-
-
-@pytest.fixture()
-def leaderboard_defaults_payload() -> dict:
- leaderboard_defaults_file = INPUTS_DIR / "leaderboard_defaults.json"
- with leaderboard_defaults_file.open("r") as fileobj:
- payload = json.load(fileobj)
- return payload
-
-
-@pytest.fixture()
-def leaderboard_converted() -> pd.DataFrame:
- leaderboard_converted_file = INPUTS_DIR / "convert_leaderboard.pkl"
- return pd.read_pickle(leaderboard_converted_file)
-
-
-@pytest.fixture()
-def lobbies_defaults_payload() -> dict:
- lobbies_defaults_file = INPUTS_DIR / "lobbies.json"
- with lobbies_defaults_file.open("r") as fileobj:
- payload = json.load(fileobj)
- return payload
-
-
-@pytest.fixture()
-def lobbies_converted() -> pd.DataFrame:
- lobbies_converted_file = INPUTS_DIR / "convert_lobbies.pkl"
- return pd.read_pickle(lobbies_converted_file)
-
-
-@pytest.fixture()
-def last_match_steamid_payload() -> dict:
- last_match_steamid_file = INPUTS_DIR / "last_match_steamid.json"
- with last_match_steamid_file.open("r") as fileobj:
- payload = json.load(fileobj)
- return payload
-
-
-@pytest.fixture()
-def last_match_converted() -> pd.DataFrame:
- last_match_converted_file = INPUTS_DIR / "convert_last_match.pkl"
- return pd.read_pickle(last_match_converted_file)
-
-
-@pytest.fixture()
-def match_history_steamid_payload() -> dict:
- match_history_steamid_file = INPUTS_DIR / "match_history_steamid.json"
- with match_history_steamid_file.open("r") as fileobj:
- payload = json.load(fileobj)
- return payload
-
-
-@pytest.fixture()
-def match_history_converted() -> pd.DataFrame:
- match_history_converted_file = INPUTS_DIR / "convert_match_history.pkl"
- return pd.read_pickle(match_history_converted_file)
-
-
-@pytest.fixture()
-def rating_history_profileid_payload() -> dict:
- rating_history_profileid_file = INPUTS_DIR / "rating_history_profileid.json"
- with rating_history_profileid_file.open("r") as fileobj:
- payload = json.load(fileobj)
- return payload
-
-
-@pytest.fixture()
-def rating_history_converted() -> pd.DataFrame:
- rating_history_converted_file = INPUTS_DIR / "convert_rating_history.pkl"
- return pd.read_pickle(rating_history_converted_file)
-
-
-@pytest.fixture()
-def matches_defaults_payload() -> dict:
- matches_defaults_file = INPUTS_DIR / "matches_defaults.json"
- with matches_defaults_file.open("r") as fileobj:
- payload = json.load(fileobj)
- return payload
-
-
-@pytest.fixture()
-def matches_converted() -> pd.DataFrame:
- matches_converted_file = INPUTS_DIR / "convert_matches.pkl"
- return pd.read_pickle(matches_converted_file)
-
-
-@pytest.fixture()
-def match_uuid_payload() -> dict:
- match_uuid_file = INPUTS_DIR / "match_uuid.json"
- with match_uuid_file.open("r") as fileobj:
- payload = json.load(fileobj)
- return payload
-
-
-@pytest.fixture()
-def match_converted() -> pd.DataFrame:
- match_converted_file = INPUTS_DIR / "convert_match.pkl"
- return pd.read_pickle(match_converted_file)
-
-
-@pytest.fixture()
-def num_online_defaults_payload() -> dict:
- num_online_defaults_file = INPUTS_DIR / "num_online.json"
- with num_online_defaults_file.open("r") as fileobj:
- payload = json.load(fileobj)
- return payload
-
-
-@pytest.fixture()
-def num_online_converted() -> pd.DataFrame:
- num_online_converted_file = INPUTS_DIR / "convert_num_online.pkl"
- return pd.read_pickle(num_online_converted_file)
+ # @responses.activate
+ # def test_matches(self, matches_defaults_payload, matches_converted):
+ # # No longer tested as endpoint and method have been removed
+ # responses.add(
+ # responses.GET,
+ # "https://aoe2.net/api/matches",
+ # json=matches_defaults_payload,
+ # status=200,
+ # )
+
+ # result = self.client.matches()
+ # dframe = Convert.matches(result)
+
+ # assert isinstance(dframe, pd.DataFrame)
+ # assert dframe.size == 2622
+ # assert dframe.shape == (46, 57)
+ # pd.testing.assert_frame_equal(dframe, matches_converted)
+
+ # @responses.activate
+ # def test_match(self, match_uuid_payload, match_converted):
+ # # No longer tested as endpoint and method have been removed
+ # responses.add(
+ # responses.GET,
+ # "https://aoe2.net/api/match",
+ # json=match_uuid_payload,
+ # status=200,
+ # )
+
+ # result = self.client.match(uuid="66ec2575-5ee4-d241-a1fc-d7ffeffb48b6")
+ # dframe = Convert.match(result)
+
+ # assert isinstance(dframe, pd.DataFrame)
+ # assert dframe.size == 456
+ # assert dframe.shape == (8, 57)
+ # pd.testing.assert_frame_equal(dframe, match_converted)
+
+ # @responses.activate
+ # def test_num_online(self, num_online_defaults_payload, num_online_converted):
+ # # No longer tested as endpoint and method have been removed
+ # responses.add(
+ # responses.GET,
+ # "https://aoe2.net/api/stats/players",
+ # json=num_online_defaults_payload,
+ # status=200,
+ # )
+
+ # result = self.client.num_online()
+ # dframe = Convert.num_online(result)
+
+ # assert isinstance(dframe, pd.DataFrame)
+ # assert dframe.size == 5752
+ # assert dframe.shape == (719, 8)
+ # pd.testing.assert_frame_equal(dframe, num_online_converted)