working
Some checks failed
Nix Tests / nix-test (nix-runner) (push) Failing after 6m22s
Python Tests / python-test (push) Failing after 8m12s

This commit is contained in:
2025-10-07 22:04:51 -05:00
parent 4399f61259
commit 5f46443d60
8 changed files with 452 additions and 1 deletions

63
CLAUDE.md Normal file
View File

@@ -0,0 +1,63 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
This is a Python CLI client for the trygo webapp built with a hybrid development environment using both uv and Nix. The project has dual entry points: `hello` (hello_world package) and `taco` (trygo_py_cliclient.cli package).
## Development Commands
### Core Commands (via justfile)
- `just test` - Run full test suite (ruff check + mypy + pytest)
- `just fmt` - Format code using nix fmt (includes ruff, nixfmt, etc.)
- `just update-snapshots` - Update test snapshots using syrupy
- `just build` - Build the Python module (currently placeholder)
- `just check` - Run nix flake check for Nix environment validation
### Direct Tool Commands
- `uv run pytest` - Run tests with coverage
- `uv run ruff check src tests` - Lint code
- `uv run mypy src` - Type checking
- `uv run pytest --snapshot-update` - Update test snapshots
## Architecture
### Package Structure
- `src/hello_world/` - Hello world demonstration package
- `src/trygo_py_cliclient/` - Main CLI client package
- `cli/` - Command-line interface and argument parsing
- `config/` - Configuration management with TOML support
### Configuration System
- Uses dataclasses for type-safe configuration (`src/trygo_py_cliclient/config/config.py`)
- TOML-based configuration files (`config.toml`)
- Supports logging configuration and general application settings
### Development Environment
The project supports both uv and Nix environments:
- **uv mode**: Standard Python toolchain with uv for dependency management
- **Nix mode**: Reproducible environment with integrated formatting tools
- Environment detection via `DO_NIX_CUSTOM` environment variable
### Testing
- pytest with syrupy for snapshot testing
- Coverage reporting enabled (50% minimum)
- Test configuration in pyproject.toml with XML and HTML output
### Code Quality
- ruff for linting and formatting (tab indentation style)
- mypy for type checking
- flake8 for additional linting
- treefmt.nix for comprehensive formatting in Nix environment
## Entry Points
- `hello` command maps to `hello_world:main`
- `taco` command maps to `trygo_py_cliclient.cli:main`
## Configuration Files
- `pyproject.toml` - Python project configuration and dependencies
- `config.toml` - Application runtime configuration
- `flake.nix` - Nix development environment
- `treefmt.nix` - Code formatting configuration
- `justfile` - Development task automation

View File

@@ -5,7 +5,7 @@ description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
#"urllib3>=2.2.3",
"requests>=2.31.0",
"dacite>=1.9.2",
"tomli>=2.2.1",
]

View File

@@ -2,6 +2,7 @@ import argparse
import pathlib
import logging
import trygo_py_cliclient.config
import trygo_py_cliclient.client
import trygo_py_cliclient.cli.common
_logger = logging.getLogger(__name__)
@@ -15,6 +16,13 @@ def parse_args():
parser.add_argument(
"--config-file", type=str, help="config file location", default="config.toml"
)
parser.add_argument(
"--display-name", type=str, help="config file location", default=""
)
parser.add_argument("--email", type=str, help="config file location", default="")
parser.add_argument("--password", type=str, help="config file location", default="")
args = parser.parse_args()
return args
@@ -28,3 +36,7 @@ def main():
config,
)
_logger.info(f"Loaded config {config=}")
clnt = trygo_py_cliclient.client.TryGoAPIClient("http://localhost:8080/")
response = clnt.register(args.email, args.password, args.display_name)

View File

