diff --git a/pyproject.toml b/pyproject.toml index daa643a81..26f3a08b5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,7 +15,6 @@ dependencies = [ "pydantic[email]>=2.11.7", "pyperclip>=1.9.0", "openapi-core>=0.19.5", - "msgspec>=0.19.0", ] requires-python = ">=3.10" readme = "README.md" diff --git a/src/fastmcp/experimental/utilities/openapi/parser.py b/src/fastmcp/experimental/utilities/openapi/parser.py index 300a46b06..53d26a7a4 100644 --- a/src/fastmcp/experimental/utilities/openapi/parser.py +++ b/src/fastmcp/experimental/utilities/openapi/parser.py @@ -36,7 +36,7 @@ from .models import ( ) from .schemas import ( _combine_schemas_and_map_params, - _replace_ref_with_defs_recursive, + _replace_ref_with_defs, ) logger = get_logger(__name__) @@ -225,7 +225,7 @@ class OpenAPIParser( # Convert refs from OpenAPI format to JSON Schema format using recursive approach - result = _replace_ref_with_defs_recursive(result) + result = _replace_ref_with_defs(result) return result except ValueError as e: # Re-raise ValueError for external reference errors and other validation issues @@ -606,7 +606,7 @@ class OpenAPIParser( # Convert each schema definition recursively for name, schema in schema_definitions.items(): if isinstance(schema, dict): - schema_definitions[name] = _replace_ref_with_defs_recursive(schema) + schema_definitions[name] = _replace_ref_with_defs(schema) # Process paths and operations for path_str, path_item_obj in self.openapi.paths.items(): diff --git a/src/fastmcp/experimental/utilities/openapi/schemas.py b/src/fastmcp/experimental/utilities/openapi/schemas.py index 279046d32..98713ca37 100644 --- a/src/fastmcp/experimental/utilities/openapi/schemas.py +++ b/src/fastmcp/experimental/utilities/openapi/schemas.py @@ -71,7 +71,7 @@ def clean_schema_for_display(schema: JsonSchema | None) -> JsonSchema | None: return cleaned -def _replace_ref_with_defs_recursive( +def _replace_ref_with_defs( info: dict[str, Any], description: str | None = None ) -> dict[str, Any]: """ @@ -106,35 +106,22 @@ def _replace_ref_with_defs_recursive( ) elif properties := schema.get("properties"): if "$ref" in properties: - schema["properties"] = _replace_ref_with_defs_recursive(properties) + schema["properties"] = _replace_ref_with_defs(properties) else: schema["properties"] = { - prop_name: _replace_ref_with_defs_recursive(prop_schema) + prop_name: _replace_ref_with_defs(prop_schema) for prop_name, prop_schema in properties.items() } elif item_schema := schema.get("items"): - schema["items"] = _replace_ref_with_defs_recursive(item_schema) + schema["items"] = _replace_ref_with_defs(item_schema) for section in ["anyOf", "allOf", "oneOf"]: for i, item in enumerate(schema.get(section, [])): - schema[section][i] = _replace_ref_with_defs_recursive(item) + schema[section][i] = _replace_ref_with_defs(item) if info.get("description", description) and not schema.get("description"): schema["description"] = description return schema -def _ensure_refs_converted(schema: dict[str, Any]) -> dict[str, Any]: - """ - Ensure all OpenAPI refs are converted to JSON Schema format using recursive approach. - - Args: - schema: Schema that may contain OpenAPI refs - - Returns: - Schema with all refs converted to JSON Schema format - """ - return _replace_ref_with_defs_recursive(schema) - - def _make_optional_parameter_nullable(schema: dict[str, Any]) -> dict[str, Any]: """ Make an optional parameter schema nullable to allow None values. @@ -250,7 +237,7 @@ def _combine_schemas_and_map_params( # Convert refs if needed if convert_refs: - body_schema = _ensure_refs_converted( + body_schema = _replace_ref_with_defs( route.request_body.content_schema[content_type] ) else: @@ -310,7 +297,7 @@ def _combine_schemas_and_map_params( # Convert refs if needed if convert_refs: - param_schema = _ensure_refs_converted(param.schema_) + param_schema = _replace_ref_with_defs(param.schema_) else: param_schema = param.schema_ original_desc = param_schema.get("description", "") @@ -337,7 +324,7 @@ def _combine_schemas_and_map_params( # Convert refs if needed if convert_refs: - param_schema = _ensure_refs_converted(param.schema_) + param_schema = _replace_ref_with_defs(param.schema_) else: param_schema = param.schema_ @@ -384,7 +371,7 @@ def _combine_schemas_and_map_params( # Convert each schema definition recursively for name, schema in all_defs.items(): if isinstance(schema, dict): - all_defs[name] = _replace_ref_with_defs_recursive(schema) + all_defs[name] = _replace_ref_with_defs(schema) # Prune to only needed schemas used_refs = set() @@ -514,7 +501,7 @@ def extract_output_schema_from_responses( return None # Convert refs if needed - output_schema = _ensure_refs_converted(schema) + output_schema = _replace_ref_with_defs(schema) # If schema has a $ref, resolve it first before processing nullable fields if "$ref" in output_schema and schema_definitions: @@ -523,7 +510,7 @@ def extract_output_schema_from_responses( schema_name = ref_path.split("/")[-1] if schema_name in schema_definitions: # Replace $ref with the actual schema definition - output_schema = _ensure_refs_converted(schema_definitions[schema_name]) + output_schema = _replace_ref_with_defs(schema_definitions[schema_name]) # Convert OpenAPI schema to JSON Schema format # Only needed for OpenAPI 3.0 - 3.1 uses standard JSON Schema null types @@ -553,7 +540,7 @@ def extract_output_schema_from_responses( # Convert each schema definition recursively for name, schema in processed_defs.items(): if isinstance(schema, dict): - processed_defs[name] = _replace_ref_with_defs_recursive(schema) + processed_defs[name] = _replace_ref_with_defs(schema) # Convert OpenAPI schema definitions to JSON Schema format if needed if openapi_version and openapi_version.startswith("3.0"): diff --git a/tests/experimental/README.md b/tests/experimental/README.md new file mode 100644 index 000000000..1912e19d6 --- /dev/null +++ b/tests/experimental/README.md @@ -0,0 +1,3 @@ +# Experimental Tests + +Each directory in this folder tests a discrete experimental feature. The directory structure within each experiment should approximate the "normal" test directory structure to facilitate moving tests into place once/if the experiment is merged into the main codebase. \ No newline at end of file diff --git a/tests/experimental/openapi_parser/README.md b/tests/experimental/openapi_parser/README.md new file mode 100644 index 000000000..2f7302546 --- /dev/null +++ b/tests/experimental/openapi_parser/README.md @@ -0,0 +1,5 @@ +# OpenAPI Parser Experiment + +Performance-optimized OpenAPI parser with better transitive reference resolution. + +Enabled via `experimental.enable_new_openapi_parser = True` \ No newline at end of file diff --git a/tests/experimental/server/__init__.py b/tests/experimental/openapi_parser/__init__.py similarity index 100% rename from tests/experimental/server/__init__.py rename to tests/experimental/openapi_parser/__init__.py diff --git a/tests/experimental/server/conftest.py b/tests/experimental/openapi_parser/conftest.py similarity index 100% rename from tests/experimental/server/conftest.py rename to tests/experimental/openapi_parser/conftest.py diff --git a/tests/experimental/utilities/__init__.py b/tests/experimental/openapi_parser/server/__init__.py similarity index 100% rename from tests/experimental/utilities/__init__.py rename to tests/experimental/openapi_parser/server/__init__.py diff --git a/tests/experimental/server/openapi/__init__.py b/tests/experimental/openapi_parser/server/openapi/__init__.py similarity index 100% rename from tests/experimental/server/openapi/__init__.py rename to tests/experimental/openapi_parser/server/openapi/__init__.py diff --git a/tests/experimental/server/openapi/test_comprehensive.py b/tests/experimental/openapi_parser/server/openapi/test_comprehensive.py similarity index 100% rename from tests/experimental/server/openapi/test_comprehensive.py rename to tests/experimental/openapi_parser/server/openapi/test_comprehensive.py diff --git a/tests/experimental/server/openapi/test_deepobject_style.py b/tests/experimental/openapi_parser/server/openapi/test_deepobject_style.py similarity index 100% rename from tests/experimental/server/openapi/test_deepobject_style.py rename to tests/experimental/openapi_parser/server/openapi/test_deepobject_style.py diff --git a/tests/experimental/server/openapi/test_end_to_end_compatibility.py b/tests/experimental/openapi_parser/server/openapi/test_end_to_end_compatibility.py similarity index 100% rename from tests/experimental/server/openapi/test_end_to_end_compatibility.py rename to tests/experimental/openapi_parser/server/openapi/test_end_to_end_compatibility.py diff --git a/tests/experimental/server/openapi/test_openapi_features.py b/tests/experimental/openapi_parser/server/openapi/test_openapi_features.py similarity index 100% rename from tests/experimental/server/openapi/test_openapi_features.py rename to tests/experimental/openapi_parser/server/openapi/test_openapi_features.py diff --git a/tests/experimental/server/openapi/test_openapi_performance.py b/tests/experimental/openapi_parser/server/openapi/test_openapi_performance.py similarity index 100% rename from tests/experimental/server/openapi/test_openapi_performance.py rename to tests/experimental/openapi_parser/server/openapi/test_openapi_performance.py diff --git a/tests/experimental/server/openapi/test_parameter_collisions.py b/tests/experimental/openapi_parser/server/openapi/test_parameter_collisions.py similarity index 100% rename from tests/experimental/server/openapi/test_parameter_collisions.py rename to tests/experimental/openapi_parser/server/openapi/test_parameter_collisions.py diff --git a/tests/experimental/server/openapi/test_performance_comparison.py b/tests/experimental/openapi_parser/server/openapi/test_performance_comparison.py similarity index 100% rename from tests/experimental/server/openapi/test_performance_comparison.py rename to tests/experimental/openapi_parser/server/openapi/test_performance_comparison.py diff --git a/tests/experimental/server/openapi/test_server.py b/tests/experimental/openapi_parser/server/openapi/test_server.py similarity index 100% rename from tests/experimental/server/openapi/test_server.py rename to tests/experimental/openapi_parser/server/openapi/test_server.py diff --git a/tests/experimental/openapi_parser/utilities/__init__.py b/tests/experimental/openapi_parser/utilities/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/experimental/utilities/openapi/__init__.py b/tests/experimental/openapi_parser/utilities/openapi/__init__.py similarity index 100% rename from tests/experimental/utilities/openapi/__init__.py rename to tests/experimental/openapi_parser/utilities/openapi/__init__.py diff --git a/tests/experimental/utilities/openapi/conftest.py b/tests/experimental/openapi_parser/utilities/openapi/conftest.py similarity index 97% rename from tests/experimental/utilities/openapi/conftest.py rename to tests/experimental/openapi_parser/utilities/openapi/conftest.py index c5f497824..b7158dd1c 100644 --- a/tests/experimental/utilities/openapi/conftest.py +++ b/tests/experimental/openapi_parser/utilities/openapi/conftest.py @@ -2,14 +2,6 @@ import pytest -from fastmcp.utilities.tests import temporary_settings - - -@pytest.fixture(autouse=True) -def use_new_openapi_parser(): - with temporary_settings(experimental__enable_new_openapi_parser=True): - yield - @pytest.fixture def basic_openapi_30_spec(): diff --git a/tests/experimental/utilities/openapi/test_allof_requestbody.py b/tests/experimental/openapi_parser/utilities/openapi/test_allof_requestbody.py similarity index 100% rename from tests/experimental/utilities/openapi/test_allof_requestbody.py rename to tests/experimental/openapi_parser/utilities/openapi/test_allof_requestbody.py diff --git a/tests/experimental/utilities/openapi/test_director.py b/tests/experimental/openapi_parser/utilities/openapi/test_director.py similarity index 100% rename from tests/experimental/utilities/openapi/test_director.py rename to tests/experimental/openapi_parser/utilities/openapi/test_director.py diff --git a/tests/experimental/utilities/openapi/test_legacy_compatibility.py b/tests/experimental/openapi_parser/utilities/openapi/test_legacy_compatibility.py similarity index 100% rename from tests/experimental/utilities/openapi/test_legacy_compatibility.py rename to tests/experimental/openapi_parser/utilities/openapi/test_legacy_compatibility.py diff --git a/tests/experimental/utilities/openapi/test_models.py b/tests/experimental/openapi_parser/utilities/openapi/test_models.py similarity index 100% rename from tests/experimental/utilities/openapi/test_models.py rename to tests/experimental/openapi_parser/utilities/openapi/test_models.py diff --git a/tests/experimental/utilities/openapi/test_nullable_fields.py b/tests/experimental/openapi_parser/utilities/openapi/test_nullable_fields.py similarity index 100% rename from tests/experimental/utilities/openapi/test_nullable_fields.py rename to tests/experimental/openapi_parser/utilities/openapi/test_nullable_fields.py diff --git a/tests/experimental/utilities/openapi/test_parser.py b/tests/experimental/openapi_parser/utilities/openapi/test_parser.py similarity index 100% rename from tests/experimental/utilities/openapi/test_parser.py rename to tests/experimental/openapi_parser/utilities/openapi/test_parser.py diff --git a/tests/experimental/utilities/openapi/test_schemas.py b/tests/experimental/openapi_parser/utilities/openapi/test_schemas.py similarity index 99% rename from tests/experimental/utilities/openapi/test_schemas.py rename to tests/experimental/openapi_parser/utilities/openapi/test_schemas.py index 2c210e752..ea8c8e2f6 100644 --- a/tests/experimental/utilities/openapi/test_schemas.py +++ b/tests/experimental/openapi_parser/utilities/openapi/test_schemas.py @@ -10,7 +10,7 @@ from fastmcp.experimental.utilities.openapi.models import ( from fastmcp.experimental.utilities.openapi.schemas import ( _combine_schemas, _combine_schemas_and_map_params, - _replace_ref_with_defs_recursive, + _replace_ref_with_defs, ) from fastmcp.utilities.json_schema import compress_schema @@ -244,7 +244,7 @@ class TestSchemaProcessing: } # Use our recursive replacement approach - result = _replace_ref_with_defs_recursive(schema_with_ref) + result = _replace_ref_with_defs(schema_with_ref) assert result["properties"]["user"]["$ref"] == "#/$defs/User" assert result["properties"]["items"]["items"]["$ref"] == "#/$defs/Item" @@ -274,7 +274,7 @@ class TestSchemaProcessing: } # Use our recursive replacement approach - result = _replace_ref_with_defs_recursive(nested_schema) + result = _replace_ref_with_defs(nested_schema) # Check nested object property nested_prop = result["properties"]["data"]["properties"]["nested"] diff --git a/tests/experimental/utilities/openapi/test_transitive_references.py b/tests/experimental/openapi_parser/utilities/openapi/test_transitive_references.py similarity index 100% rename from tests/experimental/utilities/openapi/test_transitive_references.py rename to tests/experimental/openapi_parser/utilities/openapi/test_transitive_references.py diff --git a/uv.lock b/uv.lock index 7f6a355e0..c715793f8 100644 --- a/uv.lock +++ b/uv.lock @@ -530,7 +530,6 @@ dependencies = [ { name = "exceptiongroup" }, { name = "httpx" }, { name = "mcp" }, - { name = "msgspec" }, { name = "openapi-core" }, { name = "openapi-pydantic" }, { name = "pydantic", extra = ["email"] }, @@ -575,7 +574,6 @@ requires-dist = [ { name = "exceptiongroup", specifier = ">=1.2.2" }, { name = "httpx", specifier = ">=0.28.1" }, { name = "mcp", specifier = ">=1.12.4,<2.0.0" }, - { name = "msgspec", specifier = ">=0.19.0" }, { name = "openapi-core", specifier = ">=0.19.5" }, { name = "openapi-pydantic", specifier = ">=0.5.1" }, { name = "pydantic", extras = ["email"], specifier = ">=2.11.7" }, @@ -981,42 +979,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2b/9f/7ba6f94fc1e9ac3d2b853fdff3035fb2fa5afbed898c4a72b8a020610594/more_itertools-10.7.0-py3-none-any.whl", hash = "sha256:d43980384673cb07d2f7d2d918c616b30c659c089ee23953f601d6609c67510e", size = 65278, upload-time = "2025-04-22T14:17:40.49Z" }, ] -[[package]] -name = "msgspec" -version = "0.19.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cf/9b/95d8ce458462b8b71b8a70fa94563b2498b89933689f3a7b8911edfae3d7/msgspec-0.19.0.tar.gz", hash = "sha256:604037e7cd475345848116e89c553aa9a233259733ab51986ac924ab1b976f8e", size = 216934, upload-time = "2024-12-27T17:40:28.597Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/13/40/817282b42f58399762267b30deb8ac011d8db373f8da0c212c85fbe62b8f/msgspec-0.19.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d8dd848ee7ca7c8153462557655570156c2be94e79acec3561cf379581343259", size = 190019, upload-time = "2024-12-27T17:39:13.803Z" }, - { url = "https://files.pythonhosted.org/packages/92/99/bd7ed738c00f223a8119928661167a89124140792af18af513e6519b0d54/msgspec-0.19.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0553bbc77662e5708fe66aa75e7bd3e4b0f209709c48b299afd791d711a93c36", size = 183680, upload-time = "2024-12-27T17:39:17.847Z" }, - { url = "https://files.pythonhosted.org/packages/e5/27/322badde18eb234e36d4a14122b89edd4e2973cdbc3da61ca7edf40a1ccd/msgspec-0.19.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe2c4bf29bf4e89790b3117470dea2c20b59932772483082c468b990d45fb947", size = 209334, upload-time = "2024-12-27T17:39:19.065Z" }, - { url = "https://files.pythonhosted.org/packages/c6/65/080509c5774a1592b2779d902a70b5fe008532759927e011f068145a16cb/msgspec-0.19.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e87ecfa9795ee5214861eab8326b0e75475c2e68a384002aa135ea2a27d909", size = 211551, upload-time = "2024-12-27T17:39:21.767Z" }, - { url = "https://files.pythonhosted.org/packages/6f/2e/1c23c6b4ca6f4285c30a39def1054e2bee281389e4b681b5e3711bd5a8c9/msgspec-0.19.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3c4ec642689da44618f68c90855a10edbc6ac3ff7c1d94395446c65a776e712a", size = 215099, upload-time = "2024-12-27T17:39:24.71Z" }, - { url = "https://files.pythonhosted.org/packages/83/fe/95f9654518879f3359d1e76bc41189113aa9102452170ab7c9a9a4ee52f6/msgspec-0.19.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2719647625320b60e2d8af06b35f5b12d4f4d281db30a15a1df22adb2295f633", size = 218211, upload-time = "2024-12-27T17:39:27.396Z" }, - { url = "https://files.pythonhosted.org/packages/79/f6/71ca7e87a1fb34dfe5efea8156c9ef59dd55613aeda2ca562f122cd22012/msgspec-0.19.0-cp310-cp310-win_amd64.whl", hash = "sha256:695b832d0091edd86eeb535cd39e45f3919f48d997685f7ac31acb15e0a2ed90", size = 186174, upload-time = "2024-12-27T17:39:29.647Z" }, - { url = "https://files.pythonhosted.org/packages/24/d4/2ec2567ac30dab072cce3e91fb17803c52f0a37aab6b0c24375d2b20a581/msgspec-0.19.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aa77046904db764b0462036bc63ef71f02b75b8f72e9c9dd4c447d6da1ed8f8e", size = 187939, upload-time = "2024-12-27T17:39:32.347Z" }, - { url = "https://files.pythonhosted.org/packages/2b/c0/18226e4328897f4f19875cb62bb9259fe47e901eade9d9376ab5f251a929/msgspec-0.19.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:047cfa8675eb3bad68722cfe95c60e7afabf84d1bd8938979dd2b92e9e4a9551", size = 182202, upload-time = "2024-12-27T17:39:33.633Z" }, - { url = "https://files.pythonhosted.org/packages/81/25/3a4b24d468203d8af90d1d351b77ea3cffb96b29492855cf83078f16bfe4/msgspec-0.19.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e78f46ff39a427e10b4a61614a2777ad69559cc8d603a7c05681f5a595ea98f7", size = 209029, upload-time = "2024-12-27T17:39:35.023Z" }, - { url = "https://files.pythonhosted.org/packages/85/2e/db7e189b57901955239f7689b5dcd6ae9458637a9c66747326726c650523/msgspec-0.19.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c7adf191e4bd3be0e9231c3b6dc20cf1199ada2af523885efc2ed218eafd011", size = 210682, upload-time = "2024-12-27T17:39:36.384Z" }, - { url = "https://files.pythonhosted.org/packages/03/97/7c8895c9074a97052d7e4a1cc1230b7b6e2ca2486714eb12c3f08bb9d284/msgspec-0.19.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f04cad4385e20be7c7176bb8ae3dca54a08e9756cfc97bcdb4f18560c3042063", size = 214003, upload-time = "2024-12-27T17:39:39.097Z" }, - { url = "https://files.pythonhosted.org/packages/61/61/e892997bcaa289559b4d5869f066a8021b79f4bf8e955f831b095f47a4cd/msgspec-0.19.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:45c8fb410670b3b7eb884d44a75589377c341ec1392b778311acdbfa55187716", size = 216833, upload-time = "2024-12-27T17:39:41.203Z" }, - { url = "https://files.pythonhosted.org/packages/ce/3d/71b2dffd3a1c743ffe13296ff701ee503feaebc3f04d0e75613b6563c374/msgspec-0.19.0-cp311-cp311-win_amd64.whl", hash = "sha256:70eaef4934b87193a27d802534dc466778ad8d536e296ae2f9334e182ac27b6c", size = 186184, upload-time = "2024-12-27T17:39:43.702Z" }, - { url = "https://files.pythonhosted.org/packages/b2/5f/a70c24f075e3e7af2fae5414c7048b0e11389685b7f717bb55ba282a34a7/msgspec-0.19.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f98bd8962ad549c27d63845b50af3f53ec468b6318400c9f1adfe8b092d7b62f", size = 190485, upload-time = "2024-12-27T17:39:44.974Z" }, - { url = "https://files.pythonhosted.org/packages/89/b0/1b9763938cfae12acf14b682fcf05c92855974d921a5a985ecc197d1c672/msgspec-0.19.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:43bbb237feab761b815ed9df43b266114203f53596f9b6e6f00ebd79d178cdf2", size = 183910, upload-time = "2024-12-27T17:39:46.401Z" }, - { url = "https://files.pythonhosted.org/packages/87/81/0c8c93f0b92c97e326b279795f9c5b956c5a97af28ca0fbb9fd86c83737a/msgspec-0.19.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4cfc033c02c3e0aec52b71710d7f84cb3ca5eb407ab2ad23d75631153fdb1f12", size = 210633, upload-time = "2024-12-27T17:39:49.099Z" }, - { url = "https://files.pythonhosted.org/packages/d0/ef/c5422ce8af73928d194a6606f8ae36e93a52fd5e8df5abd366903a5ca8da/msgspec-0.19.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d911c442571605e17658ca2b416fd8579c5050ac9adc5e00c2cb3126c97f73bc", size = 213594, upload-time = "2024-12-27T17:39:51.204Z" }, - { url = "https://files.pythonhosted.org/packages/19/2b/4137bc2ed45660444842d042be2cf5b18aa06efd2cda107cff18253b9653/msgspec-0.19.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:757b501fa57e24896cf40a831442b19a864f56d253679f34f260dcb002524a6c", size = 214053, upload-time = "2024-12-27T17:39:52.866Z" }, - { url = "https://files.pythonhosted.org/packages/9d/e6/8ad51bdc806aac1dc501e8fe43f759f9ed7284043d722b53323ea421c360/msgspec-0.19.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5f0f65f29b45e2816d8bded36e6b837a4bf5fb60ec4bc3c625fa2c6da4124537", size = 219081, upload-time = "2024-12-27T17:39:55.142Z" }, - { url = "https://files.pythonhosted.org/packages/b1/ef/27dd35a7049c9a4f4211c6cd6a8c9db0a50647546f003a5867827ec45391/msgspec-0.19.0-cp312-cp312-win_amd64.whl", hash = "sha256:067f0de1c33cfa0b6a8206562efdf6be5985b988b53dd244a8e06f993f27c8c0", size = 187467, upload-time = "2024-12-27T17:39:56.531Z" }, - { url = "https://files.pythonhosted.org/packages/3c/cb/2842c312bbe618d8fefc8b9cedce37f773cdc8fa453306546dba2c21fd98/msgspec-0.19.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f12d30dd6266557aaaf0aa0f9580a9a8fbeadfa83699c487713e355ec5f0bd86", size = 190498, upload-time = "2024-12-27T17:40:00.427Z" }, - { url = "https://files.pythonhosted.org/packages/58/95/c40b01b93465e1a5f3b6c7d91b10fb574818163740cc3acbe722d1e0e7e4/msgspec-0.19.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82b2c42c1b9ebc89e822e7e13bbe9d17ede0c23c187469fdd9505afd5a481314", size = 183950, upload-time = "2024-12-27T17:40:04.219Z" }, - { url = "https://files.pythonhosted.org/packages/e8/f0/5b764e066ce9aba4b70d1db8b087ea66098c7c27d59b9dd8a3532774d48f/msgspec-0.19.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19746b50be214a54239aab822964f2ac81e38b0055cca94808359d779338c10e", size = 210647, upload-time = "2024-12-27T17:40:05.606Z" }, - { url = "https://files.pythonhosted.org/packages/9d/87/bc14f49bc95c4cb0dd0a8c56028a67c014ee7e6818ccdce74a4862af259b/msgspec-0.19.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60ef4bdb0ec8e4ad62e5a1f95230c08efb1f64f32e6e8dd2ced685bcc73858b5", size = 213563, upload-time = "2024-12-27T17:40:10.516Z" }, - { url = "https://files.pythonhosted.org/packages/53/2f/2b1c2b056894fbaa975f68f81e3014bb447516a8b010f1bed3fb0e016ed7/msgspec-0.19.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac7f7c377c122b649f7545810c6cd1b47586e3aa3059126ce3516ac7ccc6a6a9", size = 213996, upload-time = "2024-12-27T17:40:12.244Z" }, - { url = "https://files.pythonhosted.org/packages/aa/5a/4cd408d90d1417e8d2ce6a22b98a6853c1b4d7cb7669153e4424d60087f6/msgspec-0.19.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a5bc1472223a643f5ffb5bf46ccdede7f9795078194f14edd69e3aab7020d327", size = 219087, upload-time = "2024-12-27T17:40:14.881Z" }, - { url = "https://files.pythonhosted.org/packages/23/d8/f15b40611c2d5753d1abb0ca0da0c75348daf1252220e5dda2867bd81062/msgspec-0.19.0-cp313-cp313-win_amd64.whl", hash = "sha256:317050bc0f7739cb30d257ff09152ca309bf5a369854bbf1e57dffc310c1f20f", size = 187432, upload-time = "2024-12-27T17:40:16.256Z" }, -] - [[package]] name = "nodeenv" version = "1.9.1"