diff --git a/docs/cli-reference/field-customization.md b/docs/cli-reference/field-customization.md index 24da9af4e..1c9dbc872 100644 --- a/docs/cli-reference/field-customization.md +++ b/docs/cli-reference/field-customization.md @@ -3678,8 +3678,7 @@ This is useful when schemas have descriptive titles that should be preserved. class ProcessingTaskTitle(BaseModel): processing_status_union: ProcessingStatusUnionTitle | None = Field( - default_factory=lambda: ProcessingStatusUnionTitle('COMPLETED'), - title='Processing Status Union Title', + 'COMPLETED', title='Processing Status Union Title', validate_default=True ) processing_status: ProcessingStatusTitle | None = 'COMPLETED' name: str | None = None @@ -3706,10 +3705,7 @@ This is useful when schemas have descriptive titles that should be preserved. RootModel[ProcessingStatusDetail | ExtendedProcessingTask | ProcessingStatusTitle] ): root: ProcessingStatusDetail | ExtendedProcessingTask | ProcessingStatusTitle = ( - Field( - default_factory=lambda: ExtendedProcessingTask('COMPLETED'), - title='Processing Status Union Title', - ) + Field('COMPLETED', title='Processing Status Union Title', validate_default=True) ) diff --git a/docs/cli-reference/model-customization.md b/docs/cli-reference/model-customization.md index 834286e2b..c9fb7d048 100644 --- a/docs/cli-reference/model-customization.md +++ b/docs/cli-reference/model-customization.md @@ -6014,9 +6014,7 @@ The `--use-one-literal-as-default` flag configures the code generation behavior. class NestedNullableEnum(BaseModel): nested_version: NestedVersion | None = Field( - default_factory=lambda: NestedVersion('RC1'), - description='nullable enum', - examples=['RC2'], + 'RC1', description='nullable enum', examples=['RC2'], validate_default=True ) diff --git a/docs/cli-reference/template-customization.md b/docs/cli-reference/template-customization.md index 1b0a41e77..5dfa6f1e8 100644 --- a/docs/cli-reference/template-customization.md +++ b/docs/cli-reference/template-customization.md @@ -2892,7 +2892,7 @@ helps maintain consistency with codebases that prefer double-quote formatting. class MapState2(BaseModel): latitude: Latitude longitude: Longitude - zoom: Zoom | None = Field(default_factory=lambda: Zoom(0)) + zoom: Zoom | None = Field(0, validate_default=True) bearing: Bearing | None = None pitch: Pitch drag_rotate: DragRotate | None = Field(None, alias="dragRotate") diff --git a/docs/cli-reference/typing-customization.md b/docs/cli-reference/typing-customization.md index 9fba6bf03..cbb022e87 100644 --- a/docs/cli-reference/typing-customization.md +++ b/docs/cli-reference/typing-customization.md @@ -1350,9 +1350,7 @@ of Enum classes for all enumerations. class NestedNullableEnum(BaseModel): nested_version: NestedVersion | None = Field( - default_factory=lambda: NestedVersion('RC1'), - description='nullable enum', - examples=['RC2'], + 'RC1', description='nullable enum', examples=['RC2'], validate_default=True ) diff --git a/docs/llms-full.txt b/docs/llms-full.txt index 2a82a5c05..a7d06afda 100644 --- a/docs/llms-full.txt +++ b/docs/llms-full.txt @@ -7242,9 +7242,7 @@ The `--use-one-literal-as-default` flag configures the code generation behavior. class NestedNullableEnum(BaseModel): nested_version: NestedVersion | None = Field( - default_factory=lambda: NestedVersion('RC1'), - description='nullable enum', - examples=['RC2'], + 'RC1', description='nullable enum', examples=['RC2'], validate_default=True ) @@ -11141,8 +11139,7 @@ This is useful when schemas have descriptive titles that should be preserved. class ProcessingTaskTitle(BaseModel): processing_status_union: ProcessingStatusUnionTitle | None = Field( - default_factory=lambda: ProcessingStatusUnionTitle('COMPLETED'), - title='Processing Status Union Title', + 'COMPLETED', title='Processing Status Union Title', validate_default=True ) processing_status: ProcessingStatusTitle | None = 'COMPLETED' name: str | None = None @@ -11169,10 +11166,7 @@ This is useful when schemas have descriptive titles that should be preserved. RootModel[ProcessingStatusDetail | ExtendedProcessingTask | ProcessingStatusTitle] ): root: ProcessingStatusDetail | ExtendedProcessingTask | ProcessingStatusTitle = ( - Field( - default_factory=lambda: ExtendedProcessingTask('COMPLETED'), - title='Processing Status Union Title', - ) + Field('COMPLETED', title='Processing Status Union Title', validate_default=True) ) @@ -12537,9 +12531,7 @@ of Enum classes for all enumerations. class NestedNullableEnum(BaseModel): nested_version: NestedVersion | None = Field( - default_factory=lambda: NestedVersion('RC1'), - description='nullable enum', - examples=['RC2'], + 'RC1', description='nullable enum', examples=['RC2'], validate_default=True ) @@ -19083,7 +19075,7 @@ helps maintain consistency with codebases that prefer double-quote formatting. class MapState2(BaseModel): latitude: Latitude longitude: Longitude - zoom: Zoom | None = Field(default_factory=lambda: Zoom(0)) + zoom: Zoom | None = Field(0, validate_default=True) bearing: Bearing | None = None pitch: Pitch drag_rotate: DragRotate | None = Field(None, alias="dragRotate") diff --git a/pyproject.toml b/pyproject.toml index eb8a50cb3..08d435417 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -60,6 +60,9 @@ optional-dependencies.http = [ optional-dependencies.ruff = [ "ruff>=0.9.10", ] +optional-dependencies.ryaml = [ + "ryaml>=0.5.1", +] optional-dependencies.validation = [ "openapi-spec-validator>=0.2.8,<0.8", "prance>=0.18.2", diff --git a/src/datamodel_code_generator/__init__.py b/src/datamodel_code_generator/__init__.py index 8333be020..e2512b1e9 100644 --- a/src/datamodel_code_generator/__init__.py +++ b/src/datamodel_code_generator/__init__.py @@ -97,7 +97,15 @@ def load_yaml(stream: str | TextIO) -> YamlValue: - """Load YAML content from a string or file-like object.""" + """Load YAML content using ryaml (if available) or PyYAML.""" + from datamodel_code_generator.util import get_yaml_backend # noqa: PLC0415 + + if get_yaml_backend() == "ryaml": + import ryaml # noqa: PLC0415 # ty: ignore[unresolved-import] + + text = stream if isinstance(stream, str) else stream.read() + return ryaml.loads(text) + import yaml # noqa: PLC0415 from datamodel_code_generator.util import SafeLoader # noqa: PLC0415 @@ -933,11 +941,11 @@ def get_header_and_first_line(csv_file: IO[str]) -> dict[str, Any]: def infer_input_type(text: str) -> InputFileType: """Automatically detect the input file type from text content.""" - import yaml.parser # noqa: PLC0415 + from datamodel_code_generator.util import get_yaml_parse_errors # noqa: PLC0415 try: data = load_yaml(text) - except yaml.parser.ParserError: + except get_yaml_parse_errors(): return InputFileType.CSV if isinstance(data, dict): if is_openapi(data): diff --git a/src/datamodel_code_generator/util.py b/src/datamodel_code_generator/util.py index 399a1a4ae..bcde0762b 100644 --- a/src/datamodel_code_generator/util.py +++ b/src/datamodel_code_generator/util.py @@ -5,7 +5,7 @@ import re import warnings from functools import lru_cache -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, Literal if TYPE_CHECKING: from collections.abc import Callable @@ -83,6 +83,35 @@ class CustomSafeLoader(_SafeLoader): # type: ignore[valid-type,misc] return CustomSafeLoader +YamlBackend = Literal["ryaml", "pyyaml"] + + +@lru_cache(maxsize=1) +def get_yaml_backend() -> YamlBackend: + """Detect the available YAML backend ('ryaml' or 'pyyaml').""" + try: + import ryaml # noqa: PLC0415, F401 # ty: ignore[unresolved-import] + except ImportError: + return "pyyaml" + else: + return "ryaml" + + +@lru_cache(maxsize=1) +def get_yaml_parse_errors() -> tuple[type[Exception], ...]: + """Return YAML parse error types for both backends.""" + import yaml # noqa: PLC0415 + + errors: list[type[Exception]] = [yaml.YAMLError] + try: + import ryaml # noqa: PLC0415 # ty: ignore[unresolved-import] + + errors.append(ryaml.InvalidYamlError) + except ImportError: + pass + return tuple(errors) + + @lru_cache(maxsize=1) def _get_base_model_class() -> type: """Get BaseModel class with strict=False config lazily.""" diff --git a/tests/test_yaml_backend.py b/tests/test_yaml_backend.py new file mode 100644 index 000000000..0d1c1b9f9 --- /dev/null +++ b/tests/test_yaml_backend.py @@ -0,0 +1,122 @@ +"""Tests for YAML backend detection and ryaml/PyYAML switching.""" + +from __future__ import annotations + +import io +from typing import TYPE_CHECKING +from unittest.mock import MagicMock, patch + +import pytest +import yaml + +from datamodel_code_generator import InputFileType, infer_input_type, load_yaml +from datamodel_code_generator.util import get_yaml_backend, get_yaml_parse_errors + +if TYPE_CHECKING: + from collections.abc import Iterator + + +@pytest.fixture(autouse=True) +def _clear_caches() -> Iterator[None]: + """Clear lru_cache before and after each test.""" + get_yaml_backend.cache_clear() + get_yaml_parse_errors.cache_clear() + yield + get_yaml_backend.cache_clear() + get_yaml_parse_errors.cache_clear() + + +class TestGetYamlBackend: + """Tests for get_yaml_backend().""" + + def test_without_ryaml(self) -> None: + """When ryaml is not importable, returns 'pyyaml'.""" + with patch.dict("sys.modules", {"ryaml": None}): + assert get_yaml_backend() == "pyyaml" + + def test_with_ryaml(self) -> None: + """When ryaml is importable, returns 'ryaml'.""" + mock_ryaml = MagicMock() + with patch.dict("sys.modules", {"ryaml": mock_ryaml}): + assert get_yaml_backend() == "ryaml" + + +class TestGetYamlParseErrors: + """Tests for get_yaml_parse_errors().""" + + def test_pyyaml_only(self) -> None: + """Without ryaml, only yaml.YAMLError is returned.""" + with patch.dict("sys.modules", {"ryaml": None}): + errors = get_yaml_parse_errors() + assert errors == (yaml.YAMLError,) + + def test_includes_ryaml(self) -> None: + """With ryaml, InvalidYamlError is included.""" + mock_ryaml = MagicMock() + mock_ryaml.InvalidYamlError = type("InvalidYamlError", (Exception,), {}) + with patch.dict("sys.modules", {"ryaml": mock_ryaml}): + errors = get_yaml_parse_errors() + assert yaml.YAMLError in errors + assert mock_ryaml.InvalidYamlError in errors + assert len(errors) == 2 + + +class TestLoadYaml: + """Tests for load_yaml() with backend switching.""" + + def test_pyyaml_fallback_string(self) -> None: + """When ryaml is unavailable, PyYAML is used for string input.""" + with patch.dict("sys.modules", {"ryaml": None}): + result = load_yaml("key: value") + assert result == {"key": "value"} + + def test_pyyaml_fallback_textio(self) -> None: + """When ryaml is unavailable, PyYAML is used for TextIO input.""" + with patch.dict("sys.modules", {"ryaml": None}): + result = load_yaml(io.StringIO("key: value")) + assert result == {"key": "value"} + + def test_with_ryaml_string(self) -> None: + """When ryaml is available, ryaml.loads() is used for string input.""" + mock_ryaml = MagicMock() + mock_ryaml.loads.return_value = {"key": "value"} + with patch.dict("sys.modules", {"ryaml": mock_ryaml}): + result = load_yaml("key: value") + mock_ryaml.loads.assert_called_once_with("key: value") + assert result == {"key": "value"} + + def test_with_ryaml_textio(self) -> None: + """When ryaml is available, TextIO.read() is called before ryaml.loads().""" + mock_ryaml = MagicMock() + mock_ryaml.loads.return_value = {"key": "value"} + stream = io.StringIO("key: value") + with patch.dict("sys.modules", {"ryaml": mock_ryaml}): + result = load_yaml(stream) + mock_ryaml.loads.assert_called_once_with("key: value") + assert result == {"key": "value"} + + +class TestInferInputType: + """Tests for infer_input_type() with backend error handling.""" + + def test_csv_with_pyyaml_error(self) -> None: + """YAML parse error from PyYAML returns CSV type.""" + with patch.dict("sys.modules", {"ryaml": None}): + result = infer_input_type("a,b,c\n1,2,3\n::") + assert result == InputFileType.CSV + + def test_csv_with_ryaml_error(self) -> None: + """YAML parse error from ryaml returns CSV type.""" + mock_invalid_yaml_error = type("InvalidYamlError", (Exception,), {}) + mock_ryaml = MagicMock() + mock_ryaml.InvalidYamlError = mock_invalid_yaml_error + mock_ryaml.loads.side_effect = mock_invalid_yaml_error("parse error") + with patch.dict("sys.modules", {"ryaml": mock_ryaml}): + result = infer_input_type(":::invalid yaml:::") + assert result == InputFileType.CSV + + def test_openapi_detection(self) -> None: + """OpenAPI input is detected correctly regardless of backend.""" + with patch.dict("sys.modules", {"ryaml": None}): + result = infer_input_type("openapi: '3.0.0'\ninfo:\n title: Test\n version: '1.0'") + assert result == InputFileType.OpenAPI diff --git a/uv.lock b/uv.lock index 3ec4bbc98..b4ecaa462 100644 --- a/uv.lock +++ b/uv.lock @@ -816,6 +816,9 @@ http = [ ruff = [ { name = "ruff" }, ] +ryaml = [ + { name = "ryaml" }, +] validation = [ { name = "openapi-spec-validator" }, { name = "prance" }, @@ -944,11 +947,12 @@ requires-dist = [ { name = "pyyaml", specifier = ">=6.0.1" }, { name = "ruff", marker = "extra == 'all'", specifier = ">=0.9.10" }, { name = "ruff", marker = "extra == 'ruff'", specifier = ">=0.9.10" }, + { name = "ryaml", marker = "extra == 'ryaml'", specifier = ">=0.5.1" }, { name = "tomli", marker = "python_full_version < '3.12'", specifier = ">=2.2.1,<3" }, { name = "watchfiles", marker = "extra == 'all'", specifier = ">=1.1" }, { name = "watchfiles", marker = "extra == 'watch'", specifier = ">=1.1" }, ] -provides-extras = ["all", "debug", "graphql", "http", "ruff", "validation", "watch"] +provides-extras = ["all", "debug", "graphql", "http", "ruff", "ryaml", "validation", "watch"] [package.metadata.requires-dev] benchmark = [ @@ -2754,6 +2758,50 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/74/31/b0e29d572670dca3674eeee78e418f20bdf97fa8aa9ea71380885e175ca0/ruff-0.14.10-py3-none-win_arm64.whl", hash = "sha256:e51d046cf6dda98a4633b8a8a771451107413b0f07183b2bef03f075599e44e6", size = 13729839, upload-time = "2025-12-18T19:28:48.636Z" }, ] +[[package]] +name = "ryaml" +version = "0.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/91/c7/f7357990e2ee4958258fe8b8a65a1bcd5d7526d76a04c3a2a5c1707ee05c/ryaml-0.5.1.tar.gz", hash = "sha256:b092d15521016f567aace04365d0aa36bea4270f34ebe264ef9e071cea0a8381", size = 10241, upload-time = "2026-02-04T04:48:19.377Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/b1/31cd6b37cb46bf137f5f94e477192dfa260c33e79c13d686f8d97b12e2c8/ryaml-0.5.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b8e3fba79751ecf2658485cd15d41e198a42b02e786cd41f16280e4057dd87e0", size = 368949, upload-time = "2026-02-04T04:47:25.093Z" }, + { url = "https://files.pythonhosted.org/packages/69/6c/a658d94d8d40af4c83ec56a2b728aabb6be7d2a2b41359b05971137a372f/ryaml-0.5.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3d4f58bc71ef24d9d27a4f26550f11bab54c4748c55caf041dc0b07d6b759763", size = 359339, upload-time = "2026-02-04T04:47:26.5Z" }, + { url = "https://files.pythonhosted.org/packages/31/a8/5b8a8d852d7ecdfbad08a38099bfe6b9fd9991516f17dc0c58ee716f4de3/ryaml-0.5.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:e6254065931cd2fb523d92172f9e1d384a2c1b43b9b7bf1ba0600e81ac792e23", size = 395796, upload-time = "2026-02-04T04:47:28.184Z" }, + { url = "https://files.pythonhosted.org/packages/1d/f0/9086e0b36cb189b64bbb2d5bcbd1d2ce42112f0d4cc7a027bab70590292a/ryaml-0.5.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:eaa6a5b7b5d6724304f373552bc3892447c39785677676776b87aa5bd19c491f", size = 407941, upload-time = "2026-02-04T04:47:29.553Z" }, + { url = "https://files.pythonhosted.org/packages/95/52/c96626de2f41ba066daf0ad8eb001ddeadaf17b26510267e2489974882b1/ryaml-0.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:d60763caaac41f10954171dd098f950732fa54c382cfa61324e31c7e5e07d8de", size = 255612, upload-time = "2026-02-04T04:47:30.836Z" }, + { url = "https://files.pythonhosted.org/packages/bd/3e/d48d80f4d321535564c9671800618ad509f840ba607897cb8abef026abf6/ryaml-0.5.1-cp310-cp310-win_arm64.whl", hash = "sha256:d6a3c601942e47bea6aa8b2d35f16fe69d814efedb7eba4be42e22c4ea958691", size = 247317, upload-time = "2026-02-04T04:47:33.374Z" }, + { url = "https://files.pythonhosted.org/packages/f2/a7/62c1131113974d35980fbab08530b37ab3015c59fa880c9ae9824892e5b3/ryaml-0.5.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:e618371614d1abda95586f0d0786d78432562f55dcd4136029a098b6d2c1e2cf", size = 368517, upload-time = "2026-02-04T04:47:34.741Z" }, + { url = "https://files.pythonhosted.org/packages/c5/c7/414c23c9d82157b3b8162a6ac5ba8a871724d6c4c47d1170f333e62e05cd/ryaml-0.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3e730b0016ee8e2aecec7328c0be23a1ab2e1fa41b38863ad617d541ecbf3831", size = 358993, upload-time = "2026-02-04T04:47:35.939Z" }, + { url = "https://files.pythonhosted.org/packages/f5/ea/000c6983f226f042abdef0a0c639264f8369515fedc5499e8b3ce19dd0c4/ryaml-0.5.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:2d06f6e0e1eb26d0e1cff89475b2907e9b19389b0e929ca90433470a653a216f", size = 395690, upload-time = "2026-02-04T04:47:37.341Z" }, + { url = "https://files.pythonhosted.org/packages/f9/0f/a3e76d9463b778d33ddcd12d86afd721c2ff5bb64dcec0a463106f2d009a/ryaml-0.5.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:32cc3eac9019a8246316467125edf421197b78fd972289749969c8e597f71351", size = 407890, upload-time = "2026-02-04T04:47:38.676Z" }, + { url = "https://files.pythonhosted.org/packages/d7/80/a0bba909fd24ee5da2a4b18f97bd2650f513c3f325464cc269b9c4465bb0/ryaml-0.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:5907c54aa010fd6734ad455ea4000089b704c9fabdbc5b51a9f64ea1b5e07337", size = 255652, upload-time = "2026-02-04T04:47:39.838Z" }, + { url = "https://files.pythonhosted.org/packages/42/89/26756a874ca75961b0d53064ee9cbdba0526e3c2393797daa11db1c4192a/ryaml-0.5.1-cp311-cp311-win_arm64.whl", hash = "sha256:12485c5306e152671a9ea6e8cf750f5fdbc576ced07a5a439a49914b333c4f25", size = 247307, upload-time = "2026-02-04T04:47:41.086Z" }, + { url = "https://files.pythonhosted.org/packages/9a/6d/b607c234c8b680f0fe6a8611dacbf40f8502f42e5c33644fe46148018aa7/ryaml-0.5.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0f69155fb7500c4016ac7b831e40ede78baf9ba78c13278711f9130853077135", size = 366837, upload-time = "2026-02-04T04:47:42.258Z" }, + { url = "https://files.pythonhosted.org/packages/55/83/eabd91b10e080788a481899bff8f76e2a78b11eba45e4b250772b9410c8a/ryaml-0.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:10b9c86ead9f0c0052d3d03a2f474bd8c441a26164d4eda34eefccdf96e1f198", size = 356194, upload-time = "2026-02-04T04:47:44.441Z" }, + { url = "https://files.pythonhosted.org/packages/f5/2f/ebdc6a99662120ba45f3b4872770493a13161c2fe216d8ef23901de03c27/ryaml-0.5.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:b978933c7b7d8263b793243361b750d18f4e30d3103090138e5871016a40b5a7", size = 396115, upload-time = "2026-02-04T04:47:45.72Z" }, + { url = "https://files.pythonhosted.org/packages/b4/28/462af4410c05c59536ec8f42d147f5f5b1818253ff3f63eaed6933e7bdef/ryaml-0.5.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:dad0fbc510e372815ac393d821ba584822eb814a379a9e648c48aff55c856cd9", size = 407972, upload-time = "2026-02-04T04:47:47.057Z" }, + { url = "https://files.pythonhosted.org/packages/86/da/18e410a5ef71dc6e8e3a4a03c2051e44e4593800b12d0b87dffc770f98f6/ryaml-0.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:27ca38aa772c534704b5ab18c59a89598dc81e42b25f0622b88d2a0e8c148373", size = 256028, upload-time = "2026-02-04T04:47:48.565Z" }, + { url = "https://files.pythonhosted.org/packages/fe/e3/0717c4dd1a779976853265e1a8b951eab89de7638d984808c29d635fb28e/ryaml-0.5.1-cp312-cp312-win_arm64.whl", hash = "sha256:a371993494dd0b952b646652cf9f1119e77c8832c651c7b288bb6719e671f3a7", size = 247335, upload-time = "2026-02-04T04:47:49.777Z" }, + { url = "https://files.pythonhosted.org/packages/53/02/dd8f1915ddf9ce378a7a242cac5ec47645925d24f6d28f2ad87c4b268bc8/ryaml-0.5.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d51f94506a9e7293ae7bbab67b8ce064969b221f277f0b6f55704a1f2f109186", size = 366874, upload-time = "2026-02-04T04:47:50.9Z" }, + { url = "https://files.pythonhosted.org/packages/ec/84/3f2c1373b1d00e30c37248757dd20fc9d07515e92ec05d3a942fae8b59cc/ryaml-0.5.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9f7f5651fa491f5b29a804bc7295b08317d2fbd87e7bf59d0dacc56e60858fe7", size = 356363, upload-time = "2026-02-04T04:47:52.252Z" }, + { url = "https://files.pythonhosted.org/packages/63/26/9b52e8e50d8b2284a5747d47ace76c024fa5b05de30a30194d8614f8c1af/ryaml-0.5.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:40099f0600c95413f3438b612b98c7882af954d96a425e2165e15fae59f1427f", size = 395879, upload-time = "2026-02-04T04:47:53.564Z" }, + { url = "https://files.pythonhosted.org/packages/96/5a/e055ed876d3c216d214ee1c60bdb1c3199759686066627d4e1f53e585f2f/ryaml-0.5.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:7ffbe4d80ea0bc4219cce8b7f6b0e5798e67364f5fa1388db7bbaa18c84694a4", size = 408175, upload-time = "2026-02-04T04:47:54.83Z" }, + { url = "https://files.pythonhosted.org/packages/1c/97/bad5a8c5db05e7eae269955cf501968f426eda0c88e5fcd2580a685dd547/ryaml-0.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:b2a820cece5b178c5af64da1ed79ecb8de24ebd948280753a2c90b04914eb47c", size = 256189, upload-time = "2026-02-04T04:47:56.265Z" }, + { url = "https://files.pythonhosted.org/packages/cb/c3/775fd132771a57aaa191ee3d61a251116cf8bfea5ebdd998068439e77a73/ryaml-0.5.1-cp313-cp313-win_arm64.whl", hash = "sha256:685163ba87a1fa367c25b678ce4218936e156a86fcf7e951d6dc19e3554f7213", size = 247820, upload-time = "2026-02-04T04:47:57.619Z" }, + { url = "https://files.pythonhosted.org/packages/47/4f/0232f6c5b0c01600cb4abae24d938955d22632de58501b464185998ae44c/ryaml-0.5.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:b80d98af12afbe716d3b18dbc2b77cc0a04f75e079b74b46c56c8060b2dd46de", size = 367774, upload-time = "2026-02-04T04:47:59.608Z" }, + { url = "https://files.pythonhosted.org/packages/da/61/7e0a1b0d30e4d2941e0e33b60d4a27f96d945c7407001f1050347aa1d7fd/ryaml-0.5.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:847ba22d109ce399c732b0d6901a3fb1e1c21347a682e6da0e914ad2d42462b2", size = 356905, upload-time = "2026-02-04T04:48:02.789Z" }, + { url = "https://files.pythonhosted.org/packages/9b/d8/6f156b6410aa4b3b2602ad61a2b18c4f829e648d8f4568c9e2e1e1d9bd44/ryaml-0.5.1-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:84dc69302b8cb2ff4a5280c58f9f8704058349a3ed68e8f1b73db7a8d2a5209a", size = 396359, upload-time = "2026-02-04T04:48:04.204Z" }, + { url = "https://files.pythonhosted.org/packages/31/bc/f24c9e823f7d63f8ccd4f8673cfaaf42ecc9a949e38c1cc74ebbdfa699e8/ryaml-0.5.1-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:d7d7443238a9a81405bbc0ebd512f21c94770631b1b82e0b11f41254dabb06db", size = 408389, upload-time = "2026-02-04T04:48:07.097Z" }, + { url = "https://files.pythonhosted.org/packages/ff/71/eb7b8e59c237477f294b282e8abfddc1fb9b83c26f760e0dd9d60edc788a/ryaml-0.5.1-cp314-cp314-win_amd64.whl", hash = "sha256:e9b4d3e7ade4383aec1146eaf7192e8cc6e4204900b66852255387fa8fe3c688", size = 256762, upload-time = "2026-02-04T04:48:08.362Z" }, + { url = "https://files.pythonhosted.org/packages/cf/a3/62ebc097c00b754cafbef8379a15d922b7738c1d232ea61f1b5e2349bb4a/ryaml-0.5.1-cp314-cp314-win_arm64.whl", hash = "sha256:4a8584df34a935da25fc28941f0a3f96bfe6d7f4346bbae898726bd3468e9537", size = 247709, upload-time = "2026-02-04T04:48:09.623Z" }, + { url = "https://files.pythonhosted.org/packages/7e/0d/3d6416755e1b65fe58f0d5103734ed9e31ed933c49bafa648a8089dacf79/ryaml-0.5.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:41baf5391530a988d854ace25436792280a285d28bac7a5f3a501ea38ded8c72", size = 366116, upload-time = "2026-02-04T04:48:10.908Z" }, + { url = "https://files.pythonhosted.org/packages/77/45/0d567be436e646dbc3dd7bd75feea5c0e27f61009eeacdde28949dae216e/ryaml-0.5.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:845547a5494f8f42282fa242ee55c91b062f1b2078b5d5060e4859465bcba6db", size = 355251, upload-time = "2026-02-04T04:48:12.743Z" }, + { url = "https://files.pythonhosted.org/packages/f7/ae/bd0c7fabd882fe3b426346db7001174da45df67eaa451a9b9636c30893a4/ryaml-0.5.1-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:59b5010d59e8db24f6bc9213f0393985d84178b0c32ce4cfd014b0cbb8382a1e", size = 394953, upload-time = "2026-02-04T04:48:14.118Z" }, + { url = "https://files.pythonhosted.org/packages/f1/1f/207e1bf32c6c11a24b51a7d60457b00039f2af8a3554b73f5ba6ea1ed31b/ryaml-0.5.1-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:8a8821ac4f05e6d23c911d25e4a80001ae40efb411d84636bce8234e6ac5ffe1", size = 407694, upload-time = "2026-02-04T04:48:15.415Z" }, + { url = "https://files.pythonhosted.org/packages/56/86/6fde258c33495d43246d80a2c28045e58618ccccbc5e20147332aefc4211/ryaml-0.5.1-cp314-cp314t-win_amd64.whl", hash = "sha256:da56b618598e253c764a5fc27c51fd0a564384810d307d86140da0c7beeab0d4", size = 256177, upload-time = "2026-02-04T04:48:16.82Z" }, + { url = "https://files.pythonhosted.org/packages/ce/74/b89bc4c9e9115f5fa5c6ac44f2fe758b84f1d4009cada39ee41d0801972b/ryaml-0.5.1-cp314-cp314t-win_arm64.whl", hash = "sha256:55b34db34db8f201f4ff1f8e41fa2c188b617f453610a0ea623e93edcbb44161", size = 246463, upload-time = "2026-02-04T04:48:18.043Z" }, +] + [[package]] name = "secretstorage" version = "3.5.0"