cattrs
cattrs copied to clipboard
Possible to support attr.ib(init=False) ?
- cattrs version: 0.9.0
- Python version: 3.7.0
- Operating System: Windows
Description
Right now cattr can only convert dictionaries back to the respective attr object if the field can be initialized in __init__
(because it calls cl(**conv_obj)
). Is it possible for cattr to work with fields with init
set to False
?
What I Did
Python 3.7.0 (v3.7.0:1bf9cc5093, Jun 27 2018, 04:59:51) [MSC v.1914 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import attr
>>> import cattr
>>> @attr.s
... class A:
... a: int = attr.ib(init=False)
...
>>> a = A()
>>> a.a = 10
>>> s = cattr.unstructure(a)
>>> cattr.structure(s, A)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "D:\.virtualenvs\test\lib\site-packages\cattr\converters.py", line 178, in structure
return self._structure_func.dispatch(cl)(obj, cl)
File "D:\.virtualenvs\test\lib\site-packages\cattr\converters.py", line 298, in structure_attrs_fromdict
return cl(**conv_obj)
TypeError: __init__() got an unexpected keyword argument 'a'
>>>
Also interested in a fix for this.
@Tinche Would by-passing init=False
fields on both structure
and unstructure
make sense to you?
@asford The fix needs to be here: https://github.com/Tinche/cattrs/blob/161dbd7b0ebd36123480ea770efab1929b2e57c6/src/cattr/gen.py#L151 (I guess just skipping attributes which are init=False
).
I might start on this soonish. But what should the spec be? If the key is present in the payload, use a setter instead of the initializer? Or should we just ignore fields with init=False
completely when structuring?
I vote for just skip, I heavily use and abuse frozen=True
so a setter would explode if I managed to get into that situation. Could this somehow move into an option for the new GenConverter stuff if you wanted flexibility?
Yeah I agree, let's skip for now and if there's demand we can revisit later with an option.
Fully agree, this is the ideal behavior on our end for structure.
Would the plan be to skip init=False on both structure and unstructure?
In the unstructure path cattr (I believe) currently includes all fields. This seems subtly wrong is the intention is to then skip the fields on structure.
On Tue, Mar 23, 2021, 07:40 Tin Tvrtković @.***> wrote:
Yeah I agree, let's skip for now and if there's demand we can revisit later with an option.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/Tinche/cattrs/issues/40#issuecomment-804958011, or unsubscribe https://github.com/notifications/unsubscribe-auth/AACFBKE5ACIXPP44QH2FV2LTFCR57ANCNFSM4FQUPLZA .
Just out of curiosity, what do you folks use init=False
fields for? Computed attributes set in post init?
constants, if I type something with auto_attribs=True
set it automatically becomes able to be set during initialization. So I have to use init=False to ensure it is not mutable until frozen=True
takes over and protects it.
I use it very rarely for this reason, but its the only one I have had so far.
Hm, can you give an example? Your use case sounds like a ClassVar
would be appropriate?
oh 1000% a class var does the same thing. I don't use it often so I usually remember to just do and forget I should do something else.
@attrs(auto_attribs=True, frozen=True)
class Test:
a: str
b: str = attrib(init=False, default="QQ")
Just out of curiosity, what do you folks use
init=False
fields for? Computed attributes set in post init?
Yes. I have a transpiler, most of component's attr are computed and are optional
I just found this issue because I'm running into this exact problem.
I use init=False
attributes to create a "type" field to help with serialization. I don't want users to be able to change it, but cattrs seems to complain when it is included.
Had to resort to creating a special hook to remove the "type" field after determining the appropriate class.
@jpsnyder Interesting. Could you remove the field and create an unstructuring hook that adds it, for serialization?
It looks like I don't need to have the type field anymore using your example in #140 Thanks again!
However, I would still vote for supporting init=False
. I also had a use for it when I wanted to allow users to add "tags" to their constructed object, but didn't want to expose the tags in the init. However, this is purely a design decision in an effort to separate the two ideas with syntactic sugar.
@attr.define
class Element:
tags: Set[str] = attr.ib(init=False, factory=set)
def add_tag(self, *tags: Iterable[str]):
for tag in tags:
self.tags.add(tag)
return self
@attr.define
class SubOne(Element):
a: int
obj = SubOne(1).add_tag("apple", "banana")
Any update on this?
@attr.define()
class Dataset:
"""A Dataset is used for marking data dependencies between workflows."""
uri: str
extra: Optional[Dict[str, Any]] = None
@define()
class File(Dataset):
path: str
conn_id: Optional[str] = None
filetype: Optional[constants.FileType] = None
normalize_config: Optional[Dict] = None
uri: str = field(init=False, repr=False)
extra: Optional[Dict] = field(init=False, factory=dict, repr=False)
This fails with :
| File "/usr/local/lib/python3.9/site-packages/airflow/lineage/__init__.py", line 75, in _render_object
| return structure(
| File "/usr/local/lib/python3.9/site-packages/cattrs/converters.py", line 281, in structure
| return self._structure_func.dispatch(cl)(obj, cl)
| File "<cattrs generated structure astro.files.base.File>", line 44, in structure_File
| except Exception as exc: raise __c_cve('While structuring File', [exc], __cl)
| cattrs.errors.ClassValidationError: While structuring File (1 sub-exception)
+-+---------------- 1 ----------------
| Traceback (most recent call last):
| File "<cattrs generated structure astro.files.base.File>", line 41, in structure_File
| return __cl(
| TypeError: __init__() got an unexpected keyword argument 'uri'
+------------------------------------
Hi, I would be also interested in this feature. To answer your question, @Tinche:
Just out of curiosity, what do you folks use
init=False
fields for? Computed attributes set in post init?
Yes, this is my primary use case. I think there are many situations where post init is required for a clean code structure and it would be awesome if cattrs would handle these situations automatically (because I think the only other thing someone would do in this situation is to take the boilerplate route and add the trivial structure hooks manually).
Or is there any better way to achieve the same with the current capabilities?
Hello ! Any update on this?
It'll be in the next milestone, alongside new attrs aliases!
Aaaand merged!