1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
|
from __future__ import annotations
from dataclasses import dataclass, fields, InitVar # NOQA
from textwrap import dedent
from io import BytesIO
from typing import ClassVar, Union
class TestDataClasses:
def test_1(self) -> None:
from ruamel.yaml import YAML
yaml = YAML()
@yaml.register_class
@dataclass
class DC:
abc: int
klm: int
xyz: int = 0
def __post_init__(self) -> None:
self.xyz = self.abc + self.klm
dc = DC(abc=5, klm=42)
assert dc.xyz == 47
yaml_str = dedent("""\
!DC
abc: 13
klm: 37
""")
dc2 = yaml.load(yaml_str)
assert dc2.xyz == 50
def test_yamltag(self) -> None:
from ruamel.yaml import YAML
yaml = YAML()
@yaml.register_class
@dataclass
class DC:
yaml_tag: ClassVar = '!dc_example'
abc: int
klm: int
dc = DC(abc=5, klm=42)
buf = BytesIO()
yaml.dump(dc, buf)
assert buf.getvalue() == dedent("""\
!dc_example
abc: 5
klm: 42
""").encode('utf-8')
dc2 = yaml.load(buf.getvalue())
assert len(fields(dc2)) == 2 # class var is not a field
assert dc2.abc == dc.abc
assert dc2.klm == dc.klm
def test_initvar(self) -> None:
from ruamel.yaml import YAML
yaml = YAML()
@yaml.register_class
@dataclass
class DC:
abc: int
klm: int
xyz: InitVar[Union[str, None]] = None
def __post_init__(self, xyz: Union[str, None]) -> None:
# assert xyz == self.xyz # self.xyz is always None
if xyz is not None:
self.klm += len(xyz)
dc = DC(abc=5, klm=42, xyz='provided')
# this actually doesn't raise an attribute error, I would have expected it not to work
# at all, but it has the default value
assert dc.xyz is None # type: ignore
buf = BytesIO()
yaml.dump(dc, buf)
assert buf.getvalue() == dedent("""\
!DC
abc: 5
klm: 50
""").encode('utf-8')
yaml_str = dedent("""\
!DC
abc: 18
klm: 55
xyz: some string
""")
dc2 = yaml.load(yaml_str)
assert dc2.xyz is None
assert dc2.klm == 55 + len('some string')
def test_initvar_not_in_yaml(self) -> None:
from ruamel.yaml import YAML
yaml = YAML()
@yaml.register_class
@dataclass
class DC:
abc: int
klm: int
xyz: InitVar[Union[str, None]] = 'hello'
def __post_init__(self, xyz: Union[str, None]) -> None:
# assert xyz == self.xyz # self.xyz is always None
if xyz is not None:
self.klm += len(xyz)
dc = DC(abc=5, klm=42, xyz='provided')
assert dc.abc == 5
assert dc.xyz == 'hello' # type: ignore
buf = BytesIO()
yaml.dump(dc, buf)
assert buf.getvalue() == dedent("""\
!DC
abc: 5
klm: 50
""").encode('utf-8')
yaml_str = dedent("""\
!DC
abc: 18
klm: 55
""")
dc2 = yaml.load(yaml_str)
assert dc2.xyz == 'hello'
assert dc2.klm == 55 + len('hello')
def test_collection_field(self) -> None:
# https://stackoverflow.com/a/77485786/1307905
import ruamel.yaml
from dataclasses import dataclass
@dataclass
class Msg:
id: int
desc: str
fields: list[Field]
def __post_init__(self) -> None:
idx: int = 0
for field in self.fields: # why is this empty??
field.index = idx
idx += field.size
@dataclass
class Field:
id: int
name: str
units: str
size: int
index: int = -1
yaml = ruamel.yaml.YAML()
yaml.register_class(Msg)
yaml.register_class(Field)
msg: Msg = yaml.load("""\
!Msg
id: 1
desc: status
fields:
- !Field
id: 1
name: Temp
units: degC
size: 2
""")
assert msg.fields[0].index != -1
|