New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Python 3.6 attribute annotations. #151

Closed
Tinche opened this Issue Feb 20, 2017 · 17 comments

Comments

Projects
None yet
7 participants
@Tinche
Member

Tinche commented Feb 20, 2017

Python 3.6 allows this:

@attr.s
class A:
    a: int = attr.ib()

The annotation is available as A.__annotations__['a']. So, I suggest that on 3.6, we add an attribute to attr.Attribute called annotation and expose these values there if they are present.

This might be an easy one for someone looking to get started contributing.

@hynek

This comment has been minimized.

Member

hynek commented Feb 21, 2017

Feel free to use the according label generously. :)

@DRMacIver

This comment has been minimized.

DRMacIver commented Mar 4, 2017

I'm having a look into this. Some brief thoughts and questions:

  • I think it's important to support annotation on < 3.6 if you're going to support it on 3.6. How do you feel about an a = attr.ib(annotation=int) as an alternate API that does the same thing?
  • As well as exposing it on the Attribute object this feels like it should really be made available as standard type annotations on various methods. I can think of three places where the information needs to be propagated: init, property setter, and property getter. Can you think of anywhere else?
  • Propagating the type to methods is arguably a breaking change because it could cause code that currently type checks to fail type checking. Do you consider it to be one?
  • If yes, hiding it behind an off by default propagate_types flag would be sufficient. This seems like a good thing to have anyway, though my preference would be on by default.
  • How do you want to test Python 3.6 specific code? Currently your tests are all in a single undifferentiated directory.
@DRMacIver

This comment has been minimized.

DRMacIver commented Mar 4, 2017

Actually, here's a more fundamental problem with this proposal which I think means it can't/shouldn't work as written: It's flat out lying to the type checker.

In the provided example you've declared that A.a should be an int, but it's actually an Attribute object. Thus correct code using it won't type check, and incorrect code using it will.

@hynek

This comment has been minimized.

Member

hynek commented Mar 4, 2017

Seems like we need a different approach.

@hynek hynek closed this Mar 4, 2017

@Tinche

This comment has been minimized.

Member

Tinche commented Mar 4, 2017

Ok, I'm a little upset this was closed without at least waiting on my input.

Note that type annotations have other uses than mypy. attrs does declarative classes, which is a foreign concept to core Python, so of course there will be some friction, but for example

a = attr.ib()  # type: int

works just fine with the PyCharm type checker. And how is it lying to the type checker? It's setting the type checker straight.

@Tinche

This comment has been minimized.

Member

Tinche commented Mar 4, 2017

In the provided example you've declared that A.a should be an int, but it's actually an Attribute object. Thus correct code using it won't type check, and incorrect code using it will.

Actually, I'd have to had used ClassVar to have done that. In reality, A.a won't be an Attribute object but won't exist if we're dealing with dict classes or be a special descriptor if dealing with slot classes. A(1).a will be an int, though, and this is what's claimed by the annotation.

Also note this example from the official Python docs:

class Employee(NamedTuple):
    name: str
    id: int

Lying to the type checker too?

@hynek

This comment has been minimized.

Member

hynek commented Mar 4, 2017

Uh I'm sorry it looked uncontroversial to me. I'll have a deeper look in the next days. Please everyone make your points. :)

@hynek hynek reopened this Mar 4, 2017

@hynek

This comment has been minimized.

Member

hynek commented Mar 4, 2017

(I should stop making rushed decisions on my phone. 😞)

@DRMacIver

This comment has been minimized.

DRMacIver commented Mar 5, 2017

Note that type annotations have other uses than mypy.

Sure, but a number of those other uses are also type checking. e.g. pycharm. I think using them for things other than type checking is great, but using them in ways that are incompatible with type checking is problematic.

Lying to the type checker too?

