diff --git a/package/version b/package/version index 0214e6937..b0b5ddaf6 100644 --- a/package/version +++ b/package/version @@ -1 +1 @@ -0.1.156 +0.1.157 diff --git a/pykwasm/pyproject.toml b/pykwasm/pyproject.toml index 1ebfab55e..61f745a3c 100644 --- a/pykwasm/pyproject.toml +++ b/pykwasm/pyproject.toml @@ -4,13 +4,12 @@ build-backend = "hatchling.build" [project] name = "pykwasm" -version = "0.1.156" +version = "0.1.157" description = "" readme = "README.md" requires-python = "~=3.10" dependencies = [ - "kframework>=7.1.329", - "py-wasm@git+https://github.com/runtimeverification/py-wasm.git@0.3.1" + "kframework>=7.1.329" ] [[project.authors]] diff --git a/pykwasm/src/pykwasm/binary/__init__.py b/pykwasm/src/pykwasm/binary/__init__.py new file mode 100644 index 000000000..f0fe51e0c --- /dev/null +++ b/pykwasm/src/pykwasm/binary/__init__.py @@ -0,0 +1 @@ +from .module import parse_module diff --git a/pykwasm/src/pykwasm/binary/combinators.py b/pykwasm/src/pykwasm/binary/combinators.py new file mode 100644 index 000000000..0adc46731 --- /dev/null +++ b/pykwasm/src/pykwasm/binary/combinators.py @@ -0,0 +1,53 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from .integers import u32 +from .utils import WasmParseError, reset + +if TYPE_CHECKING: + from .utils import A, InputStream, Parser + + +def iterate(f: Parser[A], s: InputStream) -> list[A]: + results = [] + while True: + try: + results.append(f(s)) + except (WasmParseError, IndexError, ValueError): + break + return results + + +def sized(p: Parser[A], s: InputStream) -> A: + size = u32(s) + start_pos = s.tell() + res = p(s) + end_pos = s.tell() + if end_pos - start_pos != size: + raise WasmParseError('Size mismatch') + return res + + +def parse_n(p: Parser[A], n: int, s: InputStream) -> list[A]: + results = [] + for _ in range(n): + x = p(s) + results.append(x) + return results + + +def list_of(p: Parser[A], s: InputStream) -> list[A]: + n = u32(s) + return parse_n(p, n, s) + + +def either(ps: list[Parser[A]], s: InputStream) -> A: + for p in ps: + pos = s.tell() + try: + return p(s) + except WasmParseError: + reset(pos, s) + continue + raise WasmParseError('None of the alternatives succeeded') diff --git a/pykwasm/src/pykwasm/binary/floats.py b/pykwasm/src/pykwasm/binary/floats.py new file mode 100644 index 000000000..de1dd5359 --- /dev/null +++ b/pykwasm/src/pykwasm/binary/floats.py @@ -0,0 +1,21 @@ +from __future__ import annotations + +import struct +from typing import TYPE_CHECKING + +from .utils import read_bytes + +if TYPE_CHECKING: + from .utils import InputStream + + +def f32(s: InputStream) -> float: + bs = read_bytes(4, s) + f = struct.unpack(' float: + bs = read_bytes(8, s) + f = struct.unpack(' int: + return u32(s) + + +def funcidx(s: InputStream) -> int: + return u32(s) + + +def tableidx(s: InputStream) -> int: + return u32(s) + + +def memidx(s: InputStream) -> int: + return u32(s) + + +def globalidx(s: InputStream) -> int: + return u32(s) + + +def tagidx(s: InputStream) -> int: + return u32(s) + + +def elemidx(s: InputStream) -> int: + return u32(s) + + +def dataidx(s: InputStream) -> int: + return u32(s) + + +def localidx(s: InputStream) -> int: + return u32(s) + + +def labelidx(s: InputStream) -> int: + return u32(s) + + +def externidx(s: InputStream) -> KInner: + match read_byte(s): + case 0x00: + return wast.externidx_func(funcidx(s)) + case 0x01: + return wast.externidx_table(tableidx(s)) + case 0x02: + return wast.externidx_memory(memidx(s)) + case 0x03: + return wast.externidx_global(globalidx(s)) + case 0x04: + return wast.externidx_tag(tagidx(s)) + case x: + raise WasmParseError(f'Invalid externidx descriptor: {x}') diff --git a/pykwasm/src/pykwasm/binary/instructions.py b/pykwasm/src/pykwasm/binary/instructions.py new file mode 100644 index 000000000..c510e1f84 --- /dev/null +++ b/pykwasm/src/pykwasm/binary/instructions.py @@ -0,0 +1,546 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +import pykwasm.kwasm_ast as wast +from pykwasm.binary.combinators import list_of +from pykwasm.binary.floats import f32, f64 +from pykwasm.binary.indices import elemidx, funcidx, globalidx, labelidx, localidx, memidx, tableidx, typeidx +from pykwasm.binary.integers import i32, i64, u32, u64 +from pykwasm.binary.types import heaptype, valtype +from pykwasm.binary.utils import WasmParseError, expect_bytes, peek_byte, read_byte, skip + +if TYPE_CHECKING: + from pyk.kast.inner import KInner + + from pykwasm.binary.utils import InputStream + + +def instr_seq(terminator: int, s: InputStream) -> list[KInner]: + res: list[KInner] = [] + + while peek_byte(s) != terminator: + i = instr(s) + res.append(i) + + expect_bytes(bytes([terminator]), s) + return res + + +def instr(s: InputStream) -> KInner: + offset = s.tell() + i = _instr(s) + length = s.tell() - offset + return wast.INSTR_WITH_POS(i, offset, length) + + +def _instr(s: InputStream) -> KInner: + def blocktype(s: InputStream) -> KInner: + if peek_byte(s) == 0x40: + skip(1, s) + return wast.vec_type(wast.val_types([])) + + try: + t = valtype(s) + return wast.vec_type(wast.val_types([t])) + except WasmParseError as e: + # TODO handle the `blocktype ::= i:i33` case + raise WasmParseError('Could not parse blocktype') from e + + # returns memory index, alignment, offset + def memarg(s: InputStream) -> tuple[int, int, int]: + n = u32(s) + if n < 2**6: + m = u64(s) + return 0, n, m + elif n < 2**7: + x = memidx(s) + m = u64(s) + return x, n - 2**6, m + else: + raise WasmParseError(f'Invalid memarg. n: {n}') + + opcode = read_byte(s) + # '0xFC' is a special opcode prefix shared by several instructions. + # These instructions are distinguished by a u32 value following the opcode. + additional_value: int | None = None + if opcode == 0xFC: + try: + additional_value = u32(s) + except Exception as e: + raise WasmParseError('Cannot parse opcode, expected u32 after 0xFC') from e + + match opcode: + # Parametric instructions + case 0x00: + return wast.UNREACHABLE + case 0x01: + return wast.NOP + case 0x1A: + return wast.DROP + case 0x1B: + return wast.SELECT + case 0x1C: + # TODO handle ts + ts = list_of(valtype, s) # noqa F841 + return wast.SELECT + + # Control instructions + case 0x02: + bt = blocktype(s) + ins = instr_seq(0x0B, s) + return wast.BLOCK(bt, wast.instrs(ins)) + case 0x03: + bt = blocktype(s) + ins = instr_seq(0x0B, s) + return wast.LOOP(bt, wast.instrs(ins)) + case 0x04: + bt = blocktype(s) + in1 = instr_seq(0x05, s) + in2 = instr_seq(0x0B, s) + return wast.IF(bt, wast.instrs(in1), wast.instrs(in2)) + case 0x0C: + l = labelidx(s) + return wast.BR(l) + case 0x0D: + l = labelidx(s) + return wast.BR_IF(l) + case 0x0E: + ls = list_of(labelidx, s) + ln = labelidx(s) + return wast.BR_TABLE(tuple(ls), ln) + case 0x0F: + return wast.RETURN + case 0x10: + x = funcidx(s) + return wast.CALL(x) + case 0x11: + y = typeidx(s) + x = tableidx(s) + return wast.CALL_INDIRECT(x, y) + + # Variable instructions + case 0x20: + x = localidx(s) + return wast.GET_LOCAL(x) + case 0x21: + x = localidx(s) + return wast.SET_LOCAL(x) + case 0x22: + x = localidx(s) + return wast.TEE_LOCAL(x) + case 0x23: + x = globalidx(s) + return wast.GET_GLOBAL(x) + case 0x24: + x = globalidx(s) + return wast.SET_GLOBAL(x) + + # Table instructions + case 0x25: + x = tableidx(s) + return wast.TABLE_GET(x) + case 0x26: + x = tableidx(s) + return wast.TABLE_SET(x) + case 0xFC if additional_value == 12: + y = elemidx(s) + x = tableidx(s) + return wast.TABLE_INIT(x, y) + case 0xFC if additional_value == 13: + x = elemidx(s) + return wast.ELEM_DROP(x) + case 0xFC if additional_value == 14: + x1 = tableidx(s) + x2 = tableidx(s) + return wast.TABLE_COPY(x1, x2) + case 0xFC if additional_value == 15: + x = tableidx(s) + return wast.TABLE_GROW(x) + case 0xFC if additional_value == 16: + x = tableidx(s) + return wast.TABLE_SIZE(x) + case 0xFC if additional_value == 17: + x = tableidx(s) + return wast.TABLE_FILL(x) + + # Memory instructions + # TODO handle memory index and alignment + case 0x28: + mx, align, offset = memarg(s) # noqa F841 + return wast.I32_LOAD(offset) + case 0x29: + mx, align, offset = memarg(s) # noqa F841 + return wast.I64_LOAD(offset) + case 0x2A: + mx, align, offset = memarg(s) # noqa F841 + return wast.F32_LOAD(offset) + case 0x2B: + mx, align, offset = memarg(s) # noqa F841 + return wast.F64_LOAD(offset) + case 0x2C: + mx, align, offset = memarg(s) # noqa F841 + return wast.I32_LOAD8_S(offset) + case 0x2D: + mx, align, offset = memarg(s) # noqa F841 + return wast.I32_LOAD8_U(offset) + case 0x2E: + mx, align, offset = memarg(s) # noqa F841 + return wast.I32_LOAD16_S(offset) + case 0x2F: + mx, align, offset = memarg(s) # noqa F841 + return wast.I32_LOAD16_U(offset) + case 0x30: + mx, align, offset = memarg(s) # noqa F841 + return wast.I64_LOAD8_S(offset) + case 0x31: + mx, align, offset = memarg(s) # noqa F841 + return wast.I64_LOAD8_U(offset) + case 0x32: + mx, align, offset = memarg(s) # noqa F841 + return wast.I64_LOAD16_S(offset) + case 0x33: + mx, align, offset = memarg(s) # noqa F841 + return wast.I64_LOAD16_U(offset) + case 0x34: + mx, align, offset = memarg(s) # noqa F841 + return wast.I64_LOAD32_S(offset) + case 0x35: + mx, align, offset = memarg(s) # noqa F841 + return wast.I64_LOAD32_U(offset) + case 0x36: + mx, align, offset = memarg(s) # noqa F841 + return wast.I32_STORE(offset) + case 0x37: + mx, align, offset = memarg(s) # noqa F841 + return wast.I64_STORE(offset) + case 0x38: + mx, align, offset = memarg(s) # noqa F841 + return wast.F32_STORE(offset) + case 0x39: + mx, align, offset = memarg(s) # noqa F841 + return wast.F64_STORE(offset) + case 0x3A: + mx, align, offset = memarg(s) # noqa F841 + return wast.I32_STORE8(offset) + case 0x3B: + mx, align, offset = memarg(s) # noqa F841 + return wast.I32_STORE16(offset) + case 0x3C: + mx, align, offset = memarg(s) # noqa F841 + return wast.I64_STORE8(offset) + case 0x3D: + mx, align, offset = memarg(s) # noqa F841 + return wast.I64_STORE16(offset) + case 0x3E: + mx, align, offset = memarg(s) # noqa F841 + return wast.I64_STORE32(offset) + case 0x3F: + # TODO use memory index + _x = memidx(s) # noqa 841 + return wast.MEMORY_SIZE + case 0x40: + # TODO use memory index + _x = memidx(s) # noqa 841 + return wast.MEMORY_GROW + + # Reference instructions + case 0xD0: + ht = heaptype(s) + return wast.REF_NULL(ht) + case 0xD1: + return wast.REF_IS_NULL + case 0xD2: + x = funcidx(s) + return wast.REF_FUNC(x) + + # Numeric instructions + # Constants + case 0x41: + i = i32(s) + return wast.I32_CONST(i) + case 0x42: + i = i64(s) + return wast.I64_CONST(i) + case 0x43: + f = f32(s) + return wast.F32_CONST(f) + case 0x44: + f = f64(s) + return wast.F64_CONST(f) + + # i32 comparison + case 0x45: + return wast.I32_EQZ + case 0x46: + return wast.I32_EQ + case 0x47: + return wast.I32_NE + case 0x48: + return wast.I32_LT_S + case 0x49: + return wast.I32_LT_U + case 0x4A: + return wast.I32_GT_S + case 0x4B: + return wast.I32_GT_U + case 0x4C: + return wast.I32_LE_S + case 0x4D: + return wast.I32_LE_U + case 0x4E: + return wast.I32_GE_S + case 0x4F: + return wast.I32_GE_U + + # i64 comparison + case 0x50: + return wast.I64_EQZ + case 0x51: + return wast.I64_EQ + case 0x52: + return wast.I64_NE + case 0x53: + return wast.I64_LT_S + case 0x54: + return wast.I64_LT_U + case 0x55: + return wast.I64_GT_S + case 0x56: + return wast.I64_GT_U + case 0x57: + return wast.I64_LE_S + case 0x58: + return wast.I64_LE_U + case 0x59: + return wast.I64_GE_S + case 0x5A: + return wast.I64_GE_U + + # f32 comparison + case 0x5B: + return wast.F32_EQ + case 0x5C: + return wast.F32_NE + case 0x5D: + return wast.F32_LT + case 0x5E: + return wast.F32_GT + case 0x5F: + return wast.F32_LE + case 0x60: + return wast.F32_GE + + # f64 comparison + case 0x61: + return wast.F64_EQ + case 0x62: + return wast.F64_NE + case 0x63: + return wast.F64_LT + case 0x64: + return wast.F64_GT + case 0x65: + return wast.F64_LE + case 0x66: + return wast.F64_GE + + # i32 unary / binary + case 0x67: + return wast.I32_CLZ + case 0x68: + return wast.I32_CTZ + case 0x69: + return wast.I32_POPCNT + case 0x6A: + return wast.I32_ADD + case 0x6B: + return wast.I32_SUB + case 0x6C: + return wast.I32_MUL + case 0x6D: + return wast.I32_DIV_S + case 0x6E: + return wast.I32_DIV_U + case 0x6F: + return wast.I32_REM_S + case 0x70: + return wast.I32_REM_U + case 0x71: + return wast.I32_AND + case 0x72: + return wast.I32_OR + case 0x73: + return wast.I32_XOR + case 0x74: + return wast.I32_SHL + case 0x75: + return wast.I32_SHR_S + case 0x76: + return wast.I32_SHR_U + case 0x77: + return wast.I32_ROTL + case 0x78: + return wast.I32_ROTR + + # i64 unary / binary + case 0x79: + return wast.I64_CLZ + case 0x7A: + return wast.I64_CTZ + case 0x7B: + return wast.I64_POPCNT + case 0x7C: + return wast.I64_ADD + case 0x7D: + return wast.I64_SUB + case 0x7E: + return wast.I64_MUL + case 0x7F: + return wast.I64_DIV_S + case 0x80: + return wast.I64_DIV_U + case 0x81: + return wast.I64_REM_S + case 0x82: + return wast.I64_REM_U + case 0x83: + return wast.I64_AND + case 0x84: + return wast.I64_OR + case 0x85: + return wast.I64_XOR + case 0x86: + return wast.I64_SHL + case 0x87: + return wast.I64_SHR_S + case 0x88: + return wast.I64_SHR_U + case 0x89: + return wast.I64_ROTL + case 0x8A: + return wast.I64_ROTR + + # f32 unary / binary + case 0x8B: + return wast.F32_ABS + case 0x8C: + return wast.F32_NEG + case 0x8D: + return wast.F32_CEIL + case 0x8E: + return wast.F32_FLOOR + case 0x8F: + return wast.F32_TRUNC + case 0x90: + return wast.F32_NEAREST + case 0x91: + return wast.F32_SQRT + case 0x92: + return wast.F32_ADD + case 0x93: + return wast.F32_SUB + case 0x94: + return wast.F32_MUL + case 0x95: + return wast.F32_DIV + case 0x96: + return wast.F32_MIN + case 0x97: + return wast.F32_MAX + case 0x98: + return wast.F32_COPYSIGN + + # f64 unary / binary + case 0x99: + return wast.F64_ABS + case 0x9A: + return wast.F64_NEG + case 0x9B: + return wast.F64_CEIL + case 0x9C: + return wast.F64_FLOOR + case 0x9D: + return wast.F64_TRUNC + case 0x9E: + return wast.F64_NEAREST + case 0x9F: + return wast.F64_SQRT + case 0xA0: + return wast.F64_ADD + case 0xA1: + return wast.F64_SUB + case 0xA2: + return wast.F64_MUL + case 0xA3: + return wast.F64_DIV + case 0xA4: + return wast.F64_MIN + case 0xA5: + return wast.F64_MAX + case 0xA6: + return wast.F64_COPYSIGN + + # conversion instructions + case 0xA7: + return wast.I32_WRAP_I64 + case 0xA8: + return wast.I32_TRUNC_S_F32 + case 0xA9: + return wast.I32_TRUNC_U_F32 + case 0xAA: + return wast.I32_TRUNC_S_F64 + case 0xAB: + return wast.I32_TRUNC_U_F64 + case 0xAC: + return wast.I64_EXTEND_S_I32 + case 0xAD: + return wast.I64_EXTEND_U_I32 + case 0xAE: + return wast.I64_TRUNC_S_F32 + case 0xAF: + return wast.I64_TRUNC_U_F32 + case 0xB0: + return wast.I64_TRUNC_S_F64 + case 0xB1: + return wast.I64_TRUNC_U_F64 + case 0xB2: + return wast.F32_CONVERT_S_I32 + case 0xB3: + return wast.F32_CONVERT_U_I32 + case 0xB4: + return wast.F32_CONVERT_S_I64 + case 0xB5: + return wast.F32_CONVERT_U_I64 + case 0xB6: + return wast.F32_DEMOTE_F64 + case 0xB7: + return wast.F64_CONVERT_S_I32 + case 0xB8: + return wast.F64_CONVERT_U_I32 + case 0xB9: + return wast.F64_CONVERT_S_I64 + case 0xBA: + return wast.F64_CONVERT_U_I64 + case 0xBB: + return wast.F64_PROMOTE_F32 + + # sign-extension instructions + case 0xC0: + return wast.I32_EXTEND8_s + case 0xC1: + return wast.I32_EXTEND16_s + case 0xC2: + return wast.I64_EXTEND8_s + case 0xC3: + return wast.I64_EXTEND16_s + case 0xC4: + return wast.I64_EXTEND32_s + + # Handle unsupported opcodes + case _: + raise WasmParseError(f'Unsupported opcode: {opcode :#04x}, {additional_value}') + + +def expr(s: InputStream) -> list[KInner]: + return instr_seq(0x0B, s) diff --git a/pykwasm/src/pykwasm/binary/integers.py b/pykwasm/src/pykwasm/binary/integers.py new file mode 100644 index 000000000..e62f95a65 --- /dev/null +++ b/pykwasm/src/pykwasm/binary/integers.py @@ -0,0 +1,76 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from .utils import WasmParseError, read_byte + +if TYPE_CHECKING: + from .utils import InputStream + + +def parse_uint(bits: int, s: InputStream) -> int: + n = read_byte(s) + + # No continuation bit + if n < 2**7: + if n >= 2**bits: + raise WasmParseError(f'Value {n} exceeds u{bits} range') + return n + + # Has continuation bit + if bits <= 7: # Not enough bits remaining + raise WasmParseError(f'LEB128 encoding too long for u{bits}') + + m = parse_uint(bits - 7, s) + return (n - 2**7) + (2**7) * m + + +def parse_sint(bits: int, s: InputStream) -> int: + n = read_byte(s) + + # No continuation bit and 0 sign bit + if n < 2**6: + if n >= 2 ** (bits - 1): + raise WasmParseError(f'Value {n} exceeds s{bits} range') + return n + + # No continuation bit and 1 sign bit + if 2**6 <= n and n < 2**7: + signed_value = n - 2**7 + if signed_value < -(2 ** (bits - 1)): + raise WasmParseError(f'Value {signed_value} exceeds s{bits} range') + return n - 2**7 + + # Has continuation bit + if bits <= 7: # Not enough bits remaining + raise WasmParseError(f'LEB128 encoding too long for s{bits}') + + i = parse_sint(bits - 7, s) + return (2**7) * i + (n - 2**7) + + +def u32(s: InputStream) -> int: + return parse_uint(32, s) + + +def u64(s: InputStream) -> int: + return parse_uint(64, s) + + +def to_uninterpreted(bits: int, x: int) -> int: + if 0 <= x: + return x + return x + 2**bits + + +def parse_iint(bits: int, s: InputStream) -> int: + i = parse_sint(bits, s) + return to_uninterpreted(bits, i) + + +def i32(s: InputStream) -> int: + return parse_iint(32, s) + + +def i64(s: InputStream) -> int: + return parse_iint(64, s) diff --git a/pykwasm/src/pykwasm/binary/module.py b/pykwasm/src/pykwasm/binary/module.py new file mode 100644 index 000000000..4cb93eb47 --- /dev/null +++ b/pykwasm/src/pykwasm/binary/module.py @@ -0,0 +1,350 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from pyk.kast.inner import KApply, KLabel, KSort, KToken + +import pykwasm.kwasm_ast as wast +from pykwasm.binary.indices import externidx, funcidx, memidx, tableidx, typeidx +from pykwasm.binary.instructions import expr + +from .combinators import list_of, sized +from .integers import u32 +from .types import externtype_as_import_desc, globaltype, memtype, rectype, reftype, tabletype, valtype +from .utils import WasmEOFError, WasmParseError, peek_byte, peek_bytes, read_byte, read_bytes, skip + +if TYPE_CHECKING: + from pyk.kast.inner import KInner + + from .types import RecType + from .utils import A, InputStream, Parser + +MAGIC = b'\x00\x61\x73\x6d' +VERSION = b'\x01\x00\x00\x00' + +EMPTY_SECTION: KInner = wast.EMPTY_DEFNS + + +def parse_magic(s: InputStream) -> None: + try: + val = s.read(4) + assert val == MAGIC + except Exception as e: + raise WasmParseError('Could not read magic value') from e + + +def parse_version(s: InputStream) -> None: + try: + val = s.read(4) + assert val == VERSION + except Exception as e: + raise WasmParseError('Could not read version') from e + + +def parse_custom_section(s: InputStream) -> bytes: + try: + n = peek_byte(s) + except WasmEOFError: + return b'' + + if n != 0: + return b'' + skip(1, s) + return read_bytes(n, s) + + +def parse_custom_sections(s: InputStream) -> list[bytes]: + sects = [] + while True: + sect = parse_custom_section(s) + if not sect: + break + sects.append(sect) + return sects + + +def section(sec_id: int, p: Parser[A], default: A, s: InputStream) -> A: + try: + n = peek_byte(s) + except WasmEOFError: + return default + + if n != sec_id: + return default + skip(1, s) + return sized(p, s) + + +# TODO support recursive types in type sections +def typesec(s: InputStream) -> KInner: + def rectype_to_type_decl(rc: RecType) -> KInner: + match rc: + case [(_, [], st)]: + return wast.type(st) + case _: + raise WasmParseError('recursive types are not supported') + + rectypes = list_of(rectype, s) + type_decls = [rectype_to_type_decl(t) for t in rectypes] + return wast.defns(type_decls) + + +def byte_list(s: InputStream) -> bytes: + n = u32(s) + return read_bytes(n, s) + + +def name(s: InputStream) -> str: + bs = byte_list(s) + return bs.decode('utf-8') + + +def importsec(s: InputStream) -> KInner: + def import_(s: InputStream) -> KInner: + nm1 = wast.wasm_string(name(s)) + nm2 = wast.wasm_string(name(s)) + desc = externtype_as_import_desc(s) + return wast.imp(nm1, nm2, desc) + + imports = list_of(import_, s) + return wast.defns(imports) + + +def funcsec(s: InputStream) -> list[int]: + return list_of(typeidx, s) + + +def tablesec(s: InputStream) -> KInner: + def table(s: InputStream) -> KInner: + match peek_bytes(2, s): + case b'\x40\x00': + raise WasmParseError('table_tt_with_initializer not implemented') + case _: + # TODO this should return '𝗍𝖺𝖻𝗅𝖾 tt (𝗋𝖾𝖿.π—‡π—Žπ—…π—… ht) if tt=at lim (𝗋𝖾𝖿 π—‡π—Žπ—…π—…? ht)' + _at, lim, rt = tabletype(s) + return wast.table(lim, rt) + + tables = list_of(table, s) + return wast.defns(tables) + + +def memsec(s: InputStream) -> KInner: + mems = list_of(memtype, s) + return wast.defns([wast.memory(lim) for _at, lim in mems]) + + +def globalsec(s: InputStream) -> KInner: + def glob(s: InputStream) -> KInner: + gt = globaltype(s) + e = wast.instrs(expr(s)) + return wast.glob(gt, e) + + return wast.defns(list_of(glob, s)) + + +def exportsec(s: InputStream) -> KInner: + def export(s: InputStream) -> KInner: + nm = name(s) + xx = externidx(s) + return wast.export(wast.wasm_string(nm), index=xx) + + return wast.defns(list_of(export, s)) + + +def startsec(s: InputStream) -> KInner: + start_func = funcidx(s) + return wast.defns([wast.start(start_func)]) + + +def elemsec(s: InputStream) -> KInner: + def elemkind(s: InputStream) -> KInner: + match read_byte(s): + case 0: + return wast.funcref + case x: + raise WasmParseError(f'Invalid elemkind: {x}') + + def elem_init(init: list[list[KInner]]) -> list[int | None]: + def expr_to_int(expr: list[KInner]) -> int | None: + # 'expr' must be a constant expression consisting of a reference instruction + if len(expr) != 1: + raise WasmParseError(f'Expected a constant reference instruction as elem initializer, got {expr}') + instr = expr[0] + if isinstance(instr, KApply) and instr.label == KLabel('aInstrWithPos'): + instr = instr.args[0] + match instr: + case KApply(KLabel('aRef.func'), (KToken(sfuncidx, KSort('Int')),)): + return int(sfuncidx) + case KApply(KLabel('aRef.null'), _): + return None + case _: + raise WasmParseError(f'Expected a constant reference instruction as elem initializer, got {instr}') + + return [expr_to_int(e) for e in init] + + def elem(s: InputStream) -> KInner: + match read_byte(s): + case 0: + e0 = expr(s) + ys = list_of(funcidx, s) + return wast.elem(wast.funcref, wast.elem_active(0, wast.instrs(e0)), ys) + case 1: + rt = elemkind(s) + ys = list_of(funcidx, s) + return wast.elem(rt, wast.elem_passive(), ys) + case 2: + x = tableidx(s) + e = expr(s) + rt = elemkind(s) + ys = list_of(funcidx, s) + return wast.elem(rt, wast.elem_active(x, wast.instrs(e)), ys) + case 3: + rt = elemkind(s) + ys = list_of(funcidx, s) + return wast.elem(rt, wast.elem_declarative(), ys) + case 4: + e0 = expr(s) + es = list_of(expr, s) + return wast.elem(wast.funcref, wast.elem_active(0, wast.instrs(e0)), elem_init(es)) + case 5: + rt = reftype(s) + es = list_of(expr, s) + return wast.elem(rt, wast.elem_passive(), elem_init(es)) + case 6: + x = tableidx(s) + e0 = expr(s) + rt = reftype(s) + es = list_of(expr, s) + return wast.elem(rt, wast.elem_active(x, wast.instrs(e0)), elem_init(es)) + case 7: + rt = reftype(s) + es = list_of(expr, s) + return wast.elem(rt, wast.elem_declarative(), elem_init(es)) + case x: + raise WasmParseError(f'Invalid elem descriptor: {x}') + + return wast.defns(list_of(elem, s)) + + +def codesec(s: InputStream) -> list[tuple[list[KInner], list[KInner]]]: + def locals(s: InputStream) -> tuple[int, KInner]: + n = u32(s) + t = valtype(s) + return n, t + + def func(s: InputStream) -> tuple[list[KInner], list[KInner]]: + locs = list_of(locals, s) + body = expr(s) + + locs_flattened = [t for n, t in locs for _ in range(n)] + return locs_flattened, body + + def code(s: InputStream) -> tuple[list[KInner], list[KInner]]: + return sized(func, s) + + return list_of(code, s) + + +def datasec(s: InputStream) -> KInner: + def data(s: InputStream) -> KInner: + match read_byte(s): + case 0: + e = expr(s) + bs = byte_list(s) + return wast.data(bs, wast.datamode_active(0, wast.instrs(e))) + case 1: + bs = byte_list(s) + return wast.data(bs, wast.datamode_passive()) + case 2: + x = memidx(s) + e = expr(s) + bs = byte_list(s) + return wast.data(bs, wast.datamode_active(x, wast.instrs(e))) + case x: + raise WasmParseError(f'Invalid data segment descriptor: {x}') + + return wast.defns(list_of(data, s)) + + +def datacntsec(s: InputStream) -> int | None: + return u32(s) + + +def parse_module(s: InputStream) -> KInner: + parse_magic(s) + parse_version(s) + + custom_sections = [] + customs = lambda: custom_sections.extend(parse_custom_sections(s)) + + customs() + + type_section = section(1, typesec, EMPTY_SECTION, s) + + customs() + + import_section = section(2, importsec, EMPTY_SECTION, s) + + customs() + + function_section: list[int] = section(3, funcsec, [], s) + + customs() + + table_section = section(4, tablesec, EMPTY_SECTION, s) + + customs() + + memory_section = section(5, memsec, EMPTY_SECTION, s) + + customs() + + global_section = section(6, globalsec, EMPTY_SECTION, s) + + customs() + + export_section = section(7, exportsec, EMPTY_SECTION, s) + + customs() + + start_section = section(8, startsec, EMPTY_SECTION, s) + + customs() + + elem_section = section(9, elemsec, EMPTY_SECTION, s) + + customs() + + data_cnt_section = section(12, datacntsec, None, s) # noqa f841 + + customs() + + code_section: list[tuple[list[KInner], list[KInner]]] = section(10, codesec, [], s) + + customs() + + data_section = section(11, datasec, EMPTY_SECTION, s) + + customs() + + functions = [ + wast.func( + wast.KInt(typ_idx), + wast.vec_type(wast.val_types(locals)), + wast.instrs(code), + ) + for typ_idx, (locals, code) in zip(function_section, code_section, strict=True) + ] + + return wast.module( + types=type_section, + funcs=wast.defns(functions), + tables=table_section, + mems=memory_section, + globs=global_section, + elem=elem_section, + data=data_section, + start=start_section, + imports=import_section, + exports=export_section, + ) diff --git a/pykwasm/src/pykwasm/binary/types.py b/pykwasm/src/pykwasm/binary/types.py new file mode 100644 index 000000000..b5cecae18 --- /dev/null +++ b/pykwasm/src/pykwasm/binary/types.py @@ -0,0 +1,204 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from pykwasm.binary.indices import typeidx + +from .. import kwasm_ast as wast +from .combinators import either, list_of +from .integers import u64 +from .utils import WasmParseError, peek_byte, read_byte, skip + +if TYPE_CHECKING: + from typing import TypeAlias + + from pyk.kast.inner import KInner + + from .utils import InputStream + +# subtype struct in recursive types +# subtype ::= sub final? typeuse* comptype +SubType: TypeAlias = tuple[bool, list[int], 'KInner'] + +# A group of mutually recursive composite types +# rectype :: = rec list(subtype) +RecType: TypeAlias = list[SubType] + + +def rectype(s: InputStream) -> RecType: + n = peek_byte(s) + if n == 0x4E: + skip(1, s) + sts = list_of(parse_subtype, s) + return sts + return [parse_subtype(s)] + + +def parse_subtype(s: InputStream) -> SubType: + n = peek_byte(s) + + if n == 0x4F: + skip(1, s) + xs = list_of(typeidx, s) + ct = parse_comptype(s) + return (True, xs, ct) + if n == 0x50: + skip(1, s) + xs = list_of(typeidx, s) + ct = parse_comptype(s) + return (False, xs, ct) + + ct = parse_comptype(s) + return (True, [], ct) + + +def parse_comptype(s: InputStream) -> KInner: + n = read_byte(s) + if n == 0x60: + ts1 = resulttype(s) + ts2 = resulttype(s) + return wast.func_type(wast.vec_type(wast.val_types(ts1)), wast.vec_type(wast.val_types(ts2))) + + if n == 0x5E: + raise WasmParseError('array composite type is not yet supported') + if n == 0x5F: + raise WasmParseError('struct composite type is not yet supported') + raise WasmParseError(f'composite type not yet supported: {n}') + + +def resulttype(s: InputStream) -> list[KInner]: + return list_of(valtype, s) + + +def valtype(s: InputStream) -> KInner: + d = peek_byte(s) + + try: + return either( + [ + numtype, + reftype, + # TODO implement vectypes + # vectype, + ], + s, + ) + except WasmParseError: + raise WasmParseError(f'Could not parse valtype. Descriptor: {d}') from None + + +def reftype(s: InputStream) -> KInner: + match read_byte(s): + case 0x70: + return wast.funcref + case 0x6F: + return wast.externref + case d: + raise WasmParseError(f'Unsupported reftype descriptor: {d}') + + +def numtype(s: InputStream) -> KInner: + match read_byte(s): + case 0x7C: + return wast.f64 + case 0x7D: + return wast.f32 + case 0x7E: + return wast.i64 + case 0x7F: + return wast.i32 + case d: + raise WasmParseError(f'Invalid numtype descriptor: {d}') + + +Limits: TypeAlias = tuple[int, int | None] + + +# Returns (addressType, (n, m)) +def limits(s: InputStream) -> tuple[KInner, Limits]: + match read_byte(s): + case 0x00: + n = u64(s) + return wast.i32, (n, None) + case 0x01: + n = u64(s) + m = u64(s) + return wast.i32, (n, m) + case 0x02: + n = u64(s) + return wast.i64, (n, None) + case 0x03: + n = u64(s) + m = u64(s) + return wast.i64, (n, m) + case d: + raise WasmParseError(f'Invalid limit descriptor: {d}') + + +# Returns (addressType, limits, reftype) +def tabletype(s: InputStream) -> tuple[KInner, Limits, KInner]: + rt = reftype(s) + at, lim = limits(s) + return at, lim, rt + + +# Returns (addressType, limits) +def memtype(s: InputStream) -> tuple[KInner, Limits]: + return limits(s) + + +def mut(s: InputStream) -> KInner: + match read_byte(s): + case 0x00: + return wast.MUT_CONST + case 0x01: + return wast.MUT_VAR + case d: + raise WasmParseError(f'Invalid mut descriptor. Expected 0x00 or 0x01, got: {d}') + + +def globaltype(s: InputStream) -> KInner: + t = valtype(s) + m = mut(s) + return wast.global_type(m, t) + + +def tagtype(s: InputStream) -> int: + match read_byte(s): + case 0x00: + return typeidx(s) + case d: + raise WasmParseError(f'Invalid tagtype descriptor. Expected 0x00, got: {d}') + + +# TODO this function parses externtype from wasm 3.0 but returns ImportDesc from Wasm 1.0. +# Update the ImportDefn K definition to reflect the changes. +def externtype_as_import_desc(s: InputStream) -> KInner: + match read_byte(s): + case 0x00: + x = typeidx(s) + return wast.func_desc(x) + case 0x01: + _at, lim, _rt = tabletype(s) + return wast.table_desc(lim) + case 0x02: + _at, lim = memtype(s) + return wast.memory_desc(lim) + case 0x03: + gt = globaltype(s) + return wast.global_desc(gt) + case 0x04: + # jt = tagtype(s) + raise WasmParseError('externtype tagtype variant is not yet supported.') + case d: + raise WasmParseError(f'Invalid externtype descriptor. Expected [0x00-0x04], got: {d}') + + +def heaptype(s: InputStream) -> KInner: + match read_byte(s): + case 0x6F: + return wast.HEAPTYPE_EXTERN + case 0x70: + return wast.HEAPTYPE_FUNC + case x: + raise WasmParseError(f'Unsupported heaptype descriptor: {x}') diff --git a/pykwasm/src/pykwasm/binary/utils.py b/pykwasm/src/pykwasm/binary/utils.py new file mode 100644 index 000000000..232d2dcf0 --- /dev/null +++ b/pykwasm/src/pykwasm/binary/utils.py @@ -0,0 +1,59 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from collections.abc import Callable + from typing import TYPE_CHECKING, BinaryIO, TypeAlias, TypeVar + + InputStream: TypeAlias = BinaryIO + + A = TypeVar('A') + Parser: TypeAlias = Callable[[InputStream], A] + NParser: TypeAlias = Callable[[int, InputStream], A] + + +def read_bytes(n: int, s: InputStream) -> bytes: + b = s.read(n) + if len(b) != n: + raise WasmEOFError(f'Unexpected EOF. Expected {n} bytes, got {len(b)}') + return b + + +def read_byte(s: InputStream) -> int: + return read_bytes(1, s)[0] + + +def peek_bytes(n: int, s: InputStream) -> bytes: + pos = s.tell() + b = s.read(n) + if len(b) != n: + raise WasmEOFError(f'Unexpected EOF. Expected {n} bytes, got {len(b)}') + reset(pos, s) + return b + + +def peek_byte(s: InputStream) -> int: + return peek_bytes(1, s)[0] + + +def skip(n: int, s: InputStream) -> None: + s.seek(n, 1) + + +def reset(n: int, s: InputStream) -> None: + s.seek(n, 0) + + +def expect_bytes(expected: bytes, s: InputStream) -> None: + bs = read_bytes(len(expected), s) + if bs != expected: + raise WasmParseError(f'Expected {expected!r}, got {bs!r}') + + +class WasmParseError(Exception): + pass + + +class WasmEOFError(WasmParseError): + pass diff --git a/pykwasm/src/pykwasm/kdist/wasm-semantics/test.md b/pykwasm/src/pykwasm/kdist/wasm-semantics/test.md index 06266dac5..f264266ae 100644 --- a/pykwasm/src/pykwasm/kdist/wasm-semantics/test.md +++ b/pykwasm/src/pykwasm/kdist/wasm-semantics/test.md @@ -217,7 +217,7 @@ We allow 2 kinds of actions: rule invoke MODIDX:Int ENAME:WasmString => ( invoke FUNCADDRS {{ IDX }} orDefault -1 ):Instr ... MODIDX - ... ENAME |-> IDX ... + ... ENAME |-> #externIdxFunc( IDX ) ... FUNCADDRS ... @@ -232,7 +232,7 @@ We allow 2 kinds of actions: rule get MODIDX:Int NAME:WasmString => VAL ... MODIDX - ... NAME |-> TFIDX ... + ... NAME |-> #externIdxGlobal( TFIDX ) ... IDS ... #ContextLookup(IDS, TFIDX) |-> ADDR ... ... @@ -321,19 +321,19 @@ The conformance tests contain imports of the `"spectest"` module. start: .EmptyStmts , importDefns: .EmptyStmts , exports: - #export (... name: #token("\"global_i32\"" , "WasmStringToken") , index: 0 ) - #export (... name: #token("\"global_i64\"" , "WasmStringToken") , index: 1 ) - #export (... name: #token("\"global_f32\"" , "WasmStringToken") , index: 2 ) - #export (... name: #token("\"global_f64\"" , "WasmStringToken") , index: 3 ) - #export (... name: #token("\"table\"" , "WasmStringToken") , index: 0 ) - #export (... name: #token("\"memory\"" , "WasmStringToken") , index: 0 ) - #export (... name: #token("\"print\"" , "WasmStringToken") , index: 0 ) - #export (... name: #token("\"print_i32\"" , "WasmStringToken") , index: 1 ) - #export (... name: #token("\"print_i64\"" , "WasmStringToken") , index: 2 ) - #export (... name: #token("\"print_f32\"" , "WasmStringToken") , index: 3 ) - #export (... name: #token("\"print_f64\"" , "WasmStringToken") , index: 4 ) - #export (... name: #token("\"print_i32_f32\"" , "WasmStringToken") , index: 5 ) - #export (... name: #token("\"print_f64_f64\"" , "WasmStringToken") , index: 6 ) + #export (... name: #token("\"global_i32\"" , "WasmStringToken") , index: #externIdxGlobal(0) ) + #export (... name: #token("\"global_i64\"" , "WasmStringToken") , index: #externIdxGlobal(1) ) + #export (... name: #token("\"global_f32\"" , "WasmStringToken") , index: #externIdxGlobal(2) ) + #export (... name: #token("\"global_f64\"" , "WasmStringToken") , index: #externIdxGlobal(3) ) + #export (... name: #token("\"table\"" , "WasmStringToken") , index: #externIdxTable(0) ) + #export (... name: #token("\"memory\"" , "WasmStringToken") , index: #externIdxMemory(0) ) + #export (... name: #token("\"print\"" , "WasmStringToken") , index: #externIdxFunc(0) ) + #export (... name: #token("\"print_i32\"" , "WasmStringToken") , index: #externIdxFunc(1) ) + #export (... name: #token("\"print_i64\"" , "WasmStringToken") , index: #externIdxFunc(2) ) + #export (... name: #token("\"print_f32\"" , "WasmStringToken") , index: #externIdxFunc(3) ) + #export (... name: #token("\"print_f64\"" , "WasmStringToken") , index: #externIdxFunc(4) ) + #export (... name: #token("\"print_i32_f32\"" , "WasmStringToken") , index: #externIdxFunc(5) ) + #export (... name: #token("\"print_f64_f64\"" , "WasmStringToken") , index: #externIdxFunc(6) ) .EmptyStmts , metadata: #meta (... id: , funcIds: .Map , filename: .String ) ) diff --git a/pykwasm/src/pykwasm/kdist/wasm-semantics/wasm-text.md b/pykwasm/src/pykwasm/kdist/wasm-semantics/wasm-text.md index 4b4b3e233..8810a1d8d 100644 --- a/pykwasm/src/pykwasm/kdist/wasm-semantics/wasm-text.md +++ b/pykwasm/src/pykwasm/kdist/wasm-semantics/wasm-text.md @@ -998,19 +998,19 @@ Wasm currently supports only one table, so we do not need to resolve any identif Wasm currently supports only one memory, so we do not need to resolve any identifiers. ```k - rule #t2aDefn(( data _:Index (offset IS) DS )) => #data(0, #t2aInstrs(IS), #DS2Bytes(DS)) + rule #t2aDefn(( data _:Index (offset IS) DS )) => #data(#DS2Bytes(DS), #active(0, #t2aInstrs(IS))) ``` #### Exports ```k - rule #t2aDefn(( export ENAME ( func IDENT:Identifier ) )) => #export(ENAME, {IDS[IDENT]}:>Int) requires IDENT in_keys(IDS) - rule #t2aDefn(( export ENAME ( global IDENT:Identifier ) )) => #export(ENAME, {IDS[IDENT]}:>Int) requires IDENT in_keys(IDS) - rule #t2aDefn<_>(( export ENAME ( func I:Int ) )) => #export(ENAME, I) - rule #t2aDefn<_>(( export ENAME ( global I:Int ) )) => #export(ENAME, I) + rule #t2aDefn(( export ENAME ( func IDENT:Identifier ) )) => #export(ENAME, #externIdxFunc({IDS[IDENT]}:>Int)) requires IDENT in_keys(IDS) + rule #t2aDefn(( export ENAME ( global IDENT:Identifier ) )) => #export(ENAME, #externIdxGlobal({IDS[IDENT]}:>Int)) requires IDENT in_keys(IDS) + rule #t2aDefn<_>(( export ENAME ( func I:Int ) )) => #export(ENAME, #externIdxFunc(I)) + rule #t2aDefn<_>(( export ENAME ( global I:Int ) )) => #export(ENAME, #externIdxGlobal(I)) - rule #t2aDefn<_>(( export ENAME ( table _ ) )) => #export(ENAME, 0) - rule #t2aDefn<_>(( export ENAME ( memory _ ) )) => #export(ENAME, 0) + rule #t2aDefn<_>(( export ENAME ( table _ ) )) => #export(ENAME, #externIdxTable(0)) + rule #t2aDefn<_>(( export ENAME ( memory _ ) )) => #export(ENAME, #externIdxMemory(0)) ``` #### Other Definitions @@ -1173,9 +1173,9 @@ There are several formats of block instructions, and the text-to-abstract transf At this point, all branching identifiers should have been resolved, so we can remove the id. ```k - rule #t2aInstr( block _OID:OptionalId TDS:TypeDecls IS end _OID') => #block(gatherTypes(result, TDS), #t2aInstrs(IS), .Int) - rule #t2aInstr( loop _OID:OptionalId TDS IS end _OID') => #loop(gatherTypes(result, TDS), #t2aInstrs(IS), .Int) - rule #t2aInstr( if _OID:OptionalId TDS IS else _OID':OptionalId IS' end _OID'') => #if(gatherTypes(result, TDS), #t2aInstrs(IS), #t2aInstrs(IS'), .Int) + rule #t2aInstr( block _OID:OptionalId TDS:TypeDecls IS end _OID') => #block(gatherTypes(result, TDS), #t2aInstrs(IS)) + rule #t2aInstr( loop _OID:OptionalId TDS IS end _OID') => #loop(gatherTypes(result, TDS), #t2aInstrs(IS)) + rule #t2aInstr( if _OID:OptionalId TDS IS else _OID':OptionalId IS' end _OID'') => #if(gatherTypes(result, TDS), #t2aInstrs(IS), #t2aInstrs(IS')) ``` #### KWasm Administrative Instructions @@ -1186,7 +1186,7 @@ They are currently supported in KWasm text files, but may be deprecated. ```k rule #t2aInstr<_C>(trap) => trap - rule #t2aInstr(#block(VT:VecType, IS:Instrs, BLOCKINFO)) => #block(VT, #t2aInstrs(IS), BLOCKINFO) + rule #t2aInstr(#block(VT:VecType, IS:Instrs)) => #block(VT, #t2aInstrs(IS)) rule #t2aInstr<_>(init_local I V) => init_local I V rule #t2aInstr<_>(init_locals VS) => init_locals VS diff --git a/pykwasm/src/pykwasm/kdist/wasm-semantics/wasm.md b/pykwasm/src/pykwasm/kdist/wasm-semantics/wasm.md index 78f192f75..1ebaf8887 100644 --- a/pykwasm/src/pykwasm/kdist/wasm-semantics/wasm.md +++ b/pykwasm/src/pykwasm/kdist/wasm-semantics/wasm.md @@ -213,7 +213,7 @@ module WASM 0 - .Map + .Map // WasmString -> ExternIdx .Map 0 .ListInt @@ -507,12 +507,9 @@ It simply executes the block then records a label with an empty continuation. rule label [ TYPES ] { _ } VALSTACK' => .K ... VALSTACK => #take(lengthValTypes(TYPES), VALSTACK) ++ VALSTACK' - syntax BlockMetaData ::= OptionalInt - // ------------------------------------ - - syntax Instr ::= #block(VecType, Instrs, BlockMetaData) [symbol(aBlock)] - // ------------------------------------------------------------------------ - rule #block(VECTYP, IS, _) => sequenceInstrs(IS) ~> label VECTYP { .Instrs } VALSTACK ... + syntax Instr ::= #block(VecType, Instrs) [symbol(aBlock)] + // --------------------------------------------------------- + rule #block(VECTYP, IS) => sequenceInstrs(IS) ~> label VECTYP { .Instrs } VALSTACK ... VALSTACK => .ValStack ``` @@ -554,19 +551,19 @@ Note that, unlike in the WebAssembly specification document, we do not need the Finally, we have the conditional and loop instructions. ```k - syntax Instr ::= #if( VecType, then : Instrs, else : Instrs, blockInfo: BlockMetaData) [symbol(aIf)] - // ---------------------------------------------------------------------------------------------------- - rule #if(VECTYP, IS, _, _) => sequenceInstrs(IS) ~> label VECTYP { .Instrs } VALSTACK ... + syntax Instr ::= #if( VecType, then : Instrs, else : Instrs) [symbol(aIf)] + // -------------------------------------------------------------------------- + rule #if(VECTYP, IS, _) => sequenceInstrs(IS) ~> label VECTYP { .Instrs } VALSTACK ... < i32 > VAL : VALSTACK => VALSTACK requires VAL =/=Int 0 - rule #if(VECTYP, _, IS, _) => sequenceInstrs(IS) ~> label VECTYP { .Instrs } VALSTACK ... + rule #if(VECTYP, _, IS) => sequenceInstrs(IS) ~> label VECTYP { .Instrs } VALSTACK ... < i32 > VAL : VALSTACK => VALSTACK requires VAL ==Int 0 - syntax Instr ::= #loop(VecType, Instrs, BlockMetaData) [symbol(aLoop)] - // ---------------------------------------------------------------------- - rule #loop(VECTYP, IS, BLOCKMETA) => sequenceInstrs(IS) ~> label VECTYP { #loop(VECTYP, IS, BLOCKMETA) } VALSTACK ... + syntax Instr ::= #loop(VecType, Instrs) [symbol(aLoop)] + // -------------------------------------------------------- + rule #loop(VECTYP, IS) => sequenceInstrs(IS) ~> label VECTYP { #loop(VECTYP, IS) } VALSTACK ... VALSTACK => .ValStack ``` @@ -1223,7 +1220,7 @@ The `#take` function will return the parameter stack in the reversed order, then // ------------------------------------- rule ( invoke FADDR ) => init_locals #revs(#take(lengthValTypes(TDOMAIN), VALSTACK)) ++ #zero(TLOCALS) - ~> #block([TRANGE], INSTRS, .Int) + ~> #block([TRANGE], INSTRS) ~> frame MODIDX TRANGE #drop(lengthValTypes(TDOMAIN), VALSTACK) LOCAL ... @@ -1781,11 +1778,14 @@ Memories can be initialized with data, specified as a list of bytes together wit The `data` initializer simply puts these bytes into the specified memory, starting at the offset. ```k - syntax DataDefn ::= #data(index : Int, offset : Instrs, data : Bytes) [symbol(aDataDefn)] - | "data" "{" Int Bytes "}" + syntax DataDefn ::= #data(data: Bytes, mode: DataMode) [symbol(aDataDefn)] + syntax DataMode ::= #active(memidx: Int, offset: Instrs) [symbol(aDataModeActive)] + | "#passive" [symbol(aDataModePassive)] // -------------------------------------------- - // Default to memory 0. - rule #data(IDX, IS, DATA) => sequenceInstrs(IS) ~> data { IDX DATA } ... + syntax KItem ::= "data" "{" Int Bytes "}" + + rule #data(DATA, #active(IDX, IS)) => sequenceInstrs(IS) ~> data { IDX DATA } ... + rule #data(_DATA, #passive) => .K ... rule data { MEMIDX DSBYTES } => trap ... < i32 > OFFSET : _STACK @@ -1802,7 +1802,6 @@ The `data` initializer simply puts these bytes into the specified memory, starti requires OFFSET +Int lengthBytes(DSBYTES) >Int SIZE *Int #pageSize() - // For now, deal only with memory 0. rule data { MEMIDX DSBYTES } => .K ... < i32 > OFFSET : STACK => STACK CUR @@ -1847,7 +1846,12 @@ Export Exports make functions, tables, memories and globals available for importing into other modules. ```k - syntax ExportDefn ::= #export(name : WasmString, index : Int) [symbol(aExportDefn)] + syntax ExportDefn ::= #export(name : WasmString, index : ExternIdx) [symbol(aExportDefn)] + syntax ExternIdx ::= #externIdxFunc(Int) [symbol(aExternIdxFunc)] + | #externIdxTable(Int) [symbol(aExternIdxTable)] + | #externIdxMemory(Int) [symbol(aExternIdxMemory)] + | #externIdxGlobal(Int) [symbol(aExternIdxGlobal)] + | #externIdxTag(Int) [symbol(aExternIdxTag)] syntax Alloc ::= ExportDefn // --------------------------- rule #export(ENAME, IDX) => .K ... @@ -1880,7 +1884,7 @@ The value of a global gets copied when it is imported. MODIDX FS2 - ... NAME |-> IDX ... + ... NAME |-> #externIdxFunc( IDX ) ... ... requires isListIndex(IDX, FS2) @@ -1919,7 +1923,7 @@ The value of a global gets copied when it is imported. MODIDX IDS' ... #ContextLookup(IDS' , TFIDX) |-> ADDR ... - ... NAME |-> TFIDX ... + ... NAME |-> #externIdxTable( TFIDX ) ... ... @@ -1943,7 +1947,7 @@ The value of a global gets copied when it is imported. MODIDX IDS' ... #ContextLookup(IDS' , TFIDX) |-> ADDR ... - ... NAME |-> TFIDX ... + ... NAME |-> #externIdxMemory( TFIDX ) ... ... @@ -1968,7 +1972,7 @@ The value of a global gets copied when it is imported. MODIDX IDS' ... #ContextLookup(IDS' , TFIDX) |-> ADDR ... - ... NAME |-> TFIDX ... + ... NAME |-> #externIdxGlobal( TFIDX ) ... ... diff --git a/pykwasm/src/pykwasm/kwasm_ast.py b/pykwasm/src/pykwasm/kwasm_ast.py index 27a2b7704..0bf35ae1f 100644 --- a/pykwasm/src/pykwasm/kwasm_ast.py +++ b/pykwasm/src/pykwasm/kwasm_ast.py @@ -53,6 +53,8 @@ ELEM = 'aElemDefn' DATA = 'aDataDefn' +DATAMODE_ACTIVE = 'aDataModeActive' +DATAMODE_PASSIVE = 'aDataModePassive' START = 'aStartDefn' @@ -164,6 +166,9 @@ def idx_to_ref(idx: int | None) -> KInner: MUT_CONST = KApply('mutConst', []) MUT_VAR = KApply('mutVar', []) +HEAPTYPE_FUNC = KApply('func', []) +HEAPTYPE_EXTERN = KApply('extern', []) + def vec_type(valtypes: KInner) -> KInner: return KApply(VEC_TYPE, [valtypes]) @@ -173,7 +178,7 @@ def func_type(params: KInner, results: KInner) -> KInner: return KApply(FUNC_TYPE, [params, results]) -def limits(tup: tuple[int, int]) -> KInner: +def limits(tup: tuple[int, int | None]) -> KInner: i = tup[0] j = tup[1] if j is None: @@ -190,8 +195,8 @@ def global_type(mut: KInner, valtype: KInner) -> KInner: ########## -def INSTR_WITH_POS(instruction: KInner, start: int, end: int) -> KInner: - return KApply('aInstrWithPos', [instruction, KInt(start), KInt(end)]) +def INSTR_WITH_POS(instruction: KInner, offset: int, length: int) -> KInner: + return KApply('aInstrWithPos', [instruction, KInt(offset), KInt(length)]) ######################## @@ -202,16 +207,16 @@ def INSTR_WITH_POS(instruction: KInner, start: int, end: int) -> KInner: UNREACHABLE = KApply('aUnreachable', []) -def BLOCK(vec_type: KInner, instrs: KInner, block_info: KInner) -> KInner: - return KApply('aBlock', [vec_type, instrs, block_info]) +def BLOCK(vec_type: KInner, instrs: KInner) -> KInner: + return KApply('aBlock', [vec_type, instrs]) -def IF(vec_type: KInner, then_instrs: KInner, else_instrs: KInner, block_info: KInner) -> KInner: - return KApply('aIf', [vec_type, then_instrs, else_instrs, block_info]) +def IF(vec_type: KInner, then_instrs: KInner, else_instrs: KInner) -> KInner: + return KApply('aIf', [vec_type, then_instrs, else_instrs]) -def LOOP(vec_type: KInner, instrs: KInner, block_info: KInner) -> KInner: - return KApply('aLoop', [vec_type, instrs, block_info]) +def LOOP(vec_type: KInner, instrs: KInner) -> KInner: + return KApply('aLoop', [vec_type, instrs]) RETURN = KApply('aReturn', []) @@ -233,9 +238,9 @@ def CALL(function_idx: int) -> KInner: return KApply('aCall', [KInt(function_idx)]) -def CALL_INDIRECT(type_idx: int) -> KInner: +def CALL_INDIRECT(table_idx: int, type_idx: int) -> KInner: type_use = KApply('aTypeUseIndex', [KInt(type_idx)]) - return KApply('aCall_indirect', [KInt(0), type_use]) + return KApply('aCall_indirect', [KInt(table_idx), type_use]) ########################## @@ -250,8 +255,8 @@ def REF_FUNC(func_idx: int) -> KInner: REF_IS_NULL: KInner = KApply('aRef.is_null', []) -def REF_NULL(t: str) -> KInner: - return KApply('aRef.null', [KApply(t, [])]) +def REF_NULL(heaptype: KInner) -> KInner: + return KApply('aRef.null', [heaptype]) ########################## @@ -631,11 +636,11 @@ def global_desc(global_type: KInner, id: KInner = EMPTY_ID) -> KInner: return KApply(GLOBAL_DESC, [id, global_type]) -def table_desc(lim: tuple[int, int], id: KInner = EMPTY_ID) -> KInner: +def table_desc(lim: tuple[int, int | None], id: KInner = EMPTY_ID) -> KInner: return KApply(TABLE_DESC, [id, limits(lim)]) -def memory_desc(lim: tuple[int, int], id: KInner = EMPTY_ID) -> KInner: +def memory_desc(lim: tuple[int, int | None], id: KInner = EMPTY_ID) -> KInner: return KApply(MEMORY_DESC, [id, limits(lim)]) @@ -652,11 +657,11 @@ def func(type: KInner, locals: KInner, body: KInner, metadata: KInner = EMPTY_FU return KApply(FUNC, [type, locals, body, metadata]) -def table(lim: tuple[int, int], typ: KInner, metadata: KInner = EMPTY_ID) -> KInner: +def table(lim: tuple[int, int | None], typ: KInner, metadata: KInner = EMPTY_ID) -> KInner: return KApply(TABLE, [limits(lim), typ, metadata]) -def memory(lim: tuple[int, int], metadata: KInner = EMPTY_ID) -> KInner: +def memory(lim: tuple[int, int | None], metadata: KInner = EMPTY_ID) -> KInner: return KApply(MEMORY, [limits(lim), metadata]) @@ -680,8 +685,16 @@ def elem(typ: KInner, elem_mode: KInner, init: Iterable[int | None], metadata: K return KApply(ELEM, [typ, refs(init), elem_mode, metadata]) -def data(memory_idx: int, offset: KInner, data: bytes) -> KInner: - return KApply(DATA, [KInt(memory_idx), offset, KBytes(data)]) +def data(bs: bytes, mode: KInner) -> KInner: + return KApply(DATA, [KBytes(bs), mode]) + + +def datamode_active(memidx: int, offset: KInner) -> KInner: + return KApply(DATAMODE_ACTIVE, [KInt(memidx), offset]) + + +def datamode_passive() -> KInner: + return KApply(DATAMODE_PASSIVE, []) def start(start_idx: int) -> KInner: @@ -692,8 +705,28 @@ def imp(mod_name: KInner, name: KInner, import_desc: KInner) -> KInner: return KApply(IMPORT, [mod_name, name, import_desc]) -def export(name: KInner, index: int) -> KInner: - return KApply(EXPORT, [name, KInt(index)]) +def export(name: KInner, index: KInner) -> KInner: + return KApply(EXPORT, [name, index]) + + +def externidx_func(i: int) -> KInner: + return KApply('aExternIdxFunc', [KInt(i)]) + + +def externidx_table(i: int) -> KInner: + return KApply('aExternIdxTable', [KInt(i)]) + + +def externidx_memory(i: int) -> KInner: + return KApply('aExternIdxMemory', [KInt(i)]) + + +def externidx_global(i: int) -> KInner: + return KApply('aExternIdxGlobal', [KInt(i)]) + + +def externidx_tag(i: int) -> KInner: + return KApply('aExternIdxTag', [KInt(i)]) def module_metadata(mid: None = None, fids: None = None, filename: str | None = None) -> KInner: diff --git a/pykwasm/src/pykwasm/wasm2kast.py b/pykwasm/src/pykwasm/wasm2kast.py index 55ea382f5..3d85aba79 100644 --- a/pykwasm/src/pykwasm/wasm2kast.py +++ b/pykwasm/src/pykwasm/wasm2kast.py @@ -9,37 +9,12 @@ import sys from typing import TYPE_CHECKING -from wasm import instructions -from wasm.datatypes import GlobalType, MemoryType, Mutability, TableType, TypeIdx, ValType, addresses -from wasm.datatypes.element_segment import ElemModeActive, ElemModeDeclarative, ElemModePassive -from wasm.instructions import InstructionWithPos -from wasm.opcodes import BinaryOpcode -from wasm.parsers import parse_module - -from pykwasm import kwasm_ast as a +from .binary import parse_module if TYPE_CHECKING: - from collections.abc import Iterable from typing import IO from pyk.kast import KInner - from wasm.datatypes import ( - DataSegment, - ElementSegment, - Export, - Function, - FunctionType, - Global, - Import, - Limits, - Memory, - Module, - RefType, - StartFunction, - Table, - ) - from wasm.datatypes.element_segment import ElemMode - from wasm.instructions import BaseInstruction def main(): @@ -53,344 +28,14 @@ def main(): def wasm2kast(wasm_bytes: IO[bytes], filename=None) -> KInner: - """Returns a dictionary representing the Kast JSON.""" - ast = parse_module(wasm_bytes) - return ast2kast(ast, filename=filename) - - -def ast2kast(wasm_ast: Module, filename=None) -> KInner: - """Returns a dictionary representing the Kast JSON.""" - types = a.defns([typ(x) for x in wasm_ast.types]) - funcs = a.defns([func(x) for x in wasm_ast.funcs]) - tables = a.defns([table(x) for x in wasm_ast.tables]) - mems = a.defns([memory(x) for x in wasm_ast.mems]) - globs = a.defns([glob(x) for x in wasm_ast.globals]) - elems = a.defns([elem(x) for x in wasm_ast.elem]) - datas = a.defns([data(x) for x in wasm_ast.data]) - starts = a.defns(start(wasm_ast.start)) - imports = a.defns([imp(x) for x in wasm_ast.imports]) - exports = a.defns([export(x) for x in wasm_ast.exports]) - meta = a.module_metadata(filename=filename) - return a.module( - types=types, - funcs=funcs, - tables=tables, - mems=mems, - globs=globs, - elem=elems, - data=datas, - start=starts, - imports=imports, - exports=exports, - metadata=meta, - ) - - -######### -# Defns # -######### - - -def typ(t: FunctionType): - return a.type(func_type(t.params, t.results)) - - -def func(f: Function): - type = a.KInt(f.type_idx) - ls_list = [val_type(x) for x in f.locals] - locals = a.vec_type(a.val_types(ls_list)) - body = instrs(f.body) - return a.func(type, locals, body) - - -def table(t: Table): - ls = limits(t.type.limits) - typ = ref_type(t.type.elem_type) - return a.table(ls, typ) - - -def memory(m: Memory): - ls = limits(m.type) - return a.memory(ls) - - -def glob(g: Global): - t = global_type(g.type) - init = instrs(g.init) - return a.glob(t, init) - - -def ref_type(t: RefType): - if t is addresses.FunctionAddress: - return a.funcref - if t is addresses.ExternAddress: - return a.externref - raise ValueError(f'Invalid RefType: {t}') - - -def elem_mode(m: ElemMode) -> KInner: - if isinstance(m, ElemModeActive): - offset = instrs(m.offset) - return a.elem_active(m.table, offset) - if isinstance(m, ElemModeDeclarative): - return a.elem_declarative() - if isinstance(m, ElemModePassive): - return a.elem_passive() - raise ValueError(f'Unknown ElemMode: {m}') - - -def elem_init(init: tuple[Iterable[BaseInstruction], ...]) -> Iterable[int | None]: - def expr_to_int(expr: Iterable[BaseInstruction]) -> int | None: - # 'expr' must be a constant expression consisting of a reference instruction - assert len(expr) == 1 or len(expr) == 2 and expr[1].opcode == BinaryOpcode.END, expr - instr = expr[0] - - if isinstance(instr, InstructionWithPos): - instr = instr.instruction - - if isinstance(instr, instructions.RefFunc): - return instr.funcidx - if isinstance(instr, instructions.RefNull): - return None - raise ValueError(f'Invalid reference expression: {expr}') - - return [expr_to_int(e) for e in init] - - -def elem(e: ElementSegment): - typ = ref_type(e.type) - mode = elem_mode(e.mode) - init = elem_init(e.init) - return a.elem(typ, mode, init) - - -def data(d: DataSegment): - offset = instrs(d.offset) - return a.data(d.memory_idx, offset, d.init) - - -def start(s: StartFunction): - if s is None: - return [] - return [a.start(s.function_idx)] - - -def imp(i: Import): - mod_name = a.wasm_string(i.module_name) - name = a.wasm_string(i.as_name) - t = type(i.desc) - if t is TypeIdx: - desc = a.func_desc(i.desc) - elif t is GlobalType: - desc = a.global_desc(global_type(i.desc)) - elif t is TableType: - desc = a.table_desc(limits(i.desc.limits)) - elif t is MemoryType: - desc = a.memory_desc(limits(i.desc)) - return a.imp(mod_name, name, desc) - - -def export(e: Export): - name = a.wasm_string(e.name) - idx = e.desc - return a.export(name, idx) - - -########## -# Instrs # -########## - -block_id = 0 - - -def instrs(iis): - """Turn a list of instructions into KAST.""" - # We ignore `END`. - # The AST supplied by py-wasm has already parsed these and terminated the blocks. - # We also ignore `ELSE`. - # The AST supplied by py-wasm includes the statements in the else-branch as part of the `IF` instruction. - return a.instrs([instr(i) for i in iis if not i.opcode == BinaryOpcode.END and not i.opcode == BinaryOpcode.ELSE]) - - -def instr(i): - B = BinaryOpcode - global block_id - # TODO rewrite 'i.opcode == _' conditions as isinstance for better type-checking - if isinstance(i, InstructionWithPos): - inner = instr(i.instruction) - return a.INSTR_WITH_POS(inner, i.offset, i.length) - if i.opcode == B.BLOCK: - cur_block_id = block_id - block_id += 1 - iis = instrs(i.instructions) - res = vec_type(i.result_type) - return a.BLOCK(res, iis, a.KInt(cur_block_id)) - if i.opcode == B.BR: - return a.BR(i.label_idx) - if i.opcode == B.BR_IF: - return a.BR_IF(i.label_idx) - if i.opcode == B.BR_TABLE: - return a.BR_TABLE(i.label_indices, i.default_idx) - if i.opcode == B.CALL: - return a.CALL(i.function_idx) - if i.opcode == B.CALL_INDIRECT: - return a.CALL_INDIRECT(i.type_idx) - if i.opcode == B.ELSE: - raise (ValueError('ELSE opcode: should have been filtered out.')) - if i.opcode == B.END: - raise (ValueError('End opcode: should have been filtered out.')) - if i.opcode == B.F32_CONST: - return a.F32_CONST(i.value) - if i.opcode == B.F64_CONST: - return a.F64_CONST(i.value) - if i.opcode == B.F32_REINTERPRET_I32: - raise (ValueError('Reinterpret instructions not implemented.')) - if i.opcode == B.F64_REINTERPRET_I64: - raise (ValueError('Reinterpret instructions not implemented.')) - if i.opcode == B.GET_GLOBAL: - return a.GET_GLOBAL(i.global_idx) - if i.opcode == B.GET_LOCAL: - return a.GET_LOCAL(i.local_idx) - if i.opcode == B.I32_CONST: - return a.I32_CONST(i.value) - if i.opcode == B.I64_CONST: - return a.I64_CONST(i.value) - if i.opcode == B.I32_REINTERPRET_F32: - raise (ValueError('Reinterpret instructions not implemented.')) - if i.opcode == B.I64_REINTERPRET_F64: - raise (ValueError('Reinterpret instructions not implemented.')) - if i.opcode == B.IF: - cur_block_id = block_id - block_id += 1 - thens = instrs(i.instructions) - els = instrs(i.else_instructions) - res = vec_type(i.result_type) - return a.IF(res, thens, els, a.KInt(cur_block_id)) - if i.opcode == B.F32_STORE: - return a.F32_STORE(i.memarg.offset) - if i.opcode == B.F64_STORE: - return a.F64_STORE(i.memarg.offset) - if i.opcode == B.I32_STORE: - return a.I32_STORE(i.memarg.offset) - if i.opcode == B.I64_STORE: - return a.I64_STORE(i.memarg.offset) - if i.opcode == B.I32_STORE16: - return a.I32_STORE16(i.memarg.offset) - if i.opcode == B.I64_STORE16: - return a.I64_STORE16(i.memarg.offset) - if i.opcode == B.I32_STORE8: - return a.I32_STORE8(i.memarg.offset) - if i.opcode == B.I64_STORE8: - return a.I64_STORE8(i.memarg.offset) - if i.opcode == B.I64_STORE32: - return a.I64_STORE32(i.memarg.offset) - if i.opcode == B.F32_LOAD: - return a.F32_LOAD(i.memarg.offset) - if i.opcode == B.F64_LOAD: - return a.F64_LOAD(i.memarg.offset) - if i.opcode == B.I32_LOAD: - return a.I32_LOAD(i.memarg.offset) - if i.opcode == B.I64_LOAD: - return a.I64_LOAD(i.memarg.offset) - if i.opcode == B.I32_LOAD16_S: - return a.I32_LOAD16_S(i.memarg.offset) - if i.opcode == B.I32_LOAD16_U: - return a.I32_LOAD16_U(i.memarg.offset) - if i.opcode == B.I64_LOAD16_S: - return a.I64_LOAD16_S(i.memarg.offset) - if i.opcode == B.I64_LOAD16_U: - return a.I64_LOAD16_U(i.memarg.offset) - if i.opcode == B.I32_LOAD8_S: - return a.I32_LOAD8_S(i.memarg.offset) - if i.opcode == B.I32_LOAD8_U: - return a.I32_LOAD8_U(i.memarg.offset) - if i.opcode == B.I64_LOAD8_S: - return a.I64_LOAD8_S(i.memarg.offset) - if i.opcode == B.I64_LOAD8_U: - return a.I64_LOAD8_U(i.memarg.offset) - if i.opcode == B.I64_LOAD32_S: - return a.I64_LOAD32_S(i.memarg.offset) - if i.opcode == B.I64_LOAD32_U: - return a.I64_LOAD32_U(i.memarg.offset) - if i.opcode == B.LOOP: - cur_block_id = block_id - block_id += 1 - iis = instrs(i.instructions) - res = vec_type(i.result_type) - return a.LOOP(res, iis, a.KInt(cur_block_id)) - if i.opcode == B.SET_GLOBAL: - return a.SET_GLOBAL(i.global_idx) - if i.opcode == B.SET_LOCAL: - return a.SET_LOCAL(i.local_idx) - if i.opcode == B.TEE_LOCAL: - return a.TEE_LOCAL(i.local_idx) - if isinstance(i, instructions.RefFunc): - return a.REF_FUNC(i.funcidx) - if isinstance(i, instructions.RefNull): - if i.reftype is addresses.FunctionAddress: - return a.REF_NULL('func') - if i.reftype is addresses.ExternAddress: - return a.REF_NULL('extern') - raise ValueError(f'Unknown heap type: {i}, {i.reftype}') - if isinstance(i, instructions.TableGet): - return a.TABLE_GET(i.tableidx) - if isinstance(i, instructions.TableSet): - return a.TABLE_SET(i.tableidx) - if isinstance(i, instructions.TableInit): - return a.TABLE_INIT(i.tableidx, i.elemidx) - if isinstance(i, instructions.ElemDrop): - return a.ELEM_DROP(i.elemidx) - if isinstance(i, instructions.TableCopy): - return a.TABLE_COPY(i.tableidx1, i.tableidx2) - if isinstance(i, instructions.TableGrow): - return a.TABLE_GROW(i.tableidx) - if isinstance(i, instructions.TableSize): - return a.TABLE_SIZE(i.tableidx) - if isinstance(i, instructions.TableFill): - return a.TABLE_FILL(i.tableidx) - - # Catch all for operations without direct arguments. - return eval('a.' + i.opcode.name) - - -######## -# Data # -######## - - -def val_type(t: ValType): - if t == ValType.i32: - return a.i32 - if t == ValType.i64: - return a.i64 - if t == ValType.f32: - return a.f32 - if t == ValType.f64: - return a.f64 - if t == ValType.externref: - return a.externref - if t == ValType.funcref: - return a.funcref - raise ValueError(f'Unknown value type: {t}') - - -def vec_type(ts: Iterable[ValType]): - _ts = [val_type(x) for x in ts] - return a.vec_type(a.val_types(_ts)) - - -def func_type(params, results): - pvec = vec_type(params) - rvec = vec_type(results) - return a.func_type(pvec, rvec) - - -def limits(l: Limits): - return (l.min, l.max) + """ + Parse a WebAssembly binary module into its corresponding KAST representation. + Args: + wasm_bytes: A binary IO stream containing the WebAssembly module bytes. + filename: Optional filename associated with the module. Currently unused. -def global_type(t: GlobalType): - vt = val_type(t.valtype) - if t.mut is Mutability.const: - return a.global_type(a.MUT_CONST, vt) - return a.global_type(a.MUT_VAR, vt) + Returns: + A KAST term of sort `ModuleDecl` representing the parsed WebAssembly module. + """ + return parse_module(wasm_bytes) diff --git a/pykwasm/src/tests/integration/test_binary_parser.py b/pykwasm/src/tests/integration/test_binary_parser.py index d0c76404f..e396317ad 100644 --- a/pykwasm/src/tests/integration/test_binary_parser.py +++ b/pykwasm/src/tests/integration/test_binary_parser.py @@ -14,11 +14,12 @@ if TYPE_CHECKING: from pyk.kast import KInner + from pyk.kore.syntax import Pattern from pyk.ktool.krun import KRun BINARY_DIR = Path(__file__).parent / 'binary' -BINARY_WAT_FILES = BINARY_DIR.glob('*.wat') +BINARY_WAT_FILES = list(BINARY_DIR.glob('*.wat')) sys.setrecursionlimit(1500000000) @@ -39,7 +40,7 @@ def test_wasm2kast(krun_llvm: KRun, wat_path: Path) -> None: run_module(krun_llvm, module) -def run_module(krun: KRun, parsed_module: KInner) -> None: +def run_module(krun: KRun, parsed_module: KInner) -> Pattern: try: # Create an initial config config_kast = krun.definition.init_config(KSort('GeneratedTopCell')) @@ -53,7 +54,7 @@ def run_module(krun: KRun, parsed_module: KInner) -> None: config_kore = krun.kast_to_kore(config_with_module, KSort('GeneratedTopCell')) # Run the config - krun.run_pattern(config_kore) + return krun.run_pattern(config_kore) except Exception as e: raise Exception('Received error while running') from e diff --git a/pykwasm/src/tests/unit/test_binary_parser.py b/pykwasm/src/tests/unit/test_binary_parser.py new file mode 100644 index 000000000..9199d216b --- /dev/null +++ b/pykwasm/src/tests/unit/test_binary_parser.py @@ -0,0 +1,88 @@ +import io +import struct + +import pytest + +from pykwasm.binary import floats, integers +from pykwasm.binary.utils import WasmEOFError + + +def stream(data: bytes) -> io.BytesIO: + """Helper: wrap bytes in a seekable stream.""" + return io.BytesIO(data) + + +class TestFloats: + VALUES = [0.0, 3.14, -1.5, 1.23456789, -9.99, float('inf'), float('-inf')] + + @pytest.mark.parametrize('value', VALUES) + def test_f32(self, value: float) -> None: + encoded = struct.pack(' None: + encoded = struct.pack(' None: + with pytest.raises(WasmEOFError): + floats.f32(stream(b'\x00\x01')) # only 2 bytes, needs 4 + + def test_f64_eof_raises(self) -> None: + with pytest.raises(WasmEOFError): + floats.f64(stream(b'\x00\x01\x02\x03\x04')) # only 5 bytes, needs 8 + + +class TestIntegers: + U32_VALUES = [0, 1, 127, 128, 300, 624485, 2**32 - 1] + U64_VALUES = [0, 1, 2**32, 2**35 + 1, 2**64 - 1] + I32_VALUES = [0, 1, -1, 127, -128, 2**31 - 1, -(2**31)] + I64_VALUES = [0, 1, -1, 2**63 - 1, -(2**63)] + + @staticmethod + def encode_uleb128(value: int) -> bytes: + buf = [] + while True: + b = value & 0x7F + value >>= 7 + if value: + buf.append(b | 0x80) + else: + buf.append(b) + break + return bytes(buf) + + @staticmethod + def encode_sleb128(value: int) -> bytes: + buf = [] + while True: + b = value & 0x7F + value >>= 7 + if (value == 0 and b & 0x40 == 0) or (value == -1 and b & 0x40 != 0): + buf.append(b) + break + buf.append(b | 0x80) + return bytes(buf) + + @pytest.mark.parametrize('value', U32_VALUES) + def test_unsigned_32(self, value: int) -> None: + encoded = self.encode_uleb128(value) + assert integers.u32(stream(encoded)) == value + + @pytest.mark.parametrize('value', U64_VALUES) + def test_unsigned_64(self, value: int) -> None: + encoded = self.encode_uleb128(value) + assert integers.u64(stream(encoded)) == value + + @pytest.mark.parametrize('value', I32_VALUES) + def test_uninterpreted_32(self, value: int) -> None: + expected = integers.to_uninterpreted(32, value) + encoded = self.encode_sleb128(value) + assert integers.i32(stream(encoded)) == expected + + @pytest.mark.parametrize('value', I64_VALUES) + def test_uninterpreted_64(self, value: int) -> None: + expected = integers.to_uninterpreted(64, value) + encoded = self.encode_sleb128(value) + assert integers.i64(stream(encoded)) == expected diff --git a/pykwasm/src/tests/unit/test_wasm2kast.py b/pykwasm/src/tests/unit/test_wasm2kast.py deleted file mode 100644 index 2bf7a5f5b..000000000 --- a/pykwasm/src/tests/unit/test_wasm2kast.py +++ /dev/null @@ -1,29 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING - -import pytest -from pyk.kast.inner import KApply -from wasm.datatypes import ExternAddress, FunctionAddress, Limits, Table, TableType - -from pykwasm import wasm2kast -from pykwasm.kwasm_ast import KInt, externref, funcref - -if TYPE_CHECKING: - from pyk.kast import KInner - - -TABLE_TEST_DATA = ( - (Table(TableType(Limits(0, None), FunctionAddress)), KApply('limitsMin', [KInt(0)]), funcref), - (Table(TableType(Limits(0, 100), ExternAddress)), KApply('limitsMinMax', [KInt(0), KInt(100)]), externref), -) - - -@pytest.mark.parametrize(('input', 'limits', 'typ'), TABLE_TEST_DATA) -def test_table(input: Table, limits: KInner, typ: KInner) -> None: - # When - t = wasm2kast.table(input) - - # Then - assert limits == t.args[0] - assert typ == t.args[1] diff --git a/pykwasm/uv.lock b/pykwasm/uv.lock index ab7ae579e..315599b54 100644 --- a/pykwasm/uv.lock +++ b/pykwasm/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 3 +revision = 2 requires-python = ">=3.10, <4" resolution-markers = [ "python_full_version >= '3.15'", @@ -137,11 +137,11 @@ wheels = [ [[package]] name = "certifi" -version = "2026.5.20" +version = "2026.6.17" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/ce/ee2ecad540810a79593028e88299baeae54d346cc7a0d94b6199988b89b1/certifi-2026.5.20.tar.gz", hash = "sha256:69dea482ab64caa7b9f6aba1c6bf48bb6a5448d1c0f1b17ab42ad8c763a5344d", size = 135422, upload-time = "2026-05-20T11:46:50.073Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c9/c7/424b75da314c1045981bd9777432fad05a9e0c69daa4ed7e308bbaffe405/certifi-2026.6.17.tar.gz", hash = "sha256:024c88eeec92ca068db80f02b8b07c9cef7b9fe261d1d535abfd5abd6f6af432", size = 134594, upload-time = "2026-06-17T10:31:07.894Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/59/8c/57e832b7af6d7c5abe66eb3fbe3a3a32f4d11ea23a1aa7131371035be991/certifi-2026.5.20-py3-none-any.whl", hash = "sha256:3c52e209ba0a4ad7aebe60436a4ab349c39e1e602e8c134221e546902ad25897", size = 134134, upload-time = "2026-05-20T11:46:48.578Z" }, + { url = "https://files.pythonhosted.org/packages/ef/2f/c5464532e965badff2f4c4c1a3a83f5697f0d7c407ed0cda44aaa99bb451/certifi-2026.6.17-py3-none-any.whl", hash = "sha256:2227dcbaafe0d2f59279d1762ddddc37783ed4354594f194ffc31d20f41fc3db", size = 133289, upload-time = "2026-06-17T10:31:06.348Z" }, ] [[package]] @@ -312,115 +312,100 @@ wheels = [ [[package]] name = "coverage" -version = "7.14.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/54/fd/0ab2772530e946e1be1abd0bc09e647ec9b02e88f0867857601fefca8953/coverage-7.14.1.tar.gz", hash = "sha256:30c08f7d90415aa98b3c990385dea2939b0da55f38515e5b369b83655f8523be", size = 920132, upload-time = "2026-05-26T20:41:36.783Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/92/69/0d2ef01ff4b8fcecd4cba920d11e92fa4f96ae412441d3b56a90a258e69b/coverage-7.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3e3680291c4a1d0dadfa84a2c459576a4af5133abb617905714339a0c73138cf", size = 219722, upload-time = "2026-05-26T20:38:14.002Z" }, - { url = "https://files.pythonhosted.org/packages/f8/ae/9afdeaa31b9d9ce98124b6abf8bb49119bf71aecae04f8567c189d91299f/coverage-7.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a5274669f37f2343635a347b91a60777621341ab3378e9c6ac9335eee704bddf", size = 220240, upload-time = "2026-05-26T20:38:17.424Z" }, - { url = "https://files.pythonhosted.org/packages/51/69/c998589871df7ea7dba865cc5ee32b5a3e1d47ba6c68ef91104c7c46fa5e/coverage-7.14.1-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cfe5a5fec635799ef33428f1e5e61bafa45a92a96190ba731561ba558ccc214d", size = 246981, upload-time = "2026-05-26T20:38:19.266Z" }, - { url = "https://files.pythonhosted.org/packages/fc/10/1c7d04c13040dac531d21b712bbe08f902e6dd9b58f5d77875c4d030f8f2/coverage-7.14.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:62a9f70b52e0b5a95cfef4a5c5641b06983cadc5e538a3feeb5c00211f523ac2", size = 248812, upload-time = "2026-05-26T20:38:20.75Z" }, - { url = "https://files.pythonhosted.org/packages/c1/65/2a38a4607ef27cadcfbcee034dba5830ae2569f90144a0f4c7dbf47d30b0/coverage-7.14.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c18ebc343e15be53049b3a2dce38fe82d58f37e20ab9094b3a39c0aa4f6bb47", size = 250675, upload-time = "2026-05-26T20:38:22.159Z" }, - { url = "https://files.pythonhosted.org/packages/c9/a2/a446ed9752a4a59b79e0fb6cbb319f6facb2183045c0725462625e66f87e/coverage-7.14.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b84ffdf877644e7096aa936991efeed873f7f3df57b9cd001312b7668ab08550", size = 252590, upload-time = "2026-05-26T20:38:23.63Z" }, - { url = "https://files.pythonhosted.org/packages/9e/fd/e81fbd7ba752365546e9842b1cbdaad3d6919d2a522c590aef16a281ec5e/coverage-7.14.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e854312c4103f2ad4c0dc023b69b77ebfd2c89db5f86c4c94dc2353f9a92167e", size = 247691, upload-time = "2026-05-26T20:38:25.057Z" }, - { url = "https://files.pythonhosted.org/packages/53/35/f3c26fdaae9ea937d154ca4d372e5ea0a4167ff70d36c6074ac2eacb2f83/coverage-7.14.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c643734307300234fafa36bf2a040a7235f8f177ea1fd6ec1423aea6fb7b929f", size = 248716, upload-time = "2026-05-26T20:38:26.406Z" }, - { url = "https://files.pythonhosted.org/packages/2e/14/940b6c49551fd343e8507ee2b0ba7af5d0aa04ed5bf768285cb7c72a9884/coverage-7.14.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:84ac9499e48700399a5dd0ea7085b5091961fec52c68d66b4ec0d3cf7f4441b1", size = 246721, upload-time = "2026-05-26T20:38:28.282Z" }, - { url = "https://files.pythonhosted.org/packages/aa/2c/40fc0634186c28292a662dff578866b3913983d6c375a3c2a74020938719/coverage-7.14.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:7f02d09f70776579b926d889a4c9c235070a1f47c40458aeaca563fae5acfdb5", size = 250533, upload-time = "2026-05-26T20:38:29.753Z" }, - { url = "https://files.pythonhosted.org/packages/de/e3/2c26bf1e811f9df991ff2a9bdddebdd13ee0665d564df7d05979f9146297/coverage-7.14.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:ce66d8e46da2bb5ee313a745cbd2e391d319176c1f7a9451bfcd3a2fb920859b", size = 246990, upload-time = "2026-05-26T20:38:31.516Z" }, - { url = "https://files.pythonhosted.org/packages/a8/b0/060260ef56bd92363ebdce0c7095ce422b06e69aae71828efeca473ab1ca/coverage-7.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c912c259304cfb5ee584481cfb7ce1ff932b4d61e6c9140b8f19cb7b5ed82332", size = 247593, upload-time = "2026-05-26T20:38:33.065Z" }, - { url = "https://files.pythonhosted.org/packages/63/f3/501502046efeb0d6d94b5ca54941d95f1184183dd6bdb7f283985783bb4a/coverage-7.14.1-cp310-cp310-win32.whl", hash = "sha256:1238cb94638e610e972c60dac68e813f868dc7d6e982535270558443058d9d59", size = 222330, upload-time = "2026-05-26T20:38:35.36Z" }, - { url = "https://files.pythonhosted.org/packages/a0/5d/1bf99f2c558f128faf7906817ccbdb576ba815d3b41ce2ac1719b70a3663/coverage-7.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:fc459e5d73be2d6332fcfe8dbf3d8994671fe33c700f4565988ecfa511547253", size = 223261, upload-time = "2026-05-26T20:38:37.196Z" }, - { url = "https://files.pythonhosted.org/packages/7d/d7/477ad149490e6cb849f28abea1dabb9c823cea72e7500c81b4240ce619c0/coverage-7.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:478b5bcd63c2e1357c5c7e16c070690df7b07f676b1c114d7b93e533c664309f", size = 219848, upload-time = "2026-05-26T20:38:38.715Z" }, - { url = "https://files.pythonhosted.org/packages/91/82/a5eb47257c50601bb7b9a9d2857c67b7a3a85ad74180eb2c98bb1fbe0ce5/coverage-7.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a24a81f9715ee42ef59a316cc11611c98fe23920f7c81861315c9f3ff4a230f4", size = 220354, upload-time = "2026-05-26T20:38:40.232Z" }, - { url = "https://files.pythonhosted.org/packages/43/8b/78419b5391a5cb706b6544390507e469d83ffc9a8248b02c4011aceb9365/coverage-7.14.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:196a13319ad88d6d8ef5ab489ec4f44ddde2143c0c7d5b27786f6c3ffd56a7e1", size = 250771, upload-time = "2026-05-26T20:38:41.782Z" }, - { url = "https://files.pythonhosted.org/packages/77/63/e77aaacd491182210d639636b7a8bba23ffffa9b82aa3762da9431855fa9/coverage-7.14.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3d452fd08b5c72c5167c93e6867b5c08500bd40f2a21e1e854a500550b6cc36f", size = 252683, upload-time = "2026-05-26T20:38:43.305Z" }, - { url = "https://files.pythonhosted.org/packages/65/1c/a022e3cfbec2ac241640003cb3a817e161d9c7f5aa9b49173756cdc03204/coverage-7.14.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:23bf7fa51ac02e07fc7c96849b82946da47ae862dc8f86d183b2a4864fc38129", size = 254791, upload-time = "2026-05-26T20:38:45.361Z" }, - { url = "https://files.pythonhosted.org/packages/61/d6/967e408aca4c1ceb88cb0cc677169110ae7f5995fb5eaf5fb1f5a1bb8f5d/coverage-7.14.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bcaa50684dcaadfa599ac48f81103c756d791cfd85c97203d2217c593d48b860", size = 256748, upload-time = "2026-05-26T20:38:46.91Z" }, - { url = "https://files.pythonhosted.org/packages/b8/be/869188f7fe28638078ec479331ace6dc5f7b40b7153eb616f47ab79404d8/coverage-7.14.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4ea1c034f95c9b056e856b794630b17f9fa3d57e4800ff1e503d3be0f9c9078c", size = 250907, upload-time = "2026-05-26T20:38:48.493Z" }, - { url = "https://files.pythonhosted.org/packages/07/aa/adb7d3b4278d690e68703abcd76ab1b948242e3668d921711551b78f9ddb/coverage-7.14.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c7e057326434e441306226fbeb5d1aaf14a2637efe97ba668306635835f32ad7", size = 252483, upload-time = "2026-05-26T20:38:50.074Z" }, - { url = "https://files.pythonhosted.org/packages/43/61/331c74103c62dcb0c4b9b3a0de9a61aca016208b0a90f109592a9f9ecc28/coverage-7.14.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:59baf88468dbc8d63b1887afd92bda52e40bb1561696e5819670601403810cec", size = 250545, upload-time = "2026-05-26T20:38:51.613Z" }, - { url = "https://files.pythonhosted.org/packages/f6/b6/c5dae3c104d89be04828f61810e6b3473825482e4c288cc4ed04553e08ae/coverage-7.14.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d34d75f892b3ab73ba11cab5442cce7b3e168fd64162b16f0e1e0d09c508edef", size = 254310, upload-time = "2026-05-26T20:38:53.503Z" }, - { url = "https://files.pythonhosted.org/packages/ad/a1/2b9d5863e3b83c01ad8199e3c597802fbb3a9dc90b058885804c20296d31/coverage-7.14.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:3a56abc20a472baf0304c455721bc601477440d28ecfde8a03dde79ede07e0df", size = 250266, upload-time = "2026-05-26T20:38:55.414Z" }, - { url = "https://files.pythonhosted.org/packages/7f/5e/0e511fbdb269359be26fe678a1c3fa1f2aa2a01573cc3f54268c8d6d4797/coverage-7.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6a3cb83d1552c0cd1b4906655b6a33fd4a8473229633a901c6b73bf86914dee9", size = 251174, upload-time = "2026-05-26T20:38:57.141Z" }, - { url = "https://files.pythonhosted.org/packages/85/10/e55307b622b3dd9671cb321824502dc10f93e72f2802b9946159a8edadeb/coverage-7.14.1-cp311-cp311-win32.whl", hash = "sha256:10274a1fbeb8ec5d72966e17bb198a3104257aca4ac09d98667c5f8aca8c8548", size = 222354, upload-time = "2026-05-26T20:38:58.727Z" }, - { url = "https://files.pythonhosted.org/packages/71/cf/107421693cfb71e4f1ca5bf70443f64d4161878068d07a3e51c7ad21d17b/coverage-7.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:87ebdf787d4888e3f3f2d523eadc6e18c6d18c6d0eb173801a189641627fb37e", size = 223290, upload-time = "2026-05-26T20:39:00.413Z" }, - { url = "https://files.pythonhosted.org/packages/b8/1d/3e3644585eb29e9dafefb19555078529a4d7cce12bd21929664eea989277/coverage-7.14.1-cp311-cp311-win_arm64.whl", hash = "sha256:dd34767fa19848d35659ffc0a75314f58c7af3f1cd87ec521e8292a1238398a3", size = 221953, upload-time = "2026-05-26T20:39:02.159Z" }, - { url = "https://files.pythonhosted.org/packages/3d/b7/bdbb725ba02c5b42825b200c940f38b7a54fcad24627b7192f78f8110d76/coverage-7.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a06c76364a9360e33d6d23769aefdf7f66f38e2ffb60ceb1baaa4989d83b695c", size = 220022, upload-time = "2026-05-26T20:39:03.702Z" }, - { url = "https://files.pythonhosted.org/packages/72/81/fdc0898a55c6219223291ec1a1fe89966ef212ce82276aa0899df84b5de0/coverage-7.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fad54e871165f6ec2f536063ac74c3104508a12963e64072ba44bd822de52b0c", size = 220379, upload-time = "2026-05-26T20:39:05.381Z" }, - { url = "https://files.pythonhosted.org/packages/de/72/de048c4a25e13bce59ac6a339351c10bdf2515e07459afcdaf04dc3143a2/coverage-7.14.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:84b535f00655ecafe1d929d1fb00ed5d6fa3051ea643ab2c161a3887b86f294b", size = 251888, upload-time = "2026-05-26T20:39:07.367Z" }, - { url = "https://files.pythonhosted.org/packages/28/30/300c343f68beb9d4cbb64ec81e58c5b6b80b56927f72d2b38654ac26e013/coverage-7.14.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6b6b0853b895fe0e98cbfc580d1ec3393d9302b4b1e96a77b3f5c91fdab899e6", size = 254624, upload-time = "2026-05-26T20:39:09.037Z" }, - { url = "https://files.pythonhosted.org/packages/b1/ed/7b25642496e8170b6bac14adce00537c6e5fa2d586159401a4de3e8b49e6/coverage-7.14.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:442cc9c952b2df400cda54bb04ab87330cf2cd08a8692cbbea36773531eb6f37", size = 255739, upload-time = "2026-05-26T20:39:10.889Z" }, - { url = "https://files.pythonhosted.org/packages/7f/a2/abd210b8c4e29c24e4624916db97bb519097a91034aaeb767f937e7da794/coverage-7.14.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8270544c361ed405a27a060dbc9ed2c124b084d96dfdc2d9a2510482aef981ad", size = 257998, upload-time = "2026-05-26T20:39:12.722Z" }, - { url = "https://files.pythonhosted.org/packages/7f/24/7c50beed3792fe62f6ce0545c6686ce83379719e2c0276179333d97eae92/coverage-7.14.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:48b283b1dd6372e8de2a7a9a4c4d5dc06f4d4fd209b876f3c88a7a205a0c8f84", size = 252296, upload-time = "2026-05-26T20:39:14.259Z" }, - { url = "https://files.pythonhosted.org/packages/15/05/0f874628ebcbfc77ead559ff210281ef06a97db08481832e7dd39274a135/coverage-7.14.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5b0c99ba93a07d56f6df340bb79be53202a082b2fdb81bfe6190b741a3470d54", size = 253658, upload-time = "2026-05-26T20:39:15.923Z" }, - { url = "https://files.pythonhosted.org/packages/99/6f/ca6ad067364b337ef997802115e7ecad2abd2248b05471464b0dea02b4d4/coverage-7.14.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e471bc5769ff073b058cfadb0d736b56ce067c8560eabeb0da88462df98c23e7", size = 251803, upload-time = "2026-05-26T20:39:17.537Z" }, - { url = "https://files.pythonhosted.org/packages/c0/30/b9b4d377cd9f40baf228068f5a81faf8450c6228503011bd499708483a50/coverage-7.14.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f497a1ea81d4cd7c10ddcaa685135b9aabd291af3d55775a9ddf3cb7a364cdd9", size = 255873, upload-time = "2026-05-26T20:39:19.414Z" }, - { url = "https://files.pythonhosted.org/packages/3c/21/7c721a9e5e6bb88547d30a787aefb97512d3f54c1324c7488d9b3743f7f9/coverage-7.14.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:2222be86d0b54f5dd5a38f45f17f315f737245e857bf0bdedc70734f84a13c02", size = 251372, upload-time = "2026-05-26T20:39:21.169Z" }, - { url = "https://files.pythonhosted.org/packages/9d/8c/f8ae5a2200130e1503cd7661a6cd3b2b7bacef98277fbf3571fb13f8b766/coverage-7.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:85e85586565842f6932abebd4c18bcb1074223dc0b3576e7d173ca710622813a", size = 253245, upload-time = "2026-05-26T20:39:23.097Z" }, - { url = "https://files.pythonhosted.org/packages/34/62/70a9024672a5f6910517d9628c52c9afbdd3cf8f46426af52bb148a56fff/coverage-7.14.1-cp312-cp312-win32.whl", hash = "sha256:4a28fd227808366b196a75476dced2eb35b351d6766ba9c858dc93319e87f4f1", size = 222567, upload-time = "2026-05-26T20:39:24.868Z" }, - { url = "https://files.pythonhosted.org/packages/f6/81/8b7cd386839b039ebe1855733b9f9449a8dec5d79564018234f185a7fa70/coverage-7.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:54acdb6674a4661768d7bf7db32dfb9f46ab1d764f8aba6df75ce1a6a088724e", size = 223372, upload-time = "2026-05-26T20:39:26.603Z" }, - { url = "https://files.pythonhosted.org/packages/ae/ba/b44d472022f620d289d95fa830143235c0c36461c6f2437ea8d51e5481ed/coverage-7.14.1-cp312-cp312-win_arm64.whl", hash = "sha256:99cd41ff91afd94896fea3bc002706b6ae4ce95727d06e4a0f39c0a8d8bd8b1a", size = 221989, upload-time = "2026-05-26T20:39:28.242Z" }, - { url = "https://files.pythonhosted.org/packages/8a/9e/5f6d56327c62b185225d145191c607e07515294a0aa6338e58805cd4a5ac/coverage-7.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:be9f2c802dcfce3f71298303aa5dad0dce440a76c52f2f60dacd8656dab78793", size = 220044, upload-time = "2026-05-26T20:39:29.902Z" }, - { url = "https://files.pythonhosted.org/packages/75/92/e82aca356744cbbc0f77a0b623e38918c1872361963413a3bab5d0340393/coverage-7.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6223a72fd0e4c7156353ec0f08a5f93623e1d3034d0e2683b9bb8ea674131b1d", size = 220412, upload-time = "2026-05-26T20:39:31.561Z" }, - { url = "https://files.pythonhosted.org/packages/27/c9/385bde0bf7ed0f4bf3a7ee5367060a86b5d218718cfd6fb943c0f836b34f/coverage-7.14.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7279d2110a28cebc738b6459ecda2771735a4c18465fbbd36b3288fe5ed92247", size = 251412, upload-time = "2026-05-26T20:39:33.337Z" }, - { url = "https://files.pythonhosted.org/packages/51/8c/23faf6a2343a0d17f960a4bd56c43bc7eb4cf312f774dd6ceebd82c7d8fc/coverage-7.14.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9eeb3fcbc13ba40dfbdb22d01d196a28e9cef9ed4c29b60061a1e0e823a9929d", size = 254008, upload-time = "2026-05-26T20:39:35.009Z" }, - { url = "https://files.pythonhosted.org/packages/42/06/36f4aa9ca8a815e6036156e80706a67828bb97bd826948244f6996dda957/coverage-7.14.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f0cfc27c539f07cf5c0a4cfe211d0b6cae039f8f40526dbaa71944e64b50a7b", size = 255241, upload-time = "2026-05-26T20:39:36.71Z" }, - { url = "https://files.pythonhosted.org/packages/ca/79/95266316352f90f6b1c6736bb413302edfde2453fb32422d3911642691b3/coverage-7.14.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:221c70f316241a78e77e607c227cefc8808d4e08f28d99c04f35694690e940be", size = 257373, upload-time = "2026-05-26T20:39:38.412Z" }, - { url = "https://files.pythonhosted.org/packages/e3/9c/58316d1f66c488b5fca8a0eb3e98348807813efa8a0d0833b9021be27488/coverage-7.14.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:da028256b04ec30e5e0114b6f76172938c313991f0a2d3d894271315cf5d5e43", size = 251635, upload-time = "2026-05-26T20:39:40.268Z" }, - { url = "https://files.pythonhosted.org/packages/ef/5a/ca2398a568e16fed7bb713e84ba3603a7164fb65779abe645c565ec890d5/coverage-7.14.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:76a085d7005236a767e3426148b2c407e53ad61695c562f8a81da2d373324901", size = 253373, upload-time = "2026-05-26T20:39:42.145Z" }, - { url = "https://files.pythonhosted.org/packages/6e/2c/0396562c32deaebe7be51d865b3a41e9a87d7561acafe1a28f53b07e019a/coverage-7.14.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b553d04b5e778a8e56d57eb134aff42a92718ecba45e79c4764ecfa40efd92ff", size = 251341, upload-time = "2026-05-26T20:39:43.907Z" }, - { url = "https://files.pythonhosted.org/packages/fd/8f/a94f9221184c9cae1ee115820e3798e48b6b17777a9f19e46fb9a0c8dc74/coverage-7.14.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:46f714d2fb8ae2f4f29f23ada7f1e79b759fff5a70f94a1dac23af204c3ec9e4", size = 255497, upload-time = "2026-05-26T20:39:46.166Z" }, - { url = "https://files.pythonhosted.org/packages/71/69/505d70e47db1eaebcd002c39759707621ef184cd6b1ae084d9f41293f323/coverage-7.14.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:1896f5e19ff3f0431c7ce2172adc54890fd97f86b59ced8ca1649145d9ffe35d", size = 251159, upload-time = "2026-05-26T20:39:48.03Z" }, - { url = "https://files.pythonhosted.org/packages/e0/aa/58681c383aa33a9d2ed40a02d7a22fbf780d1fa4d575396365777828198c/coverage-7.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:62fd185ef9df3c33d1c8178c5af105f762afbad96038de9a4ae100aa6297ca33", size = 252934, upload-time = "2026-05-26T20:39:49.872Z" }, - { url = "https://files.pythonhosted.org/packages/eb/fd/11c928cd6bdffc7074bb5965c173d9ebf517fb00205e1da524b98d29ef92/coverage-7.14.1-cp313-cp313-win32.whl", hash = "sha256:ab4af6352741a604c431c6072fce5bee33bf0f20dc7a56618d6bf6bb89e9810c", size = 222584, upload-time = "2026-05-26T20:39:51.68Z" }, - { url = "https://files.pythonhosted.org/packages/6f/92/fb416fc26d340dcba19518c418d6048e913186e17243982c5e435e41fa7a/coverage-7.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:7af486dabe8954d03b087f0021540897afe084f04e16ff5579e08cc46f871416", size = 223394, upload-time = "2026-05-26T20:39:53.472Z" }, - { url = "https://files.pythonhosted.org/packages/73/c6/02d56e3867972f77d5036de924643f26c056e848f00452cafb4dbc3c29b4/coverage-7.14.1-cp313-cp313-win_arm64.whl", hash = "sha256:2224f89ffd0c5605ccce1ed7a584da162bc7c55f601ab1c946bc9de31a486b42", size = 222015, upload-time = "2026-05-26T20:39:55.374Z" }, - { url = "https://files.pythonhosted.org/packages/4d/9e/fcc77914050df73f7662fa1f00902774c79c075a8388ab334074574bf77e/coverage-7.14.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:de286598cc65d2b489411174b1faec2f5a7775fb3201fd925db2a76b4030f37d", size = 220733, upload-time = "2026-05-26T20:39:57.189Z" }, - { url = "https://files.pythonhosted.org/packages/f7/67/2963cbdaf5cbadec44efa3a1e39eaa1f02df4079585f05387607a221e126/coverage-7.14.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:042c46ded7c288aeb07cf14a28b6c1e10b78fcba40171c3fa1e939377eeef0b5", size = 221086, upload-time = "2026-05-26T20:39:59.019Z" }, - { url = "https://files.pythonhosted.org/packages/c8/c5/8701645574e11881f2f47d8930f98bc48b5d43b25eb5b4430dfc4a2f9f48/coverage-7.14.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f4ddbe407477f04c45115d1a4e5bc480f753553b534d338d4c3358b1cdd0ea52", size = 262381, upload-time = "2026-05-26T20:40:00.822Z" }, - { url = "https://files.pythonhosted.org/packages/7c/28/7a64d73598263e0c5abd5084211a8474488d31b3c552ff531c719dfcff62/coverage-7.14.1-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d13e6725992e2d2fd7d81d4f5241952d13740121dfd501da09201be39b2c003a", size = 264458, upload-time = "2026-05-26T20:40:02.506Z" }, - { url = "https://files.pythonhosted.org/packages/fa/d8/4969179db9f7eb4df218e69540adf829d1c835f59452513d065d15446802/coverage-7.14.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f747dc8edcfe740130f28f32f3995e955494285717e86ee25af51db2219df08a", size = 266884, upload-time = "2026-05-26T20:40:04.421Z" }, - { url = "https://files.pythonhosted.org/packages/a6/78/a45d5794dbc9bafd97afc96a4377c86c7820d78b6cf51b89bc1d4e919275/coverage-7.14.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ced2f09ef276fd58611a1ef502164ad266d2b75174e5a40cabbdb4033f9f6cf2", size = 268022, upload-time = "2026-05-26T20:40:06.298Z" }, - { url = "https://files.pythonhosted.org/packages/21/cb/4f5e354e9e3e67af96bd4e57113e6db6b22298c7168b13eec408a549903d/coverage-7.14.1-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b84800013769a78ccb9ef4659402e26d06867e337b61ec365f77ad008adea80e", size = 261631, upload-time = "2026-05-26T20:40:08.226Z" }, - { url = "https://files.pythonhosted.org/packages/ec/49/eced49af4cb996d5d8b7e94e736175c513e4facd3398507b89892b4326d8/coverage-7.14.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:ea8cd6ca0ee9f616aaef3afc6882e32c2cbf18b00d96313ffd76af650574034d", size = 264443, upload-time = "2026-05-26T20:40:10.137Z" }, - { url = "https://files.pythonhosted.org/packages/f1/d8/5603a88a7c5913a6b54f6cb1a8c46f7b39cbb30f27cd3f492908da09b2d7/coverage-7.14.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:aa5e304a873fabddc11e484e9b6b738bd38bd7bed17b09aa84eecf5332e8b8bb", size = 262069, upload-time = "2026-05-26T20:40:11.999Z" }, - { url = "https://files.pythonhosted.org/packages/f0/59/2ae3cb79da554a06c8619d6c88ea19dd1e4aed4b834b6a83bb1fa243bdc5/coverage-7.14.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:5a1c5215be81035e629d5bc756650634d0bf31991038db7a0eccb90f025ce16d", size = 265780, upload-time = "2026-05-26T20:40:13.858Z" }, - { url = "https://files.pythonhosted.org/packages/af/5f/b130c1dc999031f2648bd25317fbce505ad8d5562079b4ed81e736a84967/coverage-7.14.1-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:79058c47dae6788504b5effb319961bcd72d7240551464b91d474bc0ed186d69", size = 260970, upload-time = "2026-05-26T20:40:16.142Z" }, - { url = "https://files.pythonhosted.org/packages/87/d1/ec13ccddeb48ec963bdfa72a11224bac2584bd045ba13beca82f8113e9c7/coverage-7.14.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:370c5afae3fa0658e11694a32b24c2778f6bc2d17718121f94ee185e69f26b54", size = 263157, upload-time = "2026-05-26T20:40:18.382Z" }, - { url = "https://files.pythonhosted.org/packages/cf/c2/cd91ead503045161092d3845f7bb95ea2f25131ce96d3e314dd835d91b9c/coverage-7.14.1-cp313-cp313t-win32.whl", hash = "sha256:3758dd0a7f1fa57365ef2e781df0f0731d38b6e3772259d13dae4bd8a958d4b1", size = 223259, upload-time = "2026-05-26T20:40:20.381Z" }, - { url = "https://files.pythonhosted.org/packages/71/9f/1e28d97e6bd2c76b07f38b7c02870f1371255ff6717f54eca578fcbbdd0e/coverage-7.14.1-cp313-cp313t-win_amd64.whl", hash = "sha256:6ff665fb023a77386fe11685190cee1f60a7d635994a30d9b0a061533d470fce", size = 224320, upload-time = "2026-05-26T20:40:22.316Z" }, - { url = "https://files.pythonhosted.org/packages/a9/e0/d936e908f0e1efa55e52b91e01b52f1055cef5e1ab2718493390ed8e2fb8/coverage-7.14.1-cp313-cp313t-win_arm64.whl", hash = "sha256:17a5a241e5997621a956a7f402a7433ef4221e5152809b785bec79e2323799f1", size = 222577, upload-time = "2026-05-26T20:40:24.894Z" }, - { url = "https://files.pythonhosted.org/packages/d6/34/fc2f101b151af3799a101f0550b0454aa008afdc0add677394ec4aa8ea10/coverage-7.14.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d5ed429d0b8edaac649e889b4ffcedb6c80b06629a3f93050e3dddfb99235bee", size = 220091, upload-time = "2026-05-26T20:40:27.249Z" }, - { url = "https://files.pythonhosted.org/packages/3d/a7/1ebae2ab5b961b5c79bb09fe7b3ac99edb190d8be4a8c510b2cf66f46468/coverage-7.14.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8011224a62280e50dab346960c03cf47aca1a1e09e608c0fb33fd6e0cc8e9500", size = 220421, upload-time = "2026-05-26T20:40:30.084Z" }, - { url = "https://files.pythonhosted.org/packages/5e/90/92aca9cf0acc95123c96cd1eb1f08917897a7f5dee01e15738922971ec31/coverage-7.14.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:12c42ec1e14f553c4f817e989365982e646e27211f10a0f717855b94a79c8906", size = 251466, upload-time = "2026-05-26T20:40:32.542Z" }, - { url = "https://files.pythonhosted.org/packages/26/2b/78048cbe3b999f6cbf9cc0d90abba6a88a3e0863a8c1c6cbc762f3f8802f/coverage-7.14.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:06144cd511cf2624873a035c5069cf297144f6e77a73ee3d7a55b605ec5efb42", size = 253973, upload-time = "2026-05-26T20:40:34.473Z" }, - { url = "https://files.pythonhosted.org/packages/8e/21/c2e33b29d1cfde484a19d437afc343c6cd30b08d78cbbf9f5aff14e57b2b/coverage-7.14.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a311d8e1da24be5c1ccf85cbfb06315dbaa1703d5a1eab3f6432c72b837917c8", size = 255318, upload-time = "2026-05-26T20:40:38.154Z" }, - { url = "https://files.pythonhosted.org/packages/8e/ee/aad2f108d63b769121005302f16bf66db8625c88ceaba466942e09a2607e/coverage-7.14.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c79cead5b5bc584d9c71451cb984d0e3a84e0c0937379c8efcbf27c8d661b851", size = 257633, upload-time = "2026-05-26T20:40:40.164Z" }, - { url = "https://files.pythonhosted.org/packages/c2/f8/11a2c29b4fd76d9849f81d0bb812ec0017a9396df3217214e38934a8c837/coverage-7.14.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:dcbf65f1f66a26cdd88c35cf68fb4729c5d1cd2e88added72420541dfb212034", size = 251488, upload-time = "2026-05-26T20:40:42.631Z" }, - { url = "https://files.pythonhosted.org/packages/c9/b8/9a5820de4b8ac2b71d85e3b5fb49108d7469c665f0e2ad0dd7569023e305/coverage-7.14.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fd86572566fb40189a8260446158235159bc7a82dfbc87a3b39cf4fb57fcec1c", size = 253329, upload-time = "2026-05-26T20:40:45.208Z" }, - { url = "https://files.pythonhosted.org/packages/6b/ff/f33e4823667e27548e8fd8df44217515303f9808d0ff29817db56f87d990/coverage-7.14.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:7771b601718fdde84832c3a434ca9bbf4ae9adbc49d84198b4110700c3c77c36", size = 251291, upload-time = "2026-05-26T20:40:47.502Z" }, - { url = "https://files.pythonhosted.org/packages/68/9b/489db0ebb209054766b90a9014a45f6d26eb724c02ec21311c3733b5a644/coverage-7.14.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:39b21e212c55af06fa375e3dbf90a8a8e38792f3a910c580066d23563830ddd5", size = 255564, upload-time = "2026-05-26T20:40:49.372Z" }, - { url = "https://files.pythonhosted.org/packages/27/b5/16bc2d4c2409b23c7737edb68c83bc89e345f378050549fe1d75ac7d34d5/coverage-7.14.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:f2302660e32562a532b442480121aef8aa61a5bdb20b30bf0adab29f10a5a4b4", size = 251107, upload-time = "2026-05-26T20:40:51.677Z" }, - { url = "https://files.pythonhosted.org/packages/7d/0c/2629997469a00cd069d588a41c9dc887610f2775ae89d250c4791e65272a/coverage-7.14.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:03a6f93c1ec3b7f2e77b5dbcc5573a2c21f12529a5c6bbe0f16f72303cc2fa4d", size = 252764, upload-time = "2026-05-26T20:40:54.267Z" }, - { url = "https://files.pythonhosted.org/packages/d2/ee/f78d63c8f079e0d7211c7e2401fa17e311514534ba61bae03e4b287ce4ab/coverage-7.14.1-cp314-cp314-win32.whl", hash = "sha256:8a3ce026d73290f42f08dafecbd82c193a74df280461fbf97300fec51fd133ee", size = 222837, upload-time = "2026-05-26T20:40:56.496Z" }, - { url = "https://files.pythonhosted.org/packages/dc/b9/be539854f93a70dfbeec69117f33ec70dc42ff0b65b5b07ab8d40d04228e/coverage-7.14.1-cp314-cp314-win_amd64.whl", hash = "sha256:114c95ef29302423b87d159075805f4ab973254a2638a5d7d046c94887cc87d7", size = 223650, upload-time = "2026-05-26T20:40:58.351Z" }, - { url = "https://files.pythonhosted.org/packages/fe/9e/24e2842fef40f35ac82ba3a7719c8023d011bf3bf652d0675316a9d088a1/coverage-7.14.1-cp314-cp314-win_arm64.whl", hash = "sha256:a07891c3f4805442b31b71e84ba3cf29ed1aa9a428284e06deeb4b23e5b46343", size = 222218, upload-time = "2026-05-26T20:41:00.321Z" }, - { url = "https://files.pythonhosted.org/packages/0a/1d/ac0a9df5fe31c1e8bdd658074905fc12844a05c1a7e3fdb8417e97c31e23/coverage-7.14.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:1101a5ebb083aecb625ebb6209d4105b58f647b093cb2dc8122d7b33f743cfe1", size = 220822, upload-time = "2026-05-26T20:41:02.281Z" }, - { url = "https://files.pythonhosted.org/packages/32/cf/f964fd9aff20323f9f1a726c97135f8a76bcd87b92dad141a456a43f3c64/coverage-7.14.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:851b9e1e4e8a4608e77c79714b2e77c0970d2ed7202a05e92ae407817481887b", size = 221084, upload-time = "2026-05-26T20:41:04.593Z" }, - { url = "https://files.pythonhosted.org/packages/d8/5e/7e5ef2aba844de2b80d678619fcf0841b42e3f37f16411226f3fe4c1016f/coverage-7.14.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d5b89cdfb2ee051b71e8c3c70bd81a9eff81100f736a269136fe1a68efe00474", size = 262454, upload-time = "2026-05-26T20:41:06.641Z" }, - { url = "https://files.pythonhosted.org/packages/64/62/75809bded87015cc4935524218a2a8ed8dd1a8498bfed30a2f4f7a4b4d34/coverage-7.14.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0177614a0370f227888b4e436a7c55686d6a9f90eb1ade2b624ba685a1686e86", size = 264578, upload-time = "2026-05-26T20:41:08.556Z" }, - { url = "https://files.pythonhosted.org/packages/f3/42/d33392dc14633525012d2d504fa1a33b05538bf535f5c1d64675e5754b78/coverage-7.14.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2d69af5dea2de76fc485a83032a630523f985198b7e25be901ec60181587b01e", size = 266981, upload-time = "2026-05-26T20:41:10.824Z" }, - { url = "https://files.pythonhosted.org/packages/2a/49/0157c4428c2aca7f1e09d5565930586fd5ae36f1655f08b0daa7cf1fcae1/coverage-7.14.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:35ab22d91de736e8966b980dc355cbcdd2c6dbbcfe275f9a2991bc8a91b3df65", size = 268112, upload-time = "2026-05-26T20:41:12.966Z" }, - { url = "https://files.pythonhosted.org/packages/96/26/86b9ce71f4092b1ed325ce1421698081df1286b833400b6836912834d6e0/coverage-7.14.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:357d4e32935c36588aaba057d734fa32428c360c9fc2e4442afbf1b646beee6e", size = 261558, upload-time = "2026-05-26T20:41:15Z" }, - { url = "https://files.pythonhosted.org/packages/20/4c/c311210c5472cf5401d8422b0d7812cdd520f24417673afabda6c323faca/coverage-7.14.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:51bd64741cc6fa065abd300ede1afe5a5291ece9c31da8b24884deda48bcc3f8", size = 264447, upload-time = "2026-05-26T20:41:17.369Z" }, - { url = "https://files.pythonhosted.org/packages/fb/71/59513f8710ed3e6b0ac0a050a5b7e977bb9c9e880354863b5d00d8809256/coverage-7.14.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:9132cd363a68a4c3daa7c8704a654b1e39d3360f6f5b8ddd470608a945236c07", size = 262048, upload-time = "2026-05-26T20:41:19.309Z" }, - { url = "https://files.pythonhosted.org/packages/84/8d/bceed32dc494f5bbf50f775cd2e78ca814953942b5ea28d3c1c3ac316f14/coverage-7.14.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:07c6290b1697b862c0478eab545eec949a0d0e4d6d03497f446d706da3b4f2de", size = 265781, upload-time = "2026-05-26T20:41:21.559Z" }, - { url = "https://files.pythonhosted.org/packages/e7/c5/9348fe40dbfd4991aaf78df2c6c3098bfb2cc834d1fd362a64b4efef855a/coverage-7.14.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:5ea0c297e27133853b4d8a3eb799bff5a2dbd9f2f41537a240d337ac9b4df890", size = 260896, upload-time = "2026-05-26T20:41:23.428Z" }, - { url = "https://files.pythonhosted.org/packages/ca/92/1ea0f03929da7cf87206b1fa24f4c8e9c158be0455481af29ec0a1f3503f/coverage-7.14.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:01b7733daad0237daa01ef80fe2dfceffc911e6a17fa7b55d14aa8214eaaaecd", size = 263214, upload-time = "2026-05-26T20:41:25.419Z" }, - { url = "https://files.pythonhosted.org/packages/f6/a9/b2493c054c0e01a643266742ab45e15744e60743f9260cd930c7142b1124/coverage-7.14.1-cp314-cp314t-win32.whl", hash = "sha256:6adc5a36984624a70bf11d7184e20fa0a49aa7c47ffab43804106a1a695ea22e", size = 223624, upload-time = "2026-05-26T20:41:27.795Z" }, - { url = "https://files.pythonhosted.org/packages/fc/bd/3e1e6a57fccd2d7c83fcdf338e93ba98eb85c6e877dd34731ac585375490/coverage-7.14.1-cp314-cp314t-win_amd64.whl", hash = "sha256:ddf799247318f34dbcd2efa8c95a8d0642674e926bb1774cf9b63dfd2a389d1c", size = 224728, upload-time = "2026-05-26T20:41:30.098Z" }, - { url = "https://files.pythonhosted.org/packages/bb/d7/31066cf1d2f0c6c797fce911bcfa01dd35642dc6da992a950256097c5860/coverage-7.14.1-cp314-cp314t-win_arm64.whl", hash = "sha256:145986fe66647eb489f18d9a997567a3fd358584c4b5a808769113abc07466af", size = 222752, upload-time = "2026-05-26T20:41:32.123Z" }, - { url = "https://files.pythonhosted.org/packages/8a/3c/1a983b9a745d7f83d53f057bcc5bf79ba6a2bbc08266b3f0c7d6fe630c9b/coverage-7.14.1-py3-none-any.whl", hash = "sha256:a252f21c27e38347e60111a3266b03827422a7d5525951aceee313aa68bab1d2", size = 211815, upload-time = "2026-05-26T20:41:34.078Z" }, +version = "7.14.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b4/91/0a7c28934e50d8ac9a7b117712d176f2953c3170bccced5eaacfa3e96175/coverage-7.14.3.tar.gz", hash = "sha256:1a7563a443f3d53fdeb040ec8c9f7466aed7ca3dc5891aa09d3ca3625fa4387f", size = 924398, upload-time = "2026-06-22T23:10:25.584Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/bd/b01188f0de73ee8b6597cf20c63fccd898ad31405772f15165cb61a62c00/coverage-7.14.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:360bec1f58e7243e3405d3bdf7a1a8115aa9b448d54dc7cd6f7b7e0e9406b62e", size = 220378, upload-time = "2026-06-22T23:07:38.925Z" }, + { url = "https://files.pythonhosted.org/packages/33/eb/f7aa3cb46500b709070c8d12335446971ec8b8c2ea155fea05d2000b4b1f/coverage-7.14.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ed68faa5e85de2f3e400bc3f122e5c82735a58c8bb24b9f63a2215954ba17b2d", size = 220895, upload-time = "2026-06-22T23:07:41.536Z" }, + { url = "https://files.pythonhosted.org/packages/c3/c0/b41b8499fc9060ca40ad2a197d301155be1ead398f0f0bfdb27b2b4a660f/coverage-7.14.3-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:830c1fca669c572dec37ce9c838224ee45aac5be0f6961edf871e82e49d6537c", size = 247631, upload-time = "2026-06-22T23:07:43.244Z" }, + { url = "https://files.pythonhosted.org/packages/da/bb/e9ecea1307c6a549c223842cccbd5d55193cc27b82f26338782d4355047c/coverage-7.14.3-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a64caee2193563601dbaaa55fe2dcf597debef04a2f8f1fa8a07aa4bb7ac7a1e", size = 249460, upload-time = "2026-06-22T23:07:45.147Z" }, + { url = "https://files.pythonhosted.org/packages/59/cb/3821542809b7b726296fd364ed1c23d10a5770f1469957010c3b4bc5d408/coverage-7.14.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0096fd7559178f0cc9cf088f2dbd2a02ef85bacaa69732c633517286b4494610", size = 251324, upload-time = "2026-06-22T23:07:46.875Z" }, + { url = "https://files.pythonhosted.org/packages/76/27/f34f66f0ff152189ccc7b3f0582cf7909e239cb3b8c214362ed2149719b8/coverage-7.14.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6197e5a00183c11a8ce7c6abd18be1a9189fd8399084ffc95196f4f0db4f2137", size = 253237, upload-time = "2026-06-22T23:07:48.352Z" }, + { url = "https://files.pythonhosted.org/packages/22/81/aa363fa95d14fc892bd5de80edadc8d7cce584a0f6376f6336e492618e67/coverage-7.14.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7dfe427045520d6abca33687dfef767b4f635015893a1816c5decb12eb72ce18", size = 248344, upload-time = "2026-06-22T23:07:49.896Z" }, + { url = "https://files.pythonhosted.org/packages/66/fe/dc8a149441a3fea611cbbaf46bb12099adbe08f69903df1794581b0504b8/coverage-7.14.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9a3f142070eb7b82fc4085a55d887396f9c4e21250bccebe2ba22502c45b9647", size = 249365, upload-time = "2026-06-22T23:07:51.464Z" }, + { url = "https://files.pythonhosted.org/packages/f8/a2/0004127deee122e020be24a4d86ce72fa14ae28198811b945aabf91293b5/coverage-7.14.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:64b2055bb6e0dc945af35cdeceb3633e6ed9273475ef3af85592410fd6803803", size = 247369, upload-time = "2026-06-22T23:07:53.064Z" }, + { url = "https://files.pythonhosted.org/packages/1e/72/3654c004f4df4f0c5a9643d9abaed5b26e5d3c1d0ecabe788786cb425efa/coverage-7.14.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1551b4caac3e3ec9f2bfcec6bf3776e01c0edbdd2e240431a50ca1a1aac72c27", size = 251182, upload-time = "2026-06-22T23:07:54.789Z" }, + { url = "https://files.pythonhosted.org/packages/a5/2f/7bdcdf1e7c4d0632648852768063c25582a0a747bb5f8036a04e211e7eb7/coverage-7.14.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:583d50d59142f8549470bd6390471d0fe8b8c8d69d6a0f28ac71e05380cef640", size = 247639, upload-time = "2026-06-22T23:07:56.254Z" }, + { url = "https://files.pythonhosted.org/packages/03/dc/0e01b071f69021d262a51ce39345dd6bc194465db0acfc7b34fd89e6b787/coverage-7.14.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e0bb8a6bc7015efdf8a928753b25da1b9ca2d6f24ef04d2ee0688e486f32aae7", size = 248242, upload-time = "2026-06-22T23:07:57.692Z" }, + { url = "https://files.pythonhosted.org/packages/1c/51/08279e6ebe3479bf705db5fdc1a968e44ba1567e4cbc567f76b45f5e646e/coverage-7.14.3-cp310-cp310-win32.whl", hash = "sha256:d48400185564042287dc487c1f016a3397f18ab4f4c5d5ec36edc218f7ffa35b", size = 222431, upload-time = "2026-06-22T23:07:59.094Z" }, + { url = "https://files.pythonhosted.org/packages/40/2f/5c56670781fee5722ef0c415a74750c9a033bfacdb9d07b1493a0308108d/coverage-7.14.3-cp310-cp310-win_amd64.whl", hash = "sha256:eadea7aba74e40adee867a8c0eec17b820b061d308a4b014f7a0e118c2b0aa61", size = 223059, upload-time = "2026-06-22T23:08:00.662Z" }, + { url = "https://files.pythonhosted.org/packages/f1/24/efb17eb94018dd3415d0e8a76a4786a866e8964aa9c50f033399d23939c2/coverage-7.14.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e574801e1d643561594aa021206c46d80b257e9853087090ba97bed8b0a509d3", size = 220501, upload-time = "2026-06-22T23:08:02.182Z" }, + { url = "https://files.pythonhosted.org/packages/76/93/32f1bfca6cdd34259c8af42820a034b7a28dfb44969a13ed38c17e0ba5b0/coverage-7.14.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f82b6bb7d75a2613e85d07cefa3a8c973d0544a8993337f6e2728e4a1e94c305", size = 221008, upload-time = "2026-06-22T23:08:03.701Z" }, + { url = "https://files.pythonhosted.org/packages/eb/88/0d0f974855ff905d15a64f7873d00bdc4182e2736267486c6634f4af293c/coverage-7.14.3-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a2335ea5fed26af2e831094964fa3f8fae60b45f7e37fcc2d3b615b2add3ad87", size = 251420, upload-time = "2026-06-22T23:08:05.211Z" }, + { url = "https://files.pythonhosted.org/packages/39/7f/117dd2ec65e4140576f8ef991d88220f9b806769f7a8c20e0550c0f924e2/coverage-7.14.3-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fbb8c3a98e779013786ae01d229662aeacbc77100efbd3f2f245219ace5af700", size = 253331, upload-time = "2026-06-22T23:08:06.672Z" }, + { url = "https://files.pythonhosted.org/packages/87/55/f0bd6d6538e3f16829fb8a44b6c0d2fe9da638bbfdd6a20f8b5da8f4fa81/coverage-7.14.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ac082660de8f429ba0ea363595abb838998570b9a7546777c60f413ab902bbde", size = 255441, upload-time = "2026-06-22T23:08:08.208Z" }, + { url = "https://files.pythonhosted.org/packages/1e/98/aa71f7879019c846a8a9662579ea4484b0202cf1e252ffeed647075e7eca/coverage-7.14.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8ac012839ff7e396030f1e94e10553a431d14e4de2ab65cb3acb72bbd5628ca2", size = 257398, upload-time = "2026-06-22T23:08:09.749Z" }, + { url = "https://files.pythonhosted.org/packages/f3/4f/5fd367e59844190f5965015d7bee899e67a89d13eb2760118479bf836f2f/coverage-7.14.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5952f8c1bda2a5347154450379316e6dfa4d934d62ca35f6784451e6f55074fb", size = 251558, upload-time = "2026-06-22T23:08:11.37Z" }, + { url = "https://files.pythonhosted.org/packages/8f/de/5383a6ee5a6376701fe07d980fa8e4a66c0c377fead16712720340d701a3/coverage-7.14.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8cf0f2509acb4619e2471a1951089054dd58ebea7a912066d2ea56dd4c24ca4a", size = 253134, upload-time = "2026-06-22T23:08:13.04Z" }, + { url = "https://files.pythonhosted.org/packages/01/99/09542b1a99f788e3daec7f0fadc288821e71aca9ea298d51bfa1ba79fed5/coverage-7.14.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:2e41fd3aab806770008279a93879b0924b16247e09ab537c043d08bbca53b4ab", size = 251195, upload-time = "2026-06-22T23:08:14.606Z" }, + { url = "https://files.pythonhosted.org/packages/02/9d/722fe8c13f0fbb064491b9e8656e56a606286792e5068c47ca1042e773e8/coverage-7.14.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f0a47095963cfe054e0df178daca95aec21e680d6076da807c3add28dfe920f7", size = 254959, upload-time = "2026-06-22T23:08:16.431Z" }, + { url = "https://files.pythonhosted.org/packages/fb/58/943627179ff1d82da9e54d0a5b0bb907bb19cf19515599ccd921de50b469/coverage-7.14.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:a090cbf9521e78ffdb2fcf448b72902afe9f5923ff6a12d5c0d0120200348af9", size = 250914, upload-time = "2026-06-22T23:08:18.03Z" }, + { url = "https://files.pythonhosted.org/packages/a5/d4/803efcbf9ae5567454a0c71e983589529448e2704ee0da2dc0163d482f18/coverage-7.14.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4d310baf69a4fbe8a098ce727e4808a34866ac718a6f759ae659cbd3221358bc", size = 251824, upload-time = "2026-06-22T23:08:19.704Z" }, + { url = "https://files.pythonhosted.org/packages/32/79/3f78ea9563132746eed5cecb75d2e576f9d8fec45a47242b5ae0950b82a3/coverage-7.14.3-cp311-cp311-win32.whl", hash = "sha256:74fdd718d88fe144f4579b8747873a07ec3f04cb837d5faec5a25d9e22fa31a8", size = 222594, upload-time = "2026-06-22T23:08:21.311Z" }, + { url = "https://files.pythonhosted.org/packages/85/22/9ebbc5a2ab42ac5d0eea1f48648629e1de9bbe41ec243ed6b93d55a5a53f/coverage-7.14.3-cp311-cp311-win_amd64.whl", hash = "sha256:cc96aa922e21d4bc5d5ed3c915cef27dfcbc13686f47d5e378d647fbfba655a2", size = 223073, upload-time = "2026-06-22T23:08:23.318Z" }, + { url = "https://files.pythonhosted.org/packages/71/af/69d5fcc16cb555153f99cec5467922f226be0369f7335a9506856d2a7bd0/coverage-7.14.3-cp311-cp311-win_arm64.whl", hash = "sha256:c66f9f9d4f1e9712eb9b1de5310f881d4e2188cfcba5065e1a8490f38687f2c4", size = 222617, upload-time = "2026-06-22T23:08:25.054Z" }, + { url = "https://files.pythonhosted.org/packages/bd/b0/8a911f6ffe6974dac4df95b468ab9a2899d0e59f0f99a489afeec39f00bc/coverage-7.14.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3d74ff26299c4879ce3a4d826f9d3d4d556fd285fde7bbce3c0ef5a8ab1cec24", size = 220672, upload-time = "2026-06-22T23:08:26.621Z" }, + { url = "https://files.pythonhosted.org/packages/36/16/0fc0cb52538783dbbae0934b834f5a58fd5354380ee6cad4a07b15dc845d/coverage-7.14.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:96150a9cf3468ea20f0bc5d0e21b3df8972c31480ef90fa7614b773cc6429665", size = 221035, upload-time = "2026-06-22T23:08:28.372Z" }, + { url = "https://files.pythonhosted.org/packages/77/e2/421ccfbb48335ac49e93301478cf5d623b0c2bf1c0cadd8e2b2fc6c0c710/coverage-7.14.3-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:27d07a46500ba23515b838dbcf52512026af04090755cf6cc64166d88c9b9a1a", size = 252540, upload-time = "2026-06-22T23:08:30.226Z" }, + { url = "https://files.pythonhosted.org/packages/06/c2/05b8c890097c61a7f4406b35396b997a635200ded0339eda83dfbe526c5f/coverage-7.14.3-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:621e13c6108234d7960aaf5762ab5c3c00f33c30c15af06dcbff0c73bf112727", size = 255274, upload-time = "2026-06-22T23:08:31.876Z" }, + { url = "https://files.pythonhosted.org/packages/dc/be/b6d9efe447f8ba3c3c854195f326bd64c54b907d936cd2fdebf8767ec72e/coverage-7.14.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4b60ca6d8af70473491a15a343cbabab2e8f9ea66a4376e81c7aa24876a6f977", size = 256389, upload-time = "2026-06-22T23:08:33.843Z" }, + { url = "https://files.pythonhosted.org/packages/d4/3c/f26e50acc429e608bc534ac06f0a3c169019c798178ec5e9de3dbc0df9c9/coverage-7.14.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c90a7cdd5e380e1ce02f19792e2ac2fbfbf177e35a27e69fd3e873b30d895c0c", size = 258648, upload-time = "2026-06-22T23:08:35.481Z" }, + { url = "https://files.pythonhosted.org/packages/9e/a2/01c1fabf816c8e1dae197e258edf878a3d3ddc86fbda34b76e5794277d8f/coverage-7.14.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5d788e5fd55347eef06ca0732c77d04a264de67e8ff24631270cdff3767a60cf", size = 252949, upload-time = "2026-06-22T23:08:37.562Z" }, + { url = "https://files.pythonhosted.org/packages/89/c6/941166dd79c31fd44a13063780ae8d552eee0089a0a0930b9bdb7df554ed/coverage-7.14.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:62c7f79db2851c95ef020e5d28b97afde3daf9f7febcd35b53e05638f729063f", size = 254310, upload-time = "2026-06-22T23:08:39.174Z" }, + { url = "https://files.pythonhosted.org/packages/10/31/80b1fd028201a961033ce95be3cd1e39e521b3762e6b4a1ac1616cb291e7/coverage-7.14.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:90f7608aeb5d9b60b523b9fb2a4ee1973867cc4865a3f26fe6c7577073b70205", size = 252453, upload-time = "2026-06-22T23:08:40.84Z" }, + { url = "https://files.pythonhosted.org/packages/5f/85/c3d9addd94c4b524f3f4af0232075f5fe7170ce99a1386edff803e5934db/coverage-7.14.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1e3b91f9c4740aeb571ecf82e5e8d8e4ab62d34fcb5a5d4e5baa38c6f7d2857c", size = 256522, upload-time = "2026-06-22T23:08:42.494Z" }, + { url = "https://files.pythonhosted.org/packages/91/14/e5a0575f73795af3a7a9ae13dadf812e17d32422896839987dc3f86947e1/coverage-7.14.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:c946099774a7699de03cbd0ff0a64e21aed4525eed9d959adde4afe6d15758ef", size = 252023, upload-time = "2026-06-22T23:08:44.243Z" }, + { url = "https://files.pythonhosted.org/packages/38/9b/9652ee531937ce3b8a63a8896885b2b4a2d56adc30e53c9540c666286d88/coverage-7.14.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:16b206e521feb8b7133a45754643dead0538489cf8b783b90cf5f4e3299625fd", size = 253893, upload-time = "2026-06-22T23:08:46.113Z" }, + { url = "https://files.pythonhosted.org/packages/b1/05/42678841c8c38e4b08bdfc48269f5a16dfbf5806000fe6a89b4cece3c691/coverage-7.14.3-cp312-cp312-win32.whl", hash = "sha256:ea3169c7116eb6cdf7608c6c7da9ecfcb3da40688e3a510fac2d1d2bafd6dc35", size = 222734, upload-time = "2026-06-22T23:08:47.858Z" }, + { url = "https://files.pythonhosted.org/packages/df/87/07a4fcee55177a25f1b52331a8e92cf4f2c53b1a9c75ce2981fd59c684ad/coverage-7.14.3-cp312-cp312-win_amd64.whl", hash = "sha256:7ea52fc08f007bcc494d4bb3df3851e95843d881860ba38fe2c64dc100db5e7d", size = 223266, upload-time = "2026-06-22T23:08:49.494Z" }, + { url = "https://files.pythonhosted.org/packages/aa/34/2b8b66a989282ea7b370beb49f50bab29470dc30bb0b03935b6b802782f7/coverage-7.14.3-cp312-cp312-win_arm64.whl", hash = "sha256:8cec0ad652ec57790970d817490105bd917d783c2f7b38d6b58a0ca312e1a336", size = 222655, upload-time = "2026-06-22T23:08:51.766Z" }, + { url = "https://files.pythonhosted.org/packages/a9/83/7fefbf5df23ed2b7f489907564a7b34b9b07098128e12e0fdfa92626e456/coverage-7.14.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:47968988b367990ae4ab17523790c38cd125e02c6bfd379b6022be2d40bdc38c", size = 220699, upload-time = "2026-06-22T23:08:53.522Z" }, + { url = "https://files.pythonhosted.org/packages/31/e6/38c3653ff6d56d704b29241362387ca824e38e15b76fdcb7096538195790/coverage-7.14.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0ee68f5c34812780f3a7063382c0a9fcbb99985b7ddcdcaa626e4f3fb2e0783a", size = 221068, upload-time = "2026-06-22T23:08:55.571Z" }, + { url = "https://files.pythonhosted.org/packages/20/86/4f5c45d51c5cd10a128933f0fd235393c9146abbfd2ce2dfa68b3267ead3/coverage-7.14.3-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:fa9e5c6857a7e80fa22ace5cf3550ae392bbfc322f1d8dd2d2d5a8be38cec027", size = 252060, upload-time = "2026-06-22T23:08:57.464Z" }, + { url = "https://files.pythonhosted.org/packages/82/50/dfce42eff2cecabcd5a9bbad5489449c87db3415f408d23ffee417ce01f6/coverage-7.14.3-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:98a0859b0e98e43e1178a9402e19c8127766b14f7109a374d976e5a62c0e5c73", size = 254657, upload-time = "2026-06-22T23:08:59.453Z" }, + { url = "https://files.pythonhosted.org/packages/ba/d2/639ceb1bc8038fd0d66768278d5dc22df3391918b8278c2a21aa2602a531/coverage-7.14.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:69918344541ed9c8368566c2adc03c0e33d4550d7faa87d1b35e49b6a3286ea9", size = 255892, upload-time = "2026-06-22T23:09:01.291Z" }, + { url = "https://files.pythonhosted.org/packages/8b/96/002094a10e113512500dc1e10430a449417e17b0f90f7d496bcb820208b7/coverage-7.14.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b7f300ac92cd4b570724c8ffbbd0c130fee298d2447f41d5a3abf58976fae1de", size = 258026, upload-time = "2026-06-22T23:09:03.017Z" }, + { url = "https://files.pythonhosted.org/packages/0b/ec/286a5d2fad9c4bee59bd724feeb7d5bf8303c6c9200b51d1dd945a9c72b0/coverage-7.14.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:11a7ec9f97ab950f4c5af62229befc7faf208fdbc0116d3902d7e306cf2c5abd", size = 252285, upload-time = "2026-06-22T23:09:04.773Z" }, + { url = "https://files.pythonhosted.org/packages/d9/7d/a17753a0b12dd48d0d50f5fab079ad99d3be1eac790494d89f3a417ca0b9/coverage-7.14.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a571bd889cd36c5922ce8e42e059f9d37d02301531d11374afa4c87a578625d5", size = 254023, upload-time = "2026-06-22T23:09:06.513Z" }, + { url = "https://files.pythonhosted.org/packages/86/ef/a76c6ceba6a2c313f905310abf2701d534cada22d372db11731831e9e209/coverage-7.14.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:de76caefc8deabb0dd1678b6a980be97d14c8d87e213ac194dbf8b09e96d63fb", size = 251989, upload-time = "2026-06-22T23:09:08.382Z" }, + { url = "https://files.pythonhosted.org/packages/d9/39/353013a75fec0fb49f7553519f9d52b4441e902e5178c93f38eb6c07cedb/coverage-7.14.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d20a15c622194234161535459affa8f7905830391c9ccfa060d495dbfe3a1c7f", size = 256144, upload-time = "2026-06-22T23:09:10.369Z" }, + { url = "https://files.pythonhosted.org/packages/29/0e/613878555d734def11c5b20a2701a15cb3781b9e9ea749da27c5f436e928/coverage-7.14.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:b488bd4b23397db62e7a9459129d01ff06a846582a732efd24834b24a6ada498", size = 251808, upload-time = "2026-06-22T23:09:12.057Z" }, + { url = "https://files.pythonhosted.org/packages/af/76/359c058c9cfdcf1e8b107663881225b03b364a320017eda24a2a66e55102/coverage-7.14.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6a3693b4153394d265f44fb855fdc80e72403024d4d6f91c4871b334d028e4e0", size = 253579, upload-time = "2026-06-22T23:09:13.858Z" }, + { url = "https://files.pythonhosted.org/packages/1d/d9/4ba2f060933a30ebe363cef9f67a365b0a317e580c0d5d9169d56a73ef1c/coverage-7.14.3-cp313-cp313-win32.whl", hash = "sha256:338b19131ab1a6b767b462bfcbaa692e7ae22f24463e39d49b02a83410ff6b37", size = 222741, upload-time = "2026-06-22T23:09:15.636Z" }, + { url = "https://files.pythonhosted.org/packages/76/e8/196ebc25d8f34c06d43a6e9c8513c9266ef8dbf3b5672beb1a00cf5e29fa/coverage-7.14.3-cp313-cp313-win_amd64.whl", hash = "sha256:b3d77f7f196abdef7e01415de1bce09f216189e83e58159cfeef2b92d0464994", size = 223283, upload-time = "2026-06-22T23:09:17.478Z" }, + { url = "https://files.pythonhosted.org/packages/7c/af/51d2aac6417523a286f10fb25f09eb9518a84df9f1151e93ff6871f34849/coverage-7.14.3-cp313-cp313-win_arm64.whl", hash = "sha256:e6230e688c7c3e65cedd41a774eb4ec221adc6bfee13768231015b702d5e4150", size = 222678, upload-time = "2026-06-22T23:09:19.7Z" }, + { url = "https://files.pythonhosted.org/packages/61/56/14e3b97facbfa1304dd19e676e26599ad359f04714bed32f7f1c5a88efdc/coverage-7.14.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:605ab2b566a22bd94834529d66d295c364aba84afd3e5498285c7a524017b1fc", size = 220741, upload-time = "2026-06-22T23:09:21.616Z" }, + { url = "https://files.pythonhosted.org/packages/12/1d/db378b5cca433b90b893f26dab728b280ddd89f272a1fdfed4aeaa05c686/coverage-7.14.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a3c2134809e80fac091bfed18a6991b5a5eb5df5ae32b17ac4f4f99864b73dd7", size = 221068, upload-time = "2026-06-22T23:09:23.452Z" }, + { url = "https://files.pythonhosted.org/packages/47/f0/3f8421b20d9c4fcd39be9a8ca3c3fda8bc204b44efbd09fede153afd3e2f/coverage-7.14.3-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:c02efd507227bde9969cab0db8f48890eb3b5dcad6afac57a4792df4133543ce", size = 252117, upload-time = "2026-06-22T23:09:25.458Z" }, + { url = "https://files.pythonhosted.org/packages/27/ca/59ea35fb99743549ec8b37eff141ece4431fea590c89e536ed8032ef45cf/coverage-7.14.3-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1bb93c2aa61d2a5b38f1526546d95cf4132cb681e541a337bf8dfd092be816e5", size = 254622, upload-time = "2026-06-22T23:09:27.523Z" }, + { url = "https://files.pythonhosted.org/packages/c8/25/ec6de51ae7493b92a1cf74d1b763121c29636759167e2a593ba4db5881e4/coverage-7.14.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f502e948e03e866538048bba081c075caaa62e5bda6ea5b7432e45f587eb462a", size = 255968, upload-time = "2026-06-22T23:09:29.43Z" }, + { url = "https://files.pythonhosted.org/packages/5d/05/c8bfc77823f42b4664fb25842f13b567022f6f84a4c83c8ecbb16734b7cb/coverage-7.14.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9973ef2463f8e6cfb61a6324126bb3e17d67a85f22f58d856e583ea2e3ca6501", size = 258284, upload-time = "2026-06-22T23:09:31.397Z" }, + { url = "https://files.pythonhosted.org/packages/f6/15/1d1b242027124a32b26ef01f82018b8c4ef34ef174aa6aeba7b1eeef48e8/coverage-7.14.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9be4e7d4c5ca0427889f8f9d614bd630c2be741b1de7699bca3b2b6c0e41003e", size = 252143, upload-time = "2026-06-22T23:09:33.256Z" }, + { url = "https://files.pythonhosted.org/packages/74/b6/d2a9842fd2a5d7d27f1ac851c043a734a494ad75402c5331db3da79ed691/coverage-7.14.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a574912f3bde4b0619f6e97d01aa590b70998859244793769eb3a6df78ee56d3", size = 253976, upload-time = "2026-06-22T23:09:35.351Z" }, + { url = "https://files.pythonhosted.org/packages/fd/30/e1600ddf7e226db5558bb5323d2186fff00f505c4b764643ec89ce5d8175/coverage-7.14.3-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:e343fb086c9cd780b38622fea7c369acd64c1a0724312149b5d769c387a2b1f5", size = 251942, upload-time = "2026-06-22T23:09:37.313Z" }, + { url = "https://files.pythonhosted.org/packages/d9/2c/9159de64f9dd648e324328d588a44cfab1e331eb5259ce1141afe2a92dfb/coverage-7.14.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:3c68df8e61f1e09633fefc7538297145623957a048534368c9d212782aa5e845", size = 256220, upload-time = "2026-06-22T23:09:39.165Z" }, + { url = "https://files.pythonhosted.org/packages/91/67/b7f536cc2c124f48e91b22fbb741d2261f4e3d310faf6f76007f47566e5d/coverage-7.14.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:3e5b550a128419373c2f6cec28a244207013ef15f5cbcff6a5ca09d1dfaaf027", size = 251756, upload-time = "2026-06-22T23:09:41.056Z" }, + { url = "https://files.pythonhosted.org/packages/dd/ec/f3718038e2d4860c715a55428377ca7f6c75872caf98cabd982e1d76967d/coverage-7.14.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2bfc4dd0a912329eccc7484a7d0b2a38032b38c40663b1e1ac595f10c457954b", size = 253413, upload-time = "2026-06-22T23:09:43.306Z" }, + { url = "https://files.pythonhosted.org/packages/b8/a5/91f11efeef89b3cc9b30461128db15b0511ef813ab889a7b7ab636b3a497/coverage-7.14.3-cp314-cp314-win32.whl", hash = "sha256:0423d64c013057a06e70f070f073cec4b0cbc7d2b27f3c7007292f2ff1d52965", size = 222946, upload-time = "2026-06-22T23:09:45.261Z" }, + { url = "https://files.pythonhosted.org/packages/58/fd/98ac9f524d9ec378de831c034dbdeb544ca7ef7d2d9c9996daf232a037fd/coverage-7.14.3-cp314-cp314-win_amd64.whl", hash = "sha256:92c22e19ce64ca3f2ad751f16f14df1468b4c231bd6af97185063a9c292a0cb3", size = 223436, upload-time = "2026-06-22T23:09:47.177Z" }, + { url = "https://files.pythonhosted.org/packages/b4/a0/7cd612d650a772a0ae80144443406bf61981c896c3d57c9e6e79fb2cdbd1/coverage-7.14.3-cp314-cp314-win_arm64.whl", hash = "sha256:41de778bd41780586e2b04912079c73089ab5d839624e28db3bdb26de638da92", size = 222861, upload-time = "2026-06-22T23:09:49.384Z" }, + { url = "https://files.pythonhosted.org/packages/55/57/017353fab573779c0d00448e47d102edd36c792f7b6f233a4d89a7a08384/coverage-7.14.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:8427f370ca67db4c975d2a26acfc0e5783ca0b52444dbc50278ace0f35445949", size = 221474, upload-time = "2026-06-22T23:09:51.417Z" }, + { url = "https://files.pythonhosted.org/packages/69/92/90cf1f1a5c468a9c1b7ba2716e0e205293ad9b02f5f573a6de4318b15ba1/coverage-7.14.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d8e88f335544a47e22ae2e45b344772925ec65166555c958720d5ed971880891", size = 221738, upload-time = "2026-06-22T23:09:53.487Z" }, + { url = "https://files.pythonhosted.org/packages/a4/c0/4df964fa539f8399fd7679c09c472d73744de334686fd3f01e3a2465ce4e/coverage-7.14.3-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:beaab199b9e5ceaf5a225e16a9d4df136f2a1eae0a5c20de1e277c8a5225f388", size = 263101, upload-time = "2026-06-22T23:09:55.895Z" }, + { url = "https://files.pythonhosted.org/packages/06/76/e5d33b2576ae3bf2be2058cd1cae57774b61e400f2c3c58f3783dc2ffb4a/coverage-7.14.3-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3ff255799f5a1676c71c1c32ec01fd043aa09d57b3d95764b24992757184784", size = 265225, upload-time = "2026-06-22T23:09:57.904Z" }, + { url = "https://files.pythonhosted.org/packages/61/d2/e52419afe391a39ba27fdefaf0737d8e34bf03faef6ab3b3006545bbd0d0/coverage-7.14.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:878832eaac515b62decfa76965aed558775f86bf1fc8cca76993c0c84ae31aed", size = 267643, upload-time = "2026-06-22T23:09:59.938Z" }, + { url = "https://files.pythonhosted.org/packages/58/7a/f2625d8d5006b6b20fba5afaef00b24a763fe96476ea798a3076cbc1f84e/coverage-7.14.3-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:611e62cb9386096d81b63e0a05330750268617231e7bd598e1fe77482a2c58a5", size = 268762, upload-time = "2026-06-22T23:10:01.943Z" }, + { url = "https://files.pythonhosted.org/packages/7d/bf/908024006bba57127354d74e938954b9c3cd765cc2e0412dc9c37b415cda/coverage-7.14.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:02c41de2a88011b893050fc9830267d927a50a215f7ad5ec17349db7090ccf26", size = 262208, upload-time = "2026-06-22T23:10:03.954Z" }, + { url = "https://files.pythonhosted.org/packages/34/a0/d4f9296441b909817442fdb26bd77a698f08272ec683a7394b00eb2e47a0/coverage-7.14.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:526ce9721116af23b1065089f0b75046fe521e7772ab94b641cd66b7a0421889", size = 265096, upload-time = "2026-06-22T23:10:05.936Z" }, + { url = "https://files.pythonhosted.org/packages/e8/da/4ae4f3f4e477b56a4ce1e5c48a35eff38a94b50130ce5bdc897024741cfc/coverage-7.14.3-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:e4ed44705ca4bead6fc977a8b741f2145608289b33c8a9b42a95d0f15aedbf4d", size = 262699, upload-time = "2026-06-22T23:10:07.973Z" }, + { url = "https://files.pythonhosted.org/packages/d8/7a/6927148073ff32856d78baa77b4ddc07a9be7e90020f9db0661c4ca523a1/coverage-7.14.3-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:2415902f385a23dcc4ccd26e0ba803249a169af6a930c003a4c715eeb9a5444e", size = 266433, upload-time = "2026-06-22T23:10:10.145Z" }, + { url = "https://files.pythonhosted.org/packages/f7/a7/774f658dbe9c4c3f5daa86a87e0459ac3832e4e3cc67affe078547f727b9/coverage-7.14.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:b75ee850fc2d7c831e883220c445b035f2224de2ba6103f1e56dbd237ab913f7", size = 261547, upload-time = "2026-06-22T23:10:12.191Z" }, + { url = "https://files.pythonhosted.org/packages/3d/14/a0c18c0376c43cbf973f43ef6ca20019c950597180e6396232f7b6a27102/coverage-7.14.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dc9b4e35e7c3920e925ba7f14886fd5fbe481232754624e832ddba66c7535635", size = 263859, upload-time = "2026-06-22T23:10:14.492Z" }, + { url = "https://files.pythonhosted.org/packages/10/ac/43a3d0f460af524b131a6191805bc5d18b806ab4e828fbf82e8c8c3af446/coverage-7.14.3-cp314-cp314t-win32.whl", hash = "sha256:7b27c822a8161afbe48e99f1adfb098d270ae7e0f7d7b0555ce110529bdb69cc", size = 223250, upload-time = "2026-06-22T23:10:16.758Z" }, + { url = "https://files.pythonhosted.org/packages/3f/5f/d5e5c56b0712e96ce8f69fe7dbf229ff938b437bc50862743c8a0d2cea84/coverage-7.14.3-cp314-cp314t-win_amd64.whl", hash = "sha256:39e1dbbb6ff2c338e0196a482558a792a1de3aa64261196f5cdb3da016ad9cda", size = 224082, upload-time = "2026-06-22T23:10:19.23Z" }, + { url = "https://files.pythonhosted.org/packages/62/35/947cbd5be1d3bcbbdc43d6791de8a56c6501903311d42915ae06a82815f0/coverage-7.14.3-cp314-cp314t-win_arm64.whl", hash = "sha256:68520c90babfa2d560eca6d497921ed3a4f469623bd709733124491b2aa8ef3f", size = 223400, upload-time = "2026-06-22T23:10:21.24Z" }, + { url = "https://files.pythonhosted.org/packages/eb/e3/a0aa32bfa3a081951f60a23bc0e7b512891ef0eecda1153cf1d8ba36c6b1/coverage-7.14.3-py3-none-any.whl", hash = "sha256:fb7e18afb6e903c1a92401a2f0501ac277dca527bb9ca6fe1f691a8a0026a0e8", size = 212469, upload-time = "2026-06-22T23:10:23.405Z" }, ] [package.optional-dependencies] @@ -428,64 +413,6 @@ toml = [ { name = "tomli", marker = "python_full_version <= '3.11'" }, ] -[[package]] -name = "cytoolz" -version = "0.12.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "toolz" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/70/d8/8df71050b214686591241a1826d2e6934b5c295c5bc57f643e4ed697f1eb/cytoolz-0.12.3.tar.gz", hash = "sha256:4503dc59f4ced53a54643272c61dc305d1dbbfbd7d6bdf296948de9f34c3a282", size = 625899, upload-time = "2024-01-25T01:39:13.834Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/11/ebc8b85c77cc2247c169af808901a80e5d29429b3c9b0d114d4048ad2a5a/cytoolz-0.12.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bbe58e26c84b163beba0fbeacf6b065feabc8f75c6d3fe305550d33f24a2d346", size = 413462, upload-time = "2024-01-25T01:36:06.7Z" }, - { url = "https://files.pythonhosted.org/packages/19/22/57f20a9f135a08a6fc9848744c324c4facca2b1370a1ae4e8333e274c707/cytoolz-0.12.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c51b66ada9bfdb88cf711bf350fcc46f82b83a4683cf2413e633c31a64df6201", size = 396040, upload-time = "2024-01-25T01:36:08.623Z" }, - { url = "https://files.pythonhosted.org/packages/12/30/337a18b1182888bf5d59357095a83f537a88e2db32ee0381166a83deddda/cytoolz-0.12.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e70d9c615e5c9dc10d279d1e32e846085fe1fd6f08d623ddd059a92861f4e3dd", size = 1933264, upload-time = "2024-01-25T01:36:10.093Z" }, - { url = "https://files.pythonhosted.org/packages/93/a3/c244bfacd046316cd0a4cffd2e245a8dd9b685168497c3b7c776bdb72c59/cytoolz-0.12.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a83f4532707963ae1a5108e51fdfe1278cc8724e3301fee48b9e73e1316de64f", size = 2014114, upload-time = "2024-01-25T01:36:12.083Z" }, - { url = "https://files.pythonhosted.org/packages/a6/40/51bdfe7fd1cad802eabebb7bb742e694227db25c4806eebf4358229e3220/cytoolz-0.12.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d028044524ee2e815f36210a793c414551b689d4f4eda28f8bbb0883ad78bf5f", size = 1999458, upload-time = "2024-01-25T01:36:14.135Z" }, - { url = "https://files.pythonhosted.org/packages/f7/0b/5973e0ceab96349f9cabb3b20ecc3a717a19e355f68d354dfa538bebb226/cytoolz-0.12.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c2875bcd1397d0627a09a4f9172fa513185ad302c63758efc15b8eb33cc2a98", size = 1956106, upload-time = "2024-01-25T01:36:15.485Z" }, - { url = "https://files.pythonhosted.org/packages/d3/01/bb850ad1467f0d1dc5a21a05e3df6ad308a19d095f07583c4e65a85e2ba8/cytoolz-0.12.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:131ff4820e5d64a25d7ad3c3556f2d8aa65c66b3f021b03f8a8e98e4180dd808", size = 1861974, upload-time = "2024-01-25T01:36:17.185Z" }, - { url = "https://files.pythonhosted.org/packages/c9/97/207e544a2c9c3d8735102403982a319c252c337e9cf792c2753c22cff1b6/cytoolz-0.12.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:04afa90d9d9d18394c40d9bed48c51433d08b57c042e0e50c8c0f9799735dcbd", size = 1937926, upload-time = "2024-01-25T01:36:18.968Z" }, - { url = "https://files.pythonhosted.org/packages/76/1e/6cc756537caef16cd491de1550c1f25140528785e28263326bd3243a19ed/cytoolz-0.12.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:dc1ca9c610425f9854323669a671fc163300b873731584e258975adf50931164", size = 1859141, upload-time = "2024-01-25T01:36:20.62Z" }, - { url = "https://files.pythonhosted.org/packages/20/43/30819bbc4348cabb44d800ab0d0efb29a9d21db8f75060fd1199d5300953/cytoolz-0.12.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:bfa3f8e01bc423a933f2e1c510cbb0632c6787865b5242857cc955cae220d1bf", size = 2013731, upload-time = "2024-01-25T01:36:22.461Z" }, - { url = "https://files.pythonhosted.org/packages/bd/ab/cacabf2d0dfea006575fea900131eb491370ee1664783f80cb131f577001/cytoolz-0.12.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:f702e295dddef5f8af4a456db93f114539b8dc2a7a9bc4de7c7e41d169aa6ec3", size = 2015075, upload-time = "2024-01-25T01:36:24.144Z" }, - { url = "https://files.pythonhosted.org/packages/79/6f/98eeee4f735bff536654625e0c38e50c4b01c13fbd9c744d3284dbccc54d/cytoolz-0.12.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0fbad1fb9bb47e827d00e01992a099b0ba79facf5e5aa453be066033232ac4b5", size = 1953391, upload-time = "2024-01-25T01:36:25.818Z" }, - { url = "https://files.pythonhosted.org/packages/6a/81/80bcbc750e3c8f72d07f1e61c25597fdb23fec3c3d52542f220bff87372c/cytoolz-0.12.3-cp310-cp310-win32.whl", hash = "sha256:8587c3c3dbe78af90c5025288766ac10dc2240c1e76eb0a93a4e244c265ccefd", size = 320881, upload-time = "2024-01-25T01:36:27.405Z" }, - { url = "https://files.pythonhosted.org/packages/45/9e/cce296c6da9404ce210c79700b85d9ba582a6e908ee88ec707efec09bcd9/cytoolz-0.12.3-cp310-cp310-win_amd64.whl", hash = "sha256:9e45803d9e75ef90a2f859ef8f7f77614730f4a8ce1b9244375734567299d239", size = 362216, upload-time = "2024-01-25T01:36:28.625Z" }, - { url = "https://files.pythonhosted.org/packages/98/8a/618a70326d4a52998a6bbb11ca019979891a51b85cbbfce8f9762eec5d2c/cytoolz-0.12.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3ac4f2fb38bbc67ff1875b7d2f0f162a247f43bd28eb7c9d15e6175a982e558d", size = 416356, upload-time = "2024-01-25T01:36:30.344Z" }, - { url = "https://files.pythonhosted.org/packages/bb/17/542f708b9116aa8d8c9c5551500bfa6ab1bddd3edc11457070599a97c197/cytoolz-0.12.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0cf1e1e96dd86829a0539baf514a9c8473a58fbb415f92401a68e8e52a34ecd5", size = 398412, upload-time = "2024-01-25T01:36:32.628Z" }, - { url = "https://files.pythonhosted.org/packages/d6/9c/52a0460f2b59009da63794cfe11ad002d55fc88fced6ccc85f7ae10a3259/cytoolz-0.12.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08a438701c6141dd34eaf92e9e9a1f66e23a22f7840ef8a371eba274477de85d", size = 2090389, upload-time = "2024-01-25T01:36:34.398Z" }, - { url = "https://files.pythonhosted.org/packages/c7/6d/284d4a1f88b1e63b8a1c80162244ca5d4e0b31647c4f56186f3385fd0d0e/cytoolz-0.12.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c6b6f11b0d7ed91be53166aeef2a23a799e636625675bb30818f47f41ad31821", size = 2187333, upload-time = "2024-01-25T01:36:36.099Z" }, - { url = "https://files.pythonhosted.org/packages/3e/44/44efcf2acf824aefab5f3fdb97235d360c216c2425db391a8dee687f1774/cytoolz-0.12.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7fde09384d23048a7b4ac889063761e44b89a0b64015393e2d1d21d5c1f534a", size = 2173106, upload-time = "2024-01-25T01:36:37.827Z" }, - { url = "https://files.pythonhosted.org/packages/6f/74/7c2d5af25d51f481e9be771bda2cf5496631cd0fba876d2cc0086caa732d/cytoolz-0.12.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d3bfe45173cc8e6c76206be3a916d8bfd2214fb2965563e288088012f1dabfc", size = 2098805, upload-time = "2024-01-25T01:36:40.09Z" }, - { url = "https://files.pythonhosted.org/packages/e0/e5/9995bf66caee139459ab70d8b5820c37718d5f7e7380481adb85ef522e4d/cytoolz-0.12.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27513a5d5b6624372d63313574381d3217a66e7a2626b056c695179623a5cb1a", size = 1995956, upload-time = "2024-01-25T01:36:41.741Z" }, - { url = "https://files.pythonhosted.org/packages/93/73/eee7a7f455d4691f1915b35d1713ba69c6851f940c6f8fffdd2efcbe4411/cytoolz-0.12.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d294e5e81ff094fe920fd545052ff30838ea49f9e91227a55ecd9f3ca19774a0", size = 2074891, upload-time = "2024-01-25T01:36:43.887Z" }, - { url = "https://files.pythonhosted.org/packages/a3/c7/6f0c60bbb911ee3f33b243a695e2fc944285a1865bbb1dfefee1b7c35d62/cytoolz-0.12.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:727b01a2004ddb513496507a695e19b5c0cfebcdfcc68349d3efd92a1c297bf4", size = 1980228, upload-time = "2024-01-25T01:36:45.42Z" }, - { url = "https://files.pythonhosted.org/packages/58/7c/d7524626b415d410208fd2774ff2824fab4dcbe4a496d53bdba85a1407df/cytoolz-0.12.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:fe1e1779a39dbe83f13886d2b4b02f8c4b10755e3c8d9a89b630395f49f4f406", size = 2147616, upload-time = "2024-01-25T01:36:47.06Z" }, - { url = "https://files.pythonhosted.org/packages/a5/7b/9bdf8615e61958386f5c7108d857312214837ae3eacf3589873499d2f0aa/cytoolz-0.12.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:de74ef266e2679c3bf8b5fc20cee4fc0271ba13ae0d9097b1491c7a9bcadb389", size = 2148775, upload-time = "2024-01-25T01:36:48.813Z" }, - { url = "https://files.pythonhosted.org/packages/45/91/b92ab0e29bf7cfaacfedc85391a0d0365a6ac74363f2532ba307a760db14/cytoolz-0.12.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9e04d22049233394e0b08193aca9737200b4a2afa28659d957327aa780ddddf2", size = 2079810, upload-time = "2024-01-25T01:36:50.116Z" }, - { url = "https://files.pythonhosted.org/packages/2f/33/8204d65871fa639d1d3ae788e3afc187f610126f53f2324d4aa37caf0630/cytoolz-0.12.3-cp311-cp311-win32.whl", hash = "sha256:20d36430d8ac809186736fda735ee7d595b6242bdb35f69b598ef809ebfa5605", size = 320212, upload-time = "2024-01-25T01:36:51.73Z" }, - { url = "https://files.pythonhosted.org/packages/ab/e3/7ce2efaf243c9ee75fc730cd018981788b56e7ad3767f24e7a9cd14831ac/cytoolz-0.12.3-cp311-cp311-win_amd64.whl", hash = "sha256:780c06110f383344d537f48d9010d79fa4f75070d214fc47f389357dd4f010b6", size = 363570, upload-time = "2024-01-25T01:36:53.371Z" }, - { url = "https://files.pythonhosted.org/packages/7f/57/b0feabefdff4707d0b96bf75e2298ecb209ba22c2e3fd15e5230dd68441a/cytoolz-0.12.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:86923d823bd19ce35805953b018d436f6b862edd6a7c8b747a13d52b39ed5716", size = 419825, upload-time = "2024-01-25T01:36:54.522Z" }, - { url = "https://files.pythonhosted.org/packages/54/82/795aa9f91ee81818732b435755559857a76e16b7e435e1de434b47ce72b9/cytoolz-0.12.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a3e61acfd029bfb81c2c596249b508dfd2b4f72e31b7b53b62e5fb0507dd7293", size = 401673, upload-time = "2024-01-25T01:36:56.435Z" }, - { url = "https://files.pythonhosted.org/packages/23/f3/298f32b2e374f4f716ca74a9e46b64ee1df9f160de262391f7f9bc53ecca/cytoolz-0.12.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd728f4e6051af6af234651df49319da1d813f47894d4c3c8ab7455e01703a37", size = 2096352, upload-time = "2024-01-25T01:36:58.428Z" }, - { url = "https://files.pythonhosted.org/packages/39/44/49929b33bd0cdccc98b281e4ca11f0836b2963c6fdec8ebb7891807ed908/cytoolz-0.12.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fe8c6267caa7ec67bcc37e360f0d8a26bc3bdce510b15b97f2f2e0143bdd3673", size = 2161581, upload-time = "2024-01-25T01:36:59.818Z" }, - { url = "https://files.pythonhosted.org/packages/e9/77/980a8bc123490dd7112e1e068da989932b7612504355ddd25593880c3156/cytoolz-0.12.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99462abd8323c52204a2a0ce62454ce8fa0f4e94b9af397945c12830de73f27e", size = 2178057, upload-time = "2024-01-25T01:37:02.23Z" }, - { url = "https://files.pythonhosted.org/packages/23/d9/2f7a4db7f92eab4b567e56cc2fc147fed1ef35f7968e44cefcbbfb366980/cytoolz-0.12.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da125221b1fa25c690fcd030a54344cecec80074df018d906fc6a99f46c1e3a6", size = 2130881, upload-time = "2024-01-25T01:37:03.703Z" }, - { url = "https://files.pythonhosted.org/packages/85/bf/1147229198a8fc3f2090d01263234d47b5b7f8c52c20090b1cceda0929cd/cytoolz-0.12.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c18e351956f70db9e2d04ff02f28e9a41839250d3f936a4c8a1eabd1c3094d2", size = 1979975, upload-time = "2024-01-25T01:37:06.277Z" }, - { url = "https://files.pythonhosted.org/packages/5b/53/b730e9f428e74971f934bf73f0868099d325ffbf0c080a201bb16b6ab508/cytoolz-0.12.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:921e6d2440ac758c4945c587b1d1d9b781b72737ac0c0ca5d5e02ca1db8bded2", size = 2086861, upload-time = "2024-01-25T01:37:07.869Z" }, - { url = "https://files.pythonhosted.org/packages/24/24/df510850c1c57ad69a23a2700083cfd285faf27d16e61f0e503eec122473/cytoolz-0.12.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1651a9bd591a8326329ce1d6336f3129161a36d7061a4d5ea9e5377e033364cf", size = 1973211, upload-time = "2024-01-25T01:37:10.036Z" }, - { url = "https://files.pythonhosted.org/packages/b6/ad/a6e27924a6c8048b4f1ffbd8b9b0b871642bd94d3e457aa42a629c1f667b/cytoolz-0.12.3-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:8893223b87c2782bd59f9c4bd5c7bf733edd8728b523c93efb91d7468b486528", size = 2133017, upload-time = "2024-01-25T01:37:11.684Z" }, - { url = "https://files.pythonhosted.org/packages/26/a3/f9dfb406232d0a100994a86c123259cd2ab023aa8ae325b0d9926f80091f/cytoolz-0.12.3-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:e4d2961644153c5ae186db964aa9f6109da81b12df0f1d3494b4e5cf2c332ee2", size = 2156771, upload-time = "2024-01-25T01:37:13.62Z" }, - { url = "https://files.pythonhosted.org/packages/fb/bf/ce2c6f6175a29c830904b23faee557de8c167255dbb2bb84e04d7cf5605a/cytoolz-0.12.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:71b6eb97f6695f7ba8ce69c49b707a351c5f46fd97f5aeb5f6f2fb0d6e72b887", size = 2121104, upload-time = "2024-01-25T01:37:16.053Z" }, - { url = "https://files.pythonhosted.org/packages/bf/97/95a5fb2b5db9b1ea8856741a2a94741a71cb5640112403029beb7b3ef1df/cytoolz-0.12.3-cp312-cp312-win32.whl", hash = "sha256:cee3de65584e915053412cd178729ff510ad5f8f585c21c5890e91028283518f", size = 320537, upload-time = "2024-01-25T01:37:18.026Z" }, - { url = "https://files.pythonhosted.org/packages/0a/11/3e69585342ce4e6b7f2f6139c8aa235dab5a4fe5c76a051fe01696618bd0/cytoolz-0.12.3-cp312-cp312-win_amd64.whl", hash = "sha256:9eef0d23035fa4dcfa21e570961e86c375153a7ee605cdd11a8b088c24f707f6", size = 363238, upload-time = "2024-01-25T01:37:19.343Z" }, - { url = "https://files.pythonhosted.org/packages/00/8c/c58e2f14a88132722988ee0951b01ee6cc4880302384f064902d6e8fb048/cytoolz-0.12.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:55f9bd1ae6c2a27eda5abe2a0b65a83029d2385c5a1da7b8ef47af5905d7e905", size = 353101, upload-time = "2024-01-25T01:38:37.808Z" }, - { url = "https://files.pythonhosted.org/packages/35/99/6ef817203004c7055370fa1ab3bb0dfc6b7ebd0b4942099ce86007614001/cytoolz-0.12.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2d271393c378282727f1231d40391ae93b93ddc0997448acc21dd0cb6a1e56d", size = 385225, upload-time = "2024-01-25T01:38:39.382Z" }, - { url = "https://files.pythonhosted.org/packages/a2/25/64e5907b15bd16f0f38e825762540ee3293b513f2eb4b9bb51b7f78d5457/cytoolz-0.12.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee98968d6a66ee83a8ceabf31182189ab5d8598998c8ce69b6d5843daeb2db60", size = 405888, upload-time = "2024-01-25T01:38:40.836Z" }, - { url = "https://files.pythonhosted.org/packages/17/c0/0e19ab05222cf2370d580e5500409c26266f89e853fa18b2b12c530f3957/cytoolz-0.12.3-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01cfb8518828c1189200c02a5010ea404407fb18fd5589e29c126e84bbeadd36", size = 396781, upload-time = "2024-01-25T01:38:42.349Z" }, - { url = "https://files.pythonhosted.org/packages/5e/39/bee67e2b541ca3478a982c68d7d23b718fa7f2947bdfc0eecdba9c4e0882/cytoolz-0.12.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:456395d7aec01db32bf9e6db191d667347c78d8d48e77234521fa1078f60dabb", size = 340712, upload-time = "2024-01-25T01:38:45.075Z" }, -] - [[package]] name = "exceptiongroup" version = "1.3.1" @@ -509,11 +436,11 @@ wheels = [ [[package]] name = "filelock" -version = "3.29.0" +version = "3.29.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b5/fe/997687a931ab51049acce6fa1f23e8f01216374ea81374ddee763c493db5/filelock-3.29.0.tar.gz", hash = "sha256:69974355e960702e789734cb4871f884ea6fe50bd8404051a3530bc07809cf90", size = 57571, upload-time = "2026-04-19T15:39:10.068Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e6/dc/be6cbe99670cd6e4ad387123647cb08e0c32975e223f82551e914c5568a6/filelock-3.29.4.tar.gz", hash = "sha256:10cdb3656fc44541cdf30652a93fb10ec6b05325620eb316bd26893e4201538a", size = 63028, upload-time = "2026-06-13T16:12:00.744Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/81/47/dd9a212ef6e343a6857485ffe25bba537304f1913bdbed446a23f7f592e1/filelock-3.29.0-py3-none-any.whl", hash = "sha256:96f5f6344709aa1572bbf631c640e4ebeeb519e08da902c39a001882f30ac258", size = 39812, upload-time = "2026-04-19T15:39:08.752Z" }, + { url = "https://files.pythonhosted.org/packages/13/37/a065dc3bd6e49423a6532c642ca7378d3f467b1ef44c2800c937af7f9739/filelock-3.29.4-py3-none-any.whl", hash = "sha256:dac1648087d5115554850d113e7dd8c83ab2d38e3435dde2d4f163847e57b767", size = 42757, upload-time = "2026-06-13T16:11:59.582Z" }, ] [[package]] @@ -601,24 +528,24 @@ wheels = [ [[package]] name = "hypothesis" -version = "6.155.1" +version = "6.155.7" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, { name = "sortedcontainers" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/49/ef/4a94c12429986a90076057513e084bf32106a9bdc62c8e29f58673dd85a2/hypothesis-6.155.1.tar.gz", hash = "sha256:07c102031612b98d7c1be15ca3608c43e1234d9d07e3a190a53fa01536700196", size = 477300, upload-time = "2026-05-29T23:12:57.515Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f2/55/983b6bc1b6b343a5ff6020388f9d0680ab477be59a731517e6c4a0387100/hypothesis-6.155.7.tar.gz", hash = "sha256:d8d6091753d0669db3c90c5e5b346cb37c72f3dd9378c8413acb1fd5da63f7ea", size = 478291, upload-time = "2026-06-21T05:54:31.573Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/26/6e/8c9cf32201238617454303b1605dfa667d90cd1ef51226f92d9c2b3b8f7c/hypothesis-6.155.1-py3-none-any.whl", hash = "sha256:2753f469df3ba3c483b08e0c37dbcbc41d8316ebb921abcc07493ee9c8a7d187", size = 543715, upload-time = "2026-05-29T23:12:54.77Z" }, + { url = "https://files.pythonhosted.org/packages/01/f8/c151e196d4f397ed9436a071e52666c70a2f021138dea828b0a461e245db/hypothesis-6.155.7-py3-none-any.whl", hash = "sha256:9f634bdb1f9e9b8ab6ba09431cf2deedb750c96978125a6fb3c5a0f6c6db4131", size = 544762, upload-time = "2026-06-21T05:54:29.506Z" }, ] [[package]] name = "idna" -version = "3.17" +version = "3.18" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b9/28/99c51f664567218d824af024c0251650fb27e4ca066df188dab0769c5b91/idna-3.17.tar.gz", hash = "sha256:5eb0cb53bc467c12eadcf6de83163ad8527cec9416f44b9b61b19caedad2b87f", size = 196048, upload-time = "2026-05-28T14:32:38.55Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/63/9496c57188a2ee585e0f1db071d75089a11e98aa86eb99d9d7618fc1edce/idna-3.18.tar.gz", hash = "sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848", size = 196711, upload-time = "2026-06-02T14:34:07.794Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/de/a7/f76514cc40ad6234098ecdebda08732d75964776c51a42845b7da10649e2/idna-3.17-py3-none-any.whl", hash = "sha256:466e48829084efe2548012b855df21540b96f2e20e51bd124c851536556a592c", size = 65316, upload-time = "2026-05-28T14:32:37.035Z" }, + { url = "https://files.pythonhosted.org/packages/1e/5e/d4e9f1a599fb8e573b7b87160658329fbf28d19eac2718f51fc3def3aa5a/idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2", size = 65455, upload-time = "2026-06-02T14:34:06.319Z" }, ] [[package]] @@ -653,7 +580,7 @@ wheels = [ [[package]] name = "kframework" -version = "7.1.329" +version = "7.1.337" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "coloredlogs" }, @@ -672,9 +599,9 @@ dependencies = [ { name = "tomli-w" }, { name = "xdg-base-dirs" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4c/40/895de798a05cfd2a332fa3280e4a1483d4b5a5ec55ccc6833be91d65019f/kframework-7.1.329.tar.gz", hash = "sha256:2b7cd443520d7825a0d25b8a5f0a26a2a6def782f5d6b7f018134451ea0b5553", size = 247959, upload-time = "2026-05-30T06:06:08.341Z" } +sdist = { url = "https://files.pythonhosted.org/packages/82/14/b5e751e5be5c591cedfc8814a3e77ae729c145d89886fc9a18cf50b22938/kframework-7.1.337.tar.gz", hash = "sha256:9fdd05f228988757c9daee58564b45af424f0bc205912b0dd24b3d35a066b287", size = 253667, upload-time = "2026-06-18T13:51:13.311Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/95/6d/a245efc02f35fdd3d3542d94a16b55d0f8f2f97c39798737c155d6f38140/kframework-7.1.329-py3-none-any.whl", hash = "sha256:c28ae3c545a7fffe64e140f2d545283e384d742662474b183c297f5a34b86a71", size = 299676, upload-time = "2026-05-30T06:06:06.876Z" }, + { url = "https://files.pythonhosted.org/packages/57/0e/e9a214527eb818915e6820c353d7b6078eb7611a34ebc285bdbf8ff7316b/kframework-7.1.337-py3-none-any.whl", hash = "sha256:f8e0ba4effda64c1f10fbc306518e7485e1b24ddef9706021b2bbfc04ab5cb7d", size = 305216, upload-time = "2026-06-18T13:51:11.807Z" }, ] [[package]] @@ -999,38 +926,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl", hash = "sha256:d47fbf302e7d9cbbb9e2555a0d267983d2aa476bac30e90dfbe5669bd57f3762", size = 2068504, upload-time = "2025-12-08T17:02:38.159Z" }, ] -[[package]] -name = "numpy" -version = "1.26.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/65/6e/09db70a523a96d25e115e71cc56a6f9031e7b8cd166c1ac8438307c14058/numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010", size = 15786129, upload-time = "2024-02-06T00:26:44.495Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/94/ace0fdea5241a27d13543ee117cbc65868e82213fb31a8eb7fe9ff23f313/numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0", size = 20631468, upload-time = "2024-02-05T23:48:01.194Z" }, - { url = "https://files.pythonhosted.org/packages/20/f7/b24208eba89f9d1b58c1668bc6c8c4fd472b20c45573cb767f59d49fb0f6/numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a", size = 13966411, upload-time = "2024-02-05T23:48:29.038Z" }, - { url = "https://files.pythonhosted.org/packages/fc/a5/4beee6488160798683eed5bdb7eead455892c3b4e1f78d79d8d3f3b084ac/numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4", size = 14219016, upload-time = "2024-02-05T23:48:54.098Z" }, - { url = "https://files.pythonhosted.org/packages/4b/d7/ecf66c1cd12dc28b4040b15ab4d17b773b87fa9d29ca16125de01adb36cd/numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f", size = 18240889, upload-time = "2024-02-05T23:49:25.361Z" }, - { url = "https://files.pythonhosted.org/packages/24/03/6f229fe3187546435c4f6f89f6d26c129d4f5bed40552899fcf1f0bf9e50/numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a", size = 13876746, upload-time = "2024-02-05T23:49:51.983Z" }, - { url = "https://files.pythonhosted.org/packages/39/fe/39ada9b094f01f5a35486577c848fe274e374bbf8d8f472e1423a0bbd26d/numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2", size = 18078620, upload-time = "2024-02-05T23:50:22.515Z" }, - { url = "https://files.pythonhosted.org/packages/d5/ef/6ad11d51197aad206a9ad2286dc1aac6a378059e06e8cf22cd08ed4f20dc/numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07", size = 5972659, upload-time = "2024-02-05T23:50:35.834Z" }, - { url = "https://files.pythonhosted.org/packages/19/77/538f202862b9183f54108557bfda67e17603fc560c384559e769321c9d92/numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5", size = 15808905, upload-time = "2024-02-05T23:51:03.701Z" }, - { url = "https://files.pythonhosted.org/packages/11/57/baae43d14fe163fa0e4c47f307b6b2511ab8d7d30177c491960504252053/numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71", size = 20630554, upload-time = "2024-02-05T23:51:50.149Z" }, - { url = "https://files.pythonhosted.org/packages/1a/2e/151484f49fd03944c4a3ad9c418ed193cfd02724e138ac8a9505d056c582/numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef", size = 13997127, upload-time = "2024-02-05T23:52:15.314Z" }, - { url = "https://files.pythonhosted.org/packages/79/ae/7e5b85136806f9dadf4878bf73cf223fe5c2636818ba3ab1c585d0403164/numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e", size = 14222994, upload-time = "2024-02-05T23:52:47.569Z" }, - { url = "https://files.pythonhosted.org/packages/3a/d0/edc009c27b406c4f9cbc79274d6e46d634d139075492ad055e3d68445925/numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5", size = 18252005, upload-time = "2024-02-05T23:53:15.637Z" }, - { url = "https://files.pythonhosted.org/packages/09/bf/2b1aaf8f525f2923ff6cfcf134ae5e750e279ac65ebf386c75a0cf6da06a/numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a", size = 13885297, upload-time = "2024-02-05T23:53:42.16Z" }, - { url = "https://files.pythonhosted.org/packages/df/a0/4e0f14d847cfc2a633a1c8621d00724f3206cfeddeb66d35698c4e2cf3d2/numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a", size = 18093567, upload-time = "2024-02-05T23:54:11.696Z" }, - { url = "https://files.pythonhosted.org/packages/d2/b7/a734c733286e10a7f1a8ad1ae8c90f2d33bf604a96548e0a4a3a6739b468/numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20", size = 5968812, upload-time = "2024-02-05T23:54:26.453Z" }, - { url = "https://files.pythonhosted.org/packages/3f/6b/5610004206cf7f8e7ad91c5a85a8c71b2f2f8051a0c0c4d5916b76d6cbb2/numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2", size = 15811913, upload-time = "2024-02-05T23:54:53.933Z" }, - { url = "https://files.pythonhosted.org/packages/95/12/8f2020a8e8b8383ac0177dc9570aad031a3beb12e38847f7129bacd96228/numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218", size = 20335901, upload-time = "2024-02-05T23:55:32.801Z" }, - { url = "https://files.pythonhosted.org/packages/75/5b/ca6c8bd14007e5ca171c7c03102d17b4f4e0ceb53957e8c44343a9546dcc/numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b", size = 13685868, upload-time = "2024-02-05T23:55:56.28Z" }, - { url = "https://files.pythonhosted.org/packages/79/f8/97f10e6755e2a7d027ca783f63044d5b1bc1ae7acb12afe6a9b4286eac17/numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b", size = 13925109, upload-time = "2024-02-05T23:56:20.368Z" }, - { url = "https://files.pythonhosted.org/packages/0f/50/de23fde84e45f5c4fda2488c759b69990fd4512387a8632860f3ac9cd225/numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed", size = 17950613, upload-time = "2024-02-05T23:56:56.054Z" }, - { url = "https://files.pythonhosted.org/packages/4c/0c/9c603826b6465e82591e05ca230dfc13376da512b25ccd0894709b054ed0/numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a", size = 13572172, upload-time = "2024-02-05T23:57:21.56Z" }, - { url = "https://files.pythonhosted.org/packages/76/8c/2ba3902e1a0fc1c74962ea9bb33a534bb05984ad7ff9515bf8d07527cadd/numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0", size = 17786643, upload-time = "2024-02-05T23:57:56.585Z" }, - { url = "https://files.pythonhosted.org/packages/28/4a/46d9e65106879492374999e76eb85f87b15328e06bd1550668f79f7b18c6/numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110", size = 5677803, upload-time = "2024-02-05T23:58:08.963Z" }, - { url = "https://files.pythonhosted.org/packages/16/2e/86f24451c2d530c88daf997cb8d6ac622c1d40d19f5a031ed68a4b73a374/numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818", size = 15517754, upload-time = "2024-02-05T23:58:36.364Z" }, -] - [[package]] name = "packaging" version = "26.2" @@ -1093,17 +988,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/05/33/2d74d588408caedd065c2497bdb5ef83ce6082db01289a1e1147f6639802/psutil-5.9.8-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:d16bbddf0693323b8c6123dd804100241da461e41d6e332fb0ba6058f630f8c8", size = 249898, upload-time = "2024-01-19T20:47:59.238Z" }, ] -[[package]] -name = "py-wasm" -version = "0.3.1" -source = { git = "https://github.com/runtimeverification/py-wasm.git?rev=0.3.1#19f5a32b227c3f05cb49a604af65dfb7cc1ff083" } -dependencies = [ - { name = "cytoolz", marker = "implementation_name == 'cpython'" }, - { name = "mypy-extensions" }, - { name = "numpy" }, - { name = "toolz", marker = "implementation_name == 'pypy'" }, -] - [[package]] name = "pybind11" version = "2.13.6" @@ -1146,7 +1030,6 @@ version = "0.1.156" source = { editable = "." } dependencies = [ { name = "kframework" }, - { name = "py-wasm" }, ] [package.dev-dependencies] @@ -1169,10 +1052,7 @@ dev = [ ] [package.metadata] -requires-dist = [ - { name = "kframework", specifier = ">=7.1.329" }, - { name = "py-wasm", git = "https://github.com/runtimeverification/py-wasm.git?rev=0.3.1" }, -] +requires-dist = [{ name = "kframework", specifier = ">=7.1.329" }] [package.metadata.requires-dev] dev = [ @@ -1204,7 +1084,7 @@ wheels = [ [[package]] name = "pytest" -version = "9.0.3" +version = "9.1.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, @@ -1215,9 +1095,9 @@ dependencies = [ { name = "pygments" }, { name = "tomli", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165, upload-time = "2026-04-07T17:16:18.027Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/47/b9efed96c114afcfa3c9d3fe98a76a1d14c74a9e266d397cf6eb64be5e01/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313", size = 1636369, upload-time = "2026-06-19T10:58:32.857Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" }, + { url = "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c", size = 386536, upload-time = "2026-06-19T10:58:31.347Z" }, ] [[package]] @@ -1551,15 +1431,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c7/18/c86eb8e0202e32dd3df50d43d7ff9854f8e0603945ff398974c1d91ac1ef/tomli_w-1.2.0-py3-none-any.whl", hash = "sha256:188306098d013b691fcadc011abd66727d3c414c571bb01b1a174ba8c983cf90", size = 6675, upload-time = "2025-01-15T12:07:22.074Z" }, ] -[[package]] -name = "toolz" -version = "0.12.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3e/bf/5e12db234df984f6df3c7f12f1428aa680ba4e101f63f4b8b3f9e8d2e617/toolz-0.12.1.tar.gz", hash = "sha256:ecca342664893f177a13dac0e6b41cbd8ac25a358e5f215316d43e2100224f4d", size = 66550, upload-time = "2024-01-24T03:28:28.047Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/8a/d82202c9f89eab30f9fc05380daae87d617e2ad11571ab23d7c13a29bb54/toolz-0.12.1-py3-none-any.whl", hash = "sha256:d22731364c07d72eea0a0ad45bafb2c2937ab6fd38a3507bf55eae8744aa7d85", size = 56121, upload-time = "2024-01-24T03:28:25.97Z" }, -] - [[package]] name = "typing-extensions" version = "4.15.0" diff --git a/tests/proofs/loops-spec.k b/tests/proofs/loops-spec.k index 46e9dea07..d1efb8d4b 100644 --- a/tests/proofs/loops-spec.k +++ b/tests/proofs/loops-spec.k @@ -18,8 +18,7 @@ module LOOPS-SPEC #local.tee(0) ITYPE.eqz #br_if(1) - #br(0), - _ + #br(0) ) } .ValStack @@ -51,10 +50,8 @@ module LOOPS-SPEC #local.tee(0) ITYPE.eqz #br_if(1) - #br(0), - _ - ), - _ + #br(0) + ) ) => .K ...