From 2ba97fe9e342d075bf3c3c3a588fe00cff29b2b4 Mon Sep 17 00:00:00 2001 From: alingse Date: Fri, 20 Feb 2026 21:55:13 +0800 Subject: [PATCH] build: migrate to uv, add ruff and mypy for linting and type checking - Replace pdm with uv for dependency management - Replace pdm-backend with hatchling build system - Add ruff for linting and code formatting - Add mypy for static type checking - Add Makefile for common development tasks - Fix all linting issues and format code - Add type ignore comments for json module internals --- .python-version | 1 + Makefile | 39 +++ half_json/_helpers.py | 8 +- half_json/cli.py | 26 +- half_json/core.py | 2 +- half_json/diagnosis.py | 64 ++-- half_json/rules/js_rules.py | 1 + half_json/rules/structural_rules.py | 2 + pdm.lock | 9 - pyproject.toml | 68 +++- tests/test_cases.py | 42 ++- tests/test_cli.py | 12 +- tests/test_diagnosis.py | 54 +-- tests/test_integration.py | 85 +++-- tests/test_js.py | 5 +- tests/test_miss.py | 9 +- tests/test_rules/test_array_rules.py | 20 +- tests/test_rules/test_object_rules.py | 14 +- tests/test_rules/test_string_rules.py | 2 +- tests/test_rules/test_structural_rules.py | 20 +- tests/test_stop.py | 29 +- uv.lock | 398 ++++++++++++++++++++++ 22 files changed, 728 insertions(+), 182 deletions(-) create mode 100644 .python-version create mode 100644 Makefile delete mode 100644 pdm.lock create mode 100644 uv.lock diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..bd28b9c --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.9 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..24b6b09 --- /dev/null +++ b/Makefile @@ -0,0 +1,39 @@ +.PHONY: install sync lint format test check type-check all + +# Install dependencies +install: + uv sync + +# Sync dependencies (remove old and install fresh) +sync: + uv sync + +# Run ruff linter +lint: + uv run ruff check half_json tests + +# Run ruff linter with auto-fix +lint-fix: + uv run ruff check half_json tests --fix + +# Run ruff formatter +format: + uv run ruff format half_json tests + +# Run ruff formatter check (CI mode) +format-check: + uv run ruff format half_json tests --check + +# Run type checker +type-check: + uv run mypy half_json + +# Run tests +test: + uv run pytest + +# Run all checks (CI mode) +check: lint format-check type-check test + +# Run all checks with auto-fix (local development) +all: lint-fix format type-check test diff --git a/half_json/_helpers.py b/half_json/_helpers.py index 0622ecd..8f55350 100644 --- a/half_json/_helpers.py +++ b/half_json/_helpers.py @@ -21,7 +21,7 @@ def build_bracket_stack(text: str, end: int | None = None) -> tuple[str, ...]: if escape: escape = False continue - if ch == '\\' and in_string: + if ch == "\\" and in_string: escape = True continue if ch == '"': @@ -29,10 +29,8 @@ def build_bracket_stack(text: str, end: int | None = None) -> tuple[str, ...]: continue if in_string: continue - if ch in ('{', '['): + if ch in ("{", "["): stack.append(ch) - elif ch == '}' and stack and stack[-1] == '{': - stack.pop() - elif ch == ']' and stack and stack[-1] == '[': + elif ch == "}" and stack and stack[-1] == "{" or ch == "]" and stack and stack[-1] == "[": stack.pop() return tuple(stack) diff --git a/half_json/cli.py b/half_json/cli.py index e8de9ef..d63e2f8 100644 --- a/half_json/cli.py +++ b/half_json/cli.py @@ -11,15 +11,26 @@ def main(argv: list[str] | None = None) -> None: prog="jsonfixer", description="Fix invalid / truncated JSON.", ) - parser.add_argument("infile", nargs="?", type=argparse.FileType("r"), - default=sys.stdin, help="input file (default: stdin)") - parser.add_argument("outfile", nargs="?", type=argparse.FileType("w"), - default=sys.stdout, help="output file (default: stdout)") + parser.add_argument( + "infile", + nargs="?", + type=argparse.FileType("r"), + default=sys.stdin, + help="input file (default: stdin)", + ) + parser.add_argument( + "outfile", + nargs="?", + type=argparse.FileType("w"), + default=sys.stdout, + help="output file (default: stdout)", + ) parser.add_argument("--strict", dest="strict", action="store_true", default=True) parser.add_argument("--no-strict", dest="strict", action="store_false") parser.add_argument("--js-style", action="store_true", default=False) - parser.add_argument("--single", action="store_true", default=False, - help="treat entire input as one JSON value") + parser.add_argument( + "--single", action="store_true", default=False, help="treat entire input as one JSON value" + ) args = parser.parse_args(argv) fixer = JSONFixer(js_style=args.js_style) @@ -45,8 +56,7 @@ def main(argv: list[str] | None = None) -> None: else: print(result, file=sys.stderr) if total: - print(f"total is {total} and hit {hit} --> ratio:{hit * 1.0 / total}", - file=sys.stderr) + print(f"total is {total} and hit {hit} --> ratio:{hit * 1.0 / total}", file=sys.stderr) # Backward-compatible entry point (same signature as old main.py:fixjson) diff --git a/half_json/core.py b/half_json/core.py index 28f606c..b1f81b5 100644 --- a/half_json/core.py +++ b/half_json/core.py @@ -3,7 +3,7 @@ from typing import NamedTuple from half_json.diagnosis import diagnose -from half_json.rules import FixCandidate, RuleRegistry +from half_json.rules import RuleRegistry from half_json.rules.array_rules import CloseOrCommaArray, FixArrayElement from half_json.rules.js_rules import FixJSStyleKey from half_json.rules.object_rules import ( diff --git a/half_json/diagnosis.py b/half_json/diagnosis.py index fd59aae..bb7d7bf 100644 --- a/half_json/diagnosis.py +++ b/half_json/diagnosis.py @@ -4,8 +4,8 @@ import json.decoder from dataclasses import dataclass from enum import Enum, auto -from json.decoder import JSONDecoder, py_scanstring -from json.scanner import py_make_scanner +from json.decoder import JSONDecoder, py_scanstring # type: ignore[attr-defined] +from json.scanner import py_make_scanner # type: ignore[attr-defined] from typing import Any @@ -31,7 +31,11 @@ class ErrorType(Enum): ("py_scanstring", "Invalid \\uXXXX escape", ErrorType.STRING_INVALID_UXXXX), ("py_scanstring", "Invalid \\escape", ErrorType.STRING_INVALID_ESCAPE), ("py_scanstring", "Invalid control character", ErrorType.STRING_INVALID_CONTROL), - ("JSONObject", "Expecting property name enclosed in double quotes", ErrorType.OBJECT_EXPECT_KEY), + ( + "JSONObject", + "Expecting property name enclosed in double quotes", + ErrorType.OBJECT_EXPECT_KEY, + ), ("JSONObject", "Expecting ':' delimiter", ErrorType.OBJECT_EXPECT_COLON), ("JSONObject", "Expecting value", ErrorType.OBJECT_EXPECT_VALUE), ("JSONObject", "Expecting ',' delimiter", ErrorType.OBJECT_EXPECT_COMMA), @@ -43,6 +47,7 @@ class ErrorType(Enum): @dataclass(frozen=True) class ParseContext: """All context a fix rule needs.""" + input: str error_type: ErrorType pos: int @@ -56,6 +61,7 @@ class ParseContext: def _record_parser_name(parser: Any) -> Any: """Decorator that attaches parser name to exceptions.""" + def wrapper(*args: Any, **kwargs: Any) -> Any: try: return parser(*args, **kwargs) @@ -63,6 +69,7 @@ def wrapper(*args: Any, **kwargs: Any) -> Any: if "parser" not in e.__dict__: e.__dict__["parser"] = parser.__name__ raise + wrapper.__name__ = parser.__name__ return wrapper @@ -74,16 +81,16 @@ def _make_decoder(*, strict: bool = True) -> JSONDecoder: JSONObject references it from module scope — no way to inject per-decoder. """ decoder = JSONDecoder(strict=strict) - decoder.parse_string = _record_parser_name(py_scanstring) - decoder.parse_object = _record_parser_name(decoder.parse_object) - decoder.parse_array = _record_parser_name(decoder.parse_array) - decoder.scan_once = py_make_scanner(decoder) + decoder.parse_string = _record_parser_name(py_scanstring) # type: ignore[attr-defined] + decoder.parse_object = _record_parser_name(decoder.parse_object) # type: ignore[attr-defined] + decoder.parse_array = _record_parser_name(decoder.parse_array) # type: ignore[attr-defined] + decoder.scan_once = py_make_scanner(decoder) # type: ignore[attr-defined] return decoder # Patch json.decoder.scanstring once so JSONObject uses our tracked version. # This is unavoidable: JSONObject hard-references the module-level scanstring. -json.decoder.scanstring = _record_parser_name(py_scanstring) +json.decoder.scanstring = _record_parser_name(py_scanstring) # type: ignore[attr-defined] _decoder_strict = _make_decoder(strict=True) _decoder_unstrict = _make_decoder(strict=False) @@ -102,40 +109,55 @@ def diagnose(text: str, *, strict: bool = True) -> ParseContext | None: if not text.strip(): return ParseContext( - input=text, error_type=ErrorType.EMPTY_INPUT, pos=0, - message="empty input", bracket_stack=(), nextchar="", lastchar="", + input=text, + error_type=ErrorType.EMPTY_INPUT, + pos=0, + message="empty input", + bracket_stack=(), + nextchar="", + lastchar="", ) decoder = _decoder_strict if strict else _decoder_unstrict try: - obj, end = decoder.scan_once(text, 0) + obj, end = decoder.scan_once(text, 0) # type: ignore[attr-defined] if end == len(text): return None # valid JSON # Partial parse — decoded something but there's leftover remaining = text[end:].strip() return ParseContext( - input=text, error_type=ErrorType.PARTIAL_PARSE, pos=end, + input=text, + error_type=ErrorType.PARTIAL_PARSE, + pos=end, message="partial parse", bracket_stack=build_bracket_stack(text, end), - nextchar=remaining[:1], lastchar=text[end - 1: end], - partial_result=obj, consumed_end=end, + nextchar=remaining[:1], + lastchar=text[end - 1 : end], + partial_result=obj, + consumed_end=end, ) except StopIteration: return ParseContext( - input=text, error_type=ErrorType.UNEXPECTED_TOKEN, pos=0, + input=text, + error_type=ErrorType.UNEXPECTED_TOKEN, + pos=0, message="unexpected token", bracket_stack=build_bracket_stack(text), - nextchar=text[:1], lastchar="", + nextchar=text[:1], + lastchar="", ) except ValueError as e: parser = e.__dict__.get("parser", "") - etype = _classify_error(parser, e.msg) + etype = _classify_error(parser, e.msg) # type: ignore[attr-defined] if etype is None: return None # unknown error, treat as unfixable - pos = e.pos + pos = e.pos # type: ignore[attr-defined] return ParseContext( - input=text, error_type=etype, pos=pos, - message=e.msg, + input=text, + error_type=etype, + pos=pos, + message=e.msg, # type: ignore[attr-defined] bracket_stack=build_bracket_stack(text, pos), - nextchar=text[pos: pos + 1], lastchar=text[pos - 1: pos], + nextchar=text[pos : pos + 1], + lastchar=text[pos - 1 : pos], ) diff --git a/half_json/rules/js_rules.py b/half_json/rules/js_rules.py index 00fbfd0..6ca4e76 100644 --- a/half_json/rules/js_rules.py +++ b/half_json/rules/js_rules.py @@ -7,6 +7,7 @@ class FixJSStyleKey: """Convert JS-style bare or single-quoted keys to double-quoted.""" + name = "fix_js_style_key" def applies_to(self, ctx: ParseContext) -> bool: diff --git a/half_json/rules/structural_rules.py b/half_json/rules/structural_rules.py index eb0b480..f84f2e1 100644 --- a/half_json/rules/structural_rules.py +++ b/half_json/rules/structural_rules.py @@ -36,6 +36,7 @@ def _guess_left(line: str) -> str: class PrependMissingBracket: """Handle StopIteration — the scanner couldn't start parsing at all.""" + name = "prepend_missing_bracket" def __init__(self) -> None: @@ -67,6 +68,7 @@ def reset(self) -> None: class WrapPartialParse: """Handle partial parse — decoded something but leftover remains.""" + name = "wrap_partial_parse" def __init__(self) -> None: diff --git a/pdm.lock b/pdm.lock deleted file mode 100644 index 5a98b58..0000000 --- a/pdm.lock +++ /dev/null @@ -1,9 +0,0 @@ -# This file is @generated by PDM. -# It is not intended for manual editing. - -[metadata] -groups = ["default"] -cross_platform = true -static_urls = false -lock_version = "4.3" -content_hash = "sha256:cb30ff0b06924f6f0d5f726b84c255686a2e277a4180b00b7b6e427c05ca202b" diff --git a/pyproject.toml b/pyproject.toml index b5ec68b..5a7efec 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,4 +1,3 @@ - [project] name = "jsonfixer" version = "0.3.0" @@ -23,6 +22,69 @@ Homepage = "https://github.com/half-pie/half-json" jsonfixer = "half_json.cli:fixjson" [build-system] -requires = ["pdm-backend"] -build-backend = "pdm.backend" +requires = ["hatchling"] +build-backend = "hatchling.build" + +[dependency-groups] +dev = [ + "pytest>=8.0", + "ruff>=0.9.0", + "mypy>=1.14.0", +] + +# Ruff configuration +[tool.ruff] +target-version = "py39" +line-length = 100 + +[tool.ruff.lint] +select = [ + "E", # pycodestyle errors + "W", # pycodestyle warnings + "F", # Pyflakes + "I", # isort + "N", # pep8-naming + "UP", # pyupgrade + "B", # flake8-bugbear + "C4", # flake8-comprehensions + "SIM", # flake8-simplify +] +ignore = [ + "E501", # Line too long (handled by formatter) +] + +[tool.ruff.lint.pydocstyle] +convention = "google" + +[tool.ruff.format] +quote-style = "double" +indent-style = "space" +skip-magic-trailing-comma = false +line-ending = "auto" + +# Mypy configuration +[tool.mypy] +python_version = "3.9" +warn_return_any = true +warn_unused_configs = true +disallow_untyped_defs = true +disallow_incomplete_defs = true +check_untyped_defs = true +warn_redundant_casts = true +warn_unused_ignores = true +show_error_codes = true + +[[tool.mypy.overrides]] +module = "tests.*" +disallow_untyped_defs = false + +# Hatch build configuration +[tool.hatch.build.targets.wheel] +packages = ["half_json"] +# Pytest configuration +[tool.pytest.ini_options] +testpaths = ["tests"] +python_files = ["test_*.py"] +python_classes = ["Test*"] +python_functions = ["test_*"] diff --git a/tests/test_cases.py b/tests/test_cases.py index 2fae797..616f31b 100644 --- a/tests/test_cases.py +++ b/tests/test_cases.py @@ -1,25 +1,21 @@ -# coding=utf8 - import sys - import unittest from half_json.core import JSONFixer class TestSimpleCase(unittest.TestCase): - def test_half_object(self): - line = '{' + line = "{" ok, newline, _ = JSONFixer().fix(line) self.assertTrue(ok) - self.assertEqual('{}', newline) + self.assertEqual("{}", newline) def test_half_array(self): - line = '[' + line = "[" ok, newline, _ = JSONFixer().fix(line) self.assertTrue(ok) - self.assertEqual('[]', newline) + self.assertEqual("[]", newline) def test_half_string(self): line = '"a' @@ -28,34 +24,34 @@ def test_half_string(self): self.assertEqual('"a"', newline) def test_object_miss_key(self): - line = '{:1}' + line = "{:1}" ok, newline, _ = JSONFixer().fix(line) self.assertTrue(ok) self.assertEqual('{"":1}', newline) def test_half_array_with_element(self): - line = '[1' + line = "[1" ok, newline, _ = JSONFixer().fix(line) self.assertTrue(ok) - self.assertEqual('[1]', newline) + self.assertEqual("[1]", newline) def test_array_miss_element(self): - line = '[,' + line = "[," ok, newline, _ = JSONFixer().fix(line) self.assertTrue(ok) - self.assertEqual('[]', newline) + self.assertEqual("[]", newline) def test_simple_mix(self): - line = '[{' + line = "[{" ok, newline, _ = JSONFixer().fix(line) self.assertTrue(ok) - self.assertEqual('[{}]', newline) + self.assertEqual("[{}]", newline) - def test_simple_mix_A(self): - line = '[{,' + def test_simple_mix_a(self): + line = "[{," ok, newline, _ = JSONFixer().fix(line) self.assertTrue(ok) - self.assertEqual('[{}]', newline) + self.assertEqual("[{}]", newline) def test_miss_quote(self): line = '{"a' @@ -83,7 +79,7 @@ def test_case_from_stackoverflow(self): self.assertEqual('{"title": "Center ","ADVANCE":", ","text": "Business.English."}', newline) def test_case_miss_key(self): - line = '{[' + line = "{[" ok, newline, _ = JSONFixer().fix(line) self.assertTrue(ok) self.assertEqual('{"":[]}', newline) @@ -95,17 +91,17 @@ def test_object_miss_value(self): self.assertEqual('{"V":null}', newline) def test_array_miss_value(self): - line = '[,]' + line = "[,]" ok, newline, _ = JSONFixer().fix(line) self.assertTrue(ok) - self.assertEqual('[]', newline) + self.assertEqual("[]", newline) @unittest.skipIf(sys.version_info >= (3, 13), "Python 3.13+ accepts trailing commas in JSON") def test_array_miss_value_2(self): - line = '[null,]' + line = "[null,]" ok, newline, _ = JSONFixer().fix(line) self.assertTrue(ok) - self.assertEqual('[null]', newline) + self.assertEqual("[null]", newline) def test_unstrict_ok(self): line = '{"hello": "wor\nld"}' diff --git a/tests/test_cli.py b/tests/test_cli.py index 65e0045..145788b 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -8,20 +8,24 @@ def test_cli_pipe(): result = subprocess.run( [sys.executable, "-m", "half_json.cli", "--single"], input='{"a":', - capture_output=True, text=True, + capture_output=True, + text=True, ) assert result.returncode == 0 assert result.stdout.strip() == '{"a":null}' -@pytest.mark.skipif(sys.version_info >= (3, 13), reason="Python 3.13+ accepts trailing commas in JSON") +@pytest.mark.skipif( + sys.version_info >= (3, 13), reason="Python 3.13+ accepts trailing commas in JSON" +) def test_cli_multiline(): result = subprocess.run( [sys.executable, "-m", "half_json.cli"], input='{"a":1,\n[1\n', - capture_output=True, text=True, + capture_output=True, + text=True, ) assert result.returncode == 0 lines = result.stdout.strip().split("\n") assert lines[0] == '{"a":1}' - assert lines[1] == '[1]' + assert lines[1] == "[1]" diff --git a/tests/test_diagnosis.py b/tests/test_diagnosis.py index d83a343..1dd17e6 100644 --- a/tests/test_diagnosis.py +++ b/tests/test_diagnosis.py @@ -3,41 +3,47 @@ from half_json.diagnosis import ErrorType, diagnose -@pytest.mark.parametrize("text, expected_type", [ - ('{"a"', ErrorType.OBJECT_EXPECT_COLON), - ('"hello', ErrorType.STRING_UNTERMINATED), - ('{:1}', ErrorType.OBJECT_EXPECT_KEY), - ('{,}', ErrorType.OBJECT_EXPECT_KEY), - ('{"a"1}', ErrorType.OBJECT_EXPECT_COLON), - ('{"a":}', ErrorType.OBJECT_EXPECT_VALUE), - ('{"a":1"b":2}', ErrorType.OBJECT_EXPECT_COMMA), - ('[,]', ErrorType.ARRAY_EXPECT_VALUE), - ('[1 2]', ErrorType.ARRAY_EXPECT_COMMA), - ('}', ErrorType.UNEXPECTED_TOKEN), - (']', ErrorType.UNEXPECTED_TOKEN), - ('', ErrorType.EMPTY_INPUT), - (' ', ErrorType.EMPTY_INPUT), -]) +@pytest.mark.parametrize( + "text, expected_type", + [ + ('{"a"', ErrorType.OBJECT_EXPECT_COLON), + ('"hello', ErrorType.STRING_UNTERMINATED), + ("{:1}", ErrorType.OBJECT_EXPECT_KEY), + ("{,}", ErrorType.OBJECT_EXPECT_KEY), + ('{"a"1}', ErrorType.OBJECT_EXPECT_COLON), + ('{"a":}', ErrorType.OBJECT_EXPECT_VALUE), + ('{"a":1"b":2}', ErrorType.OBJECT_EXPECT_COMMA), + ("[,]", ErrorType.ARRAY_EXPECT_VALUE), + ("[1 2]", ErrorType.ARRAY_EXPECT_COMMA), + ("}", ErrorType.UNEXPECTED_TOKEN), + ("]", ErrorType.UNEXPECTED_TOKEN), + ("", ErrorType.EMPTY_INPUT), + (" ", ErrorType.EMPTY_INPUT), + ], +) def test_error_classification(text, expected_type): ctx = diagnose(text) assert ctx is not None assert ctx.error_type == expected_type -@pytest.mark.parametrize("text", [ - '{"a": 1}', - '[1, 2, 3]', - '"hello"', - 'null', - 'true', - '42', -]) +@pytest.mark.parametrize( + "text", + [ + '{"a": 1}', + "[1, 2, 3]", + '"hello"', + "null", + "true", + "42", + ], +) def test_valid_json_returns_none(text): assert diagnose(text) is None def test_partial_parse(): - ctx = diagnose('{}]') + ctx = diagnose("{}]") assert ctx is not None assert ctx.error_type == ErrorType.PARTIAL_PARSE assert ctx.consumed_end == 2 diff --git a/tests/test_integration.py b/tests/test_integration.py index 1f7d3d5..b8af25f 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -1,4 +1,5 @@ """End-to-end tests migrated from the original unittest suite.""" + import random import sys @@ -6,26 +7,41 @@ from half_json.core import JSONFixer - # --- test_cases.py equivalents --- -@pytest.mark.parametrize("input_line, expected", [ - ('{', '{}'), - ('[', '[]'), - ('"a', '"a"'), - ('{:1}', '{"":1}'), - ('[1', '[1]'), - ('[,', '[]'), - ('[{', '[{}]'), - ('[{,', '[{}]'), - ('{"a', '{"a":null}'), - ('{"a":1,"b"', '{"a":1,"b":null}'), - pytest.param('{"a":1,', '{"a":1}', marks=pytest.mark.skipif(sys.version_info >= (3, 13), reason="Python 3.13+ accepts trailing commas in JSON")), - ('{[', '{\"\":[]}'), - ('{"V":}', '{"V":null}'), - ('[,]', '[]'), - pytest.param('[null,]', '[null]', marks=pytest.mark.skipif(sys.version_info >= (3, 13), reason="Python 3.13+ accepts trailing commas in JSON")), -]) + +@pytest.mark.parametrize( + "input_line, expected", + [ + ("{", "{}"), + ("[", "[]"), + ('"a', '"a"'), + ("{:1}", '{"":1}'), + ("[1", "[1]"), + ("[,", "[]"), + ("[{", "[{}]"), + ("[{,", "[{}]"), + ('{"a', '{"a":null}'), + ('{"a":1,"b"', '{"a":1,"b":null}'), + pytest.param( + '{"a":1,', + '{"a":1}', + marks=pytest.mark.skipif( + sys.version_info >= (3, 13), reason="Python 3.13+ accepts trailing commas in JSON" + ), + ), + ("{[", '{"":[]}'), + ('{"V":}', '{"V":null}'), + ("[,]", "[]"), + pytest.param( + "[null,]", + "[null]", + marks=pytest.mark.skipif( + sys.version_info >= (3, 13), reason="Python 3.13+ accepts trailing commas in JSON" + ), + ), + ], +) def test_basic_fixes(input_line, expected): ok, line, _ = JSONFixer().fix(input_line) assert ok @@ -59,19 +75,23 @@ def test_unstrict_fix(): # --- test_stop.py equivalents --- -@pytest.mark.parametrize("input_line, expected", [ - ('}', '{}'), - (']', '[]'), - ('[]]', '[[]]'), - ('{}}', '{\"\":{}}'), - ('{}]', '[{}]'), - ('[]}', '{\"\":[]}'), - ('1, [\"\"], -1]', '[1, [\"\"], -1]'), - ('1, 2', '[1, 2]'), - ('"a":', '{"a":null}'), - ('{}[]{}}]', '[{\"\":{},\"\":[],\"\":{}}]'), - ('E"', '"E"'), -]) + +@pytest.mark.parametrize( + "input_line, expected", + [ + ("}", "{}"), + ("]", "[]"), + ("[]]", "[[]]"), + ("{}}", '{"":{}}'), + ("{}]", "[{}]"), + ("[]}", '{"":[]}'), + ('1, [""], -1]', '[1, [""], -1]'), + ("1, 2", "[1, 2]"), + ('"a":', '{"a":null}'), + ("{}[]{}}]", '[{"":{},"":[],"":{}}]'), + ('E"', '"E"'), + ], +) def test_structural_fixes(input_line, expected): ok, line, _ = JSONFixer().fix(input_line) assert ok @@ -80,8 +100,9 @@ def test_structural_fixes(input_line, expected): # --- test_js.py equivalents --- + def test_js_bare_key(): - ok, line, _ = JSONFixer(js_style=True).fix('{a:1, b:{c:3}}') + ok, line, _ = JSONFixer(js_style=True).fix("{a:1, b:{c:3}}") assert ok assert line == '{"a":1, "b":{"c":3}}' diff --git a/tests/test_js.py b/tests/test_js.py index 9fa74eb..e1b4810 100644 --- a/tests/test_js.py +++ b/tests/test_js.py @@ -1,14 +1,11 @@ -# coding=utf8 - import unittest from half_json.core import JSONFixer class TestJSCase(unittest.TestCase): - def test_bare_key(self): - line = '{a:1, b:{c:3}}' + line = "{a:1, b:{c:3}}" ok, newline, _ = JSONFixer(js_style=True).fix(line) self.assertTrue(ok) self.assertEqual('{"a":1, "b":{"c":3}}', newline) diff --git a/tests/test_miss.py b/tests/test_miss.py index 3780574..63869df 100644 --- a/tests/test_miss.py +++ b/tests/test_miss.py @@ -1,5 +1,3 @@ -# coding=utf8 - import random import unittest @@ -7,20 +5,19 @@ class TestMissTailAndHeadCase(unittest.TestCase): - # by json-generator - line = '[{"_id":"5cf12ecfb7af6c84da64571b","index":0,"guid":"c2aedc2a-7303-42e2-b5a8-d58afca2149f","isActive":false,"balance":"$1,322.22","picture":"http://placehold.it/32x32","age":24,"eyeColor":"blue","name":{"first":"Gardner","last":"Ford"},"company":"IMAGINART","email":"gardner.ford@imaginart.net","phone":"+1 (874) 563-3237","address":"779 Cortelyou Road, Manchester, North Carolina, 939","about":"Ut dolore commodo qui nisi aliquip. Ad occaecat duis ipsum laborum magna cillum non mollit est eu. Non consectetur consectetur amet sunt reprehenderit tempor ex ea pariatur deserunt magna mollit sint in. Qui cupidatat ad eiusmod laborum ad consectetur elit ut. Quis dolore irure irure mollit aliquip laborum consectetur. Culpa do et id in in eu minim exercitation labore. Anim ex laboris nulla occaecat.","registered":"Saturday, May 18, 2019 3:55 PM","latitude":"-55.587817","longitude":"89.374875","tags":["irure","culpa","sint","sint","aliqua"],"range":[0,1,2,3,4,5,6,7,8,9],"friends":[{"id":0,"name":"Malinda Estes"},{"id":1,"name":"Mueller Ryan"},{"id":2,"name":"Vilma Woods"}],"greeting":"Hello, Gardner! You have 6 unread messages.","favoriteFruit":"banana"},{"_id":"5cf12ecf5f6594406acaf90f","index":1,"guid":"eb7581e2-9471-4435-a689-7615f1324489","isActive":false,"balance":"$3,243.81","picture":"http://placehold.it/32x32","age":28,"eyeColor":"green","name":{"first":"Neva","last":"Frederick"},"company":"ACCRUEX","email":"neva.frederick@accruex.org","phone":"+1 (935) 521-3229","address":"935 Colby Court, Vallonia, Wisconsin, 9524","about":"Eiusmod dolor fugiat proident ex officia Lorem cupidatat cupidatat ut sunt minim. Sit incididunt reprehenderit cupidatat aliqua minim ad. Pariatur deserunt ad ad culpa veniam irure sint dolor quis pariatur eu laboris officia. Ad consequat voluptate cupidatat anim nulla elit veniam ex ipsum mollit. Pariatur est est excepteur laboris incididunt aliquip excepteur elit velit. Sint eu aliqua nulla dolore incididunt dolore nisi quis adipisicing est enim tempor.","registered":"Saturday, April 13, 2019 11:36 AM","latitude":"-29.541751","longitude":"-93.408621","tags":["laborum","eu","non","do","ut"],"range":[0,1,2,3,4,5,6,7,8,9],"friends":[{"id":0,"name":"Ofelia Harmon"},{"id":1,"name":"Phillips Flowers"},{"id":2,"name":"Conner Walker"}],"greeting":"Hello, Neva! You have 8 unread messages.","favoriteFruit":"strawberry"},{"_id":"5cf12ecf20d50e3aa3590acb","index":2,"guid":"f150d306-8ca8-4791-9e99-912a75bb12a1","isActive":false,"balance":"$1,897.79","picture":"http://placehold.it/32x32","age":35,"eyeColor":"green","name":{"first":"Angelia","last":"Daniels"},"company":"DIGIGEN","email":"angelia.daniels@digigen.info","phone":"+1 (924) 451-2569","address":"628 Clymer Street, Whitmer, Delaware, 2210","about":"Enim adipisicing irure nisi nisi cillum voluptate ea commodo deserunt. Labore commodo ea culpa do esse cupidatat commodo consequat. Aliquip aliqua ut enim duis commodo dolore sint incididunt nulla excepteur. Occaecat labore consectetur occaecat ipsum id dolore dolor. Incididunt sint veniam ea dolore officia mollit dolore ullamco excepteur. Esse et esse nostrud aliquip exercitation Lorem. Laboris pariatur duis consectetur tempor reprehenderit ullamco pariatur sit deserunt sint non mollit eu.","registered":"Monday, August 6, 2018 2:54 AM","latitude":"-44.812364","longitude":"-40.811892","tags":["amet","consectetur","consequat","ex","elit"],"range":[0,1,2,3,4,5,6,7,8,9],"friends":[{"id":0,"name":"Kline Delaney"},{"id":1,"name":"Agnes Patterson"},{"id":2,"name":"Donna Weeks"}],"greeting":"Hello, Angelia! You have 6 unread messages.","favoriteFruit":"apple"},{"_id":"5cf12ecf4eaebd02791a0a5d","index":3,"guid":"14fd69a4-6e19-4f46-a95f-ab99f144d383","isActive":false,"balance":"$1,647.55","picture":"http://placehold.it/32x32","age":30,"eyeColor":"green","name":{"first":"Aurelia","last":"Bentley"},"company":"ZYTREK","email":"aurelia.bentley@zytrek.tv","phone":"+1 (859) 530-2393","address":"582 Narrows Avenue, Riceville, Texas, 3979","about":"Sunt do aliquip voluptate sint pariatur adipisicing et. Irure voluptate ad voluptate anim aute ipsum laboris et. Culpa nostrud consequat in ex Lorem ex. Nostrud quis qui cupidatat occaecat incididunt aliqua elit aliqua anim labore voluptate sint consectetur ullamco. Eiusmod fugiat laborum sint velit eu do ex labore sunt labore exercitation voluptate ut aliquip.","registered":"Wednesday, October 24, 2018 10:08 AM","latitude":"-66.524116","longitude":"42.643245","tags":["aliquip","et","tempor","sit","in"],"range":[0,1,2,3,4,5,6,7,8,9],"friends":[{"id":0,"name":"Tameka Whitfield"},{"id":1,"name":"Tessa Shepard"},{"id":2,"name":"Meyers Barr"}],"greeting":"Hello, Aurelia! You have 8 unread messages.","favoriteFruit":"strawberry"},{"_id":"5cf12ecf9508728e7da0a68f","index":4,"guid":"ee0a1b2e-da52-4b3b-a3d5-353495e6098e","isActive":false,"balance":"$3,847.46","picture":"http://placehold.it/32x32","age":40,"eyeColor":"brown","name":{"first":"Sutton","last":"Hess"},"company":"LUXURIA","email":"sutton.hess@luxuria.co.uk","phone":"+1 (928) 456-2632","address":"125 Vista Place, Charco, Indiana, 3164","about":"Deserunt ut ad proident aliqua ipsum laborum officia deserunt ea aliquip. In commodo est et esse sit mollit adipisicing veniam. Nulla eiusmod voluptate minim laborum laboris in dolore in est fugiat dolor exercitation officia. Non mollit tempor id eiusmod ex anim adipisicing qui ea ullamco et cupidatat. Aliquip irure nulla amet Lorem id eiusmod eu velit sit.","registered":"Thursday, March 19, 2015 6:39 PM","latitude":"-35.426884","longitude":"-133.613414","tags":["fugiat","dolore","nostrud","consectetur","anim"],"range":[0,1,2,3,4,5,6,7,8,9],"friends":[{"id":0,"name":"Dorothea Dawson"},{"id":1,"name":"Compton Wilder"},{"id":2,"name":"Kelly Caldwell"}],"greeting":"Hello, Sutton! You have 10 unread messages.","favoriteFruit":"strawberry"},{"_id":"5cf12ecf7bde15b99bdf63b8","index":5,"guid":"fe862564-7ad7-4c8f-9a0d-005657883490","isActive":true,"balance":"$2,323.98","picture":"http://placehold.it/32x32","age":32,"eyeColor":"brown","name":{"first":"Goodman","last":"Spence"},"company":"UNIWORLD","email":"goodman.spence@uniworld.io","phone":"+1 (919) 463-3731","address":"878 High Street, Zortman, Virgin Islands, 9952","about":"Qui non velit nisi est cillum amet fugiat culpa ut anim aliqua nisi. Culpa aliquip eiusmod dolore proident deserunt minim sint officia do pariatur fugiat. Occaecat adipisicing eu esse non in consequat culpa amet fugiat aute sunt aliqua adipisicing proident. Voluptate ea duis pariatur exercitation. Irure officia consequat excepteur eu laborum occaecat amet ipsum laborum in nostrud eiusmod occaecat tempor. Ex amet culpa ipsum eiusmod adipisicing non anim ex veniam aute in ullamco ut.","registered":"Wednesday, January 10, 2018 3:37 PM","latitude":"68.089656","longitude":"29.601085","tags":["velit","ut","qui","Lorem","minim"],"range":[0,1,2,3,4,5,6,7,8,9],"friends":[{"id":0,"name":"Hutchinson Finch"},{"id":1,"name":"Banks Winters"},{"id":2,"name":"Santana Cortez"}],"greeting":"Hello, Goodman! You have 7 unread messages.","favoriteFruit":"banana"},{"_id":"5cf12ecfc62579578b1bc759","index":6,"guid":"76ec16d9-a30f-471a-8c8b-31ea2fc37912","isActive":true,"balance":"$3,657.56","picture":"http://placehold.it/32x32","age":29,"eyeColor":"green","name":{"first":"Corinne","last":"Nguyen"},"company":"REALMO","email":"corinne.nguyen@realmo.name","phone":"+1 (800) 451-3183","address":"412 Beard Street, Kapowsin, New York, 836","about":"Qui fugiat sunt culpa consequat sint cillum veniam ullamco et aute ipsum. Sit ad tempor duis ex pariatur sint aliquip. Proident magna aliquip commodo sit quis. Officia occaecat officia voluptate sit exercitation occaecat qui anim. Excepteur mollit aute proident eu. Eu esse consectetur sunt laboris.","registered":"Friday, February 3, 2017 12:02 PM","latitude":"-38.812149","longitude":"47.887586","tags":["in","aliqua","aliqua","occaecat","id"],"range":[0,1,2,3,4,5,6,7,8,9],"friends":[{"id":0,"name":"Celina Copeland"},{"id":1,"name":"Watson Santos"},{"id":2,"name":"Melba Olsen"}],"greeting":"Hello, Corinne! You have 8 unread messages.","favoriteFruit":"strawberry"},{"_id":"5cf12ecf223310f0f99c3d6e","index":7,"guid":"798e53fb-6590-4fc9-ba1b-cbb597e555de","isActive":false,"balance":"$2,244.77","picture":"http://placehold.it/32x32","age":30,"eyeColor":"green","name":{"first":"Rene","last":"Gillespie"},"company":"SCHOOLIO","email":"rene.gillespie@schoolio.us","phone":"+1 (828) 413-2911","address":"465 Ridgecrest Terrace, Joppa, Connecticut, 8512","about":"Do aliquip consequat nulla esse anim. Nulla cillum tempor labore excepteur voluptate reprehenderit amet. Nisi officia ea nisi fugiat mollit non eiusmod. Proident nulla ea sunt non quis dolor laboris magna cillum laborum eu. Sit nostrud eu enim consequat irure laborum duis et irure Lorem. Ipsum fugiat aliquip aute consequat est culpa sint cillum Lorem fugiat pariatur deserunt. Occaecat esse ea esse officia laborum.","registered":"Sunday, May 19, 2019 12:15 PM","latitude":"-65.435682","longitude":"137.573407","tags":["velit","voluptate","aliqua","consectetur","qui"],"range":[0,1,2,3,4,5,6,7,8,9],"friends":[{"id":0,"name":"Krystal Freeman"},{"id":1,"name":"Jenny Ruiz"},{"id":2,"name":"Heather Wood"}],"greeting":"Hello, Rene! You have 7 unread messages.","favoriteFruit":"banana"}]' # noqa + line = '[{"_id":"5cf12ecfb7af6c84da64571b","index":0,"guid":"c2aedc2a-7303-42e2-b5a8-d58afca2149f","isActive":false,"balance":"$1,322.22","picture":"http://placehold.it/32x32","age":24,"eyeColor":"blue","name":{"first":"Gardner","last":"Ford"},"company":"IMAGINART","email":"gardner.ford@imaginart.net","phone":"+1 (874) 563-3237","address":"779 Cortelyou Road, Manchester, North Carolina, 939","about":"Ut dolore commodo qui nisi aliquip. Ad occaecat duis ipsum laborum magna cillum non mollit est eu. Non consectetur consectetur amet sunt reprehenderit tempor ex ea pariatur deserunt magna mollit sint in. Qui cupidatat ad eiusmod laborum ad consectetur elit ut. Quis dolore irure irure mollit aliquip laborum consectetur. Culpa do et id in in eu minim exercitation labore. Anim ex laboris nulla occaecat.","registered":"Saturday, May 18, 2019 3:55 PM","latitude":"-55.587817","longitude":"89.374875","tags":["irure","culpa","sint","sint","aliqua"],"range":[0,1,2,3,4,5,6,7,8,9],"friends":[{"id":0,"name":"Malinda Estes"},{"id":1,"name":"Mueller Ryan"},{"id":2,"name":"Vilma Woods"}],"greeting":"Hello, Gardner! You have 6 unread messages.","favoriteFruit":"banana"},{"_id":"5cf12ecf5f6594406acaf90f","index":1,"guid":"eb7581e2-9471-4435-a689-7615f1324489","isActive":false,"balance":"$3,243.81","picture":"http://placehold.it/32x32","age":28,"eyeColor":"green","name":{"first":"Neva","last":"Frederick"},"company":"ACCRUEX","email":"neva.frederick@accruex.org","phone":"+1 (935) 521-3229","address":"935 Colby Court, Vallonia, Wisconsin, 9524","about":"Eiusmod dolor fugiat proident ex officia Lorem cupidatat cupidatat ut sunt minim. Sit incididunt reprehenderit cupidatat aliqua minim ad. Pariatur deserunt ad ad culpa veniam irure sint dolor quis pariatur eu laboris officia. Ad consequat voluptate cupidatat anim nulla elit veniam ex ipsum mollit. Pariatur est est excepteur laboris incididunt aliquip excepteur elit velit. Sint eu aliqua nulla dolore incididunt dolore nisi quis adipisicing est enim tempor.","registered":"Saturday, April 13, 2019 11:36 AM","latitude":"-29.541751","longitude":"-93.408621","tags":["laborum","eu","non","do","ut"],"range":[0,1,2,3,4,5,6,7,8,9],"friends":[{"id":0,"name":"Ofelia Harmon"},{"id":1,"name":"Phillips Flowers"},{"id":2,"name":"Conner Walker"}],"greeting":"Hello, Neva! You have 8 unread messages.","favoriteFruit":"strawberry"},{"_id":"5cf12ecf20d50e3aa3590acb","index":2,"guid":"f150d306-8ca8-4791-9e99-912a75bb12a1","isActive":false,"balance":"$1,897.79","picture":"http://placehold.it/32x32","age":35,"eyeColor":"green","name":{"first":"Angelia","last":"Daniels"},"company":"DIGIGEN","email":"angelia.daniels@digigen.info","phone":"+1 (924) 451-2569","address":"628 Clymer Street, Whitmer, Delaware, 2210","about":"Enim adipisicing irure nisi nisi cillum voluptate ea commodo deserunt. Labore commodo ea culpa do esse cupidatat commodo consequat. Aliquip aliqua ut enim duis commodo dolore sint incididunt nulla excepteur. Occaecat labore consectetur occaecat ipsum id dolore dolor. Incididunt sint veniam ea dolore officia mollit dolore ullamco excepteur. Esse et esse nostrud aliquip exercitation Lorem. Laboris pariatur duis consectetur tempor reprehenderit ullamco pariatur sit deserunt sint non mollit eu.","registered":"Monday, August 6, 2018 2:54 AM","latitude":"-44.812364","longitude":"-40.811892","tags":["amet","consectetur","consequat","ex","elit"],"range":[0,1,2,3,4,5,6,7,8,9],"friends":[{"id":0,"name":"Kline Delaney"},{"id":1,"name":"Agnes Patterson"},{"id":2,"name":"Donna Weeks"}],"greeting":"Hello, Angelia! You have 6 unread messages.","favoriteFruit":"apple"},{"_id":"5cf12ecf4eaebd02791a0a5d","index":3,"guid":"14fd69a4-6e19-4f46-a95f-ab99f144d383","isActive":false,"balance":"$1,647.55","picture":"http://placehold.it/32x32","age":30,"eyeColor":"green","name":{"first":"Aurelia","last":"Bentley"},"company":"ZYTREK","email":"aurelia.bentley@zytrek.tv","phone":"+1 (859) 530-2393","address":"582 Narrows Avenue, Riceville, Texas, 3979","about":"Sunt do aliquip voluptate sint pariatur adipisicing et. Irure voluptate ad voluptate anim aute ipsum laboris et. Culpa nostrud consequat in ex Lorem ex. Nostrud quis qui cupidatat occaecat incididunt aliqua elit aliqua anim labore voluptate sint consectetur ullamco. Eiusmod fugiat laborum sint velit eu do ex labore sunt labore exercitation voluptate ut aliquip.","registered":"Wednesday, October 24, 2018 10:08 AM","latitude":"-66.524116","longitude":"42.643245","tags":["aliquip","et","tempor","sit","in"],"range":[0,1,2,3,4,5,6,7,8,9],"friends":[{"id":0,"name":"Tameka Whitfield"},{"id":1,"name":"Tessa Shepard"},{"id":2,"name":"Meyers Barr"}],"greeting":"Hello, Aurelia! You have 8 unread messages.","favoriteFruit":"strawberry"},{"_id":"5cf12ecf9508728e7da0a68f","index":4,"guid":"ee0a1b2e-da52-4b3b-a3d5-353495e6098e","isActive":false,"balance":"$3,847.46","picture":"http://placehold.it/32x32","age":40,"eyeColor":"brown","name":{"first":"Sutton","last":"Hess"},"company":"LUXURIA","email":"sutton.hess@luxuria.co.uk","phone":"+1 (928) 456-2632","address":"125 Vista Place, Charco, Indiana, 3164","about":"Deserunt ut ad proident aliqua ipsum laborum officia deserunt ea aliquip. In commodo est et esse sit mollit adipisicing veniam. Nulla eiusmod voluptate minim laborum laboris in dolore in est fugiat dolor exercitation officia. Non mollit tempor id eiusmod ex anim adipisicing qui ea ullamco et cupidatat. Aliquip irure nulla amet Lorem id eiusmod eu velit sit.","registered":"Thursday, March 19, 2015 6:39 PM","latitude":"-35.426884","longitude":"-133.613414","tags":["fugiat","dolore","nostrud","consectetur","anim"],"range":[0,1,2,3,4,5,6,7,8,9],"friends":[{"id":0,"name":"Dorothea Dawson"},{"id":1,"name":"Compton Wilder"},{"id":2,"name":"Kelly Caldwell"}],"greeting":"Hello, Sutton! You have 10 unread messages.","favoriteFruit":"strawberry"},{"_id":"5cf12ecf7bde15b99bdf63b8","index":5,"guid":"fe862564-7ad7-4c8f-9a0d-005657883490","isActive":true,"balance":"$2,323.98","picture":"http://placehold.it/32x32","age":32,"eyeColor":"brown","name":{"first":"Goodman","last":"Spence"},"company":"UNIWORLD","email":"goodman.spence@uniworld.io","phone":"+1 (919) 463-3731","address":"878 High Street, Zortman, Virgin Islands, 9952","about":"Qui non velit nisi est cillum amet fugiat culpa ut anim aliqua nisi. Culpa aliquip eiusmod dolore proident deserunt minim sint officia do pariatur fugiat. Occaecat adipisicing eu esse non in consequat culpa amet fugiat aute sunt aliqua adipisicing proident. Voluptate ea duis pariatur exercitation. Irure officia consequat excepteur eu laborum occaecat amet ipsum laborum in nostrud eiusmod occaecat tempor. Ex amet culpa ipsum eiusmod adipisicing non anim ex veniam aute in ullamco ut.","registered":"Wednesday, January 10, 2018 3:37 PM","latitude":"68.089656","longitude":"29.601085","tags":["velit","ut","qui","Lorem","minim"],"range":[0,1,2,3,4,5,6,7,8,9],"friends":[{"id":0,"name":"Hutchinson Finch"},{"id":1,"name":"Banks Winters"},{"id":2,"name":"Santana Cortez"}],"greeting":"Hello, Goodman! You have 7 unread messages.","favoriteFruit":"banana"},{"_id":"5cf12ecfc62579578b1bc759","index":6,"guid":"76ec16d9-a30f-471a-8c8b-31ea2fc37912","isActive":true,"balance":"$3,657.56","picture":"http://placehold.it/32x32","age":29,"eyeColor":"green","name":{"first":"Corinne","last":"Nguyen"},"company":"REALMO","email":"corinne.nguyen@realmo.name","phone":"+1 (800) 451-3183","address":"412 Beard Street, Kapowsin, New York, 836","about":"Qui fugiat sunt culpa consequat sint cillum veniam ullamco et aute ipsum. Sit ad tempor duis ex pariatur sint aliquip. Proident magna aliquip commodo sit quis. Officia occaecat officia voluptate sit exercitation occaecat qui anim. Excepteur mollit aute proident eu. Eu esse consectetur sunt laboris.","registered":"Friday, February 3, 2017 12:02 PM","latitude":"-38.812149","longitude":"47.887586","tags":["in","aliqua","aliqua","occaecat","id"],"range":[0,1,2,3,4,5,6,7,8,9],"friends":[{"id":0,"name":"Celina Copeland"},{"id":1,"name":"Watson Santos"},{"id":2,"name":"Melba Olsen"}],"greeting":"Hello, Corinne! You have 8 unread messages.","favoriteFruit":"strawberry"},{"_id":"5cf12ecf223310f0f99c3d6e","index":7,"guid":"798e53fb-6590-4fc9-ba1b-cbb597e555de","isActive":false,"balance":"$2,244.77","picture":"http://placehold.it/32x32","age":30,"eyeColor":"green","name":{"first":"Rene","last":"Gillespie"},"company":"SCHOOLIO","email":"rene.gillespie@schoolio.us","phone":"+1 (828) 413-2911","address":"465 Ridgecrest Terrace, Joppa, Connecticut, 8512","about":"Do aliquip consequat nulla esse anim. Nulla cillum tempor labore excepteur voluptate reprehenderit amet. Nisi officia ea nisi fugiat mollit non eiusmod. Proident nulla ea sunt non quis dolor laboris magna cillum laborum eu. Sit nostrud eu enim consequat irure laborum duis et irure Lorem. Ipsum fugiat aliquip aute consequat est culpa sint cillum Lorem fugiat pariatur deserunt. Occaecat esse ea esse officia laborum.","registered":"Sunday, May 19, 2019 12:15 PM","latitude":"-65.435682","longitude":"137.573407","tags":["velit","voluptate","aliqua","consectetur","qui"],"range":[0,1,2,3,4,5,6,7,8,9],"friends":[{"id":0,"name":"Krystal Freeman"},{"id":1,"name":"Jenny Ruiz"},{"id":2,"name":"Heather Wood"}],"greeting":"Hello, Rene! You have 7 unread messages.","favoriteFruit":"banana"}]' # noqa def test_range_tail(self): fixer = JSONFixer() - for i in range(1000): + for _ in range(1000): idx = random.randint(1, len(self.line)) result = fixer.fix(self.line[:idx]) self.assertTrue(result.success) def test_range_head(self): fixer = JSONFixer(200) - for i in range(1000): + for _ in range(1000): idx = random.randint(1, len(self.line)) result = fixer.fix(self.line[idx:]) if not result.success: diff --git a/tests/test_rules/test_array_rules.py b/tests/test_rules/test_array_rules.py index 9270de2..83a64b0 100644 --- a/tests/test_rules/test_array_rules.py +++ b/tests/test_rules/test_array_rules.py @@ -10,26 +10,28 @@ class TestFixArrayElement: rule = FixArrayElement() def test_leading_comma(self): - ctx = diagnose('[,') + ctx = diagnose("[,") fix = self.rule.apply(ctx) - assert fix.text == '[' + assert fix.text == "[" - @pytest.mark.skipif(sys.version_info >= (3, 13), reason="Python 3.13+ accepts trailing commas in JSON") + @pytest.mark.skipif( + sys.version_info >= (3, 13), reason="Python 3.13+ accepts trailing commas in JSON" + ) def test_trailing_comma(self): - ctx = diagnose('[null,]') + ctx = diagnose("[null,]") fix = self.rule.apply(ctx) - assert fix.text == '[null]' + assert fix.text == "[null]" def test_empty_array_body(self): - ctx = diagnose('[') + ctx = diagnose("[") fix = self.rule.apply(ctx) - assert fix.text == '[]' + assert fix.text == "[]" class TestCloseOrCommaArray: rule = CloseOrCommaArray() def test_missing_close(self): - ctx = diagnose('[1 2]') + ctx = diagnose("[1 2]") fix = self.rule.apply(ctx) - assert fix.text == '[1 ,2]' + assert fix.text == "[1 ,2]" diff --git a/tests/test_rules/test_object_rules.py b/tests/test_rules/test_object_rules.py index 5cb4961..ba301d7 100644 --- a/tests/test_rules/test_object_rules.py +++ b/tests/test_rules/test_object_rules.py @@ -15,25 +15,27 @@ class TestInsertMissingKey: rule = InsertMissingKey() def test_empty_after_brace(self): - ctx = diagnose('{') + ctx = diagnose("{") fix = self.rule.apply(ctx) - assert fix.text == '{}' + assert fix.text == "{}" def test_colon_without_key(self): - ctx = diagnose('{:1}') + ctx = diagnose("{:1}") fix = self.rule.apply(ctx) assert fix.text == '{"":1}' - @pytest.mark.skipif(sys.version_info >= (3, 13), reason="Python 3.13+ accepts trailing commas in JSON") + @pytest.mark.skipif( + sys.version_info >= (3, 13), reason="Python 3.13+ accepts trailing commas in JSON" + ) def test_trailing_comma(self): ctx = diagnose('{"a":1,}') fix = self.rule.apply(ctx) assert fix.text == '{"a":1}' def test_double_comma(self): - ctx = diagnose('{,,') + ctx = diagnose("{,,") fix = self.rule.apply(ctx) - assert fix.text == '{,' + assert fix.text == "{," class TestInsertMissingColon: diff --git a/tests/test_rules/test_string_rules.py b/tests/test_rules/test_string_rules.py index 0dfa461..d6369af 100644 --- a/tests/test_rules/test_string_rules.py +++ b/tests/test_rules/test_string_rules.py @@ -1,4 +1,4 @@ -from half_json.diagnosis import ErrorType, diagnose +from half_json.diagnosis import diagnose from half_json.rules.string_rules import CloseUnterminatedString diff --git a/tests/test_rules/test_structural_rules.py b/tests/test_rules/test_structural_rules.py index efd05ec..a99029e 100644 --- a/tests/test_rules/test_structural_rules.py +++ b/tests/test_rules/test_structural_rules.py @@ -5,32 +5,32 @@ class TestPrependMissingBracket: def test_closing_brace(self): rule = PrependMissingBracket() - ctx = diagnose('}') + ctx = diagnose("}") fix = rule.apply(ctx) - assert fix.text == '{}' + assert fix.text == "{}" def test_closing_bracket(self): rule = PrependMissingBracket() - ctx = diagnose(']') + ctx = diagnose("]") fix = rule.apply(ctx) - assert fix.text == '[]' + assert fix.text == "[]" def test_negative_decimal(self): rule = PrependMissingBracket() - ctx = diagnose('-.5') + ctx = diagnose("-.5") fix = rule.apply(ctx) - assert fix.text == '-0.5' + assert fix.text == "-0.5" class TestWrapPartialParse: def test_partial_with_bracket(self): rule = WrapPartialParse() - ctx = diagnose('{}]') + ctx = diagnose("{}]") fix = rule.apply(ctx) - assert fix.text == '[{}]' + assert fix.text == "[{}]" def test_partial_with_comma(self): rule = WrapPartialParse() - ctx = diagnose('1, 2') + ctx = diagnose("1, 2") fix = rule.apply(ctx) - assert fix.text == '[1, 2' + assert fix.text == "[1, 2" diff --git a/tests/test_stop.py b/tests/test_stop.py index 35170ef..8a0871b 100644 --- a/tests/test_stop.py +++ b/tests/test_stop.py @@ -1,44 +1,41 @@ -# coding=utf8 - import unittest from half_json.core import JSONFixer class TestOtherCase(unittest.TestCase): - def test_patch_left_object(self): - line = '}' + line = "}" ok, newline, _ = JSONFixer().fix(line) self.assertTrue(ok) - self.assertEqual('{}', newline) + self.assertEqual("{}", newline) def test_patch_left_array(self): - line = ']' + line = "]" ok, newline, _ = JSONFixer().fix(line) self.assertTrue(ok) - self.assertEqual('[]', newline) + self.assertEqual("[]", newline) def test_patch_half_array(self): - line = '[]]' + line = "[]]" ok, newline, _ = JSONFixer().fix(line) self.assertTrue(ok) - self.assertEqual('[[]]', newline) + self.assertEqual("[[]]", newline) def test_patch_half_object(self): - line = '{}}' + line = "{}}" ok, newline, _ = JSONFixer().fix(line) self.assertTrue(ok) self.assertEqual('{"":{}}', newline) def test_patch_half_object_array(self): - line = '{}]' + line = "{}]" ok, newline, _ = JSONFixer().fix(line) self.assertTrue(ok) - self.assertEqual('[{}]', newline) + self.assertEqual("[{}]", newline) def test_patch_half_array_object(self): - line = '[]}' + line = "[]}" ok, newline, _ = JSONFixer().fix(line) self.assertTrue(ok) self.assertEqual('{"":[]}', newline) @@ -50,10 +47,10 @@ def test_patch_half_array_with_coma(self): self.assertEqual('[1, [""], -1]', newline) def test_patch_half_array_with_coma_v2(self): - line = '1, 2' + line = "1, 2" ok, newline, _ = JSONFixer().fix(line) self.assertTrue(ok) - self.assertEqual('[1, 2]', newline) + self.assertEqual("[1, 2]", newline) def test_patch_half_object_with_colon(self): line = '"a":' @@ -62,7 +59,7 @@ def test_patch_half_object_with_colon(self): self.assertEqual('{"a":null}', newline) def test_patch_many_half_object(self): - line = '{}[]{}}]' + line = "{}[]{}}]" ok, newline, _ = JSONFixer().fix(line) self.assertTrue(ok) self.assertEqual('[{"":{},"":[],"":{}}]', newline) diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..8649ea9 --- /dev/null +++ b/uv.lock @@ -0,0 +1,398 @@ +version = 1 +revision = 2 +requires-python = ">=3.9" +resolution-markers = [ + "python_full_version >= '3.10'", + "python_full_version < '3.10'", +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "exceptiongroup" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, +] + +[[package]] +name = "jsonfixer" +version = "0.3.0" +source = { editable = "." } + +[package.dev-dependencies] +dev = [ + { name = "mypy" }, + { name = "pytest", version = "8.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "pytest", version = "9.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "ruff" }, +] + +[package.metadata] + +[package.metadata.requires-dev] +dev = [ + { name = "mypy", specifier = ">=1.14.0" }, + { name = "pytest", specifier = ">=8.0" }, + { name = "ruff", specifier = ">=0.9.0" }, +] + +[[package]] +name = "librt" +version = "0.8.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/56/9c/b4b0c54d84da4a94b37bd44151e46d5e583c9534c7e02250b961b1b6d8a8/librt-0.8.1.tar.gz", hash = "sha256:be46a14693955b3bd96014ccbdb8339ee8c9346fbe11c1b78901b55125f14c73", size = 177471, upload-time = "2026-02-17T16:13:06.101Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/5f/63f5fa395c7a8a93558c0904ba8f1c8d1b997ca6a3de61bc7659970d66bf/librt-0.8.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:81fd938344fecb9373ba1b155968c8a329491d2ce38e7ddb76f30ffb938f12dc", size = 65697, upload-time = "2026-02-17T16:11:06.903Z" }, + { url = "https://files.pythonhosted.org/packages/ff/e0/0472cf37267b5920eff2f292ccfaede1886288ce35b7f3203d8de00abfe6/librt-0.8.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5db05697c82b3a2ec53f6e72b2ed373132b0c2e05135f0696784e97d7f5d48e7", size = 68376, upload-time = "2026-02-17T16:11:08.395Z" }, + { url = "https://files.pythonhosted.org/packages/c8/be/8bd1359fdcd27ab897cd5963294fa4a7c83b20a8564678e4fd12157e56a5/librt-0.8.1-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d56bc4011975f7460bea7b33e1ff425d2f1adf419935ff6707273c77f8a4ada6", size = 197084, upload-time = "2026-02-17T16:11:09.774Z" }, + { url = "https://files.pythonhosted.org/packages/e2/fe/163e33fdd091d0c2b102f8a60cc0a61fd730ad44e32617cd161e7cd67a01/librt-0.8.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cdc0f588ff4b663ea96c26d2a230c525c6fc62b28314edaaaca8ed5af931ad0", size = 207337, upload-time = "2026-02-17T16:11:11.311Z" }, + { url = "https://files.pythonhosted.org/packages/01/99/f85130582f05dcf0c8902f3d629270231d2f4afdfc567f8305a952ac7f14/librt-0.8.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:97c2b54ff6717a7a563b72627990bec60d8029df17df423f0ed37d56a17a176b", size = 219980, upload-time = "2026-02-17T16:11:12.499Z" }, + { url = "https://files.pythonhosted.org/packages/6f/54/cb5e4d03659e043a26c74e08206412ac9a3742f0477d96f9761a55313b5f/librt-0.8.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8f1125e6bbf2f1657d9a2f3ccc4a2c9b0c8b176965bb565dd4d86be67eddb4b6", size = 212921, upload-time = "2026-02-17T16:11:14.484Z" }, + { url = "https://files.pythonhosted.org/packages/b1/81/a3a01e4240579c30f3487f6fed01eb4bc8ef0616da5b4ebac27ca19775f3/librt-0.8.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8f4bb453f408137d7581be309b2fbc6868a80e7ef60c88e689078ee3a296ae71", size = 221381, upload-time = "2026-02-17T16:11:17.459Z" }, + { url = "https://files.pythonhosted.org/packages/08/b0/fc2d54b4b1c6fb81e77288ff31ff25a2c1e62eaef4424a984f228839717b/librt-0.8.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c336d61d2fe74a3195edc1646d53ff1cddd3a9600b09fa6ab75e5514ba4862a7", size = 216714, upload-time = "2026-02-17T16:11:19.197Z" }, + { url = "https://files.pythonhosted.org/packages/96/96/85daa73ffbd87e1fb287d7af6553ada66bf25a2a6b0de4764344a05469f6/librt-0.8.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:eb5656019db7c4deacf0c1a55a898c5bb8f989be904597fcb5232a2f4828fa05", size = 214777, upload-time = "2026-02-17T16:11:20.443Z" }, + { url = "https://files.pythonhosted.org/packages/12/9c/c3aa7a2360383f4bf4f04d98195f2739a579128720c603f4807f006a4225/librt-0.8.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c25d9e338d5bed46c1632f851babf3d13c78f49a225462017cf5e11e845c5891", size = 237398, upload-time = "2026-02-17T16:11:22.083Z" }, + { url = "https://files.pythonhosted.org/packages/61/19/d350ea89e5274665185dabc4bbb9c3536c3411f862881d316c8b8e00eb66/librt-0.8.1-cp310-cp310-win32.whl", hash = "sha256:aaab0e307e344cb28d800957ef3ec16605146ef0e59e059a60a176d19543d1b7", size = 54285, upload-time = "2026-02-17T16:11:23.27Z" }, + { url = "https://files.pythonhosted.org/packages/4f/d6/45d587d3d41c112e9543a0093d883eb57a24a03e41561c127818aa2a6bcc/librt-0.8.1-cp310-cp310-win_amd64.whl", hash = "sha256:56e04c14b696300d47b3bc5f1d10a00e86ae978886d0cee14e5714fafb5df5d2", size = 61352, upload-time = "2026-02-17T16:11:24.207Z" }, + { url = "https://files.pythonhosted.org/packages/1d/01/0e748af5e4fee180cf7cd12bd12b0513ad23b045dccb2a83191bde82d168/librt-0.8.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:681dc2451d6d846794a828c16c22dc452d924e9f700a485b7ecb887a30aad1fd", size = 65315, upload-time = "2026-02-17T16:11:25.152Z" }, + { url = "https://files.pythonhosted.org/packages/9d/4d/7184806efda571887c798d573ca4134c80ac8642dcdd32f12c31b939c595/librt-0.8.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3b4350b13cc0e6f5bec8fa7caf29a8fb8cdc051a3bae45cfbfd7ce64f009965", size = 68021, upload-time = "2026-02-17T16:11:26.129Z" }, + { url = "https://files.pythonhosted.org/packages/ae/88/c3c52d2a5d5101f28d3dc89298444626e7874aa904eed498464c2af17627/librt-0.8.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ac1e7817fd0ed3d14fd7c5df91daed84c48e4c2a11ee99c0547f9f62fdae13da", size = 194500, upload-time = "2026-02-17T16:11:27.177Z" }, + { url = "https://files.pythonhosted.org/packages/d6/5d/6fb0a25b6a8906e85b2c3b87bee1d6ed31510be7605b06772f9374ca5cb3/librt-0.8.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:747328be0c5b7075cde86a0e09d7a9196029800ba75a1689332348e998fb85c0", size = 205622, upload-time = "2026-02-17T16:11:28.242Z" }, + { url = "https://files.pythonhosted.org/packages/b2/a6/8006ae81227105476a45691f5831499e4d936b1c049b0c1feb17c11b02d1/librt-0.8.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f0af2bd2bc204fa27f3d6711d0f360e6b8c684a035206257a81673ab924aa11e", size = 218304, upload-time = "2026-02-17T16:11:29.344Z" }, + { url = "https://files.pythonhosted.org/packages/ee/19/60e07886ad16670aae57ef44dada41912c90906a6fe9f2b9abac21374748/librt-0.8.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d480de377f5b687b6b1bc0c0407426da556e2a757633cc7e4d2e1a057aa688f3", size = 211493, upload-time = "2026-02-17T16:11:30.445Z" }, + { url = "https://files.pythonhosted.org/packages/9c/cf/f666c89d0e861d05600438213feeb818c7514d3315bae3648b1fc145d2b6/librt-0.8.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d0ee06b5b5291f609ddb37b9750985b27bc567791bc87c76a569b3feed8481ac", size = 219129, upload-time = "2026-02-17T16:11:32.021Z" }, + { url = "https://files.pythonhosted.org/packages/8f/ef/f1bea01e40b4a879364c031476c82a0dc69ce068daad67ab96302fed2d45/librt-0.8.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9e2c6f77b9ad48ce5603b83b7da9ee3e36b3ab425353f695cba13200c5d96596", size = 213113, upload-time = "2026-02-17T16:11:33.192Z" }, + { url = "https://files.pythonhosted.org/packages/9b/80/cdab544370cc6bc1b72ea369525f547a59e6938ef6863a11ab3cd24759af/librt-0.8.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:439352ba9373f11cb8e1933da194dcc6206daf779ff8df0ed69c5e39113e6a99", size = 212269, upload-time = "2026-02-17T16:11:34.373Z" }, + { url = "https://files.pythonhosted.org/packages/9d/9c/48d6ed8dac595654f15eceab2035131c136d1ae9a1e3548e777bb6dbb95d/librt-0.8.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:82210adabbc331dbb65d7868b105185464ef13f56f7f76688565ad79f648b0fe", size = 234673, upload-time = "2026-02-17T16:11:36.063Z" }, + { url = "https://files.pythonhosted.org/packages/16/01/35b68b1db517f27a01be4467593292eb5315def8900afad29fabf56304ba/librt-0.8.1-cp311-cp311-win32.whl", hash = "sha256:52c224e14614b750c0a6d97368e16804a98c684657c7518752c356834fff83bb", size = 54597, upload-time = "2026-02-17T16:11:37.544Z" }, + { url = "https://files.pythonhosted.org/packages/71/02/796fe8f02822235966693f257bf2c79f40e11337337a657a8cfebba5febc/librt-0.8.1-cp311-cp311-win_amd64.whl", hash = "sha256:c00e5c884f528c9932d278d5c9cbbea38a6b81eb62c02e06ae53751a83a4d52b", size = 61733, upload-time = "2026-02-17T16:11:38.691Z" }, + { url = "https://files.pythonhosted.org/packages/28/ad/232e13d61f879a42a4e7117d65e4984bb28371a34bb6fb9ca54ec2c8f54e/librt-0.8.1-cp311-cp311-win_arm64.whl", hash = "sha256:f7cdf7f26c2286ffb02e46d7bac56c94655540b26347673bea15fa52a6af17e9", size = 52273, upload-time = "2026-02-17T16:11:40.308Z" }, + { url = "https://files.pythonhosted.org/packages/95/21/d39b0a87ac52fc98f621fb6f8060efb017a767ebbbac2f99fbcbc9ddc0d7/librt-0.8.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a28f2612ab566b17f3698b0da021ff9960610301607c9a5e8eaca62f5e1c350a", size = 66516, upload-time = "2026-02-17T16:11:41.604Z" }, + { url = "https://files.pythonhosted.org/packages/69/f1/46375e71441c43e8ae335905e069f1c54febee63a146278bcee8782c84fd/librt-0.8.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:60a78b694c9aee2a0f1aaeaa7d101cf713e92e8423a941d2897f4fa37908dab9", size = 68634, upload-time = "2026-02-17T16:11:43.268Z" }, + { url = "https://files.pythonhosted.org/packages/0a/33/c510de7f93bf1fa19e13423a606d8189a02624a800710f6e6a0a0f0784b3/librt-0.8.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:758509ea3f1eba2a57558e7e98f4659d0ea7670bff49673b0dde18a3c7e6c0eb", size = 198941, upload-time = "2026-02-17T16:11:44.28Z" }, + { url = "https://files.pythonhosted.org/packages/dd/36/e725903416409a533d92398e88ce665476f275081d0d7d42f9c4951999e5/librt-0.8.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:039b9f2c506bd0ab0f8725aa5ba339c6f0cd19d3b514b50d134789809c24285d", size = 209991, upload-time = "2026-02-17T16:11:45.462Z" }, + { url = "https://files.pythonhosted.org/packages/30/7a/8d908a152e1875c9f8eac96c97a480df425e657cdb47854b9efaa4998889/librt-0.8.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5bb54f1205a3a6ab41a6fd71dfcdcbd278670d3a90ca502a30d9da583105b6f7", size = 224476, upload-time = "2026-02-17T16:11:46.542Z" }, + { url = "https://files.pythonhosted.org/packages/a8/b8/a22c34f2c485b8903a06f3fe3315341fe6876ef3599792344669db98fcff/librt-0.8.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:05bd41cdee35b0c59c259f870f6da532a2c5ca57db95b5f23689fcb5c9e42440", size = 217518, upload-time = "2026-02-17T16:11:47.746Z" }, + { url = "https://files.pythonhosted.org/packages/79/6f/5c6fea00357e4f82ba44f81dbfb027921f1ab10e320d4a64e1c408d035d9/librt-0.8.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:adfab487facf03f0d0857b8710cf82d0704a309d8ffc33b03d9302b4c64e91a9", size = 225116, upload-time = "2026-02-17T16:11:49.298Z" }, + { url = "https://files.pythonhosted.org/packages/f2/a0/95ced4e7b1267fe1e2720a111685bcddf0e781f7e9e0ce59d751c44dcfe5/librt-0.8.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:153188fe98a72f206042be10a2c6026139852805215ed9539186312d50a8e972", size = 217751, upload-time = "2026-02-17T16:11:50.49Z" }, + { url = "https://files.pythonhosted.org/packages/93/c2/0517281cb4d4101c27ab59472924e67f55e375bc46bedae94ac6dc6e1902/librt-0.8.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:dd3c41254ee98604b08bd5b3af5bf0a89740d4ee0711de95b65166bf44091921", size = 218378, upload-time = "2026-02-17T16:11:51.783Z" }, + { url = "https://files.pythonhosted.org/packages/43/e8/37b3ac108e8976888e559a7b227d0ceac03c384cfd3e7a1c2ee248dbae79/librt-0.8.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e0d138c7ae532908cbb342162b2611dbd4d90c941cd25ab82084aaf71d2c0bd0", size = 241199, upload-time = "2026-02-17T16:11:53.561Z" }, + { url = "https://files.pythonhosted.org/packages/4b/5b/35812d041c53967fedf551a39399271bbe4257e681236a2cf1a69c8e7fa1/librt-0.8.1-cp312-cp312-win32.whl", hash = "sha256:43353b943613c5d9c49a25aaffdba46f888ec354e71e3529a00cca3f04d66a7a", size = 54917, upload-time = "2026-02-17T16:11:54.758Z" }, + { url = "https://files.pythonhosted.org/packages/de/d1/fa5d5331b862b9775aaf2a100f5ef86854e5d4407f71bddf102f4421e034/librt-0.8.1-cp312-cp312-win_amd64.whl", hash = "sha256:ff8baf1f8d3f4b6b7257fcb75a501f2a5499d0dda57645baa09d4d0d34b19444", size = 62017, upload-time = "2026-02-17T16:11:55.748Z" }, + { url = "https://files.pythonhosted.org/packages/c7/7c/c614252f9acda59b01a66e2ddfd243ed1c7e1deab0293332dfbccf862808/librt-0.8.1-cp312-cp312-win_arm64.whl", hash = "sha256:0f2ae3725904f7377e11cc37722d5d401e8b3d5851fb9273d7f4fe04f6b3d37d", size = 52441, upload-time = "2026-02-17T16:11:56.801Z" }, + { url = "https://files.pythonhosted.org/packages/c5/3c/f614c8e4eaac7cbf2bbdf9528790b21d89e277ee20d57dc6e559c626105f/librt-0.8.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7e6bad1cd94f6764e1e21950542f818a09316645337fd5ab9a7acc45d99a8f35", size = 66529, upload-time = "2026-02-17T16:11:57.809Z" }, + { url = "https://files.pythonhosted.org/packages/ab/96/5836544a45100ae411eda07d29e3d99448e5258b6e9c8059deb92945f5c2/librt-0.8.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cf450f498c30af55551ba4f66b9123b7185362ec8b625a773b3d39aa1a717583", size = 68669, upload-time = "2026-02-17T16:11:58.843Z" }, + { url = "https://files.pythonhosted.org/packages/06/53/f0b992b57af6d5531bf4677d75c44f095f2366a1741fb695ee462ae04b05/librt-0.8.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:eca45e982fa074090057132e30585a7e8674e9e885d402eae85633e9f449ce6c", size = 199279, upload-time = "2026-02-17T16:11:59.862Z" }, + { url = "https://files.pythonhosted.org/packages/f3/ad/4848cc16e268d14280d8168aee4f31cea92bbd2b79ce33d3e166f2b4e4fc/librt-0.8.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c3811485fccfda840861905b8c70bba5ec094e02825598bb9d4ca3936857a04", size = 210288, upload-time = "2026-02-17T16:12:00.954Z" }, + { url = "https://files.pythonhosted.org/packages/52/05/27fdc2e95de26273d83b96742d8d3b7345f2ea2bdbd2405cc504644f2096/librt-0.8.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e4af413908f77294605e28cfd98063f54b2c790561383971d2f52d113d9c363", size = 224809, upload-time = "2026-02-17T16:12:02.108Z" }, + { url = "https://files.pythonhosted.org/packages/7a/d0/78200a45ba3240cb042bc597d6f2accba9193a2c57d0356268cbbe2d0925/librt-0.8.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5212a5bd7fae98dae95710032902edcd2ec4dc994e883294f75c857b83f9aba0", size = 218075, upload-time = "2026-02-17T16:12:03.631Z" }, + { url = "https://files.pythonhosted.org/packages/af/72/a210839fa74c90474897124c064ffca07f8d4b347b6574d309686aae7ca6/librt-0.8.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e692aa2d1d604e6ca12d35e51fdc36f4cda6345e28e36374579f7ef3611b3012", size = 225486, upload-time = "2026-02-17T16:12:04.725Z" }, + { url = "https://files.pythonhosted.org/packages/a3/c1/a03cc63722339ddbf087485f253493e2b013039f5b707e8e6016141130fa/librt-0.8.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4be2a5c926b9770c9e08e717f05737a269b9d0ebc5d2f0060f0fe3fe9ce47acb", size = 218219, upload-time = "2026-02-17T16:12:05.828Z" }, + { url = "https://files.pythonhosted.org/packages/58/f5/fff6108af0acf941c6f274a946aea0e484bd10cd2dc37610287ce49388c5/librt-0.8.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:fd1a720332ea335ceb544cf0a03f81df92abd4bb887679fd1e460976b0e6214b", size = 218750, upload-time = "2026-02-17T16:12:07.09Z" }, + { url = "https://files.pythonhosted.org/packages/71/67/5a387bfef30ec1e4b4f30562c8586566faf87e47d696768c19feb49e3646/librt-0.8.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2af9e01e0ef80d95ae3c720be101227edae5f2fe7e3dc63d8857fadfc5a1d", size = 241624, upload-time = "2026-02-17T16:12:08.43Z" }, + { url = "https://files.pythonhosted.org/packages/d4/be/24f8502db11d405232ac1162eb98069ca49c3306c1d75c6ccc61d9af8789/librt-0.8.1-cp313-cp313-win32.whl", hash = "sha256:086a32dbb71336627e78cc1d6ee305a68d038ef7d4c39aaff41ae8c9aa46e91a", size = 54969, upload-time = "2026-02-17T16:12:09.633Z" }, + { url = "https://files.pythonhosted.org/packages/5c/73/c9fdf6cb2a529c1a092ce769a12d88c8cca991194dfe641b6af12fa964d2/librt-0.8.1-cp313-cp313-win_amd64.whl", hash = "sha256:e11769a1dbda4da7b00a76cfffa67aa47cfa66921d2724539eee4b9ede780b79", size = 62000, upload-time = "2026-02-17T16:12:10.632Z" }, + { url = "https://files.pythonhosted.org/packages/d3/97/68f80ca3ac4924f250cdfa6e20142a803e5e50fca96ef5148c52ee8c10ea/librt-0.8.1-cp313-cp313-win_arm64.whl", hash = "sha256:924817ab3141aca17893386ee13261f1d100d1ef410d70afe4389f2359fea4f0", size = 52495, upload-time = "2026-02-17T16:12:11.633Z" }, + { url = "https://files.pythonhosted.org/packages/c9/6a/907ef6800f7bca71b525a05f1839b21f708c09043b1c6aa77b6b827b3996/librt-0.8.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:6cfa7fe54fd4d1f47130017351a959fe5804bda7a0bc7e07a2cdbc3fdd28d34f", size = 66081, upload-time = "2026-02-17T16:12:12.766Z" }, + { url = "https://files.pythonhosted.org/packages/1b/18/25e991cd5640c9fb0f8d91b18797b29066b792f17bf8493da183bf5caabe/librt-0.8.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:228c2409c079f8c11fb2e5d7b277077f694cb93443eb760e00b3b83cb8b3176c", size = 68309, upload-time = "2026-02-17T16:12:13.756Z" }, + { url = "https://files.pythonhosted.org/packages/a4/36/46820d03f058cfb5a9de5940640ba03165ed8aded69e0733c417bb04df34/librt-0.8.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7aae78ab5e3206181780e56912d1b9bb9f90a7249ce12f0e8bf531d0462dd0fc", size = 196804, upload-time = "2026-02-17T16:12:14.818Z" }, + { url = "https://files.pythonhosted.org/packages/59/18/5dd0d3b87b8ff9c061849fbdb347758d1f724b9a82241aa908e0ec54ccd0/librt-0.8.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:172d57ec04346b047ca6af181e1ea4858086c80bdf455f61994c4aa6fc3f866c", size = 206907, upload-time = "2026-02-17T16:12:16.513Z" }, + { url = "https://files.pythonhosted.org/packages/d1/96/ef04902aad1424fd7299b62d1890e803e6ab4018c3044dca5922319c4b97/librt-0.8.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6b1977c4ea97ce5eb7755a78fae68d87e4102e4aaf54985e8b56806849cc06a3", size = 221217, upload-time = "2026-02-17T16:12:17.906Z" }, + { url = "https://files.pythonhosted.org/packages/6d/ff/7e01f2dda84a8f5d280637a2e5827210a8acca9a567a54507ef1c75b342d/librt-0.8.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:10c42e1f6fd06733ef65ae7bebce2872bcafd8d6e6b0a08fe0a05a23b044fb14", size = 214622, upload-time = "2026-02-17T16:12:19.108Z" }, + { url = "https://files.pythonhosted.org/packages/1e/8c/5b093d08a13946034fed57619742f790faf77058558b14ca36a6e331161e/librt-0.8.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4c8dfa264b9193c4ee19113c985c95f876fae5e51f731494fc4e0cf594990ba7", size = 221987, upload-time = "2026-02-17T16:12:20.331Z" }, + { url = "https://files.pythonhosted.org/packages/d3/cc/86b0b3b151d40920ad45a94ce0171dec1aebba8a9d72bb3fa00c73ab25dd/librt-0.8.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:01170b6729a438f0dedc4a26ed342e3dc4f02d1000b4b19f980e1877f0c297e6", size = 215132, upload-time = "2026-02-17T16:12:21.54Z" }, + { url = "https://files.pythonhosted.org/packages/fc/be/8588164a46edf1e69858d952654e216a9a91174688eeefb9efbb38a9c799/librt-0.8.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:7b02679a0d783bdae30d443025b94465d8c3dc512f32f5b5031f93f57ac32071", size = 215195, upload-time = "2026-02-17T16:12:23.073Z" }, + { url = "https://files.pythonhosted.org/packages/f5/f2/0b9279bea735c734d69344ecfe056c1ba211694a72df10f568745c899c76/librt-0.8.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:190b109bb69592a3401fe1ffdea41a2e73370ace2ffdc4a0e8e2b39cdea81b78", size = 237946, upload-time = "2026-02-17T16:12:24.275Z" }, + { url = "https://files.pythonhosted.org/packages/e9/cc/5f2a34fbc8aeb35314a3641f9956fa9051a947424652fad9882be7a97949/librt-0.8.1-cp314-cp314-win32.whl", hash = "sha256:e70a57ecf89a0f64c24e37f38d3fe217a58169d2fe6ed6d70554964042474023", size = 50689, upload-time = "2026-02-17T16:12:25.766Z" }, + { url = "https://files.pythonhosted.org/packages/a0/76/cd4d010ab2147339ca2b93e959c3686e964edc6de66ddacc935c325883d7/librt-0.8.1-cp314-cp314-win_amd64.whl", hash = "sha256:7e2f3edca35664499fbb36e4770650c4bd4a08abc1f4458eab9df4ec56389730", size = 57875, upload-time = "2026-02-17T16:12:27.465Z" }, + { url = "https://files.pythonhosted.org/packages/84/0f/2143cb3c3ca48bd3379dcd11817163ca50781927c4537345d608b5045998/librt-0.8.1-cp314-cp314-win_arm64.whl", hash = "sha256:0d2f82168e55ddefd27c01c654ce52379c0750ddc31ee86b4b266bcf4d65f2a3", size = 48058, upload-time = "2026-02-17T16:12:28.556Z" }, + { url = "https://files.pythonhosted.org/packages/d2/0e/9b23a87e37baf00311c3efe6b48d6b6c168c29902dfc3f04c338372fd7db/librt-0.8.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2c74a2da57a094bd48d03fa5d196da83d2815678385d2978657499063709abe1", size = 68313, upload-time = "2026-02-17T16:12:29.659Z" }, + { url = "https://files.pythonhosted.org/packages/db/9a/859c41e5a4f1c84200a7d2b92f586aa27133c8243b6cac9926f6e54d01b9/librt-0.8.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a355d99c4c0d8e5b770313b8b247411ed40949ca44e33e46a4789b9293a907ee", size = 70994, upload-time = "2026-02-17T16:12:31.516Z" }, + { url = "https://files.pythonhosted.org/packages/4c/28/10605366ee599ed34223ac2bf66404c6fb59399f47108215d16d5ad751a8/librt-0.8.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:2eb345e8b33fb748227409c9f1233d4df354d6e54091f0e8fc53acdb2ffedeb7", size = 220770, upload-time = "2026-02-17T16:12:33.294Z" }, + { url = "https://files.pythonhosted.org/packages/af/8d/16ed8fd452dafae9c48d17a6bc1ee3e818fd40ef718d149a8eff2c9f4ea2/librt-0.8.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9be2f15e53ce4e83cc08adc29b26fb5978db62ef2a366fbdf716c8a6c8901040", size = 235409, upload-time = "2026-02-17T16:12:35.443Z" }, + { url = "https://files.pythonhosted.org/packages/89/1b/7bdf3e49349c134b25db816e4a3db6b94a47ac69d7d46b1e682c2c4949be/librt-0.8.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:785ae29c1f5c6e7c2cde2c7c0e148147f4503da3abc5d44d482068da5322fd9e", size = 246473, upload-time = "2026-02-17T16:12:36.656Z" }, + { url = "https://files.pythonhosted.org/packages/4e/8a/91fab8e4fd2a24930a17188c7af5380eb27b203d72101c9cc000dbdfd95a/librt-0.8.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1d3a7da44baf692f0c6aeb5b2a09c5e6fc7a703bca9ffa337ddd2e2da53f7732", size = 238866, upload-time = "2026-02-17T16:12:37.849Z" }, + { url = "https://files.pythonhosted.org/packages/b9/e0/c45a098843fc7c07e18a7f8a24ca8496aecbf7bdcd54980c6ca1aaa79a8e/librt-0.8.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5fc48998000cbc39ec0d5311312dda93ecf92b39aaf184c5e817d5d440b29624", size = 250248, upload-time = "2026-02-17T16:12:39.445Z" }, + { url = "https://files.pythonhosted.org/packages/82/30/07627de23036640c952cce0c1fe78972e77d7d2f8fd54fa5ef4554ff4a56/librt-0.8.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:e96baa6820280077a78244b2e06e416480ed859bbd8e5d641cf5742919d8beb4", size = 240629, upload-time = "2026-02-17T16:12:40.889Z" }, + { url = "https://files.pythonhosted.org/packages/fb/c1/55bfe1ee3542eba055616f9098eaf6eddb966efb0ca0f44eaa4aba327307/librt-0.8.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:31362dbfe297b23590530007062c32c6f6176f6099646bb2c95ab1b00a57c382", size = 239615, upload-time = "2026-02-17T16:12:42.446Z" }, + { url = "https://files.pythonhosted.org/packages/2b/39/191d3d28abc26c9099b19852e6c99f7f6d400b82fa5a4e80291bd3803e19/librt-0.8.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cc3656283d11540ab0ea01978378e73e10002145117055e03722417aeab30994", size = 263001, upload-time = "2026-02-17T16:12:43.627Z" }, + { url = "https://files.pythonhosted.org/packages/b9/eb/7697f60fbe7042ab4e88f4ee6af496b7f222fffb0a4e3593ef1f29f81652/librt-0.8.1-cp314-cp314t-win32.whl", hash = "sha256:738f08021b3142c2918c03692608baed43bc51144c29e35807682f8070ee2a3a", size = 51328, upload-time = "2026-02-17T16:12:45.148Z" }, + { url = "https://files.pythonhosted.org/packages/7c/72/34bf2eb7a15414a23e5e70ecb9440c1d3179f393d9349338a91e2781c0fb/librt-0.8.1-cp314-cp314t-win_amd64.whl", hash = "sha256:89815a22daf9c51884fb5dbe4f1ef65ee6a146e0b6a8df05f753e2e4a9359bf4", size = 58722, upload-time = "2026-02-17T16:12:46.85Z" }, + { url = "https://files.pythonhosted.org/packages/b2/c8/d148e041732d631fc76036f8b30fae4e77b027a1e95b7a84bb522481a940/librt-0.8.1-cp314-cp314t-win_arm64.whl", hash = "sha256:bf512a71a23504ed08103a13c941f763db13fb11177beb3d9244c98c29fb4a61", size = 48755, upload-time = "2026-02-17T16:12:47.943Z" }, + { url = "https://files.pythonhosted.org/packages/01/1f/c7d8b66a3ca3ca3ed8ded4b32c96ee58a45920ebbbaa934355c74adcc33e/librt-0.8.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3dff3d3ca8db20e783b1bc7de49c0a2ab0b8387f31236d6a026597d07fcd68ac", size = 65990, upload-time = "2026-02-17T16:12:48.972Z" }, + { url = "https://files.pythonhosted.org/packages/56/be/ee9ba1730052313d08457f19beaa1b878619978863fba09b40aed5b5c123/librt-0.8.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:08eec3a1fc435f0d09c87b6bf1ec798986a3544f446b864e4099633a56fcd9ed", size = 68640, upload-time = "2026-02-17T16:12:50.24Z" }, + { url = "https://files.pythonhosted.org/packages/81/27/b7309298b96f7690cec3ceee38004c1a7f60fcd96d952d3ac344a1e3e8b3/librt-0.8.1-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e3f0a41487fd5fad7e760b9e8a90e251e27c2816fbc2cff36a22a0e6bcbbd9dd", size = 196099, upload-time = "2026-02-17T16:12:52.788Z" }, + { url = "https://files.pythonhosted.org/packages/10/48/160a5aacdcb21824b10a52378c39e88c46a29bb31efdaf3910dd1f9b670e/librt-0.8.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bacdb58d9939d95cc557b4dbaa86527c9db2ac1ed76a18bc8d26f6dc8647d851", size = 206663, upload-time = "2026-02-17T16:12:55.017Z" }, + { url = "https://files.pythonhosted.org/packages/ee/65/33dd1d8caabb7c6805d87d095b143417dc96b0277c06ffa0508361422c82/librt-0.8.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b6d7ab1f01aa753188605b09a51faa44a3327400b00b8cce424c71910fc0a128", size = 219318, upload-time = "2026-02-17T16:12:56.145Z" }, + { url = "https://files.pythonhosted.org/packages/09/d4/353805aa6181c7950a2462bd6e855366eeca21a501f375228d72a51547df/librt-0.8.1-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4998009e7cb9e896569f4be7004f09d0ed70d386fa99d42b6d363f6d200501ac", size = 212191, upload-time = "2026-02-17T16:12:57.326Z" }, + { url = "https://files.pythonhosted.org/packages/06/08/725b3f304d61eba56c713c251fb833a06d84bf93381caad5152366f5d2bb/librt-0.8.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2cc68eeeef5e906839c7bb0815748b5b0a974ec27125beefc0f942715785b551", size = 220672, upload-time = "2026-02-17T16:12:58.497Z" }, + { url = "https://files.pythonhosted.org/packages/0e/55/e8cdf04145872b3b97cb9b68287b22d1c08348227063f305aec11a3e6ce7/librt-0.8.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:0bf69d79a23f4f40b8673a947a234baeeb133b5078b483b7297c5916539cf5d5", size = 216172, upload-time = "2026-02-17T16:12:59.751Z" }, + { url = "https://files.pythonhosted.org/packages/8f/d8/23b1c6592d2422dd6829c672f45b1f1c257f219926b0d216fedb572d0184/librt-0.8.1-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:22b46eabd76c1986ee7d231b0765ad387d7673bbd996aa0d0d054b38ac65d8f6", size = 214116, upload-time = "2026-02-17T16:13:01.056Z" }, + { url = "https://files.pythonhosted.org/packages/c9/92/2b44fd3cc3313f44e43bdbb41343735b568fa675fa351642b408ee48d418/librt-0.8.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:237796479f4d0637d6b9cbcb926ff424a97735e68ade6facf402df4ec93375ed", size = 236664, upload-time = "2026-02-17T16:13:02.314Z" }, + { url = "https://files.pythonhosted.org/packages/00/23/92313ecdab80e142d8ea10e8dfa6297694359dbaacc9e81679bdc8cbceb6/librt-0.8.1-cp39-cp39-win32.whl", hash = "sha256:4beb04b8c66c6ae62f8c1e0b2f097c1ebad9295c929a8d5286c05eae7c2fc7dc", size = 54368, upload-time = "2026-02-17T16:13:03.549Z" }, + { url = "https://files.pythonhosted.org/packages/68/36/18f6e768afad6b55a690d38427c53251b69b7ba8795512730fd2508b31a9/librt-0.8.1-cp39-cp39-win_amd64.whl", hash = "sha256:64548cde61b692dc0dc379f4b5f59a2f582c2ebe7890d09c1ae3b9e66fa015b7", size = 61507, upload-time = "2026-02-17T16:13:04.556Z" }, +] + +[[package]] +name = "mypy" +version = "1.19.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "librt", marker = "platform_python_implementation != 'PyPy'" }, + { name = "mypy-extensions" }, + { name = "pathspec" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f5/db/4efed9504bc01309ab9c2da7e352cc223569f05478012b5d9ece38fd44d2/mypy-1.19.1.tar.gz", hash = "sha256:19d88bb05303fe63f71dd2c6270daca27cb9401c4ca8255fe50d1d920e0eb9ba", size = 3582404, upload-time = "2025-12-15T05:03:48.42Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2f/63/e499890d8e39b1ff2df4c0c6ce5d371b6844ee22b8250687a99fd2f657a8/mypy-1.19.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f05aa3d375b385734388e844bc01733bd33c644ab48e9684faa54e5389775ec", size = 13101333, upload-time = "2025-12-15T05:03:03.28Z" }, + { url = "https://files.pythonhosted.org/packages/72/4b/095626fc136fba96effc4fd4a82b41d688ab92124f8c4f7564bffe5cf1b0/mypy-1.19.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:022ea7279374af1a5d78dfcab853fe6a536eebfda4b59deab53cd21f6cd9f00b", size = 12164102, upload-time = "2025-12-15T05:02:33.611Z" }, + { url = "https://files.pythonhosted.org/packages/0c/5b/952928dd081bf88a83a5ccd49aaecfcd18fd0d2710c7ff07b8fb6f7032b9/mypy-1.19.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee4c11e460685c3e0c64a4c5de82ae143622410950d6be863303a1c4ba0e36d6", size = 12765799, upload-time = "2025-12-15T05:03:28.44Z" }, + { url = "https://files.pythonhosted.org/packages/2a/0d/93c2e4a287f74ef11a66fb6d49c7a9f05e47b0a4399040e6719b57f500d2/mypy-1.19.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de759aafbae8763283b2ee5869c7255391fbc4de3ff171f8f030b5ec48381b74", size = 13522149, upload-time = "2025-12-15T05:02:36.011Z" }, + { url = "https://files.pythonhosted.org/packages/7b/0e/33a294b56aaad2b338d203e3a1d8b453637ac36cb278b45005e0901cf148/mypy-1.19.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ab43590f9cd5108f41aacf9fca31841142c786827a74ab7cc8a2eacb634e09a1", size = 13810105, upload-time = "2025-12-15T05:02:40.327Z" }, + { url = "https://files.pythonhosted.org/packages/0e/fd/3e82603a0cb66b67c5e7abababce6bf1a929ddf67bf445e652684af5c5a0/mypy-1.19.1-cp310-cp310-win_amd64.whl", hash = "sha256:2899753e2f61e571b3971747e302d5f420c3fd09650e1951e99f823bc3089dac", size = 10057200, upload-time = "2025-12-15T05:02:51.012Z" }, + { url = "https://files.pythonhosted.org/packages/ef/47/6b3ebabd5474d9cdc170d1342fbf9dddc1b0ec13ec90bf9004ee6f391c31/mypy-1.19.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d8dfc6ab58ca7dda47d9237349157500468e404b17213d44fc1cb77bce532288", size = 13028539, upload-time = "2025-12-15T05:03:44.129Z" }, + { url = "https://files.pythonhosted.org/packages/5c/a6/ac7c7a88a3c9c54334f53a941b765e6ec6c4ebd65d3fe8cdcfbe0d0fd7db/mypy-1.19.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e3f276d8493c3c97930e354b2595a44a21348b320d859fb4a2b9f66da9ed27ab", size = 12083163, upload-time = "2025-12-15T05:03:37.679Z" }, + { url = "https://files.pythonhosted.org/packages/67/af/3afa9cf880aa4a2c803798ac24f1d11ef72a0c8079689fac5cfd815e2830/mypy-1.19.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2abb24cf3f17864770d18d673c85235ba52456b36a06b6afc1e07c1fdcd3d0e6", size = 12687629, upload-time = "2025-12-15T05:02:31.526Z" }, + { url = "https://files.pythonhosted.org/packages/2d/46/20f8a7114a56484ab268b0ab372461cb3a8f7deed31ea96b83a4e4cfcfca/mypy-1.19.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a009ffa5a621762d0c926a078c2d639104becab69e79538a494bcccb62cc0331", size = 13436933, upload-time = "2025-12-15T05:03:15.606Z" }, + { url = "https://files.pythonhosted.org/packages/5b/f8/33b291ea85050a21f15da910002460f1f445f8007adb29230f0adea279cb/mypy-1.19.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f7cee03c9a2e2ee26ec07479f38ea9c884e301d42c6d43a19d20fb014e3ba925", size = 13661754, upload-time = "2025-12-15T05:02:26.731Z" }, + { url = "https://files.pythonhosted.org/packages/fd/a3/47cbd4e85bec4335a9cd80cf67dbc02be21b5d4c9c23ad6b95d6c5196bac/mypy-1.19.1-cp311-cp311-win_amd64.whl", hash = "sha256:4b84a7a18f41e167f7995200a1d07a4a6810e89d29859df936f1c3923d263042", size = 10055772, upload-time = "2025-12-15T05:03:26.179Z" }, + { url = "https://files.pythonhosted.org/packages/06/8a/19bfae96f6615aa8a0604915512e0289b1fad33d5909bf7244f02935d33a/mypy-1.19.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a8174a03289288c1f6c46d55cef02379b478bfbc8e358e02047487cad44c6ca1", size = 13206053, upload-time = "2025-12-15T05:03:46.622Z" }, + { url = "https://files.pythonhosted.org/packages/a5/34/3e63879ab041602154ba2a9f99817bb0c85c4df19a23a1443c8986e4d565/mypy-1.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffcebe56eb09ff0c0885e750036a095e23793ba6c2e894e7e63f6d89ad51f22e", size = 12219134, upload-time = "2025-12-15T05:03:24.367Z" }, + { url = "https://files.pythonhosted.org/packages/89/cc/2db6f0e95366b630364e09845672dbee0cbf0bbe753a204b29a944967cd9/mypy-1.19.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b64d987153888790bcdb03a6473d321820597ab8dd9243b27a92153c4fa50fd2", size = 12731616, upload-time = "2025-12-15T05:02:44.725Z" }, + { url = "https://files.pythonhosted.org/packages/00/be/dd56c1fd4807bc1eba1cf18b2a850d0de7bacb55e158755eb79f77c41f8e/mypy-1.19.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c35d298c2c4bba75feb2195655dfea8124d855dfd7343bf8b8c055421eaf0cf8", size = 13620847, upload-time = "2025-12-15T05:03:39.633Z" }, + { url = "https://files.pythonhosted.org/packages/6d/42/332951aae42b79329f743bf1da088cd75d8d4d9acc18fbcbd84f26c1af4e/mypy-1.19.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:34c81968774648ab5ac09c29a375fdede03ba253f8f8287847bd480782f73a6a", size = 13834976, upload-time = "2025-12-15T05:03:08.786Z" }, + { url = "https://files.pythonhosted.org/packages/6f/63/e7493e5f90e1e085c562bb06e2eb32cae27c5057b9653348d38b47daaecc/mypy-1.19.1-cp312-cp312-win_amd64.whl", hash = "sha256:b10e7c2cd7870ba4ad9b2d8a6102eb5ffc1f16ca35e3de6bfa390c1113029d13", size = 10118104, upload-time = "2025-12-15T05:03:10.834Z" }, + { url = "https://files.pythonhosted.org/packages/de/9f/a6abae693f7a0c697dbb435aac52e958dc8da44e92e08ba88d2e42326176/mypy-1.19.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e3157c7594ff2ef1634ee058aafc56a82db665c9438fd41b390f3bde1ab12250", size = 13201927, upload-time = "2025-12-15T05:02:29.138Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a4/45c35ccf6e1c65afc23a069f50e2c66f46bd3798cbe0d680c12d12935caa/mypy-1.19.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdb12f69bcc02700c2b47e070238f42cb87f18c0bc1fc4cdb4fb2bc5fd7a3b8b", size = 12206730, upload-time = "2025-12-15T05:03:01.325Z" }, + { url = "https://files.pythonhosted.org/packages/05/bb/cdcf89678e26b187650512620eec8368fded4cfd99cfcb431e4cdfd19dec/mypy-1.19.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f859fb09d9583a985be9a493d5cfc5515b56b08f7447759a0c5deaf68d80506e", size = 12724581, upload-time = "2025-12-15T05:03:20.087Z" }, + { url = "https://files.pythonhosted.org/packages/d1/32/dd260d52babf67bad8e6770f8e1102021877ce0edea106e72df5626bb0ec/mypy-1.19.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9a6538e0415310aad77cb94004ca6482330fece18036b5f360b62c45814c4ef", size = 13616252, upload-time = "2025-12-15T05:02:49.036Z" }, + { url = "https://files.pythonhosted.org/packages/71/d0/5e60a9d2e3bd48432ae2b454b7ef2b62a960ab51292b1eda2a95edd78198/mypy-1.19.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:da4869fc5e7f62a88f3fe0b5c919d1d9f7ea3cef92d3689de2823fd27e40aa75", size = 13840848, upload-time = "2025-12-15T05:02:55.95Z" }, + { url = "https://files.pythonhosted.org/packages/98/76/d32051fa65ecf6cc8c6610956473abdc9b4c43301107476ac03559507843/mypy-1.19.1-cp313-cp313-win_amd64.whl", hash = "sha256:016f2246209095e8eda7538944daa1d60e1e8134d98983b9fc1e92c1fc0cb8dd", size = 10135510, upload-time = "2025-12-15T05:02:58.438Z" }, + { url = "https://files.pythonhosted.org/packages/de/eb/b83e75f4c820c4247a58580ef86fcd35165028f191e7e1ba57128c52782d/mypy-1.19.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06e6170bd5836770e8104c8fdd58e5e725cfeb309f0a6c681a811f557e97eac1", size = 13199744, upload-time = "2025-12-15T05:03:30.823Z" }, + { url = "https://files.pythonhosted.org/packages/94/28/52785ab7bfa165f87fcbb61547a93f98bb20e7f82f90f165a1f69bce7b3d/mypy-1.19.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:804bd67b8054a85447c8954215a906d6eff9cabeabe493fb6334b24f4bfff718", size = 12215815, upload-time = "2025-12-15T05:02:42.323Z" }, + { url = "https://files.pythonhosted.org/packages/0a/c6/bdd60774a0dbfb05122e3e925f2e9e846c009e479dcec4821dad881f5b52/mypy-1.19.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21761006a7f497cb0d4de3d8ef4ca70532256688b0523eee02baf9eec895e27b", size = 12740047, upload-time = "2025-12-15T05:03:33.168Z" }, + { url = "https://files.pythonhosted.org/packages/32/2a/66ba933fe6c76bd40d1fe916a83f04fed253152f451a877520b3c4a5e41e/mypy-1.19.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:28902ee51f12e0f19e1e16fbe2f8f06b6637f482c459dd393efddd0ec7f82045", size = 13601998, upload-time = "2025-12-15T05:03:13.056Z" }, + { url = "https://files.pythonhosted.org/packages/e3/da/5055c63e377c5c2418760411fd6a63ee2b96cf95397259038756c042574f/mypy-1.19.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:481daf36a4c443332e2ae9c137dfee878fcea781a2e3f895d54bd3002a900957", size = 13807476, upload-time = "2025-12-15T05:03:17.977Z" }, + { url = "https://files.pythonhosted.org/packages/cd/09/4ebd873390a063176f06b0dbf1f7783dd87bd120eae7727fa4ae4179b685/mypy-1.19.1-cp314-cp314-win_amd64.whl", hash = "sha256:8bb5c6f6d043655e055be9b542aa5f3bdd30e4f3589163e85f93f3640060509f", size = 10281872, upload-time = "2025-12-15T05:03:05.549Z" }, + { url = "https://files.pythonhosted.org/packages/b5/f7/88436084550ca9af5e610fa45286be04c3b63374df3e021c762fe8c4369f/mypy-1.19.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7bcfc336a03a1aaa26dfce9fff3e287a3ba99872a157561cbfcebe67c13308e3", size = 13102606, upload-time = "2025-12-15T05:02:46.833Z" }, + { url = "https://files.pythonhosted.org/packages/ca/a5/43dfad311a734b48a752790571fd9e12d61893849a01bff346a54011957f/mypy-1.19.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b7951a701c07ea584c4fe327834b92a30825514c868b1f69c30445093fdd9d5a", size = 12164496, upload-time = "2025-12-15T05:03:41.947Z" }, + { url = "https://files.pythonhosted.org/packages/88/f0/efbfa391395cce2f2771f937e0620cfd185ec88f2b9cd88711028a768e96/mypy-1.19.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b13cfdd6c87fc3efb69ea4ec18ef79c74c3f98b4e5498ca9b85ab3b2c2329a67", size = 12772068, upload-time = "2025-12-15T05:02:53.689Z" }, + { url = "https://files.pythonhosted.org/packages/25/05/58b3ba28f5aed10479e899a12d2120d582ba9fa6288851b20bf1c32cbb4f/mypy-1.19.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f28f99c824ecebcdaa2e55d82953e38ff60ee5ec938476796636b86afa3956e", size = 13520385, upload-time = "2025-12-15T05:02:38.328Z" }, + { url = "https://files.pythonhosted.org/packages/c5/a0/c006ccaff50b31e542ae69b92fe7e2f55d99fba3a55e01067dd564325f85/mypy-1.19.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c608937067d2fc5a4dd1a5ce92fd9e1398691b8c5d012d66e1ddd430e9244376", size = 13796221, upload-time = "2025-12-15T05:03:22.147Z" }, + { url = "https://files.pythonhosted.org/packages/b2/ff/8bdb051cd710f01b880472241bd36b3f817a8e1c5d5540d0b761675b6de2/mypy-1.19.1-cp39-cp39-win_amd64.whl", hash = "sha256:409088884802d511ee52ca067707b90c883426bd95514e8cfda8281dc2effe24", size = 10055456, upload-time = "2025-12-15T05:03:35.169Z" }, + { url = "https://files.pythonhosted.org/packages/8d/f4/4ce9a05ce5ded1de3ec1c1d96cf9f9504a04e54ce0ed55cfa38619a32b8d/mypy-1.19.1-py3-none-any.whl", hash = "sha256:f1235f5ea01b7db5468d53ece6aaddf1ad0b88d9e7462b86ef96fe04995d7247", size = 2471239, upload-time = "2025-12-15T05:03:07.248Z" }, +] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, +] + +[[package]] +name = "packaging" +version = "26.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, +] + +[[package]] +name = "pathspec" +version = "1.0.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fa/36/e27608899f9b8d4dff0617b2d9ab17ca5608956ca44461ac14ac48b44015/pathspec-1.0.4.tar.gz", hash = "sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645", size = 131200, upload-time = "2026-01-27T03:59:46.938Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723", size = 55206, upload-time = "2026-01-27T03:59:45.137Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "pygments" +version = "2.19.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, +] + +[[package]] +name = "pytest" +version = "8.4.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +dependencies = [ + { name = "colorama", marker = "python_full_version < '3.10' and sys_platform == 'win32'" }, + { name = "exceptiongroup", marker = "python_full_version < '3.10'" }, + { name = "iniconfig", version = "2.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "packaging", marker = "python_full_version < '3.10'" }, + { name = "pluggy", marker = "python_full_version < '3.10'" }, + { name = "pygments", marker = "python_full_version < '3.10'" }, + { name = "tomli", marker = "python_full_version < '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" }, +] + +[[package]] +name = "pytest" +version = "9.0.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.10'", +] +dependencies = [ + { name = "colorama", marker = "python_full_version >= '3.10' and sys_platform == 'win32'" }, + { name = "exceptiongroup", marker = "python_full_version == '3.10.*'" }, + { name = "iniconfig", version = "2.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "packaging", marker = "python_full_version >= '3.10'" }, + { name = "pluggy", marker = "python_full_version >= '3.10'" }, + { name = "pygments", marker = "python_full_version >= '3.10'" }, + { name = "tomli", marker = "python_full_version == '3.10.*'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" }, +] + +[[package]] +name = "ruff" +version = "0.15.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/06/04/eab13a954e763b0606f460443fcbf6bb5a0faf06890ea3754ff16523dce5/ruff-0.15.2.tar.gz", hash = "sha256:14b965afee0969e68bb871eba625343b8673375f457af4abe98553e8bbb98342", size = 4558148, upload-time = "2026-02-19T22:32:20.271Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2f/70/3a4dc6d09b13cb3e695f28307e5d889b2e1a66b7af9c5e257e796695b0e6/ruff-0.15.2-py3-none-linux_armv6l.whl", hash = "sha256:120691a6fdae2f16d65435648160f5b81a9625288f75544dc40637436b5d3c0d", size = 10430565, upload-time = "2026-02-19T22:32:41.824Z" }, + { url = "https://files.pythonhosted.org/packages/71/0b/bb8457b56185ece1305c666dc895832946d24055be90692381c31d57466d/ruff-0.15.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:a89056d831256099658b6bba4037ac6dd06f49d194199215befe2bb10457ea5e", size = 10820354, upload-time = "2026-02-19T22:32:07.366Z" }, + { url = "https://files.pythonhosted.org/packages/2d/c1/e0532d7f9c9e0b14c46f61b14afd563298b8b83f337b6789ddd987e46121/ruff-0.15.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:e36dee3a64be0ebd23c86ffa3aa3fd3ac9a712ff295e192243f814a830b6bd87", size = 10170767, upload-time = "2026-02-19T22:32:13.188Z" }, + { url = "https://files.pythonhosted.org/packages/47/e8/da1aa341d3af017a21c7a62fb5ec31d4e7ad0a93ab80e3a508316efbcb23/ruff-0.15.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9fb47b6d9764677f8c0a193c0943ce9a05d6763523f132325af8a858eadc2b9", size = 10529591, upload-time = "2026-02-19T22:32:02.547Z" }, + { url = "https://files.pythonhosted.org/packages/93/74/184fbf38e9f3510231fbc5e437e808f0b48c42d1df9434b208821efcd8d6/ruff-0.15.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f376990f9d0d6442ea9014b19621d8f2aaf2b8e39fdbfc79220b7f0c596c9b80", size = 10260771, upload-time = "2026-02-19T22:32:36.938Z" }, + { url = "https://files.pythonhosted.org/packages/05/ac/605c20b8e059a0bc4b42360414baa4892ff278cec1c91fff4be0dceedefd/ruff-0.15.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2dcc987551952d73cbf5c88d9fdee815618d497e4df86cd4c4824cc59d5dd75f", size = 11045791, upload-time = "2026-02-19T22:32:31.642Z" }, + { url = "https://files.pythonhosted.org/packages/fd/52/db6e419908f45a894924d410ac77d64bdd98ff86901d833364251bd08e22/ruff-0.15.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:42a47fd785cbe8c01b9ff45031af875d101b040ad8f4de7bbb716487c74c9a77", size = 11879271, upload-time = "2026-02-19T22:32:29.305Z" }, + { url = "https://files.pythonhosted.org/packages/3e/d8/7992b18f2008bdc9231d0f10b16df7dda964dbf639e2b8b4c1b4e91b83af/ruff-0.15.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cbe9f49354866e575b4c6943856989f966421870e85cd2ac94dccb0a9dcb2fea", size = 11303707, upload-time = "2026-02-19T22:32:22.492Z" }, + { url = "https://files.pythonhosted.org/packages/d7/02/849b46184bcfdd4b64cde61752cc9a146c54759ed036edd11857e9b8443b/ruff-0.15.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7a672c82b5f9887576087d97be5ce439f04bbaf548ee987b92d3a7dede41d3a", size = 11149151, upload-time = "2026-02-19T22:32:44.234Z" }, + { url = "https://files.pythonhosted.org/packages/70/04/f5284e388bab60d1d3b99614a5a9aeb03e0f333847e2429bebd2aaa1feec/ruff-0.15.2-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:72ecc64f46f7019e2bcc3cdc05d4a7da958b629a5ab7033195e11a438403d956", size = 11091132, upload-time = "2026-02-19T22:32:24.691Z" }, + { url = "https://files.pythonhosted.org/packages/fa/ae/88d844a21110e14d92cf73d57363fab59b727ebeabe78009b9ccb23500af/ruff-0.15.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:8dcf243b15b561c655c1ef2f2b0050e5d50db37fe90115507f6ff37d865dc8b4", size = 10504717, upload-time = "2026-02-19T22:32:26.75Z" }, + { url = "https://files.pythonhosted.org/packages/64/27/867076a6ada7f2b9c8292884ab44d08fd2ba71bd2b5364d4136f3cd537e1/ruff-0.15.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:dab6941c862c05739774677c6273166d2510d254dac0695c0e3f5efa1b5585de", size = 10263122, upload-time = "2026-02-19T22:32:10.036Z" }, + { url = "https://files.pythonhosted.org/packages/e7/ef/faf9321d550f8ebf0c6373696e70d1758e20ccdc3951ad7af00c0956be7c/ruff-0.15.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1b9164f57fc36058e9a6806eb92af185b0697c9fe4c7c52caa431c6554521e5c", size = 10735295, upload-time = "2026-02-19T22:32:39.227Z" }, + { url = "https://files.pythonhosted.org/packages/2f/55/e8089fec62e050ba84d71b70e7834b97709ca9b7aba10c1a0b196e493f97/ruff-0.15.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:80d24fcae24d42659db7e335b9e1531697a7102c19185b8dc4a028b952865fd8", size = 11241641, upload-time = "2026-02-19T22:32:34.617Z" }, + { url = "https://files.pythonhosted.org/packages/23/01/1c30526460f4d23222d0fabd5888868262fd0e2b71a00570ca26483cd993/ruff-0.15.2-py3-none-win32.whl", hash = "sha256:fd5ff9e5f519a7e1bd99cbe8daa324010a74f5e2ebc97c6242c08f26f3714f6f", size = 10507885, upload-time = "2026-02-19T22:32:15.635Z" }, + { url = "https://files.pythonhosted.org/packages/5c/10/3d18e3bbdf8fc50bbb4ac3cc45970aa5a9753c5cb51bf9ed9a3cd8b79fa3/ruff-0.15.2-py3-none-win_amd64.whl", hash = "sha256:d20014e3dfa400f3ff84830dfb5755ece2de45ab62ecea4af6b7262d0fb4f7c5", size = 11623725, upload-time = "2026-02-19T22:32:04.947Z" }, + { url = "https://files.pythonhosted.org/packages/6d/78/097c0798b1dab9f8affe73da9642bb4500e098cb27fd8dc9724816ac747b/ruff-0.15.2-py3-none-win_arm64.whl", hash = "sha256:cabddc5822acdc8f7b5527b36ceac55cc51eec7b1946e60181de8fe83ca8876e", size = 10941649, upload-time = "2026-02-19T22:32:18.108Z" }, +] + +[[package]] +name = "tomli" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/30/31573e9457673ab10aa432461bee537ce6cef177667deca369efb79df071/tomli-2.4.0.tar.gz", hash = "sha256:aa89c3f6c277dd275d8e243ad24f3b5e701491a860d5121f2cdd399fbb31fc9c", size = 17477, upload-time = "2026-01-11T11:22:38.165Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/d9/3dc2289e1f3b32eb19b9785b6a006b28ee99acb37d1d47f78d4c10e28bf8/tomli-2.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b5ef256a3fd497d4973c11bf142e9ed78b150d36f5773f1ca6088c230ffc5867", size = 153663, upload-time = "2026-01-11T11:21:45.27Z" }, + { url = "https://files.pythonhosted.org/packages/51/32/ef9f6845e6b9ca392cd3f64f9ec185cc6f09f0a2df3db08cbe8809d1d435/tomli-2.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5572e41282d5268eb09a697c89a7bee84fae66511f87533a6f88bd2f7b652da9", size = 148469, upload-time = "2026-01-11T11:21:46.873Z" }, + { url = "https://files.pythonhosted.org/packages/d6/c2/506e44cce89a8b1b1e047d64bd495c22c9f71f21e05f380f1a950dd9c217/tomli-2.4.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:551e321c6ba03b55676970b47cb1b73f14a0a4dce6a3e1a9458fd6d921d72e95", size = 236039, upload-time = "2026-01-11T11:21:48.503Z" }, + { url = "https://files.pythonhosted.org/packages/b3/40/e1b65986dbc861b7e986e8ec394598187fa8aee85b1650b01dd925ca0be8/tomli-2.4.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e3f639a7a8f10069d0e15408c0b96a2a828cfdec6fca05296ebcdcc28ca7c76", size = 243007, upload-time = "2026-01-11T11:21:49.456Z" }, + { url = "https://files.pythonhosted.org/packages/9c/6f/6e39ce66b58a5b7ae572a0f4352ff40c71e8573633deda43f6a379d56b3e/tomli-2.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1b168f2731796b045128c45982d3a4874057626da0e2ef1fdd722848b741361d", size = 240875, upload-time = "2026-01-11T11:21:50.755Z" }, + { url = "https://files.pythonhosted.org/packages/aa/ad/cb089cb190487caa80204d503c7fd0f4d443f90b95cf4ef5cf5aa0f439b0/tomli-2.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:133e93646ec4300d651839d382d63edff11d8978be23da4cc106f5a18b7d0576", size = 246271, upload-time = "2026-01-11T11:21:51.81Z" }, + { url = "https://files.pythonhosted.org/packages/0b/63/69125220e47fd7a3a27fd0de0c6398c89432fec41bc739823bcc66506af6/tomli-2.4.0-cp311-cp311-win32.whl", hash = "sha256:b6c78bdf37764092d369722d9946cb65b8767bfa4110f902a1b2542d8d173c8a", size = 96770, upload-time = "2026-01-11T11:21:52.647Z" }, + { url = "https://files.pythonhosted.org/packages/1e/0d/a22bb6c83f83386b0008425a6cd1fa1c14b5f3dd4bad05e98cf3dbbf4a64/tomli-2.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:d3d1654e11d724760cdb37a3d7691f0be9db5fbdaef59c9f532aabf87006dbaa", size = 107626, upload-time = "2026-01-11T11:21:53.459Z" }, + { url = "https://files.pythonhosted.org/packages/2f/6d/77be674a3485e75cacbf2ddba2b146911477bd887dda9d8c9dfb2f15e871/tomli-2.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:cae9c19ed12d4e8f3ebf46d1a75090e4c0dc16271c5bce1c833ac168f08fb614", size = 94842, upload-time = "2026-01-11T11:21:54.831Z" }, + { url = "https://files.pythonhosted.org/packages/3c/43/7389a1869f2f26dba52404e1ef13b4784b6b37dac93bac53457e3ff24ca3/tomli-2.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:920b1de295e72887bafa3ad9f7a792f811847d57ea6b1215154030cf131f16b1", size = 154894, upload-time = "2026-01-11T11:21:56.07Z" }, + { url = "https://files.pythonhosted.org/packages/e9/05/2f9bf110b5294132b2edf13fe6ca6ae456204f3d749f623307cbb7a946f2/tomli-2.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d6d9a4aee98fac3eab4952ad1d73aee87359452d1c086b5ceb43ed02ddb16b8", size = 149053, upload-time = "2026-01-11T11:21:57.467Z" }, + { url = "https://files.pythonhosted.org/packages/e8/41/1eda3ca1abc6f6154a8db4d714a4d35c4ad90adc0bcf700657291593fbf3/tomli-2.4.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36b9d05b51e65b254ea6c2585b59d2c4cb91c8a3d91d0ed0f17591a29aaea54a", size = 243481, upload-time = "2026-01-11T11:21:58.661Z" }, + { url = "https://files.pythonhosted.org/packages/d2/6d/02ff5ab6c8868b41e7d4b987ce2b5f6a51d3335a70aa144edd999e055a01/tomli-2.4.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c8a885b370751837c029ef9bc014f27d80840e48bac415f3412e6593bbc18c1", size = 251720, upload-time = "2026-01-11T11:22:00.178Z" }, + { url = "https://files.pythonhosted.org/packages/7b/57/0405c59a909c45d5b6f146107c6d997825aa87568b042042f7a9c0afed34/tomli-2.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8768715ffc41f0008abe25d808c20c3d990f42b6e2e58305d5da280ae7d1fa3b", size = 247014, upload-time = "2026-01-11T11:22:01.238Z" }, + { url = "https://files.pythonhosted.org/packages/2c/0e/2e37568edd944b4165735687cbaf2fe3648129e440c26d02223672ee0630/tomli-2.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b438885858efd5be02a9a133caf5812b8776ee0c969fea02c45e8e3f296ba51", size = 251820, upload-time = "2026-01-11T11:22:02.727Z" }, + { url = "https://files.pythonhosted.org/packages/5a/1c/ee3b707fdac82aeeb92d1a113f803cf6d0f37bdca0849cb489553e1f417a/tomli-2.4.0-cp312-cp312-win32.whl", hash = "sha256:0408e3de5ec77cc7f81960c362543cbbd91ef883e3138e81b729fc3eea5b9729", size = 97712, upload-time = "2026-01-11T11:22:03.777Z" }, + { url = "https://files.pythonhosted.org/packages/69/13/c07a9177d0b3bab7913299b9278845fc6eaaca14a02667c6be0b0a2270c8/tomli-2.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:685306e2cc7da35be4ee914fd34ab801a6acacb061b6a7abca922aaf9ad368da", size = 108296, upload-time = "2026-01-11T11:22:04.86Z" }, + { url = "https://files.pythonhosted.org/packages/18/27/e267a60bbeeee343bcc279bb9e8fbed0cbe224bc7b2a3dc2975f22809a09/tomli-2.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:5aa48d7c2356055feef06a43611fc401a07337d5b006be13a30f6c58f869e3c3", size = 94553, upload-time = "2026-01-11T11:22:05.854Z" }, + { url = "https://files.pythonhosted.org/packages/34/91/7f65f9809f2936e1f4ce6268ae1903074563603b2a2bd969ebbda802744f/tomli-2.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84d081fbc252d1b6a982e1870660e7330fb8f90f676f6e78b052ad4e64714bf0", size = 154915, upload-time = "2026-01-11T11:22:06.703Z" }, + { url = "https://files.pythonhosted.org/packages/20/aa/64dd73a5a849c2e8f216b755599c511badde80e91e9bc2271baa7b2cdbb1/tomli-2.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9a08144fa4cba33db5255f9b74f0b89888622109bd2776148f2597447f92a94e", size = 149038, upload-time = "2026-01-11T11:22:07.56Z" }, + { url = "https://files.pythonhosted.org/packages/9e/8a/6d38870bd3d52c8d1505ce054469a73f73a0fe62c0eaf5dddf61447e32fa/tomli-2.4.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c73add4bb52a206fd0c0723432db123c0c75c280cbd67174dd9d2db228ebb1b4", size = 242245, upload-time = "2026-01-11T11:22:08.344Z" }, + { url = "https://files.pythonhosted.org/packages/59/bb/8002fadefb64ab2669e5b977df3f5e444febea60e717e755b38bb7c41029/tomli-2.4.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fb2945cbe303b1419e2706e711b7113da57b7db31ee378d08712d678a34e51e", size = 250335, upload-time = "2026-01-11T11:22:09.951Z" }, + { url = "https://files.pythonhosted.org/packages/a5/3d/4cdb6f791682b2ea916af2de96121b3cb1284d7c203d97d92d6003e91c8d/tomli-2.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bbb1b10aa643d973366dc2cb1ad94f99c1726a02343d43cbc011edbfac579e7c", size = 245962, upload-time = "2026-01-11T11:22:11.27Z" }, + { url = "https://files.pythonhosted.org/packages/f2/4a/5f25789f9a460bd858ba9756ff52d0830d825b458e13f754952dd15fb7bb/tomli-2.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4cbcb367d44a1f0c2be408758b43e1ffb5308abe0ea222897d6bfc8e8281ef2f", size = 250396, upload-time = "2026-01-11T11:22:12.325Z" }, + { url = "https://files.pythonhosted.org/packages/aa/2f/b73a36fea58dfa08e8b3a268750e6853a6aac2a349241a905ebd86f3047a/tomli-2.4.0-cp313-cp313-win32.whl", hash = "sha256:7d49c66a7d5e56ac959cb6fc583aff0651094ec071ba9ad43df785abc2320d86", size = 97530, upload-time = "2026-01-11T11:22:13.865Z" }, + { url = "https://files.pythonhosted.org/packages/3b/af/ca18c134b5d75de7e8dc551c5234eaba2e8e951f6b30139599b53de9c187/tomli-2.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:3cf226acb51d8f1c394c1b310e0e0e61fecdd7adcb78d01e294ac297dd2e7f87", size = 108227, upload-time = "2026-01-11T11:22:15.224Z" }, + { url = "https://files.pythonhosted.org/packages/22/c3/b386b832f209fee8073c8138ec50f27b4460db2fdae9ffe022df89a57f9b/tomli-2.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:d20b797a5c1ad80c516e41bc1fb0443ddb5006e9aaa7bda2d71978346aeb9132", size = 94748, upload-time = "2026-01-11T11:22:16.009Z" }, + { url = "https://files.pythonhosted.org/packages/f3/c4/84047a97eb1004418bc10bdbcfebda209fca6338002eba2dc27cc6d13563/tomli-2.4.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:26ab906a1eb794cd4e103691daa23d95c6919cc2fa9160000ac02370cc9dd3f6", size = 154725, upload-time = "2026-01-11T11:22:17.269Z" }, + { url = "https://files.pythonhosted.org/packages/a8/5d/d39038e646060b9d76274078cddf146ced86dc2b9e8bbf737ad5983609a0/tomli-2.4.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:20cedb4ee43278bc4f2fee6cb50daec836959aadaf948db5172e776dd3d993fc", size = 148901, upload-time = "2026-01-11T11:22:18.287Z" }, + { url = "https://files.pythonhosted.org/packages/73/e5/383be1724cb30f4ce44983d249645684a48c435e1cd4f8b5cded8a816d3c/tomli-2.4.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:39b0b5d1b6dd03684b3fb276407ebed7090bbec989fa55838c98560c01113b66", size = 243375, upload-time = "2026-01-11T11:22:19.154Z" }, + { url = "https://files.pythonhosted.org/packages/31/f0/bea80c17971c8d16d3cc109dc3585b0f2ce1036b5f4a8a183789023574f2/tomli-2.4.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a26d7ff68dfdb9f87a016ecfd1e1c2bacbe3108f4e0f8bcd2228ef9a766c787d", size = 250639, upload-time = "2026-01-11T11:22:20.168Z" }, + { url = "https://files.pythonhosted.org/packages/2c/8f/2853c36abbb7608e3f945d8a74e32ed3a74ee3a1f468f1ffc7d1cb3abba6/tomli-2.4.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:20ffd184fb1df76a66e34bd1b36b4a4641bd2b82954befa32fe8163e79f1a702", size = 246897, upload-time = "2026-01-11T11:22:21.544Z" }, + { url = "https://files.pythonhosted.org/packages/49/f0/6c05e3196ed5337b9fe7ea003e95fd3819a840b7a0f2bf5a408ef1dad8ed/tomli-2.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:75c2f8bbddf170e8effc98f5e9084a8751f8174ea6ccf4fca5398436e0320bc8", size = 254697, upload-time = "2026-01-11T11:22:23.058Z" }, + { url = "https://files.pythonhosted.org/packages/f3/f5/2922ef29c9f2951883525def7429967fc4d8208494e5ab524234f06b688b/tomli-2.4.0-cp314-cp314-win32.whl", hash = "sha256:31d556d079d72db7c584c0627ff3a24c5d3fb4f730221d3444f3efb1b2514776", size = 98567, upload-time = "2026-01-11T11:22:24.033Z" }, + { url = "https://files.pythonhosted.org/packages/7b/31/22b52e2e06dd2a5fdbc3ee73226d763b184ff21fc24e20316a44ccc4d96b/tomli-2.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:43e685b9b2341681907759cf3a04e14d7104b3580f808cfde1dfdb60ada85475", size = 108556, upload-time = "2026-01-11T11:22:25.378Z" }, + { url = "https://files.pythonhosted.org/packages/48/3d/5058dff3255a3d01b705413f64f4306a141a8fd7a251e5a495e3f192a998/tomli-2.4.0-cp314-cp314-win_arm64.whl", hash = "sha256:3d895d56bd3f82ddd6faaff993c275efc2ff38e52322ea264122d72729dca2b2", size = 96014, upload-time = "2026-01-11T11:22:26.138Z" }, + { url = "https://files.pythonhosted.org/packages/b8/4e/75dab8586e268424202d3a1997ef6014919c941b50642a1682df43204c22/tomli-2.4.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:5b5807f3999fb66776dbce568cc9a828544244a8eb84b84b9bafc080c99597b9", size = 163339, upload-time = "2026-01-11T11:22:27.143Z" }, + { url = "https://files.pythonhosted.org/packages/06/e3/b904d9ab1016829a776d97f163f183a48be6a4deb87304d1e0116a349519/tomli-2.4.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c084ad935abe686bd9c898e62a02a19abfc9760b5a79bc29644463eaf2840cb0", size = 159490, upload-time = "2026-01-11T11:22:28.399Z" }, + { url = "https://files.pythonhosted.org/packages/e3/5a/fc3622c8b1ad823e8ea98a35e3c632ee316d48f66f80f9708ceb4f2a0322/tomli-2.4.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f2e3955efea4d1cfbcb87bc321e00dc08d2bcb737fd1d5e398af111d86db5df", size = 269398, upload-time = "2026-01-11T11:22:29.345Z" }, + { url = "https://files.pythonhosted.org/packages/fd/33/62bd6152c8bdd4c305ad9faca48f51d3acb2df1f8791b1477d46ff86e7f8/tomli-2.4.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e0fe8a0b8312acf3a88077a0802565cb09ee34107813bba1c7cd591fa6cfc8d", size = 276515, upload-time = "2026-01-11T11:22:30.327Z" }, + { url = "https://files.pythonhosted.org/packages/4b/ff/ae53619499f5235ee4211e62a8d7982ba9e439a0fb4f2f351a93d67c1dd2/tomli-2.4.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:413540dce94673591859c4c6f794dfeaa845e98bf35d72ed59636f869ef9f86f", size = 273806, upload-time = "2026-01-11T11:22:32.56Z" }, + { url = "https://files.pythonhosted.org/packages/47/71/cbca7787fa68d4d0a9f7072821980b39fbb1b6faeb5f5cf02f4a5559fa28/tomli-2.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0dc56fef0e2c1c470aeac5b6ca8cc7b640bb93e92d9803ddaf9ea03e198f5b0b", size = 281340, upload-time = "2026-01-11T11:22:33.505Z" }, + { url = "https://files.pythonhosted.org/packages/f5/00/d595c120963ad42474cf6ee7771ad0d0e8a49d0f01e29576ee9195d9ecdf/tomli-2.4.0-cp314-cp314t-win32.whl", hash = "sha256:d878f2a6707cc9d53a1be1414bbb419e629c3d6e67f69230217bb663e76b5087", size = 108106, upload-time = "2026-01-11T11:22:34.451Z" }, + { url = "https://files.pythonhosted.org/packages/de/69/9aa0c6a505c2f80e519b43764f8b4ba93b5a0bbd2d9a9de6e2b24271b9a5/tomli-2.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2add28aacc7425117ff6364fe9e06a183bb0251b03f986df0e78e974047571fd", size = 120504, upload-time = "2026-01-11T11:22:35.764Z" }, + { url = "https://files.pythonhosted.org/packages/b3/9f/f1668c281c58cfae01482f7114a4b88d345e4c140386241a1a24dcc9e7bc/tomli-2.4.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2b1e3b80e1d5e52e40e9b924ec43d81570f0e7d09d11081b797bc4692765a3d4", size = 99561, upload-time = "2026-01-11T11:22:36.624Z" }, + { url = "https://files.pythonhosted.org/packages/23/d1/136eb2cb77520a31e1f64cbae9d33ec6df0d78bdf4160398e86eec8a8754/tomli-2.4.0-py3-none-any.whl", hash = "sha256:1f776e7d669ebceb01dee46484485f43a4048746235e683bcdffacdf1fb4785a", size = 14477, upload-time = "2026-01-11T11:22:37.446Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +]