Add vuln discovery eval (#68)

* Add vuln discovery eval script

* Handle multiple markdown blocks in response

* Add langfuse metadata

* Add timestamp to output dirs

* Make abstract class

* Move vuln discovery eval script

* Make VulnDiscovery class

* Add eval script to test current vuln discovery implementation

* Switch to Claude 3.5 Sonnet for seed-gen

* Update vuln discovery prompts

* Appease ruff

* Update seed-gen for changes from main

* Fix ReproduceMultiple
This commit is contained in:
Ronald Eytchison
2025-02-21 11:44:02 -05:00
committed by GitHub
parent 4cf093043c
commit fecf87293d
13 changed files with 414 additions and 516 deletions
+1 -1
View File
@@ -86,7 +86,7 @@ def get_langfuse_callbacks() -> list[BaseCallbackHandler]:
def create_default_llm(**kwargs: Any) -> BaseChatModel:
"""Create an LLM object with the default configuration."""
return create_llm(
model_name=ButtercupLLM.OPENAI_GPT_4O.value,
model_name=kwargs.pop("model_name", ButtercupLLM.OPENAI_GPT_4O.value),
temperature=kwargs.pop("temperature", 0.1),
timeout=420.0,
max_retries=3,
@@ -17,8 +17,8 @@ class ReproduceMultiple:
with task.get_rw_copy(self.wdir) as local_task:
yield (build, local_task.reproduce_pov(harness_name, pov))
def get_first_crash(self) -> tuple[BuildOutput, ReproduceResult] | None:
for build, result in self.attempt_reproduce():
def get_first_crash(self, pov: Path, harness_name: str) -> tuple[BuildOutput, ReproduceResult] | None:
for build, result in self.attempt_reproduce(pov, harness_name):
if result.command_result.returncode is not None and result.stacktrace() is not None and result.did_crash():
return build, result
return None
@@ -0,0 +1,50 @@
{
"configs": [
{
"name": "gpt-4o",
"llm_kwargs": {
"model_name": "openai-gpt-4o",
"temperature": 0.1,
"timeout": 420.0,
"max_retries": 3
}
},
{
"name": "gpt-4o-mini",
"llm_kwargs": {
"model_name": "openai-gpt-4o-mini",
"temperature": 0.1,
"timeout": 420.0,
"max_retries": 3
}
},
{
"name": "o3-mini",
"llm_kwargs": {
"model_name": "openai-o3-mini",
"timeout": 420.0,
"max_retries": 3
}
},
{
"name": "o1",
"llm_kwargs": {
"model_name": "openai-o1",
"timeout": 420.0,
"max_retries": 3
}
},
{
"name": "3.5-sonnet",
"llm_kwargs": {
"model_name": "claude-3.5-sonnet",
"temperature": 0.1,
"timeout": 420.0,
"max_retries": 3
}
}
],
"attempts_per_config": 1,
"package_name": "libpng",
"harness_name": "libpng_read_fuzzer"
}
+187
View File
@@ -0,0 +1,187 @@
"""
Evaluate a prompt with different configurations for vulnerability discovery
Example usage:
python seed-gen/eval/vuln_discovery_base.py
--prompt seed-gen/eval-prompt.txt \
--eval-config seed-gen/eval/configs/libpng_model_eval_config.json \
--task-dir sample-task-libpng/162fc720-b291-48cc-9554-efd8207cfb16-e408da6a-5c31-4d/ \
--out-dir model-eval-out
"""
import argparse
import json
import shutil
import tempfile
from abc import ABC, abstractmethod
from dataclasses import dataclass
from datetime import datetime
from pathlib import Path
from typing import Any
from langchain_core.runnables import RunnableConfig
from tqdm import tqdm
from buttercup.common.challenge_task import ChallengeTask, ChallengeTaskError
from buttercup.common.llm import create_llm, get_langfuse_callbacks
from buttercup.common.logger import setup_package_logger
from buttercup.seed_gen.sandbox.sandbox import sandbox_exec_funcs
from buttercup.seed_gen.utils import extract_md
logger = setup_package_logger(__name__, "DEBUG")
@dataclass
class TestConfig:
name: str
llm_kwargs: dict[str, Any]
@classmethod
def from_dict(cls, data: dict[str, Any]) -> "TestConfig":
return cls(**data)
@dataclass
class EvalConfig:
configs: list[TestConfig]
attempts_per_config: int
package_name: str
harness_name: str
@classmethod
def from_json(cls, json_path: str) -> "EvalConfig":
with open(json_path) as f:
data = json.load(f)
# Convert the configs list from dicts to TestConfig objects
data["configs"] = [TestConfig.from_dict(c) for c in data["configs"]]
return cls(**data)
class VulnDiscoveryEvaluatorBase(ABC):
def __init__(self, task_dir: Path, out_dir: Path, eval_config: EvalConfig):
"""Initialize the evaluator.
Args:
task_dir: Path to the challenge task directory
out_dir: Path to the output directory
eval_config: Evaluation configuration
"""
self.task_dir = task_dir
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
out_dir = out_dir / f"run-{timestamp}"
self.out_dir = out_dir
self.eval_config = EvalConfig.from_json(eval_config)
## set up out dir
self.out_dir.mkdir(parents=True)
self.metadata = {
"package_name": self.eval_config.package_name,
"harness_name": self.eval_config.harness_name,
"eval_type": "vuln-discovery",
}
shutil.copy(eval_config, self.out_dir / "config.json")
def test_povs(self, rw_task: ChallengeTask, pov_dir: Path) -> tuple[list[Path], list[Path]]:
valid_povs = []
invalid_povs = []
for pov in pov_dir.iterdir():
try:
pov_output = rw_task.reproduce_pov(self.eval_config.harness_name, pov)
if not pov_output.did_crash():
valid_povs.append(pov)
else:
invalid_povs.append(pov)
except ChallengeTaskError as exc:
logger.error(f"Error reproducing PoV {pov}: {exc}")
return valid_povs, invalid_povs
def evaluate_pov_generation(self) -> dict[str, Any]:
"""
Evaluate test configurations for vulnerability discovery.
"""
# Create challenge task for verification
chall_task = ChallengeTask(read_only_task_dir=self.task_dir)
with chall_task.get_rw_copy(work_dir=None) as rw_task:
for config in tqdm(self.eval_config.configs, desc="Evaluating configs"):
config_dir = self.out_dir / config.name
config_dir.mkdir()
for i in tqdm(
range(self.eval_config.attempts_per_config), desc=f"Evaluating {config.name}"
):
pov_dir = config_dir / f"attempt_{i}"
pov_dir.mkdir()
with tempfile.TemporaryDirectory() as workdir_str:
workdir = Path(workdir_str)
try:
# Generate PoV using the prompt
pov_funcs, trace_id = self.generate_pov_funcs(config)
trace_id_file = pov_dir / "langfuse_trace_id"
trace_id_file.write_text(trace_id)
sandbox_exec_funcs(pov_funcs, workdir)
valid_povs, invalid_povs = self.test_povs(rw_task, workdir)
valid_pov_count = len(valid_povs)
total_pov_count = valid_pov_count + len(invalid_povs)
logger.info(
f"Attempt {i}: generated {valid_pov_count} valid PoVs of {total_pov_count} total" # noqa: E501
)
valid_dir = pov_dir / "valid"
invalid_dir = pov_dir / "invalid"
valid_dir.mkdir()
invalid_dir.mkdir()
for pov in valid_povs:
shutil.copy(pov, valid_dir / pov.name)
for pov in invalid_povs:
shutil.copy(pov, invalid_dir / pov.name)
except Exception as e:
logger.error(f"Error during PoV generation: {str(e)}")
@abstractmethod
def generate_pov_funcs(self, config: TestConfig) -> tuple[str, str]:
"""Generate a string with PoV functions from a config"""
pass
class VulnDiscoveryEvaluator(VulnDiscoveryEvaluatorBase):
def __init__(self, task_dir: Path, out_dir: Path, eval_config: EvalConfig, prompt: Path):
"""Initialize the evaluator.
Args:
task_dir: Path to the challenge task directory
out_dir: Path to the output directory
eval_config: Evaluation configuration
prompt: Path to the prompt file
"""
super().__init__(task_dir, out_dir, eval_config)
self.prompt = prompt.read_text()
def generate_pov_funcs(self, config: TestConfig) -> tuple[str, str]:
llm_callbacks = get_langfuse_callbacks()
llm = create_llm(**config.llm_kwargs, callbacks=llm_callbacks)
chain = llm | extract_md
chain_config = chain.with_config(RunnableConfig(metadata=self.metadata))
res = chain_config.invoke(self.prompt)
trace_id = llm_callbacks[0].trace.trace_id
return res, trace_id
def main():
"""Main function to run the evaluation."""
parser = argparse.ArgumentParser(description="Eval vuln discovery effectiveness")
parser.add_argument("--prompt", required=True, help="Path to prompt file", type=Path)
parser.add_argument(
"--eval-config", required=True, help="Path to evaluation json config", type=Path
)
parser.add_argument(
"--task-dir", required=True, help="Path to (built) challenge task directory", type=Path
)
parser.add_argument("--out-dir", required=True, help="Eval output directory", type=Path)
args = parser.parse_args()
args.out_dir.mkdir(parents=True, exist_ok=True)
evaluator = VulnDiscoveryEvaluator(args.task_dir, args.out_dir, args.eval_config, args.prompt)
evaluator.evaluate_pov_generation()
if __name__ == "__main__":
main()
+52
View File
@@ -0,0 +1,52 @@
"""
Evaluate vulnerability discovery with different configurations
"""
import argparse
from pathlib import Path
from vuln_discovery_base import TestConfig, VulnDiscoveryEvaluatorBase
from buttercup.common.llm import create_llm, get_langfuse_callbacks
from buttercup.seed_gen.mock_context.mock import get_diff, get_harness
from buttercup.seed_gen.tasks import VULN_DISCOVERY_MAX_POV_COUNT
from buttercup.seed_gen.vuln_discovery import VulnDiscovery
class VulnDiscoveryEvaluator(VulnDiscoveryEvaluatorBase):
def generate_pov_funcs(self, config: TestConfig) -> tuple[str, str]:
"""Generate PoV functions"""
llm_callbacks = get_langfuse_callbacks()
llm = create_llm(**config.llm_kwargs, callbacks=llm_callbacks)
vuln_discovery = VulnDiscovery(llm)
harness = get_harness(self.eval_config.package_name)
diff = get_diff(self.eval_config.package_name)
analysis = vuln_discovery.analyze_diff(diff, harness)
pov_funcs = vuln_discovery.write_pov_funcs(
analysis=analysis, harness=harness, diff=diff, max_povs=VULN_DISCOVERY_MAX_POV_COUNT
)
trace_id = llm_callbacks[0].trace.trace_id
return pov_funcs, trace_id
def main():
"""Main function to run the evaluation."""
parser = argparse.ArgumentParser(description="Eval multi-step vuln discovery effectiveness")
parser.add_argument(
"--eval-config", required=True, help="Path to evaluation json config", type=Path
)
parser.add_argument(
"--task-dir", required=True, help="Path to (built) challenge task directory", type=Path
)
parser.add_argument("--out-dir", required=True, help="Eval output directory", type=Path)
args = parser.parse_args()
args.out_dir.mkdir(parents=True, exist_ok=True)
evaluator = VulnDiscoveryEvaluator(args.task_dir, args.out_dir, args.eval_config)
evaluator.evaluate_pov_generation()
if __name__ == "__main__":
main()
+5 -12
View File
@@ -24,19 +24,12 @@ requires = ["hatchling"]
build-backend = "hatchling.build"
[project.optional-dependencies]
doc = []
test = ["pytest", "pytest-cov", "pretend", "coverage[toml]"]
lint = [
# NOTE: ruff is under active development, so we pin conservatively here
# and let Dependabot periodically perform this update.
dev = [
"mypy ~= 1.15.0",
"pytest ~= 8.3.4",
"ruff ~= 0.6.2",
"mypy >= 1.0",
"types-html5lib",
"types-requests",
"types-toml",
"interrogate",
"tqdm ~= 4.67.1",
]
dev = ["seed-gen[doc,test,lint]", "twine", "build"]
[project.scripts]
"seed-gen" = "buttercup.seed_gen._cli:main"
@@ -66,7 +59,7 @@ warn_unused_ignores = true
[tool.ruff]
line-length = 100
include = ["src/**/*.py", "test/**/*.py"]
include = ["src/**/*.py", "test/**/*.py", "eval/**/*.py"]
[tool.ruff.lint]
select = ["E", "F", "I", "W", "UP"]
+18 -24
View File
@@ -51,32 +51,24 @@ The python functions are:
"""
DIFF_ANALYSIS_SYSTEM_PROMPT = """
You are a security engineer. Your job is to analyze whether a diff introduces a vulnerability in a project.
You are a security engineer. Your job is to analyze the vulnerability introduced by a commit diff in a project.
"""
DIFF_ANALYSIS_USER_PROMPT = """
You are a security engineer. Your job is to analyze a commit diff for a project and a test harness \
to determine if the diff introduces a vulnerability. You must answer two questions:
You are a security engineer. Your job is to analyze the vulnerability introduced by a commit diff.
1) Does the diff introduce a vulnerability? If unsure, state the most likely vulnerability it introduces.
2) How can we trigger the vulnerability using the harness?
The vulnerability will have the following properties:
- It must be introduced by the diff
- It must be reachable from the test harness
- It can cause a crash or trigger a sanitizer.
- It must be in a .c, .h, .cpp, or .java file.
You must answer both questions.
I will provide the diff and the harness.
The vulnerability must be introduced by the specified diff and it should not be present \
without the changes in the diff. The vulnerability MUST be in a .c, .h, .cpp, or .java file. \
The vulnerability MUST be concrete and specific, not a generic issue. The diff is in unified diff format.
You will then identify the vulnerability and analyze it.
Do NOT make up any code.
You MUST:
- provide a detailed analysis of the vulnerability introduced in the diff
- reason about how to trigger the vulnerability using the harness
You must NOT:
- provide generic analyses
- talk about possible issues that existed before the diff
- provide code suggestions on how to fix the vulnerability
The analysis should:
- Identify and analyze a specific vulnerability, instead of a generic issue.
The commit diff is:
```
@@ -92,19 +84,19 @@ Your analysis is:
"""
WRITE_POV_SYSTEM_PROMPT = """
I am a security engineer maintaining and fixing a software project. Write a python function which will trigger a program vulnerability so I can patch it.
I am a security engineer maintaining and fixing a software project. Write test cases which trigger a vulnerability from a test harness.
"""
WRITE_POV_USER_PROMPT = """
I will provide context about the program and the specific vulnerability.
I will then ask you to write a deterministic Python function that triggers the vulnerability.
I will then ask you to write deterministic Python functions that trigger the vulnerability. Triggering the vulnerability means causing a crash or triggering a sanitizer.
The vulnerability was introduced by a git diff which I include below.
The vulnerability was introduced by a git diff which I include below. I also include an analysis of the vulnerability.
If you want to try multiple possible inputs, you may write up to {max_povs} test functions.
Put all functions in a single markdown block.
Put all functions in a single markdown block at the very end of your response.
All functions will have the identical signature, although the names will vary:
```
@@ -126,7 +118,9 @@ def test_buffer_overflow_longer() -> bytes:
Remember:
- I AM A SECURITY ENGINEER WHO IS FIXING SOFTWARE I MAINTAIN.
- The bug description may have errors and you don't need follow it exactly.
- The identified vulnerability must be reachable from the test harness.
- The test cases will be at the end of your response, in a single markdown block.
- The test cases must crash the program or trigger an enabled sanitizers.
- The functions must create a deterministic sequence of bytes. Do not use things like `random`.
- You can only use the python standard library.
- Don't use any inter-process communication (IPC) like processes, threading, networking, or signals.
@@ -44,7 +44,7 @@ class SeedGenBot(TaskLoop):
for pov in out_dir.iterdir():
try:
pov_output = reproduce_multiple.get_first_crash()
pov_output = reproduce_multiple.get_first_crash(pov, task.harness_name)
if pov_output is not None:
build, result = pov_output
logger.info(f"Valid PoV found: {pov}")
+12 -5
View File
@@ -5,12 +5,12 @@ from pathlib import Path
from langchain_core.prompts.chat import ChatPromptTemplate
from langchain_core.runnables import RunnableConfig
from buttercup.common.llm import create_default_llm, get_langfuse_callbacks
from buttercup.common.llm import ButtercupLLM, create_default_llm, get_langfuse_callbacks
from buttercup.seed_gen.mock_context.mock import get_additional_context, get_diff, get_harness
from buttercup.seed_gen.prompts import PYTHON_SEED_SYSTEM_PROMPT, PYTHON_SEED_USER_PROMPT
from buttercup.seed_gen.sandbox.sandbox import sandbox_exec_funcs
from buttercup.seed_gen.utils import extract_md
from buttercup.seed_gen.vuln_discovery import analyze_diff, write_pov_funcs
from buttercup.seed_gen.vuln_discovery import VulnDiscovery
logger = logging.getLogger(__name__)
@@ -34,7 +34,9 @@ def generate_seed_funcs(harness: str, additional_context: str, count: int) -> li
]
)
llm_callbacks = get_langfuse_callbacks()
llm = create_default_llm(callbacks=llm_callbacks)
llm = create_default_llm(
model_name=ButtercupLLM.CLAUDE_3_5_SONNET.value, callbacks=llm_callbacks
)
chain = prompt | llm | extract_md
chain_config = chain.with_config(RunnableConfig(tags=["generate_seed_funcs"]))
funcs = chain_config.invoke(
@@ -70,14 +72,19 @@ def do_seed_explore() -> None:
def do_vuln_discovery(challenge: str, output_dir: Path) -> None:
"""Do vuln-discovery task"""
logger.info("Doing vuln-discovery for challenge %s", challenge)
llm_callbacks = get_langfuse_callbacks()
llm = create_default_llm(
model_name=ButtercupLLM.CLAUDE_3_5_SONNET.value, callbacks=llm_callbacks
)
vuln_discovery = VulnDiscovery(llm)
max_povs = VULN_DISCOVERY_MAX_POV_COUNT
harness = get_harness(challenge)
diff = get_diff(challenge)
try:
logger.info("Analyzing the diff in challenge %s", challenge)
analysis = analyze_diff(diff, harness)
analysis = vuln_discovery.analyze_diff(diff, harness)
logger.info("Making PoVs for the challenge %s", challenge)
pov_funcs = write_pov_funcs(analysis, harness, diff, max_povs)
pov_funcs = vuln_discovery.write_pov_funcs(analysis, harness, diff, max_povs)
sandbox_exec_funcs(pov_funcs, output_dir)
except Exception as err:
logger.error("Failed vuln-discovery for challenge %s: %s", challenge, str(err))
+7 -2
View File
@@ -20,7 +20,7 @@ def resolve_module_subpath(subpath: str) -> Path:
def extract_md(msg: AIMessage) -> str:
"""Extract the markdown from the AI message."""
"""Extract last markdown block from the AI message."""
if not isinstance(msg, AIMessage):
raise OutputParserException(
"extract_md: did not receive an AIMessage. Received: %s", type(msg)
@@ -32,7 +32,12 @@ def extract_md(msg: AIMessage) -> str:
"extract_md: content is not a string. Content is %s", type(content)
)
match = re.search(r"```([A-Za-z]*)\n(.*?)```", content, re.DOTALL)
# get last markdown block
find_iter = re.finditer(r"```([A-Za-z]*)\n(.*?)```", content, re.DOTALL)
match = None
for m in find_iter:
match = m
if match is not None:
content = match.group(2)
else:
@@ -1,10 +1,10 @@
import logging
from langchain_core.language_models import BaseChatModel
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts.chat import ChatPromptTemplate
from langchain_core.runnables import RunnableConfig
from buttercup.common.llm import create_default_llm, get_langfuse_callbacks
from buttercup.seed_gen.prompts import (
DIFF_ANALYSIS_SYSTEM_PROMPT,
DIFF_ANALYSIS_USER_PROMPT,
@@ -16,49 +16,48 @@ from buttercup.seed_gen.utils import extract_md
logger = logging.getLogger(__name__)
def analyze_diff(diff: str, harness: str) -> str:
"""
Analyze a diff for a project and a test harness to determine if the diff introduces a vuln.
"""
llm_callbacks = get_langfuse_callbacks()
llm = create_default_llm(callbacks=llm_callbacks)
prompt = ChatPromptTemplate.from_messages(
[
("system", DIFF_ANALYSIS_SYSTEM_PROMPT),
("human", DIFF_ANALYSIS_USER_PROMPT),
]
)
chain = prompt | llm | StrOutputParser()
chain_config = chain.with_config(RunnableConfig(tags=["analyze_diff"]))
analysis = chain_config.invoke(
{
"diff": diff,
"harness": harness,
}
)
return analysis
class VulnDiscovery:
def __init__(self, llm: BaseChatModel):
self.llm = llm
def analyze_diff(self, diff: str, harness: str) -> str:
"""
Analyze a diff for a project and a test harness to determine if the diff introduces a vuln.
"""
prompt = ChatPromptTemplate.from_messages(
[
("system", DIFF_ANALYSIS_SYSTEM_PROMPT),
("human", DIFF_ANALYSIS_USER_PROMPT),
]
)
chain = prompt | self.llm | StrOutputParser()
chain_config = chain.with_config(RunnableConfig(tags=["analyze_diff"]))
analysis = chain_config.invoke(
{
"diff": diff,
"harness": harness,
}
)
return analysis
def write_pov_funcs(analysis: str, harness: str, diff: str, max_povs: int) -> str:
"""
Write PoVs for a vulnerability.
"""
llm_callbacks = get_langfuse_callbacks()
llm = create_default_llm(callbacks=llm_callbacks)
prompt = ChatPromptTemplate.from_messages(
[
("system", WRITE_POV_SYSTEM_PROMPT),
("human", WRITE_POV_USER_PROMPT),
]
)
chain = prompt | llm | extract_md
chain_config = chain.with_config(RunnableConfig(tags=["write_pov_funcs"]))
pov_funcs = chain_config.invoke(
{
"analysis": analysis,
"harness": harness,
"diff": diff,
"max_povs": max_povs,
}
)
return pov_funcs
def write_pov_funcs(self, analysis: str, harness: str, diff: str, max_povs: int) -> str:
"""
Write PoVs for a vulnerability.
"""
prompt = ChatPromptTemplate.from_messages(
[
("system", WRITE_POV_SYSTEM_PROMPT),
("human", WRITE_POV_USER_PROMPT),
]
)
chain = prompt | self.llm | extract_md
chain_config = chain.with_config(RunnableConfig(tags=["write_pov_funcs"]))
pov_funcs = chain_config.invoke(
{
"analysis": analysis,
"harness": harness,
"diff": diff,
"max_povs": max_povs,
}
)
return pov_funcs
+30
View File
@@ -0,0 +1,30 @@
from langchain_core.messages import AIMessage
from buttercup.seed_gen.utils import extract_md
def test_extract_md_no_markdown():
message = AIMessage(content="This is a message with no markdown blocks")
result = extract_md(message)
assert result == "This is a message with no markdown blocks"
def test_extract_md_single_block():
message = AIMessage(
content="Some text before\n```python\nprint('hello')\nprint('hello')\n```\nText after"
)
result = extract_md(message)
assert result == "print('hello')\nprint('hello')\n"
def test_extract_md_multiple_blocks():
message = AIMessage(
content=(
"First block:\n"
"```python\nprint('first')\n```\n"
"Middle text\n"
"```python\nprint('second')\n```"
)
)
result = extract_md(message)
assert result == "print('second')\n"
+5 -424
View File
@@ -100,20 +100,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/df/73/b6e24bd22e6720ca8ee9a85a0c4a2971af8497d8f3193fa05390cbd46e09/backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8", size = 15148 },
]
[[package]]
name = "build"
version = "1.2.2.post1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "os_name == 'nt'" },
{ name = "packaging" },
{ name = "pyproject-hooks" },
]
sdist = { url = "https://files.pythonhosted.org/packages/7d/46/aeab111f8e06793e4f0e421fcad593d547fb8313b50990f31681ee2fb1ad/build-1.2.2.post1.tar.gz", hash = "sha256:b36993e92ca9375a219c99e606a122ff365a760a2d4bba0caa09bd5278b608b7", size = 46701 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/84/c2/80633736cd183ee4a62107413def345f7e6e3c01563dbca1417363cf957e/build-1.2.2.post1-py3-none-any.whl", hash = "sha256:1d61c0887fa860c01971625baae8bdd338e517b836a2f70dd1f7aa3a6b2fc5b5", size = 22950 },
]
[[package]]
name = "cachetools"
version = "4.2.4"
@@ -191,18 +177,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767 },
]
[[package]]
name = "click"
version = "8.1.8"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 },
]
[[package]]
name = "clusterfuzz"
version = "2.6.0"
@@ -280,54 +254,6 @@ dev = [
{ name = "ruff", specifier = ">=0.9.2" },
]
[[package]]
name = "coverage"
version = "7.6.12"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/0c/d6/2b53ab3ee99f2262e6f0b8369a43f6d66658eab45510331c0b3d5c8c4272/coverage-7.6.12.tar.gz", hash = "sha256:48cfc4641d95d34766ad41d9573cc0f22a48aa88d22657a1fe01dca0dbae4de2", size = 805941 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e2/7f/4af2ed1d06ce6bee7eafc03b2ef748b14132b0bdae04388e451e4b2c529b/coverage-7.6.12-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b172f8e030e8ef247b3104902cc671e20df80163b60a203653150d2fc204d1ad", size = 208645 },
{ url = "https://files.pythonhosted.org/packages/dc/60/d19df912989117caa95123524d26fc973f56dc14aecdec5ccd7d0084e131/coverage-7.6.12-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:641dfe0ab73deb7069fb972d4d9725bf11c239c309ce694dd50b1473c0f641c3", size = 208898 },
{ url = "https://files.pythonhosted.org/packages/bd/10/fecabcf438ba676f706bf90186ccf6ff9f6158cc494286965c76e58742fa/coverage-7.6.12-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e549f54ac5f301e8e04c569dfdb907f7be71b06b88b5063ce9d6953d2d58574", size = 242987 },
{ url = "https://files.pythonhosted.org/packages/4c/53/4e208440389e8ea936f5f2b0762dcd4cb03281a7722def8e2bf9dc9c3d68/coverage-7.6.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:959244a17184515f8c52dcb65fb662808767c0bd233c1d8a166e7cf74c9ea985", size = 239881 },
{ url = "https://files.pythonhosted.org/packages/c4/47/2ba744af8d2f0caa1f17e7746147e34dfc5f811fb65fc153153722d58835/coverage-7.6.12-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bda1c5f347550c359f841d6614fb8ca42ae5cb0b74d39f8a1e204815ebe25750", size = 242142 },
{ url = "https://files.pythonhosted.org/packages/e9/90/df726af8ee74d92ee7e3bf113bf101ea4315d71508952bd21abc3fae471e/coverage-7.6.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1ceeb90c3eda1f2d8c4c578c14167dbd8c674ecd7d38e45647543f19839dd6ea", size = 241437 },
{ url = "https://files.pythonhosted.org/packages/f6/af/995263fd04ae5f9cf12521150295bf03b6ba940d0aea97953bb4a6db3e2b/coverage-7.6.12-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f16f44025c06792e0fb09571ae454bcc7a3ec75eeb3c36b025eccf501b1a4c3", size = 239724 },
{ url = "https://files.pythonhosted.org/packages/1c/8e/5bb04f0318805e190984c6ce106b4c3968a9562a400180e549855d8211bd/coverage-7.6.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b076e625396e787448d27a411aefff867db2bffac8ed04e8f7056b07024eed5a", size = 241329 },
{ url = "https://files.pythonhosted.org/packages/9e/9d/fa04d9e6c3f6459f4e0b231925277cfc33d72dfab7fa19c312c03e59da99/coverage-7.6.12-cp312-cp312-win32.whl", hash = "sha256:00b2086892cf06c7c2d74983c9595dc511acca00665480b3ddff749ec4fb2a95", size = 211289 },
{ url = "https://files.pythonhosted.org/packages/53/40/53c7ffe3c0c3fff4d708bc99e65f3d78c129110d6629736faf2dbd60ad57/coverage-7.6.12-cp312-cp312-win_amd64.whl", hash = "sha256:7ae6eabf519bc7871ce117fb18bf14e0e343eeb96c377667e3e5dd12095e0288", size = 212079 },
{ url = "https://files.pythonhosted.org/packages/fb/b2/f655700e1024dec98b10ebaafd0cedbc25e40e4abe62a3c8e2ceef4f8f0a/coverage-7.6.12-py3-none-any.whl", hash = "sha256:eb8668cfbc279a536c633137deeb9435d2962caec279c3f8cf8b91fff6ff8953", size = 200552 },
]
[[package]]
name = "cryptography"
version = "44.0.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/c7/67/545c79fe50f7af51dbad56d16b23fe33f63ee6a5d956b3cb68ea110cbe64/cryptography-44.0.1.tar.gz", hash = "sha256:f51f5705ab27898afda1aaa430f34ad90dc117421057782022edf0600bec5f14", size = 710819 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/34/b9/4d1fa8d73ae6ec350012f89c3abfbff19fc95fe5420cf972e12a8d182986/cryptography-44.0.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd7c7e2d71d908dc0f8d2027e1604102140d84b155e658c20e8ad1304317691f", size = 3943865 },
{ url = "https://files.pythonhosted.org/packages/6e/57/371a9f3f3a4500807b5fcd29fec77f418ba27ffc629d88597d0d1049696e/cryptography-44.0.1-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:887143b9ff6bad2b7570da75a7fe8bbf5f65276365ac259a5d2d5147a73775f2", size = 4162562 },
{ url = "https://files.pythonhosted.org/packages/c5/1d/5b77815e7d9cf1e3166988647f336f87d5634a5ccecec2ffbe08ef8dd481/cryptography-44.0.1-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:322eb03ecc62784536bc173f1483e76747aafeb69c8728df48537eb431cd1911", size = 3951923 },
{ url = "https://files.pythonhosted.org/packages/28/01/604508cd34a4024467cd4105887cf27da128cba3edd435b54e2395064bfb/cryptography-44.0.1-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:21377472ca4ada2906bc313168c9dc7b1d7ca417b63c1c3011d0c74b7de9ae69", size = 3685194 },
{ url = "https://files.pythonhosted.org/packages/c6/3d/d3c55d4f1d24580a236a6753902ef6d8aafd04da942a1ee9efb9dc8fd0cb/cryptography-44.0.1-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:df978682c1504fc93b3209de21aeabf2375cb1571d4e61907b3e7a2540e83026", size = 4187790 },
{ url = "https://files.pythonhosted.org/packages/ea/a6/44d63950c8588bfa8594fd234d3d46e93c3841b8e84a066649c566afb972/cryptography-44.0.1-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:eb3889330f2a4a148abead555399ec9a32b13b7c8ba969b72d8e500eb7ef84cd", size = 3951343 },
{ url = "https://files.pythonhosted.org/packages/c1/17/f5282661b57301204cbf188254c1a0267dbd8b18f76337f0a7ce1038888c/cryptography-44.0.1-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:8e6a85a93d0642bd774460a86513c5d9d80b5c002ca9693e63f6e540f1815ed0", size = 4187127 },
{ url = "https://files.pythonhosted.org/packages/f3/68/abbae29ed4f9d96596687f3ceea8e233f65c9645fbbec68adb7c756bb85a/cryptography-44.0.1-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:6f76fdd6fd048576a04c5210d53aa04ca34d2ed63336d4abd306d0cbe298fddf", size = 4070666 },
{ url = "https://files.pythonhosted.org/packages/0f/10/cf91691064a9e0a88ae27e31779200b1505d3aee877dbe1e4e0d73b4f155/cryptography-44.0.1-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6c8acf6f3d1f47acb2248ec3ea261171a671f3d9428e34ad0357148d492c7864", size = 4288811 },
{ url = "https://files.pythonhosted.org/packages/ba/9f/1775600eb69e72d8f9931a104120f2667107a0ee478f6ad4fe4001559345/cryptography-44.0.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b8272f257cf1cbd3f2e120f14c68bff2b6bdfcc157fafdee84a1b795efd72862", size = 3943269 },
{ url = "https://files.pythonhosted.org/packages/25/ba/e00d5ad6b58183829615be7f11f55a7b6baa5a06910faabdc9961527ba44/cryptography-44.0.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e8d181e90a777b63f3f0caa836844a1182f1f265687fac2115fcf245f5fbec3", size = 4166461 },
{ url = "https://files.pythonhosted.org/packages/b3/45/690a02c748d719a95ab08b6e4decb9d81e0ec1bac510358f61624c86e8a3/cryptography-44.0.1-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:436df4f203482f41aad60ed1813811ac4ab102765ecae7a2bbb1dbb66dcff5a7", size = 3950314 },
{ url = "https://files.pythonhosted.org/packages/e6/50/bf8d090911347f9b75adc20f6f6569ed6ca9b9bff552e6e390f53c2a1233/cryptography-44.0.1-cp39-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4f422e8c6a28cf8b7f883eb790695d6d45b0c385a2583073f3cec434cc705e1a", size = 3686675 },
{ url = "https://files.pythonhosted.org/packages/e1/e7/cfb18011821cc5f9b21efb3f94f3241e3a658d267a3bf3a0f45543858ed8/cryptography-44.0.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:72198e2b5925155497a5a3e8c216c7fb3e64c16ccee11f0e7da272fa93b35c4c", size = 4190429 },
{ url = "https://files.pythonhosted.org/packages/07/ef/77c74d94a8bfc1a8a47b3cafe54af3db537f081742ee7a8a9bd982b62774/cryptography-44.0.1-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:2a46a89ad3e6176223b632056f321bc7de36b9f9b93b2cc1cccf935a3849dc62", size = 3950039 },
{ url = "https://files.pythonhosted.org/packages/6d/b9/8be0ff57c4592382b77406269b1e15650c9f1a167f9e34941b8515b97159/cryptography-44.0.1-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:53f23339864b617a3dfc2b0ac8d5c432625c80014c25caac9082314e9de56f41", size = 4189713 },
{ url = "https://files.pythonhosted.org/packages/78/e1/4b6ac5f4100545513b0847a4d276fe3c7ce0eacfa73e3b5ebd31776816ee/cryptography-44.0.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:888fcc3fce0c888785a4876ca55f9f43787f4c5c1cc1e2e0da71ad481ff82c5b", size = 4071193 },
{ url = "https://files.pythonhosted.org/packages/3d/cb/afff48ceaed15531eab70445abe500f07f8f96af2bb35d98af6bfa89ebd4/cryptography-44.0.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:00918d859aa4e57db8299607086f793fa7813ae2ff5a4637e318a25ef82730f7", size = 4289566 },
]
[[package]]
name = "distro"
version = "1.9.0"
@@ -346,15 +272,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/68/1b/e0a87d256e40e8c888847551b20a017a6b98139178505dc7ffb96f04e954/dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86", size = 313632 },
]
[[package]]
name = "docutils"
version = "0.21.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/ae/ed/aefcc8cd0ba62a0560c3c18c33925362d46c6075480bfa4df87b28e169a9/docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f", size = 2204444 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408 },
]
[[package]]
name = "frozenlist"
version = "1.5.0"
@@ -798,18 +715,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 },
]
[[package]]
name = "id"
version = "1.5.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "requests" },
]
sdist = { url = "https://files.pythonhosted.org/packages/22/11/102da08f88412d875fa2f1a9a469ff7ad4c874b0ca6fed0048fe385bdb3d/id-1.5.0.tar.gz", hash = "sha256:292cb8a49eacbbdbce97244f47a97b4c62540169c976552e497fd57df0734c1d", size = 15237 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/9f/cb/18326d2d89ad3b0dd143da971e77afd1e6ca6674f1b1c3df4b6bec6279fc/id-1.5.0-py3-none-any.whl", hash = "sha256:f1434e1cef91f2cbb8a4ec64663d5a23b9ed43ef44c4c957d02583d61714c658", size = 13611 },
]
[[package]]
name = "idna"
version = "3.10"
@@ -837,64 +742,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 },
]
[[package]]
name = "interrogate"
version = "1.7.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "attrs" },
{ name = "click" },
{ name = "colorama" },
{ name = "py" },
{ name = "tabulate" },
]
sdist = { url = "https://files.pythonhosted.org/packages/8b/22/74f7fcc96280eea46cf2bcbfa1354ac31de0e60a4be6f7966f12cef20893/interrogate-1.7.0.tar.gz", hash = "sha256:a320d6ec644dfd887cc58247a345054fc4d9f981100c45184470068f4b3719b0", size = 159636 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/12/c9/6869a1dcf4aaf309b9543ec070be3ec3adebee7c9bec9af8c230494134b9/interrogate-1.7.0-py3-none-any.whl", hash = "sha256:b13ff4dd8403369670e2efe684066de9fcb868ad9d7f2b4095d8112142dc9d12", size = 46982 },
]
[[package]]
name = "jaraco-classes"
version = "3.4.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "more-itertools" },
]
sdist = { url = "https://files.pythonhosted.org/packages/06/c0/ed4a27bc5571b99e3cff68f8a9fa5b56ff7df1c2251cc715a652ddd26402/jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", size = 11780 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/7f/66/b15ce62552d84bbfcec9a4873ab79d993a1dd4edb922cbfccae192bd5b5f/jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790", size = 6777 },
]
[[package]]
name = "jaraco-context"
version = "6.0.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/df/ad/f3777b81bf0b6e7bc7514a1656d3e637b2e8e15fab2ce3235730b3e7a4e6/jaraco_context-6.0.1.tar.gz", hash = "sha256:9bae4ea555cf0b14938dc0aee7c9f32ed303aa20a3b73e7dc80111628792d1b3", size = 13912 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ff/db/0c52c4cf5e4bd9f5d7135ec7669a3a767af21b3a308e1ed3674881e52b62/jaraco.context-6.0.1-py3-none-any.whl", hash = "sha256:f797fc481b490edb305122c9181830a3a5b76d84ef6d1aef2fb9b47ab956f9e4", size = 6825 },
]
[[package]]
name = "jaraco-functools"
version = "4.1.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "more-itertools" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ab/23/9894b3df5d0a6eb44611c36aec777823fc2e07740dabbd0b810e19594013/jaraco_functools-4.1.0.tar.gz", hash = "sha256:70f7e0e2ae076498e212562325e805204fc092d7b4c17e0e86c959e249701a9d", size = 19159 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/9f/4f/24b319316142c44283d7540e76c7b5a6dbd5db623abd86bb7b3491c21018/jaraco.functools-4.1.0-py3-none-any.whl", hash = "sha256:ad159f13428bc4acbf5541ad6dec511f91573b90fba04df61dafa2a1231cf649", size = 10187 },
]
[[package]]
name = "jeepney"
version = "0.8.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d6/f4/154cf374c2daf2020e05c3c6a03c91348d59b23c5366e968feb198306fdf/jeepney-0.8.0.tar.gz", hash = "sha256:5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806", size = 106005 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ae/72/2a1e2290f1ab1e06f71f3d0f1646c9e4634e70e1d37491535e19266e8dc9/jeepney-0.8.0-py3-none-any.whl", hash = "sha256:c0a454ad016ca575060802ee4d590dd912e35c122fa04e70306de3d076cce755", size = 48435 },
]
[[package]]
name = "jiter"
version = "0.8.2"
@@ -936,23 +783,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/71/92/5e77f98553e9e75130c78900d000368476aed74276eb8ae8796f65f00918/jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942", size = 7595 },
]
[[package]]
name = "keyring"
version = "25.6.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "jaraco-classes" },
{ name = "jaraco-context" },
{ name = "jaraco-functools" },
{ name = "jeepney", marker = "sys_platform == 'linux'" },
{ name = "pywin32-ctypes", marker = "sys_platform == 'win32'" },
{ name = "secretstorage", marker = "sys_platform == 'linux'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/70/09/d904a6e96f76ff214be59e7aa6ef7190008f52a0ab6689760a98de0bf37d/keyring-25.6.0.tar.gz", hash = "sha256:0b39998aa941431eb3d9b0d4b2460bc773b9df6fed7621c2dfb291a7e0187a66", size = 62750 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d3/32/da7f44bcb1105d3e88a0b74ebdca50c59121d2ddf71c9e34ba47df7f3a56/keyring-25.6.0-py3-none-any.whl", hash = "sha256:552a3f7af126ece7ed5c89753650eec89c7eaae8617d0aa4d9ad2b75111266bd", size = 39085 },
]
[[package]]
name = "langchain"
version = "0.3.19"
@@ -1054,36 +884,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/8b/e4/5380e8229c442e406404977d2ec71a9db6a3e6a89fce7791c6ad7cd2bdbe/langsmith-0.3.8-py3-none-any.whl", hash = "sha256:fbb9dd97b0f090219447fca9362698d07abaeda1da85aa7cc6ec6517b36581b1", size = 332800 },
]
[[package]]
name = "markdown-it-py"
version = "3.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "mdurl" },
]
sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 },
]
[[package]]
name = "mdurl"
version = "0.1.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 },
]
[[package]]
name = "more-itertools"
version = "10.6.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/88/3b/7fa1fe835e2e93fd6d7b52b2f95ae810cf5ba133e1845f726f5a992d62c2/more-itertools-10.6.0.tar.gz", hash = "sha256:2cd7fad1009c31cc9fb6a035108509e6547547a7a738374f10bd49a09eb3ee3b", size = 125009 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/23/62/0fe302c6d1be1c777cab0616e6302478251dfbf9055ad426f5d0def75c89/more_itertools-10.6.0-py3-none-any.whl", hash = "sha256:6eb054cb4b6db1473f6e15fcc676a08e4732548acd47c708f0e179c2c7c01e89", size = 63038 },
]
[[package]]
name = "mozfile"
version = "3.0.0"
@@ -1173,28 +973,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 },
]
[[package]]
name = "nh3"
version = "0.2.20"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/46/f2/eb781d94c7855e9129cbbdd3ab09a470441e4176a82a396ae1df270a7333/nh3-0.2.20.tar.gz", hash = "sha256:9705c42d7ff88a0bea546c82d7fe5e59135e3d3f057e485394f491248a1f8ed5", size = 17489 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/92/08/5e3b61eed1bc0efeb330ddc5cf5194f28a0b7be7943aa20bd44cfe14650b/nh3-0.2.20-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:76e2f603b30c02ff6456b233a83fc377dedab6a50947b04e960a6b905637b776", size = 1202141 },
{ url = "https://files.pythonhosted.org/packages/29/d2/3377f8006c71e95e007b07b5bfcac22c9de4744ca3efb23b396d3deb9581/nh3-0.2.20-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:181063c581defe683bd4bb78188ac9936d208aebbc74c7f7c16b6a32ae2ebb38", size = 760699 },
{ url = "https://files.pythonhosted.org/packages/37/d7/7077f925d7d680d53dcb6e18a4af13d1a7da59761c06c193bfa249a7470a/nh3-0.2.20-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:231addb7643c952cd6d71f1c8702d703f8fe34afcb20becb3efb319a501a12d7", size = 747353 },
{ url = "https://files.pythonhosted.org/packages/cb/59/6b2f32af477aae81f1454a7f6ef490ebc3c22dd9e1370e73fcfe243dc07a/nh3-0.2.20-cp38-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:1b9a8340a0aab991c68a5ca938d35ef4a8a3f4bf1b455da8855a40bee1fa0ace", size = 854125 },
{ url = "https://files.pythonhosted.org/packages/5b/f2/c3d2f7b801477b8b387b51fbefd16dc7ade888aeac547f18ba0558fd6f48/nh3-0.2.20-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:10317cd96fe4bbd4eb6b95f3920b71c902157ad44fed103fdcde43e3b8ee8be6", size = 817453 },
{ url = "https://files.pythonhosted.org/packages/42/4d/f7e3a35506a0eba6eedafc21ad52773985511eb838812e9f96354831ad3c/nh3-0.2.20-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8698db4c04b140800d1a1cd3067fda399e36e1e2b8fc1fe04292a907350a3e9b", size = 891694 },
{ url = "https://files.pythonhosted.org/packages/e6/0e/c499453c296fb40366e3069cd68fde77a10f0a30a17b9d3b491eb3ebc5bf/nh3-0.2.20-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3eb04b9c3deb13c3a375ea39fd4a3c00d1f92e8fb2349f25f1e3e4506751774b", size = 744388 },
{ url = "https://files.pythonhosted.org/packages/18/67/c3de8022ba2719bdbbdd3704d1e32dbc7d3f8ac8646247711645fc90d051/nh3-0.2.20-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:92f3f1c4f47a2c6f3ca7317b1d5ced05bd29556a75d3a4e2715652ae9d15c05d", size = 764831 },
{ url = "https://files.pythonhosted.org/packages/f0/14/a4ea40e2439717d11c3104fc2dc0ac412301b7aeb81d6a3d0e6505c77e7d/nh3-0.2.20-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ddefa9fd6794a87e37d05827d299d4b53a3ec6f23258101907b96029bfef138a", size = 923334 },
{ url = "https://files.pythonhosted.org/packages/ed/ae/e8ee8afaf67903dd304f390056d1ea620327524e2ad66127a331b14d5d98/nh3-0.2.20-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:ce3731c8f217685d33d9268362e5b4f770914e922bba94d368ab244a59a6c397", size = 994873 },
{ url = "https://files.pythonhosted.org/packages/20/b5/02122cfe3b36cf0ba0fcd73a04fd462e1f7a9d91b456f6e0b70e46df21c7/nh3-0.2.20-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:09f037c02fc2c43b211ff1523de32801dcfb0918648d8e651c36ef890f1731ec", size = 915707 },
{ url = "https://files.pythonhosted.org/packages/47/d3/5df43cc3570cdc9eb1dc79a39191f89fedf8bcefd8d30a161ff1dffb146c/nh3-0.2.20-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:813f1c8012dd64c990514b795508abb90789334f76a561fa0fd4ca32d2275330", size = 908539 },
{ url = "https://files.pythonhosted.org/packages/4f/fd/aa000f6c76a832c488eac26f20d2e8a221ba2b965efce692f14ebc4290bf/nh3-0.2.20-cp38-abi3-win32.whl", hash = "sha256:47b2946c0e13057855209daeffb45dc910bd0c55daf10190bb0b4b60e2999784", size = 540439 },
{ url = "https://files.pythonhosted.org/packages/19/31/d65594efd3b42b1de2335d576eb77525691fc320dbf8617948ee05c008e5/nh3-0.2.20-cp38-abi3-win_amd64.whl", hash = "sha256:da87573f03084edae8eb87cfe811ec338606288f81d333c07d2a9a0b9b976c0b", size = 541249 },
]
[[package]]
name = "numpy"
version = "2.2.3"
@@ -1296,15 +1074,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 },
]
[[package]]
name = "pretend"
version = "1.0.9"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/3c/f8/7c86fd40c9e83deb10891a60d2dcb1af0b3b38064d72ebdb12486acc824f/pretend-1.0.9.tar.gz", hash = "sha256:c90eb810cde8ebb06dafcb8796f9a95228ce796531bc806e794c2f4649aa1b10", size = 4848 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/49/1f/3d4f0579913edd3ad5b23ad52fcc42531cb736ad52af2ba6c057da8785b6/pretend-1.0.9-py2.py3-none-any.whl", hash = "sha256:e389b12b7073604be67845dbe32bf8297360ad9a609b24846fe15d86e0b7dc01", size = 3848 },
]
[[package]]
name = "propcache"
version = "0.2.1"
@@ -1366,15 +1135,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/50/1b/6921afe68c74868b4c9fa424dad3be35b095e16687989ebbb50ce4fceb7c/psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553", size = 244885 },
]
[[package]]
name = "py"
version = "1.11.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/98/ff/fec109ceb715d2a6b4c4a85a61af3b40c723a961e8828319fbcb15b868dc/py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719", size = 207796 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f6/f0/10642828a8dfb741e5f3fbaac830550a518a775c7fff6f04a007259b0548/py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378", size = 98708 },
]
[[package]]
name = "pyasn1"
version = "0.6.1"
@@ -1457,15 +1217,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/b4/46/93416fdae86d40879714f72956ac14df9c7b76f7d41a4d68aa9f71a0028b/pydantic_settings-2.7.1-py3-none-any.whl", hash = "sha256:590be9e6e24d06db33a4262829edef682500ef008565a969c73d39d5f8bfb3fd", size = 29718 },
]
[[package]]
name = "pygments"
version = "2.19.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293 },
]
[[package]]
name = "pymemcache"
version = "4.0.0"
@@ -1504,15 +1255,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/1c/a7/c8a2d361bf89c0d9577c934ebb7421b25dc84bf3a8e3ac0a40aed9acc547/pyparsing-3.2.1-py3-none-any.whl", hash = "sha256:506ff4f4386c4cec0590ec19e6302d3aedb992fdc02c761e90416f158dacf8e1", size = 107716 },
]
[[package]]
name = "pyproject-hooks"
version = "1.2.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/e7/82/28175b2414effca1cdac8dc99f76d660e7a4fb0ceefa4b4ab8f5f6742925/pyproject_hooks-1.2.0.tar.gz", hash = "sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8", size = 19228 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl", hash = "sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913", size = 10216 },
]
[[package]]
name = "pytest"
version = "8.3.4"
@@ -1528,19 +1270,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/11/92/76a1c94d3afee238333bc0a42b82935dd8f9cf8ce9e336ff87ee14d9e1cf/pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6", size = 343083 },
]
[[package]]
name = "pytest-cov"
version = "6.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "coverage" },
{ name = "pytest" },
]
sdist = { url = "https://files.pythonhosted.org/packages/be/45/9b538de8cef30e17c7b45ef42f538a94889ed6a16f2387a6c89e73220651/pytest-cov-6.0.0.tar.gz", hash = "sha256:fde0b595ca248bb8e2d76f020b465f3b107c9632e6a1d1705f17834c89dcadc0", size = 66945 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/36/3b/48e79f2cd6a61dbbd4807b4ed46cb564b4fd50a76166b1c4ea5c1d9e2371/pytest_cov-6.0.0-py3-none-any.whl", hash = "sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35", size = 22949 },
]
[[package]]
name = "python-dotenv"
version = "1.0.1"
@@ -1559,15 +1288,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/eb/38/ac33370d784287baa1c3d538978b5e2ea064d4c1b93ffbd12826c190dd10/pytz-2025.1-py2.py3-none-any.whl", hash = "sha256:89dd22dca55b46eac6eda23b2d72721bf1bdfef212645d81513ef5d03038de57", size = 507930 },
]
[[package]]
name = "pywin32-ctypes"
version = "0.2.3"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756 },
]
[[package]]
name = "pyyaml"
version = "6.0.2"
@@ -1585,20 +1305,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338 },
]
[[package]]
name = "readme-renderer"
version = "44.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "docutils" },
{ name = "nh3" },
{ name = "pygments" },
]
sdist = { url = "https://files.pythonhosted.org/packages/5a/a9/104ec9234c8448c4379768221ea6df01260cd6c2ce13182d4eac531c8342/readme_renderer-44.0.tar.gz", hash = "sha256:8712034eabbfa6805cacf1402b4eeb2a73028f72d1166d6f5cb7f9c047c5d1e1", size = 32056 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e1/67/921ec3024056483db83953ae8e48079ad62b92db7880013ca77632921dd0/readme_renderer-44.0-py3-none-any.whl", hash = "sha256:2fbca89b81a08526aadf1357a8c2ae889ec05fb03f5da67f9769c9a592166151", size = 13310 },
]
[[package]]
name = "redis"
version = "5.2.1"
@@ -1671,28 +1377,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06", size = 54481 },
]
[[package]]
name = "rfc3986"
version = "2.0.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/85/40/1520d68bfa07ab5a6f065a186815fb6610c86fe957bc065754e47f7b0840/rfc3986-2.0.0.tar.gz", hash = "sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c", size = 49026 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ff/9a/9afaade874b2fa6c752c36f1548f718b5b83af81ed9b76628329dab81c1b/rfc3986-2.0.0-py2.py3-none-any.whl", hash = "sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd", size = 31326 },
]
[[package]]
name = "rich"
version = "13.9.4"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "markdown-it-py" },
{ name = "pygments" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", size = 223149 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424 },
]
[[package]]
name = "rsa"
version = "4.9"
@@ -1730,19 +1414,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/3e/14/fd026bc74ded05e2351681545a5f626e78ef831f8edce064d61acd2e6ec7/ruff-0.6.9-py3-none-win_arm64.whl", hash = "sha256:a9641e31476d601f83cd602608739a0840e348bda93fec9f1ee816f8b6798b93", size = 8679879 },
]
[[package]]
name = "secretstorage"
version = "3.3.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cryptography" },
{ name = "jeepney" },
]
sdist = { url = "https://files.pythonhosted.org/packages/53/a4/f48c9d79cb507ed1373477dbceaba7401fd8a23af63b837fa61f1dcd3691/SecretStorage-3.3.3.tar.gz", hash = "sha256:2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77", size = 19739 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/54/24/b4293291fa1dd830f353d2cb163295742fa87f179fcc8a20a306a81978b7/SecretStorage-3.3.3-py3-none-any.whl", hash = "sha256:f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99", size = 15221 },
]
[[package]]
name = "seed-gen"
version = "0.1.0"
@@ -1755,51 +1426,20 @@ dependencies = [
[package.optional-dependencies]
dev = [
{ name = "build" },
{ name = "coverage" },
{ name = "interrogate" },
{ name = "mypy" },
{ name = "pretend" },
{ name = "pytest" },
{ name = "pytest-cov" },
{ name = "ruff" },
{ name = "twine" },
{ name = "types-html5lib" },
{ name = "types-requests" },
{ name = "types-toml" },
]
lint = [
{ name = "interrogate" },
{ name = "mypy" },
{ name = "ruff" },
{ name = "types-html5lib" },
{ name = "types-requests" },
{ name = "types-toml" },
]
test = [
{ name = "coverage" },
{ name = "pretend" },
{ name = "pytest" },
{ name = "pytest-cov" },
{ name = "tqdm" },
]
[package.metadata]
requires-dist = [
{ name = "build", marker = "extra == 'dev'" },
{ name = "common", editable = "../common" },
{ name = "coverage", extras = ["toml"], marker = "extra == 'test'" },
{ name = "interrogate", marker = "extra == 'lint'" },
{ name = "mypy", marker = "extra == 'lint'", specifier = ">=1.0" },
{ name = "pretend", marker = "extra == 'test'" },
{ name = "pytest", marker = "extra == 'test'" },
{ name = "pytest-cov", marker = "extra == 'test'" },
{ name = "mypy", marker = "extra == 'dev'", specifier = "~=1.15.0" },
{ name = "pytest", marker = "extra == 'dev'", specifier = "~=8.3.4" },
{ name = "redis", specifier = "~=5.2.1" },
{ name = "ruff", marker = "extra == 'lint'", specifier = "~=0.6.2" },
{ name = "seed-gen", extras = ["doc", "test", "lint"], marker = "extra == 'dev'" },
{ name = "twine", marker = "extra == 'dev'" },
{ name = "types-html5lib", marker = "extra == 'lint'" },
{ name = "types-requests", marker = "extra == 'lint'" },
{ name = "types-toml", marker = "extra == 'lint'" },
{ name = "ruff", marker = "extra == 'dev'", specifier = "~=0.6.2" },
{ name = "tqdm", marker = "extra == 'dev'", specifier = "~=4.67.1" },
{ name = "wasmtime", specifier = "~=29.0.0" },
]
@@ -1851,15 +1491,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/aa/e4/592120713a314621c692211eba034d09becaf6bc8848fabc1dc2a54d8c16/SQLAlchemy-2.0.38-py3-none-any.whl", hash = "sha256:63178c675d4c80def39f1febd625a6333f44c0ba269edd8a468b156394b27753", size = 1896347 },
]
[[package]]
name = "tabulate"
version = "0.9.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/ec/fe/802052aecb21e3797b8f7902564ab6ea0d60ff8ca23952079064155d1ae1/tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c", size = 81090 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f", size = 35252 },
]
[[package]]
name = "tenacity"
version = "9.0.0"
@@ -1899,56 +1530,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540 },
]
[[package]]
name = "twine"
version = "6.1.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "id" },
{ name = "keyring", marker = "platform_machine != 'ppc64le' and platform_machine != 's390x'" },
{ name = "packaging" },
{ name = "readme-renderer" },
{ name = "requests" },
{ name = "requests-toolbelt" },
{ name = "rfc3986" },
{ name = "rich" },
{ name = "urllib3" },
]
sdist = { url = "https://files.pythonhosted.org/packages/c8/a2/6df94fc5c8e2170d21d7134a565c3a8fb84f9797c1dd65a5976aaf714418/twine-6.1.0.tar.gz", hash = "sha256:be324f6272eff91d07ee93f251edf232fc647935dd585ac003539b42404a8dbd", size = 168404 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/7c/b6/74e927715a285743351233f33ea3c684528a0d374d2e43ff9ce9585b73fe/twine-6.1.0-py3-none-any.whl", hash = "sha256:a47f973caf122930bf0fbbf17f80b83bc1602c9ce393c7845f289a3001dc5384", size = 40791 },
]
[[package]]
name = "types-html5lib"
version = "1.1.11.20241018"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/b6/9d/f6fbcc8246f5e46845b4f989c4e17e6fb3ce572f7065b185e515bf8a3be7/types-html5lib-1.1.11.20241018.tar.gz", hash = "sha256:98042555ff78d9e3a51c77c918b1041acbb7eb6c405408d8a9e150ff5beccafa", size = 11370 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ba/7c/f862b1dc31268ef10fe95b43dcdf216ba21a592fafa2d124445cd6b92e93/types_html5lib-1.1.11.20241018-py3-none-any.whl", hash = "sha256:3f1e064d9ed2c289001ae6392c84c93833abb0816165c6ff0abfc304a779f403", size = 17292 },
]
[[package]]
name = "types-requests"
version = "2.32.0.20241016"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "urllib3" },
]
sdist = { url = "https://files.pythonhosted.org/packages/fa/3c/4f2a430c01a22abd49a583b6b944173e39e7d01b688190a5618bd59a2e22/types-requests-2.32.0.20241016.tar.gz", hash = "sha256:0d9cad2f27515d0e3e3da7134a1b6f28fb97129d86b867f24d9c726452634d95", size = 18065 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d7/01/485b3026ff90e5190b5e24f1711522e06c79f4a56c8f4b95848ac072e20f/types_requests-2.32.0.20241016-py3-none-any.whl", hash = "sha256:4195d62d6d3e043a4eaaf08ff8a62184584d2e8684e9d2aa178c7915a7da3747", size = 15836 },
]
[[package]]
name = "types-toml"
version = "0.10.8.20240310"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/86/47/3e4c75042792bff8e90d7991aa5c51812cc668828cc6cce711e97f63a607/types-toml-0.10.8.20240310.tar.gz", hash = "sha256:3d41501302972436a6b8b239c850b26689657e25281b48ff0ec06345b8830331", size = 4392 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/da/a2/d32ab58c0b216912638b140ab2170ee4b8644067c293b170e19fba340ccc/types_toml-0.10.8.20240310-py3-none-any.whl", hash = "sha256:627b47775d25fa29977d9c70dc0cbab3f314f32c8d8d0c012f2ef5de7aaec05d", size = 4777 },
]
[[package]]
name = "typing-extensions"
version = "4.12.2"