kopia lustrzana https://github.com/corrscope/corrscope
Improve handling of init=False or _fields, fix crashes
- Don't dump init=False fields - Don't crash when loading init=False fields - Pass field to __init__(field), if internally named _field Fixes crash when playing PostTriggerConfig from GUI.pull/357/head
rodzic
a289349d9c
commit
e21aeef072
|
@ -153,11 +153,15 @@ class DumpableAttrs:
|
||||||
cls = type(self)
|
cls = type(self)
|
||||||
|
|
||||||
for field in attr.fields(cls):
|
for field in attr.fields(cls):
|
||||||
# Skip deprecated fields with leading underscores.
|
"""
|
||||||
# They have already been baked into other config fields.
|
Skip fields which cannot be passed into __init__().
|
||||||
|
- init=False
|
||||||
|
- leading underscores: deprecated fields.
|
||||||
|
- They have already been baked into other config fields.
|
||||||
|
- Even if you want to keep them, __init__(field) removes underscore.
|
||||||
|
"""
|
||||||
name = field.name
|
name = field.name
|
||||||
if name[0] == "_":
|
if name[0] == "_" or not field.init:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
value = getattr(self, name)
|
value = getattr(self, name)
|
||||||
|
@ -184,12 +188,16 @@ class DumpableAttrs:
|
||||||
""" Redirect `Alias(key)=value` to `key=value`.
|
""" Redirect `Alias(key)=value` to `key=value`.
|
||||||
Then call the dataclass constructor (to validate parameters). """
|
Then call the dataclass constructor (to validate parameters). """
|
||||||
|
|
||||||
self_name = obj_name(self)
|
cls = type(self)
|
||||||
field_names = attr.fields_dict(type(self)).keys()
|
cls_name = cls.__name__
|
||||||
|
fields = attr.fields_dict(cls)
|
||||||
|
|
||||||
|
# All names which can be passed into __init__()
|
||||||
|
field_names = {name.lstrip("_") for name, field in fields.items() if field.init}
|
||||||
|
|
||||||
new_state = {}
|
new_state = {}
|
||||||
for key, value in dict(state).items():
|
for key, value in dict(state).items():
|
||||||
class_var = getattr(self, key, None)
|
class_var = getattr(cls, key, None)
|
||||||
|
|
||||||
if class_var is Ignored:
|
if class_var is Ignored:
|
||||||
pass
|
pass
|
||||||
|
@ -198,21 +206,21 @@ class DumpableAttrs:
|
||||||
target = class_var.key
|
target = class_var.key
|
||||||
if target in state:
|
if target in state:
|
||||||
raise CorrError(
|
raise CorrError(
|
||||||
f"{self_name} received both Alias {key} and "
|
f"{cls_name} received both Alias {key} and "
|
||||||
f"equivalent {target}"
|
f"equivalent {target}"
|
||||||
)
|
)
|
||||||
new_state[target] = value
|
new_state[target] = value
|
||||||
|
|
||||||
elif key not in field_names:
|
elif key not in field_names:
|
||||||
warnings.warn(
|
warnings.warn(
|
||||||
f'Unrecognized field "{key}" in !{self_name}, ignoring', CorrWarning
|
f'Unrecognized field "{key}" in !{cls_name}, ignoring', CorrWarning
|
||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
new_state[key] = value
|
new_state[key] = value
|
||||||
|
|
||||||
del state
|
del state
|
||||||
obj = type(self)(**new_state)
|
obj = cls(**new_state)
|
||||||
self.__dict__ = obj.__dict__
|
self.__dict__ = obj.__dict__
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -291,6 +291,36 @@ foo: 0
|
||||||
assert yaml.load(s) == Foo(99)
|
assert yaml.load(s) == Foo(99)
|
||||||
|
|
||||||
|
|
||||||
|
# Test handling of _prefix fields, or init=False
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.filterwarnings("ignore:")
|
||||||
|
def test_skip_dump_load():
|
||||||
|
"""Ensure _fields or init=False are not dumped,
|
||||||
|
and don't crash on loading.
|
||||||
|
"""
|
||||||
|
|
||||||
|
class Foo(DumpableAttrs):
|
||||||
|
_underscore: int
|
||||||
|
init_false: int = attr.ib(init=False)
|
||||||
|
|
||||||
|
def __attrs_post_init__(self):
|
||||||
|
self.init_false = 1
|
||||||
|
|
||||||
|
# Ensure fields are not dumped.
|
||||||
|
foo = Foo(underscore=1)
|
||||||
|
assert yaml.dump(foo).strip() == "!Foo {}"
|
||||||
|
|
||||||
|
# Ensure init=False fields don't crash on loading.
|
||||||
|
evil = """\
|
||||||
|
!Foo
|
||||||
|
underscore: 1
|
||||||
|
_underscore: 2
|
||||||
|
init_false: 3
|
||||||
|
"""
|
||||||
|
assert yaml.load(evil)._underscore == 1
|
||||||
|
|
||||||
|
|
||||||
# Test always_dump validation.
|
# Test always_dump validation.
|
||||||
|
|
||||||
|
|
||||||
|
|
Ładowanie…
Reference in New Issue