No, because there are no values assigned that are not of the claimed type (though I admit it's weird).

@ambv

This comment has been minimized.

Contributor

ambv commented Mar 7, 2017

I think Tinche's syntax suggestion, e.g. using variable annotations / # type: comments is more practical than #162. It already does useful things with existing type checkers today whereas no tool understand annotation= at all.

We'll need some plumbing to make it work. Would be good for 3.6+ if attrs preserved __annotations__ for generated methods if it can read them from variable annotations. Would be great to add type checker support for the @attr.s class decorator. I'm eager to implement this.

But first let's address @DRMacIver's concerns around lying to the type checker. Let's look at what mypy will actually do.

Attribute use on instances

mypy will do the intuitive thing here. Consider the following snippet:

@attr.s()
class Yeah:
    name: str = attr.ib()
    age: int = attr.ib(convert=int, default=0)


def main() -> None:
    y = Yeah('Sue', '16')
    print(y.name.upper())
    print('Next year:', y.age + 1)

First of all look at the snippet again. It looks obvious to human readers, at least the ones familiar with attrs. They understand the attr.ib() plumbing is for instances. They won't have trouble understanding the type annotation behaves the same. I also don't think anybody will be surprised that the class attribute is an Attribute instance.

But let's get back to mypy. It type-checks the attribute usage in main() cleanly. The only thing it's complaining about is the magic __init__ accepting arguments:

example.py:11: error: Too many arguments for "Yeah"

But try flipping the attributes instead in main():

    print(y.age.upper())
    print('Next year:', y.name + 1)

and you'll see it complain about invalid operations, as you'd expect:

example.py:11: error: Too many arguments for "Yeah"
example.py:12: error: "int" has no attribute "upper"
example.py:13: error: Unsupported operand types for + ("str" and "int")

So far, so good.

Attribute use on classes

Here mypy gets it wrong. If we want to access Attribute objects on the class, mypy will raise false positives. print(Yeah.name.validator) will produce:

example.py:15: error: "str" has no attribute "validator"

If we try to mistakenly access the class attribute as an instance attribute (for example by treating Yeah.name as a string, like print(Yeah.name.upper())), then mypy doesn't generate a warning. The program fails at runtime.

Summing up

Even though mypy will get the class-level access wrong, I still prefer using type annotations because:

  • it is already immediately useful in the common case; and
  • it reads better to human readers.

The fundamental issue is that @attr.s is a class decorator that defines or redefines quite a bunch of methods that aren't there on the original class. The type checker can't see those anyway. So, all in all, we need to teach the type checker about the class decorator so that:

  • it understands our __init__ (and other?) generated methods better;
  • we silence false positives around Attribute object usage;
  • it will raise errors when users try to access class attributes like instance attributes.

The first one is the most important because it generates false positives in the common case.
The second one is less important because it generates false positives in the uncommon case. It also can be worked around with casting.
The third one is the least important because it's simply silent. Users of gradual typing expect that the absence of generated errors is no proof of correctness.

@Tinche

This comment has been minimized.

Member

Tinche commented Mar 22, 2017

I basically agree with @ambv but I'm going to share my two Croatian lipa anyway.

In order to get full mypy support we're almost certainly going to have to do extra work, like writing a mypy plugin when that's available. There's probably no chance mypy is going to be competent enough to grok our __init__ construction code by itself.

I see the main bone of contention here is that the core premise of attrs is you write one thing in order to declare another. So this would be the proper way of writing a class:

@attr.s
class A:
    a: ClassVar[_CountingAttr] = attr.ib(annotation=int)

This describes exactly what's happening while you're inside the class, defining the attributes. The only benefit this has is it might help you if you're using another function, like a wrapper for attr.ib(), and you're using it incorrectly - maybe you mistyped a function name, and it doesn't return a _CountingAttr instance but an int, and then mypy will complain. Or if you by mistake use something else other than attr.ib().

@attr.s
class A:
    a: ClassVar[_CountingAttr] = cattr.typed(annotation=int)   # cattr.typed actually returns something else! And mypy might catch this!

The downsides are it's kind of misleading you, the reader. When attr.s finishes processing the class, A.a won't actually be a _CountingAttr. In the case of slot classes, it will be a slot descriptor (a Python internal thing), and in the case of dict classes it won't exist (see http://attrs.readthedocs.io/en/stable/changelog.html#deprecations, the fact it exists now is deprecated and cleanup logic will be added).

The proposed approach,

@attr.s
class A:
    a: int = attr.ib()

is, yes, basically lying to the type checker. But I would argue it's intuitively much clearer to human readers.

Also consider that the point of attrs is reducing boilerplate and making writing classes fun, and compare the two approaches:

@attr.s
class A:
    a: int = attr.ib()
    b: ClassVar[_CountingAttr] = attr.ib(annotation=int)

One is, imo, more beautiful/readable than the other :)

