Adding stubs for `ruamel.yaml`
ruamel.yaml is a fork of PyYAML which features a new API and can perform round-trip conversion preserving the original format and comments. It also claims to have fixed various issues when the development of PyYAML was inactive.
Typeshed testing depends on this package via pre-commit-hooks.
- PyPI: https://pypi.org/project/ruamel.yaml/
- Documentation: https://yaml.readthedocs.io/en/latest/
- PyYAML documentation: https://pyyaml.org/wiki/PyYAMLDocumentation
- YAML spec: https://yaml.org/spec/1.2.2/
| Repository | SourceForge | GitHub mirror |
|---|---|---|
| ruamel.yaml | https://sourceforge.net/p/ruamel-yaml/ | https://github.com/commx/ruamel-yaml |
| ruamel.yaml.clib | https://sourceforge.net/p/ruamel-yaml-clib/ | https://github.com/ruamel/yaml.clib |
| Test data | https://sourceforge.net/p/ruamel-yaml-data/ |
Although the package ships a py.typed file, almost every type hint is Any, which makes coding experience terrible. This falls into the situation mentioned in https://github.com/python/typeshed/issues/11955, which might need some discussion.
The typing information in the package seems to originate from https://sourceforge.net/p/ruamel-yaml/tickets/42/ (from https://github.com/common-workflow-language/schema_salad) in 2016 when things were still in Python 2. Then it was never properly maintained and became a pile of Anys.
This package doesn't follow the best practice of prefixing private properties and methods with underscore(s), which makes writing stubs a lot more complex due to the large number of "public" properties and methods.
It's also worth noting that the documentation isn't well-structured, nor does it cover necessary information like every format option, which makes it even harder to use this library. Adding proper typing information might provide some help.
~~Since there are already stubs for PyYAML, many modules of ruamel.yaml can be added by referring to the corresponding ones in PyYAML.~~ They turned out to be not very helpful.
A special case in this library is the YAML class, which you need to pass a typ argument to select its parsing mode. Depending on the typ, the methods of YAML behave differently. For example, YAML(typ='rt').seq() returns a CommentedSeq with round-trip-specific methods, while YAML(typ='safe') returns a plain list. To make coding easier, I added pseudo-subclasses of YAML (like _RoundTripYAML) and converted the __init__() method into multiple @overload def __new__() methods which returns different variants based on the typ parameter.
There's also an undocumented rtsc (round-trip split comments) type in the library, but the parsers seem to be very experimental and less-maintained, so I intentionally omitted it in the main module. I found the only usage at https://github.com/SoulMelody/LibreSVIP/blob/main/libresvip/utils/yamlutils/init.py#L15 which doesn't seem to be intentional or necessary. Edit: The parser doesn't seem to be working at all on a YAML document with comments.
Just for reference, I found the one and only ruamel.yaml plug-in code example at https://github.com/dstl/Stone-Soup/blob/main/stonesoup/serialise.py which might help understanding how that plug_in parameter works.
MonkeyType provides great help.
Diff from mypy_primer, showing the effect of this PR on open source code:
mitmproxy (https://github.com/mitmproxy/mitmproxy)
+ mitmproxy/optmanager.py:496: error: Argument 1 to "dump" of "YAML" has incompatible type "CommentedMap"; expected "Path | str | bytes | SupportsRead[str | bytes]" [arg-type]
paasta (https://github.com/yelp/paasta)
+ paasta_tools/cli/cmds/validate.py:44: error: Module "ruamel.yaml" has no attribute "SafeConstructor" [attr-defined]
+ paasta_tools/cli/cmds/validate.py:339: error: "Type[BaseConstructor]" has no attribute "flatten_mapping" [attr-defined]
check-jsonschema (https://github.com/python-jsonschema/check-jsonschema)
+ src/check_jsonschema/parsers/yaml.py:8: error: Module has no attribute "YAMLError" [attr-defined]
+ src/check_jsonschema/parsers/yaml.py:18: error: Name "ruamel.yaml.SafeConstructor" is not defined [name-defined]
+ src/check_jsonschema/parsers/yaml.py:27: error: Constructor? has no attribute "yaml_constructors" [attr-defined]
+ src/check_jsonschema/parsers/yaml.py:28: error: Constructor? has no attribute "yaml_constructors" [attr-defined]
+ src/check_jsonschema/parsers/yaml.py:59: error: Name "ruamel.yaml.YAMLError" is not defined [name-defined]
+ src/check_jsonschema/parsers/yaml.py:66: error: Module has no attribute "YAMLError" [attr-defined]
+ src/check_jsonschema/transforms/gitlab.py:22: error: Name "ruamel.yaml.BaseConstructor" is not defined [name-defined]
Diff from mypy_primer, showing the effect of this PR on open source code:
mitmproxy (https://github.com/mitmproxy/mitmproxy)
+ mitmproxy/optmanager.py:482: error: Need type annotation for "s" [var-annotated]
+ mitmproxy/optmanager.py:496: error: Argument 1 to "dump" of "YAML" has incompatible type "CommentedMap[Any, Any]"; expected "Path | SupportsWrite[str | bytes]" [arg-type]
+ mitmproxy/tools/console/keymap.py:239: error: Item "StreamMark" of "Mark | StreamMark | None" has no attribute "get_snippet" [union-attr]
+ mitmproxy/tools/console/keymap.py:239: error: Item "None" of "Mark | StreamMark | None" has no attribute "get_snippet" [union-attr]
+ mitmproxy/tools/console/keymap.py:242: error: Item "None" of "Mark | StreamMark | None" has no attribute "line" [union-attr]
paasta (https://github.com/yelp/paasta)
+ paasta_tools/cli/cmds/validate.py:44: error: Module "ruamel.yaml" has no attribute "SafeConstructor" [attr-defined]
+ paasta_tools/cli/cmds/validate.py:339: error: "Type[BaseConstructor]" has no attribute "flatten_mapping" [attr-defined]
spack (https://github.com/spack/spack)
+ lib/spack/spack/util/spack_yaml.py:201: error: Cannot infer type argument 1 of "add_representer" of "BaseRepresenter" [misc]
+ lib/spack/spack/util/spack_yaml.py:204: error: Cannot infer type argument 1 of "add_representer" of "BaseRepresenter" [misc]
check-jsonschema (https://github.com/python-jsonschema/check-jsonschema)
+ src/check_jsonschema/parsers/yaml.py:8: error: Module has no attribute "YAMLError" [attr-defined]
+ src/check_jsonschema/parsers/yaml.py:18: error: Name "ruamel.yaml.SafeConstructor" is not defined [name-defined]
+ src/check_jsonschema/parsers/yaml.py:27: error: Constructor? has no attribute "yaml_constructors" [attr-defined]
+ src/check_jsonschema/parsers/yaml.py:28: error: Constructor? has no attribute "yaml_constructors" [attr-defined]
+ src/check_jsonschema/parsers/yaml.py:59: error: Name "ruamel.yaml.YAMLError" is not defined [name-defined]
+ src/check_jsonschema/parsers/yaml.py:66: error: Module has no attribute "YAMLError" [attr-defined]
+ src/check_jsonschema/transforms/gitlab.py:22: error: Name "ruamel.yaml.BaseConstructor" is not defined [name-defined]
Diff from mypy_primer, showing the effect of this PR on open source code:
mitmproxy (https://github.com/mitmproxy/mitmproxy)
+ mitmproxy/optmanager.py:482: error: Need type annotation for "s" [var-annotated]
+ mitmproxy/optmanager.py:496: error: Argument 1 to "dump" of "YAML" has incompatible type "CommentedMap[Any, Any]"; expected "Path | SupportsWrite[str | bytes]" [arg-type]
+ mitmproxy/tools/console/keymap.py:239: error: Item "StreamMark" of "Mark | StreamMark | None" has no attribute "get_snippet" [union-attr]
+ mitmproxy/tools/console/keymap.py:239: error: Item "None" of "Mark | StreamMark | None" has no attribute "get_snippet" [union-attr]
+ mitmproxy/tools/console/keymap.py:242: error: Item "None" of "Mark | StreamMark | None" has no attribute "line" [union-attr]
paasta (https://github.com/yelp/paasta)
+ paasta_tools/cli/cmds/validate.py:44: error: Module "ruamel.yaml" has no attribute "SafeConstructor" [attr-defined]
+ paasta_tools/cli/cmds/validate.py:339: error: "Type[BaseConstructor]" has no attribute "flatten_mapping" [attr-defined]
spack (https://github.com/spack/spack)
+ lib/spack/spack/util/spack_yaml.py:201: error: Cannot infer type argument 1 of "add_representer" of "BaseRepresenter" [misc]
+ lib/spack/spack/util/spack_yaml.py:204: error: Cannot infer type argument 1 of "add_representer" of "BaseRepresenter" [misc]
check-jsonschema (https://github.com/python-jsonschema/check-jsonschema)
+ src/check_jsonschema/parsers/yaml.py:8: error: Module has no attribute "YAMLError" [attr-defined]
+ src/check_jsonschema/parsers/yaml.py:18: error: Name "ruamel.yaml.SafeConstructor" is not defined [name-defined]
+ src/check_jsonschema/parsers/yaml.py:27: error: Constructor? has no attribute "yaml_constructors" [attr-defined]
+ src/check_jsonschema/parsers/yaml.py:28: error: Constructor? has no attribute "yaml_constructors" [attr-defined]
+ src/check_jsonschema/parsers/yaml.py:59: error: Name "ruamel.yaml.YAMLError" is not defined [name-defined]
+ src/check_jsonschema/parsers/yaml.py:66: error: Module has no attribute "YAMLError" [attr-defined]
+ src/check_jsonschema/transforms/gitlab.py:22: error: Name "ruamel.yaml.BaseConstructor" is not defined [name-defined]
Diff from mypy_primer, showing the effect of this PR on open source code:
mitmproxy (https://github.com/mitmproxy/mitmproxy)
+ mitmproxy/optmanager.py:482: error: Need type annotation for "s" [var-annotated]
+ mitmproxy/optmanager.py:496: error: Argument 1 to "dump" of "YAML" has incompatible type "CommentedMap[Any, Any]"; expected "Path | SupportsWrite[str | bytes]" [arg-type]
+ mitmproxy/tools/console/keymap.py:239: error: Item "StreamMark" of "Mark | StreamMark | None" has no attribute "get_snippet" [union-attr]
+ mitmproxy/tools/console/keymap.py:239: error: Item "None" of "Mark | StreamMark | None" has no attribute "get_snippet" [union-attr]
+ mitmproxy/tools/console/keymap.py:242: error: Item "None" of "Mark | StreamMark | None" has no attribute "line" [union-attr]
paasta (https://github.com/yelp/paasta)
+ paasta_tools/cli/cmds/validate.py:44: error: Module "ruamel.yaml" has no attribute "SafeConstructor" [attr-defined]
+ paasta_tools/cli/cmds/validate.py:339: error: "Type[BaseConstructor]" has no attribute "flatten_mapping" [attr-defined]
spack (https://github.com/spack/spack)
+ lib/spack/spack/util/spack_yaml.py:201: error: Cannot infer type argument 1 of "add_representer" of "BaseRepresenter" [misc]
+ lib/spack/spack/util/spack_yaml.py:204: error: Cannot infer type argument 1 of "add_representer" of "BaseRepresenter" [misc]
check-jsonschema (https://github.com/python-jsonschema/check-jsonschema)
+ src/check_jsonschema/parsers/yaml.py:8: error: Module has no attribute "YAMLError" [attr-defined]
+ src/check_jsonschema/parsers/yaml.py:18: error: Name "ruamel.yaml.SafeConstructor" is not defined [name-defined]
+ src/check_jsonschema/parsers/yaml.py:27: error: Constructor? has no attribute "yaml_constructors" [attr-defined]
+ src/check_jsonschema/parsers/yaml.py:28: error: Constructor? has no attribute "yaml_constructors" [attr-defined]
+ src/check_jsonschema/parsers/yaml.py:59: error: Name "ruamel.yaml.YAMLError" is not defined [name-defined]
+ src/check_jsonschema/parsers/yaml.py:66: error: Module has no attribute "YAMLError" [attr-defined]
+ src/check_jsonschema/transforms/gitlab.py:22: error: Name "ruamel.yaml.BaseConstructor" is not defined [name-defined]
Diff from mypy_primer, showing the effect of this PR on open source code:
mitmproxy (https://github.com/mitmproxy/mitmproxy)
+ mitmproxy/optmanager.py:482: error: Need type annotation for "s" [var-annotated]
+ mitmproxy/tools/console/keymap.py:239: error: Item "StreamMark" of "Mark | StreamMark | None" has no attribute "get_snippet" [union-attr]
+ mitmproxy/tools/console/keymap.py:239: error: Item "None" of "Mark | StreamMark | None" has no attribute "get_snippet" [union-attr]
+ mitmproxy/tools/console/keymap.py:242: error: Item "None" of "Mark | StreamMark | None" has no attribute "line" [union-attr]
paasta (https://github.com/yelp/paasta)
+ paasta_tools/cli/cmds/validate.py:339: error: "Type[BaseConstructor]" has no attribute "flatten_mapping" [attr-defined]
spack (https://github.com/spack/spack)
+ lib/spack/spack/util/spack_yaml.py:201: error: Cannot infer type argument 1 of "add_representer" of "BaseRepresenter" [misc]
+ lib/spack/spack/util/spack_yaml.py:204: error: Cannot infer type argument 1 of "add_representer" of "BaseRepresenter" [misc]
check-jsonschema (https://github.com/python-jsonschema/check-jsonschema)
+ src/check_jsonschema/parsers/yaml.py:8: error: Module has no attribute "YAMLError" [attr-defined]
+ src/check_jsonschema/parsers/yaml.py:59: error: Name "ruamel.yaml.YAMLError" is not defined [name-defined]
+ src/check_jsonschema/parsers/yaml.py:66: error: Module has no attribute "YAMLError" [attr-defined]
Diff from mypy_primer, showing the effect of this PR on open source code:
mitmproxy (https://github.com/mitmproxy/mitmproxy)
+ mitmproxy/optmanager.py:482: error: Need type annotation for "s" [var-annotated]
+ mitmproxy/tools/console/keymap.py:239: error: Item "StreamMark" of "Mark | StreamMark | None" has no attribute "get_snippet" [union-attr]
+ mitmproxy/tools/console/keymap.py:239: error: Item "None" of "Mark | StreamMark | None" has no attribute "get_snippet" [union-attr]
+ mitmproxy/tools/console/keymap.py:242: error: Item "None" of "Mark | StreamMark | None" has no attribute "line" [union-attr]
paasta (https://github.com/yelp/paasta)
+ paasta_tools/cli/cmds/validate.py:339: error: "Type[BaseConstructor]" has no attribute "flatten_mapping" [attr-defined]
Diff from mypy_primer, showing the effect of this PR on open source code:
mitmproxy (https://github.com/mitmproxy/mitmproxy)
+ mitmproxy/optmanager.py:482: error: Need type annotation for "s" [var-annotated]
+ mitmproxy/tools/console/keymap.py:239: error: Item "StreamMark" of "Mark | StreamMark | None" has no attribute "get_snippet" [union-attr]
+ mitmproxy/tools/console/keymap.py:239: error: Item "None" of "Mark | StreamMark | None" has no attribute "get_snippet" [union-attr]
+ mitmproxy/tools/console/keymap.py:242: error: Item "None" of "Mark | StreamMark | None" has no attribute "line" [union-attr]
paasta (https://github.com/yelp/paasta)
+ paasta_tools/cli/cmds/validate.py:339: error: "Type[BaseConstructor]" has no attribute "flatten_mapping" [attr-defined]
Diff from mypy_primer, showing the effect of this PR on open source code:
mitmproxy (https://github.com/mitmproxy/mitmproxy)
+ mitmproxy/optmanager.py:482: error: Need type annotation for "s" [var-annotated]
+ mitmproxy/tools/console/keymap.py:239: error: Item "StreamMark" of "Mark | StreamMark | None" has no attribute "get_snippet" [union-attr]
+ mitmproxy/tools/console/keymap.py:239: error: Item "None" of "Mark | StreamMark | None" has no attribute "get_snippet" [union-attr]
+ mitmproxy/tools/console/keymap.py:242: error: Item "None" of "Mark | StreamMark | None" has no attribute "line" [union-attr]
paasta (https://github.com/yelp/paasta)
+ paasta_tools/cli/cmds/validate.py:339: error: "Type[BaseConstructor]" has no attribute "flatten_mapping" [attr-defined]
Currently stubtest fails to run:
error: not checking stubs due to mypy build errors: /tmp/tmps3q1g0pn/lib/python3.11/site-packages/ruamel/yaml/main.py:47: error: Cannot assign to a type [misc] /tmp/tmps3q1g0pn/lib/python3.11/site-packages/ruamel/yaml/main.py:47: error: Incompatible types in assignment (expression has type "None", variable has type "type[CParser]") [assignment] /tmp/tmps3q1g0pn/lib/python3.11/site-packages/ruamel/yaml/main.py:47: error: Incompatible types in assignment (expression has type "None", variable has type "type[CEmitter]") [assignment] /tmp/tmps3q1g0pn/lib/python3.11/site-packages/ruamel/yaml/main.py:223: error: Unexpected keyword argument "loader" [call-arg] stubs/ruamel.yaml/_ruamel_yaml.pyi:22: note: Called function defined here /tmp/tmps3q1g0pn/lib/python3.11/site-packages/ruamel/yaml/main.py:223: error: Unexpected keyword argument "loader" [call-arg] stubs/ruamel.yaml/_ruamel_yaml.pyi:22: note: Called function defined here
For the latter one, self.Parser is type[Parser | CParser] and mypy doesn't seem to be willing to narrow the type to type[Parser].
Not sure how to fix those.
Stubsabot dry run also fails to run:
Marking ruamel.yaml as obsolete since '0.15.99'
asyncio.run(main())
File "/opt/hostedtoolcache/Python/3.12.5/x64/lib/python3.12/asyncio/runners.py", line 194, in run
return runner.run(main)
^^^^^^^^^^^^^^^^
File "/opt/hostedtoolcache/Python/3.12.5/x64/lib/python3.12/asyncio/runners.py", line 118, in run
return self._loop.run_until_complete(task)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/hostedtoolcache/Python/3.12.5/x64/lib/python3.12/asyncio/base_events.py", line 687, in run_until_complete
return future.result()
^^^^^^^^^^^^^^^
File "/home/runner/work/typeshed/typeshed/scripts/stubsabot.py", line 835, in main
await suggest_typeshed_obsolete(update, session, action_level=args.action_level)
File "/home/runner/work/typeshed/typeshed/scripts/stubsabot.py", line 735, in suggest_typeshed_obsolete
with open(obsolete.stub_path / "METADATA.toml", "rb") as f:
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
FileNotFoundError: [Errno 2] No such file or directory: 'stubs/ruamel.yaml/METADATA.toml'
Help needed!
Diff from mypy_primer, showing the effect of this PR on open source code:
mitmproxy (https://github.com/mitmproxy/mitmproxy)
+ mitmproxy/optmanager.py:482: error: Need type annotation for "s" [var-annotated]
+ mitmproxy/tools/console/keymap.py:239: error: Item "StreamMark" of "Mark | StreamMark | None" has no attribute "get_snippet" [union-attr]
+ mitmproxy/tools/console/keymap.py:239: error: Item "None" of "Mark | StreamMark | None" has no attribute "get_snippet" [union-attr]
+ mitmproxy/tools/console/keymap.py:242: error: Item "None" of "Mark | StreamMark | None" has no attribute "line" [union-attr]
paasta (https://github.com/yelp/paasta)
+ paasta_tools/cli/cmds/validate.py:339: error: "Type[BaseConstructor]" has no attribute "flatten_mapping" [attr-defined]
mitmproxy/tools/console/keymap.py:239: error: Item "StreamMark" of "Mark | StreamMark | None" has no attribute "get_snippet" [union-attr] mitmproxy/tools/console/keymap.py:239: error: Item "None" of "Mark | StreamMark | None" has no attribute "get_snippet" [union-attr] mitmproxy/tools/console/keymap.py:242: error: Item "None" of "Mark | StreamMark | None" has no attribute "line" [union-attr]
Remarks on Mark | StreamMark | None:
PyYAML is using the Any trick in nodes to relax the explicit None check:
https://github.com/python/typeshed/blob/dbe4d32a2a7e9c92689cdf850c00153c59ac2286/stubs/PyYAML/yaml/nodes.pyi#L5-L13
Sadly, the same trick can't be fully effective here. As you see, ruamel.yaml added a FileMark type for IO streams, which has no get_snippet method, so replacing None with Any won't help with the error on line 239 anyway. The line attribute check can be relaxed though. Let me know what you think.
https://github.com/python/typeshed/blob/1f10574d189b7b3ab56fc59c77a96741e217e852/stubs/ruamel.yaml/ruamel/yaml/error.pyi#L22-L37
Diff from mypy_primer, showing the effect of this PR on open source code:
mitmproxy (https://github.com/mitmproxy/mitmproxy)
+ mitmproxy/optmanager.py:482: error: Need type annotation for "s" [var-annotated]
+ mitmproxy/tools/console/keymap.py:239: error: Item "StreamMark" of "Mark | StreamMark | None" has no attribute "get_snippet" [union-attr]
+ mitmproxy/tools/console/keymap.py:239: error: Item "None" of "Mark | StreamMark | None" has no attribute "get_snippet" [union-attr]
+ mitmproxy/tools/console/keymap.py:242: error: Item "None" of "Mark | StreamMark | None" has no attribute "line" [union-attr]
paasta (https://github.com/yelp/paasta)
+ paasta_tools/cli/cmds/validate.py:339: error: "Type[BaseConstructor]" has no attribute "flatten_mapping" [attr-defined]
Diff from mypy_primer, showing the effect of this PR on open source code:
mitmproxy (https://github.com/mitmproxy/mitmproxy)
+ mitmproxy/optmanager.py:482: error: Need type annotation for "s" [var-annotated]
+ mitmproxy/tools/console/keymap.py:239: error: Item "StreamMark" of "Mark | StreamMark | None" has no attribute "get_snippet" [union-attr]
+ mitmproxy/tools/console/keymap.py:239: error: Item "None" of "Mark | StreamMark | None" has no attribute "get_snippet" [union-attr]
+ mitmproxy/tools/console/keymap.py:242: error: Item "None" of "Mark | StreamMark | None" has no attribute "line" [union-attr]
paasta (https://github.com/yelp/paasta)
+ paasta_tools/cli/cmds/validate.py:339: error: "Type[BaseConstructor]" has no attribute "flatten_mapping" [attr-defined]