So I'm not sure this makes 100% sense, but technically we could start adding type hints to the signature of our __init__.
I guess there would be some runtime introspection benefits?
Ooh, yes please - Hypothesis does runtime introspection of type hints to work out how to call things when the user didn't supply all the required arguments for an example object to test. We'd like deeper integration with attrs on our end at some point, but adding type hints would be a great start - and also work for other projects doing similar things 馃槃
+1 for this. I'm trying to use attrs with injector, and currently it does not work because __init__ generated by attrs "looses" annotations:
>>> class Inner: pass
...
>>> @attr.s
... class Outer:
... inner: Inner = attr.ib()
...
>>> Outer.__init__.__annotations__
{}
I came here specifically to see about adding support for annotation of __init__. My use case is exactly the same as @haizaar except that I plan to use auto_attribs.
I'd do it myself, but the code isn't particularly beginner-friendly.
One approach would be to simply add a block inside _ClassBuilder.add_init where we get the annotations off the decorated class and put them on the generated init method. Possibly by doing something like:
init = self._cls_dict['__init__']
annotations = get_type_hints(self._cls)
init_arg_list = init.__code__.co_varnames
for annotation_key, annotation_value in annotations
if annotation_key in init_arg_list:
add_annotation(init, annotation_key, annotation_value)
remove_annotation(self._cls, annotation_key)
although i suspect this is extremely unportable (on the other hand, it appears that it could work in pypy3).
Also, there is a great deal of stuff I don't really understand (and which is not related to my use case) involving super-attrs and whatnot. I don't understand what those are, so I can't tell if they would affect a solution like the one I sketched.
I wouldn't use get_type_hints in this case - from 3.7 Python is moving towards lazy evaluation of type annotations, and forcing them from string to object form in attrs could break people's code (shouldn't, but could) and would almost certainly have a substantial performance hit for some users.
Instead, I'd just copy keys out of the __annotations__ dict, or inspect.getfullargspec(...).annotations
@Zac-HD I don't know enough about the current discussion around python types direction to comment specifically on the stability of the approach, but the fact that there's ambiguity suggests that we (possibly "you"; i'm not a contributor) should add a little layer of abstraction here (possibly just get_type_annotations_for_the_attrs_pkg()).
I'm currently collecting the above into a little package to allow a user to write
@inject_attr.s
class Thing:
field: KlassA
other_field: KlassB
Although since it's basically just for me, not all python users everywhere, I'm not going to be worrying too much about the fwd-compatibility of the annotations stuff.
By the way, you wouldn't happen to know if there's a way to delete specific type annotations from an object? It's not a big deal, but in the attrs-context, field and other_field are args to the ctor, and I can copy their annotations to Thing.__init__, but the annotations still remain as annotations for (i _think_) non-existent class variables on Thing.
@haizaar in the meantime, you could try my shitty new inject-attrs package (or something like it).
@hynek you might take into consideration the fact that I've been motivated to make this.
I was going to say that almost all my FOSS time goes to Hypothesis (currently working on a major release), but after that it would start to address HypothesisWorks/hypothesis-python#954 ... So maybe, we'll see :smile:
I think adding the annotations to __init__ is great idea. And what @Zac-HD recommends is definitely the way to do it.
However, I don't think think removing the annotation is necessary because
class Thing:
field: KlassA
other_field: KlassB
means that field is an instance variable of type KlassA. If you wanted a class variable you'd use ClassVar[KlassA]
I added a PR and I can work on finishing it if it fixes your problems.
Most helpful comment
I think adding the annotations to
__init__is great idea. And what @Zac-HD recommends is definitely the way to do it.However, I don't think think removing the annotation is necessary because
means that
fieldis an instance variable of typeKlassA. If you wanted a class variable you'd useClassVar[KlassA]