@hynek

This comment has been minimized.

Member

hynek commented Mar 23, 2017

yep, b is the clear winner here. ;P

Trolling aside, it’s my understanding, that we can’t really move on, until python/mypy#2088 is fixed ~somehow~, correct?

@Tinche

This comment has been minimized.

Member

Tinche commented Mar 24, 2017

Ok, let's put this on hold until the mypy situation is clearer.

@hynek hynek added the On Hold label Mar 24, 2017

@agronholm

This comment has been minimized.

agronholm commented Apr 19, 2017

Couldn't we simply add a variable when its annotation is found? Like simply a: int ? Do we really need the extra boilerplate of = attr.ib()?

@Screwtapello

This comment has been minimized.

Screwtapello commented Jun 12, 2017

It may be instructive to review pydantic, a vaguely similar library that does support Python 3 type-annotations for data validation.

@chadrik

This comment has been minimized.

Contributor

chadrik commented Aug 28, 2017

I added a PR for pyi stubs: #238. I did not see this issue first, but I independently came to the same conclusion as @Tinche that we had to lie about the actual type returned by attr.ib. See my notes there, if you're interested.

Also relevant is this PR for basic type support: #239

@hynek

This comment has been minimized.

Member

hynek commented Sep 24, 2017

This has been fixed by #239 although there are some questions open as to what to do with the information. That discussions should happen on #215.

@hynek hynek closed this Sep 24, 2017

bors-fusion bot added a commit to fusionapp/fusion-index that referenced this issue Nov 15, 2017

Merge #167
167: Scheduled weekly dependency update for week 46 r=mithrandi




## Updates
Here's a list of all the updates bundled in this pull request. I've added some links to make it easier for you to find all the information you need.
<table align="center">

<tr>
<td><b>attrs</b></td>
<td align="center">17.2.0</td>
<td align="center">&raquo;</td>
<td align="center">17.3.0</td>
<td>
     <a href="https://pypi.python.org/pypi/attrs">PyPI</a> | <a href="https://pyup.io/changelogs/attrs/">Changelog</a> | <a href="http://www.attrs.org/">Homepage</a> 

</td>

<tr>
<td><b>cryptography</b></td>
<td align="center">2.1.2</td>
<td align="center">&raquo;</td>
<td align="center">2.1.3</td>
<td>
     <a href="https://pypi.python.org/pypi/cryptography">PyPI</a> | <a href="https://pyup.io/changelogs/cryptography/">Changelog</a> | <a href="https://github.com/pyca/cryptography">Repo</a> 

</td>

<tr>
<td><b>hypothesis</b></td>
<td align="center">3.33.0</td>
<td align="center">&raquo;</td>
<td align="center">3.37.0</td>
<td>
     <a href="https://pypi.python.org/pypi/hypothesis">PyPI</a> | <a href="https://pyup.io/changelogs/hypothesis/">Changelog</a> | <a href="https://github.com/HypothesisWorks/hypothesis/issues">Repo</a> 

</td>

</tr>
</table>



## Changelogs


### attrs 17.2.0 -> 17.3.0

>### 17.3.0

>-------------------

>Backward-incompatible Changes
>^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

>- Attributes are not defined on the class body anymore.

>  This means that if you define a class ``C`` with an attribute ``x``, the class will *not* have an attribute ``x`` for introspection anymore.
>  Instead of ``C.x``, use ``attr.fields(C).x`` or look at ``C.__attrs_attrs__``.
>  The old behavior has been deprecated since version 16.1.
>  (`253 &lt;https://github.com/python-attrs/attrs/issues/253&gt;`_)


