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 @@
- - Github Actions + + Github Actions @@ -26,20 +26,19 @@
- Code Style - - Linter + + Linter - - Build tool + + Build tool @@ -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)