ialdev-core 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- iad/core/__init__.py +9 -0
- iad/core/array.py +1961 -0
- iad/core/binary.py +377 -0
- iad/core/cache.py +903 -0
- iad/core/codetools.py +203 -0
- iad/core/datatools.py +671 -0
- iad/core/docs/locators.ipynb +754 -0
- iad/core/dotstyle.py +99 -0
- iad/core/env.py +271 -0
- iad/core/events.py +650 -0
- iad/core/filesproc.py +1046 -0
- iad/core/fnctools.py +390 -0
- iad/core/label.py +240 -0
- iad/core/logs.py +182 -0
- iad/core/nptools.py +449 -0
- iad/core/one_dark.puml +881 -0
- iad/core/param/__init__.py +17 -0
- iad/core/param/confargparse.py +55 -0
- iad/core/param/paramaze.py +339 -0
- iad/core/param/tbox.py +277 -0
- iad/core/paths.py +563 -0
- iad/core/pdtools.py +2570 -0
- iad/core/pydantools/__init__.py +5 -0
- iad/core/pydantools/fixed_pydantic_yaml/__init__.py +32 -0
- iad/core/pydantools/fixed_pydantic_yaml/compat/__init__.py +0 -0
- iad/core/pydantools/fixed_pydantic_yaml/compat/hacks.py +76 -0
- iad/core/pydantools/fixed_pydantic_yaml/compat/old_enums.py +37 -0
- iad/core/pydantools/fixed_pydantic_yaml/compat/representers.py +92 -0
- iad/core/pydantools/fixed_pydantic_yaml/compat/types.py +122 -0
- iad/core/pydantools/fixed_pydantic_yaml/compat/yaml_lib.py +104 -0
- iad/core/pydantools/fixed_pydantic_yaml/ext/__init__.py +1 -0
- iad/core/pydantools/fixed_pydantic_yaml/ext/semver.py +152 -0
- iad/core/pydantools/fixed_pydantic_yaml/ext/versioned_model.py +113 -0
- iad/core/pydantools/fixed_pydantic_yaml/main.py +30 -0
- iad/core/pydantools/fixed_pydantic_yaml/mixin.py +281 -0
- iad/core/pydantools/fixed_pydantic_yaml/model.py +20 -0
- iad/core/pydantools/fixed_pydantic_yaml/py.typed +1 -0
- iad/core/pydantools/fixed_pydantic_yaml/version.py +1 -0
- iad/core/pydantools/models.py +560 -0
- iad/core/regexp.py +348 -0
- iad/core/short.py +308 -0
- iad/core/strings.py +635 -0
- iad/core/unc_panda.py +270 -0
- iad/core/units.py +58 -0
- iad/core/wrap.py +420 -0
- ialdev_core-0.1.0.dist-info/METADATA +73 -0
- ialdev_core-0.1.0.dist-info/RECORD +48 -0
- ialdev_core-0.1.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""YAML-enabled Pydantic models."""
|
|
2
|
+
|
|
3
|
+
from .version import __version__
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
__all__ = [
|
|
7
|
+
"__version__",
|
|
8
|
+
"SemVer",
|
|
9
|
+
"yaml",
|
|
10
|
+
"VersionedYamlModel",
|
|
11
|
+
"YamlEnum", # deprecated class
|
|
12
|
+
"YamlInt",
|
|
13
|
+
"YamlIntEnum",
|
|
14
|
+
"YamlModel",
|
|
15
|
+
"YamlModelMixin",
|
|
16
|
+
"YamlModelMixinConfig",
|
|
17
|
+
"YamlStr",
|
|
18
|
+
"YamlStrEnum",
|
|
19
|
+
]
|
|
20
|
+
from .main import (
|
|
21
|
+
SemVer,
|
|
22
|
+
VersionedYamlModel,
|
|
23
|
+
YamlEnum,
|
|
24
|
+
YamlInt,
|
|
25
|
+
YamlIntEnum,
|
|
26
|
+
YamlModel,
|
|
27
|
+
YamlModelMixin,
|
|
28
|
+
YamlModelMixinConfig,
|
|
29
|
+
YamlStr,
|
|
30
|
+
YamlStrEnum,
|
|
31
|
+
yaml,
|
|
32
|
+
)
|
|
File without changes
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""Module for some hacks that are needed to work around the YAML library interfaces.
|
|
2
|
+
|
|
3
|
+
Mainy, this defines "safe" representers for common types that Pydantic is able to parse.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
__all__ = ["inject_all"]
|
|
7
|
+
|
|
8
|
+
from typing import List, Type
|
|
9
|
+
|
|
10
|
+
from .representers import register_str_like, register_int_like
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def get_str_like_types() -> List[Type]:
|
|
14
|
+
"""Returns many string-like types from stdlib and Pydantic."""
|
|
15
|
+
|
|
16
|
+
# flake8: noqa
|
|
17
|
+
|
|
18
|
+
from uuid import UUID
|
|
19
|
+
from pathlib import (
|
|
20
|
+
Path,
|
|
21
|
+
PosixPath,
|
|
22
|
+
PurePath,
|
|
23
|
+
PurePosixPath,
|
|
24
|
+
PureWindowsPath,
|
|
25
|
+
WindowsPath,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
from pydantic.v1 import types, networks
|
|
29
|
+
|
|
30
|
+
def _chk(x) -> bool:
|
|
31
|
+
try:
|
|
32
|
+
return issubclass(x, (str, Path, PurePath, UUID))
|
|
33
|
+
except Exception:
|
|
34
|
+
return False
|
|
35
|
+
|
|
36
|
+
# Get most candidates
|
|
37
|
+
candidates = (
|
|
38
|
+
list(locals().values())
|
|
39
|
+
+ [getattr(types, v) for v in types.__all__]
|
|
40
|
+
+ [getattr(networks, v) for v in networks.__all__]
|
|
41
|
+
)
|
|
42
|
+
str_like = [v for v in candidates if _chk(v)]
|
|
43
|
+
|
|
44
|
+
# SecretStr, SecretBytes are stringified as "**********" by Pydantic
|
|
45
|
+
str_like += [types.SecretStr, types.SecretBytes]
|
|
46
|
+
return str_like
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def get_int_like_types() -> List[Type]:
|
|
50
|
+
"""Returns many int-like types from stdlib and Pydantic."""
|
|
51
|
+
|
|
52
|
+
from pydantic.v1 import types
|
|
53
|
+
|
|
54
|
+
def _chk(x) -> bool:
|
|
55
|
+
try:
|
|
56
|
+
return issubclass(x, (int))
|
|
57
|
+
except Exception:
|
|
58
|
+
return False
|
|
59
|
+
|
|
60
|
+
candidates = list(locals().values()) + [getattr(types, v) for v in types.__all__]
|
|
61
|
+
int_like = [v for v in candidates if _chk(v)]
|
|
62
|
+
return int_like
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def inject_all():
|
|
66
|
+
"""Injects all necessary "hacks" into the namespace.
|
|
67
|
+
|
|
68
|
+
What this currently does:
|
|
69
|
+
- Registers many str-like and int-like types (from Pydantic and the standard
|
|
70
|
+
library) to be representable in YAML.
|
|
71
|
+
"""
|
|
72
|
+
# Add representers for string-like and int-like values.
|
|
73
|
+
for cls in get_str_like_types():
|
|
74
|
+
register_str_like(cls, method=str)
|
|
75
|
+
for cls in get_int_like_types():
|
|
76
|
+
register_int_like(cls, method=int)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""Old class for YamlEnum that is now deprecated (and marked as such)."""
|
|
2
|
+
|
|
3
|
+
from enum import Enum
|
|
4
|
+
from inspect import isabstract
|
|
5
|
+
|
|
6
|
+
from deprecated import deprecated
|
|
7
|
+
|
|
8
|
+
from .representers import register_str_like
|
|
9
|
+
|
|
10
|
+
__all__ = ["YamlEnum"]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@deprecated(version="0.5.0", reason="Use the `YamlStrEnum` class instead.")
|
|
14
|
+
class YamlEnum(Enum):
|
|
15
|
+
"""This class is DEPRECATED, please use `pydantic_yaml.YamlStrEnum` instead.
|
|
16
|
+
|
|
17
|
+
Enumeration that serializes as the proper underlying object type.
|
|
18
|
+
|
|
19
|
+
You can use this instead of `enum.Enum`, for example:
|
|
20
|
+
class MyEnum(str, YamlEnum):
|
|
21
|
+
val1 = "Value 1"
|
|
22
|
+
val2 = "Value 2"
|
|
23
|
+
|
|
24
|
+
Note
|
|
25
|
+
----
|
|
26
|
+
This is actually a lie, it only supports `str` enums for now. Oops.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def __init_subclass__(cls):
|
|
30
|
+
if not isabstract(cls):
|
|
31
|
+
register_str_like(cls)
|
|
32
|
+
|
|
33
|
+
def __repr__(self) -> str:
|
|
34
|
+
return repr(self.value)
|
|
35
|
+
|
|
36
|
+
def __str__(self) -> str:
|
|
37
|
+
return str(self.value)
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""Representers for dumping common objects to YAML."""
|
|
2
|
+
|
|
3
|
+
from functools import partial
|
|
4
|
+
from typing import Any, Callable, TypeVar
|
|
5
|
+
|
|
6
|
+
from .yaml_lib import yaml, dumper_classes, representer_classes
|
|
7
|
+
|
|
8
|
+
CType = TypeVar("CType")
|
|
9
|
+
|
|
10
|
+
__all__ = ["register_str_like", "register_int_like"]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def dump_as_str(dumper: yaml.Dumper, data: Any, method: Callable[[Any], str] = str):
|
|
14
|
+
"""Represents an object as a string in YAML.
|
|
15
|
+
|
|
16
|
+
Parameters
|
|
17
|
+
----------
|
|
18
|
+
dumper : yaml.Dumper
|
|
19
|
+
The dumper instance, such as `yaml.SafeDumper()`.
|
|
20
|
+
data
|
|
21
|
+
The object to dump. Ideally this is string-like.
|
|
22
|
+
method : callable
|
|
23
|
+
A method that converts `data` to a string. Default is `str`.
|
|
24
|
+
|
|
25
|
+
Returns
|
|
26
|
+
-------
|
|
27
|
+
node
|
|
28
|
+
The YAML internal node representation.
|
|
29
|
+
"""
|
|
30
|
+
return dumper.represent_str(method(data))
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def dump_as_int(dumper: yaml.Dumper, data: Any, method: Callable[[Any], int] = int):
|
|
34
|
+
"""Represents an object as an integer in YAML.
|
|
35
|
+
|
|
36
|
+
Parameters
|
|
37
|
+
----------
|
|
38
|
+
dumper : yaml.Dumper
|
|
39
|
+
The dumper instance, such as `yaml.SafeDumper()`.
|
|
40
|
+
data
|
|
41
|
+
The object to dump. Ideally this is int-like.
|
|
42
|
+
method : callable
|
|
43
|
+
A method that converts `data` to an integer. Default is `int`.
|
|
44
|
+
|
|
45
|
+
Returns
|
|
46
|
+
-------
|
|
47
|
+
node
|
|
48
|
+
The YAML internal node representation.
|
|
49
|
+
"""
|
|
50
|
+
return dumper.represent_int(method(data))
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def register_str_like(cls: CType, method: Callable[[Any], str] = str) -> CType:
|
|
54
|
+
"""Registers `cls` to be dumped to YAML as a string.
|
|
55
|
+
|
|
56
|
+
Parameters
|
|
57
|
+
----------
|
|
58
|
+
cls : Type
|
|
59
|
+
The class to represent.
|
|
60
|
+
method : callable
|
|
61
|
+
A method that converts objects of type `cls` to a string.
|
|
62
|
+
Default is `str`.
|
|
63
|
+
|
|
64
|
+
Returns
|
|
65
|
+
-------
|
|
66
|
+
cls
|
|
67
|
+
This is the same as the input `cls`.
|
|
68
|
+
"""
|
|
69
|
+
for x_cls in dumper_classes + representer_classes:
|
|
70
|
+
x_cls.add_representer(cls, partial(dump_as_str, method=method))
|
|
71
|
+
return cls
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def register_int_like(cls: CType, method: Callable[[Any], int] = int) -> CType:
|
|
75
|
+
"""Registers `cls` to be dumped to YAML as an integer.
|
|
76
|
+
|
|
77
|
+
Parameters
|
|
78
|
+
----------
|
|
79
|
+
cls : Type
|
|
80
|
+
The class to represent.
|
|
81
|
+
method : callable
|
|
82
|
+
A method that converts objects of type `cls` to an integer.
|
|
83
|
+
Default is `int`.
|
|
84
|
+
|
|
85
|
+
Returns
|
|
86
|
+
-------
|
|
87
|
+
cls
|
|
88
|
+
This is the same as the input `cls`.
|
|
89
|
+
"""
|
|
90
|
+
for x_cls in dumper_classes + representer_classes:
|
|
91
|
+
x_cls.add_representer(cls, partial(dump_as_int, method=method))
|
|
92
|
+
return cls
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"""Defines useful types for building your own YAML-compatible types."""
|
|
2
|
+
|
|
3
|
+
from enum import Enum
|
|
4
|
+
from inspect import isabstract
|
|
5
|
+
from typing import Dict, Tuple, Union
|
|
6
|
+
|
|
7
|
+
from .representers import register_int_like, register_str_like
|
|
8
|
+
from .yaml_lib import yaml_safe_dump
|
|
9
|
+
|
|
10
|
+
__all__ = ["YamlInt", "YamlIntEnum", "YamlStr", "YamlStrEnum"]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class YamlStr(str):
|
|
14
|
+
"""A `str` subclass that serializes to YAML as an integer."""
|
|
15
|
+
|
|
16
|
+
def __init_subclass__(cls):
|
|
17
|
+
"""Adds a representer when defining a subclass."""
|
|
18
|
+
res = super().__init_subclass__()
|
|
19
|
+
if not isabstract(cls):
|
|
20
|
+
register_str_like(cls, method=cls._to_yaml_str)
|
|
21
|
+
return res
|
|
22
|
+
|
|
23
|
+
@classmethod
|
|
24
|
+
def _to_yaml_str(cls, v) -> str:
|
|
25
|
+
"""Represent a value of this type as a string."""
|
|
26
|
+
return str(v)
|
|
27
|
+
|
|
28
|
+
def __str__(self) -> str:
|
|
29
|
+
"""This just returns the wrapped string."""
|
|
30
|
+
return super().__str__()
|
|
31
|
+
|
|
32
|
+
def __repr__(self) -> str:
|
|
33
|
+
"""Repr string that keeps the class type. You may redefine this."""
|
|
34
|
+
return type(self).__qualname__ + "(" + super().__repr__() + ")"
|
|
35
|
+
|
|
36
|
+
def __getnewargs__(self) -> Tuple[str]:
|
|
37
|
+
"""Implementation to prevent Enum from sabotaging us.
|
|
38
|
+
|
|
39
|
+
I'm not joking! See `_make_class_unpicklable()` from `enum.py`
|
|
40
|
+
"""
|
|
41
|
+
return super().__getnewargs__()
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class YamlInt(int):
|
|
45
|
+
"""An `int` subclass that serializes to YAML as an integer."""
|
|
46
|
+
|
|
47
|
+
def __init_subclass__(cls):
|
|
48
|
+
"""Adds a representer when defining a subclass."""
|
|
49
|
+
res = super().__init_subclass__()
|
|
50
|
+
if not isabstract(cls):
|
|
51
|
+
register_int_like(cls, method=cls._to_yaml_int)
|
|
52
|
+
return res
|
|
53
|
+
|
|
54
|
+
@classmethod
|
|
55
|
+
def _to_yaml_int(cls, v) -> int:
|
|
56
|
+
"""Represent a value of this type as an integer."""
|
|
57
|
+
return int(v)
|
|
58
|
+
|
|
59
|
+
def __int__(self) -> int:
|
|
60
|
+
return super().__int__()
|
|
61
|
+
|
|
62
|
+
def __str__(self) -> str:
|
|
63
|
+
"""This returns the wrapped int."""
|
|
64
|
+
return super().__str__()
|
|
65
|
+
|
|
66
|
+
def __repr__(self) -> str:
|
|
67
|
+
"""Repr string that keeps the class type. You may redefine this."""
|
|
68
|
+
return type(self).__qualname__ + "(" + super().__repr__() + ")"
|
|
69
|
+
|
|
70
|
+
def __getnewargs__(self) -> Tuple[int]:
|
|
71
|
+
"""Implementation to prevent Enum from sabotaging us.
|
|
72
|
+
|
|
73
|
+
I'm not joking! See `_make_class_unpicklable()` from `enum.py`
|
|
74
|
+
"""
|
|
75
|
+
return super().__getnewargs__()
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class YamlStrEnum(YamlStr, Enum):
|
|
79
|
+
"""String-valued enum that serializes to YAML directly as a string.
|
|
80
|
+
|
|
81
|
+
This also checks that your enum can properly be serialized.
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
def __init_subclass__(cls):
|
|
85
|
+
res = super().__init_subclass__()
|
|
86
|
+
if not isabstract(cls):
|
|
87
|
+
vals: Dict[str, cls] = dict(cls.__members__)
|
|
88
|
+
yaml_safe_dump(vals)
|
|
89
|
+
return res
|
|
90
|
+
|
|
91
|
+
def __str__(self) -> str:
|
|
92
|
+
"""This returns the wrapped string."""
|
|
93
|
+
return str.__str__(self)
|
|
94
|
+
|
|
95
|
+
def __repr__(self) -> str:
|
|
96
|
+
"""Repr string that keeps the class type. You may redefine this."""
|
|
97
|
+
return type(self).__qualname__ + "(" + str.__repr__(self) + ")"
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class YamlIntEnum(YamlInt, Enum):
|
|
101
|
+
"""Integer-valued enum that serializes to YAML directly as an integer.
|
|
102
|
+
|
|
103
|
+
This also checks that your enum can properly be serialized.
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
def __init_subclass__(cls):
|
|
107
|
+
res = super().__init_subclass__()
|
|
108
|
+
if not isabstract(cls):
|
|
109
|
+
vals: Dict[str, cls] = dict(cls.__members__)
|
|
110
|
+
yaml_safe_dump(vals)
|
|
111
|
+
return res
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
if __name__ == "__main__":
|
|
115
|
+
|
|
116
|
+
class A(YamlStrEnum):
|
|
117
|
+
a = "a"
|
|
118
|
+
b = "b"
|
|
119
|
+
|
|
120
|
+
class X(YamlIntEnum):
|
|
121
|
+
x1 = 1
|
|
122
|
+
x2 = 2
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""Imports an installed YAML library."""
|
|
2
|
+
|
|
3
|
+
# flake8: noqa
|
|
4
|
+
|
|
5
|
+
from typing import Any, Optional
|
|
6
|
+
from io import BytesIO, StringIO, IOBase
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
try:
|
|
10
|
+
import ruamel.yaml as yaml # type: ignore
|
|
11
|
+
|
|
12
|
+
# ruamel.yaml doesn't have type annotations
|
|
13
|
+
|
|
14
|
+
if yaml.__version__ < "0.15.0":
|
|
15
|
+
__yaml_lib__ = "ruamel-old"
|
|
16
|
+
else:
|
|
17
|
+
__yaml_lib__ = "ruamel-new"
|
|
18
|
+
except ImportError:
|
|
19
|
+
try:
|
|
20
|
+
import yaml # type: ignore
|
|
21
|
+
|
|
22
|
+
__yaml_lib__ = "pyyaml"
|
|
23
|
+
except ImportError:
|
|
24
|
+
raise ImportError(
|
|
25
|
+
"Could not import ruamel.yaml or pyyaml, "
|
|
26
|
+
"please install at least one of them."
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
dumper_classes = []
|
|
30
|
+
for _fld in dir(yaml):
|
|
31
|
+
try:
|
|
32
|
+
if "Dumper" in _fld:
|
|
33
|
+
_obj = getattr(yaml, _fld)
|
|
34
|
+
dumper_classes.append(_obj)
|
|
35
|
+
except Exception:
|
|
36
|
+
pass
|
|
37
|
+
|
|
38
|
+
representer_classes = []
|
|
39
|
+
for _fld in dir(yaml):
|
|
40
|
+
try:
|
|
41
|
+
if "Representer" in _fld:
|
|
42
|
+
_obj = getattr(yaml, _fld)
|
|
43
|
+
representer_classes.append(_obj)
|
|
44
|
+
except Exception:
|
|
45
|
+
pass
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def yaml_safe_load(stream) -> Any:
|
|
49
|
+
"""Wrapper around YAML library loader."""
|
|
50
|
+
if __yaml_lib__ in ["ruamel-old", "pyyaml"]:
|
|
51
|
+
return yaml.safe_load(stream)
|
|
52
|
+
# Fixing deprecation warning in new ruamel.yaml versions
|
|
53
|
+
assert __yaml_lib__ == "ruamel-new"
|
|
54
|
+
ruamel_obj = yaml.YAML(typ="safe", pure=True)
|
|
55
|
+
if isinstance(stream, str):
|
|
56
|
+
return ruamel_obj.load(StringIO(stream))
|
|
57
|
+
elif isinstance(stream, bytes):
|
|
58
|
+
return ruamel_obj.load(BytesIO(stream))
|
|
59
|
+
# we hope it's a stream, but don't enforce it
|
|
60
|
+
return ruamel_obj.load(stream)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def yaml_safe_dump(
|
|
64
|
+
data: Any, stream=None, *, sort_keys: bool = False, **kwds
|
|
65
|
+
) -> Optional[Any]:
|
|
66
|
+
"""Wrapper around YAML library dumper.
|
|
67
|
+
|
|
68
|
+
Parameters
|
|
69
|
+
----------
|
|
70
|
+
data
|
|
71
|
+
The data you want to dump, typically a mapping (dict).
|
|
72
|
+
stream
|
|
73
|
+
The stream to dump to. By default (if no stream is given, i.e. None),
|
|
74
|
+
this will instead dump to a text stream.
|
|
75
|
+
sort_keys : bool
|
|
76
|
+
Whether to sort the keys of mappings before dumping.
|
|
77
|
+
Default value is False, rather than True, which is the YAML default.
|
|
78
|
+
This is because Pydantic configs are easier to read if dumped in the
|
|
79
|
+
same order as they are defined.
|
|
80
|
+
kwds
|
|
81
|
+
Other keyword arguments to set on the YAML dumper instance.
|
|
82
|
+
"""
|
|
83
|
+
if __yaml_lib__ in ["ruamel-old", "pyyaml"]:
|
|
84
|
+
return yaml.safe_dump(data, stream=stream, sort_keys=sort_keys, **kwds)
|
|
85
|
+
# Fixing deprecation warning in new ruamel.yaml versions
|
|
86
|
+
assert __yaml_lib__ == "ruamel-new"
|
|
87
|
+
ruamel_obj = yaml.YAML(typ="safe", pure=True)
|
|
88
|
+
ruamel_obj.sort_base_mapping_type_on_output = sort_keys # type: ignore
|
|
89
|
+
# Hacking some options that aren't available
|
|
90
|
+
for kw in ["encoding", "default_flow_style", "default_style", "indent"]:
|
|
91
|
+
if kw in kwds:
|
|
92
|
+
setattr(ruamel_obj, kw, kwds[kw])
|
|
93
|
+
|
|
94
|
+
if stream is None:
|
|
95
|
+
text_stream = StringIO()
|
|
96
|
+
ruamel_obj.dump(data, stream=text_stream)
|
|
97
|
+
text_stream.seek(0) # otherwise we always get ''
|
|
98
|
+
return text_stream.read()
|
|
99
|
+
else:
|
|
100
|
+
ruamel_obj.dump(data, stream=stream)
|
|
101
|
+
return None
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
__all__ = ["yaml_safe_dump", "yaml_safe_load", "yaml", "__yaml_lib__"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Additional extension types that may be useful specifically for YAML files."""
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
from functools import wraps
|
|
2
|
+
from typing import Any, Callable, Optional, Union, no_type_check
|
|
3
|
+
|
|
4
|
+
from pydantic.v1 import errors
|
|
5
|
+
from semver import VersionInfo
|
|
6
|
+
|
|
7
|
+
from ..compat.types import YamlStr
|
|
8
|
+
|
|
9
|
+
__all__ = ["SemVer"]
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
Comparator = Callable[["SemVer", Any], bool]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _comparator(operator: Comparator) -> Comparator:
|
|
16
|
+
"""Wrap a Version binary op method in a type-check."""
|
|
17
|
+
|
|
18
|
+
@wraps(operator)
|
|
19
|
+
def wrapper(self: "SemVer", other: Any) -> bool:
|
|
20
|
+
if not isinstance(other, SemVer):
|
|
21
|
+
try:
|
|
22
|
+
other = SemVer(other)
|
|
23
|
+
except Exception:
|
|
24
|
+
return NotImplemented
|
|
25
|
+
return operator(self, other)
|
|
26
|
+
|
|
27
|
+
return wrapper
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class SemVer(YamlStr): # want to inherit from VersionInfo, but metaclass conflict
|
|
31
|
+
"""Semantic Version string for Pydantic.
|
|
32
|
+
|
|
33
|
+
Depends on `semver` versions 2 or 3. Look here for how to fix the version:
|
|
34
|
+
https://python-semver.readthedocs.io/en/3.0.0-dev.2/install.html#release-policy
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
allow_build: bool = True
|
|
38
|
+
allow_prerelease: bool = True
|
|
39
|
+
|
|
40
|
+
__slots__ = ["_info"]
|
|
41
|
+
|
|
42
|
+
@no_type_check
|
|
43
|
+
def __new__(cls, version: Optional[str], **kwargs) -> object:
|
|
44
|
+
return YamlStr.__new__(cls, cls.parse(**kwargs) if version is None else version)
|
|
45
|
+
|
|
46
|
+
def __init__(self, version: str):
|
|
47
|
+
str.__init__(version)
|
|
48
|
+
self._info = VersionInfo.parse(version)
|
|
49
|
+
|
|
50
|
+
@classmethod
|
|
51
|
+
def parse(
|
|
52
|
+
self,
|
|
53
|
+
major: int,
|
|
54
|
+
minor: int = 0,
|
|
55
|
+
patch: int = 0,
|
|
56
|
+
prerelease: Optional[str] = None,
|
|
57
|
+
build: Optional[str] = None,
|
|
58
|
+
) -> str:
|
|
59
|
+
return str(VersionInfo(major, minor, patch, prerelease, build))
|
|
60
|
+
|
|
61
|
+
@classmethod
|
|
62
|
+
def __get_validators__(cls):
|
|
63
|
+
yield cls.validate
|
|
64
|
+
|
|
65
|
+
@classmethod
|
|
66
|
+
def validate(cls, value: Union[str, "SemVer"]) -> "SemVer":
|
|
67
|
+
vi = VersionInfo.parse(value)
|
|
68
|
+
if not cls.allow_build and (vi.build is None):
|
|
69
|
+
raise errors.NotNoneError()
|
|
70
|
+
if not cls.allow_prerelease and (vi.prerelease is None):
|
|
71
|
+
raise errors.NotNoneError()
|
|
72
|
+
return cls(value)
|
|
73
|
+
|
|
74
|
+
@property
|
|
75
|
+
def info(self) -> VersionInfo:
|
|
76
|
+
return self._info
|
|
77
|
+
|
|
78
|
+
@info.setter
|
|
79
|
+
def info(self, value):
|
|
80
|
+
raise AttributeError("attribute 'info' is readonly")
|
|
81
|
+
|
|
82
|
+
@property
|
|
83
|
+
def major(self) -> int:
|
|
84
|
+
"""The major part of a version (read-only)."""
|
|
85
|
+
return self._info.major
|
|
86
|
+
|
|
87
|
+
@major.setter
|
|
88
|
+
def major(self, value):
|
|
89
|
+
raise AttributeError("attribute 'major' is readonly")
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def minor(self) -> int:
|
|
93
|
+
"""The minor part of a version (read-only)."""
|
|
94
|
+
return self._info.minor
|
|
95
|
+
|
|
96
|
+
@minor.setter
|
|
97
|
+
def minor(self, value):
|
|
98
|
+
raise AttributeError("attribute 'minor' is readonly")
|
|
99
|
+
|
|
100
|
+
@property
|
|
101
|
+
def patch(self) -> int:
|
|
102
|
+
"""The patch part of a version (read-only)."""
|
|
103
|
+
return self._info.patch
|
|
104
|
+
|
|
105
|
+
@patch.setter
|
|
106
|
+
def patch(self, value):
|
|
107
|
+
raise AttributeError("attribute 'patch' is readonly")
|
|
108
|
+
|
|
109
|
+
@property
|
|
110
|
+
def prerelease(self) -> Optional[str]:
|
|
111
|
+
"""The prerelease part of a version (read-only)."""
|
|
112
|
+
return self._info.prerelease
|
|
113
|
+
|
|
114
|
+
@prerelease.setter
|
|
115
|
+
def prerelease(self, value):
|
|
116
|
+
raise AttributeError("attribute 'prerelease' is readonly")
|
|
117
|
+
|
|
118
|
+
@property
|
|
119
|
+
def build(self) -> Optional[str]:
|
|
120
|
+
"""The build part of a version (read-only)."""
|
|
121
|
+
return self._info.build
|
|
122
|
+
|
|
123
|
+
@build.setter
|
|
124
|
+
def build(self, value):
|
|
125
|
+
raise AttributeError("attribute 'build' is readonly")
|
|
126
|
+
|
|
127
|
+
def __hash__(self) -> int:
|
|
128
|
+
return super.__hash__(self) # use string hashing
|
|
129
|
+
|
|
130
|
+
@_comparator
|
|
131
|
+
def __eq__(self, other: "SemVer"):
|
|
132
|
+
return self._info == other._info
|
|
133
|
+
|
|
134
|
+
@_comparator
|
|
135
|
+
def __ne__(self, other: "SemVer"):
|
|
136
|
+
return self._info != other._info
|
|
137
|
+
|
|
138
|
+
@_comparator
|
|
139
|
+
def __lt__(self, other: "SemVer"):
|
|
140
|
+
return self._info < other._info
|
|
141
|
+
|
|
142
|
+
@_comparator
|
|
143
|
+
def __le__(self, other: "SemVer"):
|
|
144
|
+
return self._info <= other._info
|
|
145
|
+
|
|
146
|
+
@_comparator
|
|
147
|
+
def __gt__(self, other: "SemVer"):
|
|
148
|
+
return self._info > other._info
|
|
149
|
+
|
|
150
|
+
@_comparator
|
|
151
|
+
def __ge__(self, other: "SemVer"):
|
|
152
|
+
return self._info >= other._info
|