>Changes
>^^^^^^^

>- ``super()`` and ``__class__`` now work on Python 3 when ``slots=True``.
>  (`102 &lt;https://github.com/python-attrs/attrs/issues/102&gt;`_, `226 &lt;https://github.com/python-attrs/attrs/issues/226&gt;`_, `269 &lt;https://github.com/python-attrs/attrs/issues/269&gt;`_, `270 &lt;https://github.com/python-attrs/attrs/issues/270&gt;`_, `272 &lt;https://github.com/python-attrs/attrs/issues/272&gt;`_)
>- Added ``type`` argument to ``attr.ib()`` and corresponding ``type`` attribute to ``attr.Attribute``.

>  This change paves the way for automatic type checking and serialization (though as of this release ``attrs`` does not make use of it).
>  In Python 3.6 or higher, the value of ``attr.Attribute.type`` can alternately be set using variable type annotations
>  (see `PEP 526 &lt;https://www.python.org/dev/peps/pep-0526/&gt;`_). (`151 &lt;https://github.com/python-attrs/attrs/issues/151&gt;`_, `214 &lt;https://github.com/python-attrs/attrs/issues/214&gt;`_, `215 &lt;https://github.com/python-attrs/attrs/issues/215&gt;`_, `239 &lt;https://github.com/python-attrs/attrs/issues/239&gt;`_)
>- The combination of ``str=True`` and ``slots=True`` now works on Python 2.
>  (`198 &lt;https://github.com/python-attrs/attrs/issues/198&gt;`_)
>- ``attr.Factory`` is hashable again. (`204
>  &lt;https://github.com/python-attrs/attrs/issues/204&gt;`_)
>- Subclasses now can overwrite attribute definitions of their superclass.

>  That means that you can -- for example -- change the default value for an attribute by redefining it.
>  (`221 &lt;https://github.com/python-attrs/attrs/issues/221&gt;`_, `229 &lt;https://github.com/python-attrs/attrs/issues/229&gt;`_)
>- Added new option ``auto_attribs`` to ``attr.s`` that allows to collect annotated fields without setting them to ``attr.ib()``.

>  Setting a field to an ``attr.ib()`` is still possible to supply options like validators.
>  Setting it to any other value is treated like it was passed as ``attr.ib(default=value)`` -- passing an instance of ``attr.Factory`` also works as expected.
>  (`262 &lt;https://github.com/python-attrs/attrs/issues/262&gt;`_, `277 &lt;https://github.com/python-attrs/attrs/issues/277&gt;`_)
>- Instances of classes created using ``attr.make_class()`` can now be pickled.
>  (`282 &lt;https://github.com/python-attrs/attrs/issues/282&gt;`_)


>----








### hypothesis 3.33.0 -> 3.37.0

>### 3.37.0

>-------------------

>This is a deprecation release for some health check related features.

>The following are now deprecated:

>* Passing :attr:`~hypothesis.HealthCheck.exception_in_generation` to
>  :attr:`~hypothesis.settings.suppress_health_check`. This no longer does
>  anything even when passed -  All errors that occur during data generation
>  will now be immediately reraised rather than going through the health check
>  mechanism.
>* Passing :attr:`~hypothesis.HealthCheck.random_module` to
>  :attr:`~hypothesis.settings.suppress_health_check`. This hasn&#39;t done anything
>  for a long time, but was never explicitly deprecated. Hypothesis always seeds
>  the random module when running given tests, so this is no longer an error
>  and suppressing it doesn&#39;t do anything.
>* Passing non-:class:`~hypothesis.HealthCheck` values in
>  :attr:`~hypothesis.settings.suppress_health_check`. This was previously
>  allowed but never did anything useful.

>In addition, passing a non-iterable value as :attr:`~hypothesis.settings.suppress_health_check`
>will now raise an error immediately (it would never have worked correctly, but
>it would previously have failed later). Some validation error messages have
>also been updated.

>This work was funded by `Smarkets &lt;https://smarkets.com/&gt;`_.

>-------------------


>### 3.36.1

