Support `from typing import ClassVar` (#367)

* Support `from typing import ClassVar`

Also updated the docstring to reflect why exactly we're doing what we're doing.
The previous comment was incorrect (`typing` is already imported in
applications using annotations).

I added `t.ClassVar` as well which is the only third alternative import I found
in use for typing-related classes.

Fixes #361

* Tests, docstrings, et al.
This commit is contained in:
Łukasz Langa 2018-04-23 06:54:47 -07:00 committed by Hynek Schlawack
parent 57817b2c0e
commit 7cb8c82762
2 changed files with 10 additions and 5 deletions

View File

@ -23,6 +23,8 @@ _obj_setattr = object.__setattr__
_init_converter_pat = "__attr_converter_{}"
_init_factory_pat = "__attr_factory_{}"
_tuple_property_pat = " {attr_name} = property(itemgetter({index}))"
_classvar_prefixes = ("typing.ClassVar", "t.ClassVar", "ClassVar")
_empty_metadata_singleton = metadata_proxy({})
@ -232,10 +234,11 @@ def _is_class_var(annot):
"""
Check whether *annot* is a typing.ClassVar.
The implementation is gross but importing `typing` is slow and there are
discussions to remove it from the stdlib alltogether.
The string comparison hack is used to avoid evaluating all string
annotations which would put attrs-based classes at a performance
disadvantage compared to plain old classes.
"""
return str(annot).startswith("typing.ClassVar")
return str(annot).startswith(_classvar_prefixes)
def _get_annotations(cls):

View File

@ -11,6 +11,7 @@ import pytest
import attr
from attr._make import _classvar_prefixes
from attr.exceptions import UnannotatedAttributeError
@ -204,13 +205,14 @@ class TestAnnotations:
assert A.__init__.__annotations__ == {'return': None}
@pytest.mark.parametrize("slots", [True, False])
def test_annotations_strings(self, slots):
@pytest.mark.parametrize("classvar", _classvar_prefixes)
def test_annotations_strings(self, slots, classvar):
"""
String annotations are passed into __init__ as is.
"""
@attr.s(auto_attribs=True, slots=slots)
class C:
cls_var: 'typing.ClassVar[int]' = 23
cls_var: classvar + '[int]' = 23
a: 'int'
x: 'typing.List[int]' = attr.Factory(list)
y: 'int' = 2