From a4b8c54766d8a516e4b62463b3bde1b3bc83e557 Mon Sep 17 00:00:00 2001 From: harshmishra2701 Date: Wed, 11 Feb 2026 16:27:29 +0530 Subject: [PATCH 01/18] [RTY-260011]: git commit -m "feat(cache): add in-memory caching with TTL and documentation" --- .vscode/settings.json | 42 ++-- README.md | 268 +++++++++++++------------- app/api/fast_api.py | 33 ++-- app/main.py | 97 ++++++---- app/static/qr/zSDrr0.png | Bin 41041 -> 0 bytes app/utils/cache.py | 52 +++++ docs/cache.md | 200 +++++++++++++++++++ poetry.lock | 405 +++++++++++++++++++++++++-------------- requirements.txt | 6 +- 9 files changed, 740 insertions(+), 363 deletions(-) delete mode 100644 app/static/qr/zSDrr0.png create mode 100644 app/utils/cache.py create mode 100644 docs/cache.md diff --git a/.vscode/settings.json b/.vscode/settings.json index fda4174..c6b17fc 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,24 +1,22 @@ { - "workbench.colorCustomizations": { - "activityBar.activeBackground": "#def6c4", - "activityBar.background": "#def6c4", - "activityBar.foreground": "#15202b", - "activityBar.inactiveForeground": "#15202b99", - "activityBarBadge.background": "#59a2e6", - "activityBarBadge.foreground": "#15202b", - "commandCenter.border": "#15202b99", - "sash.hoverBorder": "#def6c4", - "statusBar.background": "#c5ef98", - "statusBar.foreground": "#15202b", - "statusBarItem.hoverBackground": "#ace86c", - "statusBarItem.remoteBackground": "#c5ef98", - "statusBarItem.remoteForeground": "#15202b", - "titleBar.activeBackground": "#c5ef98", - "titleBar.activeForeground": "#15202b", - "titleBar.inactiveBackground": "#c5ef9899", - "titleBar.inactiveForeground": "#15202b99", - "sideBar.border": "#def6c4", - "tab.activeBorder": "#def6c4" - }, - "peacock.color": "#c5ef98" + "workbench.colorCustomizations": { + "activityBar.activeBackground": "#def6c4", + "activityBar.background": "#def6c4", + "activityBar.foreground": "#15202b", + "activityBar.inactiveForeground": "#15202b99", + "activityBarBadge.background": "#59a2e6", + "activityBarBadge.foreground": "#15202b", + "commandCenter.border": "#15202b99", + "sash.hoverBorder": "#def6c4", + "statusBar.background": "#c5ef98", + "statusBar.foreground": "#15202b", + "statusBarItem.hoverBackground": "#ace86c", + "statusBarItem.remoteBackground": "#c5ef98", + "statusBarItem.remoteForeground": "#15202b", + "titleBar.activeBackground": "#c5ef98", + "titleBar.activeForeground": "#15202b", + "titleBar.inactiveBackground": "#c5ef9899", + "titleBar.inactiveForeground": "#15202b99" + }, + "peacock.color": "#c5ef98" } \ No newline at end of file diff --git a/README.md b/README.md index bc8fe43..276e6fd 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,10 @@ # Tiny URL Generator -> A modern, Bitly-style tiny URL web application built with FastAPI & MongoDB +> A modern, Bitly-style tiny URL web application built with FastAPI, optional MongoDB, and a sleek web UI. -![Python](https://img.shields.io/badge/Python-3.10-blue.svg) -![MongoDB](https://img.shields.io/badge/Database-MongoDB-green.svg) +![Python](https://img.shields.io/badge/Python-3.11-blue.svg) +![FastAPI](https://img.shields.io/badge/FastAPI-Backend-teal.svg) +![MongoDB]() ![License](https://img.shields.io/badge/License-MIT-yellow.svg) ![Status](https://img.shields.io/badge/Status-Active-success.svg) @@ -11,12 +12,22 @@ ## Overview -**tiny URL** is a sleek, fast, and modern URL shortening platform built using **FastAPI**, and **MongoDB**. +Tiny URL is a sleek, fast, and modern URL shortening platform built using FastAPI with optional MongoDB persistence. It converts long URLs into short, shareable links β€” just like Bitly. The project supports: -- πŸš€ **FastAPI REST API** (developers / integrations) +- Web UI (FastAPI + Jinja templates) +- REST API (FastAPI) +- Offline Mode (No MongoDB required) + +This project is designed with: + +- Clean startup lifecycle (no racing configs) +- Optional database dependency +- Graceful degradation when MongoDB is unavailable +- In-memory cache fallback +- QR code generation with auto folder creation --- @@ -29,13 +40,12 @@ The project supports: - Clean Bitly-style result card - Copy & share buttons - Download URL button -- Share URL -- Copy button with animation -- Smooth URL validation and sanitization -- Auto Dark/Light Mode (saves preference) -- Mobile-friendly QR Codes -- Fully responsive design -- Recent URLs page +- URL validation and sanitization +- Fully responsive UI +- Recent URLs page (when DB is available) +- Visit count tracking (when DB is available) +- QR image auto-generation with logo +- Cache-accelerated redirects ### API & Developer Features @@ -74,27 +84,20 @@ def generate_code(length=6): --- -| Layer | Technology | -| ----------- | --------------------- | -| API Backend | FastAPI | -| Database | MongoDB | -| Frontend | HTML, CSS, Vanilla JS | -| API Server | Uvicorn | -| Validation | Pydantic v2 | -| CLI | Click | -| Data | JSON | - -| Layer | Technology | -| ----------- | --------------------- | -| UI Backend | FastAPI | -| API Backend | FastAPI | -| Database | MongoDB (Optional) | -| Frontend | HTML, CSS, Vanilla JS | -| QR Code | qrcode + Pillow | -| API Server | Uvicorn | -| Validation | Pydantic v2 | -| Env Mgmt | python-dotenv | -| Tooling | Poetry | +## Tech Stack + +| Layer | Technology | +| ----------- | ----------------------- | +| UI Backend | FastAPI | +| API Backend | FastAPI | +| Database | MongoDB (Optional) | +| Cache | In-Memory (Python dict) | +| Frontend | HTML, CSS, Vanilla JS | +| QR Code | qrcode + Pillow | +| API Server | Uvicorn | +| Validation | Pydantic v2 | +| Env Mgmt | python-dotenv | +| Tooling | Poetry | --- @@ -105,40 +108,37 @@ Directory structure: tiny/ β”œβ”€β”€ CHANGELOG.md β”œβ”€β”€ LICENSE +β”œβ”€β”€ README.md β”œβ”€β”€ app/ β”‚ β”œβ”€β”€__init__.py β”‚ β”œβ”€β”€ main.py β”‚ β”œβ”€β”€ cli.py β”‚ β”œβ”€β”€ api/ -| | └──__init__.py β”‚ β”‚ └── fast_api.py | β”œβ”€β”€assets/images β”‚ β”œβ”€β”€ db/ | | └──__init__.py | | └──data.py -| β”œβ”€β”€ static/ +β”‚ β”œβ”€β”€ static/ | | └── images -| | └── qr -| | └── style.css -| β”œβ”€β”€ templates/ +| | └── qr +| | └──style.css +| └── templates/ | | └── index.html | | └── recent.html +| | └── coming-soon.html β”‚ β”œβ”€β”€ utils/ -| | └── __init__.py -| | └── _version.py +| | └──__init__.py +| | └──_version.py +| | └── cache.py | | └── config.py | | └── helper.py | | └── lint.py | | └── qr.py -| β”œβ”€β”€ docs/ | └── build-test.md -| └─ run_with_curl.md -| -β”œβ”€β”€ request/ -| └── mixed.json -| └── single.json -| └── urls.json +| └── cache.md +| └── run_with_curl.md β”œβ”€β”€ pyproject.toml | └── poetry.lock β”œβ”€β”€README.md @@ -149,55 +149,59 @@ tiny/ ``` -βš™οΈ How to Run the Project Locally +## βš™οΈ How to Run the Project Locally -## How to start +`Virtual Environment Configuration` -```sh -poetry install +```bash +poetry config virtualenvs.path /your/desired/path ``` -### 3. Install with other dependent packages +## Environment Configuration -```sh -poetry install --all-extras --with dev -``` +Ensure below files are configured (create if not exist) properly to run the project; ---- +Supported env files: -## Running the App +- .env.development +- .env.local +- .env (production) -```sh -poetry run uvicorn app.main:app --reload +``` +ENV=development +DOMAIN=http://127.0.0.1:8000 +MONGO_URI=mongodb://:@localhost:27017/tiny_url?authSource=tiny_url +DATABASE_NAME=tiny_url ``` -or +## Install Dependencies -```sh -poetry run tiny dev +```bash +poetry lock --no-cache --regenerate +poetry install --all-extras --with dev ``` -Open: - +Or manually ---- +```bash +poetry install +``` -## Environment Configuration +## How to Run -``` -ENV=development -DOMAIN=http://127.0.0.1:8000 -MONGO_URI=mongodb://:@localhost:27017/tiny_url?authSource=tiny_url -DATABASE_NAME=tiny_url +```bash +poetry run tiny dev ``` -Supported env files: +Access: -- .env.development -- .env.local -- .env (production) +## Run FastAPI Server ---- +```bash +poetry run tiny api +``` + +Access: ## Offline Mode (No Database) @@ -209,6 +213,7 @@ TinyURL supports graceful offline mode. - UI loads - Short URLs are generated - QR codes are generated +- Redirects work from in-memory cache ### What is disabled @@ -232,14 +237,6 @@ Log message: ## Switching Modes -### With MongoDB - -```sh -poetry install --with mongodb -sudo systemctl start mongod -poetry run tiny dev -``` - ### Without MongoDB ```sh @@ -256,54 +253,16 @@ poetry run tiny dev --- -## REST API (FastAPI) - -### API Base URL - - - -### Swagger Docs - - - -### Shorten URL - -POST /api/shorten - -Request: - -```json -{ - "url": "https://example.com" -} -``` - -Response: - -```json -{ - "input_url": "https://example.com", - "output_url": "http://127.0.0.1:8000/AbX92p", - "created_on": "2026-01-03T13:25:10+00:00" -} -``` - -### API Version - -GET /api/version +## Troubleshooting -Response: +sometimes there might be chances that virtual environment get corrupted then delete the old virtual environment and start afresh. -```json -{ - "version": "0.1.0" -} +```sh +poetry env info +# this will provide virtual environment name +poetry env remove ``` ---- - -## Troubleshooting - ### Mongo auth error Encode special chars: @@ -322,10 +281,38 @@ MONGO_URI=mongodb://user%40gmail.com:Pass%40123@localhost:27017/tiny_url?authSou ```sh sudo systemctl start mongod -poetry run uvicorn app.main:app --reload +poetry run tiny dev ``` ---- +## Build & Packaging + +## Build Package + +```bash +poetry clean +poetry build +``` + +Artifacts in `dist/` + +- tiny-x.y.0-py3-none-any.whl +- tiny-x.y.0.tar.gz + +## Test Locally + +```bash +python -m venv .venv-dist +source .venv-dist/bin/activate +# Windows +.venv-dist\Scripts\activate +``` + +### Install package + +```bash +pip install dist/*.whl +pip install --upgrade dist/*.whl +``` ## License @@ -334,15 +321,20 @@ poetry run uvicorn app.main:app --reload Screenshots: Home Page: -![home page](./assets/images/home.png) -![home dark mode](./assets/images/home_dark.png) -![home page](./assets/images/valid.png) -![home layout](./assets/images/short_url.png) -![recent](./assets/images/recent.png) +![home page](assets/images/home.png) +![home dark mode](assets/images/home_dark.png) +![home page](assets/images/valid.png) +![home layout](assets/images/short_url.png) +![recent](assets/images/recent.png) tiny API Page: -![API](./assets/images/API_page.png) -![API1](./assets/images/api_page2.png) - +![API](assets/images/API_page.png) +![API1](assets/images/api_page2.png) +No DB Mode: +![NO DB](assets/images/no-db.png) πŸ“œLicense [MIT](LICENSE) + +``` + +``` diff --git a/app/api/fast_api.py b/app/api/fast_api.py index a20c51c..2190c0d 100644 --- a/app/api/fast_api.py +++ b/app/api/fast_api.py @@ -2,28 +2,28 @@ import re import traceback from datetime import datetime, timezone -from typing import Any from fastapi import APIRouter, FastAPI, Request from fastapi.responses import HTMLResponse, JSONResponse from pydantic import BaseModel, Field -from app import __version__ -from app.db import data as db_data -from app.utils.helper import generate_code, is_valid_url, sanitize_url +from typing import TYPE_CHECKING -PyMongoError: Any -try: - from pymongo.errors import PyMongoError as _RealPyMongoError +if TYPE_CHECKING: + from pymongo.errors import PyMongoError +else: + try: + from pymongo.errors import PyMongoError + except ImportError: - PyMongoError = _RealPyMongoError -except (ImportError, ModuleNotFoundError): - # 2. Fallback: Define our own only if the real one fails - class _FallbackPyMongoError(Exception): - pass + class PyMongoError(Exception): + pass - # Assign our fallback to the same local name - PyMongoError = _FallbackPyMongoError + +from app import __version__ +from app.db import data as db_data +from app.utils.cache import get_short_from_cache, set_cache_pair +from app.utils.helper import generate_code, is_valid_url, sanitize_url SHORT_CODE_PATTERN = re.compile(r"^[A-Za-z0-9]{6}$") MAX_URL_LENGTH = 2048 @@ -156,8 +156,9 @@ def shorten_url(payload: ShortenRequest): ) if db_data.urls is None: - short_code = generate_code() - + cached_short = get_short_from_cache(original_url) + short_code = cached_short or generate_code() + set_cache_pair(short_code, original_url) return { "success": True, "input_url": original_url, diff --git a/app/main.py b/app/main.py index 15c0519..8f7eeca 100644 --- a/app/main.py +++ b/app/main.py @@ -2,7 +2,8 @@ import os from contextlib import asynccontextmanager from pathlib import Path -from typing import Any, Optional +from typing import Optional + from fastapi import FastAPI, Form, Request from fastapi.responses import HTMLResponse, PlainTextResponse, RedirectResponse @@ -10,8 +11,29 @@ from fastapi.templating import Jinja2Templates from starlette.middleware.sessions import SessionMiddleware +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from pymongo.errors import PyMongoError +else: + try: + from pymongo.errors import PyMongoError + except ImportError: + + class PyMongoError(Exception): + pass + + from app.api.fast_api import app as api_app from app.db import data as db_data +from app.utils.qr import generate_qr_with_logo +from app.utils.cache import ( + get_from_cache, + get_short_from_cache, + set_cache_pair, + url_cache, + rev_cache, +) from app.utils.config import load_env from app.utils.helper import ( format_date, @@ -19,31 +41,6 @@ is_valid_url, sanitize_url, ) -from app.utils.qr import generate_qr_with_logo - -load_env() # βœ… load env ONCE - -RED = "\033[31m" -GREEN = "\033[32m" -BLUE = "\033[34m" -RESET = "\033[0m" - -app_name = os.getenv("APP_NAME", "TinyURL") -print(f"Environment loaded as {BLUE}{app_name}{RESET}") - -# 1. MongoDB error handling: Try to import the real exception class first -PyMongoError: Any -try: - from pymongo.errors import PyMongoError as _RealPyMongoError - - PyMongoError = _RealPyMongoError -except (ImportError, ModuleNotFoundError): - # 2. Fallback: Define our own only if the real one fails - class _FallbackPyMongoError(Exception): - pass - - # Assign our fallback to the same local name - PyMongoError = _FallbackPyMongoError # ----------------------------- @@ -51,6 +48,7 @@ class _FallbackPyMongoError(Exception): # ----------------------------- @asynccontextmanager async def lifespan(app: FastAPI): + load_env() # βœ… load env ONCE connected = db_data.connect_db() # βœ… connect DB ONCE app.state.db_available = connected yield @@ -158,20 +156,22 @@ async def create_short_url( pass if not short_code: - short_code = generate_code() - - if db_available(request) and db_data.urls is not None: - try: - db_data.urls.insert_one( - { - "short_code": short_code, - "original_url": original_url, - "created_at": datetime.datetime.utcnow(), - "visit_count": 0, - } - ) - except PyMongoError: - pass + cached_short = get_short_from_cache(original_url) + short_code = cached_short or generate_code() + set_cache_pair(short_code, original_url) + + if db_available(request) and db_data.urls is not None: + try: + db_data.urls.insert_one( + { + "short_code": short_code, + "original_url": original_url, + "created_at": datetime.datetime.utcnow(), + "visit_count": 0, + } + ) + except PyMongoError: + pass new_short_url = build_short_url(short_code, str(request.base_url)) session.update( @@ -210,11 +210,16 @@ async def delete_url(request: Request, short_code: str): except PyMongoError: return PlainTextResponse("Database connection lost.", status_code=503) - return RedirectResponse("/recent", status_code=303) + url_cache.pop(short_code, None) + return PlainTextResponse("", status_code=204) @app.get("/{short_code}") async def redirect_short(request: Request, short_code: str): + cached_url = get_from_cache(short_code) + if cached_url: + return RedirectResponse(cached_url) + if not db_available(request) or db_data.urls is None: return PlainTextResponse("Database is not connected.", status_code=503) @@ -229,7 +234,17 @@ async def redirect_short(request: Request, short_code: str): if not doc: return PlainTextResponse("Invalid or expired short URL", status_code=404) + set_cache_pair(short_code, doc["original_url"]) return RedirectResponse(doc["original_url"]) app.mount("/api", api_app) + + +@app.get("/_debug/cache") +async def debug_cache(): + return { + "url_cache": url_cache, + "rev_cache": rev_cache, + "size": {"url_cache": len(url_cache), "rev_cache": len(rev_cache)}, + } diff --git a/app/static/qr/zSDrr0.png b/app/static/qr/zSDrr0.png deleted file mode 100644 index 0b8b9fbc8c5fb35eb88b52d72aa3896b638d2540..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 41041 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 str | None: + data = url_cache.get(short_code) + + if not data or data["expires_at"] < time.time(): + url_cache.pop(short_code, None) + return None + + return data["url"] + + +def get_short_from_cache(original_url: str) -> str | None: + data = rev_cache.get(original_url) + + if not data or data["expires_at"] < time.time(): + rev_cache.pop(original_url, None) + return None + + return data["short_code"] + + +def set_cache_pair(short_code: str, original_url: str) -> None: + expires_at = time.time() + CACHE_TTL + + url_cache[short_code] = { + "url": original_url, + "expires_at": expires_at, + } + + rev_cache[original_url] = { + "short_code": short_code, + "expires_at": expires_at, + } diff --git a/docs/cache.md b/docs/cache.md new file mode 100644 index 0000000..a69ef3e --- /dev/null +++ b/docs/cache.md @@ -0,0 +1,200 @@ +# πŸ“˜ Cache – Complete Guide (Concepts, How It Works, How to Use It) + +`1. What is Cache?` + +Cache is a temporary storage layer used to store frequently accessed data so that future requests can be served faster. + +Instead of fetching data repeatedly from a slow source (like a database, API, or disk), the application first checks the cache. +If the data exists in cache, it is returned immediately. + +If not, the application fetches it from the original source and stores it in the cache for next time. + +Simple definition: + +Cache is a fast memory layer that stores frequently used data to reduce latency and load on the main data source. + +# 2. Why Cache is Needed + +Without cache: + +- Every request hits the database or external service + +- Response time is slow + +- Database load increases + +- Application does not scale well + +With cache: + +- Most requests are served from fast memory + +- Database load is reduced + +- Response time improves significantly + +- Application handles more traffic with the same resources + +Key benefits: + +- Performance improvement + +- Reduced load on database + +- Better user experience + +- Lower infrastructure cost + +- Improved scalability + + # 3. How Cache Works (High-Level Flow) + +Basic flow: + +``` +User Request + ↓ +Application checks Cache + ↓ +Cache Hit? ── Yes ──> Return data from Cache + β”‚ + No + ↓ +Fetch from Database / Source + ↓ +Store result in Cache + ↓ +Return data to User + +``` + +Cache Hit vs Cache Miss + +- Cache Hit: + Data is found in cache β†’ fast response + +- Cache Miss: + Data is not in cache β†’ fetch from main source β†’ save to cache β†’ return response + +# 5. Cache Lifetime (TTL – Time To Live) + +Cached data should not live forever. +A TTL (Time To Live) defines how long data remains in cache before it expires. + +`Why TTL is important:` + +- Prevents stale data + +- Frees memory + +- Keeps cache fresh + +- Avoids serving outdated values + +`Examples:` + +- Short TTL β†’ fast-changing data + +- Long TTL β†’ stable data + +`Once TTL expires:` + +- Data is removed from cache automatically + +- Next request becomes a cache miss + +# 6. Cache Invalidation (Keeping Cache Correct) + +Cache invalidation means removing or updating cached data when the original data changes. + +`When to invalidate cache:` + +- Data is updated + +- Data is deleted + +- Data becomes invalid + +- TTL expires + +`Why invalidation is important:` + +- Prevents serving outdated or incorrect data + +- Keeps cache consistent with main data source + Cache invalidation is one of the hardest problems in system design because it requires keeping cache and source data in sync. + +# 7. Common Cache Strategies (Conceptual) + +`1️⃣ Read-Through Cache` + +- Application reads from cache first + +- On miss, fetches from source and stores in cache + +`2️⃣ Write-Through Cache` + +- Data is written to cache and main storage together + +`3️⃣ Write-Behind Cache` + +- Data is written to cache first + +- Main storage is updated later asynchronously + +`Each strategy has trade-offs between:` + +- Speed + +- Consistency + +- Reliability + +# 1. Cache Consistency vs Performance + +Cache improves performance but can introduce stale data. + +There is always a trade-off between: + +- Strong consistency β†’ always fresh data + +- High performance β†’ faster responses + +`Design choice depends on:` + +- How critical correctness is + +- How often data changes + +- How much stale data is acceptable + +# 9. Cache Failure Handling + +Cache should never be a single point of failure. + +`Good design principles:` + +- Application should still work if cache is unavailable + +- Cache is an optimization, not the source of truth + +- Main data source remains authoritative + +`If cache fails:` + +- Application should fallback to main data source + +- Cache can be repopulated later + +# 10. Cache in Application Architecture + +`Cache usually sits between application and main storage:` + +```Client + ↓ +Application + ↓ +Cache Layer + ↓ +Database / External Service +``` diff --git a/poetry.lock b/poetry.lock index 1a589f1..1b94a62 100644 --- a/poetry.lock +++ b/poetry.lock @@ -430,62 +430,62 @@ markers = {main = "sys_platform == \"win32\" or platform_system == \"Windows\"", [[package]] name = "cryptography" -version = "46.0.4" +version = "46.0.5" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = "!=3.9.0,!=3.9.1,>=3.8" groups = ["dev"] markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\"" files = [ - {file = "cryptography-46.0.4-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:281526e865ed4166009e235afadf3a4c4cba6056f99336a99efba65336fd5485"}, - {file = "cryptography-46.0.4-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5f14fba5bf6f4390d7ff8f086c566454bff0411f6d8aa7af79c88b6f9267aecc"}, - {file = "cryptography-46.0.4-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:47bcd19517e6389132f76e2d5303ded6cf3f78903da2158a671be8de024f4cd0"}, - {file = "cryptography-46.0.4-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:01df4f50f314fbe7009f54046e908d1754f19d0c6d3070df1e6268c5a4af09fa"}, - {file = "cryptography-46.0.4-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5aa3e463596b0087b3da0dbe2b2487e9fc261d25da85754e30e3b40637d61f81"}, - {file = "cryptography-46.0.4-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:0a9ad24359fee86f131836a9ac3bffc9329e956624a2d379b613f8f8abaf5255"}, - {file = "cryptography-46.0.4-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:dc1272e25ef673efe72f2096e92ae39dea1a1a450dd44918b15351f72c5a168e"}, - {file = "cryptography-46.0.4-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:de0f5f4ec8711ebc555f54735d4c673fc34b65c44283895f1a08c2b49d2fd99c"}, - {file = "cryptography-46.0.4-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:eeeb2e33d8dbcccc34d64651f00a98cb41b2dc69cef866771a5717e6734dfa32"}, - {file = "cryptography-46.0.4-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:3d425eacbc9aceafd2cb429e42f4e5d5633c6f873f5e567077043ef1b9bbf616"}, - {file = "cryptography-46.0.4-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:91627ebf691d1ea3976a031b61fb7bac1ccd745afa03602275dda443e11c8de0"}, - {file = "cryptography-46.0.4-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2d08bc22efd73e8854b0b7caff402d735b354862f1145d7be3b9c0f740fef6a0"}, - {file = "cryptography-46.0.4-cp311-abi3-win32.whl", hash = "sha256:82a62483daf20b8134f6e92898da70d04d0ef9a75829d732ea1018678185f4f5"}, - {file = "cryptography-46.0.4-cp311-abi3-win_amd64.whl", hash = "sha256:6225d3ebe26a55dbc8ead5ad1265c0403552a63336499564675b29eb3184c09b"}, - {file = "cryptography-46.0.4-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:485e2b65d25ec0d901bca7bcae0f53b00133bf3173916d8e421f6fddde103908"}, - {file = "cryptography-46.0.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:078e5f06bd2fa5aea5a324f2a09f914b1484f1d0c2a4d6a8a28c74e72f65f2da"}, - {file = "cryptography-46.0.4-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:dce1e4f068f03008da7fa51cc7abc6ddc5e5de3e3d1550334eaf8393982a5829"}, - {file = "cryptography-46.0.4-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:2067461c80271f422ee7bdbe79b9b4be54a5162e90345f86a23445a0cf3fd8a2"}, - {file = "cryptography-46.0.4-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:c92010b58a51196a5f41c3795190203ac52edfd5dc3ff99149b4659eba9d2085"}, - {file = "cryptography-46.0.4-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:829c2b12bbc5428ab02d6b7f7e9bbfd53e33efd6672d21341f2177470171ad8b"}, - {file = "cryptography-46.0.4-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:62217ba44bf81b30abaeda1488686a04a702a261e26f87db51ff61d9d3510abd"}, - {file = "cryptography-46.0.4-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:9c2da296c8d3415b93e6053f5a728649a87a48ce084a9aaf51d6e46c87c7f2d2"}, - {file = "cryptography-46.0.4-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:9b34d8ba84454641a6bf4d6762d15847ecbd85c1316c0a7984e6e4e9f748ec2e"}, - {file = "cryptography-46.0.4-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:df4a817fa7138dd0c96c8c8c20f04b8aaa1fac3bbf610913dcad8ea82e1bfd3f"}, - {file = "cryptography-46.0.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:b1de0ebf7587f28f9190b9cb526e901bf448c9e6a99655d2b07fff60e8212a82"}, - {file = "cryptography-46.0.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9b4d17bc7bd7cdd98e3af40b441feaea4c68225e2eb2341026c84511ad246c0c"}, - {file = "cryptography-46.0.4-cp314-cp314t-win32.whl", hash = "sha256:c411f16275b0dea722d76544a61d6421e2cc829ad76eec79280dbdc9ddf50061"}, - {file = "cryptography-46.0.4-cp314-cp314t-win_amd64.whl", hash = "sha256:728fedc529efc1439eb6107b677f7f7558adab4553ef8669f0d02d42d7b959a7"}, - {file = "cryptography-46.0.4-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a9556ba711f7c23f77b151d5798f3ac44a13455cc68db7697a1096e6d0563cab"}, - {file = "cryptography-46.0.4-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8bf75b0259e87fa70bddc0b8b4078b76e7fd512fd9afae6c1193bcf440a4dbef"}, - {file = "cryptography-46.0.4-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3c268a3490df22270955966ba236d6bc4a8f9b6e4ffddb78aac535f1a5ea471d"}, - {file = "cryptography-46.0.4-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:812815182f6a0c1d49a37893a303b44eaac827d7f0d582cecfc81b6427f22973"}, - {file = "cryptography-46.0.4-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:a90e43e3ef65e6dcf969dfe3bb40cbf5aef0d523dff95bfa24256be172a845f4"}, - {file = "cryptography-46.0.4-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a05177ff6296644ef2876fce50518dffb5bcdf903c85250974fc8bc85d54c0af"}, - {file = "cryptography-46.0.4-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:daa392191f626d50f1b136c9b4cf08af69ca8279d110ea24f5c2700054d2e263"}, - {file = "cryptography-46.0.4-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e07ea39c5b048e085f15923511d8121e4a9dc45cee4e3b970ca4f0d338f23095"}, - {file = "cryptography-46.0.4-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:d5a45ddc256f492ce42a4e35879c5e5528c09cd9ad12420828c972951d8e016b"}, - {file = "cryptography-46.0.4-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:6bb5157bf6a350e5b28aee23beb2d84ae6f5be390b2f8ee7ea179cda077e1019"}, - {file = "cryptography-46.0.4-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:dd5aba870a2c40f87a3af043e0dee7d9eb02d4aff88a797b48f2b43eff8c3ab4"}, - {file = "cryptography-46.0.4-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:93d8291da8d71024379ab2cb0b5c57915300155ad42e07f76bea6ad838d7e59b"}, - {file = "cryptography-46.0.4-cp38-abi3-win32.whl", hash = "sha256:0563655cb3c6d05fb2afe693340bc050c30f9f34e15763361cf08e94749401fc"}, - {file = "cryptography-46.0.4-cp38-abi3-win_amd64.whl", hash = "sha256:fa0900b9ef9c49728887d1576fd8d9e7e3ea872fa9b25ef9b64888adc434e976"}, - {file = "cryptography-46.0.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:766330cce7416c92b5e90c3bb71b1b79521760cdcfc3a6a1a182d4c9fab23d2b"}, - {file = "cryptography-46.0.4-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c236a44acfb610e70f6b3e1c3ca20ff24459659231ef2f8c48e879e2d32b73da"}, - {file = "cryptography-46.0.4-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:8a15fb869670efa8f83cbffbc8753c1abf236883225aed74cd179b720ac9ec80"}, - {file = "cryptography-46.0.4-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:fdc3daab53b212472f1524d070735b2f0c214239df131903bae1d598016fa822"}, - {file = "cryptography-46.0.4-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:44cc0675b27cadb71bdbb96099cca1fa051cd11d2ade09e5cd3a2edb929ed947"}, - {file = "cryptography-46.0.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:be8c01a7d5a55f9a47d1888162b76c8f49d62b234d88f0ff91a9fbebe32ffbc3"}, - {file = "cryptography-46.0.4.tar.gz", hash = "sha256:bfd019f60f8abc2ed1b9be4ddc21cfef059c841d86d710bb69909a688cbb8f59"}, + {file = "cryptography-46.0.5-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:351695ada9ea9618b3500b490ad54c739860883df6c1f555e088eaf25b1bbaad"}, + {file = "cryptography-46.0.5-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c18ff11e86df2e28854939acde2d003f7984f721eba450b56a200ad90eeb0e6b"}, + {file = "cryptography-46.0.5-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d7e3d356b8cd4ea5aff04f129d5f66ebdc7b6f8eae802b93739ed520c47c79b"}, + {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:50bfb6925eff619c9c023b967d5b77a54e04256c4281b0e21336a130cd7fc263"}, + {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:803812e111e75d1aa73690d2facc295eaefd4439be1023fefc4995eaea2af90d"}, + {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ee190460e2fbe447175cda91b88b84ae8322a104fc27766ad09428754a618ed"}, + {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:f145bba11b878005c496e93e257c1e88f154d278d2638e6450d17e0f31e558d2"}, + {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e9251e3be159d1020c4030bd2e5f84d6a43fe54b6c19c12f51cde9542a2817b2"}, + {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:47fb8a66058b80e509c47118ef8a75d14c455e81ac369050f20ba0d23e77fee0"}, + {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:4c3341037c136030cb46e4b1e17b7418ea4cbd9dd207e4a6f3b2b24e0d4ac731"}, + {file = "cryptography-46.0.5-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:890bcb4abd5a2d3f852196437129eb3667d62630333aacc13dfd470fad3aaa82"}, + {file = "cryptography-46.0.5-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:80a8d7bfdf38f87ca30a5391c0c9ce4ed2926918e017c29ddf643d0ed2778ea1"}, + {file = "cryptography-46.0.5-cp311-abi3-win32.whl", hash = "sha256:60ee7e19e95104d4c03871d7d7dfb3d22ef8a9b9c6778c94e1c8fcc8365afd48"}, + {file = "cryptography-46.0.5-cp311-abi3-win_amd64.whl", hash = "sha256:38946c54b16c885c72c4f59846be9743d699eee2b69b6988e0a00a01f46a61a4"}, + {file = "cryptography-46.0.5-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:94a76daa32eb78d61339aff7952ea819b1734b46f73646a07decb40e5b3448e2"}, + {file = "cryptography-46.0.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5be7bf2fb40769e05739dd0046e7b26f9d4670badc7b032d6ce4db64dddc0678"}, + {file = "cryptography-46.0.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fe346b143ff9685e40192a4960938545c699054ba11d4f9029f94751e3f71d87"}, + {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:c69fd885df7d089548a42d5ec05be26050ebcd2283d89b3d30676eb32ff87dee"}, + {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:8293f3dea7fc929ef7240796ba231413afa7b68ce38fd21da2995549f5961981"}, + {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:1abfdb89b41c3be0365328a410baa9df3ff8a9110fb75e7b52e66803ddabc9a9"}, + {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:d66e421495fdb797610a08f43b05269e0a5ea7f5e652a89bfd5a7d3c1dee3648"}, + {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:4e817a8920bfbcff8940ecfd60f23d01836408242b30f1a708d93198393a80b4"}, + {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:68f68d13f2e1cb95163fa3b4db4bf9a159a418f5f6e7242564fc75fcae667fd0"}, + {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:a3d1fae9863299076f05cb8a778c467578262fae09f9dc0ee9b12eb4268ce663"}, + {file = "cryptography-46.0.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c4143987a42a2397f2fc3b4d7e3a7d313fbe684f67ff443999e803dd75a76826"}, + {file = "cryptography-46.0.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7d731d4b107030987fd61a7f8ab512b25b53cef8f233a97379ede116f30eb67d"}, + {file = "cryptography-46.0.5-cp314-cp314t-win32.whl", hash = "sha256:c3bcce8521d785d510b2aad26ae2c966092b7daa8f45dd8f44734a104dc0bc1a"}, + {file = "cryptography-46.0.5-cp314-cp314t-win_amd64.whl", hash = "sha256:4d8ae8659ab18c65ced284993c2265910f6c9e650189d4e3f68445ef82a810e4"}, + {file = "cryptography-46.0.5-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:4108d4c09fbbf2789d0c926eb4152ae1760d5a2d97612b92d508d96c861e4d31"}, + {file = "cryptography-46.0.5-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d1f30a86d2757199cb2d56e48cce14deddf1f9c95f1ef1b64ee91ea43fe2e18"}, + {file = "cryptography-46.0.5-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:039917b0dc418bb9f6edce8a906572d69e74bd330b0b3fea4f79dab7f8ddd235"}, + {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ba2a27ff02f48193fc4daeadf8ad2590516fa3d0adeeb34336b96f7fa64c1e3a"}, + {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:61aa400dce22cb001a98014f647dc21cda08f7915ceb95df0c9eaf84b4b6af76"}, + {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ce58ba46e1bc2aac4f7d9290223cead56743fa6ab94a5d53292ffaac6a91614"}, + {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:420d0e909050490d04359e7fdb5ed7e667ca5c3c402b809ae2563d7e66a92229"}, + {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:582f5fcd2afa31622f317f80426a027f30dc792e9c80ffee87b993200ea115f1"}, + {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:bfd56bb4b37ed4f330b82402f6f435845a5f5648edf1ad497da51a8452d5d62d"}, + {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:a3d507bb6a513ca96ba84443226af944b0f7f47dcc9a399d110cd6146481d24c"}, + {file = "cryptography-46.0.5-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9f16fbdf4da055efb21c22d81b89f155f02ba420558db21288b3d0035bafd5f4"}, + {file = "cryptography-46.0.5-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ced80795227d70549a411a4ab66e8ce307899fad2220ce5ab2f296e687eacde9"}, + {file = "cryptography-46.0.5-cp38-abi3-win32.whl", hash = "sha256:02f547fce831f5096c9a567fd41bc12ca8f11df260959ecc7c3202555cc47a72"}, + {file = "cryptography-46.0.5-cp38-abi3-win_amd64.whl", hash = "sha256:556e106ee01aa13484ce9b0239bca667be5004efb0aabbed28d353df86445595"}, + {file = "cryptography-46.0.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:3b4995dc971c9fb83c25aa44cf45f02ba86f71ee600d81091c2f0cbae116b06c"}, + {file = "cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:bc84e875994c3b445871ea7181d424588171efec3e185dced958dad9e001950a"}, + {file = "cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:2ae6971afd6246710480e3f15824ed3029a60fc16991db250034efd0b9fb4356"}, + {file = "cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:d861ee9e76ace6cf36a6a89b959ec08e7bc2493ee39d07ffe5acb23ef46d27da"}, + {file = "cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:2b7a67c9cd56372f3249b39699f2ad479f6991e62ea15800973b956f4b73e257"}, + {file = "cryptography-46.0.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:8456928655f856c6e1533ff59d5be76578a7157224dbd9ce6872f25055ab9ab7"}, + {file = "cryptography-46.0.5.tar.gz", hash = "sha256:abace499247268e3757271b2f1e244b36b06f8515cf27c4d49468fc9eb16e93d"}, ] [package.dependencies] @@ -499,7 +499,7 @@ nox = ["nox[uv] (>=2024.4.15)"] pep8test = ["check-sdist", "click (>=8.0.1)", "mypy (>=1.14)", "ruff (>=0.11.11)"] sdist = ["build (>=1.0.0)"] ssh = ["bcrypt (>=3.1.5)"] -test = ["certifi (>=2024)", "cryptography-vectors (==46.0.4)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] +test = ["certifi (>=2024)", "cryptography-vectors (==46.0.5)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] test-randomorder = ["pytest-randomly"] [[package]] @@ -812,6 +812,24 @@ files = [ test = ["async-timeout ; python_version < \"3.11\"", "pytest", "pytest-asyncio (>=0.17)", "pytest-trio", "testpath", "trio"] trio = ["trio"] +[[package]] +name = "jinja2" +version = "3.1.6" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, + {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + [[package]] name = "keyring" version = "25.7.0" @@ -972,6 +990,105 @@ profiling = ["gprof2dot"] rtd = ["ipykernel", "jupyter_sphinx", "mdit-py-plugins (>=0.5.0)", "myst-parser", "pyyaml", "sphinx", "sphinx-book-theme (>=1.0,<2.0)", "sphinx-copybutton", "sphinx-design"] testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions", "requests"] +[[package]] +name = "markupsafe" +version = "3.0.3" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559"}, + {file = "markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1"}, + {file = "markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa"}, + {file = "markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8"}, + {file = "markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1"}, + {file = "markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad"}, + {file = "markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a"}, + {file = "markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19"}, + {file = "markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01"}, + {file = "markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c"}, + {file = "markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e"}, + {file = "markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b"}, + {file = "markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d"}, + {file = "markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c"}, + {file = "markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f"}, + {file = "markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795"}, + {file = "markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12"}, + {file = "markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed"}, + {file = "markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5"}, + {file = "markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485"}, + {file = "markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73"}, + {file = "markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287"}, + {file = "markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe"}, + {file = "markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe"}, + {file = "markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9"}, + {file = "markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581"}, + {file = "markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4"}, + {file = "markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab"}, + {file = "markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa"}, + {file = "markupsafe-3.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26"}, + {file = "markupsafe-3.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8fc20152abba6b83724d7ff268c249fa196d8259ff481f3b1476383f8f24e42"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:949b8d66bc381ee8b007cd945914c721d9aba8e27f71959d750a46f7c282b20b"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:3537e01efc9d4dccdf77221fb1cb3b8e1a38d5428920e0657ce299b20324d758"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:591ae9f2a647529ca990bc681daebdd52c8791ff06c2bfa05b65163e28102ef2"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a320721ab5a1aba0a233739394eb907f8c8da5c98c9181d1161e77a0c8e36f2d"}, + {file = "markupsafe-3.0.3-cp39-cp39-win32.whl", hash = "sha256:df2449253ef108a379b8b5d6b43f4b1a8e81a061d6537becd5582fba5f9196d7"}, + {file = "markupsafe-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:7c3fb7d25180895632e5d3148dbdc29ea38ccb7fd210aa27acbd1201a1902c6e"}, + {file = "markupsafe-3.0.3-cp39-cp39-win_arm64.whl", hash = "sha256:38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8"}, + {file = "markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698"}, +] + [[package]] name = "mccabe" version = "0.7.0" @@ -1241,103 +1358,103 @@ tests = ["pytest (>=9)", "typing-extensions (>=4.15)"] [[package]] name = "pillow" -version = "12.1.0" +version = "12.1.1" description = "Python Imaging Library (fork)" optional = false python-versions = ">=3.10" groups = ["main"] files = [ - {file = "pillow-12.1.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:fb125d860738a09d363a88daa0f59c4533529a90e564785e20fe875b200b6dbd"}, - {file = "pillow-12.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cad302dc10fac357d3467a74a9561c90609768a6f73a1923b0fd851b6486f8b0"}, - {file = "pillow-12.1.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a40905599d8079e09f25027423aed94f2823adaf2868940de991e53a449e14a8"}, - {file = "pillow-12.1.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:92a7fe4225365c5e3a8e598982269c6d6698d3e783b3b1ae979e7819f9cd55c1"}, - {file = "pillow-12.1.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f10c98f49227ed8383d28174ee95155a675c4ed7f85e2e573b04414f7e371bda"}, - {file = "pillow-12.1.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8637e29d13f478bc4f153d8daa9ffb16455f0a6cb287da1b432fdad2bfbd66c7"}, - {file = "pillow-12.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:21e686a21078b0f9cb8c8a961d99e6a4ddb88e0fc5ea6e130172ddddc2e5221a"}, - {file = "pillow-12.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2415373395a831f53933c23ce051021e79c8cd7979822d8cc478547a3f4da8ef"}, - {file = "pillow-12.1.0-cp310-cp310-win32.whl", hash = "sha256:e75d3dba8fc1ddfec0cd752108f93b83b4f8d6ab40e524a95d35f016b9683b09"}, - {file = "pillow-12.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:64efdf00c09e31efd754448a383ea241f55a994fd079866b92d2bbff598aad91"}, - {file = "pillow-12.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:f188028b5af6b8fb2e9a76ac0f841a575bd1bd396e46ef0840d9b88a48fdbcea"}, - {file = "pillow-12.1.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:a83e0850cb8f5ac975291ebfc4170ba481f41a28065277f7f735c202cd8e0af3"}, - {file = "pillow-12.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b6e53e82ec2db0717eabb276aa56cf4e500c9a7cec2c2e189b55c24f65a3e8c0"}, - {file = "pillow-12.1.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:40a8e3b9e8773876d6e30daed22f016509e3987bab61b3b7fe309d7019a87451"}, - {file = "pillow-12.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:800429ac32c9b72909c671aaf17ecd13110f823ddb7db4dfef412a5587c2c24e"}, - {file = "pillow-12.1.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0b022eaaf709541b391ee069f0022ee5b36c709df71986e3f7be312e46f42c84"}, - {file = "pillow-12.1.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1f345e7bc9d7f368887c712aa5054558bad44d2a301ddf9248599f4161abc7c0"}, - {file = "pillow-12.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d70347c8a5b7ccd803ec0c85c8709f036e6348f1e6a5bf048ecd9c64d3550b8b"}, - {file = "pillow-12.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1fcc52d86ce7a34fd17cb04e87cfdb164648a3662a6f20565910a99653d66c18"}, - {file = "pillow-12.1.0-cp311-cp311-win32.whl", hash = "sha256:3ffaa2f0659e2f740473bcf03c702c39a8d4b2b7ffc629052028764324842c64"}, - {file = "pillow-12.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:806f3987ffe10e867bab0ddad45df1148a2b98221798457fa097ad85d6e8bc75"}, - {file = "pillow-12.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:9f5fefaca968e700ad1a4a9de98bf0869a94e397fe3524c4c9450c1445252304"}, - {file = "pillow-12.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a332ac4ccb84b6dde65dbace8431f3af08874bf9770719d32a635c4ef411b18b"}, - {file = "pillow-12.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:907bfa8a9cb790748a9aa4513e37c88c59660da3bcfffbd24a7d9e6abf224551"}, - {file = "pillow-12.1.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:efdc140e7b63b8f739d09a99033aa430accce485ff78e6d311973a67b6bf3208"}, - {file = "pillow-12.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bef9768cab184e7ae6e559c032e95ba8d07b3023c289f79a2bd36e8bf85605a5"}, - {file = "pillow-12.1.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:742aea052cf5ab5034a53c3846165bc3ce88d7c38e954120db0ab867ca242661"}, - {file = "pillow-12.1.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a6dfc2af5b082b635af6e08e0d1f9f1c4e04d17d4e2ca0ef96131e85eda6eb17"}, - {file = "pillow-12.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:609e89d9f90b581c8d16358c9087df76024cf058fa693dd3e1e1620823f39670"}, - {file = "pillow-12.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:43b4899cfd091a9693a1278c4982f3e50f7fb7cff5153b05174b4afc9593b616"}, - {file = "pillow-12.1.0-cp312-cp312-win32.whl", hash = "sha256:aa0c9cc0b82b14766a99fbe6084409972266e82f459821cd26997a488a7261a7"}, - {file = "pillow-12.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:d70534cea9e7966169ad29a903b99fc507e932069a881d0965a1a84bb57f6c6d"}, - {file = "pillow-12.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:65b80c1ee7e14a87d6a068dd3b0aea268ffcabfe0498d38661b00c5b4b22e74c"}, - {file = "pillow-12.1.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:7b5dd7cbae20285cdb597b10eb5a2c13aa9de6cde9bb64a3c1317427b1db1ae1"}, - {file = "pillow-12.1.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:29a4cef9cb672363926f0470afc516dbf7305a14d8c54f7abbb5c199cd8f8179"}, - {file = "pillow-12.1.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:681088909d7e8fa9e31b9799aaa59ba5234c58e5e4f1951b4c4d1082a2e980e0"}, - {file = "pillow-12.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:983976c2ab753166dc66d36af6e8ec15bb511e4a25856e2227e5f7e00a160587"}, - {file = "pillow-12.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:db44d5c160a90df2d24a24760bbd37607d53da0b34fb546c4c232af7192298ac"}, - {file = "pillow-12.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6b7a9d1db5dad90e2991645874f708e87d9a3c370c243c2d7684d28f7e133e6b"}, - {file = "pillow-12.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6258f3260986990ba2fa8a874f8b6e808cf5abb51a94015ca3dc3c68aa4f30ea"}, - {file = "pillow-12.1.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e115c15e3bc727b1ca3e641a909f77f8ca72a64fff150f666fcc85e57701c26c"}, - {file = "pillow-12.1.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6741e6f3074a35e47c77b23a4e4f2d90db3ed905cb1c5e6e0d49bff2045632bc"}, - {file = "pillow-12.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:935b9d1aed48fcfb3f838caac506f38e29621b44ccc4f8a64d575cb1b2a88644"}, - {file = "pillow-12.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5fee4c04aad8932da9f8f710af2c1a15a83582cfb884152a9caa79d4efcdbf9c"}, - {file = "pillow-12.1.0-cp313-cp313-win32.whl", hash = "sha256:a786bf667724d84aa29b5db1c61b7bfdde380202aaca12c3461afd6b71743171"}, - {file = "pillow-12.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:461f9dfdafa394c59cd6d818bdfdbab4028b83b02caadaff0ffd433faf4c9a7a"}, - {file = "pillow-12.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:9212d6b86917a2300669511ed094a9406888362e085f2431a7da985a6b124f45"}, - {file = "pillow-12.1.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:00162e9ca6d22b7c3ee8e61faa3c3253cd19b6a37f126cad04f2f88b306f557d"}, - {file = "pillow-12.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:7d6daa89a00b58c37cb1747ec9fb7ac3bc5ffd5949f5888657dfddde6d1312e0"}, - {file = "pillow-12.1.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e2479c7f02f9d505682dc47df8c0ea1fc5e264c4d1629a5d63fe3e2334b89554"}, - {file = "pillow-12.1.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f188d580bd870cda1e15183790d1cc2fa78f666e76077d103edf048eed9c356e"}, - {file = "pillow-12.1.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0fde7ec5538ab5095cc02df38ee99b0443ff0e1c847a045554cf5f9af1f4aa82"}, - {file = "pillow-12.1.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0ed07dca4a8464bada6139ab38f5382f83e5f111698caf3191cb8dbf27d908b4"}, - {file = "pillow-12.1.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f45bd71d1fa5e5749587613037b172e0b3b23159d1c00ef2fc920da6f470e6f0"}, - {file = "pillow-12.1.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:277518bf4fe74aa91489e1b20577473b19ee70fb97c374aa50830b279f25841b"}, - {file = "pillow-12.1.0-cp313-cp313t-win32.whl", hash = "sha256:7315f9137087c4e0ee73a761b163fc9aa3b19f5f606a7fc08d83fd3e4379af65"}, - {file = "pillow-12.1.0-cp313-cp313t-win_amd64.whl", hash = "sha256:0ddedfaa8b5f0b4ffbc2fa87b556dc59f6bb4ecb14a53b33f9189713ae8053c0"}, - {file = "pillow-12.1.0-cp313-cp313t-win_arm64.whl", hash = "sha256:80941e6d573197a0c28f394753de529bb436b1ca990ed6e765cf42426abc39f8"}, - {file = "pillow-12.1.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:5cb7bc1966d031aec37ddb9dcf15c2da5b2e9f7cc3ca7c54473a20a927e1eb91"}, - {file = "pillow-12.1.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:97e9993d5ed946aba26baf9c1e8cf18adbab584b99f452ee72f7ee8acb882796"}, - {file = "pillow-12.1.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:414b9a78e14ffeb98128863314e62c3f24b8a86081066625700b7985b3f529bd"}, - {file = "pillow-12.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:e6bdb408f7c9dd2a5ff2b14a3b0bb6d4deb29fb9961e6eb3ae2031ae9a5cec13"}, - {file = "pillow-12.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3413c2ae377550f5487991d444428f1a8ae92784aac79caa8b1e3b89b175f77e"}, - {file = "pillow-12.1.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e5dcbe95016e88437ecf33544ba5db21ef1b8dd6e1b434a2cb2a3d605299e643"}, - {file = "pillow-12.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d0a7735df32ccbcc98b98a1ac785cc4b19b580be1bdf0aeb5c03223220ea09d5"}, - {file = "pillow-12.1.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c27407a2d1b96774cbc4a7594129cc027339fd800cd081e44497722ea1179de"}, - {file = "pillow-12.1.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15c794d74303828eaa957ff8070846d0efe8c630901a1c753fdc63850e19ecd9"}, - {file = "pillow-12.1.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c990547452ee2800d8506c4150280757f88532f3de2a58e3022e9b179107862a"}, - {file = "pillow-12.1.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b63e13dd27da389ed9475b3d28510f0f954bca0041e8e551b2a4eb1eab56a39a"}, - {file = "pillow-12.1.0-cp314-cp314-win32.whl", hash = "sha256:1a949604f73eb07a8adab38c4fe50791f9919344398bdc8ac6b307f755fc7030"}, - {file = "pillow-12.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:4f9f6a650743f0ddee5593ac9e954ba1bdbc5e150bc066586d4f26127853ab94"}, - {file = "pillow-12.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:808b99604f7873c800c4840f55ff389936ef1948e4e87645eaf3fccbc8477ac4"}, - {file = "pillow-12.1.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:bc11908616c8a283cf7d664f77411a5ed2a02009b0097ff8abbba5e79128ccf2"}, - {file = "pillow-12.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:896866d2d436563fa2a43a9d72f417874f16b5545955c54a64941e87c1376c61"}, - {file = "pillow-12.1.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8e178e3e99d3c0ea8fc64b88447f7cac8ccf058af422a6cedc690d0eadd98c51"}, - {file = "pillow-12.1.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:079af2fb0c599c2ec144ba2c02766d1b55498e373b3ac64687e43849fbbef5bc"}, - {file = "pillow-12.1.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bdec5e43377761c5dbca620efb69a77f6855c5a379e32ac5b158f54c84212b14"}, - {file = "pillow-12.1.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:565c986f4b45c020f5421a4cea13ef294dde9509a8577f29b2fc5edc7587fff8"}, - {file = "pillow-12.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:43aca0a55ce1eefc0aefa6253661cb54571857b1a7b2964bd8a1e3ef4b729924"}, - {file = "pillow-12.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0deedf2ea233722476b3a81e8cdfbad786f7adbed5d848469fa59fe52396e4ef"}, - {file = "pillow-12.1.0-cp314-cp314t-win32.whl", hash = "sha256:b17fbdbe01c196e7e159aacb889e091f28e61020a8abeac07b68079b6e626988"}, - {file = "pillow-12.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27b9baecb428899db6c0de572d6d305cfaf38ca1596b5c0542a5182e3e74e8c6"}, - {file = "pillow-12.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:f61333d817698bdcdd0f9d7793e365ac3d2a21c1f1eb02b32ad6aefb8d8ea831"}, - {file = "pillow-12.1.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ca94b6aac0d7af2a10ba08c0f888b3d5114439b6b3ef39968378723622fed377"}, - {file = "pillow-12.1.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:351889afef0f485b84078ea40fe33727a0492b9af3904661b0abbafee0355b72"}, - {file = "pillow-12.1.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bb0984b30e973f7e2884362b7d23d0a348c7143ee559f38ef3eaab640144204c"}, - {file = "pillow-12.1.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:84cabc7095dd535ca934d57e9ce2a72ffd216e435a84acb06b2277b1de2689bd"}, - {file = "pillow-12.1.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:53d8b764726d3af1a138dd353116f774e3862ec7e3794e0c8781e30db0f35dfc"}, - {file = "pillow-12.1.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5da841d81b1a05ef940a8567da92decaa15bc4d7dedb540a8c219ad83d91808a"}, - {file = "pillow-12.1.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:75af0b4c229ac519b155028fa1be632d812a519abba9b46b20e50c6caa184f19"}, - {file = "pillow-12.1.0.tar.gz", hash = "sha256:5c5ae0a06e9ea030ab786b0251b32c7e4ce10e58d983c0d5c56029455180b5b9"}, + {file = "pillow-12.1.1-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1f1625b72740fdda5d77b4def688eb8fd6490975d06b909fd19f13f391e077e0"}, + {file = "pillow-12.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:178aa072084bd88ec759052feca8e56cbb14a60b39322b99a049e58090479713"}, + {file = "pillow-12.1.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b66e95d05ba806247aaa1561f080abc7975daf715c30780ff92a20e4ec546e1b"}, + {file = "pillow-12.1.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:89c7e895002bbe49cdc5426150377cbbc04767d7547ed145473f496dfa40408b"}, + {file = "pillow-12.1.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a5cbdcddad0af3da87cb16b60d23648bc3b51967eb07223e9fed77a82b457c4"}, + {file = "pillow-12.1.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9f51079765661884a486727f0729d29054242f74b46186026582b4e4769918e4"}, + {file = "pillow-12.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:99c1506ea77c11531d75e3a412832a13a71c7ebc8192ab9e4b2e355555920e3e"}, + {file = "pillow-12.1.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:36341d06738a9f66c8287cf8b876d24b18db9bd8740fa0672c74e259ad408cff"}, + {file = "pillow-12.1.1-cp310-cp310-win32.whl", hash = "sha256:6c52f062424c523d6c4db85518774cc3d50f5539dd6eed32b8f6229b26f24d40"}, + {file = "pillow-12.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:c6008de247150668a705a6338156efb92334113421ceecf7438a12c9a12dab23"}, + {file = "pillow-12.1.1-cp310-cp310-win_arm64.whl", hash = "sha256:1a9b0ee305220b392e1124a764ee4265bd063e54a751a6b62eff69992f457fa9"}, + {file = "pillow-12.1.1-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:e879bb6cd5c73848ef3b2b48b8af9ff08c5b71ecda8048b7dd22d8a33f60be32"}, + {file = "pillow-12.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:365b10bb9417dd4498c0e3b128018c4a624dc11c7b97d8cc54effe3b096f4c38"}, + {file = "pillow-12.1.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d4ce8e329c93845720cd2014659ca67eac35f6433fd3050393d85f3ecef0dad5"}, + {file = "pillow-12.1.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc354a04072b765eccf2204f588a7a532c9511e8b9c7f900e1b64e3e33487090"}, + {file = "pillow-12.1.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7e7976bf1910a8116b523b9f9f58bf410f3e8aa330cd9a2bb2953f9266ab49af"}, + {file = "pillow-12.1.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:597bd9c8419bc7c6af5604e55847789b69123bbe25d65cc6ad3012b4f3c98d8b"}, + {file = "pillow-12.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2c1fc0f2ca5f96a3c8407e41cca26a16e46b21060fe6d5b099d2cb01412222f5"}, + {file = "pillow-12.1.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:578510d88c6229d735855e1f278aa305270438d36a05031dfaae5067cc8eb04d"}, + {file = "pillow-12.1.1-cp311-cp311-win32.whl", hash = "sha256:7311c0a0dcadb89b36b7025dfd8326ecfa36964e29913074d47382706e516a7c"}, + {file = "pillow-12.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:fbfa2a7c10cc2623f412753cddf391c7f971c52ca40a3f65dc5039b2939e8563"}, + {file = "pillow-12.1.1-cp311-cp311-win_arm64.whl", hash = "sha256:b81b5e3511211631b3f672a595e3221252c90af017e399056d0faabb9538aa80"}, + {file = "pillow-12.1.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ab323b787d6e18b3d91a72fc99b1a2c28651e4358749842b8f8dfacd28ef2052"}, + {file = "pillow-12.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:adebb5bee0f0af4909c30db0d890c773d1a92ffe83da908e2e9e720f8edf3984"}, + {file = "pillow-12.1.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bb66b7cc26f50977108790e2456b7921e773f23db5630261102233eb355a3b79"}, + {file = "pillow-12.1.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:aee2810642b2898bb187ced9b349e95d2a7272930796e022efaf12e99dccd293"}, + {file = "pillow-12.1.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a0b1cd6232e2b618adcc54d9882e4e662a089d5768cd188f7c245b4c8c44a397"}, + {file = "pillow-12.1.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7aac39bcf8d4770d089588a2e1dd111cbaa42df5a94be3114222057d68336bd0"}, + {file = "pillow-12.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ab174cd7d29a62dd139c44bf74b698039328f45cb03b4596c43473a46656b2f3"}, + {file = "pillow-12.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:339ffdcb7cbeaa08221cd401d517d4b1fe7a9ed5d400e4a8039719238620ca35"}, + {file = "pillow-12.1.1-cp312-cp312-win32.whl", hash = "sha256:5d1f9575a12bed9e9eedd9a4972834b08c97a352bd17955ccdebfeca5913fa0a"}, + {file = "pillow-12.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:21329ec8c96c6e979cd0dfd29406c40c1d52521a90544463057d2aaa937d66a6"}, + {file = "pillow-12.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:af9a332e572978f0218686636610555ae3defd1633597be015ed50289a03c523"}, + {file = "pillow-12.1.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:d242e8ac078781f1de88bf823d70c1a9b3c7950a44cdf4b7c012e22ccbcd8e4e"}, + {file = "pillow-12.1.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:02f84dfad02693676692746df05b89cf25597560db2857363a208e393429f5e9"}, + {file = "pillow-12.1.1-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:e65498daf4b583091ccbb2556c7000abf0f3349fcd57ef7adc9a84a394ed29f6"}, + {file = "pillow-12.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6c6db3b84c87d48d0088943bf33440e0c42370b99b1c2a7989216f7b42eede60"}, + {file = "pillow-12.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8b7e5304e34942bf62e15184219a7b5ad4ff7f3bb5cca4d984f37df1a0e1aee2"}, + {file = "pillow-12.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:18e5bddd742a44b7e6b1e773ab5db102bd7a94c32555ba656e76d319d19c3850"}, + {file = "pillow-12.1.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc44ef1f3de4f45b50ccf9136999d71abb99dca7706bc75d222ed350b9fd2289"}, + {file = "pillow-12.1.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5a8eb7ed8d4198bccbd07058416eeec51686b498e784eda166395a23eb99138e"}, + {file = "pillow-12.1.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:47b94983da0c642de92ced1702c5b6c292a84bd3a8e1d1702ff923f183594717"}, + {file = "pillow-12.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:518a48c2aab7ce596d3bf79d0e275661b846e86e4d0e7dec34712c30fe07f02a"}, + {file = "pillow-12.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a550ae29b95c6dc13cf69e2c9dc5747f814c54eeb2e32d683e5e93af56caa029"}, + {file = "pillow-12.1.1-cp313-cp313-win32.whl", hash = "sha256:a003d7422449f6d1e3a34e3dd4110c22148336918ddbfc6a32581cd54b2e0b2b"}, + {file = "pillow-12.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:344cf1e3dab3be4b1fa08e449323d98a2a3f819ad20f4b22e77a0ede31f0faa1"}, + {file = "pillow-12.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:5c0dd1636633e7e6a0afe7bf6a51a14992b7f8e60de5789018ebbdfae55b040a"}, + {file = "pillow-12.1.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0330d233c1a0ead844fc097a7d16c0abff4c12e856c0b325f231820fee1f39da"}, + {file = "pillow-12.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5dae5f21afb91322f2ff791895ddd8889e5e947ff59f71b46041c8ce6db790bc"}, + {file = "pillow-12.1.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2e0c664be47252947d870ac0d327fea7e63985a08794758aa8af5b6cb6ec0c9c"}, + {file = "pillow-12.1.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:691ab2ac363b8217f7d31b3497108fb1f50faab2f75dfb03284ec2f217e87bf8"}, + {file = "pillow-12.1.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e9e8064fb1cc019296958595f6db671fba95209e3ceb0c4734c9baf97de04b20"}, + {file = "pillow-12.1.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:472a8d7ded663e6162dafdf20015c486a7009483ca671cece7a9279b512fcb13"}, + {file = "pillow-12.1.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:89b54027a766529136a06cfebeecb3a04900397a3590fd252160b888479517bf"}, + {file = "pillow-12.1.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:86172b0831b82ce4f7877f280055892b31179e1576aa00d0df3bb1bbf8c3e524"}, + {file = "pillow-12.1.1-cp313-cp313t-win32.whl", hash = "sha256:44ce27545b6efcf0fdbdceb31c9a5bdea9333e664cda58a7e674bb74608b3986"}, + {file = "pillow-12.1.1-cp313-cp313t-win_amd64.whl", hash = "sha256:a285e3eb7a5a45a2ff504e31f4a8d1b12ef62e84e5411c6804a42197c1cf586c"}, + {file = "pillow-12.1.1-cp313-cp313t-win_arm64.whl", hash = "sha256:cc7d296b5ea4d29e6570dabeaed58d31c3fea35a633a69679fb03d7664f43fb3"}, + {file = "pillow-12.1.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:417423db963cb4be8bac3fc1204fe61610f6abeed1580a7a2cbb2fbda20f12af"}, + {file = "pillow-12.1.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:b957b71c6b2387610f556a7eb0828afbe40b4a98036fc0d2acfa5a44a0c2036f"}, + {file = "pillow-12.1.1-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:097690ba1f2efdeb165a20469d59d8bb03c55fb6621eb2041a060ae8ea3e9642"}, + {file = "pillow-12.1.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2815a87ab27848db0321fb78c7f0b2c8649dee134b7f2b80c6a45c6831d75ccd"}, + {file = "pillow-12.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f7ed2c6543bad5a7d5530eb9e78c53132f93dfa44a28492db88b41cdab885202"}, + {file = "pillow-12.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:652a2c9ccfb556235b2b501a3a7cf3742148cd22e04b5625c5fe057ea3e3191f"}, + {file = "pillow-12.1.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d6e4571eedf43af33d0fc233a382a76e849badbccdf1ac438841308652a08e1f"}, + {file = "pillow-12.1.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b574c51cf7d5d62e9be37ba446224b59a2da26dc4c1bb2ecbe936a4fb1a7cb7f"}, + {file = "pillow-12.1.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a37691702ed687799de29a518d63d4682d9016932db66d4e90c345831b02fb4e"}, + {file = "pillow-12.1.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f95c00d5d6700b2b890479664a06e754974848afaae5e21beb4d83c106923fd0"}, + {file = "pillow-12.1.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:559b38da23606e68681337ad74622c4dbba02254fc9cb4488a305dd5975c7eeb"}, + {file = "pillow-12.1.1-cp314-cp314-win32.whl", hash = "sha256:03edcc34d688572014ff223c125a3f77fb08091e4607e7745002fc214070b35f"}, + {file = "pillow-12.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:50480dcd74fa63b8e78235957d302d98d98d82ccbfac4c7e12108ba9ecbdba15"}, + {file = "pillow-12.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:5cb1785d97b0c3d1d1a16bc1d710c4a0049daefc4935f3a8f31f827f4d3d2e7f"}, + {file = "pillow-12.1.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:1f90cff8aa76835cba5769f0b3121a22bd4eb9e6884cfe338216e557a9a548b8"}, + {file = "pillow-12.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1f1be78ce9466a7ee64bfda57bdba0f7cc499d9794d518b854816c41bf0aa4e9"}, + {file = "pillow-12.1.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:42fc1f4677106188ad9a55562bbade416f8b55456f522430fadab3cef7cd4e60"}, + {file = "pillow-12.1.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:98edb152429ab62a1818039744d8fbb3ccab98a7c29fc3d5fcef158f3f1f68b7"}, + {file = "pillow-12.1.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d470ab1178551dd17fdba0fef463359c41aaa613cdcd7ff8373f54be629f9f8f"}, + {file = "pillow-12.1.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6408a7b064595afcab0a49393a413732a35788f2a5092fdc6266952ed67de586"}, + {file = "pillow-12.1.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5d8c41325b382c07799a3682c1c258469ea2ff97103c53717b7893862d0c98ce"}, + {file = "pillow-12.1.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c7697918b5be27424e9ce568193efd13d925c4481dd364e43f5dff72d33e10f8"}, + {file = "pillow-12.1.1-cp314-cp314t-win32.whl", hash = "sha256:d2912fd8114fc5545aa3a4b5576512f64c55a03f3ebcca4c10194d593d43ea36"}, + {file = "pillow-12.1.1-cp314-cp314t-win_amd64.whl", hash = "sha256:4ceb838d4bd9dab43e06c363cab2eebf63846d6a4aeaea283bbdfd8f1a8ed58b"}, + {file = "pillow-12.1.1-cp314-cp314t-win_arm64.whl", hash = "sha256:7b03048319bfc6170e93bd60728a1af51d3dd7704935feb228c4d4faab35d334"}, + {file = "pillow-12.1.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:600fd103672b925fe62ed08e0d874ea34d692474df6f4bf7ebe148b30f89f39f"}, + {file = "pillow-12.1.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:665e1b916b043cef294bc54d47bf02d87e13f769bc4bc5fa225a24b3a6c5aca9"}, + {file = "pillow-12.1.1-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:495c302af3aad1ca67420ddd5c7bd480c8867ad173528767d906428057a11f0e"}, + {file = "pillow-12.1.1-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8fd420ef0c52c88b5a035a0886f367748c72147b2b8f384c9d12656678dfdfa9"}, + {file = "pillow-12.1.1-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f975aa7ef9684ce7e2c18a3aa8f8e2106ce1e46b94ab713d156b2898811651d3"}, + {file = "pillow-12.1.1-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8089c852a56c2966cf18835db62d9b34fef7ba74c726ad943928d494fa7f4735"}, + {file = "pillow-12.1.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:cb9bb857b2d057c6dfc72ac5f3b44836924ba15721882ef103cecb40d002d80e"}, + {file = "pillow-12.1.1.tar.gz", hash = "sha256:9ad8fa5937ab05218e2b6a4cff30295ad35afd2f83ac592e68c0d871bb0fdbc4"}, ] [package.extras] @@ -2319,4 +2436,4 @@ mongodb = ["pymongo"] [metadata] lock-version = "2.1" python-versions = ">=3.10,<3.13" -content-hash = "c1db37cabcddd05d318573d632529be139b1ce1430e7eda7c915bb9d171c7a77" +content-hash = "349ea21b64217dca053564867ff9f7cf5375fe7c8dee163a5689d9f3a25fd371" diff --git a/requirements.txt b/requirements.txt index 7857e7c..d52bc8e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,15 +4,17 @@ anyio==4.12.1 ; python_version >= "3.10" and python_version < "3.13" async-timeout==5.0.1 ; python_version >= "3.10" and python_full_version < "3.11.3" click==8.3.1 ; python_version >= "3.10" and python_version < "3.13" colorama==0.4.6 ; python_version >= "3.10" and python_version < "3.13" and (sys_platform == "win32" or platform_system == "Windows") +dnspython==2.8.0 ; python_version >= "3.10" and python_version < "3.13" exceptiongroup==1.3.1 ; python_version == "3.10" fastapi==0.128.7 ; python_version >= "3.10" and python_version < "3.13" h11==0.16.0 ; python_version >= "3.10" and python_version < "3.13" idna==3.11 ; python_version >= "3.10" and python_version < "3.13" itsdangerous==2.2.0 ; python_version >= "3.10" and python_version < "3.13" -pillow==10.4.0 ; python_version >= "3.10" and python_version < "3.13" +pillow==12.1.0 ; python_version >= "3.10" and python_version < "3.13" pydantic-core==2.41.5 ; python_version >= "3.10" and python_version < "3.13" pydantic==2.12.5 ; python_version >= "3.10" and python_version < "3.13" -python-dotenv==1.0.1 ; python_version >= "3.10" and python_version < "3.13" +pymongo==4.16.0 ; python_version >= "3.10" and python_version < "3.13" +python-dotenv==1.2.1 ; python_version >= "3.10" and python_version < "3.13" python-multipart==0.0.22 ; python_version >= "3.10" and python_version < "3.13" qrcode==8.2 ; python_version >= "3.10" and python_version < "3.13" redis==7.1.1 ; python_version >= "3.10" and python_version < "3.13" From c770be2a50cf411235fa14de7dc6b9dcc2ea33da Mon Sep 17 00:00:00 2001 From: harshmishra2701 Date: Wed, 11 Feb 2026 16:35:49 +0530 Subject: [PATCH 02/18] [RTY-260011]: update readme .md --- README.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/README.md b/README.md index 276e6fd..e3468e2 100644 --- a/README.md +++ b/README.md @@ -317,7 +317,7 @@ pip install --upgrade dist/*.whl ## License πŸ“œDocs -[run_with_curl](run_with_curl) +[run_with_curl](docs/run_with_curl) Screenshots: Home Page: @@ -334,7 +334,3 @@ No DB Mode: πŸ“œLicense [MIT](LICENSE) - -``` - -``` From b9a21717bccf75aad69bca23115d1155fb479252 Mon Sep 17 00:00:00 2001 From: harshmishra2701 Date: Wed, 11 Feb 2026 20:35:31 +0530 Subject: [PATCH 03/18] [RTY-260011]: feat(url-shortener): add bulk URL shortener script and example JSON input --- group_shorten.ps1 | 134 ++++++++++++++++++++++++++++++++++++++++++++++ request/urls.json | 32 +++++++++++ 2 files changed, 166 insertions(+) create mode 100644 group_shorten.ps1 create mode 100644 request/urls.json diff --git a/group_shorten.ps1 b/group_shorten.ps1 new file mode 100644 index 0000000..2ecaa87 --- /dev/null +++ b/group_shorten.ps1 @@ -0,0 +1,134 @@ +<# +========================================================= + PowerShell Script: group_shorten.ps1 +--------------------------------------------------------- + Description: + Bulk URL shortener client for Tiny API. + Reads a JSON file containing multiple URLs and sends + them one-by-one to the /api/shorten endpoint. + +--------------------------------------------------------- + HOW TO RUN (Windows PowerShell TERMINAL) +--------------------------------------------------------- + + 1. Open PowerShell as Administrator + + 2. Allow script execution (one-time): + Set-ExecutionPolicy RemoteSigned + + 3. Navigate to project root: + cd path\to\your\project + + 4. Run with JSON file: + .\group_shorten.ps1 -file ".\request\mixed_urls.json" + + 5. Or run without argument (interactive): + .\group_shorten.ps1 + + +--------------------------------------------------------- + HOW TO RUN (Windows PowerShell ISE) +--------------------------------------------------------- + + Method 1: Run with parameter + -------------------------------- + 1. Open Windows PowerShell ISE + 2. Open this script file (group_shorten.ps1) + 3. In the top menu, click: + File β†’ New PowerShell Tab + 4. In the console pane (bottom), run: + .\group_shorten.ps1 -file ".\request\mixed_urls.json" + + Method 2: Run interactively + -------------------------------- + 1. Open Windows PowerShell ISE + 2. Open this script file + 3. Press the green ▢️ Run Script button + 4. When prompted, enter the JSON file path + +--------------------------------------------------------- + Expected JSON input format: + [ + { "url": "https://example.com" }, + { "url": "https://google.com" } + ] +========================================================= +#> + +param ( + [string]$file +) + +# Ask for file path if not provided +if (-not $file) { + $file = Read-Host "Enter path to JSON file" +} + +# Validate file +if (-not (Test-Path $file)) { + Write-Host "❌ File not found: $file" -ForegroundColor Red + exit 1 +} + +# Load JSON +try { + $data = Get-Content $file -Raw | ConvertFrom-Json +} +catch { + Write-Host "❌ Invalid JSON file" -ForegroundColor Red + exit 1 +} + +$apiUrl = "http://127.0.0.1:8001/api/v1/shorten" + +Write-Host "`nπŸš€ Processing URLs..." -ForegroundColor Cyan + +foreach ($item in $data) { + + # Support string + object format + $url = if ($item -is [string]) { $item } else { $item.url } + + if (-not $url) { + Write-Host "⚠️ Empty URL, skipped" -ForegroundColor Yellow + continue + } + + # -------- LOCAL URL VALIDATION -------- + if ($url -notmatch '^[a-zA-Z]+://') { + Write-Host "❌ ERROR:" $url "- Missing protocol (http/https)" -ForegroundColor yellow + continue + } + + if ($url -notmatch '^https?://') { + Write-Host "❌ ERROR:" $url "- Unsupported protocol" -ForegroundColor yellow + continue + } + + if ($url -match 'https?:/[^/]') { + Write-Host "❌ ERROR:" $url "- Malformed URL (missing /)" -ForegroundColor yellow + continue + } + + if ($url -match '\.\.') { + Write-Host "❌ ERROR:" $url "- Invalid domain format" -ForegroundColor yellow + continue + } + + # -------- API REQUEST -------- + $body = @{ url = $url } | ConvertTo-Json + + try { + $response = Invoke-RestMethod ` + -Uri $apiUrl ` + -Method POST ` + -ContentType "application/json" ` + -Body $body + + Write-Host "βœ… SUCCESS:" $url "β†’" $response.short_code -ForegroundColor Green + } + catch { + Write-Host "❌ ERROR:" $url "- Rejected by API" -ForegroundColor Yellow + } +} + +Write-Host "`nπŸŽ‰ Done!" -ForegroundColor Cyan diff --git a/request/urls.json b/request/urls.json new file mode 100644 index 0000000..5d8e5a7 --- /dev/null +++ b/request/urls.json @@ -0,0 +1,32 @@ +[ + { + "url": "https://example.com" + }, + { + "url": "https://google.com" + }, + { + "url": "htt//github.com" + }, + { + "url": "https://stackoverflow.com" + }, + { + "url": "https://microsoft.com" + }, + { + "url": "https://apple.com" + }, + { + "url": "https:openai.com" + }, + { + "url": "https://python.org" + }, + { + "url": "https://fastapi.tiangolo.com" + }, + { + "url": "https://docs.github.com" + } +] \ No newline at end of file From 4804e3cb62003ed2a14743651973d4dd0dc3a706 Mon Sep 17 00:00:00 2001 From: RecursiveZero Date: Thu, 12 Feb 2026 00:33:52 +0530 Subject: [PATCH 04/18] update the UI --- app/main.py | 25 ++- app/static/css/tiny.css | 208 +++++++++++++++++++ app/static/qr/Q2RGq9.png | Bin 0 -> 41000 bytes app/templates/index.html | 424 +++++++++----------------------------- app/templates/layout.html | 37 ++++ app/templates/xindex.html | 348 +++++++++++++++++++++++++++++++ poetry.lock | 411 +++++++++++++++++++++++------------- 7 files changed, 972 insertions(+), 481 deletions(-) create mode 100644 app/static/css/tiny.css create mode 100644 app/static/qr/Q2RGq9.png create mode 100644 app/templates/layout.html create mode 100644 app/templates/xindex.html diff --git a/app/main.py b/app/main.py index 15c0519..a15988d 100644 --- a/app/main.py +++ b/app/main.py @@ -57,11 +57,8 @@ async def lifespan(app: FastAPI): app = FastAPI(title="TinyURL", lifespan=lifespan) - - app.add_middleware(SessionMiddleware, secret_key="super-secret-key") - BASE_DIR = Path(__file__).resolve().parent STATIC_DIR = BASE_DIR / "static" @@ -82,6 +79,7 @@ def build_short_url(short_code: str, request_host_url: str) -> str: async def index(request: Request): session = request.session + # Pop session variables new_short_url = session.pop("new_short_url", None) qr_enabled = session.pop("qr_enabled", False) qr_type = session.pop("qr_type", "short") @@ -93,16 +91,25 @@ async def index(request: Request): qr_image = None qr_data = None + # --- RESTORED GENERATION LOGIC --- if qr_enabled and new_short_url and short_code: qr_data = new_short_url if qr_type == "short" else original_url qr_filename = f"{short_code}.png" - generate_qr_with_logo(qr_data, qr_filename) + + # Ensure the directory exists (Ubuntu best practice) + qr_dir = STATIC_DIR / "qr" + qr_dir.mkdir(parents=True, exist_ok=True) + + # Generate the physical file + generate_qr_with_logo(qr_data, str(qr_dir / qr_filename)) qr_image = f"/static/qr/{qr_filename}" + # -------------------------------- + # Fetch URLs for the Bento Grid all_urls = [] if db_available(request) and db_data.urls is not None: try: - all_urls = list(db_data.urls.find().sort("created_at", -1)) + all_urls = list(db_data.urls.find().sort("created_at", -1).limit(10)) except PyMongoError: all_urls = [] @@ -112,12 +119,12 @@ async def index(request: Request): "request": request, "urls": all_urls, "new_short_url": new_short_url, - "error": error, - "info_message": info_message, + "qr_image": qr_image, "qr_data": qr_data, "qr_enabled": qr_enabled, - "qr_type": qr_type, - "qr_image": qr_image, + "original_url": original_url, + "error": error, + "info_message": info_message, "db_available": db_available(request), }, ) diff --git a/app/static/css/tiny.css b/app/static/css/tiny.css new file mode 100644 index 0000000..4e9208c --- /dev/null +++ b/app/static/css/tiny.css @@ -0,0 +1,208 @@ +:root { + --bg: #0a0a0c; + --glass: rgba(255, 255, 255, 0.03); + --glass-border: rgba(255, 255, 255, 0.07); + --accent: #6366f1; + --text-primary: #ffffff; + --text-secondary: #a1a1aa; +} + +body { + background: var(--bg); + color: var(--text-primary); + font-family: 'Inter', system-ui, sans-serif; + margin: 0; + overflow-x: hidden; + background-image: radial-gradient(circle at 50% -20%, #1e1e2e 0%, transparent 50%); +} + +.main-layout { + max-width: 900px; + margin: 0 auto; + padding: 6rem 1.5rem; + display: flex; + flex-direction: column; + align-items: center; + gap: 2rem; +} + +/* --- LARGE HERO INPUT --- */ +.hero-input-card { + width: 100%; + background: var(--glass); + backdrop-filter: blur(20px); + border: 1px solid var(--glass-border); + border-radius: 2.5rem; + padding: 3rem; + box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5); +} + +.hero-input-card h1 { + font-size: 3.5rem; + font-weight: 800; + letter-spacing: -3px; + margin: 0 0 1.5rem 0; + text-align: center; + background: linear-gradient(to right, #fff, #666); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +} + +.input-wrapper { + position: relative; + display: flex; + gap: 1rem; +} + +.input-wrapper input { + flex: 1; + background: rgba(255, 255, 255, 0.05); + border: 1px solid var(--glass-border); + border-radius: 1.5rem; + padding: 1.5rem 2rem; + color: white; + font-size: 1.2rem; + outline: none; + transition: all 0.3s ease; +} + +.input-wrapper input:focus { + border-color: var(--accent); + background: rgba(255, 255, 255, 0.08); + box-shadow: 0 0 0 4px rgba(99, 102, 241, 0.1); +} + +/* --- OUTPUT CARD (Immediate Result) --- */ +.result-card { + width: 100%; + background: linear-gradient(145deg, rgba(99, 102, 241, 0.1), rgba(0, 0, 0, 0)); + border: 1px solid rgba(99, 102, 241, 0.2); + border-radius: 2rem; + padding: 2rem; + display: flex; + align-items: center; + justify-content: space-between; + animation: slideUp 0.6s cubic-bezier(0.16, 1, 0.3, 1); +} + +/* --- SCROLLABLE RECENT TRAY --- */ +.recent-tray { + width: 100%; + margin-top: 4rem; +} + +.recent-header { + display: flex; + justify-content: space-between; + margin-bottom: 1rem; + padding: 0 1rem; +} + +.scroll-container { + display: flex; + gap: 1rem; + overflow-x: auto; + padding: 1rem; + mask-image: linear-gradient(to right, black 85%, transparent); +} + +.scroll-container::-webkit-scrollbar { + display: none; +} + +.recent-item { + min-width: 250px; + background: var(--glass); + border: 1px solid var(--glass-border); + padding: 1.5rem; + border-radius: 1.5rem; + font-size: 0.9rem; +} + +@keyframes slideUp { + from { + opacity: 0; + transform: translateY(20px); + } + + to { + opacity: 1; + transform: translateY(0); + } +} + +/* --- LARGE FOOTER --- */ +.big-footer { + background: rgba(255, 255, 255, 0.01); + border-top: 1px solid var(--border); + padding: 6rem 0 3rem 0; + margin-top: 8rem; +} + +.footer-grid { + max-width: 1200px; + margin: 0 auto; + padding: 0 1.5rem; + display: grid; + grid-template-columns: 2fr 1fr 1fr 1fr; + gap: 4rem; +} + +.footer-brand h3 { + font-size: 1.5rem; + margin: 0 0 1rem 0; + letter-spacing: -1px; +} + +.footer-brand p { + color: var(--text-dim); + line-height: 1.6; + max-width: 320px; +} + +.footer-col h4 { + font-size: 0.85rem; + text-transform: uppercase; + letter-spacing: 0.1em; + margin-bottom: 1.5rem; + color: var(--text); +} + +.footer-col ul { + list-style: none; + padding: 0; + margin: 0; +} + +.footer-col ul li { + margin-bottom: 0.8rem; +} + +.footer-col ul li a { + color: var(--text-dim); + text-decoration: none; + font-size: 0.9rem; + transition: color 0.2s; +} + +.footer-col ul li a:hover { + color: var(--accent); +} + +.footer-bottom { + max-width: 1200px; + margin: 4rem auto 0; + padding: 2rem 1.5rem 0; + border-top: 1px solid var(--border); + display: flex; + justify-content: space-between; + align-items: center; + color: var(--text-dim); + font-size: 0.8rem; +} + +@media (max-width: 900px) { + .footer-grid { + grid-template-columns: 1fr 1fr; + } +} \ No newline at end of file diff --git a/app/static/qr/Q2RGq9.png b/app/static/qr/Q2RGq9.png new file mode 100644 index 0000000000000000000000000000000000000000..49e0be6e53d5c47614c94a7d78267c7ec789519e GIT binary patch literal 41000 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¥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 - - - - - - tiny URL - - - - - - - - - -
-
- - tiny URL -
- - -
- -
-
- - - -
-
+{% extends "layout.html" %} + +{% block content %} +
+
+

tiny URL

+
+
+ + +
+
+ + Analytics Enabled +
+
+
+ {% if new_short_url %} +
+
+ {% if qr_image %} + + {% endif %} +
+ Ready + +
- {% if not db_available %} -
- ⚠️ Running in Limited Mode (No Database) +
+ + {% if qr_image %}Download + QR{% endif %}
- {% endif %} +
+ {% endif %} - -
-

Shorten Your URLs with Precision

-

Transform long links into short, memorable URLs.

+
+
+

Recently Shortened

+ View History β†’
- - -
- - -
-
- - -
-
- - - - - - - -
- - -
- {% if error %} -
{{ error }}
- {% endif %} - {% if info_message %} -
- {{ info_message }} -
- {% endif %} - - - -
- - - - -
- -
-
- - - {% if new_short_url %} -
-
-
βœ“
-
-
Your shortened URL is ready!
- -
Copy or share your short link, download the - QR code, or view details.
-
-
-
- -
- - {{ new_short_url }} - - - - -
-
- - - -
- -
- {% if qr_data %} - QR Code - - {% else %} -
-
- {% endif %} - -
Original URL
- {% set display_url = original_url or (urls[0].original_url if urls|length > 0 else '') %} - - {% if display_url %} - - {% endif %} - - -
-
-
-
-
-
-
-
- - - - -
- - -
+
+ {% for url in urls %} +
+
/{{ url.short_code }}
+
+ {{ url.original_url }}
+ {% endfor %}
- {% endif %} - - - - -
+
- -
- +
+ + + + + + + + + + \ No newline at end of file diff --git a/poetry.lock b/poetry.lock index 1a589f1..1ecedbd 100644 --- a/poetry.lock +++ b/poetry.lock @@ -430,62 +430,62 @@ markers = {main = "sys_platform == \"win32\" or platform_system == \"Windows\"", [[package]] name = "cryptography" -version = "46.0.4" +version = "46.0.5" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = "!=3.9.0,!=3.9.1,>=3.8" groups = ["dev"] markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\"" files = [ - {file = "cryptography-46.0.4-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:281526e865ed4166009e235afadf3a4c4cba6056f99336a99efba65336fd5485"}, - {file = "cryptography-46.0.4-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5f14fba5bf6f4390d7ff8f086c566454bff0411f6d8aa7af79c88b6f9267aecc"}, - {file = "cryptography-46.0.4-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:47bcd19517e6389132f76e2d5303ded6cf3f78903da2158a671be8de024f4cd0"}, - {file = "cryptography-46.0.4-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:01df4f50f314fbe7009f54046e908d1754f19d0c6d3070df1e6268c5a4af09fa"}, - {file = "cryptography-46.0.4-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5aa3e463596b0087b3da0dbe2b2487e9fc261d25da85754e30e3b40637d61f81"}, - {file = "cryptography-46.0.4-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:0a9ad24359fee86f131836a9ac3bffc9329e956624a2d379b613f8f8abaf5255"}, - {file = "cryptography-46.0.4-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:dc1272e25ef673efe72f2096e92ae39dea1a1a450dd44918b15351f72c5a168e"}, - {file = "cryptography-46.0.4-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:de0f5f4ec8711ebc555f54735d4c673fc34b65c44283895f1a08c2b49d2fd99c"}, - {file = "cryptography-46.0.4-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:eeeb2e33d8dbcccc34d64651f00a98cb41b2dc69cef866771a5717e6734dfa32"}, - {file = "cryptography-46.0.4-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:3d425eacbc9aceafd2cb429e42f4e5d5633c6f873f5e567077043ef1b9bbf616"}, - {file = "cryptography-46.0.4-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:91627ebf691d1ea3976a031b61fb7bac1ccd745afa03602275dda443e11c8de0"}, - {file = "cryptography-46.0.4-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2d08bc22efd73e8854b0b7caff402d735b354862f1145d7be3b9c0f740fef6a0"}, - {file = "cryptography-46.0.4-cp311-abi3-win32.whl", hash = "sha256:82a62483daf20b8134f6e92898da70d04d0ef9a75829d732ea1018678185f4f5"}, - {file = "cryptography-46.0.4-cp311-abi3-win_amd64.whl", hash = "sha256:6225d3ebe26a55dbc8ead5ad1265c0403552a63336499564675b29eb3184c09b"}, - {file = "cryptography-46.0.4-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:485e2b65d25ec0d901bca7bcae0f53b00133bf3173916d8e421f6fddde103908"}, - {file = "cryptography-46.0.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:078e5f06bd2fa5aea5a324f2a09f914b1484f1d0c2a4d6a8a28c74e72f65f2da"}, - {file = "cryptography-46.0.4-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:dce1e4f068f03008da7fa51cc7abc6ddc5e5de3e3d1550334eaf8393982a5829"}, - {file = "cryptography-46.0.4-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:2067461c80271f422ee7bdbe79b9b4be54a5162e90345f86a23445a0cf3fd8a2"}, - {file = "cryptography-46.0.4-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:c92010b58a51196a5f41c3795190203ac52edfd5dc3ff99149b4659eba9d2085"}, - {file = "cryptography-46.0.4-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:829c2b12bbc5428ab02d6b7f7e9bbfd53e33efd6672d21341f2177470171ad8b"}, - {file = "cryptography-46.0.4-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:62217ba44bf81b30abaeda1488686a04a702a261e26f87db51ff61d9d3510abd"}, - {file = "cryptography-46.0.4-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:9c2da296c8d3415b93e6053f5a728649a87a48ce084a9aaf51d6e46c87c7f2d2"}, - {file = "cryptography-46.0.4-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:9b34d8ba84454641a6bf4d6762d15847ecbd85c1316c0a7984e6e4e9f748ec2e"}, - {file = "cryptography-46.0.4-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:df4a817fa7138dd0c96c8c8c20f04b8aaa1fac3bbf610913dcad8ea82e1bfd3f"}, - {file = "cryptography-46.0.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:b1de0ebf7587f28f9190b9cb526e901bf448c9e6a99655d2b07fff60e8212a82"}, - {file = "cryptography-46.0.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9b4d17bc7bd7cdd98e3af40b441feaea4c68225e2eb2341026c84511ad246c0c"}, - {file = "cryptography-46.0.4-cp314-cp314t-win32.whl", hash = "sha256:c411f16275b0dea722d76544a61d6421e2cc829ad76eec79280dbdc9ddf50061"}, - {file = "cryptography-46.0.4-cp314-cp314t-win_amd64.whl", hash = "sha256:728fedc529efc1439eb6107b677f7f7558adab4553ef8669f0d02d42d7b959a7"}, - {file = "cryptography-46.0.4-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a9556ba711f7c23f77b151d5798f3ac44a13455cc68db7697a1096e6d0563cab"}, - {file = "cryptography-46.0.4-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8bf75b0259e87fa70bddc0b8b4078b76e7fd512fd9afae6c1193bcf440a4dbef"}, - {file = "cryptography-46.0.4-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3c268a3490df22270955966ba236d6bc4a8f9b6e4ffddb78aac535f1a5ea471d"}, - {file = "cryptography-46.0.4-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:812815182f6a0c1d49a37893a303b44eaac827d7f0d582cecfc81b6427f22973"}, - {file = "cryptography-46.0.4-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:a90e43e3ef65e6dcf969dfe3bb40cbf5aef0d523dff95bfa24256be172a845f4"}, - {file = "cryptography-46.0.4-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a05177ff6296644ef2876fce50518dffb5bcdf903c85250974fc8bc85d54c0af"}, - {file = "cryptography-46.0.4-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:daa392191f626d50f1b136c9b4cf08af69ca8279d110ea24f5c2700054d2e263"}, - {file = "cryptography-46.0.4-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e07ea39c5b048e085f15923511d8121e4a9dc45cee4e3b970ca4f0d338f23095"}, - {file = "cryptography-46.0.4-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:d5a45ddc256f492ce42a4e35879c5e5528c09cd9ad12420828c972951d8e016b"}, - {file = "cryptography-46.0.4-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:6bb5157bf6a350e5b28aee23beb2d84ae6f5be390b2f8ee7ea179cda077e1019"}, - {file = "cryptography-46.0.4-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:dd5aba870a2c40f87a3af043e0dee7d9eb02d4aff88a797b48f2b43eff8c3ab4"}, - {file = "cryptography-46.0.4-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:93d8291da8d71024379ab2cb0b5c57915300155ad42e07f76bea6ad838d7e59b"}, - {file = "cryptography-46.0.4-cp38-abi3-win32.whl", hash = "sha256:0563655cb3c6d05fb2afe693340bc050c30f9f34e15763361cf08e94749401fc"}, - {file = "cryptography-46.0.4-cp38-abi3-win_amd64.whl", hash = "sha256:fa0900b9ef9c49728887d1576fd8d9e7e3ea872fa9b25ef9b64888adc434e976"}, - {file = "cryptography-46.0.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:766330cce7416c92b5e90c3bb71b1b79521760cdcfc3a6a1a182d4c9fab23d2b"}, - {file = "cryptography-46.0.4-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c236a44acfb610e70f6b3e1c3ca20ff24459659231ef2f8c48e879e2d32b73da"}, - {file = "cryptography-46.0.4-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:8a15fb869670efa8f83cbffbc8753c1abf236883225aed74cd179b720ac9ec80"}, - {file = "cryptography-46.0.4-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:fdc3daab53b212472f1524d070735b2f0c214239df131903bae1d598016fa822"}, - {file = "cryptography-46.0.4-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:44cc0675b27cadb71bdbb96099cca1fa051cd11d2ade09e5cd3a2edb929ed947"}, - {file = "cryptography-46.0.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:be8c01a7d5a55f9a47d1888162b76c8f49d62b234d88f0ff91a9fbebe32ffbc3"}, - {file = "cryptography-46.0.4.tar.gz", hash = "sha256:bfd019f60f8abc2ed1b9be4ddc21cfef059c841d86d710bb69909a688cbb8f59"}, + {file = "cryptography-46.0.5-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:351695ada9ea9618b3500b490ad54c739860883df6c1f555e088eaf25b1bbaad"}, + {file = "cryptography-46.0.5-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c18ff11e86df2e28854939acde2d003f7984f721eba450b56a200ad90eeb0e6b"}, + {file = "cryptography-46.0.5-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d7e3d356b8cd4ea5aff04f129d5f66ebdc7b6f8eae802b93739ed520c47c79b"}, + {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:50bfb6925eff619c9c023b967d5b77a54e04256c4281b0e21336a130cd7fc263"}, + {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:803812e111e75d1aa73690d2facc295eaefd4439be1023fefc4995eaea2af90d"}, + {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ee190460e2fbe447175cda91b88b84ae8322a104fc27766ad09428754a618ed"}, + {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:f145bba11b878005c496e93e257c1e88f154d278d2638e6450d17e0f31e558d2"}, + {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e9251e3be159d1020c4030bd2e5f84d6a43fe54b6c19c12f51cde9542a2817b2"}, + {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:47fb8a66058b80e509c47118ef8a75d14c455e81ac369050f20ba0d23e77fee0"}, + {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:4c3341037c136030cb46e4b1e17b7418ea4cbd9dd207e4a6f3b2b24e0d4ac731"}, + {file = "cryptography-46.0.5-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:890bcb4abd5a2d3f852196437129eb3667d62630333aacc13dfd470fad3aaa82"}, + {file = "cryptography-46.0.5-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:80a8d7bfdf38f87ca30a5391c0c9ce4ed2926918e017c29ddf643d0ed2778ea1"}, + {file = "cryptography-46.0.5-cp311-abi3-win32.whl", hash = "sha256:60ee7e19e95104d4c03871d7d7dfb3d22ef8a9b9c6778c94e1c8fcc8365afd48"}, + {file = "cryptography-46.0.5-cp311-abi3-win_amd64.whl", hash = "sha256:38946c54b16c885c72c4f59846be9743d699eee2b69b6988e0a00a01f46a61a4"}, + {file = "cryptography-46.0.5-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:94a76daa32eb78d61339aff7952ea819b1734b46f73646a07decb40e5b3448e2"}, + {file = "cryptography-46.0.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5be7bf2fb40769e05739dd0046e7b26f9d4670badc7b032d6ce4db64dddc0678"}, + {file = "cryptography-46.0.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fe346b143ff9685e40192a4960938545c699054ba11d4f9029f94751e3f71d87"}, + {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:c69fd885df7d089548a42d5ec05be26050ebcd2283d89b3d30676eb32ff87dee"}, + {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:8293f3dea7fc929ef7240796ba231413afa7b68ce38fd21da2995549f5961981"}, + {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:1abfdb89b41c3be0365328a410baa9df3ff8a9110fb75e7b52e66803ddabc9a9"}, + {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:d66e421495fdb797610a08f43b05269e0a5ea7f5e652a89bfd5a7d3c1dee3648"}, + {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:4e817a8920bfbcff8940ecfd60f23d01836408242b30f1a708d93198393a80b4"}, + {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:68f68d13f2e1cb95163fa3b4db4bf9a159a418f5f6e7242564fc75fcae667fd0"}, + {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:a3d1fae9863299076f05cb8a778c467578262fae09f9dc0ee9b12eb4268ce663"}, + {file = "cryptography-46.0.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c4143987a42a2397f2fc3b4d7e3a7d313fbe684f67ff443999e803dd75a76826"}, + {file = "cryptography-46.0.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7d731d4b107030987fd61a7f8ab512b25b53cef8f233a97379ede116f30eb67d"}, + {file = "cryptography-46.0.5-cp314-cp314t-win32.whl", hash = "sha256:c3bcce8521d785d510b2aad26ae2c966092b7daa8f45dd8f44734a104dc0bc1a"}, + {file = "cryptography-46.0.5-cp314-cp314t-win_amd64.whl", hash = "sha256:4d8ae8659ab18c65ced284993c2265910f6c9e650189d4e3f68445ef82a810e4"}, + {file = "cryptography-46.0.5-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:4108d4c09fbbf2789d0c926eb4152ae1760d5a2d97612b92d508d96c861e4d31"}, + {file = "cryptography-46.0.5-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d1f30a86d2757199cb2d56e48cce14deddf1f9c95f1ef1b64ee91ea43fe2e18"}, + {file = "cryptography-46.0.5-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:039917b0dc418bb9f6edce8a906572d69e74bd330b0b3fea4f79dab7f8ddd235"}, + {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ba2a27ff02f48193fc4daeadf8ad2590516fa3d0adeeb34336b96f7fa64c1e3a"}, + {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:61aa400dce22cb001a98014f647dc21cda08f7915ceb95df0c9eaf84b4b6af76"}, + {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ce58ba46e1bc2aac4f7d9290223cead56743fa6ab94a5d53292ffaac6a91614"}, + {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:420d0e909050490d04359e7fdb5ed7e667ca5c3c402b809ae2563d7e66a92229"}, + {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:582f5fcd2afa31622f317f80426a027f30dc792e9c80ffee87b993200ea115f1"}, + {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:bfd56bb4b37ed4f330b82402f6f435845a5f5648edf1ad497da51a8452d5d62d"}, + {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:a3d507bb6a513ca96ba84443226af944b0f7f47dcc9a399d110cd6146481d24c"}, + {file = "cryptography-46.0.5-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9f16fbdf4da055efb21c22d81b89f155f02ba420558db21288b3d0035bafd5f4"}, + {file = "cryptography-46.0.5-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ced80795227d70549a411a4ab66e8ce307899fad2220ce5ab2f296e687eacde9"}, + {file = "cryptography-46.0.5-cp38-abi3-win32.whl", hash = "sha256:02f547fce831f5096c9a567fd41bc12ca8f11df260959ecc7c3202555cc47a72"}, + {file = "cryptography-46.0.5-cp38-abi3-win_amd64.whl", hash = "sha256:556e106ee01aa13484ce9b0239bca667be5004efb0aabbed28d353df86445595"}, + {file = "cryptography-46.0.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:3b4995dc971c9fb83c25aa44cf45f02ba86f71ee600d81091c2f0cbae116b06c"}, + {file = "cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:bc84e875994c3b445871ea7181d424588171efec3e185dced958dad9e001950a"}, + {file = "cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:2ae6971afd6246710480e3f15824ed3029a60fc16991db250034efd0b9fb4356"}, + {file = "cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:d861ee9e76ace6cf36a6a89b959ec08e7bc2493ee39d07ffe5acb23ef46d27da"}, + {file = "cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:2b7a67c9cd56372f3249b39699f2ad479f6991e62ea15800973b956f4b73e257"}, + {file = "cryptography-46.0.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:8456928655f856c6e1533ff59d5be76578a7157224dbd9ce6872f25055ab9ab7"}, + {file = "cryptography-46.0.5.tar.gz", hash = "sha256:abace499247268e3757271b2f1e244b36b06f8515cf27c4d49468fc9eb16e93d"}, ] [package.dependencies] @@ -499,7 +499,7 @@ nox = ["nox[uv] (>=2024.4.15)"] pep8test = ["check-sdist", "click (>=8.0.1)", "mypy (>=1.14)", "ruff (>=0.11.11)"] sdist = ["build (>=1.0.0)"] ssh = ["bcrypt (>=3.1.5)"] -test = ["certifi (>=2024)", "cryptography-vectors (==46.0.4)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] +test = ["certifi (>=2024)", "cryptography-vectors (==46.0.5)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] test-randomorder = ["pytest-randomly"] [[package]] @@ -592,14 +592,14 @@ test = ["pytest (>=6)"] [[package]] name = "fastapi" -version = "0.128.7" +version = "0.128.8" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "fastapi-0.128.7-py3-none-any.whl", hash = "sha256:6bd9bd31cb7047465f2d3fa3ba3f33b0870b17d4eaf7cdb36d1576ab060ad662"}, - {file = "fastapi-0.128.7.tar.gz", hash = "sha256:783c273416995486c155ad2c0e2b45905dedfaf20b9ef8d9f6a9124670639a24"}, + {file = "fastapi-0.128.8-py3-none-any.whl", hash = "sha256:5618f492d0fe973a778f8fec97723f598aa9deee495040a8d51aaf3cf123ecf1"}, + {file = "fastapi-0.128.8.tar.gz", hash = "sha256:3171f9f328c4a218f0a8d2ba8310ac3a55d1ee12c28c949650288aee25966007"}, ] [package.dependencies] @@ -812,6 +812,24 @@ files = [ test = ["async-timeout ; python_version < \"3.11\"", "pytest", "pytest-asyncio (>=0.17)", "pytest-trio", "testpath", "trio"] trio = ["trio"] +[[package]] +name = "jinja2" +version = "3.1.6" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, + {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + [[package]] name = "keyring" version = "25.7.0" @@ -972,6 +990,105 @@ profiling = ["gprof2dot"] rtd = ["ipykernel", "jupyter_sphinx", "mdit-py-plugins (>=0.5.0)", "myst-parser", "pyyaml", "sphinx", "sphinx-book-theme (>=1.0,<2.0)", "sphinx-copybutton", "sphinx-design"] testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions", "requests"] +[[package]] +name = "markupsafe" +version = "3.0.3" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559"}, + {file = "markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1"}, + {file = "markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa"}, + {file = "markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8"}, + {file = "markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1"}, + {file = "markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad"}, + {file = "markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a"}, + {file = "markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19"}, + {file = "markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01"}, + {file = "markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c"}, + {file = "markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e"}, + {file = "markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b"}, + {file = "markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d"}, + {file = "markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c"}, + {file = "markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f"}, + {file = "markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795"}, + {file = "markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12"}, + {file = "markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed"}, + {file = "markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5"}, + {file = "markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485"}, + {file = "markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73"}, + {file = "markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287"}, + {file = "markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe"}, + {file = "markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe"}, + {file = "markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9"}, + {file = "markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581"}, + {file = "markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4"}, + {file = "markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab"}, + {file = "markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa"}, + {file = "markupsafe-3.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26"}, + {file = "markupsafe-3.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8fc20152abba6b83724d7ff268c249fa196d8259ff481f3b1476383f8f24e42"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:949b8d66bc381ee8b007cd945914c721d9aba8e27f71959d750a46f7c282b20b"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:3537e01efc9d4dccdf77221fb1cb3b8e1a38d5428920e0657ce299b20324d758"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:591ae9f2a647529ca990bc681daebdd52c8791ff06c2bfa05b65163e28102ef2"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a320721ab5a1aba0a233739394eb907f8c8da5c98c9181d1161e77a0c8e36f2d"}, + {file = "markupsafe-3.0.3-cp39-cp39-win32.whl", hash = "sha256:df2449253ef108a379b8b5d6b43f4b1a8e81a061d6537becd5582fba5f9196d7"}, + {file = "markupsafe-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:7c3fb7d25180895632e5d3148dbdc29ea38ccb7fd210aa27acbd1201a1902c6e"}, + {file = "markupsafe-3.0.3-cp39-cp39-win_arm64.whl", hash = "sha256:38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8"}, + {file = "markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698"}, +] + [[package]] name = "mccabe" version = "0.7.0" @@ -1241,103 +1358,103 @@ tests = ["pytest (>=9)", "typing-extensions (>=4.15)"] [[package]] name = "pillow" -version = "12.1.0" +version = "12.1.1" description = "Python Imaging Library (fork)" optional = false python-versions = ">=3.10" groups = ["main"] files = [ - {file = "pillow-12.1.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:fb125d860738a09d363a88daa0f59c4533529a90e564785e20fe875b200b6dbd"}, - {file = "pillow-12.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cad302dc10fac357d3467a74a9561c90609768a6f73a1923b0fd851b6486f8b0"}, - {file = "pillow-12.1.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a40905599d8079e09f25027423aed94f2823adaf2868940de991e53a449e14a8"}, - {file = "pillow-12.1.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:92a7fe4225365c5e3a8e598982269c6d6698d3e783b3b1ae979e7819f9cd55c1"}, - {file = "pillow-12.1.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f10c98f49227ed8383d28174ee95155a675c4ed7f85e2e573b04414f7e371bda"}, - {file = "pillow-12.1.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8637e29d13f478bc4f153d8daa9ffb16455f0a6cb287da1b432fdad2bfbd66c7"}, - {file = "pillow-12.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:21e686a21078b0f9cb8c8a961d99e6a4ddb88e0fc5ea6e130172ddddc2e5221a"}, - {file = "pillow-12.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2415373395a831f53933c23ce051021e79c8cd7979822d8cc478547a3f4da8ef"}, - {file = "pillow-12.1.0-cp310-cp310-win32.whl", hash = "sha256:e75d3dba8fc1ddfec0cd752108f93b83b4f8d6ab40e524a95d35f016b9683b09"}, - {file = "pillow-12.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:64efdf00c09e31efd754448a383ea241f55a994fd079866b92d2bbff598aad91"}, - {file = "pillow-12.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:f188028b5af6b8fb2e9a76ac0f841a575bd1bd396e46ef0840d9b88a48fdbcea"}, - {file = "pillow-12.1.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:a83e0850cb8f5ac975291ebfc4170ba481f41a28065277f7f735c202cd8e0af3"}, - {file = "pillow-12.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b6e53e82ec2db0717eabb276aa56cf4e500c9a7cec2c2e189b55c24f65a3e8c0"}, - {file = "pillow-12.1.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:40a8e3b9e8773876d6e30daed22f016509e3987bab61b3b7fe309d7019a87451"}, - {file = "pillow-12.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:800429ac32c9b72909c671aaf17ecd13110f823ddb7db4dfef412a5587c2c24e"}, - {file = "pillow-12.1.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0b022eaaf709541b391ee069f0022ee5b36c709df71986e3f7be312e46f42c84"}, - {file = "pillow-12.1.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1f345e7bc9d7f368887c712aa5054558bad44d2a301ddf9248599f4161abc7c0"}, - {file = "pillow-12.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d70347c8a5b7ccd803ec0c85c8709f036e6348f1e6a5bf048ecd9c64d3550b8b"}, - {file = "pillow-12.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1fcc52d86ce7a34fd17cb04e87cfdb164648a3662a6f20565910a99653d66c18"}, - {file = "pillow-12.1.0-cp311-cp311-win32.whl", hash = "sha256:3ffaa2f0659e2f740473bcf03c702c39a8d4b2b7ffc629052028764324842c64"}, - {file = "pillow-12.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:806f3987ffe10e867bab0ddad45df1148a2b98221798457fa097ad85d6e8bc75"}, - {file = "pillow-12.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:9f5fefaca968e700ad1a4a9de98bf0869a94e397fe3524c4c9450c1445252304"}, - {file = "pillow-12.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a332ac4ccb84b6dde65dbace8431f3af08874bf9770719d32a635c4ef411b18b"}, - {file = "pillow-12.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:907bfa8a9cb790748a9aa4513e37c88c59660da3bcfffbd24a7d9e6abf224551"}, - {file = "pillow-12.1.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:efdc140e7b63b8f739d09a99033aa430accce485ff78e6d311973a67b6bf3208"}, - {file = "pillow-12.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bef9768cab184e7ae6e559c032e95ba8d07b3023c289f79a2bd36e8bf85605a5"}, - {file = "pillow-12.1.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:742aea052cf5ab5034a53c3846165bc3ce88d7c38e954120db0ab867ca242661"}, - {file = "pillow-12.1.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a6dfc2af5b082b635af6e08e0d1f9f1c4e04d17d4e2ca0ef96131e85eda6eb17"}, - {file = "pillow-12.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:609e89d9f90b581c8d16358c9087df76024cf058fa693dd3e1e1620823f39670"}, - {file = "pillow-12.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:43b4899cfd091a9693a1278c4982f3e50f7fb7cff5153b05174b4afc9593b616"}, - {file = "pillow-12.1.0-cp312-cp312-win32.whl", hash = "sha256:aa0c9cc0b82b14766a99fbe6084409972266e82f459821cd26997a488a7261a7"}, - {file = "pillow-12.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:d70534cea9e7966169ad29a903b99fc507e932069a881d0965a1a84bb57f6c6d"}, - {file = "pillow-12.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:65b80c1ee7e14a87d6a068dd3b0aea268ffcabfe0498d38661b00c5b4b22e74c"}, - {file = "pillow-12.1.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:7b5dd7cbae20285cdb597b10eb5a2c13aa9de6cde9bb64a3c1317427b1db1ae1"}, - {file = "pillow-12.1.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:29a4cef9cb672363926f0470afc516dbf7305a14d8c54f7abbb5c199cd8f8179"}, - {file = "pillow-12.1.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:681088909d7e8fa9e31b9799aaa59ba5234c58e5e4f1951b4c4d1082a2e980e0"}, - {file = "pillow-12.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:983976c2ab753166dc66d36af6e8ec15bb511e4a25856e2227e5f7e00a160587"}, - {file = "pillow-12.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:db44d5c160a90df2d24a24760bbd37607d53da0b34fb546c4c232af7192298ac"}, - {file = "pillow-12.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6b7a9d1db5dad90e2991645874f708e87d9a3c370c243c2d7684d28f7e133e6b"}, - {file = "pillow-12.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6258f3260986990ba2fa8a874f8b6e808cf5abb51a94015ca3dc3c68aa4f30ea"}, - {file = "pillow-12.1.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e115c15e3bc727b1ca3e641a909f77f8ca72a64fff150f666fcc85e57701c26c"}, - {file = "pillow-12.1.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6741e6f3074a35e47c77b23a4e4f2d90db3ed905cb1c5e6e0d49bff2045632bc"}, - {file = "pillow-12.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:935b9d1aed48fcfb3f838caac506f38e29621b44ccc4f8a64d575cb1b2a88644"}, - {file = "pillow-12.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5fee4c04aad8932da9f8f710af2c1a15a83582cfb884152a9caa79d4efcdbf9c"}, - {file = "pillow-12.1.0-cp313-cp313-win32.whl", hash = "sha256:a786bf667724d84aa29b5db1c61b7bfdde380202aaca12c3461afd6b71743171"}, - {file = "pillow-12.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:461f9dfdafa394c59cd6d818bdfdbab4028b83b02caadaff0ffd433faf4c9a7a"}, - {file = "pillow-12.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:9212d6b86917a2300669511ed094a9406888362e085f2431a7da985a6b124f45"}, - {file = "pillow-12.1.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:00162e9ca6d22b7c3ee8e61faa3c3253cd19b6a37f126cad04f2f88b306f557d"}, - {file = "pillow-12.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:7d6daa89a00b58c37cb1747ec9fb7ac3bc5ffd5949f5888657dfddde6d1312e0"}, - {file = "pillow-12.1.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e2479c7f02f9d505682dc47df8c0ea1fc5e264c4d1629a5d63fe3e2334b89554"}, - {file = "pillow-12.1.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f188d580bd870cda1e15183790d1cc2fa78f666e76077d103edf048eed9c356e"}, - {file = "pillow-12.1.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0fde7ec5538ab5095cc02df38ee99b0443ff0e1c847a045554cf5f9af1f4aa82"}, - {file = "pillow-12.1.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0ed07dca4a8464bada6139ab38f5382f83e5f111698caf3191cb8dbf27d908b4"}, - {file = "pillow-12.1.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f45bd71d1fa5e5749587613037b172e0b3b23159d1c00ef2fc920da6f470e6f0"}, - {file = "pillow-12.1.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:277518bf4fe74aa91489e1b20577473b19ee70fb97c374aa50830b279f25841b"}, - {file = "pillow-12.1.0-cp313-cp313t-win32.whl", hash = "sha256:7315f9137087c4e0ee73a761b163fc9aa3b19f5f606a7fc08d83fd3e4379af65"}, - {file = "pillow-12.1.0-cp313-cp313t-win_amd64.whl", hash = "sha256:0ddedfaa8b5f0b4ffbc2fa87b556dc59f6bb4ecb14a53b33f9189713ae8053c0"}, - {file = "pillow-12.1.0-cp313-cp313t-win_arm64.whl", hash = "sha256:80941e6d573197a0c28f394753de529bb436b1ca990ed6e765cf42426abc39f8"}, - {file = "pillow-12.1.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:5cb7bc1966d031aec37ddb9dcf15c2da5b2e9f7cc3ca7c54473a20a927e1eb91"}, - {file = "pillow-12.1.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:97e9993d5ed946aba26baf9c1e8cf18adbab584b99f452ee72f7ee8acb882796"}, - {file = "pillow-12.1.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:414b9a78e14ffeb98128863314e62c3f24b8a86081066625700b7985b3f529bd"}, - {file = "pillow-12.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:e6bdb408f7c9dd2a5ff2b14a3b0bb6d4deb29fb9961e6eb3ae2031ae9a5cec13"}, - {file = "pillow-12.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3413c2ae377550f5487991d444428f1a8ae92784aac79caa8b1e3b89b175f77e"}, - {file = "pillow-12.1.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e5dcbe95016e88437ecf33544ba5db21ef1b8dd6e1b434a2cb2a3d605299e643"}, - {file = "pillow-12.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d0a7735df32ccbcc98b98a1ac785cc4b19b580be1bdf0aeb5c03223220ea09d5"}, - {file = "pillow-12.1.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c27407a2d1b96774cbc4a7594129cc027339fd800cd081e44497722ea1179de"}, - {file = "pillow-12.1.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15c794d74303828eaa957ff8070846d0efe8c630901a1c753fdc63850e19ecd9"}, - {file = "pillow-12.1.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c990547452ee2800d8506c4150280757f88532f3de2a58e3022e9b179107862a"}, - {file = "pillow-12.1.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b63e13dd27da389ed9475b3d28510f0f954bca0041e8e551b2a4eb1eab56a39a"}, - {file = "pillow-12.1.0-cp314-cp314-win32.whl", hash = "sha256:1a949604f73eb07a8adab38c4fe50791f9919344398bdc8ac6b307f755fc7030"}, - {file = "pillow-12.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:4f9f6a650743f0ddee5593ac9e954ba1bdbc5e150bc066586d4f26127853ab94"}, - {file = "pillow-12.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:808b99604f7873c800c4840f55ff389936ef1948e4e87645eaf3fccbc8477ac4"}, - {file = "pillow-12.1.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:bc11908616c8a283cf7d664f77411a5ed2a02009b0097ff8abbba5e79128ccf2"}, - {file = "pillow-12.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:896866d2d436563fa2a43a9d72f417874f16b5545955c54a64941e87c1376c61"}, - {file = "pillow-12.1.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8e178e3e99d3c0ea8fc64b88447f7cac8ccf058af422a6cedc690d0eadd98c51"}, - {file = "pillow-12.1.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:079af2fb0c599c2ec144ba2c02766d1b55498e373b3ac64687e43849fbbef5bc"}, - {file = "pillow-12.1.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bdec5e43377761c5dbca620efb69a77f6855c5a379e32ac5b158f54c84212b14"}, - {file = "pillow-12.1.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:565c986f4b45c020f5421a4cea13ef294dde9509a8577f29b2fc5edc7587fff8"}, - {file = "pillow-12.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:43aca0a55ce1eefc0aefa6253661cb54571857b1a7b2964bd8a1e3ef4b729924"}, - {file = "pillow-12.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0deedf2ea233722476b3a81e8cdfbad786f7adbed5d848469fa59fe52396e4ef"}, - {file = "pillow-12.1.0-cp314-cp314t-win32.whl", hash = "sha256:b17fbdbe01c196e7e159aacb889e091f28e61020a8abeac07b68079b6e626988"}, - {file = "pillow-12.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27b9baecb428899db6c0de572d6d305cfaf38ca1596b5c0542a5182e3e74e8c6"}, - {file = "pillow-12.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:f61333d817698bdcdd0f9d7793e365ac3d2a21c1f1eb02b32ad6aefb8d8ea831"}, - {file = "pillow-12.1.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ca94b6aac0d7af2a10ba08c0f888b3d5114439b6b3ef39968378723622fed377"}, - {file = "pillow-12.1.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:351889afef0f485b84078ea40fe33727a0492b9af3904661b0abbafee0355b72"}, - {file = "pillow-12.1.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bb0984b30e973f7e2884362b7d23d0a348c7143ee559f38ef3eaab640144204c"}, - {file = "pillow-12.1.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:84cabc7095dd535ca934d57e9ce2a72ffd216e435a84acb06b2277b1de2689bd"}, - {file = "pillow-12.1.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:53d8b764726d3af1a138dd353116f774e3862ec7e3794e0c8781e30db0f35dfc"}, - {file = "pillow-12.1.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5da841d81b1a05ef940a8567da92decaa15bc4d7dedb540a8c219ad83d91808a"}, - {file = "pillow-12.1.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:75af0b4c229ac519b155028fa1be632d812a519abba9b46b20e50c6caa184f19"}, - {file = "pillow-12.1.0.tar.gz", hash = "sha256:5c5ae0a06e9ea030ab786b0251b32c7e4ce10e58d983c0d5c56029455180b5b9"}, + {file = "pillow-12.1.1-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1f1625b72740fdda5d77b4def688eb8fd6490975d06b909fd19f13f391e077e0"}, + {file = "pillow-12.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:178aa072084bd88ec759052feca8e56cbb14a60b39322b99a049e58090479713"}, + {file = "pillow-12.1.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b66e95d05ba806247aaa1561f080abc7975daf715c30780ff92a20e4ec546e1b"}, + {file = "pillow-12.1.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:89c7e895002bbe49cdc5426150377cbbc04767d7547ed145473f496dfa40408b"}, + {file = "pillow-12.1.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a5cbdcddad0af3da87cb16b60d23648bc3b51967eb07223e9fed77a82b457c4"}, + {file = "pillow-12.1.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9f51079765661884a486727f0729d29054242f74b46186026582b4e4769918e4"}, + {file = "pillow-12.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:99c1506ea77c11531d75e3a412832a13a71c7ebc8192ab9e4b2e355555920e3e"}, + {file = "pillow-12.1.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:36341d06738a9f66c8287cf8b876d24b18db9bd8740fa0672c74e259ad408cff"}, + {file = "pillow-12.1.1-cp310-cp310-win32.whl", hash = "sha256:6c52f062424c523d6c4db85518774cc3d50f5539dd6eed32b8f6229b26f24d40"}, + {file = "pillow-12.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:c6008de247150668a705a6338156efb92334113421ceecf7438a12c9a12dab23"}, + {file = "pillow-12.1.1-cp310-cp310-win_arm64.whl", hash = "sha256:1a9b0ee305220b392e1124a764ee4265bd063e54a751a6b62eff69992f457fa9"}, + {file = "pillow-12.1.1-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:e879bb6cd5c73848ef3b2b48b8af9ff08c5b71ecda8048b7dd22d8a33f60be32"}, + {file = "pillow-12.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:365b10bb9417dd4498c0e3b128018c4a624dc11c7b97d8cc54effe3b096f4c38"}, + {file = "pillow-12.1.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d4ce8e329c93845720cd2014659ca67eac35f6433fd3050393d85f3ecef0dad5"}, + {file = "pillow-12.1.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc354a04072b765eccf2204f588a7a532c9511e8b9c7f900e1b64e3e33487090"}, + {file = "pillow-12.1.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7e7976bf1910a8116b523b9f9f58bf410f3e8aa330cd9a2bb2953f9266ab49af"}, + {file = "pillow-12.1.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:597bd9c8419bc7c6af5604e55847789b69123bbe25d65cc6ad3012b4f3c98d8b"}, + {file = "pillow-12.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2c1fc0f2ca5f96a3c8407e41cca26a16e46b21060fe6d5b099d2cb01412222f5"}, + {file = "pillow-12.1.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:578510d88c6229d735855e1f278aa305270438d36a05031dfaae5067cc8eb04d"}, + {file = "pillow-12.1.1-cp311-cp311-win32.whl", hash = "sha256:7311c0a0dcadb89b36b7025dfd8326ecfa36964e29913074d47382706e516a7c"}, + {file = "pillow-12.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:fbfa2a7c10cc2623f412753cddf391c7f971c52ca40a3f65dc5039b2939e8563"}, + {file = "pillow-12.1.1-cp311-cp311-win_arm64.whl", hash = "sha256:b81b5e3511211631b3f672a595e3221252c90af017e399056d0faabb9538aa80"}, + {file = "pillow-12.1.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ab323b787d6e18b3d91a72fc99b1a2c28651e4358749842b8f8dfacd28ef2052"}, + {file = "pillow-12.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:adebb5bee0f0af4909c30db0d890c773d1a92ffe83da908e2e9e720f8edf3984"}, + {file = "pillow-12.1.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bb66b7cc26f50977108790e2456b7921e773f23db5630261102233eb355a3b79"}, + {file = "pillow-12.1.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:aee2810642b2898bb187ced9b349e95d2a7272930796e022efaf12e99dccd293"}, + {file = "pillow-12.1.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a0b1cd6232e2b618adcc54d9882e4e662a089d5768cd188f7c245b4c8c44a397"}, + {file = "pillow-12.1.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7aac39bcf8d4770d089588a2e1dd111cbaa42df5a94be3114222057d68336bd0"}, + {file = "pillow-12.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ab174cd7d29a62dd139c44bf74b698039328f45cb03b4596c43473a46656b2f3"}, + {file = "pillow-12.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:339ffdcb7cbeaa08221cd401d517d4b1fe7a9ed5d400e4a8039719238620ca35"}, + {file = "pillow-12.1.1-cp312-cp312-win32.whl", hash = "sha256:5d1f9575a12bed9e9eedd9a4972834b08c97a352bd17955ccdebfeca5913fa0a"}, + {file = "pillow-12.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:21329ec8c96c6e979cd0dfd29406c40c1d52521a90544463057d2aaa937d66a6"}, + {file = "pillow-12.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:af9a332e572978f0218686636610555ae3defd1633597be015ed50289a03c523"}, + {file = "pillow-12.1.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:d242e8ac078781f1de88bf823d70c1a9b3c7950a44cdf4b7c012e22ccbcd8e4e"}, + {file = "pillow-12.1.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:02f84dfad02693676692746df05b89cf25597560db2857363a208e393429f5e9"}, + {file = "pillow-12.1.1-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:e65498daf4b583091ccbb2556c7000abf0f3349fcd57ef7adc9a84a394ed29f6"}, + {file = "pillow-12.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6c6db3b84c87d48d0088943bf33440e0c42370b99b1c2a7989216f7b42eede60"}, + {file = "pillow-12.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8b7e5304e34942bf62e15184219a7b5ad4ff7f3bb5cca4d984f37df1a0e1aee2"}, + {file = "pillow-12.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:18e5bddd742a44b7e6b1e773ab5db102bd7a94c32555ba656e76d319d19c3850"}, + {file = "pillow-12.1.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc44ef1f3de4f45b50ccf9136999d71abb99dca7706bc75d222ed350b9fd2289"}, + {file = "pillow-12.1.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5a8eb7ed8d4198bccbd07058416eeec51686b498e784eda166395a23eb99138e"}, + {file = "pillow-12.1.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:47b94983da0c642de92ced1702c5b6c292a84bd3a8e1d1702ff923f183594717"}, + {file = "pillow-12.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:518a48c2aab7ce596d3bf79d0e275661b846e86e4d0e7dec34712c30fe07f02a"}, + {file = "pillow-12.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a550ae29b95c6dc13cf69e2c9dc5747f814c54eeb2e32d683e5e93af56caa029"}, + {file = "pillow-12.1.1-cp313-cp313-win32.whl", hash = "sha256:a003d7422449f6d1e3a34e3dd4110c22148336918ddbfc6a32581cd54b2e0b2b"}, + {file = "pillow-12.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:344cf1e3dab3be4b1fa08e449323d98a2a3f819ad20f4b22e77a0ede31f0faa1"}, + {file = "pillow-12.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:5c0dd1636633e7e6a0afe7bf6a51a14992b7f8e60de5789018ebbdfae55b040a"}, + {file = "pillow-12.1.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0330d233c1a0ead844fc097a7d16c0abff4c12e856c0b325f231820fee1f39da"}, + {file = "pillow-12.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5dae5f21afb91322f2ff791895ddd8889e5e947ff59f71b46041c8ce6db790bc"}, + {file = "pillow-12.1.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2e0c664be47252947d870ac0d327fea7e63985a08794758aa8af5b6cb6ec0c9c"}, + {file = "pillow-12.1.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:691ab2ac363b8217f7d31b3497108fb1f50faab2f75dfb03284ec2f217e87bf8"}, + {file = "pillow-12.1.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e9e8064fb1cc019296958595f6db671fba95209e3ceb0c4734c9baf97de04b20"}, + {file = "pillow-12.1.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:472a8d7ded663e6162dafdf20015c486a7009483ca671cece7a9279b512fcb13"}, + {file = "pillow-12.1.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:89b54027a766529136a06cfebeecb3a04900397a3590fd252160b888479517bf"}, + {file = "pillow-12.1.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:86172b0831b82ce4f7877f280055892b31179e1576aa00d0df3bb1bbf8c3e524"}, + {file = "pillow-12.1.1-cp313-cp313t-win32.whl", hash = "sha256:44ce27545b6efcf0fdbdceb31c9a5bdea9333e664cda58a7e674bb74608b3986"}, + {file = "pillow-12.1.1-cp313-cp313t-win_amd64.whl", hash = "sha256:a285e3eb7a5a45a2ff504e31f4a8d1b12ef62e84e5411c6804a42197c1cf586c"}, + {file = "pillow-12.1.1-cp313-cp313t-win_arm64.whl", hash = "sha256:cc7d296b5ea4d29e6570dabeaed58d31c3fea35a633a69679fb03d7664f43fb3"}, + {file = "pillow-12.1.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:417423db963cb4be8bac3fc1204fe61610f6abeed1580a7a2cbb2fbda20f12af"}, + {file = "pillow-12.1.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:b957b71c6b2387610f556a7eb0828afbe40b4a98036fc0d2acfa5a44a0c2036f"}, + {file = "pillow-12.1.1-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:097690ba1f2efdeb165a20469d59d8bb03c55fb6621eb2041a060ae8ea3e9642"}, + {file = "pillow-12.1.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2815a87ab27848db0321fb78c7f0b2c8649dee134b7f2b80c6a45c6831d75ccd"}, + {file = "pillow-12.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f7ed2c6543bad5a7d5530eb9e78c53132f93dfa44a28492db88b41cdab885202"}, + {file = "pillow-12.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:652a2c9ccfb556235b2b501a3a7cf3742148cd22e04b5625c5fe057ea3e3191f"}, + {file = "pillow-12.1.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d6e4571eedf43af33d0fc233a382a76e849badbccdf1ac438841308652a08e1f"}, + {file = "pillow-12.1.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b574c51cf7d5d62e9be37ba446224b59a2da26dc4c1bb2ecbe936a4fb1a7cb7f"}, + {file = "pillow-12.1.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a37691702ed687799de29a518d63d4682d9016932db66d4e90c345831b02fb4e"}, + {file = "pillow-12.1.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f95c00d5d6700b2b890479664a06e754974848afaae5e21beb4d83c106923fd0"}, + {file = "pillow-12.1.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:559b38da23606e68681337ad74622c4dbba02254fc9cb4488a305dd5975c7eeb"}, + {file = "pillow-12.1.1-cp314-cp314-win32.whl", hash = "sha256:03edcc34d688572014ff223c125a3f77fb08091e4607e7745002fc214070b35f"}, + {file = "pillow-12.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:50480dcd74fa63b8e78235957d302d98d98d82ccbfac4c7e12108ba9ecbdba15"}, + {file = "pillow-12.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:5cb1785d97b0c3d1d1a16bc1d710c4a0049daefc4935f3a8f31f827f4d3d2e7f"}, + {file = "pillow-12.1.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:1f90cff8aa76835cba5769f0b3121a22bd4eb9e6884cfe338216e557a9a548b8"}, + {file = "pillow-12.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1f1be78ce9466a7ee64bfda57bdba0f7cc499d9794d518b854816c41bf0aa4e9"}, + {file = "pillow-12.1.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:42fc1f4677106188ad9a55562bbade416f8b55456f522430fadab3cef7cd4e60"}, + {file = "pillow-12.1.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:98edb152429ab62a1818039744d8fbb3ccab98a7c29fc3d5fcef158f3f1f68b7"}, + {file = "pillow-12.1.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d470ab1178551dd17fdba0fef463359c41aaa613cdcd7ff8373f54be629f9f8f"}, + {file = "pillow-12.1.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6408a7b064595afcab0a49393a413732a35788f2a5092fdc6266952ed67de586"}, + {file = "pillow-12.1.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5d8c41325b382c07799a3682c1c258469ea2ff97103c53717b7893862d0c98ce"}, + {file = "pillow-12.1.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c7697918b5be27424e9ce568193efd13d925c4481dd364e43f5dff72d33e10f8"}, + {file = "pillow-12.1.1-cp314-cp314t-win32.whl", hash = "sha256:d2912fd8114fc5545aa3a4b5576512f64c55a03f3ebcca4c10194d593d43ea36"}, + {file = "pillow-12.1.1-cp314-cp314t-win_amd64.whl", hash = "sha256:4ceb838d4bd9dab43e06c363cab2eebf63846d6a4aeaea283bbdfd8f1a8ed58b"}, + {file = "pillow-12.1.1-cp314-cp314t-win_arm64.whl", hash = "sha256:7b03048319bfc6170e93bd60728a1af51d3dd7704935feb228c4d4faab35d334"}, + {file = "pillow-12.1.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:600fd103672b925fe62ed08e0d874ea34d692474df6f4bf7ebe148b30f89f39f"}, + {file = "pillow-12.1.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:665e1b916b043cef294bc54d47bf02d87e13f769bc4bc5fa225a24b3a6c5aca9"}, + {file = "pillow-12.1.1-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:495c302af3aad1ca67420ddd5c7bd480c8867ad173528767d906428057a11f0e"}, + {file = "pillow-12.1.1-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8fd420ef0c52c88b5a035a0886f367748c72147b2b8f384c9d12656678dfdfa9"}, + {file = "pillow-12.1.1-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f975aa7ef9684ce7e2c18a3aa8f8e2106ce1e46b94ab713d156b2898811651d3"}, + {file = "pillow-12.1.1-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8089c852a56c2966cf18835db62d9b34fef7ba74c726ad943928d494fa7f4735"}, + {file = "pillow-12.1.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:cb9bb857b2d057c6dfc72ac5f3b44836924ba15721882ef103cecb40d002d80e"}, + {file = "pillow-12.1.1.tar.gz", hash = "sha256:9ad8fa5937ab05218e2b6a4cff30295ad35afd2f83ac592e68c0d871bb0fdbc4"}, ] [package.extras] @@ -2319,4 +2436,4 @@ mongodb = ["pymongo"] [metadata] lock-version = "2.1" python-versions = ">=3.10,<3.13" -content-hash = "c1db37cabcddd05d318573d632529be139b1ce1430e7eda7c915bb9d171c7a77" +content-hash = "349ea21b64217dca053564867ff9f7cf5375fe7c8dee163a5689d9f3a25fd371" From 96ffe1bc00e44da20ee70a2557f82176abe39ba6 Mon Sep 17 00:00:00 2001 From: harshmishra2701 Date: Thu, 12 Feb 2026 01:13:00 +0530 Subject: [PATCH 05/18] [RTY-260011]: feat(docs): update project folder structure --- README.md | 50 ++-------------------------------------------- app/main.py | 5 +++++ docs/build-test.md | 2 +- docs/tree.md | 47 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 55 insertions(+), 49 deletions(-) create mode 100644 docs/tree.md diff --git a/README.md b/README.md index e3468e2..e5507d6 100644 --- a/README.md +++ b/README.md @@ -101,53 +101,7 @@ def generate_code(length=6): --- -## Project Folder Structure - -```text -Directory structure: -tiny/ -β”œβ”€β”€ CHANGELOG.md -β”œβ”€β”€ LICENSE -β”œβ”€β”€ README.md -β”œβ”€β”€ app/ -β”‚ β”œβ”€β”€__init__.py -β”‚ β”œβ”€β”€ main.py -β”‚ β”œβ”€β”€ cli.py -β”‚ β”œβ”€β”€ api/ -β”‚ β”‚ └── fast_api.py -| β”œβ”€β”€assets/images -β”‚ β”œβ”€β”€ db/ -| | └──__init__.py -| | └──data.py -β”‚ β”œβ”€β”€ static/ -| | └── images -| | └── qr -| | └──style.css -| └── templates/ -| | └── index.html -| | └── recent.html -| | └── coming-soon.html -β”‚ β”œβ”€β”€ utils/ -| | └──__init__.py -| | └──_version.py -| | └── cache.py -| | └── config.py -| | └── helper.py -| | └── lint.py -| | └── qr.py -β”œβ”€β”€ docs/ -| └── build-test.md -| └── cache.md -| └── run_with_curl.md -β”œβ”€β”€ pyproject.toml -| └── poetry.lock -β”œβ”€β”€README.md -| └── CHANGELOG.md -| └── requirements.txt -β”œβ”€β”€ tiny.code-workspace -└── .gitignore - -``` +[Project Tree](./docs/tree.md) ## βš™οΈ How to Run the Project Locally @@ -167,7 +121,7 @@ Supported env files: - .env.local - .env (production) -``` +```text ENV=development DOMAIN=http://127.0.0.1:8000 MONGO_URI=mongodb://:@localhost:27017/tiny_url?authSource=tiny_url diff --git a/app/main.py b/app/main.py index 8f7eeca..786125f 100644 --- a/app/main.py +++ b/app/main.py @@ -238,6 +238,11 @@ async def redirect_short(request: Request, short_code: str): return RedirectResponse(doc["original_url"]) +@app.get("/coming-soon", response_class=HTMLResponse) +async def coming_soon(request: Request): + return templates.TemplateResponse("coming-soon.html", {"request": request}) + + app.mount("/api", api_app) diff --git a/docs/build-test.md b/docs/build-test.md index 7418014..39f619a 100644 --- a/docs/build-test.md +++ b/docs/build-test.md @@ -1,6 +1,6 @@ # Build & Test Guide (Tiny URL Project) -This document explains how to **build**, **install**, and **run tests locally** for the Tiny URL project using Poetry, FastAPI (API), and Flask (UI). +This document explains how to **build**, **install**, and **run tests locally** for the Tiny URL project using Poetry, FastAPI (API), and Fast(UI). --- diff --git a/docs/tree.md b/docs/tree.md new file mode 100644 index 0000000..3786778 --- /dev/null +++ b/docs/tree.md @@ -0,0 +1,47 @@ +## Project Folder Structure + +```text +Directory structure: +tiny/ +β”œβ”€β”€ CHANGELOG.md +β”œβ”€β”€ LICENSE +β”œβ”€β”€ README.md +β”œβ”€β”€ app/ +β”‚ β”œβ”€β”€__init__.py +β”‚ β”œβ”€β”€ main.py +β”‚ β”œβ”€β”€ cli.py +β”‚ β”œβ”€β”€ api/ +β”‚ β”‚ └── fast_api.py +| β”œβ”€β”€assets/images +β”‚ β”œβ”€β”€ db/ +| | └──__init__.py +| | └──data.py +β”‚ β”œβ”€β”€ static/ +| | └── images +| | └── qr +| | └──style.css +| └── templates/ +| | └── index.html +| | └── recent.html +| | └── coming-soon.html +β”‚ β”œβ”€β”€ utils/ +| | └──__init__.py +| | └──_version.py +| | └── cache.py +| | └── config.py +| | └── helper.py +| | └── lint.py +| | └── qr.py +β”œβ”€β”€ docs/ +| └── build-test.md +| └── cache.md +| └── run_with_curl.md +β”œβ”€β”€ pyproject.toml +| └── poetry.lock +β”œβ”€β”€README.md +| └── CHANGELOG.md +| └── requirements.txt +β”œβ”€β”€ tiny.code-workspace +└── .gitignore + +``` From 8c5b5d17a4a546b44a533bf34042f321e5872e64 Mon Sep 17 00:00:00 2001 From: harshmishra2701 Date: Wed, 11 Feb 2026 19:44:58 +0000 Subject: [PATCH 06/18] [RTY-260011]: chore: back-merge develop into feature/RTY-260011 --- .env.local | 16 +- .githooks/post-checkout | 28 +- .githooks/post-merge | 24 +- .githooks/pre-commit | 104 +- .githooks/prepare-commit-msg | 34 +- .github/CODEOWNERS | 12 +- .github/CODE_OF_CONDUCT.md | 306 +- .github/CONTRIBUTING.md | 398 +- .github/DISCUSSION_TEMPLATE/announcements.yml | 76 +- .github/DISCUSSION_TEMPLATE/ideas.yml | 94 +- .github/ISSUE_TEMPLATE/BUG_REPORT.yml | 154 +- .github/ISSUE_TEMPLATE/ENHANCEMENT.yml | 44 +- .github/ISSUE_TEMPLATE/FEATURE_REQUEST.md | 38 +- .github/ISSUE_TEMPLATE/QUESTION.md | 30 +- .github/ISSUE_TEMPLATE/config.yml | 16 +- .github/PULL_REQUEST_TEMPLATE.md | 82 +- .github/SECURITY.md | 74 +- .github/SUPPORT.md | 100 +- .github/actions/format-issue-title/format.sh | 100 +- .github/workflows/echo.yml | 26 +- .gitignore | 122 +- .poetryignore | 82 +- .vscode/cspell.json | 70 +- .vscode/extensions.json | 38 +- .vscode/javascriptreact.json | 50 +- .vscode/settings.json | 42 +- .vscode/tasks.json | 22 +- .vscode/template.code-snippets | 42 +- CHANGELOG.md | 26 +- LICENSE | 42 +- README.md | 580 +- app/__init__.py | 6 +- app/api/__init__.py | 6 +- app/api/fast_api.py | 424 +- app/db/data.py | 94 +- app/main.py | 510 +- app/static/style.css | 1630 +++--- app/templates/coming-soon.html | 238 +- app/templates/index.html | 694 +-- app/templates/recent.html | 418 +- app/utils/__init__.py | 2 +- app/utils/_version.py | 28 +- app/utils/cache.py | 104 +- app/utils/helper.py | 56 +- app/utils/lint.py | 46 +- app/utils/qr.py | 88 +- docs/build-test.md | 174 +- docs/cache.md | 400 +- docs/tree.md | 94 +- group_shorten.ps1 | 268 +- mypy.ini | 22 +- poetry.lock | 4878 ++++++++--------- pyproject.toml | 148 +- request/urls.json | 62 +- requirements.txt | 50 +- 55 files changed, 6656 insertions(+), 6656 deletions(-) diff --git a/.env.local b/.env.local index e9d7637..14a96a5 100644 --- a/.env.local +++ b/.env.local @@ -1,9 +1,9 @@ -MODE=local -MONGO_URI=mongodb://:@127.0.0.1:27017/?authSource=admin&retryWrites=true&w=majority -DOMAIN=https://rzro.link -PORT=8001 -API_VERSION="" -APP_NAMe="LOCAL" - -MONGO_URI="mongodb://@:127.0.0.1:27017/?retryWrites=true&w=majority&appName=" +MODE=local +MONGO_URI=mongodb://:@127.0.0.1:27017/?authSource=admin&retryWrites=true&w=majority +DOMAIN=https://rzro.link +PORT=8001 +API_VERSION="" +APP_NAMe="LOCAL" + +MONGO_URI="mongodb://@:127.0.0.1:27017/?retryWrites=true&w=majority&appName=" DATABASE_NAME=" [!CAUTION] -> Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: - -### 1. Correction - -**Community Impact**: Use of inappropriate language or other behavior deemed -unprofessional or unwelcome in the community. - -**Consequence**: A private, written warning from community leaders, providing -clarity around the nature of the violation and an explanation of why the -behavior was inappropriate. A public apology may be requested. - -### 2. Warning - -**Community Impact**: A violation through a single incident or series -of actions. - -**Consequence**: A warning with consequences for continued behavior. No -interaction with the people involved, including unsolicited interaction with -those enforcing the Code of Conduct, for a specified period of time. This -includes avoiding interactions in community spaces as well as external channels -like social media. Violating these terms may lead to a temporary or -permanent ban. - -### 3. Temporary Ban - -**Community Impact**: A serious violation of community standards, including -sustained inappropriate behavior. - -**Consequence**: A temporary ban from any sort of interaction or public -communication with the community for a specified period of time. No public or -private interaction with the people involved, including unsolicited interaction -with those enforcing the Code of Conduct, is allowed during this period. -Violating these terms may lead to a permanent ban. - -### 4. Permanent Ban - -**Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an -individual, or aggression toward or disparagement of classes of individuals. - -**Consequence**: A permanent ban from any sort of public interaction within -the community. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], -version 2.0, available at . - -Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). - -[homepage]: https://www.contributor-covenant.org - -For answers to common questions about this code of conduct -see the FAQ at . -Translations are available at . - -[contact]: mailto:recursivezero@outlook.com +# Contributor Covenant Code of Conduct + +**Table of Contents:** + +- [Contributor Covenant Code of Conduct](#contributor-covenant-code-of-conduct) + - [Summary](#summary) + - [Our Pledge](#our-pledge) + - [Our Standards](#our-standards) + - [Enforcement Responsibilities](#enforcement-responsibilities) + - [Scope](#scope) + - [Enforcement](#enforcement) + - [Enforcement Guidelines](#enforcement-guidelines) + - [1. Correction](#1-correction) + - [2. Warning](#2-warning) + - [3. Temporary Ban](#3-temporary-ban) + - [4. Permanent Ban](#4-permanent-ban) + - [Attribution](#attribution) + +**Version**: 1.0.0 + +## Summary + +As contributors and maintainers of this projects, we will respect everyone who contributes by posting issues, updating documentation, submitting pull requests, providing feedback in comments, and any other activities. + +Communication regarding the projects through any channel must be constructive and never resort to personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct. Courtesy and respect shall be extended to +everyone involved in this project. Our experiences as individuals differs widely, and as such contributors are expected to be respectful of differing viewpoints and ideas. + +We expect all contributors to uphold our standards of conduct. If any member of the community violates this code of conduct, the Embedded Artistry team and project maintainers will take action. We reserve the right to remove issues, comments, +and PRs that do not comply with our conduct standards. Repeated or significant offenses will result in blocked accounts and disassociation with our projects and the Embedded Artistry community. + +If you are subject to or witness unacceptable behavior, or have any other concerns, please [email us][contact]. + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity +and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +- Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +- The use of sexualized language or imagery, and sexual attention or + advances of any kind +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or email + address, without their explicit permission +- Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +[_this email_][contact]. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +> [!CAUTION] +> Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at . + +Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct +see the FAQ at . +Translations are available at . + +[contact]: mailto:recursivezero@outlook.com diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index c01230e..fb64b0c 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,199 +1,199 @@ -# Contribute - -This article explains how to contribute to project. Please read through the following guidelines. - -Write something nice and instructive as an intro. Talk about what kind of contributions you are interested in. - -> Welcome! We love receiving contributions from our community, so thanks for stopping by! There are many ways to contribute, including submitting bug reports, improving documentation, submitting feature requests, reviewing new submissions, -> or contributing code that can be incorporated into the project. - -## Summary - -> [!Note] -> Before participating in our community, please read our [code of conduct][coc]. -> By interacting with this repository, organization, or community you agree to abide by its terms. - -This document describes our development process. Following these guidelines shows that you respect the time and effort of the developers managing this project. In return, you will be shown respect in addressing your issue, -reviewing your changes, and incorporating your contributions. - -## Contributions - -There’s several ways to contribute, not just by writing code. If you have questions, see [support][support]. - -### Financial support - -It’s possible to support us financially by becoming a backer or sponsor through [Open Collective][collective]. - -### Improve docs - -As a user you’re perfect for helping us improve our docs.Typo corrections, error fixes, better explanations, new examples, etcetera. - -### Improve issues - -Some issues lack information, aren’t reproducible, or are just incorrect. You can help by trying to make them easier to resolve. -Existing issues might benefit from your unique experience or opinions. - -### Write code - -Code contributions are very welcome. -It’s probably a good idea to first post a question or open an issue to report a bug or suggest a new feature before creating a pull request. - -## Submitting an issue - -- The issue tracker is for issues. Use discussions for support -- Search the issue tracker (including closed issues) before opening a new issue -- Ensure you’re using the latest version of our packages -- Use a clear and descriptive title -- Include as much information as possible: steps to reproduce the issue, error message, version, operating system, etcetera -- The more time you put into an issue, the better we will be able to help you -- The best issue report is a proper reproduction step to prove it - -## Development Process - -What is your development process? - -> [!Tip] -> This project follows the basic git glow - -Check and follow [README][readme] file and run on your local. - -Talk about branches people should work on. Specifically, where is the starting point? `main`, `feature`, `hotfix` `task` etc. - -### Testing - -If you add code you need to add tests! We’ve learned the hard way that code without tests is undependable. If your pull request reduces our test coverage because it lacks tests then it will be rejected. - -Provide instructions for adding new tests. Provide instructions for running tests. - -```sh -npm run test -``` - -### Style Guidelines - -run below command - -```sh -npm run lint -``` - -### Code Formatting - -use code formatter in your IDE, add prettier and some other useful extension in your IDE. - -### Git Commit Guidelines - -below are the guidelines for your commit messages. - -- add clear message and with 50 lines -- prefix feature / issue number from issue page - -### Submitting a pull request - -- Run `npm test` locally to build, format, and test your changes -- Non-trivial changes are often best discussed in an issue first, to prevent you from doing unnecessary work -- For ambitious tasks, you should try to get your work in front of the community for feedback as soon as possible -- New features should be accompanied by tests and documentation -- Don’t include unrelated changes -- Test before submitting code by running `npm test` -- Write a convincing description of why we should land your pull request: it’s your job to convince us - -## Pull Request Process - -Add notes for pushing your branch: - -When you are ready to generate a pull request, either for preliminary review, or for consideration of merging into the project you must first push your local topic branch back up to GitHub: - -```sh -git push origin feature/branch-name -``` - -Include a note about submitting the PR: - -Once you've committed and pushed all of your changes to GitHub, go to the page for your fork on GitHub, select your development branch, and click the pull request button. -If you need to make any adjustments to your pull request, just push the updates to your branch. Your pull request will automatically track the changes on your development branch and update. - -1. Ensure any install or build dependencies are removed before the end of the layer when doing a build. -2. Update the README.md with details of changes to the interface, this includes new environment variables, exposed ports, useful file locations and container parameters. -3. Increase the version numbers in any examples files and the README.md to the new version that this Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/). -4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you do not have permission to do that, you may request the second reviewer to merge it for you. - -### Review Process - -Who reviews it? Who needs to sign off before it’s accepted? When should a contributor expect to hear from you? How can contributors get commit access, if at all? - -- The core team looks at Pull Requests on a regular basis in a weekly triage meeting that we hold in a public domain. The is announced in the weekly status updates. -- Our Reviewer will provide constructive Feedback by writing Review Comments (RC). Pull Requester have to address all RC on time. -- After feedback has been given we expect responses within two weeks. After two weeks we may close the pull request if it isn't showing any activity. -- Except for critical, urgent or very small fixes, we try to leave pull requests open for most of the day or overnight if something comes in late in the day, so that multiple people have the chance to review/comment. - Anyone who reviews a pull request should leave a note to let others know that someone has looked at it. For larger commits, we like to have a +1 from someone else on the core team and/or from other contributor(s). - Please note if you reviewed the code or tested locally -- a +1 by itself will typically be interpreted as your thinking its a good idea, but not having reviewed in detail. - -Perhaps also provide the steps your team will use for checking a PR. Or discuss the steps run on your CI server if you have one. This will help developers understand how to investigate any failures or test the process on their own. - -### Addressing Feedback - -Once a PR has been submitted, your changes will be reviewed and constructive feedback may be provided. Feedback isn't meant as an attack, but to help make sure the highest-quality code makes it into our project. -Changes will be approved once required feedback has been addressed. - -If a maintainer asks you to "rebase" your PR, they're saying that a lot of code has changed, and that you need to update your fork so it's easier to merge. - -To update your forked repository, follow these steps: - -### Fetch upstream master and merge with your repo's main branch - -```sh -git fetch upstream -git checkout main -git merge upstream/main -``` - -#### If there were any new commits, rebase your development branch - -```sh -git checkout feature/branch-name -git rebase main -``` - -If too much code has changed for git to automatically apply your branches changes to the new master, you will need to manually resolve the merge conflicts yourself. - -Once your new branch has no conflicts and works correctly, you can override your old branch using this command: - -```sh -git push origin feature/branch-name -``` - -Note that this will overwrite the old branch on the server, so make sure you are happy with your changes first! - -## Community - -Do you have a mailing list, Google group, slack channel, IRC channel? Link to them here. - -Include Other Notes on how people can contribute - -- You can help us answer questions our users have here: -- You can help build and design our website here: -- You can help write blog posts about the project by: -- You can help with newsletters and internal communications by: - -- Create an example of the project in real world by building something or showing what others have built. -- Write about other people’s projects based on this project. Show how it’s used in daily life. Take screenshots and make videos! - -## Resources - -- [How to contribute to open source](https://opensource.guide/how-to-contribute/) -- [Making your first contribution](https://medium.com/@vadimdemedes/making-your-first-contribution-de6576ddb190) -- [Using pull requests](https://help.github.com/articles/about-pull-requests/) -- [GitHub help](https://help.github.com) -- [git commit message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) - -## Author - -Β© RecursiveZero Private Limited - - - -[collective]: https://opencollective.com/recursivezero -[readme]: https://github.com/recursivezero/tiny/blob/main/README.md -[support]: https://github.com/recursivezero/tiny/blob/main/.github/SUPPORT.md -[coc]: https://github.com/recursivezero/tiny/blob/main/.github/CODE_OF_CONDUCT.md +# Contribute + +This article explains how to contribute to project. Please read through the following guidelines. + +Write something nice and instructive as an intro. Talk about what kind of contributions you are interested in. + +> Welcome! We love receiving contributions from our community, so thanks for stopping by! There are many ways to contribute, including submitting bug reports, improving documentation, submitting feature requests, reviewing new submissions, +> or contributing code that can be incorporated into the project. + +## Summary + +> [!Note] +> Before participating in our community, please read our [code of conduct][coc]. +> By interacting with this repository, organization, or community you agree to abide by its terms. + +This document describes our development process. Following these guidelines shows that you respect the time and effort of the developers managing this project. In return, you will be shown respect in addressing your issue, +reviewing your changes, and incorporating your contributions. + +## Contributions + +There’s several ways to contribute, not just by writing code. If you have questions, see [support][support]. + +### Financial support + +It’s possible to support us financially by becoming a backer or sponsor through [Open Collective][collective]. + +### Improve docs + +As a user you’re perfect for helping us improve our docs.Typo corrections, error fixes, better explanations, new examples, etcetera. + +### Improve issues + +Some issues lack information, aren’t reproducible, or are just incorrect. You can help by trying to make them easier to resolve. +Existing issues might benefit from your unique experience or opinions. + +### Write code + +Code contributions are very welcome. +It’s probably a good idea to first post a question or open an issue to report a bug or suggest a new feature before creating a pull request. + +## Submitting an issue + +- The issue tracker is for issues. Use discussions for support +- Search the issue tracker (including closed issues) before opening a new issue +- Ensure you’re using the latest version of our packages +- Use a clear and descriptive title +- Include as much information as possible: steps to reproduce the issue, error message, version, operating system, etcetera +- The more time you put into an issue, the better we will be able to help you +- The best issue report is a proper reproduction step to prove it + +## Development Process + +What is your development process? + +> [!Tip] +> This project follows the basic git glow + +Check and follow [README][readme] file and run on your local. + +Talk about branches people should work on. Specifically, where is the starting point? `main`, `feature`, `hotfix` `task` etc. + +### Testing + +If you add code you need to add tests! We’ve learned the hard way that code without tests is undependable. If your pull request reduces our test coverage because it lacks tests then it will be rejected. + +Provide instructions for adding new tests. Provide instructions for running tests. + +```sh +npm run test +``` + +### Style Guidelines + +run below command + +```sh +npm run lint +``` + +### Code Formatting + +use code formatter in your IDE, add prettier and some other useful extension in your IDE. + +### Git Commit Guidelines + +below are the guidelines for your commit messages. + +- add clear message and with 50 lines +- prefix feature / issue number from issue page + +### Submitting a pull request + +- Run `npm test` locally to build, format, and test your changes +- Non-trivial changes are often best discussed in an issue first, to prevent you from doing unnecessary work +- For ambitious tasks, you should try to get your work in front of the community for feedback as soon as possible +- New features should be accompanied by tests and documentation +- Don’t include unrelated changes +- Test before submitting code by running `npm test` +- Write a convincing description of why we should land your pull request: it’s your job to convince us + +## Pull Request Process + +Add notes for pushing your branch: + +When you are ready to generate a pull request, either for preliminary review, or for consideration of merging into the project you must first push your local topic branch back up to GitHub: + +```sh +git push origin feature/branch-name +``` + +Include a note about submitting the PR: + +Once you've committed and pushed all of your changes to GitHub, go to the page for your fork on GitHub, select your development branch, and click the pull request button. +If you need to make any adjustments to your pull request, just push the updates to your branch. Your pull request will automatically track the changes on your development branch and update. + +1. Ensure any install or build dependencies are removed before the end of the layer when doing a build. +2. Update the README.md with details of changes to the interface, this includes new environment variables, exposed ports, useful file locations and container parameters. +3. Increase the version numbers in any examples files and the README.md to the new version that this Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/). +4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you do not have permission to do that, you may request the second reviewer to merge it for you. + +### Review Process + +Who reviews it? Who needs to sign off before it’s accepted? When should a contributor expect to hear from you? How can contributors get commit access, if at all? + +- The core team looks at Pull Requests on a regular basis in a weekly triage meeting that we hold in a public domain. The is announced in the weekly status updates. +- Our Reviewer will provide constructive Feedback by writing Review Comments (RC). Pull Requester have to address all RC on time. +- After feedback has been given we expect responses within two weeks. After two weeks we may close the pull request if it isn't showing any activity. +- Except for critical, urgent or very small fixes, we try to leave pull requests open for most of the day or overnight if something comes in late in the day, so that multiple people have the chance to review/comment. + Anyone who reviews a pull request should leave a note to let others know that someone has looked at it. For larger commits, we like to have a +1 from someone else on the core team and/or from other contributor(s). + Please note if you reviewed the code or tested locally -- a +1 by itself will typically be interpreted as your thinking its a good idea, but not having reviewed in detail. + +Perhaps also provide the steps your team will use for checking a PR. Or discuss the steps run on your CI server if you have one. This will help developers understand how to investigate any failures or test the process on their own. + +### Addressing Feedback + +Once a PR has been submitted, your changes will be reviewed and constructive feedback may be provided. Feedback isn't meant as an attack, but to help make sure the highest-quality code makes it into our project. +Changes will be approved once required feedback has been addressed. + +If a maintainer asks you to "rebase" your PR, they're saying that a lot of code has changed, and that you need to update your fork so it's easier to merge. + +To update your forked repository, follow these steps: + +### Fetch upstream master and merge with your repo's main branch + +```sh +git fetch upstream +git checkout main +git merge upstream/main +``` + +#### If there were any new commits, rebase your development branch + +```sh +git checkout feature/branch-name +git rebase main +``` + +If too much code has changed for git to automatically apply your branches changes to the new master, you will need to manually resolve the merge conflicts yourself. + +Once your new branch has no conflicts and works correctly, you can override your old branch using this command: + +```sh +git push origin feature/branch-name +``` + +Note that this will overwrite the old branch on the server, so make sure you are happy with your changes first! + +## Community + +Do you have a mailing list, Google group, slack channel, IRC channel? Link to them here. + +Include Other Notes on how people can contribute + +- You can help us answer questions our users have here: +- You can help build and design our website here: +- You can help write blog posts about the project by: +- You can help with newsletters and internal communications by: + +- Create an example of the project in real world by building something or showing what others have built. +- Write about other people’s projects based on this project. Show how it’s used in daily life. Take screenshots and make videos! + +## Resources + +- [How to contribute to open source](https://opensource.guide/how-to-contribute/) +- [Making your first contribution](https://medium.com/@vadimdemedes/making-your-first-contribution-de6576ddb190) +- [Using pull requests](https://help.github.com/articles/about-pull-requests/) +- [GitHub help](https://help.github.com) +- [git commit message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) + +## Author + +Β© RecursiveZero Private Limited + + + +[collective]: https://opencollective.com/recursivezero +[readme]: https://github.com/recursivezero/tiny/blob/main/README.md +[support]: https://github.com/recursivezero/tiny/blob/main/.github/SUPPORT.md +[coc]: https://github.com/recursivezero/tiny/blob/main/.github/CODE_OF_CONDUCT.md diff --git a/.github/DISCUSSION_TEMPLATE/announcements.yml b/.github/DISCUSSION_TEMPLATE/announcements.yml index fdb1089..ee9c3fd 100644 --- a/.github/DISCUSSION_TEMPLATE/announcements.yml +++ b/.github/DISCUSSION_TEMPLATE/announcements.yml @@ -1,38 +1,38 @@ -title: "[General] " -labels: ["General Introduction"] -body: - - type: markdown - attributes: - value: | - introduce yourself! - - - type: textarea - id: improvements - attributes: - label: Top 3 improvements - description: "What are the top 3 improvements we could make to this project?" - value: | - 1. - 2. - 3. - ... - render: bash - validations: - required: true - - - type: textarea - id: has-id - attributes: - label: write us suggestion - description: A description about suggestions to help you - validations: - required: true - - - type: checkboxes - id: terms - attributes: - label: Before submitting - description: By submitting this announcement, you agree to follow our [Contributing Guidelines](https://github.com/recursivezero/tiny/blob/main/.github/CONTRIBUTING.md). - options: - - label: I've made research efforts and searched the documentation - required: true +title: "[General] " +labels: ["General Introduction"] +body: + - type: markdown + attributes: + value: | + introduce yourself! + + - type: textarea + id: improvements + attributes: + label: Top 3 improvements + description: "What are the top 3 improvements we could make to this project?" + value: | + 1. + 2. + 3. + ... + render: bash + validations: + required: true + + - type: textarea + id: has-id + attributes: + label: write us suggestion + description: A description about suggestions to help you + validations: + required: true + + - type: checkboxes + id: terms + attributes: + label: Before submitting + description: By submitting this announcement, you agree to follow our [Contributing Guidelines](https://github.com/recursivezero/tiny/blob/main/.github/CONTRIBUTING.md). + options: + - label: I've made research efforts and searched the documentation + required: true diff --git a/.github/DISCUSSION_TEMPLATE/ideas.yml b/.github/DISCUSSION_TEMPLATE/ideas.yml index 64387a9..3df6db8 100644 --- a/.github/DISCUSSION_TEMPLATE/ideas.yml +++ b/.github/DISCUSSION_TEMPLATE/ideas.yml @@ -1,47 +1,47 @@ -title: "[Idea]" -labels: ["Share your Idea"] -body: - - type: textarea - id: idea - attributes: - label: Idea highlight - description: "What are the idea we could make to this project?" - value: - render: bash - validations: - required: true - - - type: dropdown - id: improvement - attributes: - label: Which area of this project could be most improved? - options: - - Documentation - - Pull request review time - - Bug fix time - - Enhancement - validations: - required: true - - - type: input - id: id - attributes: - label: email - description: your contact email - validations: - required: false - - - type: checkboxes - id: terms - attributes: - label: Before submitting - description: By submitting this idea, you agree to follow our [Contributing Guidelines](https://github.com/recursivezero/tiny/blob/main/.github/CONTRIBUTING.md). - options: - - label: I've made research efforts and searched the documentation - required: true - - - type: markdown - attributes: - value: | - ### Thank you - _we will contact you_ **soon** +title: "[Idea]" +labels: ["Share your Idea"] +body: + - type: textarea + id: idea + attributes: + label: Idea highlight + description: "What are the idea we could make to this project?" + value: + render: bash + validations: + required: true + + - type: dropdown + id: improvement + attributes: + label: Which area of this project could be most improved? + options: + - Documentation + - Pull request review time + - Bug fix time + - Enhancement + validations: + required: true + + - type: input + id: id + attributes: + label: email + description: your contact email + validations: + required: false + + - type: checkboxes + id: terms + attributes: + label: Before submitting + description: By submitting this idea, you agree to follow our [Contributing Guidelines](https://github.com/recursivezero/tiny/blob/main/.github/CONTRIBUTING.md). + options: + - label: I've made research efforts and searched the documentation + required: true + + - type: markdown + attributes: + value: | + ### Thank you + _we will contact you_ **soon** diff --git a/.github/ISSUE_TEMPLATE/BUG_REPORT.yml b/.github/ISSUE_TEMPLATE/BUG_REPORT.yml index a61018f..c727bf7 100644 --- a/.github/ISSUE_TEMPLATE/BUG_REPORT.yml +++ b/.github/ISSUE_TEMPLATE/BUG_REPORT.yml @@ -1,77 +1,77 @@ -name: Bug Report -description: File a bug report to help us improve. -title: "πŸ› " -labels: ["bug"] -assignees: - - "xkeshav" - -body: - - type: textarea - id: problem - attributes: - label: What happened? - description: | - Please provide as much info as possible. - placeholder: Tell us what you see! - value: A bug happened - validations: - required: true - - - type: textarea - id: expected - attributes: - label: What did you expect to happen? - description: | - Please provide expected result/output. - placeholder: Tell us what is expected ! - validations: - required: true - - - type: textarea - id: additional - attributes: - label: Anything else we need to know? - description: | - Please provide other details if it is necessary. - placeholder: Software version and device details! - validations: - required: false - - - type: dropdown - id: browsers - attributes: - label: What browsers are you seeing the problem on? - multiple: true - options: - - Firefox - - Chrome - - Safari - - Microsoft Edge - - Other - - - type: textarea - id: logs - attributes: - label: Relevant log output - description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. - render: shell - validations: - required: false - - - type: input - id: contact - attributes: - label: Contact Details - description: How can we get in touch with you if we need more info? - placeholder: ex. email@example.com - validations: - required: false - - - type: checkboxes - id: terms - attributes: - label: Code of Conduct - description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/recursivezero/tiny/blob/main/README.md). - options: - - label: I agree to follow this project's Code of Conduct - required: true +name: Bug Report +description: File a bug report to help us improve. +title: "πŸ› " +labels: ["bug"] +assignees: + - "xkeshav" + +body: + - type: textarea + id: problem + attributes: + label: What happened? + description: | + Please provide as much info as possible. + placeholder: Tell us what you see! + value: A bug happened + validations: + required: true + + - type: textarea + id: expected + attributes: + label: What did you expect to happen? + description: | + Please provide expected result/output. + placeholder: Tell us what is expected ! + validations: + required: true + + - type: textarea + id: additional + attributes: + label: Anything else we need to know? + description: | + Please provide other details if it is necessary. + placeholder: Software version and device details! + validations: + required: false + + - type: dropdown + id: browsers + attributes: + label: What browsers are you seeing the problem on? + multiple: true + options: + - Firefox + - Chrome + - Safari + - Microsoft Edge + - Other + + - type: textarea + id: logs + attributes: + label: Relevant log output + description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. + render: shell + validations: + required: false + + - type: input + id: contact + attributes: + label: Contact Details + description: How can we get in touch with you if we need more info? + placeholder: ex. email@example.com + validations: + required: false + + - type: checkboxes + id: terms + attributes: + label: Code of Conduct + description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/recursivezero/tiny/blob/main/README.md). + options: + - label: I agree to follow this project's Code of Conduct + required: true diff --git a/.github/ISSUE_TEMPLATE/ENHANCEMENT.yml b/.github/ISSUE_TEMPLATE/ENHANCEMENT.yml index e209432..7516de1 100644 --- a/.github/ISSUE_TEMPLATE/ENHANCEMENT.yml +++ b/.github/ISSUE_TEMPLATE/ENHANCEMENT.yml @@ -1,22 +1,22 @@ -name: Enhancement Tracking Issue -description: Provide supporting details for a feature in development -title: "πŸͺ‘ " -labels: [enhancement] -assignees: - - "recursivezero" -body: - - type: textarea - id: feature - attributes: - label: What would you like to be added? - description: | - Feature requests are unlikely to make progress as issues. - validations: - required: true - - - type: textarea - id: rationale - attributes: - label: Why is this needed? - validations: - required: true +name: Enhancement Tracking Issue +description: Provide supporting details for a feature in development +title: "πŸͺ‘ " +labels: [enhancement] +assignees: + - "recursivezero" +body: + - type: textarea + id: feature + attributes: + label: What would you like to be added? + description: | + Feature requests are unlikely to make progress as issues. + validations: + required: true + + - type: textarea + id: rationale + attributes: + label: Why is this needed? + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md b/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md index baaccc7..cfa9954 100644 --- a/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md +++ b/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md @@ -1,19 +1,19 @@ ---- -name: Feature request -about: "Suggest a feature for this project" -title: "❇️ " -labels: ["enhancement"] -assignees: ["recursivezero"] ---- - -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] - -**Describe the solution you'd like** -A clear and concise description of what you want to happen. - -**Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. - -**Additional context** -Add any other context or screenshots about the feature request here. +--- +name: Feature request +about: "Suggest a feature for this project" +title: "❇️ " +labels: ["enhancement"] +assignees: ["recursivezero"] +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/ISSUE_TEMPLATE/QUESTION.md b/.github/ISSUE_TEMPLATE/QUESTION.md index a905acc..fbe1e12 100644 --- a/.github/ISSUE_TEMPLATE/QUESTION.md +++ b/.github/ISSUE_TEMPLATE/QUESTION.md @@ -1,15 +1,15 @@ ---- -name: Question -about: Use this template to ask a question about the project -title: "❓ " -labels: question -assignees: recursivezero ---- - -## Question - -State your question - -## Sample Code - -Please include relevant code snippets or files that provide context for your question. +--- +name: Question +about: Use this template to ask a question about the project +title: "❓ " +labels: question +assignees: recursivezero +--- + +## Question + +State your question + +## Sample Code + +Please include relevant code snippets or files that provide context for your question. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 136b402..09c2909 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,8 +1,8 @@ -blank_issues_enabled: false -contact_links: - - name: GitHub Community Support - url: https://github.com/orgs/community/discussions - about: Please ask and answer questions here. - - name: Social Media Support - url: https://twitter.com/recursivezero - about: Please connect on social media here. +blank_issues_enabled: false +contact_links: + - name: GitHub Community Support + url: https://github.com/orgs/community/discussions + about: Please ask and answer questions here. + - name: Social Media Support + url: https://twitter.com/recursivezero + about: Please connect on social media here. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index a8cc1d1..9114c19 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,41 +1,41 @@ -# Pull Request Template - -## Description - -Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change. - -Fixes # (issue) - -## Type of change - -Please delete options that are not relevant. - -- [ ] Bug fix (non-breaking change which fixes an issue) -- [ ] New feature (non-breaking change which adds functionality) -- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) -- [ ] This change requires a documentation update - -## How Has This Been Tested? - -Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration - -- [ ] Test A -- [ ] Test B - -**Test Configuration**: - -- Browser: -- Device: -- Toolchain: - -## Checklist - -- [ ] My code follows the style guidelines of this project -- [ ] I have performed a self-review of my own code -- [ ] I have commented my code, particularly in hard-to-understand areas -- [ ] I have made corresponding changes to the documentation -- [ ] My changes generate no new warnings -- [ ] I have added tests that prove my fix is effective or that my feature works -- [ ] New and existing unit tests pass locally with my changes -- [ ] Any dependent changes have been merged and published in downstream modules -- [ ] I have checked my code and corrected any misspellings +# Pull Request Template + +## Description + +Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change. + +Fixes # (issue) + +## Type of change + +Please delete options that are not relevant. + +- [ ] Bug fix (non-breaking change which fixes an issue) +- [ ] New feature (non-breaking change which adds functionality) +- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) +- [ ] This change requires a documentation update + +## How Has This Been Tested? + +Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration + +- [ ] Test A +- [ ] Test B + +**Test Configuration**: + +- Browser: +- Device: +- Toolchain: + +## Checklist + +- [ ] My code follows the style guidelines of this project +- [ ] I have performed a self-review of my own code +- [ ] I have commented my code, particularly in hard-to-understand areas +- [ ] I have made corresponding changes to the documentation +- [ ] My changes generate no new warnings +- [ ] I have added tests that prove my fix is effective or that my feature works +- [ ] New and existing unit tests pass locally with my changes +- [ ] Any dependent changes have been merged and published in downstream modules +- [ ] I have checked my code and corrected any misspellings diff --git a/.github/SECURITY.md b/.github/SECURITY.md index 4ffec2a..16c0887 100644 --- a/.github/SECURITY.md +++ b/.github/SECURITY.md @@ -1,37 +1,37 @@ -# Security Policy - -## Scope - -Keeping users safe and secure is a top priority for us.We welcome the contribution of external security researchers. - -If you believe you’ve found a security or vulnerability issue in the repo we encourage you to notify us. - -There are no hard and fast rules to determine if a bug is worth reporting as a security issue or a β€œregular” issue. -When in doubt, please do send us a report. - -## How to submit a report - -Security issues can be reported by sending [an email][contact]. - -The team will acknowledge your email within 48 hours. You will receive a more detailed response within 96 hours. - -We will create a maintainer security advisory on GitHub to discuss internally, and when needed, invite you to the advisory. - -## Purpose - -- Make a good faith effort to avoid privacy violations, destruction of data, and interruption or degradation of our services -- Only interact with accounts you own or with explicit permission of the account holder. If you do encounter Personally Identifiable Information (PII) contact us immediately, - do not proceed with access, and immediately purge any local information -- Provide us with a reasonable amount of time to resolve vulnerabilities prior to any disclosure to the public or a third-party -- We will consider activities conducted consistent with this policy to constitute β€œauthorized” conduct and will not pursue civil action or initiate a complaint to law enforcement. - We will help to the extent we can if legal action is initiated by a third party against you - -Please submit a report to us before engaging in conduct that may be inconsistent with or unaddressed by this policy. - -## Preferences - -- Please provide detailed reports with reproducible steps and a clearly defined impact -- Submit one vulnerability per report -- Social engineering (such as phishing, vishing, smishing) is prohibited - -[contact]: mailto:recursivelyzero@gmail.com +# Security Policy + +## Scope + +Keeping users safe and secure is a top priority for us.We welcome the contribution of external security researchers. + +If you believe you’ve found a security or vulnerability issue in the repo we encourage you to notify us. + +There are no hard and fast rules to determine if a bug is worth reporting as a security issue or a β€œregular” issue. +When in doubt, please do send us a report. + +## How to submit a report + +Security issues can be reported by sending [an email][contact]. + +The team will acknowledge your email within 48 hours. You will receive a more detailed response within 96 hours. + +We will create a maintainer security advisory on GitHub to discuss internally, and when needed, invite you to the advisory. + +## Purpose + +- Make a good faith effort to avoid privacy violations, destruction of data, and interruption or degradation of our services +- Only interact with accounts you own or with explicit permission of the account holder. If you do encounter Personally Identifiable Information (PII) contact us immediately, + do not proceed with access, and immediately purge any local information +- Provide us with a reasonable amount of time to resolve vulnerabilities prior to any disclosure to the public or a third-party +- We will consider activities conducted consistent with this policy to constitute β€œauthorized” conduct and will not pursue civil action or initiate a complaint to law enforcement. + We will help to the extent we can if legal action is initiated by a third party against you + +Please submit a report to us before engaging in conduct that may be inconsistent with or unaddressed by this policy. + +## Preferences + +- Please provide detailed reports with reproducible steps and a clearly defined impact +- Submit one vulnerability per report +- Social engineering (such as phishing, vishing, smishing) is prohibited + +[contact]: mailto:recursivelyzero@gmail.com diff --git a/.github/SUPPORT.md b/.github/SUPPORT.md index 284ee48..e89776f 100644 --- a/.github/SUPPORT.md +++ b/.github/SUPPORT.md @@ -1,50 +1,50 @@ -# Support - -This article explains where to get help with remark. -Please read through the following guidelines. - -> [!Note] -> before participating in our community, please read our [code of conduct][coc]. -> By interacting with this repository, organization, or community you agree to abide by its terms. - -## Asking quality questions - -Questions can go to [GitHub discussions][dicussion]. - -Help us help you! - -Spend time framing questions and add links and resources. -Spending the extra time up front can help save everyone time in the long run. - -> [!Tip] -> Here are some tips - -- [Talk to us][chat]! -- Don’t fall for the [XY problem][xy] -- Search to find out if a similar question has been asked -- Try to define what you need help with: - - Is there something in particular you want to do? - - What problem are you encountering and what steps have you taken to try and fix it? - - Is there a concept you don’t understand? -- Provide sample code, such as a [CodeSandbox][cs] or [StackBlitz][sb] or a small video, if possible -- Screenshots can help, but if there’s important text such as code or error messages in them, please also provide those as text -- The more time you put into asking your question, the better we can help you - -## Contributions - -See [`contributing.md`][contributing] on how to contribute. - -## License - -Β© [Keshav Mohta][author] - - - -[author]: https://xkeshav.com -[coc]: https://github.com/recursivezero/tiny/blob/main/.github/CODE_OF_CONDUCT.md -[chat]: https://github.com/recursivezero/tiny/discussions/new?category=general -[dicussion]: https://github.com/recursivezero/tiny/discussions/new?category=q-a -[contributing]: https://github.com/recursivezero/tiny/blob/main/.github/CONTRIBUTING.md -[xy]: https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem/66378#66378 -[cs]: https://codesandbox.io -[sb]: https://stackblitz.com +# Support + +This article explains where to get help with remark. +Please read through the following guidelines. + +> [!Note] +> before participating in our community, please read our [code of conduct][coc]. +> By interacting with this repository, organization, or community you agree to abide by its terms. + +## Asking quality questions + +Questions can go to [GitHub discussions][dicussion]. + +Help us help you! + +Spend time framing questions and add links and resources. +Spending the extra time up front can help save everyone time in the long run. + +> [!Tip] +> Here are some tips + +- [Talk to us][chat]! +- Don’t fall for the [XY problem][xy] +- Search to find out if a similar question has been asked +- Try to define what you need help with: + - Is there something in particular you want to do? + - What problem are you encountering and what steps have you taken to try and fix it? + - Is there a concept you don’t understand? +- Provide sample code, such as a [CodeSandbox][cs] or [StackBlitz][sb] or a small video, if possible +- Screenshots can help, but if there’s important text such as code or error messages in them, please also provide those as text +- The more time you put into asking your question, the better we can help you + +## Contributions + +See [`contributing.md`][contributing] on how to contribute. + +## License + +Β© [Keshav Mohta][author] + + + +[author]: https://xkeshav.com +[coc]: https://github.com/recursivezero/tiny/blob/main/.github/CODE_OF_CONDUCT.md +[chat]: https://github.com/recursivezero/tiny/discussions/new?category=general +[dicussion]: https://github.com/recursivezero/tiny/discussions/new?category=q-a +[contributing]: https://github.com/recursivezero/tiny/blob/main/.github/CONTRIBUTING.md +[xy]: https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem/66378#66378 +[cs]: https://codesandbox.io +[sb]: https://stackblitz.com diff --git a/.github/actions/format-issue-title/format.sh b/.github/actions/format-issue-title/format.sh index 8d1298c..e12499f 100755 --- a/.github/actions/format-issue-title/format.sh +++ b/.github/actions/format-issue-title/format.sh @@ -1,50 +1,50 @@ -#!/bin/bash -set -e - -ISSUE_NUMBER=$1 -OLD_TITLE=$2 -PREFIX=$3 -PLACEHOLDER=$4 -DRY_RUN=$5 - -# Fallback to default if prefix is empty -if [ -z "$PREFIX" ]; then - PREFIX="GEN" -fi - -YEAR=$(date +%y) -PADDED_NUM=$(printf "%04d" "$ISSUE_NUMBER") -IDENTIFIER="[${PREFIX}-${YEAR}${PADDED_NUM}]: " - -# Skip if already present -if echo "$OLD_TITLE" | grep -q "$IDENTIFIER"; then - echo "Identifier already present. Skipping." - exit 0 -fi - -# Replace placeholder at start if provided -if [ -n "$PLACEHOLDER" ] && echo "$OLD_TITLE" | grep -q "^$PLACEHOLDER"; then - NEW_TITLE=$(echo "$OLD_TITLE" | sed "s/^$PLACEHOLDER[[:space:]]*/$IDENTIFIER/") -else - # Insert after emoji if present - if [[ "$OLD_TITLE" =~ ^([[:space:]]*[^[:alnum:][:space:]]+[[:space:]]*)(.*) ]]; then - EMOJI="${BASH_REMATCH[1]}" - REST="${BASH_REMATCH[2]}" - NEW_TITLE="${EMOJI} ${IDENTIFIER}${REST}" - else - NEW_TITLE="${IDENTIFIER}${OLD_TITLE}" - fi -fi - -# Normalize spacing -NEW_TITLE=$(echo "$NEW_TITLE" | sed 's/ */ /g' | sed 's/^ *//;s/ *$//') - -echo "New title would be: $NEW_TITLE" - -if [ "$DRY_RUN" = "true" ]; then - echo "Dry-run enabled. Not updating title." - exit 0 -fi - -gh issue edit "$ISSUE_NUMBER" --title "$NEW_TITLE" --repo "${GITHUB_REPOSITORY}" -echo "Title updated successfully." +#!/bin/bash +set -e + +ISSUE_NUMBER=$1 +OLD_TITLE=$2 +PREFIX=$3 +PLACEHOLDER=$4 +DRY_RUN=$5 + +# Fallback to default if prefix is empty +if [ -z "$PREFIX" ]; then + PREFIX="GEN" +fi + +YEAR=$(date +%y) +PADDED_NUM=$(printf "%04d" "$ISSUE_NUMBER") +IDENTIFIER="[${PREFIX}-${YEAR}${PADDED_NUM}]: " + +# Skip if already present +if echo "$OLD_TITLE" | grep -q "$IDENTIFIER"; then + echo "Identifier already present. Skipping." + exit 0 +fi + +# Replace placeholder at start if provided +if [ -n "$PLACEHOLDER" ] && echo "$OLD_TITLE" | grep -q "^$PLACEHOLDER"; then + NEW_TITLE=$(echo "$OLD_TITLE" | sed "s/^$PLACEHOLDER[[:space:]]*/$IDENTIFIER/") +else + # Insert after emoji if present + if [[ "$OLD_TITLE" =~ ^([[:space:]]*[^[:alnum:][:space:]]+[[:space:]]*)(.*) ]]; then + EMOJI="${BASH_REMATCH[1]}" + REST="${BASH_REMATCH[2]}" + NEW_TITLE="${EMOJI} ${IDENTIFIER}${REST}" + else + NEW_TITLE="${IDENTIFIER}${OLD_TITLE}" + fi +fi + +# Normalize spacing +NEW_TITLE=$(echo "$NEW_TITLE" | sed 's/ */ /g' | sed 's/^ *//;s/ *$//') + +echo "New title would be: $NEW_TITLE" + +if [ "$DRY_RUN" = "true" ]; then + echo "Dry-run enabled. Not updating title." + exit 0 +fi + +gh issue edit "$ISSUE_NUMBER" --title "$NEW_TITLE" --repo "${GITHUB_REPOSITORY}" +echo "Title updated successfully." diff --git a/.github/workflows/echo.yml b/.github/workflows/echo.yml index 602d55a..aef8b65 100644 --- a/.github/workflows/echo.yml +++ b/.github/workflows/echo.yml @@ -1,13 +1,13 @@ -name: Organization CI -on: - push: - branches: [$default-branch] - pull_request: - branches: [$default-branch] -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v5 - - name: Run a one-line script - run: echo Hello from Action-Club +name: Organization CI +on: + push: + branches: [$default-branch] + pull_request: + branches: [$default-branch] +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - name: Run a one-line script + run: echo Hello from Action-Club diff --git a/.gitignore b/.gitignore index fa1b42e..ac5982f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,61 +1,61 @@ -__pycache__/ -*.py[cod] -*.pyo -*$py.class - -.venv* -venv/ -env/ -.venv* - - -.env -.env.* -!.env.local - - -instance/ -*.db -*.sqlite -*.sqlite3 - - -dump/ -*.bson -urls_export.json - - -.cache/ -.pytest_cache/ -.mypy_cache/ -.pytype/ -.coverage -htmlcov/ - - -*.log -logs/ -log/ - - -.vscode/ -.idea/ -*.swp -*.swo - - -.DS_Store -Thumbs.db - - -build/ -dist/ -.eggs/ -*.egg-info/ - - -poetry.lock - -*.tmp -*.temp -*.bak +__pycache__/ +*.py[cod] +*.pyo +*$py.class + +.venv* +venv/ +env/ +.venv* + + +.env +.env.* +!.env.local + + +instance/ +*.db +*.sqlite +*.sqlite3 + + +dump/ +*.bson +urls_export.json + + +.cache/ +.pytest_cache/ +.mypy_cache/ +.pytype/ +.coverage +htmlcov/ + + +*.log +logs/ +log/ + + +.vscode/ +.idea/ +*.swp +*.swo + + +.DS_Store +Thumbs.db + + +build/ +dist/ +.eggs/ +*.egg-info/ + + +poetry.lock + +*.tmp +*.temp +*.bak diff --git a/.poetryignore b/.poetryignore index d553ebc..add52dd 100644 --- a/.poetryignore +++ b/.poetryignore @@ -1,41 +1,41 @@ -# Ignore Python cache and venv - -**pycache**/ -_.py[cod] -_.so -\*.egg-info/ -.venv/ -venv/ -env/ - -## Ignore build output - -build/ -dist/ -_.egg -_.whl - -## Ignore IDE/editor settings - -.vscode/ -.idea/ -_.swp -_.bak - -## Ignore local config and test logs - -.env -.env.\* -\*.log - -## Ignore test data/output - -tests/ -_.test._ -debug*\*.py -trace*\*.py - -## Ignore OS files - -.DS_Store -Thumbs.db +# Ignore Python cache and venv + +**pycache**/ +_.py[cod] +_.so +\*.egg-info/ +.venv/ +venv/ +env/ + +## Ignore build output + +build/ +dist/ +_.egg +_.whl + +## Ignore IDE/editor settings + +.vscode/ +.idea/ +_.swp +_.bak + +## Ignore local config and test logs + +.env +.env.\* +\*.log + +## Ignore test data/output + +tests/ +_.test._ +debug*\*.py +trace*\*.py + +## Ignore OS files + +.DS_Store +Thumbs.db diff --git a/.vscode/cspell.json b/.vscode/cspell.json index d612dba..a1bfc3d 100755 --- a/.vscode/cspell.json +++ b/.vscode/cspell.json @@ -1,35 +1,35 @@ -{ - "dictionaryDefinitions": [ - { - "name": "projectTerms", - "path": "./dictionaries/project-words.txt" - }, - { - "name": "teamMember", - "path": "./dictionaries/team-member.txt" - } - ], - "dictionaries": [ - "projectTerms", - "teamMember" - ], - "languageSettings": [ - { - "languageId": "*", - "dictionaries": [ - "projectTerms" - ] - }, - { - "languageId": "json, md", - "dictionaries": [ - "teamMember" - ] - } - ], - "enableFiletypes": [ - "astro", - "jsx", - "tsx" - ] -} +{ + "dictionaryDefinitions": [ + { + "name": "projectTerms", + "path": "./dictionaries/project-words.txt" + }, + { + "name": "teamMember", + "path": "./dictionaries/team-member.txt" + } + ], + "dictionaries": [ + "projectTerms", + "teamMember" + ], + "languageSettings": [ + { + "languageId": "*", + "dictionaries": [ + "projectTerms" + ] + }, + { + "languageId": "json, md", + "dictionaries": [ + "teamMember" + ] + } + ], + "enableFiletypes": [ + "astro", + "jsx", + "tsx" + ] +} diff --git a/.vscode/extensions.json b/.vscode/extensions.json index c303d82..d7f443b 100755 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,19 +1,19 @@ -{ - "recommendations": [ - "streetsidesoftware.code-spell-checker", - "alefragnani.project-manager", - "esbenp.prettier-vscode", - "oderwat.indent-rainbow", - "davidanson.vscode-markdownlint", - "mkxml.vscode-filesize", - "christian-kohler.path-intellisense", - "artdiniz.quitcontrol-vscode", - "enkia.tokyo-night", - "dbaeumer.vscode-eslint", - "johnpapa.vscode-peacock", - "aaron-bond.better-comments", - "Heron.firefox-devtools-theme", - "GitHub.github-vscode-theme", - "xkeshav.css-color-collector" - ] -} +{ + "recommendations": [ + "streetsidesoftware.code-spell-checker", + "alefragnani.project-manager", + "esbenp.prettier-vscode", + "oderwat.indent-rainbow", + "davidanson.vscode-markdownlint", + "mkxml.vscode-filesize", + "christian-kohler.path-intellisense", + "artdiniz.quitcontrol-vscode", + "enkia.tokyo-night", + "dbaeumer.vscode-eslint", + "johnpapa.vscode-peacock", + "aaron-bond.better-comments", + "Heron.firefox-devtools-theme", + "GitHub.github-vscode-theme", + "xkeshav.css-color-collector" + ] +} diff --git a/.vscode/javascriptreact.json b/.vscode/javascriptreact.json index 604637e..012c647 100644 --- a/.vscode/javascriptreact.json +++ b/.vscode/javascriptreact.json @@ -1,26 +1,26 @@ -{ - "React: useState": { - "scope": "typescriptreact, typescript, javascript, javascriptreact", - "prefix": "rus", - "body": [ - "const [${1:state}, set${1/(.*)/${1:/capitalize}/}] = useState(${2:null});", - "$3" - ], - "description": "React Hook: useState" - }, - "React: Functional Component": { - "scope": "typescriptreact, typescript, javascript, javascriptreact", - "prefix": "rfc", - "body": [ - "import { Fragment } from 'react';\n", - "export type ${2:${1}Props} = { }\n", - "export const ${1:${TM_FILENAME_BASE}} = (props: React.FC<${2}>) => {", - "\tconsole.log({ props }); $BLOCK_COMMENT_START added to suppress lint error $BLOCK_COMMENT_END", - "\treturn (", - "\t\t$0", - "\t);", - "}" - ], - "description": "React Functional Component" - } +{ + "React: useState": { + "scope": "typescriptreact, typescript, javascript, javascriptreact", + "prefix": "rus", + "body": [ + "const [${1:state}, set${1/(.*)/${1:/capitalize}/}] = useState(${2:null});", + "$3" + ], + "description": "React Hook: useState" + }, + "React: Functional Component": { + "scope": "typescriptreact, typescript, javascript, javascriptreact", + "prefix": "rfc", + "body": [ + "import { Fragment } from 'react';\n", + "export type ${2:${1}Props} = { }\n", + "export const ${1:${TM_FILENAME_BASE}} = (props: React.FC<${2}>) => {", + "\tconsole.log({ props }); $BLOCK_COMMENT_START added to suppress lint error $BLOCK_COMMENT_END", + "\treturn (", + "\t\t$0", + "\t);", + "}" + ], + "description": "React Functional Component" + } } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index c6b17fc..8a5f03d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,22 +1,22 @@ -{ - "workbench.colorCustomizations": { - "activityBar.activeBackground": "#def6c4", - "activityBar.background": "#def6c4", - "activityBar.foreground": "#15202b", - "activityBar.inactiveForeground": "#15202b99", - "activityBarBadge.background": "#59a2e6", - "activityBarBadge.foreground": "#15202b", - "commandCenter.border": "#15202b99", - "sash.hoverBorder": "#def6c4", - "statusBar.background": "#c5ef98", - "statusBar.foreground": "#15202b", - "statusBarItem.hoverBackground": "#ace86c", - "statusBarItem.remoteBackground": "#c5ef98", - "statusBarItem.remoteForeground": "#15202b", - "titleBar.activeBackground": "#c5ef98", - "titleBar.activeForeground": "#15202b", - "titleBar.inactiveBackground": "#c5ef9899", - "titleBar.inactiveForeground": "#15202b99" - }, - "peacock.color": "#c5ef98" +{ + "workbench.colorCustomizations": { + "activityBar.activeBackground": "#def6c4", + "activityBar.background": "#def6c4", + "activityBar.foreground": "#15202b", + "activityBar.inactiveForeground": "#15202b99", + "activityBarBadge.background": "#59a2e6", + "activityBarBadge.foreground": "#15202b", + "commandCenter.border": "#15202b99", + "sash.hoverBorder": "#def6c4", + "statusBar.background": "#c5ef98", + "statusBar.foreground": "#15202b", + "statusBarItem.hoverBackground": "#ace86c", + "statusBarItem.remoteBackground": "#c5ef98", + "statusBarItem.remoteForeground": "#15202b", + "titleBar.activeBackground": "#c5ef98", + "titleBar.activeForeground": "#15202b", + "titleBar.inactiveBackground": "#c5ef9899", + "titleBar.inactiveForeground": "#15202b99" + }, + "peacock.color": "#c5ef98" } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index fdfa730..1877924 100755 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,12 +1,12 @@ -{ - // See https://go.microsoft.com/fwlink/?LinkId=733558 - // for the documentation about the tasks.json format - "version": "2.0.0", - "tasks": [ - { - "label": "echo", - "type": "shell", - "command": "echo Hello" - } - ] +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "echo", + "type": "shell", + "command": "echo Hello" + } + ] } \ No newline at end of file diff --git a/.vscode/template.code-snippets b/.vscode/template.code-snippets index f9549de..b48f3f7 100644 --- a/.vscode/template.code-snippets +++ b/.vscode/template.code-snippets @@ -1,22 +1,22 @@ -{ - "console-obj": { - "scope": "javascript, javascriptreact, typescript, typescriptreact", - "prefix": "cl", - "body": [ - "console.log({ $0 });" - ], - "description": "destructured console log" - }, - "log-clip": { - "scope": "javascript, javascriptreact, typescript, typescriptreact", - "prefix": "log-clip", - "body": ["//${3: ${CLIPBOARD/(.*)\\./$2/g}}", "console.log({$CLIPBOARD})"], - "description": "clip to console" - }, - "createDocumentFragment": { - "scope": "javascript, javascriptreact, typescript, typescriptreact", - "prefix": "cdf", - "body": "const ${1:varName} = document.createDocumentFragment();${0}", - "description": "Creates an element using document.createDoumentFragment" - } +{ + "console-obj": { + "scope": "javascript, javascriptreact, typescript, typescriptreact", + "prefix": "cl", + "body": [ + "console.log({ $0 });" + ], + "description": "destructured console log" + }, + "log-clip": { + "scope": "javascript, javascriptreact, typescript, typescriptreact", + "prefix": "log-clip", + "body": ["//${3: ${CLIPBOARD/(.*)\\./$2/g}}", "console.log({$CLIPBOARD})"], + "description": "clip to console" + }, + "createDocumentFragment": { + "scope": "javascript, javascriptreact, typescript, typescriptreact", + "prefix": "cdf", + "body": "const ${1:varName} = document.createDocumentFragment();${0}", + "description": "Creates an element using document.createDoumentFragment" + } } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 5cf5522..ad57eb9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,13 @@ -# ChangeLog - -All notable changes to this repository will be documented in this file. - -## [0.0.1] - -- Initial release. -- Health Files - -## [0.1.0] - -- Restructure folder structure -- added poetry dev script +# ChangeLog + +All notable changes to this repository will be documented in this file. + +## [0.0.1] + +- Initial release. +- Health Files + +## [0.1.0] + +- Restructure folder structure +- added poetry dev script diff --git a/LICENSE b/LICENSE index d38c94e..2426c1b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,21 @@ -MIT License - -Copyright (c) 2024 RecursiveZero Private Limited - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +MIT License + +Copyright (c) 2024 RecursiveZero Private Limited + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index e5507d6..ff01fba 100644 --- a/README.md +++ b/README.md @@ -1,290 +1,290 @@ -# Tiny URL Generator - -> A modern, Bitly-style tiny URL web application built with FastAPI, optional MongoDB, and a sleek web UI. - -![Python](https://img.shields.io/badge/Python-3.11-blue.svg) -![FastAPI](https://img.shields.io/badge/FastAPI-Backend-teal.svg) -![MongoDB]() -![License](https://img.shields.io/badge/License-MIT-yellow.svg) -![Status](https://img.shields.io/badge/Status-Active-success.svg) - ---- - -## Overview - -Tiny URL is a sleek, fast, and modern URL shortening platform built using FastAPI with optional MongoDB persistence. -It converts long URLs into short, shareable links β€” just like Bitly. - -The project supports: - -- Web UI (FastAPI + Jinja templates) -- REST API (FastAPI) -- Offline Mode (No MongoDB required) - -This project is designed with: - -- Clean startup lifecycle (no racing configs) -- Optional database dependency -- Graceful degradation when MongoDB is unavailable -- In-memory cache fallback -- QR code generation with auto folder creation - ---- - -## Features - -### User Features - -- Convert long URLs into short, unique codes -- Default checkbox QR code generation -- Clean Bitly-style result card -- Copy & share buttons -- Download URL button -- URL validation and sanitization -- Fully responsive UI -- Recent URLs page (when DB is available) -- Visit count tracking (when DB is available) -- QR image auto-generation with logo -- Cache-accelerated redirects - -### API & Developer Features - -- REST API for URL shortening -- API version endpoint -- Swagger / OpenAPI documentation -- API landing page -- Cache layer for fast redirects -- Graceful offline mode (no DB required) -- Clean startup lifecycle using FastAPI lifespan -- Optional MongoDB dependency - ---- - -## Short Code Generation Algorithm - -The app uses a Random Alphanumeric Short Code Generator. - -### Algorithm Details - -- Uses `string.ascii_letters + string.digits` -- Randomly picks characters -- Generates a 6-character short ID -- Checks MongoDB for collisions (if DB is enabled) -- Automatically regenerates on collision - -### Example - -```python -import random, string - -def generate_code(length=6): - chars = string.ascii_letters + string.digits - return ''.join(random.choice(chars) for _ in range(length)) -``` - ---- - -## Tech Stack - -| Layer | Technology | -| ----------- | ----------------------- | -| UI Backend | FastAPI | -| API Backend | FastAPI | -| Database | MongoDB (Optional) | -| Cache | In-Memory (Python dict) | -| Frontend | HTML, CSS, Vanilla JS | -| QR Code | qrcode + Pillow | -| API Server | Uvicorn | -| Validation | Pydantic v2 | -| Env Mgmt | python-dotenv | -| Tooling | Poetry | - ---- - -[Project Tree](./docs/tree.md) - -## βš™οΈ How to Run the Project Locally - -`Virtual Environment Configuration` - -```bash -poetry config virtualenvs.path /your/desired/path -``` - -## Environment Configuration - -Ensure below files are configured (create if not exist) properly to run the project; - -Supported env files: - -- .env.development -- .env.local -- .env (production) - -```text -ENV=development -DOMAIN=http://127.0.0.1:8000 -MONGO_URI=mongodb://:@localhost:27017/tiny_url?authSource=tiny_url -DATABASE_NAME=tiny_url -``` - -## Install Dependencies - -```bash -poetry lock --no-cache --regenerate -poetry install --all-extras --with dev -``` - -Or manually - -```bash -poetry install -``` - -## How to Run - -```bash -poetry run tiny dev -``` - -Access: - -## Run FastAPI Server - -```bash -poetry run tiny api -``` - -Access: - -## Offline Mode (No Database) - -TinyURL supports graceful offline mode. - -### What works - -- App starts normally -- UI loads -- Short URLs are generated -- QR codes are generated -- Redirects work from in-memory cache - -### What is disabled - -- Recent URLs page -- Persistent redirects after restart -- Visit count tracking - -Offline Mode activates automatically when: - -- MongoDB is down -- OR pymongo is not installed -- OR MONGO_URI is missing/invalid - -Log message: - -``` -⚠️ MongoDB connection failed. Running in NO-DB mode. -``` - ---- - -## Switching Modes - -### Without MongoDB - -```sh -sudo systemctl stop mongod -poetry run tiny dev -``` - -or - -```sh -poetry run pip uninstall pymongo -poetry run tiny dev -``` - ---- - -## Troubleshooting - -sometimes there might be chances that virtual environment get corrupted then delete the old virtual environment and start afresh. - -```sh -poetry env info -# this will provide virtual environment name -poetry env remove -``` - -### Mongo auth error - -Encode special chars: - -@ ? %40 - -Example: - -``` -MONGO_URI=mongodb://user%40gmail.com:Pass%40123@localhost:27017/tiny_url?authSource=tiny_url -``` - ---- - -## WSL Notes - -```sh -sudo systemctl start mongod -poetry run tiny dev -``` - -## Build & Packaging - -## Build Package - -```bash -poetry clean -poetry build -``` - -Artifacts in `dist/` - -- tiny-x.y.0-py3-none-any.whl -- tiny-x.y.0.tar.gz - -## Test Locally - -```bash -python -m venv .venv-dist -source .venv-dist/bin/activate -# Windows -.venv-dist\Scripts\activate -``` - -### Install package - -```bash -pip install dist/*.whl -pip install --upgrade dist/*.whl -``` - -## License - -πŸ“œDocs -[run_with_curl](docs/run_with_curl) - -Screenshots: -Home Page: -![home page](assets/images/home.png) -![home dark mode](assets/images/home_dark.png) -![home page](assets/images/valid.png) -![home layout](assets/images/short_url.png) -![recent](assets/images/recent.png) -tiny API Page: -![API](assets/images/API_page.png) -![API1](assets/images/api_page2.png) -No DB Mode: -![NO DB](assets/images/no-db.png) -πŸ“œLicense - -[MIT](LICENSE) +# Tiny URL Generator + +> A modern, Bitly-style tiny URL web application built with FastAPI, optional MongoDB, and a sleek web UI. + +![Python](https://img.shields.io/badge/Python-3.11-blue.svg) +![FastAPI](https://img.shields.io/badge/FastAPI-Backend-teal.svg) +![MongoDB]() +![License](https://img.shields.io/badge/License-MIT-yellow.svg) +![Status](https://img.shields.io/badge/Status-Active-success.svg) + +--- + +## Overview + +Tiny URL is a sleek, fast, and modern URL shortening platform built using FastAPI with optional MongoDB persistence. +It converts long URLs into short, shareable links β€” just like Bitly. + +The project supports: + +- Web UI (FastAPI + Jinja templates) +- REST API (FastAPI) +- Offline Mode (No MongoDB required) + +This project is designed with: + +- Clean startup lifecycle (no racing configs) +- Optional database dependency +- Graceful degradation when MongoDB is unavailable +- In-memory cache fallback +- QR code generation with auto folder creation + +--- + +## Features + +### User Features + +- Convert long URLs into short, unique codes +- Default checkbox QR code generation +- Clean Bitly-style result card +- Copy & share buttons +- Download URL button +- URL validation and sanitization +- Fully responsive UI +- Recent URLs page (when DB is available) +- Visit count tracking (when DB is available) +- QR image auto-generation with logo +- Cache-accelerated redirects + +### API & Developer Features + +- REST API for URL shortening +- API version endpoint +- Swagger / OpenAPI documentation +- API landing page +- Cache layer for fast redirects +- Graceful offline mode (no DB required) +- Clean startup lifecycle using FastAPI lifespan +- Optional MongoDB dependency + +--- + +## Short Code Generation Algorithm + +The app uses a Random Alphanumeric Short Code Generator. + +### Algorithm Details + +- Uses `string.ascii_letters + string.digits` +- Randomly picks characters +- Generates a 6-character short ID +- Checks MongoDB for collisions (if DB is enabled) +- Automatically regenerates on collision + +### Example + +```python +import random, string + +def generate_code(length=6): + chars = string.ascii_letters + string.digits + return ''.join(random.choice(chars) for _ in range(length)) +``` + +--- + +## Tech Stack + +| Layer | Technology | +| ----------- | ----------------------- | +| UI Backend | FastAPI | +| API Backend | FastAPI | +| Database | MongoDB (Optional) | +| Cache | In-Memory (Python dict) | +| Frontend | HTML, CSS, Vanilla JS | +| QR Code | qrcode + Pillow | +| API Server | Uvicorn | +| Validation | Pydantic v2 | +| Env Mgmt | python-dotenv | +| Tooling | Poetry | + +--- + +[Project Tree](./docs/tree.md) + +## βš™οΈ How to Run the Project Locally + +`Virtual Environment Configuration` + +```bash +poetry config virtualenvs.path /your/desired/path +``` + +## Environment Configuration + +Ensure below files are configured (create if not exist) properly to run the project; + +Supported env files: + +- .env.development +- .env.local +- .env (production) + +```text +ENV=development +DOMAIN=http://127.0.0.1:8000 +MONGO_URI=mongodb://:@localhost:27017/tiny_url?authSource=tiny_url +DATABASE_NAME=tiny_url +``` + +## Install Dependencies + +```bash +poetry lock --no-cache --regenerate +poetry install --all-extras --with dev +``` + +Or manually + +```bash +poetry install +``` + +## How to Run + +```bash +poetry run tiny dev +``` + +Access: + +## Run FastAPI Server + +```bash +poetry run tiny api +``` + +Access: + +## Offline Mode (No Database) + +TinyURL supports graceful offline mode. + +### What works + +- App starts normally +- UI loads +- Short URLs are generated +- QR codes are generated +- Redirects work from in-memory cache + +### What is disabled + +- Recent URLs page +- Persistent redirects after restart +- Visit count tracking + +Offline Mode activates automatically when: + +- MongoDB is down +- OR pymongo is not installed +- OR MONGO_URI is missing/invalid + +Log message: + +``` +⚠️ MongoDB connection failed. Running in NO-DB mode. +``` + +--- + +## Switching Modes + +### Without MongoDB + +```sh +sudo systemctl stop mongod +poetry run tiny dev +``` + +or + +```sh +poetry run pip uninstall pymongo +poetry run tiny dev +``` + +--- + +## Troubleshooting + +sometimes there might be chances that virtual environment get corrupted then delete the old virtual environment and start afresh. + +```sh +poetry env info +# this will provide virtual environment name +poetry env remove +``` + +### Mongo auth error + +Encode special chars: + +@ ? %40 + +Example: + +``` +MONGO_URI=mongodb://user%40gmail.com:Pass%40123@localhost:27017/tiny_url?authSource=tiny_url +``` + +--- + +## WSL Notes + +```sh +sudo systemctl start mongod +poetry run tiny dev +``` + +## Build & Packaging + +## Build Package + +```bash +poetry clean +poetry build +``` + +Artifacts in `dist/` + +- tiny-x.y.0-py3-none-any.whl +- tiny-x.y.0.tar.gz + +## Test Locally + +```bash +python -m venv .venv-dist +source .venv-dist/bin/activate +# Windows +.venv-dist\Scripts\activate +``` + +### Install package + +```bash +pip install dist/*.whl +pip install --upgrade dist/*.whl +``` + +## License + +πŸ“œDocs +[run_with_curl](docs/run_with_curl) + +Screenshots: +Home Page: +![home page](assets/images/home.png) +![home dark mode](assets/images/home_dark.png) +![home page](assets/images/valid.png) +![home layout](assets/images/short_url.png) +![recent](assets/images/recent.png) +tiny API Page: +![API](assets/images/API_page.png) +![API1](assets/images/api_page2.png) +No DB Mode: +![NO DB](assets/images/no-db.png) +πŸ“œLicense + +[MIT](LICENSE) diff --git a/app/__init__.py b/app/__init__.py index 5529dc1..683fb26 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -1,3 +1,3 @@ -from app.utils._version import get_version - -__version__ = get_version() +from app.utils._version import get_version + +__version__ = get_version() diff --git a/app/api/__init__.py b/app/api/__init__.py index 5529dc1..683fb26 100644 --- a/app/api/__init__.py +++ b/app/api/__init__.py @@ -1,3 +1,3 @@ -from app.utils._version import get_version - -__version__ = get_version() +from app.utils._version import get_version + +__version__ = get_version() diff --git a/app/api/fast_api.py b/app/api/fast_api.py index 2190c0d..1a29e19 100644 --- a/app/api/fast_api.py +++ b/app/api/fast_api.py @@ -1,212 +1,212 @@ -import os -import re -import traceback -from datetime import datetime, timezone - -from fastapi import APIRouter, FastAPI, Request -from fastapi.responses import HTMLResponse, JSONResponse -from pydantic import BaseModel, Field - -from typing import TYPE_CHECKING - -if TYPE_CHECKING: - from pymongo.errors import PyMongoError -else: - try: - from pymongo.errors import PyMongoError - except ImportError: - - class PyMongoError(Exception): - pass - - -from app import __version__ -from app.db import data as db_data -from app.utils.cache import get_short_from_cache, set_cache_pair -from app.utils.helper import generate_code, is_valid_url, sanitize_url - -SHORT_CODE_PATTERN = re.compile(r"^[A-Za-z0-9]{6}$") -MAX_URL_LENGTH = 2048 - -app = FastAPI( - title="Tiny API", - version=__version__, - description="Tiny URL Shortener API built with FastAPI", -) - -api_v1 = APIRouter(prefix=os.getenv("API_VERSION", "/api/v1"), tags=["v1"]) - - -@app.exception_handler(Exception) -async def global_exception_handler(request: Request, exc: Exception): - traceback.print_exc() - return JSONResponse( - status_code=500, - content={"success": False, "error": "INTERNAL_SERVER_ERROR"}, - ) - - -class ShortenRequest(BaseModel): - url: str = Field(..., examples=["https://abcdkbd.com"]) - - -class ShortenResponse(BaseModel): - success: bool = True - input_url: str - short_code: str - created_on: datetime - - -class ErrorResponse(BaseModel): - success: bool = False - error: str - input_url: str - message: str - - -class VersionResponse(BaseModel): - version: str - - -# ------------------------------------------------- -# Home -# ------------------------------------------------- -@app.get("/", response_class=HTMLResponse, tags=["Home"]) -async def read_root(_: Request): - return """ - - - πŸŒ™ tiny API πŸŒ™ - - - -
-

πŸš€ tiny API

-

FastAPI backend for the Tiny URL shortener

- View API Documentation -
- - - """ - - -@api_v1.post("/shorten", response_model=ShortenResponse, status_code=201) -def shorten_url(payload: ShortenRequest): - print(" SHORTEN ENDPOINT HIT ", payload.url) - raw_url = payload.url.strip() - - if len(raw_url) > MAX_URL_LENGTH: - return JSONResponse( - status_code=413, content={"success": False, "input_url": payload.url} - ) - - original_url = sanitize_url(raw_url) - - if not is_valid_url(original_url): - return JSONResponse( - status_code=400, - content={ - "success": False, - "error": "INVALID_URL", - "input_url": payload.url, - "message": "Invalid URL", - }, - ) - - if db_data.urls is None: - cached_short = get_short_from_cache(original_url) - short_code = cached_short or generate_code() - set_cache_pair(short_code, original_url) - return { - "success": True, - "input_url": original_url, - "short_code": short_code, - "created_on": datetime.now(timezone.utc), - } - - try: - existing = db_data.urls.find_one({"original_url": original_url}) - except PyMongoError: - existing = None - - if existing: - return { - "success": True, - "input_url": original_url, - "short_code": existing["short_code"], - "created_on": existing["created_at"], - } - - short_code = generate_code() - try: - db_data.urls.insert_one( - { - "short_code": short_code, - "original_url": original_url, - "created_at": datetime.now(timezone.utc), - } - ) - except PyMongoError: - pass - - return { - "success": True, - "input_url": original_url, - "short_code": short_code, - "created_on": datetime.now(timezone.utc), - } - - -@app.get("/version") -def api_version(): - return {"version": __version__} - - -@api_v1.get("/help") -def get_help(): - return {"message": "Welcome to Tiny API. Visit /docs for API documentation."} - - -app.include_router(api_v1) +import os +import re +import traceback +from datetime import datetime, timezone + +from fastapi import APIRouter, FastAPI, Request +from fastapi.responses import HTMLResponse, JSONResponse +from pydantic import BaseModel, Field + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from pymongo.errors import PyMongoError +else: + try: + from pymongo.errors import PyMongoError + except ImportError: + + class PyMongoError(Exception): + pass + + +from app import __version__ +from app.db import data as db_data +from app.utils.cache import get_short_from_cache, set_cache_pair +from app.utils.helper import generate_code, is_valid_url, sanitize_url + +SHORT_CODE_PATTERN = re.compile(r"^[A-Za-z0-9]{6}$") +MAX_URL_LENGTH = 2048 + +app = FastAPI( + title="Tiny API", + version=__version__, + description="Tiny URL Shortener API built with FastAPI", +) + +api_v1 = APIRouter(prefix=os.getenv("API_VERSION", "/api/v1"), tags=["v1"]) + + +@app.exception_handler(Exception) +async def global_exception_handler(request: Request, exc: Exception): + traceback.print_exc() + return JSONResponse( + status_code=500, + content={"success": False, "error": "INTERNAL_SERVER_ERROR"}, + ) + + +class ShortenRequest(BaseModel): + url: str = Field(..., examples=["https://abcdkbd.com"]) + + +class ShortenResponse(BaseModel): + success: bool = True + input_url: str + short_code: str + created_on: datetime + + +class ErrorResponse(BaseModel): + success: bool = False + error: str + input_url: str + message: str + + +class VersionResponse(BaseModel): + version: str + + +# ------------------------------------------------- +# Home +# ------------------------------------------------- +@app.get("/", response_class=HTMLResponse, tags=["Home"]) +async def read_root(_: Request): + return """ + + + πŸŒ™ tiny API πŸŒ™ + + + +
+

πŸš€ tiny API

+

FastAPI backend for the Tiny URL shortener

+ View API Documentation +
+ + + """ + + +@api_v1.post("/shorten", response_model=ShortenResponse, status_code=201) +def shorten_url(payload: ShortenRequest): + print(" SHORTEN ENDPOINT HIT ", payload.url) + raw_url = payload.url.strip() + + if len(raw_url) > MAX_URL_LENGTH: + return JSONResponse( + status_code=413, content={"success": False, "input_url": payload.url} + ) + + original_url = sanitize_url(raw_url) + + if not is_valid_url(original_url): + return JSONResponse( + status_code=400, + content={ + "success": False, + "error": "INVALID_URL", + "input_url": payload.url, + "message": "Invalid URL", + }, + ) + + if db_data.urls is None: + cached_short = get_short_from_cache(original_url) + short_code = cached_short or generate_code() + set_cache_pair(short_code, original_url) + return { + "success": True, + "input_url": original_url, + "short_code": short_code, + "created_on": datetime.now(timezone.utc), + } + + try: + existing = db_data.urls.find_one({"original_url": original_url}) + except PyMongoError: + existing = None + + if existing: + return { + "success": True, + "input_url": original_url, + "short_code": existing["short_code"], + "created_on": existing["created_at"], + } + + short_code = generate_code() + try: + db_data.urls.insert_one( + { + "short_code": short_code, + "original_url": original_url, + "created_at": datetime.now(timezone.utc), + } + ) + except PyMongoError: + pass + + return { + "success": True, + "input_url": original_url, + "short_code": short_code, + "created_on": datetime.now(timezone.utc), + } + + +@app.get("/version") +def api_version(): + return {"version": __version__} + + +@api_v1.get("/help") +def get_help(): + return {"message": "Welcome to Tiny API. Visit /docs for API documentation."} + + +app.include_router(api_v1) diff --git a/app/db/data.py b/app/db/data.py index 36e476c..91ef6e0 100644 --- a/app/db/data.py +++ b/app/db/data.py @@ -1,47 +1,47 @@ -import os -from typing import Any - -# --- DEFENSIVE IMPORT --- -try: - from pymongo import MongoClient - - MONGO_INSTALLED = True -except ImportError: - # This allows the app to start even if 'pip install pymongo' wasn't run - MONGO_INSTALLED = False - -client: Any = None -db: Any = None -urls: Any = None -url_stats: Any = None - - -def connect_db(): - global client, db, urls, url_stats - - # 1. Check if the library is even there - if not MONGO_INSTALLED: - print("⚠️ pymongo is not installed. Running in NO-DB mode.") - return False - - # 2. Check if the config is there - MONGO_URI = os.getenv("MONGO_URI") - DB_NAME = os.getenv("DATABASE_NAME", "tiny_url") - - if not MONGO_URI: - print("⚠️ MONGO_URI missing. Running in NO-DB mode.") - return False - - try: - client = MongoClient(MONGO_URI, serverSelectionTimeoutMS=2000) - client.admin.command("ping") - - db = client[DB_NAME] - urls = db["urls"] - url_stats = db["url_stats"] - - print(f"βœ… MongoDB connected: '{DB_NAME}'") - return True - except Exception as e: - print(f"⚠️ MongoDB connection failed: {e}. Running in NO-DB mode.") - return False +import os +from typing import Any + +# --- DEFENSIVE IMPORT --- +try: + from pymongo import MongoClient + + MONGO_INSTALLED = True +except ImportError: + # This allows the app to start even if 'pip install pymongo' wasn't run + MONGO_INSTALLED = False + +client: Any = None +db: Any = None +urls: Any = None +url_stats: Any = None + + +def connect_db(): + global client, db, urls, url_stats + + # 1. Check if the library is even there + if not MONGO_INSTALLED: + print("⚠️ pymongo is not installed. Running in NO-DB mode.") + return False + + # 2. Check if the config is there + MONGO_URI = os.getenv("MONGO_URI") + DB_NAME = os.getenv("DATABASE_NAME", "tiny_url") + + if not MONGO_URI: + print("⚠️ MONGO_URI missing. Running in NO-DB mode.") + return False + + try: + client = MongoClient(MONGO_URI, serverSelectionTimeoutMS=2000) + client.admin.command("ping") + + db = client[DB_NAME] + urls = db["urls"] + url_stats = db["url_stats"] + + print(f"βœ… MongoDB connected: '{DB_NAME}'") + return True + except Exception as e: + print(f"⚠️ MongoDB connection failed: {e}. Running in NO-DB mode.") + return False diff --git a/app/main.py b/app/main.py index 786125f..3f93ba5 100644 --- a/app/main.py +++ b/app/main.py @@ -1,255 +1,255 @@ -import datetime -import os -from contextlib import asynccontextmanager -from pathlib import Path -from typing import Optional - - -from fastapi import FastAPI, Form, Request -from fastapi.responses import HTMLResponse, PlainTextResponse, RedirectResponse -from fastapi.staticfiles import StaticFiles -from fastapi.templating import Jinja2Templates -from starlette.middleware.sessions import SessionMiddleware - -from typing import TYPE_CHECKING - -if TYPE_CHECKING: - from pymongo.errors import PyMongoError -else: - try: - from pymongo.errors import PyMongoError - except ImportError: - - class PyMongoError(Exception): - pass - - -from app.api.fast_api import app as api_app -from app.db import data as db_data -from app.utils.qr import generate_qr_with_logo -from app.utils.cache import ( - get_from_cache, - get_short_from_cache, - set_cache_pair, - url_cache, - rev_cache, -) -from app.utils.config import load_env -from app.utils.helper import ( - format_date, - generate_code, - is_valid_url, - sanitize_url, -) - - -# ----------------------------- -# Lifespan: env + DB connect ONCE -# ----------------------------- -@asynccontextmanager -async def lifespan(app: FastAPI): - load_env() # βœ… load env ONCE - connected = db_data.connect_db() # βœ… connect DB ONCE - app.state.db_available = connected - yield - - -app = FastAPI(title="TinyURL", lifespan=lifespan) - - -app.add_middleware(SessionMiddleware, secret_key="super-secret-key") - - -BASE_DIR = Path(__file__).resolve().parent -STATIC_DIR = BASE_DIR / "static" - -app.mount("/static", StaticFiles(directory=STATIC_DIR), name="static") -templates = Jinja2Templates(directory=str(BASE_DIR / "templates")) - - -def db_available(request: Request) -> bool: - return getattr(request.app.state, "db_available", False) - - -def build_short_url(short_code: str, request_host_url: str) -> str: - base_url = os.getenv("DOMAIN", request_host_url).rstrip("/") - return f"{base_url}/{short_code}" - - -@app.get("/", response_class=HTMLResponse) -async def index(request: Request): - session = request.session - - new_short_url = session.pop("new_short_url", None) - qr_enabled = session.pop("qr_enabled", False) - qr_type = session.pop("qr_type", "short") - original_url = session.pop("original_url", None) - short_code = session.pop("short_code", None) - info_message = session.pop("info_message", None) - error = session.pop("error", None) - - qr_image = None - qr_data = None - - if qr_enabled and new_short_url and short_code: - qr_data = new_short_url if qr_type == "short" else original_url - qr_filename = f"{short_code}.png" - generate_qr_with_logo(qr_data, qr_filename) - qr_image = f"/static/qr/{qr_filename}" - - all_urls = [] - if db_available(request) and db_data.urls is not None: - try: - all_urls = list(db_data.urls.find().sort("created_at", -1)) - except PyMongoError: - all_urls = [] - - return templates.TemplateResponse( - "index.html", - { - "request": request, - "urls": all_urls, - "new_short_url": new_short_url, - "error": error, - "info_message": info_message, - "qr_data": qr_data, - "qr_enabled": qr_enabled, - "qr_type": qr_type, - "qr_image": qr_image, - "db_available": db_available(request), - }, - ) - - -@app.post("/", response_class=RedirectResponse) -async def create_short_url( - request: Request, - original_url: str = Form(""), - generate_qr: Optional[str] = Form(None), - qr_type: str = Form("short"), -): - session = request.session - qr_enabled = generate_qr == "on" - original_url = sanitize_url(original_url) - - if not original_url: - session["error"] = "URL cannot be empty." - return RedirectResponse("/", status_code=303) - - if not is_valid_url(original_url): - session["error"] = ( - "Please enter a valid URL (must start with http:// or https://)." - ) - return RedirectResponse("/", status_code=303) - - short_code = None - - if db_available(request) and db_data.urls is not None: - try: - existing = db_data.urls.find_one({"original_url": original_url}) - if existing: - short_code = existing["short_code"] - session["info_message"] = ( - "Already shortened before β€” using existing short URL." - ) - except PyMongoError: - pass - - if not short_code: - cached_short = get_short_from_cache(original_url) - short_code = cached_short or generate_code() - set_cache_pair(short_code, original_url) - - if db_available(request) and db_data.urls is not None: - try: - db_data.urls.insert_one( - { - "short_code": short_code, - "original_url": original_url, - "created_at": datetime.datetime.utcnow(), - "visit_count": 0, - } - ) - except PyMongoError: - pass - - new_short_url = build_short_url(short_code, str(request.base_url)) - session.update( - { - "new_short_url": new_short_url, - "qr_enabled": qr_enabled, - "qr_type": qr_type, - "original_url": original_url, - "short_code": short_code, - } - ) - - return RedirectResponse("/", status_code=303) - - -@app.get("/recent", response_class=HTMLResponse) -async def recent_urls(request: Request): - recent_urls_list = [] - if db_available(request) and db_data.urls is not None: - try: - recent_urls_list = list(db_data.urls.find().sort("created_at", -1)) - except PyMongoError: - pass - - return templates.TemplateResponse( - "recent.html", - {"request": request, "urls": recent_urls_list, "format_date": format_date}, - ) - - -@app.post("/delete/{short_code}") -async def delete_url(request: Request, short_code: str): - if db_available(request) and db_data.urls is not None: - try: - db_data.urls.delete_one({"short_code": short_code}) - except PyMongoError: - return PlainTextResponse("Database connection lost.", status_code=503) - - url_cache.pop(short_code, None) - return PlainTextResponse("", status_code=204) - - -@app.get("/{short_code}") -async def redirect_short(request: Request, short_code: str): - cached_url = get_from_cache(short_code) - if cached_url: - return RedirectResponse(cached_url) - - if not db_available(request) or db_data.urls is None: - return PlainTextResponse("Database is not connected.", status_code=503) - - try: - doc = db_data.urls.find_one_and_update( - {"short_code": short_code}, - {"$inc": {"visit_count": 1}}, - ) - except PyMongoError: - return PlainTextResponse("Database connection lost.", status_code=503) - - if not doc: - return PlainTextResponse("Invalid or expired short URL", status_code=404) - - set_cache_pair(short_code, doc["original_url"]) - return RedirectResponse(doc["original_url"]) - - -@app.get("/coming-soon", response_class=HTMLResponse) -async def coming_soon(request: Request): - return templates.TemplateResponse("coming-soon.html", {"request": request}) - - -app.mount("/api", api_app) - - -@app.get("/_debug/cache") -async def debug_cache(): - return { - "url_cache": url_cache, - "rev_cache": rev_cache, - "size": {"url_cache": len(url_cache), "rev_cache": len(rev_cache)}, - } +import datetime +import os +from contextlib import asynccontextmanager +from pathlib import Path +from typing import Optional + + +from fastapi import FastAPI, Form, Request +from fastapi.responses import HTMLResponse, PlainTextResponse, RedirectResponse +from fastapi.staticfiles import StaticFiles +from fastapi.templating import Jinja2Templates +from starlette.middleware.sessions import SessionMiddleware + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from pymongo.errors import PyMongoError +else: + try: + from pymongo.errors import PyMongoError + except ImportError: + + class PyMongoError(Exception): + pass + + +from app.api.fast_api import app as api_app +from app.db import data as db_data +from app.utils.qr import generate_qr_with_logo +from app.utils.cache import ( + get_from_cache, + get_short_from_cache, + set_cache_pair, + url_cache, + rev_cache, +) +from app.utils.config import load_env +from app.utils.helper import ( + format_date, + generate_code, + is_valid_url, + sanitize_url, +) + + +# ----------------------------- +# Lifespan: env + DB connect ONCE +# ----------------------------- +@asynccontextmanager +async def lifespan(app: FastAPI): + load_env() # βœ… load env ONCE + connected = db_data.connect_db() # βœ… connect DB ONCE + app.state.db_available = connected + yield + + +app = FastAPI(title="TinyURL", lifespan=lifespan) + + +app.add_middleware(SessionMiddleware, secret_key="super-secret-key") + + +BASE_DIR = Path(__file__).resolve().parent +STATIC_DIR = BASE_DIR / "static" + +app.mount("/static", StaticFiles(directory=STATIC_DIR), name="static") +templates = Jinja2Templates(directory=str(BASE_DIR / "templates")) + + +def db_available(request: Request) -> bool: + return getattr(request.app.state, "db_available", False) + + +def build_short_url(short_code: str, request_host_url: str) -> str: + base_url = os.getenv("DOMAIN", request_host_url).rstrip("/") + return f"{base_url}/{short_code}" + + +@app.get("/", response_class=HTMLResponse) +async def index(request: Request): + session = request.session + + new_short_url = session.pop("new_short_url", None) + qr_enabled = session.pop("qr_enabled", False) + qr_type = session.pop("qr_type", "short") + original_url = session.pop("original_url", None) + short_code = session.pop("short_code", None) + info_message = session.pop("info_message", None) + error = session.pop("error", None) + + qr_image = None + qr_data = None + + if qr_enabled and new_short_url and short_code: + qr_data = new_short_url if qr_type == "short" else original_url + qr_filename = f"{short_code}.png" + generate_qr_with_logo(qr_data, qr_filename) + qr_image = f"/static/qr/{qr_filename}" + + all_urls = [] + if db_available(request) and db_data.urls is not None: + try: + all_urls = list(db_data.urls.find().sort("created_at", -1)) + except PyMongoError: + all_urls = [] + + return templates.TemplateResponse( + "index.html", + { + "request": request, + "urls": all_urls, + "new_short_url": new_short_url, + "error": error, + "info_message": info_message, + "qr_data": qr_data, + "qr_enabled": qr_enabled, + "qr_type": qr_type, + "qr_image": qr_image, + "db_available": db_available(request), + }, + ) + + +@app.post("/", response_class=RedirectResponse) +async def create_short_url( + request: Request, + original_url: str = Form(""), + generate_qr: Optional[str] = Form(None), + qr_type: str = Form("short"), +): + session = request.session + qr_enabled = generate_qr == "on" + original_url = sanitize_url(original_url) + + if not original_url: + session["error"] = "URL cannot be empty." + return RedirectResponse("/", status_code=303) + + if not is_valid_url(original_url): + session["error"] = ( + "Please enter a valid URL (must start with http:// or https://)." + ) + return RedirectResponse("/", status_code=303) + + short_code = None + + if db_available(request) and db_data.urls is not None: + try: + existing = db_data.urls.find_one({"original_url": original_url}) + if existing: + short_code = existing["short_code"] + session["info_message"] = ( + "Already shortened before β€” using existing short URL." + ) + except PyMongoError: + pass + + if not short_code: + cached_short = get_short_from_cache(original_url) + short_code = cached_short or generate_code() + set_cache_pair(short_code, original_url) + + if db_available(request) and db_data.urls is not None: + try: + db_data.urls.insert_one( + { + "short_code": short_code, + "original_url": original_url, + "created_at": datetime.datetime.utcnow(), + "visit_count": 0, + } + ) + except PyMongoError: + pass + + new_short_url = build_short_url(short_code, str(request.base_url)) + session.update( + { + "new_short_url": new_short_url, + "qr_enabled": qr_enabled, + "qr_type": qr_type, + "original_url": original_url, + "short_code": short_code, + } + ) + + return RedirectResponse("/", status_code=303) + + +@app.get("/recent", response_class=HTMLResponse) +async def recent_urls(request: Request): + recent_urls_list = [] + if db_available(request) and db_data.urls is not None: + try: + recent_urls_list = list(db_data.urls.find().sort("created_at", -1)) + except PyMongoError: + pass + + return templates.TemplateResponse( + "recent.html", + {"request": request, "urls": recent_urls_list, "format_date": format_date}, + ) + + +@app.post("/delete/{short_code}") +async def delete_url(request: Request, short_code: str): + if db_available(request) and db_data.urls is not None: + try: + db_data.urls.delete_one({"short_code": short_code}) + except PyMongoError: + return PlainTextResponse("Database connection lost.", status_code=503) + + url_cache.pop(short_code, None) + return PlainTextResponse("", status_code=204) + + +@app.get("/{short_code}") +async def redirect_short(request: Request, short_code: str): + cached_url = get_from_cache(short_code) + if cached_url: + return RedirectResponse(cached_url) + + if not db_available(request) or db_data.urls is None: + return PlainTextResponse("Database is not connected.", status_code=503) + + try: + doc = db_data.urls.find_one_and_update( + {"short_code": short_code}, + {"$inc": {"visit_count": 1}}, + ) + except PyMongoError: + return PlainTextResponse("Database connection lost.", status_code=503) + + if not doc: + return PlainTextResponse("Invalid or expired short URL", status_code=404) + + set_cache_pair(short_code, doc["original_url"]) + return RedirectResponse(doc["original_url"]) + + +@app.get("/coming-soon", response_class=HTMLResponse) +async def coming_soon(request: Request): + return templates.TemplateResponse("coming-soon.html", {"request": request}) + + +app.mount("/api", api_app) + + +@app.get("/_debug/cache") +async def debug_cache(): + return { + "url_cache": url_cache, + "rev_cache": rev_cache, + "size": {"url_cache": len(url_cache), "rev_cache": len(rev_cache)}, + } diff --git a/app/static/style.css b/app/static/style.css index ff47cab..b754687 100644 --- a/app/static/style.css +++ b/app/static/style.css @@ -1,815 +1,815 @@ -html, -body { - height: 100%; - margin: 0; - font-family: Arial; - padding: 0; - font-family: "Poppins", system-ui, Arial, sans-serif; - background: var(--bg); - background-size: cover; - background-position: center; - background-size: cover; - background-position: center; -} -input { - width: 70%; - margin-top: 2px; - margin-bottom: 2px; - font-size: 16px; -} -.admin-box { - margin: 120px auto 60px; - /* space from header + footer */ -} -.app-layout { - min-height: 100vh; - display: flex; - flex-direction: column; - margin-top: var(--header-height); -} -button { - padding: 8px; - margin: 5px; -} -.error-box { - margin-bottom: 15px; - padding: 10px; - color: #ff4d4d; - border-radius: 8px; - font-weight: 600; -} - -.dark-theme h1 { - background: linear-gradient(90deg, #ffffff, #dddddd, #ffffff); - -webkit-background-clip: text; - background-clip: text; - -webkit-text-fill-color: transparent; - text-shadow: 0px 0px 10px rgba(255, 255, 255, 0.4); -} - -.dark-theme p { - background: linear-gradient(90deg, #ffffff, #dddddd, #ffffff); - -webkit-background-clip: text; - background-clip: text; - -webkit-text-fill-color: transparent; - text-shadow: 0px 0px 10px rgba(255, 255, 255, 0.4); -} -.dark-theme { - --bg-overlay: rgba(0, 0, 0, 0.75); - --glass-bg: rgba(0, 0, 0, 0.4); - --text-color: #fff; - --input-bg: rgba(50, 50, 50, 0.8); - --input-text-color: #fff; -} - -@keyframes pop { - 0% { - transform: scale(0.7); - opacity: 0; - } - 100% { - transform: scale(1); - opacity: 1; - } -} -/* INPUT CONTAINER */ -.input-field { - flex: 1 1 700px; - display: flex; - align-items: center; - gap: 12px; - border-radius: 12px; - border: 2px solid rgb(6, 0, 0); - background: transparent; /* IMPORTANT */ - padding: 12px 12px; -} -.dark-theme .input-field { - border-color: #ffffff; -} -/* INPUT ITSELF */ -.input-field input[type="text"] { - width: 100%; - border: none; - outline: none; - background-color: transparent !important; - background-image: none !important; - box-shadow: none !important; - font-size: 23px; -} - -.input-field input { - color: #000 !important; -} - -.dark-theme .input-field input { - color: #fff !important; -} - -.input-field input:-webkit-autofill, -.input-field input:-webkit-autofill:hover, -.input-field input:-webkit-autofill:focus, -.input-field input:-webkit-autofill:active { - -webkit-box-shadow: 0 0 0 1000px transparent inset !important; - box-shadow: 0 0 0 1000px transparent inset !important; - background-color: transparent !important; - background-image: none !important; - transition: background-color 9999s ease-in-out 0s; -} - -.input-field input:-webkit-autofill { - -webkit-text-fill-color: #000 !important; -} - -.dark-theme .input-field input:-webkit-autofill { - -webkit-text-fill-color: #fff !important; -} -.input-field input::selection, -.input-field input::-moz-selection { - background: transparent; - color: inherit; -} -.short-code { - color: #0a0000; /* blue like links */ - font-weight: 700; -} - -.app-header { - position: fixed; - top: 0; - left: 0; - width: 97%; - height: 55px; - background: white; - display: flex; - align-items: center; - padding: 0 28px; - box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08); - z-index: 1000; -} - -/* Dark mode */ -.dark-theme .app-header { - background: linear-gradient(180deg, #0b1220, #050b14); -} - -footer { - margin-top: 0; -} - -body.dark-theme, -body.dark-theme .page, -body.dark-theme main, -body.dark-theme section { - background: #0f1720 !important; -} - -.header-left { - display: flex; - align-items: center; - gap: 12px; -} - -.app-logo { - width: 36px; - height: 36px; - background: linear-gradient(135deg, #2563eb, #5ab9ff); - color: white; - border-radius: 10px; - display: flex; - align-items: center; - justify-content: center; - font-size: 18px; -} - -.app-name { - font-size: 20px; - font-weight: 700; - color: #111827; -} - -.dark-theme .app-name { - color: #f8fafc; -} - -.header-nav { - position: absolute; - left: 50%; - transform: translateX(-50%); - display: flex; - gap: 26px; -} - -.nav-link { - text-decoration: none; - color: #111827; - font-weight: 500; - position: relative; -} - -.dark-theme .nav-link { - color: #e5e7eb; -} - -.nav-link:hover { - color: #2563eb; -} - -.nav-link.active::after { - content: ""; - position: absolute; - bottom: -6px; - left: 0; - width: 100%; - height: 2px; - background: #111827; -} - -.dark-theme .nav-link.active::after { - background: #f8fafc; -} - -.header-right { - margin-left: auto; - display: flex; - align-items: center; -} - -:root { - --header-height: 55px; - --bg: #eefaf8; - --card: rgba(255, 255, 255, 0.95); - --muted: #7b8b8a; - --accent-1: #5ab9ff; - --accent-2: #4cb39f; - --accent-grad: linear-gradient(90deg, #4cb39f, #5ab9ff); - --success: #2fb06e; - --glass: rgba(255, 255, 255, 0.85); -} - -.dark-theme { - --bg-overlay: rgba(0, 0, 0, 0.75); - --glass-bg: rgba(0, 0, 0, 0.4); - --text-color: #f3f3f3; - --input-bg: rgba(11, 10, 10, 0.8); - --button-bg: linear-gradient(90deg, #4444ff, #2266ff); - --recent-bg: rgba(255, 255, 255, 0.1); -} - -/* Preserve your dark theme variables too */ -body.dark-theme { - --bg: #0f1720; - --card: rgba(10, 14, 18, 0.92); - --muted: #9aa7a6; -} - -.page { - flex: 1; - display: flex; - flex-direction: column; - align-items: center; - gap: 1rem; - padding: 2rem; - min-height: 80vh; -} - -.theme-toggle { - background: transparent; - border: none; - cursor: pointer; - padding: 8px; - border-radius: 8px; - font-weight: 700; - background: var(--card); -} - -/* Hero */ -.hero { - width: 100%; - max-width: 1100px; - background: transparent; - text-align: center; - padding: 10px; -} - -.hero h1 { - margin: 10px 0 14px; - font-size: 36px; - line-height: 1.05; - color: #000606; -} - -.hero p { - margin: var(--bg-overlay); - color: var(--muted); - max-width: 820px; - margin-left: auto; - margin-right: auto; - color: #000606; -} - -/* Main card & input */ -.card { - width: 100%; - max-width: 1100px; - background: var(--card); - border-radius: 14px; - padding: 15px; - box-shadow: 0 18px 50px rgba(8, 24, 24, 0.06); -} - -.cta { - min-width: 220px; - padding: 14px 22px; - border-radius: 12px; - border: none; - color: rgb(12, 1, 1); - font-weight: 700; - cursor: pointer; - background: var(--accent-grad); - box-shadow: 0 12px 28px rgba(77, 163, 185, 0.12); -} - -.small-action { - display: flex; - align-items: center; - gap: 8px; - color: var(--muted); - margin-top: 10px; -} - -.result { - margin-top: 26px; - background: white; - border-radius: 12px; - padding: 20px; - border: 1px solid rgba(22, 60, 55, 0.03); - box-shadow: 0 8px 28px rgba(7, 20, 20, 0.03); -} - -.result-header { - display: flex; - align-items: center; - gap: 12px; - margin-bottom: 12px; -} - -.result-header .dot { - width: 30px; - height: 30px; - background: var(--success); - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - color: white; - font-weight: 700; -} - -.short-actions { - display: flex; - justify-content: center; - align-items: center; - gap: 8px; - padding: 10px 14px; - border-radius: 12px; - background: linear-gradient(180deg, rgba(75, 194, 176, 0.06), rgba(94, 207, 255, 0.04)); -} - -.short-box input { - align-items: center; - padding: 10px; - font-size: 15px; -} - -.btn-copy { - border: none; - padding: 10px 14px; - border-radius: 8px; - color: white; - font-weight: 700; - cursor: pointer; -} - -.btn-share { - background: #f2f5f5; - border: none; - padding: 10px 14px; - border-radius: 8px; - color: #0b2b2a; - font-weight: 700; - cursor: pointer; - margin-left: 6px; -} - -.meta-row { - align-items: center; - justify-content: center; - display: grid; - grid-template-columns: 1fr 1fr; - gap: 2px; - padding: 16px; - margin-top: 1px; - align-items: top; - color: black; -} -.result-body { - margin-top: 30px; - - display: flex; - flex-direction: column; - align-items: center; - text-align: center; -} - -.qr-block { - text-align: center; - padding-top: 8px; -} - -.qr-block img { - height: 15rem; - align-items: center; - aspect-ratio: 1; - box-shadow: 0 10px 20px rgba(10, 20, 30, 0.06); - outline: 2px solid green; - outline-offset: 4px; -} - -.download-qr { - display: inline-block; - margin-top: 12px; - text-decoration: none; - color: var(--accent-1); - font-weight: 700; -} - -.action-row { - display: flex; - justify-content: right; - align-items: right; -} - -.action-secondary { - background: #f6fbfb; - border: 1px solid rgba(0, 0, 0, 0.03); - border-radius: 10px; - cursor: pointer; - font-weight: 700; -} - -/* Force Generate QR to stay on one line */ -.qr-inline { - display: inline-flex; - align-items: center; - gap: 8px; - white-space: nowrap; -} - -.qr-inline input { - margin: 0; -} - -/* Responsive */ -@media (max-width: 880px) { - .input-row { - flex-direction: column; - } - - .cta { - width: 100%; - } - - .meta-row { - grid-template-columns: 1fr; - } -} -.result-title { - font-weight: 700; - color: #0e34f6; -} - -.dark-theme .result-title { - color: #150cff; -} - -footer { - min-height: auto; -} - -.app-footer { - background: white; - color: #e5e7eb; - padding: 8px 10px; - margin-top: auto; - position: relative; -} -.dark-theme .app-footer { - background: linear-gradient(180deg, #0b1220, #050b14); -} - -.footer-container { - margin: auto; - display: flex; - gap: 60px; - justify-content: space-between; - flex-wrap: wrap; -} - -.footer-brand { - max-width: 420px; -} - -.footer-logo { - width: 42px; - height: 42px; - background: linear-gradient(135deg, #2563eb, #5ab9ff); - border-radius: 14px; - display: flex; - align-items: center; - justify-content: center; - font-size: 22px; -} -.dark-theme .footer-brand h3, -.dark-theme .footer-brand p, -.dark-theme .footer-col h4, -.dark-theme .app-footer a, -.dark-theme .footer-bottom { - color: #f8fafc; -} - -.footer-brand h3 { - margin: 0; - color: #000000; - font-size: 22px; - font-weight: 700; -} - -.footer-brand p { - margin-top: 8px; - color: #000000; - line-height: 1.6; - font-size: 14px; -} - -/* MAIN CONTENT */ - -/* FOOTER */ -.app-footer { - margin-top: auto; -} - -/* GitHub button */ -.github-btn { - display: inline-flex; - align-items: center; - gap: 2px; - margin-top: 1px; - padding: 10px 16px; - border-radius: 8px; - background: rgba(255, 255, 255, 0.06); - color: #000000; - text-decoration: none; - font-weight: 600; - transition: all 0.25s ease; -} - -.github-btn:hover { - background: black(11, 1, 1); - transform: translateY(-2px); -} - -.footer-links { - display: flex; - gap: 80px; - flex-wrap: wrap; -} - -.footer-col h4 { - margin-bottom: 14px; - font-size: 16px; - color: #000000; - font-weight: 700; -} - -.footer-col a { - display: block; - text-decoration: none; - color: #000000; - margin-bottom: 10px; - font-size: 14px; - transition: color 0.2s ease; -} - -.footer-col a:hover { - text-decoration: underline; -} - -/* Bottom */ -.footer-bottom { - margin-top: 10px; - border-top: 1px solid rgba(255, 255, 255, 0.153); - padding-top: 8px; - padding-bottom: 1px; - text-align: center; - font-size: 14px; - color: #080808; -} -.footer-bottom a { - color: #030000; - font-weight: 600; - text-decoration: none; -} - -.footer-bottom a:hover { - text-decoration: underline; -} - -/* Responsive */ -@media (max-width: 768px) { - .footer-container { - flex-direction: column; - gap: 40px; - } - - .footer-links { - gap: 40px; - } -} -/* REMOVE white line above footer in dark mode */ -footer { - margin-top: 0 !important; -} -.recent-table-wrapper { - margin-top: 20px; - width: 100%; - overflow-x: auto; -} - -.recent-table { - width: 100%; - border-collapse: collapse; - border-radius: 12px; - overflow: hidden; -} - -.recent-table thead { - background: rgb(0, 0, 0); -} -.recent-table th { - color: rgb(0, 0, 0); - padding: 8px 14px; - text-align: left; - font-size: 16px; -} -.short-code a { - color: #2563eb; - font-weight: 600; - text-decoration: none; -} - -.short-code a:hover { - color: #1d4ed8; - text-decoration: underline; -} -.recent-table td { - color: rgb(34, 48, 77); - padding: 10px 14px; - text-align: left; - font-size: 14px; -} - -.created-time { - font-size: 14px; - color: #374151; - white-space: nowrap; -} - -.time-ago { - color: #374151; - font-size: 13px; - margin-left: 2px; -} -.recent-table th { - font-weight: 700; -} - -.recent-table tbody tr, -th { - background: rgb(255, 255, 255); - border-bottom: 1px solid rgb(0, 0, 0); -} -.dark-theme.recent-table tbody tr, -td { - background: rgba(255, 255, 255, 0.04); - border-bottom: 1px solid rgb(0, 0, 0); -} -.recent-table tbody tr:hover { - background: rgb(196, 196, 196); -} - -/* Short code */ -.short-code { - font-weight: 700; -} - -.original-url { - color: #22c55e; - word-break: break-all; -} - -/* Action buttons */ -.action-col { - display: flex; - gap: 10px; -} - -.action-btn { - width: 36px; - height: 36px; - border-radius: 10px; - display: flex; - align-items: center; - justify-content: center; - text-decoration: none; - font-size: 16px; - transition: 0.2s ease; -} - -.open-btn { - background: #3b82f6; - color: #fff; -} - -.delete-btn { - background: #ef4444; - color: #fff; -} - -.recent-table-wrapper { - margin-bottom: 20px; -} -/* ========================= - Coming Soon Page -========================= */ - -.coming-soon-page { - display: flex; - align-items: center; - justify-content: center; - padding: 120px 20px 60px; -} - -.coming-soon-card { - max-width: 520px; - width: 100%; - background: var(--card); - border-radius: 16px; - padding: 50px 40px; - text-align: center; - box-shadow: 0 20px 50px rgba(0, 0, 0, 0.08); -} - -.coming-icon { - font-size: 48px; - margin-bottom: 18px; -} - -.coming-soon-card h1 { - font-size: 34px; - margin-bottom: 14px; - color: #000; -} - -.dark-theme .coming-soon-card h1 { - color: #fff; -} - -.coming-soon-card p { - font-size: 15px; - color: var(--muted); - line-height: 1.6; - margin-bottom: 28px; -} - -.coming-btn { - display: inline-block; - padding: 12px 22px; - border-radius: 10px; - background: var(--accent-grad); - color: #fff; - font-weight: 700; - text-decoration: none; - transition: 0.25s ease; -} - -.coming-btn:hover { - transform: scale(1.05); - box-shadow: 0 12px 28px rgba(77, 163, 185, 0.25); -} -.info-box { - margin-bottom: 15px; - padding: 10px; - color: #0e34f6; - border-radius: 8px; - font-weight: 700; -} +html, +body { + height: 100%; + margin: 0; + font-family: Arial; + padding: 0; + font-family: "Poppins", system-ui, Arial, sans-serif; + background: var(--bg); + background-size: cover; + background-position: center; + background-size: cover; + background-position: center; +} +input { + width: 70%; + margin-top: 2px; + margin-bottom: 2px; + font-size: 16px; +} +.admin-box { + margin: 120px auto 60px; + /* space from header + footer */ +} +.app-layout { + min-height: 100vh; + display: flex; + flex-direction: column; + margin-top: var(--header-height); +} +button { + padding: 8px; + margin: 5px; +} +.error-box { + margin-bottom: 15px; + padding: 10px; + color: #ff4d4d; + border-radius: 8px; + font-weight: 600; +} + +.dark-theme h1 { + background: linear-gradient(90deg, #ffffff, #dddddd, #ffffff); + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; + text-shadow: 0px 0px 10px rgba(255, 255, 255, 0.4); +} + +.dark-theme p { + background: linear-gradient(90deg, #ffffff, #dddddd, #ffffff); + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; + text-shadow: 0px 0px 10px rgba(255, 255, 255, 0.4); +} +.dark-theme { + --bg-overlay: rgba(0, 0, 0, 0.75); + --glass-bg: rgba(0, 0, 0, 0.4); + --text-color: #fff; + --input-bg: rgba(50, 50, 50, 0.8); + --input-text-color: #fff; +} + +@keyframes pop { + 0% { + transform: scale(0.7); + opacity: 0; + } + 100% { + transform: scale(1); + opacity: 1; + } +} +/* INPUT CONTAINER */ +.input-field { + flex: 1 1 700px; + display: flex; + align-items: center; + gap: 12px; + border-radius: 12px; + border: 2px solid rgb(6, 0, 0); + background: transparent; /* IMPORTANT */ + padding: 12px 12px; +} +.dark-theme .input-field { + border-color: #ffffff; +} +/* INPUT ITSELF */ +.input-field input[type="text"] { + width: 100%; + border: none; + outline: none; + background-color: transparent !important; + background-image: none !important; + box-shadow: none !important; + font-size: 23px; +} + +.input-field input { + color: #000 !important; +} + +.dark-theme .input-field input { + color: #fff !important; +} + +.input-field input:-webkit-autofill, +.input-field input:-webkit-autofill:hover, +.input-field input:-webkit-autofill:focus, +.input-field input:-webkit-autofill:active { + -webkit-box-shadow: 0 0 0 1000px transparent inset !important; + box-shadow: 0 0 0 1000px transparent inset !important; + background-color: transparent !important; + background-image: none !important; + transition: background-color 9999s ease-in-out 0s; +} + +.input-field input:-webkit-autofill { + -webkit-text-fill-color: #000 !important; +} + +.dark-theme .input-field input:-webkit-autofill { + -webkit-text-fill-color: #fff !important; +} +.input-field input::selection, +.input-field input::-moz-selection { + background: transparent; + color: inherit; +} +.short-code { + color: #0a0000; /* blue like links */ + font-weight: 700; +} + +.app-header { + position: fixed; + top: 0; + left: 0; + width: 97%; + height: 55px; + background: white; + display: flex; + align-items: center; + padding: 0 28px; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08); + z-index: 1000; +} + +/* Dark mode */ +.dark-theme .app-header { + background: linear-gradient(180deg, #0b1220, #050b14); +} + +footer { + margin-top: 0; +} + +body.dark-theme, +body.dark-theme .page, +body.dark-theme main, +body.dark-theme section { + background: #0f1720 !important; +} + +.header-left { + display: flex; + align-items: center; + gap: 12px; +} + +.app-logo { + width: 36px; + height: 36px; + background: linear-gradient(135deg, #2563eb, #5ab9ff); + color: white; + border-radius: 10px; + display: flex; + align-items: center; + justify-content: center; + font-size: 18px; +} + +.app-name { + font-size: 20px; + font-weight: 700; + color: #111827; +} + +.dark-theme .app-name { + color: #f8fafc; +} + +.header-nav { + position: absolute; + left: 50%; + transform: translateX(-50%); + display: flex; + gap: 26px; +} + +.nav-link { + text-decoration: none; + color: #111827; + font-weight: 500; + position: relative; +} + +.dark-theme .nav-link { + color: #e5e7eb; +} + +.nav-link:hover { + color: #2563eb; +} + +.nav-link.active::after { + content: ""; + position: absolute; + bottom: -6px; + left: 0; + width: 100%; + height: 2px; + background: #111827; +} + +.dark-theme .nav-link.active::after { + background: #f8fafc; +} + +.header-right { + margin-left: auto; + display: flex; + align-items: center; +} + +:root { + --header-height: 55px; + --bg: #eefaf8; + --card: rgba(255, 255, 255, 0.95); + --muted: #7b8b8a; + --accent-1: #5ab9ff; + --accent-2: #4cb39f; + --accent-grad: linear-gradient(90deg, #4cb39f, #5ab9ff); + --success: #2fb06e; + --glass: rgba(255, 255, 255, 0.85); +} + +.dark-theme { + --bg-overlay: rgba(0, 0, 0, 0.75); + --glass-bg: rgba(0, 0, 0, 0.4); + --text-color: #f3f3f3; + --input-bg: rgba(11, 10, 10, 0.8); + --button-bg: linear-gradient(90deg, #4444ff, #2266ff); + --recent-bg: rgba(255, 255, 255, 0.1); +} + +/* Preserve your dark theme variables too */ +body.dark-theme { + --bg: #0f1720; + --card: rgba(10, 14, 18, 0.92); + --muted: #9aa7a6; +} + +.page { + flex: 1; + display: flex; + flex-direction: column; + align-items: center; + gap: 1rem; + padding: 2rem; + min-height: 80vh; +} + +.theme-toggle { + background: transparent; + border: none; + cursor: pointer; + padding: 8px; + border-radius: 8px; + font-weight: 700; + background: var(--card); +} + +/* Hero */ +.hero { + width: 100%; + max-width: 1100px; + background: transparent; + text-align: center; + padding: 10px; +} + +.hero h1 { + margin: 10px 0 14px; + font-size: 36px; + line-height: 1.05; + color: #000606; +} + +.hero p { + margin: var(--bg-overlay); + color: var(--muted); + max-width: 820px; + margin-left: auto; + margin-right: auto; + color: #000606; +} + +/* Main card & input */ +.card { + width: 100%; + max-width: 1100px; + background: var(--card); + border-radius: 14px; + padding: 15px; + box-shadow: 0 18px 50px rgba(8, 24, 24, 0.06); +} + +.cta { + min-width: 220px; + padding: 14px 22px; + border-radius: 12px; + border: none; + color: rgb(12, 1, 1); + font-weight: 700; + cursor: pointer; + background: var(--accent-grad); + box-shadow: 0 12px 28px rgba(77, 163, 185, 0.12); +} + +.small-action { + display: flex; + align-items: center; + gap: 8px; + color: var(--muted); + margin-top: 10px; +} + +.result { + margin-top: 26px; + background: white; + border-radius: 12px; + padding: 20px; + border: 1px solid rgba(22, 60, 55, 0.03); + box-shadow: 0 8px 28px rgba(7, 20, 20, 0.03); +} + +.result-header { + display: flex; + align-items: center; + gap: 12px; + margin-bottom: 12px; +} + +.result-header .dot { + width: 30px; + height: 30px; + background: var(--success); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + color: white; + font-weight: 700; +} + +.short-actions { + display: flex; + justify-content: center; + align-items: center; + gap: 8px; + padding: 10px 14px; + border-radius: 12px; + background: linear-gradient(180deg, rgba(75, 194, 176, 0.06), rgba(94, 207, 255, 0.04)); +} + +.short-box input { + align-items: center; + padding: 10px; + font-size: 15px; +} + +.btn-copy { + border: none; + padding: 10px 14px; + border-radius: 8px; + color: white; + font-weight: 700; + cursor: pointer; +} + +.btn-share { + background: #f2f5f5; + border: none; + padding: 10px 14px; + border-radius: 8px; + color: #0b2b2a; + font-weight: 700; + cursor: pointer; + margin-left: 6px; +} + +.meta-row { + align-items: center; + justify-content: center; + display: grid; + grid-template-columns: 1fr 1fr; + gap: 2px; + padding: 16px; + margin-top: 1px; + align-items: top; + color: black; +} +.result-body { + margin-top: 30px; + + display: flex; + flex-direction: column; + align-items: center; + text-align: center; +} + +.qr-block { + text-align: center; + padding-top: 8px; +} + +.qr-block img { + height: 15rem; + align-items: center; + aspect-ratio: 1; + box-shadow: 0 10px 20px rgba(10, 20, 30, 0.06); + outline: 2px solid green; + outline-offset: 4px; +} + +.download-qr { + display: inline-block; + margin-top: 12px; + text-decoration: none; + color: var(--accent-1); + font-weight: 700; +} + +.action-row { + display: flex; + justify-content: right; + align-items: right; +} + +.action-secondary { + background: #f6fbfb; + border: 1px solid rgba(0, 0, 0, 0.03); + border-radius: 10px; + cursor: pointer; + font-weight: 700; +} + +/* Force Generate QR to stay on one line */ +.qr-inline { + display: inline-flex; + align-items: center; + gap: 8px; + white-space: nowrap; +} + +.qr-inline input { + margin: 0; +} + +/* Responsive */ +@media (max-width: 880px) { + .input-row { + flex-direction: column; + } + + .cta { + width: 100%; + } + + .meta-row { + grid-template-columns: 1fr; + } +} +.result-title { + font-weight: 700; + color: #0e34f6; +} + +.dark-theme .result-title { + color: #150cff; +} + +footer { + min-height: auto; +} + +.app-footer { + background: white; + color: #e5e7eb; + padding: 8px 10px; + margin-top: auto; + position: relative; +} +.dark-theme .app-footer { + background: linear-gradient(180deg, #0b1220, #050b14); +} + +.footer-container { + margin: auto; + display: flex; + gap: 60px; + justify-content: space-between; + flex-wrap: wrap; +} + +.footer-brand { + max-width: 420px; +} + +.footer-logo { + width: 42px; + height: 42px; + background: linear-gradient(135deg, #2563eb, #5ab9ff); + border-radius: 14px; + display: flex; + align-items: center; + justify-content: center; + font-size: 22px; +} +.dark-theme .footer-brand h3, +.dark-theme .footer-brand p, +.dark-theme .footer-col h4, +.dark-theme .app-footer a, +.dark-theme .footer-bottom { + color: #f8fafc; +} + +.footer-brand h3 { + margin: 0; + color: #000000; + font-size: 22px; + font-weight: 700; +} + +.footer-brand p { + margin-top: 8px; + color: #000000; + line-height: 1.6; + font-size: 14px; +} + +/* MAIN CONTENT */ + +/* FOOTER */ +.app-footer { + margin-top: auto; +} + +/* GitHub button */ +.github-btn { + display: inline-flex; + align-items: center; + gap: 2px; + margin-top: 1px; + padding: 10px 16px; + border-radius: 8px; + background: rgba(255, 255, 255, 0.06); + color: #000000; + text-decoration: none; + font-weight: 600; + transition: all 0.25s ease; +} + +.github-btn:hover { + background: black(11, 1, 1); + transform: translateY(-2px); +} + +.footer-links { + display: flex; + gap: 80px; + flex-wrap: wrap; +} + +.footer-col h4 { + margin-bottom: 14px; + font-size: 16px; + color: #000000; + font-weight: 700; +} + +.footer-col a { + display: block; + text-decoration: none; + color: #000000; + margin-bottom: 10px; + font-size: 14px; + transition: color 0.2s ease; +} + +.footer-col a:hover { + text-decoration: underline; +} + +/* Bottom */ +.footer-bottom { + margin-top: 10px; + border-top: 1px solid rgba(255, 255, 255, 0.153); + padding-top: 8px; + padding-bottom: 1px; + text-align: center; + font-size: 14px; + color: #080808; +} +.footer-bottom a { + color: #030000; + font-weight: 600; + text-decoration: none; +} + +.footer-bottom a:hover { + text-decoration: underline; +} + +/* Responsive */ +@media (max-width: 768px) { + .footer-container { + flex-direction: column; + gap: 40px; + } + + .footer-links { + gap: 40px; + } +} +/* REMOVE white line above footer in dark mode */ +footer { + margin-top: 0 !important; +} +.recent-table-wrapper { + margin-top: 20px; + width: 100%; + overflow-x: auto; +} + +.recent-table { + width: 100%; + border-collapse: collapse; + border-radius: 12px; + overflow: hidden; +} + +.recent-table thead { + background: rgb(0, 0, 0); +} +.recent-table th { + color: rgb(0, 0, 0); + padding: 8px 14px; + text-align: left; + font-size: 16px; +} +.short-code a { + color: #2563eb; + font-weight: 600; + text-decoration: none; +} + +.short-code a:hover { + color: #1d4ed8; + text-decoration: underline; +} +.recent-table td { + color: rgb(34, 48, 77); + padding: 10px 14px; + text-align: left; + font-size: 14px; +} + +.created-time { + font-size: 14px; + color: #374151; + white-space: nowrap; +} + +.time-ago { + color: #374151; + font-size: 13px; + margin-left: 2px; +} +.recent-table th { + font-weight: 700; +} + +.recent-table tbody tr, +th { + background: rgb(255, 255, 255); + border-bottom: 1px solid rgb(0, 0, 0); +} +.dark-theme.recent-table tbody tr, +td { + background: rgba(255, 255, 255, 0.04); + border-bottom: 1px solid rgb(0, 0, 0); +} +.recent-table tbody tr:hover { + background: rgb(196, 196, 196); +} + +/* Short code */ +.short-code { + font-weight: 700; +} + +.original-url { + color: #22c55e; + word-break: break-all; +} + +/* Action buttons */ +.action-col { + display: flex; + gap: 10px; +} + +.action-btn { + width: 36px; + height: 36px; + border-radius: 10px; + display: flex; + align-items: center; + justify-content: center; + text-decoration: none; + font-size: 16px; + transition: 0.2s ease; +} + +.open-btn { + background: #3b82f6; + color: #fff; +} + +.delete-btn { + background: #ef4444; + color: #fff; +} + +.recent-table-wrapper { + margin-bottom: 20px; +} +/* ========================= + Coming Soon Page +========================= */ + +.coming-soon-page { + display: flex; + align-items: center; + justify-content: center; + padding: 120px 20px 60px; +} + +.coming-soon-card { + max-width: 520px; + width: 100%; + background: var(--card); + border-radius: 16px; + padding: 50px 40px; + text-align: center; + box-shadow: 0 20px 50px rgba(0, 0, 0, 0.08); +} + +.coming-icon { + font-size: 48px; + margin-bottom: 18px; +} + +.coming-soon-card h1 { + font-size: 34px; + margin-bottom: 14px; + color: #000; +} + +.dark-theme .coming-soon-card h1 { + color: #fff; +} + +.coming-soon-card p { + font-size: 15px; + color: var(--muted); + line-height: 1.6; + margin-bottom: 28px; +} + +.coming-btn { + display: inline-block; + padding: 12px 22px; + border-radius: 10px; + background: var(--accent-grad); + color: #fff; + font-weight: 700; + text-decoration: none; + transition: 0.25s ease; +} + +.coming-btn:hover { + transform: scale(1.05); + box-shadow: 0 12px 28px rgba(77, 163, 185, 0.25); +} +.info-box { + margin-bottom: 15px; + padding: 10px; + color: #0e34f6; + border-radius: 8px; + font-weight: 700; +} diff --git a/app/templates/coming-soon.html b/app/templates/coming-soon.html index 3f86d1d..1a5e5eb 100644 --- a/app/templates/coming-soon.html +++ b/app/templates/coming-soon.html @@ -1,120 +1,120 @@ - - - - - - - Coming-soon - - - - - - -
-
- - tiny URL -
- - -
- -
-
- - -
-
-
🚧
-

Coming Soon

-

- This feature is under active development. -

- - ← Back to Home -
-
- - - - + + + + + + + Coming-soon + + + + + + +
+
+ + tiny URL +
+ + +
+ +
+
+ + +
+
+
🚧
+

Coming Soon

+

+ This feature is under active development. +

+ + ← Back to Home +
+
+ + + + \ No newline at end of file diff --git a/app/templates/index.html b/app/templates/index.html index 494fb2c..2796784 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -1,348 +1,348 @@ - - - - - - - tiny URL - - - - - - - - - -
-
- - tiny URL -
- - -
- -
-
- - - -
-
- -
- {% if not db_available %} -
- ⚠️ Running in Limited Mode (No Database) -
- {% endif %} - - -
-

Shorten Your URLs with Precision

-

Transform long links into short, memorable URLs.

-
- - -
- - -
-
- - -
-
- - - - - - - -
- - -
- {% if error %} -
{{ error }}
- {% endif %} - {% if info_message %} -
- {{ info_message }} -
- {% endif %} - - - -
- - - - -
- -
-
- - - {% if new_short_url %} -
-
-
βœ“
-
-
Your shortened URL is ready!
- -
Copy or share your short link, download the - QR code, or view details.
-
-
-
- -
- - {{ new_short_url }} - - - - -
-
- - - -
- -
- {% if qr_data %} - QR Code - - {% else %} -
-
- {% endif %} - -
Original URL
- {% set display_url = original_url or (urls[0].original_url if urls|length > 0 else '') %} - - {% if display_url %} - - {% endif %} - - -
-
-
-
-
-
-
-
- - - - -
- - -
-
-
- {% endif %} - - - - - -
- - - - - - - - - + + + + + + + tiny URL + + + + + + + + + +
+
+ + tiny URL +
+ + +
+ +
+
+ + + +
+
+ +
+ {% if not db_available %} +
+ ⚠️ Running in Limited Mode (No Database) +
+ {% endif %} + + +
+

Shorten Your URLs with Precision

+

Transform long links into short, memorable URLs.

+
+ + +
+ + +
+
+ + +
+
+ + + + + + + +
+ + +
+ {% if error %} +
{{ error }}
+ {% endif %} + {% if info_message %} +
+ {{ info_message }} +
+ {% endif %} + + + +
+ + + + +
+ +
+
+ + + {% if new_short_url %} +
+
+
βœ“
+
+
Your shortened URL is ready!
+ +
Copy or share your short link, download the + QR code, or view details.
+
+
+
+ +
+ + {{ new_short_url }} + + + + +
+
+ + + +
+ +
+ {% if qr_data %} + QR Code + + {% else %} +
+
+ {% endif %} + +
Original URL
+ {% set display_url = original_url or (urls[0].original_url if urls|length > 0 else '') %} + + {% if display_url %} + + {% endif %} + + +
+
+
+
+
+
+
+
+ + + + +
+ + +
+
+
+ {% endif %} + + + + + +
+ + + + + + + + + \ No newline at end of file diff --git a/app/templates/recent.html b/app/templates/recent.html index 0e44929..26c5d69 100644 --- a/app/templates/recent.html +++ b/app/templates/recent.html @@ -1,210 +1,210 @@ - - - - - - - Recent URLs - - - - - - -
-
- - tiny URL -
- - -
- -
-
- - - -
-
- -
- - - -
-

Recent Shortened URLs

-
- - -
- -
- - - - - - - - - - - - - - {% for url in urls %} - - - - - - - - - - - - - - - {% else %} - - - - {% endfor %} - -
#Short URLOriginal URLCreatedVisitsActions
{{ loop.index }} - - {{ request.host_url }}{{ url.short_code }} - - - - {{ url.original_url }} - - - {{ format_date(url.created_at) }} - {{ url.visit_count }} - - β†— - - - - πŸ—‘ - -
- No URLs found. -
-
- -
- -
- - - - - - + + + + + + + Recent URLs + + + + + + +
+
+ + tiny URL +
+ + +
+ +
+
+ + + +
+
+ +
+ + + +
+

Recent Shortened URLs

+
+ + +
+ +
+ + + + + + + + + + + + + + {% for url in urls %} + + + + + + + + + + + + + + + {% else %} + + + + {% endfor %} + +
#Short URLOriginal URLCreatedVisitsActions
{{ loop.index }} + + {{ request.host_url }}{{ url.short_code }} + + + + {{ url.original_url }} + + + {{ format_date(url.created_at) }} + {{ url.visit_count }} + + β†— + + + + πŸ—‘ + +
+ No URLs found. +
+
+ +
+ +
+ + + + + + \ No newline at end of file diff --git a/app/utils/__init__.py b/app/utils/__init__.py index 3dc1f76..3f5c4a7 100644 --- a/app/utils/__init__.py +++ b/app/utils/__init__.py @@ -1 +1 @@ -__version__ = "0.1.0" +__version__ = "0.1.0" diff --git a/app/utils/_version.py b/app/utils/_version.py index d6ba74d..71f139c 100644 --- a/app/utils/_version.py +++ b/app/utils/_version.py @@ -1,14 +1,14 @@ -# use this to update version in setup.py automatically -# when building the package, so that we don't have to maintain version in two places - -try: - from importlib.metadata import PackageNotFoundError, version -except ImportError: # Python <3.8 - from importlib.metadata import PackageNotFoundError, version - - -def get_version() -> str: - try: - return version("tiny") # must match pyproject.toml [project].name - except PackageNotFoundError: - return "0.0.1" +# use this to update version in setup.py automatically +# when building the package, so that we don't have to maintain version in two places + +try: + from importlib.metadata import PackageNotFoundError, version +except ImportError: # Python <3.8 + from importlib.metadata import PackageNotFoundError, version + + +def get_version() -> str: + try: + return version("tiny") # must match pyproject.toml [project].name + except PackageNotFoundError: + return "0.0.1" diff --git a/app/utils/cache.py b/app/utils/cache.py index 3ad9336..8d4cced 100644 --- a/app/utils/cache.py +++ b/app/utils/cache.py @@ -1,52 +1,52 @@ -import time -from typing import TypedDict - -CACHE_TTL = 900 # 15 minutes - - -class UrlCacheItem(TypedDict): - url: str - expires_at: float - - -class RevCacheItem(TypedDict): - short_code: str - expires_at: float - - -url_cache: dict[str, UrlCacheItem] = {} -rev_cache: dict[str, RevCacheItem] = {} - - -def get_from_cache(short_code: str) -> str | None: - data = url_cache.get(short_code) - - if not data or data["expires_at"] < time.time(): - url_cache.pop(short_code, None) - return None - - return data["url"] - - -def get_short_from_cache(original_url: str) -> str | None: - data = rev_cache.get(original_url) - - if not data or data["expires_at"] < time.time(): - rev_cache.pop(original_url, None) - return None - - return data["short_code"] - - -def set_cache_pair(short_code: str, original_url: str) -> None: - expires_at = time.time() + CACHE_TTL - - url_cache[short_code] = { - "url": original_url, - "expires_at": expires_at, - } - - rev_cache[original_url] = { - "short_code": short_code, - "expires_at": expires_at, - } +import time +from typing import TypedDict + +CACHE_TTL = 900 # 15 minutes + + +class UrlCacheItem(TypedDict): + url: str + expires_at: float + + +class RevCacheItem(TypedDict): + short_code: str + expires_at: float + + +url_cache: dict[str, UrlCacheItem] = {} +rev_cache: dict[str, RevCacheItem] = {} + + +def get_from_cache(short_code: str) -> str | None: + data = url_cache.get(short_code) + + if not data or data["expires_at"] < time.time(): + url_cache.pop(short_code, None) + return None + + return data["url"] + + +def get_short_from_cache(original_url: str) -> str | None: + data = rev_cache.get(original_url) + + if not data or data["expires_at"] < time.time(): + rev_cache.pop(original_url, None) + return None + + return data["short_code"] + + +def set_cache_pair(short_code: str, original_url: str) -> None: + expires_at = time.time() + CACHE_TTL + + url_cache[short_code] = { + "url": original_url, + "expires_at": expires_at, + } + + rev_cache[original_url] = { + "short_code": short_code, + "expires_at": expires_at, + } diff --git a/app/utils/helper.py b/app/utils/helper.py index c0dad93..07bdcf3 100644 --- a/app/utils/helper.py +++ b/app/utils/helper.py @@ -1,28 +1,28 @@ -import string -import random -from datetime import timezone -import validators - - -def is_valid_url(url: str) -> bool: - return bool(validators.url(url)) - - -def sanitize_url(url: str) -> str: - url = url.strip() - if not url: - return "" - if not url.startswith(("http://", "https://")): - url = "https://" + url - return url - - -def generate_code(length: int = 6) -> str: - chars = string.ascii_letters + string.digits - return "".join(random.choice(chars) for _ in range(length)) - - -def format_date(dt): - if dt.tzinfo is None: - dt = dt.replace(tzinfo=timezone.utc) - return dt.strftime("%d %b %Y") +import string +import random +from datetime import timezone +import validators + + +def is_valid_url(url: str) -> bool: + return bool(validators.url(url)) + + +def sanitize_url(url: str) -> str: + url = url.strip() + if not url: + return "" + if not url.startswith(("http://", "https://")): + url = "https://" + url + return url + + +def generate_code(length: int = 6) -> str: + chars = string.ascii_letters + string.digits + return "".join(random.choice(chars) for _ in range(length)) + + +def format_date(dt): + if dt.tzinfo is None: + dt = dt.replace(tzinfo=timezone.utc) + return dt.strftime("%d %b %Y") diff --git a/app/utils/lint.py b/app/utils/lint.py index 44ddcc2..3a88cb1 100644 --- a/app/utils/lint.py +++ b/app/utils/lint.py @@ -1,23 +1,23 @@ -import subprocess -import sys - -commands = [ - ["black", "."], - ["flake8", "."], - ["mypy", "-p", "app"], - ["ruff", "check", "."], -] - - -def main(): - for cmd in commands: - print(f"\nπŸš€ Running: {' '.join(cmd)}") - result = subprocess.run(["poetry", "run"] + cmd) - if result.returncode != 0: - print(f"❌ Command failed: {' '.join(cmd)}") - sys.exit(result.returncode) - print("\nβœ… All linters passed successfully!") - - -if __name__ == "__main__": - main() +import subprocess +import sys + +commands = [ + ["black", "."], + ["flake8", "."], + ["mypy", "-p", "app"], + ["ruff", "check", "."], +] + + +def main(): + for cmd in commands: + print(f"\nπŸš€ Running: {' '.join(cmd)}") + result = subprocess.run(["poetry", "run"] + cmd) + if result.returncode != 0: + print(f"❌ Command failed: {' '.join(cmd)}") + sys.exit(result.returncode) + print("\nβœ… All linters passed successfully!") + + +if __name__ == "__main__": + main() diff --git a/app/utils/qr.py b/app/utils/qr.py index fc99801..cf09213 100644 --- a/app/utils/qr.py +++ b/app/utils/qr.py @@ -1,44 +1,44 @@ -import qrcode -from PIL import Image -from pathlib import Path - - -def generate_qr_with_logo(data, filename): - qr = qrcode.QRCode( - error_correction=qrcode.constants.ERROR_CORRECT_H, - box_size=20, - border=1, - ) - qr.add_data(data) - qr.make(fit=True) - - qr_img = qr.make_image(fill_color="black", back_color="white").convert("RGB") - - # βœ… Resolve project app directory (app/) - APP_DIR = Path(__file__).resolve().parents[1] # goes up to /app - STATIC_DIR = APP_DIR / "static" - QR_DIR = STATIC_DIR / "qr" - IMAGES_DIR = STATIC_DIR / "images" - - # βœ… Ensure QR dir exists - QR_DIR.mkdir(parents=True, exist_ok=True) - - logo_path = IMAGES_DIR / "logo.png" - - if not logo_path.exists(): - raise FileNotFoundError(f"Logo not found at: {logo_path}") - - logo = Image.open(logo_path) - - qr_width, qr_height = qr_img.size - logo_size = qr_width // 3 - logo = logo.resize((logo_size, logo_size)) - - pos = ((qr_width - logo_size) // 2, (qr_height - logo_size) // 2) - - qr_img.paste(logo, pos, mask=logo if logo.mode == "RGBA" else None) - - save_path = QR_DIR / filename - qr_img.save(save_path) - - return str(save_path) +import qrcode +from PIL import Image +from pathlib import Path + + +def generate_qr_with_logo(data, filename): + qr = qrcode.QRCode( + error_correction=qrcode.constants.ERROR_CORRECT_H, + box_size=20, + border=1, + ) + qr.add_data(data) + qr.make(fit=True) + + qr_img = qr.make_image(fill_color="black", back_color="white").convert("RGB") + + # βœ… Resolve project app directory (app/) + APP_DIR = Path(__file__).resolve().parents[1] # goes up to /app + STATIC_DIR = APP_DIR / "static" + QR_DIR = STATIC_DIR / "qr" + IMAGES_DIR = STATIC_DIR / "images" + + # βœ… Ensure QR dir exists + QR_DIR.mkdir(parents=True, exist_ok=True) + + logo_path = IMAGES_DIR / "logo.png" + + if not logo_path.exists(): + raise FileNotFoundError(f"Logo not found at: {logo_path}") + + logo = Image.open(logo_path) + + qr_width, qr_height = qr_img.size + logo_size = qr_width // 3 + logo = logo.resize((logo_size, logo_size)) + + pos = ((qr_width - logo_size) // 2, (qr_height - logo_size) // 2) + + qr_img.paste(logo, pos, mask=logo if logo.mode == "RGBA" else None) + + save_path = QR_DIR / filename + qr_img.save(save_path) + + return str(save_path) diff --git a/docs/build-test.md b/docs/build-test.md index 39f619a..7f4c5fc 100644 --- a/docs/build-test.md +++ b/docs/build-test.md @@ -1,87 +1,87 @@ -# Build & Test Guide (Tiny URL Project) - -This document explains how to **build**, **install**, and **run tests locally** for the Tiny URL project using Poetry, FastAPI (API), and Fast(UI). - ---- - -## βœ… Prerequisites - -- Python 3.10+ -- Poetry installed -- MongoDB running locally -- `.env.local` file created (not committed to Git) - -Example `.env.local`: - -πŸ“¦ 1. Install Dependencies - -``` -poetry install -``` - -πŸ—οΈ 2. Build the Project - -This creates a source distribution and wheel package: - -``` -poetry build -``` - -Expected output: - -``` -Built tiny-0.0.1.tar.gz -Built tiny-0.0.1-py3-none-any.whl -``` - -πŸ§ͺ 3. Test the Built Wheel - -Create a clean virtual environment and install the wheel: - -``` -python3 -m venv /tmp/tiny-test -source /tmp/tiny-test/bin/activate - -pip install dist/tiny-0.0.1-py3-none-any.whl -``` - -Verify imports: - -``` -python -c "import app; print('app import OK')" -``` - -πŸš€ 4. Run API & UI Together (Two Terminals) - -Run both services in separate terminals. - -Terminal 1 – FastAPI (API) - -``` -poetry run uvicorn app.api.fast_api:app --reload --port 8001 -``` - -API will run at: - -```json -http://127.0.0.1:8000 -``` - -Docs: - -```json -http://127.0.0.1:8000/docs -``` - -Terminal 2 – Fast (UI) - -``` -poetry run uvicorn app.main:app --reload - -``` - -UI will run at: - -```json -http://127.0.0.1:5000 -``` +# Build & Test Guide (Tiny URL Project) + +This document explains how to **build**, **install**, and **run tests locally** for the Tiny URL project using Poetry, FastAPI (API), and Fast(UI). + +--- + +## βœ… Prerequisites + +- Python 3.10+ +- Poetry installed +- MongoDB running locally +- `.env.local` file created (not committed to Git) + +Example `.env.local`: + +πŸ“¦ 1. Install Dependencies + +``` +poetry install +``` + +πŸ—οΈ 2. Build the Project + +This creates a source distribution and wheel package: + +``` +poetry build +``` + +Expected output: + +``` +Built tiny-0.0.1.tar.gz +Built tiny-0.0.1-py3-none-any.whl +``` + +πŸ§ͺ 3. Test the Built Wheel + +Create a clean virtual environment and install the wheel: + +``` +python3 -m venv /tmp/tiny-test +source /tmp/tiny-test/bin/activate + +pip install dist/tiny-0.0.1-py3-none-any.whl +``` + +Verify imports: + +``` +python -c "import app; print('app import OK')" +``` + +πŸš€ 4. Run API & UI Together (Two Terminals) + +Run both services in separate terminals. + +Terminal 1 – FastAPI (API) + +``` +poetry run uvicorn app.api.fast_api:app --reload --port 8001 +``` + +API will run at: + +```json +http://127.0.0.1:8000 +``` + +Docs: + +```json +http://127.0.0.1:8000/docs +``` + +Terminal 2 – Fast (UI) + +``` +poetry run uvicorn app.main:app --reload + +``` + +UI will run at: + +```json +http://127.0.0.1:5000 +``` diff --git a/docs/cache.md b/docs/cache.md index a69ef3e..2f4e718 100644 --- a/docs/cache.md +++ b/docs/cache.md @@ -1,200 +1,200 @@ -# πŸ“˜ Cache – Complete Guide (Concepts, How It Works, How to Use It) - -`1. What is Cache?` - -Cache is a temporary storage layer used to store frequently accessed data so that future requests can be served faster. - -Instead of fetching data repeatedly from a slow source (like a database, API, or disk), the application first checks the cache. -If the data exists in cache, it is returned immediately. - -If not, the application fetches it from the original source and stores it in the cache for next time. - -Simple definition: - -Cache is a fast memory layer that stores frequently used data to reduce latency and load on the main data source. - -# 2. Why Cache is Needed - -Without cache: - -- Every request hits the database or external service - -- Response time is slow - -- Database load increases - -- Application does not scale well - -With cache: - -- Most requests are served from fast memory - -- Database load is reduced - -- Response time improves significantly - -- Application handles more traffic with the same resources - -Key benefits: - -- Performance improvement - -- Reduced load on database - -- Better user experience - -- Lower infrastructure cost - -- Improved scalability - - # 3. How Cache Works (High-Level Flow) - -Basic flow: - -``` -User Request - ↓ -Application checks Cache - ↓ -Cache Hit? ── Yes ──> Return data from Cache - β”‚ - No - ↓ -Fetch from Database / Source - ↓ -Store result in Cache - ↓ -Return data to User - -``` - -Cache Hit vs Cache Miss - -- Cache Hit: - Data is found in cache β†’ fast response - -- Cache Miss: - Data is not in cache β†’ fetch from main source β†’ save to cache β†’ return response - -# 5. Cache Lifetime (TTL – Time To Live) - -Cached data should not live forever. -A TTL (Time To Live) defines how long data remains in cache before it expires. - -`Why TTL is important:` - -- Prevents stale data - -- Frees memory - -- Keeps cache fresh - -- Avoids serving outdated values - -`Examples:` - -- Short TTL β†’ fast-changing data - -- Long TTL β†’ stable data - -`Once TTL expires:` - -- Data is removed from cache automatically - -- Next request becomes a cache miss - -# 6. Cache Invalidation (Keeping Cache Correct) - -Cache invalidation means removing or updating cached data when the original data changes. - -`When to invalidate cache:` - -- Data is updated - -- Data is deleted - -- Data becomes invalid - -- TTL expires - -`Why invalidation is important:` - -- Prevents serving outdated or incorrect data - -- Keeps cache consistent with main data source - Cache invalidation is one of the hardest problems in system design because it requires keeping cache and source data in sync. - -# 7. Common Cache Strategies (Conceptual) - -`1️⃣ Read-Through Cache` - -- Application reads from cache first - -- On miss, fetches from source and stores in cache - -`2️⃣ Write-Through Cache` - -- Data is written to cache and main storage together - -`3️⃣ Write-Behind Cache` - -- Data is written to cache first - -- Main storage is updated later asynchronously - -`Each strategy has trade-offs between:` - -- Speed - -- Consistency - -- Reliability - -# 1. Cache Consistency vs Performance - -Cache improves performance but can introduce stale data. - -There is always a trade-off between: - -- Strong consistency β†’ always fresh data - -- High performance β†’ faster responses - -`Design choice depends on:` - -- How critical correctness is - -- How often data changes - -- How much stale data is acceptable - -# 9. Cache Failure Handling - -Cache should never be a single point of failure. - -`Good design principles:` - -- Application should still work if cache is unavailable - -- Cache is an optimization, not the source of truth - -- Main data source remains authoritative - -`If cache fails:` - -- Application should fallback to main data source - -- Cache can be repopulated later - -# 10. Cache in Application Architecture - -`Cache usually sits between application and main storage:` - -```Client - ↓ -Application - ↓ -Cache Layer - ↓ -Database / External Service -``` +# πŸ“˜ Cache – Complete Guide (Concepts, How It Works, How to Use It) + +`1. What is Cache?` + +Cache is a temporary storage layer used to store frequently accessed data so that future requests can be served faster. + +Instead of fetching data repeatedly from a slow source (like a database, API, or disk), the application first checks the cache. +If the data exists in cache, it is returned immediately. + +If not, the application fetches it from the original source and stores it in the cache for next time. + +Simple definition: + +Cache is a fast memory layer that stores frequently used data to reduce latency and load on the main data source. + +# 2. Why Cache is Needed + +Without cache: + +- Every request hits the database or external service + +- Response time is slow + +- Database load increases + +- Application does not scale well + +With cache: + +- Most requests are served from fast memory + +- Database load is reduced + +- Response time improves significantly + +- Application handles more traffic with the same resources + +Key benefits: + +- Performance improvement + +- Reduced load on database + +- Better user experience + +- Lower infrastructure cost + +- Improved scalability + + # 3. How Cache Works (High-Level Flow) + +Basic flow: + +``` +User Request + ↓ +Application checks Cache + ↓ +Cache Hit? ── Yes ──> Return data from Cache + β”‚ + No + ↓ +Fetch from Database / Source + ↓ +Store result in Cache + ↓ +Return data to User + +``` + +Cache Hit vs Cache Miss + +- Cache Hit: + Data is found in cache β†’ fast response + +- Cache Miss: + Data is not in cache β†’ fetch from main source β†’ save to cache β†’ return response + +# 5. Cache Lifetime (TTL – Time To Live) + +Cached data should not live forever. +A TTL (Time To Live) defines how long data remains in cache before it expires. + +`Why TTL is important:` + +- Prevents stale data + +- Frees memory + +- Keeps cache fresh + +- Avoids serving outdated values + +`Examples:` + +- Short TTL β†’ fast-changing data + +- Long TTL β†’ stable data + +`Once TTL expires:` + +- Data is removed from cache automatically + +- Next request becomes a cache miss + +# 6. Cache Invalidation (Keeping Cache Correct) + +Cache invalidation means removing or updating cached data when the original data changes. + +`When to invalidate cache:` + +- Data is updated + +- Data is deleted + +- Data becomes invalid + +- TTL expires + +`Why invalidation is important:` + +- Prevents serving outdated or incorrect data + +- Keeps cache consistent with main data source + Cache invalidation is one of the hardest problems in system design because it requires keeping cache and source data in sync. + +# 7. Common Cache Strategies (Conceptual) + +`1️⃣ Read-Through Cache` + +- Application reads from cache first + +- On miss, fetches from source and stores in cache + +`2️⃣ Write-Through Cache` + +- Data is written to cache and main storage together + +`3️⃣ Write-Behind Cache` + +- Data is written to cache first + +- Main storage is updated later asynchronously + +`Each strategy has trade-offs between:` + +- Speed + +- Consistency + +- Reliability + +# 1. Cache Consistency vs Performance + +Cache improves performance but can introduce stale data. + +There is always a trade-off between: + +- Strong consistency β†’ always fresh data + +- High performance β†’ faster responses + +`Design choice depends on:` + +- How critical correctness is + +- How often data changes + +- How much stale data is acceptable + +# 9. Cache Failure Handling + +Cache should never be a single point of failure. + +`Good design principles:` + +- Application should still work if cache is unavailable + +- Cache is an optimization, not the source of truth + +- Main data source remains authoritative + +`If cache fails:` + +- Application should fallback to main data source + +- Cache can be repopulated later + +# 10. Cache in Application Architecture + +`Cache usually sits between application and main storage:` + +```Client + ↓ +Application + ↓ +Cache Layer + ↓ +Database / External Service +``` diff --git a/docs/tree.md b/docs/tree.md index 3786778..ad9374d 100644 --- a/docs/tree.md +++ b/docs/tree.md @@ -1,47 +1,47 @@ -## Project Folder Structure - -```text -Directory structure: -tiny/ -β”œβ”€β”€ CHANGELOG.md -β”œβ”€β”€ LICENSE -β”œβ”€β”€ README.md -β”œβ”€β”€ app/ -β”‚ β”œβ”€β”€__init__.py -β”‚ β”œβ”€β”€ main.py -β”‚ β”œβ”€β”€ cli.py -β”‚ β”œβ”€β”€ api/ -β”‚ β”‚ └── fast_api.py -| β”œβ”€β”€assets/images -β”‚ β”œβ”€β”€ db/ -| | └──__init__.py -| | └──data.py -β”‚ β”œβ”€β”€ static/ -| | └── images -| | └── qr -| | └──style.css -| └── templates/ -| | └── index.html -| | └── recent.html -| | └── coming-soon.html -β”‚ β”œβ”€β”€ utils/ -| | └──__init__.py -| | └──_version.py -| | └── cache.py -| | └── config.py -| | └── helper.py -| | └── lint.py -| | └── qr.py -β”œβ”€β”€ docs/ -| └── build-test.md -| └── cache.md -| └── run_with_curl.md -β”œβ”€β”€ pyproject.toml -| └── poetry.lock -β”œβ”€β”€README.md -| └── CHANGELOG.md -| └── requirements.txt -β”œβ”€β”€ tiny.code-workspace -└── .gitignore - -``` +## Project Folder Structure + +```text +Directory structure: +tiny/ +β”œβ”€β”€ CHANGELOG.md +β”œβ”€β”€ LICENSE +β”œβ”€β”€ README.md +β”œβ”€β”€ app/ +β”‚ β”œβ”€β”€__init__.py +β”‚ β”œβ”€β”€ main.py +β”‚ β”œβ”€β”€ cli.py +β”‚ β”œβ”€β”€ api/ +β”‚ β”‚ └── fast_api.py +| β”œβ”€β”€assets/images +β”‚ β”œβ”€β”€ db/ +| | └──__init__.py +| | └──data.py +β”‚ β”œβ”€β”€ static/ +| | └── images +| | └── qr +| | └──style.css +| └── templates/ +| | └── index.html +| | └── recent.html +| | └── coming-soon.html +β”‚ β”œβ”€β”€ utils/ +| | └──__init__.py +| | └──_version.py +| | └── cache.py +| | └── config.py +| | └── helper.py +| | └── lint.py +| | └── qr.py +β”œβ”€β”€ docs/ +| └── build-test.md +| └── cache.md +| └── run_with_curl.md +β”œβ”€β”€ pyproject.toml +| └── poetry.lock +β”œβ”€β”€README.md +| └── CHANGELOG.md +| └── requirements.txt +β”œβ”€β”€ tiny.code-workspace +└── .gitignore + +``` diff --git a/group_shorten.ps1 b/group_shorten.ps1 index 2ecaa87..3e52c10 100644 --- a/group_shorten.ps1 +++ b/group_shorten.ps1 @@ -1,134 +1,134 @@ -<# -========================================================= - PowerShell Script: group_shorten.ps1 ---------------------------------------------------------- - Description: - Bulk URL shortener client for Tiny API. - Reads a JSON file containing multiple URLs and sends - them one-by-one to the /api/shorten endpoint. - ---------------------------------------------------------- - HOW TO RUN (Windows PowerShell TERMINAL) ---------------------------------------------------------- - - 1. Open PowerShell as Administrator - - 2. Allow script execution (one-time): - Set-ExecutionPolicy RemoteSigned - - 3. Navigate to project root: - cd path\to\your\project - - 4. Run with JSON file: - .\group_shorten.ps1 -file ".\request\mixed_urls.json" - - 5. Or run without argument (interactive): - .\group_shorten.ps1 - - ---------------------------------------------------------- - HOW TO RUN (Windows PowerShell ISE) ---------------------------------------------------------- - - Method 1: Run with parameter - -------------------------------- - 1. Open Windows PowerShell ISE - 2. Open this script file (group_shorten.ps1) - 3. In the top menu, click: - File β†’ New PowerShell Tab - 4. In the console pane (bottom), run: - .\group_shorten.ps1 -file ".\request\mixed_urls.json" - - Method 2: Run interactively - -------------------------------- - 1. Open Windows PowerShell ISE - 2. Open this script file - 3. Press the green ▢️ Run Script button - 4. When prompted, enter the JSON file path - ---------------------------------------------------------- - Expected JSON input format: - [ - { "url": "https://example.com" }, - { "url": "https://google.com" } - ] -========================================================= -#> - -param ( - [string]$file -) - -# Ask for file path if not provided -if (-not $file) { - $file = Read-Host "Enter path to JSON file" -} - -# Validate file -if (-not (Test-Path $file)) { - Write-Host "❌ File not found: $file" -ForegroundColor Red - exit 1 -} - -# Load JSON -try { - $data = Get-Content $file -Raw | ConvertFrom-Json -} -catch { - Write-Host "❌ Invalid JSON file" -ForegroundColor Red - exit 1 -} - -$apiUrl = "http://127.0.0.1:8001/api/v1/shorten" - -Write-Host "`nπŸš€ Processing URLs..." -ForegroundColor Cyan - -foreach ($item in $data) { - - # Support string + object format - $url = if ($item -is [string]) { $item } else { $item.url } - - if (-not $url) { - Write-Host "⚠️ Empty URL, skipped" -ForegroundColor Yellow - continue - } - - # -------- LOCAL URL VALIDATION -------- - if ($url -notmatch '^[a-zA-Z]+://') { - Write-Host "❌ ERROR:" $url "- Missing protocol (http/https)" -ForegroundColor yellow - continue - } - - if ($url -notmatch '^https?://') { - Write-Host "❌ ERROR:" $url "- Unsupported protocol" -ForegroundColor yellow - continue - } - - if ($url -match 'https?:/[^/]') { - Write-Host "❌ ERROR:" $url "- Malformed URL (missing /)" -ForegroundColor yellow - continue - } - - if ($url -match '\.\.') { - Write-Host "❌ ERROR:" $url "- Invalid domain format" -ForegroundColor yellow - continue - } - - # -------- API REQUEST -------- - $body = @{ url = $url } | ConvertTo-Json - - try { - $response = Invoke-RestMethod ` - -Uri $apiUrl ` - -Method POST ` - -ContentType "application/json" ` - -Body $body - - Write-Host "βœ… SUCCESS:" $url "β†’" $response.short_code -ForegroundColor Green - } - catch { - Write-Host "❌ ERROR:" $url "- Rejected by API" -ForegroundColor Yellow - } -} - -Write-Host "`nπŸŽ‰ Done!" -ForegroundColor Cyan +<# +========================================================= + PowerShell Script: group_shorten.ps1 +--------------------------------------------------------- + Description: + Bulk URL shortener client for Tiny API. + Reads a JSON file containing multiple URLs and sends + them one-by-one to the /api/shorten endpoint. + +--------------------------------------------------------- + HOW TO RUN (Windows PowerShell TERMINAL) +--------------------------------------------------------- + + 1. Open PowerShell as Administrator + + 2. Allow script execution (one-time): + Set-ExecutionPolicy RemoteSigned + + 3. Navigate to project root: + cd path\to\your\project + + 4. Run with JSON file: + .\group_shorten.ps1 -file ".\request\mixed_urls.json" + + 5. Or run without argument (interactive): + .\group_shorten.ps1 + + +--------------------------------------------------------- + HOW TO RUN (Windows PowerShell ISE) +--------------------------------------------------------- + + Method 1: Run with parameter + -------------------------------- + 1. Open Windows PowerShell ISE + 2. Open this script file (group_shorten.ps1) + 3. In the top menu, click: + File β†’ New PowerShell Tab + 4. In the console pane (bottom), run: + .\group_shorten.ps1 -file ".\request\mixed_urls.json" + + Method 2: Run interactively + -------------------------------- + 1. Open Windows PowerShell ISE + 2. Open this script file + 3. Press the green ▢️ Run Script button + 4. When prompted, enter the JSON file path + +--------------------------------------------------------- + Expected JSON input format: + [ + { "url": "https://example.com" }, + { "url": "https://google.com" } + ] +========================================================= +#> + +param ( + [string]$file +) + +# Ask for file path if not provided +if (-not $file) { + $file = Read-Host "Enter path to JSON file" +} + +# Validate file +if (-not (Test-Path $file)) { + Write-Host "❌ File not found: $file" -ForegroundColor Red + exit 1 +} + +# Load JSON +try { + $data = Get-Content $file -Raw | ConvertFrom-Json +} +catch { + Write-Host "❌ Invalid JSON file" -ForegroundColor Red + exit 1 +} + +$apiUrl = "http://127.0.0.1:8001/api/v1/shorten" + +Write-Host "`nπŸš€ Processing URLs..." -ForegroundColor Cyan + +foreach ($item in $data) { + + # Support string + object format + $url = if ($item -is [string]) { $item } else { $item.url } + + if (-not $url) { + Write-Host "⚠️ Empty URL, skipped" -ForegroundColor Yellow + continue + } + + # -------- LOCAL URL VALIDATION -------- + if ($url -notmatch '^[a-zA-Z]+://') { + Write-Host "❌ ERROR:" $url "- Missing protocol (http/https)" -ForegroundColor yellow + continue + } + + if ($url -notmatch '^https?://') { + Write-Host "❌ ERROR:" $url "- Unsupported protocol" -ForegroundColor yellow + continue + } + + if ($url -match 'https?:/[^/]') { + Write-Host "❌ ERROR:" $url "- Malformed URL (missing /)" -ForegroundColor yellow + continue + } + + if ($url -match '\.\.') { + Write-Host "❌ ERROR:" $url "- Invalid domain format" -ForegroundColor yellow + continue + } + + # -------- API REQUEST -------- + $body = @{ url = $url } | ConvertTo-Json + + try { + $response = Invoke-RestMethod ` + -Uri $apiUrl ` + -Method POST ` + -ContentType "application/json" ` + -Body $body + + Write-Host "βœ… SUCCESS:" $url "β†’" $response.short_code -ForegroundColor Green + } + catch { + Write-Host "❌ ERROR:" $url "- Rejected by API" -ForegroundColor Yellow + } +} + +Write-Host "`nπŸŽ‰ Done!" -ForegroundColor Cyan diff --git a/mypy.ini b/mypy.ini index cbe57d5..b8d8384 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1,11 +1,11 @@ -[mypy-untyped_package.*] -follow_untyped_imports = True - -[mypy] -ignore_missing_imports = True -disable_error_code = import-untyped,attr-defined - - -[mypy-win32com.*] -ignore_missing_imports = True - +[mypy-untyped_package.*] +follow_untyped_imports = True + +[mypy] +ignore_missing_imports = True +disable_error_code = import-untyped,attr-defined + + +[mypy-win32com.*] +ignore_missing_imports = True + diff --git a/poetry.lock b/poetry.lock index 1b94a62..d551953 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,2439 +1,2439 @@ -# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. - -[[package]] -name = "annotated-doc" -version = "0.0.4" -description = "Document parameters, class attributes, return types, and variables inline, with Annotated." -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320"}, - {file = "annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4"}, -] - -[[package]] -name = "annotated-types" -version = "0.7.0" -description = "Reusable constraint types to use with typing.Annotated" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, - {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, -] - -[[package]] -name = "anyio" -version = "4.12.1" -description = "High-level concurrency and networking framework on top of asyncio or Trio" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c"}, - {file = "anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703"}, -] - -[package.dependencies] -exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} -idna = ">=2.8" -typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} - -[package.extras] -trio = ["trio (>=0.31.0) ; python_version < \"3.10\"", "trio (>=0.32.0) ; python_version >= \"3.10\""] - -[[package]] -name = "async-timeout" -version = "5.0.1" -description = "Timeout context manager for asyncio programs" -optional = false -python-versions = ">=3.8" -groups = ["main"] -markers = "python_full_version < \"3.11.3\"" -files = [ - {file = "async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c"}, - {file = "async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3"}, -] - -[[package]] -name = "backports-tarfile" -version = "1.2.0" -description = "Backport of CPython tarfile module" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and python_version < \"3.12\"" -files = [ - {file = "backports.tarfile-1.2.0-py3-none-any.whl", hash = "sha256:77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34"}, - {file = "backports_tarfile-1.2.0.tar.gz", hash = "sha256:d75e02c268746e1b8144c278978b6e98e85de6ad16f8e4b0844a154557eca991"}, -] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["jaraco.test", "pytest (!=8.0.*)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)"] - -[[package]] -name = "black" -version = "26.1.0" -description = "The uncompromising code formatter." -optional = false -python-versions = ">=3.10" -groups = ["dev"] -files = [ - {file = "black-26.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ca699710dece84e3ebf6e92ee15f5b8f72870ef984bf944a57a777a48357c168"}, - {file = "black-26.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5e8e75dabb6eb83d064b0db46392b25cabb6e784ea624219736e8985a6b3675d"}, - {file = "black-26.1.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eb07665d9a907a1a645ee41a0df8a25ffac8ad9c26cdb557b7b88eeeeec934e0"}, - {file = "black-26.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:7ed300200918147c963c87700ccf9966dceaefbbb7277450a8d646fc5646bf24"}, - {file = "black-26.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:c5b7713daea9bf943f79f8c3b46f361cc5229e0e604dcef6a8bb6d1c37d9df89"}, - {file = "black-26.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3cee1487a9e4c640dc7467aaa543d6c0097c391dc8ac74eb313f2fbf9d7a7cb5"}, - {file = "black-26.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d62d14ca31c92adf561ebb2e5f2741bf8dea28aef6deb400d49cca011d186c68"}, - {file = "black-26.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fb1dafbbaa3b1ee8b4550a84425aac8874e5f390200f5502cf3aee4a2acb2f14"}, - {file = "black-26.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:101540cb2a77c680f4f80e628ae98bd2bd8812fb9d72ade4f8995c5ff019e82c"}, - {file = "black-26.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:6f3977a16e347f1b115662be07daa93137259c711e526402aa444d7a88fdc9d4"}, - {file = "black-26.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6eeca41e70b5f5c84f2f913af857cf2ce17410847e1d54642e658e078da6544f"}, - {file = "black-26.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:dd39eef053e58e60204f2cdf059e2442e2eb08f15989eefe259870f89614c8b6"}, - {file = "black-26.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9459ad0d6cd483eacad4c6566b0f8e42af5e8b583cee917d90ffaa3778420a0a"}, - {file = "black-26.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:a19915ec61f3a8746e8b10adbac4a577c6ba9851fa4a9e9fbfbcf319887a5791"}, - {file = "black-26.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:643d27fb5facc167c0b1b59d0315f2674a6e950341aed0fc05cf307d22bf4954"}, - {file = "black-26.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ba1d768fbfb6930fc93b0ecc32a43d8861ded16f47a40f14afa9bb04ab93d304"}, - {file = "black-26.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2b807c240b64609cb0e80d2200a35b23c7df82259f80bef1b2c96eb422b4aac9"}, - {file = "black-26.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1de0f7d01cc894066a1153b738145b194414cc6eeaad8ef4397ac9abacf40f6b"}, - {file = "black-26.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:91a68ae46bf07868963671e4d05611b179c2313301bd756a89ad4e3b3db2325b"}, - {file = "black-26.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:be5e2fe860b9bd9edbf676d5b60a9282994c03fbbd40fe8f5e75d194f96064ca"}, - {file = "black-26.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:9dc8c71656a79ca49b8d3e2ce8103210c9481c57798b48deeb3a8bb02db5f115"}, - {file = "black-26.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b22b3810451abe359a964cc88121d57f7bce482b53a066de0f1584988ca36e79"}, - {file = "black-26.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:53c62883b3f999f14e5d30b5a79bd437236658ad45b2f853906c7cbe79de00af"}, - {file = "black-26.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:f016baaadc423dc960cdddf9acae679e71ee02c4c341f78f3179d7e4819c095f"}, - {file = "black-26.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:66912475200b67ef5a0ab665011964bf924745103f51977a78b4fb92a9fc1bf0"}, - {file = "black-26.1.0-py3-none-any.whl", hash = "sha256:1054e8e47ebd686e078c0bb0eaf31e6ce69c966058d122f2c0c950311f9f3ede"}, - {file = "black-26.1.0.tar.gz", hash = "sha256:d294ac3340eef9c9eb5d29288e96dc719ff269a88e27b396340459dd85da4c58"}, -] - -[package.dependencies] -click = ">=8.0.0" -mypy-extensions = ">=0.4.3" -packaging = ">=22.0" -pathspec = ">=1.0.0" -platformdirs = ">=2" -pytokens = ">=0.3.0" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} - -[package.extras] -colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.10)"] -jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] -uvloop = ["uvloop (>=0.15.2)"] - -[[package]] -name = "boolean-py" -version = "5.0" -description = "Define boolean algebras, create and parse boolean expressions and create custom boolean DSL." -optional = false -python-versions = "*" -groups = ["dev"] -files = [ - {file = "boolean_py-5.0-py3-none-any.whl", hash = "sha256:ef28a70bd43115208441b53a045d1549e2f0ec6e3d08a9d142cbc41c1938e8d9"}, - {file = "boolean_py-5.0.tar.gz", hash = "sha256:60cbc4bad079753721d32649545505362c754e121570ada4658b852a3a318d95"}, -] - -[package.extras] -dev = ["build", "twine"] -docs = ["Sphinx (>=3.3.1)", "doc8 (>=0.8.1)", "sphinx-rtd-theme (>=0.5.0)", "sphinxcontrib-apidoc (>=0.3.0)"] -linting = ["black", "isort", "pycodestyle"] -testing = ["pytest (>=6,!=7.0.0)", "pytest-xdist (>=2)"] - -[[package]] -name = "cachecontrol" -version = "0.14.4" -description = "httplib2 caching for requests" -optional = false -python-versions = ">=3.10" -groups = ["dev"] -files = [ - {file = "cachecontrol-0.14.4-py3-none-any.whl", hash = "sha256:b7ac014ff72ee199b5f8af1de29d60239954f223e948196fa3d84adaffc71d2b"}, - {file = "cachecontrol-0.14.4.tar.gz", hash = "sha256:e6220afafa4c22a47dd0badb319f84475d79108100d04e26e8542ef7d3ab05a1"}, -] - -[package.dependencies] -filelock = {version = ">=3.8.0", optional = true, markers = "extra == \"filecache\""} -msgpack = ">=0.5.2,<2.0.0" -requests = ">=2.16.0" - -[package.extras] -dev = ["cachecontrol[filecache,redis]", "cheroot (>=11.1.2)", "cherrypy", "codespell", "furo", "mypy", "pytest", "pytest-cov", "ruff", "sphinx", "sphinx-copybutton", "types-redis", "types-requests"] -filecache = ["filelock (>=3.8.0)"] -redis = ["redis (>=2.10.5)"] - -[[package]] -name = "certifi" -version = "2026.1.4" -description = "Python package for providing Mozilla's CA Bundle." -optional = false -python-versions = ">=3.7" -groups = ["dev"] -files = [ - {file = "certifi-2026.1.4-py3-none-any.whl", hash = "sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c"}, - {file = "certifi-2026.1.4.tar.gz", hash = "sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120"}, -] - -[[package]] -name = "cffi" -version = "2.0.0" -description = "Foreign Function Interface for Python calling C code." -optional = false -python-versions = ">=3.9" -groups = ["dev"] -markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\" and platform_python_implementation != \"PyPy\"" -files = [ - {file = "cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44"}, - {file = "cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49"}, - {file = "cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c"}, - {file = "cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb"}, - {file = "cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0"}, - {file = "cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4"}, - {file = "cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453"}, - {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495"}, - {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5"}, - {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb"}, - {file = "cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a"}, - {file = "cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739"}, - {file = "cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe"}, - {file = "cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c"}, - {file = "cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92"}, - {file = "cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93"}, - {file = "cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5"}, - {file = "cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664"}, - {file = "cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26"}, - {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9"}, - {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414"}, - {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743"}, - {file = "cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5"}, - {file = "cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5"}, - {file = "cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d"}, - {file = "cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d"}, - {file = "cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c"}, - {file = "cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe"}, - {file = "cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062"}, - {file = "cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e"}, - {file = "cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037"}, - {file = "cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba"}, - {file = "cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94"}, - {file = "cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187"}, - {file = "cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18"}, - {file = "cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5"}, - {file = "cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6"}, - {file = "cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb"}, - {file = "cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca"}, - {file = "cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b"}, - {file = "cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b"}, - {file = "cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2"}, - {file = "cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3"}, - {file = "cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26"}, - {file = "cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c"}, - {file = "cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b"}, - {file = "cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27"}, - {file = "cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75"}, - {file = "cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91"}, - {file = "cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5"}, - {file = "cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13"}, - {file = "cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b"}, - {file = "cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c"}, - {file = "cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef"}, - {file = "cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775"}, - {file = "cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205"}, - {file = "cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1"}, - {file = "cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f"}, - {file = "cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25"}, - {file = "cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad"}, - {file = "cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9"}, - {file = "cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d"}, - {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c"}, - {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8"}, - {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc"}, - {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592"}, - {file = "cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512"}, - {file = "cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4"}, - {file = "cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e"}, - {file = "cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6"}, - {file = "cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9"}, - {file = "cffi-2.0.0-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:fe562eb1a64e67dd297ccc4f5addea2501664954f2692b69a76449ec7913ecbf"}, - {file = "cffi-2.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:de8dad4425a6ca6e4e5e297b27b5c824ecc7581910bf9aee86cb6835e6812aa7"}, - {file = "cffi-2.0.0-cp39-cp39-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:4647afc2f90d1ddd33441e5b0e85b16b12ddec4fca55f0d9671fef036ecca27c"}, - {file = "cffi-2.0.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3f4d46d8b35698056ec29bca21546e1551a205058ae1a181d871e278b0b28165"}, - {file = "cffi-2.0.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:e6e73b9e02893c764e7e8d5bb5ce277f1a009cd5243f8228f75f842bf937c534"}, - {file = "cffi-2.0.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:cb527a79772e5ef98fb1d700678fe031e353e765d1ca2d409c92263c6d43e09f"}, - {file = "cffi-2.0.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:61d028e90346df14fedc3d1e5441df818d095f3b87d286825dfcbd6459b7ef63"}, - {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0f6084a0ea23d05d20c3edcda20c3d006f9b6f3fefeac38f59262e10cef47ee2"}, - {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1cd13c99ce269b3ed80b417dcd591415d3372bcac067009b6e0f59c7d4015e65"}, - {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:89472c9762729b5ae1ad974b777416bfda4ac5642423fa93bd57a09204712322"}, - {file = "cffi-2.0.0-cp39-cp39-win32.whl", hash = "sha256:2081580ebb843f759b9f617314a24ed5738c51d2aee65d31e02f6f7a2b97707a"}, - {file = "cffi-2.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:b882b3df248017dba09d6b16defe9b5c407fe32fc7c65a9c69798e6175601be9"}, - {file = "cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529"}, -] - -[package.dependencies] -pycparser = {version = "*", markers = "implementation_name != \"PyPy\""} - -[[package]] -name = "charset-normalizer" -version = "3.4.4" -description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -optional = false -python-versions = ">=3.7" -groups = ["dev"] -files = [ - {file = "charset_normalizer-3.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d"}, - {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8"}, - {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad"}, - {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8"}, - {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d"}, - {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313"}, - {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e"}, - {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93"}, - {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0"}, - {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84"}, - {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e"}, - {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db"}, - {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6"}, - {file = "charset_normalizer-3.4.4-cp310-cp310-win32.whl", hash = "sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f"}, - {file = "charset_normalizer-3.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d"}, - {file = "charset_normalizer-3.4.4-cp310-cp310-win_arm64.whl", hash = "sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69"}, - {file = "charset_normalizer-3.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8"}, - {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0"}, - {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3"}, - {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc"}, - {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897"}, - {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381"}, - {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815"}, - {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0"}, - {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161"}, - {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4"}, - {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89"}, - {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569"}, - {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224"}, - {file = "charset_normalizer-3.4.4-cp311-cp311-win32.whl", hash = "sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a"}, - {file = "charset_normalizer-3.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016"}, - {file = "charset_normalizer-3.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1"}, - {file = "charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394"}, - {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25"}, - {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef"}, - {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d"}, - {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8"}, - {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86"}, - {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a"}, - {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f"}, - {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc"}, - {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf"}, - {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15"}, - {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9"}, - {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0"}, - {file = "charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26"}, - {file = "charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525"}, - {file = "charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3"}, - {file = "charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794"}, - {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed"}, - {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72"}, - {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328"}, - {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede"}, - {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894"}, - {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1"}, - {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490"}, - {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44"}, - {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133"}, - {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3"}, - {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e"}, - {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc"}, - {file = "charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac"}, - {file = "charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14"}, - {file = "charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2"}, - {file = "charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd"}, - {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb"}, - {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e"}, - {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14"}, - {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191"}, - {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838"}, - {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6"}, - {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e"}, - {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c"}, - {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090"}, - {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152"}, - {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828"}, - {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec"}, - {file = "charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9"}, - {file = "charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c"}, - {file = "charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2"}, - {file = "charset_normalizer-3.4.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ce8a0633f41a967713a59c4139d29110c07e826d131a316b50ce11b1d79b4f84"}, - {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaabd426fe94daf8fd157c32e571c85cb12e66692f15516a83a03264b08d06c3"}, - {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c4ef880e27901b6cc782f1b95f82da9313c0eb95c3af699103088fa0ac3ce9ac"}, - {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2aaba3b0819274cc41757a1da876f810a3e4d7b6eb25699253a4effef9e8e4af"}, - {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:778d2e08eda00f4256d7f672ca9fef386071c9202f5e4607920b86d7803387f2"}, - {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f155a433c2ec037d4e8df17d18922c3a0d9b3232a396690f17175d2946f0218d"}, - {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a8bf8d0f749c5757af2142fe7903a9df1d2e8aa3841559b2bad34b08d0e2bcf3"}, - {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:194f08cbb32dc406d6e1aea671a68be0823673db2832b38405deba2fb0d88f63"}, - {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:6aee717dcfead04c6eb1ce3bd29ac1e22663cdea57f943c87d1eab9a025438d7"}, - {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:cd4b7ca9984e5e7985c12bc60a6f173f3c958eae74f3ef6624bb6b26e2abbae4"}, - {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_riscv64.whl", hash = "sha256:b7cf1017d601aa35e6bb650b6ad28652c9cd78ee6caff19f3c28d03e1c80acbf"}, - {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:e912091979546adf63357d7e2ccff9b44f026c075aeaf25a52d0e95ad2281074"}, - {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:5cb4d72eea50c8868f5288b7f7f33ed276118325c1dfd3957089f6b519e1382a"}, - {file = "charset_normalizer-3.4.4-cp38-cp38-win32.whl", hash = "sha256:837c2ce8c5a65a2035be9b3569c684358dfbf109fd3b6969630a87535495ceaa"}, - {file = "charset_normalizer-3.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:44c2a8734b333e0578090c4cd6b16f275e07aa6614ca8715e6c038e865e70576"}, - {file = "charset_normalizer-3.4.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a9768c477b9d7bd54bc0c86dbaebdec6f03306675526c9927c0e8a04e8f94af9"}, - {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1bee1e43c28aa63cb16e5c14e582580546b08e535299b8b6158a7c9c768a1f3d"}, - {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fd44c878ea55ba351104cb93cc85e74916eb8fa440ca7903e57575e97394f608"}, - {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0f04b14ffe5fdc8c4933862d8306109a2c51e0704acfa35d51598eb45a1e89fc"}, - {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:cd09d08005f958f370f539f186d10aec3377d55b9eeb0d796025d4886119d76e"}, - {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4fe7859a4e3e8457458e2ff592f15ccb02f3da787fcd31e0183879c3ad4692a1"}, - {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fa09f53c465e532f4d3db095e0c55b615f010ad81803d383195b6b5ca6cbf5f3"}, - {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7fa17817dc5625de8a027cb8b26d9fefa3ea28c8253929b8d6649e705d2835b6"}, - {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:5947809c8a2417be3267efc979c47d76a079758166f7d43ef5ae8e9f92751f88"}, - {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:4902828217069c3c5c71094537a8e623f5d097858ac6ca8252f7b4d10b7560f1"}, - {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:7c308f7e26e4363d79df40ca5b2be1c6ba9f02bdbccfed5abddb7859a6ce72cf"}, - {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:2c9d3c380143a1fedbff95a312aa798578371eb29da42106a29019368a475318"}, - {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:cb01158d8b88ee68f15949894ccc6712278243d95f344770fa7593fa2d94410c"}, - {file = "charset_normalizer-3.4.4-cp39-cp39-win32.whl", hash = "sha256:2677acec1a2f8ef614c6888b5b4ae4060cc184174a938ed4e8ef690e15d3e505"}, - {file = "charset_normalizer-3.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:f8e160feb2aed042cd657a72acc0b481212ed28b1b9a95c0cee1621b524e1966"}, - {file = "charset_normalizer-3.4.4-cp39-cp39-win_arm64.whl", hash = "sha256:b5d84d37db046c5ca74ee7bb47dd6cbc13f80665fdde3e8040bdd3fb015ecb50"}, - {file = "charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f"}, - {file = "charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a"}, -] - -[[package]] -name = "click" -version = "8.3.1" -description = "Composable command line interface toolkit" -optional = false -python-versions = ">=3.10" -groups = ["main", "dev"] -files = [ - {file = "click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6"}, - {file = "click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -groups = ["main", "dev"] -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] -markers = {main = "sys_platform == \"win32\" or platform_system == \"Windows\"", dev = "platform_system == \"Windows\""} - -[[package]] -name = "cryptography" -version = "46.0.5" -description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." -optional = false -python-versions = "!=3.9.0,!=3.9.1,>=3.8" -groups = ["dev"] -markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\"" -files = [ - {file = "cryptography-46.0.5-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:351695ada9ea9618b3500b490ad54c739860883df6c1f555e088eaf25b1bbaad"}, - {file = "cryptography-46.0.5-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c18ff11e86df2e28854939acde2d003f7984f721eba450b56a200ad90eeb0e6b"}, - {file = "cryptography-46.0.5-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d7e3d356b8cd4ea5aff04f129d5f66ebdc7b6f8eae802b93739ed520c47c79b"}, - {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:50bfb6925eff619c9c023b967d5b77a54e04256c4281b0e21336a130cd7fc263"}, - {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:803812e111e75d1aa73690d2facc295eaefd4439be1023fefc4995eaea2af90d"}, - {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ee190460e2fbe447175cda91b88b84ae8322a104fc27766ad09428754a618ed"}, - {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:f145bba11b878005c496e93e257c1e88f154d278d2638e6450d17e0f31e558d2"}, - {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e9251e3be159d1020c4030bd2e5f84d6a43fe54b6c19c12f51cde9542a2817b2"}, - {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:47fb8a66058b80e509c47118ef8a75d14c455e81ac369050f20ba0d23e77fee0"}, - {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:4c3341037c136030cb46e4b1e17b7418ea4cbd9dd207e4a6f3b2b24e0d4ac731"}, - {file = "cryptography-46.0.5-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:890bcb4abd5a2d3f852196437129eb3667d62630333aacc13dfd470fad3aaa82"}, - {file = "cryptography-46.0.5-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:80a8d7bfdf38f87ca30a5391c0c9ce4ed2926918e017c29ddf643d0ed2778ea1"}, - {file = "cryptography-46.0.5-cp311-abi3-win32.whl", hash = "sha256:60ee7e19e95104d4c03871d7d7dfb3d22ef8a9b9c6778c94e1c8fcc8365afd48"}, - {file = "cryptography-46.0.5-cp311-abi3-win_amd64.whl", hash = "sha256:38946c54b16c885c72c4f59846be9743d699eee2b69b6988e0a00a01f46a61a4"}, - {file = "cryptography-46.0.5-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:94a76daa32eb78d61339aff7952ea819b1734b46f73646a07decb40e5b3448e2"}, - {file = "cryptography-46.0.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5be7bf2fb40769e05739dd0046e7b26f9d4670badc7b032d6ce4db64dddc0678"}, - {file = "cryptography-46.0.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fe346b143ff9685e40192a4960938545c699054ba11d4f9029f94751e3f71d87"}, - {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:c69fd885df7d089548a42d5ec05be26050ebcd2283d89b3d30676eb32ff87dee"}, - {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:8293f3dea7fc929ef7240796ba231413afa7b68ce38fd21da2995549f5961981"}, - {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:1abfdb89b41c3be0365328a410baa9df3ff8a9110fb75e7b52e66803ddabc9a9"}, - {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:d66e421495fdb797610a08f43b05269e0a5ea7f5e652a89bfd5a7d3c1dee3648"}, - {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:4e817a8920bfbcff8940ecfd60f23d01836408242b30f1a708d93198393a80b4"}, - {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:68f68d13f2e1cb95163fa3b4db4bf9a159a418f5f6e7242564fc75fcae667fd0"}, - {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:a3d1fae9863299076f05cb8a778c467578262fae09f9dc0ee9b12eb4268ce663"}, - {file = "cryptography-46.0.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c4143987a42a2397f2fc3b4d7e3a7d313fbe684f67ff443999e803dd75a76826"}, - {file = "cryptography-46.0.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7d731d4b107030987fd61a7f8ab512b25b53cef8f233a97379ede116f30eb67d"}, - {file = "cryptography-46.0.5-cp314-cp314t-win32.whl", hash = "sha256:c3bcce8521d785d510b2aad26ae2c966092b7daa8f45dd8f44734a104dc0bc1a"}, - {file = "cryptography-46.0.5-cp314-cp314t-win_amd64.whl", hash = "sha256:4d8ae8659ab18c65ced284993c2265910f6c9e650189d4e3f68445ef82a810e4"}, - {file = "cryptography-46.0.5-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:4108d4c09fbbf2789d0c926eb4152ae1760d5a2d97612b92d508d96c861e4d31"}, - {file = "cryptography-46.0.5-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d1f30a86d2757199cb2d56e48cce14deddf1f9c95f1ef1b64ee91ea43fe2e18"}, - {file = "cryptography-46.0.5-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:039917b0dc418bb9f6edce8a906572d69e74bd330b0b3fea4f79dab7f8ddd235"}, - {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ba2a27ff02f48193fc4daeadf8ad2590516fa3d0adeeb34336b96f7fa64c1e3a"}, - {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:61aa400dce22cb001a98014f647dc21cda08f7915ceb95df0c9eaf84b4b6af76"}, - {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ce58ba46e1bc2aac4f7d9290223cead56743fa6ab94a5d53292ffaac6a91614"}, - {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:420d0e909050490d04359e7fdb5ed7e667ca5c3c402b809ae2563d7e66a92229"}, - {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:582f5fcd2afa31622f317f80426a027f30dc792e9c80ffee87b993200ea115f1"}, - {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:bfd56bb4b37ed4f330b82402f6f435845a5f5648edf1ad497da51a8452d5d62d"}, - {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:a3d507bb6a513ca96ba84443226af944b0f7f47dcc9a399d110cd6146481d24c"}, - {file = "cryptography-46.0.5-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9f16fbdf4da055efb21c22d81b89f155f02ba420558db21288b3d0035bafd5f4"}, - {file = "cryptography-46.0.5-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ced80795227d70549a411a4ab66e8ce307899fad2220ce5ab2f296e687eacde9"}, - {file = "cryptography-46.0.5-cp38-abi3-win32.whl", hash = "sha256:02f547fce831f5096c9a567fd41bc12ca8f11df260959ecc7c3202555cc47a72"}, - {file = "cryptography-46.0.5-cp38-abi3-win_amd64.whl", hash = "sha256:556e106ee01aa13484ce9b0239bca667be5004efb0aabbed28d353df86445595"}, - {file = "cryptography-46.0.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:3b4995dc971c9fb83c25aa44cf45f02ba86f71ee600d81091c2f0cbae116b06c"}, - {file = "cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:bc84e875994c3b445871ea7181d424588171efec3e185dced958dad9e001950a"}, - {file = "cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:2ae6971afd6246710480e3f15824ed3029a60fc16991db250034efd0b9fb4356"}, - {file = "cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:d861ee9e76ace6cf36a6a89b959ec08e7bc2493ee39d07ffe5acb23ef46d27da"}, - {file = "cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:2b7a67c9cd56372f3249b39699f2ad479f6991e62ea15800973b956f4b73e257"}, - {file = "cryptography-46.0.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:8456928655f856c6e1533ff59d5be76578a7157224dbd9ce6872f25055ab9ab7"}, - {file = "cryptography-46.0.5.tar.gz", hash = "sha256:abace499247268e3757271b2f1e244b36b06f8515cf27c4d49468fc9eb16e93d"}, -] - -[package.dependencies] -cffi = {version = ">=2.0.0", markers = "python_full_version >= \"3.9.0\" and platform_python_implementation != \"PyPy\""} -typing-extensions = {version = ">=4.13.2", markers = "python_full_version < \"3.11.0\""} - -[package.extras] -docs = ["sphinx (>=5.3.0)", "sphinx-inline-tabs", "sphinx-rtd-theme (>=3.0.0)"] -docstest = ["pyenchant (>=3)", "readme-renderer (>=30.0)", "sphinxcontrib-spelling (>=7.3.1)"] -nox = ["nox[uv] (>=2024.4.15)"] -pep8test = ["check-sdist", "click (>=8.0.1)", "mypy (>=1.14)", "ruff (>=0.11.11)"] -sdist = ["build (>=1.0.0)"] -ssh = ["bcrypt (>=3.1.5)"] -test = ["certifi (>=2024)", "cryptography-vectors (==46.0.5)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] -test-randomorder = ["pytest-randomly"] - -[[package]] -name = "cyclonedx-python-lib" -version = "11.6.0" -description = "Python library for CycloneDX" -optional = false -python-versions = "<4.0,>=3.9" -groups = ["dev"] -files = [ - {file = "cyclonedx_python_lib-11.6.0-py3-none-any.whl", hash = "sha256:94f4aae97db42a452134dafdddcfab9745324198201c4777ed131e64c8380759"}, - {file = "cyclonedx_python_lib-11.6.0.tar.gz", hash = "sha256:7fb85a4371fa3a203e5be577ac22b7e9a7157f8b0058b7448731474d6dea7bf0"}, -] - -[package.dependencies] -license-expression = ">=30,<31" -packageurl-python = ">=0.11,<2" -py-serializable = ">=2.1.0,<3.0.0" -sortedcontainers = ">=2.4.0,<3.0.0" -typing_extensions = {version = ">=4.6,<5.0", markers = "python_version < \"3.13\""} - -[package.extras] -json-validation = ["jsonschema[format-nongpl] (>=4.25,<5.0)", "referencing (>=0.28.4)"] -validation = ["jsonschema[format-nongpl] (>=4.25,<5.0)", "lxml (>=4,<7)", "referencing (>=0.28.4)"] -xml-validation = ["lxml (>=4,<7)"] - -[[package]] -name = "defusedxml" -version = "0.7.1" -description = "XML bomb protection for Python stdlib modules" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -groups = ["dev"] -files = [ - {file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"}, - {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"}, -] - -[[package]] -name = "dnspython" -version = "2.8.0" -description = "DNS toolkit" -optional = false -python-versions = ">=3.10" -groups = ["main", "dev"] -files = [ - {file = "dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af"}, - {file = "dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f"}, -] - -[package.extras] -dev = ["black (>=25.1.0)", "coverage (>=7.0)", "flake8 (>=7)", "hypercorn (>=0.17.0)", "mypy (>=1.17)", "pylint (>=3)", "pytest (>=8.4)", "pytest-cov (>=6.2.0)", "quart-trio (>=0.12.0)", "sphinx (>=8.2.0)", "sphinx-rtd-theme (>=3.0.0)", "twine (>=6.1.0)", "wheel (>=0.45.0)"] -dnssec = ["cryptography (>=45)"] -doh = ["h2 (>=4.2.0)", "httpcore (>=1.0.0)", "httpx (>=0.28.0)"] -doq = ["aioquic (>=1.2.0)"] -idna = ["idna (>=3.10)"] -trio = ["trio (>=0.30)"] -wmi = ["wmi (>=1.5.1) ; platform_system == \"Windows\""] - -[[package]] -name = "docutils" -version = "0.22.4" -description = "Docutils -- Python Documentation Utilities" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "docutils-0.22.4-py3-none-any.whl", hash = "sha256:d0013f540772d1420576855455d050a2180186c91c15779301ac2ccb3eeb68de"}, - {file = "docutils-0.22.4.tar.gz", hash = "sha256:4db53b1fde9abecbb74d91230d32ab626d94f6badfc575d6db9194a49df29968"}, -] - -[[package]] -name = "exceptiongroup" -version = "1.3.1" -description = "Backport of PEP 654 (exception groups)" -optional = false -python-versions = ">=3.7" -groups = ["main"] -markers = "python_version == \"3.10\"" -files = [ - {file = "exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598"}, - {file = "exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219"}, -] - -[package.dependencies] -typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""} - -[package.extras] -test = ["pytest (>=6)"] - -[[package]] -name = "fastapi" -version = "0.128.7" -description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "fastapi-0.128.7-py3-none-any.whl", hash = "sha256:6bd9bd31cb7047465f2d3fa3ba3f33b0870b17d4eaf7cdb36d1576ab060ad662"}, - {file = "fastapi-0.128.7.tar.gz", hash = "sha256:783c273416995486c155ad2c0e2b45905dedfaf20b9ef8d9f6a9124670639a24"}, -] - -[package.dependencies] -annotated-doc = ">=0.0.2" -pydantic = ">=2.7.0" -starlette = ">=0.40.0,<1.0.0" -typing-extensions = ">=4.8.0" -typing-inspection = ">=0.4.2" - -[package.extras] -all = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.8)", "httpx (>=0.23.0,<1.0.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=3.1.5)", "orjson (>=3.9.3)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.18)", "pyyaml (>=5.3.1)", "ujson (>=5.8.0)", "uvicorn[standard] (>=0.12.0)"] -standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.8)", "httpx (>=0.23.0,<1.0.0)", "jinja2 (>=3.1.5)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.18)", "uvicorn[standard] (>=0.12.0)"] -standard-no-fastapi-cloud-cli = ["email-validator (>=2.0.0)", "fastapi-cli[standard-no-fastapi-cloud-cli] (>=0.0.8)", "httpx (>=0.23.0,<1.0.0)", "jinja2 (>=3.1.5)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.18)", "uvicorn[standard] (>=0.12.0)"] - -[[package]] -name = "filelock" -version = "3.20.3" -description = "A platform independent file lock." -optional = false -python-versions = ">=3.10" -groups = ["dev"] -files = [ - {file = "filelock-3.20.3-py3-none-any.whl", hash = "sha256:4b0dda527ee31078689fc205ec4f1c1bf7d56cf88b6dc9426c4f230e46c2dce1"}, - {file = "filelock-3.20.3.tar.gz", hash = "sha256:18c57ee915c7ec61cff0ecf7f0f869936c7c30191bb0cf406f1341778d0834e1"}, -] - -[[package]] -name = "flake8" -version = "7.3.0" -description = "the modular source code checker: pep8 pyflakes and co" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "flake8-7.3.0-py2.py3-none-any.whl", hash = "sha256:b9696257b9ce8beb888cdbe31cf885c90d31928fe202be0889a7cdafad32f01e"}, - {file = "flake8-7.3.0.tar.gz", hash = "sha256:fe044858146b9fc69b551a4b490d69cf960fcb78ad1edcb84e7fbb1b4a8e3872"}, -] - -[package.dependencies] -mccabe = ">=0.7.0,<0.8.0" -pycodestyle = ">=2.14.0,<2.15.0" -pyflakes = ">=3.4.0,<3.5.0" - -[[package]] -name = "h11" -version = "0.16.0" -description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86"}, - {file = "h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1"}, -] - -[[package]] -name = "id" -version = "1.6.1" -description = "A tool for generating OIDC identities" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "id-1.6.1-py3-none-any.whl", hash = "sha256:f5ec41ed2629a508f5d0988eda142e190c9c6da971100612c4de9ad9f9b237ca"}, - {file = "id-1.6.1.tar.gz", hash = "sha256:d0732d624fb46fd4e7bc4e5152f00214450953b9e772c182c1c22964def1a069"}, -] - -[package.dependencies] -urllib3 = ">=2,<3" - -[package.extras] -dev = ["build", "bump (>=1.3.2)", "id[lint,test]"] -lint = ["bandit", "interrogate", "mypy", "ruff (<0.14.15)"] -test = ["coverage[toml]", "pretend", "pytest", "pytest-cov"] - -[[package]] -name = "idna" -version = "3.11" -description = "Internationalized Domain Names in Applications (IDNA)" -optional = false -python-versions = ">=3.8" -groups = ["main", "dev"] -files = [ - {file = "idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea"}, - {file = "idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902"}, -] - -[package.extras] -all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] - -[[package]] -name = "importlib-metadata" -version = "8.7.1" -description = "Read metadata from Python packages" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and python_version < \"3.12\"" -files = [ - {file = "importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151"}, - {file = "importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb"}, -] - -[package.dependencies] -zipp = ">=3.20" - -[package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] -cover = ["pytest-cov"] -doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -enabler = ["pytest-enabler (>=3.4)"] -perf = ["ipython"] -test = ["flufl.flake8", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] -type = ["mypy (<1.19) ; platform_python_implementation == \"PyPy\"", "pytest-mypy (>=1.0.1)"] - -[[package]] -name = "itsdangerous" -version = "2.2.0" -description = "Safely pass data to untrusted environments and back." -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef"}, - {file = "itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173"}, -] - -[[package]] -name = "jaraco-classes" -version = "3.4.0" -description = "Utility functions for Python class constructs" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\"" -files = [ - {file = "jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790"}, - {file = "jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd"}, -] - -[package.dependencies] -more-itertools = "*" - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)"] - -[[package]] -name = "jaraco-context" -version = "6.1.0" -description = "Useful decorators and context managers" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\"" -files = [ - {file = "jaraco_context-6.1.0-py3-none-any.whl", hash = "sha256:a43b5ed85815223d0d3cfdb6d7ca0d2bc8946f28f30b6f3216bda070f68badda"}, - {file = "jaraco_context-6.1.0.tar.gz", hash = "sha256:129a341b0a85a7db7879e22acd66902fda67882db771754574338898b2d5d86f"}, -] - -[package.dependencies] -"backports.tarfile" = {version = "*", markers = "python_version < \"3.12\""} - -[package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] -cover = ["pytest-cov"] -doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -enabler = ["pytest-enabler (>=3.4)"] -test = ["jaraco.test (>=5.6.0)", "portend", "pytest (>=6,!=8.1.*)"] -type = ["mypy (<1.19) ; platform_python_implementation == \"PyPy\"", "pytest-mypy (>=1.0.1)"] - -[[package]] -name = "jaraco-functools" -version = "4.4.0" -description = "Functools like those found in stdlib" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\"" -files = [ - {file = "jaraco_functools-4.4.0-py3-none-any.whl", hash = "sha256:9eec1e36f45c818d9bf307c8948eb03b2b56cd44087b3cdc989abca1f20b9176"}, - {file = "jaraco_functools-4.4.0.tar.gz", hash = "sha256:da21933b0417b89515562656547a77b4931f98176eb173644c0d35032a33d6bb"}, -] - -[package.dependencies] -more_itertools = "*" - -[package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] -cover = ["pytest-cov"] -doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -enabler = ["pytest-enabler (>=3.4)"] -test = ["jaraco.classes", "pytest (>=6,!=8.1.*)"] -type = ["mypy (<1.19) ; platform_python_implementation == \"PyPy\"", "pytest-mypy (>=1.0.1)"] - -[[package]] -name = "jeepney" -version = "0.9.0" -description = "Low-level, pure Python DBus protocol wrapper." -optional = false -python-versions = ">=3.7" -groups = ["dev"] -markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\"" -files = [ - {file = "jeepney-0.9.0-py3-none-any.whl", hash = "sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683"}, - {file = "jeepney-0.9.0.tar.gz", hash = "sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732"}, -] - -[package.extras] -test = ["async-timeout ; python_version < \"3.11\"", "pytest", "pytest-asyncio (>=0.17)", "pytest-trio", "testpath", "trio"] -trio = ["trio"] - -[[package]] -name = "jinja2" -version = "3.1.6" -description = "A very fast and expressive template engine." -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, - {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, -] - -[package.dependencies] -MarkupSafe = ">=2.0" - -[package.extras] -i18n = ["Babel (>=2.7)"] - -[[package]] -name = "keyring" -version = "25.7.0" -description = "Store and access your passwords safely." -optional = false -python-versions = ">=3.9" -groups = ["dev"] -markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\"" -files = [ - {file = "keyring-25.7.0-py3-none-any.whl", hash = "sha256:be4a0b195f149690c166e850609a477c532ddbfbaed96a404d4e43f8d5e2689f"}, - {file = "keyring-25.7.0.tar.gz", hash = "sha256:fe01bd85eb3f8fb3dd0405defdeac9a5b4f6f0439edbb3149577f244a2e8245b"}, -] - -[package.dependencies] -importlib_metadata = {version = ">=4.11.4", markers = "python_version < \"3.12\""} -"jaraco.classes" = "*" -"jaraco.context" = "*" -"jaraco.functools" = "*" -jeepney = {version = ">=0.4.2", markers = "sys_platform == \"linux\""} -pywin32-ctypes = {version = ">=0.2.0", markers = "sys_platform == \"win32\""} -SecretStorage = {version = ">=3.2", markers = "sys_platform == \"linux\""} - -[package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] -completion = ["shtab (>=1.1.0)"] -cover = ["pytest-cov"] -doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -enabler = ["pytest-enabler (>=3.4)"] -test = ["pyfakefs", "pytest (>=6,!=8.1.*)"] -type = ["pygobject-stubs", "pytest-mypy (>=1.0.1)", "shtab", "types-pywin32"] - -[[package]] -name = "librt" -version = "0.7.8" -description = "Mypyc runtime library" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -markers = "platform_python_implementation != \"PyPy\"" -files = [ - {file = "librt-0.7.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b45306a1fc5f53c9330fbee134d8b3227fe5da2ab09813b892790400aa49352d"}, - {file = "librt-0.7.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:864c4b7083eeee250ed55135d2127b260d7eb4b5e953a9e5df09c852e327961b"}, - {file = "librt-0.7.8-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6938cc2de153bc927ed8d71c7d2f2ae01b4e96359126c602721340eb7ce1a92d"}, - {file = "librt-0.7.8-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:66daa6ac5de4288a5bbfbe55b4caa7bf0cd26b3269c7a476ffe8ce45f837f87d"}, - {file = "librt-0.7.8-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4864045f49dc9c974dadb942ac56a74cd0479a2aafa51ce272c490a82322ea3c"}, - {file = "librt-0.7.8-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a36515b1328dc5b3ffce79fe204985ca8572525452eacabee2166f44bb387b2c"}, - {file = "librt-0.7.8-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b7e7f140c5169798f90b80d6e607ed2ba5059784968a004107c88ad61fb3641d"}, - {file = "librt-0.7.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ff71447cb778a4f772ddc4ce360e6ba9c95527ed84a52096bd1bbf9fee2ec7c0"}, - {file = "librt-0.7.8-cp310-cp310-win32.whl", hash = "sha256:047164e5f68b7a8ebdf9fae91a3c2161d3192418aadd61ddd3a86a56cbe3dc85"}, - {file = "librt-0.7.8-cp310-cp310-win_amd64.whl", hash = "sha256:d6f254d096d84156a46a84861183c183d30734e52383602443292644d895047c"}, - {file = "librt-0.7.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ff3e9c11aa260c31493d4b3197d1e28dd07768594a4f92bec4506849d736248f"}, - {file = "librt-0.7.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ddb52499d0b3ed4aa88746aaf6f36a08314677d5c346234c3987ddc506404eac"}, - {file = "librt-0.7.8-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e9c0afebbe6ce177ae8edba0c7c4d626f2a0fc12c33bb993d163817c41a7a05c"}, - {file = "librt-0.7.8-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:631599598e2c76ded400c0a8722dec09217c89ff64dc54b060f598ed68e7d2a8"}, - {file = "librt-0.7.8-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c1ba843ae20db09b9d5c80475376168feb2640ce91cd9906414f23cc267a1ff"}, - {file = "librt-0.7.8-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b5b007bb22ea4b255d3ee39dfd06d12534de2fcc3438567d9f48cdaf67ae1ae3"}, - {file = "librt-0.7.8-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:dbd79caaf77a3f590cbe32dc2447f718772d6eea59656a7dcb9311161b10fa75"}, - {file = "librt-0.7.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:87808a8d1e0bd62a01cafc41f0fd6818b5a5d0ca0d8a55326a81643cdda8f873"}, - {file = "librt-0.7.8-cp311-cp311-win32.whl", hash = "sha256:31724b93baa91512bd0a376e7cf0b59d8b631ee17923b1218a65456fa9bda2e7"}, - {file = "librt-0.7.8-cp311-cp311-win_amd64.whl", hash = "sha256:978e8b5f13e52cf23a9e80f3286d7546baa70bc4ef35b51d97a709d0b28e537c"}, - {file = "librt-0.7.8-cp311-cp311-win_arm64.whl", hash = "sha256:20e3946863d872f7cabf7f77c6c9d370b8b3d74333d3a32471c50d3a86c0a232"}, - {file = "librt-0.7.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9b6943885b2d49c48d0cff23b16be830ba46b0152d98f62de49e735c6e655a63"}, - {file = "librt-0.7.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:46ef1f4b9b6cc364b11eea0ecc0897314447a66029ee1e55859acb3dd8757c93"}, - {file = "librt-0.7.8-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:907ad09cfab21e3c86e8f1f87858f7049d1097f77196959c033612f532b4e592"}, - {file = "librt-0.7.8-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2991b6c3775383752b3ca0204842743256f3ad3deeb1d0adc227d56b78a9a850"}, - {file = "librt-0.7.8-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03679b9856932b8c8f674e87aa3c55ea11c9274301f76ae8dc4d281bda55cf62"}, - {file = "librt-0.7.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3968762fec1b2ad34ce57458b6de25dbb4142713e9ca6279a0d352fa4e9f452b"}, - {file = "librt-0.7.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:bb7a7807523a31f03061288cc4ffc065d684c39db7644c676b47d89553c0d714"}, - {file = "librt-0.7.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad64a14b1e56e702e19b24aae108f18ad1bf7777f3af5fcd39f87d0c5a814449"}, - {file = "librt-0.7.8-cp312-cp312-win32.whl", hash = "sha256:0241a6ed65e6666236ea78203a73d800dbed896cf12ae25d026d75dc1fcd1dac"}, - {file = "librt-0.7.8-cp312-cp312-win_amd64.whl", hash = "sha256:6db5faf064b5bab9675c32a873436b31e01d66ca6984c6f7f92621656033a708"}, - {file = "librt-0.7.8-cp312-cp312-win_arm64.whl", hash = "sha256:57175aa93f804d2c08d2edb7213e09276bd49097611aefc37e3fa38d1fb99ad0"}, - {file = "librt-0.7.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4c3995abbbb60b3c129490fa985dfe6cac11d88fc3c36eeb4fb1449efbbb04fc"}, - {file = "librt-0.7.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:44e0c2cbc9bebd074cf2cdbe472ca185e824be4e74b1c63a8e934cea674bebf2"}, - {file = "librt-0.7.8-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4d2f1e492cae964b3463a03dc77a7fe8742f7855d7258c7643f0ee32b6651dd3"}, - {file = "librt-0.7.8-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:451e7ffcef8f785831fdb791bd69211f47e95dc4c6ddff68e589058806f044c6"}, - {file = "librt-0.7.8-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3469e1af9f1380e093ae06bedcbdd11e407ac0b303a56bbe9afb1d6824d4982d"}, - {file = "librt-0.7.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f11b300027ce19a34f6d24ebb0a25fd0e24a9d53353225a5c1e6cadbf2916b2e"}, - {file = "librt-0.7.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4adc73614f0d3c97874f02f2c7fd2a27854e7e24ad532ea6b965459c5b757eca"}, - {file = "librt-0.7.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:60c299e555f87e4c01b2eca085dfccda1dde87f5a604bb45c2906b8305819a93"}, - {file = "librt-0.7.8-cp313-cp313-win32.whl", hash = "sha256:b09c52ed43a461994716082ee7d87618096851319bf695d57ec123f2ab708951"}, - {file = "librt-0.7.8-cp313-cp313-win_amd64.whl", hash = "sha256:f8f4a901a3fa28969d6e4519deceab56c55a09d691ea7b12ca830e2fa3461e34"}, - {file = "librt-0.7.8-cp313-cp313-win_arm64.whl", hash = "sha256:43d4e71b50763fcdcf64725ac680d8cfa1706c928b844794a7aa0fa9ac8e5f09"}, - {file = "librt-0.7.8-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:be927c3c94c74b05128089a955fba86501c3b544d1d300282cc1b4bd370cb418"}, - {file = "librt-0.7.8-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7b0803e9008c62a7ef79058233db7ff6f37a9933b8f2573c05b07ddafa226611"}, - {file = "librt-0.7.8-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:79feb4d00b2a4e0e05c9c56df707934f41fcb5fe53fd9efb7549068d0495b758"}, - {file = "librt-0.7.8-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b9122094e3f24aa759c38f46bd8863433820654927370250f460ae75488b66ea"}, - {file = "librt-0.7.8-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7e03bea66af33c95ce3addf87a9bf1fcad8d33e757bc479957ddbc0e4f7207ac"}, - {file = "librt-0.7.8-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f1ade7f31675db00b514b98f9ab9a7698c7282dad4be7492589109471852d398"}, - {file = "librt-0.7.8-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a14229ac62adcf1b90a15992f1ab9c69ae8b99ffb23cb64a90878a6e8a2f5b81"}, - {file = "librt-0.7.8-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5bcaaf624fd24e6a0cb14beac37677f90793a96864c67c064a91458611446e83"}, - {file = "librt-0.7.8-cp314-cp314-win32.whl", hash = "sha256:7aa7d5457b6c542ecaed79cec4ad98534373c9757383973e638ccced0f11f46d"}, - {file = "librt-0.7.8-cp314-cp314-win_amd64.whl", hash = "sha256:3d1322800771bee4a91f3b4bd4e49abc7d35e65166821086e5afd1e6c0d9be44"}, - {file = "librt-0.7.8-cp314-cp314-win_arm64.whl", hash = "sha256:5363427bc6a8c3b1719f8f3845ea53553d301382928a86e8fab7984426949bce"}, - {file = "librt-0.7.8-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:ca916919793a77e4a98d4a1701e345d337ce53be4a16620f063191f7322ac80f"}, - {file = "librt-0.7.8-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:54feb7b4f2f6706bb82325e836a01be805770443e2400f706e824e91f6441dde"}, - {file = "librt-0.7.8-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:39a4c76fee41007070f872b648cc2f711f9abf9a13d0c7162478043377b52c8e"}, - {file = "librt-0.7.8-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ac9c8a458245c7de80bc1b9765b177055efff5803f08e548dd4bb9ab9a8d789b"}, - {file = "librt-0.7.8-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:95b67aa7eff150f075fda09d11f6bfb26edffd300f6ab1666759547581e8f666"}, - {file = "librt-0.7.8-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:535929b6eff670c593c34ff435d5440c3096f20fa72d63444608a5aef64dd581"}, - {file = "librt-0.7.8-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:63937bd0f4d1cb56653dc7ae900d6c52c41f0015e25aaf9902481ee79943b33a"}, - {file = "librt-0.7.8-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cf243da9e42d914036fd362ac3fa77d80a41cadcd11ad789b1b5eec4daaf67ca"}, - {file = "librt-0.7.8-cp314-cp314t-win32.whl", hash = "sha256:171ca3a0a06c643bd0a2f62a8944e1902c94aa8e5da4db1ea9a8daf872685365"}, - {file = "librt-0.7.8-cp314-cp314t-win_amd64.whl", hash = "sha256:445b7304145e24c60288a2f172b5ce2ca35c0f81605f5299f3fa567e189d2e32"}, - {file = "librt-0.7.8-cp314-cp314t-win_arm64.whl", hash = "sha256:8766ece9de08527deabcd7cb1b4f1a967a385d26e33e536d6d8913db6ef74f06"}, - {file = "librt-0.7.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c7e8f88f79308d86d8f39c491773cbb533d6cb7fa6476f35d711076ee04fceb6"}, - {file = "librt-0.7.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:389bd25a0db916e1d6bcb014f11aa9676cedaa485e9ec3752dfe19f196fd377b"}, - {file = "librt-0.7.8-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:73fd300f501a052f2ba52ede721232212f3b06503fa12665408ecfc9d8fd149c"}, - {file = "librt-0.7.8-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d772edc6a5f7835635c7562f6688e031f0b97e31d538412a852c49c9a6c92d5"}, - {file = "librt-0.7.8-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bfde8a130bd0f239e45503ab39fab239ace094d63ee1d6b67c25a63d741c0f71"}, - {file = "librt-0.7.8-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fdec6e2368ae4f796fc72fad7fd4bd1753715187e6d870932b0904609e7c878e"}, - {file = "librt-0.7.8-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:00105e7d541a8f2ee5be52caacea98a005e0478cfe78c8080fbb7b5d2b340c63"}, - {file = "librt-0.7.8-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c6f8947d3dfd7f91066c5b4385812c18be26c9d5a99ca56667547f2c39149d94"}, - {file = "librt-0.7.8-cp39-cp39-win32.whl", hash = "sha256:41d7bb1e07916aeb12ae4a44e3025db3691c4149ab788d0315781b4d29b86afb"}, - {file = "librt-0.7.8-cp39-cp39-win_amd64.whl", hash = "sha256:e90a8e237753c83b8e484d478d9a996dc5e39fd5bd4c6ce32563bc8123f132be"}, - {file = "librt-0.7.8.tar.gz", hash = "sha256:1a4ede613941d9c3470b0368be851df6bb78ab218635512d0370b27a277a0862"}, -] - -[[package]] -name = "license-expression" -version = "30.4.4" -description = "license-expression is a comprehensive utility library to parse, compare, simplify and normalize license expressions (such as SPDX license expressions) using boolean logic." -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "license_expression-30.4.4-py3-none-any.whl", hash = "sha256:421788fdcadb41f049d2dc934ce666626265aeccefddd25e162a26f23bcbf8a4"}, - {file = "license_expression-30.4.4.tar.gz", hash = "sha256:73448f0aacd8d0808895bdc4b2c8e01a8d67646e4188f887375398c761f340fd"}, -] - -[package.dependencies] -"boolean.py" = ">=4.0" - -[package.extras] -dev = ["Sphinx (>=5.0.2)", "doc8 (>=0.11.2)", "pytest (>=7.0.1)", "pytest-xdist (>=2)", "ruff", "sphinx-autobuild", "sphinx-copybutton", "sphinx-reredirects (>=0.1.2)", "sphinx-rtd-dark-mode (>=1.3.0)", "sphinx-rtd-theme (>=1.0.0)", "sphinxcontrib-apidoc (>=0.4.0)", "twine"] - -[[package]] -name = "markdown-it-py" -version = "4.0.0" -description = "Python port of markdown-it. Markdown parsing, done right!" -optional = false -python-versions = ">=3.10" -groups = ["dev"] -files = [ - {file = "markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147"}, - {file = "markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3"}, -] - -[package.dependencies] -mdurl = ">=0.1,<1.0" - -[package.extras] -benchmarking = ["psutil", "pytest", "pytest-benchmark"] -compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "markdown-it-pyrs", "mistletoe (>=1.0,<2.0)", "mistune (>=3.0,<4.0)", "panflute (>=2.3,<3.0)"] -linkify = ["linkify-it-py (>=1,<3)"] -plugins = ["mdit-py-plugins (>=0.5.0)"] -profiling = ["gprof2dot"] -rtd = ["ipykernel", "jupyter_sphinx", "mdit-py-plugins (>=0.5.0)", "myst-parser", "pyyaml", "sphinx", "sphinx-book-theme (>=1.0,<2.0)", "sphinx-copybutton", "sphinx-design"] -testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions", "requests"] - -[[package]] -name = "markupsafe" -version = "3.0.3" -description = "Safely add untrusted strings to HTML/XML markup." -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559"}, - {file = "markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419"}, - {file = "markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695"}, - {file = "markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591"}, - {file = "markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c"}, - {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f"}, - {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6"}, - {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1"}, - {file = "markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa"}, - {file = "markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8"}, - {file = "markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1"}, - {file = "markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad"}, - {file = "markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a"}, - {file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50"}, - {file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf"}, - {file = "markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f"}, - {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a"}, - {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115"}, - {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a"}, - {file = "markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19"}, - {file = "markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01"}, - {file = "markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c"}, - {file = "markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e"}, - {file = "markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce"}, - {file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d"}, - {file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d"}, - {file = "markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a"}, - {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b"}, - {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f"}, - {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b"}, - {file = "markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d"}, - {file = "markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c"}, - {file = "markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f"}, - {file = "markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795"}, - {file = "markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219"}, - {file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6"}, - {file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676"}, - {file = "markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9"}, - {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1"}, - {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc"}, - {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12"}, - {file = "markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed"}, - {file = "markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5"}, - {file = "markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485"}, - {file = "markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73"}, - {file = "markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37"}, - {file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19"}, - {file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025"}, - {file = "markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6"}, - {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f"}, - {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb"}, - {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009"}, - {file = "markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354"}, - {file = "markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218"}, - {file = "markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287"}, - {file = "markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe"}, - {file = "markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026"}, - {file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737"}, - {file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97"}, - {file = "markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d"}, - {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda"}, - {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf"}, - {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe"}, - {file = "markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9"}, - {file = "markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581"}, - {file = "markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4"}, - {file = "markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab"}, - {file = "markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175"}, - {file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634"}, - {file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50"}, - {file = "markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e"}, - {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5"}, - {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523"}, - {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc"}, - {file = "markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d"}, - {file = "markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9"}, - {file = "markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa"}, - {file = "markupsafe-3.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26"}, - {file = "markupsafe-3.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc"}, - {file = "markupsafe-3.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c"}, - {file = "markupsafe-3.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8fc20152abba6b83724d7ff268c249fa196d8259ff481f3b1476383f8f24e42"}, - {file = "markupsafe-3.0.3-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:949b8d66bc381ee8b007cd945914c721d9aba8e27f71959d750a46f7c282b20b"}, - {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:3537e01efc9d4dccdf77221fb1cb3b8e1a38d5428920e0657ce299b20324d758"}, - {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:591ae9f2a647529ca990bc681daebdd52c8791ff06c2bfa05b65163e28102ef2"}, - {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a320721ab5a1aba0a233739394eb907f8c8da5c98c9181d1161e77a0c8e36f2d"}, - {file = "markupsafe-3.0.3-cp39-cp39-win32.whl", hash = "sha256:df2449253ef108a379b8b5d6b43f4b1a8e81a061d6537becd5582fba5f9196d7"}, - {file = "markupsafe-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:7c3fb7d25180895632e5d3148dbdc29ea38ccb7fd210aa27acbd1201a1902c6e"}, - {file = "markupsafe-3.0.3-cp39-cp39-win_arm64.whl", hash = "sha256:38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8"}, - {file = "markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698"}, -] - -[[package]] -name = "mccabe" -version = "0.7.0" -description = "McCabe checker, plugin for flake8" -optional = false -python-versions = ">=3.6" -groups = ["dev"] -files = [ - {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, - {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, -] - -[[package]] -name = "mdurl" -version = "0.1.2" -description = "Markdown URL utilities" -optional = false -python-versions = ">=3.7" -groups = ["dev"] -files = [ - {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, - {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, -] - -[[package]] -name = "more-itertools" -version = "10.8.0" -description = "More routines for operating on iterables, beyond itertools" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\"" -files = [ - {file = "more_itertools-10.8.0-py3-none-any.whl", hash = "sha256:52d4362373dcf7c52546bc4af9a86ee7c4579df9a8dc268be0a2f949d376cc9b"}, - {file = "more_itertools-10.8.0.tar.gz", hash = "sha256:f638ddf8a1a0d134181275fb5d58b086ead7c6a72429ad725c67503f13ba30bd"}, -] - -[[package]] -name = "msgpack" -version = "1.1.2" -description = "MessagePack serializer" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "msgpack-1.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0051fffef5a37ca2cd16978ae4f0aef92f164df86823871b5162812bebecd8e2"}, - {file = "msgpack-1.1.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a605409040f2da88676e9c9e5853b3449ba8011973616189ea5ee55ddbc5bc87"}, - {file = "msgpack-1.1.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b696e83c9f1532b4af884045ba7f3aa741a63b2bc22617293a2c6a7c645f251"}, - {file = "msgpack-1.1.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:365c0bbe981a27d8932da71af63ef86acc59ed5c01ad929e09a0b88c6294e28a"}, - {file = "msgpack-1.1.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:41d1a5d875680166d3ac5c38573896453bbbea7092936d2e107214daf43b1d4f"}, - {file = "msgpack-1.1.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:354e81bcdebaab427c3df4281187edc765d5d76bfb3a7c125af9da7a27e8458f"}, - {file = "msgpack-1.1.2-cp310-cp310-win32.whl", hash = "sha256:e64c8d2f5e5d5fda7b842f55dec6133260ea8f53c4257d64494c534f306bf7a9"}, - {file = "msgpack-1.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:db6192777d943bdaaafb6ba66d44bf65aa0e9c5616fa1d2da9bb08828c6b39aa"}, - {file = "msgpack-1.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2e86a607e558d22985d856948c12a3fa7b42efad264dca8a3ebbcfa2735d786c"}, - {file = "msgpack-1.1.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:283ae72fc89da59aa004ba147e8fc2f766647b1251500182fac0350d8af299c0"}, - {file = "msgpack-1.1.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:61c8aa3bd513d87c72ed0b37b53dd5c5a0f58f2ff9f26e1555d3bd7948fb7296"}, - {file = "msgpack-1.1.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:454e29e186285d2ebe65be34629fa0e8605202c60fbc7c4c650ccd41870896ef"}, - {file = "msgpack-1.1.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7bc8813f88417599564fafa59fd6f95be417179f76b40325b500b3c98409757c"}, - {file = "msgpack-1.1.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bafca952dc13907bdfdedfc6a5f579bf4f292bdd506fadb38389afa3ac5b208e"}, - {file = "msgpack-1.1.2-cp311-cp311-win32.whl", hash = "sha256:602b6740e95ffc55bfb078172d279de3773d7b7db1f703b2f1323566b878b90e"}, - {file = "msgpack-1.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:d198d275222dc54244bf3327eb8cbe00307d220241d9cec4d306d49a44e85f68"}, - {file = "msgpack-1.1.2-cp311-cp311-win_arm64.whl", hash = "sha256:86f8136dfa5c116365a8a651a7d7484b65b13339731dd6faebb9a0242151c406"}, - {file = "msgpack-1.1.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:70a0dff9d1f8da25179ffcf880e10cf1aad55fdb63cd59c9a49a1b82290062aa"}, - {file = "msgpack-1.1.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:446abdd8b94b55c800ac34b102dffd2f6aa0ce643c55dfc017ad89347db3dbdb"}, - {file = "msgpack-1.1.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c63eea553c69ab05b6747901b97d620bb2a690633c77f23feb0c6a947a8a7b8f"}, - {file = "msgpack-1.1.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:372839311ccf6bdaf39b00b61288e0557916c3729529b301c52c2d88842add42"}, - {file = "msgpack-1.1.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2929af52106ca73fcb28576218476ffbb531a036c2adbcf54a3664de124303e9"}, - {file = "msgpack-1.1.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:be52a8fc79e45b0364210eef5234a7cf8d330836d0a64dfbb878efa903d84620"}, - {file = "msgpack-1.1.2-cp312-cp312-win32.whl", hash = "sha256:1fff3d825d7859ac888b0fbda39a42d59193543920eda9d9bea44d958a878029"}, - {file = "msgpack-1.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:1de460f0403172cff81169a30b9a92b260cb809c4cb7e2fc79ae8d0510c78b6b"}, - {file = "msgpack-1.1.2-cp312-cp312-win_arm64.whl", hash = "sha256:be5980f3ee0e6bd44f3a9e9dea01054f175b50c3e6cdb692bc9424c0bbb8bf69"}, - {file = "msgpack-1.1.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4efd7b5979ccb539c221a4c4e16aac1a533efc97f3b759bb5a5ac9f6d10383bf"}, - {file = "msgpack-1.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:42eefe2c3e2af97ed470eec850facbe1b5ad1d6eacdbadc42ec98e7dcf68b4b7"}, - {file = "msgpack-1.1.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1fdf7d83102bf09e7ce3357de96c59b627395352a4024f6e2458501f158bf999"}, - {file = "msgpack-1.1.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fac4be746328f90caa3cd4bc67e6fe36ca2bf61d5c6eb6d895b6527e3f05071e"}, - {file = "msgpack-1.1.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:fffee09044073e69f2bad787071aeec727183e7580443dfeb8556cbf1978d162"}, - {file = "msgpack-1.1.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5928604de9b032bc17f5099496417f113c45bc6bc21b5c6920caf34b3c428794"}, - {file = "msgpack-1.1.2-cp313-cp313-win32.whl", hash = "sha256:a7787d353595c7c7e145e2331abf8b7ff1e6673a6b974ded96e6d4ec09f00c8c"}, - {file = "msgpack-1.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:a465f0dceb8e13a487e54c07d04ae3ba131c7c5b95e2612596eafde1dccf64a9"}, - {file = "msgpack-1.1.2-cp313-cp313-win_arm64.whl", hash = "sha256:e69b39f8c0aa5ec24b57737ebee40be647035158f14ed4b40e6f150077e21a84"}, - {file = "msgpack-1.1.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e23ce8d5f7aa6ea6d2a2b326b4ba46c985dbb204523759984430db7114f8aa00"}, - {file = "msgpack-1.1.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:6c15b7d74c939ebe620dd8e559384be806204d73b4f9356320632d783d1f7939"}, - {file = "msgpack-1.1.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:99e2cb7b9031568a2a5c73aa077180f93dd2e95b4f8d3b8e14a73ae94a9e667e"}, - {file = "msgpack-1.1.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:180759d89a057eab503cf62eeec0aa61c4ea1200dee709f3a8e9397dbb3b6931"}, - {file = "msgpack-1.1.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:04fb995247a6e83830b62f0b07bf36540c213f6eac8e851166d8d86d83cbd014"}, - {file = "msgpack-1.1.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8e22ab046fa7ede9e36eeb4cfad44d46450f37bb05d5ec482b02868f451c95e2"}, - {file = "msgpack-1.1.2-cp314-cp314-win32.whl", hash = "sha256:80a0ff7d4abf5fecb995fcf235d4064b9a9a8a40a3ab80999e6ac1e30b702717"}, - {file = "msgpack-1.1.2-cp314-cp314-win_amd64.whl", hash = "sha256:9ade919fac6a3e7260b7f64cea89df6bec59104987cbea34d34a2fa15d74310b"}, - {file = "msgpack-1.1.2-cp314-cp314-win_arm64.whl", hash = "sha256:59415c6076b1e30e563eb732e23b994a61c159cec44deaf584e5cc1dd662f2af"}, - {file = "msgpack-1.1.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:897c478140877e5307760b0ea66e0932738879e7aa68144d9b78ea4c8302a84a"}, - {file = "msgpack-1.1.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a668204fa43e6d02f89dbe79a30b0d67238d9ec4c5bd8a940fc3a004a47b721b"}, - {file = "msgpack-1.1.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5559d03930d3aa0f3aacb4c42c776af1a2ace2611871c84a75afe436695e6245"}, - {file = "msgpack-1.1.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:70c5a7a9fea7f036b716191c29047374c10721c389c21e9ffafad04df8c52c90"}, - {file = "msgpack-1.1.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:f2cb069d8b981abc72b41aea1c580ce92d57c673ec61af4c500153a626cb9e20"}, - {file = "msgpack-1.1.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d62ce1f483f355f61adb5433ebfd8868c5f078d1a52d042b0a998682b4fa8c27"}, - {file = "msgpack-1.1.2-cp314-cp314t-win32.whl", hash = "sha256:1d1418482b1ee984625d88aa9585db570180c286d942da463533b238b98b812b"}, - {file = "msgpack-1.1.2-cp314-cp314t-win_amd64.whl", hash = "sha256:5a46bf7e831d09470ad92dff02b8b1ac92175ca36b087f904a0519857c6be3ff"}, - {file = "msgpack-1.1.2-cp314-cp314t-win_arm64.whl", hash = "sha256:d99ef64f349d5ec3293688e91486c5fdb925ed03807f64d98d205d2713c60b46"}, - {file = "msgpack-1.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ea5405c46e690122a76531ab97a079e184c0daf491e588592d6a23d3e32af99e"}, - {file = "msgpack-1.1.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9fba231af7a933400238cb357ecccf8ab5d51535ea95d94fc35b7806218ff844"}, - {file = "msgpack-1.1.2-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a8f6e7d30253714751aa0b0c84ae28948e852ee7fb0524082e6716769124bc23"}, - {file = "msgpack-1.1.2-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:94fd7dc7d8cb0a54432f296f2246bc39474e017204ca6f4ff345941d4ed285a7"}, - {file = "msgpack-1.1.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:350ad5353a467d9e3b126d8d1b90fe05ad081e2e1cef5753f8c345217c37e7b8"}, - {file = "msgpack-1.1.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6bde749afe671dc44893f8d08e83bf475a1a14570d67c4bb5cec5573463c8833"}, - {file = "msgpack-1.1.2-cp39-cp39-win32.whl", hash = "sha256:ad09b984828d6b7bb52d1d1d0c9be68ad781fa004ca39216c8a1e63c0f34ba3c"}, - {file = "msgpack-1.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:67016ae8c8965124fdede9d3769528ad8284f14d635337ffa6a713a580f6c030"}, - {file = "msgpack-1.1.2.tar.gz", hash = "sha256:3b60763c1373dd60f398488069bcdc703cd08a711477b5d480eecc9f9626f47e"}, -] - -[[package]] -name = "mypy" -version = "1.19.1" -description = "Optional static typing for Python" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "mypy-1.19.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f05aa3d375b385734388e844bc01733bd33c644ab48e9684faa54e5389775ec"}, - {file = "mypy-1.19.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:022ea7279374af1a5d78dfcab853fe6a536eebfda4b59deab53cd21f6cd9f00b"}, - {file = "mypy-1.19.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee4c11e460685c3e0c64a4c5de82ae143622410950d6be863303a1c4ba0e36d6"}, - {file = "mypy-1.19.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de759aafbae8763283b2ee5869c7255391fbc4de3ff171f8f030b5ec48381b74"}, - {file = "mypy-1.19.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ab43590f9cd5108f41aacf9fca31841142c786827a74ab7cc8a2eacb634e09a1"}, - {file = "mypy-1.19.1-cp310-cp310-win_amd64.whl", hash = "sha256:2899753e2f61e571b3971747e302d5f420c3fd09650e1951e99f823bc3089dac"}, - {file = "mypy-1.19.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d8dfc6ab58ca7dda47d9237349157500468e404b17213d44fc1cb77bce532288"}, - {file = "mypy-1.19.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e3f276d8493c3c97930e354b2595a44a21348b320d859fb4a2b9f66da9ed27ab"}, - {file = "mypy-1.19.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2abb24cf3f17864770d18d673c85235ba52456b36a06b6afc1e07c1fdcd3d0e6"}, - {file = "mypy-1.19.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a009ffa5a621762d0c926a078c2d639104becab69e79538a494bcccb62cc0331"}, - {file = "mypy-1.19.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f7cee03c9a2e2ee26ec07479f38ea9c884e301d42c6d43a19d20fb014e3ba925"}, - {file = "mypy-1.19.1-cp311-cp311-win_amd64.whl", hash = "sha256:4b84a7a18f41e167f7995200a1d07a4a6810e89d29859df936f1c3923d263042"}, - {file = "mypy-1.19.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a8174a03289288c1f6c46d55cef02379b478bfbc8e358e02047487cad44c6ca1"}, - {file = "mypy-1.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffcebe56eb09ff0c0885e750036a095e23793ba6c2e894e7e63f6d89ad51f22e"}, - {file = "mypy-1.19.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b64d987153888790bcdb03a6473d321820597ab8dd9243b27a92153c4fa50fd2"}, - {file = "mypy-1.19.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c35d298c2c4bba75feb2195655dfea8124d855dfd7343bf8b8c055421eaf0cf8"}, - {file = "mypy-1.19.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:34c81968774648ab5ac09c29a375fdede03ba253f8f8287847bd480782f73a6a"}, - {file = "mypy-1.19.1-cp312-cp312-win_amd64.whl", hash = "sha256:b10e7c2cd7870ba4ad9b2d8a6102eb5ffc1f16ca35e3de6bfa390c1113029d13"}, - {file = "mypy-1.19.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e3157c7594ff2ef1634ee058aafc56a82db665c9438fd41b390f3bde1ab12250"}, - {file = "mypy-1.19.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdb12f69bcc02700c2b47e070238f42cb87f18c0bc1fc4cdb4fb2bc5fd7a3b8b"}, - {file = "mypy-1.19.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f859fb09d9583a985be9a493d5cfc5515b56b08f7447759a0c5deaf68d80506e"}, - {file = "mypy-1.19.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9a6538e0415310aad77cb94004ca6482330fece18036b5f360b62c45814c4ef"}, - {file = "mypy-1.19.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:da4869fc5e7f62a88f3fe0b5c919d1d9f7ea3cef92d3689de2823fd27e40aa75"}, - {file = "mypy-1.19.1-cp313-cp313-win_amd64.whl", hash = "sha256:016f2246209095e8eda7538944daa1d60e1e8134d98983b9fc1e92c1fc0cb8dd"}, - {file = "mypy-1.19.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06e6170bd5836770e8104c8fdd58e5e725cfeb309f0a6c681a811f557e97eac1"}, - {file = "mypy-1.19.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:804bd67b8054a85447c8954215a906d6eff9cabeabe493fb6334b24f4bfff718"}, - {file = "mypy-1.19.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21761006a7f497cb0d4de3d8ef4ca70532256688b0523eee02baf9eec895e27b"}, - {file = "mypy-1.19.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:28902ee51f12e0f19e1e16fbe2f8f06b6637f482c459dd393efddd0ec7f82045"}, - {file = "mypy-1.19.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:481daf36a4c443332e2ae9c137dfee878fcea781a2e3f895d54bd3002a900957"}, - {file = "mypy-1.19.1-cp314-cp314-win_amd64.whl", hash = "sha256:8bb5c6f6d043655e055be9b542aa5f3bdd30e4f3589163e85f93f3640060509f"}, - {file = "mypy-1.19.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7bcfc336a03a1aaa26dfce9fff3e287a3ba99872a157561cbfcebe67c13308e3"}, - {file = "mypy-1.19.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b7951a701c07ea584c4fe327834b92a30825514c868b1f69c30445093fdd9d5a"}, - {file = "mypy-1.19.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b13cfdd6c87fc3efb69ea4ec18ef79c74c3f98b4e5498ca9b85ab3b2c2329a67"}, - {file = "mypy-1.19.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f28f99c824ecebcdaa2e55d82953e38ff60ee5ec938476796636b86afa3956e"}, - {file = "mypy-1.19.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c608937067d2fc5a4dd1a5ce92fd9e1398691b8c5d012d66e1ddd430e9244376"}, - {file = "mypy-1.19.1-cp39-cp39-win_amd64.whl", hash = "sha256:409088884802d511ee52ca067707b90c883426bd95514e8cfda8281dc2effe24"}, - {file = "mypy-1.19.1-py3-none-any.whl", hash = "sha256:f1235f5ea01b7db5468d53ece6aaddf1ad0b88d9e7462b86ef96fe04995d7247"}, - {file = "mypy-1.19.1.tar.gz", hash = "sha256:19d88bb05303fe63f71dd2c6270daca27cb9401c4ca8255fe50d1d920e0eb9ba"}, -] - -[package.dependencies] -librt = {version = ">=0.6.2", markers = "platform_python_implementation != \"PyPy\""} -mypy_extensions = ">=1.0.0" -pathspec = ">=0.9.0" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing_extensions = ">=4.6.0" - -[package.extras] -dmypy = ["psutil (>=4.0)"] -faster-cache = ["orjson"] -install-types = ["pip"] -mypyc = ["setuptools (>=50)"] -reports = ["lxml"] - -[[package]] -name = "mypy-extensions" -version = "1.1.0" -description = "Type system extensions for programs checked with the mypy type checker." -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, - {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, -] - -[[package]] -name = "nh3" -version = "0.3.2" -description = "Python binding to Ammonia HTML sanitizer Rust crate" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "nh3-0.3.2-cp314-cp314t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:d18957a90806d943d141cc5e4a0fefa1d77cf0d7a156878bf9a66eed52c9cc7d"}, - {file = "nh3-0.3.2-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45c953e57028c31d473d6b648552d9cab1efe20a42ad139d78e11d8f42a36130"}, - {file = "nh3-0.3.2-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2c9850041b77a9147d6bbd6dbbf13eeec7009eb60b44e83f07fcb2910075bf9b"}, - {file = "nh3-0.3.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:403c11563e50b915d0efdb622866d1d9e4506bce590ef7da57789bf71dd148b5"}, - {file = "nh3-0.3.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:0dca4365db62b2d71ff1620ee4f800c4729849906c5dd504ee1a7b2389558e31"}, - {file = "nh3-0.3.2-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:0fe7ee035dd7b2290715baf29cb27167dddd2ff70ea7d052c958dbd80d323c99"}, - {file = "nh3-0.3.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a40202fd58e49129764f025bbaae77028e420f1d5b3c8e6f6fd3a6490d513868"}, - {file = "nh3-0.3.2-cp314-cp314t-win32.whl", hash = "sha256:1f9ba555a797dbdcd844b89523f29cdc90973d8bd2e836ea6b962cf567cadd93"}, - {file = "nh3-0.3.2-cp314-cp314t-win_amd64.whl", hash = "sha256:dce4248edc427c9b79261f3e6e2b3ecbdd9b88c267012168b4a7b3fc6fd41d13"}, - {file = "nh3-0.3.2-cp314-cp314t-win_arm64.whl", hash = "sha256:019ecbd007536b67fdf76fab411b648fb64e2257ca3262ec80c3425c24028c80"}, - {file = "nh3-0.3.2-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:7064ccf5ace75825bd7bf57859daaaf16ed28660c1c6b306b649a9eda4b54b1e"}, - {file = "nh3-0.3.2-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c8745454cdd28bbbc90861b80a0111a195b0e3961b9fa2e672be89eb199fa5d8"}, - {file = "nh3-0.3.2-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72d67c25a84579f4a432c065e8b4274e53b7cf1df8f792cf846abfe2c3090866"}, - {file = "nh3-0.3.2-cp38-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:13398e676a14d6233f372c75f52d5ae74f98210172991f7a3142a736bd92b131"}, - {file = "nh3-0.3.2-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:03d617e5c8aa7331bd2659c654e021caf9bba704b109e7b2b28b039a00949fe5"}, - {file = "nh3-0.3.2-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2f55c4d2d5a207e74eefe4d828067bbb01300e06e2a7436142f915c5928de07"}, - {file = "nh3-0.3.2-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bb18403f02b655a1bbe4e3a4696c2ae1d6ae8f5991f7cacb684b1ae27e6c9f7"}, - {file = "nh3-0.3.2-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6d66f41672eb4060cf87c037f760bdbc6847852ca9ef8e9c5a5da18f090abf87"}, - {file = "nh3-0.3.2-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:f97f8b25cb2681d25e2338148159447e4d689aafdccfcf19e61ff7db3905768a"}, - {file = "nh3-0.3.2-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:2ab70e8c6c7d2ce953d2a58102eefa90c2d0a5ed7aa40c7e29a487bc5e613131"}, - {file = "nh3-0.3.2-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:1710f3901cd6440ca92494ba2eb6dc260f829fa8d9196b659fa10de825610ce0"}, - {file = "nh3-0.3.2-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:91e9b001101fb4500a2aafe3e7c92928d85242d38bf5ac0aba0b7480da0a4cd6"}, - {file = "nh3-0.3.2-cp38-abi3-win32.whl", hash = "sha256:169db03df90da63286e0560ea0efa9b6f3b59844a9735514a1d47e6bb2c8c61b"}, - {file = "nh3-0.3.2-cp38-abi3-win_amd64.whl", hash = "sha256:562da3dca7a17f9077593214a9781a94b8d76de4f158f8c895e62f09573945fe"}, - {file = "nh3-0.3.2-cp38-abi3-win_arm64.whl", hash = "sha256:cf5964d54edd405e68583114a7cba929468bcd7db5e676ae38ee954de1cfc104"}, - {file = "nh3-0.3.2.tar.gz", hash = "sha256:f394759a06df8b685a4ebfb1874fb67a9cbfd58c64fc5ed587a663c0e63ec376"}, -] - -[[package]] -name = "packageurl-python" -version = "0.17.6" -description = "A purl aka. Package URL parser and builder" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "packageurl_python-0.17.6-py3-none-any.whl", hash = "sha256:31a85c2717bc41dd818f3c62908685ff9eebcb68588213745b14a6ee9e7df7c9"}, - {file = "packageurl_python-0.17.6.tar.gz", hash = "sha256:1252ce3a102372ca6f86eb968e16f9014c4ba511c5c37d95a7f023e2ca6e5c25"}, -] - -[package.extras] -build = ["setuptools", "wheel"] -lint = ["black", "isort", "mypy"] -sqlalchemy = ["sqlalchemy (>=2.0.0)"] -test = ["pytest"] - -[[package]] -name = "packaging" -version = "26.0" -description = "Core utilities for Python packages" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529"}, - {file = "packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4"}, -] - -[[package]] -name = "pathspec" -version = "1.0.4" -description = "Utility library for gitignore style pattern matching of file paths." -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723"}, - {file = "pathspec-1.0.4.tar.gz", hash = "sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645"}, -] - -[package.extras] -hyperscan = ["hyperscan (>=0.7)"] -optional = ["typing-extensions (>=4)"] -re2 = ["google-re2 (>=1.1)"] -tests = ["pytest (>=9)", "typing-extensions (>=4.15)"] - -[[package]] -name = "pillow" -version = "12.1.1" -description = "Python Imaging Library (fork)" -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "pillow-12.1.1-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1f1625b72740fdda5d77b4def688eb8fd6490975d06b909fd19f13f391e077e0"}, - {file = "pillow-12.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:178aa072084bd88ec759052feca8e56cbb14a60b39322b99a049e58090479713"}, - {file = "pillow-12.1.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b66e95d05ba806247aaa1561f080abc7975daf715c30780ff92a20e4ec546e1b"}, - {file = "pillow-12.1.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:89c7e895002bbe49cdc5426150377cbbc04767d7547ed145473f496dfa40408b"}, - {file = "pillow-12.1.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a5cbdcddad0af3da87cb16b60d23648bc3b51967eb07223e9fed77a82b457c4"}, - {file = "pillow-12.1.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9f51079765661884a486727f0729d29054242f74b46186026582b4e4769918e4"}, - {file = "pillow-12.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:99c1506ea77c11531d75e3a412832a13a71c7ebc8192ab9e4b2e355555920e3e"}, - {file = "pillow-12.1.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:36341d06738a9f66c8287cf8b876d24b18db9bd8740fa0672c74e259ad408cff"}, - {file = "pillow-12.1.1-cp310-cp310-win32.whl", hash = "sha256:6c52f062424c523d6c4db85518774cc3d50f5539dd6eed32b8f6229b26f24d40"}, - {file = "pillow-12.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:c6008de247150668a705a6338156efb92334113421ceecf7438a12c9a12dab23"}, - {file = "pillow-12.1.1-cp310-cp310-win_arm64.whl", hash = "sha256:1a9b0ee305220b392e1124a764ee4265bd063e54a751a6b62eff69992f457fa9"}, - {file = "pillow-12.1.1-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:e879bb6cd5c73848ef3b2b48b8af9ff08c5b71ecda8048b7dd22d8a33f60be32"}, - {file = "pillow-12.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:365b10bb9417dd4498c0e3b128018c4a624dc11c7b97d8cc54effe3b096f4c38"}, - {file = "pillow-12.1.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d4ce8e329c93845720cd2014659ca67eac35f6433fd3050393d85f3ecef0dad5"}, - {file = "pillow-12.1.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc354a04072b765eccf2204f588a7a532c9511e8b9c7f900e1b64e3e33487090"}, - {file = "pillow-12.1.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7e7976bf1910a8116b523b9f9f58bf410f3e8aa330cd9a2bb2953f9266ab49af"}, - {file = "pillow-12.1.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:597bd9c8419bc7c6af5604e55847789b69123bbe25d65cc6ad3012b4f3c98d8b"}, - {file = "pillow-12.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2c1fc0f2ca5f96a3c8407e41cca26a16e46b21060fe6d5b099d2cb01412222f5"}, - {file = "pillow-12.1.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:578510d88c6229d735855e1f278aa305270438d36a05031dfaae5067cc8eb04d"}, - {file = "pillow-12.1.1-cp311-cp311-win32.whl", hash = "sha256:7311c0a0dcadb89b36b7025dfd8326ecfa36964e29913074d47382706e516a7c"}, - {file = "pillow-12.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:fbfa2a7c10cc2623f412753cddf391c7f971c52ca40a3f65dc5039b2939e8563"}, - {file = "pillow-12.1.1-cp311-cp311-win_arm64.whl", hash = "sha256:b81b5e3511211631b3f672a595e3221252c90af017e399056d0faabb9538aa80"}, - {file = "pillow-12.1.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ab323b787d6e18b3d91a72fc99b1a2c28651e4358749842b8f8dfacd28ef2052"}, - {file = "pillow-12.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:adebb5bee0f0af4909c30db0d890c773d1a92ffe83da908e2e9e720f8edf3984"}, - {file = "pillow-12.1.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bb66b7cc26f50977108790e2456b7921e773f23db5630261102233eb355a3b79"}, - {file = "pillow-12.1.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:aee2810642b2898bb187ced9b349e95d2a7272930796e022efaf12e99dccd293"}, - {file = "pillow-12.1.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a0b1cd6232e2b618adcc54d9882e4e662a089d5768cd188f7c245b4c8c44a397"}, - {file = "pillow-12.1.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7aac39bcf8d4770d089588a2e1dd111cbaa42df5a94be3114222057d68336bd0"}, - {file = "pillow-12.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ab174cd7d29a62dd139c44bf74b698039328f45cb03b4596c43473a46656b2f3"}, - {file = "pillow-12.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:339ffdcb7cbeaa08221cd401d517d4b1fe7a9ed5d400e4a8039719238620ca35"}, - {file = "pillow-12.1.1-cp312-cp312-win32.whl", hash = "sha256:5d1f9575a12bed9e9eedd9a4972834b08c97a352bd17955ccdebfeca5913fa0a"}, - {file = "pillow-12.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:21329ec8c96c6e979cd0dfd29406c40c1d52521a90544463057d2aaa937d66a6"}, - {file = "pillow-12.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:af9a332e572978f0218686636610555ae3defd1633597be015ed50289a03c523"}, - {file = "pillow-12.1.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:d242e8ac078781f1de88bf823d70c1a9b3c7950a44cdf4b7c012e22ccbcd8e4e"}, - {file = "pillow-12.1.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:02f84dfad02693676692746df05b89cf25597560db2857363a208e393429f5e9"}, - {file = "pillow-12.1.1-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:e65498daf4b583091ccbb2556c7000abf0f3349fcd57ef7adc9a84a394ed29f6"}, - {file = "pillow-12.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6c6db3b84c87d48d0088943bf33440e0c42370b99b1c2a7989216f7b42eede60"}, - {file = "pillow-12.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8b7e5304e34942bf62e15184219a7b5ad4ff7f3bb5cca4d984f37df1a0e1aee2"}, - {file = "pillow-12.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:18e5bddd742a44b7e6b1e773ab5db102bd7a94c32555ba656e76d319d19c3850"}, - {file = "pillow-12.1.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc44ef1f3de4f45b50ccf9136999d71abb99dca7706bc75d222ed350b9fd2289"}, - {file = "pillow-12.1.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5a8eb7ed8d4198bccbd07058416eeec51686b498e784eda166395a23eb99138e"}, - {file = "pillow-12.1.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:47b94983da0c642de92ced1702c5b6c292a84bd3a8e1d1702ff923f183594717"}, - {file = "pillow-12.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:518a48c2aab7ce596d3bf79d0e275661b846e86e4d0e7dec34712c30fe07f02a"}, - {file = "pillow-12.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a550ae29b95c6dc13cf69e2c9dc5747f814c54eeb2e32d683e5e93af56caa029"}, - {file = "pillow-12.1.1-cp313-cp313-win32.whl", hash = "sha256:a003d7422449f6d1e3a34e3dd4110c22148336918ddbfc6a32581cd54b2e0b2b"}, - {file = "pillow-12.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:344cf1e3dab3be4b1fa08e449323d98a2a3f819ad20f4b22e77a0ede31f0faa1"}, - {file = "pillow-12.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:5c0dd1636633e7e6a0afe7bf6a51a14992b7f8e60de5789018ebbdfae55b040a"}, - {file = "pillow-12.1.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0330d233c1a0ead844fc097a7d16c0abff4c12e856c0b325f231820fee1f39da"}, - {file = "pillow-12.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5dae5f21afb91322f2ff791895ddd8889e5e947ff59f71b46041c8ce6db790bc"}, - {file = "pillow-12.1.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2e0c664be47252947d870ac0d327fea7e63985a08794758aa8af5b6cb6ec0c9c"}, - {file = "pillow-12.1.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:691ab2ac363b8217f7d31b3497108fb1f50faab2f75dfb03284ec2f217e87bf8"}, - {file = "pillow-12.1.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e9e8064fb1cc019296958595f6db671fba95209e3ceb0c4734c9baf97de04b20"}, - {file = "pillow-12.1.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:472a8d7ded663e6162dafdf20015c486a7009483ca671cece7a9279b512fcb13"}, - {file = "pillow-12.1.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:89b54027a766529136a06cfebeecb3a04900397a3590fd252160b888479517bf"}, - {file = "pillow-12.1.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:86172b0831b82ce4f7877f280055892b31179e1576aa00d0df3bb1bbf8c3e524"}, - {file = "pillow-12.1.1-cp313-cp313t-win32.whl", hash = "sha256:44ce27545b6efcf0fdbdceb31c9a5bdea9333e664cda58a7e674bb74608b3986"}, - {file = "pillow-12.1.1-cp313-cp313t-win_amd64.whl", hash = "sha256:a285e3eb7a5a45a2ff504e31f4a8d1b12ef62e84e5411c6804a42197c1cf586c"}, - {file = "pillow-12.1.1-cp313-cp313t-win_arm64.whl", hash = "sha256:cc7d296b5ea4d29e6570dabeaed58d31c3fea35a633a69679fb03d7664f43fb3"}, - {file = "pillow-12.1.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:417423db963cb4be8bac3fc1204fe61610f6abeed1580a7a2cbb2fbda20f12af"}, - {file = "pillow-12.1.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:b957b71c6b2387610f556a7eb0828afbe40b4a98036fc0d2acfa5a44a0c2036f"}, - {file = "pillow-12.1.1-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:097690ba1f2efdeb165a20469d59d8bb03c55fb6621eb2041a060ae8ea3e9642"}, - {file = "pillow-12.1.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2815a87ab27848db0321fb78c7f0b2c8649dee134b7f2b80c6a45c6831d75ccd"}, - {file = "pillow-12.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f7ed2c6543bad5a7d5530eb9e78c53132f93dfa44a28492db88b41cdab885202"}, - {file = "pillow-12.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:652a2c9ccfb556235b2b501a3a7cf3742148cd22e04b5625c5fe057ea3e3191f"}, - {file = "pillow-12.1.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d6e4571eedf43af33d0fc233a382a76e849badbccdf1ac438841308652a08e1f"}, - {file = "pillow-12.1.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b574c51cf7d5d62e9be37ba446224b59a2da26dc4c1bb2ecbe936a4fb1a7cb7f"}, - {file = "pillow-12.1.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a37691702ed687799de29a518d63d4682d9016932db66d4e90c345831b02fb4e"}, - {file = "pillow-12.1.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f95c00d5d6700b2b890479664a06e754974848afaae5e21beb4d83c106923fd0"}, - {file = "pillow-12.1.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:559b38da23606e68681337ad74622c4dbba02254fc9cb4488a305dd5975c7eeb"}, - {file = "pillow-12.1.1-cp314-cp314-win32.whl", hash = "sha256:03edcc34d688572014ff223c125a3f77fb08091e4607e7745002fc214070b35f"}, - {file = "pillow-12.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:50480dcd74fa63b8e78235957d302d98d98d82ccbfac4c7e12108ba9ecbdba15"}, - {file = "pillow-12.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:5cb1785d97b0c3d1d1a16bc1d710c4a0049daefc4935f3a8f31f827f4d3d2e7f"}, - {file = "pillow-12.1.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:1f90cff8aa76835cba5769f0b3121a22bd4eb9e6884cfe338216e557a9a548b8"}, - {file = "pillow-12.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1f1be78ce9466a7ee64bfda57bdba0f7cc499d9794d518b854816c41bf0aa4e9"}, - {file = "pillow-12.1.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:42fc1f4677106188ad9a55562bbade416f8b55456f522430fadab3cef7cd4e60"}, - {file = "pillow-12.1.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:98edb152429ab62a1818039744d8fbb3ccab98a7c29fc3d5fcef158f3f1f68b7"}, - {file = "pillow-12.1.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d470ab1178551dd17fdba0fef463359c41aaa613cdcd7ff8373f54be629f9f8f"}, - {file = "pillow-12.1.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6408a7b064595afcab0a49393a413732a35788f2a5092fdc6266952ed67de586"}, - {file = "pillow-12.1.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5d8c41325b382c07799a3682c1c258469ea2ff97103c53717b7893862d0c98ce"}, - {file = "pillow-12.1.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c7697918b5be27424e9ce568193efd13d925c4481dd364e43f5dff72d33e10f8"}, - {file = "pillow-12.1.1-cp314-cp314t-win32.whl", hash = "sha256:d2912fd8114fc5545aa3a4b5576512f64c55a03f3ebcca4c10194d593d43ea36"}, - {file = "pillow-12.1.1-cp314-cp314t-win_amd64.whl", hash = "sha256:4ceb838d4bd9dab43e06c363cab2eebf63846d6a4aeaea283bbdfd8f1a8ed58b"}, - {file = "pillow-12.1.1-cp314-cp314t-win_arm64.whl", hash = "sha256:7b03048319bfc6170e93bd60728a1af51d3dd7704935feb228c4d4faab35d334"}, - {file = "pillow-12.1.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:600fd103672b925fe62ed08e0d874ea34d692474df6f4bf7ebe148b30f89f39f"}, - {file = "pillow-12.1.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:665e1b916b043cef294bc54d47bf02d87e13f769bc4bc5fa225a24b3a6c5aca9"}, - {file = "pillow-12.1.1-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:495c302af3aad1ca67420ddd5c7bd480c8867ad173528767d906428057a11f0e"}, - {file = "pillow-12.1.1-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8fd420ef0c52c88b5a035a0886f367748c72147b2b8f384c9d12656678dfdfa9"}, - {file = "pillow-12.1.1-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f975aa7ef9684ce7e2c18a3aa8f8e2106ce1e46b94ab713d156b2898811651d3"}, - {file = "pillow-12.1.1-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8089c852a56c2966cf18835db62d9b34fef7ba74c726ad943928d494fa7f4735"}, - {file = "pillow-12.1.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:cb9bb857b2d057c6dfc72ac5f3b44836924ba15721882ef103cecb40d002d80e"}, - {file = "pillow-12.1.1.tar.gz", hash = "sha256:9ad8fa5937ab05218e2b6a4cff30295ad35afd2f83ac592e68c0d871bb0fdbc4"}, -] - -[package.extras] -docs = ["furo", "olefile", "sphinx (>=8.2)", "sphinx-autobuild", "sphinx-copybutton", "sphinx-inline-tabs", "sphinxext-opengraph"] -fpx = ["olefile"] -mic = ["olefile"] -test-arrow = ["arro3-compute", "arro3-core", "nanoarrow", "pyarrow"] -tests = ["check-manifest", "coverage (>=7.4.2)", "defusedxml", "markdown2", "olefile", "packaging", "pyroma (>=5)", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "trove-classifiers (>=2024.10.12)"] -xmp = ["defusedxml"] - -[[package]] -name = "pip" -version = "26.0.1" -description = "The PyPA recommended tool for installing Python packages." -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "pip-26.0.1-py3-none-any.whl", hash = "sha256:bdb1b08f4274833d62c1aa29e20907365a2ceb950410df15fc9521bad440122b"}, - {file = "pip-26.0.1.tar.gz", hash = "sha256:c4037d8a277c89b320abe636d59f91e6d0922d08a05b60e85e53b296613346d8"}, -] - -[[package]] -name = "pip-api" -version = "0.0.34" -description = "An unofficial, importable pip API" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "pip_api-0.0.34-py3-none-any.whl", hash = "sha256:8b2d7d7c37f2447373aa2cf8b1f60a2f2b27a84e1e9e0294a3f6ef10eb3ba6bb"}, - {file = "pip_api-0.0.34.tar.gz", hash = "sha256:9b75e958f14c5a2614bae415f2adf7eeb54d50a2cfbe7e24fd4826471bac3625"}, -] - -[package.dependencies] -pip = "*" - -[[package]] -name = "pip-audit" -version = "2.10.0" -description = "A tool for scanning Python environments for known vulnerabilities" -optional = false -python-versions = ">=3.10" -groups = ["dev"] -files = [ - {file = "pip_audit-2.10.0-py3-none-any.whl", hash = "sha256:16e02093872fac97580303f0848fa3ad64f7ecf600736ea7835a2b24de49613f"}, - {file = "pip_audit-2.10.0.tar.gz", hash = "sha256:427ea5bf61d1d06b98b1ae29b7feacc00288a2eced52c9c58ceed5253ef6c2a4"}, -] - -[package.dependencies] -CacheControl = {version = ">=0.13.0", extras = ["filecache"]} -cyclonedx-python-lib = ">=5,<12" -packaging = ">=23.0.0" -pip-api = ">=0.0.28" -pip-requirements-parser = ">=32.0.0" -platformdirs = ">=4.2.0" -requests = ">=2.31.0" -rich = ">=12.4" -tomli = ">=2.2.1" -tomli-w = ">=1.2.0" - -[package.extras] -cov = ["coverage[toml] (>=7.0,!=7.3.3,<8.0)"] -dev = ["build", "pip-audit[doc,lint,test]"] -doc = ["pdoc"] -lint = ["interrogate (>=1.6,<2.0)", "mypy", "ruff (>=0.11)", "types-requests", "types-toml"] -test = ["pip-audit[cov]", "pretend", "pytest"] - -[[package]] -name = "pip-requirements-parser" -version = "32.0.1" -description = "pip requirements parser - a mostly correct pip requirements parsing library because it uses pip's own code." -optional = false -python-versions = ">=3.6.0" -groups = ["dev"] -files = [ - {file = "pip-requirements-parser-32.0.1.tar.gz", hash = "sha256:b4fa3a7a0be38243123cf9d1f3518da10c51bdb165a2b2985566247f9155a7d3"}, - {file = "pip_requirements_parser-32.0.1-py3-none-any.whl", hash = "sha256:4659bc2a667783e7a15d190f6fccf8b2486685b6dba4c19c3876314769c57526"}, -] - -[package.dependencies] -packaging = "*" -pyparsing = "*" - -[package.extras] -docs = ["Sphinx (>=3.3.1)", "doc8 (>=0.8.1)", "sphinx-rtd-theme (>=0.5.0)"] -testing = ["aboutcode-toolkit (>=6.0.0)", "black", "pytest (>=6,!=7.0.0)", "pytest-xdist (>=2)"] - -[[package]] -name = "platformdirs" -version = "4.5.1" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." -optional = false -python-versions = ">=3.10" -groups = ["dev"] -files = [ - {file = "platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31"}, - {file = "platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda"}, -] - -[package.extras] -docs = ["furo (>=2025.9.25)", "proselint (>=0.14)", "sphinx (>=8.2.3)", "sphinx-autodoc-typehints (>=3.2)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.4.2)", "pytest-cov (>=7)", "pytest-mock (>=3.15.1)"] -type = ["mypy (>=1.18.2)"] - -[[package]] -name = "py-serializable" -version = "2.1.0" -description = "Library for serializing and deserializing Python Objects to and from JSON and XML." -optional = false -python-versions = "<4.0,>=3.8" -groups = ["dev"] -files = [ - {file = "py_serializable-2.1.0-py3-none-any.whl", hash = "sha256:b56d5d686b5a03ba4f4db5e769dc32336e142fc3bd4d68a8c25579ebb0a67304"}, - {file = "py_serializable-2.1.0.tar.gz", hash = "sha256:9d5db56154a867a9b897c0163b33a793c804c80cee984116d02d49e4578fc103"}, -] - -[package.dependencies] -defusedxml = ">=0.7.1,<0.8.0" - -[[package]] -name = "pycodestyle" -version = "2.14.0" -description = "Python style guide checker" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "pycodestyle-2.14.0-py2.py3-none-any.whl", hash = "sha256:dd6bf7cb4ee77f8e016f9c8e74a35ddd9f67e1d5fd4184d86c3b98e07099f42d"}, - {file = "pycodestyle-2.14.0.tar.gz", hash = "sha256:c4b5b517d278089ff9d0abdec919cd97262a3367449ea1c8b49b91529167b783"}, -] - -[[package]] -name = "pycparser" -version = "3.0" -description = "C parser in Python" -optional = false -python-versions = ">=3.10" -groups = ["dev"] -markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\" and platform_python_implementation != \"PyPy\" and implementation_name != \"PyPy\"" -files = [ - {file = "pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992"}, - {file = "pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29"}, -] - -[[package]] -name = "pydantic" -version = "2.12.5" -description = "Data validation using Python type hints" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d"}, - {file = "pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49"}, -] - -[package.dependencies] -annotated-types = ">=0.6.0" -pydantic-core = "2.41.5" -typing-extensions = ">=4.14.1" -typing-inspection = ">=0.4.2" - -[package.extras] -email = ["email-validator (>=2.0.0)"] -timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows\""] - -[[package]] -name = "pydantic-core" -version = "2.41.5" -description = "Core functionality for Pydantic validation and serialization" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "pydantic_core-2.41.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146"}, - {file = "pydantic_core-2.41.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2"}, - {file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97"}, - {file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9"}, - {file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52"}, - {file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941"}, - {file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a"}, - {file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c"}, - {file = "pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2"}, - {file = "pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556"}, - {file = "pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49"}, - {file = "pydantic_core-2.41.5-cp310-cp310-win32.whl", hash = "sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba"}, - {file = "pydantic_core-2.41.5-cp310-cp310-win_amd64.whl", hash = "sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9"}, - {file = "pydantic_core-2.41.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6"}, - {file = "pydantic_core-2.41.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b"}, - {file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a"}, - {file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8"}, - {file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e"}, - {file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1"}, - {file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b"}, - {file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b"}, - {file = "pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284"}, - {file = "pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594"}, - {file = "pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e"}, - {file = "pydantic_core-2.41.5-cp311-cp311-win32.whl", hash = "sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b"}, - {file = "pydantic_core-2.41.5-cp311-cp311-win_amd64.whl", hash = "sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe"}, - {file = "pydantic_core-2.41.5-cp311-cp311-win_arm64.whl", hash = "sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f"}, - {file = "pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7"}, - {file = "pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0"}, - {file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69"}, - {file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75"}, - {file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05"}, - {file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc"}, - {file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c"}, - {file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5"}, - {file = "pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c"}, - {file = "pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294"}, - {file = "pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1"}, - {file = "pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d"}, - {file = "pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815"}, - {file = "pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3"}, - {file = "pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9"}, - {file = "pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34"}, - {file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0"}, - {file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33"}, - {file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e"}, - {file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2"}, - {file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586"}, - {file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d"}, - {file = "pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740"}, - {file = "pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e"}, - {file = "pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858"}, - {file = "pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36"}, - {file = "pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11"}, - {file = "pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd"}, - {file = "pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a"}, - {file = "pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14"}, - {file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1"}, - {file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66"}, - {file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869"}, - {file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2"}, - {file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375"}, - {file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553"}, - {file = "pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90"}, - {file = "pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07"}, - {file = "pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb"}, - {file = "pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23"}, - {file = "pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf"}, - {file = "pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0"}, - {file = "pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a"}, - {file = "pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3"}, - {file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c"}, - {file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612"}, - {file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d"}, - {file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9"}, - {file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660"}, - {file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9"}, - {file = "pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3"}, - {file = "pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf"}, - {file = "pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470"}, - {file = "pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa"}, - {file = "pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c"}, - {file = "pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008"}, - {file = "pydantic_core-2.41.5-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:8bfeaf8735be79f225f3fefab7f941c712aaca36f1128c9d7e2352ee1aa87bdf"}, - {file = "pydantic_core-2.41.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:346285d28e4c8017da95144c7f3acd42740d637ff41946af5ce6e5e420502dd5"}, - {file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a75dafbf87d6276ddc5b2bf6fae5254e3d0876b626eb24969a574fff9149ee5d"}, - {file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7b93a4d08587e2b7e7882de461e82b6ed76d9026ce91ca7915e740ecc7855f60"}, - {file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e8465ab91a4bd96d36dde3263f06caa6a8a6019e4113f24dc753d79a8b3a3f82"}, - {file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:299e0a22e7ae2b85c1a57f104538b2656e8ab1873511fd718a1c1c6f149b77b5"}, - {file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:707625ef0983fcfb461acfaf14de2067c5942c6bb0f3b4c99158bed6fedd3cf3"}, - {file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f41eb9797986d6ebac5e8edff36d5cef9de40def462311b3eb3eeded1431e425"}, - {file = "pydantic_core-2.41.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0384e2e1021894b1ff5a786dbf94771e2986ebe2869533874d7e43bc79c6f504"}, - {file = "pydantic_core-2.41.5-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:f0cd744688278965817fd0839c4a4116add48d23890d468bc436f78beb28abf5"}, - {file = "pydantic_core-2.41.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:753e230374206729bf0a807954bcc6c150d3743928a73faffee51ac6557a03c3"}, - {file = "pydantic_core-2.41.5-cp39-cp39-win32.whl", hash = "sha256:873e0d5b4fb9b89ef7c2d2a963ea7d02879d9da0da8d9d4933dee8ee86a8b460"}, - {file = "pydantic_core-2.41.5-cp39-cp39-win_amd64.whl", hash = "sha256:e4f4a984405e91527a0d62649ee21138f8e3d0ef103be488c1dc11a80d7f184b"}, - {file = "pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034"}, - {file = "pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c"}, - {file = "pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2"}, - {file = "pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad"}, - {file = "pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd"}, - {file = "pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc"}, - {file = "pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56"}, - {file = "pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b"}, - {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8"}, - {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a"}, - {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b"}, - {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2"}, - {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093"}, - {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a"}, - {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963"}, - {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a"}, - {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26"}, - {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808"}, - {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc"}, - {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1"}, - {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84"}, - {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770"}, - {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f"}, - {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51"}, - {file = "pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e"}, -] - -[package.dependencies] -typing-extensions = ">=4.14.1" - -[[package]] -name = "pyflakes" -version = "3.4.0" -description = "passive checker of Python programs" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "pyflakes-3.4.0-py2.py3-none-any.whl", hash = "sha256:f742a7dbd0d9cb9ea41e9a24a918996e8170c799fa528688d40dd582c8265f4f"}, - {file = "pyflakes-3.4.0.tar.gz", hash = "sha256:b24f96fafb7d2ab0ec5075b7350b3d2d2218eab42003821c06344973d3ea2f58"}, -] - -[[package]] -name = "pygments" -version = "2.19.2" -description = "Pygments is a syntax highlighting package written in Python." -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, - {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, -] - -[package.extras] -windows-terminal = ["colorama (>=0.4.6)"] - -[[package]] -name = "pymongo" -version = "4.16.0" -description = "PyMongo - the Official MongoDB Python driver" -optional = false -python-versions = ">=3.9" -groups = ["main", "dev"] -files = [ - {file = "pymongo-4.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ed162b2227f98d5b270ecbe1d53be56c8c81db08a1a8f5f02d89c7bb4d19591d"}, - {file = "pymongo-4.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4a9390dce61d705a88218f0d7b54d7e1fa1b421da8129fc7c009e029a9a6b81e"}, - {file = "pymongo-4.16.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:92a232af9927710de08a6c16a9710cc1b175fb9179c0d946cd4e213b92b2a69a"}, - {file = "pymongo-4.16.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4d79aa147ce86aef03079096d83239580006ffb684eead593917186aee407767"}, - {file = "pymongo-4.16.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:19a1c96e7f39c7a59a9cfd4d17920cf9382f6f684faeff4649bf587dc59f8edc"}, - {file = "pymongo-4.16.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efe020c46ce3c3a89af6baec6569635812129df6fb6cf76d4943af3ba6ee2069"}, - {file = "pymongo-4.16.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9dc2c00bed568732b89e211b6adca389053d5e6d2d5a8979e80b813c3ec4d1f9"}, - {file = "pymongo-4.16.0-cp310-cp310-win32.whl", hash = "sha256:5b9c6d689bbe5beb156374508133218610e14f8c81e35bc17d7a14e30ab593e6"}, - {file = "pymongo-4.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:2290909275c9b8f637b0a92eb9b89281e18a72922749ebb903403ab6cc7da914"}, - {file = "pymongo-4.16.0-cp310-cp310-win_arm64.whl", hash = "sha256:6af1aaa26f0835175d2200e62205b78e7ec3ffa430682e322cc91aaa1a0dbf28"}, - {file = "pymongo-4.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6f2077ec24e2f1248f9cac7b9a2dfb894e50cc7939fcebfb1759f99304caabef"}, - {file = "pymongo-4.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4d4f7ba040f72a9f43a44059872af5a8c8c660aa5d7f90d5344f2ed1c3c02721"}, - {file = "pymongo-4.16.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:8a0f73af1ea56c422b2dcfc0437459148a799ef4231c6aee189d2d4c59d6728f"}, - {file = "pymongo-4.16.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa30cd16ddd2f216d07ba01d9635c873e97ddb041c61cf0847254edc37d1c60e"}, - {file = "pymongo-4.16.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1d638b0b1b294d95d0fdc73688a3b61e05cc4188872818cd240d51460ccabcb5"}, - {file = "pymongo-4.16.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:21d02cc10a158daa20cb040985e280e7e439832fc6b7857bff3d53ef6914ad50"}, - {file = "pymongo-4.16.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4fbb8d3552c2ad99d9e236003c0b5f96d5f05e29386ba7abae73949bfebc13dd"}, - {file = "pymongo-4.16.0-cp311-cp311-win32.whl", hash = "sha256:be1099a8295b1a722d03fb7b48be895d30f4301419a583dcf50e9045968a041c"}, - {file = "pymongo-4.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:61567f712bda04c7545a037e3284b4367cad8d29b3dec84b4bf3b2147020a75b"}, - {file = "pymongo-4.16.0-cp311-cp311-win_arm64.whl", hash = "sha256:c53338613043038005bf2e41a2fafa08d29cdbc0ce80891b5366c819456c1ae9"}, - {file = "pymongo-4.16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bd4911c40a43a821dfd93038ac824b756b6e703e26e951718522d29f6eb166a8"}, - {file = "pymongo-4.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:25a6b03a68f9907ea6ec8bc7cf4c58a1b51a18e23394f962a6402f8e46d41211"}, - {file = "pymongo-4.16.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:91ac0cb0fe2bf17616c2039dac88d7c9a5088f5cb5829b27c9d250e053664d31"}, - {file = "pymongo-4.16.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cf0ec79e8ca7077f455d14d915d629385153b6a11abc0b93283ed73a8013e376"}, - {file = "pymongo-4.16.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2d0082631a7510318befc2b4fdab140481eb4b9dd62d9245e042157085da2a70"}, - {file = "pymongo-4.16.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:85dc2f3444c346ea019a371e321ac868a4fab513b7a55fe368f0cc78de8177cc"}, - {file = "pymongo-4.16.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dabbf3c14de75a20cc3c30bf0c6527157224a93dfb605838eabb1a2ee3be008d"}, - {file = "pymongo-4.16.0-cp312-cp312-win32.whl", hash = "sha256:60307bb91e0ab44e560fe3a211087748b2b5f3e31f403baf41f5b7b0a70bd104"}, - {file = "pymongo-4.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:f513b2c6c0d5c491f478422f6b5b5c27ac1af06a54c93ef8631806f7231bd92e"}, - {file = "pymongo-4.16.0-cp312-cp312-win_arm64.whl", hash = "sha256:dfc320f08ea9a7ec5b2403dc4e8150636f0d6150f4b9792faaae539c88e7db3b"}, - {file = "pymongo-4.16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d15f060bc6d0964a8bb70aba8f0cb6d11ae99715438f640cff11bbcf172eb0e8"}, - {file = "pymongo-4.16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a19ea46a0fe71248965305a020bc076a163311aefbaa1d83e47d06fa30ac747"}, - {file = "pymongo-4.16.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:311d4549d6bf1f8c61d025965aebb5ba29d1481dc6471693ab91610aaffbc0eb"}, - {file = "pymongo-4.16.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:46ffb728d92dd5b09fc034ed91acf5595657c7ca17d4cf3751322cd554153c17"}, - {file = "pymongo-4.16.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:acda193f440dd88c2023cb00aa8bd7b93a9df59978306d14d87a8b12fe426b05"}, - {file = "pymongo-4.16.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5d9fdb386cf958e6ef6ff537d6149be7edb76c3268cd6833e6c36aa447e4443f"}, - {file = "pymongo-4.16.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:91899dd7fb9a8c50f09c3c1cf0cb73bfbe2737f511f641f19b9650deb61c00ca"}, - {file = "pymongo-4.16.0-cp313-cp313-win32.whl", hash = "sha256:2cd60cd1e05de7f01927f8e25ca26b3ea2c09de8723241e5d3bcfdc70eaff76b"}, - {file = "pymongo-4.16.0-cp313-cp313-win_amd64.whl", hash = "sha256:3ead8a0050c53eaa55935895d6919d393d0328ec24b2b9115bdbe881aa222673"}, - {file = "pymongo-4.16.0-cp313-cp313-win_arm64.whl", hash = "sha256:dbbc5b254c36c37d10abb50e899bc3939bbb7ab1e7c659614409af99bd3e7675"}, - {file = "pymongo-4.16.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:8a254d49a9ffe9d7f888e3c677eed3729b14ce85abb08cd74732cead6ccc3c66"}, - {file = "pymongo-4.16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a1bf44e13cf2d44d2ea2e928a8140d5d667304abe1a61c4d55b4906f389fbe64"}, - {file = "pymongo-4.16.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f1c5f1f818b669875d191323a48912d3fcd2e4906410e8297bb09ac50c4d5ccc"}, - {file = "pymongo-4.16.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:77cfd37a43a53b02b7bd930457c7994c924ad8bbe8dff91817904bcbf291b371"}, - {file = "pymongo-4.16.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:36ef2fee50eee669587d742fb456e349634b4fcf8926208766078b089054b24b"}, - {file = "pymongo-4.16.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:55f8d5a6fe2fa0b823674db2293f92d74cd5f970bc0360f409a1fc21003862d3"}, - {file = "pymongo-4.16.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9caacac0dd105e2555521002e2d17afc08665187017b466b5753e84c016628e6"}, - {file = "pymongo-4.16.0-cp314-cp314-win32.whl", hash = "sha256:c789236366525c3ee3cd6e4e450a9ff629a7d1f4d88b8e18a0aea0615fd7ecf8"}, - {file = "pymongo-4.16.0-cp314-cp314-win_amd64.whl", hash = "sha256:2b0714d7764efb29bf9d3c51c964aed7c4c7237b341f9346f15ceaf8321fdb35"}, - {file = "pymongo-4.16.0-cp314-cp314-win_arm64.whl", hash = "sha256:12762e7cc0f8374a8cae3b9f9ed8dabb5d438c7b33329232dd9b7de783454033"}, - {file = "pymongo-4.16.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:1c01e8a7cd0ea66baf64a118005535ab5bf9f9eb63a1b50ac3935dccf9a54abe"}, - {file = "pymongo-4.16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:4c4872299ebe315a79f7f922051061634a64fda95b6b17677ba57ef00b2ba2a4"}, - {file = "pymongo-4.16.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:78037d02389745e247fe5ab0bcad5d1ab30726eaac3ad79219c7d6bbb07eec53"}, - {file = "pymongo-4.16.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c126fb72be2518395cc0465d4bae03125119136462e1945aea19840e45d89cfc"}, - {file = "pymongo-4.16.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f3867dc225d9423c245a51eaac2cfcd53dde8e0a8d8090bb6aed6e31bd6c2d4f"}, - {file = "pymongo-4.16.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f25001a955073b80510c0c3db0e043dbbc36904fd69e511c74e3d8640b8a5111"}, - {file = "pymongo-4.16.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d9885aad05f82fd7ea0c9ca505d60939746b39263fa273d0125170da8f59098"}, - {file = "pymongo-4.16.0-cp314-cp314t-win32.whl", hash = "sha256:948152b30eddeae8355495f9943a3bf66b708295c0b9b6f467de1c620f215487"}, - {file = "pymongo-4.16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:f6e42c1bc985d9beee884780ae6048790eb4cd565c46251932906bdb1630034a"}, - {file = "pymongo-4.16.0-cp314-cp314t-win_arm64.whl", hash = "sha256:6b2a20edb5452ac8daa395890eeb076c570790dfce6b7a44d788af74c2f8cf96"}, - {file = "pymongo-4.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e2d509786344aa844ae243f68f833ca1ac92ac3e35a92ae038e2ceb44aa355ef"}, - {file = "pymongo-4.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:15bb062c0d6d4b0be650410032152de656a2a9a2aa4e1a7443a22695afacb103"}, - {file = "pymongo-4.16.0-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4cd047ba6cc83cc24193b9208c93e134a985ead556183077678c59af7aacc725"}, - {file = "pymongo-4.16.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96aa7ab896889bf330209d26459e493d00f8855772a9453bfb4520bb1f495baf"}, - {file = "pymongo-4.16.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:66af44ed23686dd5422307619a6db4b56733c5e36fe8c4adf91326dcf993a043"}, - {file = "pymongo-4.16.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:03f42396c1b2c6f46f5401c5b185adc25f6113716e16d9503977ee5386fca0fb"}, - {file = "pymongo-4.16.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d284bf68daffc57516535f752e290609b3b643f4bd54b28fc13cb16a89a8bda6"}, - {file = "pymongo-4.16.0-cp39-cp39-win32.whl", hash = "sha256:7902882ed0efb7f0e991458ab3b8cf0eb052957264949ece2f09b63c58b04f78"}, - {file = "pymongo-4.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:e37469602473f41221cea93fd3736708f561f0fa08ab6b2873dd962014390d52"}, - {file = "pymongo-4.16.0-cp39-cp39-win_arm64.whl", hash = "sha256:2a3ba6be3d8acf64b77cdcd4e36f0e4a8e87965f14a8b09b90ca86f10a1dd2f2"}, - {file = "pymongo-4.16.0.tar.gz", hash = "sha256:8ba8405065f6e258a6f872fe62d797a28f383a12178c7153c01ed04e845c600c"}, -] - -[package.dependencies] -dnspython = ">=2.6.1,<3.0.0" - -[package.extras] -aws = ["pymongo-auth-aws (>=1.1.0,<2.0.0)"] -docs = ["furo (==2025.12.19)", "readthedocs-sphinx-search (>=0.3,<1.0)", "sphinx (>=5.3,<9)", "sphinx-autobuild (>=2020.9.1)", "sphinx-rtd-theme (>=2,<4)", "sphinxcontrib-shellcheck (>=1,<2)"] -encryption = ["certifi (>=2023.7.22) ; os_name == \"nt\" or sys_platform == \"darwin\"", "pymongo-auth-aws (>=1.1.0,<2.0.0)", "pymongocrypt (>=1.13.0,<2.0.0)"] -gssapi = ["pykerberos (>=1.2.4) ; os_name != \"nt\"", "winkerberos (>=0.5.0) ; os_name == \"nt\""] -ocsp = ["certifi (>=2023.7.22) ; os_name == \"nt\" or sys_platform == \"darwin\"", "cryptography (>=42.0.0)", "pyopenssl (>=23.2.0)", "requests (>=2.23.0,<3.0)", "service-identity (>=23.1.0)"] -snappy = ["python-snappy (>=0.6.0)"] -test = ["importlib-metadata (>=7.0) ; python_version < \"3.13\"", "pytest (>=8.2)", "pytest-asyncio (>=0.24.0)"] -zstd = ["backports-zstd (>=1.0.0) ; python_version < \"3.14\""] - -[[package]] -name = "pyparsing" -version = "3.3.2" -description = "pyparsing - Classes and methods to define and execute parsing grammars" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d"}, - {file = "pyparsing-3.3.2.tar.gz", hash = "sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc"}, -] - -[package.extras] -diagrams = ["jinja2", "railroad-diagrams"] - -[[package]] -name = "python-dotenv" -version = "1.2.1" -description = "Read key-value pairs from a .env file and set them as environment variables" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61"}, - {file = "python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6"}, -] - -[package.extras] -cli = ["click (>=5.0)"] - -[[package]] -name = "python-multipart" -version = "0.0.22" -description = "A streaming multipart parser for Python" -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "python_multipart-0.0.22-py3-none-any.whl", hash = "sha256:2b2cd894c83d21bf49d702499531c7bafd057d730c201782048f7945d82de155"}, - {file = "python_multipart-0.0.22.tar.gz", hash = "sha256:7340bef99a7e0032613f56dc36027b959fd3b30a787ed62d310e951f7c3a3a58"}, -] - -[[package]] -name = "pytokens" -version = "0.4.1" -description = "A Fast, spec compliant Python 3.14+ tokenizer that runs on older Pythons." -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "pytokens-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a44ed93ea23415c54f3face3b65ef2b844d96aeb3455b8a69b3df6beab6acc5"}, - {file = "pytokens-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:add8bf86b71a5d9fb5b89f023a80b791e04fba57960aa790cc6125f7f1d39dfe"}, - {file = "pytokens-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:670d286910b531c7b7e3c0b453fd8156f250adb140146d234a82219459b9640c"}, - {file = "pytokens-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4e691d7f5186bd2842c14813f79f8884bb03f5995f0575272009982c5ac6c0f7"}, - {file = "pytokens-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:27b83ad28825978742beef057bfe406ad6ed524b2d28c252c5de7b4a6dd48fa2"}, - {file = "pytokens-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d70e77c55ae8380c91c0c18dea05951482e263982911fc7410b1ffd1dadd3440"}, - {file = "pytokens-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a58d057208cb9075c144950d789511220b07636dd2e4708d5645d24de666bdc"}, - {file = "pytokens-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b49750419d300e2b5a3813cf229d4e5a4c728dae470bcc89867a9ad6f25a722d"}, - {file = "pytokens-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d9907d61f15bf7261d7e775bd5d7ee4d2930e04424bab1972591918497623a16"}, - {file = "pytokens-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:ee44d0f85b803321710f9239f335aafe16553b39106384cef8e6de40cb4ef2f6"}, - {file = "pytokens-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:140709331e846b728475786df8aeb27d24f48cbcf7bcd449f8de75cae7a45083"}, - {file = "pytokens-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d6c4268598f762bc8e91f5dbf2ab2f61f7b95bdc07953b602db879b3c8c18e1"}, - {file = "pytokens-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24afde1f53d95348b5a0eb19488661147285ca4dd7ed752bbc3e1c6242a304d1"}, - {file = "pytokens-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5ad948d085ed6c16413eb5fec6b3e02fa00dc29a2534f088d3302c47eb59adf9"}, - {file = "pytokens-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:3f901fe783e06e48e8cbdc82d631fca8f118333798193e026a50ce1b3757ea68"}, - {file = "pytokens-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8bdb9d0ce90cbf99c525e75a2fa415144fd570a1ba987380190e8b786bc6ef9b"}, - {file = "pytokens-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5502408cab1cb18e128570f8d598981c68a50d0cbd7c61312a90507cd3a1276f"}, - {file = "pytokens-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29d1d8fb1030af4d231789959f21821ab6325e463f0503a61d204343c9b355d1"}, - {file = "pytokens-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:970b08dd6b86058b6dc07efe9e98414f5102974716232d10f32ff39701e841c4"}, - {file = "pytokens-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:9bd7d7f544d362576be74f9d5901a22f317efc20046efe2034dced238cbbfe78"}, - {file = "pytokens-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4a14d5f5fc78ce85e426aa159489e2d5961acf0e47575e08f35584009178e321"}, - {file = "pytokens-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f50fd18543be72da51dd505e2ed20d2228c74e0464e4262e4899797803d7fa"}, - {file = "pytokens-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc74c035f9bfca0255c1af77ddd2d6ae8419012805453e4b0e7513e17904545d"}, - {file = "pytokens-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f66a6bbe741bd431f6d741e617e0f39ec7257ca1f89089593479347cc4d13324"}, - {file = "pytokens-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:b35d7e5ad269804f6697727702da3c517bb8a5228afa450ab0fa787732055fc9"}, - {file = "pytokens-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8fcb9ba3709ff77e77f1c7022ff11d13553f3c30299a9fe246a166903e9091eb"}, - {file = "pytokens-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:79fc6b8699564e1f9b521582c35435f1bd32dd06822322ec44afdeba666d8cb3"}, - {file = "pytokens-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d31b97b3de0f61571a124a00ffe9a81fb9939146c122c11060725bd5aea79975"}, - {file = "pytokens-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:967cf6e3fd4adf7de8fc73cd3043754ae79c36475c1c11d514fc72cf5490094a"}, - {file = "pytokens-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:584c80c24b078eec1e227079d56dc22ff755e0ba8654d8383b2c549107528918"}, - {file = "pytokens-0.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:da5baeaf7116dced9c6bb76dc31ba04a2dc3695f3d9f74741d7910122b456edc"}, - {file = "pytokens-0.4.1-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:11edda0942da80ff58c4408407616a310adecae1ddd22eef8c692fe266fa5009"}, - {file = "pytokens-0.4.1-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0fc71786e629cef478cbf29d7ea1923299181d0699dbe7c3c0f4a583811d9fc1"}, - {file = "pytokens-0.4.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:dcafc12c30dbaf1e2af0490978352e0c4041a7cde31f4f81435c2a5e8b9cabb6"}, - {file = "pytokens-0.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:42f144f3aafa5d92bad964d471a581651e28b24434d184871bd02e3a0d956037"}, - {file = "pytokens-0.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:34bcc734bd2f2d5fe3b34e7b3c0116bfb2397f2d9666139988e7a3eb5f7400e3"}, - {file = "pytokens-0.4.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:941d4343bf27b605e9213b26bfa1c4bf197c9c599a9627eb7305b0defcfe40c1"}, - {file = "pytokens-0.4.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3ad72b851e781478366288743198101e5eb34a414f1d5627cdd585ca3b25f1db"}, - {file = "pytokens-0.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:682fa37ff4d8e95f7df6fe6fe6a431e8ed8e788023c6bcc0f0880a12eab80ad1"}, - {file = "pytokens-0.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:30f51edd9bb7f85c748979384165601d028b84f7bd13fe14d3e065304093916a"}, - {file = "pytokens-0.4.1-py3-none-any.whl", hash = "sha256:26cef14744a8385f35d0e095dc8b3a7583f6c953c2e3d269c7f82484bf5ad2de"}, - {file = "pytokens-0.4.1.tar.gz", hash = "sha256:292052fe80923aae2260c073f822ceba21f3872ced9a68bb7953b348e561179a"}, -] - -[package.extras] -dev = ["black", "build", "mypy", "pytest", "pytest-cov", "setuptools", "tox", "twine", "wheel"] - -[[package]] -name = "pywin32-ctypes" -version = "0.2.3" -description = "A (partial) reimplementation of pywin32 using ctypes/cffi" -optional = false -python-versions = ">=3.6" -groups = ["dev"] -markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"win32\"" -files = [ - {file = "pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755"}, - {file = "pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8"}, -] - -[[package]] -name = "qrcode" -version = "8.2" -description = "QR Code image generator" -optional = false -python-versions = "<4.0,>=3.9" -groups = ["main"] -files = [ - {file = "qrcode-8.2-py3-none-any.whl", hash = "sha256:16e64e0716c14960108e85d853062c9e8bba5ca8252c0b4d0231b9df4060ff4f"}, - {file = "qrcode-8.2.tar.gz", hash = "sha256:35c3f2a4172b33136ab9f6b3ef1c00260dd2f66f858f24d88418a015f446506c"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "sys_platform == \"win32\""} - -[package.extras] -all = ["pillow (>=9.1.0)", "pypng"] -pil = ["pillow (>=9.1.0)"] -png = ["pypng"] - -[[package]] -name = "readme-renderer" -version = "44.0" -description = "readme_renderer is a library for rendering readme descriptions for Warehouse" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "readme_renderer-44.0-py3-none-any.whl", hash = "sha256:2fbca89b81a08526aadf1357a8c2ae889ec05fb03f5da67f9769c9a592166151"}, - {file = "readme_renderer-44.0.tar.gz", hash = "sha256:8712034eabbfa6805cacf1402b4eeb2a73028f72d1166d6f5cb7f9c047c5d1e1"}, -] - -[package.dependencies] -docutils = ">=0.21.2" -nh3 = ">=0.2.14" -Pygments = ">=2.5.1" - -[package.extras] -md = ["cmarkgfm (>=0.8.0)"] - -[[package]] -name = "redis" -version = "7.1.1" -description = "Python client for Redis database and key-value store" -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "redis-7.1.1-py3-none-any.whl", hash = "sha256:f77817f16071c2950492c67d40b771fa493eb3fccc630a424a10976dbb794b7a"}, - {file = "redis-7.1.1.tar.gz", hash = "sha256:a2814b2bda15b39dad11391cc48edac4697214a8a5a4bd10abe936ab4892eb43"}, -] - -[package.dependencies] -async-timeout = {version = ">=4.0.3", markers = "python_full_version < \"3.11.3\""} - -[package.extras] -circuit-breaker = ["pybreaker (>=1.4.0)"] -hiredis = ["hiredis (>=3.2.0)"] -jwt = ["pyjwt (>=2.9.0)"] -ocsp = ["cryptography (>=36.0.1)", "pyopenssl (>=20.0.1)", "requests (>=2.31.0)"] - -[[package]] -name = "requests" -version = "2.32.5" -description = "Python HTTP for Humans." -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6"}, - {file = "requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf"}, -] - -[package.dependencies] -certifi = ">=2017.4.17" -charset_normalizer = ">=2,<4" -idna = ">=2.5,<4" -urllib3 = ">=1.21.1,<3" - -[package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] - -[[package]] -name = "requests-toolbelt" -version = "1.0.0" -description = "A utility belt for advanced users of python-requests" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -groups = ["dev"] -files = [ - {file = "requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6"}, - {file = "requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06"}, -] - -[package.dependencies] -requests = ">=2.0.1,<3.0.0" - -[[package]] -name = "rfc3986" -version = "2.0.0" -description = "Validating URI References per RFC 3986" -optional = false -python-versions = ">=3.7" -groups = ["dev"] -files = [ - {file = "rfc3986-2.0.0-py2.py3-none-any.whl", hash = "sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd"}, - {file = "rfc3986-2.0.0.tar.gz", hash = "sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c"}, -] - -[package.extras] -idna2008 = ["idna"] - -[[package]] -name = "rich" -version = "14.3.2" -description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" -optional = false -python-versions = ">=3.8.0" -groups = ["dev"] -files = [ - {file = "rich-14.3.2-py3-none-any.whl", hash = "sha256:08e67c3e90884651da3239ea668222d19bea7b589149d8014a21c633420dbb69"}, - {file = "rich-14.3.2.tar.gz", hash = "sha256:e712f11c1a562a11843306f5ed999475f09ac31ffb64281f73ab29ffdda8b3b8"}, -] - -[package.dependencies] -markdown-it-py = ">=2.2.0" -pygments = ">=2.13.0,<3.0.0" - -[package.extras] -jupyter = ["ipywidgets (>=7.5.1,<9)"] - -[[package]] -name = "ruff" -version = "0.15.0" -description = "An extremely fast Python linter and code formatter, written in Rust." -optional = false -python-versions = ">=3.7" -groups = ["dev"] -files = [ - {file = "ruff-0.15.0-py3-none-linux_armv6l.whl", hash = "sha256:aac4ebaa612a82b23d45964586f24ae9bc23ca101919f5590bdb368d74ad5455"}, - {file = "ruff-0.15.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:dcd4be7cc75cfbbca24a98d04d0b9b36a270d0833241f776b788d59f4142b14d"}, - {file = "ruff-0.15.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d747e3319b2bce179c7c1eaad3d884dc0a199b5f4d5187620530adf9105268ce"}, - {file = "ruff-0.15.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:650bd9c56ae03102c51a5e4b554d74d825ff3abe4db22b90fd32d816c2e90621"}, - {file = "ruff-0.15.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a6664b7eac559e3048223a2da77769c2f92b43a6dfd4720cef42654299a599c9"}, - {file = "ruff-0.15.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f811f97b0f092b35320d1556f3353bf238763420ade5d9e62ebd2b73f2ff179"}, - {file = "ruff-0.15.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:761ec0a66680fab6454236635a39abaf14198818c8cdf691e036f4bc0f406b2d"}, - {file = "ruff-0.15.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:940f11c2604d317e797b289f4f9f3fa5555ffe4fb574b55ed006c3d9b6f0eb78"}, - {file = "ruff-0.15.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bcbca3d40558789126da91d7ef9a7c87772ee107033db7191edefa34e2c7f1b4"}, - {file = "ruff-0.15.0-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:9a121a96db1d75fa3eb39c4539e607f628920dd72ff1f7c5ee4f1b768ac62d6e"}, - {file = "ruff-0.15.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:5298d518e493061f2eabd4abd067c7e4fb89e2f63291c94332e35631c07c3662"}, - {file = "ruff-0.15.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:afb6e603d6375ff0d6b0cee563fa21ab570fd15e65c852cb24922cef25050cf1"}, - {file = "ruff-0.15.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:77e515f6b15f828b94dc17d2b4ace334c9ddb7d9468c54b2f9ed2b9c1593ef16"}, - {file = "ruff-0.15.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:6f6e80850a01eb13b3e42ee0ebdf6e4497151b48c35051aab51c101266d187a3"}, - {file = "ruff-0.15.0-py3-none-win32.whl", hash = "sha256:238a717ef803e501b6d51e0bdd0d2c6e8513fe9eec14002445134d3907cd46c3"}, - {file = "ruff-0.15.0-py3-none-win_amd64.whl", hash = "sha256:dd5e4d3301dc01de614da3cdffc33d4b1b96fb89e45721f1598e5532ccf78b18"}, - {file = "ruff-0.15.0-py3-none-win_arm64.whl", hash = "sha256:c480d632cc0ca3f0727acac8b7d053542d9e114a462a145d0b00e7cd658c515a"}, - {file = "ruff-0.15.0.tar.gz", hash = "sha256:6bdea47cdbea30d40f8f8d7d69c0854ba7c15420ec75a26f463290949d7f7e9a"}, -] - -[[package]] -name = "secretstorage" -version = "3.5.0" -description = "Python bindings to FreeDesktop.org Secret Service API" -optional = false -python-versions = ">=3.10" -groups = ["dev"] -markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\"" -files = [ - {file = "secretstorage-3.5.0-py3-none-any.whl", hash = "sha256:0ce65888c0725fcb2c5bc0fdb8e5438eece02c523557ea40ce0703c266248137"}, - {file = "secretstorage-3.5.0.tar.gz", hash = "sha256:f04b8e4689cbce351744d5537bf6b1329c6fc68f91fa666f60a380edddcd11be"}, -] - -[package.dependencies] -cryptography = ">=2.0" -jeepney = ">=0.6" - -[[package]] -name = "sortedcontainers" -version = "2.4.0" -description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set" -optional = false -python-versions = "*" -groups = ["dev"] -files = [ - {file = "sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0"}, - {file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"}, -] - -[[package]] -name = "starlette" -version = "0.52.1" -description = "The little ASGI library that shines." -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "starlette-0.52.1-py3-none-any.whl", hash = "sha256:0029d43eb3d273bc4f83a08720b4912ea4b071087a3b48db01b7c839f7954d74"}, - {file = "starlette-0.52.1.tar.gz", hash = "sha256:834edd1b0a23167694292e94f597773bc3f89f362be6effee198165a35d62933"}, -] - -[package.dependencies] -anyio = ">=3.6.2,<5" -typing-extensions = {version = ">=4.10.0", markers = "python_version < \"3.13\""} - -[package.extras] -full = ["httpx (>=0.27.0,<0.29.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.18)", "pyyaml"] - -[[package]] -name = "tomli" -version = "2.4.0" -description = "A lil' TOML parser" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "tomli-2.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b5ef256a3fd497d4973c11bf142e9ed78b150d36f5773f1ca6088c230ffc5867"}, - {file = "tomli-2.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5572e41282d5268eb09a697c89a7bee84fae66511f87533a6f88bd2f7b652da9"}, - {file = "tomli-2.4.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:551e321c6ba03b55676970b47cb1b73f14a0a4dce6a3e1a9458fd6d921d72e95"}, - {file = "tomli-2.4.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e3f639a7a8f10069d0e15408c0b96a2a828cfdec6fca05296ebcdcc28ca7c76"}, - {file = "tomli-2.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1b168f2731796b045128c45982d3a4874057626da0e2ef1fdd722848b741361d"}, - {file = "tomli-2.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:133e93646ec4300d651839d382d63edff11d8978be23da4cc106f5a18b7d0576"}, - {file = "tomli-2.4.0-cp311-cp311-win32.whl", hash = "sha256:b6c78bdf37764092d369722d9946cb65b8767bfa4110f902a1b2542d8d173c8a"}, - {file = "tomli-2.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:d3d1654e11d724760cdb37a3d7691f0be9db5fbdaef59c9f532aabf87006dbaa"}, - {file = "tomli-2.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:cae9c19ed12d4e8f3ebf46d1a75090e4c0dc16271c5bce1c833ac168f08fb614"}, - {file = "tomli-2.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:920b1de295e72887bafa3ad9f7a792f811847d57ea6b1215154030cf131f16b1"}, - {file = "tomli-2.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d6d9a4aee98fac3eab4952ad1d73aee87359452d1c086b5ceb43ed02ddb16b8"}, - {file = "tomli-2.4.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36b9d05b51e65b254ea6c2585b59d2c4cb91c8a3d91d0ed0f17591a29aaea54a"}, - {file = "tomli-2.4.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c8a885b370751837c029ef9bc014f27d80840e48bac415f3412e6593bbc18c1"}, - {file = "tomli-2.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8768715ffc41f0008abe25d808c20c3d990f42b6e2e58305d5da280ae7d1fa3b"}, - {file = "tomli-2.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b438885858efd5be02a9a133caf5812b8776ee0c969fea02c45e8e3f296ba51"}, - {file = "tomli-2.4.0-cp312-cp312-win32.whl", hash = "sha256:0408e3de5ec77cc7f81960c362543cbbd91ef883e3138e81b729fc3eea5b9729"}, - {file = "tomli-2.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:685306e2cc7da35be4ee914fd34ab801a6acacb061b6a7abca922aaf9ad368da"}, - {file = "tomli-2.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:5aa48d7c2356055feef06a43611fc401a07337d5b006be13a30f6c58f869e3c3"}, - {file = "tomli-2.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84d081fbc252d1b6a982e1870660e7330fb8f90f676f6e78b052ad4e64714bf0"}, - {file = "tomli-2.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9a08144fa4cba33db5255f9b74f0b89888622109bd2776148f2597447f92a94e"}, - {file = "tomli-2.4.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c73add4bb52a206fd0c0723432db123c0c75c280cbd67174dd9d2db228ebb1b4"}, - {file = "tomli-2.4.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fb2945cbe303b1419e2706e711b7113da57b7db31ee378d08712d678a34e51e"}, - {file = "tomli-2.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bbb1b10aa643d973366dc2cb1ad94f99c1726a02343d43cbc011edbfac579e7c"}, - {file = "tomli-2.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4cbcb367d44a1f0c2be408758b43e1ffb5308abe0ea222897d6bfc8e8281ef2f"}, - {file = "tomli-2.4.0-cp313-cp313-win32.whl", hash = "sha256:7d49c66a7d5e56ac959cb6fc583aff0651094ec071ba9ad43df785abc2320d86"}, - {file = "tomli-2.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:3cf226acb51d8f1c394c1b310e0e0e61fecdd7adcb78d01e294ac297dd2e7f87"}, - {file = "tomli-2.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:d20b797a5c1ad80c516e41bc1fb0443ddb5006e9aaa7bda2d71978346aeb9132"}, - {file = "tomli-2.4.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:26ab906a1eb794cd4e103691daa23d95c6919cc2fa9160000ac02370cc9dd3f6"}, - {file = "tomli-2.4.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:20cedb4ee43278bc4f2fee6cb50daec836959aadaf948db5172e776dd3d993fc"}, - {file = "tomli-2.4.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:39b0b5d1b6dd03684b3fb276407ebed7090bbec989fa55838c98560c01113b66"}, - {file = "tomli-2.4.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a26d7ff68dfdb9f87a016ecfd1e1c2bacbe3108f4e0f8bcd2228ef9a766c787d"}, - {file = "tomli-2.4.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:20ffd184fb1df76a66e34bd1b36b4a4641bd2b82954befa32fe8163e79f1a702"}, - {file = "tomli-2.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:75c2f8bbddf170e8effc98f5e9084a8751f8174ea6ccf4fca5398436e0320bc8"}, - {file = "tomli-2.4.0-cp314-cp314-win32.whl", hash = "sha256:31d556d079d72db7c584c0627ff3a24c5d3fb4f730221d3444f3efb1b2514776"}, - {file = "tomli-2.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:43e685b9b2341681907759cf3a04e14d7104b3580f808cfde1dfdb60ada85475"}, - {file = "tomli-2.4.0-cp314-cp314-win_arm64.whl", hash = "sha256:3d895d56bd3f82ddd6faaff993c275efc2ff38e52322ea264122d72729dca2b2"}, - {file = "tomli-2.4.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:5b5807f3999fb66776dbce568cc9a828544244a8eb84b84b9bafc080c99597b9"}, - {file = "tomli-2.4.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c084ad935abe686bd9c898e62a02a19abfc9760b5a79bc29644463eaf2840cb0"}, - {file = "tomli-2.4.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f2e3955efea4d1cfbcb87bc321e00dc08d2bcb737fd1d5e398af111d86db5df"}, - {file = "tomli-2.4.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e0fe8a0b8312acf3a88077a0802565cb09ee34107813bba1c7cd591fa6cfc8d"}, - {file = "tomli-2.4.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:413540dce94673591859c4c6f794dfeaa845e98bf35d72ed59636f869ef9f86f"}, - {file = "tomli-2.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0dc56fef0e2c1c470aeac5b6ca8cc7b640bb93e92d9803ddaf9ea03e198f5b0b"}, - {file = "tomli-2.4.0-cp314-cp314t-win32.whl", hash = "sha256:d878f2a6707cc9d53a1be1414bbb419e629c3d6e67f69230217bb663e76b5087"}, - {file = "tomli-2.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2add28aacc7425117ff6364fe9e06a183bb0251b03f986df0e78e974047571fd"}, - {file = "tomli-2.4.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2b1e3b80e1d5e52e40e9b924ec43d81570f0e7d09d11081b797bc4692765a3d4"}, - {file = "tomli-2.4.0-py3-none-any.whl", hash = "sha256:1f776e7d669ebceb01dee46484485f43a4048746235e683bcdffacdf1fb4785a"}, - {file = "tomli-2.4.0.tar.gz", hash = "sha256:aa89c3f6c277dd275d8e243ad24f3b5e701491a860d5121f2cdd399fbb31fc9c"}, -] - -[[package]] -name = "tomli-w" -version = "1.2.0" -description = "A lil' TOML writer" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "tomli_w-1.2.0-py3-none-any.whl", hash = "sha256:188306098d013b691fcadc011abd66727d3c414c571bb01b1a174ba8c983cf90"}, - {file = "tomli_w-1.2.0.tar.gz", hash = "sha256:2dd14fac5a47c27be9cd4c976af5a12d87fb1f0b4512f81d69cce3b35ae25021"}, -] - -[[package]] -name = "twine" -version = "6.2.0" -description = "Collection of utilities for publishing packages on PyPI" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "twine-6.2.0-py3-none-any.whl", hash = "sha256:418ebf08ccda9a8caaebe414433b0ba5e25eb5e4a927667122fbe8f829f985d8"}, - {file = "twine-6.2.0.tar.gz", hash = "sha256:e5ed0d2fd70c9959770dce51c8f39c8945c574e18173a7b81802dab51b4b75cf"}, -] - -[package.dependencies] -id = "*" -keyring = {version = ">=21.2.0", markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\""} -packaging = ">=24.0" -readme-renderer = ">=35.0" -requests = ">=2.20" -requests-toolbelt = ">=0.8.0,<0.9.0 || >0.9.0" -rfc3986 = ">=1.4.0" -rich = ">=12.0.0" -urllib3 = ">=1.26.0" - -[package.extras] -keyring = ["keyring (>=21.2.0)"] - -[[package]] -name = "types-pillow" -version = "10.2.0.20240822" -description = "Typing stubs for Pillow" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "types-Pillow-10.2.0.20240822.tar.gz", hash = "sha256:559fb52a2ef991c326e4a0d20accb3bb63a7ba8d40eb493e0ecb0310ba52f0d3"}, - {file = "types_Pillow-10.2.0.20240822-py3-none-any.whl", hash = "sha256:d9dab025aba07aeb12fd50a6799d4eac52a9603488eca09d7662543983f16c5d"}, -] - -[[package]] -name = "types-pywin32" -version = "311.0.0.20251008" -description = "Typing stubs for pywin32" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "types_pywin32-311.0.0.20251008-py3-none-any.whl", hash = "sha256:775e1046e0bad6d29ca47501301cce67002f6661b9cebbeca93f9c388c53fab4"}, - {file = "types_pywin32-311.0.0.20251008.tar.gz", hash = "sha256:d6d4faf8e0d7fdc0e0a1ff297b80be07d6d18510f102d793bf54e9e3e86f6d06"}, -] - -[[package]] -name = "typing-extensions" -version = "4.15.0" -description = "Backported and Experimental Type Hints for Python 3.9+" -optional = false -python-versions = ">=3.9" -groups = ["main", "dev"] -files = [ - {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, - {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, -] - -[[package]] -name = "typing-inspection" -version = "0.4.2" -description = "Runtime typing introspection tools" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7"}, - {file = "typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464"}, -] - -[package.dependencies] -typing-extensions = ">=4.12.0" - -[[package]] -name = "urllib3" -version = "2.6.3" -description = "HTTP library with thread-safe connection pooling, file post, and more." -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4"}, - {file = "urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed"}, -] - -[package.extras] -brotli = ["brotli (>=1.2.0) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=1.2.0.0) ; platform_python_implementation != \"CPython\""] -h2 = ["h2 (>=4,<5)"] -socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] -zstd = ["backports-zstd (>=1.0.0) ; python_version < \"3.14\""] - -[[package]] -name = "uvicorn" -version = "0.40.0" -description = "The lightning-fast ASGI server." -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "uvicorn-0.40.0-py3-none-any.whl", hash = "sha256:c6c8f55bc8bf13eb6fa9ff87ad62308bbbc33d0b67f84293151efe87e0d5f2ee"}, - {file = "uvicorn-0.40.0.tar.gz", hash = "sha256:839676675e87e73694518b5574fd0f24c9d97b46bea16df7b8c05ea1a51071ea"}, -] - -[package.dependencies] -click = ">=7.0" -h11 = ">=0.8" -typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""} - -[package.extras] -standard = ["colorama (>=0.4) ; sys_platform == \"win32\"", "httptools (>=0.6.3)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.15.1) ; sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\"", "watchfiles (>=0.13)", "websockets (>=10.4)"] - -[[package]] -name = "validators" -version = "0.35.0" -description = "Python Data Validation for Humansβ„’" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "validators-0.35.0-py3-none-any.whl", hash = "sha256:e8c947097eae7892cb3d26868d637f79f47b4a0554bc6b80065dfe5aac3705dd"}, - {file = "validators-0.35.0.tar.gz", hash = "sha256:992d6c48a4e77c81f1b4daba10d16c3a9bb0dbb79b3a19ea847ff0928e70497a"}, -] - -[package.extras] -crypto-eth-addresses = ["eth-hash[pycryptodome] (>=0.7.0)"] - -[[package]] -name = "vulture" -version = "2.14" -description = "Find dead code" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "vulture-2.14-py2.py3-none-any.whl", hash = "sha256:d9a90dba89607489548a49d557f8bac8112bd25d3cbc8aeef23e860811bd5ed9"}, - {file = "vulture-2.14.tar.gz", hash = "sha256:cb8277902a1138deeab796ec5bef7076a6e0248ca3607a3f3dee0b6d9e9b8415"}, -] - -[package.dependencies] -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} - -[[package]] -name = "zipp" -version = "3.23.0" -description = "Backport of pathlib-compatible object wrapper for zip files" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and python_version < \"3.12\"" -files = [ - {file = "zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e"}, - {file = "zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166"}, -] - -[package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] -cover = ["pytest-cov"] -doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -enabler = ["pytest-enabler (>=2.2)"] -test = ["big-O", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more_itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] -type = ["pytest-mypy"] - -[extras] -mongodb = ["pymongo"] - -[metadata] -lock-version = "2.1" -python-versions = ">=3.10,<3.13" -content-hash = "349ea21b64217dca053564867ff9f7cf5375fe7c8dee163a5689d9f3a25fd371" +# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. + +[[package]] +name = "annotated-doc" +version = "0.0.4" +description = "Document parameters, class attributes, return types, and variables inline, with Annotated." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320"}, + {file = "annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4"}, +] + +[[package]] +name = "annotated-types" +version = "0.7.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, + {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, +] + +[[package]] +name = "anyio" +version = "4.12.1" +description = "High-level concurrency and networking framework on top of asyncio or Trio" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c"}, + {file = "anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703"}, +] + +[package.dependencies] +exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} +idna = ">=2.8" +typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} + +[package.extras] +trio = ["trio (>=0.31.0) ; python_version < \"3.10\"", "trio (>=0.32.0) ; python_version >= \"3.10\""] + +[[package]] +name = "async-timeout" +version = "5.0.1" +description = "Timeout context manager for asyncio programs" +optional = false +python-versions = ">=3.8" +groups = ["main"] +markers = "python_full_version < \"3.11.3\"" +files = [ + {file = "async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c"}, + {file = "async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3"}, +] + +[[package]] +name = "backports-tarfile" +version = "1.2.0" +description = "Backport of CPython tarfile module" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and python_version < \"3.12\"" +files = [ + {file = "backports.tarfile-1.2.0-py3-none-any.whl", hash = "sha256:77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34"}, + {file = "backports_tarfile-1.2.0.tar.gz", hash = "sha256:d75e02c268746e1b8144c278978b6e98e85de6ad16f8e4b0844a154557eca991"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["jaraco.test", "pytest (!=8.0.*)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)"] + +[[package]] +name = "black" +version = "26.1.0" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "black-26.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ca699710dece84e3ebf6e92ee15f5b8f72870ef984bf944a57a777a48357c168"}, + {file = "black-26.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5e8e75dabb6eb83d064b0db46392b25cabb6e784ea624219736e8985a6b3675d"}, + {file = "black-26.1.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eb07665d9a907a1a645ee41a0df8a25ffac8ad9c26cdb557b7b88eeeeec934e0"}, + {file = "black-26.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:7ed300200918147c963c87700ccf9966dceaefbbb7277450a8d646fc5646bf24"}, + {file = "black-26.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:c5b7713daea9bf943f79f8c3b46f361cc5229e0e604dcef6a8bb6d1c37d9df89"}, + {file = "black-26.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3cee1487a9e4c640dc7467aaa543d6c0097c391dc8ac74eb313f2fbf9d7a7cb5"}, + {file = "black-26.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d62d14ca31c92adf561ebb2e5f2741bf8dea28aef6deb400d49cca011d186c68"}, + {file = "black-26.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fb1dafbbaa3b1ee8b4550a84425aac8874e5f390200f5502cf3aee4a2acb2f14"}, + {file = "black-26.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:101540cb2a77c680f4f80e628ae98bd2bd8812fb9d72ade4f8995c5ff019e82c"}, + {file = "black-26.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:6f3977a16e347f1b115662be07daa93137259c711e526402aa444d7a88fdc9d4"}, + {file = "black-26.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6eeca41e70b5f5c84f2f913af857cf2ce17410847e1d54642e658e078da6544f"}, + {file = "black-26.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:dd39eef053e58e60204f2cdf059e2442e2eb08f15989eefe259870f89614c8b6"}, + {file = "black-26.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9459ad0d6cd483eacad4c6566b0f8e42af5e8b583cee917d90ffaa3778420a0a"}, + {file = "black-26.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:a19915ec61f3a8746e8b10adbac4a577c6ba9851fa4a9e9fbfbcf319887a5791"}, + {file = "black-26.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:643d27fb5facc167c0b1b59d0315f2674a6e950341aed0fc05cf307d22bf4954"}, + {file = "black-26.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ba1d768fbfb6930fc93b0ecc32a43d8861ded16f47a40f14afa9bb04ab93d304"}, + {file = "black-26.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2b807c240b64609cb0e80d2200a35b23c7df82259f80bef1b2c96eb422b4aac9"}, + {file = "black-26.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1de0f7d01cc894066a1153b738145b194414cc6eeaad8ef4397ac9abacf40f6b"}, + {file = "black-26.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:91a68ae46bf07868963671e4d05611b179c2313301bd756a89ad4e3b3db2325b"}, + {file = "black-26.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:be5e2fe860b9bd9edbf676d5b60a9282994c03fbbd40fe8f5e75d194f96064ca"}, + {file = "black-26.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:9dc8c71656a79ca49b8d3e2ce8103210c9481c57798b48deeb3a8bb02db5f115"}, + {file = "black-26.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b22b3810451abe359a964cc88121d57f7bce482b53a066de0f1584988ca36e79"}, + {file = "black-26.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:53c62883b3f999f14e5d30b5a79bd437236658ad45b2f853906c7cbe79de00af"}, + {file = "black-26.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:f016baaadc423dc960cdddf9acae679e71ee02c4c341f78f3179d7e4819c095f"}, + {file = "black-26.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:66912475200b67ef5a0ab665011964bf924745103f51977a78b4fb92a9fc1bf0"}, + {file = "black-26.1.0-py3-none-any.whl", hash = "sha256:1054e8e47ebd686e078c0bb0eaf31e6ce69c966058d122f2c0c950311f9f3ede"}, + {file = "black-26.1.0.tar.gz", hash = "sha256:d294ac3340eef9c9eb5d29288e96dc719ff269a88e27b396340459dd85da4c58"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=1.0.0" +platformdirs = ">=2" +pytokens = ">=0.3.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.10)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "boolean-py" +version = "5.0" +description = "Define boolean algebras, create and parse boolean expressions and create custom boolean DSL." +optional = false +python-versions = "*" +groups = ["dev"] +files = [ + {file = "boolean_py-5.0-py3-none-any.whl", hash = "sha256:ef28a70bd43115208441b53a045d1549e2f0ec6e3d08a9d142cbc41c1938e8d9"}, + {file = "boolean_py-5.0.tar.gz", hash = "sha256:60cbc4bad079753721d32649545505362c754e121570ada4658b852a3a318d95"}, +] + +[package.extras] +dev = ["build", "twine"] +docs = ["Sphinx (>=3.3.1)", "doc8 (>=0.8.1)", "sphinx-rtd-theme (>=0.5.0)", "sphinxcontrib-apidoc (>=0.3.0)"] +linting = ["black", "isort", "pycodestyle"] +testing = ["pytest (>=6,!=7.0.0)", "pytest-xdist (>=2)"] + +[[package]] +name = "cachecontrol" +version = "0.14.4" +description = "httplib2 caching for requests" +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "cachecontrol-0.14.4-py3-none-any.whl", hash = "sha256:b7ac014ff72ee199b5f8af1de29d60239954f223e948196fa3d84adaffc71d2b"}, + {file = "cachecontrol-0.14.4.tar.gz", hash = "sha256:e6220afafa4c22a47dd0badb319f84475d79108100d04e26e8542ef7d3ab05a1"}, +] + +[package.dependencies] +filelock = {version = ">=3.8.0", optional = true, markers = "extra == \"filecache\""} +msgpack = ">=0.5.2,<2.0.0" +requests = ">=2.16.0" + +[package.extras] +dev = ["cachecontrol[filecache,redis]", "cheroot (>=11.1.2)", "cherrypy", "codespell", "furo", "mypy", "pytest", "pytest-cov", "ruff", "sphinx", "sphinx-copybutton", "types-redis", "types-requests"] +filecache = ["filelock (>=3.8.0)"] +redis = ["redis (>=2.10.5)"] + +[[package]] +name = "certifi" +version = "2026.1.4" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "certifi-2026.1.4-py3-none-any.whl", hash = "sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c"}, + {file = "certifi-2026.1.4.tar.gz", hash = "sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120"}, +] + +[[package]] +name = "cffi" +version = "2.0.0" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = ">=3.9" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\" and platform_python_implementation != \"PyPy\"" +files = [ + {file = "cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44"}, + {file = "cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453"}, + {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495"}, + {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5"}, + {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb"}, + {file = "cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a"}, + {file = "cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739"}, + {file = "cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe"}, + {file = "cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26"}, + {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9"}, + {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414"}, + {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743"}, + {file = "cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5"}, + {file = "cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5"}, + {file = "cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d"}, + {file = "cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d"}, + {file = "cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba"}, + {file = "cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94"}, + {file = "cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187"}, + {file = "cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18"}, + {file = "cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5"}, + {file = "cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6"}, + {file = "cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb"}, + {file = "cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26"}, + {file = "cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c"}, + {file = "cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b"}, + {file = "cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27"}, + {file = "cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75"}, + {file = "cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91"}, + {file = "cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5"}, + {file = "cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775"}, + {file = "cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205"}, + {file = "cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1"}, + {file = "cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f"}, + {file = "cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25"}, + {file = "cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad"}, + {file = "cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9"}, + {file = "cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592"}, + {file = "cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512"}, + {file = "cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4"}, + {file = "cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e"}, + {file = "cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6"}, + {file = "cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9"}, + {file = "cffi-2.0.0-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:fe562eb1a64e67dd297ccc4f5addea2501664954f2692b69a76449ec7913ecbf"}, + {file = "cffi-2.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:de8dad4425a6ca6e4e5e297b27b5c824ecc7581910bf9aee86cb6835e6812aa7"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:4647afc2f90d1ddd33441e5b0e85b16b12ddec4fca55f0d9671fef036ecca27c"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3f4d46d8b35698056ec29bca21546e1551a205058ae1a181d871e278b0b28165"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:e6e73b9e02893c764e7e8d5bb5ce277f1a009cd5243f8228f75f842bf937c534"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:cb527a79772e5ef98fb1d700678fe031e353e765d1ca2d409c92263c6d43e09f"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:61d028e90346df14fedc3d1e5441df818d095f3b87d286825dfcbd6459b7ef63"}, + {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0f6084a0ea23d05d20c3edcda20c3d006f9b6f3fefeac38f59262e10cef47ee2"}, + {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1cd13c99ce269b3ed80b417dcd591415d3372bcac067009b6e0f59c7d4015e65"}, + {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:89472c9762729b5ae1ad974b777416bfda4ac5642423fa93bd57a09204712322"}, + {file = "cffi-2.0.0-cp39-cp39-win32.whl", hash = "sha256:2081580ebb843f759b9f617314a24ed5738c51d2aee65d31e02f6f7a2b97707a"}, + {file = "cffi-2.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:b882b3df248017dba09d6b16defe9b5c407fe32fc7c65a9c69798e6175601be9"}, + {file = "cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529"}, +] + +[package.dependencies] +pycparser = {version = "*", markers = "implementation_name != \"PyPy\""} + +[[package]] +name = "charset-normalizer" +version = "3.4.4" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "charset_normalizer-3.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-win32.whl", hash = "sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-win_arm64.whl", hash = "sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-win32.whl", hash = "sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ce8a0633f41a967713a59c4139d29110c07e826d131a316b50ce11b1d79b4f84"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaabd426fe94daf8fd157c32e571c85cb12e66692f15516a83a03264b08d06c3"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c4ef880e27901b6cc782f1b95f82da9313c0eb95c3af699103088fa0ac3ce9ac"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2aaba3b0819274cc41757a1da876f810a3e4d7b6eb25699253a4effef9e8e4af"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:778d2e08eda00f4256d7f672ca9fef386071c9202f5e4607920b86d7803387f2"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f155a433c2ec037d4e8df17d18922c3a0d9b3232a396690f17175d2946f0218d"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a8bf8d0f749c5757af2142fe7903a9df1d2e8aa3841559b2bad34b08d0e2bcf3"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:194f08cbb32dc406d6e1aea671a68be0823673db2832b38405deba2fb0d88f63"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:6aee717dcfead04c6eb1ce3bd29ac1e22663cdea57f943c87d1eab9a025438d7"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:cd4b7ca9984e5e7985c12bc60a6f173f3c958eae74f3ef6624bb6b26e2abbae4"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_riscv64.whl", hash = "sha256:b7cf1017d601aa35e6bb650b6ad28652c9cd78ee6caff19f3c28d03e1c80acbf"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:e912091979546adf63357d7e2ccff9b44f026c075aeaf25a52d0e95ad2281074"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:5cb4d72eea50c8868f5288b7f7f33ed276118325c1dfd3957089f6b519e1382a"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-win32.whl", hash = "sha256:837c2ce8c5a65a2035be9b3569c684358dfbf109fd3b6969630a87535495ceaa"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:44c2a8734b333e0578090c4cd6b16f275e07aa6614ca8715e6c038e865e70576"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a9768c477b9d7bd54bc0c86dbaebdec6f03306675526c9927c0e8a04e8f94af9"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1bee1e43c28aa63cb16e5c14e582580546b08e535299b8b6158a7c9c768a1f3d"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fd44c878ea55ba351104cb93cc85e74916eb8fa440ca7903e57575e97394f608"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0f04b14ffe5fdc8c4933862d8306109a2c51e0704acfa35d51598eb45a1e89fc"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:cd09d08005f958f370f539f186d10aec3377d55b9eeb0d796025d4886119d76e"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4fe7859a4e3e8457458e2ff592f15ccb02f3da787fcd31e0183879c3ad4692a1"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fa09f53c465e532f4d3db095e0c55b615f010ad81803d383195b6b5ca6cbf5f3"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7fa17817dc5625de8a027cb8b26d9fefa3ea28c8253929b8d6649e705d2835b6"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:5947809c8a2417be3267efc979c47d76a079758166f7d43ef5ae8e9f92751f88"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:4902828217069c3c5c71094537a8e623f5d097858ac6ca8252f7b4d10b7560f1"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:7c308f7e26e4363d79df40ca5b2be1c6ba9f02bdbccfed5abddb7859a6ce72cf"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:2c9d3c380143a1fedbff95a312aa798578371eb29da42106a29019368a475318"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:cb01158d8b88ee68f15949894ccc6712278243d95f344770fa7593fa2d94410c"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-win32.whl", hash = "sha256:2677acec1a2f8ef614c6888b5b4ae4060cc184174a938ed4e8ef690e15d3e505"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:f8e160feb2aed042cd657a72acc0b481212ed28b1b9a95c0cee1621b524e1966"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-win_arm64.whl", hash = "sha256:b5d84d37db046c5ca74ee7bb47dd6cbc13f80665fdde3e8040bdd3fb015ecb50"}, + {file = "charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f"}, + {file = "charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a"}, +] + +[[package]] +name = "click" +version = "8.3.1" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.10" +groups = ["main", "dev"] +files = [ + {file = "click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6"}, + {file = "click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["main", "dev"] +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] +markers = {main = "sys_platform == \"win32\" or platform_system == \"Windows\"", dev = "platform_system == \"Windows\""} + +[[package]] +name = "cryptography" +version = "46.0.5" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +optional = false +python-versions = "!=3.9.0,!=3.9.1,>=3.8" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\"" +files = [ + {file = "cryptography-46.0.5-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:351695ada9ea9618b3500b490ad54c739860883df6c1f555e088eaf25b1bbaad"}, + {file = "cryptography-46.0.5-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c18ff11e86df2e28854939acde2d003f7984f721eba450b56a200ad90eeb0e6b"}, + {file = "cryptography-46.0.5-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d7e3d356b8cd4ea5aff04f129d5f66ebdc7b6f8eae802b93739ed520c47c79b"}, + {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:50bfb6925eff619c9c023b967d5b77a54e04256c4281b0e21336a130cd7fc263"}, + {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:803812e111e75d1aa73690d2facc295eaefd4439be1023fefc4995eaea2af90d"}, + {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ee190460e2fbe447175cda91b88b84ae8322a104fc27766ad09428754a618ed"}, + {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:f145bba11b878005c496e93e257c1e88f154d278d2638e6450d17e0f31e558d2"}, + {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e9251e3be159d1020c4030bd2e5f84d6a43fe54b6c19c12f51cde9542a2817b2"}, + {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:47fb8a66058b80e509c47118ef8a75d14c455e81ac369050f20ba0d23e77fee0"}, + {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:4c3341037c136030cb46e4b1e17b7418ea4cbd9dd207e4a6f3b2b24e0d4ac731"}, + {file = "cryptography-46.0.5-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:890bcb4abd5a2d3f852196437129eb3667d62630333aacc13dfd470fad3aaa82"}, + {file = "cryptography-46.0.5-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:80a8d7bfdf38f87ca30a5391c0c9ce4ed2926918e017c29ddf643d0ed2778ea1"}, + {file = "cryptography-46.0.5-cp311-abi3-win32.whl", hash = "sha256:60ee7e19e95104d4c03871d7d7dfb3d22ef8a9b9c6778c94e1c8fcc8365afd48"}, + {file = "cryptography-46.0.5-cp311-abi3-win_amd64.whl", hash = "sha256:38946c54b16c885c72c4f59846be9743d699eee2b69b6988e0a00a01f46a61a4"}, + {file = "cryptography-46.0.5-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:94a76daa32eb78d61339aff7952ea819b1734b46f73646a07decb40e5b3448e2"}, + {file = "cryptography-46.0.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5be7bf2fb40769e05739dd0046e7b26f9d4670badc7b032d6ce4db64dddc0678"}, + {file = "cryptography-46.0.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fe346b143ff9685e40192a4960938545c699054ba11d4f9029f94751e3f71d87"}, + {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:c69fd885df7d089548a42d5ec05be26050ebcd2283d89b3d30676eb32ff87dee"}, + {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:8293f3dea7fc929ef7240796ba231413afa7b68ce38fd21da2995549f5961981"}, + {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:1abfdb89b41c3be0365328a410baa9df3ff8a9110fb75e7b52e66803ddabc9a9"}, + {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:d66e421495fdb797610a08f43b05269e0a5ea7f5e652a89bfd5a7d3c1dee3648"}, + {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:4e817a8920bfbcff8940ecfd60f23d01836408242b30f1a708d93198393a80b4"}, + {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:68f68d13f2e1cb95163fa3b4db4bf9a159a418f5f6e7242564fc75fcae667fd0"}, + {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:a3d1fae9863299076f05cb8a778c467578262fae09f9dc0ee9b12eb4268ce663"}, + {file = "cryptography-46.0.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c4143987a42a2397f2fc3b4d7e3a7d313fbe684f67ff443999e803dd75a76826"}, + {file = "cryptography-46.0.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7d731d4b107030987fd61a7f8ab512b25b53cef8f233a97379ede116f30eb67d"}, + {file = "cryptography-46.0.5-cp314-cp314t-win32.whl", hash = "sha256:c3bcce8521d785d510b2aad26ae2c966092b7daa8f45dd8f44734a104dc0bc1a"}, + {file = "cryptography-46.0.5-cp314-cp314t-win_amd64.whl", hash = "sha256:4d8ae8659ab18c65ced284993c2265910f6c9e650189d4e3f68445ef82a810e4"}, + {file = "cryptography-46.0.5-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:4108d4c09fbbf2789d0c926eb4152ae1760d5a2d97612b92d508d96c861e4d31"}, + {file = "cryptography-46.0.5-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d1f30a86d2757199cb2d56e48cce14deddf1f9c95f1ef1b64ee91ea43fe2e18"}, + {file = "cryptography-46.0.5-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:039917b0dc418bb9f6edce8a906572d69e74bd330b0b3fea4f79dab7f8ddd235"}, + {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ba2a27ff02f48193fc4daeadf8ad2590516fa3d0adeeb34336b96f7fa64c1e3a"}, + {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:61aa400dce22cb001a98014f647dc21cda08f7915ceb95df0c9eaf84b4b6af76"}, + {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ce58ba46e1bc2aac4f7d9290223cead56743fa6ab94a5d53292ffaac6a91614"}, + {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:420d0e909050490d04359e7fdb5ed7e667ca5c3c402b809ae2563d7e66a92229"}, + {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:582f5fcd2afa31622f317f80426a027f30dc792e9c80ffee87b993200ea115f1"}, + {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:bfd56bb4b37ed4f330b82402f6f435845a5f5648edf1ad497da51a8452d5d62d"}, + {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:a3d507bb6a513ca96ba84443226af944b0f7f47dcc9a399d110cd6146481d24c"}, + {file = "cryptography-46.0.5-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9f16fbdf4da055efb21c22d81b89f155f02ba420558db21288b3d0035bafd5f4"}, + {file = "cryptography-46.0.5-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ced80795227d70549a411a4ab66e8ce307899fad2220ce5ab2f296e687eacde9"}, + {file = "cryptography-46.0.5-cp38-abi3-win32.whl", hash = "sha256:02f547fce831f5096c9a567fd41bc12ca8f11df260959ecc7c3202555cc47a72"}, + {file = "cryptography-46.0.5-cp38-abi3-win_amd64.whl", hash = "sha256:556e106ee01aa13484ce9b0239bca667be5004efb0aabbed28d353df86445595"}, + {file = "cryptography-46.0.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:3b4995dc971c9fb83c25aa44cf45f02ba86f71ee600d81091c2f0cbae116b06c"}, + {file = "cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:bc84e875994c3b445871ea7181d424588171efec3e185dced958dad9e001950a"}, + {file = "cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:2ae6971afd6246710480e3f15824ed3029a60fc16991db250034efd0b9fb4356"}, + {file = "cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:d861ee9e76ace6cf36a6a89b959ec08e7bc2493ee39d07ffe5acb23ef46d27da"}, + {file = "cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:2b7a67c9cd56372f3249b39699f2ad479f6991e62ea15800973b956f4b73e257"}, + {file = "cryptography-46.0.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:8456928655f856c6e1533ff59d5be76578a7157224dbd9ce6872f25055ab9ab7"}, + {file = "cryptography-46.0.5.tar.gz", hash = "sha256:abace499247268e3757271b2f1e244b36b06f8515cf27c4d49468fc9eb16e93d"}, +] + +[package.dependencies] +cffi = {version = ">=2.0.0", markers = "python_full_version >= \"3.9.0\" and platform_python_implementation != \"PyPy\""} +typing-extensions = {version = ">=4.13.2", markers = "python_full_version < \"3.11.0\""} + +[package.extras] +docs = ["sphinx (>=5.3.0)", "sphinx-inline-tabs", "sphinx-rtd-theme (>=3.0.0)"] +docstest = ["pyenchant (>=3)", "readme-renderer (>=30.0)", "sphinxcontrib-spelling (>=7.3.1)"] +nox = ["nox[uv] (>=2024.4.15)"] +pep8test = ["check-sdist", "click (>=8.0.1)", "mypy (>=1.14)", "ruff (>=0.11.11)"] +sdist = ["build (>=1.0.0)"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["certifi (>=2024)", "cryptography-vectors (==46.0.5)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] +test-randomorder = ["pytest-randomly"] + +[[package]] +name = "cyclonedx-python-lib" +version = "11.6.0" +description = "Python library for CycloneDX" +optional = false +python-versions = "<4.0,>=3.9" +groups = ["dev"] +files = [ + {file = "cyclonedx_python_lib-11.6.0-py3-none-any.whl", hash = "sha256:94f4aae97db42a452134dafdddcfab9745324198201c4777ed131e64c8380759"}, + {file = "cyclonedx_python_lib-11.6.0.tar.gz", hash = "sha256:7fb85a4371fa3a203e5be577ac22b7e9a7157f8b0058b7448731474d6dea7bf0"}, +] + +[package.dependencies] +license-expression = ">=30,<31" +packageurl-python = ">=0.11,<2" +py-serializable = ">=2.1.0,<3.0.0" +sortedcontainers = ">=2.4.0,<3.0.0" +typing_extensions = {version = ">=4.6,<5.0", markers = "python_version < \"3.13\""} + +[package.extras] +json-validation = ["jsonschema[format-nongpl] (>=4.25,<5.0)", "referencing (>=0.28.4)"] +validation = ["jsonschema[format-nongpl] (>=4.25,<5.0)", "lxml (>=4,<7)", "referencing (>=0.28.4)"] +xml-validation = ["lxml (>=4,<7)"] + +[[package]] +name = "defusedxml" +version = "0.7.1" +description = "XML bomb protection for Python stdlib modules" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +groups = ["dev"] +files = [ + {file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"}, + {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"}, +] + +[[package]] +name = "dnspython" +version = "2.8.0" +description = "DNS toolkit" +optional = false +python-versions = ">=3.10" +groups = ["main", "dev"] +files = [ + {file = "dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af"}, + {file = "dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f"}, +] + +[package.extras] +dev = ["black (>=25.1.0)", "coverage (>=7.0)", "flake8 (>=7)", "hypercorn (>=0.17.0)", "mypy (>=1.17)", "pylint (>=3)", "pytest (>=8.4)", "pytest-cov (>=6.2.0)", "quart-trio (>=0.12.0)", "sphinx (>=8.2.0)", "sphinx-rtd-theme (>=3.0.0)", "twine (>=6.1.0)", "wheel (>=0.45.0)"] +dnssec = ["cryptography (>=45)"] +doh = ["h2 (>=4.2.0)", "httpcore (>=1.0.0)", "httpx (>=0.28.0)"] +doq = ["aioquic (>=1.2.0)"] +idna = ["idna (>=3.10)"] +trio = ["trio (>=0.30)"] +wmi = ["wmi (>=1.5.1) ; platform_system == \"Windows\""] + +[[package]] +name = "docutils" +version = "0.22.4" +description = "Docutils -- Python Documentation Utilities" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "docutils-0.22.4-py3-none-any.whl", hash = "sha256:d0013f540772d1420576855455d050a2180186c91c15779301ac2ccb3eeb68de"}, + {file = "docutils-0.22.4.tar.gz", hash = "sha256:4db53b1fde9abecbb74d91230d32ab626d94f6badfc575d6db9194a49df29968"}, +] + +[[package]] +name = "exceptiongroup" +version = "1.3.1" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +groups = ["main"] +markers = "python_version == \"3.10\"" +files = [ + {file = "exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598"}, + {file = "exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""} + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "fastapi" +version = "0.128.7" +description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "fastapi-0.128.7-py3-none-any.whl", hash = "sha256:6bd9bd31cb7047465f2d3fa3ba3f33b0870b17d4eaf7cdb36d1576ab060ad662"}, + {file = "fastapi-0.128.7.tar.gz", hash = "sha256:783c273416995486c155ad2c0e2b45905dedfaf20b9ef8d9f6a9124670639a24"}, +] + +[package.dependencies] +annotated-doc = ">=0.0.2" +pydantic = ">=2.7.0" +starlette = ">=0.40.0,<1.0.0" +typing-extensions = ">=4.8.0" +typing-inspection = ">=0.4.2" + +[package.extras] +all = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.8)", "httpx (>=0.23.0,<1.0.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=3.1.5)", "orjson (>=3.9.3)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.18)", "pyyaml (>=5.3.1)", "ujson (>=5.8.0)", "uvicorn[standard] (>=0.12.0)"] +standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.8)", "httpx (>=0.23.0,<1.0.0)", "jinja2 (>=3.1.5)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.18)", "uvicorn[standard] (>=0.12.0)"] +standard-no-fastapi-cloud-cli = ["email-validator (>=2.0.0)", "fastapi-cli[standard-no-fastapi-cloud-cli] (>=0.0.8)", "httpx (>=0.23.0,<1.0.0)", "jinja2 (>=3.1.5)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.18)", "uvicorn[standard] (>=0.12.0)"] + +[[package]] +name = "filelock" +version = "3.20.3" +description = "A platform independent file lock." +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "filelock-3.20.3-py3-none-any.whl", hash = "sha256:4b0dda527ee31078689fc205ec4f1c1bf7d56cf88b6dc9426c4f230e46c2dce1"}, + {file = "filelock-3.20.3.tar.gz", hash = "sha256:18c57ee915c7ec61cff0ecf7f0f869936c7c30191bb0cf406f1341778d0834e1"}, +] + +[[package]] +name = "flake8" +version = "7.3.0" +description = "the modular source code checker: pep8 pyflakes and co" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "flake8-7.3.0-py2.py3-none-any.whl", hash = "sha256:b9696257b9ce8beb888cdbe31cf885c90d31928fe202be0889a7cdafad32f01e"}, + {file = "flake8-7.3.0.tar.gz", hash = "sha256:fe044858146b9fc69b551a4b490d69cf960fcb78ad1edcb84e7fbb1b4a8e3872"}, +] + +[package.dependencies] +mccabe = ">=0.7.0,<0.8.0" +pycodestyle = ">=2.14.0,<2.15.0" +pyflakes = ">=3.4.0,<3.5.0" + +[[package]] +name = "h11" +version = "0.16.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86"}, + {file = "h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1"}, +] + +[[package]] +name = "id" +version = "1.6.1" +description = "A tool for generating OIDC identities" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "id-1.6.1-py3-none-any.whl", hash = "sha256:f5ec41ed2629a508f5d0988eda142e190c9c6da971100612c4de9ad9f9b237ca"}, + {file = "id-1.6.1.tar.gz", hash = "sha256:d0732d624fb46fd4e7bc4e5152f00214450953b9e772c182c1c22964def1a069"}, +] + +[package.dependencies] +urllib3 = ">=2,<3" + +[package.extras] +dev = ["build", "bump (>=1.3.2)", "id[lint,test]"] +lint = ["bandit", "interrogate", "mypy", "ruff (<0.14.15)"] +test = ["coverage[toml]", "pretend", "pytest", "pytest-cov"] + +[[package]] +name = "idna" +version = "3.11" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.8" +groups = ["main", "dev"] +files = [ + {file = "idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea"}, + {file = "idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902"}, +] + +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + +[[package]] +name = "importlib-metadata" +version = "8.7.1" +description = "Read metadata from Python packages" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and python_version < \"3.12\"" +files = [ + {file = "importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151"}, + {file = "importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb"}, +] + +[package.dependencies] +zipp = ">=3.20" + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=3.4)"] +perf = ["ipython"] +test = ["flufl.flake8", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] +type = ["mypy (<1.19) ; platform_python_implementation == \"PyPy\"", "pytest-mypy (>=1.0.1)"] + +[[package]] +name = "itsdangerous" +version = "2.2.0" +description = "Safely pass data to untrusted environments and back." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef"}, + {file = "itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173"}, +] + +[[package]] +name = "jaraco-classes" +version = "3.4.0" +description = "Utility functions for Python class constructs" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\"" +files = [ + {file = "jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790"}, + {file = "jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd"}, +] + +[package.dependencies] +more-itertools = "*" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)"] + +[[package]] +name = "jaraco-context" +version = "6.1.0" +description = "Useful decorators and context managers" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\"" +files = [ + {file = "jaraco_context-6.1.0-py3-none-any.whl", hash = "sha256:a43b5ed85815223d0d3cfdb6d7ca0d2bc8946f28f30b6f3216bda070f68badda"}, + {file = "jaraco_context-6.1.0.tar.gz", hash = "sha256:129a341b0a85a7db7879e22acd66902fda67882db771754574338898b2d5d86f"}, +] + +[package.dependencies] +"backports.tarfile" = {version = "*", markers = "python_version < \"3.12\""} + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=3.4)"] +test = ["jaraco.test (>=5.6.0)", "portend", "pytest (>=6,!=8.1.*)"] +type = ["mypy (<1.19) ; platform_python_implementation == \"PyPy\"", "pytest-mypy (>=1.0.1)"] + +[[package]] +name = "jaraco-functools" +version = "4.4.0" +description = "Functools like those found in stdlib" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\"" +files = [ + {file = "jaraco_functools-4.4.0-py3-none-any.whl", hash = "sha256:9eec1e36f45c818d9bf307c8948eb03b2b56cd44087b3cdc989abca1f20b9176"}, + {file = "jaraco_functools-4.4.0.tar.gz", hash = "sha256:da21933b0417b89515562656547a77b4931f98176eb173644c0d35032a33d6bb"}, +] + +[package.dependencies] +more_itertools = "*" + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=3.4)"] +test = ["jaraco.classes", "pytest (>=6,!=8.1.*)"] +type = ["mypy (<1.19) ; platform_python_implementation == \"PyPy\"", "pytest-mypy (>=1.0.1)"] + +[[package]] +name = "jeepney" +version = "0.9.0" +description = "Low-level, pure Python DBus protocol wrapper." +optional = false +python-versions = ">=3.7" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\"" +files = [ + {file = "jeepney-0.9.0-py3-none-any.whl", hash = "sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683"}, + {file = "jeepney-0.9.0.tar.gz", hash = "sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732"}, +] + +[package.extras] +test = ["async-timeout ; python_version < \"3.11\"", "pytest", "pytest-asyncio (>=0.17)", "pytest-trio", "testpath", "trio"] +trio = ["trio"] + +[[package]] +name = "jinja2" +version = "3.1.6" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, + {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "keyring" +version = "25.7.0" +description = "Store and access your passwords safely." +optional = false +python-versions = ">=3.9" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\"" +files = [ + {file = "keyring-25.7.0-py3-none-any.whl", hash = "sha256:be4a0b195f149690c166e850609a477c532ddbfbaed96a404d4e43f8d5e2689f"}, + {file = "keyring-25.7.0.tar.gz", hash = "sha256:fe01bd85eb3f8fb3dd0405defdeac9a5b4f6f0439edbb3149577f244a2e8245b"}, +] + +[package.dependencies] +importlib_metadata = {version = ">=4.11.4", markers = "python_version < \"3.12\""} +"jaraco.classes" = "*" +"jaraco.context" = "*" +"jaraco.functools" = "*" +jeepney = {version = ">=0.4.2", markers = "sys_platform == \"linux\""} +pywin32-ctypes = {version = ">=0.2.0", markers = "sys_platform == \"win32\""} +SecretStorage = {version = ">=3.2", markers = "sys_platform == \"linux\""} + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +completion = ["shtab (>=1.1.0)"] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=3.4)"] +test = ["pyfakefs", "pytest (>=6,!=8.1.*)"] +type = ["pygobject-stubs", "pytest-mypy (>=1.0.1)", "shtab", "types-pywin32"] + +[[package]] +name = "librt" +version = "0.7.8" +description = "Mypyc runtime library" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +markers = "platform_python_implementation != \"PyPy\"" +files = [ + {file = "librt-0.7.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b45306a1fc5f53c9330fbee134d8b3227fe5da2ab09813b892790400aa49352d"}, + {file = "librt-0.7.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:864c4b7083eeee250ed55135d2127b260d7eb4b5e953a9e5df09c852e327961b"}, + {file = "librt-0.7.8-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6938cc2de153bc927ed8d71c7d2f2ae01b4e96359126c602721340eb7ce1a92d"}, + {file = "librt-0.7.8-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:66daa6ac5de4288a5bbfbe55b4caa7bf0cd26b3269c7a476ffe8ce45f837f87d"}, + {file = "librt-0.7.8-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4864045f49dc9c974dadb942ac56a74cd0479a2aafa51ce272c490a82322ea3c"}, + {file = "librt-0.7.8-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a36515b1328dc5b3ffce79fe204985ca8572525452eacabee2166f44bb387b2c"}, + {file = "librt-0.7.8-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b7e7f140c5169798f90b80d6e607ed2ba5059784968a004107c88ad61fb3641d"}, + {file = "librt-0.7.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ff71447cb778a4f772ddc4ce360e6ba9c95527ed84a52096bd1bbf9fee2ec7c0"}, + {file = "librt-0.7.8-cp310-cp310-win32.whl", hash = "sha256:047164e5f68b7a8ebdf9fae91a3c2161d3192418aadd61ddd3a86a56cbe3dc85"}, + {file = "librt-0.7.8-cp310-cp310-win_amd64.whl", hash = "sha256:d6f254d096d84156a46a84861183c183d30734e52383602443292644d895047c"}, + {file = "librt-0.7.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ff3e9c11aa260c31493d4b3197d1e28dd07768594a4f92bec4506849d736248f"}, + {file = "librt-0.7.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ddb52499d0b3ed4aa88746aaf6f36a08314677d5c346234c3987ddc506404eac"}, + {file = "librt-0.7.8-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e9c0afebbe6ce177ae8edba0c7c4d626f2a0fc12c33bb993d163817c41a7a05c"}, + {file = "librt-0.7.8-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:631599598e2c76ded400c0a8722dec09217c89ff64dc54b060f598ed68e7d2a8"}, + {file = "librt-0.7.8-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c1ba843ae20db09b9d5c80475376168feb2640ce91cd9906414f23cc267a1ff"}, + {file = "librt-0.7.8-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b5b007bb22ea4b255d3ee39dfd06d12534de2fcc3438567d9f48cdaf67ae1ae3"}, + {file = "librt-0.7.8-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:dbd79caaf77a3f590cbe32dc2447f718772d6eea59656a7dcb9311161b10fa75"}, + {file = "librt-0.7.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:87808a8d1e0bd62a01cafc41f0fd6818b5a5d0ca0d8a55326a81643cdda8f873"}, + {file = "librt-0.7.8-cp311-cp311-win32.whl", hash = "sha256:31724b93baa91512bd0a376e7cf0b59d8b631ee17923b1218a65456fa9bda2e7"}, + {file = "librt-0.7.8-cp311-cp311-win_amd64.whl", hash = "sha256:978e8b5f13e52cf23a9e80f3286d7546baa70bc4ef35b51d97a709d0b28e537c"}, + {file = "librt-0.7.8-cp311-cp311-win_arm64.whl", hash = "sha256:20e3946863d872f7cabf7f77c6c9d370b8b3d74333d3a32471c50d3a86c0a232"}, + {file = "librt-0.7.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9b6943885b2d49c48d0cff23b16be830ba46b0152d98f62de49e735c6e655a63"}, + {file = "librt-0.7.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:46ef1f4b9b6cc364b11eea0ecc0897314447a66029ee1e55859acb3dd8757c93"}, + {file = "librt-0.7.8-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:907ad09cfab21e3c86e8f1f87858f7049d1097f77196959c033612f532b4e592"}, + {file = "librt-0.7.8-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2991b6c3775383752b3ca0204842743256f3ad3deeb1d0adc227d56b78a9a850"}, + {file = "librt-0.7.8-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03679b9856932b8c8f674e87aa3c55ea11c9274301f76ae8dc4d281bda55cf62"}, + {file = "librt-0.7.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3968762fec1b2ad34ce57458b6de25dbb4142713e9ca6279a0d352fa4e9f452b"}, + {file = "librt-0.7.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:bb7a7807523a31f03061288cc4ffc065d684c39db7644c676b47d89553c0d714"}, + {file = "librt-0.7.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad64a14b1e56e702e19b24aae108f18ad1bf7777f3af5fcd39f87d0c5a814449"}, + {file = "librt-0.7.8-cp312-cp312-win32.whl", hash = "sha256:0241a6ed65e6666236ea78203a73d800dbed896cf12ae25d026d75dc1fcd1dac"}, + {file = "librt-0.7.8-cp312-cp312-win_amd64.whl", hash = "sha256:6db5faf064b5bab9675c32a873436b31e01d66ca6984c6f7f92621656033a708"}, + {file = "librt-0.7.8-cp312-cp312-win_arm64.whl", hash = "sha256:57175aa93f804d2c08d2edb7213e09276bd49097611aefc37e3fa38d1fb99ad0"}, + {file = "librt-0.7.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4c3995abbbb60b3c129490fa985dfe6cac11d88fc3c36eeb4fb1449efbbb04fc"}, + {file = "librt-0.7.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:44e0c2cbc9bebd074cf2cdbe472ca185e824be4e74b1c63a8e934cea674bebf2"}, + {file = "librt-0.7.8-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4d2f1e492cae964b3463a03dc77a7fe8742f7855d7258c7643f0ee32b6651dd3"}, + {file = "librt-0.7.8-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:451e7ffcef8f785831fdb791bd69211f47e95dc4c6ddff68e589058806f044c6"}, + {file = "librt-0.7.8-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3469e1af9f1380e093ae06bedcbdd11e407ac0b303a56bbe9afb1d6824d4982d"}, + {file = "librt-0.7.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f11b300027ce19a34f6d24ebb0a25fd0e24a9d53353225a5c1e6cadbf2916b2e"}, + {file = "librt-0.7.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4adc73614f0d3c97874f02f2c7fd2a27854e7e24ad532ea6b965459c5b757eca"}, + {file = "librt-0.7.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:60c299e555f87e4c01b2eca085dfccda1dde87f5a604bb45c2906b8305819a93"}, + {file = "librt-0.7.8-cp313-cp313-win32.whl", hash = "sha256:b09c52ed43a461994716082ee7d87618096851319bf695d57ec123f2ab708951"}, + {file = "librt-0.7.8-cp313-cp313-win_amd64.whl", hash = "sha256:f8f4a901a3fa28969d6e4519deceab56c55a09d691ea7b12ca830e2fa3461e34"}, + {file = "librt-0.7.8-cp313-cp313-win_arm64.whl", hash = "sha256:43d4e71b50763fcdcf64725ac680d8cfa1706c928b844794a7aa0fa9ac8e5f09"}, + {file = "librt-0.7.8-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:be927c3c94c74b05128089a955fba86501c3b544d1d300282cc1b4bd370cb418"}, + {file = "librt-0.7.8-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7b0803e9008c62a7ef79058233db7ff6f37a9933b8f2573c05b07ddafa226611"}, + {file = "librt-0.7.8-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:79feb4d00b2a4e0e05c9c56df707934f41fcb5fe53fd9efb7549068d0495b758"}, + {file = "librt-0.7.8-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b9122094e3f24aa759c38f46bd8863433820654927370250f460ae75488b66ea"}, + {file = "librt-0.7.8-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7e03bea66af33c95ce3addf87a9bf1fcad8d33e757bc479957ddbc0e4f7207ac"}, + {file = "librt-0.7.8-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f1ade7f31675db00b514b98f9ab9a7698c7282dad4be7492589109471852d398"}, + {file = "librt-0.7.8-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a14229ac62adcf1b90a15992f1ab9c69ae8b99ffb23cb64a90878a6e8a2f5b81"}, + {file = "librt-0.7.8-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5bcaaf624fd24e6a0cb14beac37677f90793a96864c67c064a91458611446e83"}, + {file = "librt-0.7.8-cp314-cp314-win32.whl", hash = "sha256:7aa7d5457b6c542ecaed79cec4ad98534373c9757383973e638ccced0f11f46d"}, + {file = "librt-0.7.8-cp314-cp314-win_amd64.whl", hash = "sha256:3d1322800771bee4a91f3b4bd4e49abc7d35e65166821086e5afd1e6c0d9be44"}, + {file = "librt-0.7.8-cp314-cp314-win_arm64.whl", hash = "sha256:5363427bc6a8c3b1719f8f3845ea53553d301382928a86e8fab7984426949bce"}, + {file = "librt-0.7.8-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:ca916919793a77e4a98d4a1701e345d337ce53be4a16620f063191f7322ac80f"}, + {file = "librt-0.7.8-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:54feb7b4f2f6706bb82325e836a01be805770443e2400f706e824e91f6441dde"}, + {file = "librt-0.7.8-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:39a4c76fee41007070f872b648cc2f711f9abf9a13d0c7162478043377b52c8e"}, + {file = "librt-0.7.8-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ac9c8a458245c7de80bc1b9765b177055efff5803f08e548dd4bb9ab9a8d789b"}, + {file = "librt-0.7.8-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:95b67aa7eff150f075fda09d11f6bfb26edffd300f6ab1666759547581e8f666"}, + {file = "librt-0.7.8-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:535929b6eff670c593c34ff435d5440c3096f20fa72d63444608a5aef64dd581"}, + {file = "librt-0.7.8-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:63937bd0f4d1cb56653dc7ae900d6c52c41f0015e25aaf9902481ee79943b33a"}, + {file = "librt-0.7.8-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cf243da9e42d914036fd362ac3fa77d80a41cadcd11ad789b1b5eec4daaf67ca"}, + {file = "librt-0.7.8-cp314-cp314t-win32.whl", hash = "sha256:171ca3a0a06c643bd0a2f62a8944e1902c94aa8e5da4db1ea9a8daf872685365"}, + {file = "librt-0.7.8-cp314-cp314t-win_amd64.whl", hash = "sha256:445b7304145e24c60288a2f172b5ce2ca35c0f81605f5299f3fa567e189d2e32"}, + {file = "librt-0.7.8-cp314-cp314t-win_arm64.whl", hash = "sha256:8766ece9de08527deabcd7cb1b4f1a967a385d26e33e536d6d8913db6ef74f06"}, + {file = "librt-0.7.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c7e8f88f79308d86d8f39c491773cbb533d6cb7fa6476f35d711076ee04fceb6"}, + {file = "librt-0.7.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:389bd25a0db916e1d6bcb014f11aa9676cedaa485e9ec3752dfe19f196fd377b"}, + {file = "librt-0.7.8-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:73fd300f501a052f2ba52ede721232212f3b06503fa12665408ecfc9d8fd149c"}, + {file = "librt-0.7.8-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d772edc6a5f7835635c7562f6688e031f0b97e31d538412a852c49c9a6c92d5"}, + {file = "librt-0.7.8-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bfde8a130bd0f239e45503ab39fab239ace094d63ee1d6b67c25a63d741c0f71"}, + {file = "librt-0.7.8-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fdec6e2368ae4f796fc72fad7fd4bd1753715187e6d870932b0904609e7c878e"}, + {file = "librt-0.7.8-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:00105e7d541a8f2ee5be52caacea98a005e0478cfe78c8080fbb7b5d2b340c63"}, + {file = "librt-0.7.8-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c6f8947d3dfd7f91066c5b4385812c18be26c9d5a99ca56667547f2c39149d94"}, + {file = "librt-0.7.8-cp39-cp39-win32.whl", hash = "sha256:41d7bb1e07916aeb12ae4a44e3025db3691c4149ab788d0315781b4d29b86afb"}, + {file = "librt-0.7.8-cp39-cp39-win_amd64.whl", hash = "sha256:e90a8e237753c83b8e484d478d9a996dc5e39fd5bd4c6ce32563bc8123f132be"}, + {file = "librt-0.7.8.tar.gz", hash = "sha256:1a4ede613941d9c3470b0368be851df6bb78ab218635512d0370b27a277a0862"}, +] + +[[package]] +name = "license-expression" +version = "30.4.4" +description = "license-expression is a comprehensive utility library to parse, compare, simplify and normalize license expressions (such as SPDX license expressions) using boolean logic." +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "license_expression-30.4.4-py3-none-any.whl", hash = "sha256:421788fdcadb41f049d2dc934ce666626265aeccefddd25e162a26f23bcbf8a4"}, + {file = "license_expression-30.4.4.tar.gz", hash = "sha256:73448f0aacd8d0808895bdc4b2c8e01a8d67646e4188f887375398c761f340fd"}, +] + +[package.dependencies] +"boolean.py" = ">=4.0" + +[package.extras] +dev = ["Sphinx (>=5.0.2)", "doc8 (>=0.11.2)", "pytest (>=7.0.1)", "pytest-xdist (>=2)", "ruff", "sphinx-autobuild", "sphinx-copybutton", "sphinx-reredirects (>=0.1.2)", "sphinx-rtd-dark-mode (>=1.3.0)", "sphinx-rtd-theme (>=1.0.0)", "sphinxcontrib-apidoc (>=0.4.0)", "twine"] + +[[package]] +name = "markdown-it-py" +version = "4.0.0" +description = "Python port of markdown-it. Markdown parsing, done right!" +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147"}, + {file = "markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3"}, +] + +[package.dependencies] +mdurl = ">=0.1,<1.0" + +[package.extras] +benchmarking = ["psutil", "pytest", "pytest-benchmark"] +compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "markdown-it-pyrs", "mistletoe (>=1.0,<2.0)", "mistune (>=3.0,<4.0)", "panflute (>=2.3,<3.0)"] +linkify = ["linkify-it-py (>=1,<3)"] +plugins = ["mdit-py-plugins (>=0.5.0)"] +profiling = ["gprof2dot"] +rtd = ["ipykernel", "jupyter_sphinx", "mdit-py-plugins (>=0.5.0)", "myst-parser", "pyyaml", "sphinx", "sphinx-book-theme (>=1.0,<2.0)", "sphinx-copybutton", "sphinx-design"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions", "requests"] + +[[package]] +name = "markupsafe" +version = "3.0.3" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559"}, + {file = "markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1"}, + {file = "markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa"}, + {file = "markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8"}, + {file = "markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1"}, + {file = "markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad"}, + {file = "markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a"}, + {file = "markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19"}, + {file = "markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01"}, + {file = "markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c"}, + {file = "markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e"}, + {file = "markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b"}, + {file = "markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d"}, + {file = "markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c"}, + {file = "markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f"}, + {file = "markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795"}, + {file = "markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12"}, + {file = "markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed"}, + {file = "markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5"}, + {file = "markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485"}, + {file = "markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73"}, + {file = "markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287"}, + {file = "markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe"}, + {file = "markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe"}, + {file = "markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9"}, + {file = "markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581"}, + {file = "markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4"}, + {file = "markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab"}, + {file = "markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa"}, + {file = "markupsafe-3.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26"}, + {file = "markupsafe-3.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8fc20152abba6b83724d7ff268c249fa196d8259ff481f3b1476383f8f24e42"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:949b8d66bc381ee8b007cd945914c721d9aba8e27f71959d750a46f7c282b20b"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:3537e01efc9d4dccdf77221fb1cb3b8e1a38d5428920e0657ce299b20324d758"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:591ae9f2a647529ca990bc681daebdd52c8791ff06c2bfa05b65163e28102ef2"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a320721ab5a1aba0a233739394eb907f8c8da5c98c9181d1161e77a0c8e36f2d"}, + {file = "markupsafe-3.0.3-cp39-cp39-win32.whl", hash = "sha256:df2449253ef108a379b8b5d6b43f4b1a8e81a061d6537becd5582fba5f9196d7"}, + {file = "markupsafe-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:7c3fb7d25180895632e5d3148dbdc29ea38ccb7fd210aa27acbd1201a1902c6e"}, + {file = "markupsafe-3.0.3-cp39-cp39-win_arm64.whl", hash = "sha256:38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8"}, + {file = "markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698"}, +] + +[[package]] +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" +optional = false +python-versions = ">=3.6" +groups = ["dev"] +files = [ + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +description = "Markdown URL utilities" +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, + {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, +] + +[[package]] +name = "more-itertools" +version = "10.8.0" +description = "More routines for operating on iterables, beyond itertools" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\"" +files = [ + {file = "more_itertools-10.8.0-py3-none-any.whl", hash = "sha256:52d4362373dcf7c52546bc4af9a86ee7c4579df9a8dc268be0a2f949d376cc9b"}, + {file = "more_itertools-10.8.0.tar.gz", hash = "sha256:f638ddf8a1a0d134181275fb5d58b086ead7c6a72429ad725c67503f13ba30bd"}, +] + +[[package]] +name = "msgpack" +version = "1.1.2" +description = "MessagePack serializer" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "msgpack-1.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0051fffef5a37ca2cd16978ae4f0aef92f164df86823871b5162812bebecd8e2"}, + {file = "msgpack-1.1.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a605409040f2da88676e9c9e5853b3449ba8011973616189ea5ee55ddbc5bc87"}, + {file = "msgpack-1.1.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b696e83c9f1532b4af884045ba7f3aa741a63b2bc22617293a2c6a7c645f251"}, + {file = "msgpack-1.1.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:365c0bbe981a27d8932da71af63ef86acc59ed5c01ad929e09a0b88c6294e28a"}, + {file = "msgpack-1.1.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:41d1a5d875680166d3ac5c38573896453bbbea7092936d2e107214daf43b1d4f"}, + {file = "msgpack-1.1.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:354e81bcdebaab427c3df4281187edc765d5d76bfb3a7c125af9da7a27e8458f"}, + {file = "msgpack-1.1.2-cp310-cp310-win32.whl", hash = "sha256:e64c8d2f5e5d5fda7b842f55dec6133260ea8f53c4257d64494c534f306bf7a9"}, + {file = "msgpack-1.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:db6192777d943bdaaafb6ba66d44bf65aa0e9c5616fa1d2da9bb08828c6b39aa"}, + {file = "msgpack-1.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2e86a607e558d22985d856948c12a3fa7b42efad264dca8a3ebbcfa2735d786c"}, + {file = "msgpack-1.1.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:283ae72fc89da59aa004ba147e8fc2f766647b1251500182fac0350d8af299c0"}, + {file = "msgpack-1.1.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:61c8aa3bd513d87c72ed0b37b53dd5c5a0f58f2ff9f26e1555d3bd7948fb7296"}, + {file = "msgpack-1.1.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:454e29e186285d2ebe65be34629fa0e8605202c60fbc7c4c650ccd41870896ef"}, + {file = "msgpack-1.1.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7bc8813f88417599564fafa59fd6f95be417179f76b40325b500b3c98409757c"}, + {file = "msgpack-1.1.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bafca952dc13907bdfdedfc6a5f579bf4f292bdd506fadb38389afa3ac5b208e"}, + {file = "msgpack-1.1.2-cp311-cp311-win32.whl", hash = "sha256:602b6740e95ffc55bfb078172d279de3773d7b7db1f703b2f1323566b878b90e"}, + {file = "msgpack-1.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:d198d275222dc54244bf3327eb8cbe00307d220241d9cec4d306d49a44e85f68"}, + {file = "msgpack-1.1.2-cp311-cp311-win_arm64.whl", hash = "sha256:86f8136dfa5c116365a8a651a7d7484b65b13339731dd6faebb9a0242151c406"}, + {file = "msgpack-1.1.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:70a0dff9d1f8da25179ffcf880e10cf1aad55fdb63cd59c9a49a1b82290062aa"}, + {file = "msgpack-1.1.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:446abdd8b94b55c800ac34b102dffd2f6aa0ce643c55dfc017ad89347db3dbdb"}, + {file = "msgpack-1.1.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c63eea553c69ab05b6747901b97d620bb2a690633c77f23feb0c6a947a8a7b8f"}, + {file = "msgpack-1.1.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:372839311ccf6bdaf39b00b61288e0557916c3729529b301c52c2d88842add42"}, + {file = "msgpack-1.1.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2929af52106ca73fcb28576218476ffbb531a036c2adbcf54a3664de124303e9"}, + {file = "msgpack-1.1.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:be52a8fc79e45b0364210eef5234a7cf8d330836d0a64dfbb878efa903d84620"}, + {file = "msgpack-1.1.2-cp312-cp312-win32.whl", hash = "sha256:1fff3d825d7859ac888b0fbda39a42d59193543920eda9d9bea44d958a878029"}, + {file = "msgpack-1.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:1de460f0403172cff81169a30b9a92b260cb809c4cb7e2fc79ae8d0510c78b6b"}, + {file = "msgpack-1.1.2-cp312-cp312-win_arm64.whl", hash = "sha256:be5980f3ee0e6bd44f3a9e9dea01054f175b50c3e6cdb692bc9424c0bbb8bf69"}, + {file = "msgpack-1.1.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4efd7b5979ccb539c221a4c4e16aac1a533efc97f3b759bb5a5ac9f6d10383bf"}, + {file = "msgpack-1.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:42eefe2c3e2af97ed470eec850facbe1b5ad1d6eacdbadc42ec98e7dcf68b4b7"}, + {file = "msgpack-1.1.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1fdf7d83102bf09e7ce3357de96c59b627395352a4024f6e2458501f158bf999"}, + {file = "msgpack-1.1.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fac4be746328f90caa3cd4bc67e6fe36ca2bf61d5c6eb6d895b6527e3f05071e"}, + {file = "msgpack-1.1.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:fffee09044073e69f2bad787071aeec727183e7580443dfeb8556cbf1978d162"}, + {file = "msgpack-1.1.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5928604de9b032bc17f5099496417f113c45bc6bc21b5c6920caf34b3c428794"}, + {file = "msgpack-1.1.2-cp313-cp313-win32.whl", hash = "sha256:a7787d353595c7c7e145e2331abf8b7ff1e6673a6b974ded96e6d4ec09f00c8c"}, + {file = "msgpack-1.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:a465f0dceb8e13a487e54c07d04ae3ba131c7c5b95e2612596eafde1dccf64a9"}, + {file = "msgpack-1.1.2-cp313-cp313-win_arm64.whl", hash = "sha256:e69b39f8c0aa5ec24b57737ebee40be647035158f14ed4b40e6f150077e21a84"}, + {file = "msgpack-1.1.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e23ce8d5f7aa6ea6d2a2b326b4ba46c985dbb204523759984430db7114f8aa00"}, + {file = "msgpack-1.1.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:6c15b7d74c939ebe620dd8e559384be806204d73b4f9356320632d783d1f7939"}, + {file = "msgpack-1.1.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:99e2cb7b9031568a2a5c73aa077180f93dd2e95b4f8d3b8e14a73ae94a9e667e"}, + {file = "msgpack-1.1.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:180759d89a057eab503cf62eeec0aa61c4ea1200dee709f3a8e9397dbb3b6931"}, + {file = "msgpack-1.1.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:04fb995247a6e83830b62f0b07bf36540c213f6eac8e851166d8d86d83cbd014"}, + {file = "msgpack-1.1.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8e22ab046fa7ede9e36eeb4cfad44d46450f37bb05d5ec482b02868f451c95e2"}, + {file = "msgpack-1.1.2-cp314-cp314-win32.whl", hash = "sha256:80a0ff7d4abf5fecb995fcf235d4064b9a9a8a40a3ab80999e6ac1e30b702717"}, + {file = "msgpack-1.1.2-cp314-cp314-win_amd64.whl", hash = "sha256:9ade919fac6a3e7260b7f64cea89df6bec59104987cbea34d34a2fa15d74310b"}, + {file = "msgpack-1.1.2-cp314-cp314-win_arm64.whl", hash = "sha256:59415c6076b1e30e563eb732e23b994a61c159cec44deaf584e5cc1dd662f2af"}, + {file = "msgpack-1.1.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:897c478140877e5307760b0ea66e0932738879e7aa68144d9b78ea4c8302a84a"}, + {file = "msgpack-1.1.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a668204fa43e6d02f89dbe79a30b0d67238d9ec4c5bd8a940fc3a004a47b721b"}, + {file = "msgpack-1.1.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5559d03930d3aa0f3aacb4c42c776af1a2ace2611871c84a75afe436695e6245"}, + {file = "msgpack-1.1.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:70c5a7a9fea7f036b716191c29047374c10721c389c21e9ffafad04df8c52c90"}, + {file = "msgpack-1.1.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:f2cb069d8b981abc72b41aea1c580ce92d57c673ec61af4c500153a626cb9e20"}, + {file = "msgpack-1.1.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d62ce1f483f355f61adb5433ebfd8868c5f078d1a52d042b0a998682b4fa8c27"}, + {file = "msgpack-1.1.2-cp314-cp314t-win32.whl", hash = "sha256:1d1418482b1ee984625d88aa9585db570180c286d942da463533b238b98b812b"}, + {file = "msgpack-1.1.2-cp314-cp314t-win_amd64.whl", hash = "sha256:5a46bf7e831d09470ad92dff02b8b1ac92175ca36b087f904a0519857c6be3ff"}, + {file = "msgpack-1.1.2-cp314-cp314t-win_arm64.whl", hash = "sha256:d99ef64f349d5ec3293688e91486c5fdb925ed03807f64d98d205d2713c60b46"}, + {file = "msgpack-1.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ea5405c46e690122a76531ab97a079e184c0daf491e588592d6a23d3e32af99e"}, + {file = "msgpack-1.1.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9fba231af7a933400238cb357ecccf8ab5d51535ea95d94fc35b7806218ff844"}, + {file = "msgpack-1.1.2-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a8f6e7d30253714751aa0b0c84ae28948e852ee7fb0524082e6716769124bc23"}, + {file = "msgpack-1.1.2-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:94fd7dc7d8cb0a54432f296f2246bc39474e017204ca6f4ff345941d4ed285a7"}, + {file = "msgpack-1.1.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:350ad5353a467d9e3b126d8d1b90fe05ad081e2e1cef5753f8c345217c37e7b8"}, + {file = "msgpack-1.1.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6bde749afe671dc44893f8d08e83bf475a1a14570d67c4bb5cec5573463c8833"}, + {file = "msgpack-1.1.2-cp39-cp39-win32.whl", hash = "sha256:ad09b984828d6b7bb52d1d1d0c9be68ad781fa004ca39216c8a1e63c0f34ba3c"}, + {file = "msgpack-1.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:67016ae8c8965124fdede9d3769528ad8284f14d635337ffa6a713a580f6c030"}, + {file = "msgpack-1.1.2.tar.gz", hash = "sha256:3b60763c1373dd60f398488069bcdc703cd08a711477b5d480eecc9f9626f47e"}, +] + +[[package]] +name = "mypy" +version = "1.19.1" +description = "Optional static typing for Python" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "mypy-1.19.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f05aa3d375b385734388e844bc01733bd33c644ab48e9684faa54e5389775ec"}, + {file = "mypy-1.19.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:022ea7279374af1a5d78dfcab853fe6a536eebfda4b59deab53cd21f6cd9f00b"}, + {file = "mypy-1.19.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee4c11e460685c3e0c64a4c5de82ae143622410950d6be863303a1c4ba0e36d6"}, + {file = "mypy-1.19.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de759aafbae8763283b2ee5869c7255391fbc4de3ff171f8f030b5ec48381b74"}, + {file = "mypy-1.19.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ab43590f9cd5108f41aacf9fca31841142c786827a74ab7cc8a2eacb634e09a1"}, + {file = "mypy-1.19.1-cp310-cp310-win_amd64.whl", hash = "sha256:2899753e2f61e571b3971747e302d5f420c3fd09650e1951e99f823bc3089dac"}, + {file = "mypy-1.19.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d8dfc6ab58ca7dda47d9237349157500468e404b17213d44fc1cb77bce532288"}, + {file = "mypy-1.19.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e3f276d8493c3c97930e354b2595a44a21348b320d859fb4a2b9f66da9ed27ab"}, + {file = "mypy-1.19.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2abb24cf3f17864770d18d673c85235ba52456b36a06b6afc1e07c1fdcd3d0e6"}, + {file = "mypy-1.19.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a009ffa5a621762d0c926a078c2d639104becab69e79538a494bcccb62cc0331"}, + {file = "mypy-1.19.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f7cee03c9a2e2ee26ec07479f38ea9c884e301d42c6d43a19d20fb014e3ba925"}, + {file = "mypy-1.19.1-cp311-cp311-win_amd64.whl", hash = "sha256:4b84a7a18f41e167f7995200a1d07a4a6810e89d29859df936f1c3923d263042"}, + {file = "mypy-1.19.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a8174a03289288c1f6c46d55cef02379b478bfbc8e358e02047487cad44c6ca1"}, + {file = "mypy-1.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffcebe56eb09ff0c0885e750036a095e23793ba6c2e894e7e63f6d89ad51f22e"}, + {file = "mypy-1.19.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b64d987153888790bcdb03a6473d321820597ab8dd9243b27a92153c4fa50fd2"}, + {file = "mypy-1.19.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c35d298c2c4bba75feb2195655dfea8124d855dfd7343bf8b8c055421eaf0cf8"}, + {file = "mypy-1.19.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:34c81968774648ab5ac09c29a375fdede03ba253f8f8287847bd480782f73a6a"}, + {file = "mypy-1.19.1-cp312-cp312-win_amd64.whl", hash = "sha256:b10e7c2cd7870ba4ad9b2d8a6102eb5ffc1f16ca35e3de6bfa390c1113029d13"}, + {file = "mypy-1.19.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e3157c7594ff2ef1634ee058aafc56a82db665c9438fd41b390f3bde1ab12250"}, + {file = "mypy-1.19.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdb12f69bcc02700c2b47e070238f42cb87f18c0bc1fc4cdb4fb2bc5fd7a3b8b"}, + {file = "mypy-1.19.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f859fb09d9583a985be9a493d5cfc5515b56b08f7447759a0c5deaf68d80506e"}, + {file = "mypy-1.19.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9a6538e0415310aad77cb94004ca6482330fece18036b5f360b62c45814c4ef"}, + {file = "mypy-1.19.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:da4869fc5e7f62a88f3fe0b5c919d1d9f7ea3cef92d3689de2823fd27e40aa75"}, + {file = "mypy-1.19.1-cp313-cp313-win_amd64.whl", hash = "sha256:016f2246209095e8eda7538944daa1d60e1e8134d98983b9fc1e92c1fc0cb8dd"}, + {file = "mypy-1.19.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06e6170bd5836770e8104c8fdd58e5e725cfeb309f0a6c681a811f557e97eac1"}, + {file = "mypy-1.19.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:804bd67b8054a85447c8954215a906d6eff9cabeabe493fb6334b24f4bfff718"}, + {file = "mypy-1.19.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21761006a7f497cb0d4de3d8ef4ca70532256688b0523eee02baf9eec895e27b"}, + {file = "mypy-1.19.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:28902ee51f12e0f19e1e16fbe2f8f06b6637f482c459dd393efddd0ec7f82045"}, + {file = "mypy-1.19.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:481daf36a4c443332e2ae9c137dfee878fcea781a2e3f895d54bd3002a900957"}, + {file = "mypy-1.19.1-cp314-cp314-win_amd64.whl", hash = "sha256:8bb5c6f6d043655e055be9b542aa5f3bdd30e4f3589163e85f93f3640060509f"}, + {file = "mypy-1.19.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7bcfc336a03a1aaa26dfce9fff3e287a3ba99872a157561cbfcebe67c13308e3"}, + {file = "mypy-1.19.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b7951a701c07ea584c4fe327834b92a30825514c868b1f69c30445093fdd9d5a"}, + {file = "mypy-1.19.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b13cfdd6c87fc3efb69ea4ec18ef79c74c3f98b4e5498ca9b85ab3b2c2329a67"}, + {file = "mypy-1.19.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f28f99c824ecebcdaa2e55d82953e38ff60ee5ec938476796636b86afa3956e"}, + {file = "mypy-1.19.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c608937067d2fc5a4dd1a5ce92fd9e1398691b8c5d012d66e1ddd430e9244376"}, + {file = "mypy-1.19.1-cp39-cp39-win_amd64.whl", hash = "sha256:409088884802d511ee52ca067707b90c883426bd95514e8cfda8281dc2effe24"}, + {file = "mypy-1.19.1-py3-none-any.whl", hash = "sha256:f1235f5ea01b7db5468d53ece6aaddf1ad0b88d9e7462b86ef96fe04995d7247"}, + {file = "mypy-1.19.1.tar.gz", hash = "sha256:19d88bb05303fe63f71dd2c6270daca27cb9401c4ca8255fe50d1d920e0eb9ba"}, +] + +[package.dependencies] +librt = {version = ">=0.6.2", markers = "platform_python_implementation != \"PyPy\""} +mypy_extensions = ">=1.0.0" +pathspec = ">=0.9.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing_extensions = ">=4.6.0" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +faster-cache = ["orjson"] +install-types = ["pip"] +mypyc = ["setuptools (>=50)"] +reports = ["lxml"] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, + {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, +] + +[[package]] +name = "nh3" +version = "0.3.2" +description = "Python binding to Ammonia HTML sanitizer Rust crate" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "nh3-0.3.2-cp314-cp314t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:d18957a90806d943d141cc5e4a0fefa1d77cf0d7a156878bf9a66eed52c9cc7d"}, + {file = "nh3-0.3.2-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45c953e57028c31d473d6b648552d9cab1efe20a42ad139d78e11d8f42a36130"}, + {file = "nh3-0.3.2-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2c9850041b77a9147d6bbd6dbbf13eeec7009eb60b44e83f07fcb2910075bf9b"}, + {file = "nh3-0.3.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:403c11563e50b915d0efdb622866d1d9e4506bce590ef7da57789bf71dd148b5"}, + {file = "nh3-0.3.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:0dca4365db62b2d71ff1620ee4f800c4729849906c5dd504ee1a7b2389558e31"}, + {file = "nh3-0.3.2-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:0fe7ee035dd7b2290715baf29cb27167dddd2ff70ea7d052c958dbd80d323c99"}, + {file = "nh3-0.3.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a40202fd58e49129764f025bbaae77028e420f1d5b3c8e6f6fd3a6490d513868"}, + {file = "nh3-0.3.2-cp314-cp314t-win32.whl", hash = "sha256:1f9ba555a797dbdcd844b89523f29cdc90973d8bd2e836ea6b962cf567cadd93"}, + {file = "nh3-0.3.2-cp314-cp314t-win_amd64.whl", hash = "sha256:dce4248edc427c9b79261f3e6e2b3ecbdd9b88c267012168b4a7b3fc6fd41d13"}, + {file = "nh3-0.3.2-cp314-cp314t-win_arm64.whl", hash = "sha256:019ecbd007536b67fdf76fab411b648fb64e2257ca3262ec80c3425c24028c80"}, + {file = "nh3-0.3.2-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:7064ccf5ace75825bd7bf57859daaaf16ed28660c1c6b306b649a9eda4b54b1e"}, + {file = "nh3-0.3.2-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c8745454cdd28bbbc90861b80a0111a195b0e3961b9fa2e672be89eb199fa5d8"}, + {file = "nh3-0.3.2-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72d67c25a84579f4a432c065e8b4274e53b7cf1df8f792cf846abfe2c3090866"}, + {file = "nh3-0.3.2-cp38-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:13398e676a14d6233f372c75f52d5ae74f98210172991f7a3142a736bd92b131"}, + {file = "nh3-0.3.2-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:03d617e5c8aa7331bd2659c654e021caf9bba704b109e7b2b28b039a00949fe5"}, + {file = "nh3-0.3.2-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2f55c4d2d5a207e74eefe4d828067bbb01300e06e2a7436142f915c5928de07"}, + {file = "nh3-0.3.2-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bb18403f02b655a1bbe4e3a4696c2ae1d6ae8f5991f7cacb684b1ae27e6c9f7"}, + {file = "nh3-0.3.2-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6d66f41672eb4060cf87c037f760bdbc6847852ca9ef8e9c5a5da18f090abf87"}, + {file = "nh3-0.3.2-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:f97f8b25cb2681d25e2338148159447e4d689aafdccfcf19e61ff7db3905768a"}, + {file = "nh3-0.3.2-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:2ab70e8c6c7d2ce953d2a58102eefa90c2d0a5ed7aa40c7e29a487bc5e613131"}, + {file = "nh3-0.3.2-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:1710f3901cd6440ca92494ba2eb6dc260f829fa8d9196b659fa10de825610ce0"}, + {file = "nh3-0.3.2-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:91e9b001101fb4500a2aafe3e7c92928d85242d38bf5ac0aba0b7480da0a4cd6"}, + {file = "nh3-0.3.2-cp38-abi3-win32.whl", hash = "sha256:169db03df90da63286e0560ea0efa9b6f3b59844a9735514a1d47e6bb2c8c61b"}, + {file = "nh3-0.3.2-cp38-abi3-win_amd64.whl", hash = "sha256:562da3dca7a17f9077593214a9781a94b8d76de4f158f8c895e62f09573945fe"}, + {file = "nh3-0.3.2-cp38-abi3-win_arm64.whl", hash = "sha256:cf5964d54edd405e68583114a7cba929468bcd7db5e676ae38ee954de1cfc104"}, + {file = "nh3-0.3.2.tar.gz", hash = "sha256:f394759a06df8b685a4ebfb1874fb67a9cbfd58c64fc5ed587a663c0e63ec376"}, +] + +[[package]] +name = "packageurl-python" +version = "0.17.6" +description = "A purl aka. Package URL parser and builder" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "packageurl_python-0.17.6-py3-none-any.whl", hash = "sha256:31a85c2717bc41dd818f3c62908685ff9eebcb68588213745b14a6ee9e7df7c9"}, + {file = "packageurl_python-0.17.6.tar.gz", hash = "sha256:1252ce3a102372ca6f86eb968e16f9014c4ba511c5c37d95a7f023e2ca6e5c25"}, +] + +[package.extras] +build = ["setuptools", "wheel"] +lint = ["black", "isort", "mypy"] +sqlalchemy = ["sqlalchemy (>=2.0.0)"] +test = ["pytest"] + +[[package]] +name = "packaging" +version = "26.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529"}, + {file = "packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4"}, +] + +[[package]] +name = "pathspec" +version = "1.0.4" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723"}, + {file = "pathspec-1.0.4.tar.gz", hash = "sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645"}, +] + +[package.extras] +hyperscan = ["hyperscan (>=0.7)"] +optional = ["typing-extensions (>=4)"] +re2 = ["google-re2 (>=1.1)"] +tests = ["pytest (>=9)", "typing-extensions (>=4.15)"] + +[[package]] +name = "pillow" +version = "12.1.1" +description = "Python Imaging Library (fork)" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "pillow-12.1.1-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1f1625b72740fdda5d77b4def688eb8fd6490975d06b909fd19f13f391e077e0"}, + {file = "pillow-12.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:178aa072084bd88ec759052feca8e56cbb14a60b39322b99a049e58090479713"}, + {file = "pillow-12.1.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b66e95d05ba806247aaa1561f080abc7975daf715c30780ff92a20e4ec546e1b"}, + {file = "pillow-12.1.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:89c7e895002bbe49cdc5426150377cbbc04767d7547ed145473f496dfa40408b"}, + {file = "pillow-12.1.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a5cbdcddad0af3da87cb16b60d23648bc3b51967eb07223e9fed77a82b457c4"}, + {file = "pillow-12.1.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9f51079765661884a486727f0729d29054242f74b46186026582b4e4769918e4"}, + {file = "pillow-12.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:99c1506ea77c11531d75e3a412832a13a71c7ebc8192ab9e4b2e355555920e3e"}, + {file = "pillow-12.1.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:36341d06738a9f66c8287cf8b876d24b18db9bd8740fa0672c74e259ad408cff"}, + {file = "pillow-12.1.1-cp310-cp310-win32.whl", hash = "sha256:6c52f062424c523d6c4db85518774cc3d50f5539dd6eed32b8f6229b26f24d40"}, + {file = "pillow-12.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:c6008de247150668a705a6338156efb92334113421ceecf7438a12c9a12dab23"}, + {file = "pillow-12.1.1-cp310-cp310-win_arm64.whl", hash = "sha256:1a9b0ee305220b392e1124a764ee4265bd063e54a751a6b62eff69992f457fa9"}, + {file = "pillow-12.1.1-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:e879bb6cd5c73848ef3b2b48b8af9ff08c5b71ecda8048b7dd22d8a33f60be32"}, + {file = "pillow-12.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:365b10bb9417dd4498c0e3b128018c4a624dc11c7b97d8cc54effe3b096f4c38"}, + {file = "pillow-12.1.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d4ce8e329c93845720cd2014659ca67eac35f6433fd3050393d85f3ecef0dad5"}, + {file = "pillow-12.1.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc354a04072b765eccf2204f588a7a532c9511e8b9c7f900e1b64e3e33487090"}, + {file = "pillow-12.1.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7e7976bf1910a8116b523b9f9f58bf410f3e8aa330cd9a2bb2953f9266ab49af"}, + {file = "pillow-12.1.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:597bd9c8419bc7c6af5604e55847789b69123bbe25d65cc6ad3012b4f3c98d8b"}, + {file = "pillow-12.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2c1fc0f2ca5f96a3c8407e41cca26a16e46b21060fe6d5b099d2cb01412222f5"}, + {file = "pillow-12.1.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:578510d88c6229d735855e1f278aa305270438d36a05031dfaae5067cc8eb04d"}, + {file = "pillow-12.1.1-cp311-cp311-win32.whl", hash = "sha256:7311c0a0dcadb89b36b7025dfd8326ecfa36964e29913074d47382706e516a7c"}, + {file = "pillow-12.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:fbfa2a7c10cc2623f412753cddf391c7f971c52ca40a3f65dc5039b2939e8563"}, + {file = "pillow-12.1.1-cp311-cp311-win_arm64.whl", hash = "sha256:b81b5e3511211631b3f672a595e3221252c90af017e399056d0faabb9538aa80"}, + {file = "pillow-12.1.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ab323b787d6e18b3d91a72fc99b1a2c28651e4358749842b8f8dfacd28ef2052"}, + {file = "pillow-12.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:adebb5bee0f0af4909c30db0d890c773d1a92ffe83da908e2e9e720f8edf3984"}, + {file = "pillow-12.1.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bb66b7cc26f50977108790e2456b7921e773f23db5630261102233eb355a3b79"}, + {file = "pillow-12.1.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:aee2810642b2898bb187ced9b349e95d2a7272930796e022efaf12e99dccd293"}, + {file = "pillow-12.1.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a0b1cd6232e2b618adcc54d9882e4e662a089d5768cd188f7c245b4c8c44a397"}, + {file = "pillow-12.1.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7aac39bcf8d4770d089588a2e1dd111cbaa42df5a94be3114222057d68336bd0"}, + {file = "pillow-12.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ab174cd7d29a62dd139c44bf74b698039328f45cb03b4596c43473a46656b2f3"}, + {file = "pillow-12.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:339ffdcb7cbeaa08221cd401d517d4b1fe7a9ed5d400e4a8039719238620ca35"}, + {file = "pillow-12.1.1-cp312-cp312-win32.whl", hash = "sha256:5d1f9575a12bed9e9eedd9a4972834b08c97a352bd17955ccdebfeca5913fa0a"}, + {file = "pillow-12.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:21329ec8c96c6e979cd0dfd29406c40c1d52521a90544463057d2aaa937d66a6"}, + {file = "pillow-12.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:af9a332e572978f0218686636610555ae3defd1633597be015ed50289a03c523"}, + {file = "pillow-12.1.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:d242e8ac078781f1de88bf823d70c1a9b3c7950a44cdf4b7c012e22ccbcd8e4e"}, + {file = "pillow-12.1.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:02f84dfad02693676692746df05b89cf25597560db2857363a208e393429f5e9"}, + {file = "pillow-12.1.1-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:e65498daf4b583091ccbb2556c7000abf0f3349fcd57ef7adc9a84a394ed29f6"}, + {file = "pillow-12.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6c6db3b84c87d48d0088943bf33440e0c42370b99b1c2a7989216f7b42eede60"}, + {file = "pillow-12.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8b7e5304e34942bf62e15184219a7b5ad4ff7f3bb5cca4d984f37df1a0e1aee2"}, + {file = "pillow-12.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:18e5bddd742a44b7e6b1e773ab5db102bd7a94c32555ba656e76d319d19c3850"}, + {file = "pillow-12.1.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc44ef1f3de4f45b50ccf9136999d71abb99dca7706bc75d222ed350b9fd2289"}, + {file = "pillow-12.1.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5a8eb7ed8d4198bccbd07058416eeec51686b498e784eda166395a23eb99138e"}, + {file = "pillow-12.1.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:47b94983da0c642de92ced1702c5b6c292a84bd3a8e1d1702ff923f183594717"}, + {file = "pillow-12.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:518a48c2aab7ce596d3bf79d0e275661b846e86e4d0e7dec34712c30fe07f02a"}, + {file = "pillow-12.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a550ae29b95c6dc13cf69e2c9dc5747f814c54eeb2e32d683e5e93af56caa029"}, + {file = "pillow-12.1.1-cp313-cp313-win32.whl", hash = "sha256:a003d7422449f6d1e3a34e3dd4110c22148336918ddbfc6a32581cd54b2e0b2b"}, + {file = "pillow-12.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:344cf1e3dab3be4b1fa08e449323d98a2a3f819ad20f4b22e77a0ede31f0faa1"}, + {file = "pillow-12.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:5c0dd1636633e7e6a0afe7bf6a51a14992b7f8e60de5789018ebbdfae55b040a"}, + {file = "pillow-12.1.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0330d233c1a0ead844fc097a7d16c0abff4c12e856c0b325f231820fee1f39da"}, + {file = "pillow-12.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5dae5f21afb91322f2ff791895ddd8889e5e947ff59f71b46041c8ce6db790bc"}, + {file = "pillow-12.1.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2e0c664be47252947d870ac0d327fea7e63985a08794758aa8af5b6cb6ec0c9c"}, + {file = "pillow-12.1.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:691ab2ac363b8217f7d31b3497108fb1f50faab2f75dfb03284ec2f217e87bf8"}, + {file = "pillow-12.1.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e9e8064fb1cc019296958595f6db671fba95209e3ceb0c4734c9baf97de04b20"}, + {file = "pillow-12.1.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:472a8d7ded663e6162dafdf20015c486a7009483ca671cece7a9279b512fcb13"}, + {file = "pillow-12.1.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:89b54027a766529136a06cfebeecb3a04900397a3590fd252160b888479517bf"}, + {file = "pillow-12.1.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:86172b0831b82ce4f7877f280055892b31179e1576aa00d0df3bb1bbf8c3e524"}, + {file = "pillow-12.1.1-cp313-cp313t-win32.whl", hash = "sha256:44ce27545b6efcf0fdbdceb31c9a5bdea9333e664cda58a7e674bb74608b3986"}, + {file = "pillow-12.1.1-cp313-cp313t-win_amd64.whl", hash = "sha256:a285e3eb7a5a45a2ff504e31f4a8d1b12ef62e84e5411c6804a42197c1cf586c"}, + {file = "pillow-12.1.1-cp313-cp313t-win_arm64.whl", hash = "sha256:cc7d296b5ea4d29e6570dabeaed58d31c3fea35a633a69679fb03d7664f43fb3"}, + {file = "pillow-12.1.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:417423db963cb4be8bac3fc1204fe61610f6abeed1580a7a2cbb2fbda20f12af"}, + {file = "pillow-12.1.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:b957b71c6b2387610f556a7eb0828afbe40b4a98036fc0d2acfa5a44a0c2036f"}, + {file = "pillow-12.1.1-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:097690ba1f2efdeb165a20469d59d8bb03c55fb6621eb2041a060ae8ea3e9642"}, + {file = "pillow-12.1.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2815a87ab27848db0321fb78c7f0b2c8649dee134b7f2b80c6a45c6831d75ccd"}, + {file = "pillow-12.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f7ed2c6543bad5a7d5530eb9e78c53132f93dfa44a28492db88b41cdab885202"}, + {file = "pillow-12.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:652a2c9ccfb556235b2b501a3a7cf3742148cd22e04b5625c5fe057ea3e3191f"}, + {file = "pillow-12.1.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d6e4571eedf43af33d0fc233a382a76e849badbccdf1ac438841308652a08e1f"}, + {file = "pillow-12.1.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b574c51cf7d5d62e9be37ba446224b59a2da26dc4c1bb2ecbe936a4fb1a7cb7f"}, + {file = "pillow-12.1.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a37691702ed687799de29a518d63d4682d9016932db66d4e90c345831b02fb4e"}, + {file = "pillow-12.1.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f95c00d5d6700b2b890479664a06e754974848afaae5e21beb4d83c106923fd0"}, + {file = "pillow-12.1.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:559b38da23606e68681337ad74622c4dbba02254fc9cb4488a305dd5975c7eeb"}, + {file = "pillow-12.1.1-cp314-cp314-win32.whl", hash = "sha256:03edcc34d688572014ff223c125a3f77fb08091e4607e7745002fc214070b35f"}, + {file = "pillow-12.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:50480dcd74fa63b8e78235957d302d98d98d82ccbfac4c7e12108ba9ecbdba15"}, + {file = "pillow-12.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:5cb1785d97b0c3d1d1a16bc1d710c4a0049daefc4935f3a8f31f827f4d3d2e7f"}, + {file = "pillow-12.1.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:1f90cff8aa76835cba5769f0b3121a22bd4eb9e6884cfe338216e557a9a548b8"}, + {file = "pillow-12.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1f1be78ce9466a7ee64bfda57bdba0f7cc499d9794d518b854816c41bf0aa4e9"}, + {file = "pillow-12.1.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:42fc1f4677106188ad9a55562bbade416f8b55456f522430fadab3cef7cd4e60"}, + {file = "pillow-12.1.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:98edb152429ab62a1818039744d8fbb3ccab98a7c29fc3d5fcef158f3f1f68b7"}, + {file = "pillow-12.1.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d470ab1178551dd17fdba0fef463359c41aaa613cdcd7ff8373f54be629f9f8f"}, + {file = "pillow-12.1.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6408a7b064595afcab0a49393a413732a35788f2a5092fdc6266952ed67de586"}, + {file = "pillow-12.1.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5d8c41325b382c07799a3682c1c258469ea2ff97103c53717b7893862d0c98ce"}, + {file = "pillow-12.1.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c7697918b5be27424e9ce568193efd13d925c4481dd364e43f5dff72d33e10f8"}, + {file = "pillow-12.1.1-cp314-cp314t-win32.whl", hash = "sha256:d2912fd8114fc5545aa3a4b5576512f64c55a03f3ebcca4c10194d593d43ea36"}, + {file = "pillow-12.1.1-cp314-cp314t-win_amd64.whl", hash = "sha256:4ceb838d4bd9dab43e06c363cab2eebf63846d6a4aeaea283bbdfd8f1a8ed58b"}, + {file = "pillow-12.1.1-cp314-cp314t-win_arm64.whl", hash = "sha256:7b03048319bfc6170e93bd60728a1af51d3dd7704935feb228c4d4faab35d334"}, + {file = "pillow-12.1.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:600fd103672b925fe62ed08e0d874ea34d692474df6f4bf7ebe148b30f89f39f"}, + {file = "pillow-12.1.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:665e1b916b043cef294bc54d47bf02d87e13f769bc4bc5fa225a24b3a6c5aca9"}, + {file = "pillow-12.1.1-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:495c302af3aad1ca67420ddd5c7bd480c8867ad173528767d906428057a11f0e"}, + {file = "pillow-12.1.1-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8fd420ef0c52c88b5a035a0886f367748c72147b2b8f384c9d12656678dfdfa9"}, + {file = "pillow-12.1.1-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f975aa7ef9684ce7e2c18a3aa8f8e2106ce1e46b94ab713d156b2898811651d3"}, + {file = "pillow-12.1.1-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8089c852a56c2966cf18835db62d9b34fef7ba74c726ad943928d494fa7f4735"}, + {file = "pillow-12.1.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:cb9bb857b2d057c6dfc72ac5f3b44836924ba15721882ef103cecb40d002d80e"}, + {file = "pillow-12.1.1.tar.gz", hash = "sha256:9ad8fa5937ab05218e2b6a4cff30295ad35afd2f83ac592e68c0d871bb0fdbc4"}, +] + +[package.extras] +docs = ["furo", "olefile", "sphinx (>=8.2)", "sphinx-autobuild", "sphinx-copybutton", "sphinx-inline-tabs", "sphinxext-opengraph"] +fpx = ["olefile"] +mic = ["olefile"] +test-arrow = ["arro3-compute", "arro3-core", "nanoarrow", "pyarrow"] +tests = ["check-manifest", "coverage (>=7.4.2)", "defusedxml", "markdown2", "olefile", "packaging", "pyroma (>=5)", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "trove-classifiers (>=2024.10.12)"] +xmp = ["defusedxml"] + +[[package]] +name = "pip" +version = "26.0.1" +description = "The PyPA recommended tool for installing Python packages." +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pip-26.0.1-py3-none-any.whl", hash = "sha256:bdb1b08f4274833d62c1aa29e20907365a2ceb950410df15fc9521bad440122b"}, + {file = "pip-26.0.1.tar.gz", hash = "sha256:c4037d8a277c89b320abe636d59f91e6d0922d08a05b60e85e53b296613346d8"}, +] + +[[package]] +name = "pip-api" +version = "0.0.34" +description = "An unofficial, importable pip API" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "pip_api-0.0.34-py3-none-any.whl", hash = "sha256:8b2d7d7c37f2447373aa2cf8b1f60a2f2b27a84e1e9e0294a3f6ef10eb3ba6bb"}, + {file = "pip_api-0.0.34.tar.gz", hash = "sha256:9b75e958f14c5a2614bae415f2adf7eeb54d50a2cfbe7e24fd4826471bac3625"}, +] + +[package.dependencies] +pip = "*" + +[[package]] +name = "pip-audit" +version = "2.10.0" +description = "A tool for scanning Python environments for known vulnerabilities" +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "pip_audit-2.10.0-py3-none-any.whl", hash = "sha256:16e02093872fac97580303f0848fa3ad64f7ecf600736ea7835a2b24de49613f"}, + {file = "pip_audit-2.10.0.tar.gz", hash = "sha256:427ea5bf61d1d06b98b1ae29b7feacc00288a2eced52c9c58ceed5253ef6c2a4"}, +] + +[package.dependencies] +CacheControl = {version = ">=0.13.0", extras = ["filecache"]} +cyclonedx-python-lib = ">=5,<12" +packaging = ">=23.0.0" +pip-api = ">=0.0.28" +pip-requirements-parser = ">=32.0.0" +platformdirs = ">=4.2.0" +requests = ">=2.31.0" +rich = ">=12.4" +tomli = ">=2.2.1" +tomli-w = ">=1.2.0" + +[package.extras] +cov = ["coverage[toml] (>=7.0,!=7.3.3,<8.0)"] +dev = ["build", "pip-audit[doc,lint,test]"] +doc = ["pdoc"] +lint = ["interrogate (>=1.6,<2.0)", "mypy", "ruff (>=0.11)", "types-requests", "types-toml"] +test = ["pip-audit[cov]", "pretend", "pytest"] + +[[package]] +name = "pip-requirements-parser" +version = "32.0.1" +description = "pip requirements parser - a mostly correct pip requirements parsing library because it uses pip's own code." +optional = false +python-versions = ">=3.6.0" +groups = ["dev"] +files = [ + {file = "pip-requirements-parser-32.0.1.tar.gz", hash = "sha256:b4fa3a7a0be38243123cf9d1f3518da10c51bdb165a2b2985566247f9155a7d3"}, + {file = "pip_requirements_parser-32.0.1-py3-none-any.whl", hash = "sha256:4659bc2a667783e7a15d190f6fccf8b2486685b6dba4c19c3876314769c57526"}, +] + +[package.dependencies] +packaging = "*" +pyparsing = "*" + +[package.extras] +docs = ["Sphinx (>=3.3.1)", "doc8 (>=0.8.1)", "sphinx-rtd-theme (>=0.5.0)"] +testing = ["aboutcode-toolkit (>=6.0.0)", "black", "pytest (>=6,!=7.0.0)", "pytest-xdist (>=2)"] + +[[package]] +name = "platformdirs" +version = "4.5.1" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31"}, + {file = "platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda"}, +] + +[package.extras] +docs = ["furo (>=2025.9.25)", "proselint (>=0.14)", "sphinx (>=8.2.3)", "sphinx-autodoc-typehints (>=3.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.4.2)", "pytest-cov (>=7)", "pytest-mock (>=3.15.1)"] +type = ["mypy (>=1.18.2)"] + +[[package]] +name = "py-serializable" +version = "2.1.0" +description = "Library for serializing and deserializing Python Objects to and from JSON and XML." +optional = false +python-versions = "<4.0,>=3.8" +groups = ["dev"] +files = [ + {file = "py_serializable-2.1.0-py3-none-any.whl", hash = "sha256:b56d5d686b5a03ba4f4db5e769dc32336e142fc3bd4d68a8c25579ebb0a67304"}, + {file = "py_serializable-2.1.0.tar.gz", hash = "sha256:9d5db56154a867a9b897c0163b33a793c804c80cee984116d02d49e4578fc103"}, +] + +[package.dependencies] +defusedxml = ">=0.7.1,<0.8.0" + +[[package]] +name = "pycodestyle" +version = "2.14.0" +description = "Python style guide checker" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pycodestyle-2.14.0-py2.py3-none-any.whl", hash = "sha256:dd6bf7cb4ee77f8e016f9c8e74a35ddd9f67e1d5fd4184d86c3b98e07099f42d"}, + {file = "pycodestyle-2.14.0.tar.gz", hash = "sha256:c4b5b517d278089ff9d0abdec919cd97262a3367449ea1c8b49b91529167b783"}, +] + +[[package]] +name = "pycparser" +version = "3.0" +description = "C parser in Python" +optional = false +python-versions = ">=3.10" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\" and platform_python_implementation != \"PyPy\" and implementation_name != \"PyPy\"" +files = [ + {file = "pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992"}, + {file = "pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29"}, +] + +[[package]] +name = "pydantic" +version = "2.12.5" +description = "Data validation using Python type hints" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d"}, + {file = "pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49"}, +] + +[package.dependencies] +annotated-types = ">=0.6.0" +pydantic-core = "2.41.5" +typing-extensions = ">=4.14.1" +typing-inspection = ">=0.4.2" + +[package.extras] +email = ["email-validator (>=2.0.0)"] +timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows\""] + +[[package]] +name = "pydantic-core" +version = "2.41.5" +description = "Core functionality for Pydantic validation and serialization" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "pydantic_core-2.41.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146"}, + {file = "pydantic_core-2.41.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2"}, + {file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97"}, + {file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9"}, + {file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52"}, + {file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941"}, + {file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a"}, + {file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c"}, + {file = "pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2"}, + {file = "pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556"}, + {file = "pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49"}, + {file = "pydantic_core-2.41.5-cp310-cp310-win32.whl", hash = "sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba"}, + {file = "pydantic_core-2.41.5-cp310-cp310-win_amd64.whl", hash = "sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9"}, + {file = "pydantic_core-2.41.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6"}, + {file = "pydantic_core-2.41.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b"}, + {file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a"}, + {file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8"}, + {file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e"}, + {file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1"}, + {file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b"}, + {file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b"}, + {file = "pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284"}, + {file = "pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594"}, + {file = "pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e"}, + {file = "pydantic_core-2.41.5-cp311-cp311-win32.whl", hash = "sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b"}, + {file = "pydantic_core-2.41.5-cp311-cp311-win_amd64.whl", hash = "sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe"}, + {file = "pydantic_core-2.41.5-cp311-cp311-win_arm64.whl", hash = "sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f"}, + {file = "pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7"}, + {file = "pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0"}, + {file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69"}, + {file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75"}, + {file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05"}, + {file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc"}, + {file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c"}, + {file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5"}, + {file = "pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c"}, + {file = "pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294"}, + {file = "pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1"}, + {file = "pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d"}, + {file = "pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815"}, + {file = "pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3"}, + {file = "pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9"}, + {file = "pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34"}, + {file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0"}, + {file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33"}, + {file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e"}, + {file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2"}, + {file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586"}, + {file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d"}, + {file = "pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740"}, + {file = "pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e"}, + {file = "pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858"}, + {file = "pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36"}, + {file = "pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11"}, + {file = "pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd"}, + {file = "pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a"}, + {file = "pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14"}, + {file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1"}, + {file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66"}, + {file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869"}, + {file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2"}, + {file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375"}, + {file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553"}, + {file = "pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90"}, + {file = "pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07"}, + {file = "pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb"}, + {file = "pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23"}, + {file = "pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf"}, + {file = "pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0"}, + {file = "pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a"}, + {file = "pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3"}, + {file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c"}, + {file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612"}, + {file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d"}, + {file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9"}, + {file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660"}, + {file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9"}, + {file = "pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3"}, + {file = "pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf"}, + {file = "pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470"}, + {file = "pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa"}, + {file = "pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c"}, + {file = "pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008"}, + {file = "pydantic_core-2.41.5-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:8bfeaf8735be79f225f3fefab7f941c712aaca36f1128c9d7e2352ee1aa87bdf"}, + {file = "pydantic_core-2.41.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:346285d28e4c8017da95144c7f3acd42740d637ff41946af5ce6e5e420502dd5"}, + {file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a75dafbf87d6276ddc5b2bf6fae5254e3d0876b626eb24969a574fff9149ee5d"}, + {file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7b93a4d08587e2b7e7882de461e82b6ed76d9026ce91ca7915e740ecc7855f60"}, + {file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e8465ab91a4bd96d36dde3263f06caa6a8a6019e4113f24dc753d79a8b3a3f82"}, + {file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:299e0a22e7ae2b85c1a57f104538b2656e8ab1873511fd718a1c1c6f149b77b5"}, + {file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:707625ef0983fcfb461acfaf14de2067c5942c6bb0f3b4c99158bed6fedd3cf3"}, + {file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f41eb9797986d6ebac5e8edff36d5cef9de40def462311b3eb3eeded1431e425"}, + {file = "pydantic_core-2.41.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0384e2e1021894b1ff5a786dbf94771e2986ebe2869533874d7e43bc79c6f504"}, + {file = "pydantic_core-2.41.5-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:f0cd744688278965817fd0839c4a4116add48d23890d468bc436f78beb28abf5"}, + {file = "pydantic_core-2.41.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:753e230374206729bf0a807954bcc6c150d3743928a73faffee51ac6557a03c3"}, + {file = "pydantic_core-2.41.5-cp39-cp39-win32.whl", hash = "sha256:873e0d5b4fb9b89ef7c2d2a963ea7d02879d9da0da8d9d4933dee8ee86a8b460"}, + {file = "pydantic_core-2.41.5-cp39-cp39-win_amd64.whl", hash = "sha256:e4f4a984405e91527a0d62649ee21138f8e3d0ef103be488c1dc11a80d7f184b"}, + {file = "pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034"}, + {file = "pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c"}, + {file = "pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2"}, + {file = "pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad"}, + {file = "pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd"}, + {file = "pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc"}, + {file = "pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56"}, + {file = "pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b"}, + {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8"}, + {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a"}, + {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b"}, + {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2"}, + {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093"}, + {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a"}, + {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963"}, + {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a"}, + {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26"}, + {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808"}, + {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc"}, + {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1"}, + {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84"}, + {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770"}, + {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f"}, + {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51"}, + {file = "pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e"}, +] + +[package.dependencies] +typing-extensions = ">=4.14.1" + +[[package]] +name = "pyflakes" +version = "3.4.0" +description = "passive checker of Python programs" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pyflakes-3.4.0-py2.py3-none-any.whl", hash = "sha256:f742a7dbd0d9cb9ea41e9a24a918996e8170c799fa528688d40dd582c8265f4f"}, + {file = "pyflakes-3.4.0.tar.gz", hash = "sha256:b24f96fafb7d2ab0ec5075b7350b3d2d2218eab42003821c06344973d3ea2f58"}, +] + +[[package]] +name = "pygments" +version = "2.19.2" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, + {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "pymongo" +version = "4.16.0" +description = "PyMongo - the Official MongoDB Python driver" +optional = false +python-versions = ">=3.9" +groups = ["main", "dev"] +files = [ + {file = "pymongo-4.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ed162b2227f98d5b270ecbe1d53be56c8c81db08a1a8f5f02d89c7bb4d19591d"}, + {file = "pymongo-4.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4a9390dce61d705a88218f0d7b54d7e1fa1b421da8129fc7c009e029a9a6b81e"}, + {file = "pymongo-4.16.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:92a232af9927710de08a6c16a9710cc1b175fb9179c0d946cd4e213b92b2a69a"}, + {file = "pymongo-4.16.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4d79aa147ce86aef03079096d83239580006ffb684eead593917186aee407767"}, + {file = "pymongo-4.16.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:19a1c96e7f39c7a59a9cfd4d17920cf9382f6f684faeff4649bf587dc59f8edc"}, + {file = "pymongo-4.16.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efe020c46ce3c3a89af6baec6569635812129df6fb6cf76d4943af3ba6ee2069"}, + {file = "pymongo-4.16.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9dc2c00bed568732b89e211b6adca389053d5e6d2d5a8979e80b813c3ec4d1f9"}, + {file = "pymongo-4.16.0-cp310-cp310-win32.whl", hash = "sha256:5b9c6d689bbe5beb156374508133218610e14f8c81e35bc17d7a14e30ab593e6"}, + {file = "pymongo-4.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:2290909275c9b8f637b0a92eb9b89281e18a72922749ebb903403ab6cc7da914"}, + {file = "pymongo-4.16.0-cp310-cp310-win_arm64.whl", hash = "sha256:6af1aaa26f0835175d2200e62205b78e7ec3ffa430682e322cc91aaa1a0dbf28"}, + {file = "pymongo-4.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6f2077ec24e2f1248f9cac7b9a2dfb894e50cc7939fcebfb1759f99304caabef"}, + {file = "pymongo-4.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4d4f7ba040f72a9f43a44059872af5a8c8c660aa5d7f90d5344f2ed1c3c02721"}, + {file = "pymongo-4.16.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:8a0f73af1ea56c422b2dcfc0437459148a799ef4231c6aee189d2d4c59d6728f"}, + {file = "pymongo-4.16.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa30cd16ddd2f216d07ba01d9635c873e97ddb041c61cf0847254edc37d1c60e"}, + {file = "pymongo-4.16.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1d638b0b1b294d95d0fdc73688a3b61e05cc4188872818cd240d51460ccabcb5"}, + {file = "pymongo-4.16.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:21d02cc10a158daa20cb040985e280e7e439832fc6b7857bff3d53ef6914ad50"}, + {file = "pymongo-4.16.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4fbb8d3552c2ad99d9e236003c0b5f96d5f05e29386ba7abae73949bfebc13dd"}, + {file = "pymongo-4.16.0-cp311-cp311-win32.whl", hash = "sha256:be1099a8295b1a722d03fb7b48be895d30f4301419a583dcf50e9045968a041c"}, + {file = "pymongo-4.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:61567f712bda04c7545a037e3284b4367cad8d29b3dec84b4bf3b2147020a75b"}, + {file = "pymongo-4.16.0-cp311-cp311-win_arm64.whl", hash = "sha256:c53338613043038005bf2e41a2fafa08d29cdbc0ce80891b5366c819456c1ae9"}, + {file = "pymongo-4.16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bd4911c40a43a821dfd93038ac824b756b6e703e26e951718522d29f6eb166a8"}, + {file = "pymongo-4.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:25a6b03a68f9907ea6ec8bc7cf4c58a1b51a18e23394f962a6402f8e46d41211"}, + {file = "pymongo-4.16.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:91ac0cb0fe2bf17616c2039dac88d7c9a5088f5cb5829b27c9d250e053664d31"}, + {file = "pymongo-4.16.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cf0ec79e8ca7077f455d14d915d629385153b6a11abc0b93283ed73a8013e376"}, + {file = "pymongo-4.16.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2d0082631a7510318befc2b4fdab140481eb4b9dd62d9245e042157085da2a70"}, + {file = "pymongo-4.16.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:85dc2f3444c346ea019a371e321ac868a4fab513b7a55fe368f0cc78de8177cc"}, + {file = "pymongo-4.16.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dabbf3c14de75a20cc3c30bf0c6527157224a93dfb605838eabb1a2ee3be008d"}, + {file = "pymongo-4.16.0-cp312-cp312-win32.whl", hash = "sha256:60307bb91e0ab44e560fe3a211087748b2b5f3e31f403baf41f5b7b0a70bd104"}, + {file = "pymongo-4.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:f513b2c6c0d5c491f478422f6b5b5c27ac1af06a54c93ef8631806f7231bd92e"}, + {file = "pymongo-4.16.0-cp312-cp312-win_arm64.whl", hash = "sha256:dfc320f08ea9a7ec5b2403dc4e8150636f0d6150f4b9792faaae539c88e7db3b"}, + {file = "pymongo-4.16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d15f060bc6d0964a8bb70aba8f0cb6d11ae99715438f640cff11bbcf172eb0e8"}, + {file = "pymongo-4.16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a19ea46a0fe71248965305a020bc076a163311aefbaa1d83e47d06fa30ac747"}, + {file = "pymongo-4.16.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:311d4549d6bf1f8c61d025965aebb5ba29d1481dc6471693ab91610aaffbc0eb"}, + {file = "pymongo-4.16.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:46ffb728d92dd5b09fc034ed91acf5595657c7ca17d4cf3751322cd554153c17"}, + {file = "pymongo-4.16.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:acda193f440dd88c2023cb00aa8bd7b93a9df59978306d14d87a8b12fe426b05"}, + {file = "pymongo-4.16.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5d9fdb386cf958e6ef6ff537d6149be7edb76c3268cd6833e6c36aa447e4443f"}, + {file = "pymongo-4.16.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:91899dd7fb9a8c50f09c3c1cf0cb73bfbe2737f511f641f19b9650deb61c00ca"}, + {file = "pymongo-4.16.0-cp313-cp313-win32.whl", hash = "sha256:2cd60cd1e05de7f01927f8e25ca26b3ea2c09de8723241e5d3bcfdc70eaff76b"}, + {file = "pymongo-4.16.0-cp313-cp313-win_amd64.whl", hash = "sha256:3ead8a0050c53eaa55935895d6919d393d0328ec24b2b9115bdbe881aa222673"}, + {file = "pymongo-4.16.0-cp313-cp313-win_arm64.whl", hash = "sha256:dbbc5b254c36c37d10abb50e899bc3939bbb7ab1e7c659614409af99bd3e7675"}, + {file = "pymongo-4.16.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:8a254d49a9ffe9d7f888e3c677eed3729b14ce85abb08cd74732cead6ccc3c66"}, + {file = "pymongo-4.16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a1bf44e13cf2d44d2ea2e928a8140d5d667304abe1a61c4d55b4906f389fbe64"}, + {file = "pymongo-4.16.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f1c5f1f818b669875d191323a48912d3fcd2e4906410e8297bb09ac50c4d5ccc"}, + {file = "pymongo-4.16.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:77cfd37a43a53b02b7bd930457c7994c924ad8bbe8dff91817904bcbf291b371"}, + {file = "pymongo-4.16.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:36ef2fee50eee669587d742fb456e349634b4fcf8926208766078b089054b24b"}, + {file = "pymongo-4.16.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:55f8d5a6fe2fa0b823674db2293f92d74cd5f970bc0360f409a1fc21003862d3"}, + {file = "pymongo-4.16.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9caacac0dd105e2555521002e2d17afc08665187017b466b5753e84c016628e6"}, + {file = "pymongo-4.16.0-cp314-cp314-win32.whl", hash = "sha256:c789236366525c3ee3cd6e4e450a9ff629a7d1f4d88b8e18a0aea0615fd7ecf8"}, + {file = "pymongo-4.16.0-cp314-cp314-win_amd64.whl", hash = "sha256:2b0714d7764efb29bf9d3c51c964aed7c4c7237b341f9346f15ceaf8321fdb35"}, + {file = "pymongo-4.16.0-cp314-cp314-win_arm64.whl", hash = "sha256:12762e7cc0f8374a8cae3b9f9ed8dabb5d438c7b33329232dd9b7de783454033"}, + {file = "pymongo-4.16.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:1c01e8a7cd0ea66baf64a118005535ab5bf9f9eb63a1b50ac3935dccf9a54abe"}, + {file = "pymongo-4.16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:4c4872299ebe315a79f7f922051061634a64fda95b6b17677ba57ef00b2ba2a4"}, + {file = "pymongo-4.16.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:78037d02389745e247fe5ab0bcad5d1ab30726eaac3ad79219c7d6bbb07eec53"}, + {file = "pymongo-4.16.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c126fb72be2518395cc0465d4bae03125119136462e1945aea19840e45d89cfc"}, + {file = "pymongo-4.16.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f3867dc225d9423c245a51eaac2cfcd53dde8e0a8d8090bb6aed6e31bd6c2d4f"}, + {file = "pymongo-4.16.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f25001a955073b80510c0c3db0e043dbbc36904fd69e511c74e3d8640b8a5111"}, + {file = "pymongo-4.16.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d9885aad05f82fd7ea0c9ca505d60939746b39263fa273d0125170da8f59098"}, + {file = "pymongo-4.16.0-cp314-cp314t-win32.whl", hash = "sha256:948152b30eddeae8355495f9943a3bf66b708295c0b9b6f467de1c620f215487"}, + {file = "pymongo-4.16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:f6e42c1bc985d9beee884780ae6048790eb4cd565c46251932906bdb1630034a"}, + {file = "pymongo-4.16.0-cp314-cp314t-win_arm64.whl", hash = "sha256:6b2a20edb5452ac8daa395890eeb076c570790dfce6b7a44d788af74c2f8cf96"}, + {file = "pymongo-4.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e2d509786344aa844ae243f68f833ca1ac92ac3e35a92ae038e2ceb44aa355ef"}, + {file = "pymongo-4.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:15bb062c0d6d4b0be650410032152de656a2a9a2aa4e1a7443a22695afacb103"}, + {file = "pymongo-4.16.0-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4cd047ba6cc83cc24193b9208c93e134a985ead556183077678c59af7aacc725"}, + {file = "pymongo-4.16.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96aa7ab896889bf330209d26459e493d00f8855772a9453bfb4520bb1f495baf"}, + {file = "pymongo-4.16.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:66af44ed23686dd5422307619a6db4b56733c5e36fe8c4adf91326dcf993a043"}, + {file = "pymongo-4.16.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:03f42396c1b2c6f46f5401c5b185adc25f6113716e16d9503977ee5386fca0fb"}, + {file = "pymongo-4.16.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d284bf68daffc57516535f752e290609b3b643f4bd54b28fc13cb16a89a8bda6"}, + {file = "pymongo-4.16.0-cp39-cp39-win32.whl", hash = "sha256:7902882ed0efb7f0e991458ab3b8cf0eb052957264949ece2f09b63c58b04f78"}, + {file = "pymongo-4.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:e37469602473f41221cea93fd3736708f561f0fa08ab6b2873dd962014390d52"}, + {file = "pymongo-4.16.0-cp39-cp39-win_arm64.whl", hash = "sha256:2a3ba6be3d8acf64b77cdcd4e36f0e4a8e87965f14a8b09b90ca86f10a1dd2f2"}, + {file = "pymongo-4.16.0.tar.gz", hash = "sha256:8ba8405065f6e258a6f872fe62d797a28f383a12178c7153c01ed04e845c600c"}, +] + +[package.dependencies] +dnspython = ">=2.6.1,<3.0.0" + +[package.extras] +aws = ["pymongo-auth-aws (>=1.1.0,<2.0.0)"] +docs = ["furo (==2025.12.19)", "readthedocs-sphinx-search (>=0.3,<1.0)", "sphinx (>=5.3,<9)", "sphinx-autobuild (>=2020.9.1)", "sphinx-rtd-theme (>=2,<4)", "sphinxcontrib-shellcheck (>=1,<2)"] +encryption = ["certifi (>=2023.7.22) ; os_name == \"nt\" or sys_platform == \"darwin\"", "pymongo-auth-aws (>=1.1.0,<2.0.0)", "pymongocrypt (>=1.13.0,<2.0.0)"] +gssapi = ["pykerberos (>=1.2.4) ; os_name != \"nt\"", "winkerberos (>=0.5.0) ; os_name == \"nt\""] +ocsp = ["certifi (>=2023.7.22) ; os_name == \"nt\" or sys_platform == \"darwin\"", "cryptography (>=42.0.0)", "pyopenssl (>=23.2.0)", "requests (>=2.23.0,<3.0)", "service-identity (>=23.1.0)"] +snappy = ["python-snappy (>=0.6.0)"] +test = ["importlib-metadata (>=7.0) ; python_version < \"3.13\"", "pytest (>=8.2)", "pytest-asyncio (>=0.24.0)"] +zstd = ["backports-zstd (>=1.0.0) ; python_version < \"3.14\""] + +[[package]] +name = "pyparsing" +version = "3.3.2" +description = "pyparsing - Classes and methods to define and execute parsing grammars" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d"}, + {file = "pyparsing-3.3.2.tar.gz", hash = "sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc"}, +] + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] + +[[package]] +name = "python-dotenv" +version = "1.2.1" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61"}, + {file = "python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + +[[package]] +name = "python-multipart" +version = "0.0.22" +description = "A streaming multipart parser for Python" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "python_multipart-0.0.22-py3-none-any.whl", hash = "sha256:2b2cd894c83d21bf49d702499531c7bafd057d730c201782048f7945d82de155"}, + {file = "python_multipart-0.0.22.tar.gz", hash = "sha256:7340bef99a7e0032613f56dc36027b959fd3b30a787ed62d310e951f7c3a3a58"}, +] + +[[package]] +name = "pytokens" +version = "0.4.1" +description = "A Fast, spec compliant Python 3.14+ tokenizer that runs on older Pythons." +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "pytokens-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a44ed93ea23415c54f3face3b65ef2b844d96aeb3455b8a69b3df6beab6acc5"}, + {file = "pytokens-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:add8bf86b71a5d9fb5b89f023a80b791e04fba57960aa790cc6125f7f1d39dfe"}, + {file = "pytokens-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:670d286910b531c7b7e3c0b453fd8156f250adb140146d234a82219459b9640c"}, + {file = "pytokens-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4e691d7f5186bd2842c14813f79f8884bb03f5995f0575272009982c5ac6c0f7"}, + {file = "pytokens-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:27b83ad28825978742beef057bfe406ad6ed524b2d28c252c5de7b4a6dd48fa2"}, + {file = "pytokens-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d70e77c55ae8380c91c0c18dea05951482e263982911fc7410b1ffd1dadd3440"}, + {file = "pytokens-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a58d057208cb9075c144950d789511220b07636dd2e4708d5645d24de666bdc"}, + {file = "pytokens-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b49750419d300e2b5a3813cf229d4e5a4c728dae470bcc89867a9ad6f25a722d"}, + {file = "pytokens-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d9907d61f15bf7261d7e775bd5d7ee4d2930e04424bab1972591918497623a16"}, + {file = "pytokens-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:ee44d0f85b803321710f9239f335aafe16553b39106384cef8e6de40cb4ef2f6"}, + {file = "pytokens-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:140709331e846b728475786df8aeb27d24f48cbcf7bcd449f8de75cae7a45083"}, + {file = "pytokens-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d6c4268598f762bc8e91f5dbf2ab2f61f7b95bdc07953b602db879b3c8c18e1"}, + {file = "pytokens-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24afde1f53d95348b5a0eb19488661147285ca4dd7ed752bbc3e1c6242a304d1"}, + {file = "pytokens-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5ad948d085ed6c16413eb5fec6b3e02fa00dc29a2534f088d3302c47eb59adf9"}, + {file = "pytokens-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:3f901fe783e06e48e8cbdc82d631fca8f118333798193e026a50ce1b3757ea68"}, + {file = "pytokens-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8bdb9d0ce90cbf99c525e75a2fa415144fd570a1ba987380190e8b786bc6ef9b"}, + {file = "pytokens-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5502408cab1cb18e128570f8d598981c68a50d0cbd7c61312a90507cd3a1276f"}, + {file = "pytokens-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29d1d8fb1030af4d231789959f21821ab6325e463f0503a61d204343c9b355d1"}, + {file = "pytokens-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:970b08dd6b86058b6dc07efe9e98414f5102974716232d10f32ff39701e841c4"}, + {file = "pytokens-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:9bd7d7f544d362576be74f9d5901a22f317efc20046efe2034dced238cbbfe78"}, + {file = "pytokens-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4a14d5f5fc78ce85e426aa159489e2d5961acf0e47575e08f35584009178e321"}, + {file = "pytokens-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f50fd18543be72da51dd505e2ed20d2228c74e0464e4262e4899797803d7fa"}, + {file = "pytokens-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc74c035f9bfca0255c1af77ddd2d6ae8419012805453e4b0e7513e17904545d"}, + {file = "pytokens-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f66a6bbe741bd431f6d741e617e0f39ec7257ca1f89089593479347cc4d13324"}, + {file = "pytokens-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:b35d7e5ad269804f6697727702da3c517bb8a5228afa450ab0fa787732055fc9"}, + {file = "pytokens-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8fcb9ba3709ff77e77f1c7022ff11d13553f3c30299a9fe246a166903e9091eb"}, + {file = "pytokens-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:79fc6b8699564e1f9b521582c35435f1bd32dd06822322ec44afdeba666d8cb3"}, + {file = "pytokens-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d31b97b3de0f61571a124a00ffe9a81fb9939146c122c11060725bd5aea79975"}, + {file = "pytokens-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:967cf6e3fd4adf7de8fc73cd3043754ae79c36475c1c11d514fc72cf5490094a"}, + {file = "pytokens-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:584c80c24b078eec1e227079d56dc22ff755e0ba8654d8383b2c549107528918"}, + {file = "pytokens-0.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:da5baeaf7116dced9c6bb76dc31ba04a2dc3695f3d9f74741d7910122b456edc"}, + {file = "pytokens-0.4.1-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:11edda0942da80ff58c4408407616a310adecae1ddd22eef8c692fe266fa5009"}, + {file = "pytokens-0.4.1-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0fc71786e629cef478cbf29d7ea1923299181d0699dbe7c3c0f4a583811d9fc1"}, + {file = "pytokens-0.4.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:dcafc12c30dbaf1e2af0490978352e0c4041a7cde31f4f81435c2a5e8b9cabb6"}, + {file = "pytokens-0.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:42f144f3aafa5d92bad964d471a581651e28b24434d184871bd02e3a0d956037"}, + {file = "pytokens-0.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:34bcc734bd2f2d5fe3b34e7b3c0116bfb2397f2d9666139988e7a3eb5f7400e3"}, + {file = "pytokens-0.4.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:941d4343bf27b605e9213b26bfa1c4bf197c9c599a9627eb7305b0defcfe40c1"}, + {file = "pytokens-0.4.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3ad72b851e781478366288743198101e5eb34a414f1d5627cdd585ca3b25f1db"}, + {file = "pytokens-0.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:682fa37ff4d8e95f7df6fe6fe6a431e8ed8e788023c6bcc0f0880a12eab80ad1"}, + {file = "pytokens-0.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:30f51edd9bb7f85c748979384165601d028b84f7bd13fe14d3e065304093916a"}, + {file = "pytokens-0.4.1-py3-none-any.whl", hash = "sha256:26cef14744a8385f35d0e095dc8b3a7583f6c953c2e3d269c7f82484bf5ad2de"}, + {file = "pytokens-0.4.1.tar.gz", hash = "sha256:292052fe80923aae2260c073f822ceba21f3872ced9a68bb7953b348e561179a"}, +] + +[package.extras] +dev = ["black", "build", "mypy", "pytest", "pytest-cov", "setuptools", "tox", "twine", "wheel"] + +[[package]] +name = "pywin32-ctypes" +version = "0.2.3" +description = "A (partial) reimplementation of pywin32 using ctypes/cffi" +optional = false +python-versions = ">=3.6" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"win32\"" +files = [ + {file = "pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755"}, + {file = "pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8"}, +] + +[[package]] +name = "qrcode" +version = "8.2" +description = "QR Code image generator" +optional = false +python-versions = "<4.0,>=3.9" +groups = ["main"] +files = [ + {file = "qrcode-8.2-py3-none-any.whl", hash = "sha256:16e64e0716c14960108e85d853062c9e8bba5ca8252c0b4d0231b9df4060ff4f"}, + {file = "qrcode-8.2.tar.gz", hash = "sha256:35c3f2a4172b33136ab9f6b3ef1c00260dd2f66f858f24d88418a015f446506c"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} + +[package.extras] +all = ["pillow (>=9.1.0)", "pypng"] +pil = ["pillow (>=9.1.0)"] +png = ["pypng"] + +[[package]] +name = "readme-renderer" +version = "44.0" +description = "readme_renderer is a library for rendering readme descriptions for Warehouse" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "readme_renderer-44.0-py3-none-any.whl", hash = "sha256:2fbca89b81a08526aadf1357a8c2ae889ec05fb03f5da67f9769c9a592166151"}, + {file = "readme_renderer-44.0.tar.gz", hash = "sha256:8712034eabbfa6805cacf1402b4eeb2a73028f72d1166d6f5cb7f9c047c5d1e1"}, +] + +[package.dependencies] +docutils = ">=0.21.2" +nh3 = ">=0.2.14" +Pygments = ">=2.5.1" + +[package.extras] +md = ["cmarkgfm (>=0.8.0)"] + +[[package]] +name = "redis" +version = "7.1.1" +description = "Python client for Redis database and key-value store" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "redis-7.1.1-py3-none-any.whl", hash = "sha256:f77817f16071c2950492c67d40b771fa493eb3fccc630a424a10976dbb794b7a"}, + {file = "redis-7.1.1.tar.gz", hash = "sha256:a2814b2bda15b39dad11391cc48edac4697214a8a5a4bd10abe936ab4892eb43"}, +] + +[package.dependencies] +async-timeout = {version = ">=4.0.3", markers = "python_full_version < \"3.11.3\""} + +[package.extras] +circuit-breaker = ["pybreaker (>=1.4.0)"] +hiredis = ["hiredis (>=3.2.0)"] +jwt = ["pyjwt (>=2.9.0)"] +ocsp = ["cryptography (>=36.0.1)", "pyopenssl (>=20.0.1)", "requests (>=2.31.0)"] + +[[package]] +name = "requests" +version = "2.32.5" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6"}, + {file = "requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset_normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "requests-toolbelt" +version = "1.0.0" +description = "A utility belt for advanced users of python-requests" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["dev"] +files = [ + {file = "requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6"}, + {file = "requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06"}, +] + +[package.dependencies] +requests = ">=2.0.1,<3.0.0" + +[[package]] +name = "rfc3986" +version = "2.0.0" +description = "Validating URI References per RFC 3986" +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "rfc3986-2.0.0-py2.py3-none-any.whl", hash = "sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd"}, + {file = "rfc3986-2.0.0.tar.gz", hash = "sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c"}, +] + +[package.extras] +idna2008 = ["idna"] + +[[package]] +name = "rich" +version = "14.3.2" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +optional = false +python-versions = ">=3.8.0" +groups = ["dev"] +files = [ + {file = "rich-14.3.2-py3-none-any.whl", hash = "sha256:08e67c3e90884651da3239ea668222d19bea7b589149d8014a21c633420dbb69"}, + {file = "rich-14.3.2.tar.gz", hash = "sha256:e712f11c1a562a11843306f5ed999475f09ac31ffb64281f73ab29ffdda8b3b8"}, +] + +[package.dependencies] +markdown-it-py = ">=2.2.0" +pygments = ">=2.13.0,<3.0.0" + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<9)"] + +[[package]] +name = "ruff" +version = "0.15.0" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "ruff-0.15.0-py3-none-linux_armv6l.whl", hash = "sha256:aac4ebaa612a82b23d45964586f24ae9bc23ca101919f5590bdb368d74ad5455"}, + {file = "ruff-0.15.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:dcd4be7cc75cfbbca24a98d04d0b9b36a270d0833241f776b788d59f4142b14d"}, + {file = "ruff-0.15.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d747e3319b2bce179c7c1eaad3d884dc0a199b5f4d5187620530adf9105268ce"}, + {file = "ruff-0.15.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:650bd9c56ae03102c51a5e4b554d74d825ff3abe4db22b90fd32d816c2e90621"}, + {file = "ruff-0.15.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a6664b7eac559e3048223a2da77769c2f92b43a6dfd4720cef42654299a599c9"}, + {file = "ruff-0.15.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f811f97b0f092b35320d1556f3353bf238763420ade5d9e62ebd2b73f2ff179"}, + {file = "ruff-0.15.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:761ec0a66680fab6454236635a39abaf14198818c8cdf691e036f4bc0f406b2d"}, + {file = "ruff-0.15.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:940f11c2604d317e797b289f4f9f3fa5555ffe4fb574b55ed006c3d9b6f0eb78"}, + {file = "ruff-0.15.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bcbca3d40558789126da91d7ef9a7c87772ee107033db7191edefa34e2c7f1b4"}, + {file = "ruff-0.15.0-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:9a121a96db1d75fa3eb39c4539e607f628920dd72ff1f7c5ee4f1b768ac62d6e"}, + {file = "ruff-0.15.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:5298d518e493061f2eabd4abd067c7e4fb89e2f63291c94332e35631c07c3662"}, + {file = "ruff-0.15.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:afb6e603d6375ff0d6b0cee563fa21ab570fd15e65c852cb24922cef25050cf1"}, + {file = "ruff-0.15.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:77e515f6b15f828b94dc17d2b4ace334c9ddb7d9468c54b2f9ed2b9c1593ef16"}, + {file = "ruff-0.15.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:6f6e80850a01eb13b3e42ee0ebdf6e4497151b48c35051aab51c101266d187a3"}, + {file = "ruff-0.15.0-py3-none-win32.whl", hash = "sha256:238a717ef803e501b6d51e0bdd0d2c6e8513fe9eec14002445134d3907cd46c3"}, + {file = "ruff-0.15.0-py3-none-win_amd64.whl", hash = "sha256:dd5e4d3301dc01de614da3cdffc33d4b1b96fb89e45721f1598e5532ccf78b18"}, + {file = "ruff-0.15.0-py3-none-win_arm64.whl", hash = "sha256:c480d632cc0ca3f0727acac8b7d053542d9e114a462a145d0b00e7cd658c515a"}, + {file = "ruff-0.15.0.tar.gz", hash = "sha256:6bdea47cdbea30d40f8f8d7d69c0854ba7c15420ec75a26f463290949d7f7e9a"}, +] + +[[package]] +name = "secretstorage" +version = "3.5.0" +description = "Python bindings to FreeDesktop.org Secret Service API" +optional = false +python-versions = ">=3.10" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\"" +files = [ + {file = "secretstorage-3.5.0-py3-none-any.whl", hash = "sha256:0ce65888c0725fcb2c5bc0fdb8e5438eece02c523557ea40ce0703c266248137"}, + {file = "secretstorage-3.5.0.tar.gz", hash = "sha256:f04b8e4689cbce351744d5537bf6b1329c6fc68f91fa666f60a380edddcd11be"}, +] + +[package.dependencies] +cryptography = ">=2.0" +jeepney = ">=0.6" + +[[package]] +name = "sortedcontainers" +version = "2.4.0" +description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set" +optional = false +python-versions = "*" +groups = ["dev"] +files = [ + {file = "sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0"}, + {file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"}, +] + +[[package]] +name = "starlette" +version = "0.52.1" +description = "The little ASGI library that shines." +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "starlette-0.52.1-py3-none-any.whl", hash = "sha256:0029d43eb3d273bc4f83a08720b4912ea4b071087a3b48db01b7c839f7954d74"}, + {file = "starlette-0.52.1.tar.gz", hash = "sha256:834edd1b0a23167694292e94f597773bc3f89f362be6effee198165a35d62933"}, +] + +[package.dependencies] +anyio = ">=3.6.2,<5" +typing-extensions = {version = ">=4.10.0", markers = "python_version < \"3.13\""} + +[package.extras] +full = ["httpx (>=0.27.0,<0.29.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.18)", "pyyaml"] + +[[package]] +name = "tomli" +version = "2.4.0" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "tomli-2.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b5ef256a3fd497d4973c11bf142e9ed78b150d36f5773f1ca6088c230ffc5867"}, + {file = "tomli-2.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5572e41282d5268eb09a697c89a7bee84fae66511f87533a6f88bd2f7b652da9"}, + {file = "tomli-2.4.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:551e321c6ba03b55676970b47cb1b73f14a0a4dce6a3e1a9458fd6d921d72e95"}, + {file = "tomli-2.4.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e3f639a7a8f10069d0e15408c0b96a2a828cfdec6fca05296ebcdcc28ca7c76"}, + {file = "tomli-2.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1b168f2731796b045128c45982d3a4874057626da0e2ef1fdd722848b741361d"}, + {file = "tomli-2.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:133e93646ec4300d651839d382d63edff11d8978be23da4cc106f5a18b7d0576"}, + {file = "tomli-2.4.0-cp311-cp311-win32.whl", hash = "sha256:b6c78bdf37764092d369722d9946cb65b8767bfa4110f902a1b2542d8d173c8a"}, + {file = "tomli-2.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:d3d1654e11d724760cdb37a3d7691f0be9db5fbdaef59c9f532aabf87006dbaa"}, + {file = "tomli-2.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:cae9c19ed12d4e8f3ebf46d1a75090e4c0dc16271c5bce1c833ac168f08fb614"}, + {file = "tomli-2.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:920b1de295e72887bafa3ad9f7a792f811847d57ea6b1215154030cf131f16b1"}, + {file = "tomli-2.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d6d9a4aee98fac3eab4952ad1d73aee87359452d1c086b5ceb43ed02ddb16b8"}, + {file = "tomli-2.4.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36b9d05b51e65b254ea6c2585b59d2c4cb91c8a3d91d0ed0f17591a29aaea54a"}, + {file = "tomli-2.4.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c8a885b370751837c029ef9bc014f27d80840e48bac415f3412e6593bbc18c1"}, + {file = "tomli-2.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8768715ffc41f0008abe25d808c20c3d990f42b6e2e58305d5da280ae7d1fa3b"}, + {file = "tomli-2.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b438885858efd5be02a9a133caf5812b8776ee0c969fea02c45e8e3f296ba51"}, + {file = "tomli-2.4.0-cp312-cp312-win32.whl", hash = "sha256:0408e3de5ec77cc7f81960c362543cbbd91ef883e3138e81b729fc3eea5b9729"}, + {file = "tomli-2.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:685306e2cc7da35be4ee914fd34ab801a6acacb061b6a7abca922aaf9ad368da"}, + {file = "tomli-2.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:5aa48d7c2356055feef06a43611fc401a07337d5b006be13a30f6c58f869e3c3"}, + {file = "tomli-2.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84d081fbc252d1b6a982e1870660e7330fb8f90f676f6e78b052ad4e64714bf0"}, + {file = "tomli-2.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9a08144fa4cba33db5255f9b74f0b89888622109bd2776148f2597447f92a94e"}, + {file = "tomli-2.4.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c73add4bb52a206fd0c0723432db123c0c75c280cbd67174dd9d2db228ebb1b4"}, + {file = "tomli-2.4.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fb2945cbe303b1419e2706e711b7113da57b7db31ee378d08712d678a34e51e"}, + {file = "tomli-2.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bbb1b10aa643d973366dc2cb1ad94f99c1726a02343d43cbc011edbfac579e7c"}, + {file = "tomli-2.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4cbcb367d44a1f0c2be408758b43e1ffb5308abe0ea222897d6bfc8e8281ef2f"}, + {file = "tomli-2.4.0-cp313-cp313-win32.whl", hash = "sha256:7d49c66a7d5e56ac959cb6fc583aff0651094ec071ba9ad43df785abc2320d86"}, + {file = "tomli-2.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:3cf226acb51d8f1c394c1b310e0e0e61fecdd7adcb78d01e294ac297dd2e7f87"}, + {file = "tomli-2.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:d20b797a5c1ad80c516e41bc1fb0443ddb5006e9aaa7bda2d71978346aeb9132"}, + {file = "tomli-2.4.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:26ab906a1eb794cd4e103691daa23d95c6919cc2fa9160000ac02370cc9dd3f6"}, + {file = "tomli-2.4.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:20cedb4ee43278bc4f2fee6cb50daec836959aadaf948db5172e776dd3d993fc"}, + {file = "tomli-2.4.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:39b0b5d1b6dd03684b3fb276407ebed7090bbec989fa55838c98560c01113b66"}, + {file = "tomli-2.4.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a26d7ff68dfdb9f87a016ecfd1e1c2bacbe3108f4e0f8bcd2228ef9a766c787d"}, + {file = "tomli-2.4.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:20ffd184fb1df76a66e34bd1b36b4a4641bd2b82954befa32fe8163e79f1a702"}, + {file = "tomli-2.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:75c2f8bbddf170e8effc98f5e9084a8751f8174ea6ccf4fca5398436e0320bc8"}, + {file = "tomli-2.4.0-cp314-cp314-win32.whl", hash = "sha256:31d556d079d72db7c584c0627ff3a24c5d3fb4f730221d3444f3efb1b2514776"}, + {file = "tomli-2.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:43e685b9b2341681907759cf3a04e14d7104b3580f808cfde1dfdb60ada85475"}, + {file = "tomli-2.4.0-cp314-cp314-win_arm64.whl", hash = "sha256:3d895d56bd3f82ddd6faaff993c275efc2ff38e52322ea264122d72729dca2b2"}, + {file = "tomli-2.4.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:5b5807f3999fb66776dbce568cc9a828544244a8eb84b84b9bafc080c99597b9"}, + {file = "tomli-2.4.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c084ad935abe686bd9c898e62a02a19abfc9760b5a79bc29644463eaf2840cb0"}, + {file = "tomli-2.4.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f2e3955efea4d1cfbcb87bc321e00dc08d2bcb737fd1d5e398af111d86db5df"}, + {file = "tomli-2.4.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e0fe8a0b8312acf3a88077a0802565cb09ee34107813bba1c7cd591fa6cfc8d"}, + {file = "tomli-2.4.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:413540dce94673591859c4c6f794dfeaa845e98bf35d72ed59636f869ef9f86f"}, + {file = "tomli-2.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0dc56fef0e2c1c470aeac5b6ca8cc7b640bb93e92d9803ddaf9ea03e198f5b0b"}, + {file = "tomli-2.4.0-cp314-cp314t-win32.whl", hash = "sha256:d878f2a6707cc9d53a1be1414bbb419e629c3d6e67f69230217bb663e76b5087"}, + {file = "tomli-2.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2add28aacc7425117ff6364fe9e06a183bb0251b03f986df0e78e974047571fd"}, + {file = "tomli-2.4.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2b1e3b80e1d5e52e40e9b924ec43d81570f0e7d09d11081b797bc4692765a3d4"}, + {file = "tomli-2.4.0-py3-none-any.whl", hash = "sha256:1f776e7d669ebceb01dee46484485f43a4048746235e683bcdffacdf1fb4785a"}, + {file = "tomli-2.4.0.tar.gz", hash = "sha256:aa89c3f6c277dd275d8e243ad24f3b5e701491a860d5121f2cdd399fbb31fc9c"}, +] + +[[package]] +name = "tomli-w" +version = "1.2.0" +description = "A lil' TOML writer" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "tomli_w-1.2.0-py3-none-any.whl", hash = "sha256:188306098d013b691fcadc011abd66727d3c414c571bb01b1a174ba8c983cf90"}, + {file = "tomli_w-1.2.0.tar.gz", hash = "sha256:2dd14fac5a47c27be9cd4c976af5a12d87fb1f0b4512f81d69cce3b35ae25021"}, +] + +[[package]] +name = "twine" +version = "6.2.0" +description = "Collection of utilities for publishing packages on PyPI" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "twine-6.2.0-py3-none-any.whl", hash = "sha256:418ebf08ccda9a8caaebe414433b0ba5e25eb5e4a927667122fbe8f829f985d8"}, + {file = "twine-6.2.0.tar.gz", hash = "sha256:e5ed0d2fd70c9959770dce51c8f39c8945c574e18173a7b81802dab51b4b75cf"}, +] + +[package.dependencies] +id = "*" +keyring = {version = ">=21.2.0", markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\""} +packaging = ">=24.0" +readme-renderer = ">=35.0" +requests = ">=2.20" +requests-toolbelt = ">=0.8.0,<0.9.0 || >0.9.0" +rfc3986 = ">=1.4.0" +rich = ">=12.0.0" +urllib3 = ">=1.26.0" + +[package.extras] +keyring = ["keyring (>=21.2.0)"] + +[[package]] +name = "types-pillow" +version = "10.2.0.20240822" +description = "Typing stubs for Pillow" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "types-Pillow-10.2.0.20240822.tar.gz", hash = "sha256:559fb52a2ef991c326e4a0d20accb3bb63a7ba8d40eb493e0ecb0310ba52f0d3"}, + {file = "types_Pillow-10.2.0.20240822-py3-none-any.whl", hash = "sha256:d9dab025aba07aeb12fd50a6799d4eac52a9603488eca09d7662543983f16c5d"}, +] + +[[package]] +name = "types-pywin32" +version = "311.0.0.20251008" +description = "Typing stubs for pywin32" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "types_pywin32-311.0.0.20251008-py3-none-any.whl", hash = "sha256:775e1046e0bad6d29ca47501301cce67002f6661b9cebbeca93f9c388c53fab4"}, + {file = "types_pywin32-311.0.0.20251008.tar.gz", hash = "sha256:d6d4faf8e0d7fdc0e0a1ff297b80be07d6d18510f102d793bf54e9e3e86f6d06"}, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +description = "Backported and Experimental Type Hints for Python 3.9+" +optional = false +python-versions = ">=3.9" +groups = ["main", "dev"] +files = [ + {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, + {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, +] + +[[package]] +name = "typing-inspection" +version = "0.4.2" +description = "Runtime typing introspection tools" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7"}, + {file = "typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464"}, +] + +[package.dependencies] +typing-extensions = ">=4.12.0" + +[[package]] +name = "urllib3" +version = "2.6.3" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4"}, + {file = "urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed"}, +] + +[package.extras] +brotli = ["brotli (>=1.2.0) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=1.2.0.0) ; platform_python_implementation != \"CPython\""] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["backports-zstd (>=1.0.0) ; python_version < \"3.14\""] + +[[package]] +name = "uvicorn" +version = "0.40.0" +description = "The lightning-fast ASGI server." +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "uvicorn-0.40.0-py3-none-any.whl", hash = "sha256:c6c8f55bc8bf13eb6fa9ff87ad62308bbbc33d0b67f84293151efe87e0d5f2ee"}, + {file = "uvicorn-0.40.0.tar.gz", hash = "sha256:839676675e87e73694518b5574fd0f24c9d97b46bea16df7b8c05ea1a51071ea"}, +] + +[package.dependencies] +click = ">=7.0" +h11 = ">=0.8" +typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""} + +[package.extras] +standard = ["colorama (>=0.4) ; sys_platform == \"win32\"", "httptools (>=0.6.3)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.15.1) ; sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\"", "watchfiles (>=0.13)", "websockets (>=10.4)"] + +[[package]] +name = "validators" +version = "0.35.0" +description = "Python Data Validation for Humansβ„’" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "validators-0.35.0-py3-none-any.whl", hash = "sha256:e8c947097eae7892cb3d26868d637f79f47b4a0554bc6b80065dfe5aac3705dd"}, + {file = "validators-0.35.0.tar.gz", hash = "sha256:992d6c48a4e77c81f1b4daba10d16c3a9bb0dbb79b3a19ea847ff0928e70497a"}, +] + +[package.extras] +crypto-eth-addresses = ["eth-hash[pycryptodome] (>=0.7.0)"] + +[[package]] +name = "vulture" +version = "2.14" +description = "Find dead code" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "vulture-2.14-py2.py3-none-any.whl", hash = "sha256:d9a90dba89607489548a49d557f8bac8112bd25d3cbc8aeef23e860811bd5ed9"}, + {file = "vulture-2.14.tar.gz", hash = "sha256:cb8277902a1138deeab796ec5bef7076a6e0248ca3607a3f3dee0b6d9e9b8415"}, +] + +[package.dependencies] +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} + +[[package]] +name = "zipp" +version = "3.23.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and python_version < \"3.12\"" +files = [ + {file = "zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e"}, + {file = "zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166"}, +] + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["big-O", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more_itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] +type = ["pytest-mypy"] + +[extras] +mongodb = ["pymongo"] + +[metadata] +lock-version = "2.1" +python-versions = ">=3.10,<3.13" +content-hash = "349ea21b64217dca053564867ff9f7cf5375fe7c8dee163a5689d9f3a25fd371" diff --git a/pyproject.toml b/pyproject.toml index 054fc24..5bf28fe 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,74 +1,74 @@ -[project] -name = "tiny" -version = "1.0.0" -description = "A package for URL Shortener with QR Code generation" -authors = [ - { name = "recursivezero", email = "152776938+recursivezero@users.noreply.github.com" }, -] -maintainers = [ - { name = "Harsh Mishra", email = "187759129+harshmishra2701@users.noreply.github.com" }, -] -license = { text = "MIT" } -readme = "README.md" -requires-python = ">=3.10,<3.13" -keywords = ["url-shortener", "qr-code", "fastapi", "qrcode"] -classifiers = [ - "Programming Language :: Python :: 3", - "Topic :: Software Development :: Libraries", -] - -# Use PEP 508 syntax for production dependencies -dependencies = [ - "python-dotenv>=1.0.1", - "qrcode>=8.2", - "pillow>=10.4.0", - "redis>=7.1.0,<8.0.0", - "uvicorn>=0.40.0,<0.41.0", - "fastapi>=0.128.0,<0.129.0", - "pydantic>=2.12.5,<3.0.0", - "validators>=0.35.0,<0.36.0", - "itsdangerous>=2.2.0,<3.0.0", - "python-multipart>=0.0.22,<0.0.23", - "jinja2>=3.1.2", -] -[project.optional-dependencies] -mongodb = ["pymongo>=4.16.0,<5.0.0"] - -[project.scripts] -tiny = "app.cli:main" -lint = "app.utils.lint:main" - -# MODERN: Use dependency-groups (PEP 735) instead of tool.poetry.group -[dependency-groups] -dev = [ - "black>=24.10.0", - "flake8>=7.1.1", - "mypy>=1.14.1", - "pip-audit>=2.9.0", - "ruff>=0.5.3", - "twine>=6.2.0", - "vulture>=2.3", - "pymongo>=4.16.0", - "types-pillow>=10.2.0.20240822", - "types-pywin32>=308.0.0.20250128", -] - -[build-system] -requires = ["poetry-core>=2.0.0"] -build-backend = "poetry.core.masonry.api" - -[tool.poetry] -# We keep this for Poetry-specific settings like the package location -packages = [{ include = "app" }] -requires-poetry = ">=2.0.0" - -[tool.poetry.requires-plugins] -poetry-plugin-export = "^1.9" - -[tool.poetry-auto-export] -output = "requirements.txt" -without_hashes = true -without = ["dev"] - -[tool.flake8] -max_line_length = 190 +[project] +name = "tiny" +version = "1.0.0" +description = "A package for URL Shortener with QR Code generation" +authors = [ + { name = "recursivezero", email = "152776938+recursivezero@users.noreply.github.com" }, +] +maintainers = [ + { name = "Harsh Mishra", email = "187759129+harshmishra2701@users.noreply.github.com" }, +] +license = { text = "MIT" } +readme = "README.md" +requires-python = ">=3.10,<3.13" +keywords = ["url-shortener", "qr-code", "fastapi", "qrcode"] +classifiers = [ + "Programming Language :: Python :: 3", + "Topic :: Software Development :: Libraries", +] + +# Use PEP 508 syntax for production dependencies +dependencies = [ + "python-dotenv>=1.0.1", + "qrcode>=8.2", + "pillow>=10.4.0", + "redis>=7.1.0,<8.0.0", + "uvicorn>=0.40.0,<0.41.0", + "fastapi>=0.128.0,<0.129.0", + "pydantic>=2.12.5,<3.0.0", + "validators>=0.35.0,<0.36.0", + "itsdangerous>=2.2.0,<3.0.0", + "python-multipart>=0.0.22,<0.0.23", + "jinja2>=3.1.2", +] +[project.optional-dependencies] +mongodb = ["pymongo>=4.16.0,<5.0.0"] + +[project.scripts] +tiny = "app.cli:main" +lint = "app.utils.lint:main" + +# MODERN: Use dependency-groups (PEP 735) instead of tool.poetry.group +[dependency-groups] +dev = [ + "black>=24.10.0", + "flake8>=7.1.1", + "mypy>=1.14.1", + "pip-audit>=2.9.0", + "ruff>=0.5.3", + "twine>=6.2.0", + "vulture>=2.3", + "pymongo>=4.16.0", + "types-pillow>=10.2.0.20240822", + "types-pywin32>=308.0.0.20250128", +] + +[build-system] +requires = ["poetry-core>=2.0.0"] +build-backend = "poetry.core.masonry.api" + +[tool.poetry] +# We keep this for Poetry-specific settings like the package location +packages = [{ include = "app" }] +requires-poetry = ">=2.0.0" + +[tool.poetry.requires-plugins] +poetry-plugin-export = "^1.9" + +[tool.poetry-auto-export] +output = "requirements.txt" +without_hashes = true +without = ["dev"] + +[tool.flake8] +max_line_length = 190 diff --git a/request/urls.json b/request/urls.json index 5d8e5a7..7f48e57 100644 --- a/request/urls.json +++ b/request/urls.json @@ -1,32 +1,32 @@ -[ - { - "url": "https://example.com" - }, - { - "url": "https://google.com" - }, - { - "url": "htt//github.com" - }, - { - "url": "https://stackoverflow.com" - }, - { - "url": "https://microsoft.com" - }, - { - "url": "https://apple.com" - }, - { - "url": "https:openai.com" - }, - { - "url": "https://python.org" - }, - { - "url": "https://fastapi.tiangolo.com" - }, - { - "url": "https://docs.github.com" - } +[ + { + "url": "https://example.com" + }, + { + "url": "https://google.com" + }, + { + "url": "htt//github.com" + }, + { + "url": "https://stackoverflow.com" + }, + { + "url": "https://microsoft.com" + }, + { + "url": "https://apple.com" + }, + { + "url": "https:openai.com" + }, + { + "url": "https://python.org" + }, + { + "url": "https://fastapi.tiangolo.com" + }, + { + "url": "https://docs.github.com" + } ] \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index d52bc8e..4f2abec 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,25 +1,25 @@ -annotated-doc==0.0.4 ; python_version >= "3.10" and python_version < "3.13" -annotated-types==0.7.0 ; python_version >= "3.10" and python_version < "3.13" -anyio==4.12.1 ; python_version >= "3.10" and python_version < "3.13" -async-timeout==5.0.1 ; python_version >= "3.10" and python_full_version < "3.11.3" -click==8.3.1 ; python_version >= "3.10" and python_version < "3.13" -colorama==0.4.6 ; python_version >= "3.10" and python_version < "3.13" and (sys_platform == "win32" or platform_system == "Windows") -dnspython==2.8.0 ; python_version >= "3.10" and python_version < "3.13" -exceptiongroup==1.3.1 ; python_version == "3.10" -fastapi==0.128.7 ; python_version >= "3.10" and python_version < "3.13" -h11==0.16.0 ; python_version >= "3.10" and python_version < "3.13" -idna==3.11 ; python_version >= "3.10" and python_version < "3.13" -itsdangerous==2.2.0 ; python_version >= "3.10" and python_version < "3.13" -pillow==12.1.0 ; python_version >= "3.10" and python_version < "3.13" -pydantic-core==2.41.5 ; python_version >= "3.10" and python_version < "3.13" -pydantic==2.12.5 ; python_version >= "3.10" and python_version < "3.13" -pymongo==4.16.0 ; python_version >= "3.10" and python_version < "3.13" -python-dotenv==1.2.1 ; python_version >= "3.10" and python_version < "3.13" -python-multipart==0.0.22 ; python_version >= "3.10" and python_version < "3.13" -qrcode==8.2 ; python_version >= "3.10" and python_version < "3.13" -redis==7.1.1 ; python_version >= "3.10" and python_version < "3.13" -starlette==0.52.1 ; python_version >= "3.10" and python_version < "3.13" -typing-extensions==4.15.0 ; python_version >= "3.10" and python_version < "3.13" -typing-inspection==0.4.2 ; python_version >= "3.10" and python_version < "3.13" -uvicorn==0.40.0 ; python_version >= "3.10" and python_version < "3.13" -validators==0.35.0 ; python_version >= "3.10" and python_version < "3.13" +annotated-doc==0.0.4 ; python_version >= "3.10" and python_version < "3.13" +annotated-types==0.7.0 ; python_version >= "3.10" and python_version < "3.13" +anyio==4.12.1 ; python_version >= "3.10" and python_version < "3.13" +async-timeout==5.0.1 ; python_version >= "3.10" and python_full_version < "3.11.3" +click==8.3.1 ; python_version >= "3.10" and python_version < "3.13" +colorama==0.4.6 ; python_version >= "3.10" and python_version < "3.13" and (sys_platform == "win32" or platform_system == "Windows") +dnspython==2.8.0 ; python_version >= "3.10" and python_version < "3.13" +exceptiongroup==1.3.1 ; python_version == "3.10" +fastapi==0.128.7 ; python_version >= "3.10" and python_version < "3.13" +h11==0.16.0 ; python_version >= "3.10" and python_version < "3.13" +idna==3.11 ; python_version >= "3.10" and python_version < "3.13" +itsdangerous==2.2.0 ; python_version >= "3.10" and python_version < "3.13" +pillow==12.1.0 ; python_version >= "3.10" and python_version < "3.13" +pydantic-core==2.41.5 ; python_version >= "3.10" and python_version < "3.13" +pydantic==2.12.5 ; python_version >= "3.10" and python_version < "3.13" +pymongo==4.16.0 ; python_version >= "3.10" and python_version < "3.13" +python-dotenv==1.2.1 ; python_version >= "3.10" and python_version < "3.13" +python-multipart==0.0.22 ; python_version >= "3.10" and python_version < "3.13" +qrcode==8.2 ; python_version >= "3.10" and python_version < "3.13" +redis==7.1.1 ; python_version >= "3.10" and python_version < "3.13" +starlette==0.52.1 ; python_version >= "3.10" and python_version < "3.13" +typing-extensions==4.15.0 ; python_version >= "3.10" and python_version < "3.13" +typing-inspection==0.4.2 ; python_version >= "3.10" and python_version < "3.13" +uvicorn==0.40.0 ; python_version >= "3.10" and python_version < "3.13" +validators==0.35.0 ; python_version >= "3.10" and python_version < "3.13" From 20f41999d9fe7bfabd6f7152f6d92f749f468c24 Mon Sep 17 00:00:00 2001 From: harshmishra2701 Date: Thu, 12 Feb 2026 17:15:47 +0530 Subject: [PATCH 07/18] [RTY-260011]: feat(cache): add cache-aside + write-through DB strategy --- app/main.py | 132 ++++++++++++++++++++++++++++++-------------- app/utils/cache.py | 84 +++++++++++++++++++++++++++- app/utils/helper.py | 6 +- requirements.txt | 6 +- 4 files changed, 181 insertions(+), 47 deletions(-) diff --git a/app/main.py b/app/main.py index d2c1543..cb70c84 100644 --- a/app/main.py +++ b/app/main.py @@ -29,6 +29,8 @@ class PyMongoError(Exception): rev_cache, set_cache_pair, url_cache, + add_recent, + get_recent, ) from app.utils.config import load_env from app.utils.helper import ( @@ -45,8 +47,8 @@ class PyMongoError(Exception): # ----------------------------- @asynccontextmanager async def lifespan(app: FastAPI): - load_env() # βœ… load env ONCE - connected = db_data.connect_db() # βœ… connect DB ONCE + load_env() + connected = db_data.connect_db() app.state.db_available = connected yield @@ -74,7 +76,6 @@ def build_short_url(short_code: str, request_host_url: str) -> str: async def index(request: Request): session = request.session - # Pop session variables new_short_url = session.pop("new_short_url", None) qr_enabled = session.pop("qr_enabled", False) qr_type = session.pop("qr_type", "short") @@ -86,27 +87,23 @@ async def index(request: Request): qr_image = None qr_data = None - # --- RESTORED GENERATION LOGIC --- if qr_enabled and new_short_url and short_code: qr_data = new_short_url if qr_type == "short" else original_url qr_filename = f"{short_code}.png" - # Ensure the directory exists (Ubuntu best practice) qr_dir = STATIC_DIR / "qr" qr_dir.mkdir(parents=True, exist_ok=True) - # Generate the physical file generate_qr_with_logo(qr_data, str(qr_dir / qr_filename)) qr_image = f"/static/qr/{qr_filename}" - # -------------------------------- - # Fetch URLs for the Bento Grid - all_urls = [] if db_available(request) and db_data.urls is not None: try: all_urls = list(db_data.urls.find().sort("created_at", -1).limit(10)) except PyMongoError: - all_urls = [] + all_urls = get_recent() + else: + all_urls = get_recent() return templates.TemplateResponse( "index.html", @@ -146,37 +143,46 @@ async def create_short_url( ) return RedirectResponse("/", status_code=303) - short_code = None - - if db_available(request) and db_data.urls is not None: - try: - existing = db_data.urls.find_one({"original_url": original_url}) - if existing: - short_code = existing["short_code"] - session["info_message"] = ( - "Already shortened before β€” using existing short URL." - ) - except PyMongoError: - pass - - if not short_code: - cached_short = get_short_from_cache(original_url) - short_code = cached_short or generate_code() - set_cache_pair(short_code, original_url) + cached_short = get_short_from_cache(original_url) + if cached_short: + short_code = cached_short + session["info_message"] = "Already shortened before β€” fetched from cache." + add_recent(short_code, original_url) + else: + short_code = None if db_available(request) and db_data.urls is not None: try: - db_data.urls.insert_one( - { - "short_code": short_code, - "original_url": original_url, - "created_at": datetime.datetime.utcnow(), - "visit_count": 0, - } - ) + existing = db_data.urls.find_one({"original_url": original_url}) + if existing: + short_code = existing["short_code"] + set_cache_pair(short_code, original_url) + session["info_message"] = ( + "Already shortened before β€” fetched from database." + ) + add_recent(short_code, original_url) except PyMongoError: pass + if not short_code: + short_code = generate_code() + set_cache_pair(short_code, original_url) + + if db_available(request) and db_data.urls is not None: + try: + db_data.urls.insert_one( + { + "short_code": short_code, + "original_url": original_url, + "created_at": datetime.datetime.utcnow(), + "visit_count": 0, + } + ) + except PyMongoError: + pass + + add_recent(short_code, original_url) + new_short_url = build_short_url(short_code, str(request.base_url)) session.update( { @@ -193,28 +199,65 @@ async def create_short_url( @app.get("/recent", response_class=HTMLResponse) async def recent_urls(request: Request): - recent_urls_list = [] if db_available(request) and db_data.urls is not None: try: - recent_urls_list = list(db_data.urls.find().sort("created_at", -1)) + recent_urls_list = list( + db_data.urls.find().sort("created_at", -1).limit(10) + ) except PyMongoError: - pass + recent_urls_list = get_recent() + else: + # βœ… Force recent URLs from cache when DB is down + recent_urls_list = get_recent() + + # βœ… Normalize cache data to DB shape so template doesn't crash + normalized = [] + for item in recent_urls_list: + normalized.append( + { + "short_code": item.get("short_code"), + "original_url": item.get("original_url"), + "created_at": item.get("created_at"), # None is OK + "visit_count": item.get("visit_count", 0), + } + ) return templates.TemplateResponse( "recent.html", - {"request": request, "urls": recent_urls_list, "format_date": format_date}, + { + "request": request, + "urls": normalized, + "format_date": format_date, # keep your existing formatter + }, ) @app.post("/delete/{short_code}") async def delete_url(request: Request, short_code: str): + # Delete from DB (if available) if db_available(request) and db_data.urls is not None: try: db_data.urls.delete_one({"short_code": short_code}) except PyMongoError: return PlainTextResponse("Database connection lost.", status_code=503) - url_cache.pop(short_code, None) + # βœ… Delete from cache (short_code -> url) + cached = url_cache.pop(short_code, None) + + # βœ… Delete from reverse cache (url -> short_code) + if cached: + rev_cache.pop(cached.get("url"), None) + + # βœ… Delete from recent URLs cache + try: + from app.utils.cache import recent_urls # local import to avoid circulars + + recent_urls[:] = [ + item for item in recent_urls if item.get("short_code") != short_code + ] + except Exception: + pass + return PlainTextResponse("", status_code=204) @@ -222,6 +265,7 @@ async def delete_url(request: Request, short_code: str): async def redirect_short(request: Request, short_code: str): cached_url = get_from_cache(short_code) if cached_url: + add_recent(short_code, cached_url) return RedirectResponse(cached_url) if not db_available(request) or db_data.urls is None: @@ -239,6 +283,7 @@ async def redirect_short(request: Request, short_code: str): return PlainTextResponse("Invalid or expired short URL", status_code=404) set_cache_pair(short_code, doc["original_url"]) + add_recent(short_code, doc["original_url"]) return RedirectResponse(doc["original_url"]) @@ -255,5 +300,10 @@ async def debug_cache(): return { "url_cache": url_cache, "rev_cache": rev_cache, - "size": {"url_cache": len(url_cache), "rev_cache": len(rev_cache)}, + "recent": get_recent(), + "size": { + "url_cache": len(url_cache), + "rev_cache": len(rev_cache), + "recent": len(get_recent()), + }, } diff --git a/app/utils/cache.py b/app/utils/cache.py index 8d4cced..7fb49b4 100644 --- a/app/utils/cache.py +++ b/app/utils/cache.py @@ -14,14 +14,24 @@ class RevCacheItem(TypedDict): expires_at: float +# short_code -> original_url url_cache: dict[str, UrlCacheItem] = {} + +# original_url -> short_code rev_cache: dict[str, RevCacheItem] = {} +def _now() -> float: + return time.time() + + def get_from_cache(short_code: str) -> str | None: data = url_cache.get(short_code) - if not data or data["expires_at"] < time.time(): + if not data: + return None + + if data["expires_at"] < _now(): url_cache.pop(short_code, None) return None @@ -31,7 +41,10 @@ def get_from_cache(short_code: str) -> str | None: def get_short_from_cache(original_url: str) -> str | None: data = rev_cache.get(original_url) - if not data or data["expires_at"] < time.time(): + if not data: + return None + + if data["expires_at"] < _now(): rev_cache.pop(original_url, None) return None @@ -39,7 +52,7 @@ def get_short_from_cache(original_url: str) -> str | None: def set_cache_pair(short_code: str, original_url: str) -> None: - expires_at = time.time() + CACHE_TTL + expires_at = _now() + CACHE_TTL url_cache[short_code] = { "url": original_url, @@ -50,3 +63,68 @@ def set_cache_pair(short_code: str, original_url: str) -> None: "short_code": short_code, "expires_at": expires_at, } + + +def clear_cache() -> None: + """ + Useful for tests or if DB goes down and you want to reset cache. + """ + url_cache.clear() + rev_cache.clear() + + +def cleanup_expired() -> None: + """ + Optional: Manually remove expired cache entries. + Can be called periodically (cron/background task). + """ + now = _now() + + expired_short_codes = [ + key for key, value in url_cache.items() if value["expires_at"] < now + ] + + for key in expired_short_codes: + url_cache.pop(key, None) + + expired_urls = [ + key for key, value in rev_cache.items() if value["expires_at"] < now + ] + + for key in expired_urls: + rev_cache.pop(key, None) + + +# ---- Recent URLs Cache (Unique, Ordered, DB-shaped) ---- + +MAX_RECENT = 10 +recent_urls: list[dict] = [] # same shape as DB docs + + +def add_recent(short_code: str, original_url: str) -> None: + global recent_urls + + # Remove duplicates + recent_urls = [ + item + for item in recent_urls + if item["short_code"] != short_code and item["original_url"] != original_url + ] + + # Insert newest on top (DB-like shape) + recent_urls.insert( + 0, + { + "short_code": short_code, + "original_url": original_url, + "created_at": None, # DB fallback + "visit_count": 0, # default safe value + }, + ) + + # Limit size + recent_urls = recent_urls[:MAX_RECENT] + + +def get_recent() -> list[dict]: + return recent_urls diff --git a/app/utils/helper.py b/app/utils/helper.py index 07bdcf3..55fb750 100644 --- a/app/utils/helper.py +++ b/app/utils/helper.py @@ -23,6 +23,10 @@ def generate_code(length: int = 6) -> str: def format_date(dt): + if not dt: + return "Just now (cache)" + if dt.tzinfo is None: dt = dt.replace(tzinfo=timezone.utc) - return dt.strftime("%d %b %Y") + + return dt.strftime("%d %b %Y, %I:%M %p") diff --git a/requirements.txt b/requirements.txt index 4f2abec..bf81eea 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,11 +6,13 @@ click==8.3.1 ; python_version >= "3.10" and python_version < "3.13" colorama==0.4.6 ; python_version >= "3.10" and python_version < "3.13" and (sys_platform == "win32" or platform_system == "Windows") dnspython==2.8.0 ; python_version >= "3.10" and python_version < "3.13" exceptiongroup==1.3.1 ; python_version == "3.10" -fastapi==0.128.7 ; python_version >= "3.10" and python_version < "3.13" +fastapi==0.128.8 ; python_version >= "3.10" and python_version < "3.13" h11==0.16.0 ; python_version >= "3.10" and python_version < "3.13" idna==3.11 ; python_version >= "3.10" and python_version < "3.13" itsdangerous==2.2.0 ; python_version >= "3.10" and python_version < "3.13" -pillow==12.1.0 ; python_version >= "3.10" and python_version < "3.13" +jinja2==3.1.6 ; python_version >= "3.10" and python_version < "3.13" +markupsafe==3.0.3 ; python_version >= "3.10" and python_version < "3.13" +pillow==12.1.1 ; python_version >= "3.10" and python_version < "3.13" pydantic-core==2.41.5 ; python_version >= "3.10" and python_version < "3.13" pydantic==2.12.5 ; python_version >= "3.10" and python_version < "3.13" pymongo==4.16.0 ; python_version >= "3.10" and python_version < "3.13" From bd92cf11fbc5df80cd60d7108b6bb1f14a8c5124 Mon Sep 17 00:00:00 2001 From: harshmishra2701 Date: Thu, 12 Feb 2026 18:31:58 +0530 Subject: [PATCH 08/18] [RTY-260011]: refactor(config): centralize constants and env loading --- app/main.py | 20 +++++---------- app/utils/cache.py | 4 +-- app/utils/config.py | 62 +++++++++++++++++++++++++++++++++++++++++++++ app/utils/helper.py | 5 ++-- 4 files changed, 73 insertions(+), 18 deletions(-) diff --git a/app/main.py b/app/main.py index cb70c84..ea68815 100644 --- a/app/main.py +++ b/app/main.py @@ -1,5 +1,4 @@ import datetime -import os from contextlib import asynccontextmanager from pathlib import Path from typing import TYPE_CHECKING, Optional @@ -32,7 +31,7 @@ class PyMongoError(Exception): add_recent, get_recent, ) -from app.utils.config import load_env +from app.utils.config import load_env, SESSION_SECRET, DOMAIN from app.utils.helper import ( format_date, generate_code, @@ -54,7 +53,7 @@ async def lifespan(app: FastAPI): app = FastAPI(title="TinyURL", lifespan=lifespan) -app.add_middleware(SessionMiddleware, secret_key="super-secret-key") +app.add_middleware(SessionMiddleware, secret_key=SESSION_SECRET) BASE_DIR = Path(__file__).resolve().parent STATIC_DIR = BASE_DIR / "static" @@ -68,7 +67,7 @@ def db_available(request: Request) -> bool: def build_short_url(short_code: str, request_host_url: str) -> str: - base_url = os.getenv("DOMAIN", request_host_url).rstrip("/") + base_url = DOMAIN.rstrip("/") return f"{base_url}/{short_code}" @@ -207,17 +206,15 @@ async def recent_urls(request: Request): except PyMongoError: recent_urls_list = get_recent() else: - # βœ… Force recent URLs from cache when DB is down recent_urls_list = get_recent() - # βœ… Normalize cache data to DB shape so template doesn't crash normalized = [] for item in recent_urls_list: normalized.append( { "short_code": item.get("short_code"), "original_url": item.get("original_url"), - "created_at": item.get("created_at"), # None is OK + "created_at": item.get("created_at"), "visit_count": item.get("visit_count", 0), } ) @@ -227,30 +224,26 @@ async def recent_urls(request: Request): { "request": request, "urls": normalized, - "format_date": format_date, # keep your existing formatter + "format_date": format_date, }, ) @app.post("/delete/{short_code}") async def delete_url(request: Request, short_code: str): - # Delete from DB (if available) if db_available(request) and db_data.urls is not None: try: db_data.urls.delete_one({"short_code": short_code}) except PyMongoError: return PlainTextResponse("Database connection lost.", status_code=503) - # βœ… Delete from cache (short_code -> url) cached = url_cache.pop(short_code, None) - # βœ… Delete from reverse cache (url -> short_code) if cached: rev_cache.pop(cached.get("url"), None) - # βœ… Delete from recent URLs cache try: - from app.utils.cache import recent_urls # local import to avoid circulars + from app.utils.cache import recent_urls recent_urls[:] = [ item for item in recent_urls if item.get("short_code") != short_code @@ -304,6 +297,5 @@ async def debug_cache(): "size": { "url_cache": len(url_cache), "rev_cache": len(rev_cache), - "recent": len(get_recent()), }, } diff --git a/app/utils/cache.py b/app/utils/cache.py index 7fb49b4..edaf2fb 100644 --- a/app/utils/cache.py +++ b/app/utils/cache.py @@ -1,7 +1,7 @@ import time from typing import TypedDict -CACHE_TTL = 900 # 15 minutes +from app.utils.config import CACHE_TTL, MAX_RECENT_URLS class UrlCacheItem(TypedDict): @@ -97,7 +97,7 @@ def cleanup_expired() -> None: # ---- Recent URLs Cache (Unique, Ordered, DB-shaped) ---- -MAX_RECENT = 10 +MAX_RECENT = MAX_RECENT_URLS recent_urls: list[dict] = [] # same shape as DB docs diff --git a/app/utils/config.py b/app/utils/config.py index c38c606..c4ad05d 100644 --- a/app/utils/config.py +++ b/app/utils/config.py @@ -13,3 +13,65 @@ def load_env(): load_dotenv(file_map.get(env, ".env.development"), override=True) print(f"Environment selected: {env}") print(f"MODE value: {os.getenv('MODE')}") + + +# βœ… IMPORTANT: Load env immediately when config is imported +load_env() + + +# ------------------------- +# Helpers +# ------------------------- +def _get_bool(key: str, default: bool) -> bool: + return os.getenv(key, str(default)).lower() in ("1", "true", "yes", "on") + + +def _get_int(key: str, default: int) -> int: + try: + return int(os.getenv(key, default)) + except (TypeError, ValueError): + return default + + +# ------------------------- +# App (constants + light env override) +# ------------------------- +APP_NAME = "TinyURL" +MODE = os.getenv("MODE", "local") +DEBUG = MODE == "local" # auto-debug in local + +API_VERSION = os.getenv("API_VERSION", "v1") + +# ------------------------- +# Server +# ------------------------- +HOST = "127.0.0.1" +PORT = _get_int("PORT", 8000) + +# If DOMAIN not provided, derive from HOST + PORT +DOMAIN = os.getenv("DOMAIN", f"http://{HOST}:{PORT}") + +# ------------------------- +# Database +# ------------------------- +MONGO_URI = os.getenv("MONGO_URI") +MONGO_DB_NAME = "tiny_url" + +# ------------------------- +# Cache (constants) +# ------------------------- +USE_CACHE = True +CACHE_TTL = 900 # 15 minutes +MAX_CACHE_SIZE = 10_000 +MAX_RECENT_URLS = 10 + +# ------------------------- +# Security / Sessions +# ------------------------- +SESSION_SECRET = os.getenv("SESSION_SECRET", "super-secret-key") + +# ------------------------- +# Short URL (constants) +# ------------------------- +SHORT_CODE_LENGTH = 6 +MAX_URL_LENGTH = 2048 diff --git a/app/utils/helper.py b/app/utils/helper.py index 55fb750..30be0b2 100644 --- a/app/utils/helper.py +++ b/app/utils/helper.py @@ -2,6 +2,7 @@ import random from datetime import timezone import validators +from app.utils.config import SHORT_CODE_LENGTH def is_valid_url(url: str) -> bool: @@ -17,14 +18,14 @@ def sanitize_url(url: str) -> str: return url -def generate_code(length: int = 6) -> str: +def generate_code(length: int = SHORT_CODE_LENGTH) -> str: chars = string.ascii_letters + string.digits return "".join(random.choice(chars) for _ in range(length)) def format_date(dt): if not dt: - return "Just now (cache)" + return "Just now" if dt.tzinfo is None: dt = dt.replace(tzinfo=timezone.utc) From 58674e4ac0dab2e09f27b16cc808827317ecdfce Mon Sep 17 00:00:00 2001 From: harshmishra2701 Date: Fri, 13 Feb 2026 17:15:39 +0530 Subject: [PATCH 09/18] [RTY-260011]: feat: add MongoDB connection utility with defensive imports - Implemented a new module for MongoDB connection in app/utils/data.py. --- app/api/fast_api.py | 2 +- app/db/__init__.py | 0 app/db/data.py | 47 --------- app/main.py | 85 ++++++++------- app/templates/index.html | 2 +- app/utils/cache.py | 13 +-- app/utils/data.py | 79 ++++++++++++++ poetry.lock | 223 ++++++++++++++++++++------------------- 8 files changed, 246 insertions(+), 205 deletions(-) delete mode 100644 app/db/__init__.py delete mode 100644 app/db/data.py create mode 100644 app/utils/data.py diff --git a/app/api/fast_api.py b/app/api/fast_api.py index 1a29e19..6d82b9c 100644 --- a/app/api/fast_api.py +++ b/app/api/fast_api.py @@ -21,7 +21,7 @@ class PyMongoError(Exception): from app import __version__ -from app.db import data as db_data +from app.utils import data as db_data from app.utils.cache import get_short_from_cache, set_cache_pair from app.utils.helper import generate_code, is_valid_url, sanitize_url diff --git a/app/db/__init__.py b/app/db/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/app/db/data.py b/app/db/data.py deleted file mode 100644 index 91ef6e0..0000000 --- a/app/db/data.py +++ /dev/null @@ -1,47 +0,0 @@ -import os -from typing import Any - -# --- DEFENSIVE IMPORT --- -try: - from pymongo import MongoClient - - MONGO_INSTALLED = True -except ImportError: - # This allows the app to start even if 'pip install pymongo' wasn't run - MONGO_INSTALLED = False - -client: Any = None -db: Any = None -urls: Any = None -url_stats: Any = None - - -def connect_db(): - global client, db, urls, url_stats - - # 1. Check if the library is even there - if not MONGO_INSTALLED: - print("⚠️ pymongo is not installed. Running in NO-DB mode.") - return False - - # 2. Check if the config is there - MONGO_URI = os.getenv("MONGO_URI") - DB_NAME = os.getenv("DATABASE_NAME", "tiny_url") - - if not MONGO_URI: - print("⚠️ MONGO_URI missing. Running in NO-DB mode.") - return False - - try: - client = MongoClient(MONGO_URI, serverSelectionTimeoutMS=2000) - client.admin.command("ping") - - db = client[DB_NAME] - urls = db["urls"] - url_stats = db["url_stats"] - - print(f"βœ… MongoDB connected: '{DB_NAME}'") - return True - except Exception as e: - print(f"⚠️ MongoDB connection failed: {e}. Running in NO-DB mode.") - return False diff --git a/app/main.py b/app/main.py index ea68815..861ecd1 100644 --- a/app/main.py +++ b/app/main.py @@ -21,7 +21,7 @@ class PyMongoError(Exception): from app.api.fast_api import app as api_app -from app.db import data as db_data +from app.utils import data as db_data from app.utils.cache import ( get_from_cache, get_short_from_cache, @@ -47,8 +47,7 @@ class PyMongoError(Exception): @asynccontextmanager async def lifespan(app: FastAPI): load_env() - connected = db_data.connect_db() - app.state.db_available = connected + db_data.connect_db() yield @@ -62,10 +61,6 @@ async def lifespan(app: FastAPI): templates = Jinja2Templates(directory=str(BASE_DIR / "templates")) -def db_available(request: Request) -> bool: - return getattr(request.app.state, "db_available", False) - - def build_short_url(short_code: str, request_host_url: str) -> str: base_url = DOMAIN.rstrip("/") return f"{base_url}/{short_code}" @@ -89,16 +84,16 @@ async def index(request: Request): if qr_enabled and new_short_url and short_code: qr_data = new_short_url if qr_type == "short" else original_url qr_filename = f"{short_code}.png" - qr_dir = STATIC_DIR / "qr" qr_dir.mkdir(parents=True, exist_ok=True) - generate_qr_with_logo(qr_data, str(qr_dir / qr_filename)) qr_image = f"/static/qr/{qr_filename}" - if db_available(request) and db_data.urls is not None: + collection = db_data.get_collection() + + if collection is not None: try: - all_urls = list(db_data.urls.find().sort("created_at", -1).limit(10)) + all_urls = list(collection.find().sort("created_at", -1).limit(10)) except PyMongoError: all_urls = get_recent() else: @@ -116,12 +111,12 @@ async def index(request: Request): "original_url": original_url, "error": error, "info_message": info_message, - "db_available": db_available(request), + "db_available": collection is not None, }, ) -@app.post("/", response_class=RedirectResponse) +@app.post("/shorten", response_class=RedirectResponse) async def create_short_url( request: Request, original_url: str = Form(""), @@ -129,7 +124,7 @@ async def create_short_url( qr_type: str = Form("short"), ): session = request.session - qr_enabled = generate_qr == "on" + qr_enabled = generate_qr == "true" original_url = sanitize_url(original_url) if not original_url: @@ -150,9 +145,11 @@ async def create_short_url( else: short_code = None - if db_available(request) and db_data.urls is not None: + collection = db_data.get_collection() + + if collection is not None: try: - existing = db_data.urls.find_one({"original_url": original_url}) + existing = collection.find_one({"original_url": original_url}) if existing: short_code = existing["short_code"] set_cache_pair(short_code, original_url) @@ -167,9 +164,9 @@ async def create_short_url( short_code = generate_code() set_cache_pair(short_code, original_url) - if db_available(request) and db_data.urls is not None: + if collection is not None: try: - db_data.urls.insert_one( + collection.insert_one( { "short_code": short_code, "original_url": original_url, @@ -198,11 +195,11 @@ async def create_short_url( @app.get("/recent", response_class=HTMLResponse) async def recent_urls(request: Request): - if db_available(request) and db_data.urls is not None: + collection = db_data.get_collection() + + if collection is not None: try: - recent_urls_list = list( - db_data.urls.find().sort("created_at", -1).limit(10) - ) + recent_urls_list = list(collection.find().sort("created_at", -1).limit(10)) except PyMongoError: recent_urls_list = get_recent() else: @@ -231,14 +228,15 @@ async def recent_urls(request: Request): @app.post("/delete/{short_code}") async def delete_url(request: Request, short_code: str): - if db_available(request) and db_data.urls is not None: + collection = db_data.get_collection() + + if collection is not None: try: - db_data.urls.delete_one({"short_code": short_code}) + collection.delete_one({"short_code": short_code}) except PyMongoError: return PlainTextResponse("Database connection lost.", status_code=503) cached = url_cache.pop(short_code, None) - if cached: rev_cache.pop(cached.get("url"), None) @@ -256,28 +254,35 @@ async def delete_url(request: Request, short_code: str): @app.get("/{short_code}") async def redirect_short(request: Request, short_code: str): + collection = db_data.get_collection() + + # Always try to increment visit count if DB is available + if collection is not None: + try: + doc = collection.find_one_and_update( + {"short_code": short_code}, + {"$inc": {"visit_count": 1}}, + ) + except PyMongoError: + doc = None + else: + doc = None + + # Then resolve URL (cache first, DB fallback) cached_url = get_from_cache(short_code) if cached_url: add_recent(short_code, cached_url) return RedirectResponse(cached_url) - if not db_available(request) or db_data.urls is None: - return PlainTextResponse("Database is not connected.", status_code=503) + if doc: + set_cache_pair(short_code, doc["original_url"]) + add_recent(short_code, doc["original_url"]) + return RedirectResponse(doc["original_url"]) - try: - doc = db_data.urls.find_one_and_update( - {"short_code": short_code}, - {"$inc": {"visit_count": 1}}, - ) - except PyMongoError: - return PlainTextResponse("Database connection lost.", status_code=503) - - if not doc: - return PlainTextResponse("Invalid or expired short URL", status_code=404) + if collection is None: + return PlainTextResponse("Database is not connected.", status_code=503) - set_cache_pair(short_code, doc["original_url"]) - add_recent(short_code, doc["original_url"]) - return RedirectResponse(doc["original_url"]) + return PlainTextResponse("Invalid or expired short URL", status_code=404) @app.get("/coming-soon", response_class=HTMLResponse) diff --git a/app/templates/index.html b/app/templates/index.html index 9b1ab3c..3fb543a 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -4,7 +4,7 @@

tiny URL

-
+
diff --git a/app/utils/cache.py b/app/utils/cache.py index edaf2fb..4e06e49 100644 --- a/app/utils/cache.py +++ b/app/utils/cache.py @@ -102,28 +102,23 @@ def cleanup_expired() -> None: def add_recent(short_code: str, original_url: str) -> None: - global recent_urls - - # Remove duplicates - recent_urls = [ + recent_urls[:] = [ item for item in recent_urls if item["short_code"] != short_code and item["original_url"] != original_url ] - # Insert newest on top (DB-like shape) recent_urls.insert( 0, { "short_code": short_code, "original_url": original_url, - "created_at": None, # DB fallback - "visit_count": 0, # default safe value + "created_at": None, + "visit_count": 0, }, ) - # Limit size - recent_urls = recent_urls[:MAX_RECENT] + del recent_urls[MAX_RECENT:] def get_recent() -> list[dict]: diff --git a/app/utils/data.py b/app/utils/data.py new file mode 100644 index 0000000..706fc7b --- /dev/null +++ b/app/utils/data.py @@ -0,0 +1,79 @@ +# app/db/data.py + +import os +from typing import Any, Optional + +# --- DEFENSIVE IMPORT --- +try: + from pymongo import MongoClient + from pymongo.errors import PyMongoError + + MONGO_INSTALLED = True +except ImportError: + MongoClient = None # type: ignore + + class PyMongoError(Exception): + pass + + MONGO_INSTALLED = False + + +class _DBState: + client: Optional[Any] = None + db: Optional[Any] = None + collection: Optional[Any] = None + available: bool = False + + +_state = _DBState() + + +def connect_db() -> bool: + """ + Connect to MongoDB using config from env. + Initializes shared collection. + """ + if not MONGO_INSTALLED: + print("⚠️ pymongo not installed. Running in NO-DB mode.") + _state.client = None + _state.db = None + _state.collection = None + _state.available = False + return False + + mongo_uri = os.getenv("MONGO_URI") + mongo_db_name = os.getenv("MONGO_DB_NAME", "tiny_url") + + if not mongo_uri: + print("⚠️ MONGO_URI not set. Running in NO-DB mode.") + _state.client = None + _state.db = None + _state.collection = None + _state.available = False + return False + + try: + client = MongoClient(mongo_uri, serverSelectionTimeoutMS=2000) + client.admin.command("ping") + db = client[mongo_db_name] + collection = db["urls"] + + _state.client = client + _state.db = db + _state.collection = collection + _state.available = True + + print("βœ… MongoDB connected") + return True + except PyMongoError: + print("❌ MongoDB connection failed") + _state.client = None + _state.db = None + _state.collection = None + _state.available = False + return False + + +def get_collection(): + """Return the shared Mongo collection or None.""" + return _state.collection diff --git a/poetry.lock b/poetry.lock index 1ecedbd..0d9aade 100644 --- a/poetry.lock +++ b/poetry.lock @@ -616,14 +616,14 @@ standard-no-fastapi-cloud-cli = ["email-validator (>=2.0.0)", "fastapi-cli[stand [[package]] name = "filelock" -version = "3.20.3" +version = "3.21.2" description = "A platform independent file lock." optional = false python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "filelock-3.20.3-py3-none-any.whl", hash = "sha256:4b0dda527ee31078689fc205ec4f1c1bf7d56cf88b6dc9426c4f230e46c2dce1"}, - {file = "filelock-3.20.3.tar.gz", hash = "sha256:18c57ee915c7ec61cff0ecf7f0f869936c7c30191bb0cf406f1341778d0834e1"}, + {file = "filelock-3.21.2-py3-none-any.whl", hash = "sha256:d6cd4dbef3e1bb63bc16500fc5aa100f16e405bbff3fb4231711851be50c1560"}, + {file = "filelock-3.21.2.tar.gz", hash = "sha256:cfd218cfccf8b947fce7837da312ec3359d10ef2a47c8602edd59e0bacffb708"}, ] [[package]] @@ -863,89 +863,103 @@ type = ["pygobject-stubs", "pytest-mypy (>=1.0.1)", "shtab", "types-pywin32"] [[package]] name = "librt" -version = "0.7.8" +version = "0.8.0" description = "Mypyc runtime library" optional = false python-versions = ">=3.9" groups = ["dev"] markers = "platform_python_implementation != \"PyPy\"" files = [ - {file = "librt-0.7.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b45306a1fc5f53c9330fbee134d8b3227fe5da2ab09813b892790400aa49352d"}, - {file = "librt-0.7.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:864c4b7083eeee250ed55135d2127b260d7eb4b5e953a9e5df09c852e327961b"}, - {file = "librt-0.7.8-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6938cc2de153bc927ed8d71c7d2f2ae01b4e96359126c602721340eb7ce1a92d"}, - {file = "librt-0.7.8-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:66daa6ac5de4288a5bbfbe55b4caa7bf0cd26b3269c7a476ffe8ce45f837f87d"}, - {file = "librt-0.7.8-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4864045f49dc9c974dadb942ac56a74cd0479a2aafa51ce272c490a82322ea3c"}, - {file = "librt-0.7.8-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a36515b1328dc5b3ffce79fe204985ca8572525452eacabee2166f44bb387b2c"}, - {file = "librt-0.7.8-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b7e7f140c5169798f90b80d6e607ed2ba5059784968a004107c88ad61fb3641d"}, - {file = "librt-0.7.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ff71447cb778a4f772ddc4ce360e6ba9c95527ed84a52096bd1bbf9fee2ec7c0"}, - {file = "librt-0.7.8-cp310-cp310-win32.whl", hash = "sha256:047164e5f68b7a8ebdf9fae91a3c2161d3192418aadd61ddd3a86a56cbe3dc85"}, - {file = "librt-0.7.8-cp310-cp310-win_amd64.whl", hash = "sha256:d6f254d096d84156a46a84861183c183d30734e52383602443292644d895047c"}, - {file = "librt-0.7.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ff3e9c11aa260c31493d4b3197d1e28dd07768594a4f92bec4506849d736248f"}, - {file = "librt-0.7.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ddb52499d0b3ed4aa88746aaf6f36a08314677d5c346234c3987ddc506404eac"}, - {file = "librt-0.7.8-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e9c0afebbe6ce177ae8edba0c7c4d626f2a0fc12c33bb993d163817c41a7a05c"}, - {file = "librt-0.7.8-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:631599598e2c76ded400c0a8722dec09217c89ff64dc54b060f598ed68e7d2a8"}, - {file = "librt-0.7.8-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c1ba843ae20db09b9d5c80475376168feb2640ce91cd9906414f23cc267a1ff"}, - {file = "librt-0.7.8-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b5b007bb22ea4b255d3ee39dfd06d12534de2fcc3438567d9f48cdaf67ae1ae3"}, - {file = "librt-0.7.8-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:dbd79caaf77a3f590cbe32dc2447f718772d6eea59656a7dcb9311161b10fa75"}, - {file = "librt-0.7.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:87808a8d1e0bd62a01cafc41f0fd6818b5a5d0ca0d8a55326a81643cdda8f873"}, - {file = "librt-0.7.8-cp311-cp311-win32.whl", hash = "sha256:31724b93baa91512bd0a376e7cf0b59d8b631ee17923b1218a65456fa9bda2e7"}, - {file = "librt-0.7.8-cp311-cp311-win_amd64.whl", hash = "sha256:978e8b5f13e52cf23a9e80f3286d7546baa70bc4ef35b51d97a709d0b28e537c"}, - {file = "librt-0.7.8-cp311-cp311-win_arm64.whl", hash = "sha256:20e3946863d872f7cabf7f77c6c9d370b8b3d74333d3a32471c50d3a86c0a232"}, - {file = "librt-0.7.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9b6943885b2d49c48d0cff23b16be830ba46b0152d98f62de49e735c6e655a63"}, - {file = "librt-0.7.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:46ef1f4b9b6cc364b11eea0ecc0897314447a66029ee1e55859acb3dd8757c93"}, - {file = "librt-0.7.8-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:907ad09cfab21e3c86e8f1f87858f7049d1097f77196959c033612f532b4e592"}, - {file = "librt-0.7.8-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2991b6c3775383752b3ca0204842743256f3ad3deeb1d0adc227d56b78a9a850"}, - {file = "librt-0.7.8-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03679b9856932b8c8f674e87aa3c55ea11c9274301f76ae8dc4d281bda55cf62"}, - {file = "librt-0.7.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3968762fec1b2ad34ce57458b6de25dbb4142713e9ca6279a0d352fa4e9f452b"}, - {file = "librt-0.7.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:bb7a7807523a31f03061288cc4ffc065d684c39db7644c676b47d89553c0d714"}, - {file = "librt-0.7.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad64a14b1e56e702e19b24aae108f18ad1bf7777f3af5fcd39f87d0c5a814449"}, - {file = "librt-0.7.8-cp312-cp312-win32.whl", hash = "sha256:0241a6ed65e6666236ea78203a73d800dbed896cf12ae25d026d75dc1fcd1dac"}, - {file = "librt-0.7.8-cp312-cp312-win_amd64.whl", hash = "sha256:6db5faf064b5bab9675c32a873436b31e01d66ca6984c6f7f92621656033a708"}, - {file = "librt-0.7.8-cp312-cp312-win_arm64.whl", hash = "sha256:57175aa93f804d2c08d2edb7213e09276bd49097611aefc37e3fa38d1fb99ad0"}, - {file = "librt-0.7.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4c3995abbbb60b3c129490fa985dfe6cac11d88fc3c36eeb4fb1449efbbb04fc"}, - {file = "librt-0.7.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:44e0c2cbc9bebd074cf2cdbe472ca185e824be4e74b1c63a8e934cea674bebf2"}, - {file = "librt-0.7.8-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4d2f1e492cae964b3463a03dc77a7fe8742f7855d7258c7643f0ee32b6651dd3"}, - {file = "librt-0.7.8-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:451e7ffcef8f785831fdb791bd69211f47e95dc4c6ddff68e589058806f044c6"}, - {file = "librt-0.7.8-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3469e1af9f1380e093ae06bedcbdd11e407ac0b303a56bbe9afb1d6824d4982d"}, - {file = "librt-0.7.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f11b300027ce19a34f6d24ebb0a25fd0e24a9d53353225a5c1e6cadbf2916b2e"}, - {file = "librt-0.7.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4adc73614f0d3c97874f02f2c7fd2a27854e7e24ad532ea6b965459c5b757eca"}, - {file = "librt-0.7.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:60c299e555f87e4c01b2eca085dfccda1dde87f5a604bb45c2906b8305819a93"}, - {file = "librt-0.7.8-cp313-cp313-win32.whl", hash = "sha256:b09c52ed43a461994716082ee7d87618096851319bf695d57ec123f2ab708951"}, - {file = "librt-0.7.8-cp313-cp313-win_amd64.whl", hash = "sha256:f8f4a901a3fa28969d6e4519deceab56c55a09d691ea7b12ca830e2fa3461e34"}, - {file = "librt-0.7.8-cp313-cp313-win_arm64.whl", hash = "sha256:43d4e71b50763fcdcf64725ac680d8cfa1706c928b844794a7aa0fa9ac8e5f09"}, - {file = "librt-0.7.8-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:be927c3c94c74b05128089a955fba86501c3b544d1d300282cc1b4bd370cb418"}, - {file = "librt-0.7.8-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7b0803e9008c62a7ef79058233db7ff6f37a9933b8f2573c05b07ddafa226611"}, - {file = "librt-0.7.8-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:79feb4d00b2a4e0e05c9c56df707934f41fcb5fe53fd9efb7549068d0495b758"}, - {file = "librt-0.7.8-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b9122094e3f24aa759c38f46bd8863433820654927370250f460ae75488b66ea"}, - {file = "librt-0.7.8-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7e03bea66af33c95ce3addf87a9bf1fcad8d33e757bc479957ddbc0e4f7207ac"}, - {file = "librt-0.7.8-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f1ade7f31675db00b514b98f9ab9a7698c7282dad4be7492589109471852d398"}, - {file = "librt-0.7.8-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a14229ac62adcf1b90a15992f1ab9c69ae8b99ffb23cb64a90878a6e8a2f5b81"}, - {file = "librt-0.7.8-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5bcaaf624fd24e6a0cb14beac37677f90793a96864c67c064a91458611446e83"}, - {file = "librt-0.7.8-cp314-cp314-win32.whl", hash = "sha256:7aa7d5457b6c542ecaed79cec4ad98534373c9757383973e638ccced0f11f46d"}, - {file = "librt-0.7.8-cp314-cp314-win_amd64.whl", hash = "sha256:3d1322800771bee4a91f3b4bd4e49abc7d35e65166821086e5afd1e6c0d9be44"}, - {file = "librt-0.7.8-cp314-cp314-win_arm64.whl", hash = "sha256:5363427bc6a8c3b1719f8f3845ea53553d301382928a86e8fab7984426949bce"}, - {file = "librt-0.7.8-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:ca916919793a77e4a98d4a1701e345d337ce53be4a16620f063191f7322ac80f"}, - {file = "librt-0.7.8-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:54feb7b4f2f6706bb82325e836a01be805770443e2400f706e824e91f6441dde"}, - {file = "librt-0.7.8-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:39a4c76fee41007070f872b648cc2f711f9abf9a13d0c7162478043377b52c8e"}, - {file = "librt-0.7.8-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ac9c8a458245c7de80bc1b9765b177055efff5803f08e548dd4bb9ab9a8d789b"}, - {file = "librt-0.7.8-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:95b67aa7eff150f075fda09d11f6bfb26edffd300f6ab1666759547581e8f666"}, - {file = "librt-0.7.8-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:535929b6eff670c593c34ff435d5440c3096f20fa72d63444608a5aef64dd581"}, - {file = "librt-0.7.8-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:63937bd0f4d1cb56653dc7ae900d6c52c41f0015e25aaf9902481ee79943b33a"}, - {file = "librt-0.7.8-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cf243da9e42d914036fd362ac3fa77d80a41cadcd11ad789b1b5eec4daaf67ca"}, - {file = "librt-0.7.8-cp314-cp314t-win32.whl", hash = "sha256:171ca3a0a06c643bd0a2f62a8944e1902c94aa8e5da4db1ea9a8daf872685365"}, - {file = "librt-0.7.8-cp314-cp314t-win_amd64.whl", hash = "sha256:445b7304145e24c60288a2f172b5ce2ca35c0f81605f5299f3fa567e189d2e32"}, - {file = "librt-0.7.8-cp314-cp314t-win_arm64.whl", hash = "sha256:8766ece9de08527deabcd7cb1b4f1a967a385d26e33e536d6d8913db6ef74f06"}, - {file = "librt-0.7.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c7e8f88f79308d86d8f39c491773cbb533d6cb7fa6476f35d711076ee04fceb6"}, - {file = "librt-0.7.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:389bd25a0db916e1d6bcb014f11aa9676cedaa485e9ec3752dfe19f196fd377b"}, - {file = "librt-0.7.8-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:73fd300f501a052f2ba52ede721232212f3b06503fa12665408ecfc9d8fd149c"}, - {file = "librt-0.7.8-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d772edc6a5f7835635c7562f6688e031f0b97e31d538412a852c49c9a6c92d5"}, - {file = "librt-0.7.8-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bfde8a130bd0f239e45503ab39fab239ace094d63ee1d6b67c25a63d741c0f71"}, - {file = "librt-0.7.8-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fdec6e2368ae4f796fc72fad7fd4bd1753715187e6d870932b0904609e7c878e"}, - {file = "librt-0.7.8-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:00105e7d541a8f2ee5be52caacea98a005e0478cfe78c8080fbb7b5d2b340c63"}, - {file = "librt-0.7.8-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c6f8947d3dfd7f91066c5b4385812c18be26c9d5a99ca56667547f2c39149d94"}, - {file = "librt-0.7.8-cp39-cp39-win32.whl", hash = "sha256:41d7bb1e07916aeb12ae4a44e3025db3691c4149ab788d0315781b4d29b86afb"}, - {file = "librt-0.7.8-cp39-cp39-win_amd64.whl", hash = "sha256:e90a8e237753c83b8e484d478d9a996dc5e39fd5bd4c6ce32563bc8123f132be"}, - {file = "librt-0.7.8.tar.gz", hash = "sha256:1a4ede613941d9c3470b0368be851df6bb78ab218635512d0370b27a277a0862"}, + {file = "librt-0.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:db63cf3586a24241e89ca1ce0b56baaec9d371a328bd186c529b27c914c9a1ef"}, + {file = "librt-0.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ba9d9e60651615bc614be5e21a82cdb7b1769a029369cf4b4d861e4f19686fb6"}, + {file = "librt-0.8.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cb4b3ad543084ed79f186741470b251b9d269cd8b03556f15a8d1a99a64b7de5"}, + {file = "librt-0.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3d2720335020219197380ccfa5c895f079ac364b4c429e96952cd6509934d8eb"}, + {file = "librt-0.8.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9726305d3e53419d27fc8cdfcd3f9571f0ceae22fa6b5ea1b3662c2e538f833e"}, + {file = "librt-0.8.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cc3d107f603b5ee7a79b6aa6f166551b99b32fb4a5303c4dfcb4222fc6a0335e"}, + {file = "librt-0.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:41064a0c07b4cc7a81355ccc305cb097d6027002209ffca51306e65ee8293630"}, + {file = "librt-0.8.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c6e4c10761ddbc0d67d2f6e2753daf99908db85d8b901729bf2bf5eaa60e0567"}, + {file = "librt-0.8.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:ba581acad5ac8f33e2ff1746e8a57e001b47c6721873121bf8bbcf7ba8bd3aa4"}, + {file = "librt-0.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bdab762e2c0b48bab76f1a08acb3f4c77afd2123bedac59446aeaaeed3d086cf"}, + {file = "librt-0.8.0-cp310-cp310-win32.whl", hash = "sha256:6a3146c63220d814c4a2c7d6a1eacc8d5c14aed0ff85115c1dfea868080cd18f"}, + {file = "librt-0.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:bbebd2bba5c6ae02907df49150e55870fdd7440d727b6192c46b6f754723dde9"}, + {file = "librt-0.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0ce33a9778e294507f3a0e3468eccb6a698b5166df7db85661543eca1cfc5369"}, + {file = "librt-0.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8070aa3368559de81061ef752770d03ca1f5fc9467d4d512d405bd0483bfffe6"}, + {file = "librt-0.8.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:20f73d4fecba969efc15cdefd030e382502d56bb6f1fc66b580cce582836c9fa"}, + {file = "librt-0.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a512c88900bdb1d448882f5623a0b1ad27ba81a9bd75dacfe17080b72272ca1f"}, + {file = "librt-0.8.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:015e2dde6e096d27c10238bf9f6492ba6c65822dfb69d2bf74c41a8e88b7ddef"}, + {file = "librt-0.8.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1c25a131013eadd3c600686a0c0333eb2896483cbc7f65baa6a7ee761017aef9"}, + {file = "librt-0.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:21b14464bee0b604d80a638cf1ee3148d84ca4cc163dcdcecb46060c1b3605e4"}, + {file = "librt-0.8.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:05a3dd3f116747f7e1a2b475ccdc6fb637fd4987126d109e03013a79d40bf9e6"}, + {file = "librt-0.8.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:fa37f99bff354ff191c6bcdffbc9d7cdd4fc37faccfc9be0ef3a4fd5613977da"}, + {file = "librt-0.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1566dbb9d1eb0987264c9b9460d212e809ba908d2f4a3999383a84d765f2f3f1"}, + {file = "librt-0.8.0-cp311-cp311-win32.whl", hash = "sha256:70defb797c4d5402166787a6b3c66dfb3fa7f93d118c0509ffafa35a392f4258"}, + {file = "librt-0.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:db953b675079884ffda33d1dca7189fb961b6d372153750beb81880384300817"}, + {file = "librt-0.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:75d1a8cab20b2043f03f7aab730551e9e440adc034d776f15f6f8d582b0a5ad4"}, + {file = "librt-0.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:17269dd2745dbe8e42475acb28e419ad92dfa38214224b1b01020b8cac70b645"}, + {file = "librt-0.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f4617cef654fca552f00ce5ffdf4f4b68770f18950e4246ce94629b789b92467"}, + {file = "librt-0.8.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5cb11061a736a9db45e3c1293cfcb1e3caf205912dfa085734ba750f2197ff9a"}, + {file = "librt-0.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4bb00bd71b448f16749909b08a0ff16f58b079e2261c2e1000f2bbb2a4f0a45"}, + {file = "librt-0.8.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:95a719a049f0eefaf1952673223cf00d442952273cbd20cf2ed7ec423a0ef58d"}, + {file = "librt-0.8.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bd32add59b58fba3439d48d6f36ac695830388e3da3e92e4fc26d2d02670d19c"}, + {file = "librt-0.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4f764b2424cb04524ff7a486b9c391e93f93dc1bd8305b2136d25e582e99aa2f"}, + {file = "librt-0.8.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:f04ca50e847abc486fa8f4107250566441e693779a5374ba211e96e238f298b9"}, + {file = "librt-0.8.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:9ab3a3475a55b89b87ffd7e6665838e8458e0b596c22e0177e0f961434ec474a"}, + {file = "librt-0.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3e36a8da17134ffc29373775d88c04832f9ecfab1880470661813e6c7991ef79"}, + {file = "librt-0.8.0-cp312-cp312-win32.whl", hash = "sha256:4eb5e06ebcc668677ed6389164f52f13f71737fc8be471101fa8b4ce77baeb0c"}, + {file = "librt-0.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:0a33335eb59921e77c9acc05d0e654e4e32e45b014a4d61517897c11591094f8"}, + {file = "librt-0.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:24a01c13a2a9bdad20997a4443ebe6e329df063d1978bbe2ebbf637878a46d1e"}, + {file = "librt-0.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7f820210e21e3a8bf8fde2ae3c3d10106d4de9ead28cbfdf6d0f0f41f5b12fa1"}, + {file = "librt-0.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4831c44b8919e75ca0dfb52052897c1ef59fdae19d3589893fbd068f1e41afbf"}, + {file = "librt-0.8.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:88c6e75540f1f10f5e0fc5e87b4b6c290f0e90d1db8c6734f670840494764af8"}, + {file = "librt-0.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9646178cd794704d722306c2c920c221abbf080fede3ba539d5afdec16c46dad"}, + {file = "librt-0.8.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6e1af31a710e17891d9adf0dbd9a5fcd94901a3922a96499abdbf7ce658f4e01"}, + {file = "librt-0.8.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:507e94f4bec00b2f590fbe55f48cd518a208e2474a3b90a60aa8f29136ddbada"}, + {file = "librt-0.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f1178e0de0c271231a660fbef9be6acdfa1d596803464706862bef6644cc1cae"}, + {file = "librt-0.8.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:71fc517efc14f75c2f74b1f0a5d5eb4a8e06aa135c34d18eaf3522f4a53cd62d"}, + {file = "librt-0.8.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:0583aef7e9a720dd40f26a2ad5a1bf2ccbb90059dac2b32ac516df232c701db3"}, + {file = "librt-0.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5d0f76fc73480d42285c609c0ea74d79856c160fa828ff9aceab574ea4ecfd7b"}, + {file = "librt-0.8.0-cp313-cp313-win32.whl", hash = "sha256:e79dbc8f57de360f0ed987dc7de7be814b4803ef0e8fc6d3ff86e16798c99935"}, + {file = "librt-0.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:25b3e667cbfc9000c4740b282df599ebd91dbdcc1aa6785050e4c1d6be5329ab"}, + {file = "librt-0.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:e9a3a38eb4134ad33122a6d575e6324831f930a771d951a15ce232e0237412c2"}, + {file = "librt-0.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:421765e8c6b18e64d21c8ead315708a56fc24f44075059702e421d164575fdda"}, + {file = "librt-0.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:48f84830a8f8ad7918afd743fd7c4eb558728bceab7b0e38fd5a5cf78206a556"}, + {file = "librt-0.8.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:9f09d4884f882baa39a7e36bbf3eae124c4ca2a223efb91e567381d1c55c6b06"}, + {file = "librt-0.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:693697133c3b32aa9b27f040e3691be210e9ac4d905061859a9ed519b1d5a376"}, + {file = "librt-0.8.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c5512aae4648152abaf4d48b59890503fcbe86e85abc12fb9b096fe948bdd816"}, + {file = "librt-0.8.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:995d24caa6bbb34bcdd4a41df98ac6d1af637cfa8975cb0790e47d6623e70e3e"}, + {file = "librt-0.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b9aef96d7593584e31ef6ac1eb9775355b0099fee7651fae3a15bc8657b67b52"}, + {file = "librt-0.8.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:4f6e975377fbc4c9567cb33ea9ab826031b6c7ec0515bfae66a4fb110d40d6da"}, + {file = "librt-0.8.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:daae5e955764be8fd70a93e9e5133c75297f8bce1e802e1d3683b98f77e1c5ab"}, + {file = "librt-0.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7bd68cebf3131bb920d5984f75fe302d758db33264e44b45ad139385662d7bc3"}, + {file = "librt-0.8.0-cp314-cp314-win32.whl", hash = "sha256:1e6811cac1dcb27ca4c74e0ca4a5917a8e06db0d8408d30daee3a41724bfde7a"}, + {file = "librt-0.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:178707cda89d910c3b28bf5aa5f69d3d4734e0f6ae102f753ad79edef83a83c7"}, + {file = "librt-0.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:3e8b77b5f54d0937b26512774916041756c9eb3e66f1031971e626eea49d0bf4"}, + {file = "librt-0.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:789911e8fa40a2e82f41120c936b1965f3213c67f5a483fc5a41f5839a05dcbb"}, + {file = "librt-0.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2b37437e7e4ef5e15a297b36ba9e577f73e29564131d86dd75875705e97402b5"}, + {file = "librt-0.8.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:671a6152edf3b924d98a5ed5e6982ec9cb30894085482acadce0975f031d4c5c"}, + {file = "librt-0.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8992ca186a1678107b0af3d0c9303d8c7305981b9914989b9788319ed4d89546"}, + {file = "librt-0.8.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:001e5330093d887b8b9165823eca6c5c4db183fe4edea4fdc0680bbac5f46944"}, + {file = "librt-0.8.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d920789eca7ef71df7f31fd547ec0d3002e04d77f30ba6881e08a630e7b2c30e"}, + {file = "librt-0.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:82fb4602d1b3e303a58bfe6165992b5a78d823ec646445356c332cd5f5bbaa61"}, + {file = "librt-0.8.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:4d3e38797eb482485b486898f89415a6ab163bc291476bd95712e42cf4383c05"}, + {file = "librt-0.8.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:a905091a13e0884701226860836d0386b88c72ce5c2fdfba6618e14c72be9f25"}, + {file = "librt-0.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:375eda7acfce1f15f5ed56cfc960669eefa1ec8732e3e9087c3c4c3f2066759c"}, + {file = "librt-0.8.0-cp314-cp314t-win32.whl", hash = "sha256:2ccdd20d9a72c562ffb73098ac411de351b53a6fbb3390903b2d33078ef90447"}, + {file = "librt-0.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:25e82d920d4d62ad741592fcf8d0f3bda0e3fc388a184cb7d2f566c681c5f7b9"}, + {file = "librt-0.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:92249938ab744a5890580d3cb2b22042f0dce71cdaa7c1369823df62bedf7cbc"}, + {file = "librt-0.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4b705f85311ee76acec5ee70806990a51f0deb519ea0c29c1d1652d79127604d"}, + {file = "librt-0.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7ce0a8cb67e702dcb06342b2aaaa3da9fb0ddc670417879adfa088b44cf7b3b6"}, + {file = "librt-0.8.0-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:aaadec87f45a3612b6818d1db5fbfe93630669b7ee5d6bdb6427ae08a1aa2141"}, + {file = "librt-0.8.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:56901f1eec031396f230db71c59a01d450715cbbef9856bf636726994331195d"}, + {file = "librt-0.8.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b055bb3abaf69abed25743d8fc1ab691e4f51a912ee0a6f9a6c84f4bbddb283d"}, + {file = "librt-0.8.0-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1ef3bd856373cf8e7382402731f43bfe978a8613b4039e49e166e1e0dc590216"}, + {file = "librt-0.8.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2e0ffe88ebb5962f8fb0ddcbaaff30f1ea06a79501069310e1e030eafb1ad787"}, + {file = "librt-0.8.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:82e61cd1c563745ad495387c3b65806bfd453badb4adbc019df3389dddee1bf6"}, + {file = "librt-0.8.0-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:667e2513cf69bfd1e1ed9a00d6c736d5108714ec071192afb737987955888a25"}, + {file = "librt-0.8.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6b6caff69e25d80c269b1952be8493b4d94ef745f438fa619d7931066bdd26de"}, + {file = "librt-0.8.0-cp39-cp39-win32.whl", hash = "sha256:02a9fe85410cc9bef045e7cb7fd26fdde6669e6d173f99df659aa7f6335961e9"}, + {file = "librt-0.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:de076eaba208d16efb5962f99539867f8e2c73480988cb513fcf1b5dbb0c9dcf"}, + {file = "librt-0.8.0.tar.gz", hash = "sha256:cb74cdcbc0103fc988e04e5c58b0b31e8e5dd2babb9182b6f9490488eb36324b"}, ] [[package]] @@ -1545,21 +1559,16 @@ testing = ["aboutcode-toolkit (>=6.0.0)", "black", "pytest (>=6,!=7.0.0)", "pyte [[package]] name = "platformdirs" -version = "4.5.1" +version = "4.7.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31"}, - {file = "platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda"}, + {file = "platformdirs-4.7.0-py3-none-any.whl", hash = "sha256:1ed8db354e344c5bb6039cd727f096af975194b508e37177719d562b2b540ee6"}, + {file = "platformdirs-4.7.0.tar.gz", hash = "sha256:fd1a5f8599c85d49b9ac7d6e450bc2f1aaf4a23f1fe86d09952fe20ad365cf36"}, ] -[package.extras] -docs = ["furo (>=2025.9.25)", "proselint (>=0.14)", "sphinx (>=8.2.3)", "sphinx-autodoc-typehints (>=3.2)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.4.2)", "pytest-cov (>=7)", "pytest-mock (>=3.15.1)"] -type = ["mypy (>=1.18.2)"] - [[package]] name = "py-serializable" version = "2.1.0" @@ -2121,30 +2130,30 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"] [[package]] name = "ruff" -version = "0.15.0" +version = "0.15.1" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" groups = ["dev"] files = [ - {file = "ruff-0.15.0-py3-none-linux_armv6l.whl", hash = "sha256:aac4ebaa612a82b23d45964586f24ae9bc23ca101919f5590bdb368d74ad5455"}, - {file = "ruff-0.15.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:dcd4be7cc75cfbbca24a98d04d0b9b36a270d0833241f776b788d59f4142b14d"}, - {file = "ruff-0.15.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d747e3319b2bce179c7c1eaad3d884dc0a199b5f4d5187620530adf9105268ce"}, - {file = "ruff-0.15.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:650bd9c56ae03102c51a5e4b554d74d825ff3abe4db22b90fd32d816c2e90621"}, - {file = "ruff-0.15.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a6664b7eac559e3048223a2da77769c2f92b43a6dfd4720cef42654299a599c9"}, - {file = "ruff-0.15.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f811f97b0f092b35320d1556f3353bf238763420ade5d9e62ebd2b73f2ff179"}, - {file = "ruff-0.15.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:761ec0a66680fab6454236635a39abaf14198818c8cdf691e036f4bc0f406b2d"}, - {file = "ruff-0.15.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:940f11c2604d317e797b289f4f9f3fa5555ffe4fb574b55ed006c3d9b6f0eb78"}, - {file = "ruff-0.15.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bcbca3d40558789126da91d7ef9a7c87772ee107033db7191edefa34e2c7f1b4"}, - {file = "ruff-0.15.0-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:9a121a96db1d75fa3eb39c4539e607f628920dd72ff1f7c5ee4f1b768ac62d6e"}, - {file = "ruff-0.15.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:5298d518e493061f2eabd4abd067c7e4fb89e2f63291c94332e35631c07c3662"}, - {file = "ruff-0.15.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:afb6e603d6375ff0d6b0cee563fa21ab570fd15e65c852cb24922cef25050cf1"}, - {file = "ruff-0.15.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:77e515f6b15f828b94dc17d2b4ace334c9ddb7d9468c54b2f9ed2b9c1593ef16"}, - {file = "ruff-0.15.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:6f6e80850a01eb13b3e42ee0ebdf6e4497151b48c35051aab51c101266d187a3"}, - {file = "ruff-0.15.0-py3-none-win32.whl", hash = "sha256:238a717ef803e501b6d51e0bdd0d2c6e8513fe9eec14002445134d3907cd46c3"}, - {file = "ruff-0.15.0-py3-none-win_amd64.whl", hash = "sha256:dd5e4d3301dc01de614da3cdffc33d4b1b96fb89e45721f1598e5532ccf78b18"}, - {file = "ruff-0.15.0-py3-none-win_arm64.whl", hash = "sha256:c480d632cc0ca3f0727acac8b7d053542d9e114a462a145d0b00e7cd658c515a"}, - {file = "ruff-0.15.0.tar.gz", hash = "sha256:6bdea47cdbea30d40f8f8d7d69c0854ba7c15420ec75a26f463290949d7f7e9a"}, + {file = "ruff-0.15.1-py3-none-linux_armv6l.whl", hash = "sha256:b101ed7cf4615bda6ffe65bdb59f964e9f4a0d3f85cbf0e54f0ab76d7b90228a"}, + {file = "ruff-0.15.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:939c995e9277e63ea632cc8d3fae17aa758526f49a9a850d2e7e758bfef46602"}, + {file = "ruff-0.15.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:1d83466455fdefe60b8d9c8df81d3c1bbb2115cede53549d3b522ce2bc703899"}, + {file = "ruff-0.15.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9457e3c3291024866222b96108ab2d8265b477e5b1534c7ddb1810904858d16"}, + {file = "ruff-0.15.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:92c92b003e9d4f7fbd33b1867bb15a1b785b1735069108dfc23821ba045b29bc"}, + {file = "ruff-0.15.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fe5c41ab43e3a06778844c586251eb5a510f67125427625f9eb2b9526535779"}, + {file = "ruff-0.15.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:66a6dd6df4d80dc382c6484f8ce1bcceb55c32e9f27a8b94c32f6c7331bf14fb"}, + {file = "ruff-0.15.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6a4a42cbb8af0bda9bcd7606b064d7c0bc311a88d141d02f78920be6acb5aa83"}, + {file = "ruff-0.15.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ab064052c31dddada35079901592dfba2e05f5b1e43af3954aafcbc1096a5b2"}, + {file = "ruff-0.15.1-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:5631c940fe9fe91f817a4c2ea4e81f47bee3ca4aa646134a24374f3c19ad9454"}, + {file = "ruff-0.15.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:68138a4ba184b4691ccdc39f7795c66b3c68160c586519e7e8444cf5a53e1b4c"}, + {file = "ruff-0.15.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:518f9af03bfc33c03bdb4cb63fabc935341bb7f54af500f92ac309ecfbba6330"}, + {file = "ruff-0.15.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:da79f4d6a826caaea95de0237a67e33b81e6ec2e25fc7e1993a4015dffca7c61"}, + {file = "ruff-0.15.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:3dd86dccb83cd7d4dcfac303ffc277e6048600dfc22e38158afa208e8bf94a1f"}, + {file = "ruff-0.15.1-py3-none-win32.whl", hash = "sha256:660975d9cb49b5d5278b12b03bb9951d554543a90b74ed5d366b20e2c57c2098"}, + {file = "ruff-0.15.1-py3-none-win_amd64.whl", hash = "sha256:c820fef9dd5d4172a6570e5721704a96c6679b80cf7be41659ed439653f62336"}, + {file = "ruff-0.15.1-py3-none-win_arm64.whl", hash = "sha256:5ff7d5f0f88567850f45081fac8f4ec212be8d0b963e385c3f7d0d2eb4899416"}, + {file = "ruff-0.15.1.tar.gz", hash = "sha256:c590fe13fb57c97141ae975c03a1aedb3d3156030cabd740d6ff0b0d601e203f"}, ] [[package]] From c791f037d1249c9d079cac1b18e02d38ba5892b8 Mon Sep 17 00:00:00 2001 From: harshmishra2701 Date: Fri, 13 Feb 2026 21:05:31 +0530 Subject: [PATCH 10/18] [RTY-260011]: refactor(cache): remove unused fields from recent URLs cache --- app/utils/cache.py | 2 -- app/utils/data.py | 8 +++++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/utils/cache.py b/app/utils/cache.py index 4e06e49..905b94e 100644 --- a/app/utils/cache.py +++ b/app/utils/cache.py @@ -113,8 +113,6 @@ def add_recent(short_code: str, original_url: str) -> None: { "short_code": short_code, "original_url": original_url, - "created_at": None, - "visit_count": 0, }, ) diff --git a/app/utils/data.py b/app/utils/data.py index 706fc7b..2b8087e 100644 --- a/app/utils/data.py +++ b/app/utils/data.py @@ -1,6 +1,5 @@ # app/db/data.py -import os from typing import Any, Optional # --- DEFENSIVE IMPORT --- @@ -18,6 +17,9 @@ class PyMongoError(Exception): MONGO_INSTALLED = False +from app.utils.config import MONGO_URI, MONGO_DB_NAME + + class _DBState: client: Optional[Any] = None db: Optional[Any] = None @@ -41,8 +43,8 @@ def connect_db() -> bool: _state.available = False return False - mongo_uri = os.getenv("MONGO_URI") - mongo_db_name = os.getenv("MONGO_DB_NAME", "tiny_url") + mongo_uri = MONGO_URI + mongo_db_name = MONGO_DB_NAME if not mongo_uri: print("⚠️ MONGO_URI not set. Running in NO-DB mode.") From 35e2d2d6e05973bfe993c948e917b63232728465 Mon Sep 17 00:00:00 2001 From: RecursiveZero Date: Fri, 13 Feb 2026 22:39:19 +0530 Subject: [PATCH 11/18] auto assign project reusable workflow --- .github/workflows/comment-on-issue.yml | 2 +- .github/workflows/format-issue-title.yml | 2 +- .github/workflows/project-assign.yml | 19 +++++++++++++++++++ .vscode/settings.json | 4 +--- 4 files changed, 22 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/project-assign.yml diff --git a/.github/workflows/comment-on-issue.yml b/.github/workflows/comment-on-issue.yml index fb98fa4..9c52701 100644 --- a/.github/workflows/comment-on-issue.yml +++ b/.github/workflows/comment-on-issue.yml @@ -2,7 +2,7 @@ name: "Comment on New Issue" on: issues: - types: [opened, labeled] + types: [opened] workflow_dispatch: inputs: diff --git a/.github/workflows/format-issue-title.yml b/.github/workflows/format-issue-title.yml index 57881b9..e1fb2c6 100644 --- a/.github/workflows/format-issue-title.yml +++ b/.github/workflows/format-issue-title.yml @@ -2,7 +2,7 @@ name: "Auto Format Issue Title" on: issues: - types: [opened, labeled] + types: [opened, edited] permissions: issues: write diff --git a/.github/workflows/project-assign.yml b/.github/workflows/project-assign.yml new file mode 100644 index 0000000..2e81a5c --- /dev/null +++ b/.github/workflows/project-assign.yml @@ -0,0 +1,19 @@ +name: Assign Project +run-name: Assign project under issue and pull requests + +on: + issues: + types: [opened, labeled] + pull_request: + types: [opened, reopened] + +permissions: + issues: write + +jobs: + sync: + uses: recursivezero/action-club/.github/workflows/auto-assign-project.yml@main + with: + project_number: 5 # That's it! + secrets: + PROJECT_PAT: ${{ secrets.PROJECT_PAT }} diff --git a/.vscode/settings.json b/.vscode/settings.json index fda4174..a1b8a72 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -16,9 +16,7 @@ "titleBar.activeBackground": "#c5ef98", "titleBar.activeForeground": "#15202b", "titleBar.inactiveBackground": "#c5ef9899", - "titleBar.inactiveForeground": "#15202b99", - "sideBar.border": "#def6c4", - "tab.activeBorder": "#def6c4" + "titleBar.inactiveForeground": "#15202b99" }, "peacock.color": "#c5ef98" } \ No newline at end of file From dbf799f6c228e6e5a58c2ef0ab2658ab46bdc971 Mon Sep 17 00:00:00 2001 From: RecursiveZero Date: Fri, 13 Feb 2026 22:57:14 +0530 Subject: [PATCH 12/18] only during open --- .github/workflows/project-assign.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/project-assign.yml b/.github/workflows/project-assign.yml index 2e81a5c..33ebe00 100644 --- a/.github/workflows/project-assign.yml +++ b/.github/workflows/project-assign.yml @@ -3,7 +3,7 @@ run-name: Assign project under issue and pull requests on: issues: - types: [opened, labeled] + types: [opened, reopened] pull_request: types: [opened, reopened] From 35777b4ca73f4778d2e80947691447aba60b081e Mon Sep 17 00:00:00 2001 From: RecursiveZero Date: Fri, 13 Feb 2026 22:57:30 +0530 Subject: [PATCH 13/18] only opened --- .github/workflows/project-assign.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/project-assign.yml b/.github/workflows/project-assign.yml index 33ebe00..7e1f4fd 100644 --- a/.github/workflows/project-assign.yml +++ b/.github/workflows/project-assign.yml @@ -3,7 +3,7 @@ run-name: Assign project under issue and pull requests on: issues: - types: [opened, reopened] + types: [opened] pull_request: types: [opened, reopened] From e23301077ae56b960d810c09fa12bc28342f0e19 Mon Sep 17 00:00:00 2001 From: harshmishra2701 Date: Sat, 14 Feb 2026 01:21:23 +0530 Subject: [PATCH 14/18] [RTY-260011]: refactor(main,cache,data): centralize DB access and use rev_cache for recent URLs --- app/main.py | 123 ++++++++---------------------------------- app/utils/cache.py | 50 +++++++++-------- app/utils/config.py | 4 +- app/utils/data.py | 129 ++++++++++++++++++++++++++------------------ 4 files changed, 130 insertions(+), 176 deletions(-) diff --git a/app/main.py b/app/main.py index 861ecd1..562e8c0 100644 --- a/app/main.py +++ b/app/main.py @@ -1,7 +1,6 @@ -import datetime from contextlib import asynccontextmanager from pathlib import Path -from typing import TYPE_CHECKING, Optional +from typing import Optional from fastapi import FastAPI, Form, Request from fastapi.responses import HTMLResponse, PlainTextResponse, RedirectResponse @@ -9,29 +8,17 @@ from fastapi.templating import Jinja2Templates from starlette.middleware.sessions import SessionMiddleware -if TYPE_CHECKING: - from pymongo.errors import PyMongoError -else: - try: - from pymongo.errors import PyMongoError - except ImportError: - - class PyMongoError(Exception): - pass - - from app.api.fast_api import app as api_app from app.utils import data as db_data from app.utils.cache import ( get_from_cache, get_short_from_cache, + get_recent_from_cache, rev_cache, set_cache_pair, url_cache, - add_recent, - get_recent, ) -from app.utils.config import load_env, SESSION_SECRET, DOMAIN +from app.utils.config import load_env, SESSION_SECRET, DOMAIN, MAX_RECENT_URLS from app.utils.helper import ( format_date, generate_code, @@ -89,15 +76,9 @@ async def index(request: Request): generate_qr_with_logo(qr_data, str(qr_dir / qr_filename)) qr_image = f"/static/qr/{qr_filename}" - collection = db_data.get_collection() - - if collection is not None: - try: - all_urls = list(collection.find().sort("created_at", -1).limit(10)) - except PyMongoError: - all_urls = get_recent() - else: - all_urls = get_recent() + all_urls = db_data.get_recent_urls(MAX_RECENT_URLS) or get_recent_from_cache( + MAX_RECENT_URLS + ) return templates.TemplateResponse( "index.html", @@ -111,7 +92,7 @@ async def index(request: Request): "original_url": original_url, "error": error, "info_message": info_message, - "db_available": collection is not None, + "db_available": db_data.get_collection() is not None, }, ) @@ -124,7 +105,7 @@ async def create_short_url( qr_type: str = Form("short"), ): session = request.session - qr_enabled = generate_qr == "true" + qr_enabled = bool(generate_qr) original_url = sanitize_url(original_url) if not original_url: @@ -141,43 +122,21 @@ async def create_short_url( if cached_short: short_code = cached_short session["info_message"] = "Already shortened before β€” fetched from cache." - add_recent(short_code, original_url) else: short_code = None - collection = db_data.get_collection() - - if collection is not None: - try: - existing = collection.find_one({"original_url": original_url}) - if existing: - short_code = existing["short_code"] - set_cache_pair(short_code, original_url) - session["info_message"] = ( - "Already shortened before β€” fetched from database." - ) - add_recent(short_code, original_url) - except PyMongoError: - pass + existing = db_data.find_by_original_url(original_url) + if existing: + short_code = existing["short_code"] + set_cache_pair(short_code, original_url) + session["info_message"] = ( + "Already shortened before β€” fetched from database." + ) if not short_code: short_code = generate_code() set_cache_pair(short_code, original_url) - - if collection is not None: - try: - collection.insert_one( - { - "short_code": short_code, - "original_url": original_url, - "created_at": datetime.datetime.utcnow(), - "visit_count": 0, - } - ) - except PyMongoError: - pass - - add_recent(short_code, original_url) + db_data.insert_url(short_code, original_url) new_short_url = build_short_url(short_code, str(request.base_url)) session.update( @@ -195,15 +154,9 @@ async def create_short_url( @app.get("/recent", response_class=HTMLResponse) async def recent_urls(request: Request): - collection = db_data.get_collection() - - if collection is not None: - try: - recent_urls_list = list(collection.find().sort("created_at", -1).limit(10)) - except PyMongoError: - recent_urls_list = get_recent() - else: - recent_urls_list = get_recent() + recent_urls_list = db_data.get_recent_urls( + MAX_RECENT_URLS + ) or get_recent_from_cache(MAX_RECENT_URLS) normalized = [] for item in recent_urls_list: @@ -228,58 +181,28 @@ async def recent_urls(request: Request): @app.post("/delete/{short_code}") async def delete_url(request: Request, short_code: str): - collection = db_data.get_collection() - - if collection is not None: - try: - collection.delete_one({"short_code": short_code}) - except PyMongoError: - return PlainTextResponse("Database connection lost.", status_code=503) + db_data.delete_by_short_code(short_code) cached = url_cache.pop(short_code, None) if cached: rev_cache.pop(cached.get("url"), None) - try: - from app.utils.cache import recent_urls - - recent_urls[:] = [ - item for item in recent_urls if item.get("short_code") != short_code - ] - except Exception: - pass - return PlainTextResponse("", status_code=204) @app.get("/{short_code}") async def redirect_short(request: Request, short_code: str): - collection = db_data.get_collection() - - # Always try to increment visit count if DB is available - if collection is not None: - try: - doc = collection.find_one_and_update( - {"short_code": short_code}, - {"$inc": {"visit_count": 1}}, - ) - except PyMongoError: - doc = None - else: - doc = None + doc = db_data.increment_visit(short_code) - # Then resolve URL (cache first, DB fallback) cached_url = get_from_cache(short_code) if cached_url: - add_recent(short_code, cached_url) return RedirectResponse(cached_url) if doc: set_cache_pair(short_code, doc["original_url"]) - add_recent(short_code, doc["original_url"]) return RedirectResponse(doc["original_url"]) - if collection is None: + if not db_data.get_collection(): return PlainTextResponse("Database is not connected.", status_code=503) return PlainTextResponse("Invalid or expired short URL", status_code=404) @@ -298,7 +221,7 @@ async def debug_cache(): return { "url_cache": url_cache, "rev_cache": rev_cache, - "recent": get_recent(), + "recent_from_cache": get_recent_from_cache(MAX_RECENT_URLS), "size": { "url_cache": len(url_cache), "rev_cache": len(rev_cache), diff --git a/app/utils/cache.py b/app/utils/cache.py index 905b94e..a6f1c6a 100644 --- a/app/utils/cache.py +++ b/app/utils/cache.py @@ -12,12 +12,13 @@ class UrlCacheItem(TypedDict): class RevCacheItem(TypedDict): short_code: str expires_at: float + last_accessed: float # short_code -> original_url url_cache: dict[str, UrlCacheItem] = {} -# original_url -> short_code +# original_url -> short_code (+ metadata for recent tracking) rev_cache: dict[str, RevCacheItem] = {} @@ -48,11 +49,15 @@ def get_short_from_cache(original_url: str) -> str | None: rev_cache.pop(original_url, None) return None + # Touch for recent tracking + data["last_accessed"] = _now() + return data["short_code"] def set_cache_pair(short_code: str, original_url: str) -> None: - expires_at = _now() + CACHE_TTL + now = _now() + expires_at = now + CACHE_TTL url_cache[short_code] = { "url": original_url, @@ -62,6 +67,7 @@ def set_cache_pair(short_code: str, original_url: str) -> None: rev_cache[original_url] = { "short_code": short_code, "expires_at": expires_at, + "last_accessed": now, } @@ -83,41 +89,39 @@ def cleanup_expired() -> None: expired_short_codes = [ key for key, value in url_cache.items() if value["expires_at"] < now ] - for key in expired_short_codes: url_cache.pop(key, None) expired_urls = [ key for key, value in rev_cache.items() if value["expires_at"] < now ] - for key in expired_urls: rev_cache.pop(key, None) -# ---- Recent URLs Cache (Unique, Ordered, DB-shaped) ---- +# ----------------------- +# Recent URLs (derived from rev_cache) +# ----------------------- -MAX_RECENT = MAX_RECENT_URLS -recent_urls: list[dict] = [] # same shape as DB docs +def get_recent_from_cache(limit: int = MAX_RECENT_URLS) -> list[dict]: + """ + Returns recent URLs based on cache activity (no duplicates, TTL-aware). + Shape matches DB docs. + """ + now = _now() -def add_recent(short_code: str, original_url: str) -> None: - recent_urls[:] = [ - item - for item in recent_urls - if item["short_code"] != short_code and item["original_url"] != original_url - ] - - recent_urls.insert( - 0, + items = [ { - "short_code": short_code, + "short_code": data["short_code"], "original_url": original_url, - }, - ) - - del recent_urls[MAX_RECENT:] + } + for original_url, data in rev_cache.items() + if data["expires_at"] >= now + ] + items.sort( + key=lambda x: rev_cache[x["original_url"]]["last_accessed"], reverse=True + ) -def get_recent() -> list[dict]: - return recent_urls + return items[:limit] diff --git a/app/utils/config.py b/app/utils/config.py index c4ad05d..89fa3d8 100644 --- a/app/utils/config.py +++ b/app/utils/config.py @@ -56,6 +56,8 @@ def _get_int(key: str, default: int) -> int: # ------------------------- MONGO_URI = os.getenv("MONGO_URI") MONGO_DB_NAME = "tiny_url" +MONGO_COLLECTION = os.getenv("MONGO_COLLECTION", "urls") + # ------------------------- # Cache (constants) @@ -63,7 +65,7 @@ def _get_int(key: str, default: int) -> int: USE_CACHE = True CACHE_TTL = 900 # 15 minutes MAX_CACHE_SIZE = 10_000 -MAX_RECENT_URLS = 10 +MAX_RECENT_URLS = 20 # ------------------------- # Security / Sessions diff --git a/app/utils/data.py b/app/utils/data.py index 2b8087e..98e1714 100644 --- a/app/utils/data.py +++ b/app/utils/data.py @@ -1,81 +1,106 @@ -# app/db/data.py - from typing import Any, Optional -# --- DEFENSIVE IMPORT --- try: from pymongo import MongoClient from pymongo.errors import PyMongoError MONGO_INSTALLED = True except ImportError: - MongoClient = None # type: ignore - - class PyMongoError(Exception): - pass - + MongoClient = None + PyMongoError = Exception MONGO_INSTALLED = False +from app.utils.config import MONGO_URI, MONGO_DB_NAME, MONGO_COLLECTION -from app.utils.config import MONGO_URI, MONGO_DB_NAME - - -class _DBState: - client: Optional[Any] = None - db: Optional[Any] = None - collection: Optional[Any] = None - available: bool = False - - -_state = _DBState() +client: Any = None +db: Any = None +collection: Any = None def connect_db() -> bool: - """ - Connect to MongoDB using config from env. - Initializes shared collection. - """ + global client, db, collection + if not MONGO_INSTALLED: print("⚠️ pymongo not installed. Running in NO-DB mode.") - _state.client = None - _state.db = None - _state.collection = None - _state.available = False return False - mongo_uri = MONGO_URI - mongo_db_name = MONGO_DB_NAME + try: + client = MongoClient(MONGO_URI, serverSelectionTimeoutMS=2000) + db = client[MONGO_DB_NAME] + collection = db[MONGO_COLLECTION] - if not mongo_uri: - print("⚠️ MONGO_URI not set. Running in NO-DB mode.") - _state.client = None - _state.db = None - _state.collection = None - _state.available = False + client.admin.command("ping") + print("βœ… MongoDB connected successfully") + return True + + except Exception: + print("❌ MongoDB connection failed") + client = db = collection = None return False + +def get_collection(): + return collection + + +# ------------------------ +# DB Operations +# ------------------------ + + +def find_by_original_url(original_url: str) -> Optional[dict]: + if collection is None: + return None try: - client = MongoClient(mongo_uri, serverSelectionTimeoutMS=2000) - client.admin.command("ping") - db = client[mongo_db_name] - collection = db["urls"] + return collection.find_one({"original_url": original_url}) + except PyMongoError: + return None - _state.client = client - _state.db = db - _state.collection = collection - _state.available = True - print("βœ… MongoDB connected") +def insert_url(short_code: str, original_url: str) -> bool: + if collection is None: + return False + try: + collection.insert_one( + { + "short_code": short_code, + "original_url": original_url, + "created_at": __import__("datetime").datetime.utcnow(), + "visit_count": 0, + } + ) return True except PyMongoError: - print("❌ MongoDB connection failed") - _state.client = None - _state.db = None - _state.collection = None - _state.available = False return False -def get_collection(): - """Return the shared Mongo collection or None.""" - return _state.collection +def delete_by_short_code(short_code: str) -> bool: + if collection is None: + return False + try: + collection.delete_one({"short_code": short_code}) + return True + except PyMongoError: + return False + + +def get_recent_urls(limit: int = 10) -> list[dict]: + if collection is None: + return [] + try: + return list(collection.find().sort("created_at", -1).limit(limit)) + except PyMongoError: + return [] + + +def increment_visit(short_code: str) -> Optional[dict]: + if collection is None: + return None + try: + return collection.find_one_and_update( + {"short_code": short_code}, + {"$inc": {"visit_count": 1}}, + return_document=True, + ) + except PyMongoError: + return None From 17f94c6227cad186fdc9a2f09537b7689c655727 Mon Sep 17 00:00:00 2001 From: RecursiveZero Date: Mon, 16 Feb 2026 20:26:42 +0530 Subject: [PATCH 15/18] fix lint issues --- app/api/fast_api.py | 9 +- app/main.py | 39 +++-- app/static/css/tiny.css | 52 +++++- app/templates/index.html | 45 +++-- app/templates/xindex.html | 348 -------------------------------------- app/utils/config.py | 4 - app/utils/data.py | 23 +-- app/utils/qr.py | 24 ++- 8 files changed, 143 insertions(+), 401 deletions(-) delete mode 100644 app/templates/xindex.html diff --git a/app/api/fast_api.py b/app/api/fast_api.py index 6d82b9c..fad63e4 100644 --- a/app/api/fast_api.py +++ b/app/api/fast_api.py @@ -2,13 +2,12 @@ import re import traceback from datetime import datetime, timezone +from typing import TYPE_CHECKING from fastapi import APIRouter, FastAPI, Request from fastapi.responses import HTMLResponse, JSONResponse from pydantic import BaseModel, Field -from typing import TYPE_CHECKING - if TYPE_CHECKING: from pymongo.errors import PyMongoError else: @@ -155,7 +154,7 @@ def shorten_url(payload: ShortenRequest): }, ) - if db_data.urls is None: + if db_data.collection is None: cached_short = get_short_from_cache(original_url) short_code = cached_short or generate_code() set_cache_pair(short_code, original_url) @@ -167,7 +166,7 @@ def shorten_url(payload: ShortenRequest): } try: - existing = db_data.urls.find_one({"original_url": original_url}) + existing = db_data.collection.find_one({"original_url": original_url}) except PyMongoError: existing = None @@ -181,7 +180,7 @@ def shorten_url(payload: ShortenRequest): short_code = generate_code() try: - db_data.urls.insert_one( + db_data.collection.insert_one( { "short_code": short_code, "original_url": original_url, diff --git a/app/main.py b/app/main.py index 562e8c0..cea15eb 100644 --- a/app/main.py +++ b/app/main.py @@ -12,13 +12,13 @@ from app.utils import data as db_data from app.utils.cache import ( get_from_cache, - get_short_from_cache, get_recent_from_cache, + get_short_from_cache, rev_cache, set_cache_pair, url_cache, ) -from app.utils.config import load_env, SESSION_SECRET, DOMAIN, MAX_RECENT_URLS +from app.utils.config import DOMAIN, MAX_RECENT_URLS, SESSION_SECRET, load_env from app.utils.helper import ( format_date, generate_code, @@ -27,13 +27,14 @@ ) from app.utils.qr import generate_qr_with_logo +load_env() + # ----------------------------- # Lifespan: env + DB connect ONCE # ----------------------------- @asynccontextmanager async def lifespan(app: FastAPI): - load_env() db_data.connect_db() yield @@ -118,27 +119,40 @@ async def create_short_url( ) return RedirectResponse("/", status_code=303) - cached_short = get_short_from_cache(original_url) - if cached_short: - short_code = cached_short + # 1. Try Cache First + short_code: Optional[str] = get_short_from_cache(original_url) + + if short_code: session["info_message"] = "Already shortened before β€” fetched from cache." else: - short_code = None - + # 2. Try Database existing = db_data.find_by_original_url(original_url) - if existing: - short_code = existing["short_code"] - set_cache_pair(short_code, original_url) + # Pull the value and check it in one go + db_code = existing.get("short_code") if existing else None + if isinstance(db_code, str): + short_code = db_code + set_cache_pair(short_code, original_url) # Cache it for future session["info_message"] = ( "Already shortened before β€” fetched from database." ) + # 3. Generate New if still None if not short_code: short_code = generate_code() set_cache_pair(short_code, original_url) db_data.insert_url(short_code, original_url) + # --- TYPE GUARD FOR MYPY --- + # At this point, short_code could still technically be Optional[str] + # if generate_code() wasn't strictly typed. We cast or assert. + if not isinstance(short_code, str): + # This acts as a final safety net for production + session["error"] = "Internal server error: Code generation failed." + return RedirectResponse("/", status_code=303) + + # Mypy now knows short_code is strictly 'str' new_short_url = build_short_url(short_code, str(request.base_url)) + session.update( { "new_short_url": new_short_url, @@ -201,8 +215,7 @@ async def redirect_short(request: Request, short_code: str): if doc: set_cache_pair(short_code, doc["original_url"]) return RedirectResponse(doc["original_url"]) - - if not db_data.get_collection(): + if db_data.get_collection() is None: return PlainTextResponse("Database is not connected.", status_code=503) return PlainTextResponse("Invalid or expired short URL", status_code=404) diff --git a/app/static/css/tiny.css b/app/static/css/tiny.css index 4e9208c..db092a6 100644 --- a/app/static/css/tiny.css +++ b/app/static/css/tiny.css @@ -10,7 +10,7 @@ body { background: var(--bg); color: var(--text-primary); - font-family: 'Inter', system-ui, sans-serif; + font-family: "Inter", system-ui, sans-serif; margin: 0; overflow-x: hidden; background-image: radial-gradient(circle at 50% -20%, #1e1e2e 0%, transparent 50%); @@ -24,6 +24,7 @@ body { flex-direction: column; align-items: center; gap: 2rem; + min-height: 52vh; } /* --- LARGE HERO INPUT --- */ @@ -205,4 +206,51 @@ body { .footer-grid { grid-template-columns: 1fr 1fr; } -} \ No newline at end of file +} + +/* The container for the icon/button */ +.copy-btn { + position: relative; + cursor: pointer; + transition: transform 0.1s ease; +} + +.copy-btn:active { + transform: scale(0.9); +} + +/* The "Smart" Tooltip */ +.copy-btn.copy-success::after { + content: attr(data-tooltip); + position: absolute; + top: -30px; + left: 50%; + transform: translateX(-50%); + background: #333; + color: #fff; + padding: 4px 8px; + border-radius: 4px; + font-size: 12px; + white-space: nowrap; + animation: fadeInOut 2s forwards; +} + +@keyframes fadeInOut { + 0% { + opacity: 0; + transform: translate(-50%, 5px); + } + + 15% { + opacity: 1; + transform: translate(-50%, 0); + } + + 85% { + opacity: 1; + } + + 100% { + opacity: 0; + } +} diff --git a/app/templates/index.html b/app/templates/index.html index 3fb543a..eeadff1 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -39,12 +39,17 @@

tiny URL

- - {% if qr_image %}Download - QR{% endif %} + + + {% if qr_image %} + + Download QR + + {% endif %}
{% endif %} @@ -113,10 +118,30 @@

Legal

{% endblock %} \ No newline at end of file diff --git a/app/templates/xindex.html b/app/templates/xindex.html deleted file mode 100644 index 494fb2c..0000000 --- a/app/templates/xindex.html +++ /dev/null @@ -1,348 +0,0 @@ - - - - - - - tiny URL - - - - - - - - - -
-
- - tiny URL -
- - -
- -
-
- - - -
-
- -
- {% if not db_available %} -
- ⚠️ Running in Limited Mode (No Database) -
- {% endif %} - - -
-

Shorten Your URLs with Precision

-

Transform long links into short, memorable URLs.

-
- - -
- - - -
- - -
-
- - - - - - - -
- - -
- {% if error %} -
{{ error }}
- {% endif %} - {% if info_message %} -
- {{ info_message }} -
- {% endif %} - - - -
- - - - -
- -
- - - - {% if new_short_url %} -
-
-
βœ“
-
-
Your shortened URL is ready!
- -
Copy or share your short link, download the - QR code, or view details.
-
-
-
- -
- - {{ new_short_url }} - - - - -
-
- - - -
- -
- {% if qr_data %} - QR Code - - {% else %} -
-
- {% endif %} - -
Original URL
- {% set display_url = original_url or (urls[0].original_url if urls|length > 0 else '') %} - - {% if display_url %} - - {% endif %} - - -
-
-
-
-
-
-
-
- - - - -
- - -
-
-
- {% endif %} - - - - - -
- - - - - - - - - - \ No newline at end of file diff --git a/app/utils/config.py b/app/utils/config.py index 89fa3d8..c29ffec 100644 --- a/app/utils/config.py +++ b/app/utils/config.py @@ -15,10 +15,6 @@ def load_env(): print(f"MODE value: {os.getenv('MODE')}") -# βœ… IMPORTANT: Load env immediately when config is imported -load_env() - - # ------------------------- # Helpers # ------------------------- diff --git a/app/utils/data.py b/app/utils/data.py index 98e1714..50c6f6e 100644 --- a/app/utils/data.py +++ b/app/utils/data.py @@ -6,11 +6,12 @@ MONGO_INSTALLED = True except ImportError: - MongoClient = None - PyMongoError = Exception + MongoClient: Any = None # type: ignore + Collection: Any # type: ignore + PyMongoError = Exception # type: ignore MONGO_INSTALLED = False -from app.utils.config import MONGO_URI, MONGO_DB_NAME, MONGO_COLLECTION +from app.utils.config import MONGO_COLLECTION, MONGO_DB_NAME, MONGO_URI client: Any = None db: Any = None @@ -21,25 +22,25 @@ def connect_db() -> bool: global client, db, collection if not MONGO_INSTALLED: - print("⚠️ pymongo not installed. Running in NO-DB mode.") return False try: - client = MongoClient(MONGO_URI, serverSelectionTimeoutMS=2000) - db = client[MONGO_DB_NAME] + # Create instance + new_client: Any = MongoClient(MONGO_URI, serverSelectionTimeoutMS=2000) + new_client.admin.command("ping") + client = new_client + db = new_client[MONGO_DB_NAME] collection = db[MONGO_COLLECTION] - - client.admin.command("ping") - print("βœ… MongoDB connected successfully") return True except Exception: - print("❌ MongoDB connection failed") client = db = collection = None return False + return False + -def get_collection(): +def get_collection() -> Optional[dict[str, Any]]: return collection diff --git a/app/utils/qr.py b/app/utils/qr.py index cf09213..246f95f 100644 --- a/app/utils/qr.py +++ b/app/utils/qr.py @@ -1,26 +1,32 @@ +from pathlib import Path +from typing import cast + import qrcode from PIL import Image -from pathlib import Path -def generate_qr_with_logo(data, filename): +def generate_qr_with_logo(data: str, filename: str) -> str: + # 1. Use direct attribute access or explicit import for constants qr = qrcode.QRCode( - error_correction=qrcode.constants.ERROR_CORRECT_H, + version=1, + error_correction=qrcode.constants.ERROR_CORRECT_H, # type: ignore box_size=20, border=1, ) qr.add_data(data) qr.make(fit=True) - qr_img = qr.make_image(fill_color="black", back_color="white").convert("RGB") + # 2. Cast the result to PIL.Image.Image so Pylance sees .convert() + # The default factory for qrcode is a PIL-based image + qr_img = cast( + Image.Image, qr.make_image(fill_color="black", back_color="white") + ).convert("RGB") - # βœ… Resolve project app directory (app/) - APP_DIR = Path(__file__).resolve().parents[1] # goes up to /app + APP_DIR = Path(__file__).resolve().parents[1] STATIC_DIR = APP_DIR / "static" QR_DIR = STATIC_DIR / "qr" IMAGES_DIR = STATIC_DIR / "images" - # βœ… Ensure QR dir exists QR_DIR.mkdir(parents=True, exist_ok=True) logo_path = IMAGES_DIR / "logo.png" @@ -36,7 +42,9 @@ def generate_qr_with_logo(data, filename): pos = ((qr_width - logo_size) // 2, (qr_height - logo_size) // 2) - qr_img.paste(logo, pos, mask=logo if logo.mode == "RGBA" else None) + # Use the logo itself as a mask if it has an alpha channel + mask = logo if logo.mode == "RGBA" else None + qr_img.paste(logo, pos, mask=mask) save_path = QR_DIR / filename qr_img.save(save_path) From 562e228913c69a43c84a3229cd3510c8dec5afe7 Mon Sep 17 00:00:00 2001 From: RecursiveZero Date: Mon, 16 Feb 2026 20:28:32 +0530 Subject: [PATCH 16/18] added env sample --- .env.local | 9 --------- .env.sample | 6 ++++++ .gitignore | 2 +- pyproject.toml | 2 +- 4 files changed, 8 insertions(+), 11 deletions(-) delete mode 100644 .env.local create mode 100644 .env.sample diff --git a/.env.local b/.env.local deleted file mode 100644 index 14a96a5..0000000 --- a/.env.local +++ /dev/null @@ -1,9 +0,0 @@ -MODE=local -MONGO_URI=mongodb://:@127.0.0.1:27017/?authSource=admin&retryWrites=true&w=majority -DOMAIN=https://rzro.link -PORT=8001 -API_VERSION="" -APP_NAMe="LOCAL" - -MONGO_URI="mongodb://@:127.0.0.1:27017/?retryWrites=true&w=majority&appName=" -DATABASE_NAME=":@127.0.0.1:27017/?authSource=admin&retryWrites=true&w=majority +DOMAIN=https://localhost:8001 +PORT=8001 +API_VERSION="" +APP_NAMe="LOCAL" \ No newline at end of file diff --git a/.gitignore b/.gitignore index ac5982f..ae1cf18 100644 --- a/.gitignore +++ b/.gitignore @@ -11,7 +11,7 @@ env/ .env .env.* -!.env.local +!.env.sample instance/ diff --git a/pyproject.toml b/pyproject.toml index 5bf28fe..413922a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "tiny" -version = "1.0.0" +version = "1.0.1" description = "A package for URL Shortener with QR Code generation" authors = [ { name = "recursivezero", email = "152776938+recursivezero@users.noreply.github.com" }, From 53ab98071ad62c8402e0db4f37d1aa095ec302f8 Mon Sep 17 00:00:00 2001 From: RecursiveZero Date: Mon, 16 Feb 2026 22:03:51 +0530 Subject: [PATCH 17/18] separate config env --- app/main.py | 5 +- app/utils/config.py | 23 ++----- app/utils/config_env.py | 15 +++++ group_shorten.ps1 | 134 ---------------------------------------- 4 files changed, 22 insertions(+), 155 deletions(-) create mode 100644 app/utils/config_env.py delete mode 100644 group_shorten.ps1 diff --git a/app/main.py b/app/main.py index cea15eb..09bb540 100644 --- a/app/main.py +++ b/app/main.py @@ -18,7 +18,7 @@ set_cache_pair, url_cache, ) -from app.utils.config import DOMAIN, MAX_RECENT_URLS, SESSION_SECRET, load_env +from app.utils.config import DOMAIN, MAX_RECENT_URLS, SESSION_SECRET from app.utils.helper import ( format_date, generate_code, @@ -27,7 +27,6 @@ ) from app.utils.qr import generate_qr_with_logo -load_env() # ----------------------------- @@ -151,7 +150,7 @@ async def create_short_url( return RedirectResponse("/", status_code=303) # Mypy now knows short_code is strictly 'str' - new_short_url = build_short_url(short_code, str(request.base_url)) + new_short_url = build_short_url(short_code, DOMAIN) session.update( { diff --git a/app/utils/config.py b/app/utils/config.py index c29ffec..0b9d73f 100644 --- a/app/utils/config.py +++ b/app/utils/config.py @@ -1,27 +1,14 @@ -import os - -from dotenv import load_dotenv - - -def load_env(): - env = os.getenv("ENV", "development") - file_map = { - "production": ".env", - "local": ".env.local", - "development": ".env.development", - } - load_dotenv(file_map.get(env, ".env.development"), override=True) - print(f"Environment selected: {env}") - print(f"MODE value: {os.getenv('MODE')}") - +import os # ------------------------- # Helpers # ------------------------- -def _get_bool(key: str, default: bool) -> bool: - return os.getenv(key, str(default)).lower() in ("1", "true", "yes", "on") +from app.utils.config_env import load_env # noqa: F401 + +load_env() + def _get_int(key: str, default: int) -> int: try: return int(os.getenv(key, default)) diff --git a/app/utils/config_env.py b/app/utils/config_env.py new file mode 100644 index 0000000..049232d --- /dev/null +++ b/app/utils/config_env.py @@ -0,0 +1,15 @@ +import os + +from dotenv import load_dotenv + + +def load_env(): + env = os.getenv("ENV", "development") + file_map = { + "production": ".env", + "local": ".env.local", + "development": ".env.development", + } + load_dotenv(file_map.get(env, ".env.development"), override=True) + print(f"Environment selected: {env}") + print(f"MODE value: {os.getenv('MODE')}") diff --git a/group_shorten.ps1 b/group_shorten.ps1 deleted file mode 100644 index 3e52c10..0000000 --- a/group_shorten.ps1 +++ /dev/null @@ -1,134 +0,0 @@ -<# -========================================================= - PowerShell Script: group_shorten.ps1 ---------------------------------------------------------- - Description: - Bulk URL shortener client for Tiny API. - Reads a JSON file containing multiple URLs and sends - them one-by-one to the /api/shorten endpoint. - ---------------------------------------------------------- - HOW TO RUN (Windows PowerShell TERMINAL) ---------------------------------------------------------- - - 1. Open PowerShell as Administrator - - 2. Allow script execution (one-time): - Set-ExecutionPolicy RemoteSigned - - 3. Navigate to project root: - cd path\to\your\project - - 4. Run with JSON file: - .\group_shorten.ps1 -file ".\request\mixed_urls.json" - - 5. Or run without argument (interactive): - .\group_shorten.ps1 - - ---------------------------------------------------------- - HOW TO RUN (Windows PowerShell ISE) ---------------------------------------------------------- - - Method 1: Run with parameter - -------------------------------- - 1. Open Windows PowerShell ISE - 2. Open this script file (group_shorten.ps1) - 3. In the top menu, click: - File β†’ New PowerShell Tab - 4. In the console pane (bottom), run: - .\group_shorten.ps1 -file ".\request\mixed_urls.json" - - Method 2: Run interactively - -------------------------------- - 1. Open Windows PowerShell ISE - 2. Open this script file - 3. Press the green ▢️ Run Script button - 4. When prompted, enter the JSON file path - ---------------------------------------------------------- - Expected JSON input format: - [ - { "url": "https://example.com" }, - { "url": "https://google.com" } - ] -========================================================= -#> - -param ( - [string]$file -) - -# Ask for file path if not provided -if (-not $file) { - $file = Read-Host "Enter path to JSON file" -} - -# Validate file -if (-not (Test-Path $file)) { - Write-Host "❌ File not found: $file" -ForegroundColor Red - exit 1 -} - -# Load JSON -try { - $data = Get-Content $file -Raw | ConvertFrom-Json -} -catch { - Write-Host "❌ Invalid JSON file" -ForegroundColor Red - exit 1 -} - -$apiUrl = "http://127.0.0.1:8001/api/v1/shorten" - -Write-Host "`nπŸš€ Processing URLs..." -ForegroundColor Cyan - -foreach ($item in $data) { - - # Support string + object format - $url = if ($item -is [string]) { $item } else { $item.url } - - if (-not $url) { - Write-Host "⚠️ Empty URL, skipped" -ForegroundColor Yellow - continue - } - - # -------- LOCAL URL VALIDATION -------- - if ($url -notmatch '^[a-zA-Z]+://') { - Write-Host "❌ ERROR:" $url "- Missing protocol (http/https)" -ForegroundColor yellow - continue - } - - if ($url -notmatch '^https?://') { - Write-Host "❌ ERROR:" $url "- Unsupported protocol" -ForegroundColor yellow - continue - } - - if ($url -match 'https?:/[^/]') { - Write-Host "❌ ERROR:" $url "- Malformed URL (missing /)" -ForegroundColor yellow - continue - } - - if ($url -match '\.\.') { - Write-Host "❌ ERROR:" $url "- Invalid domain format" -ForegroundColor yellow - continue - } - - # -------- API REQUEST -------- - $body = @{ url = $url } | ConvertTo-Json - - try { - $response = Invoke-RestMethod ` - -Uri $apiUrl ` - -Method POST ` - -ContentType "application/json" ` - -Body $body - - Write-Host "βœ… SUCCESS:" $url "β†’" $response.short_code -ForegroundColor Green - } - catch { - Write-Host "❌ ERROR:" $url "- Rejected by API" -ForegroundColor Yellow - } -} - -Write-Host "`nπŸŽ‰ Done!" -ForegroundColor Cyan From 2669d81b8fe5b8bbd98418c6972d561f6a969858 Mon Sep 17 00:00:00 2001 From: RecursiveZero Date: Tue, 17 Feb 2026 13:44:33 +0530 Subject: [PATCH 18/18] update theme based UI --- app/static/css/tiny.css | 343 ++++++++++++++++++++++++++++---------- app/templates/header.html | 11 ++ app/templates/index.html | 71 ++++---- app/templates/layout.html | 4 +- 4 files changed, 293 insertions(+), 136 deletions(-) create mode 100644 app/templates/header.html diff --git a/app/static/css/tiny.css b/app/static/css/tiny.css index db092a6..873f075 100644 --- a/app/static/css/tiny.css +++ b/app/static/css/tiny.css @@ -16,64 +16,158 @@ body { background-image: radial-gradient(circle at 50% -20%, #1e1e2e 0%, transparent 50%); } +body { + background: var(--bg); + color: var(--text-primary); + font-family: "Inter", system-ui, sans-serif; + margin: 0; + overflow-x: hidden; + background-image: radial-gradient(circle at 50% -20%, #1e1e2e 0%, transparent 50%); +} + +/* Light theme overrides */ +body.light-theme { + --bg: #f9fafb; + --glass: rgba(0, 0, 0, 0.03); + --glass-border: rgba(0, 0, 0, 0.07); + --accent: #2563eb; + --text-primary: #111827; + --text-secondary: #4b5563; + + /* Remove or soften the dark gradient */ + background-image: none; + /* clean white background */ + /* Or use a subtle light gradient if you prefer */ + /* background-image: radial-gradient(circle at 50% -20%, #e5e7eb 0%, transparent 50%); */ +} + +/* Layout */ .main-layout { max-width: 900px; margin: 0 auto; - padding: 6rem 1.5rem; + padding: 4rem 1rem; display: flex; flex-direction: column; - align-items: center; gap: 2rem; - min-height: 52vh; } -/* --- LARGE HERO INPUT --- */ +.site-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 1rem 1.5rem; + background: var(--glass); + border-bottom: 1px solid var(--glass-border); + backdrop-filter: blur(10px); +} + +.header-left, +.header-right { + display: flex; + gap: 1rem; + align-items: center; +} + +.header-center { + flex: 1; + text-align: center; +} + +.logo { + margin: 0; + font-size: 1.5rem; + font-weight: 700; + color: var(--text-primary); +} + +.icon-btn { + background: none; + border: none; + color: var(--text-primary); + font-size: 1.2rem; + cursor: pointer; + transition: color 0.3s; +} + +.icon-btn:hover { + color: var(--accent); +} + +@media (max-width: 600px) { + .logo { + font-size: 1.2rem; + } +} + +/* Hero input */ .hero-input-card { width: 100%; background: var(--glass); backdrop-filter: blur(20px); border: 1px solid var(--glass-border); - border-radius: 2.5rem; - padding: 3rem; - box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5); + border-radius: 2rem; + padding: 2rem; } .hero-input-card h1 { - font-size: 3.5rem; + font-size: 2.5rem; font-weight: 800; - letter-spacing: -3px; - margin: 0 0 1.5rem 0; text-align: center; - background: linear-gradient(to right, #fff, #666); + margin-bottom: 1.5rem; + background: linear-gradient(to right, var(--text-primary), var(--text-secondary)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; } .input-wrapper { - position: relative; display: flex; gap: 1rem; + flex-wrap: wrap; } .input-wrapper input { flex: 1; + min-width: 200px; background: rgba(255, 255, 255, 0.05); border: 1px solid var(--glass-border); border-radius: 1.5rem; - padding: 1.5rem 2rem; - color: white; - font-size: 1.2rem; - outline: none; - transition: all 0.3s ease; + padding: 1rem; + font-size: 1rem; + color: var(--text-primary); } -.input-wrapper input:focus { - border-color: var(--accent); - background: rgba(255, 255, 255, 0.08); - box-shadow: 0 0 0 4px rgba(99, 102, 241, 0.1); +.btn-primary { + padding: 0.8rem 2rem; + border-radius: 1.5rem; + background: var(--accent); + color: #fff; + font-weight: bold; + border: none; + cursor: pointer; + transition: background 0.3s; +} + +.btn-primary:hover { + background: #4f46e5; +} + +.options-row { + margin-top: 1rem; + display: flex; + flex-wrap: wrap; + gap: 1rem; + font-size: 0.85rem; + color: var(--text-secondary); +} + +.checkbox-label { + display: flex; + align-items: center; + gap: 0.5rem; + cursor: pointer; } -/* --- OUTPUT CARD (Immediate Result) --- */ +/* Result card */ .result-card { width: 100%; background: linear-gradient(145deg, rgba(99, 102, 241, 0.1), rgba(0, 0, 0, 0)); @@ -81,30 +175,89 @@ body { border-radius: 2rem; padding: 2rem; display: flex; - align-items: center; justify-content: space-between; - animation: slideUp 0.6s cubic-bezier(0.16, 1, 0.3, 1); + align-items: center; + gap: 2rem; +} + +.result-content { + display: flex; + align-items: center; + gap: 1.5rem; } -/* --- SCROLLABLE RECENT TRAY --- */ +.qr-image { + width: 80px; + height: 80px; + border-radius: 1rem; + background: #fff; + padding: 0.5rem; +} + +.ready-label { + font-size: 0.8rem; + text-transform: uppercase; + color: var(--accent); + font-weight: 800; +} + +.short-url a { + font-size: 1.5rem; + font-weight: 700; + color: var(--text-primary); + text-decoration: none; +} + +.result-actions { + display: flex; + flex-direction: column; + align-items: flex-end; + gap: 0.5rem; +} + +.btn-copy { + background: rgba(255, 255, 255, 0.05); + border: 1px solid var(--glass-border); + color: var(--text-primary); + padding: 0.8rem 1.5rem; + border-radius: 1rem; + cursor: pointer; + transition: all 0.3s ease; +} + +.btn-copy:hover { + border-color: var(--accent); + color: var(--accent); +} + +.download-link { + font-size: 0.75rem; + color: var(--text-secondary); + text-decoration: none; +} + +.download-link:hover { + color: var(--accent); +} + +/* Recent tray */ .recent-tray { width: 100%; - margin-top: 4rem; + margin-top: 2rem; } .recent-header { display: flex; justify-content: space-between; margin-bottom: 1rem; - padding: 0 1rem; + padding: 0 0.5rem; } .scroll-container { display: flex; gap: 1rem; overflow-x: auto; - padding: 1rem; - mask-image: linear-gradient(to right, black 85%, transparent); + padding: 1rem 0; } .scroll-container::-webkit-scrollbar { @@ -112,51 +265,53 @@ body { } .recent-item { - min-width: 250px; + min-width: 220px; background: var(--glass); border: 1px solid var(--glass-border); - padding: 1.5rem; - border-radius: 1.5rem; - font-size: 0.9rem; + padding: 1rem; + border-radius: 1rem; + display: flex; + flex-direction: column; + gap: 0.5rem; + flex-shrink: 0; } -@keyframes slideUp { - from { - opacity: 0; - transform: translateY(20px); - } +.short-code { + color: var(--accent); + font-weight: bold; +} - to { - opacity: 1; - transform: translateY(0); - } +.original-url { + color: var(--text-secondary); + font-size: 0.8rem; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } -/* --- LARGE FOOTER --- */ +/* Footer */ .big-footer { background: rgba(255, 255, 255, 0.01); - border-top: 1px solid var(--border); - padding: 6rem 0 3rem 0; - margin-top: 8rem; + border-top: 1px solid var(--glass-border); + padding: 4rem 1rem 2rem; + margin-top: 4rem; } .footer-grid { max-width: 1200px; margin: 0 auto; - padding: 0 1.5rem; display: grid; grid-template-columns: 2fr 1fr 1fr 1fr; - gap: 4rem; + gap: 2rem; } .footer-brand h3 { font-size: 1.5rem; - margin: 0 0 1rem 0; - letter-spacing: -1px; + margin-bottom: 1rem; } .footer-brand p { - color: var(--text-dim); + color: var(--text-secondary); line-height: 1.6; max-width: 320px; } @@ -165,8 +320,8 @@ body { font-size: 0.85rem; text-transform: uppercase; letter-spacing: 0.1em; - margin-bottom: 1.5rem; - color: var(--text); + margin-bottom: 1rem; + color: var(--text-primary); } .footer-col ul { @@ -180,7 +335,7 @@ body { } .footer-col ul li a { - color: var(--text-dim); + color: var(--text-secondary); text-decoration: none; font-size: 0.9rem; transition: color 0.2s; @@ -192,65 +347,69 @@ body { .footer-bottom { max-width: 1200px; - margin: 4rem auto 0; - padding: 2rem 1.5rem 0; - border-top: 1px solid var(--border); + margin: 2rem auto 0; + padding-top: 1rem; + border-top: 1px solid var(--glass-border); display: flex; + flex-wrap: wrap; justify-content: space-between; align-items: center; - color: var(--text-dim); + color: var(--text-secondary); font-size: 0.8rem; } +.footer-meta { + display: flex; + gap: 1rem; +} + +.footer-meta a { + color: inherit; + text-decoration: none; +} + +.footer-meta a:hover { + color: var(--accent); +} + +/* Responsive adjustments */ @media (max-width: 900px) { .footer-grid { grid-template-columns: 1fr 1fr; } } -/* The container for the icon/button */ -.copy-btn { - position: relative; - cursor: pointer; - transition: transform 0.1s ease; -} +@media (max-width: 700px) { + .result-card { + flex-direction: column; + align-items: flex-start; + } -.copy-btn:active { - transform: scale(0.9); -} + .result-actions { + align-items: flex-start; + } -/* The "Smart" Tooltip */ -.copy-btn.copy-success::after { - content: attr(data-tooltip); - position: absolute; - top: -30px; - left: 50%; - transform: translateX(-50%); - background: #333; - color: #fff; - padding: 4px 8px; - border-radius: 4px; - font-size: 12px; - white-space: nowrap; - animation: fadeInOut 2s forwards; + .recent-item { + min-width: 180px; + } } -@keyframes fadeInOut { - 0% { - opacity: 0; - transform: translate(-50%, 5px); +@media (max-width: 600px) { + .hero-input-card h1 { + font-size: 2rem; } - 15% { - opacity: 1; - transform: translate(-50%, 0); + .short-url a { + font-size: 1.2rem; } - 85% { - opacity: 1; + .footer-grid { + grid-template-columns: 1fr; } - 100% { - opacity: 0; + .footer-bottom { + flex-direction: column; + gap: 1rem; + text-align: center; } } diff --git a/app/templates/header.html b/app/templates/header.html new file mode 100644 index 0000000..c7b19b1 --- /dev/null +++ b/app/templates/header.html @@ -0,0 +1,11 @@ + \ No newline at end of file diff --git a/app/templates/index.html b/app/templates/index.html index eeadff1..7a5ecd4 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -8,47 +8,34 @@

tiny URL

- +
-
-
{% if new_short_url %}
-
+
{% if qr_image %} - + {% endif %}
- Ready -
-
- - +
+ {% if qr_image %} - - Download QR - + Download QR {% endif %}
@@ -56,16 +43,14 @@

tiny URL

-

Recently Shortened

- View History β†’ +

Recently Shortened

+ View History β†’
{% for url in urls %}
-
/{{ url.short_code }}
-
- {{ url.original_url }}
+
/{{ url.short_code }}
+
{{ url.original_url }}
{% endfor %}
@@ -110,38 +95,38 @@

Legal