├── .python-version ├── src └── linkup │ ├── py.typed │ ├── _version.py │ ├── __init__.py │ ├── _errors.py │ ├── _types.py │ └── _client.py ├── .env.example ├── .mdformat.toml ├── .yamlfmt.yaml ├── .taplo.toml ├── .gitignore ├── tests └── unit │ ├── conftest.py │ └── client_test.py ├── .editorconfig ├── .github ├── pull_request_template.md └── workflows │ ├── test.yml │ └── release.yml ├── examples ├── 5_fetch.py ├── 1_search_results_search.py ├── 2_sourced_answer_search.py ├── 3_structured_search.py └── 4_asynchronous_search.py ├── Makefile ├── .pre-commit-config.yaml ├── LICENSE ├── pyproject.toml ├── README.md ├── CHANGELOG.md └── uv.lock /.python-version: -------------------------------------------------------------------------------- 1 | 3.9 2 | -------------------------------------------------------------------------------- /src/linkup/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | LINKUP_API_KEY= 2 | -------------------------------------------------------------------------------- /.mdformat.toml: -------------------------------------------------------------------------------- 1 | number = true 2 | wrap = 100 3 | -------------------------------------------------------------------------------- /.yamlfmt.yaml: -------------------------------------------------------------------------------- 1 | formatter: 2 | type: basic 3 | retain_line_breaks_single: true 4 | max_line_length: 100 5 | -------------------------------------------------------------------------------- /src/linkup/_version.py: -------------------------------------------------------------------------------- 1 | from importlib.metadata import version as get_version 2 | 3 | __version__: str = get_version("linkup-sdk") 4 | -------------------------------------------------------------------------------- /.taplo.toml: -------------------------------------------------------------------------------- 1 | [formatting] 2 | align_comments = false 3 | allowed_blank_lines = 1 4 | column_width = 100 5 | reorder_arrays = true 6 | reorder_inline_tables = true 7 | reorder_keys = true 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Python-generated files 2 | .idea/ 3 | .ipynb_checkpoints/ 4 | .venv/ 5 | .vscode/ 6 | __pycache__/ 7 | build/ 8 | dist/ 9 | wheels/ 10 | .coverage 11 | .env 12 | *.egg-info 13 | *.py[oc] 14 | 15 | # MacOS stuff 16 | .DS_Store 17 | -------------------------------------------------------------------------------- /tests/unit/conftest.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import pytest 4 | 5 | from linkup import LinkupClient 6 | 7 | 8 | @pytest.fixture(scope="session") 9 | def client() -> LinkupClient: 10 | if os.getenv("LINKUP_API_KEY") is None: 11 | os.environ["LINKUP_API_KEY"] = "" 12 | return LinkupClient() 13 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | insert_final_newline = true 6 | 7 | [*.md] 8 | indent_style = space 9 | indent_size = 2 10 | max_line_length = 100 11 | 12 | [*.py] 13 | indent_style = space 14 | indent_size = 4 15 | max_line_length = 100 16 | 17 | [*.toml] 18 | indent_style = space 19 | indent_size = 2 20 | max_line_length = 100 21 | 22 | [*.yaml] 23 | indent_style = space 24 | indent_size = 2 25 | max_line_length = 100 26 | 27 | [Makefile] 28 | indent_style = tab 29 | indent_size = 8 30 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | 4 | 5 | ## Checklist 6 | 7 | 8 | 9 | - [ ] I have installed pre-commit on this project (for instance with the `make install-dev` command) 10 | **before** creating any commit, or I have run successfully the `make format-lint` command on my 11 | changes. 12 | - [ ] I have run successfully the `make test` command on my changes. 13 | - [ ] I have updated the `README.md` if my changes affected it. 14 | -------------------------------------------------------------------------------- /examples/5_fetch.py: -------------------------------------------------------------------------------- 1 | """Example of web page fetch. 2 | 3 | The Linkup fetch can output the content of a web page as a cleaned up markdown. 4 | 5 | To use this script, copy the `.env.example` file at the root of the repository inside a `.env`, and 6 | fill the missing values, or pass a Linkup API key to the `LinkupClient` initialization. 7 | """ 8 | 9 | import rich 10 | from dotenv import load_dotenv 11 | 12 | from linkup import LinkupClient 13 | 14 | load_dotenv() 15 | client = LinkupClient() 16 | 17 | response = client.fetch( 18 | url="https://docs.linkup.so", 19 | ) 20 | rich.print(response) 21 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | pull_request: 5 | branches: ["main"] 6 | types: [opened, synchronize, reopened, ready_for_review] 7 | 8 | jobs: 9 | test: 10 | if: github.event_name != 'pull_request' || github.event.pull_request.draft == false 11 | runs-on: ubuntu-latest 12 | strategy: 13 | matrix: 14 | python-version: ["3.9", "3.13"] # Min and max supported versions 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v5 18 | 19 | - name: Install uv 20 | uses: astral-sh/setup-uv@v6 21 | with: 22 | python-version: ${{ matrix.python-version }} 23 | 24 | - name: Install dependencies 25 | run: make install 26 | 27 | - name: Test 28 | run: make test 29 | -------------------------------------------------------------------------------- /examples/1_search_results_search.py: -------------------------------------------------------------------------------- 1 | """Example of search results search. 2 | 3 | The Linkup search can output raw search results which can then be re-used in different use-cases, 4 | for instance in a RAG system, with the `output_type` parameter set to `searchResults`. 5 | 6 | To use this script, copy the `.env.example` file at the root of the repository inside a `.env`, and 7 | fill the missing values, or pass a Linkup API key to the `LinkupClient` initialization. 8 | """ 9 | 10 | import rich 11 | from dotenv import load_dotenv 12 | 13 | from linkup import LinkupClient 14 | 15 | load_dotenv() 16 | client = LinkupClient() 17 | 18 | response = client.search( 19 | query="What are the 3 major events in the life of Abraham Lincoln?", 20 | depth="standard", # or "deep" 21 | output_type="searchResults", 22 | ) 23 | rich.print(response) 24 | -------------------------------------------------------------------------------- /examples/2_sourced_answer_search.py: -------------------------------------------------------------------------------- 1 | """Example of sourced answer search. 2 | 3 | The Linkup search can also be used to perform direct Question Answering, with `output_type` set to 4 | `sourcedAnswer`. In this case, the API will output an answer to the query in natural language, 5 | along with the sources supporting it. 6 | 7 | To use this script, copy the `.env.example` file at the root of the repository inside a `.env`, and 8 | fill the missing values, or pass a Linkup API key to the `LinkupClient` initialization. 9 | """ 10 | 11 | import rich 12 | from dotenv import load_dotenv 13 | 14 | from linkup import LinkupClient 15 | 16 | load_dotenv() 17 | client = LinkupClient() 18 | 19 | response = client.search( 20 | query="What are the 3 major events in the life of Abraham Lincoln ?", 21 | depth="standard", # or "deep" 22 | output_type="sourcedAnswer", 23 | include_inline_citations=False, 24 | ) 25 | rich.print(response) 26 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | install: 2 | uv sync 3 | install-dev: 4 | @$(MAKE) install 5 | uv run pre-commit install 6 | 7 | format-lint: 8 | SKIP=no-commit-to-branch uv run pre-commit run --all-files 9 | format-lint-unsafe: 10 | uv run --with ruff ruff check --fix --unsafe-fixes . 11 | @echo 12 | @$(MAKE) format-lint 13 | 14 | test-mypy: 15 | @# Avoid running mypy on the whole directory ("./") to avoid potential conflicts with files with the same name (e.g. between different types of tests) 16 | uv run mypy ./src/ 17 | uv run mypy ./tests/unit/ 18 | test-pytest: 19 | uv run pytest --cov=src/linkup/ ./tests/unit/ 20 | test: 21 | @$(MAKE) test-mypy 22 | @echo 23 | @$(MAKE) test-pytest 24 | 25 | update-uv: 26 | uv lock --upgrade 27 | uv sync 28 | update-pre-commit: 29 | uv run pre-commit autoupdate 30 | 31 | clean: 32 | rm -rf dist/ 33 | rm -f .coverage 34 | rm -rf .mypy_cache/ 35 | rm -rf .pytest_cache/ 36 | rm -rf .ruff_cache/ 37 | rm -rf **/*/__pycache__/ 38 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v6.0.0 4 | hooks: 5 | - id: check-added-large-files 6 | - id: check-merge-conflict 7 | - id: detect-private-key 8 | - id: end-of-file-fixer 9 | - id: name-tests-test 10 | - id: no-commit-to-branch 11 | args: [--branch, main] 12 | 13 | - repo: https://github.com/astral-sh/ruff-pre-commit 14 | rev: v0.13.0 15 | hooks: 16 | - id: ruff 17 | args: [--fix] 18 | - id: ruff-format 19 | 20 | - repo: https://github.com/hukkin/mdformat 21 | rev: 0.7.22 22 | hooks: 23 | - id: mdformat 24 | exclude: CHANGELOG.md 25 | 26 | - repo: https://github.com/ComPWA/taplo-pre-commit 27 | rev: v0.9.3 28 | hooks: 29 | - id: taplo-format 30 | 31 | - repo: https://github.com/google/yamlfmt 32 | rev: v0.17.2 33 | hooks: 34 | - id: yamlfmt 35 | 36 | - repo: https://github.com/gitleaks/gitleaks 37 | rev: v8.28.0 38 | hooks: 39 | - id: gitleaks 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Linkup Technologies 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/linkup/__init__.py: -------------------------------------------------------------------------------- 1 | from ._client import LinkupClient 2 | from ._errors import ( 3 | LinkupAuthenticationError, 4 | LinkupFailedFetchError, 5 | LinkupInsufficientCreditError, 6 | LinkupInvalidRequestError, 7 | LinkupNoResultError, 8 | LinkupTooManyRequestsError, 9 | LinkupUnknownError, 10 | ) 11 | from ._types import ( 12 | LinkupFetchResponse, 13 | LinkupSearchImageResult, 14 | LinkupSearchResults, 15 | LinkupSearchStructuredResponse, 16 | LinkupSearchTextResult, 17 | LinkupSource, 18 | LinkupSourcedAnswer, 19 | ) 20 | from ._version import __version__ 21 | 22 | __all__ = [ 23 | "LinkupAuthenticationError", 24 | "LinkupClient", 25 | "LinkupFailedFetchError", 26 | "LinkupFetchResponse", 27 | "LinkupInsufficientCreditError", 28 | "LinkupInvalidRequestError", 29 | "LinkupNoResultError", 30 | "LinkupSearchImageResult", 31 | "LinkupSearchResults", 32 | "LinkupSearchStructuredResponse", 33 | "LinkupSearchTextResult", 34 | "LinkupSource", 35 | "LinkupSourcedAnswer", 36 | "LinkupTooManyRequestsError", 37 | "LinkupUnknownError", 38 | "__version__", 39 | ] 40 | -------------------------------------------------------------------------------- /examples/3_structured_search.py: -------------------------------------------------------------------------------- 1 | """Example of a structured search. 2 | 3 | With `output_type` set to `structured`, the Linkup search can be used to require any arbitrary data 4 | structure, based on a JSON schema or a `pydantic.BaseModel`. This can be used with a well defined 5 | and documented schema to steer the Linkup search in any direction. 6 | 7 | To use this script, copy the `.env.example` file at the root of the repository inside a `.env`, and 8 | fill the missing values, or pass a Linkup API key to the `LinkupClient` initialization. 9 | """ 10 | 11 | import rich 12 | from dotenv import load_dotenv 13 | from pydantic import BaseModel, Field 14 | 15 | from linkup import LinkupClient 16 | 17 | 18 | class Event(BaseModel): 19 | date: str = Field(description="The date of the event") 20 | description: str = Field(description="The description of the event") 21 | 22 | 23 | class Events(BaseModel): 24 | events: list[Event] = Field(description="The list of events") 25 | 26 | 27 | load_dotenv() 28 | client = LinkupClient() 29 | 30 | response = client.search( 31 | query="What are the 3 major events in the life of Abraham Lincoln?", 32 | depth="standard", # or "deep" 33 | output_type="structured", 34 | structured_output_schema=Events, # or json.dumps(Events.model_json_schema()) 35 | include_sources=False, 36 | ) 37 | rich.print(response) 38 | -------------------------------------------------------------------------------- /examples/4_asynchronous_search.py: -------------------------------------------------------------------------------- 1 | """Example of asynchronous search. 2 | 3 | All Linkup entrypoints come with an asynchronous version. This snippet demonstrates how to run 4 | multiple asynchronous searches concurrently, which decreases by a lot the total computation 5 | duration. 6 | 7 | To use this script, copy the `.env.example` file at the root of the repository inside a `.env`, and 8 | fill the missing values, or pass a Linkup API key to the `LinkupClient` initialization. 9 | """ 10 | 11 | import asyncio 12 | import time 13 | 14 | import rich 15 | from dotenv import load_dotenv 16 | 17 | from linkup import LinkupClient 18 | 19 | load_dotenv() 20 | client = LinkupClient() 21 | 22 | queries: list[str] = [ 23 | "What are the 3 major events in the life of Abraham Lincoln?", 24 | "What are the 3 major events in the life of George Washington?", 25 | ] 26 | 27 | t0: float = time.time() 28 | 29 | 30 | async def search(idx: int, query: str) -> None: 31 | """Run an asynchronous search and display its results and the duration from the beginning.""" 32 | response = await client.async_search( 33 | query=query, 34 | depth="standard", # or "deep" 35 | output_type="searchResults", # or "sourcedAnswer" or "structured" 36 | ) 37 | print(f"{idx + 1}: {time.time() - t0:.3f}s") 38 | rich.print(response) 39 | print("-" * 100) 40 | 41 | 42 | async def main() -> None: 43 | """Run multiple asynchronous searches concurrently.""" 44 | coroutines = [search(idx=idx, query=query) for idx, query in enumerate(queries)] 45 | await asyncio.gather(*coroutines) 46 | print(f"Total time: {time.time() - t0:.3f}s") 47 | 48 | 49 | asyncio.run(main()) 50 | -------------------------------------------------------------------------------- /src/linkup/_errors.py: -------------------------------------------------------------------------------- 1 | """Linkup custom errors.""" 2 | 3 | 4 | class LinkupInvalidRequestError(Exception): 5 | """Invalid request error, raised when the Linkup API returns a 400 status code. 6 | 7 | It is returned by the Linkup API when the request is invalid, typically when a mandatory 8 | parameter is missing, or isn't valid (type, structure, etc.). 9 | """ 10 | 11 | pass 12 | 13 | 14 | class LinkupNoResultError(Exception): 15 | """No result error, raised when the Linkup API returns a 400 status code. 16 | 17 | It is returned by the Linkup API when the search query did not yield any result. 18 | """ 19 | 20 | pass 21 | 22 | 23 | class LinkupAuthenticationError(Exception): 24 | """Authentication error, raised when the Linkup API returns a 403 status code. 25 | 26 | It is returned when there is an authentication issue, typically when the API key is not valid. 27 | """ 28 | 29 | pass 30 | 31 | 32 | class LinkupInsufficientCreditError(Exception): 33 | """Insufficient credit error, raised when the Linkup API returns a 429 status code. 34 | 35 | It is returned when you have run out of credits. 36 | """ 37 | 38 | pass 39 | 40 | 41 | class LinkupTooManyRequestsError(Exception): 42 | """Too many requests error, raised when the Linkup API returns a 429 status code. 43 | 44 | It is returned when you are sending too many requests at a time. 45 | """ 46 | 47 | pass 48 | 49 | 50 | class LinkupFailedFetchError(Exception): 51 | """Failed fetch error, raised when the Linkup API search returns a 400 status code. 52 | 53 | It is returned when the Linkup API failed to fetch the content of an URL due to technical 54 | reasons. 55 | """ 56 | 57 | pass 58 | 59 | 60 | class LinkupUnknownError(Exception): 61 | """Unknown error, raised when the Linkup API returns an unknown status code.""" 62 | 63 | pass 64 | -------------------------------------------------------------------------------- /src/linkup/_types.py: -------------------------------------------------------------------------------- 1 | """Input and output types for Linkup functions.""" 2 | 3 | # ruff: noqa: FA100 (pydantic models don't play well with future annotations) 4 | 5 | from typing import Any, Literal, Optional, Union 6 | 7 | from pydantic import BaseModel, ConfigDict, Field 8 | 9 | 10 | class LinkupSearchTextResult(BaseModel): 11 | """A text result from a Linkup search. 12 | 13 | Attributes: 14 | type: The type of the search result, in this case "text". 15 | name: The name of the search result. 16 | url: The URL of the search result. 17 | content: The text of the search result. 18 | """ 19 | 20 | type: Literal["text"] 21 | name: str 22 | url: str 23 | content: str 24 | 25 | 26 | class LinkupSearchImageResult(BaseModel): 27 | """An image result from a Linkup search. 28 | 29 | Attributes: 30 | type: The type of the search result, in this case "image". 31 | name: The name of the image result. 32 | url: The URL of the image result. 33 | """ 34 | 35 | type: Literal["image"] 36 | name: str 37 | url: str 38 | 39 | 40 | class LinkupSearchResults(BaseModel): 41 | """The results of the Linkup search. 42 | 43 | Attributes: 44 | results: The results of the Linkup search. 45 | """ 46 | 47 | results: list[Union[LinkupSearchTextResult, LinkupSearchImageResult]] 48 | 49 | 50 | class LinkupSource(BaseModel): 51 | """A source supporting a Linkup answer. 52 | 53 | Attributes: 54 | name: The name of the source. 55 | url: The URL of the source. 56 | snippet: The text excerpt supporting the Linkup answer. Can be empty for image sources. 57 | """ 58 | 59 | name: str 60 | url: str 61 | snippet: str = "" 62 | 63 | 64 | class LinkupSourcedAnswer(BaseModel): 65 | """A Linkup answer, with the sources supporting it. 66 | 67 | Attributes: 68 | answer: The answer text. 69 | sources: The sources supporting the answer. 70 | """ 71 | 72 | answer: str 73 | sources: list[LinkupSource] 74 | 75 | 76 | class LinkupSearchStructuredResponse(BaseModel): 77 | """A Linkup `search` structured response, with the sources supporting it. 78 | 79 | Attributes: 80 | data: The answer data, either as a Pydantic model or an arbitrary JSON structure. 81 | sources: The sources supporting the answer. 82 | """ 83 | 84 | data: Any 85 | sources: list[Union[LinkupSearchTextResult, LinkupSearchImageResult]] 86 | 87 | 88 | class LinkupFetchImageExtraction(BaseModel): 89 | """An image extraction from a Linkup web page fetch. 90 | 91 | Attributes: 92 | alt: The alt text of the image. 93 | url: The URL of the image. 94 | """ 95 | 96 | alt: str 97 | url: str 98 | 99 | 100 | class LinkupFetchResponse(BaseModel): 101 | """The response from a Linkup web page fetch. 102 | 103 | Attributes: 104 | markdown: The cleaned up markdown content. 105 | raw_html: The optional raw HTML content. 106 | images: The optional list of image URLs. 107 | """ 108 | 109 | model_config = ConfigDict(populate_by_name=True) 110 | 111 | markdown: str 112 | raw_html: Optional[str] = Field(default=None, validation_alias="rawHtml") 113 | images: Optional[list[LinkupFetchImageExtraction]] = Field(default=None) 114 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # See source with more comments and detailed explanations at: 2 | # https://python-semantic-release.readthedocs.io/en/latest/configuration/automatic-releases/github-actions.html#gh-actions-examples 3 | 4 | name: Release 5 | 6 | on: 7 | push: 8 | branches: 9 | - main 10 | 11 | permissions: 12 | contents: read 13 | 14 | jobs: 15 | release: 16 | runs-on: ubuntu-latest 17 | outputs: 18 | released: ${{ steps.release.outputs.released }} 19 | concurrency: 20 | group: ${{ github.workflow }}-release-${{ github.ref_name }} 21 | cancel-in-progress: false 22 | 23 | permissions: 24 | contents: write 25 | 26 | steps: 27 | - name: Setup | Checkout Repository on Release Branch 28 | uses: actions/checkout@v4 29 | with: 30 | ref: ${{ github.ref_name }} 31 | fetch-depth: 0 32 | token: ${{ secrets.GH_TOKEN_FOR_SEMANTIC_RELEASE }} 33 | 34 | - name: Setup | Force release branch to be at workflow sha 35 | run: | 36 | git reset --hard ${{ github.sha }} 37 | 38 | - name: Evaluate | Verify upstream has NOT changed 39 | shell: bash 40 | run: | 41 | set +o pipefail 42 | 43 | UPSTREAM_BRANCH_NAME="$(git status -sb | head -n 1 | awk -F '\\.\\.\\.' '{print $2}' | cut -d ' ' -f1)" 44 | printf '%s\n' "Upstream branch name: $UPSTREAM_BRANCH_NAME" 45 | 46 | set -o pipefail 47 | 48 | if [ -z "$UPSTREAM_BRANCH_NAME" ]; then 49 | printf >&2 '%s\n' "::error::Unable to determine upstream branch name!" 50 | exit 1 51 | fi 52 | 53 | git fetch "${UPSTREAM_BRANCH_NAME%%/*}" 54 | 55 | if ! UPSTREAM_SHA="$(git rev-parse "$UPSTREAM_BRANCH_NAME")"; then 56 | printf >&2 '%s\n' "::error::Unable to determine upstream branch sha!" 57 | exit 1 58 | fi 59 | 60 | HEAD_SHA="$(git rev-parse HEAD)" 61 | 62 | if [ "$HEAD_SHA" != "$UPSTREAM_SHA" ]; then 63 | printf >&2 '%s\n' "[HEAD SHA] $HEAD_SHA != $UPSTREAM_SHA [UPSTREAM SHA]" 64 | printf >&2 '%s\n' "::error::Upstream has changed, aborting release..." 65 | exit 1 66 | fi 67 | 68 | printf '%s\n' "Verified upstream branch has not changed, continuing with release..." 69 | 70 | - name: Action | Semantic Version Release 71 | id: release 72 | uses: python-semantic-release/python-semantic-release@v10.4.1 73 | with: 74 | github_token: ${{ secrets.GH_TOKEN_FOR_SEMANTIC_RELEASE }} 75 | git_committer_name: "github-actions" 76 | git_committer_email: "actions@users.noreply.github.com" 77 | 78 | - name: Publish | Upload to GitHub Release Assets 79 | uses: python-semantic-release/publish-action@v10.4.1 80 | if: steps.release.outputs.released == 'true' 81 | with: 82 | github_token: ${{ secrets.GH_TOKEN_FOR_SEMANTIC_RELEASE }} 83 | tag: ${{ steps.release.outputs.tag }} 84 | 85 | - name: Upload | Distribution Artifacts 86 | uses: actions/upload-artifact@v4 87 | if: steps.release.outputs.released == 'true' 88 | with: 89 | name: distribution-artifacts 90 | path: dist 91 | if-no-files-found: error 92 | 93 | deploy: 94 | runs-on: ubuntu-latest 95 | needs: release 96 | if: ${{ needs.release.outputs.released == 'true' }} 97 | 98 | permissions: 99 | contents: read 100 | id-token: write 101 | 102 | environment: 103 | name: pypi 104 | url: https://pypi.org/p/linkup-sdk 105 | 106 | steps: 107 | - name: Setup | Download Build Artifacts 108 | uses: actions/download-artifact@v4 109 | id: artifact-download 110 | with: 111 | name: distribution-artifacts 112 | path: dist 113 | 114 | - name: Publish package distributions to PyPI 115 | uses: pypa/gh-action-pypi-publish@release/v1 116 | with: 117 | packages-dir: dist 118 | print-hash: true 119 | verbose: true 120 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | authors = [{ email = "contact@linkup.so", name = "LINKUP TECHNOLOGIES" }] 3 | description = "A Python Client SDK for the Linkup API" 4 | keywords = ["api", "client", "linkup", "sdk", "search"] 5 | license = "MIT" 6 | name = "linkup-sdk" 7 | readme = "README.md" 8 | requires-python = ">=3.9" 9 | version = "0.9.0" 10 | 11 | classifiers = [ 12 | "Intended Audience :: Developers", 13 | "License :: OSI Approved :: MIT License", 14 | "Operating System :: OS Independent", 15 | "Topic :: Software Development :: Libraries :: Python Modules", 16 | ] 17 | 18 | dependencies = ["httpx>=0.23.0", "pydantic>=2.0.0"] 19 | 20 | [project.optional-dependencies] 21 | build = ["uv>=0.8.0,<0.9.0"] # For python-semantic-release build command, used in GitHub actions 22 | 23 | [project.urls] 24 | Documentation = "https://github.com/LinkupPlatform/linkup-python-sdk#readme" 25 | Homepage = "https://github.com/LinkupPlatform/linkup-python-sdk" 26 | Source = "https://github.com/LinkupPlatform/linkup-python-sdk" 27 | Tracker = "https://github.com/LinkupPlatform/linkup-python-sdk/issues" 28 | 29 | [dependency-groups] 30 | dev = [ 31 | "mypy>=1.16.1", 32 | "pre-commit>=4.2.0", 33 | "pytest-asyncio>=1.0.0", 34 | "pytest-cov>=6.2.1", 35 | "pytest-mock>=3.14.1", 36 | "pytest>=8.4.1", 37 | "python-dotenv>=1.1.1", 38 | "rich>=14.1.0", 39 | ] 40 | 41 | [tool.mypy] 42 | strict = true 43 | warn_unreachable = true 44 | 45 | [tool.pytest.ini_options] 46 | asyncio_default_fixture_loop_scope = "function" 47 | 48 | [tool.coverage.report] 49 | exclude_also = ["raise TypeError", "raise ValueError"] 50 | show_missing = true 51 | skip_covered = true 52 | 53 | [tool.ruff] 54 | line-length = 100 55 | target-version = "py39" 56 | 57 | [tool.ruff.lint] 58 | extend-ignore = ["D107"] 59 | pydocstyle = { convention = "google" } 60 | select = [ 61 | "A", # flake8-builtins: avoid shadowing built-in names 62 | "ANN", # flake8-annotations: check for missing type annotations 63 | "ASYNC", # flake8-async: enforce best practices for async code 64 | "B", # flake8-bugbear: find likely bugs and design problems in your program 65 | "C4", # flake8-comprehensions: enforce best practices for list/set/dict comprehensions 66 | "D", # pydocstyle: check compliance with docstring conventions 67 | "E", # pycodestyle errors: check for PEP 8 style convention errors 68 | "F", # pyflakes: check for Python source file errors 69 | "FA", # flake8-future-annotations: enforce usage of future annotations when relevant 70 | "I", # isort: enforce import sorting 71 | "ICN", # flake8-import-conventions: enforce general import conventions 72 | "ISC", # flake8-implicit-str-concat: check for invalid implicit or explicit string concatenation 73 | "N", # pep8-naming: check for naming convention violations 74 | "PERF", # perflint: check for performance anti-patterns 75 | "PT", # flake8-pytest-style: check common style issues and inconsistencies in pytest-based tests 76 | "PTH", # flake8-use-pathlib: enforce usage of pathlib for path manipulations instead of os.path 77 | "Q", # flake8-quotes: enforce consistent string quote usage 78 | "RET", # flake8-return: enforce best practices for return statements 79 | "RSE", # flake8-raise: enforce best practices for raise statements 80 | "RUF", # ruff: enforce ruff specific rules 81 | "S", # flake8-bandit: check for security issues 82 | "SLF", # flake8-self: prevent accessing private class member 83 | "TC", # flake8-type-checking: enforce best practices for type checking imports 84 | "TD", # flake8-todos: check issues on TODO comment syntax 85 | "UP", # pyupgrade: upgrade syntax for newer versions of Python 86 | "W", # pycodestyle warnings: check for PEP 8 style convention warnings 87 | ] 88 | 89 | [tool.ruff.lint.extend-per-file-ignores] 90 | "examples/*.py" = ["D"] 91 | "src/linkup/__init__.py" = ["D104"] 92 | "tests/**/*test.py" = ["D", "S101"] 93 | 94 | [build-system] 95 | build-backend = "hatchling.build" 96 | requires = ["hatchling"] 97 | 98 | [tool.hatch.build.targets.wheel] 99 | packages = ["src/linkup"] # Because project and source code directory names differ 100 | 101 | [tool.semantic_release] 102 | allow_zero_version = true 103 | build_command = """ 104 | python -m pip install -e '.[build]' 105 | uv lock --upgrade-package "$PACKAGE_NAME" 106 | git add uv.lock 107 | uv build 108 | """ 109 | commit_message = "chore: release v{version}\n\nAutomatically generated by python-semantic-release\n\n[skip ci]" # [skip ci] is needed to recursively calling the release CI 110 | major_on_zero = false 111 | version_toml = ["pyproject.toml:project.version"] 112 | 113 | [tool.semantic_release.commit_parser_options] 114 | parse_squash_commits = false 115 | 116 | [tool.uv] 117 | required-version = ">=0.8.0,<0.9.0" 118 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🚀 Linkup Python SDK 2 | 3 | [![PyPI version](https://badge.fury.io/py/linkup-sdk.svg)](https://pypi.org/project/linkup-sdk/) 4 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) 5 | ![PyPI - Downloads](https://img.shields.io/pypi/dm/linkup-sdk) 6 | [![Discord](https://img.shields.io/discord/1303713168916348959?color=7289da&logo=discord&logoColor=white)](https://discord.gg/9q9mCYJa86) 7 | 8 | A Python SDK for the [Linkup API](https://www.linkup.so/), allowing easy integration with Linkup's 9 | services in any Python application. 🐍 10 | 11 | Checkout the official 12 | [API documentation](https://docs.linkup.so/pages/documentation/get-started/introduction) and 13 | [SDK documentation](https://docs.linkup.so/pages/sdk/python/python) for additional details on how to 14 | benefit from Linkup services to the full extent. 📝 15 | 16 | ## 🌟 Features 17 | 18 | - ✅ **Simple and intuitive API client.** 19 | - 🔍 **Support all Linkup entrypoints and parameters.** 20 | - ⚡ **Support synchronous and asynchronous calls.** 21 | - 🔒 **Handle authentication and request management.** 22 | 23 | ## 📦 Installation 24 | 25 | Simply install the Linkup Python SDK as any Python package, for instance using `pip`: 26 | 27 | ```bash 28 | pip install linkup-sdk 29 | ``` 30 | 31 | ## 🛠️ Usage 32 | 33 | ### Setting Up Your Environment 34 | 35 | 1. **🔑 Obtain an API Key:** 36 | 37 | Sign up on Linkup to get your API key. 38 | 39 | 2. **⚙️ Set-up the API Key:** 40 | 41 | Option 1: Export the `LINKUP_API_KEY` environment variable in your shell before using the Python 42 | SDK. 43 | 44 | ```bash 45 | export LINKUP_API_KEY= 46 | ``` 47 | 48 | Option 2: Set the `LINKUP_API_KEY` environment variable directly within Python, using for 49 | instance `os.environ` or [python-dotenv](https://github.com/theskumar/python-dotenv) with a 50 | `.env` file (`python-dotenv` needs to be installed separately in this case), before creating the 51 | Linkup Client. 52 | 53 | ```python 54 | import os 55 | from linkup import LinkupClient 56 | 57 | os.environ["LINKUP_API_KEY"] = "" 58 | # or dotenv.load_dotenv() 59 | client = LinkupClient() 60 | ... 61 | ``` 62 | 63 | Option 3: Pass the Linkup API key to the Linkup Client when creating it. 64 | 65 | ```python 66 | from linkup import LinkupClient 67 | 68 | client = LinkupClient(api_key="") 69 | ... 70 | ``` 71 | 72 | ### 📋 Examples 73 | 74 | #### 📝 Search 75 | 76 | The `search` function can be used to performs web searches. It supports two very different 77 | complexity modes: 78 | 79 | - with `depth="standard"`, the search will be straightforward and fast, suited for relatively simple 80 | queries (e.g. "What's the weather in Paris today?") 81 | - with `depth="deep"`, the search will use an agentic workflow, which makes it in general slower, 82 | but it will be able to solve more complex queries (e.g. "What is the company profile of LangChain 83 | accross the last few years, and how does it compare to its concurrents?") 84 | 85 | The `search` function also supports three output types: 86 | 87 | - with `output_type="searchResults"`, the search will return a list of relevant documents 88 | - with `output_type="sourcedAnswer"`, the search will return a concise answer with sources 89 | - with `output_type="structured"`, the search will return a structured output according to a 90 | user-defined schema 91 | 92 | ```python 93 | from typing import Any 94 | 95 | from linkup import LinkupClient, LinkupSourcedAnswer 96 | 97 | client = LinkupClient() # API key can be read from the environment variable or passed as an argument 98 | search_response: Any = client.search( 99 | query="What are the 3 major events in the life of Abraham Lincoln?", 100 | depth="deep", # "standard" or "deep" 101 | output_type="sourcedAnswer", # "searchResults" or "sourcedAnswer" or "structured" 102 | structured_output_schema=None, # must be filled if output_type is "structured" 103 | ) 104 | assert isinstance(search_response, LinkupSourcedAnswer) 105 | print(search_response.model_dump()) 106 | ``` 107 | 108 | Which prints: 109 | 110 | ```bash 111 | { 112 | answer="The three major events in the life of Abraham Lincoln are: 1. ...", 113 | sources=[ 114 | { 115 | "name": "HISTORY", 116 | "url": "https://www.history.com/topics/us-presidents/abraham-lincoln", 117 | "snippet": "Abraham Lincoln - Facts & Summary - HISTORY ..." 118 | }, 119 | ... 120 | ] 121 | } 122 | ``` 123 | 124 | Check the code or the 125 | [official documentation](https://docs.linkup.so/pages/documentation/api-reference/endpoint/post-search) 126 | for the detailed list of available parameters. 127 | 128 | #### 🪝 Fetch 129 | 130 | The `fetch` function can be used to retrieve the content of a given web page in a cleaned up 131 | markdown format. 132 | 133 | You can use the `render_js` flag to execute the JavaScript code of the page before returning the 134 | content, and ask to `include_raw_html` to the response if you feel like it. 135 | 136 | ```python 137 | from linkup import LinkupClient, LinkupFetchResponse 138 | 139 | client = LinkupClient() # API key can be read from the environment variable or passed as an argument 140 | fetch_response: LinkupFetchResponse = client.fetch( 141 | url="https://docs.linkup.so", 142 | render_js=False, 143 | include_raw_html=True, 144 | ) 145 | print(fetch_response.model_dump()) 146 | ``` 147 | 148 | Which prints: 149 | 150 | ```bash 151 | { 152 | markdown="Get started for free, no credit card required...", 153 | raw_html="......" 154 | } 155 | ``` 156 | 157 | Check the code or the 158 | [official documentation](https://docs.linkup.so/pages/documentation/api-reference/endpoint/post-fetch) 159 | for the detailed list of available parameters. 160 | 161 | #### ⌛ Asynchronous Calls 162 | 163 | All the Linkup main functions come with an asynchronous counterpart, with the same behavior and the 164 | same name prefixed by `async_` (e.g. `async_search` for `search`). This should be favored in 165 | production use cases to avoid blocking the main thread while waiting for the Linkup API to respond. 166 | This makes possible to call the Linkup API several times concurrently for instance. 167 | 168 | ```python 169 | import asyncio 170 | from typing import Any 171 | 172 | from linkup import LinkupClient, LinkupSourcedAnswer 173 | 174 | async def main() -> None: 175 | client = LinkupClient() # API key can be read from the environment variable or passed as an argument 176 | search_response: Any = await client.async_search( 177 | query="What are the 3 major events in the life of Abraham Lincoln?", 178 | depth="deep", # "standard" or "deep" 179 | output_type="sourcedAnswer", # "searchResults" or "sourcedAnswer" or "structured" 180 | structured_output_schema=None, # must be filled if output_type is "structured" 181 | ) 182 | assert isinstance(search_response, LinkupSourcedAnswer) 183 | print(search_response.model_dump()) 184 | 185 | asyncio.run(main()) 186 | ``` 187 | 188 | Which prints: 189 | 190 | ```bash 191 | { 192 | answer="The three major events in the life of Abraham Lincoln are: 1. ...", 193 | sources=[ 194 | { 195 | "name": "HISTORY", 196 | "url": "https://www.history.com/topics/us-presidents/abraham-lincoln", 197 | "snippet": "Abraham Lincoln - Facts & Summary - HISTORY ..." 198 | }, 199 | ... 200 | ] 201 | } 202 | ``` 203 | 204 | #### 📚 More Examples 205 | 206 | See the `examples/` directory for more examples and documentation, for instance on how to use Linkup 207 | entrypoints using asynchronous functions to call the Linkup API several times concurrenly. 208 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | 4 | 5 | ## v0.9.0 (2025-11-14) 6 | 7 | ### Chores 8 | 9 | - Complete test coverage 10 | ([`c44b3d0`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/c44b3d064a72a70b25eea9d9c4c832cce6b10431)) 11 | 12 | - Enable all lint rules 13 | ([`1ac1768`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/1ac17681b220c6105878f0e36dd1139695d4017a)) 14 | 15 | ### Features 16 | 17 | - Max_results parameter 18 | ([`be65c19`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/be65c19a02a8e69f6d984eb48e820ec19e739931)) 19 | 20 | 21 | ## v0.8.0 (2025-10-31) 22 | 23 | ### Chores 24 | 25 | - Enforce new ruff lint rules 26 | ([`e851c63`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/e851c63bd3df1920e55b42c0152059e54c40a266)) 27 | 28 | - Make internal modules private 29 | ([`b0fdff5`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/b0fdff59c5547b892b17cb0a49f4ae1a4d9f73ce)) 30 | 31 | - Use simple _ for private variables 32 | ([`cdcba7f`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/cdcba7f69f29b85f2d6ca7b1478754c714328563)) 33 | 34 | ### Documentation 35 | 36 | - Add pycodestyle checks 37 | ([`562883b`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/562883bf4ca1ba3f0029154497138d5de90192dc)) 38 | 39 | - Improve README examples readability 40 | ([`d00e631`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/d00e6311bcb583a5410e0157f665fe8be5be7bca)) 41 | 42 | - Mention async functions in README 43 | ([`f7fff79`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/f7fff790c78b6cffa73c25755d51745e699653d1)) 44 | 45 | ### Features 46 | 47 | - Accept API key as pydantic.SecretStr 48 | ([`1c34bf3`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/1c34bf34776e59117cf4b849c1c8445d988f7c96)) 49 | 50 | 51 | ## v0.7.0 (2025-10-28) 52 | 53 | ### Features 54 | 55 | - **fetch**: Add extract_images parameter 56 | ([`4204d7e`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/4204d7ea49f7ab0e1e44ac5b8f32c9964ff3935d)) 57 | 58 | 59 | ## v0.6.0 (2025-09-22) 60 | 61 | ### Documentation 62 | 63 | - Add links to the documentation 64 | ([`73673bb`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/73673bb1ad84e44ac98a683a1d3eedb9425244f4)) 65 | 66 | ### Features 67 | 68 | - Include_sources parameter 69 | ([`64fd4c5`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/64fd4c58508f447f6cbdda80e76d43a065c11df5)) 70 | 71 | 72 | ## v0.5.0 (2025-09-18) 73 | 74 | ### Features 75 | 76 | - Include_inline_citations search parameter 77 | ([`bd1e7fc`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/bd1e7fc0f91a832bb62813f41474e5948715249d)) 78 | 79 | 80 | ## v0.4.0 (2025-09-18) 81 | 82 | ### Bug Fixes 83 | 84 | - Specify minimal version of main requirements 85 | ([`9aaf6ae`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/9aaf6ae1319e47fc70b05091967d661a11820b73)) 86 | 87 | - Use API parameters order and defaults 88 | ([`5958298`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/595829815bfe54c3f71f75484dce56e03eb99e64)) 89 | 90 | 91 | ## v0.3.0 (2025-09-17) 92 | 93 | ### Features 94 | 95 | - Fetch endpoint 96 | ([`efdebdb`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/efdebdbda787542e7b38e5058e455c7d694db5df)) 97 | 98 | 99 | ## v0.2.11 (2025-09-17) 100 | 101 | ### Bug Fixes 102 | 103 | - From_ and to_date default values 104 | ([`575e7c4`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/575e7c43bd6b255853139da1e81ebfaca6285ce4)) 105 | 106 | ### Chores 107 | 108 | - Add load_dotenv in example scripts 109 | ([`e6babc9`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/e6babc998422d4544f7e53373435aa5bcb44f5f0)) 110 | 111 | - Pass all parameters in search payload 112 | ([`2319388`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/23193883dba53b049f565f1ca772c9e508aa6746)) 113 | 114 | ### Refactoring 115 | 116 | - Streamline tests and add more 117 | ([`2d58d85`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/2d58d85881b74098a51cad27b93dbb4228cfc441)) 118 | 119 | 120 | ## v0.2.10 (2025-09-16) 121 | 122 | ### Bug Fixes 123 | 124 | - Small docstring wording 125 | ([`6330633`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/63306336551a5f381f0c2a6fa726f87046eafe1a)) 126 | 127 | - Types and documentation improvements 128 | ([`ecdb6a9`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/ecdb6a9639c8be58fea454c42dab9312dc015fe5)) 129 | 130 | ### Chores 131 | 132 | - Format and lint 133 | ([`a4d10d7`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/a4d10d739f24d12190b7e5ba6954aaa252507a5d)) 134 | 135 | - Standardize tools and configs 136 | ([`8c9224d`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/8c9224da3409884126f6d44135f8b4976f4b741e)) 137 | 138 | - Update PR template 139 | ([`8567d24`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/8567d247dbb9a5987bfc036fe8358b76d3dfa22f)) 140 | 141 | - Use lowest supported Python version 142 | ([`fe3a7fe`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/fe3a7fed6c5c6f5fd1bc164774ff4dc65f5f5b5e)) 143 | 144 | - Use rich.print in examples 145 | ([`94d5d42`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/94d5d4211f852214a7c3f67d69875f9cb057ff26)) 146 | 147 | 148 | ## v0.2.9 (2025-09-15) 149 | 150 | ### Bug Fixes 151 | 152 | - Fetch all commit history for semantic-release 153 | ([`d811306`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/d81130693ae04d6b2c13908811adeb0783a8ae65)) 154 | 155 | - Install uv in python-semantic-release action 156 | ([`b7af0eb`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/b7af0eb341ab091408d5b8aace0f630a80683006)) 157 | 158 | - Keep version in 0.x.x 159 | ([`0bbcfbd`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/0bbcfbdce4e9f4833812a8d39c77f7b23d05ac51)) 160 | 161 | - Outdated package version 162 | ([`20ae5d0`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/20ae5d0201614c908d6e2f152637b3760059ea8c)) 163 | 164 | - Update and simplify github actions 165 | ([`8e48be3`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/8e48be3b29d47fe9363f5e03f83ffc99671af8a7)) 166 | 167 | - Use semantic release GH actions 168 | ([`069f7e0`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/069f7e039ba3e4723111ba88aae9ecaa3e4eaa75)) 169 | 170 | ### Chores 171 | 172 | - Add semantic release ([#34](https://github.com/LinkupPlatform/linkup-python-sdk/pull/34), 173 | [`a666011`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/a6660113c41e72af918c134e2f2ba304c874a169)) 174 | 175 | - Add uv for python sdk ([#33](https://github.com/LinkupPlatform/linkup-python-sdk/pull/33), 176 | [`2fb5984`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/2fb5984af59244f314840e4a5f0888c1921cea2c)) 177 | 178 | - Don't parse squash commits in semantic release 179 | ([`586321c`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/586321c8f79eeb08793753e5aac87f4e754ab1b0)) 180 | 181 | - Revert version to pre-semantic-release 182 | ([`89acb72`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/89acb729c97230179bab715eac6a4a7cc975652d)) 183 | 184 | - Specify environment in release job 185 | ([`223b02d`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/223b02de596c711d1fe9fed96cb239f47fc255fc)) 186 | 187 | - Update pyproject.toml config 188 | ([`c573df7`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/c573df7f600c5f8d28346ded7043047fc65d656c)) 189 | 190 | ### Continuous Integration 191 | 192 | - Automatically bump pyproject.toml version 193 | ([`ee13a6a`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/ee13a6a36f5fbbb508d1a880ed2c377b8ad38a81)) 194 | 195 | - Change secret variable name 196 | ([`7e750d1`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/7e750d11a6d34d4971ba16190b5cef02fadd498c)) 197 | 198 | - Expose outputs in release job 199 | ([`0af76cb`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/0af76cb83614e88f796ca45ca614ba1d111e3c0e)) 200 | 201 | - Skip remaining release jobs if no release is made 202 | ([`16df390`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/16df390ed42e39e5ba8b4c96bda376baa6cf5fad)) 203 | 204 | 205 | ## v0.2.8 (2025-07-01) 206 | 207 | ### Bug Fixes 208 | 209 | - Send search params as json instead of form-encoded 210 | ([#32](https://github.com/LinkupPlatform/linkup-python-sdk/pull/32), 211 | [`c1d1f9f`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/c1d1f9fb5b384c43db03f783dfc4fe146d314844)) 212 | 213 | 214 | ## v0.2.7 (2025-06-23) 215 | 216 | 217 | ## v0.2.6 (2025-05-30) 218 | 219 | ### Documentation 220 | 221 | - Fix URL in README 222 | ([`706e59b`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/706e59bb47e375bc435ed29a036b173f11a51925)) 223 | 224 | 225 | ## v0.2.5 (2025-05-09) 226 | 227 | ### Bug Fixes 228 | 229 | - From_date/to_date in async search 230 | ([`e35c84b`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/e35c84bc2298a1450da3580068b385670dc14ab1)) 231 | 232 | ### Chores 233 | 234 | - Bump version 235 | ([`10f3bcf`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/10f3bcff79f8a411ffa90024f192c98037d24c05)) 236 | 237 | 238 | ## v0.2.4 (2025-03-19) 239 | 240 | 241 | ## v0.2.3 (2025-02-06) 242 | 243 | ### Features 244 | 245 | - Implement new api errors and use the post endpoint 246 | ([`672bcea`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/672bceae398a8736417535be2b38c838ca7bd6bd)) 247 | 248 | - Upgrade version 249 | ([`40391df`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/40391df9c4414f2264f0fc3f1d9ceb888b60141f)) 250 | 251 | 252 | ## v0.2.2 (2025-01-09) 253 | 254 | ### Bug Fixes 255 | 256 | - Bad wording for the world Authentication 257 | ([`8ec5f19`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/8ec5f1990b0b24dbf3bed0d2d95f44d406594bc0)) 258 | 259 | 260 | ## v0.2.1 (2024-12-10) 261 | 262 | 263 | ## v0.2.0 (2024-12-03) 264 | 265 | ### Bug Fixes 266 | 267 | - Don't handle missing structured schema error 268 | ([`2f41452`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/2f414525135b6a7a002e5913ecd8ba9ec5544540)) 269 | 270 | - SDK version in client 271 | ([`687730a`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/687730ae6c849818f19221cd4df01524cbc52dd8)) 272 | 273 | ### Chores 274 | 275 | - Add gitleaks in pre-commit and update hook versions 276 | ([`916e461`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/916e4614e8bbb929b1b14505cbfacac78b7d0964)) 277 | 278 | - Drop support for Python 3.8 and test in Python 3.13 279 | ([`5343cbd`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/5343cbd15f60d5da2df2860b7049e4894cbda2b4)) 280 | 281 | - Read the package version from linkup/_version.py 282 | ([`b52ac83`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/b52ac8341b14148fbead49d33671a6487abd05cd)) 283 | 284 | - Remove content entrypoint 285 | ([`f52b0e6`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/f52b0e6d4167ea2cefb1edee1ef8bab068fb25ab)) 286 | 287 | - Update package version to 0.2.0 288 | ([`dcb7586`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/dcb7586a57a003fa0db1309ab212481791fc52f9)) 289 | 290 | ### Documentation 291 | 292 | - Add Client docstring 293 | ([`58abc7c`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/58abc7c8ebefbff2d123e7b966dbf23baee4f895)) 294 | 295 | - Add PR template 296 | ([`9860498`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/9860498b820b67bae54fc8c1b7c05c87906c6fd0)) 297 | 298 | - Move example README section in usage 299 | ([`d2f4c0f`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/d2f4c0fb13e6c02e381cf81c90d643e4430d5cae)) 300 | 301 | 302 | ## v0.1.8 (2024-11-21) 303 | 304 | ### Bug Fixes 305 | 306 | - Remove some default values, improve docstrings and error types 307 | ([`dbcb31a`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/dbcb31abea725763d9633f7decc4677281175621)) 308 | 309 | ### Chores 310 | 311 | - Upgrade package to version 0.1.8 312 | ([`ae15b5f`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/ae15b5fdf66e1ff3e79fb173ac97d8629f5da207)) 313 | 314 | ### Continuous Integration 315 | 316 | - Only test min/max python versions 317 | ([`f04a578`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/f04a5789627a6f54109d8beb444efc86724c355b)) 318 | 319 | ### Documentation 320 | 321 | - Update documentation 322 | ([`b661d1a`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/b661d1a290957e10b9291e8ad75f6aa6678f15cf)) 323 | 324 | ### Testing 325 | 326 | - Improve test setup 327 | ([`d2bb998`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/d2bb9981425484cf88057a78c59256158d205c4e)) 328 | 329 | 330 | ## v0.1.7 (2024-11-20) 331 | 332 | ### Bug Fixes 333 | 334 | - Add missing setuptools dev dependency 335 | ([`bda6803`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/bda6803ee9c3852857d9bb9c66d3af42646b5979)) 336 | 337 | - Add py.typed marker 338 | ([`42d1e7d`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/42d1e7dbce92d3fff5dc7752088c82ef82492a53)) 339 | 340 | ### Chores 341 | 342 | - Remove requirements.txt 343 | ([`ca8510e`](https://github.com/LinkupPlatform/linkup-python-sdk/commit/ca8510ec566981884468b96657d720e178a857d8)) 344 | 345 | 346 | ## v0.1.6 (2024-11-20) 347 | 348 | 349 | ## v0.1.5 (2024-11-15) 350 | 351 | 352 | ## v0.1.4 (2024-11-15) 353 | 354 | 355 | ## v0.1.3 (2024-11-15) 356 | 357 | 358 | ## v0.1.2 (2024-11-14) 359 | 360 | - Initial Release 361 | -------------------------------------------------------------------------------- /tests/unit/client_test.py: -------------------------------------------------------------------------------- 1 | import json 2 | from datetime import date 3 | from typing import Any 4 | 5 | import pytest 6 | from httpx import Response 7 | from pydantic import BaseModel 8 | from pytest_mock import MockerFixture 9 | 10 | from linkup import ( 11 | LinkupAuthenticationError, 12 | LinkupClient, 13 | LinkupFailedFetchError, 14 | LinkupFetchResponse, 15 | LinkupInsufficientCreditError, 16 | LinkupInvalidRequestError, 17 | LinkupNoResultError, 18 | LinkupSearchImageResult, 19 | LinkupSearchResults, 20 | LinkupSearchStructuredResponse, 21 | LinkupSearchTextResult, 22 | LinkupSource, 23 | LinkupSourcedAnswer, 24 | LinkupTooManyRequestsError, 25 | LinkupUnknownError, 26 | ) 27 | 28 | 29 | class Company(BaseModel): 30 | name: str 31 | creation_date: str 32 | website_url: str 33 | founders_names: list[str] 34 | 35 | 36 | test_search_parameters = [ 37 | ( 38 | {"query": "query", "depth": "standard", "output_type": "searchResults"}, 39 | {"q": "query", "depth": "standard", "outputType": "searchResults"}, 40 | b""" 41 | { 42 | "results": [ 43 | { 44 | "type": "text", 45 | "name": "foo", 46 | "url": "https://foo.com", 47 | "content": "lorem ipsum dolor sit amet" 48 | }, 49 | {"type": "image", "name": "bar", "url": "https://bar.com"} 50 | ] 51 | } 52 | """, 53 | LinkupSearchResults( 54 | results=[ 55 | LinkupSearchTextResult( 56 | type="text", 57 | name="foo", 58 | url="https://foo.com", 59 | content="lorem ipsum dolor sit amet", 60 | ), 61 | LinkupSearchImageResult( 62 | type="image", 63 | name="bar", 64 | url="https://bar.com", 65 | ), 66 | ] 67 | ), 68 | ), 69 | ( 70 | { 71 | "query": "A long query.", 72 | "depth": "deep", 73 | "output_type": "searchResults", 74 | "include_images": True, 75 | "from_date": date(2023, 1, 1), 76 | "to_date": date(2023, 12, 31), 77 | "exclude_domains": ["excluded.com"], 78 | "include_domains": ["example.com", "example.org"], 79 | "max_results": 10, 80 | "include_inline_citations": True, 81 | "include_sources": True, 82 | }, 83 | { 84 | "q": "A long query.", 85 | "depth": "deep", 86 | "outputType": "searchResults", 87 | "includeImages": True, 88 | "fromDate": "2023-01-01", 89 | "toDate": "2023-12-31", 90 | "excludeDomains": ["excluded.com"], 91 | "includeDomains": ["example.com", "example.org"], 92 | "maxResults": 10, 93 | "includeInlineCitations": True, 94 | "includeSources": True, 95 | }, 96 | b'{"results": []}', 97 | LinkupSearchResults(results=[]), 98 | ), 99 | ( 100 | {"query": "query", "depth": "standard", "output_type": "sourcedAnswer"}, 101 | {"q": "query", "depth": "standard", "outputType": "sourcedAnswer"}, 102 | b""" 103 | { 104 | "answer": "foo bar baz", 105 | "sources": [ 106 | {"name": "foo", "url": "https://foo.com", "snippet": "lorem ipsum dolor sit amet"}, 107 | {"name": "bar", "url": "https://bar.com", "snippet": "consectetur adipiscing elit"}, 108 | {"name": "baz", "url": "https://baz.com"} 109 | ] 110 | } 111 | """, 112 | LinkupSourcedAnswer( 113 | answer="foo bar baz", 114 | sources=[ 115 | LinkupSource( 116 | name="foo", 117 | url="https://foo.com", 118 | snippet="lorem ipsum dolor sit amet", 119 | ), 120 | LinkupSource( 121 | name="bar", 122 | url="https://bar.com", 123 | snippet="consectetur adipiscing elit", 124 | ), 125 | LinkupSource( 126 | name="baz", 127 | url="https://baz.com", 128 | snippet="", 129 | ), 130 | ], 131 | ), 132 | ), 133 | ( 134 | { 135 | "query": "query", 136 | "depth": "standard", 137 | "output_type": "structured", 138 | "structured_output_schema": Company, 139 | }, 140 | { 141 | "q": "query", 142 | "depth": "standard", 143 | "outputType": "structured", 144 | "structuredOutputSchema": json.dumps(Company.model_json_schema()), 145 | }, 146 | b""" 147 | { 148 | "name": "Linkup", 149 | "founders_names": ["Philippe Mizrahi", "Denis Charrier", "Boris Toledano"], 150 | "creation_date": "2024", 151 | "website_url": "https://www.linkup.so/" 152 | } 153 | """, 154 | Company( 155 | name="Linkup", 156 | founders_names=["Philippe Mizrahi", "Denis Charrier", "Boris Toledano"], 157 | creation_date="2024", 158 | website_url="https://www.linkup.so/", 159 | ), 160 | ), 161 | ( 162 | { 163 | "query": "query", 164 | "depth": "standard", 165 | "output_type": "structured", 166 | "structured_output_schema": json.dumps(Company.model_json_schema()), 167 | }, 168 | { 169 | "q": "query", 170 | "depth": "standard", 171 | "outputType": "structured", 172 | "structuredOutputSchema": json.dumps(Company.model_json_schema()), 173 | }, 174 | b""" 175 | { 176 | "name": "Linkup", 177 | "founders_names": ["Philippe Mizrahi", "Denis Charrier", "Boris Toledano"], 178 | "creation_date": "2024", 179 | "website_url": "https://www.linkup.so/" 180 | } 181 | """, 182 | { 183 | "name": "Linkup", 184 | "founders_names": ["Philippe Mizrahi", "Denis Charrier", "Boris Toledano"], 185 | "creation_date": "2024", 186 | "website_url": "https://www.linkup.so/", 187 | }, 188 | ), 189 | ( 190 | { 191 | "query": "query", 192 | "depth": "standard", 193 | "output_type": "structured", 194 | "structured_output_schema": Company, 195 | "include_sources": True, 196 | }, 197 | { 198 | "q": "query", 199 | "depth": "standard", 200 | "outputType": "structured", 201 | "structuredOutputSchema": json.dumps(Company.model_json_schema()), 202 | "includeSources": True, 203 | }, 204 | b""" 205 | { 206 | "data": { 207 | "name": "Linkup", 208 | "founders_names": ["Philippe Mizrahi", "Denis Charrier", "Boris Toledano"], 209 | "creation_date": "2024", 210 | "website_url": "https://www.linkup.so/" 211 | }, 212 | "sources": [ 213 | { 214 | "type": "text", 215 | "name": "foo", 216 | "url": "https://foo.com", 217 | "content": "lorem ipsum dolor sit amet" 218 | }, 219 | {"type": "image", "name": "bar", "url": "https://bar.com"} 220 | ] 221 | } 222 | """, 223 | LinkupSearchStructuredResponse( 224 | data=Company( 225 | name="Linkup", 226 | founders_names=["Philippe Mizrahi", "Denis Charrier", "Boris Toledano"], 227 | creation_date="2024", 228 | website_url="https://www.linkup.so/", 229 | ), 230 | sources=[ 231 | LinkupSearchTextResult( 232 | type="text", 233 | name="foo", 234 | url="https://foo.com", 235 | content="lorem ipsum dolor sit amet", 236 | ), 237 | LinkupSearchImageResult(type="image", name="bar", url="https://bar.com"), 238 | ], 239 | ), 240 | ), 241 | ] 242 | 243 | 244 | @pytest.mark.parametrize( 245 | ( 246 | "search_kwargs", 247 | "expected_request_params", 248 | "mock_request_response_content", 249 | "expected_search_response", 250 | ), 251 | test_search_parameters, 252 | ) 253 | def test_search( 254 | mocker: MockerFixture, 255 | client: LinkupClient, 256 | search_kwargs: dict[str, Any], 257 | expected_request_params: dict[str, Any], 258 | mock_request_response_content: bytes, 259 | expected_search_response: Any, # noqa: ANN401 260 | ) -> None: 261 | mocker.patch("linkup._client.date").today.return_value = date(2000, 1, 1) 262 | request_mock = mocker.patch( 263 | "linkup._client.LinkupClient._request", 264 | return_value=Response( 265 | status_code=200, 266 | content=mock_request_response_content, 267 | ), 268 | ) 269 | 270 | search_response: Any = client.search(**search_kwargs) 271 | request_mock.assert_called_once_with( 272 | method="POST", 273 | url="/search", 274 | json=expected_request_params, 275 | timeout=None, 276 | ) 277 | assert search_response == expected_search_response 278 | 279 | 280 | @pytest.mark.asyncio 281 | @pytest.mark.parametrize( 282 | ( 283 | "search_kwargs", 284 | "expected_request_params", 285 | "mock_request_response_content", 286 | "expected_search_response", 287 | ), 288 | test_search_parameters, 289 | ) 290 | async def test_async_search( 291 | mocker: MockerFixture, 292 | client: LinkupClient, 293 | search_kwargs: dict[str, Any], 294 | expected_request_params: dict[str, Any], 295 | mock_request_response_content: bytes, 296 | expected_search_response: Any, # noqa: ANN401 297 | ) -> None: 298 | mocker.patch("linkup._client.date").today.return_value = date(2000, 1, 1) 299 | request_mock = mocker.patch( 300 | "linkup._client.LinkupClient._async_request", 301 | return_value=Response( 302 | status_code=200, 303 | content=mock_request_response_content, 304 | ), 305 | ) 306 | 307 | search_response: Any = await client.async_search(**search_kwargs) 308 | request_mock.assert_called_once_with( 309 | method="POST", 310 | url="/search", 311 | json=expected_request_params, 312 | timeout=None, 313 | ) 314 | assert search_response == expected_search_response 315 | 316 | 317 | test_search_error_parameters = [ 318 | ( 319 | 403, 320 | b""" 321 | { 322 | "error": { 323 | "code": "FORBIDDEN", 324 | "message": "Forbidden action", 325 | "details": [] 326 | } 327 | } 328 | """, 329 | LinkupAuthenticationError, 330 | ), 331 | ( 332 | 401, 333 | b""" 334 | { 335 | "error": { 336 | "code": "UNAUTHORIZED", 337 | "message": "Unauthorized action", 338 | "details": [] 339 | } 340 | } 341 | """, 342 | LinkupAuthenticationError, 343 | ), 344 | ( 345 | 429, 346 | b""" 347 | { 348 | "error": { 349 | "code": "INSUFFICIENT_FUNDS_CREDITS", 350 | "message": "You do not have enough credits to perform this request.", 351 | "details": [] 352 | } 353 | } 354 | """, 355 | LinkupInsufficientCreditError, 356 | ), 357 | ( 358 | 429, 359 | b""" 360 | { 361 | "error": { 362 | "code": "TOO_MANY_REQUESTS", 363 | "message": "Too many requests.", 364 | "details": [] 365 | } 366 | } 367 | """, 368 | LinkupTooManyRequestsError, 369 | ), 370 | ( 371 | 429, 372 | b""" 373 | { 374 | "error": { 375 | "code": "FOOBAR", 376 | "message": "Foobar", 377 | "details": [] 378 | } 379 | } 380 | """, 381 | LinkupUnknownError, 382 | ), 383 | ( 384 | 400, 385 | b""" 386 | { 387 | "error": { 388 | "code": "VALIDATION_ERROR", 389 | "message": "Validation failed", 390 | "details": [ 391 | { 392 | "field": "structuredOutputSchema", 393 | "message": "structuredOutputSchema must be valid JSON schema of type" 394 | } 395 | ] 396 | } 397 | } 398 | """, 399 | LinkupInvalidRequestError, 400 | ), 401 | ( 402 | 400, 403 | b""" 404 | { 405 | "error": { 406 | "code": "SEARCH_QUERY_NO_RESULT", 407 | "message": "The query did not yield any result", 408 | "details": [] 409 | } 410 | } 411 | """, 412 | LinkupNoResultError, 413 | ), 414 | ( 415 | 500, 416 | b""" 417 | { 418 | "error": { 419 | "code": "INTERNAL_SERVER_ERROR", 420 | "message": "Internal server error", 421 | "details": [] 422 | } 423 | } 424 | """, 425 | LinkupUnknownError, 426 | ), 427 | ] 428 | 429 | 430 | @pytest.mark.parametrize( 431 | ("mock_request_response_status_code", "mock_request_response_content", "expected_exception"), 432 | test_search_error_parameters, 433 | ) 434 | def test_search_error( 435 | mocker: MockerFixture, 436 | client: LinkupClient, 437 | mock_request_response_status_code: int, 438 | mock_request_response_content: bytes, 439 | expected_exception: type[Exception], 440 | ) -> None: 441 | request_mock = mocker.patch( 442 | "linkup._client.LinkupClient._request", 443 | return_value=Response( 444 | status_code=mock_request_response_status_code, 445 | content=mock_request_response_content, 446 | ), 447 | ) 448 | 449 | with pytest.raises(expected_exception): 450 | client.search(query="query", depth="standard", output_type="searchResults") 451 | request_mock.assert_called_once() 452 | 453 | 454 | @pytest.mark.asyncio 455 | @pytest.mark.parametrize( 456 | ("mock_request_response_status_code", "mock_request_response_content", "expected_exception"), 457 | test_search_error_parameters, 458 | ) 459 | async def test_async_search_error( 460 | mocker: MockerFixture, 461 | client: LinkupClient, 462 | mock_request_response_status_code: int, 463 | mock_request_response_content: bytes, 464 | expected_exception: type[Exception], 465 | ) -> None: 466 | request_mock = mocker.patch( 467 | "linkup._client.LinkupClient._async_request", 468 | return_value=Response( 469 | status_code=mock_request_response_status_code, 470 | content=mock_request_response_content, 471 | ), 472 | ) 473 | 474 | with pytest.raises(expected_exception): 475 | await client.async_search(query="query", depth="standard", output_type="searchResults") 476 | request_mock.assert_called_once() 477 | 478 | 479 | test_fetch_parameters = [ 480 | ( 481 | {"url": "https://example.com"}, 482 | {"url": "https://example.com"}, 483 | b'{"markdown": "Some web page content"}', 484 | LinkupFetchResponse(markdown="Some web page content", raw_html=None), 485 | ), 486 | ( 487 | { 488 | "url": "https://example.com", 489 | "include_raw_html": True, 490 | "render_js": True, 491 | "extract_images": True, 492 | }, 493 | { 494 | "url": "https://example.com", 495 | "includeRawHtml": True, 496 | "renderJs": True, 497 | "extractImages": True, 498 | }, 499 | b'{"markdown": "#Some web page content", "rawHtml": "..."}', 500 | LinkupFetchResponse(markdown="#Some web page content", raw_html="..."), 501 | ), 502 | ] 503 | 504 | 505 | @pytest.mark.parametrize( 506 | ( 507 | "fetch_kwargs", 508 | "expected_request_params", 509 | "mock_request_response_content", 510 | "expected_fetch_response", 511 | ), 512 | test_fetch_parameters, 513 | ) 514 | def test_fetch( 515 | mocker: MockerFixture, 516 | client: LinkupClient, 517 | fetch_kwargs: dict[str, Any], 518 | expected_request_params: dict[str, Any], 519 | mock_request_response_content: bytes, 520 | expected_fetch_response: LinkupFetchResponse, 521 | ) -> None: 522 | request_mock = mocker.patch( 523 | "linkup._client.LinkupClient._request", 524 | return_value=Response( 525 | status_code=200, 526 | content=mock_request_response_content, 527 | ), 528 | ) 529 | 530 | fetch_response: LinkupFetchResponse = client.fetch(**fetch_kwargs) 531 | request_mock.assert_called_once_with( 532 | method="POST", 533 | url="/fetch", 534 | json=expected_request_params, 535 | timeout=None, 536 | ) 537 | assert fetch_response == expected_fetch_response 538 | 539 | 540 | @pytest.mark.asyncio 541 | @pytest.mark.parametrize( 542 | ( 543 | "fetch_kwargs", 544 | "expected_request_params", 545 | "mock_request_response_content", 546 | "expected_fetch_response", 547 | ), 548 | test_fetch_parameters, 549 | ) 550 | async def test_async_fetch( 551 | mocker: MockerFixture, 552 | client: LinkupClient, 553 | fetch_kwargs: dict[str, Any], 554 | expected_request_params: dict[str, Any], 555 | mock_request_response_content: bytes, 556 | expected_fetch_response: LinkupFetchResponse, 557 | ) -> None: 558 | request_mock = mocker.patch( 559 | "linkup._client.LinkupClient._async_request", 560 | return_value=Response( 561 | status_code=200, 562 | content=mock_request_response_content, 563 | ), 564 | ) 565 | 566 | fetch_response: LinkupFetchResponse = await client.async_fetch(**fetch_kwargs) 567 | request_mock.assert_called_once_with( 568 | method="POST", 569 | url="/fetch", 570 | json=expected_request_params, 571 | timeout=None, 572 | ) 573 | assert fetch_response == expected_fetch_response 574 | 575 | 576 | test_fetch_error_parameters = [ 577 | ( 578 | 400, 579 | b""" 580 | { 581 | "error": { 582 | "code": "FETCH_ERROR", 583 | "message": "Could not fetch the URL", 584 | "details": [] 585 | } 586 | } 587 | """, 588 | LinkupFailedFetchError, 589 | ), 590 | ( 591 | 400, 592 | b""" 593 | { 594 | "error": { 595 | "code": "VALIDATION_ERROR", 596 | "message": "Validation failed", 597 | "details": [ 598 | { 599 | "field": "url", 600 | "message": "url must be a valid URL" 601 | } 602 | ] 603 | } 604 | } 605 | """, 606 | LinkupInvalidRequestError, 607 | ), 608 | ] 609 | 610 | 611 | @pytest.mark.parametrize( 612 | ("mock_request_response_status_code", "mock_request_response_content", "expected_exception"), 613 | test_fetch_error_parameters, 614 | ) 615 | def test_fetch_error( 616 | mocker: MockerFixture, 617 | client: LinkupClient, 618 | mock_request_response_status_code: int, 619 | mock_request_response_content: bytes, 620 | expected_exception: type[Exception], 621 | ) -> None: 622 | request_mock = mocker.patch( 623 | "linkup._client.LinkupClient._request", 624 | return_value=Response( 625 | status_code=mock_request_response_status_code, 626 | content=mock_request_response_content, 627 | ), 628 | ) 629 | 630 | with pytest.raises(expected_exception): 631 | client.fetch(url="https://example.com") 632 | request_mock.assert_called_once() 633 | 634 | 635 | @pytest.mark.asyncio 636 | @pytest.mark.parametrize( 637 | ("mock_request_response_status_code", "mock_request_response_content", "expected_exception"), 638 | test_fetch_error_parameters, 639 | ) 640 | async def test_async_fetch_error( 641 | mocker: MockerFixture, 642 | client: LinkupClient, 643 | mock_request_response_status_code: int, 644 | mock_request_response_content: bytes, 645 | expected_exception: type[Exception], 646 | ) -> None: 647 | request_mock = mocker.patch( 648 | "linkup._client.LinkupClient._async_request", 649 | return_value=Response( 650 | status_code=mock_request_response_status_code, 651 | content=mock_request_response_content, 652 | ), 653 | ) 654 | 655 | with pytest.raises(expected_exception): 656 | await client.async_fetch(url="https://example.com") 657 | request_mock.assert_called_once() 658 | -------------------------------------------------------------------------------- /src/linkup/_client.py: -------------------------------------------------------------------------------- 1 | """Linkup client, the entrypoint for Linkup functions.""" 2 | 3 | from __future__ import annotations 4 | 5 | import json 6 | import os 7 | from datetime import date # noqa: TC003 (`date` is used in test mocks) 8 | from typing import Any, Literal 9 | 10 | import httpx 11 | from pydantic import BaseModel, SecretStr 12 | 13 | from ._errors import ( 14 | LinkupAuthenticationError, 15 | LinkupFailedFetchError, 16 | LinkupInsufficientCreditError, 17 | LinkupInvalidRequestError, 18 | LinkupNoResultError, 19 | LinkupTooManyRequestsError, 20 | LinkupUnknownError, 21 | ) 22 | from ._types import ( 23 | LinkupFetchResponse, 24 | LinkupSearchResults, 25 | LinkupSearchStructuredResponse, 26 | LinkupSourcedAnswer, 27 | ) 28 | from ._version import __version__ 29 | 30 | 31 | class LinkupClient: 32 | """The Linkup Client class, providing functions to call the Linkup API endpoints using Python. 33 | 34 | Args: 35 | api_key: The API key for the Linkup API. If None, the API key will be read from the 36 | environment variable `LINKUP_API_KEY`. 37 | base_url: The base URL for the Linkup API, for development purposes. 38 | 39 | Raises: 40 | ValueError: If the API key is not provided and not found in the environment variable. 41 | """ 42 | 43 | __version__ = __version__ 44 | 45 | def __init__( 46 | self, 47 | api_key: str | SecretStr | None = None, 48 | base_url: str = "https://api.linkup.so/v1", 49 | ) -> None: 50 | if api_key is None: 51 | api_key = os.getenv("LINKUP_API_KEY") 52 | if not api_key: 53 | raise ValueError("The Linkup API key was not provided") 54 | if isinstance(api_key, str): 55 | api_key = SecretStr(api_key) 56 | 57 | self._api_key: SecretStr = api_key 58 | self._base_url: str = base_url 59 | 60 | def search( 61 | self, 62 | query: str, 63 | depth: Literal["standard", "deep"], 64 | output_type: Literal["searchResults", "sourcedAnswer", "structured"], 65 | structured_output_schema: type[BaseModel] | str | None = None, 66 | include_images: bool | None = None, 67 | from_date: date | None = None, 68 | to_date: date | None = None, 69 | exclude_domains: list[str] | None = None, 70 | include_domains: list[str] | None = None, 71 | max_results: int | None = None, 72 | include_inline_citations: bool | None = None, 73 | include_sources: bool | None = None, 74 | ) -> Any: # noqa: ANN401 75 | """Perform a web search using the Linkup API `search` endpoint. 76 | 77 | All optional parameters will default to the Linkup API defaults when not provided. The 78 | Linkup API defaults are available in the 79 | [official documentation](https://docs.linkup.so/pages/documentation/api-reference/endpoint/post-search). 80 | 81 | Args: 82 | query: The search query. 83 | depth: The depth of the search. Can be either "standard", for a straighforward and 84 | fast search, or "deep" for a more powerful agentic workflow. 85 | output_type: The type of output which is expected: "searchResults" will output raw 86 | search results, "sourcedAnswer" will output the answer to the query and sources 87 | supporting it, and "structured" will base the output on the format provided in 88 | structured_output_schema. 89 | structured_output_schema: If output_type is "structured", specify the schema of the 90 | output. Supported formats are a pydantic.BaseModel or a string representing a 91 | valid object JSON schema. 92 | include_images: Indicate whether images should be included during the search. 93 | from_date: The date from which the search results should be considered. If None, the 94 | search results will not be filtered by date. 95 | to_date: The date until which the search results should be considered. If None, the 96 | search results will not be filtered by date. 97 | exclude_domains: If you want to exclude specific domains from your search. 98 | include_domains: If you want the search to only return results from certain domains. 99 | max_results: The maximum number of results to return. 100 | include_inline_citations: If output_type is "sourcedAnswer", indicate whether the 101 | answer should include inline citations. 102 | include_sources: If output_type is "structured", indicate whether the answer should 103 | include sources. This will modify the schema of the structured response. 104 | 105 | Returns: 106 | The Linkup API search result, which can have different types based on the parameters: 107 | - LinkupSearchResults if output_type is "searchResults" 108 | - LinkupSourcedAnswer if output_type is "sourcedAnswer" 109 | - the provided pydantic.BaseModel or an arbitrary data structure if output_type is 110 | "structured" and include_sources is False 111 | - LinkupSearchStructuredResponse with the provided pydantic.BaseModel or an arbitrary 112 | data structure as data field, if output_type is "structured" and include_sources is 113 | True 114 | 115 | Raises: 116 | TypeError: If structured_output_schema is not provided or is not a string or a 117 | pydantic.BaseModel when output_type is "structured". 118 | LinkupInvalidRequestError: If structured_output_schema doesn't represent a valid object 119 | JSON schema when output_type is "structured". 120 | LinkupAuthenticationError: If the Linkup API key is invalid. 121 | LinkupInsufficientCreditError: If you have run out of credit. 122 | LinkupNoResultError: If the search query did not yield any result. 123 | """ 124 | params: dict[str, str | bool | int | list[str]] = self._get_search_params( 125 | query=query, 126 | depth=depth, 127 | output_type=output_type, 128 | structured_output_schema=structured_output_schema, 129 | include_images=include_images, 130 | from_date=from_date, 131 | to_date=to_date, 132 | exclude_domains=exclude_domains, 133 | include_domains=include_domains, 134 | max_results=max_results, 135 | include_inline_citations=include_inline_citations, 136 | include_sources=include_sources, 137 | ) 138 | 139 | response: httpx.Response = self._request( 140 | method="POST", 141 | url="/search", 142 | json=params, 143 | timeout=None, 144 | ) 145 | if response.status_code != 200: 146 | self._raise_linkup_error(response=response) 147 | 148 | return self._parse_search_response( 149 | response=response, 150 | output_type=output_type, 151 | structured_output_schema=structured_output_schema, 152 | include_sources=include_sources, 153 | ) 154 | 155 | async def async_search( 156 | self, 157 | query: str, 158 | depth: Literal["standard", "deep"], 159 | output_type: Literal["searchResults", "sourcedAnswer", "structured"], 160 | structured_output_schema: type[BaseModel] | str | None = None, 161 | include_images: bool | None = None, 162 | from_date: date | None = None, 163 | to_date: date | None = None, 164 | exclude_domains: list[str] | None = None, 165 | include_domains: list[str] | None = None, 166 | max_results: int | None = None, 167 | include_inline_citations: bool | None = None, 168 | include_sources: bool | None = None, 169 | ) -> Any: # noqa: ANN401 170 | """Asynchronously perform a web search using the Linkup API `search` endpoint. 171 | 172 | All optional parameters will default to the Linkup API defaults when not provided. The 173 | Linkup API defaults are available in the 174 | [official documentation](https://docs.linkup.so/pages/documentation/api-reference/endpoint/post-search). 175 | 176 | Args: 177 | query: The search query. 178 | depth: The depth of the search. Can be either "standard", for a straighforward and 179 | fast search, or "deep" for a more powerful agentic workflow. 180 | output_type: The type of output which is expected: "searchResults" will output raw 181 | search results, "sourcedAnswer" will output the answer to the query and sources 182 | supporting it, and "structured" will base the output on the format provided in 183 | structured_output_schema. 184 | structured_output_schema: If output_type is "structured", specify the schema of the 185 | output. Supported formats are a pydantic.BaseModel or a string representing a 186 | valid object JSON schema. 187 | include_images: Indicate whether images should be included during the search. 188 | from_date: The date from which the search results should be considered. If None, the 189 | search results will not be filtered by date. 190 | to_date: The date until which the search results should be considered. If None, the 191 | search results will not be filtered by date. 192 | exclude_domains: If you want to exclude specific domains from your search. 193 | include_domains: If you want the search to only return results from certain domains. 194 | max_results: The maximum number of results to return. 195 | include_inline_citations: If output_type is "sourcedAnswer", indicate whether the 196 | answer should include inline citations. 197 | include_sources: If output_type is "structured", indicate whether the answer should 198 | include sources. This will modify the schema of the structured response. 199 | 200 | Returns: 201 | The Linkup API search result, which can have different types based on the parameters: 202 | - LinkupSearchResults if output_type is "searchResults" 203 | - LinkupSourcedAnswer if output_type is "sourcedAnswer" 204 | - the provided pydantic.BaseModel or an arbitrary data structure if output_type is 205 | "structured" and include_sources is False 206 | - LinkupSearchStructuredResponse with the provided pydantic.BaseModel or an arbitrary 207 | data structure as data field, if output_type is "structured" and include_sources is 208 | True 209 | 210 | Raises: 211 | TypeError: If structured_output_schema is not provided or is not a string or a 212 | pydantic.BaseModel when output_type is "structured". 213 | LinkupInvalidRequestError: If structured_output_schema doesn't represent a valid object 214 | JSON schema when output_type is "structured". 215 | LinkupAuthenticationError: If the Linkup API key is invalid. 216 | LinkupInsufficientCreditError: If you have run out of credit. 217 | LinkupNoResultError: If the search query did not yield any result. 218 | """ 219 | params: dict[str, str | bool | int | list[str]] = self._get_search_params( 220 | query=query, 221 | depth=depth, 222 | output_type=output_type, 223 | structured_output_schema=structured_output_schema, 224 | include_images=include_images, 225 | from_date=from_date, 226 | to_date=to_date, 227 | exclude_domains=exclude_domains, 228 | include_domains=include_domains, 229 | max_results=max_results, 230 | include_inline_citations=include_inline_citations, 231 | include_sources=include_sources, 232 | ) 233 | 234 | response: httpx.Response = await self._async_request( 235 | method="POST", 236 | url="/search", 237 | json=params, 238 | timeout=None, 239 | ) 240 | if response.status_code != 200: 241 | self._raise_linkup_error(response=response) 242 | 243 | return self._parse_search_response( 244 | response=response, 245 | output_type=output_type, 246 | structured_output_schema=structured_output_schema, 247 | include_sources=include_sources, 248 | ) 249 | 250 | def fetch( 251 | self, 252 | url: str, 253 | include_raw_html: bool | None = None, 254 | render_js: bool | None = None, 255 | extract_images: bool | None = None, 256 | ) -> LinkupFetchResponse: 257 | """Fetch the content of a web page using the Linkup API `fetch` endpoint. 258 | 259 | All optional parameters will default to the Linkup API defaults when not provided. The 260 | Linkup API defaults are available in the 261 | [official documentation](https://docs.linkup.so/pages/documentation/api-reference/endpoint/post-fetch). 262 | 263 | Args: 264 | url: The URL of the web page to fetch. 265 | include_raw_html: Whether to include the raw HTML of the webpage in the response. 266 | render_js: Whether the API should render the JavaScript of the webpage. 267 | extract_images: Whether the API should extract images from the webpage and return them 268 | in the response. 269 | 270 | Returns: 271 | The response of the web page fetch, containing the web page content. 272 | 273 | Raises: 274 | LinkupInvalidRequestError: If the provided URL is not valid. 275 | LinkupFailedFetchError: If the provided URL is not found or can't be fetched. 276 | """ 277 | params: dict[str, str | bool] = self._get_fetch_params( 278 | url=url, 279 | include_raw_html=include_raw_html, 280 | render_js=render_js, 281 | extract_images=extract_images, 282 | ) 283 | 284 | response: httpx.Response = self._request( 285 | method="POST", 286 | url="/fetch", 287 | json=params, 288 | timeout=None, 289 | ) 290 | if response.status_code != 200: 291 | self._raise_linkup_error(response=response) 292 | 293 | return self._parse_fetch_response(response=response) 294 | 295 | async def async_fetch( 296 | self, 297 | url: str, 298 | include_raw_html: bool | None = None, 299 | render_js: bool | None = None, 300 | extract_images: bool | None = None, 301 | ) -> LinkupFetchResponse: 302 | """Asynchronously fetch the content of a web page using the Linkup API `fetch` endpoint. 303 | 304 | All optional parameters will default to the Linkup API defaults when not provided. The 305 | Linkup API defaults are available in the 306 | [official documentation](https://docs.linkup.so/pages/documentation/api-reference/endpoint/post-fetch). 307 | 308 | Args: 309 | url: The URL of the web page to fetch. 310 | include_raw_html: Whether to include the raw HTML of the webpage in the response. 311 | render_js: Whether the API should render the JavaScript of the webpage. 312 | extract_images: Whether the API should extract images from the webpage and return them 313 | in the response. 314 | 315 | Returns: 316 | The response of the web page fetch, containing the web page content. 317 | 318 | Raises: 319 | LinkupInvalidRequestError: If the provided URL is not valid. 320 | LinkupFailedFetchError: If the provided URL is not found or can't be fetched. 321 | """ 322 | params: dict[str, str | bool] = self._get_fetch_params( 323 | url=url, 324 | include_raw_html=include_raw_html, 325 | render_js=render_js, 326 | extract_images=extract_images, 327 | ) 328 | 329 | response: httpx.Response = await self._async_request( 330 | method="POST", 331 | url="/fetch", 332 | json=params, 333 | timeout=None, 334 | ) 335 | if response.status_code != 200: 336 | self._raise_linkup_error(response=response) 337 | 338 | return self._parse_fetch_response(response=response) 339 | 340 | def _user_agent(self) -> str: # pragma: no cover 341 | return f"Linkup-Python/{self.__version__}" 342 | 343 | def _headers(self) -> dict[str, str]: # pragma: no cover 344 | return { 345 | "Authorization": f"Bearer {self._api_key.get_secret_value()}", 346 | "User-Agent": self._user_agent(), 347 | } 348 | 349 | def _request( 350 | self, 351 | method: str, 352 | url: str, 353 | **kwargs: Any, # noqa: ANN401 354 | ) -> httpx.Response: # pragma: no cover 355 | with httpx.Client(base_url=self._base_url, headers=self._headers()) as client: 356 | return client.request( 357 | method=method, 358 | url=url, 359 | **kwargs, 360 | ) 361 | 362 | async def _async_request( 363 | self, 364 | method: str, 365 | url: str, 366 | **kwargs: Any, # noqa: ANN401 367 | ) -> httpx.Response: # pragma: no cover 368 | async with httpx.AsyncClient(base_url=self._base_url, headers=self._headers()) as client: 369 | return await client.request( 370 | method=method, 371 | url=url, 372 | **kwargs, 373 | ) 374 | 375 | def _raise_linkup_error(self, response: httpx.Response) -> None: 376 | error_data = response.json() 377 | 378 | if "error" in error_data: 379 | error = error_data["error"] 380 | code = error.get("code", "") 381 | error_msg = error.get("message", "") 382 | details = error.get("details", []) 383 | 384 | if details and isinstance(details, list): 385 | for detail in details: 386 | if isinstance(detail, dict): 387 | field = detail.get("field", "") 388 | field_message = detail.get("message", "") 389 | error_msg += f" {field}: {field_message}" 390 | 391 | if response.status_code == 400: 392 | if code == "SEARCH_QUERY_NO_RESULT": 393 | raise LinkupNoResultError( 394 | "The Linkup API returned a no result error (400). " 395 | "Try rephrasing you query.\n" 396 | f"Original error message: {error_msg}." 397 | ) 398 | if code == "FETCH_ERROR": 399 | raise LinkupFailedFetchError( 400 | "The Linkup API returned a fetch error (400). " 401 | "The provided URL might not be found or can't be fetched.\n" 402 | f"Original error message: {error_msg}." 403 | ) 404 | raise LinkupInvalidRequestError( 405 | "The Linkup API returned an invalid request error (400). Make sure the " 406 | "parameters you used are valid (correct values, types, mandatory " 407 | "parameters, etc.) and you are using the latest version of the Python " 408 | "SDK.\n" 409 | f"Original error message: {error_msg}." 410 | ) 411 | if response.status_code == 401: 412 | raise LinkupAuthenticationError( 413 | "The Linkup API returned an authentication error (401). Make sure your API " 414 | "key is valid.\n" 415 | f"Original error message: {error_msg}." 416 | ) 417 | if response.status_code == 403: 418 | raise LinkupAuthenticationError( 419 | "The Linkup API returned an authorization error (403). Make sure your API " 420 | "key is valid.\n" 421 | f"Original error message: {error_msg}." 422 | ) 423 | if response.status_code == 429: 424 | if code == "INSUFFICIENT_FUNDS_CREDITS": 425 | raise LinkupInsufficientCreditError( 426 | "The Linkup API returned an insufficient credit error (429). Make sure " 427 | "you haven't exhausted your credits.\n" 428 | f"Original error message: {error_msg}." 429 | ) 430 | if code == "TOO_MANY_REQUESTS": 431 | raise LinkupTooManyRequestsError( 432 | "The Linkup API returned a too many requests error (429). Make sure " 433 | "you not sending too many requests at a time.\n" 434 | f"Original error message: {error_msg}." 435 | ) 436 | raise LinkupUnknownError( 437 | "The Linkup API returned an invalid request error (429). Make sure the " 438 | "parameters you used are valid (correct values, types, mandatory " 439 | "parameters, etc.) and you are using the latest version of the Python " 440 | "SDK.\n" 441 | f"Original error message: {error_msg}." 442 | ) 443 | raise LinkupUnknownError( 444 | f"The Linkup API returned an unknown error ({response.status_code}).\n" 445 | f"Original error message: ({error_msg})." 446 | ) 447 | 448 | def _get_search_params( 449 | self, 450 | query: str, 451 | depth: Literal["standard", "deep"], 452 | output_type: Literal["searchResults", "sourcedAnswer", "structured"], 453 | structured_output_schema: type[BaseModel] | str | None, 454 | include_images: bool | None, 455 | from_date: date | None, 456 | to_date: date | None, 457 | exclude_domains: list[str] | None, 458 | include_domains: list[str] | None, 459 | max_results: int | None, 460 | include_inline_citations: bool | None, 461 | include_sources: bool | None, 462 | ) -> dict[str, str | bool | int | list[str]]: 463 | params: dict[str, str | bool | int | list[str]] = { 464 | "q": query, 465 | "depth": depth, 466 | "outputType": output_type, 467 | } 468 | 469 | if structured_output_schema is not None: 470 | if isinstance(structured_output_schema, str): 471 | params["structuredOutputSchema"] = structured_output_schema 472 | elif issubclass(structured_output_schema, BaseModel): 473 | json_schema: dict[str, Any] = structured_output_schema.model_json_schema() 474 | params["structuredOutputSchema"] = json.dumps(json_schema) 475 | else: 476 | raise TypeError( 477 | f"Unexpected structured_output_schema type: '{type(structured_output_schema)}'" 478 | ) 479 | if include_images is not None: 480 | params["includeImages"] = include_images 481 | if from_date is not None: 482 | params["fromDate"] = from_date.isoformat() 483 | if to_date is not None: 484 | params["toDate"] = to_date.isoformat() 485 | if exclude_domains is not None: 486 | params["excludeDomains"] = exclude_domains 487 | if include_domains is not None: 488 | params["includeDomains"] = include_domains 489 | if max_results is not None: 490 | params["maxResults"] = max_results 491 | if include_inline_citations is not None: 492 | params["includeInlineCitations"] = include_inline_citations 493 | if include_sources is not None: 494 | params["includeSources"] = include_sources 495 | 496 | return params 497 | 498 | def _get_fetch_params( 499 | self, 500 | url: str, 501 | include_raw_html: bool | None, 502 | render_js: bool | None, 503 | extract_images: bool | None, 504 | ) -> dict[str, str | bool]: 505 | params: dict[str, str | bool] = { 506 | "url": url, 507 | } 508 | if include_raw_html is not None: 509 | params["includeRawHtml"] = include_raw_html 510 | if render_js is not None: 511 | params["renderJs"] = render_js 512 | if extract_images is not None: 513 | params["extractImages"] = extract_images 514 | return params 515 | 516 | def _parse_search_response( 517 | self, 518 | response: httpx.Response, 519 | output_type: Literal["searchResults", "sourcedAnswer", "structured"], 520 | structured_output_schema: type[BaseModel] | str | None, 521 | include_sources: bool | None, 522 | ) -> Any: # noqa: ANN401 523 | response_data: Any = response.json() 524 | if output_type == "searchResults": 525 | return LinkupSearchResults.model_validate(response_data) 526 | if output_type == "sourcedAnswer": 527 | return LinkupSourcedAnswer.model_validate(response_data) 528 | if output_type == "structured": 529 | if structured_output_schema is None: 530 | raise ValueError( 531 | "structured_output_schema must be provided when output_type is 'structured'" 532 | ) 533 | # HACK: we assume that `include_sources` will default to False, since the API output can 534 | # be arbitrary so we can't guess if it includes sources or not 535 | if include_sources: 536 | if not isinstance(structured_output_schema, str) and issubclass( 537 | structured_output_schema, BaseModel 538 | ): 539 | response_data["data"] = structured_output_schema.model_validate( 540 | response_data["data"] 541 | ) 542 | return LinkupSearchStructuredResponse.model_validate(response_data) 543 | if not isinstance(structured_output_schema, str) and issubclass( 544 | structured_output_schema, BaseModel 545 | ): 546 | return structured_output_schema.model_validate(response_data) 547 | return response_data 548 | raise ValueError(f"Unexpected output_type value: '{output_type}'") 549 | 550 | def _parse_fetch_response(self, response: httpx.Response) -> LinkupFetchResponse: 551 | return LinkupFetchResponse.model_validate(response.json()) 552 | -------------------------------------------------------------------------------- /uv.lock: -------------------------------------------------------------------------------- 1 | version = 1 2 | revision = 3 3 | requires-python = ">=3.9" 4 | resolution-markers = [ 5 | "python_full_version >= '3.10'", 6 | "python_full_version < '3.10'", 7 | ] 8 | 9 | [[package]] 10 | name = "annotated-types" 11 | version = "0.7.0" 12 | source = { registry = "https://pypi.org/simple" } 13 | sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } 14 | wheels = [ 15 | { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, 16 | ] 17 | 18 | [[package]] 19 | name = "anyio" 20 | version = "4.9.0" 21 | source = { registry = "https://pypi.org/simple" } 22 | dependencies = [ 23 | { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, 24 | { name = "idna" }, 25 | { name = "sniffio" }, 26 | { name = "typing-extensions", marker = "python_full_version < '3.13'" }, 27 | ] 28 | sdist = { url = "https://files.pythonhosted.org/packages/95/7d/4c1bd541d4dffa1b52bd83fb8527089e097a106fc90b467a7313b105f840/anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028", size = 190949, upload-time = "2025-03-17T00:02:54.77Z" } 29 | wheels = [ 30 | { url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916, upload-time = "2025-03-17T00:02:52.713Z" }, 31 | ] 32 | 33 | [[package]] 34 | name = "backports-asyncio-runner" 35 | version = "1.2.0" 36 | source = { registry = "https://pypi.org/simple" } 37 | sdist = { url = "https://files.pythonhosted.org/packages/8e/ff/70dca7d7cb1cbc0edb2c6cc0c38b65cba36cccc491eca64cabd5fe7f8670/backports_asyncio_runner-1.2.0.tar.gz", hash = "sha256:a5aa7b2b7d8f8bfcaa2b57313f70792df84e32a2a746f585213373f900b42162", size = 69893, upload-time = "2025-07-02T02:27:15.685Z" } 38 | wheels = [ 39 | { url = "https://files.pythonhosted.org/packages/a0/59/76ab57e3fe74484f48a53f8e337171b4a2349e506eabe136d7e01d059086/backports_asyncio_runner-1.2.0-py3-none-any.whl", hash = "sha256:0da0a936a8aeb554eccb426dc55af3ba63bcdc69fa1a600b5bb305413a4477b5", size = 12313, upload-time = "2025-07-02T02:27:14.263Z" }, 40 | ] 41 | 42 | [[package]] 43 | name = "certifi" 44 | version = "2025.7.14" 45 | source = { registry = "https://pypi.org/simple" } 46 | sdist = { url = "https://files.pythonhosted.org/packages/b3/76/52c535bcebe74590f296d6c77c86dabf761c41980e1347a2422e4aa2ae41/certifi-2025.7.14.tar.gz", hash = "sha256:8ea99dbdfaaf2ba2f9bac77b9249ef62ec5218e7c2b2e903378ed5fccf765995", size = 163981, upload-time = "2025-07-14T03:29:28.449Z" } 47 | wheels = [ 48 | { url = "https://files.pythonhosted.org/packages/4f/52/34c6cf5bb9285074dc3531c437b3919e825d976fde097a7a73f79e726d03/certifi-2025.7.14-py3-none-any.whl", hash = "sha256:6b31f564a415d79ee77df69d757bb49a5bb53bd9f756cbbe24394ffd6fc1f4b2", size = 162722, upload-time = "2025-07-14T03:29:26.863Z" }, 49 | ] 50 | 51 | [[package]] 52 | name = "cfgv" 53 | version = "3.4.0" 54 | source = { registry = "https://pypi.org/simple" } 55 | sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114, upload-time = "2023-08-12T20:38:17.776Z" } 56 | wheels = [ 57 | { url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249, upload-time = "2023-08-12T20:38:16.269Z" }, 58 | ] 59 | 60 | [[package]] 61 | name = "colorama" 62 | version = "0.4.6" 63 | source = { registry = "https://pypi.org/simple" } 64 | sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } 65 | wheels = [ 66 | { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, 67 | ] 68 | 69 | [[package]] 70 | name = "coverage" 71 | version = "7.9.2" 72 | source = { registry = "https://pypi.org/simple" } 73 | sdist = { url = "https://files.pythonhosted.org/packages/04/b7/c0465ca253df10a9e8dae0692a4ae6e9726d245390aaef92360e1d6d3832/coverage-7.9.2.tar.gz", hash = "sha256:997024fa51e3290264ffd7492ec97d0690293ccd2b45a6cd7d82d945a4a80c8b", size = 813556, upload-time = "2025-07-03T10:54:15.101Z" } 74 | wheels = [ 75 | { url = "https://files.pythonhosted.org/packages/a1/0d/5c2114fd776c207bd55068ae8dc1bef63ecd1b767b3389984a8e58f2b926/coverage-7.9.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:66283a192a14a3854b2e7f3418d7db05cdf411012ab7ff5db98ff3b181e1f912", size = 212039, upload-time = "2025-07-03T10:52:38.955Z" }, 76 | { url = "https://files.pythonhosted.org/packages/cf/ad/dc51f40492dc2d5fcd31bb44577bc0cc8920757d6bc5d3e4293146524ef9/coverage-7.9.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4e01d138540ef34fcf35c1aa24d06c3de2a4cffa349e29a10056544f35cca15f", size = 212428, upload-time = "2025-07-03T10:52:41.36Z" }, 77 | { url = "https://files.pythonhosted.org/packages/a2/a3/55cb3ff1b36f00df04439c3993d8529193cdf165a2467bf1402539070f16/coverage-7.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f22627c1fe2745ee98d3ab87679ca73a97e75ca75eb5faee48660d060875465f", size = 241534, upload-time = "2025-07-03T10:52:42.956Z" }, 78 | { url = "https://files.pythonhosted.org/packages/eb/c9/a8410b91b6be4f6e9c2e9f0dce93749b6b40b751d7065b4410bf89cb654b/coverage-7.9.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b1c2d8363247b46bd51f393f86c94096e64a1cf6906803fa8d5a9d03784bdbf", size = 239408, upload-time = "2025-07-03T10:52:44.199Z" }, 79 | { url = "https://files.pythonhosted.org/packages/ff/c4/6f3e56d467c612b9070ae71d5d3b114c0b899b5788e1ca3c93068ccb7018/coverage-7.9.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c10c882b114faf82dbd33e876d0cbd5e1d1ebc0d2a74ceef642c6152f3f4d547", size = 240552, upload-time = "2025-07-03T10:52:45.477Z" }, 80 | { url = "https://files.pythonhosted.org/packages/fd/20/04eda789d15af1ce79bce5cc5fd64057c3a0ac08fd0576377a3096c24663/coverage-7.9.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:de3c0378bdf7066c3988d66cd5232d161e933b87103b014ab1b0b4676098fa45", size = 240464, upload-time = "2025-07-03T10:52:46.809Z" }, 81 | { url = "https://files.pythonhosted.org/packages/a9/5a/217b32c94cc1a0b90f253514815332d08ec0812194a1ce9cca97dda1cd20/coverage-7.9.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:1e2f097eae0e5991e7623958a24ced3282676c93c013dde41399ff63e230fcf2", size = 239134, upload-time = "2025-07-03T10:52:48.149Z" }, 82 | { url = "https://files.pythonhosted.org/packages/34/73/1d019c48f413465eb5d3b6898b6279e87141c80049f7dbf73fd020138549/coverage-7.9.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:28dc1f67e83a14e7079b6cea4d314bc8b24d1aed42d3582ff89c0295f09b181e", size = 239405, upload-time = "2025-07-03T10:52:49.687Z" }, 83 | { url = "https://files.pythonhosted.org/packages/49/6c/a2beca7aa2595dad0c0d3f350382c381c92400efe5261e2631f734a0e3fe/coverage-7.9.2-cp310-cp310-win32.whl", hash = "sha256:bf7d773da6af9e10dbddacbf4e5cab13d06d0ed93561d44dae0188a42c65be7e", size = 214519, upload-time = "2025-07-03T10:52:51.036Z" }, 84 | { url = "https://files.pythonhosted.org/packages/fc/c8/91e5e4a21f9a51e2c7cdd86e587ae01a4fcff06fc3fa8cde4d6f7cf68df4/coverage-7.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:0c0378ba787681ab1897f7c89b415bd56b0b2d9a47e5a3d8dc0ea55aac118d6c", size = 215400, upload-time = "2025-07-03T10:52:52.313Z" }, 85 | { url = "https://files.pythonhosted.org/packages/39/40/916786453bcfafa4c788abee4ccd6f592b5b5eca0cd61a32a4e5a7ef6e02/coverage-7.9.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a7a56a2964a9687b6aba5b5ced6971af308ef6f79a91043c05dd4ee3ebc3e9ba", size = 212152, upload-time = "2025-07-03T10:52:53.562Z" }, 86 | { url = "https://files.pythonhosted.org/packages/9f/66/cc13bae303284b546a030762957322bbbff1ee6b6cb8dc70a40f8a78512f/coverage-7.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:123d589f32c11d9be7fe2e66d823a236fe759b0096f5db3fb1b75b2fa414a4fa", size = 212540, upload-time = "2025-07-03T10:52:55.196Z" }, 87 | { url = "https://files.pythonhosted.org/packages/0f/3c/d56a764b2e5a3d43257c36af4a62c379df44636817bb5f89265de4bf8bd7/coverage-7.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:333b2e0ca576a7dbd66e85ab402e35c03b0b22f525eed82681c4b866e2e2653a", size = 245097, upload-time = "2025-07-03T10:52:56.509Z" }, 88 | { url = "https://files.pythonhosted.org/packages/b1/46/bd064ea8b3c94eb4ca5d90e34d15b806cba091ffb2b8e89a0d7066c45791/coverage-7.9.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:326802760da234baf9f2f85a39e4a4b5861b94f6c8d95251f699e4f73b1835dc", size = 242812, upload-time = "2025-07-03T10:52:57.842Z" }, 89 | { url = "https://files.pythonhosted.org/packages/43/02/d91992c2b29bc7afb729463bc918ebe5f361be7f1daae93375a5759d1e28/coverage-7.9.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19e7be4cfec248df38ce40968c95d3952fbffd57b400d4b9bb580f28179556d2", size = 244617, upload-time = "2025-07-03T10:52:59.239Z" }, 90 | { url = "https://files.pythonhosted.org/packages/b7/4f/8fadff6bf56595a16d2d6e33415841b0163ac660873ed9a4e9046194f779/coverage-7.9.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0b4a4cb73b9f2b891c1788711408ef9707666501ba23684387277ededab1097c", size = 244263, upload-time = "2025-07-03T10:53:00.601Z" }, 91 | { url = "https://files.pythonhosted.org/packages/9b/d2/e0be7446a2bba11739edb9f9ba4eff30b30d8257370e237418eb44a14d11/coverage-7.9.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:2c8937fa16c8c9fbbd9f118588756e7bcdc7e16a470766a9aef912dd3f117dbd", size = 242314, upload-time = "2025-07-03T10:53:01.932Z" }, 92 | { url = "https://files.pythonhosted.org/packages/9d/7d/dcbac9345000121b8b57a3094c2dfcf1ccc52d8a14a40c1d4bc89f936f80/coverage-7.9.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:42da2280c4d30c57a9b578bafd1d4494fa6c056d4c419d9689e66d775539be74", size = 242904, upload-time = "2025-07-03T10:53:03.478Z" }, 93 | { url = "https://files.pythonhosted.org/packages/41/58/11e8db0a0c0510cf31bbbdc8caf5d74a358b696302a45948d7c768dfd1cf/coverage-7.9.2-cp311-cp311-win32.whl", hash = "sha256:14fa8d3da147f5fdf9d298cacc18791818f3f1a9f542c8958b80c228320e90c6", size = 214553, upload-time = "2025-07-03T10:53:05.174Z" }, 94 | { url = "https://files.pythonhosted.org/packages/3a/7d/751794ec8907a15e257136e48dc1021b1f671220ecccfd6c4eaf30802714/coverage-7.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:549cab4892fc82004f9739963163fd3aac7a7b0df430669b75b86d293d2df2a7", size = 215441, upload-time = "2025-07-03T10:53:06.472Z" }, 95 | { url = "https://files.pythonhosted.org/packages/62/5b/34abcedf7b946c1c9e15b44f326cb5b0da852885312b30e916f674913428/coverage-7.9.2-cp311-cp311-win_arm64.whl", hash = "sha256:c2667a2b913e307f06aa4e5677f01a9746cd08e4b35e14ebcde6420a9ebb4c62", size = 213873, upload-time = "2025-07-03T10:53:07.699Z" }, 96 | { url = "https://files.pythonhosted.org/packages/53/d7/7deefc6fd4f0f1d4c58051f4004e366afc9e7ab60217ac393f247a1de70a/coverage-7.9.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ae9eb07f1cfacd9cfe8eaee6f4ff4b8a289a668c39c165cd0c8548484920ffc0", size = 212344, upload-time = "2025-07-03T10:53:09.3Z" }, 97 | { url = "https://files.pythonhosted.org/packages/95/0c/ee03c95d32be4d519e6a02e601267769ce2e9a91fc8faa1b540e3626c680/coverage-7.9.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9ce85551f9a1119f02adc46d3014b5ee3f765deac166acf20dbb851ceb79b6f3", size = 212580, upload-time = "2025-07-03T10:53:11.52Z" }, 98 | { url = "https://files.pythonhosted.org/packages/8b/9f/826fa4b544b27620086211b87a52ca67592622e1f3af9e0a62c87aea153a/coverage-7.9.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8f6389ac977c5fb322e0e38885fbbf901743f79d47f50db706e7644dcdcb6e1", size = 246383, upload-time = "2025-07-03T10:53:13.134Z" }, 99 | { url = "https://files.pythonhosted.org/packages/7f/b3/4477aafe2a546427b58b9c540665feff874f4db651f4d3cb21b308b3a6d2/coverage-7.9.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff0d9eae8cdfcd58fe7893b88993723583a6ce4dfbfd9f29e001922544f95615", size = 243400, upload-time = "2025-07-03T10:53:14.614Z" }, 100 | { url = "https://files.pythonhosted.org/packages/f8/c2/efffa43778490c226d9d434827702f2dfbc8041d79101a795f11cbb2cf1e/coverage-7.9.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fae939811e14e53ed8a9818dad51d434a41ee09df9305663735f2e2d2d7d959b", size = 245591, upload-time = "2025-07-03T10:53:15.872Z" }, 101 | { url = "https://files.pythonhosted.org/packages/c6/e7/a59888e882c9a5f0192d8627a30ae57910d5d449c80229b55e7643c078c4/coverage-7.9.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:31991156251ec202c798501e0a42bbdf2169dcb0f137b1f5c0f4267f3fc68ef9", size = 245402, upload-time = "2025-07-03T10:53:17.124Z" }, 102 | { url = "https://files.pythonhosted.org/packages/92/a5/72fcd653ae3d214927edc100ce67440ed8a0a1e3576b8d5e6d066ed239db/coverage-7.9.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d0d67963f9cbfc7c7f96d4ac74ed60ecbebd2ea6eeb51887af0f8dce205e545f", size = 243583, upload-time = "2025-07-03T10:53:18.781Z" }, 103 | { url = "https://files.pythonhosted.org/packages/5c/f5/84e70e4df28f4a131d580d7d510aa1ffd95037293da66fd20d446090a13b/coverage-7.9.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:49b752a2858b10580969ec6af6f090a9a440a64a301ac1528d7ca5f7ed497f4d", size = 244815, upload-time = "2025-07-03T10:53:20.168Z" }, 104 | { url = "https://files.pythonhosted.org/packages/39/e7/d73d7cbdbd09fdcf4642655ae843ad403d9cbda55d725721965f3580a314/coverage-7.9.2-cp312-cp312-win32.whl", hash = "sha256:88d7598b8ee130f32f8a43198ee02edd16d7f77692fa056cb779616bbea1b355", size = 214719, upload-time = "2025-07-03T10:53:21.521Z" }, 105 | { url = "https://files.pythonhosted.org/packages/9f/d6/7486dcc3474e2e6ad26a2af2db7e7c162ccd889c4c68fa14ea8ec189c9e9/coverage-7.9.2-cp312-cp312-win_amd64.whl", hash = "sha256:9dfb070f830739ee49d7c83e4941cc767e503e4394fdecb3b54bfdac1d7662c0", size = 215509, upload-time = "2025-07-03T10:53:22.853Z" }, 106 | { url = "https://files.pythonhosted.org/packages/b7/34/0439f1ae2593b0346164d907cdf96a529b40b7721a45fdcf8b03c95fcd90/coverage-7.9.2-cp312-cp312-win_arm64.whl", hash = "sha256:4e2c058aef613e79df00e86b6d42a641c877211384ce5bd07585ed7ba71ab31b", size = 213910, upload-time = "2025-07-03T10:53:24.472Z" }, 107 | { url = "https://files.pythonhosted.org/packages/94/9d/7a8edf7acbcaa5e5c489a646226bed9591ee1c5e6a84733c0140e9ce1ae1/coverage-7.9.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:985abe7f242e0d7bba228ab01070fde1d6c8fa12f142e43debe9ed1dde686038", size = 212367, upload-time = "2025-07-03T10:53:25.811Z" }, 108 | { url = "https://files.pythonhosted.org/packages/e8/9e/5cd6f130150712301f7e40fb5865c1bc27b97689ec57297e568d972eec3c/coverage-7.9.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82c3939264a76d44fde7f213924021ed31f55ef28111a19649fec90c0f109e6d", size = 212632, upload-time = "2025-07-03T10:53:27.075Z" }, 109 | { url = "https://files.pythonhosted.org/packages/a8/de/6287a2c2036f9fd991c61cefa8c64e57390e30c894ad3aa52fac4c1e14a8/coverage-7.9.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae5d563e970dbe04382f736ec214ef48103d1b875967c89d83c6e3f21706d5b3", size = 245793, upload-time = "2025-07-03T10:53:28.408Z" }, 110 | { url = "https://files.pythonhosted.org/packages/06/cc/9b5a9961d8160e3cb0b558c71f8051fe08aa2dd4b502ee937225da564ed1/coverage-7.9.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdd612e59baed2a93c8843c9a7cb902260f181370f1d772f4842987535071d14", size = 243006, upload-time = "2025-07-03T10:53:29.754Z" }, 111 | { url = "https://files.pythonhosted.org/packages/49/d9/4616b787d9f597d6443f5588619c1c9f659e1f5fc9eebf63699eb6d34b78/coverage-7.9.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:256ea87cb2a1ed992bcdfc349d8042dcea1b80436f4ddf6e246d6bee4b5d73b6", size = 244990, upload-time = "2025-07-03T10:53:31.098Z" }, 112 | { url = "https://files.pythonhosted.org/packages/48/83/801cdc10f137b2d02b005a761661649ffa60eb173dcdaeb77f571e4dc192/coverage-7.9.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f44ae036b63c8ea432f610534a2668b0c3aee810e7037ab9d8ff6883de480f5b", size = 245157, upload-time = "2025-07-03T10:53:32.717Z" }, 113 | { url = "https://files.pythonhosted.org/packages/c8/a4/41911ed7e9d3ceb0ffb019e7635468df7499f5cc3edca5f7dfc078e9c5ec/coverage-7.9.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:82d76ad87c932935417a19b10cfe7abb15fd3f923cfe47dbdaa74ef4e503752d", size = 243128, upload-time = "2025-07-03T10:53:34.009Z" }, 114 | { url = "https://files.pythonhosted.org/packages/10/41/344543b71d31ac9cb00a664d5d0c9ef134a0fe87cb7d8430003b20fa0b7d/coverage-7.9.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:619317bb86de4193debc712b9e59d5cffd91dc1d178627ab2a77b9870deb2868", size = 244511, upload-time = "2025-07-03T10:53:35.434Z" }, 115 | { url = "https://files.pythonhosted.org/packages/d5/81/3b68c77e4812105e2a060f6946ba9e6f898ddcdc0d2bfc8b4b152a9ae522/coverage-7.9.2-cp313-cp313-win32.whl", hash = "sha256:0a07757de9feb1dfafd16ab651e0f628fd7ce551604d1bf23e47e1ddca93f08a", size = 214765, upload-time = "2025-07-03T10:53:36.787Z" }, 116 | { url = "https://files.pythonhosted.org/packages/06/a2/7fac400f6a346bb1a4004eb2a76fbff0e242cd48926a2ce37a22a6a1d917/coverage-7.9.2-cp313-cp313-win_amd64.whl", hash = "sha256:115db3d1f4d3f35f5bb021e270edd85011934ff97c8797216b62f461dd69374b", size = 215536, upload-time = "2025-07-03T10:53:38.188Z" }, 117 | { url = "https://files.pythonhosted.org/packages/08/47/2c6c215452b4f90d87017e61ea0fd9e0486bb734cb515e3de56e2c32075f/coverage-7.9.2-cp313-cp313-win_arm64.whl", hash = "sha256:48f82f889c80af8b2a7bb6e158d95a3fbec6a3453a1004d04e4f3b5945a02694", size = 213943, upload-time = "2025-07-03T10:53:39.492Z" }, 118 | { url = "https://files.pythonhosted.org/packages/a3/46/e211e942b22d6af5e0f323faa8a9bc7c447a1cf1923b64c47523f36ed488/coverage-7.9.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:55a28954545f9d2f96870b40f6c3386a59ba8ed50caf2d949676dac3ecab99f5", size = 213088, upload-time = "2025-07-03T10:53:40.874Z" }, 119 | { url = "https://files.pythonhosted.org/packages/d2/2f/762551f97e124442eccd907bf8b0de54348635b8866a73567eb4e6417acf/coverage-7.9.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cdef6504637731a63c133bb2e6f0f0214e2748495ec15fe42d1e219d1b133f0b", size = 213298, upload-time = "2025-07-03T10:53:42.218Z" }, 120 | { url = "https://files.pythonhosted.org/packages/7a/b7/76d2d132b7baf7360ed69be0bcab968f151fa31abe6d067f0384439d9edb/coverage-7.9.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bcd5ebe66c7a97273d5d2ddd4ad0ed2e706b39630ed4b53e713d360626c3dbb3", size = 256541, upload-time = "2025-07-03T10:53:43.823Z" }, 121 | { url = "https://files.pythonhosted.org/packages/a0/17/392b219837d7ad47d8e5974ce5f8dc3deb9f99a53b3bd4d123602f960c81/coverage-7.9.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9303aed20872d7a3c9cb39c5d2b9bdbe44e3a9a1aecb52920f7e7495410dfab8", size = 252761, upload-time = "2025-07-03T10:53:45.19Z" }, 122 | { url = "https://files.pythonhosted.org/packages/d5/77/4256d3577fe1b0daa8d3836a1ebe68eaa07dd2cbaf20cf5ab1115d6949d4/coverage-7.9.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc18ea9e417a04d1920a9a76fe9ebd2f43ca505b81994598482f938d5c315f46", size = 254917, upload-time = "2025-07-03T10:53:46.931Z" }, 123 | { url = "https://files.pythonhosted.org/packages/53/99/fc1a008eef1805e1ddb123cf17af864743354479ea5129a8f838c433cc2c/coverage-7.9.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6406cff19880aaaadc932152242523e892faff224da29e241ce2fca329866584", size = 256147, upload-time = "2025-07-03T10:53:48.289Z" }, 124 | { url = "https://files.pythonhosted.org/packages/92/c0/f63bf667e18b7f88c2bdb3160870e277c4874ced87e21426128d70aa741f/coverage-7.9.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2d0d4f6ecdf37fcc19c88fec3e2277d5dee740fb51ffdd69b9579b8c31e4232e", size = 254261, upload-time = "2025-07-03T10:53:49.99Z" }, 125 | { url = "https://files.pythonhosted.org/packages/8c/32/37dd1c42ce3016ff8ec9e4b607650d2e34845c0585d3518b2a93b4830c1a/coverage-7.9.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c33624f50cf8de418ab2b4d6ca9eda96dc45b2c4231336bac91454520e8d1fac", size = 255099, upload-time = "2025-07-03T10:53:51.354Z" }, 126 | { url = "https://files.pythonhosted.org/packages/da/2e/af6b86f7c95441ce82f035b3affe1cd147f727bbd92f563be35e2d585683/coverage-7.9.2-cp313-cp313t-win32.whl", hash = "sha256:1df6b76e737c6a92210eebcb2390af59a141f9e9430210595251fbaf02d46926", size = 215440, upload-time = "2025-07-03T10:53:52.808Z" }, 127 | { url = "https://files.pythonhosted.org/packages/4d/bb/8a785d91b308867f6b2e36e41c569b367c00b70c17f54b13ac29bcd2d8c8/coverage-7.9.2-cp313-cp313t-win_amd64.whl", hash = "sha256:f5fd54310b92741ebe00d9c0d1d7b2b27463952c022da6d47c175d246a98d1bd", size = 216537, upload-time = "2025-07-03T10:53:54.273Z" }, 128 | { url = "https://files.pythonhosted.org/packages/1d/a0/a6bffb5e0f41a47279fd45a8f3155bf193f77990ae1c30f9c224b61cacb0/coverage-7.9.2-cp313-cp313t-win_arm64.whl", hash = "sha256:c48c2375287108c887ee87d13b4070a381c6537d30e8487b24ec721bf2a781cb", size = 214398, upload-time = "2025-07-03T10:53:56.715Z" }, 129 | { url = "https://files.pythonhosted.org/packages/62/ab/b4b06662ccaa00ca7bbee967b7035a33a58b41efb92d8c89a6c523a2ccd5/coverage-7.9.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ddc39510ac922a5c4c27849b739f875d3e1d9e590d1e7b64c98dadf037a16cce", size = 212037, upload-time = "2025-07-03T10:53:58.055Z" }, 130 | { url = "https://files.pythonhosted.org/packages/bb/5e/04619995657acc898d15bfad42b510344b3a74d4d5bc34f2e279d46c781c/coverage-7.9.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a535c0c7364acd55229749c2b3e5eebf141865de3a8f697076a3291985f02d30", size = 212412, upload-time = "2025-07-03T10:53:59.451Z" }, 131 | { url = "https://files.pythonhosted.org/packages/14/e7/1465710224dc6d31c534e7714cbd907210622a044adc81c810e72eea873f/coverage-7.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df0f9ef28e0f20c767ccdccfc5ae5f83a6f4a2fbdfbcbcc8487a8a78771168c8", size = 241164, upload-time = "2025-07-03T10:54:00.852Z" }, 132 | { url = "https://files.pythonhosted.org/packages/ab/f2/44c6fbd2794afeb9ab6c0a14d3c088ab1dae3dff3df2624609981237bbb4/coverage-7.9.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f3da12e0ccbcb348969221d29441ac714bbddc4d74e13923d3d5a7a0bebef7a", size = 239032, upload-time = "2025-07-03T10:54:02.25Z" }, 133 | { url = "https://files.pythonhosted.org/packages/6a/d2/7a79845429c0aa2e6788bc45c26a2e3052fa91082c9ea1dea56fb531952c/coverage-7.9.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a17eaf46f56ae0f870f14a3cbc2e4632fe3771eab7f687eda1ee59b73d09fe4", size = 240148, upload-time = "2025-07-03T10:54:03.618Z" }, 134 | { url = "https://files.pythonhosted.org/packages/9c/7d/2731d1b4c9c672d82d30d218224dfc62939cf3800bc8aba0258fefb191f5/coverage-7.9.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:669135a9d25df55d1ed56a11bf555f37c922cf08d80799d4f65d77d7d6123fcf", size = 239875, upload-time = "2025-07-03T10:54:05.022Z" }, 135 | { url = "https://files.pythonhosted.org/packages/1b/83/685958715429a9da09cf172c15750ca5c795dd7259466f2645403696557b/coverage-7.9.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:9d3a700304d01a627df9db4322dc082a0ce1e8fc74ac238e2af39ced4c083193", size = 238127, upload-time = "2025-07-03T10:54:06.366Z" }, 136 | { url = "https://files.pythonhosted.org/packages/34/ff/161a4313308b3783126790adfae1970adbe4886fda8788792e435249910a/coverage-7.9.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:71ae8b53855644a0b1579d4041304ddc9995c7b21c8a1f16753c4d8903b4dfed", size = 239064, upload-time = "2025-07-03T10:54:07.878Z" }, 137 | { url = "https://files.pythonhosted.org/packages/17/14/fe33f41b2e80811021de059621f44c01ebe4d6b08bdb82d54a514488e933/coverage-7.9.2-cp39-cp39-win32.whl", hash = "sha256:dd7a57b33b5cf27acb491e890720af45db05589a80c1ffc798462a765be6d4d7", size = 214522, upload-time = "2025-07-03T10:54:09.331Z" }, 138 | { url = "https://files.pythonhosted.org/packages/6e/30/63d850ec31b5c6f6a7b4e853016375b846258300320eda29376e2786ceeb/coverage-7.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:f65bb452e579d5540c8b37ec105dd54d8b9307b07bcaa186818c104ffda22441", size = 215419, upload-time = "2025-07-03T10:54:10.681Z" }, 139 | { url = "https://files.pythonhosted.org/packages/d7/85/f8bbefac27d286386961c25515431482a425967e23d3698b75a250872924/coverage-7.9.2-pp39.pp310.pp311-none-any.whl", hash = "sha256:8a1166db2fb62473285bcb092f586e081e92656c7dfa8e9f62b4d39d7e6b5050", size = 204013, upload-time = "2025-07-03T10:54:12.084Z" }, 140 | { url = "https://files.pythonhosted.org/packages/3c/38/bbe2e63902847cf79036ecc75550d0698af31c91c7575352eb25190d0fb3/coverage-7.9.2-py3-none-any.whl", hash = "sha256:e425cd5b00f6fc0ed7cdbd766c70be8baab4b7839e4d4fe5fac48581dd968ea4", size = 204005, upload-time = "2025-07-03T10:54:13.491Z" }, 141 | ] 142 | 143 | [package.optional-dependencies] 144 | toml = [ 145 | { name = "tomli", marker = "python_full_version <= '3.11'" }, 146 | ] 147 | 148 | [[package]] 149 | name = "distlib" 150 | version = "0.4.0" 151 | source = { registry = "https://pypi.org/simple" } 152 | sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" } 153 | wheels = [ 154 | { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, 155 | ] 156 | 157 | [[package]] 158 | name = "exceptiongroup" 159 | version = "1.3.0" 160 | source = { registry = "https://pypi.org/simple" } 161 | dependencies = [ 162 | { name = "typing-extensions", marker = "python_full_version < '3.13'" }, 163 | ] 164 | sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749, upload-time = "2025-05-10T17:42:51.123Z" } 165 | wheels = [ 166 | { url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674, upload-time = "2025-05-10T17:42:49.33Z" }, 167 | ] 168 | 169 | [[package]] 170 | name = "filelock" 171 | version = "3.18.0" 172 | source = { registry = "https://pypi.org/simple" } 173 | sdist = { url = "https://files.pythonhosted.org/packages/0a/10/c23352565a6544bdc5353e0b15fc1c563352101f30e24bf500207a54df9a/filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2", size = 18075, upload-time = "2025-03-14T07:11:40.47Z" } 174 | wheels = [ 175 | { url = "https://files.pythonhosted.org/packages/4d/36/2a115987e2d8c300a974597416d9de88f2444426de9571f4b59b2cca3acc/filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de", size = 16215, upload-time = "2025-03-14T07:11:39.145Z" }, 176 | ] 177 | 178 | [[package]] 179 | name = "h11" 180 | version = "0.16.0" 181 | source = { registry = "https://pypi.org/simple" } 182 | sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } 183 | wheels = [ 184 | { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, 185 | ] 186 | 187 | [[package]] 188 | name = "httpcore" 189 | version = "1.0.9" 190 | source = { registry = "https://pypi.org/simple" } 191 | dependencies = [ 192 | { name = "certifi" }, 193 | { name = "h11" }, 194 | ] 195 | sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } 196 | wheels = [ 197 | { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, 198 | ] 199 | 200 | [[package]] 201 | name = "httpx" 202 | version = "0.28.1" 203 | source = { registry = "https://pypi.org/simple" } 204 | dependencies = [ 205 | { name = "anyio" }, 206 | { name = "certifi" }, 207 | { name = "httpcore" }, 208 | { name = "idna" }, 209 | ] 210 | sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } 211 | wheels = [ 212 | { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, 213 | ] 214 | 215 | [[package]] 216 | name = "identify" 217 | version = "2.6.12" 218 | source = { registry = "https://pypi.org/simple" } 219 | sdist = { url = "https://files.pythonhosted.org/packages/a2/88/d193a27416618628a5eea64e3223acd800b40749a96ffb322a9b55a49ed1/identify-2.6.12.tar.gz", hash = "sha256:d8de45749f1efb108badef65ee8386f0f7bb19a7f26185f74de6367bffbaf0e6", size = 99254, upload-time = "2025-05-23T20:37:53.3Z" } 220 | wheels = [ 221 | { url = "https://files.pythonhosted.org/packages/7a/cd/18f8da995b658420625f7ef13f037be53ae04ec5ad33f9b718240dcfd48c/identify-2.6.12-py2.py3-none-any.whl", hash = "sha256:ad9672d5a72e0d2ff7c5c8809b62dfa60458626352fb0eb7b55e69bdc45334a2", size = 99145, upload-time = "2025-05-23T20:37:51.495Z" }, 222 | ] 223 | 224 | [[package]] 225 | name = "idna" 226 | version = "3.10" 227 | source = { registry = "https://pypi.org/simple" } 228 | sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } 229 | wheels = [ 230 | { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, 231 | ] 232 | 233 | [[package]] 234 | name = "iniconfig" 235 | version = "2.1.0" 236 | source = { registry = "https://pypi.org/simple" } 237 | sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" } 238 | wheels = [ 239 | { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, 240 | ] 241 | 242 | [[package]] 243 | name = "linkup-sdk" 244 | version = "0.9.0" 245 | source = { editable = "." } 246 | dependencies = [ 247 | { name = "httpx" }, 248 | { name = "pydantic" }, 249 | ] 250 | 251 | [package.optional-dependencies] 252 | build = [ 253 | { name = "uv" }, 254 | ] 255 | 256 | [package.dev-dependencies] 257 | dev = [ 258 | { name = "mypy" }, 259 | { name = "pre-commit" }, 260 | { name = "pytest" }, 261 | { name = "pytest-asyncio" }, 262 | { name = "pytest-cov" }, 263 | { name = "pytest-mock" }, 264 | { name = "python-dotenv" }, 265 | { name = "rich" }, 266 | ] 267 | 268 | [package.metadata] 269 | requires-dist = [ 270 | { name = "httpx", specifier = ">=0.23.0" }, 271 | { name = "pydantic", specifier = ">=2.0.0" }, 272 | { name = "uv", marker = "extra == 'build'", specifier = ">=0.8.0,<0.9.0" }, 273 | ] 274 | provides-extras = ["build"] 275 | 276 | [package.metadata.requires-dev] 277 | dev = [ 278 | { name = "mypy", specifier = ">=1.16.1" }, 279 | { name = "pre-commit", specifier = ">=4.2.0" }, 280 | { name = "pytest", specifier = ">=8.4.1" }, 281 | { name = "pytest-asyncio", specifier = ">=1.0.0" }, 282 | { name = "pytest-cov", specifier = ">=6.2.1" }, 283 | { name = "pytest-mock", specifier = ">=3.14.1" }, 284 | { name = "python-dotenv", specifier = ">=1.1.1" }, 285 | { name = "rich", specifier = ">=14.1.0" }, 286 | ] 287 | 288 | [[package]] 289 | name = "markdown-it-py" 290 | version = "3.0.0" 291 | source = { registry = "https://pypi.org/simple" } 292 | resolution-markers = [ 293 | "python_full_version < '3.10'", 294 | ] 295 | dependencies = [ 296 | { name = "mdurl", marker = "python_full_version < '3.10'" }, 297 | ] 298 | sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596, upload-time = "2023-06-03T06:41:14.443Z" } 299 | wheels = [ 300 | { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528, upload-time = "2023-06-03T06:41:11.019Z" }, 301 | ] 302 | 303 | [[package]] 304 | name = "markdown-it-py" 305 | version = "4.0.0" 306 | source = { registry = "https://pypi.org/simple" } 307 | resolution-markers = [ 308 | "python_full_version >= '3.10'", 309 | ] 310 | dependencies = [ 311 | { name = "mdurl", marker = "python_full_version >= '3.10'" }, 312 | ] 313 | sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" } 314 | wheels = [ 315 | { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" }, 316 | ] 317 | 318 | [[package]] 319 | name = "mdurl" 320 | version = "0.1.2" 321 | source = { registry = "https://pypi.org/simple" } 322 | sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } 323 | wheels = [ 324 | { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, 325 | ] 326 | 327 | [[package]] 328 | name = "mypy" 329 | version = "1.17.0" 330 | source = { registry = "https://pypi.org/simple" } 331 | dependencies = [ 332 | { name = "mypy-extensions" }, 333 | { name = "pathspec" }, 334 | { name = "tomli", marker = "python_full_version < '3.11'" }, 335 | { name = "typing-extensions" }, 336 | ] 337 | sdist = { url = "https://files.pythonhosted.org/packages/1e/e3/034322d5a779685218ed69286c32faa505247f1f096251ef66c8fd203b08/mypy-1.17.0.tar.gz", hash = "sha256:e5d7ccc08ba089c06e2f5629c660388ef1fee708444f1dee0b9203fa031dee03", size = 3352114, upload-time = "2025-07-14T20:34:30.181Z" } 338 | wheels = [ 339 | { url = "https://files.pythonhosted.org/packages/6a/31/e762baa3b73905c856d45ab77b4af850e8159dffffd86a52879539a08c6b/mypy-1.17.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f8e08de6138043108b3b18f09d3f817a4783912e48828ab397ecf183135d84d6", size = 10998313, upload-time = "2025-07-14T20:33:24.519Z" }, 340 | { url = "https://files.pythonhosted.org/packages/1c/c1/25b2f0d46fb7e0b5e2bee61ec3a47fe13eff9e3c2f2234f144858bbe6485/mypy-1.17.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce4a17920ec144647d448fc43725b5873548b1aae6c603225626747ededf582d", size = 10128922, upload-time = "2025-07-14T20:34:06.414Z" }, 341 | { url = "https://files.pythonhosted.org/packages/02/78/6d646603a57aa8a2886df1b8881fe777ea60f28098790c1089230cd9c61d/mypy-1.17.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6ff25d151cc057fdddb1cb1881ef36e9c41fa2a5e78d8dd71bee6e4dcd2bc05b", size = 11913524, upload-time = "2025-07-14T20:33:19.109Z" }, 342 | { url = "https://files.pythonhosted.org/packages/4f/19/dae6c55e87ee426fb76980f7e78484450cad1c01c55a1dc4e91c930bea01/mypy-1.17.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93468cf29aa9a132bceb103bd8475f78cacde2b1b9a94fd978d50d4bdf616c9a", size = 12650527, upload-time = "2025-07-14T20:32:44.095Z" }, 343 | { url = "https://files.pythonhosted.org/packages/86/e1/f916845a235235a6c1e4d4d065a3930113767001d491b8b2e1b61ca56647/mypy-1.17.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:98189382b310f16343151f65dd7e6867386d3e35f7878c45cfa11383d175d91f", size = 12897284, upload-time = "2025-07-14T20:33:38.168Z" }, 344 | { url = "https://files.pythonhosted.org/packages/ae/dc/414760708a4ea1b096bd214d26a24e30ac5e917ef293bc33cdb6fe22d2da/mypy-1.17.0-cp310-cp310-win_amd64.whl", hash = "sha256:c004135a300ab06a045c1c0d8e3f10215e71d7b4f5bb9a42ab80236364429937", size = 9506493, upload-time = "2025-07-14T20:34:01.093Z" }, 345 | { url = "https://files.pythonhosted.org/packages/d4/24/82efb502b0b0f661c49aa21cfe3e1999ddf64bf5500fc03b5a1536a39d39/mypy-1.17.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9d4fe5c72fd262d9c2c91c1117d16aac555e05f5beb2bae6a755274c6eec42be", size = 10914150, upload-time = "2025-07-14T20:31:51.985Z" }, 346 | { url = "https://files.pythonhosted.org/packages/03/96/8ef9a6ff8cedadff4400e2254689ca1dc4b420b92c55255b44573de10c54/mypy-1.17.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d96b196e5c16f41b4f7736840e8455958e832871990c7ba26bf58175e357ed61", size = 10039845, upload-time = "2025-07-14T20:32:30.527Z" }, 347 | { url = "https://files.pythonhosted.org/packages/df/32/7ce359a56be779d38021d07941cfbb099b41411d72d827230a36203dbb81/mypy-1.17.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:73a0ff2dd10337ceb521c080d4147755ee302dcde6e1a913babd59473904615f", size = 11837246, upload-time = "2025-07-14T20:32:01.28Z" }, 348 | { url = "https://files.pythonhosted.org/packages/82/16/b775047054de4d8dbd668df9137707e54b07fe18c7923839cd1e524bf756/mypy-1.17.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24cfcc1179c4447854e9e406d3af0f77736d631ec87d31c6281ecd5025df625d", size = 12571106, upload-time = "2025-07-14T20:34:26.942Z" }, 349 | { url = "https://files.pythonhosted.org/packages/a1/cf/fa33eaf29a606102c8d9ffa45a386a04c2203d9ad18bf4eef3e20c43ebc8/mypy-1.17.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3c56f180ff6430e6373db7a1d569317675b0a451caf5fef6ce4ab365f5f2f6c3", size = 12759960, upload-time = "2025-07-14T20:33:42.882Z" }, 350 | { url = "https://files.pythonhosted.org/packages/94/75/3f5a29209f27e739ca57e6350bc6b783a38c7621bdf9cac3ab8a08665801/mypy-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:eafaf8b9252734400f9b77df98b4eee3d2eecab16104680d51341c75702cad70", size = 9503888, upload-time = "2025-07-14T20:32:34.392Z" }, 351 | { url = "https://files.pythonhosted.org/packages/12/e9/e6824ed620bbf51d3bf4d6cbbe4953e83eaf31a448d1b3cfb3620ccb641c/mypy-1.17.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f986f1cab8dbec39ba6e0eaa42d4d3ac6686516a5d3dccd64be095db05ebc6bb", size = 11086395, upload-time = "2025-07-14T20:34:11.452Z" }, 352 | { url = "https://files.pythonhosted.org/packages/ba/51/a4afd1ae279707953be175d303f04a5a7bd7e28dc62463ad29c1c857927e/mypy-1.17.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:51e455a54d199dd6e931cd7ea987d061c2afbaf0960f7f66deef47c90d1b304d", size = 10120052, upload-time = "2025-07-14T20:33:09.897Z" }, 353 | { url = "https://files.pythonhosted.org/packages/8a/71/19adfeac926ba8205f1d1466d0d360d07b46486bf64360c54cb5a2bd86a8/mypy-1.17.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3204d773bab5ff4ebbd1f8efa11b498027cd57017c003ae970f310e5b96be8d8", size = 11861806, upload-time = "2025-07-14T20:32:16.028Z" }, 354 | { url = "https://files.pythonhosted.org/packages/0b/64/d6120eca3835baf7179e6797a0b61d6c47e0bc2324b1f6819d8428d5b9ba/mypy-1.17.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1051df7ec0886fa246a530ae917c473491e9a0ba6938cfd0ec2abc1076495c3e", size = 12744371, upload-time = "2025-07-14T20:33:33.503Z" }, 355 | { url = "https://files.pythonhosted.org/packages/1f/dc/56f53b5255a166f5bd0f137eed960e5065f2744509dfe69474ff0ba772a5/mypy-1.17.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f773c6d14dcc108a5b141b4456b0871df638eb411a89cd1c0c001fc4a9d08fc8", size = 12914558, upload-time = "2025-07-14T20:33:56.961Z" }, 356 | { url = "https://files.pythonhosted.org/packages/69/ac/070bad311171badc9add2910e7f89271695a25c136de24bbafc7eded56d5/mypy-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:1619a485fd0e9c959b943c7b519ed26b712de3002d7de43154a489a2d0fd817d", size = 9585447, upload-time = "2025-07-14T20:32:20.594Z" }, 357 | { url = "https://files.pythonhosted.org/packages/be/7b/5f8ab461369b9e62157072156935cec9d272196556bdc7c2ff5f4c7c0f9b/mypy-1.17.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2c41aa59211e49d717d92b3bb1238c06d387c9325d3122085113c79118bebb06", size = 11070019, upload-time = "2025-07-14T20:32:07.99Z" }, 358 | { url = "https://files.pythonhosted.org/packages/9c/f8/c49c9e5a2ac0badcc54beb24e774d2499748302c9568f7f09e8730e953fa/mypy-1.17.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0e69db1fb65b3114f98c753e3930a00514f5b68794ba80590eb02090d54a5d4a", size = 10114457, upload-time = "2025-07-14T20:33:47.285Z" }, 359 | { url = "https://files.pythonhosted.org/packages/89/0c/fb3f9c939ad9beed3e328008b3fb90b20fda2cddc0f7e4c20dbefefc3b33/mypy-1.17.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:03ba330b76710f83d6ac500053f7727270b6b8553b0423348ffb3af6f2f7b889", size = 11857838, upload-time = "2025-07-14T20:33:14.462Z" }, 360 | { url = "https://files.pythonhosted.org/packages/4c/66/85607ab5137d65e4f54d9797b77d5a038ef34f714929cf8ad30b03f628df/mypy-1.17.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:037bc0f0b124ce46bfde955c647f3e395c6174476a968c0f22c95a8d2f589bba", size = 12731358, upload-time = "2025-07-14T20:32:25.579Z" }, 361 | { url = "https://files.pythonhosted.org/packages/73/d0/341dbbfb35ce53d01f8f2969facbb66486cee9804048bf6c01b048127501/mypy-1.17.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c38876106cb6132259683632b287238858bd58de267d80defb6f418e9ee50658", size = 12917480, upload-time = "2025-07-14T20:34:21.868Z" }, 362 | { url = "https://files.pythonhosted.org/packages/64/63/70c8b7dbfc520089ac48d01367a97e8acd734f65bd07813081f508a8c94c/mypy-1.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:d30ba01c0f151998f367506fab31c2ac4527e6a7b2690107c7a7f9e3cb419a9c", size = 9589666, upload-time = "2025-07-14T20:34:16.841Z" }, 363 | { url = "https://files.pythonhosted.org/packages/9f/a0/6263dd11941231f688f0a8f2faf90ceac1dc243d148d314a089d2fe25108/mypy-1.17.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:63e751f1b5ab51d6f3d219fe3a2fe4523eaa387d854ad06906c63883fde5b1ab", size = 10988185, upload-time = "2025-07-14T20:33:04.797Z" }, 364 | { url = "https://files.pythonhosted.org/packages/02/13/b8f16d6b0dc80277129559c8e7dbc9011241a0da8f60d031edb0e6e9ac8f/mypy-1.17.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f7fb09d05e0f1c329a36dcd30e27564a3555717cde87301fae4fb542402ddfad", size = 10120169, upload-time = "2025-07-14T20:32:38.84Z" }, 365 | { url = "https://files.pythonhosted.org/packages/14/ef/978ba79df0d65af680e20d43121363cf643eb79b04bf3880d01fc8afeb6f/mypy-1.17.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b72c34ce05ac3a1361ae2ebb50757fb6e3624032d91488d93544e9f82db0ed6c", size = 11918121, upload-time = "2025-07-14T20:33:52.328Z" }, 366 | { url = "https://files.pythonhosted.org/packages/f4/10/55ef70b104151a0d8280474f05268ff0a2a79be8d788d5e647257d121309/mypy-1.17.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:434ad499ad8dde8b2f6391ddfa982f41cb07ccda8e3c67781b1bfd4e5f9450a8", size = 12648821, upload-time = "2025-07-14T20:32:59.631Z" }, 367 | { url = "https://files.pythonhosted.org/packages/26/8c/7781fcd2e1eef48fbedd3a422c21fe300a8e03ed5be2eb4bd10246a77f4e/mypy-1.17.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f105f61a5eff52e137fd73bee32958b2add9d9f0a856f17314018646af838e97", size = 12896955, upload-time = "2025-07-14T20:32:49.543Z" }, 368 | { url = "https://files.pythonhosted.org/packages/78/13/03ac759dabe86e98ca7b6681f114f90ee03f3ff8365a57049d311bd4a4e3/mypy-1.17.0-cp39-cp39-win_amd64.whl", hash = "sha256:ba06254a5a22729853209550d80f94e28690d5530c661f9416a68ac097b13fc4", size = 9512957, upload-time = "2025-07-14T20:33:28.619Z" }, 369 | { url = "https://files.pythonhosted.org/packages/e3/fc/ee058cc4316f219078464555873e99d170bde1d9569abd833300dbeb484a/mypy-1.17.0-py3-none-any.whl", hash = "sha256:15d9d0018237ab058e5de3d8fce61b6fa72cc59cc78fd91f1b474bce12abf496", size = 2283195, upload-time = "2025-07-14T20:31:54.753Z" }, 370 | ] 371 | 372 | [[package]] 373 | name = "mypy-extensions" 374 | version = "1.1.0" 375 | source = { registry = "https://pypi.org/simple" } 376 | sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } 377 | wheels = [ 378 | { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, 379 | ] 380 | 381 | [[package]] 382 | name = "nodeenv" 383 | version = "1.9.1" 384 | source = { registry = "https://pypi.org/simple" } 385 | sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437, upload-time = "2024-06-04T18:44:11.171Z" } 386 | wheels = [ 387 | { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314, upload-time = "2024-06-04T18:44:08.352Z" }, 388 | ] 389 | 390 | [[package]] 391 | name = "packaging" 392 | version = "25.0" 393 | source = { registry = "https://pypi.org/simple" } 394 | sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } 395 | wheels = [ 396 | { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, 397 | ] 398 | 399 | [[package]] 400 | name = "pathspec" 401 | version = "0.12.1" 402 | source = { registry = "https://pypi.org/simple" } 403 | sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" } 404 | wheels = [ 405 | { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" }, 406 | ] 407 | 408 | [[package]] 409 | name = "platformdirs" 410 | version = "4.3.8" 411 | source = { registry = "https://pypi.org/simple" } 412 | sdist = { url = "https://files.pythonhosted.org/packages/fe/8b/3c73abc9c759ecd3f1f7ceff6685840859e8070c4d947c93fae71f6a0bf2/platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc", size = 21362, upload-time = "2025-05-07T22:47:42.121Z" } 413 | wheels = [ 414 | { url = "https://files.pythonhosted.org/packages/fe/39/979e8e21520d4e47a0bbe349e2713c0aac6f3d853d0e5b34d76206c439aa/platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4", size = 18567, upload-time = "2025-05-07T22:47:40.376Z" }, 415 | ] 416 | 417 | [[package]] 418 | name = "pluggy" 419 | version = "1.6.0" 420 | source = { registry = "https://pypi.org/simple" } 421 | sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } 422 | wheels = [ 423 | { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, 424 | ] 425 | 426 | [[package]] 427 | name = "pre-commit" 428 | version = "4.2.0" 429 | source = { registry = "https://pypi.org/simple" } 430 | dependencies = [ 431 | { name = "cfgv" }, 432 | { name = "identify" }, 433 | { name = "nodeenv" }, 434 | { name = "pyyaml" }, 435 | { name = "virtualenv" }, 436 | ] 437 | sdist = { url = "https://files.pythonhosted.org/packages/08/39/679ca9b26c7bb2999ff122d50faa301e49af82ca9c066ec061cfbc0c6784/pre_commit-4.2.0.tar.gz", hash = "sha256:601283b9757afd87d40c4c4a9b2b5de9637a8ea02eaff7adc2d0fb4e04841146", size = 193424, upload-time = "2025-03-18T21:35:20.987Z" } 438 | wheels = [ 439 | { url = "https://files.pythonhosted.org/packages/88/74/a88bf1b1efeae488a0c0b7bdf71429c313722d1fc0f377537fbe554e6180/pre_commit-4.2.0-py2.py3-none-any.whl", hash = "sha256:a009ca7205f1eb497d10b845e52c838a98b6cdd2102a6c8e4540e94ee75c58bd", size = 220707, upload-time = "2025-03-18T21:35:19.343Z" }, 440 | ] 441 | 442 | [[package]] 443 | name = "pydantic" 444 | version = "2.11.7" 445 | source = { registry = "https://pypi.org/simple" } 446 | dependencies = [ 447 | { name = "annotated-types" }, 448 | { name = "pydantic-core" }, 449 | { name = "typing-extensions" }, 450 | { name = "typing-inspection" }, 451 | ] 452 | sdist = { url = "https://files.pythonhosted.org/packages/00/dd/4325abf92c39ba8623b5af936ddb36ffcfe0beae70405d456ab1fb2f5b8c/pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db", size = 788350, upload-time = "2025-06-14T08:33:17.137Z" } 453 | wheels = [ 454 | { url = "https://files.pythonhosted.org/packages/6a/c0/ec2b1c8712ca690e5d61979dee872603e92b8a32f94cc1b72d53beab008a/pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b", size = 444782, upload-time = "2025-06-14T08:33:14.905Z" }, 455 | ] 456 | 457 | [[package]] 458 | name = "pydantic-core" 459 | version = "2.33.2" 460 | source = { registry = "https://pypi.org/simple" } 461 | dependencies = [ 462 | { name = "typing-extensions" }, 463 | ] 464 | sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195, upload-time = "2025-04-23T18:33:52.104Z" } 465 | wheels = [ 466 | { url = "https://files.pythonhosted.org/packages/e5/92/b31726561b5dae176c2d2c2dc43a9c5bfba5d32f96f8b4c0a600dd492447/pydantic_core-2.33.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8", size = 2028817, upload-time = "2025-04-23T18:30:43.919Z" }, 467 | { url = "https://files.pythonhosted.org/packages/a3/44/3f0b95fafdaca04a483c4e685fe437c6891001bf3ce8b2fded82b9ea3aa1/pydantic_core-2.33.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d", size = 1861357, upload-time = "2025-04-23T18:30:46.372Z" }, 468 | { url = "https://files.pythonhosted.org/packages/30/97/e8f13b55766234caae05372826e8e4b3b96e7b248be3157f53237682e43c/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0069c9acc3f3981b9ff4cdfaf088e98d83440a4c7ea1bc07460af3d4dc22e72d", size = 1898011, upload-time = "2025-04-23T18:30:47.591Z" }, 469 | { url = "https://files.pythonhosted.org/packages/9b/a3/99c48cf7bafc991cc3ee66fd544c0aae8dc907b752f1dad2d79b1b5a471f/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d53b22f2032c42eaaf025f7c40c2e3b94568ae077a606f006d206a463bc69572", size = 1982730, upload-time = "2025-04-23T18:30:49.328Z" }, 470 | { url = "https://files.pythonhosted.org/packages/de/8e/a5b882ec4307010a840fb8b58bd9bf65d1840c92eae7534c7441709bf54b/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0405262705a123b7ce9f0b92f123334d67b70fd1f20a9372b907ce1080c7ba02", size = 2136178, upload-time = "2025-04-23T18:30:50.907Z" }, 471 | { url = "https://files.pythonhosted.org/packages/e4/bb/71e35fc3ed05af6834e890edb75968e2802fe98778971ab5cba20a162315/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b25d91e288e2c4e0662b8038a28c6a07eaac3e196cfc4ff69de4ea3db992a1b", size = 2736462, upload-time = "2025-04-23T18:30:52.083Z" }, 472 | { url = "https://files.pythonhosted.org/packages/31/0d/c8f7593e6bc7066289bbc366f2235701dcbebcd1ff0ef8e64f6f239fb47d/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bdfe4b3789761f3bcb4b1ddf33355a71079858958e3a552f16d5af19768fef2", size = 2005652, upload-time = "2025-04-23T18:30:53.389Z" }, 473 | { url = "https://files.pythonhosted.org/packages/d2/7a/996d8bd75f3eda405e3dd219ff5ff0a283cd8e34add39d8ef9157e722867/pydantic_core-2.33.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:efec8db3266b76ef9607c2c4c419bdb06bf335ae433b80816089ea7585816f6a", size = 2113306, upload-time = "2025-04-23T18:30:54.661Z" }, 474 | { url = "https://files.pythonhosted.org/packages/ff/84/daf2a6fb2db40ffda6578a7e8c5a6e9c8affb251a05c233ae37098118788/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:031c57d67ca86902726e0fae2214ce6770bbe2f710dc33063187a68744a5ecac", size = 2073720, upload-time = "2025-04-23T18:30:56.11Z" }, 475 | { url = "https://files.pythonhosted.org/packages/77/fb/2258da019f4825128445ae79456a5499c032b55849dbd5bed78c95ccf163/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:f8de619080e944347f5f20de29a975c2d815d9ddd8be9b9b7268e2e3ef68605a", size = 2244915, upload-time = "2025-04-23T18:30:57.501Z" }, 476 | { url = "https://files.pythonhosted.org/packages/d8/7a/925ff73756031289468326e355b6fa8316960d0d65f8b5d6b3a3e7866de7/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:73662edf539e72a9440129f231ed3757faab89630d291b784ca99237fb94db2b", size = 2241884, upload-time = "2025-04-23T18:30:58.867Z" }, 477 | { url = "https://files.pythonhosted.org/packages/0b/b0/249ee6d2646f1cdadcb813805fe76265745c4010cf20a8eba7b0e639d9b2/pydantic_core-2.33.2-cp310-cp310-win32.whl", hash = "sha256:0a39979dcbb70998b0e505fb1556a1d550a0781463ce84ebf915ba293ccb7e22", size = 1910496, upload-time = "2025-04-23T18:31:00.078Z" }, 478 | { url = "https://files.pythonhosted.org/packages/66/ff/172ba8f12a42d4b552917aa65d1f2328990d3ccfc01d5b7c943ec084299f/pydantic_core-2.33.2-cp310-cp310-win_amd64.whl", hash = "sha256:b0379a2b24882fef529ec3b4987cb5d003b9cda32256024e6fe1586ac45fc640", size = 1955019, upload-time = "2025-04-23T18:31:01.335Z" }, 479 | { url = "https://files.pythonhosted.org/packages/3f/8d/71db63483d518cbbf290261a1fc2839d17ff89fce7089e08cad07ccfce67/pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7", size = 2028584, upload-time = "2025-04-23T18:31:03.106Z" }, 480 | { url = "https://files.pythonhosted.org/packages/24/2f/3cfa7244ae292dd850989f328722d2aef313f74ffc471184dc509e1e4e5a/pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246", size = 1855071, upload-time = "2025-04-23T18:31:04.621Z" }, 481 | { url = "https://files.pythonhosted.org/packages/b3/d3/4ae42d33f5e3f50dd467761304be2fa0a9417fbf09735bc2cce003480f2a/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f", size = 1897823, upload-time = "2025-04-23T18:31:06.377Z" }, 482 | { url = "https://files.pythonhosted.org/packages/f4/f3/aa5976e8352b7695ff808599794b1fba2a9ae2ee954a3426855935799488/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc", size = 1983792, upload-time = "2025-04-23T18:31:07.93Z" }, 483 | { url = "https://files.pythonhosted.org/packages/d5/7a/cda9b5a23c552037717f2b2a5257e9b2bfe45e687386df9591eff7b46d28/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de", size = 2136338, upload-time = "2025-04-23T18:31:09.283Z" }, 484 | { url = "https://files.pythonhosted.org/packages/2b/9f/b8f9ec8dd1417eb9da784e91e1667d58a2a4a7b7b34cf4af765ef663a7e5/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a", size = 2730998, upload-time = "2025-04-23T18:31:11.7Z" }, 485 | { url = "https://files.pythonhosted.org/packages/47/bc/cd720e078576bdb8255d5032c5d63ee5c0bf4b7173dd955185a1d658c456/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef", size = 2003200, upload-time = "2025-04-23T18:31:13.536Z" }, 486 | { url = "https://files.pythonhosted.org/packages/ca/22/3602b895ee2cd29d11a2b349372446ae9727c32e78a94b3d588a40fdf187/pydantic_core-2.33.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e", size = 2113890, upload-time = "2025-04-23T18:31:15.011Z" }, 487 | { url = "https://files.pythonhosted.org/packages/ff/e6/e3c5908c03cf00d629eb38393a98fccc38ee0ce8ecce32f69fc7d7b558a7/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d", size = 2073359, upload-time = "2025-04-23T18:31:16.393Z" }, 488 | { url = "https://files.pythonhosted.org/packages/12/e7/6a36a07c59ebefc8777d1ffdaf5ae71b06b21952582e4b07eba88a421c79/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30", size = 2245883, upload-time = "2025-04-23T18:31:17.892Z" }, 489 | { url = "https://files.pythonhosted.org/packages/16/3f/59b3187aaa6cc0c1e6616e8045b284de2b6a87b027cce2ffcea073adf1d2/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf", size = 2241074, upload-time = "2025-04-23T18:31:19.205Z" }, 490 | { url = "https://files.pythonhosted.org/packages/e0/ed/55532bb88f674d5d8f67ab121a2a13c385df382de2a1677f30ad385f7438/pydantic_core-2.33.2-cp311-cp311-win32.whl", hash = "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51", size = 1910538, upload-time = "2025-04-23T18:31:20.541Z" }, 491 | { url = "https://files.pythonhosted.org/packages/fe/1b/25b7cccd4519c0b23c2dd636ad39d381abf113085ce4f7bec2b0dc755eb1/pydantic_core-2.33.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab", size = 1952909, upload-time = "2025-04-23T18:31:22.371Z" }, 492 | { url = "https://files.pythonhosted.org/packages/49/a9/d809358e49126438055884c4366a1f6227f0f84f635a9014e2deb9b9de54/pydantic_core-2.33.2-cp311-cp311-win_arm64.whl", hash = "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65", size = 1897786, upload-time = "2025-04-23T18:31:24.161Z" }, 493 | { url = "https://files.pythonhosted.org/packages/18/8a/2b41c97f554ec8c71f2a8a5f85cb56a8b0956addfe8b0efb5b3d77e8bdc3/pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc", size = 2009000, upload-time = "2025-04-23T18:31:25.863Z" }, 494 | { url = "https://files.pythonhosted.org/packages/a1/02/6224312aacb3c8ecbaa959897af57181fb6cf3a3d7917fd44d0f2917e6f2/pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7", size = 1847996, upload-time = "2025-04-23T18:31:27.341Z" }, 495 | { url = "https://files.pythonhosted.org/packages/d6/46/6dcdf084a523dbe0a0be59d054734b86a981726f221f4562aed313dbcb49/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025", size = 1880957, upload-time = "2025-04-23T18:31:28.956Z" }, 496 | { url = "https://files.pythonhosted.org/packages/ec/6b/1ec2c03837ac00886ba8160ce041ce4e325b41d06a034adbef11339ae422/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011", size = 1964199, upload-time = "2025-04-23T18:31:31.025Z" }, 497 | { url = "https://files.pythonhosted.org/packages/2d/1d/6bf34d6adb9debd9136bd197ca72642203ce9aaaa85cfcbfcf20f9696e83/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f", size = 2120296, upload-time = "2025-04-23T18:31:32.514Z" }, 498 | { url = "https://files.pythonhosted.org/packages/e0/94/2bd0aaf5a591e974b32a9f7123f16637776c304471a0ab33cf263cf5591a/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88", size = 2676109, upload-time = "2025-04-23T18:31:33.958Z" }, 499 | { url = "https://files.pythonhosted.org/packages/f9/41/4b043778cf9c4285d59742281a769eac371b9e47e35f98ad321349cc5d61/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1", size = 2002028, upload-time = "2025-04-23T18:31:39.095Z" }, 500 | { url = "https://files.pythonhosted.org/packages/cb/d5/7bb781bf2748ce3d03af04d5c969fa1308880e1dca35a9bd94e1a96a922e/pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b", size = 2100044, upload-time = "2025-04-23T18:31:41.034Z" }, 501 | { url = "https://files.pythonhosted.org/packages/fe/36/def5e53e1eb0ad896785702a5bbfd25eed546cdcf4087ad285021a90ed53/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1", size = 2058881, upload-time = "2025-04-23T18:31:42.757Z" }, 502 | { url = "https://files.pythonhosted.org/packages/01/6c/57f8d70b2ee57fc3dc8b9610315949837fa8c11d86927b9bb044f8705419/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6", size = 2227034, upload-time = "2025-04-23T18:31:44.304Z" }, 503 | { url = "https://files.pythonhosted.org/packages/27/b9/9c17f0396a82b3d5cbea4c24d742083422639e7bb1d5bf600e12cb176a13/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea", size = 2234187, upload-time = "2025-04-23T18:31:45.891Z" }, 504 | { url = "https://files.pythonhosted.org/packages/b0/6a/adf5734ffd52bf86d865093ad70b2ce543415e0e356f6cacabbc0d9ad910/pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290", size = 1892628, upload-time = "2025-04-23T18:31:47.819Z" }, 505 | { url = "https://files.pythonhosted.org/packages/43/e4/5479fecb3606c1368d496a825d8411e126133c41224c1e7238be58b87d7e/pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2", size = 1955866, upload-time = "2025-04-23T18:31:49.635Z" }, 506 | { url = "https://files.pythonhosted.org/packages/0d/24/8b11e8b3e2be9dd82df4b11408a67c61bb4dc4f8e11b5b0fc888b38118b5/pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab", size = 1888894, upload-time = "2025-04-23T18:31:51.609Z" }, 507 | { url = "https://files.pythonhosted.org/packages/46/8c/99040727b41f56616573a28771b1bfa08a3d3fe74d3d513f01251f79f172/pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", size = 2015688, upload-time = "2025-04-23T18:31:53.175Z" }, 508 | { url = "https://files.pythonhosted.org/packages/3a/cc/5999d1eb705a6cefc31f0b4a90e9f7fc400539b1a1030529700cc1b51838/pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", size = 1844808, upload-time = "2025-04-23T18:31:54.79Z" }, 509 | { url = "https://files.pythonhosted.org/packages/6f/5e/a0a7b8885c98889a18b6e376f344da1ef323d270b44edf8174d6bce4d622/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", size = 1885580, upload-time = "2025-04-23T18:31:57.393Z" }, 510 | { url = "https://files.pythonhosted.org/packages/3b/2a/953581f343c7d11a304581156618c3f592435523dd9d79865903272c256a/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", size = 1973859, upload-time = "2025-04-23T18:31:59.065Z" }, 511 | { url = "https://files.pythonhosted.org/packages/e6/55/f1a813904771c03a3f97f676c62cca0c0a4138654107c1b61f19c644868b/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", size = 2120810, upload-time = "2025-04-23T18:32:00.78Z" }, 512 | { url = "https://files.pythonhosted.org/packages/aa/c3/053389835a996e18853ba107a63caae0b9deb4a276c6b472931ea9ae6e48/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", size = 2676498, upload-time = "2025-04-23T18:32:02.418Z" }, 513 | { url = "https://files.pythonhosted.org/packages/eb/3c/f4abd740877a35abade05e437245b192f9d0ffb48bbbbd708df33d3cda37/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", size = 2000611, upload-time = "2025-04-23T18:32:04.152Z" }, 514 | { url = "https://files.pythonhosted.org/packages/59/a7/63ef2fed1837d1121a894d0ce88439fe3e3b3e48c7543b2a4479eb99c2bd/pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", size = 2107924, upload-time = "2025-04-23T18:32:06.129Z" }, 515 | { url = "https://files.pythonhosted.org/packages/04/8f/2551964ef045669801675f1cfc3b0d74147f4901c3ffa42be2ddb1f0efc4/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", size = 2063196, upload-time = "2025-04-23T18:32:08.178Z" }, 516 | { url = "https://files.pythonhosted.org/packages/26/bd/d9602777e77fc6dbb0c7db9ad356e9a985825547dce5ad1d30ee04903918/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", size = 2236389, upload-time = "2025-04-23T18:32:10.242Z" }, 517 | { url = "https://files.pythonhosted.org/packages/42/db/0e950daa7e2230423ab342ae918a794964b053bec24ba8af013fc7c94846/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", size = 2239223, upload-time = "2025-04-23T18:32:12.382Z" }, 518 | { url = "https://files.pythonhosted.org/packages/58/4d/4f937099c545a8a17eb52cb67fe0447fd9a373b348ccfa9a87f141eeb00f/pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", size = 1900473, upload-time = "2025-04-23T18:32:14.034Z" }, 519 | { url = "https://files.pythonhosted.org/packages/a0/75/4a0a9bac998d78d889def5e4ef2b065acba8cae8c93696906c3a91f310ca/pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", size = 1955269, upload-time = "2025-04-23T18:32:15.783Z" }, 520 | { url = "https://files.pythonhosted.org/packages/f9/86/1beda0576969592f1497b4ce8e7bc8cbdf614c352426271b1b10d5f0aa64/pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", size = 1893921, upload-time = "2025-04-23T18:32:18.473Z" }, 521 | { url = "https://files.pythonhosted.org/packages/a4/7d/e09391c2eebeab681df2b74bfe6c43422fffede8dc74187b2b0bf6fd7571/pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", size = 1806162, upload-time = "2025-04-23T18:32:20.188Z" }, 522 | { url = "https://files.pythonhosted.org/packages/f1/3d/847b6b1fed9f8ed3bb95a9ad04fbd0b212e832d4f0f50ff4d9ee5a9f15cf/pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", size = 1981560, upload-time = "2025-04-23T18:32:22.354Z" }, 523 | { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777, upload-time = "2025-04-23T18:32:25.088Z" }, 524 | { url = "https://files.pythonhosted.org/packages/53/ea/bbe9095cdd771987d13c82d104a9c8559ae9aec1e29f139e286fd2e9256e/pydantic_core-2.33.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a2b911a5b90e0374d03813674bf0a5fbbb7741570dcd4b4e85a2e48d17def29d", size = 2028677, upload-time = "2025-04-23T18:32:27.227Z" }, 525 | { url = "https://files.pythonhosted.org/packages/49/1d/4ac5ed228078737d457a609013e8f7edc64adc37b91d619ea965758369e5/pydantic_core-2.33.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6fa6dfc3e4d1f734a34710f391ae822e0a8eb8559a85c6979e14e65ee6ba2954", size = 1864735, upload-time = "2025-04-23T18:32:29.019Z" }, 526 | { url = "https://files.pythonhosted.org/packages/23/9a/2e70d6388d7cda488ae38f57bc2f7b03ee442fbcf0d75d848304ac7e405b/pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c54c939ee22dc8e2d545da79fc5381f1c020d6d3141d3bd747eab59164dc89fb", size = 1898467, upload-time = "2025-04-23T18:32:31.119Z" }, 527 | { url = "https://files.pythonhosted.org/packages/ff/2e/1568934feb43370c1ffb78a77f0baaa5a8b6897513e7a91051af707ffdc4/pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:53a57d2ed685940a504248187d5685e49eb5eef0f696853647bf37c418c538f7", size = 1983041, upload-time = "2025-04-23T18:32:33.655Z" }, 528 | { url = "https://files.pythonhosted.org/packages/01/1a/1a1118f38ab64eac2f6269eb8c120ab915be30e387bb561e3af904b12499/pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09fb9dd6571aacd023fe6aaca316bd01cf60ab27240d7eb39ebd66a3a15293b4", size = 2136503, upload-time = "2025-04-23T18:32:35.519Z" }, 529 | { url = "https://files.pythonhosted.org/packages/5c/da/44754d1d7ae0f22d6d3ce6c6b1486fc07ac2c524ed8f6eca636e2e1ee49b/pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0e6116757f7959a712db11f3e9c0a99ade00a5bbedae83cb801985aa154f071b", size = 2736079, upload-time = "2025-04-23T18:32:37.659Z" }, 530 | { url = "https://files.pythonhosted.org/packages/4d/98/f43cd89172220ec5aa86654967b22d862146bc4d736b1350b4c41e7c9c03/pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d55ab81c57b8ff8548c3e4947f119551253f4e3787a7bbc0b6b3ca47498a9d3", size = 2006508, upload-time = "2025-04-23T18:32:39.637Z" }, 531 | { url = "https://files.pythonhosted.org/packages/2b/cc/f77e8e242171d2158309f830f7d5d07e0531b756106f36bc18712dc439df/pydantic_core-2.33.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c20c462aa4434b33a2661701b861604913f912254e441ab8d78d30485736115a", size = 2113693, upload-time = "2025-04-23T18:32:41.818Z" }, 532 | { url = "https://files.pythonhosted.org/packages/54/7a/7be6a7bd43e0a47c147ba7fbf124fe8aaf1200bc587da925509641113b2d/pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:44857c3227d3fb5e753d5fe4a3420d6376fa594b07b621e220cd93703fe21782", size = 2074224, upload-time = "2025-04-23T18:32:44.033Z" }, 533 | { url = "https://files.pythonhosted.org/packages/2a/07/31cf8fadffbb03be1cb520850e00a8490c0927ec456e8293cafda0726184/pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:eb9b459ca4df0e5c87deb59d37377461a538852765293f9e6ee834f0435a93b9", size = 2245403, upload-time = "2025-04-23T18:32:45.836Z" }, 534 | { url = "https://files.pythonhosted.org/packages/b6/8d/bbaf4c6721b668d44f01861f297eb01c9b35f612f6b8e14173cb204e6240/pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9fcd347d2cc5c23b06de6d3b7b8275be558a0c90549495c699e379a80bf8379e", size = 2242331, upload-time = "2025-04-23T18:32:47.618Z" }, 535 | { url = "https://files.pythonhosted.org/packages/bb/93/3cc157026bca8f5006250e74515119fcaa6d6858aceee8f67ab6dc548c16/pydantic_core-2.33.2-cp39-cp39-win32.whl", hash = "sha256:83aa99b1285bc8f038941ddf598501a86f1536789740991d7d8756e34f1e74d9", size = 1910571, upload-time = "2025-04-23T18:32:49.401Z" }, 536 | { url = "https://files.pythonhosted.org/packages/5b/90/7edc3b2a0d9f0dda8806c04e511a67b0b7a41d2187e2003673a996fb4310/pydantic_core-2.33.2-cp39-cp39-win_amd64.whl", hash = "sha256:f481959862f57f29601ccced557cc2e817bce7533ab8e01a797a48b49c9692b3", size = 1956504, upload-time = "2025-04-23T18:32:51.287Z" }, 537 | { url = "https://files.pythonhosted.org/packages/30/68/373d55e58b7e83ce371691f6eaa7175e3a24b956c44628eb25d7da007917/pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5c4aa4e82353f65e548c476b37e64189783aa5384903bfea4f41580f255fddfa", size = 2023982, upload-time = "2025-04-23T18:32:53.14Z" }, 538 | { url = "https://files.pythonhosted.org/packages/a4/16/145f54ac08c96a63d8ed6442f9dec17b2773d19920b627b18d4f10a061ea/pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d946c8bf0d5c24bf4fe333af284c59a19358aa3ec18cb3dc4370080da1e8ad29", size = 1858412, upload-time = "2025-04-23T18:32:55.52Z" }, 539 | { url = "https://files.pythonhosted.org/packages/41/b1/c6dc6c3e2de4516c0bb2c46f6a373b91b5660312342a0cf5826e38ad82fa/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87b31b6846e361ef83fedb187bb5b4372d0da3f7e28d85415efa92d6125d6e6d", size = 1892749, upload-time = "2025-04-23T18:32:57.546Z" }, 540 | { url = "https://files.pythonhosted.org/packages/12/73/8cd57e20afba760b21b742106f9dbdfa6697f1570b189c7457a1af4cd8a0/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa9d91b338f2df0508606f7009fde642391425189bba6d8c653afd80fd6bb64e", size = 2067527, upload-time = "2025-04-23T18:32:59.771Z" }, 541 | { url = "https://files.pythonhosted.org/packages/e3/d5/0bb5d988cc019b3cba4a78f2d4b3854427fc47ee8ec8e9eaabf787da239c/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2058a32994f1fde4ca0480ab9d1e75a0e8c87c22b53a3ae66554f9af78f2fe8c", size = 2108225, upload-time = "2025-04-23T18:33:04.51Z" }, 542 | { url = "https://files.pythonhosted.org/packages/f1/c5/00c02d1571913d496aabf146106ad8239dc132485ee22efe08085084ff7c/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:0e03262ab796d986f978f79c943fc5f620381be7287148b8010b4097f79a39ec", size = 2069490, upload-time = "2025-04-23T18:33:06.391Z" }, 543 | { url = "https://files.pythonhosted.org/packages/22/a8/dccc38768274d3ed3a59b5d06f59ccb845778687652daa71df0cab4040d7/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1a8695a8d00c73e50bff9dfda4d540b7dee29ff9b8053e38380426a85ef10052", size = 2237525, upload-time = "2025-04-23T18:33:08.44Z" }, 544 | { url = "https://files.pythonhosted.org/packages/d4/e7/4f98c0b125dda7cf7ccd14ba936218397b44f50a56dd8c16a3091df116c3/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:fa754d1850735a0b0e03bcffd9d4b4343eb417e47196e4485d9cca326073a42c", size = 2238446, upload-time = "2025-04-23T18:33:10.313Z" }, 545 | { url = "https://files.pythonhosted.org/packages/ce/91/2ec36480fdb0b783cd9ef6795753c1dea13882f2e68e73bce76ae8c21e6a/pydantic_core-2.33.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a11c8d26a50bfab49002947d3d237abe4d9e4b5bdc8846a63537b6488e197808", size = 2066678, upload-time = "2025-04-23T18:33:12.224Z" }, 546 | { url = "https://files.pythonhosted.org/packages/7b/27/d4ae6487d73948d6f20dddcd94be4ea43e74349b56eba82e9bdee2d7494c/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8", size = 2025200, upload-time = "2025-04-23T18:33:14.199Z" }, 547 | { url = "https://files.pythonhosted.org/packages/f1/b8/b3cb95375f05d33801024079b9392a5ab45267a63400bf1866e7ce0f0de4/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593", size = 1859123, upload-time = "2025-04-23T18:33:16.555Z" }, 548 | { url = "https://files.pythonhosted.org/packages/05/bc/0d0b5adeda59a261cd30a1235a445bf55c7e46ae44aea28f7bd6ed46e091/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612", size = 1892852, upload-time = "2025-04-23T18:33:18.513Z" }, 549 | { url = "https://files.pythonhosted.org/packages/3e/11/d37bdebbda2e449cb3f519f6ce950927b56d62f0b84fd9cb9e372a26a3d5/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7", size = 2067484, upload-time = "2025-04-23T18:33:20.475Z" }, 550 | { url = "https://files.pythonhosted.org/packages/8c/55/1f95f0a05ce72ecb02a8a8a1c3be0579bbc29b1d5ab68f1378b7bebc5057/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e", size = 2108896, upload-time = "2025-04-23T18:33:22.501Z" }, 551 | { url = "https://files.pythonhosted.org/packages/53/89/2b2de6c81fa131f423246a9109d7b2a375e83968ad0800d6e57d0574629b/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8", size = 2069475, upload-time = "2025-04-23T18:33:24.528Z" }, 552 | { url = "https://files.pythonhosted.org/packages/b8/e9/1f7efbe20d0b2b10f6718944b5d8ece9152390904f29a78e68d4e7961159/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf", size = 2239013, upload-time = "2025-04-23T18:33:26.621Z" }, 553 | { url = "https://files.pythonhosted.org/packages/3c/b2/5309c905a93811524a49b4e031e9851a6b00ff0fb668794472ea7746b448/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb", size = 2238715, upload-time = "2025-04-23T18:33:28.656Z" }, 554 | { url = "https://files.pythonhosted.org/packages/32/56/8a7ca5d2cd2cda1d245d34b1c9a942920a718082ae8e54e5f3e5a58b7add/pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1", size = 2066757, upload-time = "2025-04-23T18:33:30.645Z" }, 555 | { url = "https://files.pythonhosted.org/packages/08/98/dbf3fdfabaf81cda5622154fda78ea9965ac467e3239078e0dcd6df159e7/pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:87acbfcf8e90ca885206e98359d7dca4bcbb35abdc0ff66672a293e1d7a19101", size = 2024034, upload-time = "2025-04-23T18:33:32.843Z" }, 556 | { url = "https://files.pythonhosted.org/packages/8d/99/7810aa9256e7f2ccd492590f86b79d370df1e9292f1f80b000b6a75bd2fb/pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7f92c15cd1e97d4b12acd1cc9004fa092578acfa57b67ad5e43a197175d01a64", size = 1858578, upload-time = "2025-04-23T18:33:34.912Z" }, 557 | { url = "https://files.pythonhosted.org/packages/d8/60/bc06fa9027c7006cc6dd21e48dbf39076dc39d9abbaf718a1604973a9670/pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3f26877a748dc4251cfcfda9dfb5f13fcb034f5308388066bcfe9031b63ae7d", size = 1892858, upload-time = "2025-04-23T18:33:36.933Z" }, 558 | { url = "https://files.pythonhosted.org/packages/f2/40/9d03997d9518816c68b4dfccb88969756b9146031b61cd37f781c74c9b6a/pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac89aea9af8cd672fa7b510e7b8c33b0bba9a43186680550ccf23020f32d535", size = 2068498, upload-time = "2025-04-23T18:33:38.997Z" }, 559 | { url = "https://files.pythonhosted.org/packages/d8/62/d490198d05d2d86672dc269f52579cad7261ced64c2df213d5c16e0aecb1/pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:970919794d126ba8645f3837ab6046fb4e72bbc057b3709144066204c19a455d", size = 2108428, upload-time = "2025-04-23T18:33:41.18Z" }, 560 | { url = "https://files.pythonhosted.org/packages/9a/ec/4cd215534fd10b8549015f12ea650a1a973da20ce46430b68fc3185573e8/pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3eb3fe62804e8f859c49ed20a8451342de53ed764150cb14ca71357c765dc2a6", size = 2069854, upload-time = "2025-04-23T18:33:43.446Z" }, 561 | { url = "https://files.pythonhosted.org/packages/1a/1a/abbd63d47e1d9b0d632fee6bb15785d0889c8a6e0a6c3b5a8e28ac1ec5d2/pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:3abcd9392a36025e3bd55f9bd38d908bd17962cc49bc6da8e7e96285336e2bca", size = 2237859, upload-time = "2025-04-23T18:33:45.56Z" }, 562 | { url = "https://files.pythonhosted.org/packages/80/1c/fa883643429908b1c90598fd2642af8839efd1d835b65af1f75fba4d94fe/pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:3a1c81334778f9e3af2f8aeb7a960736e5cab1dfebfb26aabca09afd2906c039", size = 2239059, upload-time = "2025-04-23T18:33:47.735Z" }, 563 | { url = "https://files.pythonhosted.org/packages/d4/29/3cade8a924a61f60ccfa10842f75eb12787e1440e2b8660ceffeb26685e7/pydantic_core-2.33.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2807668ba86cb38c6817ad9bc66215ab8584d1d304030ce4f0887336f28a5e27", size = 2066661, upload-time = "2025-04-23T18:33:49.995Z" }, 564 | ] 565 | 566 | [[package]] 567 | name = "pygments" 568 | version = "2.19.2" 569 | source = { registry = "https://pypi.org/simple" } 570 | sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } 571 | wheels = [ 572 | { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, 573 | ] 574 | 575 | [[package]] 576 | name = "pytest" 577 | version = "8.4.1" 578 | source = { registry = "https://pypi.org/simple" } 579 | dependencies = [ 580 | { name = "colorama", marker = "sys_platform == 'win32'" }, 581 | { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, 582 | { name = "iniconfig" }, 583 | { name = "packaging" }, 584 | { name = "pluggy" }, 585 | { name = "pygments" }, 586 | { name = "tomli", marker = "python_full_version < '3.11'" }, 587 | ] 588 | sdist = { url = "https://files.pythonhosted.org/packages/08/ba/45911d754e8eba3d5a841a5ce61a65a685ff1798421ac054f85aa8747dfb/pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c", size = 1517714, upload-time = "2025-06-18T05:48:06.109Z" } 589 | wheels = [ 590 | { url = "https://files.pythonhosted.org/packages/29/16/c8a903f4c4dffe7a12843191437d7cd8e32751d5de349d45d3fe69544e87/pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7", size = 365474, upload-time = "2025-06-18T05:48:03.955Z" }, 591 | ] 592 | 593 | [[package]] 594 | name = "pytest-asyncio" 595 | version = "1.1.0" 596 | source = { registry = "https://pypi.org/simple" } 597 | dependencies = [ 598 | { name = "backports-asyncio-runner", marker = "python_full_version < '3.11'" }, 599 | { name = "pytest" }, 600 | { name = "typing-extensions", marker = "python_full_version < '3.10'" }, 601 | ] 602 | sdist = { url = "https://files.pythonhosted.org/packages/4e/51/f8794af39eeb870e87a8c8068642fc07bce0c854d6865d7dd0f2a9d338c2/pytest_asyncio-1.1.0.tar.gz", hash = "sha256:796aa822981e01b68c12e4827b8697108f7205020f24b5793b3c41555dab68ea", size = 46652, upload-time = "2025-07-16T04:29:26.393Z" } 603 | wheels = [ 604 | { url = "https://files.pythonhosted.org/packages/c7/9d/bf86eddabf8c6c9cb1ea9a869d6873b46f105a5d292d3a6f7071f5b07935/pytest_asyncio-1.1.0-py3-none-any.whl", hash = "sha256:5fe2d69607b0bd75c656d1211f969cadba035030156745ee09e7d71740e58ecf", size = 15157, upload-time = "2025-07-16T04:29:24.929Z" }, 605 | ] 606 | 607 | [[package]] 608 | name = "pytest-cov" 609 | version = "6.2.1" 610 | source = { registry = "https://pypi.org/simple" } 611 | dependencies = [ 612 | { name = "coverage", extra = ["toml"] }, 613 | { name = "pluggy" }, 614 | { name = "pytest" }, 615 | ] 616 | sdist = { url = "https://files.pythonhosted.org/packages/18/99/668cade231f434aaa59bbfbf49469068d2ddd945000621d3d165d2e7dd7b/pytest_cov-6.2.1.tar.gz", hash = "sha256:25cc6cc0a5358204b8108ecedc51a9b57b34cc6b8c967cc2c01a4e00d8a67da2", size = 69432, upload-time = "2025-06-12T10:47:47.684Z" } 617 | wheels = [ 618 | { url = "https://files.pythonhosted.org/packages/bc/16/4ea354101abb1287856baa4af2732be351c7bee728065aed451b678153fd/pytest_cov-6.2.1-py3-none-any.whl", hash = "sha256:f5bc4c23f42f1cdd23c70b1dab1bbaef4fc505ba950d53e0081d0730dd7e86d5", size = 24644, upload-time = "2025-06-12T10:47:45.932Z" }, 619 | ] 620 | 621 | [[package]] 622 | name = "pytest-mock" 623 | version = "3.14.1" 624 | source = { registry = "https://pypi.org/simple" } 625 | dependencies = [ 626 | { name = "pytest" }, 627 | ] 628 | sdist = { url = "https://files.pythonhosted.org/packages/71/28/67172c96ba684058a4d24ffe144d64783d2a270d0af0d9e792737bddc75c/pytest_mock-3.14.1.tar.gz", hash = "sha256:159e9edac4c451ce77a5cdb9fc5d1100708d2dd4ba3c3df572f14097351af80e", size = 33241, upload-time = "2025-05-26T13:58:45.167Z" } 629 | wheels = [ 630 | { url = "https://files.pythonhosted.org/packages/b2/05/77b60e520511c53d1c1ca75f1930c7dd8e971d0c4379b7f4b3f9644685ba/pytest_mock-3.14.1-py3-none-any.whl", hash = "sha256:178aefcd11307d874b4cd3100344e7e2d888d9791a6a1d9bfe90fbc1b74fd1d0", size = 9923, upload-time = "2025-05-26T13:58:43.487Z" }, 631 | ] 632 | 633 | [[package]] 634 | name = "python-dotenv" 635 | version = "1.1.1" 636 | source = { registry = "https://pypi.org/simple" } 637 | sdist = { url = "https://files.pythonhosted.org/packages/f6/b0/4bc07ccd3572a2f9df7e6782f52b0c6c90dcbb803ac4a167702d7d0dfe1e/python_dotenv-1.1.1.tar.gz", hash = "sha256:a8a6399716257f45be6a007360200409fce5cda2661e3dec71d23dc15f6189ab", size = 41978, upload-time = "2025-06-24T04:21:07.341Z" } 638 | wheels = [ 639 | { url = "https://files.pythonhosted.org/packages/5f/ed/539768cf28c661b5b068d66d96a2f155c4971a5d55684a514c1a0e0dec2f/python_dotenv-1.1.1-py3-none-any.whl", hash = "sha256:31f23644fe2602f88ff55e1f5c79ba497e01224ee7737937930c448e4d0e24dc", size = 20556, upload-time = "2025-06-24T04:21:06.073Z" }, 640 | ] 641 | 642 | [[package]] 643 | name = "pyyaml" 644 | version = "6.0.2" 645 | source = { registry = "https://pypi.org/simple" } 646 | sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload-time = "2024-08-06T20:33:50.674Z" } 647 | wheels = [ 648 | { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199, upload-time = "2024-08-06T20:31:40.178Z" }, 649 | { url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758, upload-time = "2024-08-06T20:31:42.173Z" }, 650 | { url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463, upload-time = "2024-08-06T20:31:44.263Z" }, 651 | { url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280, upload-time = "2024-08-06T20:31:50.199Z" }, 652 | { url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239, upload-time = "2024-08-06T20:31:52.292Z" }, 653 | { url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802, upload-time = "2024-08-06T20:31:53.836Z" }, 654 | { url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527, upload-time = "2024-08-06T20:31:55.565Z" }, 655 | { url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052, upload-time = "2024-08-06T20:31:56.914Z" }, 656 | { url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774, upload-time = "2024-08-06T20:31:58.304Z" }, 657 | { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612, upload-time = "2024-08-06T20:32:03.408Z" }, 658 | { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040, upload-time = "2024-08-06T20:32:04.926Z" }, 659 | { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829, upload-time = "2024-08-06T20:32:06.459Z" }, 660 | { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167, upload-time = "2024-08-06T20:32:08.338Z" }, 661 | { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952, upload-time = "2024-08-06T20:32:14.124Z" }, 662 | { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301, upload-time = "2024-08-06T20:32:16.17Z" }, 663 | { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638, upload-time = "2024-08-06T20:32:18.555Z" }, 664 | { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850, upload-time = "2024-08-06T20:32:19.889Z" }, 665 | { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980, upload-time = "2024-08-06T20:32:21.273Z" }, 666 | { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873, upload-time = "2024-08-06T20:32:25.131Z" }, 667 | { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302, upload-time = "2024-08-06T20:32:26.511Z" }, 668 | { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154, upload-time = "2024-08-06T20:32:28.363Z" }, 669 | { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223, upload-time = "2024-08-06T20:32:30.058Z" }, 670 | { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542, upload-time = "2024-08-06T20:32:31.881Z" }, 671 | { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164, upload-time = "2024-08-06T20:32:37.083Z" }, 672 | { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611, upload-time = "2024-08-06T20:32:38.898Z" }, 673 | { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591, upload-time = "2024-08-06T20:32:40.241Z" }, 674 | { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338, upload-time = "2024-08-06T20:32:41.93Z" }, 675 | { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309, upload-time = "2024-08-06T20:32:43.4Z" }, 676 | { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679, upload-time = "2024-08-06T20:32:44.801Z" }, 677 | { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428, upload-time = "2024-08-06T20:32:46.432Z" }, 678 | { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361, upload-time = "2024-08-06T20:32:51.188Z" }, 679 | { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523, upload-time = "2024-08-06T20:32:53.019Z" }, 680 | { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660, upload-time = "2024-08-06T20:32:54.708Z" }, 681 | { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597, upload-time = "2024-08-06T20:32:56.985Z" }, 682 | { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527, upload-time = "2024-08-06T20:33:03.001Z" }, 683 | { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446, upload-time = "2024-08-06T20:33:04.33Z" }, 684 | { url = "https://files.pythonhosted.org/packages/65/d8/b7a1db13636d7fb7d4ff431593c510c8b8fca920ade06ca8ef20015493c5/PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d", size = 184777, upload-time = "2024-08-06T20:33:25.896Z" }, 685 | { url = "https://files.pythonhosted.org/packages/0a/02/6ec546cd45143fdf9840b2c6be8d875116a64076218b61d68e12548e5839/PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f", size = 172318, upload-time = "2024-08-06T20:33:27.212Z" }, 686 | { url = "https://files.pythonhosted.org/packages/0e/9a/8cc68be846c972bda34f6c2a93abb644fb2476f4dcc924d52175786932c9/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290", size = 720891, upload-time = "2024-08-06T20:33:28.974Z" }, 687 | { url = "https://files.pythonhosted.org/packages/e9/6c/6e1b7f40181bc4805e2e07f4abc10a88ce4648e7e95ff1abe4ae4014a9b2/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12", size = 722614, upload-time = "2024-08-06T20:33:34.157Z" }, 688 | { url = "https://files.pythonhosted.org/packages/3d/32/e7bd8535d22ea2874cef6a81021ba019474ace0d13a4819c2a4bce79bd6a/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19", size = 737360, upload-time = "2024-08-06T20:33:35.84Z" }, 689 | { url = "https://files.pythonhosted.org/packages/d7/12/7322c1e30b9be969670b672573d45479edef72c9a0deac3bb2868f5d7469/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e", size = 699006, upload-time = "2024-08-06T20:33:37.501Z" }, 690 | { url = "https://files.pythonhosted.org/packages/82/72/04fcad41ca56491995076630c3ec1e834be241664c0c09a64c9a2589b507/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725", size = 723577, upload-time = "2024-08-06T20:33:39.389Z" }, 691 | { url = "https://files.pythonhosted.org/packages/ed/5e/46168b1f2757f1fcd442bc3029cd8767d88a98c9c05770d8b420948743bb/PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631", size = 144593, upload-time = "2024-08-06T20:33:46.63Z" }, 692 | { url = "https://files.pythonhosted.org/packages/19/87/5124b1c1f2412bb95c59ec481eaf936cd32f0fe2a7b16b97b81c4c017a6a/PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8", size = 162312, upload-time = "2024-08-06T20:33:49.073Z" }, 693 | ] 694 | 695 | [[package]] 696 | name = "rich" 697 | version = "14.1.0" 698 | source = { registry = "https://pypi.org/simple" } 699 | dependencies = [ 700 | { name = "markdown-it-py", version = "3.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, 701 | { name = "markdown-it-py", version = "4.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, 702 | { name = "pygments" }, 703 | ] 704 | sdist = { url = "https://files.pythonhosted.org/packages/fe/75/af448d8e52bf1d8fa6a9d089ca6c07ff4453d86c65c145d0a300bb073b9b/rich-14.1.0.tar.gz", hash = "sha256:e497a48b844b0320d45007cdebfeaeed8db2a4f4bcf49f15e455cfc4af11eaa8", size = 224441, upload-time = "2025-07-25T07:32:58.125Z" } 705 | wheels = [ 706 | { url = "https://files.pythonhosted.org/packages/e3/30/3c4d035596d3cf444529e0b2953ad0466f6049528a879d27534700580395/rich-14.1.0-py3-none-any.whl", hash = "sha256:536f5f1785986d6dbdea3c75205c473f970777b4a0d6c6dd1b696aa05a3fa04f", size = 243368, upload-time = "2025-07-25T07:32:56.73Z" }, 707 | ] 708 | 709 | [[package]] 710 | name = "sniffio" 711 | version = "1.3.1" 712 | source = { registry = "https://pypi.org/simple" } 713 | sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } 714 | wheels = [ 715 | { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, 716 | ] 717 | 718 | [[package]] 719 | name = "tomli" 720 | version = "2.2.1" 721 | source = { registry = "https://pypi.org/simple" } 722 | sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175, upload-time = "2024-11-27T22:38:36.873Z" } 723 | wheels = [ 724 | { url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077, upload-time = "2024-11-27T22:37:54.956Z" }, 725 | { url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429, upload-time = "2024-11-27T22:37:56.698Z" }, 726 | { url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067, upload-time = "2024-11-27T22:37:57.63Z" }, 727 | { url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030, upload-time = "2024-11-27T22:37:59.344Z" }, 728 | { url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898, upload-time = "2024-11-27T22:38:00.429Z" }, 729 | { url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894, upload-time = "2024-11-27T22:38:02.094Z" }, 730 | { url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319, upload-time = "2024-11-27T22:38:03.206Z" }, 731 | { url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273, upload-time = "2024-11-27T22:38:04.217Z" }, 732 | { url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310, upload-time = "2024-11-27T22:38:05.908Z" }, 733 | { url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309, upload-time = "2024-11-27T22:38:06.812Z" }, 734 | { url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762, upload-time = "2024-11-27T22:38:07.731Z" }, 735 | { url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453, upload-time = "2024-11-27T22:38:09.384Z" }, 736 | { url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486, upload-time = "2024-11-27T22:38:10.329Z" }, 737 | { url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349, upload-time = "2024-11-27T22:38:11.443Z" }, 738 | { url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159, upload-time = "2024-11-27T22:38:13.099Z" }, 739 | { url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243, upload-time = "2024-11-27T22:38:14.766Z" }, 740 | { url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645, upload-time = "2024-11-27T22:38:15.843Z" }, 741 | { url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584, upload-time = "2024-11-27T22:38:17.645Z" }, 742 | { url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875, upload-time = "2024-11-27T22:38:19.159Z" }, 743 | { url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418, upload-time = "2024-11-27T22:38:20.064Z" }, 744 | { url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708, upload-time = "2024-11-27T22:38:21.659Z" }, 745 | { url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582, upload-time = "2024-11-27T22:38:22.693Z" }, 746 | { url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543, upload-time = "2024-11-27T22:38:24.367Z" }, 747 | { url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691, upload-time = "2024-11-27T22:38:26.081Z" }, 748 | { url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170, upload-time = "2024-11-27T22:38:27.921Z" }, 749 | { url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530, upload-time = "2024-11-27T22:38:29.591Z" }, 750 | { url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666, upload-time = "2024-11-27T22:38:30.639Z" }, 751 | { url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954, upload-time = "2024-11-27T22:38:31.702Z" }, 752 | { url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724, upload-time = "2024-11-27T22:38:32.837Z" }, 753 | { url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383, upload-time = "2024-11-27T22:38:34.455Z" }, 754 | { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257, upload-time = "2024-11-27T22:38:35.385Z" }, 755 | ] 756 | 757 | [[package]] 758 | name = "typing-extensions" 759 | version = "4.14.1" 760 | source = { registry = "https://pypi.org/simple" } 761 | sdist = { url = "https://files.pythonhosted.org/packages/98/5a/da40306b885cc8c09109dc2e1abd358d5684b1425678151cdaed4731c822/typing_extensions-4.14.1.tar.gz", hash = "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36", size = 107673, upload-time = "2025-07-04T13:28:34.16Z" } 762 | wheels = [ 763 | { url = "https://files.pythonhosted.org/packages/b5/00/d631e67a838026495268c2f6884f3711a15a9a2a96cd244fdaea53b823fb/typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76", size = 43906, upload-time = "2025-07-04T13:28:32.743Z" }, 764 | ] 765 | 766 | [[package]] 767 | name = "typing-inspection" 768 | version = "0.4.1" 769 | source = { registry = "https://pypi.org/simple" } 770 | dependencies = [ 771 | { name = "typing-extensions" }, 772 | ] 773 | sdist = { url = "https://files.pythonhosted.org/packages/f8/b1/0c11f5058406b3af7609f121aaa6b609744687f1d158b3c3a5bf4cc94238/typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28", size = 75726, upload-time = "2025-05-21T18:55:23.885Z" } 774 | wheels = [ 775 | { url = "https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", size = 14552, upload-time = "2025-05-21T18:55:22.152Z" }, 776 | ] 777 | 778 | [[package]] 779 | name = "uv" 780 | version = "0.8.17" 781 | source = { registry = "https://pypi.org/simple" } 782 | sdist = { url = "https://files.pythonhosted.org/packages/6f/4c/c270c6b8ed3e8c7fe38ea0b99df9eff09c332421b93d55a158371f75220e/uv-0.8.17.tar.gz", hash = "sha256:2afd4525a53c8ab3a11a5a15093c503d27da67e76257a649b05e4f0bc2ebb5ae", size = 3615060, upload-time = "2025-09-10T21:51:25.067Z" } 783 | wheels = [ 784 | { url = "https://files.pythonhosted.org/packages/b9/7d/bbaa45c88b2c91e02714a8a5c9e787c47e4898bddfdd268569163492ba45/uv-0.8.17-py3-none-linux_armv6l.whl", hash = "sha256:c51c9633ca93ef63c07df2443941e6264efd2819cc9faabfd9fe11899c6a0d6a", size = 20242144, upload-time = "2025-09-10T21:50:18.081Z" }, 785 | { url = "https://files.pythonhosted.org/packages/65/34/609b72034df0c62bcfb0c0ad4b11e2b55e537c0f0817588b5337d3dcca71/uv-0.8.17-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:c28fba6d7bb5c34ade2c8da5000faebe8425a287f42a043ca01ceb24ebc81590", size = 19363081, upload-time = "2025-09-10T21:50:22.731Z" }, 786 | { url = "https://files.pythonhosted.org/packages/b6/bc/9417df48f0c18a9d54c2444096e03f2f56a3534c5b869f50ac620729cbc8/uv-0.8.17-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b009f1ec9e28de00f76814ad66e35aaae82c98a0f24015de51943dcd1c2a1895", size = 17943513, upload-time = "2025-09-10T21:50:25.824Z" }, 787 | { url = "https://files.pythonhosted.org/packages/63/1c/14fd54c852fd592a2b5da4b7960f3bf4a15c7e51eb20eaddabe8c8cca32d/uv-0.8.17-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:84d56ae50ca71aec032577adf9737974554a82a94e52cee57722745656c1d383", size = 19507222, upload-time = "2025-09-10T21:50:29.237Z" }, 788 | { url = "https://files.pythonhosted.org/packages/be/47/f6a68cc310feca37c965bcbd57eb999e023d35eaeda9c9759867bf3ed232/uv-0.8.17-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:85c2140f8553b9a4387a7395dc30cd151ef94046785fe8b198f13f2c380fb39b", size = 19865652, upload-time = "2025-09-10T21:50:32.87Z" }, 789 | { url = "https://files.pythonhosted.org/packages/ab/6a/fdeb2d4a2635a6927c6d549b07177bcaf6ce15bdef58e8253e75c1b70f54/uv-0.8.17-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2076119783e4a6d3c9e25638956cb123f0eabf4d7d407d9661cdf7f84818dcb9", size = 20831760, upload-time = "2025-09-10T21:50:37.803Z" }, 790 | { url = "https://files.pythonhosted.org/packages/d0/4c/bd58b8a76015aa9ac49d6b4e1211ae1ca98a0aade0c49e1a5f645fb5cd38/uv-0.8.17-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:707a55660d302924fdbcb509e63dfec8842e19d35b69bcc17af76c25db15ad6f", size = 22209056, upload-time = "2025-09-10T21:50:41.749Z" }, 791 | { url = "https://files.pythonhosted.org/packages/7e/2e/28f59c00a2ed6532502fb1e27da9394e505fb7b41cc0274475104b43561b/uv-0.8.17-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1824b76911a14aaa9eee65ad9e180e6a4d2d7c86826232c2f28ae86aee56ed0e", size = 21871684, upload-time = "2025-09-10T21:50:45.331Z" }, 792 | { url = "https://files.pythonhosted.org/packages/5a/1d/a8a4fc08de1f767316467e7a1989bb125734b7ed9cd98ce8969386a70653/uv-0.8.17-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bb9b515cc813fb1b08f1e7592f76e437e2fb44945e53cde4fee11dee3b16d0c3", size = 21145154, upload-time = "2025-09-10T21:50:50.388Z" }, 793 | { url = "https://files.pythonhosted.org/packages/8f/35/cb47d2d07a383c07b0e5043c6fe5555f0fd79683c6d7f9760222987c8be9/uv-0.8.17-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6d30d02fb65193309fc12a20f9e1a9fab67f469d3e487a254ca1145fd06788f", size = 21106619, upload-time = "2025-09-10T21:50:54.5Z" }, 794 | { url = "https://files.pythonhosted.org/packages/6e/93/c310f0153b9dfe79bdd7f7eaef6380a8545c8939dbfc4e6bdee8f3ee7050/uv-0.8.17-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:3941cecd9a6a46d3d4505753912c9cf3e8ae5eea30b9d0813f3656210f8c5d01", size = 19777591, upload-time = "2025-09-10T21:50:57.765Z" }, 795 | { url = "https://files.pythonhosted.org/packages/6c/4f/971d3c84c2f09cf8df4536c33644e6b97e10a259d8630a0c1696c1fa6e94/uv-0.8.17-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:cd0ad366cfe4cbe9212bd660b5b9f3a827ff35a7601cefdac2d153bfc8079eb7", size = 20845039, upload-time = "2025-09-10T21:51:01.167Z" }, 796 | { url = "https://files.pythonhosted.org/packages/4a/29/8ad9038e75cb91f54b81cc933dd14fcfa92fa6f8706117d43d4251a8a662/uv-0.8.17-py3-none-musllinux_1_1_armv7l.whl", hash = "sha256:505854bc75c497b95d2c65590291dc820999a4a7d9dfab4f44a9434a6cff7b5f", size = 19820370, upload-time = "2025-09-10T21:51:04.616Z" }, 797 | { url = "https://files.pythonhosted.org/packages/f2/c9/fc8482d1e7dfe187c6e03dcefbac0db41a5dd72aa7b017c0f80f91a04444/uv-0.8.17-py3-none-musllinux_1_1_i686.whl", hash = "sha256:dc479f661da449df37d68b36fdffa641e89fb53ad38c16a5c9f98f3211785b63", size = 20289951, upload-time = "2025-09-10T21:51:08.605Z" }, 798 | { url = "https://files.pythonhosted.org/packages/2d/84/ad878ed045f02aa973be46636c802d494f8270caf5ea8bd04b7bbc68aa23/uv-0.8.17-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:a1d11cd805be6d137ffef4a8227905f87f459031c645ac5031c30a3bcd08abd6", size = 21234644, upload-time = "2025-09-10T21:51:12.429Z" }, 799 | { url = "https://files.pythonhosted.org/packages/f8/03/3fa2641513922988e641050b3adbc87de527f44c2cc8328510703616be6a/uv-0.8.17-py3-none-win32.whl", hash = "sha256:d13a616eb0b2b33c7aa09746cc85860101d595655b58653f0b499af19f33467c", size = 19216757, upload-time = "2025-09-10T21:51:16.021Z" }, 800 | { url = "https://files.pythonhosted.org/packages/1a/c4/0082f437bac162ab95e5a3a389a184c122d45eb5593960aab92fdf80374b/uv-0.8.17-py3-none-win_amd64.whl", hash = "sha256:cf85b84b81b41d57a9b6eeded8473ec06ace8ee959ad0bb57e102b5ad023bd34", size = 21125811, upload-time = "2025-09-10T21:51:19.397Z" }, 801 | { url = "https://files.pythonhosted.org/packages/50/a2/29f57b118b3492c9d5ab1a99ba4906e7d7f8b658881d31bc2c4408d64d07/uv-0.8.17-py3-none-win_arm64.whl", hash = "sha256:64d649a8c4c3732b05dc712544963b004cf733d95fdc5d26f43c5493553ff0a7", size = 19564631, upload-time = "2025-09-10T21:51:22.599Z" }, 802 | ] 803 | 804 | [[package]] 805 | name = "virtualenv" 806 | version = "20.32.0" 807 | source = { registry = "https://pypi.org/simple" } 808 | dependencies = [ 809 | { name = "distlib" }, 810 | { name = "filelock" }, 811 | { name = "platformdirs" }, 812 | ] 813 | sdist = { url = "https://files.pythonhosted.org/packages/a9/96/0834f30fa08dca3738614e6a9d42752b6420ee94e58971d702118f7cfd30/virtualenv-20.32.0.tar.gz", hash = "sha256:886bf75cadfdc964674e6e33eb74d787dff31ca314ceace03ca5810620f4ecf0", size = 6076970, upload-time = "2025-07-21T04:09:50.985Z" } 814 | wheels = [ 815 | { url = "https://files.pythonhosted.org/packages/5c/c6/f8f28009920a736d0df434b52e9feebfb4d702ba942f15338cb4a83eafc1/virtualenv-20.32.0-py3-none-any.whl", hash = "sha256:2c310aecb62e5aa1b06103ed7c2977b81e042695de2697d01017ff0f1034af56", size = 6057761, upload-time = "2025-07-21T04:09:48.059Z" }, 816 | ] 817 | --------------------------------------------------------------------------------