mirror of
https://github.com/Paillat-dev/esc-dramatic-unpause.git
synced 2026-01-02 01:06:21 +00:00
Add ESC data and improve scoring system functionality
This commit is contained in:
@@ -13,6 +13,7 @@ dependencies = [
|
|||||||
[dependency-groups]
|
[dependency-groups]
|
||||||
dev = [
|
dev = [
|
||||||
"basedpyright>=1.29.2",
|
"basedpyright>=1.29.2",
|
||||||
|
"orjson>=3.10.18",
|
||||||
"ruff>=0.11.13",
|
"ruff>=0.11.13",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -38,3 +39,13 @@ docstring-code-line-length = "dynamic"
|
|||||||
|
|
||||||
[tool.ruff.lint]
|
[tool.ruff.lint]
|
||||||
select = ["ALL"]
|
select = ["ALL"]
|
||||||
|
ignore = [
|
||||||
|
"PLR0915",
|
||||||
|
"D100",
|
||||||
|
"D104",
|
||||||
|
"COM812",
|
||||||
|
"D213",
|
||||||
|
"D203",
|
||||||
|
"ISC003" # conflicts with basedpyright reportImplicitStringConcatenation"
|
||||||
|
]
|
||||||
|
per-file-ignores = { "tests/**/*" = ["S101", "BLE001"] }
|
||||||
@@ -1,22 +1,25 @@
|
|||||||
from pathlib import Path
|
|
||||||
import time
|
import time
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
import typer
|
import typer
|
||||||
from rich import print
|
from rich import print # noqa: A004
|
||||||
from rich.table import Table
|
|
||||||
from rich.console import Console
|
from rich.console import Console
|
||||||
from rich.prompt import Prompt, Confirm
|
|
||||||
from rich.spinner import Spinner
|
|
||||||
from rich.panel import Panel
|
|
||||||
from rich.live import Live
|
from rich.live import Live
|
||||||
|
from rich.panel import Panel
|
||||||
|
from rich.prompt import Confirm, Prompt
|
||||||
|
from rich.spinner import Spinner
|
||||||
|
from rich.table import Table
|
||||||
|
|
||||||
app = typer.Typer()
|
app = typer.Typer()
|
||||||
|
|
||||||
|
|
||||||
def parse_jury_file(file_path: Path) -> dict[str, int]:
|
def parse_jury_file(file_path: Path) -> dict[str, int]:
|
||||||
|
"""Parse the jury file and return a dictionary of scores."""
|
||||||
with file_path.open("r", encoding="utf-8") as file:
|
with file_path.open("r", encoding="utf-8") as file:
|
||||||
jury_data: dict[str, int] = {}
|
jury_data: dict[str, int] = {}
|
||||||
for line in file:
|
for line in file:
|
||||||
parts = line.strip().split()
|
parts = line.strip().split()
|
||||||
if len(parts) >= 2:
|
if len(parts) >= 2: # noqa: PLR2004
|
||||||
name = " ".join(parts[:-1])
|
name = " ".join(parts[:-1])
|
||||||
score = parts[-1]
|
score = parts[-1]
|
||||||
jury_data[name] = int(score)
|
jury_data[name] = int(score)
|
||||||
@@ -24,7 +27,9 @@ def parse_jury_file(file_path: Path) -> dict[str, int]:
|
|||||||
print(f"[red]Invalid line format in [bold]{file_path}[/bold]: {line.strip()}[/red]")
|
print(f"[red]Invalid line format in [bold]{file_path}[/bold]: {line.strip()}[/red]")
|
||||||
return jury_data
|
return jury_data
|
||||||
|
|
||||||
|
|
||||||
def render_table(jury_data: dict[str, int], televoting_data: dict[str, int]) -> Table:
|
def render_table(jury_data: dict[str, int], televoting_data: dict[str, int]) -> Table:
|
||||||
|
"""Render the jury and televoting scores table."""
|
||||||
table = Table("Name", "Jury Score", "Televoting Score", title="Scores")
|
table = Table("Name", "Jury Score", "Televoting Score", title="Scores")
|
||||||
for name in sorted(jury_data.keys()):
|
for name in sorted(jury_data.keys()):
|
||||||
jury_score = jury_data.get(name, 0)
|
jury_score = jury_data.get(name, 0)
|
||||||
@@ -32,21 +37,28 @@ def render_table(jury_data: dict[str, int], televoting_data: dict[str, int]) ->
|
|||||||
table.add_row(name, str(jury_score), str(televoting_score))
|
table.add_row(name, str(jury_score), str(televoting_score))
|
||||||
return table
|
return table
|
||||||
|
|
||||||
def render_table_final(final_scores: dict[str, int], jury_data: dict[str, int], televoting_data: dict[str, int]) -> Table:
|
|
||||||
|
def render_table_final(
|
||||||
|
final_scores: dict[str, int], jury_data: dict[str, int], televoting_data: dict[str, int]
|
||||||
|
) -> Table:
|
||||||
|
"""Render the final scores table with jury and televoting scores."""
|
||||||
table = Table("Name", "Jury Score", "Televoting Score", "Final Score", title="Final Scores")
|
table = Table("Name", "Jury Score", "Televoting Score", "Final Score", title="Final Scores")
|
||||||
for name in final_scores.keys():
|
for name, score in final_scores.items():
|
||||||
jury_score = jury_data.get(name, 0)
|
jury_score = jury_data.get(name, 0)
|
||||||
televoting_score = televoting_data.get(name, 0)
|
televoting_score = televoting_data.get(name, 0)
|
||||||
final_score = final_scores[name]
|
final_score = score
|
||||||
table.add_row(name, str(jury_score), str(televoting_score), str(final_score))
|
table.add_row(name, str(jury_score), str(televoting_score), str(final_score))
|
||||||
return table
|
return table
|
||||||
|
|
||||||
def sort_dict[A, B](d: dict[A, B], reverse: bool = True) -> dict[A, B]:
|
|
||||||
|
def sort_dict[A, B](d: dict[A, B], *, reverse: bool = True) -> dict[A, B]:
|
||||||
"""Sorts a dictionary by its values in descending order."""
|
"""Sorts a dictionary by its values in descending order."""
|
||||||
return dict(sorted(d.items(), key=lambda item: item[1], reverse=reverse))
|
return dict(sorted(d.items(), key=lambda item: item[1], reverse=reverse)) # pyright: ignore [reportCallIssue, reportUnknownArgumentType, reportArgumentType]
|
||||||
|
|
||||||
|
|
||||||
@app.command()
|
@app.command()
|
||||||
def run(jury_path: Path = "jury.txt"): # pyright: ignore [reportArgumentType]
|
def run(jury_path: Path = "jury.txt", participating_countries: int = 37) -> None: # pyright: ignore [reportArgumentType]
|
||||||
|
"""Run the ESC jury and televoting scoring prediction system."""
|
||||||
jury_file = Path(jury_path)
|
jury_file = Path(jury_path)
|
||||||
if not jury_file.exists():
|
if not jury_file.exists():
|
||||||
print(f"[red]File [bold]{jury_path}[bold/] does not exist.[/red]")
|
print(f"[red]File [bold]{jury_path}[bold/] does not exist.[/red]")
|
||||||
@@ -61,7 +73,7 @@ def run(jury_path: Path = "jury.txt"): # pyright: ignore [reportArgumentType]
|
|||||||
table = render_table(jury_scores, televoting_data)
|
table = render_table(jury_scores, televoting_data)
|
||||||
console.print(table)
|
console.print(table)
|
||||||
|
|
||||||
# Collect N–1 televote scores
|
# Collect N-1 televote scores
|
||||||
while len(televoting_data) < len(jury_scores) - 1:
|
while len(televoting_data) < len(jury_scores) - 1:
|
||||||
name = list(jury_scores.keys())[len(televoting_data)]
|
name = list(jury_scores.keys())[len(televoting_data)]
|
||||||
r: str = Prompt.ask(f"Enter televoting score for [bold]{name}[/bold] (go back with b):", default="0")
|
r: str = Prompt.ask(f"Enter televoting score for [bold]{name}[/bold] (go back with b):", default="0")
|
||||||
@@ -86,13 +98,13 @@ def run(jury_path: Path = "jury.txt"): # pyright: ignore [reportArgumentType]
|
|||||||
console.print(render_table(jury_scores, televoting_data))
|
console.print(render_table(jury_scores, televoting_data))
|
||||||
|
|
||||||
# After N-1 entries, confirm before computing last
|
# After N-1 entries, confirm before computing last
|
||||||
if len(televoting_data) == len(jury_scores) - 1:
|
if len(televoting_data) == len(jury_scores) - 1 and not Confirm.ask(
|
||||||
if not Confirm.ask("Final score entered! Review and continue?", default=True):
|
"Final score entered! Review and continue?", default=True
|
||||||
# remove last entry if they want to re-enter
|
):
|
||||||
last = list(televoting_data.keys())[-1]
|
last = list(televoting_data.keys())[-1]
|
||||||
del televoting_data[last]
|
del televoting_data[last]
|
||||||
console.clear()
|
console.clear()
|
||||||
console.print(render_table(jury_scores, televoting_data))
|
console.print(render_table(jury_scores, televoting_data))
|
||||||
|
|
||||||
console.clear()
|
console.clear()
|
||||||
print("[green]Televoting scores have been successfully entered![/green]")
|
print("[green]Televoting scores have been successfully entered![/green]")
|
||||||
@@ -103,8 +115,10 @@ def run(jury_path: Path = "jury.txt"): # pyright: ignore [reportArgumentType]
|
|||||||
for _ in range(20):
|
for _ in range(20):
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
|
|
||||||
# Compute the 38×58 total and subtract the N-1 sum
|
# Compute the (participating_countries + 1)x58 total and subtract the N-1 sum
|
||||||
total_available = sum((12, 10, 8, 7, 6, 5, 4, 3, 2, 1)) * (len(jury_scores) + 1) # N-1 televoting + 1 rest of the world
|
total_available = sum((12, 10, 8, 7, 6, 5, 4, 3, 2, 1)) * (
|
||||||
|
participating_countries + 1
|
||||||
|
) # N-1 televoting + 1 rest of the world
|
||||||
sum_entered = sum(televoting_data.values())
|
sum_entered = sum(televoting_data.values())
|
||||||
missing = set(jury_scores) - set(televoting_data)
|
missing = set(jury_scores) - set(televoting_data)
|
||||||
country_to_predict = missing.pop()
|
country_to_predict = missing.pop()
|
||||||
@@ -112,12 +126,9 @@ def run(jury_path: Path = "jury.txt"): # pyright: ignore [reportArgumentType]
|
|||||||
|
|
||||||
# Sort and compute finals
|
# Sort and compute finals
|
||||||
televoting_data = sort_dict(televoting_data)
|
televoting_data = sort_dict(televoting_data)
|
||||||
|
|
||||||
jury_scores = sort_dict(jury_scores)
|
jury_scores = sort_dict(jury_scores)
|
||||||
final_scores: dict[str, int] = {
|
final_scores: dict[str, int] = {name: jury_scores[name] + televoting_data.get(name, 0) for name in jury_scores}
|
||||||
name: jury_scores[name] + televoting_data.get(name, 0)
|
|
||||||
for name in jury_scores
|
|
||||||
}
|
|
||||||
final_scores = dict(sorted(final_scores.items(), key=lambda item: item[1], reverse=True))
|
final_scores = dict(sorted(final_scores.items(), key=lambda item: item[1], reverse=True))
|
||||||
|
|
||||||
# Render and announce
|
# Render and announce
|
||||||
|
|||||||
0
tests/__init__.py
Normal file
0
tests/__init__.py
Normal file
118
tests/esc_data.json
Normal file
118
tests/esc_data.json
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
{
|
||||||
|
"2025": {
|
||||||
|
"jury": {
|
||||||
|
"Austria": 258,
|
||||||
|
"Switzerland": 214,
|
||||||
|
"France": 180,
|
||||||
|
"Italy": 159,
|
||||||
|
"Netherlands": 133,
|
||||||
|
"Sweden": 126,
|
||||||
|
"Latvia": 116,
|
||||||
|
"Greece": 105,
|
||||||
|
"Estonia": 98,
|
||||||
|
"United Kingdom": 88,
|
||||||
|
"Finland": 88,
|
||||||
|
"Malta": 83,
|
||||||
|
"Germany": 77,
|
||||||
|
"Ukraine": 60,
|
||||||
|
"Israel": 60,
|
||||||
|
"Albania": 45,
|
||||||
|
"Denmark": 45,
|
||||||
|
"Armenia": 42,
|
||||||
|
"Portugal": 37,
|
||||||
|
"Lithuania": 34,
|
||||||
|
"Spain": 27,
|
||||||
|
"Luxembourg": 23,
|
||||||
|
"Norway": 22,
|
||||||
|
"Poland": 17,
|
||||||
|
"San Marino": 9,
|
||||||
|
"Iceland": 0
|
||||||
|
},
|
||||||
|
"televote": {
|
||||||
|
"Israel": 297,
|
||||||
|
"Estonia": 258,
|
||||||
|
"Sweden": 195,
|
||||||
|
"Austria": 178,
|
||||||
|
"Albania": 173,
|
||||||
|
"Ukraine": 158,
|
||||||
|
"Poland": 139,
|
||||||
|
"Greece": 126,
|
||||||
|
"Finland": 108,
|
||||||
|
"Italy": 97,
|
||||||
|
"Germany": 74,
|
||||||
|
"Norway": 67,
|
||||||
|
"Lithuania": 62,
|
||||||
|
"France": 50,
|
||||||
|
"Latvia": 42,
|
||||||
|
"Netherlands": 42,
|
||||||
|
"Iceland": 33,
|
||||||
|
"Armenia": 30,
|
||||||
|
"Luxembourg": 24,
|
||||||
|
"San Marino": 18,
|
||||||
|
"Portugal": 13,
|
||||||
|
"Spain": 10,
|
||||||
|
"Malta": 8,
|
||||||
|
"Denmark": 2,
|
||||||
|
"United Kingdom": 0,
|
||||||
|
"Switzerland": 0
|
||||||
|
},
|
||||||
|
"winner": "Austria"
|
||||||
|
},
|
||||||
|
"2024": {
|
||||||
|
"jury": {
|
||||||
|
"Switzerland": 365,
|
||||||
|
"France": 218,
|
||||||
|
"Croatia": 210,
|
||||||
|
"Italy": 164,
|
||||||
|
"Ukraine": 146,
|
||||||
|
"Ireland": 142,
|
||||||
|
"Portugal": 139,
|
||||||
|
"Sweden": 125,
|
||||||
|
"Armenia": 101,
|
||||||
|
"Germany": 99,
|
||||||
|
"Luxembourg": 83,
|
||||||
|
"Israel": 52,
|
||||||
|
"United Kingdom": 46,
|
||||||
|
"Greece": 41,
|
||||||
|
"Latvia": 36,
|
||||||
|
"Cyprus": 34,
|
||||||
|
"Lithuania": 32,
|
||||||
|
"Serbia": 22,
|
||||||
|
"Spain": 19,
|
||||||
|
"Austria": 19,
|
||||||
|
"Georgia": 15,
|
||||||
|
"Slovenia": 15,
|
||||||
|
"Norway": 12,
|
||||||
|
"Finland": 7,
|
||||||
|
"Estonia": 4
|
||||||
|
},
|
||||||
|
"televote": {
|
||||||
|
"Croatia": 337,
|
||||||
|
"Israel": 323,
|
||||||
|
"Ukraine": 307,
|
||||||
|
"France": 227,
|
||||||
|
"Switzerland": 226,
|
||||||
|
"Ireland": 136,
|
||||||
|
"Italy": 104,
|
||||||
|
"Greece": 85,
|
||||||
|
"Armenia": 82,
|
||||||
|
"Lithuania": 58,
|
||||||
|
"Sweden": 49,
|
||||||
|
"Cyprus": 44,
|
||||||
|
"Estonia": 33,
|
||||||
|
"Serbia": 32,
|
||||||
|
"Finland": 31,
|
||||||
|
"Latvia": 28,
|
||||||
|
"Luxembourg": 20,
|
||||||
|
"Georgia": 19,
|
||||||
|
"Germany": 18,
|
||||||
|
"Portugal": 13,
|
||||||
|
"Slovenia": 12,
|
||||||
|
"Spain": 11,
|
||||||
|
"Austria": 5,
|
||||||
|
"Norway": 4,
|
||||||
|
"United Kingdom": 0
|
||||||
|
},
|
||||||
|
"winner": "Switzerland"
|
||||||
|
}
|
||||||
|
}
|
||||||
61
tests/test_esc.py
Normal file
61
tests/test_esc.py
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import orjson
|
||||||
|
|
||||||
|
sys.path.append(os.path.join(os.path.dirname(__file__), "..")) # noqa: PTH120, PTH118
|
||||||
|
|
||||||
|
import tempfile
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import TypedDict
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from typer.testing import CliRunner
|
||||||
|
|
||||||
|
from src.__main__ import app
|
||||||
|
|
||||||
|
data_path = Path(__file__).parent / "esc_data.json"
|
||||||
|
|
||||||
|
|
||||||
|
class ESCData(TypedDict):
|
||||||
|
"""TypedDict for ESC data."""
|
||||||
|
|
||||||
|
jury: dict[str, int]
|
||||||
|
televote: dict[str, int]
|
||||||
|
winner: str
|
||||||
|
|
||||||
|
|
||||||
|
with data_path.open("r", encoding="utf-8") as f:
|
||||||
|
data = orjson.loads(f.read())
|
||||||
|
|
||||||
|
ESC_DATA: dict[int, "ESCData"] = {int(year): value for year, value in data.items()}
|
||||||
|
|
||||||
|
|
||||||
|
TADA = "🎉"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(("year", "data"), ESC_DATA.items())
|
||||||
|
def test_esc_grand_final(year: int, data: ESCData) -> None:
|
||||||
|
"""Test the ESC grand final for a given year."""
|
||||||
|
jury_scores: dict[str, int] = data["jury"]
|
||||||
|
televote_scores: dict[str, int] = data["televote"]
|
||||||
|
expected_winner: str = data["winner"]
|
||||||
|
|
||||||
|
with tempfile.NamedTemporaryFile("w", delete=False, encoding="utf-8") as f:
|
||||||
|
for country, score in jury_scores.items():
|
||||||
|
f.write(f"{country} {score}\n")
|
||||||
|
|
||||||
|
inputs: list[str] = []
|
||||||
|
for country in reversed(list(jury_scores.keys())):
|
||||||
|
inputs.append(str(televote_scores[country])) # noqa: PERF401
|
||||||
|
inputs.append("y") # to confirm the winner
|
||||||
|
|
||||||
|
runner = CliRunner()
|
||||||
|
result = runner.invoke(app, ["--jury-path", f.name], input="\n".join(inputs))
|
||||||
|
|
||||||
|
try:
|
||||||
|
actual = result.output.split(TADA)[1].strip().split()[0]
|
||||||
|
except Exception:
|
||||||
|
pytest.fail(f"Could not parse winner from output:\n{result.output}", pytrace=False)
|
||||||
|
|
||||||
|
assert actual == expected_winner, f"For {year}, expected winner {expected_winner} but got {actual!r}"
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
import os
|
|
||||||
import sys
|
|
||||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
|
|
||||||
|
|
||||||
import tempfile
|
|
||||||
from typer.testing import CliRunner
|
|
||||||
from src.__main__ import app
|
|
||||||
|
|
||||||
# 2025 Eurovision Grand Final jury scores
|
|
||||||
JURY_SCORES = {
|
|
||||||
"Austria": 258,
|
|
||||||
"Switzerland": 214,
|
|
||||||
"France": 180,
|
|
||||||
"Italy": 159,
|
|
||||||
"Netherlands": 133,
|
|
||||||
"Sweden": 126,
|
|
||||||
"Latvia": 116,
|
|
||||||
"Greece": 105,
|
|
||||||
"Estonia": 98,
|
|
||||||
"United Kingdom": 88,
|
|
||||||
"Finland": 88,
|
|
||||||
"Malta": 83,
|
|
||||||
"Germany": 77,
|
|
||||||
"Ukraine": 60,
|
|
||||||
"Israel": 60,
|
|
||||||
"Albania": 45,
|
|
||||||
"Denmark": 45,
|
|
||||||
"Armenia": 42,
|
|
||||||
"Portugal": 37,
|
|
||||||
"Lithuania": 34,
|
|
||||||
"Spain": 27,
|
|
||||||
"Luxembourg": 23,
|
|
||||||
"Norway": 22,
|
|
||||||
"Poland": 17,
|
|
||||||
"San Marino": 9,
|
|
||||||
"Iceland": 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# 2025 Eurovision Grand Final televoting scores
|
|
||||||
TELEVOTE_SCORES = {
|
|
||||||
"Israel": 297,
|
|
||||||
"Estonia": 258,
|
|
||||||
"Sweden": 195,
|
|
||||||
"Austria": 178,
|
|
||||||
"Albania": 173,
|
|
||||||
"Ukraine": 158,
|
|
||||||
"Poland": 139,
|
|
||||||
"Greece": 126,
|
|
||||||
"Finland": 108,
|
|
||||||
"Italy": 97,
|
|
||||||
"Germany": 74,
|
|
||||||
"Norway": 67,
|
|
||||||
"Lithuania": 62,
|
|
||||||
"France": 50,
|
|
||||||
"Latvia": 42,
|
|
||||||
"Netherlands": 42,
|
|
||||||
"Iceland": 33,
|
|
||||||
"Armenia": 30,
|
|
||||||
"Luxembourg": 24,
|
|
||||||
"San Marino": 18,
|
|
||||||
"Portugal": 13,
|
|
||||||
"Spain": 10,
|
|
||||||
"Malta": 8,
|
|
||||||
"Denmark": 2,
|
|
||||||
"United Kingdom": 0,
|
|
||||||
"Switzerland": 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
RETURN = """
|
|
||||||
╭──────────────────────────── Winner Announcement ─────────────────────────────╮
|
|
||||||
│ This year's winner is: 🎉 Austria 🎉 │
|
|
||||||
╰────────────────────────────── Congratulations! ──────────────────────────────╯
|
|
||||||
"""
|
|
||||||
|
|
||||||
def test_esc_2025():
|
|
||||||
with tempfile.NamedTemporaryFile(delete=False, mode='w', encoding='utf-8') as jury_file:
|
|
||||||
jury_file.write("\n".join(f"{country} {score}" for country, score in JURY_SCORES.items()))
|
|
||||||
runner = CliRunner()
|
|
||||||
i: str = ""
|
|
||||||
for country in reversed(list(JURY_SCORES.keys())):
|
|
||||||
i += f"{TELEVOTE_SCORES[country]}\n"
|
|
||||||
i += "y\n"
|
|
||||||
result = runner.invoke(app, ["--jury-path", jury_file.name], input=i)
|
|
||||||
assert result.stdout.strip().endswith(RETURN), f"Expected output to end with {RETURN.strip()}, got {result.output.strip()}"
|
|
||||||
40
uv.lock
generated
40
uv.lock
generated
@@ -48,6 +48,7 @@ dependencies = [
|
|||||||
[package.dev-dependencies]
|
[package.dev-dependencies]
|
||||||
dev = [
|
dev = [
|
||||||
{ name = "basedpyright" },
|
{ name = "basedpyright" },
|
||||||
|
{ name = "orjson" },
|
||||||
{ name = "ruff" },
|
{ name = "ruff" },
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -61,6 +62,7 @@ requires-dist = [
|
|||||||
[package.metadata.requires-dev]
|
[package.metadata.requires-dev]
|
||||||
dev = [
|
dev = [
|
||||||
{ name = "basedpyright", specifier = ">=1.29.2" },
|
{ name = "basedpyright", specifier = ">=1.29.2" },
|
||||||
|
{ name = "orjson", specifier = ">=3.10.18" },
|
||||||
{ name = "ruff", specifier = ">=0.11.13" },
|
{ name = "ruff", specifier = ">=0.11.13" },
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -110,6 +112,44 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/91/03/a852711aec73dfb965844592dfe226024c0da28e37d1ee54083342e38f57/nodejs_wheel_binaries-22.16.0-py2.py3-none-win_arm64.whl", hash = "sha256:2728972d336d436d39ee45988978d8b5d963509e06f063e80fe41b203ee80b28", size = 38828154 },
|
{ url = "https://files.pythonhosted.org/packages/91/03/a852711aec73dfb965844592dfe226024c0da28e37d1ee54083342e38f57/nodejs_wheel_binaries-22.16.0-py2.py3-none-win_arm64.whl", hash = "sha256:2728972d336d436d39ee45988978d8b5d963509e06f063e80fe41b203ee80b28", size = 38828154 },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "orjson"
|
||||||
|
version = "3.10.18"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/81/0b/fea456a3ffe74e70ba30e01ec183a9b26bec4d497f61dcfce1b601059c60/orjson-3.10.18.tar.gz", hash = "sha256:e8da3947d92123eda795b68228cafe2724815621fe35e8e320a9e9593a4bcd53", size = 5422810 }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/21/1a/67236da0916c1a192d5f4ccbe10ec495367a726996ceb7614eaa687112f2/orjson-3.10.18-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:50c15557afb7f6d63bc6d6348e0337a880a04eaa9cd7c9d569bcb4e760a24753", size = 249184 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b3/bc/c7f1db3b1d094dc0c6c83ed16b161a16c214aaa77f311118a93f647b32dc/orjson-3.10.18-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:356b076f1662c9813d5fa56db7d63ccceef4c271b1fb3dd522aca291375fcf17", size = 133279 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/af/84/664657cd14cc11f0d81e80e64766c7ba5c9b7fc1ec304117878cc1b4659c/orjson-3.10.18-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:559eb40a70a7494cd5beab2d73657262a74a2c59aff2068fdba8f0424ec5b39d", size = 136799 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/9a/bb/f50039c5bb05a7ab024ed43ba25d0319e8722a0ac3babb0807e543349978/orjson-3.10.18-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f3c29eb9a81e2fbc6fd7ddcfba3e101ba92eaff455b8d602bf7511088bbc0eae", size = 132791 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/93/8c/ee74709fc072c3ee219784173ddfe46f699598a1723d9d49cbc78d66df65/orjson-3.10.18-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6612787e5b0756a171c7d81ba245ef63a3533a637c335aa7fcb8e665f4a0966f", size = 137059 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6a/37/e6d3109ee004296c80426b5a62b47bcadd96a3deab7443e56507823588c5/orjson-3.10.18-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ac6bd7be0dcab5b702c9d43d25e70eb456dfd2e119d512447468f6405b4a69c", size = 138359 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/4f/5d/387dafae0e4691857c62bd02839a3bf3fa648eebd26185adfac58d09f207/orjson-3.10.18-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9f72f100cee8dde70100406d5c1abba515a7df926d4ed81e20a9730c062fe9ad", size = 142853 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/27/6f/875e8e282105350b9a5341c0222a13419758545ae32ad6e0fcf5f64d76aa/orjson-3.10.18-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9dca85398d6d093dd41dc0983cbf54ab8e6afd1c547b6b8a311643917fbf4e0c", size = 133131 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/48/b2/73a1f0b4790dcb1e5a45f058f4f5dcadc8a85d90137b50d6bbc6afd0ae50/orjson-3.10.18-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:22748de2a07fcc8781a70edb887abf801bb6142e6236123ff93d12d92db3d406", size = 134834 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/56/f5/7ed133a5525add9c14dbdf17d011dd82206ca6840811d32ac52a35935d19/orjson-3.10.18-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:3a83c9954a4107b9acd10291b7f12a6b29e35e8d43a414799906ea10e75438e6", size = 413368 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/11/7c/439654221ed9c3324bbac7bdf94cf06a971206b7b62327f11a52544e4982/orjson-3.10.18-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:303565c67a6c7b1f194c94632a4a39918e067bd6176a48bec697393865ce4f06", size = 153359 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/48/e7/d58074fa0cc9dd29a8fa2a6c8d5deebdfd82c6cfef72b0e4277c4017563a/orjson-3.10.18-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:86314fdb5053a2f5a5d881f03fca0219bfdf832912aa88d18676a5175c6916b5", size = 137466 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/57/4d/fe17581cf81fb70dfcef44e966aa4003360e4194d15a3f38cbffe873333a/orjson-3.10.18-cp312-cp312-win32.whl", hash = "sha256:187ec33bbec58c76dbd4066340067d9ece6e10067bb0cc074a21ae3300caa84e", size = 142683 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e6/22/469f62d25ab5f0f3aee256ea732e72dc3aab6d73bac777bd6277955bceef/orjson-3.10.18-cp312-cp312-win_amd64.whl", hash = "sha256:f9f94cf6d3f9cd720d641f8399e390e7411487e493962213390d1ae45c7814fc", size = 134754 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/10/b0/1040c447fac5b91bc1e9c004b69ee50abb0c1ffd0d24406e1350c58a7fcb/orjson-3.10.18-cp312-cp312-win_arm64.whl", hash = "sha256:3d600be83fe4514944500fa8c2a0a77099025ec6482e8087d7659e891f23058a", size = 131218 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/04/f0/8aedb6574b68096f3be8f74c0b56d36fd94bcf47e6c7ed47a7bd1474aaa8/orjson-3.10.18-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:69c34b9441b863175cc6a01f2935de994025e773f814412030f269da4f7be147", size = 249087 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/bc/f7/7118f965541aeac6844fcb18d6988e111ac0d349c9b80cda53583e758908/orjson-3.10.18-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:1ebeda919725f9dbdb269f59bc94f861afbe2a27dce5608cdba2d92772364d1c", size = 133273 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/fb/d9/839637cc06eaf528dd8127b36004247bf56e064501f68df9ee6fd56a88ee/orjson-3.10.18-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5adf5f4eed520a4959d29ea80192fa626ab9a20b2ea13f8f6dc58644f6927103", size = 136779 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2b/6d/f226ecfef31a1f0e7d6bf9a31a0bbaf384c7cbe3fce49cc9c2acc51f902a/orjson-3.10.18-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7592bb48a214e18cd670974f289520f12b7aed1fa0b2e2616b8ed9e069e08595", size = 132811 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/73/2d/371513d04143c85b681cf8f3bce743656eb5b640cb1f461dad750ac4b4d4/orjson-3.10.18-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f872bef9f042734110642b7a11937440797ace8c87527de25e0c53558b579ccc", size = 137018 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/69/cb/a4d37a30507b7a59bdc484e4a3253c8141bf756d4e13fcc1da760a0b00cb/orjson-3.10.18-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0315317601149c244cb3ecef246ef5861a64824ccbcb8018d32c66a60a84ffbc", size = 138368 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1e/ae/cd10883c48d912d216d541eb3db8b2433415fde67f620afe6f311f5cd2ca/orjson-3.10.18-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0da26957e77e9e55a6c2ce2e7182a36a6f6b180ab7189315cb0995ec362e049", size = 142840 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6d/4c/2bda09855c6b5f2c055034c9eda1529967b042ff8d81a05005115c4e6772/orjson-3.10.18-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb70d489bc79b7519e5803e2cc4c72343c9dc1154258adf2f8925d0b60da7c58", size = 133135 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/13/4a/35971fd809a8896731930a80dfff0b8ff48eeb5d8b57bb4d0d525160017f/orjson-3.10.18-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e9e86a6af31b92299b00736c89caf63816f70a4001e750bda179e15564d7a034", size = 134810 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/99/70/0fa9e6310cda98365629182486ff37a1c6578e34c33992df271a476ea1cd/orjson-3.10.18-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:c382a5c0b5931a5fc5405053d36c1ce3fd561694738626c77ae0b1dfc0242ca1", size = 413491 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/32/cb/990a0e88498babddb74fb97855ae4fbd22a82960e9b06eab5775cac435da/orjson-3.10.18-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8e4b2ae732431127171b875cb2668f883e1234711d3c147ffd69fe5be51a8012", size = 153277 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/92/44/473248c3305bf782a384ed50dd8bc2d3cde1543d107138fd99b707480ca1/orjson-3.10.18-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2d808e34ddb24fc29a4d4041dcfafbae13e129c93509b847b14432717d94b44f", size = 137367 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ad/fd/7f1d3edd4ffcd944a6a40e9f88af2197b619c931ac4d3cfba4798d4d3815/orjson-3.10.18-cp313-cp313-win32.whl", hash = "sha256:ad8eacbb5d904d5591f27dee4031e2c1db43d559edb8f91778efd642d70e6bea", size = 142687 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/4b/03/c75c6ad46be41c16f4cfe0352a2d1450546f3c09ad2c9d341110cd87b025/orjson-3.10.18-cp313-cp313-win_amd64.whl", hash = "sha256:aed411bcb68bf62e85588f2a7e03a6082cc42e5a2796e06e72a962d7c6310b52", size = 134794 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c2/28/f53038a5a72cc4fd0b56c1eafb4ef64aec9685460d5ac34de98ca78b6e29/orjson-3.10.18-cp313-cp313-win_arm64.whl", hash = "sha256:f54c1385a0e6aba2f15a40d703b858bedad36ded0491e55d35d905b2c34a4cc3", size = 131186 },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "packaging"
|
name = "packaging"
|
||||||
version = "25.0"
|
version = "25.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user