@@ -0,0 +1,219 @@
import requests
import logging
from typing import Optional, Dict, Any
from dataclasses import asdict
from trygo_py_cliclient.models import User, RegisterRequest, LoginRequest, AuthResponse
from trygo_py_cliclient.exceptions import (
TryGoAPIError,
AuthenticationError,
ValidationError,
NetworkError,
ServerError,
)
_logger = logging.getLogger(__name__)
class TryGoAPIClient:
"""HTTP client for TryGo webapp REST API"""
def __init__(self, base_url: str, timeout: int = 30):
self.base_url = base_url.rstrip("/")
self.timeout = timeout
self.session = requests.Session()
self.session.headers.update(
{"Content-Type": "application/json", "User-Agent": "trygo-py-client/0.1.0"}
)
self._auth_token: Optional[str] = None
def _make_request(
self, method: str, endpoint: str, data: Optional[Dict[str, Any]] = None
) -> Dict[str, Any]:
"""
Make HTTP request to API endpoint
Args:
method: HTTP method (GET, POST, PUT, DELETE)
endpoint: API endpoint path
data: Request payload data
Returns:
Response data as dictionary
Raises:
NetworkError: For connection/timeout errors
AuthenticationError: For 401 errors
ValidationError: For 400 errors
ServerError: For 5xx errors
TryGoAPIError: For other API errors
"""
url = f"{self.base_url}{endpoint}"
_logger.info(f"Making {method} request to {url}")
try:
response = self.session.request(
method=method, url=url, json=data, timeout=self.timeout
)
# Log response status
_logger.debug(f"Response status: {response.status_code}")
# Handle different HTTP status codes
if response.status_code == 401:
raise AuthenticationError("Authentication failed")
elif response.status_code == 400:
error_msg = "Validation error"
try:
error_data = response.json()
if "message" in error_data:
error_msg = error_data["message"]
except Exception:
pass
raise ValidationError(error_msg)
elif 500 <= response.status_code < 600:
raise ServerError(f"Server error: {response.status_code}")
elif not response.ok:
raise TryGoAPIError(f"API error: {response.status_code}")
# Parse JSON response
try:
return response.json()
except ValueError as e:
raise TryGoAPIError(f"Invalid JSON response: {e}")
except requests.exceptions.Timeout:
raise NetworkError("Request timeout")
except requests.exceptions.ConnectionError:
raise NetworkError("Connection error")
except requests.exceptions.RequestException as e:
raise NetworkError(f"Network error: {e}")
def register(self, email: str, password: str, display_name: str) -> AuthResponse:
"""
Register a new user account
Args:
email: User's email address
password: User's password
display_name: User's display name
Returns:
AuthResponse with registration result
Raises:
ValidationError: If registration data is invalid
TryGoAPIError: If registration fails
"""
request_data = RegisterRequest(
email=email, password=password, display_name=display_name
)
_logger.info(f"Registering user: {email}")
response_data = self._make_request(
"POST", "/auth/register", asdict(request_data)
)
# Parse response and create AuthResponse
auth_response = AuthResponse(
success=response_data.get("success", False),
message=response_data.get("message", ""),
token=response_data.get("token"),
user=None,
)
# Parse user data if present
if "user" in response_data and response_data["user"]:
user_data = response_data["user"]
auth_response.user = User(
email=user_data.get("email", ""),
password="", # Never store password in response
display_name=user_data.get("display_name", ""),
id=user_data.get("id"),
)
# Store auth token on successful registration
if auth_response.success and auth_response.token:
self.set_auth_token(auth_response.token)
return auth_response
def login(self, email: str, password: str) -> AuthResponse:
"""
Authenticate user and get auth token
Args:
email: User's email address
password: User's password
Returns:
AuthResponse with login result and token
Raises:
AuthenticationError: If login credentials are invalid
TryGoAPIError: If login fails
"""
request_data = LoginRequest(email=email, password=password)
_logger.info(f"Logging in user: {email}")
response_data = self._make_request("POST", "/auth/login", asdict(request_data))
# Parse response and create AuthResponse
auth_response = AuthResponse(
success=response_data.get("success", False),
message=response_data.get("message", ""),
token=response_data.get("token"),
user=None,
)
# Parse user data if present
if "user" in response_data and response_data["user"]:
user_data = response_data["user"]
auth_response.user = User(
email=user_data.get("email", ""),
password="", # Never store password in response
display_name=user_data.get("display_name", ""),
id=user_data.get("id"),
)
# Store auth token on successful login
if auth_response.success and auth_response.token:
self.set_auth_token(auth_response.token)
return auth_response
def set_auth_token(self, token: str) -> None:
"""
Set authentication token for subsequent requests
Args:
token: JWT or session token for authentication
"""
self._auth_token = token
self.session.headers["Authorization"] = f"Bearer {token}"
_logger.debug("Authentication token set")
def logout(self) -> None:
"""
Clear authentication token and session data
Optionally calls logout endpoint if server supports it
"""
_logger.info("Logging out user")
# Try to call logout endpoint if we have a token
if self._auth_token:
try:
self._make_request("POST", "/auth/logout")
_logger.debug("Server logout successful")
except Exception as e:
_logger.warning(f"Server logout failed (continuing anyway): {e}")
# Clear local auth state
self._auth_token = None
if "Authorization" in self.session.headers:
del self.session.headers["Authorization"]
_logger.debug("Local authentication cleared")

