diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml new file mode 100644 index 0000000..2c74a9b --- /dev/null +++ b/.github/workflows/publish-pypi.yml @@ -0,0 +1,74 @@ +name: Publish PyPI package + +on: + push: + tags: + - "v*.*.*" + +jobs: + publish: + runs-on: ubuntu-latest + environment: + name: pypi-release + url: https://pypi.org/p/supertab-connect-sdk + permissions: + contents: read + id-token: write + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - uses: astral-sh/setup-uv@v5 + with: + enable-cache: true + + - name: Validate release tag format + run: | + if [[ ! "${GITHUB_REF_NAME}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$ ]]; then + echo "Tag '${GITHUB_REF_NAME}' is not valid semver" + exit 1 + fi + + - name: Verify tag version matches pyproject.toml + run: | + TAG_VERSION="${GITHUB_REF_NAME#v}" + PACKAGE_VERSION="$(python - <<'PY' + import tomllib + with open("pyproject.toml", "rb") as f: + print(tomllib.load(f)["project"]["version"]) + PY + )" + if [ "${TAG_VERSION}" != "${PACKAGE_VERSION}" ]; then + echo "Tag version (${TAG_VERSION}) does not match pyproject.toml version (${PACKAGE_VERSION})" + exit 1 + fi + + - name: Sync dependencies + run: uv sync --locked --extra dev + + - name: Check formatting + run: uv run ruff format --check . + + - name: Run linting + run: uv run ruff check . + + - name: Run type checking + run: uv run ty check + + - name: Run tests + run: uv run pytest + + - name: Build package + run: | + rm -rf dist + uv run python -m build --no-isolation + + - name: Check package metadata + run: uv run twine check dist/* + + - name: Publish package to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.gitignore b/.gitignore index 7fd844f..b165d98 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ __pycache__/ .venv/ venv/ +.uv-cache/ build/ dist/ diff --git a/README.md b/README.md index 8897722..4f6d7b8 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,106 @@ # Supertab Connect SDK -Python SDK for Supertab Connect. +Python SDK for [Supertab Connect](https://www.supertab.co/supertab-connect). -## Usage +Use this package to obtain Supertab license tokens on the customer side and verify +or enforce them on the merchant side. -Check the `examples` folder to get the idea of how to use the provided functionality. +## Installation -Dedicated SDK docs portal is coming soon. +```bash +pip install supertab-connect-sdk +``` + +Requires Python 3.12 or newer. + +## Customer Usage + +Obtain a license token for a resource URL: + +```python +import asyncio + +from supertab_connect import obtain_license_token + + +async def main() -> None: + token = await obtain_license_token( + client_id="your_client_id", + client_secret="your_client_secret", + resource_url="https://example.com/premium/article", + ) + + if token is None: + print("No token required for this usage") + return + + print(token) + + +asyncio.run(main()) +``` + +The SDK fetches `license.xml` from the resource origin, finds the best matching +`` entry, and exchanges the client credentials for a license token. + +## Merchant Usage + +Verify and record license-token usage: + +```python +import asyncio + +from supertab_connect import SupertabConnect, SupertabConnectConfig + + +async def main() -> None: + client = SupertabConnect( + SupertabConnectConfig( + api_key="your_api_key", + ) + ) + + async with client: + result = await client.verify_and_record( + token="your.jwt.token", + resource_url="https://example.com/premium/article", + user_agent="Mozilla/5.0", + request_headers={"Accept": "text/html"}, + ) + + if not result.valid: + print(f"DENY access: {result.error}") + return + + print("ALLOW access") + + +asyncio.run(main()) +``` + +For request-level enforcement, use `SupertabConnect.handle_request()` with an +`httpx.Request`. See the `examples` directory for complete merchant and customer +examples. + +## Error Handling + +Customer-side token retrieval raises `SupertabConnectError` when `license.xml` +cannot be fetched or parsed, no matching content block exists, or the token +endpoint fails. + +Merchant-side token verification returns typed result objects instead of raising +for normal invalid-token cases. Invalid tokens include a reason and a human +readable error. + +## Typing + +This package ships inline type hints and includes a `py.typed` marker for type +checkers. + +## Documentation + +See the [Supertab Connect Python SDK docs](https://supertab-connect.mintlify.app/reference/sdk/python) +for the full API reference. ## Development @@ -14,15 +108,9 @@ This project uses `hatchling` as the build backend. See [DEVELOPMENT.md](DEVELOPMENT.md) for local setup, Git hooks, and CI-aligned development commands. -## Package Layout - -```text -. -├── connect -│ ├── customer -│ └── merchant -├── examples -│ ├── obtain_license_token.py -│ └── obtain_and_verify_license_token.py -└── tests -``` +## Links + +- [Documentation](https://supertab-connect.mintlify.app/reference/sdk/python) +- [Repository](https://github.com/getsupertab/connect-sdk-python) +- [Issues](https://github.com/getsupertab/connect-sdk-python/issues) +- [License](LICENSE) diff --git a/connect/customer/__init__.py b/connect/customer/__init__.py deleted file mode 100644 index e875cc6..0000000 --- a/connect/customer/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -"""Customer functionality for Supertab Connect.""" - -from connect.customer.token import obtain_license_token -from connect.types import UsageType - -__all__ = ["UsageType", "obtain_license_token"] diff --git a/examples/merchant_handle_request.py b/examples/merchant_handle_request.py index 9d1db4f..f3708de 100644 --- a/examples/merchant_handle_request.py +++ b/examples/merchant_handle_request.py @@ -5,7 +5,7 @@ import httpx -from connect import EnforcementMode, HandlerAction, SupertabConnect, SupertabConnectConfig +from supertab_connect import EnforcementMode, HandlerAction, SupertabConnect, SupertabConnectConfig logging.basicConfig(level=logging.DEBUG) diff --git a/examples/merchant_verify_and_record_event.py b/examples/merchant_verify_and_record_event.py index 29808c8..2d727a2 100644 --- a/examples/merchant_verify_and_record_event.py +++ b/examples/merchant_verify_and_record_event.py @@ -3,7 +3,7 @@ import asyncio import logging -from connect import EnforcementMode, SupertabConnect, SupertabConnectConfig +from supertab_connect import EnforcementMode, SupertabConnect, SupertabConnectConfig logging.basicConfig(level=logging.DEBUG) diff --git a/examples/obtain_and_verify_license_token.py b/examples/obtain_and_verify_license_token.py index 54fb634..adc64b4 100644 --- a/examples/obtain_and_verify_license_token.py +++ b/examples/obtain_and_verify_license_token.py @@ -3,8 +3,8 @@ import asyncio import logging -from connect import obtain_license_token, verify_license_token -from connect.types import InvalidLicenseToken, ValidLicenseToken +from supertab_connect import obtain_license_token, verify_license_token +from supertab_connect.types import InvalidLicenseToken, ValidLicenseToken logging.basicConfig(level=logging.DEBUG) diff --git a/examples/obtain_license_token.py b/examples/obtain_license_token.py index a823ef1..d051c79 100644 --- a/examples/obtain_license_token.py +++ b/examples/obtain_license_token.py @@ -3,7 +3,7 @@ import asyncio import logging -from connect import obtain_license_token +from supertab_connect import obtain_license_token logging.basicConfig(level=logging.DEBUG) diff --git a/pyproject.toml b/pyproject.toml index 446a5dd..2cbf03e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "supertab-connect-sdk" -version = "0.0.1" +version = "0.1.0" authors = [ { name = "Supertab", email = "hello@supertab.co" }, ] @@ -12,11 +12,14 @@ description = "Supertab Connect SDK" readme = "README.md" requires-python = ">=3.12" classifiers = [ - "License :: OSI Approved :: MIT License", + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", "Operating System :: OS Independent", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.12", + "Topic :: Software Development :: Libraries :: Python Modules", + "Typing :: Typed", ] license = "MIT" license-files = ["LICEN[CS]E*"] @@ -28,11 +31,13 @@ dependencies = [ [project.optional-dependencies] dev = [ "build>=1.4.2", + "hatchling>=1.26", "prek>=0.3.8", "pytest>=8,<9", "pytest-asyncio>=0.24", "respx>=0.22", "ruff>=0.15.7", + "twine>=6", "ty>=0.0.25", ] @@ -40,6 +45,7 @@ dev = [ Homepage = "https://www.supertab.co/supertab-connect" Repository = "https://github.com/getsupertab/connect-sdk-python" Issues = "https://github.com/getsupertab/connect-sdk-python/issues" +Documentation = "https://supertab-connect.mintlify.app/reference/sdk/python" [tool.pytest.ini_options] asyncio_mode = "auto" @@ -48,4 +54,20 @@ asyncio_mode = "auto" line-length = 119 [tool.hatch.build.targets.wheel] -packages = ["connect"] +packages = ["supertab_connect"] + +[tool.hatch.build.targets.sdist] +exclude = [ + "/.github", + "/.idea", + "/.pytest_cache", + "/.ruff_cache", + "/.uv-cache", + "/.venv", + "/build", + "/dist", + "/uv.lock", + "/prek.toml", + "/DEVELOPMENT.md", +] + diff --git a/connect/__init__.py b/supertab_connect/__init__.py similarity index 55% rename from connect/__init__.py rename to supertab_connect/__init__.py index 9204341..6c12aca 100644 --- a/connect/__init__.py +++ b/supertab_connect/__init__.py @@ -1,11 +1,11 @@ """Supertab Connect SDK.""" -from connect.customer.token import obtain_license_token -from connect.exceptions import SupertabConnectError -from connect.merchant.bots import default_bot_detector -from connect.merchant.client import SupertabConnect -from connect.merchant.license import verify_license_token -from connect.types import ( +from supertab_connect.customer.token import obtain_license_token +from supertab_connect.exceptions import SupertabConnectError +from supertab_connect.merchant.bots import default_bot_detector +from supertab_connect.merchant.client import SupertabConnect +from supertab_connect.merchant.license import verify_license_token +from supertab_connect.types import ( EnforcementMode, HandlerAction, HandlerResult, diff --git a/connect/_version.py b/supertab_connect/_version.py similarity index 100% rename from connect/_version.py rename to supertab_connect/_version.py diff --git a/connect/common.py b/supertab_connect/common.py similarity index 100% rename from connect/common.py rename to supertab_connect/common.py diff --git a/supertab_connect/customer/__init__.py b/supertab_connect/customer/__init__.py new file mode 100644 index 0000000..64604ea --- /dev/null +++ b/supertab_connect/customer/__init__.py @@ -0,0 +1,6 @@ +"""Customer functionality for Supertab Connect.""" + +from supertab_connect.customer.token import obtain_license_token +from supertab_connect.types import UsageType + +__all__ = ["UsageType", "obtain_license_token"] diff --git a/connect/customer/content_matcher.py b/supertab_connect/customer/content_matcher.py similarity index 91% rename from connect/customer/content_matcher.py rename to supertab_connect/customer/content_matcher.py index 06f2660..fc48c11 100644 --- a/connect/customer/content_matcher.py +++ b/supertab_connect/customer/content_matcher.py @@ -2,9 +2,9 @@ import urllib.parse -from connect.common import debug_log -from connect.url_pattern import score_path_pattern -from connect.customer.content_parser import _ContentBlock +from supertab_connect.common import debug_log +from supertab_connect.url_pattern import score_path_pattern +from supertab_connect.customer.content_parser import _ContentBlock def _find_best_matching_content( diff --git a/connect/customer/content_parser.py b/supertab_connect/customer/content_parser.py similarity index 98% rename from connect/customer/content_parser.py rename to supertab_connect/customer/content_parser.py index 4bad3c4..7040f21 100644 --- a/connect/customer/content_parser.py +++ b/supertab_connect/customer/content_parser.py @@ -3,7 +3,7 @@ from dataclasses import dataclass from xml.etree import ElementTree -from connect.common import debug_log +from supertab_connect.common import debug_log _RSL_NAMESPACE = "https://rslstandard.org/rsl" _NS = {"rsl": _RSL_NAMESPACE} diff --git a/connect/customer/token.py b/supertab_connect/customer/token.py similarity index 97% rename from connect/customer/token.py rename to supertab_connect/customer/token.py index cef8fed..2776cb9 100644 --- a/connect/customer/token.py +++ b/supertab_connect/customer/token.py @@ -15,12 +15,12 @@ from cryptography.hazmat.primitives.asymmetric import ec, rsa from cryptography.hazmat.primitives.serialization import load_pem_private_key -from connect.common import debug_log, error_log -from connect.exceptions import SupertabConnectError -from connect.customer.content_matcher import _find_best_matching_content -from connect.customer.content_parser import _ContentBlock -from connect.customer.content_parser import _parse_content_elements -from connect.types import UsageType +from supertab_connect.common import debug_log, error_log +from supertab_connect.exceptions import SupertabConnectError +from supertab_connect.customer.content_matcher import _find_best_matching_content +from supertab_connect.customer.content_parser import _ContentBlock +from supertab_connect.customer.content_parser import _parse_content_elements +from supertab_connect.types import UsageType _SUPPORTED_ALGS = ("ES256", "RS256") _DEFAULT_HTTP_TIMEOUT_SECONDS = 10.0 diff --git a/connect/exceptions.py b/supertab_connect/exceptions.py similarity index 100% rename from connect/exceptions.py rename to supertab_connect/exceptions.py diff --git a/connect/merchant/__init__.py b/supertab_connect/merchant/__init__.py similarity index 50% rename from connect/merchant/__init__.py rename to supertab_connect/merchant/__init__.py index 806420a..66f4929 100644 --- a/connect/merchant/__init__.py +++ b/supertab_connect/merchant/__init__.py @@ -1,7 +1,7 @@ """Merchant-facing helpers for the Supertab Connect SDK.""" -from connect.merchant.bots import default_bot_detector -from connect.merchant.client import SupertabConnect +from supertab_connect.merchant.bots import default_bot_detector +from supertab_connect.merchant.client import SupertabConnect __all__ = [ "default_bot_detector", diff --git a/connect/merchant/bots.py b/supertab_connect/merchant/bots.py similarity index 100% rename from connect/merchant/bots.py rename to supertab_connect/merchant/bots.py diff --git a/connect/merchant/client.py b/supertab_connect/merchant/client.py similarity index 95% rename from connect/merchant/client.py rename to supertab_connect/merchant/client.py index cf1bab8..0e9f91a 100644 --- a/connect/merchant/client.py +++ b/supertab_connect/merchant/client.py @@ -5,15 +5,15 @@ from httpx import Request -from connect.merchant.events import aclose_http_client as aclose_events_http_client -from connect.merchant.license import ( +from supertab_connect.merchant.events import aclose_http_client as aclose_events_http_client +from supertab_connect.merchant.license import ( build_block_result, build_signal_result, verify_and_record_event, verify_license_token, ) -from connect.merchant.jwks import aclose_http_client as aclose_jwks_http_client -from connect.types import ( +from supertab_connect.merchant.jwks import aclose_http_client as aclose_jwks_http_client +from supertab_connect.types import ( BotDetector, EnforcementMode, HandlerAction, diff --git a/connect/merchant/events.py b/supertab_connect/merchant/events.py similarity index 92% rename from connect/merchant/events.py rename to supertab_connect/merchant/events.py index 50f8439..3833700 100644 --- a/connect/merchant/events.py +++ b/supertab_connect/merchant/events.py @@ -4,8 +4,8 @@ import httpx -from connect._version import _get_sdk_user_agent -from connect.common import debug_log, error_log +from supertab_connect._version import _get_sdk_user_agent +from supertab_connect.common import debug_log, error_log _http_client: httpx.AsyncClient | None = None diff --git a/connect/merchant/headers.py b/supertab_connect/merchant/headers.py similarity index 100% rename from connect/merchant/headers.py rename to supertab_connect/merchant/headers.py diff --git a/connect/merchant/jwks.py b/supertab_connect/merchant/jwks.py similarity index 94% rename from connect/merchant/jwks.py rename to supertab_connect/merchant/jwks.py index 2fb3d5b..1115aa6 100644 --- a/connect/merchant/jwks.py +++ b/supertab_connect/merchant/jwks.py @@ -5,8 +5,8 @@ import httpx -from connect.common import debug_log, error_log -from connect.exceptions import JwksKeyNotFoundError +from supertab_connect.common import debug_log, error_log +from supertab_connect.exceptions import JwksKeyNotFoundError JWKS_CACHE_TTL_SECONDS = 48 * 3600 # 48 hours diff --git a/connect/merchant/license.py b/supertab_connect/merchant/license.py similarity index 96% rename from connect/merchant/license.py rename to supertab_connect/merchant/license.py index c22c141..09190f2 100644 --- a/connect/merchant/license.py +++ b/supertab_connect/merchant/license.py @@ -9,13 +9,13 @@ import jwt.algorithms from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePublicKey -from connect._version import _get_sdk_user_agent -from connect.common import debug_log, error_log -from connect.exceptions import JwksKeyNotFoundError -from connect.merchant.events import record_event -from connect.merchant.headers import to_event_properties -from connect.merchant.jwks import _find_key_by_kid, clear_jwks_cache, fetch_platform_jwks -from connect.types import ( +from supertab_connect._version import _get_sdk_user_agent +from supertab_connect.common import debug_log, error_log +from supertab_connect.exceptions import JwksKeyNotFoundError +from supertab_connect.merchant.events import record_event +from supertab_connect.merchant.headers import to_event_properties +from supertab_connect.merchant.jwks import _find_key_by_kid, clear_jwks_cache, fetch_platform_jwks +from supertab_connect.types import ( AllowHandlerResult, BlockHandlerResult, HandlerAction, diff --git a/supertab_connect/py.typed b/supertab_connect/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/connect/types.py b/supertab_connect/types.py similarity index 97% rename from connect/types.py rename to supertab_connect/types.py index 1f7a348..54a0bf4 100644 --- a/connect/types.py +++ b/supertab_connect/types.py @@ -46,7 +46,7 @@ class UsageType(StrEnum): @dataclass(frozen=True) class SupertabConnectConfig: api_key: str - enforcement: EnforcementMode = EnforcementMode.STRICT + enforcement: EnforcementMode = EnforcementMode.SOFT supertab_base_url: str | None = None bot_detector: BotDetector | None = None debug: bool = False diff --git a/connect/url_pattern.py b/supertab_connect/url_pattern.py similarity index 100% rename from connect/url_pattern.py rename to supertab_connect/url_pattern.py diff --git a/tests/customer/conftest.py b/tests/customer/conftest.py index 284df57..41ad083 100644 --- a/tests/customer/conftest.py +++ b/tests/customer/conftest.py @@ -1,6 +1,6 @@ import pytest -from connect.customer.token import _LICENSE_TOKEN_CACHE, _LICENSE_TOKEN_LOCKS, _LICENSE_XML_CACHE +from supertab_connect.customer.token import _LICENSE_TOKEN_CACHE, _LICENSE_TOKEN_LOCKS, _LICENSE_XML_CACHE SAMPLE_XML = """ diff --git a/tests/customer/test_content_matcher.py b/tests/customer/test_content_matcher.py index 336fb2b..6f85506 100644 --- a/tests/customer/test_content_matcher.py +++ b/tests/customer/test_content_matcher.py @@ -1,11 +1,11 @@ import pytest -from connect.customer.content_matcher import ( +from supertab_connect.customer.content_matcher import ( _ContentBlock, _find_best_matching_content, ) -from connect.customer.content_parser import _parse_content_elements -from connect.url_pattern import score_path_pattern +from supertab_connect.customer.content_parser import _parse_content_elements +from supertab_connect.url_pattern import score_path_pattern from tests.customer.conftest import SAMPLE_XML diff --git a/tests/customer/test_content_parser.py b/tests/customer/test_content_parser.py index 807d9b7..315af18 100644 --- a/tests/customer/test_content_parser.py +++ b/tests/customer/test_content_parser.py @@ -1,6 +1,6 @@ import pytest -from connect.customer.content_parser import _parse_content_elements +from supertab_connect.customer.content_parser import _parse_content_elements from tests.customer.conftest import SAMPLE_XML diff --git a/tests/customer/test_tokens.py b/tests/customer/test_tokens.py index 059871e..ff4ceaf 100644 --- a/tests/customer/test_tokens.py +++ b/tests/customer/test_tokens.py @@ -10,9 +10,9 @@ from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric import ec, rsa -from connect.customer.token import _create_async_client, _generate_license_token, obtain_license_token -from connect.exceptions import SupertabConnectError -from connect.types import UsageType +from supertab_connect.customer.token import _create_async_client, _generate_license_token, obtain_license_token +from supertab_connect.exceptions import SupertabConnectError +from supertab_connect.types import UsageType from tests.customer.conftest import SAMPLE_XML @@ -29,7 +29,7 @@ def create_mock_client(**kwargs: Any) -> httpx.AsyncClient: return httpx.AsyncClient(transport=httpx.MockTransport(handler), **kwargs) monkeypatch.setattr( - "connect.customer.token._create_async_client", + "supertab_connect.customer.token._create_async_client", create_mock_client, ) diff --git a/tests/merchant/conftest.py b/tests/merchant/conftest.py index 570461d..1b7012a 100644 --- a/tests/merchant/conftest.py +++ b/tests/merchant/conftest.py @@ -83,6 +83,6 @@ def mock_jwks(jwks_response): @pytest.fixture(autouse=True) def _clear_jwks_cache(): """Ensure JWKS cache is cleared before each test.""" - from connect.merchant.jwks import clear_jwks_cache + from supertab_connect.merchant.jwks import clear_jwks_cache clear_jwks_cache() diff --git a/tests/merchant/test_bots.py b/tests/merchant/test_bots.py index cb38474..b32c44b 100644 --- a/tests/merchant/test_bots.py +++ b/tests/merchant/test_bots.py @@ -2,7 +2,7 @@ import httpx -from connect.merchant.bots import default_bot_detector +from supertab_connect.merchant.bots import default_bot_detector from tests.merchant.constants import REQUEST_URL diff --git a/tests/merchant/test_client.py b/tests/merchant/test_client.py index 7293515..9832de6 100644 --- a/tests/merchant/test_client.py +++ b/tests/merchant/test_client.py @@ -5,8 +5,8 @@ import httpx import pytest -from connect.merchant.client import SupertabConnect -from connect.types import ( +from supertab_connect.merchant.client import SupertabConnect +from supertab_connect.types import ( BlockHandlerResult, EnforcementMode, HandlerAction, @@ -85,7 +85,7 @@ async def stub_verify_license_token(token: str, *, request_url: str, supertab_ba ) return ValidLicenseToken(license_id="lic_test_123", payload={}) - monkeypatch.setattr("connect.merchant.client.verify_license_token", stub_verify_license_token) + monkeypatch.setattr("supertab_connect.merchant.client.verify_license_token", stub_verify_license_token) SupertabConnect.set_base_url("https://override.example") result = await SupertabConnect.verify(token="signed.jwt", resource_url=REQUEST_URL, debug=True) @@ -107,7 +107,7 @@ async def stub_verify_and_record_event(**kwargs): captured.update(kwargs) return ValidLicenseToken(license_id="lic_test_123", payload={}) - monkeypatch.setattr("connect.merchant.client.verify_and_record_event", stub_verify_and_record_event) + monkeypatch.setattr("supertab_connect.merchant.client.verify_and_record_event", stub_verify_and_record_event) client = SupertabConnect( SupertabConnectConfig( @@ -133,7 +133,7 @@ async def test_handle_request_allows_token_when_enforcement_disabled(monkeypatch async def fail_verify_and_record_event(**kwargs): raise AssertionError(f"verify_and_record_event should not be called: {kwargs}") - monkeypatch.setattr("connect.merchant.client.verify_and_record_event", fail_verify_and_record_event) + monkeypatch.setattr("supertab_connect.merchant.client.verify_and_record_event", fail_verify_and_record_event) client = SupertabConnect(SupertabConnectConfig(api_key="sk_test_123", enforcement=EnforcementMode.DISABLED)) result = await client.handle_request(_make_request({"Authorization": "License signed.jwt"})) @@ -152,7 +152,7 @@ async def stub_verify_and_record_event(**kwargs): license_id="lic_test_123", ) - monkeypatch.setattr("connect.merchant.client.verify_and_record_event", stub_verify_and_record_event) + monkeypatch.setattr("supertab_connect.merchant.client.verify_and_record_event", stub_verify_and_record_event) client = SupertabConnect(SupertabConnectConfig(api_key="sk_test_123", enforcement=EnforcementMode.STRICT)) result = await client.handle_request( @@ -175,7 +175,7 @@ async def test_handle_request_allows_valid_token(monkeypatch): async def stub_verify_and_record_event(**kwargs): return ValidLicenseToken(license_id="lic_test_123", payload={}) - monkeypatch.setattr("connect.merchant.client.verify_and_record_event", stub_verify_and_record_event) + monkeypatch.setattr("supertab_connect.merchant.client.verify_and_record_event", stub_verify_and_record_event) client = SupertabConnect(SupertabConnectConfig(api_key="sk_test_123")) result = await client.handle_request( @@ -197,7 +197,7 @@ async def stub_verify_and_record_event(**kwargs): captured.update(kwargs) return ValidLicenseToken(license_id="lic_test_123", payload={}) - monkeypatch.setattr("connect.merchant.client.verify_and_record_event", stub_verify_and_record_event) + monkeypatch.setattr("supertab_connect.merchant.client.verify_and_record_event", stub_verify_and_record_event) client = SupertabConnect(SupertabConnectConfig(api_key="sk_test_123")) result = await client.handle_request( @@ -222,8 +222,8 @@ async def close_events(): async def close_jwks(): called.append("jwks") - monkeypatch.setattr("connect.merchant.client.aclose_events_http_client", close_events) - monkeypatch.setattr("connect.merchant.client.aclose_jwks_http_client", close_jwks) + monkeypatch.setattr("supertab_connect.merchant.client.aclose_events_http_client", close_events) + monkeypatch.setattr("supertab_connect.merchant.client.aclose_jwks_http_client", close_jwks) async with SupertabConnect(SupertabConnectConfig(api_key="sk_test_123")): pass diff --git a/tests/merchant/test_events.py b/tests/merchant/test_events.py index e8bab4f..df90662 100644 --- a/tests/merchant/test_events.py +++ b/tests/merchant/test_events.py @@ -6,9 +6,9 @@ import httpx import respx -import connect.merchant.events as events_module -from connect.merchant.events import record_event -from connect.merchant.events import aclose_http_client +import supertab_connect.merchant.events as events_module +from supertab_connect.merchant.events import record_event +from supertab_connect.merchant.events import aclose_http_client from tests.merchant.constants import SUPERTAB_BASE_URL @@ -16,7 +16,7 @@ async def test_record_event_posts_expected_payload(monkeypatch): - monkeypatch.setattr("connect.merchant.events._get_sdk_user_agent", lambda: "sdk-test/1.2.3") + monkeypatch.setattr("supertab_connect.merchant.events._get_sdk_user_agent", lambda: "sdk-test/1.2.3") with respx.mock: route = respx.post(EVENTS_URL).respond(status_code=201, json={"ok": True}) @@ -44,7 +44,7 @@ async def test_record_event_logs_non_2xx_responses(caplog): with respx.mock: respx.post(EVENTS_URL).respond(status_code=500) - with caplog.at_level(logging.DEBUG, logger="connect.common"): + with caplog.at_level(logging.DEBUG, logger="supertab_connect.common"): await record_event( api_key="sk_test_123", base_url=SUPERTAB_BASE_URL, @@ -62,7 +62,7 @@ async def test_record_event_swallows_request_failures(caplog): with respx.mock: respx.post(EVENTS_URL).mock(side_effect=httpx.ConnectError("boom", request=request)) - with caplog.at_level(logging.ERROR, logger="connect.common"): + with caplog.at_level(logging.ERROR, logger="supertab_connect.common"): await record_event( api_key="sk_test_123", base_url=SUPERTAB_BASE_URL, @@ -83,7 +83,7 @@ class DummyClient: async def aclose(self): called["aclose"] += 1 - monkeypatch.setattr("connect.merchant.events._http_client", DummyClient()) + monkeypatch.setattr("supertab_connect.merchant.events._http_client", DummyClient()) await aclose_http_client() diff --git a/tests/merchant/test_headers.py b/tests/merchant/test_headers.py index d5569f4..1a79a14 100644 --- a/tests/merchant/test_headers.py +++ b/tests/merchant/test_headers.py @@ -1,6 +1,6 @@ """Tests for merchant event header mapping.""" -from connect.merchant.headers import to_event_properties +from supertab_connect.merchant.headers import to_event_properties def test_to_event_properties_lowercases_keys_and_prefixes_them(): diff --git a/tests/merchant/test_jwks.py b/tests/merchant/test_jwks.py index 84dcd3a..705dcd8 100644 --- a/tests/merchant/test_jwks.py +++ b/tests/merchant/test_jwks.py @@ -6,9 +6,9 @@ import pytest import respx -import connect.merchant.jwks as jwks_module -from connect.exceptions import JwksKeyNotFoundError -from connect.merchant.jwks import ( +import supertab_connect.merchant.jwks as jwks_module +from supertab_connect.exceptions import JwksKeyNotFoundError +from supertab_connect.merchant.jwks import ( JWKS_CACHE_TTL_SECONDS, _find_key_by_kid, aclose_http_client, @@ -49,7 +49,9 @@ async def test_fetch_platform_jwks_cache_expires(jwks_response): await fetch_platform_jwks(SUPERTAB_BASE_URL) assert route.call_count == 1 - with patch("connect.merchant.jwks.time.monotonic", return_value=time.monotonic() + JWKS_CACHE_TTL_SECONDS + 1): + with patch( + "supertab_connect.merchant.jwks.time.monotonic", return_value=time.monotonic() + JWKS_CACHE_TTL_SECONDS + 1 + ): await fetch_platform_jwks(SUPERTAB_BASE_URL) assert route.call_count == 2 @@ -99,7 +101,7 @@ class DummyClient: async def aclose(self): called["aclose"] += 1 - monkeypatch.setattr("connect.merchant.jwks._http_client", DummyClient()) + monkeypatch.setattr("supertab_connect.merchant.jwks._http_client", DummyClient()) await aclose_http_client() diff --git a/tests/merchant/test_license.py b/tests/merchant/test_license.py index 4dabd43..d012617 100644 --- a/tests/merchant/test_license.py +++ b/tests/merchant/test_license.py @@ -5,13 +5,13 @@ import respx -from connect.merchant.license import ( +from supertab_connect.merchant.license import ( build_block_result, build_signal_result, verify_and_record_event, verify_license_token, ) -from connect.types import HandlerAction, InvalidLicenseToken, LicenseTokenInvalidReason, ValidLicenseToken +from supertab_connect.types import HandlerAction, InvalidLicenseToken, LicenseTokenInvalidReason, ValidLicenseToken from tests.merchant.constants import JWKS_URL, REQUEST_URL, SUPERTAB_BASE_URL @@ -234,7 +234,7 @@ def test_build_block_result_sanitizes_header_value(): async def test_verify_and_record_event_records_license_used_for_valid_token(make_token, jwks_response, monkeypatch): token = make_token() - monkeypatch.setattr("connect.merchant.license._get_sdk_user_agent", lambda: "sdk-test/1.2.3") + monkeypatch.setattr("supertab_connect.merchant.license._get_sdk_user_agent", lambda: "sdk-test/1.2.3") with respx.mock: respx.get(JWKS_URL).respond(json=jwks_response) @@ -270,7 +270,7 @@ async def test_verify_and_record_event_records_license_used_for_valid_token(make async def test_verify_and_record_event_records_invalid_reason(make_token, monkeypatch): token = make_token(audience="https://other-site.com/page") - monkeypatch.setattr("connect.merchant.license._get_sdk_user_agent", lambda: "sdk-test/1.2.3") + monkeypatch.setattr("supertab_connect.merchant.license._get_sdk_user_agent", lambda: "sdk-test/1.2.3") with respx.mock: route = respx.post(EVENTS_URL).respond(status_code=201, json={"ok": True}) diff --git a/uv.lock b/uv.lock index ff02861..bcad1b0 100644 --- a/uv.lock +++ b/uv.lock @@ -95,6 +95,79 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, ] +[[package]] +name = "charset-normalizer" +version = "3.4.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/a1/67fe25fac3c7642725500a3f6cfe5821ad557c3abb11c9d20d12c7008d3e/charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5", size = 144271, upload-time = "2026-04-02T09:28:39.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/eb/4fc8d0a7110eb5fc9cc161723a34a8a6c200ce3b4fbf681bc86feee22308/charset_normalizer-3.4.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46", size = 311328, upload-time = "2026-04-02T09:26:24.331Z" }, + { url = "https://files.pythonhosted.org/packages/f8/e3/0fadc706008ac9d7b9b5be6dc767c05f9d3e5df51744ce4cc9605de7b9f4/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2", size = 208061, upload-time = "2026-04-02T09:26:25.568Z" }, + { url = "https://files.pythonhosted.org/packages/42/f0/3dd1045c47f4a4604df85ec18ad093912ae1344ac706993aff91d38773a2/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b", size = 229031, upload-time = "2026-04-02T09:26:26.865Z" }, + { url = "https://files.pythonhosted.org/packages/dc/67/675a46eb016118a2fbde5a277a5d15f4f69d5f3f5f338e5ee2f8948fcf43/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a", size = 225239, upload-time = "2026-04-02T09:26:28.044Z" }, + { url = "https://files.pythonhosted.org/packages/4b/f8/d0118a2f5f23b02cd166fa385c60f9b0d4f9194f574e2b31cef350ad7223/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116", size = 216589, upload-time = "2026-04-02T09:26:29.239Z" }, + { url = "https://files.pythonhosted.org/packages/b1/f1/6d2b0b261b6c4ceef0fcb0d17a01cc5bc53586c2d4796fa04b5c540bc13d/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb", size = 202733, upload-time = "2026-04-02T09:26:30.5Z" }, + { url = "https://files.pythonhosted.org/packages/6f/c0/7b1f943f7e87cc3db9626ba17807d042c38645f0a1d4415c7a14afb5591f/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1", size = 212652, upload-time = "2026-04-02T09:26:31.709Z" }, + { url = "https://files.pythonhosted.org/packages/38/dd/5a9ab159fe45c6e72079398f277b7d2b523e7f716acc489726115a910097/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15", size = 211229, upload-time = "2026-04-02T09:26:33.282Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ff/531a1cad5ca855d1c1a8b69cb71abfd6d85c0291580146fda7c82857caa1/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5", size = 203552, upload-time = "2026-04-02T09:26:34.845Z" }, + { url = "https://files.pythonhosted.org/packages/c1/4c/a5fb52d528a8ca41f7598cb619409ece30a169fbdf9cdce592e53b46c3a6/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d", size = 230806, upload-time = "2026-04-02T09:26:36.152Z" }, + { url = "https://files.pythonhosted.org/packages/59/7a/071feed8124111a32b316b33ae4de83d36923039ef8cf48120266844285b/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7", size = 212316, upload-time = "2026-04-02T09:26:37.672Z" }, + { url = "https://files.pythonhosted.org/packages/fd/35/f7dba3994312d7ba508e041eaac39a36b120f32d4c8662b8814dab876431/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464", size = 227274, upload-time = "2026-04-02T09:26:38.93Z" }, + { url = "https://files.pythonhosted.org/packages/8a/2d/a572df5c9204ab7688ec1edc895a73ebded3b023bb07364710b05dd1c9be/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49", size = 218468, upload-time = "2026-04-02T09:26:40.17Z" }, + { url = "https://files.pythonhosted.org/packages/86/eb/890922a8b03a568ca2f336c36585a4713c55d4d67bf0f0c78924be6315ca/charset_normalizer-3.4.7-cp312-cp312-win32.whl", hash = "sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c", size = 148460, upload-time = "2026-04-02T09:26:41.416Z" }, + { url = "https://files.pythonhosted.org/packages/35/d9/0e7dffa06c5ab081f75b1b786f0aefc88365825dfcd0ac544bdb7b2b6853/charset_normalizer-3.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6", size = 159330, upload-time = "2026-04-02T09:26:42.554Z" }, + { url = "https://files.pythonhosted.org/packages/9e/5d/481bcc2a7c88ea6b0878c299547843b2521ccbc40980cb406267088bc701/charset_normalizer-3.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d", size = 147828, upload-time = "2026-04-02T09:26:44.075Z" }, + { url = "https://files.pythonhosted.org/packages/c1/3b/66777e39d3ae1ddc77ee606be4ec6d8cbd4c801f65e5a1b6f2b11b8346dd/charset_normalizer-3.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063", size = 309627, upload-time = "2026-04-02T09:26:45.198Z" }, + { url = "https://files.pythonhosted.org/packages/2e/4e/b7f84e617b4854ade48a1b7915c8ccfadeba444d2a18c291f696e37f0d3b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c", size = 207008, upload-time = "2026-04-02T09:26:46.824Z" }, + { url = "https://files.pythonhosted.org/packages/c4/bb/ec73c0257c9e11b268f018f068f5d00aa0ef8c8b09f7753ebd5f2880e248/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66", size = 228303, upload-time = "2026-04-02T09:26:48.397Z" }, + { url = "https://files.pythonhosted.org/packages/85/fb/32d1f5033484494619f701e719429c69b766bfc4dbc61aa9e9c8c166528b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18", size = 224282, upload-time = "2026-04-02T09:26:49.684Z" }, + { url = "https://files.pythonhosted.org/packages/fa/07/330e3a0dda4c404d6da83b327270906e9654a24f6c546dc886a0eb0ffb23/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd", size = 215595, upload-time = "2026-04-02T09:26:50.915Z" }, + { url = "https://files.pythonhosted.org/packages/e3/7c/fc890655786e423f02556e0216d4b8c6bcb6bdfa890160dc66bf52dee468/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215", size = 201986, upload-time = "2026-04-02T09:26:52.197Z" }, + { url = "https://files.pythonhosted.org/packages/d8/97/bfb18b3db2aed3b90cf54dc292ad79fdd5ad65c4eae454099475cbeadd0d/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859", size = 211711, upload-time = "2026-04-02T09:26:53.49Z" }, + { url = "https://files.pythonhosted.org/packages/6f/a5/a581c13798546a7fd557c82614a5c65a13df2157e9ad6373166d2a3e645d/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8", size = 210036, upload-time = "2026-04-02T09:26:54.975Z" }, + { url = "https://files.pythonhosted.org/packages/8c/bf/b3ab5bcb478e4193d517644b0fb2bf5497fbceeaa7a1bc0f4d5b50953861/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5", size = 202998, upload-time = "2026-04-02T09:26:56.303Z" }, + { url = "https://files.pythonhosted.org/packages/e7/4e/23efd79b65d314fa320ec6017b4b5834d5c12a58ba4610aa353af2e2f577/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832", size = 230056, upload-time = "2026-04-02T09:26:57.554Z" }, + { url = "https://files.pythonhosted.org/packages/b9/9f/1e1941bc3f0e01df116e68dc37a55c4d249df5e6fa77f008841aef68264f/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6", size = 211537, upload-time = "2026-04-02T09:26:58.843Z" }, + { url = "https://files.pythonhosted.org/packages/80/0f/088cbb3020d44428964a6c97fe1edfb1b9550396bf6d278330281e8b709c/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48", size = 226176, upload-time = "2026-04-02T09:27:00.437Z" }, + { url = "https://files.pythonhosted.org/packages/6a/9f/130394f9bbe06f4f63e22641d32fc9b202b7e251c9aef4db044324dac493/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a", size = 217723, upload-time = "2026-04-02T09:27:02.021Z" }, + { url = "https://files.pythonhosted.org/packages/73/55/c469897448a06e49f8fa03f6caae97074fde823f432a98f979cc42b90e69/charset_normalizer-3.4.7-cp313-cp313-win32.whl", hash = "sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e", size = 148085, upload-time = "2026-04-02T09:27:03.192Z" }, + { url = "https://files.pythonhosted.org/packages/5d/78/1b74c5bbb3f99b77a1715c91b3e0b5bdb6fe302d95ace4f5b1bec37b0167/charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110", size = 158819, upload-time = "2026-04-02T09:27:04.454Z" }, + { url = "https://files.pythonhosted.org/packages/68/86/46bd42279d323deb8687c4a5a811fd548cb7d1de10cf6535d099877a9a9f/charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b", size = 147915, upload-time = "2026-04-02T09:27:05.971Z" }, + { url = "https://files.pythonhosted.org/packages/97/c8/c67cb8c70e19ef1960b97b22ed2a1567711de46c4ddf19799923adc836c2/charset_normalizer-3.4.7-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0", size = 309234, upload-time = "2026-04-02T09:27:07.194Z" }, + { url = "https://files.pythonhosted.org/packages/99/85/c091fdee33f20de70d6c8b522743b6f831a2f1cd3ff86de4c6a827c48a76/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a", size = 208042, upload-time = "2026-04-02T09:27:08.749Z" }, + { url = "https://files.pythonhosted.org/packages/87/1c/ab2ce611b984d2fd5d86a5a8a19c1ae26acac6bad967da4967562c75114d/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b", size = 228706, upload-time = "2026-04-02T09:27:09.951Z" }, + { url = "https://files.pythonhosted.org/packages/a8/29/2b1d2cb00bf085f59d29eb773ce58ec2d325430f8c216804a0a5cd83cbca/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41", size = 224727, upload-time = "2026-04-02T09:27:11.175Z" }, + { url = "https://files.pythonhosted.org/packages/47/5c/032c2d5a07fe4d4855fea851209cca2b6f03ebeb6d4e3afdb3358386a684/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e", size = 215882, upload-time = "2026-04-02T09:27:12.446Z" }, + { url = "https://files.pythonhosted.org/packages/2c/c2/356065d5a8b78ed04499cae5f339f091946a6a74f91e03476c33f0ab7100/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae", size = 200860, upload-time = "2026-04-02T09:27:13.721Z" }, + { url = "https://files.pythonhosted.org/packages/0c/cd/a32a84217ced5039f53b29f460962abb2d4420def55afabe45b1c3c7483d/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18", size = 211564, upload-time = "2026-04-02T09:27:15.272Z" }, + { url = "https://files.pythonhosted.org/packages/44/86/58e6f13ce26cc3b8f4a36b94a0f22ae2f00a72534520f4ae6857c4b81f89/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b", size = 211276, upload-time = "2026-04-02T09:27:16.834Z" }, + { url = "https://files.pythonhosted.org/packages/8f/fe/d17c32dc72e17e155e06883efa84514ca375f8a528ba2546bee73fc4df81/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356", size = 201238, upload-time = "2026-04-02T09:27:18.229Z" }, + { url = "https://files.pythonhosted.org/packages/6a/29/f33daa50b06525a237451cdb6c69da366c381a3dadcd833fa5676bc468b3/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab", size = 230189, upload-time = "2026-04-02T09:27:19.445Z" }, + { url = "https://files.pythonhosted.org/packages/b6/6e/52c84015394a6a0bdcd435210a7e944c5f94ea1055f5cc5d56c5fe368e7b/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46", size = 211352, upload-time = "2026-04-02T09:27:20.79Z" }, + { url = "https://files.pythonhosted.org/packages/8c/d7/4353be581b373033fb9198bf1da3cf8f09c1082561e8e922aa7b39bf9fe8/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44", size = 227024, upload-time = "2026-04-02T09:27:22.063Z" }, + { url = "https://files.pythonhosted.org/packages/30/45/99d18aa925bd1740098ccd3060e238e21115fffbfdcb8f3ece837d0ace6c/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72", size = 217869, upload-time = "2026-04-02T09:27:23.486Z" }, + { url = "https://files.pythonhosted.org/packages/5c/05/5ee478aa53f4bb7996482153d4bfe1b89e0f087f0ab6b294fcf92d595873/charset_normalizer-3.4.7-cp314-cp314-win32.whl", hash = "sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10", size = 148541, upload-time = "2026-04-02T09:27:25.146Z" }, + { url = "https://files.pythonhosted.org/packages/48/77/72dcb0921b2ce86420b2d79d454c7022bf5be40202a2a07906b9f2a35c97/charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl", hash = "sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f", size = 159634, upload-time = "2026-04-02T09:27:26.642Z" }, + { url = "https://files.pythonhosted.org/packages/c6/a3/c2369911cd72f02386e4e340770f6e158c7980267da16af8f668217abaa0/charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl", hash = "sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246", size = 148384, upload-time = "2026-04-02T09:27:28.271Z" }, + { url = "https://files.pythonhosted.org/packages/94/09/7e8a7f73d24dba1f0035fbbf014d2c36828fc1bf9c88f84093e57d315935/charset_normalizer-3.4.7-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24", size = 330133, upload-time = "2026-04-02T09:27:29.474Z" }, + { url = "https://files.pythonhosted.org/packages/8d/da/96975ddb11f8e977f706f45cddd8540fd8242f71ecdb5d18a80723dcf62c/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79", size = 216257, upload-time = "2026-04-02T09:27:30.793Z" }, + { url = "https://files.pythonhosted.org/packages/e5/e8/1d63bf8ef2d388e95c64b2098f45f84758f6d102a087552da1485912637b/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960", size = 234851, upload-time = "2026-04-02T09:27:32.44Z" }, + { url = "https://files.pythonhosted.org/packages/9b/40/e5ff04233e70da2681fa43969ad6f66ca5611d7e669be0246c4c7aaf6dc8/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4", size = 233393, upload-time = "2026-04-02T09:27:34.03Z" }, + { url = "https://files.pythonhosted.org/packages/be/c1/06c6c49d5a5450f76899992f1ee40b41d076aee9279b49cf9974d2f313d5/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e", size = 223251, upload-time = "2026-04-02T09:27:35.369Z" }, + { url = "https://files.pythonhosted.org/packages/2b/9f/f2ff16fb050946169e3e1f82134d107e5d4ae72647ec8a1b1446c148480f/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1", size = 206609, upload-time = "2026-04-02T09:27:36.661Z" }, + { url = "https://files.pythonhosted.org/packages/69/d5/a527c0cd8d64d2eab7459784fb4169a0ac76e5a6fc5237337982fd61347e/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44", size = 220014, upload-time = "2026-04-02T09:27:38.019Z" }, + { url = "https://files.pythonhosted.org/packages/7e/80/8a7b8104a3e203074dc9aa2c613d4b726c0e136bad1cc734594b02867972/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e", size = 218979, upload-time = "2026-04-02T09:27:39.37Z" }, + { url = "https://files.pythonhosted.org/packages/02/9a/b759b503d507f375b2b5c153e4d2ee0a75aa215b7f2489cf314f4541f2c0/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3", size = 209238, upload-time = "2026-04-02T09:27:40.722Z" }, + { url = "https://files.pythonhosted.org/packages/c2/4e/0f3f5d47b86bdb79256e7290b26ac847a2832d9a4033f7eb2cd4bcf4bb5b/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0", size = 236110, upload-time = "2026-04-02T09:27:42.33Z" }, + { url = "https://files.pythonhosted.org/packages/96/23/bce28734eb3ed2c91dcf93abeb8a5cf393a7b2749725030bb630e554fdd8/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e", size = 219824, upload-time = "2026-04-02T09:27:43.924Z" }, + { url = "https://files.pythonhosted.org/packages/2c/6f/6e897c6984cc4d41af319b077f2f600fc8214eb2fe2d6bcb79141b882400/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb", size = 233103, upload-time = "2026-04-02T09:27:45.348Z" }, + { url = "https://files.pythonhosted.org/packages/76/22/ef7bd0fe480a0ae9b656189ec00744b60933f68b4f42a7bb06589f6f576a/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe", size = 225194, upload-time = "2026-04-02T09:27:46.706Z" }, + { url = "https://files.pythonhosted.org/packages/c5/a7/0e0ab3e0b5bc1219bd80a6a0d4d72ca74d9250cb2382b7c699c147e06017/charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0", size = 159827, upload-time = "2026-04-02T09:27:48.053Z" }, + { url = "https://files.pythonhosted.org/packages/7a/1d/29d32e0fb40864b1f878c7f5a0b343ae676c6e2b271a2d55cc3a152391da/charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c", size = 174168, upload-time = "2026-04-02T09:27:49.795Z" }, + { url = "https://files.pythonhosted.org/packages/de/32/d92444ad05c7a6e41fb2036749777c163baf7a0301a040cb672d6b2b1ae9/charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d", size = 153018, upload-time = "2026-04-02T09:27:51.116Z" }, + { url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958, upload-time = "2026-04-02T09:28:37.794Z" }, +] + [[package]] name = "colorama" version = "0.4.6" @@ -157,6 +230,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/48/ef/0c2f4a8e31018a986949d34a01115dd057bf536905dca38897bacd21fac3/cryptography-46.0.5-cp38-abi3-win_amd64.whl", hash = "sha256:556e106ee01aa13484ce9b0239bca667be5004efb0aabbed28d353df86445595", size = 3467050, upload-time = "2026-02-10T19:18:18.899Z" }, ] +[[package]] +name = "docutils" +version = "0.22.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ae/b6/03bb70946330e88ffec97aefd3ea75ba575cb2e762061e0e62a213befee8/docutils-0.22.4.tar.gz", hash = "sha256:4db53b1fde9abecbb74d91230d32ab626d94f6badfc575d6db9194a49df29968", size = 2291750, upload-time = "2025-12-18T19:00:26.443Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/02/10/5da547df7a391dcde17f59520a231527b8571e6f46fc8efb02ccb370ab12/docutils-0.22.4-py3-none-any.whl", hash = "sha256:d0013f540772d1420576855455d050a2180186c91c15779301ac2ccb3eeb68de", size = 633196, upload-time = "2025-12-18T19:00:18.077Z" }, +] + [[package]] name = "h11" version = "0.16.0" @@ -166,6 +248,21 @@ wheels = [ { 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" }, ] +[[package]] +name = "hatchling" +version = "1.29.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, + { name = "pathspec" }, + { name = "pluggy" }, + { name = "trove-classifiers" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cf/9c/b4cfe330cd4f49cff17fd771154730555fa4123beb7f292cf0098b4e6c20/hatchling-1.29.0.tar.gz", hash = "sha256:793c31816d952cee405b83488ce001c719f325d9cda69f1fc4cd750527640ea6", size = 55656, upload-time = "2026-02-23T19:42:06.539Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d3/8a/44032265776062a89171285ede55a0bdaadc8ac00f27f0512a71a9e3e1c8/hatchling-1.29.0-py3-none-any.whl", hash = "sha256:50af9343281f34785fab12da82e445ed987a6efb34fd8c2fc0f6e6630dbcc1b0", size = 76356, upload-time = "2026-02-23T19:42:05.197Z" }, +] + [[package]] name = "httpcore" version = "1.0.9" @@ -194,6 +291,18 @@ wheels = [ { 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" }, ] +[[package]] +name = "id" +version = "1.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6d/04/c2156091427636080787aac190019dc64096e56a23b7364d3c1764ee3a06/id-1.6.1.tar.gz", hash = "sha256:d0732d624fb46fd4e7bc4e5152f00214450953b9e772c182c1c22964def1a069", size = 18088, upload-time = "2026-02-04T16:19:41.26Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/77/de194443bf38daed9452139e960c632b0ef9f9a5dd9ce605fdf18ca9f1b1/id-1.6.1-py3-none-any.whl", hash = "sha256:f5ec41ed2629a508f5d0988eda142e190c9c6da971100612c4de9ad9f9b237ca", size = 14689, upload-time = "2026-02-04T16:19:40.051Z" }, +] + [[package]] name = "idna" version = "3.11" @@ -212,6 +321,129 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, ] +[[package]] +name = "jaraco-classes" +version = "3.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "more-itertools" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/c0/ed4a27bc5571b99e3cff68f8a9fa5b56ff7df1c2251cc715a652ddd26402/jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", size = 11780, upload-time = "2024-03-31T07:27:36.643Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/66/b15ce62552d84bbfcec9a4873ab79d993a1dd4edb922cbfccae192bd5b5f/jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790", size = 6777, upload-time = "2024-03-31T07:27:34.792Z" }, +] + +[[package]] +name = "jaraco-context" +version = "6.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/af/50/4763cd07e722bb6285316d390a164bc7e479db9d90daa769f22578f698b4/jaraco_context-6.1.2.tar.gz", hash = "sha256:f1a6c9d391e661cc5b8d39861ff077a7dc24dc23833ccee564b234b81c82dfe3", size = 16801, upload-time = "2026-03-20T22:13:33.922Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f2/58/bc8954bda5fcda97bd7c19be11b85f91973d67a706ed4a3aec33e7de22db/jaraco_context-6.1.2-py3-none-any.whl", hash = "sha256:bf8150b79a2d5d91ae48629d8b427a8f7ba0e1097dd6202a9059f29a36379535", size = 7871, upload-time = "2026-03-20T22:13:32.808Z" }, +] + +[[package]] +name = "jaraco-functools" +version = "4.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "more-itertools" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0f/27/056e0638a86749374d6f57d0b0db39f29509cce9313cf91bdc0ac4d91084/jaraco_functools-4.4.0.tar.gz", hash = "sha256:da21933b0417b89515562656547a77b4931f98176eb173644c0d35032a33d6bb", size = 19943, upload-time = "2025-12-21T09:29:43.6Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/c4/813bb09f0985cb21e959f21f2464169eca882656849adf727ac7bb7e1767/jaraco_functools-4.4.0-py3-none-any.whl", hash = "sha256:9eec1e36f45c818d9bf307c8948eb03b2b56cd44087b3cdc989abca1f20b9176", size = 10481, upload-time = "2025-12-21T09:29:42.27Z" }, +] + +[[package]] +name = "jeepney" +version = "0.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7b/6f/357efd7602486741aa73ffc0617fb310a29b588ed0fd69c2399acbb85b0c/jeepney-0.9.0.tar.gz", hash = "sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732", size = 106758, upload-time = "2025-02-27T18:51:01.684Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b2/a3/e137168c9c44d18eff0376253da9f1e9234d0239e0ee230d2fee6cea8e55/jeepney-0.9.0-py3-none-any.whl", hash = "sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683", size = 49010, upload-time = "2025-02-27T18:51:00.104Z" }, +] + +[[package]] +name = "keyring" +version = "25.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jaraco-classes" }, + { name = "jaraco-context" }, + { name = "jaraco-functools" }, + { name = "jeepney", marker = "sys_platform == 'linux'" }, + { name = "pywin32-ctypes", marker = "sys_platform == 'win32'" }, + { name = "secretstorage", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/43/4b/674af6ef2f97d56f0ab5153bf0bfa28ccb6c3ed4d1babf4305449668807b/keyring-25.7.0.tar.gz", hash = "sha256:fe01bd85eb3f8fb3dd0405defdeac9a5b4f6f0439edbb3149577f244a2e8245b", size = 63516, upload-time = "2025-11-16T16:26:09.482Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/db/e655086b7f3a705df045bf0933bdd9c2f79bb3c97bfef1384598bb79a217/keyring-25.7.0-py3-none-any.whl", hash = "sha256:be4a0b195f149690c166e850609a477c532ddbfbaed96a404d4e43f8d5e2689f", size = 39160, upload-time = "2025-11-16T16:26:08.402Z" }, +] + +[[package]] +name = "markdown-it-py" +version = "4.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +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" } +wheels = [ + { 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" }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +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" } +wheels = [ + { 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" }, +] + +[[package]] +name = "more-itertools" +version = "11.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/f7/139d22fef48ac78127d18e01d80cf1be40236ae489769d17f35c3d425293/more_itertools-11.0.2.tar.gz", hash = "sha256:392a9e1e362cbc106a2457d37cabf9b36e5e12efd4ebff1654630e76597df804", size = 144659, upload-time = "2026-04-09T15:01:33.297Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/98/6af411189d9413534c3eb691182bff1f5c6d44ed2f93f2edfe52a1bbceb8/more_itertools-11.0.2-py3-none-any.whl", hash = "sha256:6e35b35f818b01f691643c6c611bc0902f2e92b46c18fffa77ae1e7c46e912e4", size = 71939, upload-time = "2026-04-09T15:01:32.21Z" }, +] + +[[package]] +name = "nh3" +version = "0.3.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9c/5f/1d19bdc7d27238e37f3672cdc02cb77c56a4a86d140cd4f4f23c90df6e16/nh3-0.3.5.tar.gz", hash = "sha256:45855e14ff056064fec77133bfcf7cd691838168e5e17bbef075394954dc9dc8", size = 20743, upload-time = "2026-04-25T10:44:16.066Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/63/b0/8587ac42a9627ab88e7e221601f1dfccbf4db80b2a29222ea63266dc9abc/nh3-0.3.5-cp314-cp314t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:23a312224875f72cd16bde417f49071451877e29ef646a60e50fcb69407cc18a", size = 1420126, upload-time = "2026-04-25T10:43:39.834Z" }, + { url = "https://files.pythonhosted.org/packages/c0/1b/1dbc4d0c43f12e8c1784ede17eaee6f061d4fbe5505757c65c49b2ceab95/nh3-0.3.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:387abd011e81959d5a35151a11350a0795c6edeb53ebfa02d2e882dc01299263", size = 793943, upload-time = "2026-04-25T10:43:41.363Z" }, + { url = "https://files.pythonhosted.org/packages/47/9f/d6758d7a14ee964bf439cc35ae4fa24a763a93399c8ef6f22bd11d532d29/nh3-0.3.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:48f45e3e914be93a596431aa143dedf1582557bf41a58153c296048d6e3798c9", size = 841150, upload-time = "2026-04-25T10:43:43.007Z" }, + { url = "https://files.pythonhosted.org/packages/b6/36/d5d1ae8374612c98f390e1ea7c610fa6c9716259a03bbf4d15b269f40073/nh3-0.3.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0a09f51806fd51b4fedbf9ea2b61fef388f19aef0d62fe51199d41648be14588", size = 1008415, upload-time = "2026-04-25T10:43:44.324Z" }, + { url = "https://files.pythonhosted.org/packages/ba/8f/d13a9c3fd2d9c131a2a281737380e9379eb0f8c33fea24c2b923aaafbb15/nh3-0.3.5-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:c357f1d042c67f135a5e6babb2b0e3b9d9224ff4a3543240f597767b01384ffd", size = 1092706, upload-time = "2026-04-25T10:43:45.653Z" }, + { url = "https://files.pythonhosted.org/packages/bb/57/2f3add7f8680fcc896afa6a675cb2bab09982853ee8af40bad621f6b61c4/nh3-0.3.5-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:38748140bf76383ab7ce2dce0ad4cb663855d8fbc9098f7f3483673d09616a17", size = 1048346, upload-time = "2026-04-25T10:43:46.974Z" }, + { url = "https://files.pythonhosted.org/packages/c1/c3/2f9e4ffa82863074d1361bfe949bc46393d91b3411579dfbbd090b24cac5/nh3-0.3.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:84bdeb082544fbcb77a12c034dd77d7da0556fdc0727b787eb6214b958c15e29", size = 1029038, upload-time = "2026-04-25T10:43:48.569Z" }, + { url = "https://files.pythonhosted.org/packages/e8/10/2804deb3f3315184c9cae41702e293c87524b5a21f766b07d7fe3ffbcfbb/nh3-0.3.5-cp314-cp314t-win32.whl", hash = "sha256:c3aae321f67ae66cff2a627115f106a377d4475d10b0e13d97959a13486b9a88", size = 603263, upload-time = "2026-04-25T10:43:49.851Z" }, + { url = "https://files.pythonhosted.org/packages/eb/a2/f6685248b49f7548fc9a8c335ab3a52f68610b72e8a61576447151e4e2e6/nh3-0.3.5-cp314-cp314t-win_amd64.whl", hash = "sha256:c88605d8d468f7fc1b31e06129bc91d6c96f6c621776c9b504a0da9beac9df5f", size = 616866, upload-time = "2026-04-25T10:43:51.005Z" }, + { url = "https://files.pythonhosted.org/packages/ca/b6/d8c9018635d4acfefde6b68470daa510eed715a350cbaa2f928ba0609f81/nh3-0.3.5-cp314-cp314t-win_arm64.whl", hash = "sha256:72c5bdedec27fa33de6a5326346ea8aa3fe54f6ac294d54c4b204fb66a9f1e79", size = 602566, upload-time = "2026-04-25T10:43:52.283Z" }, + { url = "https://files.pythonhosted.org/packages/85/30/d162e99746a2fb1d98bb0ef23af3e201b156cf09f7de867c7390c8fe1c06/nh3-0.3.5-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:3bb854485c9b33e5bb143ff3e49e577073bc6bc320f0ff8fc316dd89c0d3c101", size = 1442393, upload-time = "2026-04-25T10:43:53.556Z" }, + { url = "https://files.pythonhosted.org/packages/25/8c/072120d506978ab053e1732d0efa7c86cb478fee0ee098fda0ac0d31cb34/nh3-0.3.5-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50d401ab2d8e86d59e2126e3ab2a2f45840c405842b626d9a51624b3a33b6878", size = 837722, upload-time = "2026-04-25T10:43:55.073Z" }, + { url = "https://files.pythonhosted.org/packages/52/86/d4e06e28c5ad1c4b065f89737d02631bd49f1660b6ebcf17a87ffcd201da/nh3-0.3.5-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:acfd354e61accbe4c74f8017c6e397a776916dfe47c48643cf7fd84ade826f93", size = 822872, upload-time = "2026-04-25T10:43:56.581Z" }, + { url = "https://files.pythonhosted.org/packages/0a/62/50659255213f241ec5797ae7427464c969397373e83b3659372b341ae869/nh3-0.3.5-cp38-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:52d877980d7ca01dc3baf3936bf844828bc6f332962227a684ed79c18cce14c3", size = 1100031, upload-time = "2026-04-25T10:43:58.098Z" }, + { url = "https://files.pythonhosted.org/packages/00/7a/a12ae77593b2fcf3be25df7bc1c01967d0de448bdb4b6c7ec80fe4f5a74f/nh3-0.3.5-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:207c01801d3e9bb8ec08f08689346bdd30ce15b8bf60013a925d08b5388962a4", size = 1057669, upload-time = "2026-04-25T10:43:59.328Z" }, + { url = "https://files.pythonhosted.org/packages/2d/71/5647dc04c0233192a3956fc91708822b21403a06508cacf78083c68e7bf0/nh3-0.3.5-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea232933394d1d58bf7c4bb348dc4660eae6604e1ae81cd2ba6d9ed80d390f3b", size = 914795, upload-time = "2026-04-25T10:44:00.52Z" }, + { url = "https://files.pythonhosted.org/packages/1b/0e/bf298920729f216adcb002acf7ea01b90842603d2e4e2ce9b900d9ee8fab/nh3-0.3.5-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe3a787dc76b50de6bee54ef242f26c41dfe47654428e3e94f0fae5bb6dd2cc1", size = 806976, upload-time = "2026-04-25T10:44:01.743Z" }, + { url = "https://files.pythonhosted.org/packages/85/01/26761e1dc2b848e65a62c19e5d39ad446283287cd4afddc89f364ab86bc9/nh3-0.3.5-cp38-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:488928988caad25ba14b1eb5bc74e25e21f3b5e40341d956f3ce4a8bc19460dc", size = 834904, upload-time = "2026-04-25T10:44:03.454Z" }, + { url = "https://files.pythonhosted.org/packages/33/53/0766113e679540ac1edc1b82b1295aecd321eeb75d6fead70109a838b6ee/nh3-0.3.5-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2c069570b06aa848457713ad7af4a9905691291548c4466a9ad78ee95808382b", size = 857159, upload-time = "2026-04-25T10:44:05.003Z" }, + { url = "https://files.pythonhosted.org/packages/58/36/734d353dfaf292fed574b8b3092f0ef79dc6404f3879f7faaa61a4701fad/nh3-0.3.5-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:eeedc90ed8c42c327e8e10e621ccfa314fc6cce35d5929f4297ff1cdb89667c4", size = 1018600, upload-time = "2026-04-25T10:44:06.18Z" }, + { url = "https://files.pythonhosted.org/packages/6b/aa/d9c59c1b49669fcb7bababa55df82385f029ad5c2651f583c3a1141cfdd1/nh3-0.3.5-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:de8e8621853b6470fe928c684ee0d3f39ea8086cebafe4c416486488dea7b68d", size = 1103530, upload-time = "2026-04-25T10:44:07.68Z" }, + { url = "https://files.pythonhosted.org/packages/90/b0/cdd210bfb8d9d43fb02fc3c868336b9955934d8e15e66eb1d15a147b8af0/nh3-0.3.5-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:6ea58cc44d274c643b83547ca9654a0b1a817609b160601356f76a2b744c49ad", size = 1061754, upload-time = "2026-04-25T10:44:09.362Z" }, + { url = "https://files.pythonhosted.org/packages/ce/cb/7a39e72e668c8445bdd95e494b3e21cfdddc68329be8ea3522c8befb46c4/nh3-0.3.5-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e49c9b564e6bcb03ecd2f057213df9a0de15a95812ac9db9600b590db23d3ae9", size = 1040938, upload-time = "2026-04-25T10:44:10.775Z" }, + { url = "https://files.pythonhosted.org/packages/af/4c/fc2f9ed208a3801a319f59b5fea03cdc20cf3bd8af14be930d3a8de01224/nh3-0.3.5-cp38-abi3-win32.whl", hash = "sha256:559e4c73b689e9a7aa97ac9760b1bc488038d7c1a575aa4ab5a0e19ee9630c0f", size = 611445, upload-time = "2026-04-25T10:44:12.317Z" }, + { url = "https://files.pythonhosted.org/packages/db/1a/e4c9b5e2ae13e6092c9ec16d8ca30646cb01fcdea245f36c5b08fd21fbd5/nh3-0.3.5-cp38-abi3-win_amd64.whl", hash = "sha256:45e6a65dc88a300a2e3502cb9c8e6d1d6b831d6fba7470643333609c6aab1f30", size = 626502, upload-time = "2026-04-25T10:44:13.682Z" }, + { url = "https://files.pythonhosted.org/packages/80/7c/19cd0671d1ba2762fb388fc149697d20d0568ccfeef833b11280a619e526/nh3-0.3.5-cp38-abi3-win_arm64.whl", hash = "sha256:8f85285700a18e9f3fc5bff41fe573fa84f81542ef13b48a89f9fecca0474d3b", size = 611069, upload-time = "2026-04-25T10:44:14.934Z" }, +] + [[package]] name = "packaging" version = "26.0" @@ -221,6 +453,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, ] +[[package]] +name = "pathspec" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/82/42f767fc1c1143d6fd36efb827202a2d997a375e160a71eb2888a925aac1/pathspec-1.1.1.tar.gz", hash = "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a", size = 135180, upload-time = "2026-04-27T01:46:08.907Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/d9/7fb5aa316bc299258e68c73ba3bddbc499654a07f151cba08f6153988714/pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189", size = 57328, upload-time = "2026-04-27T01:46:07.06Z" }, +] + [[package]] name = "pluggy" version = "1.6.0" @@ -324,6 +565,56 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e5/35/f8b19922b6a25bc0880171a2f1a003eaeb93657475193ab516fd87cac9da/pytest_asyncio-1.3.0-py3-none-any.whl", hash = "sha256:611e26147c7f77640e6d0a92a38ed17c3e9848063698d5c93d5aa7aa11cebff5", size = 15075, upload-time = "2025-11-10T16:07:45.537Z" }, ] +[[package]] +name = "pywin32-ctypes" +version = "0.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471, upload-time = "2024-08-14T10:15:34.626Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756, upload-time = "2024-08-14T10:15:33.187Z" }, +] + +[[package]] +name = "readme-renderer" +version = "44.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "docutils" }, + { name = "nh3" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5a/a9/104ec9234c8448c4379768221ea6df01260cd6c2ce13182d4eac531c8342/readme_renderer-44.0.tar.gz", hash = "sha256:8712034eabbfa6805cacf1402b4eeb2a73028f72d1166d6f5cb7f9c047c5d1e1", size = 32056, upload-time = "2024-07-08T15:00:57.805Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/67/921ec3024056483db83953ae8e48079ad62b92db7880013ca77632921dd0/readme_renderer-44.0-py3-none-any.whl", hash = "sha256:2fbca89b81a08526aadf1357a8c2ae889ec05fb03f5da67f9769c9a592166151", size = 13310, upload-time = "2024-07-08T15:00:56.577Z" }, +] + +[[package]] +name = "requests" +version = "2.33.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5f/a4/98b9c7c6428a668bf7e42ebb7c79d576a1c3c1e3ae2d47e674b468388871/requests-2.33.1.tar.gz", hash = "sha256:18817f8c57c6263968bc123d237e3b8b08ac046f5456bd1e307ee8f4250d3517", size = 134120, upload-time = "2026-03-30T16:09:15.531Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d7/8e/7540e8a2036f79a125c1d2ebadf69ed7901608859186c856fa0388ef4197/requests-2.33.1-py3-none-any.whl", hash = "sha256:4e6d1ef462f3626a1f0a0a9c42dd93c63bad33f9f1c1937509b8c5c8718ab56a", size = 64947, upload-time = "2026-03-30T16:09:13.83Z" }, +] + +[[package]] +name = "requests-toolbelt" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f3/61/d7545dafb7ac2230c70d38d31cbfe4cc64f7144dc41f6e4e4b78ecd9f5bb/requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6", size = 206888, upload-time = "2023-05-01T04:11:33.229Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06", size = 54481, upload-time = "2023-05-01T04:11:28.427Z" }, +] + [[package]] name = "respx" version = "0.22.0" @@ -336,6 +627,28 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8e/67/afbb0978d5399bc9ea200f1d4489a23c9a1dad4eee6376242b8182389c79/respx-0.22.0-py2.py3-none-any.whl", hash = "sha256:631128d4c9aba15e56903fb5f66fb1eff412ce28dd387ca3a81339e52dbd3ad0", size = 25127, upload-time = "2024-12-19T22:33:57.837Z" }, ] +[[package]] +name = "rfc3986" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/85/40/1520d68bfa07ab5a6f065a186815fb6610c86fe957bc065754e47f7b0840/rfc3986-2.0.0.tar.gz", hash = "sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c", size = 49026, upload-time = "2022-01-10T00:52:30.832Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ff/9a/9afaade874b2fa6c752c36f1548f718b5b83af81ed9b76628329dab81c1b/rfc3986-2.0.0-py2.py3-none-any.whl", hash = "sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd", size = 31326, upload-time = "2022-01-10T00:52:29.594Z" }, +] + +[[package]] +name = "rich" +version = "15.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/8f/0722ca900cc807c13a6a0c696dacf35430f72e0ec571c4275d2371fca3e9/rich-15.0.0.tar.gz", hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36", size = 230680, upload-time = "2026-04-12T08:24:00.75Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb", size = 310654, upload-time = "2026-04-12T08:24:02.83Z" }, +] + [[package]] name = "ruff" version = "0.15.7" @@ -361,9 +674,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8f/e8/726643a3ea68c727da31570bde48c7a10f1aa60eddd628d94078fec586ff/ruff-0.15.7-py3-none-win_arm64.whl", hash = "sha256:18e8d73f1c3fdf27931497972250340f92e8c861722161a9caeb89a58ead6ed2", size = 11023304, upload-time = "2026-03-19T16:26:51.669Z" }, ] +[[package]] +name = "secretstorage" +version = "3.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cryptography" }, + { name = "jeepney" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1c/03/e834bcd866f2f8a49a85eaff47340affa3bfa391ee9912a952a1faa68c7b/secretstorage-3.5.0.tar.gz", hash = "sha256:f04b8e4689cbce351744d5537bf6b1329c6fc68f91fa666f60a380edddcd11be", size = 19884, upload-time = "2025-11-23T19:02:53.191Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/46/f5af3402b579fd5e11573ce652019a67074317e18c1935cc0b4ba9b35552/secretstorage-3.5.0-py3-none-any.whl", hash = "sha256:0ce65888c0725fcb2c5bc0fdb8e5438eece02c523557ea40ce0703c266248137", size = 15554, upload-time = "2025-11-23T19:02:51.545Z" }, +] + [[package]] name = "supertab-connect-sdk" -version = "0.0.1" +version = "0.1.0" source = { editable = "." } dependencies = [ { name = "httpx" }, @@ -373,17 +699,20 @@ dependencies = [ [package.optional-dependencies] dev = [ { name = "build" }, + { name = "hatchling" }, { name = "prek" }, { name = "pytest" }, { name = "pytest-asyncio" }, { name = "respx" }, { name = "ruff" }, + { name = "twine" }, { name = "ty" }, ] [package.metadata] requires-dist = [ { name = "build", marker = "extra == 'dev'", specifier = ">=1.4.2" }, + { name = "hatchling", marker = "extra == 'dev'", specifier = ">=1.26" }, { name = "httpx", specifier = ">=0.28,<1" }, { name = "prek", marker = "extra == 'dev'", specifier = ">=0.3.8" }, { name = "pyjwt", extras = ["crypto"], specifier = ">=2.10,<3" }, @@ -391,10 +720,40 @@ requires-dist = [ { name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.24" }, { name = "respx", marker = "extra == 'dev'", specifier = ">=0.22" }, { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.15.7" }, + { name = "twine", marker = "extra == 'dev'", specifier = ">=6" }, { name = "ty", marker = "extra == 'dev'", specifier = ">=0.0.25" }, ] provides-extras = ["dev"] +[[package]] +name = "trove-classifiers" +version = "2026.4.28.13" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/04/af/88fdebf242bc7bc4957c96c5358a2b2b0f07e5001401906783a521ea9f54/trove_classifiers-2026.4.28.13.tar.gz", hash = "sha256:c85bb8a53c3de7330d1699b844ed9fb809a602a09ac15dc79ad6d1a509be0676", size = 17035, upload-time = "2026-04-28T13:45:41.495Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d6/bb/fbdc4e57731efb86b4209ffdd2519520782bf27b3c961beac3e5c20d2b87/trove_classifiers-2026.4.28.13-py3-none-any.whl", hash = "sha256:8f4b1eb4e16296b57d612965444f87a83861cc989a0451ac97fe4265ddef03b8", size = 14216, upload-time = "2026-04-28T13:45:39.943Z" }, +] + +[[package]] +name = "twine" +version = "6.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "id" }, + { name = "keyring", marker = "platform_machine != 'ppc64le' and platform_machine != 's390x'" }, + { name = "packaging" }, + { name = "readme-renderer" }, + { name = "requests" }, + { name = "requests-toolbelt" }, + { name = "rfc3986" }, + { name = "rich" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e0/a8/949edebe3a82774c1ec34f637f5dd82d1cf22c25e963b7d63771083bbee5/twine-6.2.0.tar.gz", hash = "sha256:e5ed0d2fd70c9959770dce51c8f39c8945c574e18173a7b81802dab51b4b75cf", size = 172262, upload-time = "2025-09-04T15:43:17.255Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/7a/882d99539b19b1490cac5d77c67338d126e4122c8276bf640e411650c830/twine-6.2.0-py3-none-any.whl", hash = "sha256:418ebf08ccda9a8caaebe414433b0ba5e25eb5e4a927667122fbe8f829f985d8", size = 42727, upload-time = "2025-09-04T15:43:15.994Z" }, +] + [[package]] name = "ty" version = "0.0.25" @@ -427,3 +786,12 @@ sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac8 wheels = [ { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, ] + +[[package]] +name = "urllib3" +version = "2.6.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, +]