chore: import upstream snapshot with attribution
CI - Python Bindings / sdist (push) Failing after 1s
CI - Python Bindings / Build x86_64-unknown-linux-musl (push) Failing after 1s
CI / fmt (push) Failing after 1s
E2E Output Validation / compare-outputs (push) Failing after 1s
Sync Docs to Developer Hub / sync-docs (push) Failing after 1s
CI - Python Bindings / Build x86_64-unknown-linux-gnu (push) Failing after 1s
CI - WASM Bindings / Build WASM (push) Failing after 0s
CI / clippy (push) Failing after 1s
CI / build-and-test (ubuntu-latest) (push) Failing after 0s
CI - WASM Bindings / Edge runtime PDF parse test (push) Has been skipped
CI - WASM Bindings / Browser PDF parse test (push) Has been skipped
Deploy Demo to GitHub Pages / deploy (push) Failing after 1s
CI / build-docker-image (push) Failing after 3s
CI - Node Bindings / Build darwin-x64 (push) Has been cancelled
CI - Node Bindings / Test win32-x64-msvc (push) Has been cancelled
CI - Node Bindings / Build linux-arm64-gnu (push) Has been cancelled
CI - Node Bindings / Build linux-x64-gnu (push) Has been cancelled
CI - Node Bindings / Build linux-x64-musl (push) Has been cancelled
CI - Node Bindings / Build win32-arm64-msvc (push) Has been cancelled
CI - Node Bindings / Build win32-x64-msvc (push) Has been cancelled
CI - Node Bindings / Test darwin-arm64 (push) Has been cancelled
CI - Node Bindings / Test darwin-x64 (push) Has been cancelled
CI - Node Bindings / Test linux-x64-gnu (push) Has been cancelled
CI - Node Bindings / Test linux-x64-musl (push) Has been cancelled
CI - Node Bindings / Test win32-arm64-msvc (push) Has been cancelled
CI - Python Bindings / Build aarch64-pc-windows-msvc (push) Has been cancelled
CI - Python Bindings / Build x86_64-pc-windows-msvc (push) Has been cancelled
CI - Python Bindings / Build x86_64-apple-darwin (push) Has been cancelled
CI - Python Bindings / Build aarch64-apple-darwin (push) Has been cancelled
CI - Python Bindings / Build aarch64-unknown-linux-gnu (push) Has been cancelled
CI - Node Bindings / Build darwin-arm64 (push) Has been cancelled
CI - Python Bindings / Test x86_64-apple-darwin (push) Has been cancelled
CI - Python Bindings / Test aarch64-apple-darwin (push) Has been cancelled
CI - Python Bindings / Test x86_64-unknown-linux-gnu (push) Has been cancelled
CI - Python Bindings / Test x86_64-unknown-linux-musl (push) Has been cancelled
CI - Python Bindings / Test aarch64-pc-windows-msvc (push) Has been cancelled
CI - Python Bindings / Test x86_64-pc-windows-msvc (push) Has been cancelled
CI / build-and-test (macos-26-intel) (push) Has been cancelled
CI / build-and-test (macos-latest) (push) Has been cancelled
CI / build-and-test (windows-11-arm) (push) Has been cancelled
CI / build-and-test (windows-latest) (push) Has been cancelled
E2E Output Validation / upload-dataset (push) Has been cancelled
CI - Python Bindings / sdist (push) Failing after 1s
CI - Python Bindings / Build x86_64-unknown-linux-musl (push) Failing after 1s
CI / fmt (push) Failing after 1s
E2E Output Validation / compare-outputs (push) Failing after 1s
Sync Docs to Developer Hub / sync-docs (push) Failing after 1s
CI - Python Bindings / Build x86_64-unknown-linux-gnu (push) Failing after 1s
CI - WASM Bindings / Build WASM (push) Failing after 0s
CI / clippy (push) Failing after 1s
CI / build-and-test (ubuntu-latest) (push) Failing after 0s
CI - WASM Bindings / Edge runtime PDF parse test (push) Has been skipped
CI - WASM Bindings / Browser PDF parse test (push) Has been skipped
Deploy Demo to GitHub Pages / deploy (push) Failing after 1s
CI / build-docker-image (push) Failing after 3s
CI - Node Bindings / Build darwin-x64 (push) Has been cancelled
CI - Node Bindings / Test win32-x64-msvc (push) Has been cancelled
CI - Node Bindings / Build linux-arm64-gnu (push) Has been cancelled
CI - Node Bindings / Build linux-x64-gnu (push) Has been cancelled
CI - Node Bindings / Build linux-x64-musl (push) Has been cancelled
CI - Node Bindings / Build win32-arm64-msvc (push) Has been cancelled
CI - Node Bindings / Build win32-x64-msvc (push) Has been cancelled
CI - Node Bindings / Test darwin-arm64 (push) Has been cancelled
CI - Node Bindings / Test darwin-x64 (push) Has been cancelled
CI - Node Bindings / Test linux-x64-gnu (push) Has been cancelled
CI - Node Bindings / Test linux-x64-musl (push) Has been cancelled
CI - Node Bindings / Test win32-arm64-msvc (push) Has been cancelled
CI - Python Bindings / Build aarch64-pc-windows-msvc (push) Has been cancelled
CI - Python Bindings / Build x86_64-pc-windows-msvc (push) Has been cancelled
CI - Python Bindings / Build x86_64-apple-darwin (push) Has been cancelled
CI - Python Bindings / Build aarch64-apple-darwin (push) Has been cancelled
CI - Python Bindings / Build aarch64-unknown-linux-gnu (push) Has been cancelled
CI - Node Bindings / Build darwin-arm64 (push) Has been cancelled
CI - Python Bindings / Test x86_64-apple-darwin (push) Has been cancelled
CI - Python Bindings / Test aarch64-apple-darwin (push) Has been cancelled
CI - Python Bindings / Test x86_64-unknown-linux-gnu (push) Has been cancelled
CI - Python Bindings / Test x86_64-unknown-linux-musl (push) Has been cancelled
CI - Python Bindings / Test aarch64-pc-windows-msvc (push) Has been cancelled
CI - Python Bindings / Test x86_64-pc-windows-msvc (push) Has been cancelled
CI / build-and-test (macos-26-intel) (push) Has been cancelled
CI / build-and-test (macos-latest) (push) Has been cancelled
CI / build-and-test (windows-11-arm) (push) Has been cancelled
CI / build-and-test (windows-latest) (push) Has been cancelled
E2E Output Validation / upload-dataset (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
# Native extension and bundled dylib (built by maturin)
|
||||
*.so
|
||||
*.pyd
|
||||
*.dylib
|
||||
*.dll
|
||||
@@ -0,0 +1,135 @@
|
||||
# LiteParse Python
|
||||
|
||||
Python bindings for [LiteParse](https://github.com/run-llama/liteparse) — fast, lightweight PDF and document parsing with spatial text extraction.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pip install liteparse
|
||||
```
|
||||
|
||||
This also installs the `lit` CLI command.
|
||||
|
||||
## Quick Start
|
||||
|
||||
```python
|
||||
from liteparse import LiteParse
|
||||
|
||||
parser = LiteParse()
|
||||
result = parser.parse("document.pdf")
|
||||
print(result.text)
|
||||
|
||||
# Access structured data
|
||||
for page in result.pages:
|
||||
print(f"Page {page.page_num}: {len(page.text_items)} text items")
|
||||
```
|
||||
|
||||
## Markdown Output
|
||||
|
||||
LiteParse can render documents directly to Markdown including headings, tables, lists,
|
||||
images, and links reconstructed from the spatial layout. Great for feeding LLMs
|
||||
and RAG pipelines. The rendered Markdown is returned on `result.text`:
|
||||
|
||||
```python
|
||||
parser = LiteParse(
|
||||
output_format="markdown", # "json" | "text" | "markdown"
|
||||
image_mode="placeholder", # "placeholder" | "off" | "embed"
|
||||
extract_links=True, # render [text](url) link syntax (default: True)
|
||||
)
|
||||
result = parser.parse("document.pdf")
|
||||
print(result.text) # rendered Markdown
|
||||
```
|
||||
|
||||
> Reconstruction quality varies with document complexity.
|
||||
|
||||
## Configuration
|
||||
|
||||
All options are passed to the constructor:
|
||||
|
||||
```python
|
||||
parser = LiteParse(
|
||||
ocr_enabled=True, # Enable OCR (default: True)
|
||||
ocr_language="eng", # Tesseract language code
|
||||
ocr_server_url=None, # HTTP OCR server URL (optional)
|
||||
tessdata_path=None, # Path to tessdata directory (optional)
|
||||
max_pages=1000, # Max pages to parse
|
||||
target_pages="1-5,10", # Specific pages (optional)
|
||||
dpi=150, # Rendering DPI
|
||||
output_format="json", # "json" | "text" | "markdown"
|
||||
image_mode="placeholder", # Markdown image handling: "placeholder" | "off" | "embed"
|
||||
extract_links=True, # Render [text](url) links in markdown output
|
||||
preserve_very_small_text=False, # Keep tiny text
|
||||
password=None, # Password for protected documents
|
||||
quiet=False, # Suppress progress output
|
||||
num_workers=4, # Concurrent OCR workers
|
||||
)
|
||||
```
|
||||
|
||||
## Parsing from Bytes
|
||||
|
||||
Pass raw PDF bytes directly — useful for web uploads or downloaded files:
|
||||
|
||||
```python
|
||||
with open("document.pdf", "rb") as f:
|
||||
result = parser.parse(f.read())
|
||||
print(result.text)
|
||||
```
|
||||
|
||||
## Screenshots
|
||||
|
||||
Generate PNG screenshots of document pages:
|
||||
|
||||
```python
|
||||
screenshots = parser.screenshot("document.pdf", page_numbers=[1, 2, 3])
|
||||
for s in screenshots:
|
||||
print(f"Page {s.page_num}: {s.width}x{s.height}")
|
||||
with open(f"page_{s.page_num}.png", "wb") as f:
|
||||
f.write(s.image_bytes)
|
||||
```
|
||||
|
||||
## Document Complexity
|
||||
|
||||
Before committing to a full parse, check whether a document needs OCR or heavier
|
||||
processing. `is_complex` is a cheap, text-layer-only pass that returns one entry per page
|
||||
with a `needs_ocr` verdict and the signals behind it — useful for routing documents to
|
||||
different pipelines, rejecting ones you can't handle, or estimating cost.
|
||||
|
||||
```python
|
||||
parser = LiteParse()
|
||||
pages = parser.is_complex("document.pdf")
|
||||
|
||||
if any(p.needs_ocr for p in pages):
|
||||
# Route to the OCR-enabled pipeline
|
||||
result = parser.parse("document.pdf")
|
||||
else:
|
||||
# Cheap path — skip OCR entirely
|
||||
result = LiteParse(ocr_enabled=False).parse("document.pdf")
|
||||
|
||||
# Inspect why specific pages were flagged
|
||||
for page in pages:
|
||||
if page.needs_ocr:
|
||||
print(f"Page {page.page_number}: {', '.join(page.reasons)}")
|
||||
```
|
||||
|
||||
`reasons` is one of `"scanned"`, `"no-text"`, `"sparse-text"`, `"embedded-images"`,
|
||||
`"garbled"`, or `"vector-text"`. Raw bytes work here too.
|
||||
|
||||
## Supported Formats
|
||||
|
||||
- PDF (`.pdf`)
|
||||
- Microsoft Office (`.docx`, `.xlsx`, `.pptx`, etc.) — requires LibreOffice
|
||||
- OpenDocument (`.odt`, `.ods`, `.odp`) — requires LibreOffice
|
||||
- Images (`.png`, `.jpg`, `.tiff`, etc.) — requires ImageMagick
|
||||
- And more!
|
||||
|
||||
## CLI
|
||||
|
||||
The Python package includes the `lit` CLI:
|
||||
|
||||
```bash
|
||||
lit parse document.pdf
|
||||
lit parse document.pdf --format json -o output.json
|
||||
lit screenshot document.pdf -o ./screenshots
|
||||
lit batch-parse ./input ./output
|
||||
lit is-complex document.pdf
|
||||
```
|
||||
@@ -0,0 +1,27 @@
|
||||
from .parser import LiteParse, search_items
|
||||
from .types import (
|
||||
ExtractedImage,
|
||||
LiteParseConfig,
|
||||
PageComplexityStats,
|
||||
ParseResult,
|
||||
ParsedPage,
|
||||
TextItem,
|
||||
WordBox,
|
||||
ScreenshotResult,
|
||||
ParseError,
|
||||
)
|
||||
|
||||
__version__ = "2.0.0"
|
||||
__all__ = [
|
||||
"LiteParse",
|
||||
"LiteParseConfig",
|
||||
"ParseResult",
|
||||
"ParsedPage",
|
||||
"TextItem",
|
||||
"WordBox",
|
||||
"ScreenshotResult",
|
||||
"PageComplexityStats",
|
||||
"ExtractedImage",
|
||||
"ParseError",
|
||||
"search_items",
|
||||
]
|
||||
@@ -0,0 +1,19 @@
|
||||
"""CLI entry point for the `lit` command."""
|
||||
|
||||
import sys
|
||||
|
||||
from liteparse._liteparse import run_cli
|
||||
|
||||
|
||||
def main() -> None:
|
||||
try:
|
||||
run_cli(sys.argv)
|
||||
except SystemExit:
|
||||
raise
|
||||
except Exception as e:
|
||||
print(f"Error: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,388 @@
|
||||
"""LiteParse Python wrapper - native Rust bindings via PyO3."""
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional, Tuple, Union
|
||||
|
||||
from liteparse._liteparse import LiteParse as _NativeLiteParse
|
||||
from liteparse._liteparse import search_items as _native_search_items
|
||||
|
||||
from .types import (
|
||||
ExtractedImage,
|
||||
LiteParseConfig,
|
||||
PageComplexityStats,
|
||||
ParsedPage,
|
||||
ParseError,
|
||||
ParseResult,
|
||||
ScreenshotResult,
|
||||
TextItem,
|
||||
WordBox,
|
||||
)
|
||||
|
||||
|
||||
def _convert_native_result(native_result: Any) -> ParseResult:
|
||||
"""Convert a native PyParseResult to our Python ParseResult."""
|
||||
pages: List[ParsedPage] = []
|
||||
for native_page in native_result.pages:
|
||||
text_items = [
|
||||
TextItem(
|
||||
text=item.text,
|
||||
x=item.x,
|
||||
y=item.y,
|
||||
width=item.width,
|
||||
height=item.height,
|
||||
font_name=item.font_name,
|
||||
font_size=item.font_size,
|
||||
confidence=item.confidence,
|
||||
rotation=getattr(item, "rotation", 0.0),
|
||||
words=[
|
||||
WordBox(
|
||||
text=w.text,
|
||||
x=w.x,
|
||||
y=w.y,
|
||||
width=w.width,
|
||||
height=w.height,
|
||||
)
|
||||
for w in getattr(item, "words", [])
|
||||
],
|
||||
)
|
||||
for item in native_page.text_items
|
||||
]
|
||||
pages.append(
|
||||
ParsedPage(
|
||||
page_num=native_page.page_num,
|
||||
width=native_page.width,
|
||||
height=native_page.height,
|
||||
text=native_page.text,
|
||||
markdown=native_page.markdown,
|
||||
text_items=text_items,
|
||||
)
|
||||
)
|
||||
images = [
|
||||
ExtractedImage(
|
||||
id=img.id,
|
||||
page=img.page,
|
||||
format=img.format,
|
||||
bytes=img.bytes,
|
||||
)
|
||||
for img in getattr(native_result, "images", [])
|
||||
]
|
||||
return ParseResult(
|
||||
pages=pages,
|
||||
text=native_result.text,
|
||||
images=images,
|
||||
)
|
||||
|
||||
|
||||
class LiteParse:
|
||||
"""
|
||||
Python wrapper for the LiteParse document parser.
|
||||
|
||||
Uses native Rust bindings for fast, in-process PDF parsing.
|
||||
|
||||
Example:
|
||||
>>> from liteparse import LiteParse
|
||||
>>> parser = LiteParse()
|
||||
>>> result = parser.parse("document.pdf")
|
||||
>>> print(result.text)
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
ocr_enabled: Optional[bool] = None,
|
||||
ocr_server_url: Optional[str] = None,
|
||||
ocr_server_headers: Optional[Dict[str, str]] = None,
|
||||
ocr_language: Optional[str] = None,
|
||||
tessdata_path: Optional[str] = None,
|
||||
max_pages: Optional[int] = None,
|
||||
target_pages: Optional[str] = None,
|
||||
dpi: Optional[float] = None,
|
||||
output_format: Optional[str] = None,
|
||||
preserve_very_small_text: Optional[bool] = None,
|
||||
password: Optional[str] = None,
|
||||
quiet: Optional[bool] = None,
|
||||
num_workers: Optional[int] = None,
|
||||
image_mode: Optional[str] = None,
|
||||
extract_links: Optional[bool] = None,
|
||||
ocr_failure_fatal: Optional[bool] = None,
|
||||
ocr_hedge_delays_ms: Optional[List[int]] = None,
|
||||
emit_word_boxes: Optional[bool] = None,
|
||||
crop_box: Optional[Tuple[float, float, float, float]] = None,
|
||||
skip_diagonal_text: Optional[bool] = None,
|
||||
):
|
||||
"""
|
||||
Initialize LiteParse parser.
|
||||
|
||||
Args:
|
||||
ocr_enabled: Whether to enable OCR for scanned documents (default: True)
|
||||
ocr_server_url: URL of HTTP OCR server (uses Tesseract if not provided)
|
||||
ocr_server_headers: Extra HTTP headers sent with every request to
|
||||
``ocr_server_url`` (e.g. ``{"Authorization": "Bearer <token>"}``)
|
||||
ocr_language: Language code for OCR (e.g., "eng", "fra")
|
||||
tessdata_path: Path to tessdata directory for Tesseract
|
||||
max_pages: Maximum number of pages to parse
|
||||
target_pages: Specific pages to parse (e.g., "1-5,10,15-20")
|
||||
dpi: DPI for rendering (affects OCR quality)
|
||||
output_format: Output format: "json", "text", or "markdown" (default: "json")
|
||||
preserve_very_small_text: Whether to preserve very small text
|
||||
password: Password for encrypted/protected documents
|
||||
quiet: Suppress progress output
|
||||
num_workers: Number of concurrent OCR workers (default: CPU cores - 1)
|
||||
extract_links: Render hyperlink annotations as ``[text](url)`` in
|
||||
markdown output (default: True). Set False for plain anchor text.
|
||||
ocr_failure_fatal: Whether a systemic OCR failure (every OCR task
|
||||
failed and at least one was a text-sparse page) aborts the whole
|
||||
parse (default: True). Set False to keep already-recovered native
|
||||
text and return partial results instead of raising — for callers
|
||||
that prefer a degraded document over a hard failure.
|
||||
ocr_hedge_delays_ms: Request-hedging schedule for HTTP OCR, in
|
||||
milliseconds. Empty or single-element means no hedging (one
|
||||
request per attempt — the default). With multiple delays (e.g.
|
||||
``[0, 5000, 10000]``) each attempt fires a duplicate request at
|
||||
every delay and takes the first to succeed, cancelling the rest
|
||||
— trades extra OCR-server load for lower tail latency.
|
||||
emit_word_boxes: Emit per-word sub-boxes on each text item
|
||||
(``TextItem.words``). Default False. Word boxes roughly double
|
||||
the text-item payload, so enable only for word-level bbox
|
||||
attribution.
|
||||
crop_box: Restrict output to a page sub-region, as a
|
||||
``(top, right, bottom, left)`` tuple where each value is the
|
||||
fraction cropped from that side (top-left origin, each in
|
||||
``[0, 1]``); e.g. ``(0, 0, 0, 0.5)`` keeps the right half. A
|
||||
text item survives only if it lies entirely inside the
|
||||
remaining rectangle. None (default) keeps the whole page.
|
||||
skip_diagonal_text: Drop diagonal text — items whose rotation is
|
||||
more than 2° off the nearest right angle (0/90/180/270).
|
||||
Default False. Use to exclude rotated watermarks/stamps.
|
||||
"""
|
||||
kwargs = {}
|
||||
if ocr_enabled is not None:
|
||||
kwargs["ocr_enabled"] = ocr_enabled
|
||||
if ocr_server_url is not None:
|
||||
kwargs["ocr_server_url"] = ocr_server_url
|
||||
if ocr_server_headers is not None:
|
||||
kwargs["ocr_server_headers"] = ocr_server_headers
|
||||
if ocr_language is not None:
|
||||
kwargs["ocr_language"] = ocr_language
|
||||
if tessdata_path is not None:
|
||||
kwargs["tessdata_path"] = tessdata_path
|
||||
if max_pages is not None:
|
||||
kwargs["max_pages"] = max_pages
|
||||
if target_pages is not None:
|
||||
kwargs["target_pages"] = target_pages
|
||||
if dpi is not None:
|
||||
kwargs["dpi"] = dpi
|
||||
if output_format is not None:
|
||||
kwargs["output_format"] = output_format
|
||||
if preserve_very_small_text is not None:
|
||||
kwargs["preserve_very_small_text"] = preserve_very_small_text
|
||||
if password is not None:
|
||||
kwargs["password"] = password
|
||||
if quiet is not None:
|
||||
kwargs["quiet"] = quiet
|
||||
if num_workers is not None:
|
||||
kwargs["num_workers"] = num_workers
|
||||
if image_mode is not None:
|
||||
kwargs["image_mode"] = image_mode
|
||||
if extract_links is not None:
|
||||
kwargs["extract_links"] = extract_links
|
||||
if ocr_failure_fatal is not None:
|
||||
kwargs["ocr_failure_fatal"] = ocr_failure_fatal
|
||||
if ocr_hedge_delays_ms is not None:
|
||||
kwargs["ocr_hedge_delays_ms"] = ocr_hedge_delays_ms
|
||||
if emit_word_boxes is not None:
|
||||
kwargs["emit_word_boxes"] = emit_word_boxes
|
||||
if crop_box is not None:
|
||||
kwargs["crop_box"] = crop_box
|
||||
if skip_diagonal_text is not None:
|
||||
kwargs["skip_diagonal_text"] = skip_diagonal_text
|
||||
|
||||
self._native = _NativeLiteParse(**kwargs)
|
||||
|
||||
def parse(
|
||||
self,
|
||||
file_data: Union[str, Path, bytes],
|
||||
) -> ParseResult:
|
||||
"""
|
||||
Parse a document file.
|
||||
|
||||
Args:
|
||||
file_data: Path to the document file, or raw PDF bytes.
|
||||
|
||||
Returns:
|
||||
ParseResult containing the parsed document data.
|
||||
|
||||
Raises:
|
||||
ParseError: If parsing fails.
|
||||
FileNotFoundError: If the file doesn't exist.
|
||||
"""
|
||||
try:
|
||||
if isinstance(file_data, bytes):
|
||||
native_result = self._native.parse_bytes(file_data)
|
||||
else:
|
||||
file_path = Path(file_data)
|
||||
if not file_path.exists():
|
||||
raise FileNotFoundError(f"File not found: {file_path}")
|
||||
native_result = self._native.parse(str(file_path.absolute()))
|
||||
return _convert_native_result(native_result)
|
||||
except FileNotFoundError:
|
||||
raise
|
||||
except Exception as e:
|
||||
raise ParseError(str(e)) from e
|
||||
|
||||
def is_complex(
|
||||
self,
|
||||
file_data: Union[str, Path, bytes],
|
||||
) -> List[PageComplexityStats]:
|
||||
"""
|
||||
Determine per-page complexity without running a full parse.
|
||||
|
||||
Returns one entry per page with signals (text coverage, images, garbled
|
||||
text, vector area) and a ``needs_ocr`` verdict — a cheap pre-OCR check to
|
||||
decide whether a document needs advanced parsing.
|
||||
|
||||
Args:
|
||||
file_data: Path to the document file, or raw PDF bytes.
|
||||
|
||||
Returns:
|
||||
List of PageComplexityStats, one per page.
|
||||
|
||||
Raises:
|
||||
ParseError: If the check fails.
|
||||
FileNotFoundError: If the file doesn't exist.
|
||||
"""
|
||||
try:
|
||||
if isinstance(file_data, bytes):
|
||||
native_stats = self._native.is_complex_bytes(file_data)
|
||||
else:
|
||||
file_path = Path(file_data)
|
||||
if not file_path.exists():
|
||||
raise FileNotFoundError(f"File not found: {file_path}")
|
||||
native_stats = self._native.is_complex(str(file_path.absolute()))
|
||||
return [
|
||||
PageComplexityStats(
|
||||
page_number=s.page_number,
|
||||
text_length=s.text_length,
|
||||
text_coverage=s.text_coverage,
|
||||
has_substantial_images=s.has_substantial_images,
|
||||
image_block_count=s.image_block_count,
|
||||
image_coverage=s.image_coverage,
|
||||
largest_image_coverage=s.largest_image_coverage,
|
||||
full_page_image=s.full_page_image,
|
||||
uncovered_vector_area=s.uncovered_vector_area,
|
||||
is_garbled=s.is_garbled,
|
||||
page_area=s.page_area,
|
||||
needs_ocr=s.needs_ocr,
|
||||
reasons=list(s.reasons),
|
||||
)
|
||||
for s in native_stats
|
||||
]
|
||||
except FileNotFoundError:
|
||||
raise
|
||||
except Exception as e:
|
||||
raise ParseError(str(e)) from e
|
||||
|
||||
def screenshot(
|
||||
self,
|
||||
file_path: Union[str, Path],
|
||||
*,
|
||||
page_numbers: Optional[List[int]] = None,
|
||||
) -> List[ScreenshotResult]:
|
||||
"""
|
||||
Generate screenshots of document pages.
|
||||
|
||||
Supports PDFs natively. Non-PDF formats (DOCX, XLSX, images, etc.) are
|
||||
automatically converted to PDF before rendering when the required system
|
||||
tools are installed. Plain-text formats cannot be rendered.
|
||||
|
||||
Args:
|
||||
file_path: Path to the document file (PDF, DOCX, images, etc.).
|
||||
page_numbers: Specific page numbers to screenshot (1-indexed).
|
||||
If None, screenshots all pages.
|
||||
|
||||
Returns:
|
||||
List of ScreenshotResult with PNG image bytes.
|
||||
|
||||
Raises:
|
||||
FileNotFoundError: If the file doesn't exist.
|
||||
ParseError: If screenshot generation fails.
|
||||
"""
|
||||
file_path = Path(file_path)
|
||||
if not file_path.exists():
|
||||
raise FileNotFoundError(f"File not found: {file_path}")
|
||||
|
||||
try:
|
||||
native_results = self._native.screenshot(
|
||||
str(file_path.absolute()),
|
||||
page_numbers,
|
||||
)
|
||||
return [
|
||||
ScreenshotResult(
|
||||
page_num=r.page_num,
|
||||
width=r.width,
|
||||
height=r.height,
|
||||
image_bytes=r.image_bytes,
|
||||
)
|
||||
for r in native_results
|
||||
]
|
||||
except Exception as e:
|
||||
raise ParseError(str(e)) from e
|
||||
|
||||
def get_config(self) -> LiteParseConfig:
|
||||
"""Return the resolved configuration."""
|
||||
cfg = self._native.config
|
||||
return LiteParseConfig(
|
||||
ocr_language=cfg.ocr_language,
|
||||
ocr_enabled=cfg.ocr_enabled,
|
||||
ocr_server_url=cfg.ocr_server_url,
|
||||
ocr_server_headers=cfg.ocr_server_headers,
|
||||
tessdata_path=cfg.tessdata_path,
|
||||
max_pages=cfg.max_pages,
|
||||
target_pages=cfg.target_pages,
|
||||
dpi=cfg.dpi,
|
||||
output_format=cfg.output_format,
|
||||
preserve_very_small_text=cfg.preserve_very_small_text,
|
||||
password=cfg.password,
|
||||
quiet=cfg.quiet,
|
||||
num_workers=cfg.num_workers,
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return "LiteParse()"
|
||||
|
||||
|
||||
def search_items(
|
||||
items: List[TextItem],
|
||||
phrase: str,
|
||||
*,
|
||||
case_sensitive: bool = False,
|
||||
) -> List[TextItem]:
|
||||
"""
|
||||
Search text items for phrase matches, returning merged items with combined bounding boxes.
|
||||
|
||||
A phrase may span multiple adjacent text items. This function concatenates
|
||||
consecutive items, finds matches, and returns synthetic merged TextItem
|
||||
objects with combined bounding boxes.
|
||||
|
||||
Args:
|
||||
items: List of TextItem objects to search through.
|
||||
phrase: The phrase to search for.
|
||||
case_sensitive: Whether the search should be case-sensitive (default: False).
|
||||
|
||||
Returns:
|
||||
List of TextItem objects representing the matched regions.
|
||||
"""
|
||||
native_results = _native_search_items(items, phrase, case_sensitive=case_sensitive)
|
||||
return [
|
||||
TextItem(
|
||||
text=item.text,
|
||||
x=item.x,
|
||||
y=item.y,
|
||||
width=item.width,
|
||||
height=item.height,
|
||||
font_name=item.font_name,
|
||||
font_size=item.font_size,
|
||||
confidence=item.confidence,
|
||||
)
|
||||
for item in native_results
|
||||
]
|
||||
@@ -0,0 +1,128 @@
|
||||
"""Python-friendly type wrappers around the native Rust bindings."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Dict, Iterator, List, Optional
|
||||
|
||||
|
||||
@dataclass
|
||||
class WordBox:
|
||||
"""One word's bounding box within a :class:`TextItem`, in the same viewport
|
||||
space (top-left origin, 72 DPI). ``text`` excludes inter-word spaces."""
|
||||
text: str
|
||||
x: float
|
||||
y: float
|
||||
width: float
|
||||
height: float
|
||||
|
||||
|
||||
@dataclass
|
||||
class TextItem:
|
||||
"""Individual text item extracted from a document."""
|
||||
text: str
|
||||
x: float
|
||||
y: float
|
||||
width: float
|
||||
height: float
|
||||
font_name: Optional[str] = None
|
||||
font_size: Optional[float] = None
|
||||
confidence: Optional[float] = None
|
||||
rotation: float = 0.0
|
||||
#: Per-word sub-boxes. Empty unless the parser was configured with
|
||||
#: ``emit_word_boxes=True``.
|
||||
words: List[WordBox] = field(default_factory=list)
|
||||
|
||||
|
||||
@dataclass
|
||||
class ParsedPage:
|
||||
"""A parsed page from a document."""
|
||||
page_num: int
|
||||
width: float
|
||||
height: float
|
||||
text: str
|
||||
markdown: str = ""
|
||||
text_items: List[TextItem] = field(default_factory=list)
|
||||
|
||||
|
||||
@dataclass
|
||||
class ExtractedImage:
|
||||
"""An embedded raster image extracted from a page.
|
||||
|
||||
Populated only when the parser was configured with ``image_mode="embed"``.
|
||||
The ``id`` matches the reference used in the markdown output
|
||||
(e.g. ```` → ``id="p1_0"``).
|
||||
"""
|
||||
id: str
|
||||
page: int
|
||||
format: str
|
||||
bytes: bytes
|
||||
|
||||
|
||||
@dataclass
|
||||
class ParseResult:
|
||||
"""Result of parsing a document."""
|
||||
pages: List[ParsedPage]
|
||||
text: str
|
||||
images: List[ExtractedImage] = field(default_factory=list)
|
||||
|
||||
@property
|
||||
def num_pages(self) -> int:
|
||||
return len(self.pages)
|
||||
|
||||
def get_page(self, page_num: int) -> Optional[ParsedPage]:
|
||||
"""Get a specific page by number (1-indexed)."""
|
||||
for page in self.pages:
|
||||
if page.page_num == page_num:
|
||||
return page
|
||||
return None
|
||||
|
||||
|
||||
@dataclass
|
||||
class ScreenshotResult:
|
||||
"""Result of a single page screenshot."""
|
||||
page_num: int
|
||||
width: int
|
||||
height: int
|
||||
image_bytes: bytes
|
||||
|
||||
|
||||
@dataclass
|
||||
class PageComplexityStats:
|
||||
"""Per-page complexity signals used to decide whether a document needs OCR."""
|
||||
page_number: int
|
||||
text_length: int
|
||||
text_coverage: float
|
||||
has_substantial_images: bool
|
||||
image_block_count: int
|
||||
image_coverage: float
|
||||
largest_image_coverage: float
|
||||
full_page_image: bool
|
||||
uncovered_vector_area: Optional[float]
|
||||
is_garbled: bool
|
||||
page_area: float
|
||||
needs_ocr: bool
|
||||
reasons: list[str]
|
||||
|
||||
|
||||
@dataclass
|
||||
class LiteParseConfig:
|
||||
"""Resolved parser configuration."""
|
||||
ocr_language: str
|
||||
ocr_enabled: bool
|
||||
ocr_server_url: Optional[str]
|
||||
ocr_server_headers: Optional[Dict[str, str]]
|
||||
tessdata_path: Optional[str]
|
||||
max_pages: int
|
||||
target_pages: Optional[str]
|
||||
dpi: float
|
||||
output_format: str
|
||||
preserve_very_small_text: bool
|
||||
password: Optional[str]
|
||||
quiet: bool
|
||||
num_workers: int
|
||||
|
||||
|
||||
class ParseError(Exception):
|
||||
"""Exception raised when parsing fails."""
|
||||
pass
|
||||
@@ -0,0 +1,62 @@
|
||||
[build-system]
|
||||
requires = ["maturin>=1.0,<2.0"]
|
||||
build-backend = "maturin"
|
||||
|
||||
[project]
|
||||
name = "liteparse"
|
||||
version = "2.5.1"
|
||||
description = "Python bindings for LiteParse - fast, lightweight PDF and document parsing"
|
||||
readme = "README.md"
|
||||
license = "Apache-2.0"
|
||||
requires-python = ">=3.10"
|
||||
authors = [
|
||||
{ name = "Logan Markewich", email = "logan@runllama.ai" },
|
||||
]
|
||||
keywords = ["pdf", "parsing", "ocr", "document", "text-extraction"]
|
||||
classifiers = [
|
||||
"Development Status :: 4 - Beta",
|
||||
"Intended Audience :: Developers",
|
||||
"License :: OSI Approved :: Apache Software License",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3.12",
|
||||
"Programming Language :: Python :: 3.13",
|
||||
"Programming Language :: Python :: 3.14",
|
||||
"Programming Language :: Rust",
|
||||
"Topic :: Text Processing",
|
||||
"Topic :: Scientific/Engineering :: Information Analysis",
|
||||
"Typing :: Typed",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://github.com/run-llama/liteparse"
|
||||
Documentation = "https://github.com/run-llama/liteparse#readme"
|
||||
Repository = "https://github.com/run-llama/liteparse"
|
||||
|
||||
[project.scripts]
|
||||
lit = "liteparse.cli:main"
|
||||
|
||||
[project.optional-dependencies]
|
||||
dev = [
|
||||
"pytest>=7.0",
|
||||
"pytest-asyncio>=0.21",
|
||||
"mypy>=1.0",
|
||||
]
|
||||
|
||||
[tool.maturin]
|
||||
manifest-path = "../../crates/liteparse-python/Cargo.toml"
|
||||
python-source = "."
|
||||
module-name = "liteparse._liteparse"
|
||||
# Bundle libpdfium alongside the extension module
|
||||
include = [
|
||||
{ path = "liteparse/libpdfium.dylib", format = "wheel" },
|
||||
{ path = "liteparse/libpdfium.so", format = "wheel" },
|
||||
{ path = "liteparse/pdfium.dll", format = "wheel" },
|
||||
]
|
||||
|
||||
[tool.mypy]
|
||||
python_version = "3.10"
|
||||
strict = true
|
||||
warn_return_any = true
|
||||
warn_unused_ignores = true
|
||||
Executable
+77
@@ -0,0 +1,77 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Copy the pdfium shared library into the liteparse package directory
|
||||
# so it can be found at runtime via @loader_path (macOS) or $ORIGIN (Linux).
|
||||
#
|
||||
# Usage: ./scripts/copy-pdfium.sh
|
||||
#
|
||||
# The script auto-detects the pdfium library location from:
|
||||
# 1. PDFIUM_LIB_PATH env var (set by CI or user)
|
||||
# 2. The pdfium-sys build cache (~/.cache/pdfium-rs or ~/Library/Caches/pdfium-rs)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
OUTPUT_DIR="${SCRIPT_DIR}/../liteparse"
|
||||
|
||||
# Determine OS and library filename
|
||||
case "$(uname -s)" in
|
||||
Darwin*) DYLIB="libpdfium.dylib" ;;
|
||||
Linux*) DYLIB="libpdfium.so" ;;
|
||||
MINGW*|MSYS*|CYGWIN*) DYLIB="pdfium.dll" ;;
|
||||
*) echo "Unsupported OS: $(uname -s)" >&2; exit 1 ;;
|
||||
esac
|
||||
|
||||
# Find the pdfium library
|
||||
find_pdfium() {
|
||||
# 1. Explicit env var
|
||||
if [ -n "${PDFIUM_LIB_PATH:-}" ] && [ -f "${PDFIUM_LIB_PATH}/${DYLIB}" ]; then
|
||||
echo "${PDFIUM_LIB_PATH}/${DYLIB}"
|
||||
return
|
||||
fi
|
||||
|
||||
# 2. Vendor directory
|
||||
local vendor="${SCRIPT_DIR}/../../vendor/pdfium/release/lib/${DYLIB}"
|
||||
if [ -f "$vendor" ]; then
|
||||
echo "$vendor"
|
||||
return
|
||||
fi
|
||||
|
||||
# 3. Cargo build output (debug and release)
|
||||
local workspace_root="${SCRIPT_DIR}/../../../.."
|
||||
for profile in release debug; do
|
||||
local deps="${workspace_root}/target/${profile}/deps/${DYLIB}"
|
||||
if [ -f "$deps" ]; then
|
||||
echo "$deps"
|
||||
return
|
||||
fi
|
||||
done
|
||||
|
||||
# 4. Build cache
|
||||
local cache_base
|
||||
case "$(uname -s)" in
|
||||
Darwin*) cache_base="$HOME/Library/Caches/pdfium-rs" ;;
|
||||
*) cache_base="${XDG_CACHE_HOME:-$HOME/.cache}/pdfium-rs" ;;
|
||||
esac
|
||||
|
||||
if [ -d "$cache_base" ]; then
|
||||
local found
|
||||
found=$(find "$cache_base" -name "$DYLIB" -type f 2>/dev/null | head -1)
|
||||
if [ -n "$found" ]; then
|
||||
echo "$found"
|
||||
return
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
PDFIUM_PATH=$(find_pdfium)
|
||||
|
||||
if [ -z "$PDFIUM_PATH" ]; then
|
||||
echo "Error: Could not find ${DYLIB}. Set PDFIUM_LIB_PATH to the directory containing it." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cp "$PDFIUM_PATH" "${OUTPUT_DIR}/${DYLIB}"
|
||||
echo "Copied ${PDFIUM_PATH} -> ${OUTPUT_DIR}/${DYLIB}"
|
||||
Executable
+65
@@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Download the prebuilt pdfium binary for a given target triple and stage it
|
||||
# at `packages/python/liteparse/<dylib>` so maturin's `[tool.maturin] include`
|
||||
# rule packs it into the wheel (and records it in RECORD) at build time.
|
||||
#
|
||||
# Usage: ./scripts/download-pdfium.sh <target-triple>
|
||||
#
|
||||
# The release tag is parsed from `crates/pdfium-sys/build.rs` so we only have
|
||||
# one source of truth for the pdfium version.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
TARGET="${1:?usage: download-pdfium.sh <target-triple>}"
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
PKG_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
REPO_ROOT="$(cd "$PKG_DIR/../.." && pwd)"
|
||||
|
||||
BUILD_RS="$REPO_ROOT/crates/pdfium-sys/build.rs"
|
||||
TAG=$(grep -E 'const PDFIUM_RELEASE_TAG' "$BUILD_RS" | sed -E 's/.*"([^"]+)".*/\1/')
|
||||
[ -n "$TAG" ] || { echo "Could not parse PDFIUM_RELEASE_TAG from $BUILD_RS" >&2; exit 1; }
|
||||
|
||||
case "$TARGET" in
|
||||
aarch64-apple-darwin) ASSET="pdfium-mac-arm64"; DYLIB="libpdfium.dylib" ;;
|
||||
x86_64-apple-darwin) ASSET="pdfium-mac-x64"; DYLIB="libpdfium.dylib" ;;
|
||||
x86_64-unknown-linux-gnu) ASSET="pdfium-linux-x64"; DYLIB="libpdfium.so" ;;
|
||||
aarch64-unknown-linux-gnu) ASSET="pdfium-linux-arm64"; DYLIB="libpdfium.so" ;;
|
||||
x86_64-unknown-linux-musl) ASSET="pdfium-linux-musl-x64"; DYLIB="libpdfium.so" ;;
|
||||
aarch64-unknown-linux-musl) ASSET="pdfium-linux-arm64"; DYLIB="libpdfium.so" ;;
|
||||
x86_64-pc-windows-msvc) ASSET="pdfium-win-x64"; DYLIB="pdfium.dll" ;;
|
||||
aarch64-pc-windows-msvc) ASSET="pdfium-win-arm64"; DYLIB="pdfium.dll" ;;
|
||||
*) echo "Unsupported target: $TARGET" >&2; exit 1 ;;
|
||||
esac
|
||||
|
||||
URL_TAG="${TAG//\//%2F}"
|
||||
URL="https://github.com/run-llama/pdfium-binaries/releases/download/${URL_TAG}/${ASSET}.tgz"
|
||||
|
||||
TMP=$(mktemp -d)
|
||||
trap 'rm -rf "$TMP"' EXIT
|
||||
|
||||
echo "Downloading $URL"
|
||||
curl -fsSL "$URL" -o "$TMP/${ASSET}.tgz"
|
||||
tar -xzf "$TMP/${ASSET}.tgz" -C "$TMP"
|
||||
|
||||
# pdfium-binaries layout: lib/<DYLIB> on unix, bin/pdfium.dll on windows
|
||||
SRC=""
|
||||
for candidate in "$TMP/lib/$DYLIB" "$TMP/bin/$DYLIB"; do
|
||||
if [ -f "$candidate" ]; then
|
||||
SRC="$candidate"
|
||||
break
|
||||
fi
|
||||
done
|
||||
[ -n "$SRC" ] || { echo "Could not find $DYLIB in archive" >&2; ls -R "$TMP" >&2; exit 1; }
|
||||
|
||||
# Mirror the install-name fix in crates/pdfium-sys/build.rs so the dylib resolves
|
||||
# via @rpath at runtime. pdfium-binaries ships macOS dylibs with install name
|
||||
# `./libpdfium.dylib` which won't be found via our rpath.
|
||||
if [ "$DYLIB" = "libpdfium.dylib" ] && command -v install_name_tool >/dev/null 2>&1; then
|
||||
install_name_tool -id "@rpath/libpdfium.dylib" "$SRC"
|
||||
fi
|
||||
|
||||
DEST_DIR="$PKG_DIR/liteparse"
|
||||
mkdir -p "$DEST_DIR"
|
||||
cp "$SRC" "$DEST_DIR/$DYLIB"
|
||||
echo "Staged $DEST_DIR/$DYLIB"
|
||||
@@ -0,0 +1,45 @@
|
||||
"""Shared fixtures for e2e tests."""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from liteparse import LiteParse
|
||||
|
||||
# Resolve test-docs relative to the repo root
|
||||
REPO_ROOT = Path(__file__).resolve().parents[3]
|
||||
TEST_DOCS = REPO_ROOT / "test-docs"
|
||||
EXPECTED_DATASET = REPO_ROOT / "expected-dataset" / "data"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def parser() -> LiteParse:
|
||||
"""LiteParse instance (auto-detects CLI)."""
|
||||
return LiteParse()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def invoice_pdf() -> Path:
|
||||
"""Path to a small single-page invoice PDF."""
|
||||
p = TEST_DOCS / "invoice.pdf"
|
||||
if not p.exists():
|
||||
pytest.skip(f"Test document not found: {p}")
|
||||
return p
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def two_page_pdf() -> Path:
|
||||
"""Path to a 2-page PDF."""
|
||||
p = EXPECTED_DATASET / "2pages.pdf"
|
||||
if not p.exists():
|
||||
pytest.skip(f"Test document not found: {p}")
|
||||
return p
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def empty_pdf() -> Path:
|
||||
"""Path to an empty PDF."""
|
||||
p = EXPECTED_DATASET / "empty.pdf"
|
||||
if not p.exists():
|
||||
pytest.skip(f"Test document not found: {p}")
|
||||
return p
|
||||
@@ -0,0 +1,88 @@
|
||||
"""E2E tests for LiteParse.batch_parse() — validates Python types match CLI output."""
|
||||
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from liteparse import (
|
||||
BatchResult,
|
||||
LiteParse,
|
||||
OutputFormat,
|
||||
)
|
||||
|
||||
|
||||
class TestBatchParseBasic:
|
||||
"""Basic batch parsing functionality."""
|
||||
|
||||
def test_batch_parse_returns_batch_result(
|
||||
self, parser: LiteParse, invoice_pdf: Path
|
||||
):
|
||||
input_dir = invoice_pdf.parent
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
result = parser.batch_parse(
|
||||
input_dir,
|
||||
tmpdir,
|
||||
ocr_enabled=False,
|
||||
extension_filter=".pdf",
|
||||
)
|
||||
assert isinstance(result, BatchResult)
|
||||
assert result.output_dir == tmpdir
|
||||
|
||||
def test_batch_parse_creates_output_files(
|
||||
self, parser: LiteParse, invoice_pdf: Path
|
||||
):
|
||||
input_dir = invoice_pdf.parent
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
parser.batch_parse(
|
||||
input_dir,
|
||||
tmpdir,
|
||||
output_format=OutputFormat.TEXT,
|
||||
ocr_enabled=False,
|
||||
extension_filter=".pdf",
|
||||
)
|
||||
output_files = list(Path(tmpdir).rglob("*"))
|
||||
# Should have produced at least one output file
|
||||
assert len(output_files) > 0
|
||||
|
||||
def test_batch_parse_json_format(self, parser: LiteParse, invoice_pdf: Path):
|
||||
input_dir = invoice_pdf.parent
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
parser.batch_parse(
|
||||
input_dir,
|
||||
tmpdir,
|
||||
output_format="json",
|
||||
ocr_enabled=False,
|
||||
extension_filter=".pdf",
|
||||
)
|
||||
json_files = list(Path(tmpdir).rglob("*.json"))
|
||||
assert len(json_files) > 0
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_batch_parse_async(self, parser: LiteParse, invoice_pdf: Path):
|
||||
input_dir = invoice_pdf.parent
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
result = await parser.batch_parse_async(
|
||||
input_dir,
|
||||
tmpdir,
|
||||
output_format="json",
|
||||
ocr_enabled=False,
|
||||
extension_filter=".pdf",
|
||||
)
|
||||
assert isinstance(result, BatchResult)
|
||||
assert result.output_dir == tmpdir
|
||||
|
||||
|
||||
class TestBatchParseErrors:
|
||||
"""Test error handling."""
|
||||
|
||||
def test_input_dir_not_found(self, parser: LiteParse):
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
with pytest.raises(FileNotFoundError):
|
||||
parser.batch_parse("/nonexistent/dir", tmpdir)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_input_dir_not_found_async(self, parser: LiteParse):
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
with pytest.raises(FileNotFoundError):
|
||||
await parser.batch_parse_async("/nonexistent/dir", tmpdir)
|
||||
@@ -0,0 +1,176 @@
|
||||
"""E2E tests for LiteParse.parse() — validates Python types match CLI output."""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from liteparse import (
|
||||
BoundingBox,
|
||||
LiteParse,
|
||||
ParsedPage,
|
||||
ParseError,
|
||||
ParseResult,
|
||||
TextItem,
|
||||
)
|
||||
|
||||
|
||||
class TestParseBasic:
|
||||
"""Basic parse functionality and output structure."""
|
||||
|
||||
def test_parse_returns_parse_result(self, parser: LiteParse, invoice_pdf: Path):
|
||||
result = parser.parse(invoice_pdf, ocr_enabled=False)
|
||||
assert isinstance(result, ParseResult)
|
||||
|
||||
def test_parse_result_has_pages(self, parser: LiteParse, invoice_pdf: Path):
|
||||
result = parser.parse(invoice_pdf, ocr_enabled=False)
|
||||
assert len(result.pages) > 0
|
||||
assert result.num_pages == len(result.pages)
|
||||
|
||||
def test_parse_result_has_text(self, parser: LiteParse, invoice_pdf: Path):
|
||||
result = parser.parse(invoice_pdf, ocr_enabled=False)
|
||||
assert isinstance(result.text, str)
|
||||
assert len(result.text) > 0
|
||||
|
||||
def test_parse_result_has_json(self, parser: LiteParse, invoice_pdf: Path):
|
||||
result = parser.parse(invoice_pdf, ocr_enabled=False)
|
||||
assert result.json is not None
|
||||
assert "pages" in result.json
|
||||
|
||||
def test_parse_bytest_input(self, parser: LiteParse, invoice_pdf: Path):
|
||||
file_bytes = invoice_pdf.read_bytes()
|
||||
result = parser.parse(file_bytes)
|
||||
assert result.json is not None
|
||||
assert "pages" in result.json
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_parse_async(self, parser: LiteParse, invoice_pdf: Path):
|
||||
result = await parser.parse_async(invoice_pdf)
|
||||
assert result.json is not None
|
||||
assert "pages" in result.json
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_parse_async_bytes_input(self, parser: LiteParse, invoice_pdf: Path):
|
||||
file_bytes = invoice_pdf.read_bytes()
|
||||
result = await parser.parse_async(file_bytes)
|
||||
assert result.json is not None
|
||||
assert "pages" in result.json
|
||||
|
||||
|
||||
class TestParsedPageStructure:
|
||||
"""Validate ParsedPage fields match CLI output."""
|
||||
|
||||
def test_page_fields(self, parser: LiteParse, invoice_pdf: Path):
|
||||
result = parser.parse(invoice_pdf, ocr_enabled=False)
|
||||
page = result.pages[0]
|
||||
assert isinstance(page, ParsedPage)
|
||||
assert isinstance(page.pageNum, int)
|
||||
assert page.pageNum >= 1
|
||||
assert isinstance(page.width, (int, float))
|
||||
assert isinstance(page.height, (int, float))
|
||||
assert page.width > 0
|
||||
assert page.height > 0
|
||||
assert isinstance(page.text, str)
|
||||
|
||||
def test_page_has_text_items(self, parser: LiteParse, invoice_pdf: Path):
|
||||
result = parser.parse(invoice_pdf, ocr_enabled=False)
|
||||
page = result.pages[0]
|
||||
assert len(page.textItems) > 0
|
||||
|
||||
def test_text_item_fields(self, parser: LiteParse, invoice_pdf: Path):
|
||||
result = parser.parse(invoice_pdf, ocr_enabled=False)
|
||||
item = result.pages[0].textItems[0]
|
||||
assert isinstance(item, TextItem)
|
||||
assert isinstance(item.text, str)
|
||||
assert isinstance(item.x, (int, float))
|
||||
assert isinstance(item.y, (int, float))
|
||||
assert isinstance(item.width, (int, float))
|
||||
assert isinstance(item.height, (int, float))
|
||||
# fontName and fontSize may be None or present
|
||||
if item.fontName is not None:
|
||||
assert isinstance(item.fontName, str)
|
||||
if item.fontSize is not None:
|
||||
assert isinstance(item.fontSize, (int, float))
|
||||
|
||||
def test_bounding_box_fields(self, parser: LiteParse, invoice_pdf: Path):
|
||||
result = parser.parse(invoice_pdf, ocr_enabled=False, precise_bounding_box=True)
|
||||
page = result.pages[0]
|
||||
assert len(page.boundingBoxes) > 0
|
||||
bbox = page.boundingBoxes[0]
|
||||
assert isinstance(bbox, BoundingBox)
|
||||
assert isinstance(bbox.x1, (int, float))
|
||||
assert isinstance(bbox.y1, (int, float))
|
||||
assert isinstance(bbox.x2, (int, float))
|
||||
assert isinstance(bbox.y2, (int, float))
|
||||
|
||||
|
||||
class TestParseOptions:
|
||||
"""Test that CLI options are correctly forwarded."""
|
||||
|
||||
def test_target_pages(self, parser: LiteParse, invoice_pdf: Path):
|
||||
# invoice.pdf has 2 pages — parse only page 1
|
||||
result = parser.parse(invoice_pdf, ocr_enabled=False, target_pages="1")
|
||||
assert result.num_pages == 1
|
||||
assert result.pages[0].pageNum == 1
|
||||
|
||||
def test_max_pages(self, parser: LiteParse, invoice_pdf: Path):
|
||||
result = parser.parse(invoice_pdf, ocr_enabled=False, max_pages=1)
|
||||
assert result.num_pages == 1
|
||||
|
||||
def test_no_precise_bbox(self, parser: LiteParse, invoice_pdf: Path):
|
||||
result = parser.parse(
|
||||
invoice_pdf, ocr_enabled=False, precise_bounding_box=False
|
||||
)
|
||||
assert isinstance(result, ParseResult)
|
||||
# With no precise bbox, boundingBoxes should be empty
|
||||
for page in result.pages:
|
||||
assert len(page.boundingBoxes) == 0
|
||||
|
||||
def test_get_page_helper(self, parser: LiteParse, invoice_pdf: Path):
|
||||
result = parser.parse(invoice_pdf, ocr_enabled=False)
|
||||
page1 = result.get_page(1)
|
||||
assert page1 is not None
|
||||
assert page1.pageNum == 1
|
||||
assert result.get_page(999) is None
|
||||
|
||||
def test_multi_page_text_joined(self, parser: LiteParse, invoice_pdf: Path):
|
||||
result = parser.parse(invoice_pdf, ocr_enabled=False)
|
||||
assert result.num_pages == 2
|
||||
# result.text should be the join of all page texts
|
||||
expected = "\n\n".join(p.text for p in result.pages)
|
||||
assert result.text == expected
|
||||
|
||||
|
||||
class TestParseErrors:
|
||||
"""Test error handling."""
|
||||
|
||||
def test_file_not_found(self, parser: LiteParse):
|
||||
with pytest.raises(FileNotFoundError):
|
||||
parser.parse("/nonexistent/file.pdf")
|
||||
|
||||
def test_cli_not_found(self):
|
||||
parser = LiteParse(cli_path="/nonexistent/liteparse")
|
||||
# subprocess raises FileNotFoundError when the binary doesn't exist
|
||||
with pytest.raises((ParseError, FileNotFoundError, OSError)):
|
||||
parser.parse(Path(__file__)) # any existing file
|
||||
|
||||
def test_timeout(self, parser: LiteParse, invoice_pdf: Path):
|
||||
# Extremely short timeout should fail
|
||||
with pytest.raises(TimeoutError):
|
||||
parser.parse(invoice_pdf, timeout=0.001)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_file_not_found_async(self, parser: LiteParse):
|
||||
with pytest.raises(FileNotFoundError):
|
||||
await parser.parse_async("/nonexistent/file.pdf")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_cli_not_found_async(self):
|
||||
parser = LiteParse(cli_path="/nonexistent/liteparse")
|
||||
# subprocess raises FileNotFoundError when the binary doesn't exist
|
||||
with pytest.raises((ParseError, FileNotFoundError, OSError)):
|
||||
parser.parse(Path(__file__)) # any existing file
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_timeout_async(self, parser: LiteParse, invoice_pdf: Path):
|
||||
with pytest.raises(TimeoutError):
|
||||
await parser.parse_async(invoice_pdf, timeout=0.001)
|
||||
@@ -0,0 +1,102 @@
|
||||
"""E2E tests for LiteParse.screenshot() — validates Python types match CLI output."""
|
||||
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from liteparse import (
|
||||
ImageFormat,
|
||||
LiteParse,
|
||||
ScreenshotBatchResult,
|
||||
ScreenshotResult,
|
||||
)
|
||||
|
||||
|
||||
class TestScreenshotBasic:
|
||||
"""Basic screenshot functionality."""
|
||||
|
||||
def test_screenshot_returns_batch_result(
|
||||
self, parser: LiteParse, invoice_pdf: Path
|
||||
):
|
||||
result = parser.screenshot(invoice_pdf)
|
||||
assert isinstance(result, ScreenshotBatchResult)
|
||||
|
||||
def test_screenshot_has_screenshots(self, parser: LiteParse, invoice_pdf: Path):
|
||||
result = parser.screenshot(invoice_pdf)
|
||||
assert len(result) > 0
|
||||
assert len(result.screenshots) > 0
|
||||
|
||||
def test_screenshot_result_fields(self, parser: LiteParse, invoice_pdf: Path):
|
||||
result = parser.screenshot(invoice_pdf)
|
||||
ss = result.screenshots[0]
|
||||
assert isinstance(ss, ScreenshotResult)
|
||||
assert isinstance(ss.page_num, int)
|
||||
assert ss.page_num >= 1
|
||||
assert isinstance(ss.image_path, str)
|
||||
assert Path(ss.image_path).exists()
|
||||
|
||||
def test_screenshot_output_dir(self, parser: LiteParse, invoice_pdf: Path):
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
result = parser.screenshot(invoice_pdf, output_dir=tmpdir)
|
||||
assert result.output_dir == tmpdir
|
||||
# Files should be in the specified directory
|
||||
for ss in result.screenshots:
|
||||
assert ss.image_path.startswith(tmpdir)
|
||||
|
||||
def test_screenshot_png_format(self, parser: LiteParse, invoice_pdf: Path):
|
||||
result = parser.screenshot(invoice_pdf, image_format=ImageFormat.PNG)
|
||||
for ss in result.screenshots:
|
||||
assert ss.image_path.endswith(".png")
|
||||
|
||||
def test_screenshot_jpg_format(self, parser: LiteParse, invoice_pdf: Path):
|
||||
result = parser.screenshot(invoice_pdf, image_format="jpg")
|
||||
for ss in result.screenshots:
|
||||
assert ss.image_path.endswith(".jpg")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_screensho_async_basic(self, parser: LiteParse, invoice_pdf: Path):
|
||||
result = await parser.screenshot_async(invoice_pdf, image_format="png")
|
||||
assert isinstance(result, ScreenshotBatchResult)
|
||||
assert len(result.screenshots) > 0
|
||||
|
||||
|
||||
class TestScreenshotOptions:
|
||||
"""Test screenshot options are forwarded correctly."""
|
||||
|
||||
def test_target_pages(self, parser: LiteParse, invoice_pdf: Path):
|
||||
# invoice.pdf has 2 pages — screenshot only page 1
|
||||
result = parser.screenshot(invoice_pdf, target_pages="1")
|
||||
assert len(result) == 1
|
||||
assert result.screenshots[0].page_num == 1
|
||||
|
||||
def test_load_bytes(self, parser: LiteParse, invoice_pdf: Path):
|
||||
result = parser.screenshot(invoice_pdf, load_bytes=True)
|
||||
for ss in result.screenshots:
|
||||
assert ss.image_bytes is not None
|
||||
assert len(ss.image_bytes) > 0
|
||||
|
||||
def test_no_load_bytes(self, parser: LiteParse, invoice_pdf: Path):
|
||||
result = parser.screenshot(invoice_pdf, load_bytes=False)
|
||||
for ss in result.screenshots:
|
||||
assert ss.image_bytes is None
|
||||
|
||||
def test_get_page_helper(self, parser: LiteParse, invoice_pdf: Path):
|
||||
result = parser.screenshot(invoice_pdf)
|
||||
page1 = result.get_page(1)
|
||||
assert page1 is not None
|
||||
assert page1.page_num == 1
|
||||
assert result.get_page(999) is None
|
||||
|
||||
def test_iterable(self, parser: LiteParse, invoice_pdf: Path):
|
||||
result = parser.screenshot(invoice_pdf)
|
||||
pages = list(result)
|
||||
assert len(pages) == len(result)
|
||||
|
||||
|
||||
class TestScreenshotErrors:
|
||||
"""Test error handling."""
|
||||
|
||||
def test_file_not_found(self, parser: LiteParse):
|
||||
with pytest.raises(FileNotFoundError):
|
||||
parser.screenshot("/nonexistent/file.pdf")
|
||||
Generated
+333
@@ -0,0 +1,333 @@
|
||||
version = 1
|
||||
revision = 3
|
||||
requires-python = ">=3.10"
|
||||
|
||||
[[package]]
|
||||
name = "backports-asyncio-runner"
|
||||
version = "1.2.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/8e/ff/70dca7d7cb1cbc0edb2c6cc0c38b65cba36cccc491eca64cabd5fe7f8670/backports_asyncio_runner-1.2.0.tar.gz", hash = "sha256:a5aa7b2b7d8f8bfcaa2b57313f70792df84e32a2a746f585213373f900b42162", size = 69893, upload-time = "2025-07-02T02:27:15.685Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/59/76ab57e3fe74484f48a53f8e337171b4a2349e506eabe136d7e01d059086/backports_asyncio_runner-1.2.0-py3-none-any.whl", hash = "sha256:0da0a936a8aeb554eccb426dc55af3ba63bcdc69fa1a600b5bb305413a4477b5", size = 12313, upload-time = "2025-07-02T02:27:14.263Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colorama"
|
||||
version = "0.4.6"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
|
||||
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" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "exceptiongroup"
|
||||
version = "1.3.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iniconfig"
|
||||
version = "2.3.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "librt"
|
||||
version = "0.8.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/56/9c/b4b0c54d84da4a94b37bd44151e46d5e583c9534c7e02250b961b1b6d8a8/librt-0.8.1.tar.gz", hash = "sha256:be46a14693955b3bd96014ccbdb8339ee8c9346fbe11c1b78901b55125f14c73", size = 177471, upload-time = "2026-02-17T16:13:06.101Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/7c/5f/63f5fa395c7a8a93558c0904ba8f1c8d1b997ca6a3de61bc7659970d66bf/librt-0.8.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:81fd938344fecb9373ba1b155968c8a329491d2ce38e7ddb76f30ffb938f12dc", size = 65697, upload-time = "2026-02-17T16:11:06.903Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ff/e0/0472cf37267b5920eff2f292ccfaede1886288ce35b7f3203d8de00abfe6/librt-0.8.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5db05697c82b3a2ec53f6e72b2ed373132b0c2e05135f0696784e97d7f5d48e7", size = 68376, upload-time = "2026-02-17T16:11:08.395Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c8/be/8bd1359fdcd27ab897cd5963294fa4a7c83b20a8564678e4fd12157e56a5/librt-0.8.1-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d56bc4011975f7460bea7b33e1ff425d2f1adf419935ff6707273c77f8a4ada6", size = 197084, upload-time = "2026-02-17T16:11:09.774Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e2/fe/163e33fdd091d0c2b102f8a60cc0a61fd730ad44e32617cd161e7cd67a01/librt-0.8.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cdc0f588ff4b663ea96c26d2a230c525c6fc62b28314edaaaca8ed5af931ad0", size = 207337, upload-time = "2026-02-17T16:11:11.311Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/01/99/f85130582f05dcf0c8902f3d629270231d2f4afdfc567f8305a952ac7f14/librt-0.8.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:97c2b54ff6717a7a563b72627990bec60d8029df17df423f0ed37d56a17a176b", size = 219980, upload-time = "2026-02-17T16:11:12.499Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6f/54/cb5e4d03659e043a26c74e08206412ac9a3742f0477d96f9761a55313b5f/librt-0.8.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8f1125e6bbf2f1657d9a2f3ccc4a2c9b0c8b176965bb565dd4d86be67eddb4b6", size = 212921, upload-time = "2026-02-17T16:11:14.484Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b1/81/a3a01e4240579c30f3487f6fed01eb4bc8ef0616da5b4ebac27ca19775f3/librt-0.8.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8f4bb453f408137d7581be309b2fbc6868a80e7ef60c88e689078ee3a296ae71", size = 221381, upload-time = "2026-02-17T16:11:17.459Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/08/b0/fc2d54b4b1c6fb81e77288ff31ff25a2c1e62eaef4424a984f228839717b/librt-0.8.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c336d61d2fe74a3195edc1646d53ff1cddd3a9600b09fa6ab75e5514ba4862a7", size = 216714, upload-time = "2026-02-17T16:11:19.197Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/96/96/85daa73ffbd87e1fb287d7af6553ada66bf25a2a6b0de4764344a05469f6/librt-0.8.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:eb5656019db7c4deacf0c1a55a898c5bb8f989be904597fcb5232a2f4828fa05", size = 214777, upload-time = "2026-02-17T16:11:20.443Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/12/9c/c3aa7a2360383f4bf4f04d98195f2739a579128720c603f4807f006a4225/librt-0.8.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c25d9e338d5bed46c1632f851babf3d13c78f49a225462017cf5e11e845c5891", size = 237398, upload-time = "2026-02-17T16:11:22.083Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/61/19/d350ea89e5274665185dabc4bbb9c3536c3411f862881d316c8b8e00eb66/librt-0.8.1-cp310-cp310-win32.whl", hash = "sha256:aaab0e307e344cb28d800957ef3ec16605146ef0e59e059a60a176d19543d1b7", size = 54285, upload-time = "2026-02-17T16:11:23.27Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4f/d6/45d587d3d41c112e9543a0093d883eb57a24a03e41561c127818aa2a6bcc/librt-0.8.1-cp310-cp310-win_amd64.whl", hash = "sha256:56e04c14b696300d47b3bc5f1d10a00e86ae978886d0cee14e5714fafb5df5d2", size = 61352, upload-time = "2026-02-17T16:11:24.207Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1d/01/0e748af5e4fee180cf7cd12bd12b0513ad23b045dccb2a83191bde82d168/librt-0.8.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:681dc2451d6d846794a828c16c22dc452d924e9f700a485b7ecb887a30aad1fd", size = 65315, upload-time = "2026-02-17T16:11:25.152Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9d/4d/7184806efda571887c798d573ca4134c80ac8642dcdd32f12c31b939c595/librt-0.8.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3b4350b13cc0e6f5bec8fa7caf29a8fb8cdc051a3bae45cfbfd7ce64f009965", size = 68021, upload-time = "2026-02-17T16:11:26.129Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ae/88/c3c52d2a5d5101f28d3dc89298444626e7874aa904eed498464c2af17627/librt-0.8.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ac1e7817fd0ed3d14fd7c5df91daed84c48e4c2a11ee99c0547f9f62fdae13da", size = 194500, upload-time = "2026-02-17T16:11:27.177Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d6/5d/6fb0a25b6a8906e85b2c3b87bee1d6ed31510be7605b06772f9374ca5cb3/librt-0.8.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:747328be0c5b7075cde86a0e09d7a9196029800ba75a1689332348e998fb85c0", size = 205622, upload-time = "2026-02-17T16:11:28.242Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b2/a6/8006ae81227105476a45691f5831499e4d936b1c049b0c1feb17c11b02d1/librt-0.8.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f0af2bd2bc204fa27f3d6711d0f360e6b8c684a035206257a81673ab924aa11e", size = 218304, upload-time = "2026-02-17T16:11:29.344Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ee/19/60e07886ad16670aae57ef44dada41912c90906a6fe9f2b9abac21374748/librt-0.8.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d480de377f5b687b6b1bc0c0407426da556e2a757633cc7e4d2e1a057aa688f3", size = 211493, upload-time = "2026-02-17T16:11:30.445Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9c/cf/f666c89d0e861d05600438213feeb818c7514d3315bae3648b1fc145d2b6/librt-0.8.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d0ee06b5b5291f609ddb37b9750985b27bc567791bc87c76a569b3feed8481ac", size = 219129, upload-time = "2026-02-17T16:11:32.021Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8f/ef/f1bea01e40b4a879364c031476c82a0dc69ce068daad67ab96302fed2d45/librt-0.8.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9e2c6f77b9ad48ce5603b83b7da9ee3e36b3ab425353f695cba13200c5d96596", size = 213113, upload-time = "2026-02-17T16:11:33.192Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9b/80/cdab544370cc6bc1b72ea369525f547a59e6938ef6863a11ab3cd24759af/librt-0.8.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:439352ba9373f11cb8e1933da194dcc6206daf779ff8df0ed69c5e39113e6a99", size = 212269, upload-time = "2026-02-17T16:11:34.373Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9d/9c/48d6ed8dac595654f15eceab2035131c136d1ae9a1e3548e777bb6dbb95d/librt-0.8.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:82210adabbc331dbb65d7868b105185464ef13f56f7f76688565ad79f648b0fe", size = 234673, upload-time = "2026-02-17T16:11:36.063Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/16/01/35b68b1db517f27a01be4467593292eb5315def8900afad29fabf56304ba/librt-0.8.1-cp311-cp311-win32.whl", hash = "sha256:52c224e14614b750c0a6d97368e16804a98c684657c7518752c356834fff83bb", size = 54597, upload-time = "2026-02-17T16:11:37.544Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/71/02/796fe8f02822235966693f257bf2c79f40e11337337a657a8cfebba5febc/librt-0.8.1-cp311-cp311-win_amd64.whl", hash = "sha256:c00e5c884f528c9932d278d5c9cbbea38a6b81eb62c02e06ae53751a83a4d52b", size = 61733, upload-time = "2026-02-17T16:11:38.691Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/28/ad/232e13d61f879a42a4e7117d65e4984bb28371a34bb6fb9ca54ec2c8f54e/librt-0.8.1-cp311-cp311-win_arm64.whl", hash = "sha256:f7cdf7f26c2286ffb02e46d7bac56c94655540b26347673bea15fa52a6af17e9", size = 52273, upload-time = "2026-02-17T16:11:40.308Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/95/21/d39b0a87ac52fc98f621fb6f8060efb017a767ebbbac2f99fbcbc9ddc0d7/librt-0.8.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a28f2612ab566b17f3698b0da021ff9960610301607c9a5e8eaca62f5e1c350a", size = 66516, upload-time = "2026-02-17T16:11:41.604Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/69/f1/46375e71441c43e8ae335905e069f1c54febee63a146278bcee8782c84fd/librt-0.8.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:60a78b694c9aee2a0f1aaeaa7d101cf713e92e8423a941d2897f4fa37908dab9", size = 68634, upload-time = "2026-02-17T16:11:43.268Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0a/33/c510de7f93bf1fa19e13423a606d8189a02624a800710f6e6a0a0f0784b3/librt-0.8.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:758509ea3f1eba2a57558e7e98f4659d0ea7670bff49673b0dde18a3c7e6c0eb", size = 198941, upload-time = "2026-02-17T16:11:44.28Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/dd/36/e725903416409a533d92398e88ce665476f275081d0d7d42f9c4951999e5/librt-0.8.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:039b9f2c506bd0ab0f8725aa5ba339c6f0cd19d3b514b50d134789809c24285d", size = 209991, upload-time = "2026-02-17T16:11:45.462Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/30/7a/8d908a152e1875c9f8eac96c97a480df425e657cdb47854b9efaa4998889/librt-0.8.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5bb54f1205a3a6ab41a6fd71dfcdcbd278670d3a90ca502a30d9da583105b6f7", size = 224476, upload-time = "2026-02-17T16:11:46.542Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a8/b8/a22c34f2c485b8903a06f3fe3315341fe6876ef3599792344669db98fcff/librt-0.8.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:05bd41cdee35b0c59c259f870f6da532a2c5ca57db95b5f23689fcb5c9e42440", size = 217518, upload-time = "2026-02-17T16:11:47.746Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/79/6f/5c6fea00357e4f82ba44f81dbfb027921f1ab10e320d4a64e1c408d035d9/librt-0.8.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:adfab487facf03f0d0857b8710cf82d0704a309d8ffc33b03d9302b4c64e91a9", size = 225116, upload-time = "2026-02-17T16:11:49.298Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f2/a0/95ced4e7b1267fe1e2720a111685bcddf0e781f7e9e0ce59d751c44dcfe5/librt-0.8.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:153188fe98a72f206042be10a2c6026139852805215ed9539186312d50a8e972", size = 217751, upload-time = "2026-02-17T16:11:50.49Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/93/c2/0517281cb4d4101c27ab59472924e67f55e375bc46bedae94ac6dc6e1902/librt-0.8.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:dd3c41254ee98604b08bd5b3af5bf0a89740d4ee0711de95b65166bf44091921", size = 218378, upload-time = "2026-02-17T16:11:51.783Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/43/e8/37b3ac108e8976888e559a7b227d0ceac03c384cfd3e7a1c2ee248dbae79/librt-0.8.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e0d138c7ae532908cbb342162b2611dbd4d90c941cd25ab82084aaf71d2c0bd0", size = 241199, upload-time = "2026-02-17T16:11:53.561Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4b/5b/35812d041c53967fedf551a39399271bbe4257e681236a2cf1a69c8e7fa1/librt-0.8.1-cp312-cp312-win32.whl", hash = "sha256:43353b943613c5d9c49a25aaffdba46f888ec354e71e3529a00cca3f04d66a7a", size = 54917, upload-time = "2026-02-17T16:11:54.758Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/de/d1/fa5d5331b862b9775aaf2a100f5ef86854e5d4407f71bddf102f4421e034/librt-0.8.1-cp312-cp312-win_amd64.whl", hash = "sha256:ff8baf1f8d3f4b6b7257fcb75a501f2a5499d0dda57645baa09d4d0d34b19444", size = 62017, upload-time = "2026-02-17T16:11:55.748Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c7/7c/c614252f9acda59b01a66e2ddfd243ed1c7e1deab0293332dfbccf862808/librt-0.8.1-cp312-cp312-win_arm64.whl", hash = "sha256:0f2ae3725904f7377e11cc37722d5d401e8b3d5851fb9273d7f4fe04f6b3d37d", size = 52441, upload-time = "2026-02-17T16:11:56.801Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c5/3c/f614c8e4eaac7cbf2bbdf9528790b21d89e277ee20d57dc6e559c626105f/librt-0.8.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7e6bad1cd94f6764e1e21950542f818a09316645337fd5ab9a7acc45d99a8f35", size = 66529, upload-time = "2026-02-17T16:11:57.809Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ab/96/5836544a45100ae411eda07d29e3d99448e5258b6e9c8059deb92945f5c2/librt-0.8.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cf450f498c30af55551ba4f66b9123b7185362ec8b625a773b3d39aa1a717583", size = 68669, upload-time = "2026-02-17T16:11:58.843Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/06/53/f0b992b57af6d5531bf4677d75c44f095f2366a1741fb695ee462ae04b05/librt-0.8.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:eca45e982fa074090057132e30585a7e8674e9e885d402eae85633e9f449ce6c", size = 199279, upload-time = "2026-02-17T16:11:59.862Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f3/ad/4848cc16e268d14280d8168aee4f31cea92bbd2b79ce33d3e166f2b4e4fc/librt-0.8.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c3811485fccfda840861905b8c70bba5ec094e02825598bb9d4ca3936857a04", size = 210288, upload-time = "2026-02-17T16:12:00.954Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/52/05/27fdc2e95de26273d83b96742d8d3b7345f2ea2bdbd2405cc504644f2096/librt-0.8.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e4af413908f77294605e28cfd98063f54b2c790561383971d2f52d113d9c363", size = 224809, upload-time = "2026-02-17T16:12:02.108Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7a/d0/78200a45ba3240cb042bc597d6f2accba9193a2c57d0356268cbbe2d0925/librt-0.8.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5212a5bd7fae98dae95710032902edcd2ec4dc994e883294f75c857b83f9aba0", size = 218075, upload-time = "2026-02-17T16:12:03.631Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/af/72/a210839fa74c90474897124c064ffca07f8d4b347b6574d309686aae7ca6/librt-0.8.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e692aa2d1d604e6ca12d35e51fdc36f4cda6345e28e36374579f7ef3611b3012", size = 225486, upload-time = "2026-02-17T16:12:04.725Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a3/c1/a03cc63722339ddbf087485f253493e2b013039f5b707e8e6016141130fa/librt-0.8.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4be2a5c926b9770c9e08e717f05737a269b9d0ebc5d2f0060f0fe3fe9ce47acb", size = 218219, upload-time = "2026-02-17T16:12:05.828Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/58/f5/fff6108af0acf941c6f274a946aea0e484bd10cd2dc37610287ce49388c5/librt-0.8.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:fd1a720332ea335ceb544cf0a03f81df92abd4bb887679fd1e460976b0e6214b", size = 218750, upload-time = "2026-02-17T16:12:07.09Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/71/67/5a387bfef30ec1e4b4f30562c8586566faf87e47d696768c19feb49e3646/librt-0.8.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2af9e01e0ef80d95ae3c720be101227edae5f2fe7e3dc63d8857fadfc5a1d", size = 241624, upload-time = "2026-02-17T16:12:08.43Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d4/be/24f8502db11d405232ac1162eb98069ca49c3306c1d75c6ccc61d9af8789/librt-0.8.1-cp313-cp313-win32.whl", hash = "sha256:086a32dbb71336627e78cc1d6ee305a68d038ef7d4c39aaff41ae8c9aa46e91a", size = 54969, upload-time = "2026-02-17T16:12:09.633Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5c/73/c9fdf6cb2a529c1a092ce769a12d88c8cca991194dfe641b6af12fa964d2/librt-0.8.1-cp313-cp313-win_amd64.whl", hash = "sha256:e11769a1dbda4da7b00a76cfffa67aa47cfa66921d2724539eee4b9ede780b79", size = 62000, upload-time = "2026-02-17T16:12:10.632Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d3/97/68f80ca3ac4924f250cdfa6e20142a803e5e50fca96ef5148c52ee8c10ea/librt-0.8.1-cp313-cp313-win_arm64.whl", hash = "sha256:924817ab3141aca17893386ee13261f1d100d1ef410d70afe4389f2359fea4f0", size = 52495, upload-time = "2026-02-17T16:12:11.633Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c9/6a/907ef6800f7bca71b525a05f1839b21f708c09043b1c6aa77b6b827b3996/librt-0.8.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:6cfa7fe54fd4d1f47130017351a959fe5804bda7a0bc7e07a2cdbc3fdd28d34f", size = 66081, upload-time = "2026-02-17T16:12:12.766Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1b/18/25e991cd5640c9fb0f8d91b18797b29066b792f17bf8493da183bf5caabe/librt-0.8.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:228c2409c079f8c11fb2e5d7b277077f694cb93443eb760e00b3b83cb8b3176c", size = 68309, upload-time = "2026-02-17T16:12:13.756Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a4/36/46820d03f058cfb5a9de5940640ba03165ed8aded69e0733c417bb04df34/librt-0.8.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7aae78ab5e3206181780e56912d1b9bb9f90a7249ce12f0e8bf531d0462dd0fc", size = 196804, upload-time = "2026-02-17T16:12:14.818Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/59/18/5dd0d3b87b8ff9c061849fbdb347758d1f724b9a82241aa908e0ec54ccd0/librt-0.8.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:172d57ec04346b047ca6af181e1ea4858086c80bdf455f61994c4aa6fc3f866c", size = 206907, upload-time = "2026-02-17T16:12:16.513Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/96/ef04902aad1424fd7299b62d1890e803e6ab4018c3044dca5922319c4b97/librt-0.8.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6b1977c4ea97ce5eb7755a78fae68d87e4102e4aaf54985e8b56806849cc06a3", size = 221217, upload-time = "2026-02-17T16:12:17.906Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6d/ff/7e01f2dda84a8f5d280637a2e5827210a8acca9a567a54507ef1c75b342d/librt-0.8.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:10c42e1f6fd06733ef65ae7bebce2872bcafd8d6e6b0a08fe0a05a23b044fb14", size = 214622, upload-time = "2026-02-17T16:12:19.108Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1e/8c/5b093d08a13946034fed57619742f790faf77058558b14ca36a6e331161e/librt-0.8.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4c8dfa264b9193c4ee19113c985c95f876fae5e51f731494fc4e0cf594990ba7", size = 221987, upload-time = "2026-02-17T16:12:20.331Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d3/cc/86b0b3b151d40920ad45a94ce0171dec1aebba8a9d72bb3fa00c73ab25dd/librt-0.8.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:01170b6729a438f0dedc4a26ed342e3dc4f02d1000b4b19f980e1877f0c297e6", size = 215132, upload-time = "2026-02-17T16:12:21.54Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fc/be/8588164a46edf1e69858d952654e216a9a91174688eeefb9efbb38a9c799/librt-0.8.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:7b02679a0d783bdae30d443025b94465d8c3dc512f32f5b5031f93f57ac32071", size = 215195, upload-time = "2026-02-17T16:12:23.073Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f5/f2/0b9279bea735c734d69344ecfe056c1ba211694a72df10f568745c899c76/librt-0.8.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:190b109bb69592a3401fe1ffdea41a2e73370ace2ffdc4a0e8e2b39cdea81b78", size = 237946, upload-time = "2026-02-17T16:12:24.275Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e9/cc/5f2a34fbc8aeb35314a3641f9956fa9051a947424652fad9882be7a97949/librt-0.8.1-cp314-cp314-win32.whl", hash = "sha256:e70a57ecf89a0f64c24e37f38d3fe217a58169d2fe6ed6d70554964042474023", size = 50689, upload-time = "2026-02-17T16:12:25.766Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/76/cd4d010ab2147339ca2b93e959c3686e964edc6de66ddacc935c325883d7/librt-0.8.1-cp314-cp314-win_amd64.whl", hash = "sha256:7e2f3edca35664499fbb36e4770650c4bd4a08abc1f4458eab9df4ec56389730", size = 57875, upload-time = "2026-02-17T16:12:27.465Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/84/0f/2143cb3c3ca48bd3379dcd11817163ca50781927c4537345d608b5045998/librt-0.8.1-cp314-cp314-win_arm64.whl", hash = "sha256:0d2f82168e55ddefd27c01c654ce52379c0750ddc31ee86b4b266bcf4d65f2a3", size = 48058, upload-time = "2026-02-17T16:12:28.556Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d2/0e/9b23a87e37baf00311c3efe6b48d6b6c168c29902dfc3f04c338372fd7db/librt-0.8.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2c74a2da57a094bd48d03fa5d196da83d2815678385d2978657499063709abe1", size = 68313, upload-time = "2026-02-17T16:12:29.659Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/db/9a/859c41e5a4f1c84200a7d2b92f586aa27133c8243b6cac9926f6e54d01b9/librt-0.8.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a355d99c4c0d8e5b770313b8b247411ed40949ca44e33e46a4789b9293a907ee", size = 70994, upload-time = "2026-02-17T16:12:31.516Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4c/28/10605366ee599ed34223ac2bf66404c6fb59399f47108215d16d5ad751a8/librt-0.8.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:2eb345e8b33fb748227409c9f1233d4df354d6e54091f0e8fc53acdb2ffedeb7", size = 220770, upload-time = "2026-02-17T16:12:33.294Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/af/8d/16ed8fd452dafae9c48d17a6bc1ee3e818fd40ef718d149a8eff2c9f4ea2/librt-0.8.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9be2f15e53ce4e83cc08adc29b26fb5978db62ef2a366fbdf716c8a6c8901040", size = 235409, upload-time = "2026-02-17T16:12:35.443Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/89/1b/7bdf3e49349c134b25db816e4a3db6b94a47ac69d7d46b1e682c2c4949be/librt-0.8.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:785ae29c1f5c6e7c2cde2c7c0e148147f4503da3abc5d44d482068da5322fd9e", size = 246473, upload-time = "2026-02-17T16:12:36.656Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4e/8a/91fab8e4fd2a24930a17188c7af5380eb27b203d72101c9cc000dbdfd95a/librt-0.8.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1d3a7da44baf692f0c6aeb5b2a09c5e6fc7a703bca9ffa337ddd2e2da53f7732", size = 238866, upload-time = "2026-02-17T16:12:37.849Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b9/e0/c45a098843fc7c07e18a7f8a24ca8496aecbf7bdcd54980c6ca1aaa79a8e/librt-0.8.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5fc48998000cbc39ec0d5311312dda93ecf92b39aaf184c5e817d5d440b29624", size = 250248, upload-time = "2026-02-17T16:12:39.445Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/82/30/07627de23036640c952cce0c1fe78972e77d7d2f8fd54fa5ef4554ff4a56/librt-0.8.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:e96baa6820280077a78244b2e06e416480ed859bbd8e5d641cf5742919d8beb4", size = 240629, upload-time = "2026-02-17T16:12:40.889Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fb/c1/55bfe1ee3542eba055616f9098eaf6eddb966efb0ca0f44eaa4aba327307/librt-0.8.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:31362dbfe297b23590530007062c32c6f6176f6099646bb2c95ab1b00a57c382", size = 239615, upload-time = "2026-02-17T16:12:42.446Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/39/191d3d28abc26c9099b19852e6c99f7f6d400b82fa5a4e80291bd3803e19/librt-0.8.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cc3656283d11540ab0ea01978378e73e10002145117055e03722417aeab30994", size = 263001, upload-time = "2026-02-17T16:12:43.627Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b9/eb/7697f60fbe7042ab4e88f4ee6af496b7f222fffb0a4e3593ef1f29f81652/librt-0.8.1-cp314-cp314t-win32.whl", hash = "sha256:738f08021b3142c2918c03692608baed43bc51144c29e35807682f8070ee2a3a", size = 51328, upload-time = "2026-02-17T16:12:45.148Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7c/72/34bf2eb7a15414a23e5e70ecb9440c1d3179f393d9349338a91e2781c0fb/librt-0.8.1-cp314-cp314t-win_amd64.whl", hash = "sha256:89815a22daf9c51884fb5dbe4f1ef65ee6a146e0b6a8df05f753e2e4a9359bf4", size = 58722, upload-time = "2026-02-17T16:12:46.85Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b2/c8/d148e041732d631fc76036f8b30fae4e77b027a1e95b7a84bb522481a940/librt-0.8.1-cp314-cp314t-win_arm64.whl", hash = "sha256:bf512a71a23504ed08103a13c941f763db13fb11177beb3d9244c98c29fb4a61", size = 48755, upload-time = "2026-02-17T16:12:47.943Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "liteparse"
|
||||
version = "2.0.5"
|
||||
source = { editable = "." }
|
||||
|
||||
[package.optional-dependencies]
|
||||
dev = [
|
||||
{ name = "mypy" },
|
||||
{ name = "pytest" },
|
||||
{ name = "pytest-asyncio" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "mypy", marker = "extra == 'dev'", specifier = ">=1.0" },
|
||||
{ name = "pytest", marker = "extra == 'dev'", specifier = ">=7.0" },
|
||||
{ name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.21" },
|
||||
]
|
||||
provides-extras = ["dev"]
|
||||
|
||||
[[package]]
|
||||
name = "mypy"
|
||||
version = "1.19.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "librt", marker = "platform_python_implementation != 'PyPy'" },
|
||||
{ name = "mypy-extensions" },
|
||||
{ name = "pathspec" },
|
||||
{ name = "tomli", marker = "python_full_version < '3.11'" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f5/db/4efed9504bc01309ab9c2da7e352cc223569f05478012b5d9ece38fd44d2/mypy-1.19.1.tar.gz", hash = "sha256:19d88bb05303fe63f71dd2c6270daca27cb9401c4ca8255fe50d1d920e0eb9ba", size = 3582404, upload-time = "2025-12-15T05:03:48.42Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/2f/63/e499890d8e39b1ff2df4c0c6ce5d371b6844ee22b8250687a99fd2f657a8/mypy-1.19.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f05aa3d375b385734388e844bc01733bd33c644ab48e9684faa54e5389775ec", size = 13101333, upload-time = "2025-12-15T05:03:03.28Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/72/4b/095626fc136fba96effc4fd4a82b41d688ab92124f8c4f7564bffe5cf1b0/mypy-1.19.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:022ea7279374af1a5d78dfcab853fe6a536eebfda4b59deab53cd21f6cd9f00b", size = 12164102, upload-time = "2025-12-15T05:02:33.611Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0c/5b/952928dd081bf88a83a5ccd49aaecfcd18fd0d2710c7ff07b8fb6f7032b9/mypy-1.19.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee4c11e460685c3e0c64a4c5de82ae143622410950d6be863303a1c4ba0e36d6", size = 12765799, upload-time = "2025-12-15T05:03:28.44Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2a/0d/93c2e4a287f74ef11a66fb6d49c7a9f05e47b0a4399040e6719b57f500d2/mypy-1.19.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de759aafbae8763283b2ee5869c7255391fbc4de3ff171f8f030b5ec48381b74", size = 13522149, upload-time = "2025-12-15T05:02:36.011Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7b/0e/33a294b56aaad2b338d203e3a1d8b453637ac36cb278b45005e0901cf148/mypy-1.19.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ab43590f9cd5108f41aacf9fca31841142c786827a74ab7cc8a2eacb634e09a1", size = 13810105, upload-time = "2025-12-15T05:02:40.327Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0e/fd/3e82603a0cb66b67c5e7abababce6bf1a929ddf67bf445e652684af5c5a0/mypy-1.19.1-cp310-cp310-win_amd64.whl", hash = "sha256:2899753e2f61e571b3971747e302d5f420c3fd09650e1951e99f823bc3089dac", size = 10057200, upload-time = "2025-12-15T05:02:51.012Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ef/47/6b3ebabd5474d9cdc170d1342fbf9dddc1b0ec13ec90bf9004ee6f391c31/mypy-1.19.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d8dfc6ab58ca7dda47d9237349157500468e404b17213d44fc1cb77bce532288", size = 13028539, upload-time = "2025-12-15T05:03:44.129Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5c/a6/ac7c7a88a3c9c54334f53a941b765e6ec6c4ebd65d3fe8cdcfbe0d0fd7db/mypy-1.19.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e3f276d8493c3c97930e354b2595a44a21348b320d859fb4a2b9f66da9ed27ab", size = 12083163, upload-time = "2025-12-15T05:03:37.679Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/67/af/3afa9cf880aa4a2c803798ac24f1d11ef72a0c8079689fac5cfd815e2830/mypy-1.19.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2abb24cf3f17864770d18d673c85235ba52456b36a06b6afc1e07c1fdcd3d0e6", size = 12687629, upload-time = "2025-12-15T05:02:31.526Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2d/46/20f8a7114a56484ab268b0ab372461cb3a8f7deed31ea96b83a4e4cfcfca/mypy-1.19.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a009ffa5a621762d0c926a078c2d639104becab69e79538a494bcccb62cc0331", size = 13436933, upload-time = "2025-12-15T05:03:15.606Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5b/f8/33b291ea85050a21f15da910002460f1f445f8007adb29230f0adea279cb/mypy-1.19.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f7cee03c9a2e2ee26ec07479f38ea9c884e301d42c6d43a19d20fb014e3ba925", size = 13661754, upload-time = "2025-12-15T05:02:26.731Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fd/a3/47cbd4e85bec4335a9cd80cf67dbc02be21b5d4c9c23ad6b95d6c5196bac/mypy-1.19.1-cp311-cp311-win_amd64.whl", hash = "sha256:4b84a7a18f41e167f7995200a1d07a4a6810e89d29859df936f1c3923d263042", size = 10055772, upload-time = "2025-12-15T05:03:26.179Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/06/8a/19bfae96f6615aa8a0604915512e0289b1fad33d5909bf7244f02935d33a/mypy-1.19.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a8174a03289288c1f6c46d55cef02379b478bfbc8e358e02047487cad44c6ca1", size = 13206053, upload-time = "2025-12-15T05:03:46.622Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a5/34/3e63879ab041602154ba2a9f99817bb0c85c4df19a23a1443c8986e4d565/mypy-1.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffcebe56eb09ff0c0885e750036a095e23793ba6c2e894e7e63f6d89ad51f22e", size = 12219134, upload-time = "2025-12-15T05:03:24.367Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/89/cc/2db6f0e95366b630364e09845672dbee0cbf0bbe753a204b29a944967cd9/mypy-1.19.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b64d987153888790bcdb03a6473d321820597ab8dd9243b27a92153c4fa50fd2", size = 12731616, upload-time = "2025-12-15T05:02:44.725Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/00/be/dd56c1fd4807bc1eba1cf18b2a850d0de7bacb55e158755eb79f77c41f8e/mypy-1.19.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c35d298c2c4bba75feb2195655dfea8124d855dfd7343bf8b8c055421eaf0cf8", size = 13620847, upload-time = "2025-12-15T05:03:39.633Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6d/42/332951aae42b79329f743bf1da088cd75d8d4d9acc18fbcbd84f26c1af4e/mypy-1.19.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:34c81968774648ab5ac09c29a375fdede03ba253f8f8287847bd480782f73a6a", size = 13834976, upload-time = "2025-12-15T05:03:08.786Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6f/63/e7493e5f90e1e085c562bb06e2eb32cae27c5057b9653348d38b47daaecc/mypy-1.19.1-cp312-cp312-win_amd64.whl", hash = "sha256:b10e7c2cd7870ba4ad9b2d8a6102eb5ffc1f16ca35e3de6bfa390c1113029d13", size = 10118104, upload-time = "2025-12-15T05:03:10.834Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/de/9f/a6abae693f7a0c697dbb435aac52e958dc8da44e92e08ba88d2e42326176/mypy-1.19.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e3157c7594ff2ef1634ee058aafc56a82db665c9438fd41b390f3bde1ab12250", size = 13201927, upload-time = "2025-12-15T05:02:29.138Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9a/a4/45c35ccf6e1c65afc23a069f50e2c66f46bd3798cbe0d680c12d12935caa/mypy-1.19.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdb12f69bcc02700c2b47e070238f42cb87f18c0bc1fc4cdb4fb2bc5fd7a3b8b", size = 12206730, upload-time = "2025-12-15T05:03:01.325Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/05/bb/cdcf89678e26b187650512620eec8368fded4cfd99cfcb431e4cdfd19dec/mypy-1.19.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f859fb09d9583a985be9a493d5cfc5515b56b08f7447759a0c5deaf68d80506e", size = 12724581, upload-time = "2025-12-15T05:03:20.087Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/32/dd260d52babf67bad8e6770f8e1102021877ce0edea106e72df5626bb0ec/mypy-1.19.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9a6538e0415310aad77cb94004ca6482330fece18036b5f360b62c45814c4ef", size = 13616252, upload-time = "2025-12-15T05:02:49.036Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/71/d0/5e60a9d2e3bd48432ae2b454b7ef2b62a960ab51292b1eda2a95edd78198/mypy-1.19.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:da4869fc5e7f62a88f3fe0b5c919d1d9f7ea3cef92d3689de2823fd27e40aa75", size = 13840848, upload-time = "2025-12-15T05:02:55.95Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/98/76/d32051fa65ecf6cc8c6610956473abdc9b4c43301107476ac03559507843/mypy-1.19.1-cp313-cp313-win_amd64.whl", hash = "sha256:016f2246209095e8eda7538944daa1d60e1e8134d98983b9fc1e92c1fc0cb8dd", size = 10135510, upload-time = "2025-12-15T05:02:58.438Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/de/eb/b83e75f4c820c4247a58580ef86fcd35165028f191e7e1ba57128c52782d/mypy-1.19.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06e6170bd5836770e8104c8fdd58e5e725cfeb309f0a6c681a811f557e97eac1", size = 13199744, upload-time = "2025-12-15T05:03:30.823Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/94/28/52785ab7bfa165f87fcbb61547a93f98bb20e7f82f90f165a1f69bce7b3d/mypy-1.19.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:804bd67b8054a85447c8954215a906d6eff9cabeabe493fb6334b24f4bfff718", size = 12215815, upload-time = "2025-12-15T05:02:42.323Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0a/c6/bdd60774a0dbfb05122e3e925f2e9e846c009e479dcec4821dad881f5b52/mypy-1.19.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21761006a7f497cb0d4de3d8ef4ca70532256688b0523eee02baf9eec895e27b", size = 12740047, upload-time = "2025-12-15T05:03:33.168Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/32/2a/66ba933fe6c76bd40d1fe916a83f04fed253152f451a877520b3c4a5e41e/mypy-1.19.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:28902ee51f12e0f19e1e16fbe2f8f06b6637f482c459dd393efddd0ec7f82045", size = 13601998, upload-time = "2025-12-15T05:03:13.056Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e3/da/5055c63e377c5c2418760411fd6a63ee2b96cf95397259038756c042574f/mypy-1.19.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:481daf36a4c443332e2ae9c137dfee878fcea781a2e3f895d54bd3002a900957", size = 13807476, upload-time = "2025-12-15T05:03:17.977Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cd/09/4ebd873390a063176f06b0dbf1f7783dd87bd120eae7727fa4ae4179b685/mypy-1.19.1-cp314-cp314-win_amd64.whl", hash = "sha256:8bb5c6f6d043655e055be9b542aa5f3bdd30e4f3589163e85f93f3640060509f", size = 10281872, upload-time = "2025-12-15T05:03:05.549Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8d/f4/4ce9a05ce5ded1de3ec1c1d96cf9f9504a04e54ce0ed55cfa38619a32b8d/mypy-1.19.1-py3-none-any.whl", hash = "sha256:f1235f5ea01b7db5468d53ece6aaddf1ad0b88d9e7462b86ef96fe04995d7247", size = 2471239, upload-time = "2025-12-15T05:03:07.248Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mypy-extensions"
|
||||
version = "1.1.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" }
|
||||
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" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "26.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pathspec"
|
||||
version = "1.0.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/fa/36/e27608899f9b8d4dff0617b2d9ab17ca5608956ca44461ac14ac48b44015/pathspec-1.0.4.tar.gz", hash = "sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645", size = 131200, upload-time = "2026-01-27T03:59:46.938Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723", size = 55206, upload-time = "2026-01-27T03:59:45.137Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pluggy"
|
||||
version = "1.6.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" }
|
||||
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" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pygments"
|
||||
version = "2.20.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pytest"
|
||||
version = "9.0.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
||||
{ name = "exceptiongroup", marker = "python_full_version < '3.11'" },
|
||||
{ name = "iniconfig" },
|
||||
{ name = "packaging" },
|
||||
{ name = "pluggy" },
|
||||
{ name = "pygments" },
|
||||
{ name = "tomli", marker = "python_full_version < '3.11'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165, upload-time = "2026-04-07T17:16:18.027Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pytest-asyncio"
|
||||
version = "1.3.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "backports-asyncio-runner", marker = "python_full_version < '3.11'" },
|
||||
{ name = "pytest" },
|
||||
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/90/2c/8af215c0f776415f3590cac4f9086ccefd6fd463befeae41cd4d3f193e5a/pytest_asyncio-1.3.0.tar.gz", hash = "sha256:d7f52f36d231b80ee124cd216ffb19369aa168fc10095013c6b014a34d3ee9e5", size = 50087, upload-time = "2025-11-10T16:07:47.256Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/e5/35/f8b19922b6a25bc0880171a2f1a003eaeb93657475193ab516fd87cac9da/pytest_asyncio-1.3.0-py3-none-any.whl", hash = "sha256:611e26147c7f77640e6d0a92a38ed17c3e9848063698d5c93d5aa7aa11cebff5", size = 15075, upload-time = "2025-11-10T16:07:45.537Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tomli"
|
||||
version = "2.4.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/82/30/31573e9457673ab10aa432461bee537ce6cef177667deca369efb79df071/tomli-2.4.0.tar.gz", hash = "sha256:aa89c3f6c277dd275d8e243ad24f3b5e701491a860d5121f2cdd399fbb31fc9c", size = 17477, upload-time = "2026-01-11T11:22:38.165Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/3c/d9/3dc2289e1f3b32eb19b9785b6a006b28ee99acb37d1d47f78d4c10e28bf8/tomli-2.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b5ef256a3fd497d4973c11bf142e9ed78b150d36f5773f1ca6088c230ffc5867", size = 153663, upload-time = "2026-01-11T11:21:45.27Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/51/32/ef9f6845e6b9ca392cd3f64f9ec185cc6f09f0a2df3db08cbe8809d1d435/tomli-2.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5572e41282d5268eb09a697c89a7bee84fae66511f87533a6f88bd2f7b652da9", size = 148469, upload-time = "2026-01-11T11:21:46.873Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d6/c2/506e44cce89a8b1b1e047d64bd495c22c9f71f21e05f380f1a950dd9c217/tomli-2.4.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:551e321c6ba03b55676970b47cb1b73f14a0a4dce6a3e1a9458fd6d921d72e95", size = 236039, upload-time = "2026-01-11T11:21:48.503Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b3/40/e1b65986dbc861b7e986e8ec394598187fa8aee85b1650b01dd925ca0be8/tomli-2.4.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e3f639a7a8f10069d0e15408c0b96a2a828cfdec6fca05296ebcdcc28ca7c76", size = 243007, upload-time = "2026-01-11T11:21:49.456Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9c/6f/6e39ce66b58a5b7ae572a0f4352ff40c71e8573633deda43f6a379d56b3e/tomli-2.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1b168f2731796b045128c45982d3a4874057626da0e2ef1fdd722848b741361d", size = 240875, upload-time = "2026-01-11T11:21:50.755Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/aa/ad/cb089cb190487caa80204d503c7fd0f4d443f90b95cf4ef5cf5aa0f439b0/tomli-2.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:133e93646ec4300d651839d382d63edff11d8978be23da4cc106f5a18b7d0576", size = 246271, upload-time = "2026-01-11T11:21:51.81Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0b/63/69125220e47fd7a3a27fd0de0c6398c89432fec41bc739823bcc66506af6/tomli-2.4.0-cp311-cp311-win32.whl", hash = "sha256:b6c78bdf37764092d369722d9946cb65b8767bfa4110f902a1b2542d8d173c8a", size = 96770, upload-time = "2026-01-11T11:21:52.647Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1e/0d/a22bb6c83f83386b0008425a6cd1fa1c14b5f3dd4bad05e98cf3dbbf4a64/tomli-2.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:d3d1654e11d724760cdb37a3d7691f0be9db5fbdaef59c9f532aabf87006dbaa", size = 107626, upload-time = "2026-01-11T11:21:53.459Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2f/6d/77be674a3485e75cacbf2ddba2b146911477bd887dda9d8c9dfb2f15e871/tomli-2.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:cae9c19ed12d4e8f3ebf46d1a75090e4c0dc16271c5bce1c833ac168f08fb614", size = 94842, upload-time = "2026-01-11T11:21:54.831Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3c/43/7389a1869f2f26dba52404e1ef13b4784b6b37dac93bac53457e3ff24ca3/tomli-2.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:920b1de295e72887bafa3ad9f7a792f811847d57ea6b1215154030cf131f16b1", size = 154894, upload-time = "2026-01-11T11:21:56.07Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e9/05/2f9bf110b5294132b2edf13fe6ca6ae456204f3d749f623307cbb7a946f2/tomli-2.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d6d9a4aee98fac3eab4952ad1d73aee87359452d1c086b5ceb43ed02ddb16b8", size = 149053, upload-time = "2026-01-11T11:21:57.467Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e8/41/1eda3ca1abc6f6154a8db4d714a4d35c4ad90adc0bcf700657291593fbf3/tomli-2.4.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36b9d05b51e65b254ea6c2585b59d2c4cb91c8a3d91d0ed0f17591a29aaea54a", size = 243481, upload-time = "2026-01-11T11:21:58.661Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d2/6d/02ff5ab6c8868b41e7d4b987ce2b5f6a51d3335a70aa144edd999e055a01/tomli-2.4.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c8a885b370751837c029ef9bc014f27d80840e48bac415f3412e6593bbc18c1", size = 251720, upload-time = "2026-01-11T11:22:00.178Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7b/57/0405c59a909c45d5b6f146107c6d997825aa87568b042042f7a9c0afed34/tomli-2.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8768715ffc41f0008abe25d808c20c3d990f42b6e2e58305d5da280ae7d1fa3b", size = 247014, upload-time = "2026-01-11T11:22:01.238Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/0e/2e37568edd944b4165735687cbaf2fe3648129e440c26d02223672ee0630/tomli-2.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b438885858efd5be02a9a133caf5812b8776ee0c969fea02c45e8e3f296ba51", size = 251820, upload-time = "2026-01-11T11:22:02.727Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5a/1c/ee3b707fdac82aeeb92d1a113f803cf6d0f37bdca0849cb489553e1f417a/tomli-2.4.0-cp312-cp312-win32.whl", hash = "sha256:0408e3de5ec77cc7f81960c362543cbbd91ef883e3138e81b729fc3eea5b9729", size = 97712, upload-time = "2026-01-11T11:22:03.777Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/69/13/c07a9177d0b3bab7913299b9278845fc6eaaca14a02667c6be0b0a2270c8/tomli-2.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:685306e2cc7da35be4ee914fd34ab801a6acacb061b6a7abca922aaf9ad368da", size = 108296, upload-time = "2026-01-11T11:22:04.86Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/18/27/e267a60bbeeee343bcc279bb9e8fbed0cbe224bc7b2a3dc2975f22809a09/tomli-2.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:5aa48d7c2356055feef06a43611fc401a07337d5b006be13a30f6c58f869e3c3", size = 94553, upload-time = "2026-01-11T11:22:05.854Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/34/91/7f65f9809f2936e1f4ce6268ae1903074563603b2a2bd969ebbda802744f/tomli-2.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84d081fbc252d1b6a982e1870660e7330fb8f90f676f6e78b052ad4e64714bf0", size = 154915, upload-time = "2026-01-11T11:22:06.703Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/20/aa/64dd73a5a849c2e8f216b755599c511badde80e91e9bc2271baa7b2cdbb1/tomli-2.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9a08144fa4cba33db5255f9b74f0b89888622109bd2776148f2597447f92a94e", size = 149038, upload-time = "2026-01-11T11:22:07.56Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9e/8a/6d38870bd3d52c8d1505ce054469a73f73a0fe62c0eaf5dddf61447e32fa/tomli-2.4.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c73add4bb52a206fd0c0723432db123c0c75c280cbd67174dd9d2db228ebb1b4", size = 242245, upload-time = "2026-01-11T11:22:08.344Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/59/bb/8002fadefb64ab2669e5b977df3f5e444febea60e717e755b38bb7c41029/tomli-2.4.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fb2945cbe303b1419e2706e711b7113da57b7db31ee378d08712d678a34e51e", size = 250335, upload-time = "2026-01-11T11:22:09.951Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a5/3d/4cdb6f791682b2ea916af2de96121b3cb1284d7c203d97d92d6003e91c8d/tomli-2.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bbb1b10aa643d973366dc2cb1ad94f99c1726a02343d43cbc011edbfac579e7c", size = 245962, upload-time = "2026-01-11T11:22:11.27Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f2/4a/5f25789f9a460bd858ba9756ff52d0830d825b458e13f754952dd15fb7bb/tomli-2.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4cbcb367d44a1f0c2be408758b43e1ffb5308abe0ea222897d6bfc8e8281ef2f", size = 250396, upload-time = "2026-01-11T11:22:12.325Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/aa/2f/b73a36fea58dfa08e8b3a268750e6853a6aac2a349241a905ebd86f3047a/tomli-2.4.0-cp313-cp313-win32.whl", hash = "sha256:7d49c66a7d5e56ac959cb6fc583aff0651094ec071ba9ad43df785abc2320d86", size = 97530, upload-time = "2026-01-11T11:22:13.865Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3b/af/ca18c134b5d75de7e8dc551c5234eaba2e8e951f6b30139599b53de9c187/tomli-2.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:3cf226acb51d8f1c394c1b310e0e0e61fecdd7adcb78d01e294ac297dd2e7f87", size = 108227, upload-time = "2026-01-11T11:22:15.224Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/22/c3/b386b832f209fee8073c8138ec50f27b4460db2fdae9ffe022df89a57f9b/tomli-2.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:d20b797a5c1ad80c516e41bc1fb0443ddb5006e9aaa7bda2d71978346aeb9132", size = 94748, upload-time = "2026-01-11T11:22:16.009Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f3/c4/84047a97eb1004418bc10bdbcfebda209fca6338002eba2dc27cc6d13563/tomli-2.4.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:26ab906a1eb794cd4e103691daa23d95c6919cc2fa9160000ac02370cc9dd3f6", size = 154725, upload-time = "2026-01-11T11:22:17.269Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a8/5d/d39038e646060b9d76274078cddf146ced86dc2b9e8bbf737ad5983609a0/tomli-2.4.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:20cedb4ee43278bc4f2fee6cb50daec836959aadaf948db5172e776dd3d993fc", size = 148901, upload-time = "2026-01-11T11:22:18.287Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/73/e5/383be1724cb30f4ce44983d249645684a48c435e1cd4f8b5cded8a816d3c/tomli-2.4.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:39b0b5d1b6dd03684b3fb276407ebed7090bbec989fa55838c98560c01113b66", size = 243375, upload-time = "2026-01-11T11:22:19.154Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/31/f0/bea80c17971c8d16d3cc109dc3585b0f2ce1036b5f4a8a183789023574f2/tomli-2.4.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a26d7ff68dfdb9f87a016ecfd1e1c2bacbe3108f4e0f8bcd2228ef9a766c787d", size = 250639, upload-time = "2026-01-11T11:22:20.168Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/8f/2853c36abbb7608e3f945d8a74e32ed3a74ee3a1f468f1ffc7d1cb3abba6/tomli-2.4.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:20ffd184fb1df76a66e34bd1b36b4a4641bd2b82954befa32fe8163e79f1a702", size = 246897, upload-time = "2026-01-11T11:22:21.544Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/49/f0/6c05e3196ed5337b9fe7ea003e95fd3819a840b7a0f2bf5a408ef1dad8ed/tomli-2.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:75c2f8bbddf170e8effc98f5e9084a8751f8174ea6ccf4fca5398436e0320bc8", size = 254697, upload-time = "2026-01-11T11:22:23.058Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f3/f5/2922ef29c9f2951883525def7429967fc4d8208494e5ab524234f06b688b/tomli-2.4.0-cp314-cp314-win32.whl", hash = "sha256:31d556d079d72db7c584c0627ff3a24c5d3fb4f730221d3444f3efb1b2514776", size = 98567, upload-time = "2026-01-11T11:22:24.033Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7b/31/22b52e2e06dd2a5fdbc3ee73226d763b184ff21fc24e20316a44ccc4d96b/tomli-2.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:43e685b9b2341681907759cf3a04e14d7104b3580f808cfde1dfdb60ada85475", size = 108556, upload-time = "2026-01-11T11:22:25.378Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/48/3d/5058dff3255a3d01b705413f64f4306a141a8fd7a251e5a495e3f192a998/tomli-2.4.0-cp314-cp314-win_arm64.whl", hash = "sha256:3d895d56bd3f82ddd6faaff993c275efc2ff38e52322ea264122d72729dca2b2", size = 96014, upload-time = "2026-01-11T11:22:26.138Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b8/4e/75dab8586e268424202d3a1997ef6014919c941b50642a1682df43204c22/tomli-2.4.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:5b5807f3999fb66776dbce568cc9a828544244a8eb84b84b9bafc080c99597b9", size = 163339, upload-time = "2026-01-11T11:22:27.143Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/06/e3/b904d9ab1016829a776d97f163f183a48be6a4deb87304d1e0116a349519/tomli-2.4.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c084ad935abe686bd9c898e62a02a19abfc9760b5a79bc29644463eaf2840cb0", size = 159490, upload-time = "2026-01-11T11:22:28.399Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e3/5a/fc3622c8b1ad823e8ea98a35e3c632ee316d48f66f80f9708ceb4f2a0322/tomli-2.4.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f2e3955efea4d1cfbcb87bc321e00dc08d2bcb737fd1d5e398af111d86db5df", size = 269398, upload-time = "2026-01-11T11:22:29.345Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fd/33/62bd6152c8bdd4c305ad9faca48f51d3acb2df1f8791b1477d46ff86e7f8/tomli-2.4.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e0fe8a0b8312acf3a88077a0802565cb09ee34107813bba1c7cd591fa6cfc8d", size = 276515, upload-time = "2026-01-11T11:22:30.327Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4b/ff/ae53619499f5235ee4211e62a8d7982ba9e439a0fb4f2f351a93d67c1dd2/tomli-2.4.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:413540dce94673591859c4c6f794dfeaa845e98bf35d72ed59636f869ef9f86f", size = 273806, upload-time = "2026-01-11T11:22:32.56Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/47/71/cbca7787fa68d4d0a9f7072821980b39fbb1b6faeb5f5cf02f4a5559fa28/tomli-2.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0dc56fef0e2c1c470aeac5b6ca8cc7b640bb93e92d9803ddaf9ea03e198f5b0b", size = 281340, upload-time = "2026-01-11T11:22:33.505Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f5/00/d595c120963ad42474cf6ee7771ad0d0e8a49d0f01e29576ee9195d9ecdf/tomli-2.4.0-cp314-cp314t-win32.whl", hash = "sha256:d878f2a6707cc9d53a1be1414bbb419e629c3d6e67f69230217bb663e76b5087", size = 108106, upload-time = "2026-01-11T11:22:34.451Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/de/69/9aa0c6a505c2f80e519b43764f8b4ba93b5a0bbd2d9a9de6e2b24271b9a5/tomli-2.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2add28aacc7425117ff6364fe9e06a183bb0251b03f986df0e78e974047571fd", size = 120504, upload-time = "2026-01-11T11:22:35.764Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b3/9f/f1668c281c58cfae01482f7114a4b88d345e4c140386241a1a24dcc9e7bc/tomli-2.4.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2b1e3b80e1d5e52e40e9b924ec43d81570f0e7d09d11081b797bc4692765a3d4", size = 99561, upload-time = "2026-01-11T11:22:36.624Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/23/d1/136eb2cb77520a31e1f64cbae9d33ec6df0d78bdf4160398e86eec8a8754/tomli-2.4.0-py3-none-any.whl", hash = "sha256:1f776e7d669ebceb01dee46484485f43a4048746235e683bcdffacdf1fb4785a", size = 14477, upload-time = "2026-01-11T11:22:37.446Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.15.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" },
|
||||
]
|
||||
Reference in New Issue
Block a user