Merge pull request #7 from darth-veitcher/docs/playwright-screenshots
docs: automate README screenshots with Playwright
@@ -40,3 +40,12 @@ rebuild:
|
||||
# CI smoke test — builds from COPY, starts with --quick-test-for-ci, exits
|
||||
ci-smoke:
|
||||
{{ci}} up --build --exit-code-from comfyui
|
||||
|
||||
# Regenerate docs/assets/ screenshots from the live ComfyUI dev harness.
|
||||
# Starts the harness if not already running; leaves it running afterwards.
|
||||
screenshots:
|
||||
@echo "Starting dev harness (no-op if already up)…"
|
||||
{{dev}} up -d
|
||||
@echo "Waiting for ComfyUI to be healthy…"
|
||||
@until docker compose -f docker-compose.yml -f docker-compose.dev.yml ps --format json | python3 -c "import sys,json; s=[c for c in json.load(sys.stdin) if c.get('Name','').endswith('comfyui')]; exit(0 if s and s[0].get('Health')=='healthy' else 1)" 2>/dev/null; do sleep 3; done
|
||||
uv run scripts/take_screenshots.py
|
||||
|
||||
@@ -10,76 +10,73 @@ A collection of workflow efficiency and quality-of-life nodes built out of neces
|
||||
|------|-------------|
|
||||
| **Format String** | Formats a string from a Python f-string or Jinja2 template. Detects variables in the template and automatically adds/removes input sockets. |
|
||||
| **Random Choice** | Accepts any number of typed inputs and outputs one at random, with a configurable seed for reproducibility. |
|
||||
| **Circuit Breaker** | Halts the current ComfyUI queue run gracefully (raises `InterruptProcessingException`) without crashing the server. |
|
||||
| **Circuit Breaker** | Halts the current ComfyUI queue run gracefully without crashing the server. Wire the `status` toggle to a boolean condition to skip the rest of the queue when a condition isn't met. |
|
||||
|
||||
## Install
|
||||
|
||||
1. Clone this repo into your ComfyUI `custom_nodes/` directory:
|
||||
**Via ComfyUI Manager** (recommended): search for `comfydv` and click Install.
|
||||
|
||||
```bash
|
||||
cd /path/to/ComfyUI/custom_nodes
|
||||
git clone https://github.com/darth-veitcher/comfydv.git
|
||||
```
|
||||
**Manual:**
|
||||
|
||||
2. Restart ComfyUI. The nodes appear under the **dv/** category in the node menu.
|
||||
```bash
|
||||
cd /path/to/ComfyUI/custom_nodes
|
||||
git clone https://github.com/darth-veitcher/comfydv.git
|
||||
```
|
||||
|
||||
> **Dependencies** (`jinja2`, `rich`, `colorama`, `termcolor`) are listed in `pyproject.toml`. ComfyUI's Python environment must have them installed — run `pip install jinja2 rich colorama termcolor` inside that environment if they are missing.
|
||||
|
||||
## Quickstart
|
||||
|
||||
**Format String — simple f-string:**
|
||||
|
||||
1. Add a **Format String** node to your workflow.
|
||||
2. Set `template_type` to `Simple` and enter `Hello {name}` in the template field.
|
||||
3. A `name` input socket appears automatically — wire it up or type a value.
|
||||
4. Output 0 (`formatted_string`) contains `Hello <your value>`.
|
||||
|
||||
**Random Choice:**
|
||||
|
||||
1. Add a **Random Choice** node.
|
||||
2. Connect any number of inputs (strings, images, conditioning — any type).
|
||||
3. Set `seed` for reproducibility; leave at `0` for a different pick each run.
|
||||
4. Output is whichever input was selected.
|
||||
Restart ComfyUI. The nodes appear under the **dv/** category in the node menu. The only runtime dependency is `jinja2`, which is installed automatically via `requirements.txt`.
|
||||
|
||||
## Documentation
|
||||
|
||||
Full documentation can be found: [darth-veitcher.github.io/comfydv](https://darth-veitcher.github.io/comfydv/stable/)
|
||||
Full documentation: [darth-veitcher.github.io/comfydv](https://darth-veitcher.github.io/comfydv/stable/)
|
||||
|
||||
## String Formatting
|
||||
---
|
||||
|
||||
The FormatString node provides flexible string formatting with dynamic input/output configuration.
|
||||
## Format String
|
||||
|
||||
### Python F-String
|
||||
Formats text from a Python f-string or Jinja2 template. As you type the template, input sockets appear and disappear automatically — one per variable detected.
|
||||
|
||||
A simple python f-string dynamically creates the necessary inputs/outputs for the detected keys.
|
||||
### Python f-strings
|
||||
|
||||

|
||||
Type `{variable_name}` and a socket appears. Wire it to any string output in your workflow.
|
||||
|
||||
### Jinja 2
|
||||

|
||||
|
||||
Switching to Jinja2 allows you to use more advanced control blocks and other filters/features of that templating language. See [Jinja documentation](https://jinja.palletsprojects.com/en/latest/) for further details.
|
||||
Outputs are always in a stable order:
|
||||
|
||||

|
||||
| Output | Content |
|
||||
|--------|---------|
|
||||
| `formatted_string` | The rendered result |
|
||||
| `saved_file_path` | Path written to disk (if `save_path` is set) |
|
||||
| `<var>` … | Pass-through of each input value, for easy chaining |
|
||||
|
||||
### Output Structure
|
||||
### Jinja2 templates
|
||||
|
||||
The node's outputs are organized for maximum reliability and flexibility:
|
||||
Switch `template_type` to **Jinja2** to unlock filters (`| upper`, `| int`, …), conditionals (`{% if %}…{% endif %}`), and loops.
|
||||
|
||||
1. **`formatted_string`** (Output 0): The formatted result string - always in position 0
|
||||
2. **`saved_file_path`** (Output 1): Path to saved state file (if save_path provided) - always in position 1
|
||||
3. **Variable outputs** (Output 2+): Pass-through values for any variables detected in the template, enabling easy chaining
|
||||

|
||||
|
||||
For example, with template `"Hello {name}, you are {age}"`:
|
||||
Variables detected in `{{ }}` expressions become input sockets exactly as in Simple mode. See the [Jinja2 documentation](https://jinja.palletsprojects.com/en/latest/) for the full filter/test reference.
|
||||
|
||||
* Output 0: The formatted string (e.g., "Hello Alice, you are 30")
|
||||
* Output 1: The save file path (or empty string)
|
||||
* Output 2: The value of `name` (e.g., "Alice")
|
||||
* Output 3: The value of `age` (e.g., "30")
|
||||
|
||||
This structure ensures the primary outputs (`formatted_string` and `saved_file_path`) are always in predictable, fixed positions for reliable workflow connections.
|
||||
---
|
||||
|
||||
## Random Choice
|
||||
|
||||
Ability to take arbitrary length and type of inputs to then output a **choice** with a controllable seed.
|
||||
Connect any number of inputs of the same type. Each run picks one at random. Set `seed` for reproducibility.
|
||||
|
||||

|
||||

|
||||
|
||||
- Accepts any ComfyUI type (STRING, IMAGE, CONDITIONING, …)
|
||||
- Add as many inputs as you like; unused slots are removed automatically when disconnected
|
||||
- `seed = 0` randomises on every run; any other value locks the selection
|
||||
|
||||
---
|
||||
|
||||
## Circuit Breaker
|
||||
|
||||
Stops the queue gracefully when a condition isn't met — no crash, no error, just a clean halt.
|
||||
|
||||

|
||||
|
||||
Wire an image (or any trigger) into `trigger` and a boolean into `status`. When `status` is **false** the node raises `InterruptProcessingException`, which tells ComfyUI to stop the current run cleanly. When `status` is **true** the image passes through unchanged.
|
||||
|
||||
Typical use: skip an expensive upscale step when a quality-check node says the draft is already good enough.
|
||||
|
||||
|
After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 341 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 452 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 315 KiB After Width: | Height: | Size: 19 KiB |
@@ -24,6 +24,7 @@ build-backend = "hatchling.build"
|
||||
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"playwright>=1.60.0",
|
||||
"pytest>=8.4.2",
|
||||
"pytest-cov>=6.0.0",
|
||||
"pyyaml>=6.0.2",
|
||||
|
||||
@@ -0,0 +1,299 @@
|
||||
"""
|
||||
Generate documentation screenshots for comfydv nodes.
|
||||
|
||||
Usage:
|
||||
uv run scripts/take_screenshots.py
|
||||
|
||||
Requires ComfyUI to be running at http://localhost:8188 (use `just up-d` first).
|
||||
Playwright Chromium must be installed: `uv run playwright install chromium`.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from playwright.async_api import Page, async_playwright
|
||||
|
||||
COMFYUI_URL = "http://localhost:8188"
|
||||
OUTPUT_DIR = Path(__file__).parent.parent / "docs" / "assets"
|
||||
VIEWPORT = {"width": 1400, "height": 860}
|
||||
|
||||
# LiteGraph DragAndScale transform: screen = (virtual + offset) * scale
|
||||
# Place the node at virtual (60, 60) and set offset/scale so it fills the frame nicely.
|
||||
_VIEW_SCALE = 1.6
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
async def _wait_ready(page: Page) -> None:
|
||||
"""Block until the ComfyUI app object and canvas are initialised."""
|
||||
await page.wait_for_function(
|
||||
"typeof window.app !== 'undefined' && window.app.graph !== null",
|
||||
timeout=30_000,
|
||||
)
|
||||
# Give extensions time to finish registering
|
||||
await asyncio.sleep(3.0)
|
||||
|
||||
|
||||
async def _clear(page: Page) -> None:
|
||||
await page.evaluate("window.app.graph.clear(); window.app.canvas.draw(true, true);")
|
||||
await asyncio.sleep(0.2)
|
||||
|
||||
|
||||
async def _redraw(page: Page) -> None:
|
||||
await page.evaluate(
|
||||
"window.app.canvas.setDirty(true, true); window.app.canvas.draw(true, true);"
|
||||
)
|
||||
await asyncio.sleep(0.3)
|
||||
|
||||
|
||||
async def _frame_node(
|
||||
page: Page,
|
||||
node_pos: list[float],
|
||||
node_size: list[float],
|
||||
scale: float = _VIEW_SCALE,
|
||||
h_pad: float = 80,
|
||||
v_pad: float = 60,
|
||||
) -> None:
|
||||
"""Pan/zoom so the node is nicely centred in the viewport."""
|
||||
vw = VIEWPORT["width"]
|
||||
vh = VIEWPORT["height"]
|
||||
node_cx = node_pos[0] + node_size[0] / 2
|
||||
node_cy = node_pos[1] + node_size[1] / 2
|
||||
# LiteGraph: screen = (virtual + offset) * scale => offset = screen/scale - virtual
|
||||
offset_x = vw / 2 / scale - node_cx
|
||||
offset_y = vh / 2 / scale - node_cy
|
||||
await page.evaluate(
|
||||
f"""
|
||||
(() => {{
|
||||
const c = window.app.canvas;
|
||||
c.ds.scale = {scale};
|
||||
c.ds.offset = [{offset_x}, {offset_y}];
|
||||
c.draw(true, true);
|
||||
}})();
|
||||
"""
|
||||
)
|
||||
await asyncio.sleep(0.4)
|
||||
|
||||
|
||||
async def _capture(
|
||||
page: Page,
|
||||
out: Path,
|
||||
node_pos: list[float] | None = None,
|
||||
node_size: list[float] | None = None,
|
||||
scale: float = _VIEW_SCALE,
|
||||
pad: int = 60,
|
||||
) -> None:
|
||||
"""Screenshot the canvas, optionally cropped tightly around the node."""
|
||||
canvas = page.locator("canvas#graph-canvas, canvas").first
|
||||
box = await canvas.bounding_box()
|
||||
if box is None:
|
||||
await page.screenshot(path=str(out))
|
||||
elif node_pos is not None and node_size is not None:
|
||||
# Convert virtual node coords → screen coords, then clip with padding.
|
||||
# LiteGraph: screen = (virtual + offset) * scale
|
||||
offset = await page.evaluate("() => window.app.canvas.ds.offset")
|
||||
sx = (node_pos[0] + offset[0]) * scale + box["x"]
|
||||
sy = (node_pos[1] + offset[1]) * scale + box["y"]
|
||||
sw = node_size[0] * scale
|
||||
sh = node_size[1] * scale
|
||||
await page.screenshot(
|
||||
path=str(out),
|
||||
clip={
|
||||
"x": max(box["x"], sx - pad),
|
||||
"y": max(box["y"], sy - pad),
|
||||
"width": min(sw + pad * 2, box["width"]),
|
||||
"height": min(sh + pad * 2, box["height"]),
|
||||
},
|
||||
)
|
||||
else:
|
||||
await page.screenshot(
|
||||
path=str(out),
|
||||
clip={
|
||||
"x": box["x"],
|
||||
"y": box["y"],
|
||||
"width": box["width"],
|
||||
"height": box["height"],
|
||||
},
|
||||
)
|
||||
print(f" ✓ {out.relative_to(out.parent.parent.parent)}")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Individual node scenes
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
async def scene_format_string_simple(page: Page, out: Path) -> None:
|
||||
"""FormatString — simple Python f-string with two dynamic input sockets."""
|
||||
await _clear(page)
|
||||
|
||||
info = await page.evaluate(
|
||||
"""
|
||||
async () => {
|
||||
const node = LiteGraph.createNode("FormatString");
|
||||
node.pos = [60, 60];
|
||||
window.app.graph.add(node);
|
||||
|
||||
const twType = node.widgets.find(w => w.name === "template_type");
|
||||
const twTpl = node.widgets.find(w => w.name === "template");
|
||||
if (twType) twType.value = "Simple";
|
||||
if (twTpl) twTpl.value = "Hello {name}!\\nYou are {age} years old.";
|
||||
|
||||
// Trigger dynamic-input creation via the server round-trip
|
||||
if (typeof node.updateNodeConfig === "function") {
|
||||
await node.updateNodeConfig();
|
||||
}
|
||||
await new Promise(r => setTimeout(r, 600));
|
||||
|
||||
window.app.canvas.setDirty(true, true);
|
||||
window.app.canvas.draw(true, true);
|
||||
|
||||
return { pos: [node.pos[0], node.pos[1]], size: [node.size[0], node.size[1]] };
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
await _frame_node(page, info["pos"], info["size"])
|
||||
await _capture(page, out, info["pos"], info["size"])
|
||||
|
||||
|
||||
async def scene_format_string_jinja2(page: Page, out: Path) -> None:
|
||||
"""FormatString — Jinja2 template showing filters and multiple variables."""
|
||||
await _clear(page)
|
||||
|
||||
info = await page.evaluate(
|
||||
"""
|
||||
async () => {
|
||||
const node = LiteGraph.createNode("FormatString");
|
||||
node.pos = [60, 60];
|
||||
window.app.graph.add(node);
|
||||
|
||||
const twType = node.widgets.find(w => w.name === "template_type");
|
||||
const twTpl = node.widgets.find(w => w.name === "template");
|
||||
if (twType) twType.value = "Jinja2";
|
||||
if (twTpl) twTpl.value = "{{ greeting | upper }}, {{ name }}!\\n{{ score }}% → {% if score | int >= 90 %}A{% else %}B{% endif %}";
|
||||
|
||||
if (typeof node.updateNodeConfig === "function") {
|
||||
await node.updateNodeConfig();
|
||||
}
|
||||
await new Promise(r => setTimeout(r, 600));
|
||||
|
||||
window.app.canvas.setDirty(true, true);
|
||||
window.app.canvas.draw(true, true);
|
||||
|
||||
return { pos: [node.pos[0], node.pos[1]], size: [node.size[0], node.size[1]] };
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
await _frame_node(page, info["pos"], info["size"])
|
||||
await _capture(page, out, info["pos"], info["size"])
|
||||
|
||||
|
||||
async def scene_random_choice(page: Page, out: Path) -> None:
|
||||
"""RandomChoice — three typed inputs and a seed widget."""
|
||||
await _clear(page)
|
||||
|
||||
info = await page.evaluate(
|
||||
"""
|
||||
() => {
|
||||
const node = LiteGraph.createNode("RandomChoice");
|
||||
node.pos = [60, 60];
|
||||
window.app.graph.add(node);
|
||||
|
||||
// Seed widget
|
||||
const seedWidget = node.widgets.find(w => w.name === "seed");
|
||||
if (seedWidget) seedWidget.value = 42;
|
||||
|
||||
// Add extra input slots to demonstrate multi-input capability.
|
||||
// RandomChoice starts with one wildcard input; we add two more.
|
||||
node.addInput("input2", "*");
|
||||
node.addInput("input3", "*");
|
||||
|
||||
window.app.canvas.setDirty(true, true);
|
||||
window.app.canvas.draw(true, true);
|
||||
|
||||
return { pos: [node.pos[0], node.pos[1]], size: [node.size[0], node.size[1]] };
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
await _frame_node(page, info["pos"], info["size"])
|
||||
await _capture(page, out, info["pos"], info["size"])
|
||||
|
||||
|
||||
async def scene_circuit_breaker(page: Page, out: Path) -> None:
|
||||
"""CircuitBreaker — trigger and status inputs."""
|
||||
await _clear(page)
|
||||
|
||||
info = await page.evaluate(
|
||||
"""
|
||||
() => {
|
||||
const node = LiteGraph.createNode("CircuitBreaker");
|
||||
node.pos = [60, 60];
|
||||
window.app.graph.add(node);
|
||||
|
||||
// Set status to false (the interesting case — will interrupt the queue)
|
||||
const statusWidget = node.widgets && node.widgets.find(w => w.name === "status");
|
||||
if (statusWidget) statusWidget.value = false;
|
||||
|
||||
window.app.canvas.setDirty(true, true);
|
||||
window.app.canvas.draw(true, true);
|
||||
|
||||
return { pos: [node.pos[0], node.pos[1]], size: [node.size[0], node.size[1]] };
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
await _frame_node(page, info["pos"], info["size"])
|
||||
await _capture(page, out, info["pos"], info["size"])
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Main
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
SCENES = [
|
||||
("fstring.png", scene_format_string_simple),
|
||||
("jinja2.png", scene_format_string_jinja2),
|
||||
("random.png", scene_random_choice),
|
||||
("circuit_breaker.png", scene_circuit_breaker),
|
||||
]
|
||||
|
||||
|
||||
async def main() -> int:
|
||||
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
async with async_playwright() as pw:
|
||||
browser = await pw.chromium.launch(headless=True)
|
||||
context = await browser.new_context(viewport=VIEWPORT)
|
||||
page = await context.new_page()
|
||||
|
||||
print(f"Opening {COMFYUI_URL} …")
|
||||
try:
|
||||
await page.goto(COMFYUI_URL, wait_until="networkidle", timeout=20_000)
|
||||
except Exception as exc:
|
||||
print(f"ERROR: Could not reach {COMFYUI_URL}: {exc}")
|
||||
print("Is the ComfyUI harness running? Run: just up-d")
|
||||
return 1
|
||||
|
||||
print("Waiting for ComfyUI to initialise …")
|
||||
await _wait_ready(page)
|
||||
|
||||
print("Taking screenshots:")
|
||||
for filename, scene_fn in SCENES:
|
||||
out_path = OUTPUT_DIR / filename
|
||||
await scene_fn(page, out_path)
|
||||
|
||||
await browser.close()
|
||||
|
||||
print("Done.")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(asyncio.run(main()))
|
||||
@@ -250,6 +250,7 @@ dependencies = [
|
||||
|
||||
[package.dev-dependencies]
|
||||
dev = [
|
||||
{ name = "playwright" },
|
||||
{ name = "pytest" },
|
||||
{ name = "pytest-cov" },
|
||||
{ name = "pyyaml" },
|
||||
@@ -275,6 +276,7 @@ requires-dist = [
|
||||
|
||||
[package.metadata.requires-dev]
|
||||
dev = [
|
||||
{ name = "playwright", specifier = ">=1.60.0" },
|
||||
{ name = "pytest", specifier = ">=8.4.2" },
|
||||
{ name = "pytest-cov", specifier = ">=6.0.0" },
|
||||
{ name = "pyyaml", specifier = ">=6.0.2" },
|
||||
@@ -519,6 +521,83 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619", size = 11034, upload-time = "2022-05-02T15:47:14.552Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "greenlet"
|
||||
version = "3.5.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e2/f1/fbbfef6af0bad0548f09bc28948ea3c275b4edb19e17fc5ca9900a6a634d/greenlet-3.5.3.tar.gz", hash = "sha256:a61efc018fd3eb317eeca31aba90ee9e7f26f22884a79b6c6ec715bf71bb62f1", size = 200270, upload-time = "2026-06-26T19:28:24.832Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/51/58/5404031044f55afad7aad1aff8be3f22b1bed03e237cfeabbc7e5c8cfde0/greenlet-3.5.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:aca9b4ce85b152b5524ef7d88170efdff80dc0032aa8b75f9aaf7f3479ea95b4", size = 287424, upload-time = "2026-06-26T18:20:31.469Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b4/bf/1c65e9b94a54d547068fa5b5a8a06f221f3316b48908e08668d29c77cb50/greenlet-3.5.3-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f71be4920368fe1fabeeaa53d1e3548337e2b223d9565f8ad5e392a75ba23fc", size = 606523, upload-time = "2026-06-26T19:07:08.859Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b8/c7/b66baacc95775ad511287acb0137b95574a9ce5491902372b7564799d790/greenlet-3.5.3-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4d77e67f65f98449e3fb83f795b5d0a8437aead2f874ca89c96576caf4be3af6", size = 618315, upload-time = "2026-06-26T19:10:06.055Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b0/a0/68afd1ebad40db87dac0a28ffa120726b98bf9c7c40c481b0f63c105d298/greenlet-3.5.3-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e18619ba655ac05d78d80fc83cac4ba892bd6927b99e3b8237aee861aaacc8bb", size = 626155, upload-time = "2026-06-26T19:24:14.44Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/78/2b/28ed29463522fdbe4c15b1f63922041626a7478316b34ab4adda3f0a4aba/greenlet-3.5.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8540f1e6205bd13ca0ce685581037219ca54a1b41a0a15d228c6c9b8ad5903d7", size = 617381, upload-time = "2026-06-26T18:32:16.077Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/07/7f/e327d912239ec4b3b49999e3967389bcf1ee8722b9ee9194d2752ecd558a/greenlet-3.5.3-cp311-cp311-manylinux_2_39_riscv64.whl", hash = "sha256:d27c0c653a60d9535f690226474a5cc1036a8b0d7b57504d1c4f89c44a07a80c", size = 421083, upload-time = "2026-06-26T19:25:35.804Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2a/7b/ad04e9d1337fc04965dc9fc616b6a72cb65a24b800a014c011ec812f5489/greenlet-3.5.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7ef56fe650f50575bf843acde967b9c567687f3c22340941a899b7bc56e956a8", size = 1577771, upload-time = "2026-06-26T19:09:01.537Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d8/33/6c87ab7ba663f70ca21f3022aad1ffe56d3f3e0521e836c2415e13abcc3c/greenlet-3.5.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5121af01cf911e70056c00d4b46d5e9b5d1415550038573d744138bacb59e6b8", size = 1644048, upload-time = "2026-06-26T18:31:42.996Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1c/35/f0d8ee998b422cf8693b270f098e55d8d4ec8006b061b333f54f177d28d9/greenlet-3.5.3-cp311-cp311-win_amd64.whl", hash = "sha256:0f41e4a05a3c0cb31b17023eff28dd111e1d16bf7d7d00406cd7df23f31398a7", size = 239137, upload-time = "2026-06-26T18:23:21.664Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fb/96/b9820295576ef18c9edc404f10e260ae7215ceaf3781a54b720ed2627862/greenlet-3.5.3-cp311-cp311-win_arm64.whl", hash = "sha256:ec6f1af59f6b5f3fc9678e2ea062d8377d22ac644f7844cb7a292910cf12ff44", size = 237630, upload-time = "2026-06-26T18:24:00.281Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5d/6e/4c37d51a2b7f82d2ff11bb6b5f7d766d9a011726624af255e843727627a3/greenlet-3.5.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:719757059f5a53fd0dde23f78cffeafcdd97b21c850ddb7ca684a3c1a1f122e2", size = 288685, upload-time = "2026-06-26T18:22:08.977Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7a/73/815dd90131c1b71ebdf53dbc7c276cafec2a1173b97559f97aba72724a87/greenlet-3.5.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:efa9f765dd09f9d0cdac651ffdf631ee59ec5dc6ee7a73e0c012ba9c52fbdf5b", size = 604761, upload-time = "2026-06-26T19:07:10.114Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9f/57/079cfe76bcef36b153b25607ee91c6fcb58f17f8b23c86bbbeabe0c88d72/greenlet-3.5.3-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7faba15ac005376e02a0384504e0243be3370ce010296a44a820feb342b505ab", size = 617044, upload-time = "2026-06-26T19:10:07.25Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fb/fb/d97dc261209c80744b7c8132693a30d70ec6e7315e632cb0a10b3fec94dd/greenlet-3.5.3-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5795cd1101371140551c645f2d408b8d3c01a5a29cf8a9bce6e759c983682d23", size = 622351, upload-time = "2026-06-26T19:24:16.32Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/37/87/b4d095775a3fb1bcafbb483fc206b27ebb785724c83051447737085dc54e/greenlet-3.5.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:87142215824be6ac05e2e8e2786eec307ccbc27c36723c3881959df654af6861", size = 614244, upload-time = "2026-06-26T18:32:17.594Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8e/ac/e5fee13cbbd0e8de312d9a146584b8a51891c68847330ef9dc8b5109d23f/greenlet-3.5.3-cp312-cp312-manylinux_2_39_riscv64.whl", hash = "sha256:af4923b3096e26a36d7e9cf24ab88083a20f97d191e3b97f253731ce9b41b28c", size = 425395, upload-time = "2026-06-26T19:25:37.144Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8a/70/7559b609683650fa2b95b8ab84b4ab0b26556a635d19675e12aa832d826d/greenlet-3.5.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:215275b1b49320987352e6c1b054acca0064f965a2c66992bed9a6f7d913f149", size = 1574210, upload-time = "2026-06-26T19:09:03.077Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ae/73/be55392074c60fc37655ca40fa6022457bfbf6718e9e342a7b0b41f96dd2/greenlet-3.5.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6b1b0eed82364b0e32c4ea0f221452d33e6bb17ae094d9f72aed9851812747ea", size = 1638627, upload-time = "2026-06-26T18:31:44.748Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/14/40/c57489acf8e37d74e2913d4eff63aa0dba17acccc4bdeef874dde2dbbec9/greenlet-3.5.3-cp312-cp312-win_amd64.whl", hash = "sha256:cde8adafa2365676f74a979744629589999093bc86e2484214f58e61df08902c", size = 239882, upload-time = "2026-06-26T18:23:27.518Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/71/fd/6fea0e3d6600f785069481ee637e09378dd4118acdfd38ad88ae2db31c98/greenlet-3.5.3-cp312-cp312-win_arm64.whl", hash = "sha256:c4e7b79d83805475f0102008843f6eb45fd3bb0b2e88c774adab5fbaab27117d", size = 238211, upload-time = "2026-06-26T18:22:37.671Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9b/ff/a620267401db30a50cc8450ee90730e2d4a85658c055c0e760d4ed47fb13/greenlet-3.5.3-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:c8d87c2134d871df96ecdea9cec7cbaab286dadab0f56476e57aaf9e8ac11550", size = 287609, upload-time = "2026-06-26T18:21:14.724Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d6/fa/5401ac78021c826a25b6dde0c705e0a8f29b617509f9185a31dac15fbe1b/greenlet-3.5.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2d185dd1621757e70c3861cceffd5317ab4e7ed7eb09c82994828468527ade5", size = 607435, upload-time = "2026-06-26T19:07:11.412Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e9/76/1dc144a2e56e65d36405078ed774224375ea520a1870a6e46e08bb4ac7bf/greenlet-3.5.3-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1c514a468149bf8fbbab874188a3535cd8a48a3e353eb53a3d424296f8dbacd3", size = 619787, upload-time = "2026-06-26T19:10:08.396Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/57/61/2f5b1adf256d039f5dab8005de8d3d7ad2b0070a3219c0e036b3fbfeb440/greenlet-3.5.3-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9ad04dd75458c6300b047c61b8639092433d205a25a14e310d6582a480efcca1", size = 625580, upload-time = "2026-06-26T19:24:18.344Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bf/87/c298cee62df1de4ad7fec32abda73526cff347fd143a6ed4ac369246668a/greenlet-3.5.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:915f887cf2682b66419b879423a2e072634aa7b7dce6f3ada4957cfced3f1e9a", size = 616786, upload-time = "2026-06-26T18:32:19.128Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3e/d9/ab7fc9e543e44d6879b0a6ef9a4b2188940fd180cc65d6f646883ddf7201/greenlet-3.5.3-cp313-cp313-manylinux_2_39_riscv64.whl", hash = "sha256:afaabdd554cd7ae9bbb3ca070b0d7fdfd207dbf1d16865f7233837709d354bda", size = 427933, upload-time = "2026-06-26T19:25:38.219Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9e/2e/e6f009885ed0705ccf33fe0583c117cfd03cde77e31a596dd5785a30762b/greenlet-3.5.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:766cfd421c13e450feb340cd472a3ed9957d438727b7b4593ad7c76c5d2b0deb", size = 1574316, upload-time = "2026-06-26T19:09:04.273Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ef/fe/43fd110b01e40da0adb7c90ac7ea744bef2d43dca00de5095fd2351c2a68/greenlet-3.5.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2ecda9ec22edf38fa389369eaed8c3d37c05f3c54e69f69438dbb2cc1de1458b", size = 1638614, upload-time = "2026-06-26T18:31:46.297Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0f/7c/062447147a61f8b4337b156fe70d32a165fcf2f89d7ca6255e572806705c/greenlet-3.5.3-cp313-cp313-win_amd64.whl", hash = "sha256:c82304750f057167ff60d188df1d0cc1764ce9567eadf03e6a7443bcedd0b30b", size = 239850, upload-time = "2026-06-26T18:21:54.613Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c7/7e/220a7f5824a64a60443fc03b39dfac4ea63a7fb6d481efa27eafa928e7f4/greenlet-3.5.3-cp313-cp313-win_arm64.whl", hash = "sha256:dc133a1569ee667b2a6ef56ce551084aeefd87a5acbc4736d336d1e2edc6cfc4", size = 238141, upload-time = "2026-06-26T18:22:48.507Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c3/93/43e116ee114b28737ba7e12952a0d4e2f55944d0f84e42bc91ba7192a3c9/greenlet-3.5.3-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:fd2e02fa07485778536a036222d616ab957b1d533f36b3ed98ce725d9c9d3117", size = 288202, upload-time = "2026-06-26T18:23:49.604Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/82/2f/146d218299046a43d1f029fd544b3d110d0f175a09c715c7e8da4a4a345d/greenlet-3.5.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df0a0628d1597eb0897b62f55d1343f772405fd25f3b2a796c76874b0c2e22e8", size = 654096, upload-time = "2026-06-26T19:07:12.71Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/cc/04738cafb3f45fa991ea44f9de94c47dcec964f5a972300988a6751f49d9/greenlet-3.5.3-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ebd933a6adabc298bab47731a130fe6bfb888bd934eee37810f151159544540d", size = 666304, upload-time = "2026-06-26T19:10:09.503Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/86/a9/73fa62893d5b84b4205544e6b673c654cc43aa5b9899bac00f04d64af73d/greenlet-3.5.3-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8d19fe6c39ebff9259f07bcc685d3290f8fa4ea2278e51dd0008e4d6b0f2d814", size = 670657, upload-time = "2026-06-26T19:24:19.967Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ce/aa/4e0dad5e605c270c784ab911c43da6adb136ccd4d81180f763ca429a723d/greenlet-3.5.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4b9d501b40e80b70e32323c799dd9b420a5577a9601469d362ae1ffb690f3a7c", size = 663635, upload-time = "2026-06-26T18:32:20.802Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/29/7e/2ffce64929fb3cab7b65d5a0b20aaf9764e227681d731b041077fc9a525a/greenlet-3.5.3-cp314-cp314-manylinux_2_39_riscv64.whl", hash = "sha256:962c5df2db8cb446da51edf1ca5296c389d93b99c9d8aa2ee4c7d0d8f1218260", size = 473497, upload-time = "2026-06-26T19:25:39.421Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/50/13efdbea246fe3d3b735e191fec08fb50809f53cd2383ebe123d0809e44b/greenlet-3.5.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a1fad1d11e7d6aab184107baa8e4ece11ccba3ec9599cd7efa5ff4d70d43256a", size = 1621252, upload-time = "2026-06-26T19:09:05.647Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f7/22/c0a336ae4a1410fd5f5121098e5bfbf1865f64c5ef80b4b5412886c4a332/greenlet-3.5.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:fad5aec764399f1b5cc347ad250a59660f20c8f8888ea6bae1f93b769cce1154", size = 1684824, upload-time = "2026-06-26T18:31:47.738Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7a/94/91aec0030bea75c4b3244251d0de60a1f3432d1ecb53ab6c437fb5c3ba61/greenlet-3.5.3-cp314-cp314-win_amd64.whl", hash = "sha256:7669aa24cf2a1041d6f7899575b494a3ab4cf68bfcc8609b1dc0be7272db835e", size = 240754, upload-time = "2026-06-26T18:22:15.669Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e5/06/68d0983e79e02138f64b4d303c500c27ddb48e5e77f3debb80888a921eae/greenlet-3.5.3-cp314-cp314-win_arm64.whl", hash = "sha256:5b4807c4082c9d1b6d9eed56fcd041863e37f2228106eef24c30ca096e238605", size = 239549, upload-time = "2026-06-26T18:22:42.996Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/91/95/3e161213d7f1d378d15aa9e792093e9bfe01844680d04b7fd6e0107c9098/greenlet-3.5.3-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:271a8ea7c1024e8a0d7dd2be66dd66dda8a07193f41a17b9e924f7600f5b62be", size = 296389, upload-time = "2026-06-26T18:22:20.657Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/00/92/715c44721abe2b4d1ae9abde4179411868a5bff312479f54e105d372f131/greenlet-3.5.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:19131729ae0ddc3c2e1ef85e650169b5e37ee32e400f215f78b94d7b0d567310", size = 653382, upload-time = "2026-06-26T19:07:14.209Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/83/37a10372a1090a6624cca8e74c12df1a36c2dc36429ed0255b7fb1aeee23/greenlet-3.5.3-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1540dd8e5fc2a5aec40fbb98ef8e149fa47c89a4b4a1cf2575a14d3d1869d7a8", size = 659401, upload-time = "2026-06-26T19:10:10.876Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cb/73/8faec206b851c22b1733545fda900829a1f3f5b1c78ae7e0fb3dba57d9f4/greenlet-3.5.3-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b897d97759425953f69a9c0fac67f8fe333ec0ce7377ef186fb2b0c3ad5e354d", size = 659582, upload-time = "2026-06-26T19:24:21.357Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/db/e2/d1509cad4207da559cc42986ecdd8fc67ad0d1bba2bf03023c467fd5e0f3/greenlet-3.5.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e81fa194a1d20967877bdf9c7794db2bc99063e5be36aee710c08f04c5bb087f", size = 656969, upload-time = "2026-06-26T18:32:22.272Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b4/55/50c19e49f8045834ada71ef12f8ad048eba8517c6aa41161bed676328fae/greenlet-3.5.3-cp314-cp314t-manylinux_2_39_riscv64.whl", hash = "sha256:3236754d423955ea08e9bb5f6c04a7895f9e22c290b66aa7653fcb922d839eb0", size = 491037, upload-time = "2026-06-26T19:25:40.672Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/86/7d/eaf70de20aadca3a5884aec58362861c64ce45e7b277f47ed026926a3b89/greenlet-3.5.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:55cf4d777485d43110e47133cbba6d74a8885a87ec1227ef0267f9ee80c5aa21", size = 1617822, upload-time = "2026-06-26T19:09:06.893Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8a/f9/414d38fc400ae4350d4185eaad1827676f7cf5287b9136e0ed1cbbe20a7f/greenlet-3.5.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:12a248ba75f6a9a236375f52296c498c89ff1d8badf32deb9eca7abd5853f7da", size = 1677983, upload-time = "2026-06-26T18:31:49.396Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e4/15/7edb977e08f9bff702fe42d6c902702786ff6b9694058b4e6a2a6ac90e57/greenlet-3.5.3-cp314-cp314t-win_amd64.whl", hash = "sha256:efc6bd60ea02e085862c74a3ef64b147ffc6f1a5ea7d9f26e7a939943f68c1e3", size = 243626, upload-time = "2026-06-26T18:24:41.485Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/8a/93928dce91e6b3598b5e779e8d1fd6576a504640c58e78627077f6a7a91a/greenlet-3.5.3-cp315-cp315-macosx_11_0_universal2.whl", hash = "sha256:ea03f2f04367845d6b58eeed276e1e56e51f0b97d8ad5a88a7d20a91dc9056cc", size = 288860, upload-time = "2026-06-26T18:22:48.07Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4f/ca/69db42d447a1378043e2c8f19c09cbbd1263371505053c496b49066d3d16/greenlet-3.5.3-cp315-cp315-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:78dbef602fda6d97d957eb7937f70c9ce9e9527330347f8f6b6f9e554a9e7a47", size = 659747, upload-time = "2026-06-26T19:07:15.565Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a8/0b/af7ac2ef8dd41e3da1a40dda6305c23b9a03e13ba975ec916357b50f8575/greenlet-3.5.3-cp315-cp315-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6f73857adb8fee13fa56c172bd11262f888c0c648f9fea113e777bb2c7904a81", size = 670419, upload-time = "2026-06-26T19:10:12.293Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/25/aa/952cf28c2ff949a8c971134fb43854dd7eaa737218723aaef758f8c9aead/greenlet-3.5.3-cp315-cp315-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:cefa9cef4b371f9844c6053db71f1138bc6807bab1578b0dae5149c1f1141357", size = 674261, upload-time = "2026-06-26T19:24:22.79Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/51/1e/1d51640cacbfc455dbe9f9a9f594c49e4e244f63b9971a2f4764e46cc53d/greenlet-3.5.3-cp315-cp315-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:232fec92e823addaf02d9472cf7381e24a1d046a6ced1103c5caa4c21b9dfc1d", size = 668787, upload-time = "2026-06-26T18:32:24.298Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/dc/f2/b00d6f5e63e531a93562b2ec1a4c320fbee91f580fc42e6417af69d706e5/greenlet-3.5.3-cp315-cp315-manylinux_2_39_riscv64.whl", hash = "sha256:6219b6d04dbf6ba6084d77dc609e8473060dc55f759cbf626d512122781fa128", size = 480322, upload-time = "2026-06-26T19:25:41.852Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/21/66/4030d5b0b5894500023f003bb054d9bb354dfbd1e186c3a296759172f5f5/greenlet-3.5.3-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:2421c3564da9429d5586d46ca31ebb26516b5498a802cf65c041a8e8a8980d34", size = 1626305, upload-time = "2026-06-26T19:09:08.281Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0e/50/5221371c7550108dfa3c378debc41d032aa9c78e89abb01d8011cfc93289/greenlet-3.5.3-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:e0f0d160f0b2e558e6c75f7930967183255dc9735e5f5b8cae58ee09c9576d8b", size = 1688631, upload-time = "2026-06-26T18:31:51.278Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/68/5d/00d469daae3c65d2bf620b10eee82eb022127d483c6bc8c69fae6f3fbf17/greenlet-3.5.3-cp315-cp315-win_amd64.whl", hash = "sha256:dd99329bbc15ca78dcc583dba05d0b1b0bae01ab6c2174989f5aaee3e41ac930", size = 241027, upload-time = "2026-06-26T18:22:38.203Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e7/e8/883785b44c5780ed71e83d3e4437e710470be17a2e181e8b601e2da0dc4a/greenlet-3.5.3-cp315-cp315-win_arm64.whl", hash = "sha256:499fef2acede88c1864a57bb586b4bf533c81e1b82df7ab93451cdb47dfec227", size = 240085, upload-time = "2026-06-26T18:23:54.217Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1c/da/4f4a8450962fad137c1c8981a3f1b8919d06c829993d4d476f9c525d5173/greenlet-3.5.3-cp315-cp315t-macosx_11_0_universal2.whl", hash = "sha256:176bc16a721fa5fc294d70b87b4dfa5fbdd251b3da5d5372735ecef9bd7d6d0c", size = 297221, upload-time = "2026-06-26T18:23:27.176Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/57/66/b3bfae3e220a9b63ea539a0eea681800c69ab1aada757eae8789f183e7ce/greenlet-3.5.3-cp315-cp315t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:629b614d2b786e89c50440e246f33eea78f58a962d0bdbbcc809e6d13605903f", size = 657221, upload-time = "2026-06-26T19:07:16.973Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7b/81/b6d4d73a709684fc77e7fa034d7c2fe82cffa9fc920fadcaa659c2626213/greenlet-3.5.3-cp315-cp315t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2b2e857ae16f5f72142edf75f9f176fe7526ba19a2841df1420516f83831c9f2", size = 663226, upload-time = "2026-06-26T19:10:13.723Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e9/39/0e0938a75115b939d42733a2a12e1d349653c9531fe6fe563e8a681f04e6/greenlet-3.5.3-cp315-cp315t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:16d192579ed281051396dddd7f7754dac6259e6b1fb26378c87b66622f8e3f91", size = 663706, upload-time = "2026-06-26T19:24:24.312Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f5/07/e210b02b589f16e74ff48b730690e4a34ffe984219fce4f3c1a0e7ec8545/greenlet-3.5.3-cp315-cp315t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e515757e2e36bcbf1fad09a46e1557e8b1ae1797d4b44d09da7deed88ad28608", size = 660802, upload-time = "2026-06-26T18:32:26.081Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5b/41/35d1c678cdb3c3b9e6bee691728e563cfb294202b23c7a4c3c2ccc343589/greenlet-3.5.3-cp315-cp315t-manylinux_2_39_riscv64.whl", hash = "sha256:4399eb8d041f20b68d943918bc55502a93d6fdc0a37c14da7881c04139acee9d", size = 498803, upload-time = "2026-06-26T19:25:43.063Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/eb/2e/5303eb3fa06bca089060f479707182a93e360683bc252acf846c3090d34e/greenlet-3.5.3-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:b363d46ed1ea431825fdb01471bb024fc08399bad1572a616e853c7684415adb", size = 1622157, upload-time = "2026-06-26T19:09:09.527Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/54/70/50de47a488f14df260b50ae34fb5d56016e308b098eab02c878b5223c26a/greenlet-3.5.3-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:e44da2f5bbdaabaf7d80b73dbb430c7035771e9f244e3c8b769715c9d8fa0a16", size = 1681159, upload-time = "2026-06-26T18:31:52.986Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a7/13/1055e1dda7882073eda533e2b96c62e55bbd2db7fda6d5ece992febc7071/greenlet-3.5.3-cp315-cp315t-win_amd64.whl", hash = "sha256:8ff8bed3e3baa20a3ea261ce00526f1898ad4801d4886fd2220580ee0ad8fadf", size = 244007, upload-time = "2026-06-26T18:22:04.353Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b4/0d/ca7d15afbdc397e3401134c9e1800d51d12b829661786187a4ad08fe484f/greenlet-3.5.3-cp315-cp315t-win_arm64.whl", hash = "sha256:b7068bd09f761f3f5b4d214c2bed063186b2a86148c740b3873e3f56d79bac31", size = 242586, upload-time = "2026-06-26T18:23:37.93Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "griffe"
|
||||
version = "1.6.3"
|
||||
@@ -1295,6 +1374,25 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/6d/45/59578566b3275b8fd9157885918fcd0c4d74162928a5310926887b856a51/platformdirs-4.3.7-py3-none-any.whl", hash = "sha256:a03875334331946f13c549dbd8f4bac7a13a50a895a0eb1e8c6a8ace80d40a94", size = 18499, upload-time = "2025-03-19T20:36:09.038Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "playwright"
|
||||
version = "1.60.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "greenlet" },
|
||||
{ name = "pyee" },
|
||||
]
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/21/f0/832bd9677194908da118064eef20082f2791e3d18215cc6d9391ee2c5a67/playwright-1.60.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:6a8cd0fec171fb3089e95e898c8bc8a6f35dea0b78b399e12fcc19427e91b1d7", size = 43474635, upload-time = "2026-05-18T12:00:31.969Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/59/7b/e1d32ae8a3ed937ec2be3721c5f728b13d731a0b7c6442e0b3bec5094ac0/playwright-1.60.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:39b5420ba6145045b69ced4c5c47d4d9fe5bddfc8ff816c518913afcb25ec7a5", size = 42261327, upload-time = "2026-05-18T12:00:35.638Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/bc/23de499ded6411c188a20c5a0dea6f0cd4ed5d2b3cc6042a5dbd3ed609aa/playwright-1.60.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:2581d0e6a3392c71f91b27460c7fd093356818dc430f48153896c8aeeaef7705", size = 43474636, upload-time = "2026-05-18T12:00:39.294Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/22/7b/1d679f4fced4ea94efadd17103856d8c565384f68382a1681264e46f5925/playwright-1.60.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:1c2bfae7884fb3fb05b853290eab8f343d524e5016f2f1def702acbbdf14c93e", size = 47467220, upload-time = "2026-05-18T12:00:43.179Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/84/c2/1528d267d4442bd2c6b8eaeab819dd52c2030bf80e89293f0ba1f687473b/playwright-1.60.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:43e66564125ee31b07a58cefb21e256d62d67d8d1713e6858df7a3019d8ed353", size = 47154856, upload-time = "2026-05-18T12:00:46.715Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bb/4e/b008b6440a7a1624378041da94829956d4b8f7ab9ef5aad22d0dc3f2e26d/playwright-1.60.0-py3-none-win32.whl", hash = "sha256:ec94e416ea320711e0ad4bf185dcbf41833672961e90773e1885255d7db7b7e7", size = 37902157, upload-time = "2026-05-18T12:00:50.374Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/55/f0/0541524133104f9cc20bf900870ff4a736b76a23483f3a55295ddfa58409/playwright-1.60.0-py3-none-win_amd64.whl", hash = "sha256:9566821ce6030a1f9e7146a24e19355ab0d98805fd0f9be50bb3d8fef1750c02", size = 37902159, upload-time = "2026-05-18T12:00:53.728Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/80/c8/210f282d278e4709cdd71b12a31af45a30a22ab3207b387e29b37e478713/playwright-1.60.0-py3-none-win_arm64.whl", hash = "sha256:6e4f6700a4c2250efff8e690a81d66e3855754fb587b6b87cf5c784014f91537", size = 34037981, upload-time = "2026-05-18T12:00:57.584Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pluggy"
|
||||
version = "1.6.0"
|
||||
@@ -1403,6 +1501,18 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305, upload-time = "2025-10-08T19:49:00.792Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyee"
|
||||
version = "13.0.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/8b/04/e7c1fe4dc78a6fdbfd6c337b1c3732ff543b8a397683ab38378447baa331/pyee-13.0.1.tar.gz", hash = "sha256:0b931f7c14535667ed4c7e0d531716368715e860b988770fc7eb8578d1f67fc8", size = 31655, upload-time = "2026-02-14T21:12:28.044Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/c4/b4d4827c93ef43c01f599ef31453ccc1c132b353284fc6c87d535c233129/pyee-13.0.1-py3-none-any.whl", hash = "sha256:af2f8fede4171ef667dfded53f96e2ed0d6e6bd7ee3bb46437f77e3b57689228", size = 15659, upload-time = "2026-02-14T21:12:26.263Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pygments"
|
||||
version = "2.19.1"
|
||||
|
||||