Compare commits
29 Commits
v0.1.4
...
bugfix/tim
| Author | SHA1 | Date | |
|---|---|---|---|
| 06b4a8cbcb | |||
| 04e5dc652f | |||
| 4a7db8e204 | |||
| aa9477ffe8 | |||
| 2bb3dbd71d | |||
| 3f26a2fbcf | |||
| 2faf0dd0ae | |||
| 68c7d0de42 | |||
| ad64c85393 | |||
| bab53aedbe | |||
| 82e181dd7e | |||
| ee1f55635f | |||
| 6bdf28e2ae | |||
| 61bc040098 | |||
| 7b35ffb60c | |||
| 42bcc792d6 | |||
| 3f97c73b2f | |||
| 7e7ecbd48a | |||
| 7b9a06d1b5 | |||
| 3d0eda5c60 | |||
| eecf1dc65b | |||
| 3cced768a4 | |||
| 0ac9bf79ee | |||
| bf5914c0a8 | |||
| 976af9a209 | |||
| f3fd6865f9 | |||
| 340ed46df6 | |||
| a5bf66c199 | |||
| f51a962fff |
6
.gitignore
vendored
6
.gitignore
vendored
@ -175,3 +175,9 @@ cython_debug/
|
|||||||
.pypirc
|
.pypirc
|
||||||
|
|
||||||
CODE_EXECUTION.md
|
CODE_EXECUTION.md
|
||||||
|
|
||||||
|
.opencode/
|
||||||
|
# AI
|
||||||
|
.code-review-graph/
|
||||||
|
.claude
|
||||||
|
.mcp.json
|
||||||
|
|||||||
25
.pre-commit-config.yaml
Normal file
25
.pre-commit-config.yaml
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
repos:
|
||||||
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
|
rev: v0.15.10
|
||||||
|
hooks:
|
||||||
|
- id: ruff
|
||||||
|
- id: ruff-format
|
||||||
|
|
||||||
|
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||||
|
rev: v1.20.0
|
||||||
|
hooks:
|
||||||
|
- id: mypy
|
||||||
|
additional_dependencies:
|
||||||
|
- pydantic>=2.12.5
|
||||||
|
- httpx>=0.28.1
|
||||||
|
- httpx-ws>=0.9.0
|
||||||
|
- email-validator>=2.3.0
|
||||||
|
|
||||||
|
- repo: local
|
||||||
|
hooks:
|
||||||
|
- id: unit-tests
|
||||||
|
name: unit tests
|
||||||
|
entry: uv run pytest -m "not integration" -x -q
|
||||||
|
language: system
|
||||||
|
pass_filenames: false
|
||||||
|
always_run: true
|
||||||
@ -1,8 +1,11 @@
|
|||||||
when:
|
when:
|
||||||
event: push
|
event: pull_request
|
||||||
branch:
|
branch:
|
||||||
- main
|
- main
|
||||||
- dev
|
- dev
|
||||||
|
path:
|
||||||
|
- "src/**"
|
||||||
|
- "tests/**"
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
unit-tests:
|
unit-tests:
|
||||||
|
|||||||
56
AGENTS.md
Normal file
56
AGENTS.md
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
# AGENTS.md
|
||||||
|
|
||||||
|
## Project
|
||||||
|
|
||||||
|
Wrenn Python SDK — a client library for the Wrenn microVM platform. e2b drop-in replacement.
|
||||||
|
Package name: `wrenn`. Python 3.13+, managed with [uv](https://docs.astral.sh/uv/).
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uv sync # install deps
|
||||||
|
make lint # ruff check + format check (no auto-fix)
|
||||||
|
make test # unit tests only (tests/test_client.py)
|
||||||
|
make test-integration # all tests including integration (needs live server)
|
||||||
|
make generate # regenerate models from OpenAPI spec (fetches from remote)
|
||||||
|
make check # lint + unit test
|
||||||
|
```
|
||||||
|
|
||||||
|
- `make test` only runs `tests/test_client.py`, not all unit tests. To run a specific test file: `uv run pytest tests/test_capsule_features.py -v`
|
||||||
|
- No typecheck step in Makefile or CI. `mypy` is a dev dependency but not wired up — do not assume it runs.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
- `src/wrenn/` — the library package
|
||||||
|
- `capsule.py` / `async_capsule.py` — high-level `Capsule` / `AsyncCapsule` (main user-facing classes)
|
||||||
|
- `client.py` — low-level `WrennClient` / `AsyncWrennClient`
|
||||||
|
- `commands.py` — command execution and streaming
|
||||||
|
- `files.py` — filesystem operations
|
||||||
|
- `pty.py` — interactive terminal (PTY) over WebSocket
|
||||||
|
- `exceptions.py` — typed error hierarchy (`WrennError` base)
|
||||||
|
- `models/_generated.py` — **auto-generated** from OpenAPI spec via `datamodel-codegen` (never edit directly; run `make generate`)
|
||||||
|
- `sandbox.py` — deprecated `Sandbox` alias for `Capsule`
|
||||||
|
- `code_interpreter/` — specialized capsule for stateful Jupyter kernel execution
|
||||||
|
- `tests/` — unit tests use `respx` to mock `httpx`; integration tests are in `tests/integration/`
|
||||||
|
- `api/openapi.yaml` — downloaded OpenAPI spec used for code generation
|
||||||
|
|
||||||
|
## Key Conventions
|
||||||
|
|
||||||
|
- Generated code lives in `src/wrenn/models/_generated.py`. Never edit it. Run `make generate` to update.
|
||||||
|
- `Sandbox` is a deprecated alias for `Capsule`. New code should use `Capsule` / `AsyncCapsule`.
|
||||||
|
- Dual sync/async API: every major class has an `Async` counterpart.
|
||||||
|
- Uses `httpx` for HTTP, `httpx-ws` for WebSockets, `pydantic` for models.
|
||||||
|
- `__init__.py` uses `__getattr__` for lazy deprecated aliases (`Sandbox`, `WrennHostHasSandboxesError`).
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
- Unit tests mock HTTP via `respx` (httpx mocking library).
|
||||||
|
- Integration tests require env vars: `WRENN_API_KEY` (or `WRENN_TOKEN`), optionally `WRENN_BASE_URL`.
|
||||||
|
- Integration test fixtures in `tests/integration/conftest.py` create real capsules and clean them up.
|
||||||
|
- `pytest` marker: `@pytest.mark.integration` for tests needing a live server.
|
||||||
|
|
||||||
|
## CI
|
||||||
|
|
||||||
|
Woodpecker CI (`.woodpecker/check.yml`) runs on push to `main` and `dev`:
|
||||||
|
1. `make lint`
|
||||||
|
2. `make test` (unit tests only — integration tests are not in CI)
|
||||||
132
CLAUDE.md
132
CLAUDE.md
@ -1,132 +0,0 @@
|
|||||||
## Design Context
|
|
||||||
|
|
||||||
### Users
|
|
||||||
Developers across the full spectrum — solo engineers building side projects, startup teams integrating sandboxed execution into products, and platform/infra engineers at larger organizations running production workloads on Firecracker microVMs. They arrive with context: they know what a process is, what a rootfs is, what a TTY means. The interface must feel at home for all three: approachable enough not to intimidate a hacker, precise enough to earn the trust of a production ops team. Never condescend, never oversimplify. Trust the user to understand what they're looking at.
|
|
||||||
|
|
||||||
**Primary job to be done:** Understand what's running, act on it confidently, and get back to code.
|
|
||||||
|
|
||||||
### Brand Personality
|
|
||||||
**Precise. Warm. Uncompromising.**
|
|
||||||
|
|
||||||
Wrenn is an engineer's favorite tool — built with visible care, not assembled from defaults. It runs real infrastructure (Firecracker microVMs), so the UI should reflect that seriousness without becoming cold or corporate. The warmth comes from the typography and color palette; the precision comes from hierarchy, density, and data fidelity.
|
|
||||||
|
|
||||||
Emotional goal: **in control.** Users leave a session with full confidence in what's running, what happened, and what comes next. Nothing is hidden, nothing is ambiguous.
|
|
||||||
|
|
||||||
### Aesthetic Direction
|
|
||||||
**Dark-only (permanently), industrial-warm, data-forward.**
|
|
||||||
|
|
||||||
No light mode planned. All design decisions should optimize for dark. The near-black-green background palette (`#0a0c0b` through `#2a302d`) reads as "black with intention" — not pitch black (cold) and not charcoal (dated). The sage green accent (`#5e8c58`) is muted and organic, a meaningful departure from the startup-green neon that saturates the developer tool space.
|
|
||||||
|
|
||||||
**Anti-references:**
|
|
||||||
- **Supabase**: avoid the friendly, approachable startup-green energy — too generic, too eager to please
|
|
||||||
- **AWS / GCP consoles**: avoid utility-first density without craft — functional but joyless, visually dated
|
|
||||||
|
|
||||||
**References that capture the right spirit:**
|
|
||||||
- The precision of a well-calibrated instrument
|
|
||||||
- Editorial typography from technical publications
|
|
||||||
- The quiet confidence of tools that don't need to explain themselves
|
|
||||||
|
|
||||||
### Type System
|
|
||||||
Four fonts with strict roles — this is the design system's strongest personality trait and must be respected:
|
|
||||||
|
|
||||||
| Font | CSS Class | Role | When to use |
|
|
||||||
|------|-----------|------|-------------|
|
|
||||||
| **Manrope** (variable, sans) | `font-sans` | UI workhorse | All body copy, nav, labels, buttons, form text |
|
|
||||||
| **Instrument Serif** | `font-serif` | Display / editorial | Page titles (h1), dialog headings, metric values, hero moments |
|
|
||||||
| **JetBrains Mono** (variable) | `font-mono` | Data / code | IDs, timestamps, key prefixes, file paths, terminal output, metrics |
|
|
||||||
| **Alice** | brand wordmark only | Brand wordmark | "Wrenn" in sidebar and login only — nowhere else |
|
|
||||||
|
|
||||||
Instrument Serif at scale creates the signature editorial moments. Mono provides the precision signal for technical data. Never swap these roles.
|
|
||||||
|
|
||||||
**Tracking overrides (app.css):**
|
|
||||||
- `.font-serif` — `letter-spacing: 0.015em` (positive tracking; Instrument Serif reads less condensed at display sizes)
|
|
||||||
- `.font-mono` — `font-variant-numeric: tabular-nums` (numbers align in tables and metric displays)
|
|
||||||
|
|
||||||
**Type scale (root: 87.5% = 14px base):**
|
|
||||||
| Token | Value | Use |
|
|
||||||
|---|---|---|
|
|
||||||
| `--text-display` | 2.571rem (~36px) | Auth section headings |
|
|
||||||
| `--text-page` | 2rem (~28px) | Page h1 titles |
|
|
||||||
| `--text-heading` | 1.429rem (~20px) | Dialog headings, empty states |
|
|
||||||
| `--text-body` | 1rem (~14px) | Primary body, buttons, inputs |
|
|
||||||
| `--text-ui` | 0.929rem (~13px) | Nav labels, table cells |
|
|
||||||
| `--text-meta` | 0.857rem (~12px) | Key prefixes, minor info |
|
|
||||||
| `--text-label` | 0.786rem (~11px) | Uppercase section labels |
|
|
||||||
| `--text-badge` | 0.714rem (~10px) | Live badges, tiny indicators |
|
|
||||||
|
|
||||||
### Color System
|
|
||||||
|
|
||||||
All values are CSS custom properties in `frontend/src/app.css`.
|
|
||||||
|
|
||||||
**Backgrounds (6-step near-black-green scale):**
|
|
||||||
| Token | Value | Use |
|
|
||||||
|---|---|---|
|
|
||||||
| `--color-bg-0` | `#0a0c0b` | Page base, sidebar deepest layer |
|
|
||||||
| `--color-bg-1` | `#0f1211` | Sidebar surface |
|
|
||||||
| `--color-bg-2` | `#141817` | Card backgrounds |
|
|
||||||
| `--color-bg-3` | `#1a1e1c` | Table headers, elevated surfaces |
|
|
||||||
| `--color-bg-4` | `#212624` | Hover states, inputs |
|
|
||||||
| `--color-bg-5` | `#2a302d` | Highlighted items, selected rows |
|
|
||||||
|
|
||||||
**Text (5-level hierarchy):**
|
|
||||||
| Token | Value | Use |
|
|
||||||
|---|---|---|
|
|
||||||
| `--color-text-bright` | `#eae7e2` | H1s, dialog headings |
|
|
||||||
| `--color-text-primary` | `#d0cdc6` | Body copy, primary labels |
|
|
||||||
| `--color-text-secondary` | `#9b9790` | Secondary labels, descriptions |
|
|
||||||
| `--color-text-tertiary` | `#6b6862` | Hints, placeholders |
|
|
||||||
| `--color-text-muted` | `#454340` | Dividers as text, ultra-subtle |
|
|
||||||
|
|
||||||
**Accent (sage green — use sparingly, must feel earned):**
|
|
||||||
| Token | Value | Use |
|
|
||||||
|---|---|---|
|
|
||||||
| `--color-accent` | `#5e8c58` | Primary CTA, live indicators, focus rings, active nav |
|
|
||||||
| `--color-accent-mid` | `#89a785` | Hover accent text |
|
|
||||||
| `--color-accent-bright` | `#a4c89f` | Accent on dark backgrounds |
|
|
||||||
| `--color-accent-glow` | `rgba(94,140,88,0.07)` | Subtle tinted backgrounds |
|
|
||||||
| `--color-accent-glow-mid` | `rgba(94,140,88,0.14)` | Hover tint on accent items |
|
|
||||||
|
|
||||||
**Status semantics:**
|
|
||||||
| Token | Value | Use |
|
|
||||||
|---|---|---|
|
|
||||||
| `--color-amber` | `#d4a73c` | Warning, paused state |
|
|
||||||
| `--color-red` | `#cf8172` | Error, destructive actions |
|
|
||||||
| `--color-blue` | `#5a9fd4` | Info, neutral system states |
|
|
||||||
|
|
||||||
**Borders:** `--color-border` (`#1f2321`) default; `--color-border-mid` (`#2a2f2c`) for inputs/hover.
|
|
||||||
|
|
||||||
### Component Patterns
|
|
||||||
|
|
||||||
**Buttons:**
|
|
||||||
- Primary: solid sage green (`--color-accent`), hover brightness boost + micro-lift (`-translate-y-px`)
|
|
||||||
- Secondary: bordered (`--color-border-mid`), text transitions to accent on hover
|
|
||||||
- Danger: red text + subtle red background on hover
|
|
||||||
- All: `transition-all duration-150`
|
|
||||||
|
|
||||||
**Inputs:**
|
|
||||||
- Border `--color-border`, background `--color-bg-2`; focus transitions border and icon to accent
|
|
||||||
- Group focus pattern: `group` wrapper + `group-focus-within:text-[var(--color-accent)]` on icon
|
|
||||||
|
|
||||||
**Tables / data lists:**
|
|
||||||
- Grid layout; header `bg-3` + uppercase `--text-label`; row hover `hover:bg-[var(--color-bg-3)]`
|
|
||||||
- Status stripe: left border color matches sandbox state
|
|
||||||
|
|
||||||
**Status indicators:** Running = animated ping + sage green dot; Paused = amber dot; Stopped = muted gray. Color is never the sole differentiator.
|
|
||||||
|
|
||||||
**Modals & dialogs:** Border + shadow only — no accent gradient bars/strips. `fadeUp` 0.35s entrance.
|
|
||||||
|
|
||||||
**Empty states:** Large icon with glow, Instrument Serif heading, secondary body text, CTA below, `iconFloat` 4s animation.
|
|
||||||
|
|
||||||
**Animations (always respect `prefers-reduced-motion`):** `fadeUp` (entrance), `status-ping` (live indicator), `iconFloat` (empty states), `spin-once` (refresh), staggered `animation-delay` on lists.
|
|
||||||
|
|
||||||
### Design Principles
|
|
||||||
|
|
||||||
1. **Precision over friendliness.** Every element earns its place. Wrenn doesn't need to tell you it's developer-friendly — that should be self-evident from the quality of the information architecture.
|
|
||||||
|
|
||||||
2. **Density with breathing room.** Data-forward doesn't mean cramped. Strategic whitespace creates calm hierarchy within dense contexts. Sections breathe; rows don't waste space.
|
|
||||||
|
|
||||||
3. **Industrial warmth.** The serif + mono + warm-black combination prevents sterility. This is a forge, not a gallery. The warmth is in the details, not the primary colors.
|
|
||||||
|
|
||||||
4. **Legible at speed.** Users scan dashboards in seconds. Strong typographic contrast (serif h1, mono IDs, sans body), consistent patterns, and predictable placement let users orientate instantly without reading everything.
|
|
||||||
|
|
||||||
5. **Craft signals trust.** For infrastructure that runs production code, the quality of the UI is a proxy for the quality of the product. Pixel-level decisions matter. Polish is not decoration — it's a trust signal.
|
|
||||||
4
Makefile
4
Makefile
@ -36,3 +36,7 @@ test-integration:
|
|||||||
uv run pytest tests/ -v -m "integration or not integration"
|
uv run pytest tests/ -v -m "integration or not integration"
|
||||||
|
|
||||||
check: lint test
|
check: lint test
|
||||||
|
|
||||||
|
gen-docs:
|
||||||
|
mkdir -p docs
|
||||||
|
uv run pydoc-markdown > docs/reference.md
|
||||||
|
|||||||
4274
docs/reference.md
Normal file
4274
docs/reference.md
Normal file
File diff suppressed because it is too large
Load Diff
12
pydoc-markdown.yml
Normal file
12
pydoc-markdown.yml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
loaders:
|
||||||
|
- type: python
|
||||||
|
search_path: [src]
|
||||||
|
|
||||||
|
processors:
|
||||||
|
- type: google # Use Google-style docstring parser
|
||||||
|
- type: filter
|
||||||
|
- type: crossref
|
||||||
|
|
||||||
|
renderer:
|
||||||
|
type: markdown
|
||||||
|
escape_html_in_docstring: false
|
||||||
@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "wrenn"
|
name = "wrenn"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
description = "Python SDK for Wrenn"
|
description = "Python SDK for Wrenn"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
@ -36,6 +36,8 @@ build-backend = "hatchling.build"
|
|||||||
dev = [
|
dev = [
|
||||||
"datamodel-code-generator[ruff]>=0.56.0",
|
"datamodel-code-generator[ruff]>=0.56.0",
|
||||||
"mypy>=1.20.0",
|
"mypy>=1.20.0",
|
||||||
|
"pre-commit>=4.6.0",
|
||||||
|
"pydoc-markdown>=4.8.2",
|
||||||
"pytest>=9.0.3",
|
"pytest>=9.0.3",
|
||||||
"pytest-asyncio>=1.3.0",
|
"pytest-asyncio>=1.3.0",
|
||||||
"respx>=0.23.1",
|
"respx>=0.23.1",
|
||||||
|
|||||||
@ -1,33 +1,5 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
|
||||||
from dataclasses import dataclass
|
|
||||||
|
|
||||||
DEFAULT_BASE_URL = "https://app.wrenn.dev/api"
|
DEFAULT_BASE_URL = "https://app.wrenn.dev/api"
|
||||||
ENV_API_KEY = "WRENN_API_KEY"
|
ENV_API_KEY = "WRENN_API_KEY"
|
||||||
ENV_BASE_URL = "WRENN_BASE_URL"
|
ENV_BASE_URL = "WRENN_BASE_URL"
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
|
||||||
class ConnectionConfig:
|
|
||||||
"""Resolved credentials and base URL for Wrenn API calls."""
|
|
||||||
|
|
||||||
api_key: str
|
|
||||||
base_url: str
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_env(
|
|
||||||
cls,
|
|
||||||
api_key: str | None = None,
|
|
||||||
base_url: str | None = None,
|
|
||||||
) -> ConnectionConfig:
|
|
||||||
resolved_key = api_key or os.environ.get(ENV_API_KEY)
|
|
||||||
if not resolved_key:
|
|
||||||
raise ValueError(
|
|
||||||
f"No API key provided. Pass api_key= or set the {ENV_API_KEY} environment variable."
|
|
||||||
)
|
|
||||||
resolved_url = base_url or os.environ.get(ENV_BASE_URL, DEFAULT_BASE_URL)
|
|
||||||
return cls(api_key=resolved_key, base_url=resolved_url)
|
|
||||||
|
|
||||||
def auth_headers(self) -> dict[str, str]:
|
|
||||||
return {"X-API-Key": self.api_key}
|
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import logging
|
||||||
|
import builtins
|
||||||
import time
|
import time
|
||||||
from collections.abc import AsyncIterator
|
from collections.abc import AsyncIterator
|
||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
@ -102,6 +104,7 @@ class AsyncCapsule:
|
|||||||
memory_mb=memory_mb,
|
memory_mb=memory_mb,
|
||||||
timeout_sec=timeout,
|
timeout_sec=timeout,
|
||||||
)
|
)
|
||||||
|
assert info.id is not None
|
||||||
capsule = cls(
|
capsule = cls(
|
||||||
_capsule_id=info.id,
|
_capsule_id=info.id,
|
||||||
_client=client,
|
_client=client,
|
||||||
@ -240,8 +243,10 @@ class AsyncCapsule:
|
|||||||
if info.status == Status.running:
|
if info.status == Status.running:
|
||||||
self._info = info
|
self._info = info
|
||||||
return
|
return
|
||||||
if info.status in (Status.error, Status.stopped, Status.paused):
|
if info.status in (Status.error, Status.stopped):
|
||||||
raise RuntimeError(f"Capsule entered {info.status} state while waiting")
|
raise RuntimeError(f"Capsule entered {info.status} state while waiting")
|
||||||
|
if info.status == Status.paused:
|
||||||
|
info = await self._client.capsules.resume(self._id)
|
||||||
await asyncio.sleep(interval)
|
await asyncio.sleep(interval)
|
||||||
raise TimeoutError(f"Capsule {self._id} did not become ready within {timeout}s")
|
raise TimeoutError(f"Capsule {self._id} did not become ready within {timeout}s")
|
||||||
|
|
||||||
@ -284,7 +289,7 @@ class AsyncCapsule:
|
|||||||
async def pty(
|
async def pty(
|
||||||
self,
|
self,
|
||||||
cmd: str = "/bin/bash",
|
cmd: str = "/bin/bash",
|
||||||
args: list[str] | None = None,
|
args: builtins.list[str] | None = None,
|
||||||
cols: int = 80,
|
cols: int = 80,
|
||||||
rows: int = 24,
|
rows: int = 24,
|
||||||
envs: dict[str, str] | None = None,
|
envs: dict[str, str] | None = None,
|
||||||
@ -316,7 +321,7 @@ class AsyncCapsule:
|
|||||||
"""
|
"""
|
||||||
async with httpx_ws.aconnect_ws(
|
async with httpx_ws.aconnect_ws(
|
||||||
f"/v1/capsules/{self._id}/pty", client=self._client.http
|
f"/v1/capsules/{self._id}/pty", client=self._client.http
|
||||||
) as ws:
|
) as ws: # type: httpx_ws.AsyncWebSocketSession
|
||||||
session = AsyncPtySession(ws, self._id)
|
session = AsyncPtySession(ws, self._id)
|
||||||
await session._send_start(
|
await session._send_start(
|
||||||
cmd=cmd, args=args, cols=cols, rows=rows, envs=envs, cwd=cwd
|
cmd=cmd, args=args, cols=cols, rows=rows, envs=envs, cwd=cwd
|
||||||
@ -335,7 +340,7 @@ class AsyncCapsule:
|
|||||||
"""
|
"""
|
||||||
async with httpx_ws.aconnect_ws(
|
async with httpx_ws.aconnect_ws(
|
||||||
f"/v1/capsules/{self._id}/pty", client=self._client.http
|
f"/v1/capsules/{self._id}/pty", client=self._client.http
|
||||||
) as ws:
|
) as ws: # type: httpx_ws.AsyncWebSocketSession
|
||||||
session = AsyncPtySession(ws, self._id)
|
session = AsyncPtySession(ws, self._id)
|
||||||
await session._send_connect(tag)
|
await session._send_connect(tag)
|
||||||
yield session
|
yield session
|
||||||
@ -387,8 +392,8 @@ class AsyncCapsule:
|
|||||||
) -> None:
|
) -> None:
|
||||||
try:
|
try:
|
||||||
await self._instance_destroy()
|
await self._instance_destroy()
|
||||||
except Exception:
|
except Exception as exc:
|
||||||
pass
|
logging.warning("Failed to destroy capsule %s: %s", self._id, exc)
|
||||||
try:
|
try:
|
||||||
await self._client.aclose()
|
await self._client.aclose()
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import builtins
|
||||||
import time
|
import time
|
||||||
from collections.abc import Iterator
|
from collections.abc import Iterator
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
@ -94,14 +96,16 @@ class Capsule:
|
|||||||
``WRENN_BASE_URL`` or the default production endpoint.
|
``WRENN_BASE_URL`` or the default production endpoint.
|
||||||
"""
|
"""
|
||||||
if _capsule_id is not None:
|
if _capsule_id is not None:
|
||||||
# Internal construction path (from create/connect classmethods)
|
|
||||||
assert _client is not None
|
assert _client is not None
|
||||||
self._id = _capsule_id
|
self._id: str = _capsule_id
|
||||||
self._client = _client
|
self._client = _client
|
||||||
self._info = _info
|
self._info = _info
|
||||||
|
if self._id is None:
|
||||||
|
self._client.close()
|
||||||
|
raise RuntimeError("API returned a capsule without an ID")
|
||||||
else:
|
else:
|
||||||
# Public construction: create a capsule immediately
|
|
||||||
self._client = WrennClient(api_key=api_key, base_url=base_url)
|
self._client = WrennClient(api_key=api_key, base_url=base_url)
|
||||||
|
try:
|
||||||
self._info = self._client.capsules.create(
|
self._info = self._client.capsules.create(
|
||||||
template=template,
|
template=template,
|
||||||
vcpus=vcpus,
|
vcpus=vcpus,
|
||||||
@ -109,6 +113,11 @@ class Capsule:
|
|||||||
timeout_sec=timeout,
|
timeout_sec=timeout,
|
||||||
)
|
)
|
||||||
self._id = self._info.id
|
self._id = self._info.id
|
||||||
|
if self._id is None:
|
||||||
|
raise RuntimeError("API returned a capsule without an ID")
|
||||||
|
except Exception:
|
||||||
|
self._client.close()
|
||||||
|
raise
|
||||||
|
|
||||||
self.commands = Commands(self._id, self._client.http)
|
self.commands = Commands(self._id, self._client.http)
|
||||||
self.files = Files(self._id, self._client.http)
|
self.files = Files(self._id, self._client.http)
|
||||||
@ -316,8 +325,10 @@ class Capsule:
|
|||||||
if info.status == Status.running:
|
if info.status == Status.running:
|
||||||
self._info = info
|
self._info = info
|
||||||
return
|
return
|
||||||
if info.status in (Status.error, Status.stopped, Status.paused):
|
if info.status in (Status.error, Status.stopped):
|
||||||
raise RuntimeError(f"Capsule entered {info.status} state while waiting")
|
raise RuntimeError(f"Capsule entered {info.status} state while waiting")
|
||||||
|
if info.status == Status.paused:
|
||||||
|
info = self._client.capsules.resume(self._id)
|
||||||
time.sleep(interval)
|
time.sleep(interval)
|
||||||
raise TimeoutError(f"Capsule {self._id} did not become ready within {timeout}s")
|
raise TimeoutError(f"Capsule {self._id} did not become ready within {timeout}s")
|
||||||
|
|
||||||
@ -360,7 +371,7 @@ class Capsule:
|
|||||||
def pty(
|
def pty(
|
||||||
self,
|
self,
|
||||||
cmd: str = "/bin/bash",
|
cmd: str = "/bin/bash",
|
||||||
args: list[str] | None = None,
|
args: builtins.list[str] | None = None,
|
||||||
cols: int = 80,
|
cols: int = 80,
|
||||||
rows: int = 24,
|
rows: int = 24,
|
||||||
envs: dict[str, str] | None = None,
|
envs: dict[str, str] | None = None,
|
||||||
@ -391,7 +402,7 @@ class Capsule:
|
|||||||
"""
|
"""
|
||||||
with httpx_ws.connect_ws(
|
with httpx_ws.connect_ws(
|
||||||
f"/v1/capsules/{self._id}/pty", client=self._client.http
|
f"/v1/capsules/{self._id}/pty", client=self._client.http
|
||||||
) as ws:
|
) as ws: # type: httpx_ws.WebSocketSession
|
||||||
session = PtySession(ws, self._id)
|
session = PtySession(ws, self._id)
|
||||||
session._send_start(
|
session._send_start(
|
||||||
cmd=cmd, args=args, cols=cols, rows=rows, envs=envs, cwd=cwd
|
cmd=cmd, args=args, cols=cols, rows=rows, envs=envs, cwd=cwd
|
||||||
@ -410,7 +421,7 @@ class Capsule:
|
|||||||
"""
|
"""
|
||||||
with httpx_ws.connect_ws(
|
with httpx_ws.connect_ws(
|
||||||
f"/v1/capsules/{self._id}/pty", client=self._client.http
|
f"/v1/capsules/{self._id}/pty", client=self._client.http
|
||||||
) as ws:
|
) as ws: # type: httpx_ws.WebSocketSession
|
||||||
session = PtySession(ws, self._id)
|
session = PtySession(ws, self._id)
|
||||||
session._send_connect(tag)
|
session._send_connect(tag)
|
||||||
yield session
|
yield session
|
||||||
@ -462,8 +473,8 @@ class Capsule:
|
|||||||
) -> None:
|
) -> None:
|
||||||
try:
|
try:
|
||||||
self._instance_destroy()
|
self._instance_destroy()
|
||||||
except Exception:
|
except Exception as exc:
|
||||||
pass
|
logging.warning("Failed to destroy capsule %s: %s", self._id, exc)
|
||||||
try:
|
try:
|
||||||
self._client.close()
|
self._client.close()
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import httpx
|
|||||||
|
|
||||||
from wrenn._config import DEFAULT_BASE_URL, ENV_API_KEY, ENV_BASE_URL
|
from wrenn._config import DEFAULT_BASE_URL, ENV_API_KEY, ENV_BASE_URL
|
||||||
from wrenn.exceptions import handle_response
|
from wrenn.exceptions import handle_response
|
||||||
|
|
||||||
from wrenn.models import (
|
from wrenn.models import (
|
||||||
Template,
|
Template,
|
||||||
)
|
)
|
||||||
@ -13,6 +14,8 @@ from wrenn.models import (
|
|||||||
Capsule as CapsuleModel,
|
Capsule as CapsuleModel,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
_LONG_TIMEOUT = httpx.Timeout(60.0)
|
||||||
|
|
||||||
|
|
||||||
def _resolve_api_key(api_key: str | None) -> str:
|
def _resolve_api_key(api_key: str | None) -> str:
|
||||||
resolved = api_key or os.environ.get(ENV_API_KEY)
|
resolved = api_key or os.environ.get(ENV_API_KEY)
|
||||||
@ -108,7 +111,7 @@ class CapsulesResource:
|
|||||||
Raises:
|
Raises:
|
||||||
WrennNotFoundError: If no capsule with the given ID exists.
|
WrennNotFoundError: If no capsule with the given ID exists.
|
||||||
"""
|
"""
|
||||||
resp = self._http.post(f"/v1/capsules/{id}/pause")
|
resp = self._http.post(f"/v1/capsules/{id}/pause", timeout=_LONG_TIMEOUT)
|
||||||
return CapsuleModel.model_validate(handle_response(resp))
|
return CapsuleModel.model_validate(handle_response(resp))
|
||||||
|
|
||||||
def resume(self, id: str) -> CapsuleModel:
|
def resume(self, id: str) -> CapsuleModel:
|
||||||
@ -224,7 +227,7 @@ class AsyncCapsulesResource:
|
|||||||
Raises:
|
Raises:
|
||||||
WrennNotFoundError: If no capsule with the given ID exists.
|
WrennNotFoundError: If no capsule with the given ID exists.
|
||||||
"""
|
"""
|
||||||
resp = await self._http.post(f"/v1/capsules/{id}/pause")
|
resp = await self._http.post(f"/v1/capsules/{id}/pause", timeout=_LONG_TIMEOUT)
|
||||||
return CapsuleModel.model_validate(handle_response(resp))
|
return CapsuleModel.model_validate(handle_response(resp))
|
||||||
|
|
||||||
async def resume(self, id: str) -> CapsuleModel:
|
async def resume(self, id: str) -> CapsuleModel:
|
||||||
@ -285,7 +288,9 @@ class SnapshotsResource:
|
|||||||
params: dict = {}
|
params: dict = {}
|
||||||
if overwrite:
|
if overwrite:
|
||||||
params["overwrite"] = "true"
|
params["overwrite"] = "true"
|
||||||
resp = self._http.post("/v1/snapshots", json=payload, params=params)
|
resp = self._http.post(
|
||||||
|
"/v1/snapshots", json=payload, params=params, timeout=_LONG_TIMEOUT
|
||||||
|
)
|
||||||
return Template.model_validate(handle_response(resp))
|
return Template.model_validate(handle_response(resp))
|
||||||
|
|
||||||
def list(self, type: str | None = None) -> list[Template]:
|
def list(self, type: str | None = None) -> list[Template]:
|
||||||
@ -347,7 +352,9 @@ class AsyncSnapshotsResource:
|
|||||||
params: dict = {}
|
params: dict = {}
|
||||||
if overwrite:
|
if overwrite:
|
||||||
params["overwrite"] = "true"
|
params["overwrite"] = "true"
|
||||||
resp = await self._http.post("/v1/snapshots", json=payload, params=params)
|
resp = await self._http.post(
|
||||||
|
"/v1/snapshots", json=payload, params=params, timeout=_LONG_TIMEOUT
|
||||||
|
)
|
||||||
return Template.model_validate(handle_response(resp))
|
return Template.model_validate(handle_response(resp))
|
||||||
|
|
||||||
async def list(self, type: str | None = None) -> list[Template]:
|
async def list(self, type: str | None = None) -> list[Template]:
|
||||||
|
|||||||
@ -40,6 +40,28 @@ class AsyncCapsule(BaseAsyncCapsule):
|
|||||||
self._kernel_id = None
|
self._kernel_id = None
|
||||||
self._proxy_client = None
|
self._proxy_client = None
|
||||||
|
|
||||||
|
async def close(self) -> None:
|
||||||
|
if self._proxy_client is not None:
|
||||||
|
try:
|
||||||
|
await self._proxy_client.aclose()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
self._proxy_client = None
|
||||||
|
|
||||||
|
def __del__(self) -> None:
|
||||||
|
if self._proxy_client is not None:
|
||||||
|
try:
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
if loop.is_running():
|
||||||
|
loop.create_task(self._proxy_client.aclose())
|
||||||
|
else:
|
||||||
|
loop.run_until_complete(self._proxy_client.aclose())
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
self._proxy_client = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def create(
|
async def create(
|
||||||
cls,
|
cls,
|
||||||
@ -126,8 +148,10 @@ class AsyncCapsule(BaseAsyncCapsule):
|
|||||||
request=resp.request,
|
request=resp.request,
|
||||||
response=resp,
|
response=resp,
|
||||||
)
|
)
|
||||||
except httpx.HTTPStatusError:
|
except httpx.HTTPStatusError as exc:
|
||||||
|
if exc.response.status_code < 500:
|
||||||
raise
|
raise
|
||||||
|
last_exc = exc
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
last_exc = exc
|
last_exc = exc
|
||||||
await asyncio.sleep(0.5)
|
await asyncio.sleep(0.5)
|
||||||
@ -164,8 +188,6 @@ class AsyncCapsule(BaseAsyncCapsule):
|
|||||||
},
|
},
|
||||||
"buffers": [],
|
"buffers": [],
|
||||||
"channel": "shell",
|
"channel": "shell",
|
||||||
"msg_id": msg_id,
|
|
||||||
"msg_type": "execute_request",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async def run_code(
|
async def run_code(
|
||||||
@ -201,13 +223,13 @@ class AsyncCapsule(BaseAsyncCapsule):
|
|||||||
ws_url = self._jupyter_ws_url(kernel_id)
|
ws_url = self._jupyter_ws_url(kernel_id)
|
||||||
|
|
||||||
msg = self._jupyter_execute_request(code)
|
msg = self._jupyter_execute_request(code)
|
||||||
msg_id = msg["msg_id"]
|
msg_id = msg["header"]["msg_id"]
|
||||||
|
|
||||||
execution = Execution()
|
execution = Execution()
|
||||||
deadline = time.monotonic() + timeout
|
deadline = time.monotonic() + timeout
|
||||||
headers = {"X-API-Key": self._client._api_key}
|
headers = {"X-API-Key": self._client._api_key}
|
||||||
|
|
||||||
async with httpx_ws.aconnect_ws(ws_url, headers=headers) as ws:
|
async with httpx_ws.aconnect_ws(ws_url, headers=headers) as ws: # type: httpx_ws.AsyncWebSocketSession
|
||||||
await ws.send_text(json.dumps(msg))
|
await ws.send_text(json.dumps(msg))
|
||||||
while time.monotonic() < deadline:
|
while time.monotonic() < deadline:
|
||||||
time_left = deadline - time.monotonic()
|
time_left = deadline - time.monotonic()
|
||||||
@ -215,7 +237,7 @@ class AsyncCapsule(BaseAsyncCapsule):
|
|||||||
break
|
break
|
||||||
try:
|
try:
|
||||||
data = await asyncio.wait_for(ws.receive_json(), timeout=time_left)
|
data = await asyncio.wait_for(ws.receive_json(), timeout=time_left)
|
||||||
except (asyncio.TimeoutError, Exception):
|
except Exception:
|
||||||
break
|
break
|
||||||
if not data:
|
if not data:
|
||||||
break
|
break
|
||||||
|
|||||||
@ -70,6 +70,17 @@ class Capsule(BaseCapsule):
|
|||||||
self._kernel_id = None
|
self._kernel_id = None
|
||||||
self._proxy_client = None
|
self._proxy_client = None
|
||||||
|
|
||||||
|
def close(self) -> None:
|
||||||
|
if self._proxy_client is not None:
|
||||||
|
try:
|
||||||
|
self._proxy_client.close()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
self._proxy_client = None
|
||||||
|
|
||||||
|
def __del__(self) -> None:
|
||||||
|
self.close()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(
|
def create(
|
||||||
cls,
|
cls,
|
||||||
@ -150,8 +161,10 @@ class Capsule(BaseCapsule):
|
|||||||
request=resp.request,
|
request=resp.request,
|
||||||
response=resp,
|
response=resp,
|
||||||
)
|
)
|
||||||
except httpx.HTTPStatusError:
|
except httpx.HTTPStatusError as exc:
|
||||||
|
if exc.response.status_code < 500:
|
||||||
raise
|
raise
|
||||||
|
last_exc = exc
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
last_exc = exc
|
last_exc = exc
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
@ -188,8 +201,6 @@ class Capsule(BaseCapsule):
|
|||||||
},
|
},
|
||||||
"buffers": [],
|
"buffers": [],
|
||||||
"channel": "shell",
|
"channel": "shell",
|
||||||
"msg_id": msg_id,
|
|
||||||
"msg_type": "execute_request",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def run_code(
|
def run_code(
|
||||||
@ -227,13 +238,13 @@ class Capsule(BaseCapsule):
|
|||||||
ws_url = self._jupyter_ws_url(kernel_id)
|
ws_url = self._jupyter_ws_url(kernel_id)
|
||||||
|
|
||||||
msg = self._jupyter_execute_request(code)
|
msg = self._jupyter_execute_request(code)
|
||||||
msg_id = msg["msg_id"]
|
msg_id = msg["header"]["msg_id"]
|
||||||
|
|
||||||
execution = Execution()
|
execution = Execution()
|
||||||
deadline = time.monotonic() + timeout
|
deadline = time.monotonic() + timeout
|
||||||
headers = {"X-API-Key": self._client._api_key}
|
headers = {"X-API-Key": self._client._api_key}
|
||||||
|
|
||||||
with httpx_ws.connect_ws(ws_url, headers=headers) as ws:
|
with httpx_ws.connect_ws(ws_url, headers=headers) as ws: # type: httpx_ws.WebSocketSession
|
||||||
ws.send_text(json.dumps(msg))
|
ws.send_text(json.dumps(msg))
|
||||||
while time.monotonic() < deadline:
|
while time.monotonic() < deadline:
|
||||||
time_left = deadline - time.monotonic()
|
time_left = deadline - time.monotonic()
|
||||||
@ -241,7 +252,7 @@ class Capsule(BaseCapsule):
|
|||||||
break
|
break
|
||||||
try:
|
try:
|
||||||
data = ws.receive_json(timeout=time_left)
|
data = ws.receive_json(timeout=time_left)
|
||||||
except (TimeoutError, Exception):
|
except Exception:
|
||||||
break
|
break
|
||||||
if not data:
|
if not data:
|
||||||
break
|
break
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import base64
|
import base64
|
||||||
|
import builtins
|
||||||
import json
|
import json
|
||||||
from collections.abc import AsyncIterator, Iterator
|
from collections.abc import AsyncIterator, Iterator
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import overload, Literal
|
from typing import Literal, overload
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
import httpx_ws
|
import httpx_ws
|
||||||
@ -197,8 +198,17 @@ class Commands:
|
|||||||
if tag is not None:
|
if tag is not None:
|
||||||
payload["tag"] = tag
|
payload["tag"] = tag
|
||||||
|
|
||||||
resp = self._http.post(f"/v1/capsules/{self._capsule_id}/exec", json=payload)
|
http_timeout: httpx.Timeout | None = None
|
||||||
|
if not background and timeout is not None:
|
||||||
|
http_timeout = httpx.Timeout(timeout + 10, connect=5.0)
|
||||||
|
|
||||||
|
resp = self._http.post(
|
||||||
|
f"/v1/capsules/{self._capsule_id}/exec",
|
||||||
|
json=payload,
|
||||||
|
timeout=http_timeout,
|
||||||
|
)
|
||||||
data = handle_response(resp)
|
data = handle_response(resp)
|
||||||
|
assert isinstance(data, dict)
|
||||||
|
|
||||||
if background:
|
if background:
|
||||||
return CommandHandle(
|
return CommandHandle(
|
||||||
@ -217,6 +227,7 @@ class Commands:
|
|||||||
"""
|
"""
|
||||||
resp = self._http.get(f"/v1/capsules/{self._capsule_id}/processes")
|
resp = self._http.get(f"/v1/capsules/{self._capsule_id}/processes")
|
||||||
data = handle_response(resp)
|
data = handle_response(resp)
|
||||||
|
assert isinstance(data, dict)
|
||||||
return [
|
return [
|
||||||
ProcessInfo(
|
ProcessInfo(
|
||||||
pid=p.get("pid", 0),
|
pid=p.get("pid", 0),
|
||||||
@ -252,7 +263,7 @@ class Commands:
|
|||||||
with httpx_ws.connect_ws(
|
with httpx_ws.connect_ws(
|
||||||
f"/v1/capsules/{self._capsule_id}/processes/{pid}/stream",
|
f"/v1/capsules/{self._capsule_id}/processes/{pid}/stream",
|
||||||
self._http,
|
self._http,
|
||||||
) as ws:
|
) as ws: # type: httpx_ws.WebSocketSession
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
raw = ws.receive_json()
|
raw = ws.receive_json()
|
||||||
@ -263,7 +274,9 @@ class Commands:
|
|||||||
except httpx_ws.WebSocketDisconnect:
|
except httpx_ws.WebSocketDisconnect:
|
||||||
break
|
break
|
||||||
|
|
||||||
def stream(self, cmd: str, args: list[str] | None = None) -> Iterator[StreamEvent]:
|
def stream(
|
||||||
|
self, cmd: str, args: builtins.list[str] | None = None
|
||||||
|
) -> Iterator[StreamEvent]:
|
||||||
"""Execute a command via WebSocket, streaming output as events.
|
"""Execute a command via WebSocket, streaming output as events.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -280,7 +293,7 @@ class Commands:
|
|||||||
with httpx_ws.connect_ws(
|
with httpx_ws.connect_ws(
|
||||||
f"/v1/capsules/{self._capsule_id}/exec/stream",
|
f"/v1/capsules/{self._capsule_id}/exec/stream",
|
||||||
self._http,
|
self._http,
|
||||||
) as ws:
|
) as ws: # type: httpx_ws.WebSocketSession
|
||||||
if args:
|
if args:
|
||||||
start_msg: dict = {"type": "start", "cmd": cmd, "args": args}
|
start_msg: dict = {"type": "start", "cmd": cmd, "args": args}
|
||||||
else:
|
else:
|
||||||
@ -374,10 +387,17 @@ class AsyncCommands:
|
|||||||
if tag is not None:
|
if tag is not None:
|
||||||
payload["tag"] = tag
|
payload["tag"] = tag
|
||||||
|
|
||||||
|
http_timeout: httpx.Timeout | None = None
|
||||||
|
if not background and timeout is not None:
|
||||||
|
http_timeout = httpx.Timeout(timeout + 10, connect=5.0)
|
||||||
|
|
||||||
resp = await self._http.post(
|
resp = await self._http.post(
|
||||||
f"/v1/capsules/{self._capsule_id}/exec", json=payload
|
f"/v1/capsules/{self._capsule_id}/exec",
|
||||||
|
json=payload,
|
||||||
|
timeout=http_timeout,
|
||||||
)
|
)
|
||||||
data = handle_response(resp)
|
data = handle_response(resp)
|
||||||
|
assert isinstance(data, dict)
|
||||||
|
|
||||||
if background:
|
if background:
|
||||||
return CommandHandle(
|
return CommandHandle(
|
||||||
@ -396,6 +416,7 @@ class AsyncCommands:
|
|||||||
"""
|
"""
|
||||||
resp = await self._http.get(f"/v1/capsules/{self._capsule_id}/processes")
|
resp = await self._http.get(f"/v1/capsules/{self._capsule_id}/processes")
|
||||||
data = handle_response(resp)
|
data = handle_response(resp)
|
||||||
|
assert isinstance(data, dict)
|
||||||
return [
|
return [
|
||||||
ProcessInfo(
|
ProcessInfo(
|
||||||
pid=p.get("pid", 0),
|
pid=p.get("pid", 0),
|
||||||
@ -433,7 +454,7 @@ class AsyncCommands:
|
|||||||
async with httpx_ws.aconnect_ws(
|
async with httpx_ws.aconnect_ws(
|
||||||
f"/v1/capsules/{self._capsule_id}/processes/{pid}/stream",
|
f"/v1/capsules/{self._capsule_id}/processes/{pid}/stream",
|
||||||
self._http,
|
self._http,
|
||||||
) as ws:
|
) as ws: # type: httpx_ws.AsyncWebSocketSession
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
raw = await ws.receive_json()
|
raw = await ws.receive_json()
|
||||||
@ -445,7 +466,7 @@ class AsyncCommands:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
async def stream(
|
async def stream(
|
||||||
self, cmd: str, args: list[str] | None = None
|
self, cmd: str, args: builtins.list[str] | None = None
|
||||||
) -> AsyncIterator[StreamEvent]:
|
) -> AsyncIterator[StreamEvent]:
|
||||||
"""Execute a command via WebSocket, streaming output as events.
|
"""Execute a command via WebSocket, streaming output as events.
|
||||||
|
|
||||||
@ -463,7 +484,7 @@ class AsyncCommands:
|
|||||||
async with httpx_ws.aconnect_ws(
|
async with httpx_ws.aconnect_ws(
|
||||||
f"/v1/capsules/{self._capsule_id}/exec/stream",
|
f"/v1/capsules/{self._capsule_id}/exec/stream",
|
||||||
self._http,
|
self._http,
|
||||||
) as ws:
|
) as ws: # type: httpx_ws.AsyncWebSocketSession
|
||||||
if args:
|
if args:
|
||||||
start_msg: dict = {"type": "start", "cmd": cmd, "args": args}
|
start_msg: dict = {"type": "start", "cmd": cmd, "args": args}
|
||||||
else:
|
else:
|
||||||
|
|||||||
@ -110,13 +110,18 @@ _ERROR_MAP: dict[str, type[WrennError]] = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def handle_response(resp: httpx.Response) -> dict | list:
|
def _raise_for_status(resp: httpx.Response) -> None:
|
||||||
if resp.status_code >= 400:
|
if resp.status_code < 400:
|
||||||
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
body = resp.json()
|
body = resp.json()
|
||||||
except Exception:
|
except Exception:
|
||||||
resp.raise_for_status()
|
raise WrennInternalError(
|
||||||
raise
|
code="internal_error",
|
||||||
|
message=resp.text or f"HTTP {resp.status_code}",
|
||||||
|
status_code=resp.status_code,
|
||||||
|
)
|
||||||
|
|
||||||
err = body.get("error", {})
|
err = body.get("error", {})
|
||||||
code = err.get("code", "internal_error")
|
code = err.get("code", "internal_error")
|
||||||
@ -129,7 +134,7 @@ def handle_response(resp: httpx.Response) -> dict | list:
|
|||||||
code=code,
|
code=code,
|
||||||
message=message,
|
message=message,
|
||||||
status_code=resp.status_code,
|
status_code=resp.status_code,
|
||||||
capsule_ids=body.get("sandbox_ids", []),
|
capsule_ids=body.get("capsule_ids") or body.get("sandbox_ids", []),
|
||||||
)
|
)
|
||||||
|
|
||||||
raise exc_cls(
|
raise exc_cls(
|
||||||
@ -138,6 +143,10 @@ def handle_response(resp: httpx.Response) -> dict | list:
|
|||||||
status_code=resp.status_code,
|
status_code=resp.status_code,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def handle_response(resp: httpx.Response) -> dict | list:
|
||||||
|
_raise_for_status(resp)
|
||||||
|
|
||||||
if resp.status_code == 204:
|
if resp.status_code == 204:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,7 @@ from collections.abc import AsyncIterator, Iterator
|
|||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
|
|
||||||
from wrenn.exceptions import WrennNotFoundError, handle_response
|
from wrenn.exceptions import WrennNotFoundError, _raise_for_status, handle_response
|
||||||
from wrenn.models import FileEntry, ListDirResponse, MakeDirResponse
|
from wrenn.models import FileEntry, ListDirResponse, MakeDirResponse
|
||||||
|
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ class Files:
|
|||||||
f"/v1/capsules/{self._capsule_id}/files/read",
|
f"/v1/capsules/{self._capsule_id}/files/read",
|
||||||
json={"path": path},
|
json={"path": path},
|
||||||
)
|
)
|
||||||
resp.raise_for_status()
|
_raise_for_status(resp)
|
||||||
return resp.content
|
return resp.content
|
||||||
|
|
||||||
def write(self, path: str, data: str | bytes) -> None:
|
def write(self, path: str, data: str | bytes) -> None:
|
||||||
@ -65,7 +65,7 @@ class Files:
|
|||||||
files={"file": ("upload", data)},
|
files={"file": ("upload", data)},
|
||||||
data={"path": path},
|
data={"path": path},
|
||||||
)
|
)
|
||||||
resp.raise_for_status()
|
_raise_for_status(resp)
|
||||||
|
|
||||||
def list(self, path: str, depth: int = 1) -> list[FileEntry]:
|
def list(self, path: str, depth: int = 1) -> list[FileEntry]:
|
||||||
"""List directory contents.
|
"""List directory contents.
|
||||||
@ -179,7 +179,7 @@ class Files:
|
|||||||
"Content-Type": f"multipart/form-data; boundary={boundary.decode('utf-8')}"
|
"Content-Type": f"multipart/form-data; boundary={boundary.decode('utf-8')}"
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
resp.raise_for_status()
|
_raise_for_status(resp)
|
||||||
|
|
||||||
def download_stream(self, path: str) -> Iterator[bytes]:
|
def download_stream(self, path: str) -> Iterator[bytes]:
|
||||||
"""Stream a large file out of the capsule.
|
"""Stream a large file out of the capsule.
|
||||||
@ -243,7 +243,7 @@ class AsyncFiles:
|
|||||||
f"/v1/capsules/{self._capsule_id}/files/read",
|
f"/v1/capsules/{self._capsule_id}/files/read",
|
||||||
json={"path": path},
|
json={"path": path},
|
||||||
)
|
)
|
||||||
resp.raise_for_status()
|
_raise_for_status(resp)
|
||||||
return resp.content
|
return resp.content
|
||||||
|
|
||||||
async def write(self, path: str, data: str | bytes) -> None:
|
async def write(self, path: str, data: str | bytes) -> None:
|
||||||
@ -262,7 +262,7 @@ class AsyncFiles:
|
|||||||
files={"file": ("upload", data)},
|
files={"file": ("upload", data)},
|
||||||
data={"path": path},
|
data={"path": path},
|
||||||
)
|
)
|
||||||
resp.raise_for_status()
|
_raise_for_status(resp)
|
||||||
|
|
||||||
async def list(self, path: str, depth: int = 1) -> list[FileEntry]:
|
async def list(self, path: str, depth: int = 1) -> list[FileEntry]:
|
||||||
"""List directory contents.
|
"""List directory contents.
|
||||||
@ -377,7 +377,7 @@ class AsyncFiles:
|
|||||||
"Content-Type": f"multipart/form-data; boundary={boundary.decode('utf-8')}"
|
"Content-Type": f"multipart/form-data; boundary={boundary.decode('utf-8')}"
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
resp.raise_for_status()
|
_raise_for_status(resp)
|
||||||
|
|
||||||
async def download_stream(self, path: str) -> AsyncIterator[bytes]:
|
async def download_stream(self, path: str) -> AsyncIterator[bytes]:
|
||||||
"""Stream a large file out of the capsule.
|
"""Stream a large file out of the capsule.
|
||||||
|
|||||||
@ -153,7 +153,8 @@ class PtySession:
|
|||||||
if event.pid is not None:
|
if event.pid is not None:
|
||||||
self._pid = event.pid
|
self._pid = event.pid
|
||||||
if event.type == PtyEventType.exit:
|
if event.type == PtyEventType.exit:
|
||||||
raise StopIteration
|
self._done = True
|
||||||
|
return event
|
||||||
if event.type == PtyEventType.error and event.fatal:
|
if event.type == PtyEventType.error and event.fatal:
|
||||||
self._done = True
|
self._done = True
|
||||||
return event
|
return event
|
||||||
@ -281,7 +282,8 @@ class AsyncPtySession:
|
|||||||
if event.pid is not None:
|
if event.pid is not None:
|
||||||
self._pid = event.pid
|
self._pid = event.pid
|
||||||
if event.type == PtyEventType.exit:
|
if event.type == PtyEventType.exit:
|
||||||
raise StopAsyncIteration
|
self._done = True
|
||||||
|
return event
|
||||||
if event.type == PtyEventType.error and event.fatal:
|
if event.type == PtyEventType.error and event.fatal:
|
||||||
self._done = True
|
self._done = True
|
||||||
return event
|
return event
|
||||||
|
|||||||
@ -32,7 +32,7 @@ class TestCapsuleCreate:
|
|||||||
respx.post(f"{BASE}/v1/capsules").respond(
|
respx.post(f"{BASE}/v1/capsules").respond(
|
||||||
201, json={"id": "cl-1", "status": "pending", "template": "minimal"}
|
201, json={"id": "cl-1", "status": "pending", "template": "minimal"}
|
||||||
)
|
)
|
||||||
cap = Capsule(template="minimal", api_key="wrn_test1234567890abcdef12345678")
|
cap = Capsule(template="minimal", api_key="wrn_test1234567890abcdef12345678", base_url=BASE)
|
||||||
assert cap.capsule_id == "cl-1"
|
assert cap.capsule_id == "cl-1"
|
||||||
assert hasattr(cap, "commands")
|
assert hasattr(cap, "commands")
|
||||||
assert hasattr(cap, "files")
|
assert hasattr(cap, "files")
|
||||||
@ -42,7 +42,7 @@ class TestCapsuleCreate:
|
|||||||
respx.post(f"{BASE}/v1/capsules").respond(
|
respx.post(f"{BASE}/v1/capsules").respond(
|
||||||
201, json={"id": "cl-2", "status": "pending"}
|
201, json={"id": "cl-2", "status": "pending"}
|
||||||
)
|
)
|
||||||
cap = Capsule.create(api_key="wrn_test1234567890abcdef12345678")
|
cap = Capsule.create(api_key="wrn_test1234567890abcdef12345678", base_url=BASE)
|
||||||
assert cap.capsule_id == "cl-2"
|
assert cap.capsule_id == "cl-2"
|
||||||
|
|
||||||
@respx.mock
|
@respx.mock
|
||||||
@ -51,7 +51,7 @@ class TestCapsuleCreate:
|
|||||||
201, json={"id": "cl-1", "status": "pending"}
|
201, json={"id": "cl-1", "status": "pending"}
|
||||||
)
|
)
|
||||||
kill_route = respx.delete(f"{BASE}/v1/capsules/cl-1").respond(204)
|
kill_route = respx.delete(f"{BASE}/v1/capsules/cl-1").respond(204)
|
||||||
with Capsule(api_key="wrn_test1234567890abcdef12345678") as cap:
|
with Capsule(api_key="wrn_test1234567890abcdef12345678", base_url=BASE) as cap:
|
||||||
assert cap.capsule_id == "cl-1"
|
assert cap.capsule_id == "cl-1"
|
||||||
assert kill_route.called
|
assert kill_route.called
|
||||||
|
|
||||||
@ -61,7 +61,7 @@ class TestCapsuleCreate:
|
|||||||
respx.post(f"{BASE}/v1/capsules").respond(
|
respx.post(f"{BASE}/v1/capsules").respond(
|
||||||
201, json={"id": "cl-3", "status": "pending"}
|
201, json={"id": "cl-3", "status": "pending"}
|
||||||
)
|
)
|
||||||
cap = Capsule()
|
cap = Capsule(base_url=BASE)
|
||||||
assert cap.capsule_id == "cl-3"
|
assert cap.capsule_id == "cl-3"
|
||||||
|
|
||||||
|
|
||||||
@ -69,7 +69,7 @@ class TestCapsuleStaticMethods:
|
|||||||
@respx.mock
|
@respx.mock
|
||||||
def test_static_destroy(self):
|
def test_static_destroy(self):
|
||||||
route = respx.delete(f"{BASE}/v1/capsules/cl-1").respond(204)
|
route = respx.delete(f"{BASE}/v1/capsules/cl-1").respond(204)
|
||||||
Capsule._static_destroy("cl-1", api_key="wrn_test1234567890abcdef12345678")
|
Capsule._static_destroy("cl-1", api_key="wrn_test1234567890abcdef12345678", base_url=BASE)
|
||||||
assert route.called
|
assert route.called
|
||||||
|
|
||||||
@respx.mock
|
@respx.mock
|
||||||
@ -77,7 +77,7 @@ class TestCapsuleStaticMethods:
|
|||||||
respx.post(f"{BASE}/v1/capsules/cl-1/pause").respond(
|
respx.post(f"{BASE}/v1/capsules/cl-1/pause").respond(
|
||||||
200, json={"id": "cl-1", "status": "paused"}
|
200, json={"id": "cl-1", "status": "paused"}
|
||||||
)
|
)
|
||||||
info = Capsule._static_pause("cl-1", api_key="wrn_test1234567890abcdef12345678")
|
info = Capsule._static_pause("cl-1", api_key="wrn_test1234567890abcdef12345678", base_url=BASE)
|
||||||
assert info.status.value == "paused"
|
assert info.status.value == "paused"
|
||||||
|
|
||||||
@respx.mock
|
@respx.mock
|
||||||
@ -85,7 +85,7 @@ class TestCapsuleStaticMethods:
|
|||||||
respx.get(f"{BASE}/v1/capsules").respond(
|
respx.get(f"{BASE}/v1/capsules").respond(
|
||||||
200, json=[{"id": "cl-1", "status": "running"}]
|
200, json=[{"id": "cl-1", "status": "running"}]
|
||||||
)
|
)
|
||||||
items = Capsule.list(api_key="wrn_test1234567890abcdef12345678")
|
items = Capsule.list(api_key="wrn_test1234567890abcdef12345678", base_url=BASE)
|
||||||
assert len(items) == 1
|
assert len(items) == 1
|
||||||
assert items[0].id == "cl-1"
|
assert items[0].id == "cl-1"
|
||||||
|
|
||||||
@ -95,7 +95,7 @@ class TestCapsuleStaticMethods:
|
|||||||
200, json={"id": "cl-1", "status": "running"}
|
200, json={"id": "cl-1", "status": "running"}
|
||||||
)
|
)
|
||||||
info = Capsule._static_get_info(
|
info = Capsule._static_get_info(
|
||||||
"cl-1", api_key="wrn_test1234567890abcdef12345678"
|
"cl-1", api_key="wrn_test1234567890abcdef12345678", base_url=BASE
|
||||||
)
|
)
|
||||||
assert info.id == "cl-1"
|
assert info.id == "cl-1"
|
||||||
|
|
||||||
@ -106,7 +106,7 @@ class TestCapsuleConnect:
|
|||||||
respx.get(f"{BASE}/v1/capsules/cl-1").respond(
|
respx.get(f"{BASE}/v1/capsules/cl-1").respond(
|
||||||
200, json={"id": "cl-1", "status": "running"}
|
200, json={"id": "cl-1", "status": "running"}
|
||||||
)
|
)
|
||||||
cap = Capsule.connect("cl-1", api_key="wrn_test1234567890abcdef12345678")
|
cap = Capsule.connect("cl-1", api_key="wrn_test1234567890abcdef12345678", base_url=BASE)
|
||||||
assert cap.capsule_id == "cl-1"
|
assert cap.capsule_id == "cl-1"
|
||||||
|
|
||||||
@respx.mock
|
@respx.mock
|
||||||
@ -117,7 +117,7 @@ class TestCapsuleConnect:
|
|||||||
respx.post(f"{BASE}/v1/capsules/cl-1/resume").respond(
|
respx.post(f"{BASE}/v1/capsules/cl-1/resume").respond(
|
||||||
200, json={"id": "cl-1", "status": "running"}
|
200, json={"id": "cl-1", "status": "running"}
|
||||||
)
|
)
|
||||||
cap = Capsule.connect("cl-1", api_key="wrn_test1234567890abcdef12345678")
|
cap = Capsule.connect("cl-1", api_key="wrn_test1234567890abcdef12345678", base_url=BASE)
|
||||||
assert cap.capsule_id == "cl-1"
|
assert cap.capsule_id == "cl-1"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -23,13 +23,13 @@ BASE = "https://app.wrenn.dev/api"
|
|||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def client():
|
def client():
|
||||||
with WrennClient(api_key="wrn_test1234567890abcdef12345678") as c:
|
with WrennClient(api_key="wrn_test1234567890abcdef12345678", base_url=BASE) as c:
|
||||||
yield c
|
yield c
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def async_client():
|
def async_client():
|
||||||
return AsyncWrennClient(api_key="wrn_test1234567890abcdef12345678")
|
return AsyncWrennClient(api_key="wrn_test1234567890abcdef12345678", base_url=BASE)
|
||||||
|
|
||||||
|
|
||||||
class TestCapsules:
|
class TestCapsules:
|
||||||
@ -221,7 +221,8 @@ class TestAuthModes:
|
|||||||
with WrennClient(api_key="wrn_test1234567890abcdef12345678") as c:
|
with WrennClient(api_key="wrn_test1234567890abcdef12345678") as c:
|
||||||
assert c._http.headers["X-API-Key"] == "wrn_test1234567890abcdef12345678"
|
assert c._http.headers["X-API-Key"] == "wrn_test1234567890abcdef12345678"
|
||||||
|
|
||||||
def test_no_auth_raises(self):
|
def test_no_auth_raises(self, monkeypatch):
|
||||||
|
monkeypatch.delenv("WRENN_API_KEY", raising=False)
|
||||||
with pytest.raises(ValueError, match="No API key"):
|
with pytest.raises(ValueError, match="No API key"):
|
||||||
WrennClient()
|
WrennClient()
|
||||||
|
|
||||||
|
|||||||
@ -23,7 +23,7 @@ def _make_capsule(cap_id: str = "cl-abc") -> Capsule:
|
|||||||
respx.post(f"{BASE}/v1/capsules").respond(
|
respx.post(f"{BASE}/v1/capsules").respond(
|
||||||
201, json={"id": cap_id, "status": "running"}
|
201, json={"id": cap_id, "status": "running"}
|
||||||
)
|
)
|
||||||
return Capsule(api_key="wrn_test1234567890abcdef12345678")
|
return Capsule(api_key="wrn_test1234567890abcdef12345678", base_url=BASE)
|
||||||
|
|
||||||
|
|
||||||
class TestFilesRead:
|
class TestFilesRead:
|
||||||
@ -311,12 +311,14 @@ class TestPtySessionIteration:
|
|||||||
ws.receive_text.side_effect = messages
|
ws.receive_text.side_effect = messages
|
||||||
session = PtySession(ws, "cl-abc")
|
session = PtySession(ws, "cl-abc")
|
||||||
events = list(session)
|
events = list(session)
|
||||||
assert len(events) == 2
|
assert len(events) == 3
|
||||||
assert events[0].type == PtyEventType.started
|
assert events[0].type == PtyEventType.started
|
||||||
assert session.tag == "pty-abc12345"
|
assert session.tag == "pty-abc12345"
|
||||||
assert session.pid == 1
|
assert session.pid == 1
|
||||||
assert events[1].type == PtyEventType.output
|
assert events[1].type == PtyEventType.output
|
||||||
assert events[1].data == b"hello"
|
assert events[1].data == b"hello"
|
||||||
|
assert events[2].type == PtyEventType.exit
|
||||||
|
assert events[2].exit_code == 0
|
||||||
|
|
||||||
def test_iter_stops_on_fatal_error(self):
|
def test_iter_stops_on_fatal_error(self):
|
||||||
ws = MagicMock()
|
ws = MagicMock()
|
||||||
@ -461,10 +463,11 @@ class TestAsyncPtySession:
|
|||||||
events = []
|
events = []
|
||||||
async for event in session:
|
async for event in session:
|
||||||
events.append(event)
|
events.append(event)
|
||||||
assert len(events) == 2
|
assert len(events) == 3
|
||||||
assert events[0].type == PtyEventType.started
|
assert events[0].type == PtyEventType.started
|
||||||
assert session.tag == "pty-xyz"
|
assert session.tag == "pty-xyz"
|
||||||
assert session.pid == 5
|
assert session.pid == 5
|
||||||
|
assert events[2].type == PtyEventType.exit
|
||||||
|
|
||||||
|
|
||||||
class TestExports:
|
class TestExports:
|
||||||
|
|||||||
@ -73,7 +73,7 @@ def _make_git(respx_mock=None) -> Git:
|
|||||||
"""Create a Git instance bound to a test capsule."""
|
"""Create a Git instance bound to a test capsule."""
|
||||||
from wrenn.client import WrennClient
|
from wrenn.client import WrennClient
|
||||||
|
|
||||||
client = WrennClient(api_key="wrn_test1234567890abcdef12345678")
|
client = WrennClient(api_key="wrn_test1234567890abcdef12345678", base_url=BASE)
|
||||||
return Git(CAPSULE_ID, client.http)
|
return Git(CAPSULE_ID, client.http)
|
||||||
|
|
||||||
|
|
||||||
@ -81,7 +81,7 @@ def _make_async_git() -> AsyncGit:
|
|||||||
"""Create an AsyncGit instance bound to a test capsule."""
|
"""Create an AsyncGit instance bound to a test capsule."""
|
||||||
from wrenn.client import AsyncWrennClient
|
from wrenn.client import AsyncWrennClient
|
||||||
|
|
||||||
client = AsyncWrennClient(api_key="wrn_test1234567890abcdef12345678")
|
client = AsyncWrennClient(api_key="wrn_test1234567890abcdef12345678", base_url=BASE)
|
||||||
return AsyncGit(CAPSULE_ID, client.http)
|
return AsyncGit(CAPSULE_ID, client.http)
|
||||||
|
|
||||||
|
|
||||||
@ -926,7 +926,7 @@ class TestCapsuleWiring:
|
|||||||
respx.post(f"{BASE}/v1/capsules").respond(
|
respx.post(f"{BASE}/v1/capsules").respond(
|
||||||
201, json={"id": "cl-1", "status": "pending"}
|
201, json={"id": "cl-1", "status": "pending"}
|
||||||
)
|
)
|
||||||
cap = Capsule(api_key="wrn_test1234567890abcdef12345678")
|
cap = Capsule(api_key="wrn_test1234567890abcdef12345678", base_url=BASE)
|
||||||
assert hasattr(cap, "git")
|
assert hasattr(cap, "git")
|
||||||
assert isinstance(cap.git, Git)
|
assert isinstance(cap.git, Git)
|
||||||
|
|
||||||
@ -1017,7 +1017,7 @@ class TestCommandPayloadWrapping:
|
|||||||
from wrenn.client import WrennClient
|
from wrenn.client import WrennClient
|
||||||
from wrenn.commands import Commands
|
from wrenn.commands import Commands
|
||||||
|
|
||||||
client = WrennClient(api_key="wrn_test1234567890abcdef12345678")
|
client = WrennClient(api_key="wrn_test1234567890abcdef12345678", base_url=BASE)
|
||||||
commands = Commands(CAPSULE_ID, client.http)
|
commands = Commands(CAPSULE_ID, client.http)
|
||||||
|
|
||||||
route = respx.post(EXEC_URL).respond(200, json=_exec_response(stdout="3\n"))
|
route = respx.post(EXEC_URL).respond(200, json=_exec_response(stdout="3\n"))
|
||||||
@ -1031,7 +1031,7 @@ class TestCommandPayloadWrapping:
|
|||||||
from wrenn.client import WrennClient
|
from wrenn.client import WrennClient
|
||||||
from wrenn.commands import Commands
|
from wrenn.commands import Commands
|
||||||
|
|
||||||
client = WrennClient(api_key="wrn_test1234567890abcdef12345678")
|
client = WrennClient(api_key="wrn_test1234567890abcdef12345678", base_url=BASE)
|
||||||
commands = Commands(CAPSULE_ID, client.http)
|
commands = Commands(CAPSULE_ID, client.http)
|
||||||
|
|
||||||
route = respx.post(EXEC_URL).respond(200, json=_exec_response())
|
route = respx.post(EXEC_URL).respond(200, json=_exec_response())
|
||||||
@ -1045,7 +1045,7 @@ class TestCommandPayloadWrapping:
|
|||||||
from wrenn.client import WrennClient
|
from wrenn.client import WrennClient
|
||||||
from wrenn.commands import Commands
|
from wrenn.commands import Commands
|
||||||
|
|
||||||
client = WrennClient(api_key="wrn_test1234567890abcdef12345678")
|
client = WrennClient(api_key="wrn_test1234567890abcdef12345678", base_url=BASE)
|
||||||
commands = Commands(CAPSULE_ID, client.http)
|
commands = Commands(CAPSULE_ID, client.http)
|
||||||
|
|
||||||
route = respx.post(EXEC_URL).respond(200, json=_exec_response())
|
route = respx.post(EXEC_URL).respond(200, json=_exec_response())
|
||||||
@ -1059,7 +1059,7 @@ class TestCommandPayloadWrapping:
|
|||||||
from wrenn.client import WrennClient
|
from wrenn.client import WrennClient
|
||||||
from wrenn.commands import Commands
|
from wrenn.commands import Commands
|
||||||
|
|
||||||
client = WrennClient(api_key="wrn_test1234567890abcdef12345678")
|
client = WrennClient(api_key="wrn_test1234567890abcdef12345678", base_url=BASE)
|
||||||
commands = Commands(CAPSULE_ID, client.http)
|
commands = Commands(CAPSULE_ID, client.http)
|
||||||
|
|
||||||
route = respx.post(EXEC_URL).respond(200, json=_exec_response())
|
route = respx.post(EXEC_URL).respond(200, json=_exec_response())
|
||||||
@ -1073,7 +1073,7 @@ class TestCommandPayloadWrapping:
|
|||||||
from wrenn.client import WrennClient
|
from wrenn.client import WrennClient
|
||||||
from wrenn.commands import Commands
|
from wrenn.commands import Commands
|
||||||
|
|
||||||
client = WrennClient(api_key="wrn_test1234567890abcdef12345678")
|
client = WrennClient(api_key="wrn_test1234567890abcdef12345678", base_url=BASE)
|
||||||
commands = Commands(CAPSULE_ID, client.http)
|
commands = Commands(CAPSULE_ID, client.http)
|
||||||
|
|
||||||
route = respx.post(EXEC_URL).respond(200, json=_exec_response())
|
route = respx.post(EXEC_URL).respond(200, json=_exec_response())
|
||||||
@ -1089,7 +1089,7 @@ class TestCommandPayloadWrapping:
|
|||||||
from wrenn.client import WrennClient
|
from wrenn.client import WrennClient
|
||||||
from wrenn.commands import Commands
|
from wrenn.commands import Commands
|
||||||
|
|
||||||
client = WrennClient(api_key="wrn_test1234567890abcdef12345678")
|
client = WrennClient(api_key="wrn_test1234567890abcdef12345678", base_url=BASE)
|
||||||
commands = Commands(CAPSULE_ID, client.http)
|
commands = Commands(CAPSULE_ID, client.http)
|
||||||
|
|
||||||
route = respx.post(EXEC_URL).respond(200, json=_exec_response())
|
route = respx.post(EXEC_URL).respond(200, json=_exec_response())
|
||||||
@ -1119,7 +1119,7 @@ class TestCommandPayloadWrapping:
|
|||||||
from wrenn.client import WrennClient
|
from wrenn.client import WrennClient
|
||||||
from wrenn.commands import Commands
|
from wrenn.commands import Commands
|
||||||
|
|
||||||
client = WrennClient(api_key="wrn_test1234567890abcdef12345678")
|
client = WrennClient(api_key="wrn_test1234567890abcdef12345678", base_url=BASE)
|
||||||
commands = Commands(CAPSULE_ID, client.http)
|
commands = Commands(CAPSULE_ID, client.http)
|
||||||
|
|
||||||
route = respx.post(EXEC_URL).respond(200, json={"pid": 42, "tag": "bg-1"})
|
route = respx.post(EXEC_URL).respond(200, json={"pid": 42, "tag": "bg-1"})
|
||||||
|
|||||||
461
uv.lock
generated
461
uv.lock
generated
@ -72,6 +72,72 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", size = 153684, upload-time = "2026-02-25T02:54:15.766Z" },
|
{ url = "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", size = 153684, upload-time = "2026-02-25T02:54:15.766Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfgv"
|
||||||
|
version = "3.5.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/4e/b5/721b8799b04bf9afe054a3899c6cf4e880fcf8563cc71c15610242490a0c/cfgv-3.5.0.tar.gz", hash = "sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132", size = 7334, upload-time = "2025-11-19T20:55:51.612Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", size = 7445, upload-time = "2025-11-19T20:55:50.744Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[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/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]]
|
[[package]]
|
||||||
name = "click"
|
name = "click"
|
||||||
version = "8.3.2"
|
version = "8.3.2"
|
||||||
@ -93,6 +159,46 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
|
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "databind"
|
||||||
|
version = "4.5.4"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "deprecated" },
|
||||||
|
{ name = "nr-date" },
|
||||||
|
{ name = "nr-stream" },
|
||||||
|
{ name = "typeapi" },
|
||||||
|
{ name = "typing-extensions" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/71/9e/835a5211eeb7228a0e3870d54def48dd7951dbd951f51b30900020a5f9fc/databind-4.5.4.tar.gz", hash = "sha256:342e170a219b1661e5c1b20778b532aecfa67e46560ba75beb7e2c6faa2150b5", size = 43193, upload-time = "2026-04-02T22:21:47.318Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e2/db/3b8eb860b5baef89b72c7aadcc5072e1648ca0c98d6ba4b9e4eabbdc2cf5/databind-4.5.4-py3-none-any.whl", hash = "sha256:78467f874a3e80bcd1d3de349167587a0d369831bc64c03798520be86074f96e", size = 52381, upload-time = "2026-04-02T22:21:45.389Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "databind-core"
|
||||||
|
version = "4.5.4"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "databind" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/9e/dc/b63a6f6a404146e8e3f1226c9243a5cb30784a1f75218d014cafce9a411f/databind_core-4.5.4.tar.gz", hash = "sha256:a7a47af183d4a8046c893fc19fa9c085f287a15e57a05e58345016086ce0f807", size = 974, upload-time = "2026-04-02T22:21:56.588Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/fa/cf/1d1f4d37b4112f26ea5086d54200837d1dcbddaa536f3a70bb1d8b48ed9a/databind_core-4.5.4-py3-none-any.whl", hash = "sha256:25482c352f4f6fcade7c106c665553a18febeccda2972c00cf5af19f473960ab", size = 1666, upload-time = "2026-04-02T22:21:55.504Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "databind-json"
|
||||||
|
version = "4.5.4"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "databind" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/ab/72/9af59950a23ff6a03062acd85879de289595168ec43d6cec57253d00497c/databind_json-4.5.4.tar.gz", hash = "sha256:2c714f9c3039a81e42fc3826e47d7826ef020d93131c34daf4c9ae0483108e4d", size = 966, upload-time = "2026-04-02T22:22:05.538Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b3/d4/e00d531202314e29d90c9496f6b4730e3647128b9866180b8ce8ebb79394/databind_json-4.5.4-py3-none-any.whl", hash = "sha256:22e6faaeb6f2ec5cf815fd597a539dfe1f4846b80b618760112f4fe59a0898cc", size = 1664, upload-time = "2026-04-02T22:22:04.204Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "datamodel-code-generator"
|
name = "datamodel-code-generator"
|
||||||
version = "0.56.0"
|
version = "0.56.0"
|
||||||
@ -117,6 +223,27 @@ ruff = [
|
|||||||
{ name = "ruff" },
|
{ name = "ruff" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "deprecated"
|
||||||
|
version = "1.3.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "wrapt" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/49/85/12f0a49a7c4ffb70572b6c2ef13c90c88fd190debda93b23f026b25f9634/deprecated-1.3.1.tar.gz", hash = "sha256:b1b50e0ff0c1fddaa5708a2c6b0a6588bb09b892825ab2b214ac9ea9d92a5223", size = 2932523, upload-time = "2025-10-30T08:19:02.757Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/84/d0/205d54408c08b13550c733c4b85429e7ead111c7f0014309637425520a9a/deprecated-1.3.1-py2.py3-none-any.whl", hash = "sha256:597bfef186b6f60181535a29fbe44865ce137a5079f295b479886c82729d5f3f", size = 11298, upload-time = "2025-10-30T08:19:00.758Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "distlib"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dnspython"
|
name = "dnspython"
|
||||||
version = "2.8.0"
|
version = "2.8.0"
|
||||||
@ -126,6 +253,40 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094, upload-time = "2025-09-07T18:57:58.071Z" },
|
{ url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094, upload-time = "2025-09-07T18:57:58.071Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "docspec"
|
||||||
|
version = "2.2.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "databind-core" },
|
||||||
|
{ name = "databind-json" },
|
||||||
|
{ name = "deprecated" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/3c/39/7a71382107445b2cd50c67c6194e3e584f19748a817c3b29e8be8a14f00f/docspec-2.2.1.tar.gz", hash = "sha256:4854e77edc0e2de40e785e57e95880f7095a05fe978f8b54cef7a269586e15ff", size = 8646, upload-time = "2023-05-28T11:24:18.68Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/33/aa/0c9d71cc9d450afd3993d09835e2910810a45b0703f585e1aee1d9b78969/docspec-2.2.1-py3-none-any.whl", hash = "sha256:7538f750095a9688c6980ff9a4e029a823a500f64bd00b6b4bdb27951feb31cb", size = 9844, upload-time = "2023-05-28T11:24:15.419Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "docspec-python"
|
||||||
|
version = "2.2.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "black" },
|
||||||
|
{ name = "docspec" },
|
||||||
|
{ name = "nr-util" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/ea/ea/e6d9d9c2f805c6ac8072d0e3ee5b1da2dd61886c662327df937dec9f282c/docspec_python-2.2.2.tar.gz", hash = "sha256:429be834d09549461b95bf45eb53c16859f3dfb3e9220408b3bfb12812ccb3fb", size = 22154, upload-time = "2025-05-06T12:40:33.286Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/03/c2/b3226746fb6b91893da270a60e77bb420d59cf33a7b9a4e719a236955971/docspec_python-2.2.2-py3-none-any.whl", hash = "sha256:caa32dc1e8c470af8a5ecad67cca614e68c1563ac01dab0c0486c4d7f709d6b1", size = 15988, upload-time = "2025-05-06T12:40:31.554Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "docstring-parser"
|
||||||
|
version = "0.11"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/a2/ce/5d6a3782b9f88097ce3e579265015db3372ae78d12f67629b863a9208c96/docstring_parser-0.11.tar.gz", hash = "sha256:93b3f8f481c7d24e37c5d9f30293c89e2933fa209421c8abd731dd3ef0715ecb", size = 22775, upload-time = "2021-09-30T07:44:10.288Z" }
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "email-validator"
|
name = "email-validator"
|
||||||
version = "2.3.0"
|
version = "2.3.0"
|
||||||
@ -139,6 +300,15 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl", hash = "sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4", size = 35604, upload-time = "2025-08-26T13:09:05.858Z" },
|
{ url = "https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl", hash = "sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4", size = 35604, upload-time = "2025-08-26T13:09:05.858Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "filelock"
|
||||||
|
version = "3.29.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/b5/fe/997687a931ab51049acce6fa1f23e8f01216374ea81374ddee763c493db5/filelock-3.29.0.tar.gz", hash = "sha256:69974355e960702e789734cb4871f884ea6fe50bd8404051a3530bc07809cf90", size = 57571, upload-time = "2026-04-19T15:39:10.068Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/81/47/dd9a212ef6e343a6857485ffe25bba537304f1913bdbed446a23f7f592e1/filelock-3.29.0-py3-none-any.whl", hash = "sha256:96f5f6344709aa1572bbf631c640e4ebeeb519e08da902c39a001882f30ac258", size = 39812, upload-time = "2026-04-19T15:39:08.752Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "genson"
|
name = "genson"
|
||||||
version = "1.3.0"
|
version = "1.3.0"
|
||||||
@ -200,6 +370,15 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/98/f8/a6bc80313a9e93c888fa10534dfce2ad76ff86911b6f485777ce6de6a073/httpx_ws-0.9.0-py3-none-any.whl", hash = "sha256:71640d2fb1bf9a225775015b33cd755cfd4c5f7e21c885192fe3adc4c387b248", size = 15759, upload-time = "2026-03-28T14:11:11.887Z" },
|
{ url = "https://files.pythonhosted.org/packages/98/f8/a6bc80313a9e93c888fa10534dfce2ad76ff86911b6f485777ce6de6a073/httpx_ws-0.9.0-py3-none-any.whl", hash = "sha256:71640d2fb1bf9a225775015b33cd755cfd4c5f7e21c885192fe3adc4c387b248", size = 15759, upload-time = "2026-03-28T14:11:11.887Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "identify"
|
||||||
|
version = "2.6.19"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/52/63/51723b5f116cc04b061cb6f5a561790abf249d25931d515cd375e063e0f4/identify-2.6.19.tar.gz", hash = "sha256:6be5020c38fcb07da56c53733538a3081ea5aa70d36a156f83044bfbf9173842", size = 99567, upload-time = "2026-04-17T18:39:50.265Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/94/84/d9273cd09688070a6523c4aee4663a8538721b2b755c4962aafae0011e72/identify-2.6.19-py2.py3-none-any.whl", hash = "sha256:20e6a87f786f768c092a721ad107fc9df0eb89347be9396cadf3f4abbd1fb78a", size = 99397, upload-time = "2026-04-17T18:39:49.221Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "idna"
|
name = "idna"
|
||||||
version = "3.11"
|
version = "3.11"
|
||||||
@ -405,6 +584,46 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" },
|
{ url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nodeenv"
|
||||||
|
version = "1.10.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611, upload-time = "2025-12-20T14:08:54.006Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nr-date"
|
||||||
|
version = "2.1.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/a0/92/08110dd3d7ff5e2b852a220752eb6c40183839f5b7cc91f9f38dd2298e7d/nr_date-2.1.0.tar.gz", hash = "sha256:0643aea13bcdc2a8bc56af9d5e6a89ef244c9744a1ef00cdc735902ba7f7d2e6", size = 8789, upload-time = "2023-08-16T13:46:04.114Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f9/10/1d2b00172537c1522fe64bbc6fb16b015632a02f7b3864e788ccbcb4dd85/nr_date-2.1.0-py3-none-any.whl", hash = "sha256:bd672a9dfbdcf7c4b9289fea6750c42490eaee08036a72059dcc78cb236ed568", size = 10496, upload-time = "2023-08-16T13:46:02.627Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nr-stream"
|
||||||
|
version = "1.1.5"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/b7/37/e4d36d852c441233c306c5fbd98147685dce3ac9b0a8bbf4a587d0ea29ea/nr_stream-1.1.5.tar.gz", hash = "sha256:eb0216c6bfc61a46d4568dba3b588502c610ec8ddef4ac98f3932a2bd7264f65", size = 10053, upload-time = "2023-02-14T22:44:09.074Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1d/e1/f93485fe09aa36c0e1a3b76363efa1791241f7f863a010f725c95e8a74fe/nr_stream-1.1.5-py3-none-any.whl", hash = "sha256:47e12150b331ad2cb729cfd9d2abd281c9949809729ba461c6aa87dd9927b2d4", size = 10448, upload-time = "2023-02-14T22:44:07.72Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nr-util"
|
||||||
|
version = "0.8.12"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "deprecated" },
|
||||||
|
{ name = "typing-extensions" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/20/0c/078c567d95e25564bc1ede3c2cf6ce1c91f50648c83786354b47224326da/nr.util-0.8.12.tar.gz", hash = "sha256:a4549c2033d99d2f0379b3f3d233fd2a8ade286bbf0b3ad0cc7cea16022214f4", size = 63707, upload-time = "2022-06-20T13:29:29.192Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ba/58/eab08df9dbd69d9e21fc5e7be6f67454f386336ec71e6b64e378a2dddea4/nr.util-0.8.12-py3-none-any.whl", hash = "sha256:91da02ac9795eb8e015372275c1efe54bac9051231ee9b0e7e6f96b0b4e7d2bb", size = 90319, upload-time = "2022-06-20T13:29:27.312Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "packaging"
|
name = "packaging"
|
||||||
version = "26.0"
|
version = "26.0"
|
||||||
@ -441,6 +660,22 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
|
{ url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pre-commit"
|
||||||
|
version = "4.6.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "cfgv" },
|
||||||
|
{ name = "identify" },
|
||||||
|
{ name = "nodeenv" },
|
||||||
|
{ name = "pyyaml" },
|
||||||
|
{ name = "virtualenv" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/8e/22/2de9408ac81acbb8a7d05d4cc064a152ccf33b3d480ebe0cd292153db239/pre_commit-4.6.0.tar.gz", hash = "sha256:718d2208cef53fdc38206e40524a6d4d9576d103eb16f0fec11c875e7716e9d9", size = 198525, upload-time = "2026-04-21T20:31:41.613Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/80/6e/4b28b62ecb6aae56769c34a8ff1d661473ec1e9519e2d5f8b2c150086b26/pre_commit-4.6.0-py2.py3-none-any.whl", hash = "sha256:e2cf246f7299edcabcf15f9b0571fdce06058527f0a06535068a86d38089f29b", size = 226472, upload-time = "2026-04-21T20:31:40.092Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pydantic"
|
name = "pydantic"
|
||||||
version = "2.12.5"
|
version = "2.12.5"
|
||||||
@ -509,6 +744,31 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" },
|
{ url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pydoc-markdown"
|
||||||
|
version = "4.8.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "click" },
|
||||||
|
{ name = "databind-core" },
|
||||||
|
{ name = "databind-json" },
|
||||||
|
{ name = "docspec" },
|
||||||
|
{ name = "docspec-python" },
|
||||||
|
{ name = "docstring-parser" },
|
||||||
|
{ name = "jinja2" },
|
||||||
|
{ name = "nr-util" },
|
||||||
|
{ name = "pyyaml" },
|
||||||
|
{ name = "requests" },
|
||||||
|
{ name = "tomli" },
|
||||||
|
{ name = "tomli-w" },
|
||||||
|
{ name = "watchdog" },
|
||||||
|
{ name = "yapf" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/e1/8a/2c7f7ad656d22371a596d232fc140327b958d7f1d491b889632ea0cb7e87/pydoc_markdown-4.8.2.tar.gz", hash = "sha256:fb6c927e31386de17472d42f9bd3d3be2905977d026f6216881c65145aa67f0b", size = 44506, upload-time = "2023-06-26T12:37:01.152Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/83/5a/ce0b056d9a95fd0c06a6cfa5972477d79353392d19230c748a7ba5a9df04/pydoc_markdown-4.8.2-py3-none-any.whl", hash = "sha256:203f74119e6bb2f9deba43d452422de7c8ec31955b61e0620fa4dd8c2611715f", size = 67830, upload-time = "2023-06-26T12:36:59.502Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pygments"
|
name = "pygments"
|
||||||
version = "2.20.0"
|
version = "2.20.0"
|
||||||
@ -546,6 +806,19 @@ 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" },
|
{ 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 = "python-discovery"
|
||||||
|
version = "1.2.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "filelock" },
|
||||||
|
{ name = "platformdirs" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/de/ef/3bae0e537cfe91e8431efcba4434463d2c5a65f5a89edd47c6cf2f03c55f/python_discovery-1.2.2.tar.gz", hash = "sha256:876e9c57139eb757cb5878cbdd9ae5379e5d96266c99ef731119e04fffe533bb", size = 58872, upload-time = "2026-04-07T17:28:49.249Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d8/db/795879cc3ddfe338599bddea6388cc5100b088db0a4caf6e6c1af1c27e04/python_discovery-1.2.2-py3-none-any.whl", hash = "sha256:e1ae95d9af875e78f15e19aed0c6137ab1bb49c200f21f5061786490c9585c7a", size = 31894, upload-time = "2026-04-07T17:28:48.09Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pytokens"
|
name = "pytokens"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
@ -606,6 +879,21 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" },
|
{ url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[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]]
|
[[package]]
|
||||||
name = "respx"
|
name = "respx"
|
||||||
version = "0.23.1"
|
version = "0.23.1"
|
||||||
@ -643,6 +931,63 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/58/ed/dea90a65b7d9e69888890fb14c90d7f51bf0c1e82ad800aeb0160e4bacfd/ruff-0.15.10-py3-none-win_arm64.whl", hash = "sha256:601d1610a9e1f1c2165a4f561eeaa2e2ea1e97f3287c5aa258d3dab8b57c6188", size = 11035607, upload-time = "2026-04-09T14:05:47.593Z" },
|
{ url = "https://files.pythonhosted.org/packages/58/ed/dea90a65b7d9e69888890fb14c90d7f51bf0c1e82ad800aeb0160e4bacfd/ruff-0.15.10-py3-none-win_arm64.whl", hash = "sha256:601d1610a9e1f1c2165a4f561eeaa2e2ea1e97f3287c5aa258d3dab8b57c6188", size = 11035607, upload-time = "2026-04-09T14:05:47.593Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tomli"
|
||||||
|
version = "2.4.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/22/de/48c59722572767841493b26183a0d1cc411d54fd759c5607c4590b6563a6/tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f", size = 17543, upload-time = "2026-03-25T20:22:03.828Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/07/06/b823a7e818c756d9a7123ba2cda7d07bc2dd32835648d1a7b7b7a05d848d/tomli-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54", size = 155866, upload-time = "2026-03-25T20:21:31.65Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/14/6f/12645cf7f08e1a20c7eb8c297c6f11d31c1b50f316a7e7e1e1de6e2e7b7e/tomli-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a", size = 149887, upload-time = "2026-03-25T20:21:33.028Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5c/e0/90637574e5e7212c09099c67ad349b04ec4d6020324539297b634a0192b0/tomli-2.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897", size = 243704, upload-time = "2026-03-25T20:21:34.51Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/10/8f/d3ddb16c5a4befdf31a23307f72828686ab2096f068eaf56631e136c1fdd/tomli-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f", size = 251628, upload-time = "2026-03-25T20:21:36.012Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e3/f1/dbeeb9116715abee2485bf0a12d07a8f31af94d71608c171c45f64c0469d/tomli-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d", size = 247180, upload-time = "2026-03-25T20:21:37.136Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d3/74/16336ffd19ed4da28a70959f92f506233bd7cfc2332b20bdb01591e8b1d1/tomli-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5", size = 251674, upload-time = "2026-03-25T20:21:38.298Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/16/f9/229fa3434c590ddf6c0aa9af64d3af4b752540686cace29e6281e3458469/tomli-2.4.1-cp313-cp313-win32.whl", hash = "sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd", size = 97976, upload-time = "2026-03-25T20:21:39.316Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6a/1e/71dfd96bcc1c775420cb8befe7a9d35f2e5b1309798f009dca17b7708c1e/tomli-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36", size = 108755, upload-time = "2026-03-25T20:21:40.248Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/83/7a/d34f422a021d62420b78f5c538e5b102f62bea616d1d75a13f0a88acb04a/tomli-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd", size = 95265, upload-time = "2026-03-25T20:21:41.219Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3c/fb/9a5c8d27dbab540869f7c1f8eb0abb3244189ce780ba9cd73f3770662072/tomli-2.4.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf", size = 155726, upload-time = "2026-03-25T20:21:42.23Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/62/05/d2f816630cc771ad836af54f5001f47a6f611d2d39535364f148b6a92d6b/tomli-2.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac", size = 149859, upload-time = "2026-03-25T20:21:43.386Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ce/48/66341bdb858ad9bd0ceab5a86f90eddab127cf8b046418009f2125630ecb/tomli-2.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662", size = 244713, upload-time = "2026-03-25T20:21:44.474Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/df/6d/c5fad00d82b3c7a3ab6189bd4b10e60466f22cfe8a08a9394185c8a8111c/tomli-2.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853", size = 252084, upload-time = "2026-03-25T20:21:45.62Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/00/71/3a69e86f3eafe8c7a59d008d245888051005bd657760e96d5fbfb0b740c2/tomli-2.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15", size = 247973, upload-time = "2026-03-25T20:21:46.937Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/67/50/361e986652847fec4bd5e4a0208752fbe64689c603c7ae5ea7cb16b1c0ca/tomli-2.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba", size = 256223, upload-time = "2026-03-25T20:21:48.467Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/8c/9a/b4173689a9203472e5467217e0154b00e260621caa227b6fa01feab16998/tomli-2.4.1-cp314-cp314-win32.whl", hash = "sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6", size = 98973, upload-time = "2026-03-25T20:21:49.526Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/14/58/640ac93bf230cd27d002462c9af0d837779f8773bc03dee06b5835208214/tomli-2.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7", size = 109082, upload-time = "2026-03-25T20:21:50.506Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d5/2f/702d5e05b227401c1068f0d386d79a589bb12bf64c3d2c72ce0631e3bc49/tomli-2.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232", size = 96490, upload-time = "2026-03-25T20:21:51.474Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/45/4b/b877b05c8ba62927d9865dd980e34a755de541eb65fffba52b4cc495d4d2/tomli-2.4.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4", size = 164263, upload-time = "2026-03-25T20:21:52.543Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/24/79/6ab420d37a270b89f7195dec5448f79400d9e9c1826df982f3f8e97b24fd/tomli-2.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c", size = 160736, upload-time = "2026-03-25T20:21:53.674Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/02/e0/3630057d8eb170310785723ed5adcdfb7d50cb7e6455f85ba8a3deed642b/tomli-2.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d", size = 270717, upload-time = "2026-03-25T20:21:55.129Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7a/b4/1613716072e544d1a7891f548d8f9ec6ce2faf42ca65acae01d76ea06bb0/tomli-2.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41", size = 278461, upload-time = "2026-03-25T20:21:56.228Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/05/38/30f541baf6a3f6df77b3df16b01ba319221389e2da59427e221ef417ac0c/tomli-2.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c", size = 274855, upload-time = "2026-03-25T20:21:57.653Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/77/a3/ec9dd4fd2c38e98de34223b995a3b34813e6bdadf86c75314c928350ed14/tomli-2.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f", size = 283144, upload-time = "2026-03-25T20:21:59.089Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ef/be/605a6261cac79fba2ec0c9827e986e00323a1945700969b8ee0b30d85453/tomli-2.4.1-cp314-cp314t-win32.whl", hash = "sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8", size = 108683, upload-time = "2026-03-25T20:22:00.214Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/12/64/da524626d3b9cc40c168a13da8335fe1c51be12c0a63685cc6db7308daae/tomli-2.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26", size = 121196, upload-time = "2026-03-25T20:22:01.169Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5a/cd/e80b62269fc78fc36c9af5a6b89c835baa8af28ff5ad28c7028d60860320/tomli-2.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396", size = 100393, upload-time = "2026-03-25T20:22:02.137Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583, upload-time = "2026-03-25T20:22:03.012Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tomli-w"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/19/75/241269d1da26b624c0d5e110e8149093c759b7a286138f4efd61a60e75fe/tomli_w-1.2.0.tar.gz", hash = "sha256:2dd14fac5a47c27be9cd4c976af5a12d87fb1f0b4512f81d69cce3b35ae25021", size = 7184, upload-time = "2025-01-15T12:07:24.262Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c7/18/c86eb8e0202e32dd3df50d43d7ff9854f8e0603945ff398974c1d91ac1ef/tomli_w-1.2.0-py3-none-any.whl", hash = "sha256:188306098d013b691fcadc011abd66727d3c414c571bb01b1a174ba8c983cf90", size = 6675, upload-time = "2025-01-15T12:07:22.074Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typeapi"
|
||||||
|
version = "2.3.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "typing-extensions" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/d5/92/5a23ad34aa877edf00906166e339bfdc571543ea183ea7ab727bb01516c7/typeapi-2.3.0.tar.gz", hash = "sha256:a60d11f72c5ec27338cfd1c807f035b0b16ed2e3b798fb1c1d34fc5589f544be", size = 122687, upload-time = "2025-10-23T13:44:11.26Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d4/84/021bbeb7edb990dd6875cb6ab08d32faaa49fec63453d863730260a01f9e/typeapi-2.3.0-py3-none-any.whl", hash = "sha256:576b7dcb94412e91c5cae107a393674f8f99c10a24beb8be2302e3fed21d5cc2", size = 26858, upload-time = "2025-10-23T13:44:09.833Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typeguard"
|
name = "typeguard"
|
||||||
version = "4.5.1"
|
version = "4.5.1"
|
||||||
@ -676,9 +1021,107 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" },
|
{ url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[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" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "virtualenv"
|
||||||
|
version = "21.3.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "distlib" },
|
||||||
|
{ name = "filelock" },
|
||||||
|
{ name = "platformdirs" },
|
||||||
|
{ name = "python-discovery" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/3f/8b/6331f7a7fe70131c301106ec1e7cf23e2501bf7d4ca3636805801ca191bb/virtualenv-21.3.0.tar.gz", hash = "sha256:733750db978ec95c2d8eb4feadaa57091002bce404cb39ba69899cf7bd28944e", size = 7614069, upload-time = "2026-04-27T17:05:58.927Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/4b/eb/03bfb1299d4c4510329e470f13f9a4ce793df7fcb5a2fd3510f911066f61/virtualenv-21.3.0-py3-none-any.whl", hash = "sha256:4d28ee41f6d9ec8f1f00cd472b9ffbcedda1b3d3b9a575b5c94a2d004fd51bd7", size = 7594690, upload-time = "2026-04-27T17:05:55.468Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "watchdog"
|
||||||
|
version = "6.0.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220, upload-time = "2024-11-01T14:07:13.037Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/68/98/b0345cabdce2041a01293ba483333582891a3bd5769b08eceb0d406056ef/watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c", size = 96480, upload-time = "2024-11-01T14:06:42.952Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/85/83/cdf13902c626b28eedef7ec4f10745c52aad8a8fe7eb04ed7b1f111ca20e/watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134", size = 88451, upload-time = "2024-11-01T14:06:45.084Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/fe/c4/225c87bae08c8b9ec99030cd48ae9c4eca050a59bf5c2255853e18c87b50/watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b", size = 89057, upload-time = "2024-11-01T14:06:47.324Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079, upload-time = "2024-11-01T14:06:59.472Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078, upload-time = "2024-11-01T14:07:01.431Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076, upload-time = "2024-11-01T14:07:02.568Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077, upload-time = "2024-11-01T14:07:03.893Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078, upload-time = "2024-11-01T14:07:05.189Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077, upload-time = "2024-11-01T14:07:06.376Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078, upload-time = "2024-11-01T14:07:07.547Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065, upload-time = "2024-11-01T14:07:09.525Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070, upload-time = "2024-11-01T14:07:10.686Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload-time = "2024-11-01T14:07:11.845Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wrapt"
|
||||||
|
version = "2.1.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/2e/64/925f213fdcbb9baeb1530449ac71a4d57fc361c053d06bf78d0c5c7cd80c/wrapt-2.1.2.tar.gz", hash = "sha256:3996a67eecc2c68fd47b4e3c564405a5777367adfd9b8abb58387b63ee83b21e", size = 81678, upload-time = "2026-03-06T02:53:25.134Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/4c/7a/d936840735c828b38d26a854e85d5338894cda544cb7a85a9d5b8b9c4df7/wrapt-2.1.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:787fd6f4d67befa6fe2abdffcbd3de2d82dfc6fb8a6d850407c53332709d030b", size = 61259, upload-time = "2026-03-06T02:53:41.922Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5e/88/9a9b9a90ac8ca11c2fdb6a286cb3a1fc7dd774c00ed70929a6434f6bc634/wrapt-2.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4bdf26e03e6d0da3f0e9422fd36bcebf7bc0eeb55fdf9c727a09abc6b9fe472e", size = 61851, upload-time = "2026-03-06T02:52:48.672Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/03/a9/5b7d6a16fd6533fed2756900fc8fc923f678179aea62ada6d65c92718c00/wrapt-2.1.2-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bbac24d879aa22998e87f6b3f481a5216311e7d53c7db87f189a7a0266dafffb", size = 121446, upload-time = "2026-03-06T02:54:14.013Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/45/bb/34c443690c847835cfe9f892be78c533d4f32366ad2888972c094a897e39/wrapt-2.1.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:16997dfb9d67addc2e3f41b62a104341e80cac52f91110dece393923c0ebd5ca", size = 123056, upload-time = "2026-03-06T02:54:10.829Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/93/b9/ff205f391cb708f67f41ea148545f2b53ff543a7ac293b30d178af4d2271/wrapt-2.1.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:162e4e2ba7542da9027821cb6e7c5e068d64f9a10b5f15512ea28e954893a267", size = 117359, upload-time = "2026-03-06T02:53:03.623Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1f/3d/1ea04d7747825119c3c9a5e0874a40b33594ada92e5649347c457d982805/wrapt-2.1.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f29c827a8d9936ac320746747a016c4bc66ef639f5cd0d32df24f5eacbf9c69f", size = 121479, upload-time = "2026-03-06T02:53:45.844Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/78/cc/ee3a011920c7a023b25e8df26f306b2484a531ab84ca5c96260a73de76c0/wrapt-2.1.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:a9dd9813825f7ecb018c17fd147a01845eb330254dff86d3b5816f20f4d6aaf8", size = 116271, upload-time = "2026-03-06T02:54:46.356Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/98/fd/e5ff7ded41b76d802cf1191288473e850d24ba2e39a6ec540f21ae3b57cb/wrapt-2.1.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6f8dbdd3719e534860d6a78526aafc220e0241f981367018c2875178cf83a413", size = 120573, upload-time = "2026-03-06T02:52:50.163Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/47/c5/242cae3b5b080cd09bacef0591691ba1879739050cc7c801ff35c8886b66/wrapt-2.1.2-cp313-cp313-win32.whl", hash = "sha256:5c35b5d82b16a3bc6e0a04349b606a0582bc29f573786aebe98e0c159bc48db6", size = 58205, upload-time = "2026-03-06T02:53:47.494Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/12/69/c358c61e7a50f290958809b3c61ebe8b3838ea3e070d7aac9814f95a0528/wrapt-2.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:f8bc1c264d8d1cf5b3560a87bbdd31131573eb25f9f9447bb6252b8d4c44a3a1", size = 60452, upload-time = "2026-03-06T02:53:30.038Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/8e/66/c8a6fcfe321295fd8c0ab1bd685b5a01462a9b3aa2f597254462fc2bc975/wrapt-2.1.2-cp313-cp313-win_arm64.whl", hash = "sha256:3beb22f674550d5634642c645aba4c72a2c66fb185ae1aebe1e955fae5a13baf", size = 58842, upload-time = "2026-03-06T02:52:52.114Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/da/55/9c7052c349106e0b3f17ae8db4b23a691a963c334de7f9dbd60f8f74a831/wrapt-2.1.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0fc04bc8664a8bc4c8e00b37b5355cffca2535209fba1abb09ae2b7c76ddf82b", size = 63075, upload-time = "2026-03-06T02:53:19.108Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/09/a8/ce7b4006f7218248dd71b7b2b732d0710845a0e49213b18faef64811ffef/wrapt-2.1.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a9b9d50c9af998875a1482a038eb05755dfd6fe303a313f6a940bb53a83c3f18", size = 63719, upload-time = "2026-03-06T02:54:33.452Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e4/e5/2ca472e80b9e2b7a17f106bb8f9df1db11e62101652ce210f66935c6af67/wrapt-2.1.2-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2d3ff4f0024dd224290c0eabf0240f1bfc1f26363431505fb1b0283d3b08f11d", size = 152643, upload-time = "2026-03-06T02:52:42.721Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/36/42/30f0f2cefca9d9cbf6835f544d825064570203c3e70aa873d8ae12e23791/wrapt-2.1.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3278c471f4468ad544a691b31bb856374fbdefb7fee1a152153e64019379f015", size = 158805, upload-time = "2026-03-06T02:54:25.441Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/bb/67/d08672f801f604889dcf58f1a0b424fe3808860ede9e03affc1876b295af/wrapt-2.1.2-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a8914c754d3134a3032601c6984db1c576e6abaf3fc68094bb8ab1379d75ff92", size = 145990, upload-time = "2026-03-06T02:53:57.456Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/68/a7/fd371b02e73babec1de6ade596e8cd9691051058cfdadbfd62a5898f3295/wrapt-2.1.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:ff95d4264e55839be37bafe1536db2ab2de19da6b65f9244f01f332b5286cfbf", size = 155670, upload-time = "2026-03-06T02:54:55.309Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/86/2d/9fe0095dfdb621009f40117dcebf41d7396c2c22dca6eac779f4c007b86c/wrapt-2.1.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:76405518ca4e1b76fbb1b9f686cff93aebae03920cc55ceeec48ff9f719c5f67", size = 144357, upload-time = "2026-03-06T02:54:24.092Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0e/b6/ec7b4a254abbe4cde9fa15c5d2cca4518f6b07d0f1b77d4ee9655e30280e/wrapt-2.1.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c0be8b5a74c5824e9359b53e7e58bef71a729bacc82e16587db1c4ebc91f7c5a", size = 150269, upload-time = "2026-03-06T02:53:31.268Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6e/6b/2fabe8ebf148f4ee3c782aae86a795cc68ffe7d432ef550f234025ce0cfa/wrapt-2.1.2-cp313-cp313t-win32.whl", hash = "sha256:f01277d9a5fc1862f26f7626da9cf443bebc0abd2f303f41c5e995b15887dabd", size = 59894, upload-time = "2026-03-06T02:54:15.391Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ca/fb/9ba66fc2dedc936de5f8073c0217b5d4484e966d87723415cc8262c5d9c2/wrapt-2.1.2-cp313-cp313t-win_amd64.whl", hash = "sha256:84ce8f1c2104d2f6daa912b1b5b039f331febfeee74f8042ad4e04992bd95c8f", size = 63197, upload-time = "2026-03-06T02:54:41.943Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c0/1c/012d7423c95d0e337117723eb8ecf73c622ce15a97847e84cf3f8f26cd7e/wrapt-2.1.2-cp313-cp313t-win_arm64.whl", hash = "sha256:a93cd767e37faeddbe07d8fc4212d5cba660af59bdb0f6372c93faaa13e6e679", size = 60363, upload-time = "2026-03-06T02:54:48.093Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/39/25/e7ea0b417db02bb796182a5316398a75792cd9a22528783d868755e1f669/wrapt-2.1.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:1370e516598854e5b4366e09ce81e08bfe94d42b0fd569b88ec46cc56d9164a9", size = 61418, upload-time = "2026-03-06T02:53:55.706Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ec/0f/fa539e2f6a770249907757eaeb9a5ff4deb41c026f8466c1c6d799088a9b/wrapt-2.1.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:6de1a3851c27e0bd6a04ca993ea6f80fc53e6c742ee1601f486c08e9f9b900a9", size = 61914, upload-time = "2026-03-06T02:52:53.37Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/53/37/02af1867f5b1441aaeda9c82deed061b7cd1372572ddcd717f6df90b5e93/wrapt-2.1.2-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:de9f1a2bbc5ac7f6012ec24525bdd444765a2ff64b5985ac6e0692144838542e", size = 120417, upload-time = "2026-03-06T02:54:30.74Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c3/b7/0138a6238c8ba7476c77cf786a807f871672b37f37a422970342308276e7/wrapt-2.1.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:970d57ed83fa040d8b20c52fe74a6ae7e3775ae8cff5efd6a81e06b19078484c", size = 122797, upload-time = "2026-03-06T02:54:51.539Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e1/ad/819ae558036d6a15b7ed290d5b14e209ca795dd4da9c58e50c067d5927b0/wrapt-2.1.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3969c56e4563c375861c8df14fa55146e81ac11c8db49ea6fb7f2ba58bc1ff9a", size = 117350, upload-time = "2026-03-06T02:54:37.651Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/8b/2d/afc18dc57a4600a6e594f77a9ae09db54f55ba455440a54886694a84c71b/wrapt-2.1.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:57d7c0c980abdc5f1d98b11a2aa3bb159790add80258c717fa49a99921456d90", size = 121223, upload-time = "2026-03-06T02:54:35.221Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b9/5b/5ec189b22205697bc56eb3b62aed87a1e0423e9c8285d0781c7a83170d15/wrapt-2.1.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:776867878e83130c7a04237010463372e877c1c994d449ca6aaafeab6aab2586", size = 116287, upload-time = "2026-03-06T02:54:19.654Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f7/2d/f84939a7c9b5e6cdd8a8d0f6a26cabf36a0f7e468b967720e8b0cd2bdf69/wrapt-2.1.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:fab036efe5464ec3291411fabb80a7a39e2dd80bae9bcbeeca5087fdfa891e19", size = 119593, upload-time = "2026-03-06T02:54:16.697Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0b/fe/ccd22a1263159c4ac811ab9374c061bcb4a702773f6e06e38de5f81a1bdc/wrapt-2.1.2-cp314-cp314-win32.whl", hash = "sha256:e6ed62c82ddf58d001096ae84ce7f833db97ae2263bff31c9b336ba8cfe3f508", size = 58631, upload-time = "2026-03-06T02:53:06.498Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/65/0a/6bd83be7bff2e7efaac7b4ac9748da9d75a34634bbbbc8ad077d527146df/wrapt-2.1.2-cp314-cp314-win_amd64.whl", hash = "sha256:467e7c76315390331c67073073d00662015bb730c566820c9ca9b54e4d67fd04", size = 60875, upload-time = "2026-03-06T02:53:50.252Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6c/c0/0b3056397fe02ff80e5a5d72d627c11eb885d1ca78e71b1a5c1e8c7d45de/wrapt-2.1.2-cp314-cp314-win_arm64.whl", hash = "sha256:da1f00a557c66225d53b095a97eace0fc5349e3bfda28fa34ffae238978ee575", size = 59164, upload-time = "2026-03-06T02:53:59.128Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/71/ed/5d89c798741993b2371396eb9d4634f009ff1ad8a6c78d366fe2883ea7a6/wrapt-2.1.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:62503ffbc2d3a69891cf29beeaccdb4d5e0a126e2b6a851688d4777e01428dbb", size = 63163, upload-time = "2026-03-06T02:52:54.873Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c6/8c/05d277d182bf36b0a13d6bd393ed1dec3468a25b59d01fba2dd70fe4d6ae/wrapt-2.1.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c7e6cd120ef837d5b6f860a6ea3745f8763805c418bb2f12eeb1fa6e25f22d22", size = 63723, upload-time = "2026-03-06T02:52:56.374Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f4/27/6c51ec1eff4413c57e72d6106bb8dec6f0c7cdba6503d78f0fa98767bcc9/wrapt-2.1.2-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3769a77df8e756d65fbc050333f423c01ae012b4f6731aaf70cf2bef61b34596", size = 152652, upload-time = "2026-03-06T02:53:23.79Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/db/4c/d7dd662d6963fc7335bfe29d512b02b71cdfa23eeca7ab3ac74a67505deb/wrapt-2.1.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a76d61a2e851996150ba0f80582dd92a870643fa481f3b3846f229de88caf044", size = 158807, upload-time = "2026-03-06T02:53:35.742Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b4/4d/1e5eea1a78d539d346765727422976676615814029522c76b87a95f6bcdd/wrapt-2.1.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6f97edc9842cf215312b75fe737ee7c8adda75a89979f8e11558dfff6343cc4b", size = 146061, upload-time = "2026-03-06T02:52:57.574Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/89/bc/62cabea7695cd12a288023251eeefdcb8465056ddaab6227cb78a2de005b/wrapt-2.1.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4006c351de6d5007aa33a551f600404ba44228a89e833d2fadc5caa5de8edfbf", size = 155667, upload-time = "2026-03-06T02:53:39.422Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e9/99/6f2888cd68588f24df3a76572c69c2de28287acb9e1972bf0c83ce97dbc1/wrapt-2.1.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:a9372fc3639a878c8e7d87e1556fa209091b0a66e912c611e3f833e2c4202be2", size = 144392, upload-time = "2026-03-06T02:54:22.41Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/40/51/1dfc783a6c57971614c48e361a82ca3b6da9055879952587bc99fe1a7171/wrapt-2.1.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3144b027ff30cbd2fca07c0a87e67011adb717eb5f5bd8496325c17e454257a3", size = 150296, upload-time = "2026-03-06T02:54:07.848Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6c/38/cbb8b933a0201076c1f64fc42883b0023002bdc14a4964219154e6ff3350/wrapt-2.1.2-cp314-cp314t-win32.whl", hash = "sha256:3b8d15e52e195813efe5db8cec156eebe339aaf84222f4f4f051a6c01f237ed7", size = 60539, upload-time = "2026-03-06T02:54:00.594Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/82/dd/e5176e4b241c9f528402cebb238a36785a628179d7d8b71091154b3e4c9e/wrapt-2.1.2-cp314-cp314t-win_amd64.whl", hash = "sha256:08ffa54146a7559f5b8df4b289b46d963a8e74ed16ba3687f99896101a3990c5", size = 63969, upload-time = "2026-03-06T02:54:39Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5c/99/79f17046cf67e4a95b9987ea129632ba8bcec0bc81f3fb3d19bdb0bd60cd/wrapt-2.1.2-cp314-cp314t-win_arm64.whl", hash = "sha256:72aaa9d0d8e4ed0e2e98019cea47a21f823c9dd4b43c7b77bba6679ffcca6a00", size = 60554, upload-time = "2026-03-06T02:53:14.132Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1a/c7/8528ac2dfa2c1e6708f647df7ae144ead13f0a31146f43c7264b4942bf12/wrapt-2.1.2-py3-none-any.whl", hash = "sha256:b8fd6fa2b2c4e7621808f8c62e8317f4aae56e59721ad933bac5239d913cf0e8", size = 43993, upload-time = "2026-03-06T02:53:12.905Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wrenn"
|
name = "wrenn"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
source = { editable = "." }
|
source = { editable = "." }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "email-validator" },
|
{ name = "email-validator" },
|
||||||
@ -691,6 +1134,8 @@ dependencies = [
|
|||||||
dev = [
|
dev = [
|
||||||
{ name = "datamodel-code-generator", extra = ["ruff"] },
|
{ name = "datamodel-code-generator", extra = ["ruff"] },
|
||||||
{ name = "mypy" },
|
{ name = "mypy" },
|
||||||
|
{ name = "pre-commit" },
|
||||||
|
{ name = "pydoc-markdown" },
|
||||||
{ name = "pytest" },
|
{ name = "pytest" },
|
||||||
{ name = "pytest-asyncio" },
|
{ name = "pytest-asyncio" },
|
||||||
{ name = "respx" },
|
{ name = "respx" },
|
||||||
@ -709,6 +1154,8 @@ requires-dist = [
|
|||||||
dev = [
|
dev = [
|
||||||
{ name = "datamodel-code-generator", extras = ["ruff"], specifier = ">=0.56.0" },
|
{ name = "datamodel-code-generator", extras = ["ruff"], specifier = ">=0.56.0" },
|
||||||
{ name = "mypy", specifier = ">=1.20.0" },
|
{ name = "mypy", specifier = ">=1.20.0" },
|
||||||
|
{ name = "pre-commit", specifier = ">=4.6.0" },
|
||||||
|
{ name = "pydoc-markdown", specifier = ">=4.8.2" },
|
||||||
{ name = "pytest", specifier = ">=9.0.3" },
|
{ name = "pytest", specifier = ">=9.0.3" },
|
||||||
{ name = "pytest-asyncio", specifier = ">=1.3.0" },
|
{ name = "pytest-asyncio", specifier = ">=1.3.0" },
|
||||||
{ name = "respx", specifier = ">=0.23.1" },
|
{ name = "respx", specifier = ">=0.23.1" },
|
||||||
@ -726,3 +1173,15 @@ sdist = { url = "https://files.pythonhosted.org/packages/c7/79/12135bdf8b9c9367b
|
|||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/a4/f5/10b68b7b1544245097b2a1b8238f66f2fc6dcaeb24ba5d917f52bd2eed4f/wsproto-1.3.2-py3-none-any.whl", hash = "sha256:61eea322cdf56e8cc904bd3ad7573359a242ba65688716b0710a5eb12beab584", size = 24405, upload-time = "2025-11-20T18:18:00.454Z" },
|
{ url = "https://files.pythonhosted.org/packages/a4/f5/10b68b7b1544245097b2a1b8238f66f2fc6dcaeb24ba5d917f52bd2eed4f/wsproto-1.3.2-py3-none-any.whl", hash = "sha256:61eea322cdf56e8cc904bd3ad7573359a242ba65688716b0710a5eb12beab584", size = 24405, upload-time = "2025-11-20T18:18:00.454Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "yapf"
|
||||||
|
version = "0.43.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "platformdirs" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/23/97/b6f296d1e9cc1ec25c7604178b48532fa5901f721bcf1b8d8148b13e5588/yapf-0.43.0.tar.gz", hash = "sha256:00d3aa24bfedff9420b2e0d5d9f5ab6d9d4268e72afbf59bb3fa542781d5218e", size = 254907, upload-time = "2024-11-14T00:11:41.584Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/37/81/6acd6601f61e31cfb8729d3da6d5df966f80f374b78eff83760714487338/yapf-0.43.0-py3-none-any.whl", hash = "sha256:224faffbc39c428cb095818cf6ef5511fdab6f7430a10783fdfb292ccf2852ca", size = 256158, upload-time = "2024-11-14T00:11:39.37Z" },
|
||||||
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user