View File

@@ -2,6 +2,7 @@ from dataclasses import dataclass
from typing import Optional
@dataclass(frozen=True)
class GeneralConfig:
log_pattern: str = "%(asctime)s | %(process)d | %(levelname)-7s | %(name)s:%(lineno)d | %(message)s"

View File

@@ -0,0 +1,31 @@
"""Custom exceptions for TryGo API client"""
class TryGoAPIError(Exception):
"""Base exception for TryGo API client errors"""
pass
class AuthenticationError(TryGoAPIError):
"""Raised when authentication fails"""
pass
class ValidationError(TryGoAPIError):
"""Raised when request validation fails"""
pass
class NetworkError(TryGoAPIError):
"""Raised when network requests fail"""
pass
class ServerError(TryGoAPIError):
"""Raised when server returns 5xx errors"""
pass

View File

@@ -0,0 +1,39 @@
from dataclasses import dataclass
from typing import Optional
@dataclass
class User:
"""User model for API requests and responses"""
email: str
password: str
display_name: str
id: Optional[str] = None
@dataclass
class RegisterRequest:
"""Request payload for /auth/register endpoint"""
email: str
password: str
display_name: str
@dataclass
class LoginRequest:
"""Request payload for /auth/login endpoint"""
email: str
password: str
@dataclass
class AuthResponse:
"""Response from authentication endpoints"""
success: bool
message: str
user: Optional[User] = None
token: Optional[str] = None

86
uv.lock generated
View File

