diff --git a/Cargo.lock b/Cargo.lock index f08414b..5506ed3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + [[package]] name = "aho-corasick" version = "1.1.3" @@ -17,6 +23,12 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "basic-toml" version = "0.1.9" @@ -26,12 +38,37 @@ dependencies = [ "serde", ] +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "flate2" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "heck" version = "0.5.0" @@ -108,6 +145,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] + [[package]] name = "nom" version = "7.1.3" @@ -118,12 +164,27 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "once_cell" version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + [[package]] name = "percent-encoding" version = "2.3.1" @@ -296,6 +357,28 @@ dependencies = [ "serde", ] +[[package]] +name = "rmp" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "228ed7c16fa39782c3b3468e974aec2795e9089153cd08ee2e9aefb3613334c4" +dependencies = [ + "byteorder", + "num-traits", + "paste", +] + +[[package]] +name = "rmp-serde" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e599a477cf9840e92f2cde9a7189e67b42c57532749bf90aea6ec10facd4db" +dependencies = [ + "byteorder", + "rmp", + "serde", +] + [[package]] name = "rustc-hash" version = "2.0.0" @@ -361,10 +444,13 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" name = "test_results_parser" version = "0.5.1" dependencies = [ + "base64", + "flate2", "pyo3", "quick-xml", "regex", "rinja", + "rmp-serde", "serde", "serde_json", ] diff --git a/Cargo.toml b/Cargo.toml index 89e30bf..c3728f5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,3 +15,6 @@ regex = "1.11.1" serde = { version = "1.0.215", features = ["derive"] } serde_json = "1.0.133" rinja = "0.3.5" +rmp-serde = "1.3.0" +base64 = "0.22.1" +flate2 = "1.0.35" \ No newline at end of file diff --git a/README.md b/README.md index 9a8450c..0ea48ca 100644 --- a/README.md +++ b/README.md @@ -13,14 +13,15 @@ The CI uses the maturin-action to build wheels and an sdist The version of the wheels built are determined by the value of the version in the cargo.toml -There are 2 parsing function currently implemented: + +There are 2 parsing functions currently implemented: - `parse_junit_xml`: this parses `junit.xml` files -- `parse_pytest_reportlog`: this parses files produced by the `pytest-reportlog` extension -Both these functions take the path to the file to parse as an arg and return a list of `Testrun` objects. +This function takes the path to the file to parse as an arg and returns a list of `Testrun` objects. The `Testrun` objects look like this: + ``` Outcome: Pass, @@ -33,4 +34,8 @@ Testrun: outcome: Outcome duration: float testsuite: str -``` \ No newline at end of file +``` + +- `parse_raw_upload`: this parses an entire raw test results upload + +this function takes in the raw upload bytes and returns a message packed list of Testrun objects diff --git a/pyproject.toml b/pyproject.toml index 86067b3..c268531 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,4 +21,5 @@ dev-dependencies = [ "pytest-cov>=5.0.0", "pytest-reportlog>=0.4.0", "maturin>=1.7.4", + "msgpack>=1.1.0", ] diff --git a/src/lib.rs b/src/lib.rs index e71de8d..e1a4722 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,7 @@ use pyo3::prelude::*; mod compute_name; mod failure_message; mod junit; +mod raw_upload; mod testrun; pyo3::create_exception!(test_results_parser, ParserError, PyException); @@ -18,6 +19,7 @@ fn test_results_parser(py: Python, m: &Bound) -> PyResult<()> { m.add_class::()?; m.add_class::()?; + m.add_function(wrap_pyfunction!(raw_upload::parse_raw_upload, m)?)?; m.add_function(wrap_pyfunction!(junit::parse_junit_xml, m)?)?; m.add_function(wrap_pyfunction!(failure_message::build_message, m)?)?; m.add_function(wrap_pyfunction!(failure_message::escape_message, m)?)?; diff --git a/src/raw_upload.rs b/src/raw_upload.rs new file mode 100644 index 0000000..098f67e --- /dev/null +++ b/src/raw_upload.rs @@ -0,0 +1,101 @@ +use base64::prelude::*; +use pyo3::prelude::*; +use std::collections::HashSet; +use std::io::prelude::*; + +use flate2::bufread::ZlibDecoder; + +use quick_xml::reader::Reader; +use serde::Deserialize; + +use crate::junit::{get_position_info, use_reader}; +use crate::testrun::ParsingInfo; +use crate::ParserError; + +#[derive(Deserialize, Debug, Clone)] +struct TestResultFile { + filename: String, + #[serde(skip_deserializing)] + _format: String, + data: String, + #[serde(skip_deserializing)] + _labels: Vec, +} +#[derive(Deserialize, Debug, Clone)] +struct RawTestResultUpload { + #[serde(default)] + network: Option>, + test_results_files: Vec, +} + +#[derive(Debug, Clone)] +struct ReadableFile { + filename: Vec, + data: Vec, +} + +const LEGACY_FORMAT_PREFIX: &[u8] = b"# path="; +const LEGACY_FORMAT_SUFFIX: &[u8] = b"<<<<<< EOF"; + +fn serialize_to_legacy_format(readable_files: Vec) -> Vec { + let mut res = Vec::new(); + for file in readable_files { + res.extend_from_slice(LEGACY_FORMAT_PREFIX); + res.extend_from_slice(&file.filename); + res.extend_from_slice(b"\n"); + res.extend_from_slice(&file.data); + res.extend_from_slice(b"\n"); + res.extend_from_slice(LEGACY_FORMAT_SUFFIX); + res.extend_from_slice(b"\n"); + } + res +} + +#[pyfunction] +#[pyo3(signature = (raw_upload_bytes))] +pub fn parse_raw_upload(raw_upload_bytes: &[u8]) -> PyResult<(Vec, Vec)> { + let upload: RawTestResultUpload = serde_json::from_slice(raw_upload_bytes) + .map_err(|e| ParserError::new_err(format!("Error deserializing json: {}", e)))?; + let network: Option> = upload.network.map(|v| v.into_iter().collect()); + + let mut results: Vec = Vec::new(); + let mut readable_files: Vec = Vec::new(); + + for file in upload.test_results_files { + let decoded_file_bytes = BASE64_STANDARD + .decode(file.data) + .map_err(|e| ParserError::new_err(format!("Error decoding base64: {}", e)))?; + + let mut decoder = ZlibDecoder::new(&decoded_file_bytes[..]); + + let mut decompressed_file_bytes = Vec::new(); + decoder + .read_to_end(&mut decompressed_file_bytes) + .map_err(|e| ParserError::new_err(format!("Error decompressing file: {}", e)))?; + + let mut reader = Reader::from_reader(&decompressed_file_bytes[..]); + reader.config_mut().trim_text(true); + let reader_result = use_reader(&mut reader, network.as_ref()).map_err(|e| { + let pos = reader.buffer_position(); + let (line, col) = get_position_info(&decompressed_file_bytes, pos.try_into().unwrap()); + ParserError::new_err(format!( + "Error parsing JUnit XML in {} at {}:{}: {}", + file.filename, line, col, e + )) + })?; + results.push(reader_result); + + let readable_file = ReadableFile { + data: decompressed_file_bytes, + filename: file.filename.into_bytes(), + }; + readable_files.push(readable_file); + } + + let results_bytes = rmp_serde::to_vec_named(&results) + .map_err(|_| ParserError::new_err("Error serializing pr comment summary"))?; + + let readable_file = serialize_to_legacy_format(readable_files); + + Ok((results_bytes, readable_file)) +} diff --git a/src/testrun.rs b/src/testrun.rs index 14a74db..5ccd820 100644 --- a/src/testrun.rs +++ b/src/testrun.rs @@ -3,6 +3,8 @@ use std::fmt::Display; use pyo3::class::basic::CompareOp; use pyo3::{prelude::*, pyclass}; +use serde::Serialize; + #[derive(Clone, Copy, Debug, PartialEq)] // See https://github.com/PyO3/pyo3/issues/4723 #[allow(ambiguous_associated_items)] @@ -37,13 +39,22 @@ impl Outcome { } } +impl Serialize for Outcome { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + impl Display for Outcome { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match &self { - Outcome::Pass => write!(f, "Pass"), - Outcome::Failure => write!(f, "Failure"), - Outcome::Error => write!(f, "Error"), - Outcome::Skip => write!(f, "Skip"), + Outcome::Pass => write!(f, "pass"), + Outcome::Failure => write!(f, "failure"), + Outcome::Error => write!(f, "error"), + Outcome::Skip => write!(f, "skip"), } } } @@ -77,7 +88,7 @@ pub fn check_testsuites_name(testsuites_name: &str) -> Option { .next() } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Serialize)] #[pyclass] pub struct Testrun { #[pyo3(get, set)] @@ -189,7 +200,7 @@ impl Testrun { } } -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Serialize)] #[pyclass(eq, eq_int)] pub enum Framework { Pytest, @@ -221,7 +232,7 @@ impl Display for Framework { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize)] #[pyclass] pub struct ParsingInfo { #[pyo3(get, set)] diff --git a/tests/test_parse_raw_upload.py b/tests/test_parse_raw_upload.py new file mode 100644 index 0000000..96797de --- /dev/null +++ b/tests/test_parse_raw_upload.py @@ -0,0 +1,347 @@ +import pytest +import base64 +import zlib +import json +import msgpack +from test_results_parser import parse_raw_upload + +class TestParsers: + @pytest.mark.parametrize( + "filename,expected", + [ + ( + "./tests/junit.xml", + { + "framework": "Pytest", + "testruns": [ + { + "name": "test_junit[junit.xml--True]", + "classname": "tests.test_parsers.TestParsers", + "duration": 0.001, + "outcome": "failure", + "testsuite": "pytest", + "failure_message": """self = , filename = 'junit.xml', expected = '', check = True + + @pytest.mark.parametrize( + \"filename,expected,check\", + [(\"junit.xml\", \"\", True), (\"jest-junit.xml\", \"\", False)], + ) + def test_junit(self, filename, expected, check): + with open(filename) as f: + junit_string = f.read() + res = parse_junit_xml(junit_string) + print(res) + if check: +> assert res == expected +E AssertionError: assert [{'duration': '0.010', 'name': 'tests.test_parsers.TestParsers.test_junit[junit.xml-]', 'outcome': 'failure'}, {'duration': '0.063', 'name': 'tests.test_parsers.TestParsers.test_junit[jest-junit.xml-]', 'outcome': 'pass'}] == '' + +tests/test_parsers.py:16: AssertionError""", + "filename": None, + "build_url": None, + "computed_name": "tests.test_parsers.TestParsers::test_junit[junit.xml--True]", + }, + { + "name": "test_junit[jest-junit.xml--False]", + "classname": "tests.test_parsers.TestParsers", + "duration": 0.064, + "outcome": "pass", + "testsuite": "pytest", + "failure_message": None, + "filename": None, + "build_url": None, + "computed_name": "tests.test_parsers.TestParsers::test_junit[jest-junit.xml--False]", + }, + ], + }, + ), + ( + "./tests/junit-no-testcase-timestamp.xml", + { + "framework": "Pytest", + "testruns": [ + { + "name": "test_junit[junit.xml--True]", + "classname": "tests.test_parsers.TestParsers", + "duration": 0.186, + "outcome": "failure", + "testsuite": "pytest", + "failure_message": "aaaaaaa", + "filename": None, + "build_url": None, + "computed_name": "tests.test_parsers.TestParsers::test_junit[junit.xml--True]", + }, + { + "name": "test_junit[jest-junit.xml--False]", + "classname": "tests.test_parsers.TestParsers", + "duration": 0.186, + "outcome": "pass", + "testsuite": "pytest", + "failure_message": None, + "filename": None, + "build_url": None, + "computed_name": "tests.test_parsers.TestParsers::test_junit[jest-junit.xml--False]", + }, + ], + }, + ), + ( + "./tests/junit-nested-testsuite.xml", + { + "framework": "Pytest", + "testruns": [ + { + "name": "test_junit[junit.xml--True]", + "classname": "tests.test_parsers.TestParsers", + "duration": 0.186, + "outcome": "failure", + "testsuite": "nested_testsuite", + "failure_message": "aaaaaaa", + "filename": None, + "build_url": None, + "computed_name": None, + }, + { + "name": "test_junit[jest-junit.xml--False]", + "classname": "tests.test_parsers.TestParsers", + "duration": 0.186, + "outcome": "pass", + "testsuite": "pytest", + "failure_message": None, + "filename": None, + "build_url": None, + "computed_name": "tests.test_parsers.TestParsers::test_junit[jest-junit.xml--False]", + }, + ], + }, + ), + ( + "./tests/jest-junit.xml", + { + "framework": "Jest", + "testruns": [ + { + "name": "Title when rendered renders pull title", + "classname": "Title when rendered renders pull title", + "duration": 0.036, + "outcome": "pass", + "testsuite": "Title", + "failure_message": None, + "filename": None, + "build_url": None, + "computed_name": "Title when rendered renders pull title", + }, + { + "name": "Title when rendered renders pull author", + "classname": "Title when rendered renders pull author", + "duration": 0.005, + "outcome": "pass", + "testsuite": "Title", + "failure_message": None, + "filename": None, + "build_url": None, + "computed_name": "Title when rendered renders pull author", + }, + { + "name": "Title when rendered renders pull updatestamp", + "classname": "Title when rendered renders pull updatestamp", + "duration": 0.002, + "outcome": "pass", + "testsuite": "Title", + "failure_message": None, + "filename": None, + "build_url": None, + "computed_name": "Title when rendered renders pull updatestamp", + }, + { + "name": "Title when rendered for first pull request renders pull title", + "classname": "Title when rendered for first pull request renders pull title", + "duration": 0.006, + "outcome": "pass", + "testsuite": "Title", + "failure_message": None, + "filename": None, + "build_url": None, + "computed_name": "Title when rendered for first pull request renders pull title", + }, + ], + }, + ), + ( + "./tests/vitest-junit.xml", + { + "framework": "Vitest", + "testruns": [ + { + "name": "first test file > 2 + 2 should equal 4", + "classname": "__tests__/test-file-1.test.ts", + "duration": 0.01, + "outcome": "failure", + "testsuite": "__tests__/test-file-1.test.ts", + "failure_message": """AssertionError: expected 5 to be 4 // Object.is equality + ❯ __tests__/test-file-1.test.ts:20:28""", + "filename": None, + "build_url": None, + "computed_name": "__tests__/test-file-1.test.ts > first test file > 2 + 2 should equal 4", + }, + { + "name": "first test file > 4 - 2 should equal 2", + "classname": "__tests__/test-file-1.test.ts", + "duration": 0.0, + "outcome": "pass", + "testsuite": "__tests__/test-file-1.test.ts", + "failure_message": None, + "filename": None, + "build_url": None, + "computed_name": "__tests__/test-file-1.test.ts > first test file > 4 - 2 should equal 2", + }, + ], + }, + ), + ( + "./tests/empty_failure.junit.xml", + { + "framework": None, + "testruns": [ + { + "name": "test.test works", + "classname": "test.test", + "duration": 0.234, + "outcome": "pass", + "testsuite": "test", + "failure_message": None, + "filename": "./test.rb", + "build_url": None, + "computed_name": None, + }, + { + "name": "test.test fails", + "classname": "test.test", + "duration": 1.0, + "outcome": "failure", + "testsuite": "test", + "failure_message": "TestError", + "filename": "./test.rb", + "build_url": None, + "computed_name": None, + }, + ], + }, + ), + ( + "./tests/phpunit.junit.xml", + { + "framework": "PHPUnit", + "testruns": [ + { + "name": "test1", + "classname": "class.className", + "duration": 0.1, + "outcome": "pass", + "testsuite": "Thing", + "failure_message": None, + "filename": "/file1.php", + "build_url": None, + "computed_name": "class.className::test1", + }, + { + "name": "test2", + "classname": "", + "duration": 0.1, + "outcome": "pass", + "testsuite": "Thing", + "failure_message": None, + "filename": "/file1.php", + "build_url": None, + "computed_name": "::test2", + }, + ], + }, + ), + ( + "./tests/ctest.xml", + { + "framework": None, + "testruns": [ + { + "name": "a_unit_test", + "classname": "a_unit_test", + "duration": 33.4734, + "outcome": "failure", + "testsuite": "Linux-c++", + "failure_message": "Failed", + "filename": None, + "build_url": None, + "computed_name": None, + } + ], + }, + ), + ( + "./tests/no-testsuite-name.xml", + { + "framework": None, + "testruns": [ + { + "name": "a_unit_test", + "classname": "a_unit_test", + "duration": 33.4734, + "outcome": "failure", + "testsuite": "", + "failure_message": "Failed", + "filename": None, + "build_url": None, + "computed_name": None, + } + ], + }, + ), + ("./tests/testsuites.xml", {"framework": None, "testruns": []}), + ], + ) + def test_junit(self, filename, expected): + with open(filename, "b+r") as f: + file_bytes = f.read() + thing = { + "network": [ + "a/b/c.py", + ], + "test_results_files": [ + { + "filename": filename, + "format": "base64+compressed", + "data": base64.b64encode(zlib.compress(file_bytes)).decode( + "utf-8" + ), + } + ] + } + json_bytes = json.dumps(thing).encode("utf-8") + msgpack_bytes, readable_files_bytes = parse_raw_upload(json_bytes) + + + + res_list = msgpack.unpackb( + bytes(msgpack_bytes) + ) + + readable_files = bytes(readable_files_bytes) + + assert readable_files == f"""# path={filename}\n{file_bytes.decode()}\n<<<<<< EOF\n""".encode() + + + assert res_list[0]["framework"] == expected["framework"] + assert res_list[0]["testruns"] == expected["testruns"] + assert len(res_list[0]["testruns"]) == len(expected["testruns"]) + for restest, extest in zip(res_list[0]["testruns"], expected["testruns"]): + print( + restest["classname"], + restest["duration"], + restest["filename"], + restest["name"], + restest["outcome"], + restest["testsuite"], + extest["failure_message"], + extest["filename"], + extest["computed_name"], + ) + assert restest == extest diff --git a/uv.lock b/uv.lock index 77bb595..1210158 100644 --- a/uv.lock +++ b/uv.lock @@ -77,6 +77,36 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/30/38/8e27282ab6ff94291f04e3eb11ba960dfb487605d73cec75177d3a29879a/maturin-1.7.4-py3-none-win_arm64.whl", hash = "sha256:f3d38a6d0c7fd7b04bec30dd470b2173cf9bd184ab6220c1acaf49df6b48faf5", size = 6388008 }, ] +[[package]] +name = "msgpack" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cb/d0/7555686ae7ff5731205df1012ede15dd9d927f6227ea151e901c7406af4f/msgpack-1.1.0.tar.gz", hash = "sha256:dd432ccc2c72b914e4cb77afce64aab761c1137cc698be3984eee260bcb2896e", size = 167260 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/d6/716b7ca1dbde63290d2973d22bbef1b5032ca634c3ff4384a958ec3f093a/msgpack-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d46cf9e3705ea9485687aa4001a76e44748b609d260af21c4ceea7f2212a501d", size = 152421 }, + { url = "https://files.pythonhosted.org/packages/70/da/5312b067f6773429cec2f8f08b021c06af416bba340c912c2ec778539ed6/msgpack-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5dbad74103df937e1325cc4bfeaf57713be0b4f15e1c2da43ccdd836393e2ea2", size = 85277 }, + { url = "https://files.pythonhosted.org/packages/28/51/da7f3ae4462e8bb98af0d5bdf2707f1b8c65a0d4f496e46b6afb06cbc286/msgpack-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:58dfc47f8b102da61e8949708b3eafc3504509a5728f8b4ddef84bd9e16ad420", size = 82222 }, + { url = "https://files.pythonhosted.org/packages/33/af/dc95c4b2a49cff17ce47611ca9ba218198806cad7796c0b01d1e332c86bb/msgpack-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4676e5be1b472909b2ee6356ff425ebedf5142427842aa06b4dfd5117d1ca8a2", size = 392971 }, + { url = "https://files.pythonhosted.org/packages/f1/54/65af8de681fa8255402c80eda2a501ba467921d5a7a028c9c22a2c2eedb5/msgpack-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17fb65dd0bec285907f68b15734a993ad3fc94332b5bb21b0435846228de1f39", size = 401403 }, + { url = "https://files.pythonhosted.org/packages/97/8c/e333690777bd33919ab7024269dc3c41c76ef5137b211d776fbb404bfead/msgpack-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a51abd48c6d8ac89e0cfd4fe177c61481aca2d5e7ba42044fd218cfd8ea9899f", size = 385356 }, + { url = "https://files.pythonhosted.org/packages/57/52/406795ba478dc1c890559dd4e89280fa86506608a28ccf3a72fbf45df9f5/msgpack-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2137773500afa5494a61b1208619e3871f75f27b03bcfca7b3a7023284140247", size = 383028 }, + { url = "https://files.pythonhosted.org/packages/e7/69/053b6549bf90a3acadcd8232eae03e2fefc87f066a5b9fbb37e2e608859f/msgpack-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:398b713459fea610861c8a7b62a6fec1882759f308ae0795b5413ff6a160cf3c", size = 391100 }, + { url = "https://files.pythonhosted.org/packages/23/f0/d4101d4da054f04274995ddc4086c2715d9b93111eb9ed49686c0f7ccc8a/msgpack-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:06f5fd2f6bb2a7914922d935d3b8bb4a7fff3a9a91cfce6d06c13bc42bec975b", size = 394254 }, + { url = "https://files.pythonhosted.org/packages/1c/12/cf07458f35d0d775ff3a2dc5559fa2e1fcd06c46f1ef510e594ebefdca01/msgpack-1.1.0-cp312-cp312-win32.whl", hash = "sha256:ad33e8400e4ec17ba782f7b9cf868977d867ed784a1f5f2ab46e7ba53b6e1e1b", size = 69085 }, + { url = "https://files.pythonhosted.org/packages/73/80/2708a4641f7d553a63bc934a3eb7214806b5b39d200133ca7f7afb0a53e8/msgpack-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:115a7af8ee9e8cddc10f87636767857e7e3717b7a2e97379dc2054712693e90f", size = 75347 }, + { url = "https://files.pythonhosted.org/packages/c8/b0/380f5f639543a4ac413e969109978feb1f3c66e931068f91ab6ab0f8be00/msgpack-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:071603e2f0771c45ad9bc65719291c568d4edf120b44eb36324dcb02a13bfddf", size = 151142 }, + { url = "https://files.pythonhosted.org/packages/c8/ee/be57e9702400a6cb2606883d55b05784fada898dfc7fd12608ab1fdb054e/msgpack-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0f92a83b84e7c0749e3f12821949d79485971f087604178026085f60ce109330", size = 84523 }, + { url = "https://files.pythonhosted.org/packages/7e/3a/2919f63acca3c119565449681ad08a2f84b2171ddfcff1dba6959db2cceb/msgpack-1.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1964df7b81285d00a84da4e70cb1383f2e665e0f1f2a7027e683956d04b734", size = 81556 }, + { url = "https://files.pythonhosted.org/packages/7c/43/a11113d9e5c1498c145a8925768ea2d5fce7cbab15c99cda655aa09947ed/msgpack-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59caf6a4ed0d164055ccff8fe31eddc0ebc07cf7326a2aaa0dbf7a4001cd823e", size = 392105 }, + { url = "https://files.pythonhosted.org/packages/2d/7b/2c1d74ca6c94f70a1add74a8393a0138172207dc5de6fc6269483519d048/msgpack-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0907e1a7119b337971a689153665764adc34e89175f9a34793307d9def08e6ca", size = 399979 }, + { url = "https://files.pythonhosted.org/packages/82/8c/cf64ae518c7b8efc763ca1f1348a96f0e37150061e777a8ea5430b413a74/msgpack-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65553c9b6da8166e819a6aa90ad15288599b340f91d18f60b2061f402b9a4915", size = 383816 }, + { url = "https://files.pythonhosted.org/packages/69/86/a847ef7a0f5ef3fa94ae20f52a4cacf596a4e4a010197fbcc27744eb9a83/msgpack-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7a946a8992941fea80ed4beae6bff74ffd7ee129a90b4dd5cf9c476a30e9708d", size = 380973 }, + { url = "https://files.pythonhosted.org/packages/aa/90/c74cf6e1126faa93185d3b830ee97246ecc4fe12cf9d2d31318ee4246994/msgpack-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4b51405e36e075193bc051315dbf29168d6141ae2500ba8cd80a522964e31434", size = 387435 }, + { url = "https://files.pythonhosted.org/packages/7a/40/631c238f1f338eb09f4acb0f34ab5862c4e9d7eda11c1b685471a4c5ea37/msgpack-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4c01941fd2ff87c2a934ee6055bda4ed353a7846b8d4f341c428109e9fcde8c", size = 399082 }, + { url = "https://files.pythonhosted.org/packages/e9/1b/fa8a952be252a1555ed39f97c06778e3aeb9123aa4cccc0fd2acd0b4e315/msgpack-1.1.0-cp313-cp313-win32.whl", hash = "sha256:7c9a35ce2c2573bada929e0b7b3576de647b0defbd25f5139dcdaba0ae35a4cc", size = 69037 }, + { url = "https://files.pythonhosted.org/packages/b6/bc/8bd826dd03e022153bfa1766dcdec4976d6c818865ed54223d71f07862b3/msgpack-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:bce7d9e614a04d0883af0b3d4d501171fbfca038f12c77fa838d9f198147a23f", size = 75140 }, +] + [[package]] name = "packaging" version = "24.2" @@ -143,6 +173,7 @@ source = { editable = "." } [package.dev-dependencies] dev = [ { name = "maturin" }, + { name = "msgpack" }, { name = "pytest" }, { name = "pytest-cov" }, { name = "pytest-reportlog" }, @@ -153,6 +184,7 @@ dev = [ [package.metadata.requires-dev] dev = [ { name = "maturin", specifier = ">=1.7.4" }, + { name = "msgpack", specifier = ">=1.1.0" }, { name = "pytest", specifier = ">=8.3.3" }, { name = "pytest-cov", specifier = ">=5.0.0" }, { name = "pytest-reportlog", specifier = ">=0.4.0" },