kopia lustrzana https://github.com/corrscope/corrscope
Don't create a new object when calling __setstate__()
This makes evolve_compat() jankier, but __setstate__() slightly less janky and avoids constructing a temporary object.pull/509/head
rodzic
3f8d950f69
commit
0129b16955
|
|
@ -352,28 +352,17 @@ class DumpableAttrs:
|
||||||
return state
|
return state
|
||||||
|
|
||||||
# SafeConstructor.construct_yaml_object() uses __setstate__ to load objects.
|
# SafeConstructor.construct_yaml_object() uses __setstate__ to load objects.
|
||||||
def __setstate__(self, state: Dict[str, Any]) -> None:
|
def __setstate__(self, state: dict[str, Any]) -> None:
|
||||||
"""Apparently pickle.load() calls this method too?
|
"""Apparently pickle.load() calls this method too?
|
||||||
I have not evaluated whether this causes problems."""
|
I have not evaluated whether this causes problems."""
|
||||||
|
|
||||||
other = self.new_from_state(state)
|
# https://stackoverflow.com/q/23461479 "Technically it is OK".
|
||||||
|
self.__init__(**self.prepare_state(state))
|
||||||
# You're... not supposed to? create two objects sharing the same __dict__.
|
|
||||||
# self.__dict__ is empty (because __setstate__() is called instead of __init__()),
|
|
||||||
# which violates this object's contract. So steal __dict__ from the other object.
|
|
||||||
#
|
|
||||||
# If we leave it in `other` as well, we will find self.__dict__ blanked
|
|
||||||
# when other.__del__() is called at a random point in the future (#504).
|
|
||||||
# So replace `other.__dict__`. (We could swap with self.__dict__, but the syntax
|
|
||||||
# is jankier in Python than Rust since Python lacks &mut.)
|
|
||||||
self.__dict__ = other.__dict__
|
|
||||||
other.__dict__ = {}
|
|
||||||
|
|
||||||
# If called via instance, cls == type(self).
|
# If called via instance, cls == type(self).
|
||||||
@classmethod
|
@classmethod
|
||||||
def new_from_state(cls: Type[T], state: Dict[str, Any]) -> T:
|
def prepare_state(cls: Type[T], state: dict[str, Any]) -> dict[str, Any]:
|
||||||
"""Redirect `Alias(key)=value` to `key=value`.
|
"""Redirect `Alias(key)=value` to `key=value`."""
|
||||||
Then call the dataclass constructor (to validate parameters)."""
|
|
||||||
|
|
||||||
cls_name = cls.__name__
|
cls_name = cls.__name__
|
||||||
fields = attr.fields_dict(cls)
|
fields = attr.fields_dict(cls)
|
||||||
|
|
@ -406,14 +395,15 @@ class DumpableAttrs:
|
||||||
new_state[key] = value
|
new_state[key] = value
|
||||||
|
|
||||||
del state
|
del state
|
||||||
return cls(**new_state)
|
return new_state
|
||||||
|
|
||||||
|
|
||||||
def evolve_compat(obj: DumpableAttrs, **changes):
|
def evolve_compat(obj: DumpableAttrs, **changes):
|
||||||
"""Evolve an object, based on user-specified dict,
|
"""Evolve an object, based on user-specified dict,
|
||||||
while ignoring unrecognized keywords."""
|
while ignoring unrecognized keywords."""
|
||||||
# In dictionaries, later values will always override earlier ones
|
# In dictionaries, later values will always override earlier ones
|
||||||
return obj.new_from_state({**obj.__dict__, **changes})
|
new_state = obj.prepare_state({**obj.__dict__, **changes})
|
||||||
|
return type(obj)(**new_state)
|
||||||
|
|
||||||
|
|
||||||
class KeywordAttrs(DumpableAttrs):
|
class KeywordAttrs(DumpableAttrs):
|
||||||
|
|
|
||||||
Ładowanie…
Reference in New Issue