@@ -2,6 +2,57 @@ version = 1
revision = 3
requires-python = ">=3.12"
[[package]]
name = "certifi"
version = "2025.10.5"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/4c/5b/b6ce21586237c77ce67d01dc5507039d444b630dd76611bbca2d8e5dcd91/certifi-2025.10.5.tar.gz", hash = "sha256:47c09d31ccf2acf0be3f701ea53595ee7e0b8fa08801c6624be771df09ae7b43", size = 164519, upload-time = "2025-10-05T04:12:15.808Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e4/37/af0d2ef3967ac0d6113837b44a4f0bfe1328c2b9763bd5b1744520e5cfed/certifi-2025.10.5-py3-none-any.whl", hash = "sha256:0f212c2744a9bb6de0c56639a6f68afe01ecd92d91f14ae897c4fe7bbeeef0de", size = 163286, upload-time = "2025-10-05T04:12:14.03Z" },
]
[[package]]
name = "charset-normalizer"
version = "3.4.3"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/83/2d/5fd176ceb9b2fc619e63405525573493ca23441330fcdaee6bef9460e924/charset_normalizer-3.4.3.tar.gz", hash = "sha256:6fce4b8500244f6fcb71465d4a4930d132ba9ab8e71a7859e6a5d59851068d14", size = 122371, upload-time = "2025-08-09T07:57:28.46Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e9/5e/14c94999e418d9b87682734589404a25854d5f5d0408df68bc15b6ff54bb/charset_normalizer-3.4.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e28e334d3ff134e88989d90ba04b47d84382a828c061d0d1027b1b12a62b39b1", size = 205655, upload-time = "2025-08-09T07:56:08.475Z" },
{ url = "https://files.pythonhosted.org/packages/7d/a8/c6ec5d389672521f644505a257f50544c074cf5fc292d5390331cd6fc9c3/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0cacf8f7297b0c4fcb74227692ca46b4a5852f8f4f24b3c766dd94a1075c4884", size = 146223, upload-time = "2025-08-09T07:56:09.708Z" },
{ url = "https://files.pythonhosted.org/packages/fc/eb/a2ffb08547f4e1e5415fb69eb7db25932c52a52bed371429648db4d84fb1/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c6fd51128a41297f5409deab284fecbe5305ebd7e5a1f959bee1c054622b7018", size = 159366, upload-time = "2025-08-09T07:56:11.326Z" },
{ url = "https://files.pythonhosted.org/packages/82/10/0fd19f20c624b278dddaf83b8464dcddc2456cb4b02bb902a6da126b87a1/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cfb2aad70f2c6debfbcb717f23b7eb55febc0bb23dcffc0f076009da10c6392", size = 157104, upload-time = "2025-08-09T07:56:13.014Z" },
{ url = "https://files.pythonhosted.org/packages/16/ab/0233c3231af734f5dfcf0844aa9582d5a1466c985bbed6cedab85af9bfe3/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1606f4a55c0fd363d754049cdf400175ee96c992b1f8018b993941f221221c5f", size = 151830, upload-time = "2025-08-09T07:56:14.428Z" },
{ url = "https://files.pythonhosted.org/packages/ae/02/e29e22b4e02839a0e4a06557b1999d0a47db3567e82989b5bb21f3fbbd9f/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:027b776c26d38b7f15b26a5da1044f376455fb3766df8fc38563b4efbc515154", size = 148854, upload-time = "2025-08-09T07:56:16.051Z" },
{ url = "https://files.pythonhosted.org/packages/05/6b/e2539a0a4be302b481e8cafb5af8792da8093b486885a1ae4d15d452bcec/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:42e5088973e56e31e4fa58eb6bd709e42fc03799c11c42929592889a2e54c491", size = 160670, upload-time = "2025-08-09T07:56:17.314Z" },
{ url = "https://files.pythonhosted.org/packages/31/e7/883ee5676a2ef217a40ce0bffcc3d0dfbf9e64cbcfbdf822c52981c3304b/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cc34f233c9e71701040d772aa7490318673aa7164a0efe3172b2981218c26d93", size = 158501, upload-time = "2025-08-09T07:56:18.641Z" },
{ url = "https://files.pythonhosted.org/packages/c1/35/6525b21aa0db614cf8b5792d232021dca3df7f90a1944db934efa5d20bb1/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:320e8e66157cc4e247d9ddca8e21f427efc7a04bbd0ac8a9faf56583fa543f9f", size = 153173, upload-time = "2025-08-09T07:56:20.289Z" },
{ url = "https://files.pythonhosted.org/packages/50/ee/f4704bad8201de513fdc8aac1cabc87e38c5818c93857140e06e772b5892/charset_normalizer-3.4.3-cp312-cp312-win32.whl", hash = "sha256:fb6fecfd65564f208cbf0fba07f107fb661bcd1a7c389edbced3f7a493f70e37", size = 99822, upload-time = "2025-08-09T07:56:21.551Z" },
{ url = "https://files.pythonhosted.org/packages/39/f5/3b3836ca6064d0992c58c7561c6b6eee1b3892e9665d650c803bd5614522/charset_normalizer-3.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:86df271bf921c2ee3818f0522e9a5b8092ca2ad8b065ece5d7d9d0e9f4849bcc", size = 107543, upload-time = "2025-08-09T07:56:23.115Z" },
{ url = "https://files.pythonhosted.org/packages/65/ca/2135ac97709b400c7654b4b764daf5c5567c2da45a30cdd20f9eefe2d658/charset_normalizer-3.4.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:14c2a87c65b351109f6abfc424cab3927b3bdece6f706e4d12faaf3d52ee5efe", size = 205326, upload-time = "2025-08-09T07:56:24.721Z" },
{ url = "https://files.pythonhosted.org/packages/71/11/98a04c3c97dd34e49c7d247083af03645ca3730809a5509443f3c37f7c99/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41d1fc408ff5fdfb910200ec0e74abc40387bccb3252f3f27c0676731df2b2c8", size = 146008, upload-time = "2025-08-09T07:56:26.004Z" },
{ url = "https://files.pythonhosted.org/packages/60/f5/4659a4cb3c4ec146bec80c32d8bb16033752574c20b1252ee842a95d1a1e/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1bb60174149316da1c35fa5233681f7c0f9f514509b8e399ab70fea5f17e45c9", size = 159196, upload-time = "2025-08-09T07:56:27.25Z" },
{ url = "https://files.pythonhosted.org/packages/86/9e/f552f7a00611f168b9a5865a1414179b2c6de8235a4fa40189f6f79a1753/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:30d006f98569de3459c2fc1f2acde170b7b2bd265dc1943e87e1a4efe1b67c31", size = 156819, upload-time = "2025-08-09T07:56:28.515Z" },
{ url = "https://files.pythonhosted.org/packages/7e/95/42aa2156235cbc8fa61208aded06ef46111c4d3f0de233107b3f38631803/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:416175faf02e4b0810f1f38bcb54682878a4af94059a1cd63b8747244420801f", size = 151350, upload-time = "2025-08-09T07:56:29.716Z" },
{ url = "https://files.pythonhosted.org/packages/c2/a9/3865b02c56f300a6f94fc631ef54f0a8a29da74fb45a773dfd3dcd380af7/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6aab0f181c486f973bc7262a97f5aca3ee7e1437011ef0c2ec04b5a11d16c927", size = 148644, upload-time = "2025-08-09T07:56:30.984Z" },
{ url = "https://files.pythonhosted.org/packages/77/d9/cbcf1a2a5c7d7856f11e7ac2d782aec12bdfea60d104e60e0aa1c97849dc/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabf8315679312cfa71302f9bd509ded4f2f263fb5b765cf1433b39106c3cc9", size = 160468, upload-time = "2025-08-09T07:56:32.252Z" },
{ url = "https://files.pythonhosted.org/packages/f6/42/6f45efee8697b89fda4d50580f292b8f7f9306cb2971d4b53f8914e4d890/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:bd28b817ea8c70215401f657edef3a8aa83c29d447fb0b622c35403780ba11d5", size = 158187, upload-time = "2025-08-09T07:56:33.481Z" },
{ url = "https://files.pythonhosted.org/packages/70/99/f1c3bdcfaa9c45b3ce96f70b14f070411366fa19549c1d4832c935d8e2c3/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:18343b2d246dc6761a249ba1fb13f9ee9a2bcd95decc767319506056ea4ad4dc", size = 152699, upload-time = "2025-08-09T07:56:34.739Z" },
{ url = "https://files.pythonhosted.org/packages/a3/ad/b0081f2f99a4b194bcbb1934ef3b12aa4d9702ced80a37026b7607c72e58/charset_normalizer-3.4.3-cp313-cp313-win32.whl", hash = "sha256:6fb70de56f1859a3f71261cbe41005f56a7842cc348d3aeb26237560bfa5e0ce", size = 99580, upload-time = "2025-08-09T07:56:35.981Z" },
{ url = "https://files.pythonhosted.org/packages/9a/8f/ae790790c7b64f925e5c953b924aaa42a243fb778fed9e41f147b2a5715a/charset_normalizer-3.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:cf1ebb7d78e1ad8ec2a8c4732c7be2e736f6e5123a4146c5b89c9d1f585f8cef", size = 107366, upload-time = "2025-08-09T07:56:37.339Z" },
{ url = "https://files.pythonhosted.org/packages/8e/91/b5a06ad970ddc7a0e513112d40113e834638f4ca1120eb727a249fb2715e/charset_normalizer-3.4.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3cd35b7e8aedeb9e34c41385fda4f73ba609e561faedfae0a9e75e44ac558a15", size = 204342, upload-time = "2025-08-09T07:56:38.687Z" },
{ url = "https://files.pythonhosted.org/packages/ce/ec/1edc30a377f0a02689342f214455c3f6c2fbedd896a1d2f856c002fc3062/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b89bc04de1d83006373429975f8ef9e7932534b8cc9ca582e4db7d20d91816db", size = 145995, upload-time = "2025-08-09T07:56:40.048Z" },
{ url = "https://files.pythonhosted.org/packages/17/e5/5e67ab85e6d22b04641acb5399c8684f4d37caf7558a53859f0283a650e9/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2001a39612b241dae17b4687898843f254f8748b796a2e16f1051a17078d991d", size = 158640, upload-time = "2025-08-09T07:56:41.311Z" },
{ url = "https://files.pythonhosted.org/packages/f1/e5/38421987f6c697ee3722981289d554957c4be652f963d71c5e46a262e135/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8dcfc373f888e4fb39a7bc57e93e3b845e7f462dacc008d9749568b1c4ece096", size = 156636, upload-time = "2025-08-09T07:56:43.195Z" },
{ url = "https://files.pythonhosted.org/packages/a0/e4/5a075de8daa3ec0745a9a3b54467e0c2967daaaf2cec04c845f73493e9a1/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18b97b8404387b96cdbd30ad660f6407799126d26a39ca65729162fd810a99aa", size = 150939, upload-time = "2025-08-09T07:56:44.819Z" },
{ url = "https://files.pythonhosted.org/packages/02/f7/3611b32318b30974131db62b4043f335861d4d9b49adc6d57c1149cc49d4/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ccf600859c183d70eb47e05a44cd80a4ce77394d1ac0f79dbd2dd90a69a3a049", size = 148580, upload-time = "2025-08-09T07:56:46.684Z" },
{ url = "https://files.pythonhosted.org/packages/7e/61/19b36f4bd67f2793ab6a99b979b4e4f3d8fc754cbdffb805335df4337126/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:53cd68b185d98dde4ad8990e56a58dea83a4162161b1ea9272e5c9182ce415e0", size = 159870, upload-time = "2025-08-09T07:56:47.941Z" },
{ url = "https://files.pythonhosted.org/packages/06/57/84722eefdd338c04cf3030ada66889298eaedf3e7a30a624201e0cbe424a/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:30a96e1e1f865f78b030d65241c1ee850cdf422d869e9028e2fc1d5e4db73b92", size = 157797, upload-time = "2025-08-09T07:56:49.756Z" },
{ url = "https://files.pythonhosted.org/packages/72/2a/aff5dd112b2f14bcc3462c312dce5445806bfc8ab3a7328555da95330e4b/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d716a916938e03231e86e43782ca7878fb602a125a91e7acb8b5112e2e96ac16", size = 152224, upload-time = "2025-08-09T07:56:51.369Z" },
{ url = "https://files.pythonhosted.org/packages/b7/8c/9839225320046ed279c6e839d51f028342eb77c91c89b8ef2549f951f3ec/charset_normalizer-3.4.3-cp314-cp314-win32.whl", hash = "sha256:c6dbd0ccdda3a2ba7c2ecd9d77b37f3b5831687d8dc1b6ca5f56a4880cc7b7ce", size = 100086, upload-time = "2025-08-09T07:56:52.722Z" },
{ url = "https://files.pythonhosted.org/packages/ee/7a/36fbcf646e41f710ce0a563c1c9a343c6edf9be80786edeb15b6f62e17db/charset_normalizer-3.4.3-cp314-cp314-win_amd64.whl", hash = "sha256:73dc19b562516fc9bcf6e5d6e596df0b4eb98d87e4f79f3ae71840e6ed21361c", size = 107400, upload-time = "2025-08-09T07:56:55.172Z" },
{ url = "https://files.pythonhosted.org/packages/8a/1f/f041989e93b001bc4e44bb1669ccdcf54d3f00e628229a85b08d330615c5/charset_normalizer-3.4.3-py3-none-any.whl", hash = "sha256:ce571ab16d890d23b5c278547ba694193a45011ff86a9162a71307ed9f86759a", size = 53175, upload-time = "2025-08-09T07:57:26.864Z" },
]
[[package]]
name = "colorama"
version = "0.4.6"
@@ -79,6 +130,7 @@ version = "0.1.0"
source = { editable = "." }
dependencies = [
{ name = "dacite" },
{ name = "requests" },
{ name = "tomli" },
]
@@ -95,6 +147,7 @@ dev = [
[package.metadata]
requires-dist = [
{ name = "dacite", specifier = ">=1.9.2" },
{ name = "requests", specifier = ">=2.31.0" },
{ name = "tomli", specifier = ">=2.2.1" },
]
@@ -108,6 +161,15 @@ dev = [
{ name = "syrupy", specifier = ">=4.9.0" },
]
[[package]]
name = "idna"
version = "3.10"
source = { registry = "https://pypi.org/simple" }
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" }
wheels = [
{ 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" },
]
[[package]]
name = "iniconfig"
version = "2.0.0"
@@ -224,6 +286,21 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/36/3b/48e79f2cd6a61dbbd4807b4ed46cb564b4fd50a76166b1c4ea5c1d9e2371/pytest_cov-6.0.0-py3-none-any.whl", hash = "sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35", size = 22949, upload-time = "2024-10-29T20:13:33.215Z" },
]
[[package]]
name = "requests"
version = "2.32.5"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "certifi" },
{ name = "charset-normalizer" },
{ name = "idna" },
{ name = "urllib3" },
]
sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" },
]
[[package]]
name = "ruff"
version = "0.9.10"
@@ -298,3 +375,12 @@ sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec3
wheels = [
{ url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438, upload-time = "2024-06-07T18:52:13.582Z" },
]
[[package]]
name = "urllib3"
version = "2.5.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185, upload-time = "2025-06-18T14:07:41.644Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" },
]