>-------------------

>This is a yak shaving release, mostly concerned with our own tests.

>While :func:`~python:inspect.getfullargspec` was documented as deprecated
>in Python 3.5, it never actually emitted a warning.  Our code to silence
>this (nonexistent) warning has therefore been removed.

>We now run our tests with ``DeprecationWarning`` as an error, and made some
>minor changes to our own tests as a result.  This required similar upstream
>updates to :pypi:`coverage` and :pypi:`execnet` (a test-time dependency via
>:pypi:`pytest-xdist`).

>There is no user-visible change in Hypothesis itself, but we encourage you
>to consider enabling deprecations as errors in your own tests.

>-------------------


>### 3.36.0

>-------------------

>This release adds a setting to the public API, and does some internal cleanup:

>- The :attr:`~hypothesis.settings.derandomize` setting is now documented (:issue:`890`)
>- Removed - and disallowed - all &#39;bare excepts&#39; in Hypothesis (:issue:`953`)
>- Documented the :attr:`~hypothesis.settings.strict` setting as deprecated, and
>  updated the build so our docs always match deprecations in the code.

>-------------------


>### 3.35.0

>-------------------

>This minor release supports constraining :func:`~hypothesis.strategies.uuids`
>to generate :class:`~python:uuid.UUID`s of a particular version.
>(:issue:`721`)

>Thanks to Dion Misic for this feature.

>-------------------


>### 3.34.1

>-------------------

>This patch updates the documentation to suggest
>:func:`builds(callable) &lt;hypothesis.strategies.builds&gt;` instead of
>:func:`just(callable()) &lt;hypothesis.strategies.just&gt;`.

>-------------------


>### 3.34.0

>-------------------

>Hypothesis now emits deprecation warnings if you apply
>:func:`given &lt;hypothesis.given&gt;` more than once to a target.

>Applying :func:`given &lt;hypothesis.given&gt;` repeatedly wraps the target multiple
>times. Each wrapper will search the space of of possible parameters separately.
>This is equivalent but will be much more inefficient than doing it with a
>single call to :func:`given &lt;hypothesis.given&gt;`.

>For example, instead of
>``given(booleans()) given(integers())``, you could write
>``given(booleans(), integers())``

>-------------------


>### 3.33.1

>-------------------

>This is a bugfix release:

>- :func:`~hypothesis.strategies.builds` would try to infer a strategy for
>  required positional arguments of the target from type hints, even if they
>  had been given to :func:`~hypothesis.strategies.builds` as positional
>  arguments (:issue:`946`).  Now it only infers missing required arguments.
>- An internal introspection function wrongly reported ``self`` as a required
>  argument for bound methods, which might also have affected
>  :func:`~hypothesis.strategies.builds`.  Now it knows better.

>-------------------









That's it for now!

Happy merging! 🤖

bors-fusion bot added a commit to fusionapp/entropy that referenced this issue Nov 15, 2017

Merge #161
161: Scheduled weekly dependency update for week 46 r=mithrandi




## Updates
Here's a list of all the updates bundled in this pull request. I've added some links to make it easier for you to find all the information you need.
<table align="center">

<tr>
<td><b>attrs</b></td>
<td align="center">17.2.0</td>
<td align="center">&raquo;</td>
<td align="center">17.3.0</td>
<td>
     <a href="https://pypi.python.org/pypi/attrs">PyPI</a> | <a href="https://pyup.io/changelogs/attrs/">Changelog</a> | <a href="http://www.attrs.org/">Homepage</a> 

</td>

<tr>
<td><b>cryptography</b></td>
<td align="center">2.1.2</td>
<td align="center">&raquo;</td>
<td align="center">2.1.3</td>
<td>
     <a href="https://pypi.python.org/pypi/cryptography">PyPI</a> | <a href="https://pyup.io/changelogs/cryptography/">Changelog</a> | <a href="https://github.com/pyca/cryptography">Repo</a> 

</td>

<tr>
<td><b>lxml</b></td>
<td align="center">4.1.0</td>
<td align="center">&raquo;</td>
<td align="center">4.1.1</td>
<td>
     <a href="https://pypi.python.org/pypi/lxml">PyPI</a> | <a href="https://pyup.io/changelogs/lxml/">Changelog</a> | <a href="http://lxml.de/">Homepage</a> | <a href="https://bugs.launchpad.net/lxml">Bugtracker</a> 

</td>

<tr>
<td><b>pytz</b></td>
<td align="center">2017.2</td>
<td align="center">&raquo;</td>
<td align="center">2017.3</td>
<td>
     <a href="https://pypi.python.org/pypi/pytz">PyPI</a> | <a href="http://pythonhosted.org/pytz">Homepage</a> | <a href="http://pythonhosted.org/pytz/">Docs</a> 

</td>

</tr>
</table>



## Changelogs


### attrs 17.2.0 -> 17.3.0

>### 17.3.0

>-------------------

>Backward-incompatible Changes
>^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

>- Attributes are not defined on the class body anymore.

>  This means that if you define a class ``C`` with an attribute ``x``, the class will *not* have an attribute ``x`` for introspection anymore.
>  Instead of ``C.x``, use ``attr.fields(C).x`` or look at ``C.__attrs_attrs__``.
>  The old behavior has been deprecated since version 16.1.
>  (`253 &lt;https://github.com/python-attrs/attrs/issues/253&gt;`_)


>Changes
>^^^^^^^

>- ``super()`` and ``__class__`` now work on Python 3 when ``slots=True``.
>  (`102 &lt;https://github.com/python-attrs/attrs/issues/102&gt;`_, `226 &lt;https://github.com/python-attrs/attrs/issues/226&gt;`_, `269 &lt;https://github.com/python-attrs/attrs/issues/269&gt;`_, `270 &lt;https://github.com/python-attrs/attrs/issues/270&gt;`_, `272 &lt;https://github.com/python-attrs/attrs/issues/272&gt;`_)
>- Added ``type`` argument to ``attr.ib()`` and corresponding ``type`` attribute to ``attr.Attribute``.

>  This change paves the way for automatic type checking and serialization (though as of this release ``attrs`` does not make use of it).
>  In Python 3.6 or higher, the value of ``attr.Attribute.type`` can alternately be set using variable type annotations
>  (see `PEP 526 &lt;https://www.python.org/dev/peps/pep-0526/&gt;`_). (`151 &lt;https://github.com/python-attrs/attrs/issues/151&gt;`_, `214 &lt;https://github.com/python-attrs/attrs/issues/214&gt;`_, `215 &lt;https://github.com/python-attrs/attrs/issues/215&gt;`_, `239 &lt;https://github.com/python-attrs/attrs/issues/239&gt;`_)
>- The combination of ``str=True`` and ``slots=True`` now works on Python 2.
>  (`198 &lt;https://github.com/python-attrs/attrs/issues/198&gt;`_)
>- ``attr.Factory`` is hashable again. (`204
>  &lt;https://github.com/python-attrs/attrs/issues/204&gt;`_)
>- Subclasses now can overwrite attribute definitions of their superclass.

>  That means that you can -- for example -- change the default value for an attribute by redefining it.
>  (`221 &lt;https://github.com/python-attrs/attrs/issues/221&gt;`_, `229 &lt;https://github.com/python-attrs/attrs/issues/229&gt;`_)
>- Added new option ``auto_attribs`` to ``attr.s`` that allows to collect annotated fields without setting them to ``attr.ib()``.

>  Setting a field to an ``attr.ib()`` is still possible to supply options like validators.
>  Setting it to any other value is treated like it was passed as ``attr.ib(default=value)`` -- passing an instance of ``attr.Factory`` also works as expected.
>  (`262 &lt;https://github.com/python-attrs/attrs/issues/262&gt;`_, `277 &lt;https://github.com/python-attrs/attrs/issues/277&gt;`_)
>- Instances of classes created using ``attr.make_class()`` can now be pickled.
>  (`282 &lt;https://github.com/python-attrs/attrs/issues/282&gt;`_)


>----








### lxml 4.1.0 -> 4.1.1

>### 4.1.1

>==================

>* Rebuild with Cython 0.27.3 to improve support for Py3.7.











That's it for now!

Happy merging! 🤖

bors-fusion bot added a commit to fusionapp/documint that referenced this issue Nov 17, 2017

Merge #118
118: Update attrs to 17.3.0 r=mithrandi


There's a new version of [attrs](https://pypi.python.org/pypi/attrs) available.
You are currently using **17.2.0**. I have updated it to **17.3.0**



These links might come in handy:  <a href="https://pypi.python.org/pypi/attrs">PyPI</a> | <a href="https://pyup.io/changelogs/attrs/">Changelog</a> | <a href="http://www.attrs.org/">Homepage</a> 



### Changelog
> 
>### 17.3.0

>-------------------

>Backward-incompatible Changes
>^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

>- Attributes are not defined on the class body anymore.

>  This means that if you define a class ``C`` with an attribute ``x``, the class will *not* have an attribute ``x`` for introspection anymore.
>  Instead of ``C.x``, use ``attr.fields(C).x`` or look at ``C.__attrs_attrs__``.
>  The old behavior has been deprecated since version 16.1.
>  (`253 &lt;https://github.com/python-attrs/attrs/issues/253&gt;`_)


>Changes
>^^^^^^^

>- ``super()`` and ``__class__`` now work on Python 3 when ``slots=True``.
>  (`102 &lt;https://github.com/python-attrs/attrs/issues/102&gt;`_, `226 &lt;https://github.com/python-attrs/attrs/issues/226&gt;`_, `269 &lt;https://github.com/python-attrs/attrs/issues/269&gt;`_, `270 &lt;https://github.com/python-attrs/attrs/issues/270&gt;`_, `272 &lt;https://github.com/python-attrs/attrs/issues/272&gt;`_)
>- Added ``type`` argument to ``attr.ib()`` and corresponding ``type`` attribute to ``attr.Attribute``.

>  This change paves the way for automatic type checking and serialization (though as of this release ``attrs`` does not make use of it).
>  In Python 3.6 or higher, the value of ``attr.Attribute.type`` can alternately be set using variable type annotations
>  (see `PEP 526 &lt;https://www.python.org/dev/peps/pep-0526/&gt;`_). (`151 &lt;https://github.com/python-attrs/attrs/issues/151&gt;`_, `214 &lt;https://github.com/python-attrs/attrs/issues/214&gt;`_, `215 &lt;https://github.com/python-attrs/attrs/issues/215&gt;`_, `239 &lt;https://github.com/python-attrs/attrs/issues/239&gt;`_)
>- The combination of ``str=True`` and ``slots=True`` now works on Python 2.
>  (`198 &lt;https://github.com/python-attrs/attrs/issues/198&gt;`_)
>- ``attr.Factory`` is hashable again. (`204
>  &lt;https://github.com/python-attrs/attrs/issues/204&gt;`_)
>- Subclasses now can overwrite attribute definitions of their superclass.

>  That means that you can -- for example -- change the default value for an attribute by redefining it.
>  (`221 &lt;https://github.com/python-attrs/attrs/issues/221&gt;`_, `229 &lt;https://github.com/python-attrs/attrs/issues/229&gt;`_)
>- Added new option ``auto_attribs`` to ``attr.s`` that allows to collect annotated fields without setting them to ``attr.ib()``.

>  Setting a field to an ``attr.ib()`` is still possible to supply options like validators.
>  Setting it to any other value is treated like it was passed as ``attr.ib(default=value)`` -- passing an instance of ``attr.Factory`` also works as expected.
>  (`262 &lt;https://github.com/python-attrs/attrs/issues/262&gt;`_, `277 &lt;https://github.com/python-attrs/attrs/issues/277&gt;`_)
>- Instances of classes created using ``attr.make_class()`` can now be pickled.
>  (`282 &lt;https://github.com/python-attrs/attrs/issues/282&gt;`_)


>----








*Got merge conflicts? Close this PR and delete the branch. I'll create a new PR for you.*

Happy merging! 🤖
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment