Bug 1471629 - [wpt] Update attrs to version 18.1.0. r=ato
authorHenrik Skupin <mail@hskupin.info>
Thu, 28 Jun 2018 09:40:11 +0200
changeset 424333 f46b4924c2a8b72135104e59a14cfab76c4088b0
parent 424332 bccabec0bf8199937e84786434c7ffcb0eecac24
child 424334 19b5a10c06350dd843a7d3e62635e56a5d7081ba
push id34202
push userebalazs@mozilla.com
push dateFri, 29 Jun 2018 09:40:47 +0000
treeherdermozilla-central@7f2d98706144 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersato
bugs1471629
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1471629 - [wpt] Update attrs to version 18.1.0. r=ato MozReview-Commit-ID: AXybeVEZHYM
testing/web-platform/tests/tools/third_party/attrs/.coveragerc
testing/web-platform/tests/tools/third_party/attrs/.github/CODE_OF_CONDUCT.rst
testing/web-platform/tests/tools/third_party/attrs/.github/CONTRIBUTING.rst
testing/web-platform/tests/tools/third_party/attrs/.github/PULL_REQUEST_TEMPLATE.md
testing/web-platform/tests/tools/third_party/attrs/.gitignore
testing/web-platform/tests/tools/third_party/attrs/.readthedocs.yml
testing/web-platform/tests/tools/third_party/attrs/.travis.yml
testing/web-platform/tests/tools/third_party/attrs/CHANGELOG.rst
testing/web-platform/tests/tools/third_party/attrs/CODE_OF_CONDUCT.rst
testing/web-platform/tests/tools/third_party/attrs/CONTRIBUTING.rst
testing/web-platform/tests/tools/third_party/attrs/MANIFEST.in
testing/web-platform/tests/tools/third_party/attrs/PULL_REQUEST_TEMPLATE.md
testing/web-platform/tests/tools/third_party/attrs/README.rst
testing/web-platform/tests/tools/third_party/attrs/changelog.d/178.change.rst
testing/web-platform/tests/tools/third_party/attrs/changelog.d/261.change.rst
testing/web-platform/tests/tools/third_party/attrs/changelog.d/284.change.rst
testing/web-platform/tests/tools/third_party/attrs/changelog.d/285.change.rst
testing/web-platform/tests/tools/third_party/attrs/changelog.d/286.change.rst
testing/web-platform/tests/tools/third_party/attrs/changelog.d/287.change.rst
testing/web-platform/tests/tools/third_party/attrs/changelog.d/290.change.rst
testing/web-platform/tests/tools/third_party/attrs/changelog.d/291.change.rst
testing/web-platform/tests/tools/third_party/attrs/changelog.d/292.change.rst
testing/web-platform/tests/tools/third_party/attrs/changelog.d/295.change.rst
testing/web-platform/tests/tools/third_party/attrs/changelog.d/296.change.rst
testing/web-platform/tests/tools/third_party/attrs/changelog.d/300.change.rst
testing/web-platform/tests/tools/third_party/attrs/changelog.d/311.change.rst
testing/web-platform/tests/tools/third_party/attrs/changelog.d/321.change.rst
testing/web-platform/tests/tools/third_party/attrs/changelog.d/322.change.rst
testing/web-platform/tests/tools/third_party/attrs/changelog.d/323.change.rst
testing/web-platform/tests/tools/third_party/attrs/changelog.d/326.change.rst
testing/web-platform/tests/tools/third_party/attrs/changelog.d/331.change.rst
testing/web-platform/tests/tools/third_party/attrs/changelog.d/332.change.rst
testing/web-platform/tests/tools/third_party/attrs/changelog.d/334.change.rst
testing/web-platform/tests/tools/third_party/attrs/changelog.d/336.change.rst
testing/web-platform/tests/tools/third_party/attrs/changelog.d/339.change.rst
testing/web-platform/tests/tools/third_party/attrs/changelog.d/343.change.rst
testing/web-platform/tests/tools/third_party/attrs/changelog.d/349.change.rst
testing/web-platform/tests/tools/third_party/attrs/changelog.d/356.change.rst
testing/web-platform/tests/tools/third_party/attrs/changelog.d/363.change.rst
testing/web-platform/tests/tools/third_party/attrs/changelog.d/369.change.rst
testing/web-platform/tests/tools/third_party/attrs/changelog.d/370.change.rst
testing/web-platform/tests/tools/third_party/attrs/changelog.d/95.change.rst
testing/web-platform/tests/tools/third_party/attrs/changelog.d/towncrier_template.rst
testing/web-platform/tests/tools/third_party/attrs/dev-requirements.txt
testing/web-platform/tests/tools/third_party/attrs/docs-requirements.txt
testing/web-platform/tests/tools/third_party/attrs/docs/api.rst
testing/web-platform/tests/tools/third_party/attrs/docs/contributing.rst
testing/web-platform/tests/tools/third_party/attrs/docs/examples.rst
testing/web-platform/tests/tools/third_party/attrs/docs/extending.rst
testing/web-platform/tests/tools/third_party/attrs/docs/glossary.rst
testing/web-platform/tests/tools/third_party/attrs/docs/hashing.rst
testing/web-platform/tests/tools/third_party/attrs/docs/how-does-it-work.rst
testing/web-platform/tests/tools/third_party/attrs/docs/index.rst
testing/web-platform/tests/tools/third_party/attrs/docs/init.rst
testing/web-platform/tests/tools/third_party/attrs/docs/why.rst
testing/web-platform/tests/tools/third_party/attrs/pyproject.toml
testing/web-platform/tests/tools/third_party/attrs/setup.py
testing/web-platform/tests/tools/third_party/attrs/src/attr/__init__.py
testing/web-platform/tests/tools/third_party/attrs/src/attr/_compat.py
testing/web-platform/tests/tools/third_party/attrs/src/attr/_make.py
testing/web-platform/tests/tools/third_party/attrs/src/attr/filters.py
testing/web-platform/tests/tools/third_party/attrs/tests/strategies.py
testing/web-platform/tests/tools/third_party/attrs/tests/test_annotations.py
testing/web-platform/tests/tools/third_party/attrs/tests/test_dark_magic.py
testing/web-platform/tests/tools/third_party/attrs/tests/test_dunders.py
testing/web-platform/tests/tools/third_party/attrs/tests/test_funcs.py
testing/web-platform/tests/tools/third_party/attrs/tests/test_make.py
testing/web-platform/tests/tools/third_party/attrs/tests/test_validators.py
testing/web-platform/tests/tools/third_party/attrs/tests/utils.py
testing/web-platform/tests/tools/third_party/attrs/tox.ini
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/tools/third_party/attrs/.coveragerc
@@ -0,0 +1,13 @@
+[run]
+branch = True
+source =
+    attr
+
+[paths]
+source =
+   src/attr
+   .tox/*/lib/python*/site-packages/attr
+   .tox/pypy/site-packages/attr
+
+[report]
+show_missing = True
rename from testing/web-platform/tests/tools/third_party/attrs/CODE_OF_CONDUCT.rst
rename to testing/web-platform/tests/tools/third_party/attrs/.github/CODE_OF_CONDUCT.rst
--- a/testing/web-platform/tests/tools/third_party/attrs/CODE_OF_CONDUCT.rst
+++ b/testing/web-platform/tests/tools/third_party/attrs/.github/CODE_OF_CONDUCT.rst
@@ -47,9 +47,9 @@ All complaints will be reviewed and inve
 The project team is obligated to maintain confidentiality with regard to the reporter of an incident.
 Further details of specific enforcement policies may be posted separately.
 
 Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
 
 Attribution
 -----------
 
-This Code of Conduct is adapted from the `Contributor Covenant <http://contributor-covenant.org>`_, version 1.4, available at http://contributor-covenant.org/version/1/4.
+This Code of Conduct is adapted from the `Contributor Covenant <https://www.contributor-covenant.org>`_, version 1.4, available at <https://www.contributor-covenant.org/version/1/4/code-of-conduct.html>.
rename from testing/web-platform/tests/tools/third_party/attrs/CONTRIBUTING.rst
rename to testing/web-platform/tests/tools/third_party/attrs/.github/CONTRIBUTING.rst
--- a/testing/web-platform/tests/tools/third_party/attrs/CONTRIBUTING.rst
+++ b/testing/web-platform/tests/tools/third_party/attrs/.github/CONTRIBUTING.rst
@@ -1,25 +1,25 @@
 How To Contribute
 =================
 
 First off, thank you for considering contributing to ``attrs``!
-It's people like *you* who make it is such a great tool for everyone.
+It's people like *you* who make it such a great tool for everyone.
 
-This document is mainly to help you to get started by codifying tribal knowledge and expectations and make it more accessible to everyone.
-But don't be afraid to open half-finished PRs and ask questions if something is unclear!
+This document intends to make contribution more accessible by codifying tribal knowledge and expectations.
+Don't be afraid to open half-finished PRs, and ask questions if something is unclear!
 
 
 Support
 -------
 
 In case you'd like to help out but don't want to deal with GitHub, there's a great opportunity:
 help your fellow developers on `StackOverflow <https://stackoverflow.com/questions/tagged/python-attrs>`_!
 
-The offical tag is ``python-attrs`` and helping out in support frees us up for improving ``attrs`` instead!
+The offical tag is ``python-attrs`` and helping out in support frees us up to improve ``attrs`` instead!
 
 
 Workflow
 --------
 
 - No contribution is too small!
   Please submit as many fixes for typos and grammar bloopers as you can!
 - Try to limit each pull request to *one* change only.
@@ -62,64 +62,65 @@ Tests
 
      assert 42 == x.some_attribute
      assert "foo" == x._a_private_attribute
 
 - To run the test suite, all you need is a recent tox_.
   It will ensure the test suite runs with all dependencies against all Python versions just as it will on Travis CI.
   If you lack some Python versions, you can can always limit the environments like ``tox -e py27,py35`` (in that case you may want to look into pyenv_, which makes it very easy to install many different Python versions in parallel).
 - Write `good test docstrings`_.
-- To ensure new features work well with the rest of the system, they should be also added to our `Hypothesis`_ testing strategy which you find in ``tests/util.py``.
+- To ensure new features work well with the rest of the system, they should be also added to our `Hypothesis`_ testing strategy, which is found in ``tests/strategies.py``.
 
 
 Documentation
 -------------
 
 - Use `semantic newlines`_ in reStructuredText_ files (files ending in ``.rst``):
 
   .. code-block:: rst
 
      This is a sentence.
      This is another sentence.
 
-- If you start a new section, add two blank lines before and one blank line after the header except if two headers follow immediately after each other:
+- If you start a new section, add two blank lines before and one blank line after the header, except if two headers follow immediately after each other:
 
   .. code-block:: rst
 
      Last line of previous section.
 
 
      Header of New Top Section
      -------------------------
 
      Header of New Section
      ^^^^^^^^^^^^^^^^^^^^^
 
      First line of new section.
-- If you add a new feature, demonstrate its awesomeness in the `examples page`_!
+
+- If you add a new feature, demonstrate its awesomeness on the `examples page`_!
 
 
 Changelog
 ^^^^^^^^^
 
-If your change is noteworthy, there needs to be a changelog entry, so our users can learn about it!
+If your change is noteworthy, there needs to be a changelog entry so our users can learn about it!
 
 To avoid merge conflicts, we use the towncrier_ package to manage our changelog.
 ``towncrier`` uses independent files for each pull request -- so called *news fragments* -- instead of one monolithic changelog file.
-On release those news fragments are compiled into our ``CHANGELOG.rst``.
+On release, those news fragments are compiled into our ``CHANGELOG.rst``.
 
-You don't need to install ``towncrier`` yourself, you just have to abide to a few simple rules:
+You don't need to install ``towncrier`` yourself, you just have to abide by a few simple rules:
 
 - For each pull request, add a new file into ``changelog.d`` with a filename adhering to the ``pr#.(change|deprecation|breaking).rst`` schema:
-  For example ``changelog.d/42.change.rst`` for a non-breaking change, that is proposed in pull request number 42.
+  For example, ``changelog.d/42.change.rst`` for a non-breaking change that is proposed in pull request #42.
 - As with other docs, please use `semantic newlines`_ within news fragments.
-- Wrap symbols like modules, functions, or classes into double backticks so they are rendered in a monospaced font.
-- If you mention functions or other callables, add parantheses at the end of their names: ``attr.func()`` or ``attr.Class.method()``.
+- Wrap symbols like modules, functions, or classes into double backticks so they are rendered in a monospace font.
+- If you mention functions or other callables, add parentheses at the end of their names: ``attr.func()`` or ``attr.Class.method()``.
   This makes the changelog a lot more readable.
-- Prefer simple past or constructions with "now".
+- Prefer simple past tense or constructions with "now".
   For example:
 
   + Added ``attr.validators.func()``.
   + ``attr.func()`` now doesn't crash the Large Hadron Collider anymore.
 - If you want to reference multiple issues, copy the news fragment to another filename.
   ``towncrier`` will merge all news fragments with identical contents into one entry with multiple links to the respective pull requests.
 
 Example entries:
@@ -140,56 +141,55 @@ or:
 
 ``tox -e changelog`` will render the current changelog to the terminal if you have any doubts.
 
 
 Local Development Environment
 -----------------------------
 
 You can (and should) run our test suite using tox_.
-However you’ll probably want a more traditional environment too.
+However, you’ll probably want a more traditional environment as well.
 We highly recommend to develop using the latest Python 3 release because ``attrs`` tries to take advantage of modern features whenever possible.
 
 First create a `virtual environment <https://virtualenv.pypa.io/>`_.
-It’s out of scope for this document to list all the ways to manage virtual environments in Python but if you don’t have already a pet way, take some time to look at tools like `pew <https://github.com/berdario/pew>`_, `virtualfish <http://virtualfish.readthedocs.io/>`_, and `virtualenvwrapper <http://virtualenvwrapper.readthedocs.io/>`_.
+It’s out of scope for this document to list all the ways to manage virtual environments in Python, but if you don’t already have a pet way, take some time to look at tools like `pew <https://github.com/berdario/pew>`_, `virtualfish <http://virtualfish.readthedocs.io/>`_, and `virtualenvwrapper <http://virtualenvwrapper.readthedocs.io/>`_.
 
-Next get an up to date checkout of the ``attrs`` repository:
+Next, get an up to date checkout of the ``attrs`` repository:
 
 .. code-block:: bash
 
-    git checkout git@github.com:python-attrs/attrs.git
+    $ git checkout git@github.com:python-attrs/attrs.git
 
-Change into the newly created directory and **after activating your virtual environment** install an editable version of ``attrs``:
+Change into the newly created directory and **after activating your virtual environment** install an editable version of ``attrs`` along with its tests and docs requirements:
 
 .. code-block:: bash
 
-    cd attrs
-    pip install -e .
+    $ cd attrs
+    $ pip install -e .[dev]
 
-If you run the virtual environment’s Python and try to ``import attr`` it should work!
-
-To run the test suite, you'll need our development dependencies which can be installed using
+At this point,
 
 .. code-block:: bash
 
-    pip install -r dev-requirements.txt
+   $ python -m pytest
 
-At this point
+should work and pass, as should:
 
 .. code-block:: bash
 
-   python -m pytest
+   $ cd docs
+   $ make html
 
-should work and pass!
+The built documentation can then be found in ``docs/_build/html/``.
 
 
 Governance
 ----------
 
-``attrs`` is maintained by `team of volunteers`_ that is always open for new members that share our vision of a fast, lean, and magic-free library that empowers programmers to write better code with less effort.
+``attrs`` is maintained by `team of volunteers`_ that is always open to new members that share our vision of a fast, lean, and magic-free library that empowers programmers to write better code with less effort.
 If you'd like to join, just get a pull request merged and ask to be added in the very same pull request!
 
 **The simple rule is that everyone is welcome to review/merge pull requests of others but nobody is allowed to merge their own code.**
 
 `Hynek Schlawack`_ acts reluctantly as the BDFL_ and has the final say over design decisions.
 
 
 ****
@@ -200,17 +200,17 @@ Please report any harm to `Hynek Schlawa
 
 Thank you for considering contributing to ``attrs``!
 
 
 .. _`Hynek Schlawack`: https://hynek.me/about/
 .. _`PEP 8`: https://www.python.org/dev/peps/pep-0008/
 .. _`PEP 257`: https://www.python.org/dev/peps/pep-0257/
 .. _`good test docstrings`: https://jml.io/pages/test-docstrings.html
-.. _`Code of Conduct`: https://github.com/python-attrs/attrs/blob/master/CODE_OF_CONDUCT.rst
+.. _`Code of Conduct`: https://github.com/python-attrs/attrs/blob/master/.github/CODE_OF_CONDUCT.rst
 .. _changelog: https://github.com/python-attrs/attrs/blob/master/CHANGELOG.rst
 .. _`backward compatibility`: http://www.attrs.org/en/latest/backward-compatibility.html
 .. _tox: https://tox.readthedocs.io/
 .. _pyenv: https://github.com/pyenv/pyenv
 .. _reStructuredText: http://www.sphinx-doc.org/en/stable/rest.html
 .. _semantic newlines: http://rhodesmill.org/brandon/2012/one-sentence-per-line/
 .. _examples page: https://github.com/python-attrs/attrs/blob/master/docs/examples.rst
 .. _Hypothesis: https://hypothesis.readthedocs.io/
rename from testing/web-platform/tests/tools/third_party/attrs/PULL_REQUEST_TEMPLATE.md
rename to testing/web-platform/tests/tools/third_party/attrs/.github/PULL_REQUEST_TEMPLATE.md
--- a/testing/web-platform/tests/tools/third_party/attrs/PULL_REQUEST_TEMPLATE.md
+++ b/testing/web-platform/tests/tools/third_party/attrs/.github/PULL_REQUEST_TEMPLATE.md
@@ -1,12 +1,12 @@
 # Pull Request Check List
 
 This is just a reminder about the most common mistakes.  Please make sure that you tick all *appropriate* boxes.  But please read our [contribution guide](http://www.attrs.org/en/latest/contributing.html) at least once, it will save you unnecessary review cycles!
 
 - [ ] Added **tests** for changed code.
-- [ ] New features have been added to our [Hypothesis testing strategy](https://github.com/python-attrs/attrs/blob/master/tests/utils.py).
+- [ ] New features have been added to our [Hypothesis testing strategy](https://github.com/python-attrs/attrs/blob/master/tests/strategies.py).
 - [ ] Updated **documentation** for changed code.
 - [ ] Documentation in `.rst` files is written using [semantic newlines](http://rhodesmill.org/brandon/2012/one-sentence-per-line/).
 - [ ] Changed/added classes/methods/functions have appropriate `versionadded`, `versionchanged`, or `deprecated` [directives](http://www.sphinx-doc.org/en/stable/markup/para.html#directive-versionadded).
 - [ ] Changes (and possible deprecations) have news fragments in [`changelog.d`](https://github.com/python-attrs/attrs/blob/master/changelog.d).
 
 If you have *any* questions to *any* of the points above, just **submit and ask**!  This checklist is here to *help* you, not to deter you from contributing!
--- a/testing/web-platform/tests/tools/third_party/attrs/.gitignore
+++ b/testing/web-platform/tests/tools/third_party/attrs/.gitignore
@@ -1,9 +1,11 @@
-.tox
-.coverage*
 *.pyc
 *.egg-info
+.cache
+.coverage*
+.hypothesis
+.pytest_cache
+.tox
+build
+dist
 docs/_build/
 htmlcov
-dist
-.cache
-.hypothesis
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/tools/third_party/attrs/.readthedocs.yml
@@ -0,0 +1,6 @@
+---
+python:
+  version: 3
+  pip_install: true
+  extra_requirements:
+    - docs
--- a/testing/web-platform/tests/tools/third_party/attrs/.travis.yml
+++ b/testing/web-platform/tests/tools/third_party/attrs/.travis.yml
@@ -1,44 +1,57 @@
 dist: trusty
+group: travis_latest
 sudo: false
 cache:
   directories:
     - $HOME/.cache/pip
 
 language: python
 
 
 matrix:
+  fast_finish: true
+
   include:
     - python: "2.7"
       env: TOXENV=py27
     - python: "3.4"
       env: TOXENV=py34
     - python: "3.5"
       env: TOXENV=py35
     - python: "3.6"
       env: TOXENV=py36
-    - python: "pypy2.7-5.8.0"
+    - python: "pypy"
       env: TOXENV=pypy
-    - python: "pypy3.5-5.8.0"
+    - python: "pypy3"
       env: TOXENV=pypy3
 
+    # Prevent breakage by a new releases
+    - python: "3.6-dev"
+      env: TOXENV=py36
+    - python: "3.7-dev"
+      env: TOXENV=py37
+
     # Meta
     - python: "3.6"
       env: TOXENV=flake8
     - python: "3.6"
       env: TOXENV=manifest
     - python: "3.6"
       env: TOXENV=docs
     - python: "3.6"
       env: TOXENV=readme
     - python: "3.6"
       env: TOXENV=changelog
 
+  allow_failures:
+    - python: "3.6-dev"
+    - python: "3.7-dev"
+
 
 install:
   - pip install tox
 
 
 script:
   - tox
 
--- a/testing/web-platform/tests/tools/third_party/attrs/CHANGELOG.rst
+++ b/testing/web-platform/tests/tools/third_party/attrs/CHANGELOG.rst
@@ -1,24 +1,169 @@
 Changelog
 =========
 
 Versions follow `CalVer <http://calver.org>`_ with a strict backwards compatibility policy.
 The third digit is only for regressions.
 
-Changes for the upcoming release can be found in the `"changelog.d" directory <https://github.com/python-attrs/attrs/tree/master/changelog.d>`_ in our repository.
+.. towncrier release notes start
+
+18.1.0 (2018-05-03)
+-------------------
+
+Changes
+^^^^^^^
+
+- ``x=X(); x.cycle = x; repr(x)`` will no longer raise a ``RecursionError``, and will instead show as ``X(x=...)``.
+
+  `#95 <https://github.com/python-attrs/attrs/issues/95>`_
+- ``attr.ib(factory=f)`` is now syntactic sugar for the common case of ``attr.ib(default=attr.Factory(f))``.
+
+  `#178 <https://github.com/python-attrs/attrs/issues/178>`_,
+  `#356 <https://github.com/python-attrs/attrs/issues/356>`_
+- Added ``attr.field_dict()`` to return an ordered dictionary of ``attrs`` attributes for a class, whose keys are the attribute names.
+
+  `#290 <https://github.com/python-attrs/attrs/issues/290>`_,
+  `#349 <https://github.com/python-attrs/attrs/issues/349>`_
+- The order of attributes that are passed into ``attr.make_class()`` or the ``these`` argument of ``@attr.s()`` is now retained if the dictionary is ordered (i.e. ``dict`` on Python 3.6 and later, ``collections.OrderedDict`` otherwise).
+
+  Before, the order was always determined by the order in which the attributes have been defined which may not be desirable when creating classes programatically.
+
+  `#300 <https://github.com/python-attrs/attrs/issues/300>`_,
+  `#339 <https://github.com/python-attrs/attrs/issues/339>`_,
+  `#343 <https://github.com/python-attrs/attrs/issues/343>`_
+- In slotted classes, ``__getstate__`` and ``__setstate__`` now ignore the ``__weakref__`` attribute.
+
+  `#311 <https://github.com/python-attrs/attrs/issues/311>`_,
+  `#326 <https://github.com/python-attrs/attrs/issues/326>`_
+- Setting the cell type is now completely best effort.
+  This fixes ``attrs`` on Jython.
+
+  We cannot make any guarantees regarding Jython though, because our test suite cannot run due to dependency incompatabilities.
+
+  `#321 <https://github.com/python-attrs/attrs/issues/321>`_,
+  `#334 <https://github.com/python-attrs/attrs/issues/334>`_
+- If ``attr.s`` is passed a *these* argument, it will not attempt to remove attributes with the same name from the class body anymore.
 
-..
-   Do *NOT* add changelog entries here!
+  `#322 <https://github.com/python-attrs/attrs/issues/322>`_,
+  `#323 <https://github.com/python-attrs/attrs/issues/323>`_
+- The hash of ``attr.NOTHING`` is now vegan and faster on 32bit Python builds.
+
+  `#331 <https://github.com/python-attrs/attrs/issues/331>`_,
+  `#332 <https://github.com/python-attrs/attrs/issues/332>`_
+- The overhead of instantiating frozen dict classes is virtually eliminated.
+  `#336 <https://github.com/python-attrs/attrs/issues/336>`_
+- Generated ``__init__`` methods now have an ``__annotations__`` attribute derived from the types of the fields.
+
+  `#363 <https://github.com/python-attrs/attrs/issues/363>`_
+- We have restructured the documentation a bit to account for ``attrs``' growth in scope.
+  Instead of putting everything into the `examples <http://www.attrs.org/en/stable/examples.html>`_ page, we have started to extract narrative chapters.
+
+  So far, we've added chapters on `initialization <http://www.attrs.org/en/stable/init.html>`_ and `hashing <http://www.attrs.org/en/stable/hashing.html>`_.
+
+  Expect more to come!
+
+  `#369 <https://github.com/python-attrs/attrs/issues/369>`_,
+  `#370 <https://github.com/python-attrs/attrs/issues/370>`_
+
+
+----
+
+
+17.4.0 (2017-12-30)
+-------------------
+
+Backward-incompatible Changes
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+- The traversal of MROs when using multiple inheritance was backward:
+  If you defined a class ``C`` that subclasses ``A`` and ``B`` like ``C(A, B)``, ``attrs`` would have collected the attributes from ``B`` *before* those of ``A``.
+
+  This is now fixed and means that in classes that employ multiple inheritance, the output of ``__repr__`` and the order of positional arguments in ``__init__`` changes.
+  Due to the nature of this bug, a proper deprecation cycle was unfortunately impossible.
+
+  Generally speaking, it's advisable to prefer ``kwargs``-based initialization anyways – *especially* if you employ multiple inheritance and diamond-shaped hierarchies.
 
-   This changelog is managed by towncrier and is compiled at release time.
+  `#298 <https://github.com/python-attrs/attrs/issues/298>`_,
+  `#299 <https://github.com/python-attrs/attrs/issues/299>`_,
+  `#304 <https://github.com/python-attrs/attrs/issues/304>`_
+- The ``__repr__`` set by ``attrs``
+  no longer produces an ``AttributeError``
+  when the instance is missing some of the specified attributes
+  (either through deleting
+  or after using ``init=False`` on some attributes).
+
+  This can break code
+  that relied on ``repr(attr_cls_instance)`` raising ``AttributeError``
+  to check if any attr-specified members were unset.
+
+  If you were using this,
+  you can implement a custom method for checking this::
+
+      def has_unset_members(self):
+          for field in attr.fields(type(self)):
+              try:
+                  getattr(self, field.name)
+              except AttributeError:
+                  return True
+          return False
+
+  `#308 <https://github.com/python-attrs/attrs/issues/308>`_
+
+
+Deprecations
+^^^^^^^^^^^^
+
+- The ``attr.ib(convert=callable)`` option is now deprecated in favor of ``attr.ib(converter=callable)``.
+
+  This is done to achieve consistency with other noun-based arguments like *validator*.
+
+  *convert* will keep working until at least January 2019 while raising a ``DeprecationWarning``.
+
+  `#307 <https://github.com/python-attrs/attrs/issues/307>`_
+
 
-   See http://www.attrs.org/en/latest/contributing.html#changelog for details."""  # noqa
+Changes
+^^^^^^^
+
+- Generated ``__hash__`` methods now hash the class type along with the attribute values.
+  Until now the hashes of two classes with the same values were identical which was a bug.
+
+  The generated method is also *much* faster now.
+
+  `#261 <https://github.com/python-attrs/attrs/issues/261>`_,
+  `#295 <https://github.com/python-attrs/attrs/issues/295>`_,
+  `#296 <https://github.com/python-attrs/attrs/issues/296>`_
+- ``attr.ib``\ ’s ``metadata`` argument now defaults to a unique empty ``dict`` instance instead of sharing a common empty ``dict`` for all.
+  The singleton empty ``dict`` is still enforced.
+
+  `#280 <https://github.com/python-attrs/attrs/issues/280>`_
+- ``ctypes`` is optional now however if it's missing, a bare ``super()`` will not work in slotted classes.
+  This should only happen in special environments like Google App Engine.
 
-.. towncrier release notes start
+  `#284 <https://github.com/python-attrs/attrs/issues/284>`_,
+  `#286 <https://github.com/python-attrs/attrs/issues/286>`_
+- The attribute redefinition feature introduced in 17.3.0 now takes into account if an attribute is redefined via multiple inheritance.
+  In that case, the definition that is closer to the base of the class hierarchy wins.
+
+  `#285 <https://github.com/python-attrs/attrs/issues/285>`_,
+  `#287 <https://github.com/python-attrs/attrs/issues/287>`_
+- Subclasses of ``auto_attribs=True`` can be empty now.
+
+  `#291 <https://github.com/python-attrs/attrs/issues/291>`_,
+  `#292 <https://github.com/python-attrs/attrs/issues/292>`_
+- Equality tests are *much* faster now.
+
+  `#306 <https://github.com/python-attrs/attrs/issues/306>`_
+- All generated methods now have correct ``__module__``, ``__name__``, and (on Python 3) ``__qualname__`` attributes.
+
+  `#309 <https://github.com/python-attrs/attrs/issues/309>`_
+
+
+----
 
 
 17.3.0 (2017-11-08)
 -------------------
 
 Backward-incompatible Changes
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
@@ -28,17 +173,17 @@ Backward-incompatible Changes
   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 <https://github.com/python-attrs/attrs/issues/253>`_)
 
 
 Changes
 ^^^^^^^
 
-- ``super()`` and ``__class__`` now work on Python 3 when ``slots=True``.
+- ``super()`` and ``__class__`` now work with slotted classes on Python 3.
   (`#102 <https://github.com/python-attrs/attrs/issues/102>`_, `#226 <https://github.com/python-attrs/attrs/issues/226>`_, `#269 <https://github.com/python-attrs/attrs/issues/269>`_, `#270 <https://github.com/python-attrs/attrs/issues/270>`_, `#272 <https://github.com/python-attrs/attrs/issues/272>`_)
 - 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 <https://www.python.org/dev/peps/pep-0526/>`_). (`#151 <https://github.com/python-attrs/attrs/issues/151>`_, `#214 <https://github.com/python-attrs/attrs/issues/214>`_, `#215 <https://github.com/python-attrs/attrs/issues/215>`_, `#239 <https://github.com/python-attrs/attrs/issues/239>`_)
 - The combination of ``str=True`` and ``slots=True`` now works on Python 2.
   (`#198 <https://github.com/python-attrs/attrs/issues/198>`_)
@@ -191,23 +336,23 @@ Changes:
 ^^^^^^^^
 
 - Added ``attr.astuple()`` that -- similarly to ``attr.asdict()`` -- returns the instance as a tuple.
   `#77 <https://github.com/python-attrs/attrs/issues/77>`_
 - Converts now work with frozen classes.
   `#76 <https://github.com/python-attrs/attrs/issues/76>`_
 - Instantiation of ``attrs`` classes with converters is now significantly faster.
   `#80 <https://github.com/python-attrs/attrs/pull/80>`_
-- Pickling now works with ``__slots__`` classes.
+- Pickling now works with slotted classes.
   `#81 <https://github.com/python-attrs/attrs/issues/81>`_
-- ``attr.assoc()`` now works with ``__slots__`` classes.
+- ``attr.assoc()`` now works with slotted classes.
   `#84 <https://github.com/python-attrs/attrs/issues/84>`_
 - The tuple returned by ``attr.fields()`` now also allows to access the ``Attribute`` instances by name.
   Yes, we've subclassed ``tuple`` so you don't have to!
-  Therefore ``attr.fields(C).x`` is equivalent to the deprecated ``C.x`` and works with ``__slots__`` classes.
+  Therefore ``attr.fields(C).x`` is equivalent to the deprecated ``C.x`` and works with slotted classes.
   `#88 <https://github.com/python-attrs/attrs/issues/88>`_
 
 
 ----
 
 
 16.1.0 (2016-08-30)
 -------------------
@@ -266,17 +411,17 @@ Backward-incompatible Changes:
   Major Python packages like Django and Twisted dropped Python 2.6 a while ago already.
 
   Python 3.3 never had a significant user base and wasn't part of any distribution's LTS release.
 
 Changes:
 ^^^^^^^^
 
 - ``__slots__`` have arrived!
-  Classes now can automatically be `slots <https://docs.python.org/3/reference/datamodel.html#slots>`_-style (and save your precious memory) just by passing ``slots=True``.
+  Classes now can automatically be `slotted <https://docs.python.org/3/reference/datamodel.html#slots>`_-style (and save your precious memory) just by passing ``slots=True``.
   `#35 <https://github.com/python-attrs/attrs/issues/35>`_
 - Allow the case of initializing attributes that are set to ``init=False``.
   This allows for clean initializer parameter lists while being able to initialize attributes to default values.
   `#32 <https://github.com/python-attrs/attrs/issues/32>`_
 - ``attr.asdict()`` can now produce arbitrary mappings instead of Python ``dict``\ s when provided with a ``dict_factory`` argument.
   `#40 <https://github.com/python-attrs/attrs/issues/40>`_
 - Multiple performance improvements.
 
--- a/testing/web-platform/tests/tools/third_party/attrs/MANIFEST.in
+++ b/testing/web-platform/tests/tools/third_party/attrs/MANIFEST.in
@@ -1,19 +1,22 @@
-include LICENSE *.rst *.toml
+include LICENSE *.rst *.toml .readthedocs.yml
 
 # Don't package GitHub-specific files.
-exclude *.md .travis.yml
+exclude .github/*.md .travis.yml
 
 # Tests
-include tox.ini .coveragerc conftest.py dev-requirements.txt docs-requirements.txt
+include tox.ini .coveragerc conftest.py
 recursive-include tests *.py
+recursive-include .github *.rst
 
 # Documentation
 include docs/Makefile docs/docutils.conf
 recursive-include docs *.png
 recursive-include docs *.svg
 recursive-include docs *.py
 recursive-include docs *.rst
 prune docs/_build
 
-# Changelog news fragments -- is empty on releases.
-prune changelog.d
+# Just to keep check-manifest happy; on releases those files are gone.
+# Last rule wins!
+exclude changelog.d/*.rst
+include changelog.d/towncrier_template.rst
--- a/testing/web-platform/tests/tools/third_party/attrs/README.rst
+++ b/testing/web-platform/tests/tools/third_party/attrs/README.rst
@@ -27,43 +27,43 @@ Its main goal is to help you to write **
 
 For that, it gives you a class decorator and a way to declaratively define the attributes on that class:
 
 .. -code-begin-
 
 .. code-block:: pycon
 
    >>> import attr
-   
+
    >>> @attr.s
    ... class SomeClass(object):
    ...     a_number = attr.ib(default=42)
    ...     list_of_numbers = attr.ib(default=attr.Factory(list))
    ...
    ...     def hard_math(self, another_number):
    ...         return self.a_number + sum(self.list_of_numbers) * another_number
-   
-   
+
+
    >>> sc = SomeClass(1, [1, 2, 3])
    >>> sc
    SomeClass(a_number=1, list_of_numbers=[1, 2, 3])
-   
+
    >>> sc.hard_math(3)
    19
    >>> sc == SomeClass(1, [1, 2, 3])
    True
    >>> sc != SomeClass(2, [3, 2, 1])
    True
-   
+
    >>> attr.asdict(sc)
    {'a_number': 1, 'list_of_numbers': [1, 2, 3]}
-   
+
    >>> SomeClass()
    SomeClass(a_number=42, list_of_numbers=[])
-   
+
    >>> C = attr.make_class("C", ["a", "b"])
    >>> C("foo", "bar")
    C(a='foo', b='bar')
 
 
 After *declaring* your attributes ``attrs`` gives you:
 
 - a concise and explicit overview of the class's attributes,
@@ -121,9 +121,12 @@ Project Information
 ===================
 
 ``attrs`` is released under the `MIT <https://choosealicense.com/licenses/mit/>`_ license,
 its documentation lives at `Read the Docs <http://www.attrs.org/>`_,
 the code on `GitHub <https://github.com/python-attrs/attrs>`_,
 and the latest release on `PyPI <https://pypi.org/project/attrs/>`_.
 It’s rigorously tested on Python 2.7, 3.4+, and PyPy.
 
-If you'd like to contribute you're most welcome and we've written `a little guide <http://www.attrs.org/en/latest/contributing.html>`_ to get you started!
+We collect information on **third-party extensions** in our `wiki <https://github.com/python-attrs/attrs/wiki/Extensions-to-attrs>`_.
+Feel free to browse and add your own!
+
+If you'd like to contribute to ``attrs`` you're most welcome and we've written `a little guide <http://www.attrs.org/en/latest/contributing.html>`_ to get you started!
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/tools/third_party/attrs/changelog.d/178.change.rst
@@ -0,0 +1,1 @@
+``attr.ib(factory=f)`` is now syntactic sugar for the common case of ``attr.ib(default=attr.Factory(f))``.
deleted file mode 100644
--- a/testing/web-platform/tests/tools/third_party/attrs/changelog.d/261.change.rst
+++ /dev/null
@@ -1,4 +0,0 @@
-Generated ``__hash__`` methods now hash the class type along with the attribute values.
-Until now the hashes of two classes with the same values were identical which was a bug.
-
-The generated method is also *much* faster now.
deleted file mode 100644
--- a/testing/web-platform/tests/tools/third_party/attrs/changelog.d/284.change.rst
+++ /dev/null
@@ -1,2 +0,0 @@
-``ctypes`` is optional now however if it's missing, a bare ``super()`` will not work in slots classes.
-This should only happen in special environments like Google App Engine.
deleted file mode 100644
--- a/testing/web-platform/tests/tools/third_party/attrs/changelog.d/285.change.rst
+++ /dev/null
@@ -1,2 +0,0 @@
-The attribute redefinition feature introduced in 17.3.0 now takes into account if an attribute is redefined via multiple inheritance.
-In that case, the definition that is closer to the base of the class hierarchy wins.
deleted file mode 100644
--- a/testing/web-platform/tests/tools/third_party/attrs/changelog.d/286.change.rst
+++ /dev/null
@@ -1,2 +0,0 @@
-``ctypes`` is optional now however if it's missing, a bare ``super()`` will not work in slots classes.
-This should only happen in special environments like Google App Engine.
deleted file mode 100644
--- a/testing/web-platform/tests/tools/third_party/attrs/changelog.d/287.change.rst
+++ /dev/null
@@ -1,2 +0,0 @@
-The attribute redefinition feature introduced in 17.3.0 now takes into account if an attribute is redefined via multiple inheritance.
-In that case, the definition that is closer to the base of the class hierarchy wins.
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/tools/third_party/attrs/changelog.d/290.change.rst
@@ -0,0 +1,1 @@
+Added ``attr.field_dict()`` to return an ordered dictionary of ``attrs`` attributes for a class, whose keys are the attribute names.
deleted file mode 100644
--- a/testing/web-platform/tests/tools/third_party/attrs/changelog.d/291.change.rst
+++ /dev/null
@@ -1,1 +0,0 @@
-Subclasses of ``auto_attribs=True`` can be empty now.
deleted file mode 100644
--- a/testing/web-platform/tests/tools/third_party/attrs/changelog.d/292.change.rst
+++ /dev/null
@@ -1,1 +0,0 @@
-Subclasses of ``auto_attribs=True`` can be empty now.
deleted file mode 100644
--- a/testing/web-platform/tests/tools/third_party/attrs/changelog.d/295.change.rst
+++ /dev/null
@@ -1,4 +0,0 @@
-Generated ``__hash__`` methods now hash the class type along with the attribute values.
-Until now the hashes of two classes with the same values were identical which was a bug.
-
-The generated method is also *much* faster now.
deleted file mode 100644
--- a/testing/web-platform/tests/tools/third_party/attrs/changelog.d/296.change.rst
+++ /dev/null
@@ -1,4 +0,0 @@
-Generated ``__hash__`` methods now hash the class type along with the attribute values.
-Until now the hashes of two classes with the same values were identical which was a bug.
-
-The generated method is also *much* faster now.
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/tools/third_party/attrs/changelog.d/300.change.rst
@@ -0,0 +1,3 @@
+The order of attributes that are passed into ``attr.make_class()`` or the ``these`` argument of ``@attr.s()`` is now retained if the dictionary is ordered (i.e. ``dict`` on Python 3.6 and later, ``collections.OrderedDict`` otherwise).
+
+Before, the order was always determined by the order in which the attributes have been defined which may not be desirable when creating classes programatically.
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/tools/third_party/attrs/changelog.d/311.change.rst
@@ -0,0 +1,1 @@
+In slotted classes, ``__getstate__`` and ``__setstate__`` now ignore the ``__weakref__`` attribute.
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/tools/third_party/attrs/changelog.d/321.change.rst
@@ -0,0 +1,4 @@
+Setting the cell type is now completely best effort.
+This fixes ``attrs`` on Jython.
+
+We cannot make any guarantees regarding Jython though, because our test suite cannot run due to dependency incompatabilities.
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/tools/third_party/attrs/changelog.d/322.change.rst
@@ -0,0 +1,1 @@
+If ``attr.s`` is passed a *these* argument, it will not attempt to remove attributes with the same name from the class body anymore.
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/tools/third_party/attrs/changelog.d/323.change.rst
@@ -0,0 +1,1 @@
+If ``attr.s`` is passed a *these* argument, it will not attempt to remove attributes with the same name from the class body anymore.
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/tools/third_party/attrs/changelog.d/326.change.rst
@@ -0,0 +1,1 @@
+In slotted classes, ``__getstate__`` and ``__setstate__`` now ignore the ``__weakref__`` attribute.
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/tools/third_party/attrs/changelog.d/331.change.rst
@@ -0,0 +1,1 @@
+The hash of ``attr.NOTHING`` is now vegan and faster on 32bit Python builds.
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/tools/third_party/attrs/changelog.d/332.change.rst
@@ -0,0 +1,1 @@
+The hash of ``attr.NOTHING`` is now vegan and faster on 32bit Python builds.
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/tools/third_party/attrs/changelog.d/334.change.rst
@@ -0,0 +1,4 @@
+Setting the cell type is now completely best effort.
+This fixes ``attrs`` on Jython.
+
+We cannot make any guarantees regarding Jython though, because our test suite cannot run due to dependency incompatabilities.
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/tools/third_party/attrs/changelog.d/336.change.rst
@@ -0,0 +1,1 @@
+The overhead of instantiating frozen dict classes is virtually eliminated.
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/tools/third_party/attrs/changelog.d/339.change.rst
@@ -0,0 +1,3 @@
+The order of attributes that are passed into ``attr.make_class()`` or the ``these`` argument of ``@attr.s()`` is now retained if the dictionary is ordered (i.e. ``dict`` on Python 3.6 and later, ``collections.OrderedDict`` otherwise).
+
+Before, the order was always determined by the order in which the attributes have been defined which may not be desirable when creating classes programatically.
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/tools/third_party/attrs/changelog.d/343.change.rst
@@ -0,0 +1,3 @@
+The order of attributes that are passed into ``attr.make_class()`` or the ``these`` argument of ``@attr.s()`` is now retained if the dictionary is ordered (i.e. ``dict`` on Python 3.6 and later, ``collections.OrderedDict`` otherwise).
+
+Before, the order was always determined by the order in which the attributes have been defined which may not be desirable when creating classes programatically.
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/tools/third_party/attrs/changelog.d/349.change.rst
@@ -0,0 +1,1 @@
+Added ``attr.field_dict()`` to return an ordered dictionary of ``attrs`` attributes for a class, whose keys are the attribute names.
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/tools/third_party/attrs/changelog.d/356.change.rst
@@ -0,0 +1,1 @@
+``attr.ib(factory=f)`` is now syntactic sugar for the common case of ``attr.ib(default=attr.Factory(f))``.
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/tools/third_party/attrs/changelog.d/363.change.rst
@@ -0,0 +1,1 @@
+Generated ``__init__`` methods now have an ``__annotations__`` attribute derived from the types of the fields.
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/tools/third_party/attrs/changelog.d/369.change.rst
@@ -0,0 +1,6 @@
+We have restructured the documentation a bit to account for ``attrs``' growth in scope.
+Instead of putting everything into the `examples <http://www.attrs.org/en/stable/examples.html>`_ page, we have started to extract narrative chapters.
+
+So far, we've added chapters on `initialization <http://www.attrs.org/en/stable/init.html>`_ and `hashing <http://www.attrs.org/en/stable/hashing.html>`_.
+
+Expect more to come!
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/tools/third_party/attrs/changelog.d/370.change.rst
@@ -0,0 +1,6 @@
+We have restructured the documentation a bit to account for ``attrs``' growth in scope.
+Instead of putting everything into the `examples <http://www.attrs.org/en/stable/examples.html>`_ page, we have started to extract narrative chapters.
+
+So far, we've added chapters on `initialization <http://www.attrs.org/en/stable/init.html>`_ and `hashing <http://www.attrs.org/en/stable/hashing.html>`_.
+
+Expect more to come!
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/tools/third_party/attrs/changelog.d/95.change.rst
@@ -0,0 +1,1 @@
+``x=X(); x.cycle = x; repr(x)`` will no longer raise a ``RecursionError``, and will instead show as ``X(x=...)``.
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/tools/third_party/attrs/changelog.d/towncrier_template.rst
@@ -0,0 +1,35 @@
+{% for section, _ in sections.items() %}
+{% set underline = underlines[0] %}{% if section %}{{section}}
+{{ underline * section|length }}{% set underline = underlines[1] %}
+
+{% endif %}
+
+{% if sections[section] %}
+{% for category, val in definitions.items() if category in sections[section]%}
+{{ definitions[category]['name'] }}
+{{ underline * definitions[category]['name']|length }}
+
+{% if definitions[category]['showcontent'] %}
+{% for text, values in sections[section][category].items() %}
+- {{ text }}
+  {{ values|join(',\n  ') }}
+{% endfor %}
+
+{% else %}
+- {{ sections[section][category]['']|join(', ') }}
+
+{% endif %}
+{% if sections[section][category]|length == 0 %}
+No significant changes.
+
+{% else %}
+{% endif %}
+
+{% endfor %}
+{% else %}
+No significant changes.
+
+
+{% endif %}
+{% endfor %}
+----
deleted file mode 100644
--- a/testing/web-platform/tests/tools/third_party/attrs/dev-requirements.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-coverage
-hypothesis
-pympler
-pytest
-six
-zope.interface
deleted file mode 100644
--- a/testing/web-platform/tests/tools/third_party/attrs/docs-requirements.txt
+++ /dev/null
@@ -1,3 +0,0 @@
--e .
-sphinx
-zope.interface
--- a/testing/web-platform/tests/tools/third_party/attrs/docs/api.rst
+++ b/testing/web-platform/tests/tools/third_party/attrs/docs/api.rst
@@ -13,17 +13,17 @@ API Reference
 
 What follows is the API explanation, if you'd like a more hands-on introduction, have a look at :doc:`examples`.
 
 
 
 Core
 ----
 
-.. autofunction:: attr.s(these=None, repr_ns=None, repr=True, cmp=True, hash=None, init=True, slots=False, frozen=False, str=False)
+.. autofunction:: attr.s(these=None, repr_ns=None, repr=True, cmp=True, hash=None, init=True, slots=False, frozen=False, str=False, auto_attribs=False)
 
    .. note::
 
       ``attrs`` also comes with a serious business alias ``attr.attrs``.
 
    For example:
 
    .. doctest::
@@ -85,17 +85,17 @@ Core
 
    .. doctest::
 
       >>> import attr
       >>> @attr.s
       ... class C(object):
       ...     x = attr.ib()
       >>> attr.fields(C).x
-      Attribute(name='x', default=NOTHING, validator=None, repr=True, cmp=True, hash=None, init=True, convert=None, metadata=mappingproxy({}), type=None)
+      Attribute(name='x', default=NOTHING, validator=None, repr=True, cmp=True, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None)
 
 
 .. autofunction:: attr.make_class
 
    This is handy if you want to programmatically create classes.
 
    For example:
 
@@ -135,66 +135,17 @@ Core
 .. autoexception:: attr.exceptions.DefaultAlreadySetError
 .. autoexception:: attr.exceptions.UnannotatedAttributeError
 
    For example::
 
        @attr.s(auto_attribs=True)
        class C:
            x: int
-           y = attr.ib()
-
-
-Influencing Initialization
-++++++++++++++++++++++++++
-
-Generally speaking, it's best to keep logic out of your ``__init__``.
-The moment you need a finer control over how your class is instantiated, it's usually best to use a classmethod factory or to apply the `builder pattern <https://en.wikipedia.org/wiki/Builder_pattern>`_.
-
-However, sometimes you need to do that one quick thing after your class is initialized.
-And for that ``attrs`` offers the ``__attrs_post_init__`` hook that is automatically detected and run after ``attrs`` is done initializing your instance:
-
-.. doctest::
-
-   >>> @attr.s
-   ... class C(object):
-   ...     x = attr.ib()
-   ...     y = attr.ib(init=False)
-   ...     def __attrs_post_init__(self):
-   ...         self.y = self.x + 1
-   >>> C(1)
-   C(x=1, y=2)
-
-Please note that you can't directly set attributes on frozen classes:
-
-.. doctest::
-
-   >>> @attr.s(frozen=True)
-   ... class FrozenBroken(object):
-   ...     x = attr.ib()
-   ...     y = attr.ib(init=False)
-   ...     def __attrs_post_init__(self):
-   ...         self.y = self.x + 1
-   >>> FrozenBroken(1)
-   Traceback (most recent call last):
-      ...
-   attr.exceptions.FrozenInstanceError: can't set attribute
-
-If you need to set attributes on a frozen class, you'll have to resort to the :ref:`same trick <how-frozen>` as ``attrs`` and use :meth:`object.__setattr__`:
-
-.. doctest::
-
-   >>> @attr.s(frozen=True)
-   ... class Frozen(object):
-   ...     x = attr.ib()
-   ...     y = attr.ib(init=False)
-   ...     def __attrs_post_init__(self):
-   ...         object.__setattr__(self, "y", self.x + 1)
-   >>> Frozen(1)
-   Frozen(x=1, y=2)
+           y = attr.ib()  # <- ERROR!
 
 
 .. _helpers:
 
 Helpers
 -------
 
 ``attrs`` comes with a bunch of helper methods that make working with it easier:
@@ -205,22 +156,39 @@ Helpers
 
    .. doctest::
 
       >>> @attr.s
       ... class C(object):
       ...     x = attr.ib()
       ...     y = attr.ib()
       >>> attr.fields(C)
-      (Attribute(name='x', default=NOTHING, validator=None, repr=True, cmp=True, hash=None, init=True, convert=None, metadata=mappingproxy({}), type=None), Attribute(name='y', default=NOTHING, validator=None, repr=True, cmp=True, hash=None, init=True, convert=None, metadata=mappingproxy({}), type=None))
+      (Attribute(name='x', default=NOTHING, validator=None, repr=True, cmp=True, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None), Attribute(name='y', default=NOTHING, validator=None, repr=True, cmp=True, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None))
       >>> attr.fields(C)[1]
-      Attribute(name='y', default=NOTHING, validator=None, repr=True, cmp=True, hash=None, init=True, convert=None, metadata=mappingproxy({}), type=None)
+      Attribute(name='y', default=NOTHING, validator=None, repr=True, cmp=True, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None)
       >>> attr.fields(C).y is attr.fields(C)[1]
       True
 
+.. autofunction:: attr.fields_dict
+
+   For example:
+
+   .. doctest::
+
+      >>> @attr.s
+      ... class C(object):
+      ...     x = attr.ib()
+      ...     y = attr.ib()
+      >>> attr.fields_dict(C)
+      {'x': Attribute(name='x', default=NOTHING, validator=None, repr=True, cmp=True, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None), 'y': Attribute(name='y', default=NOTHING, validator=None, repr=True, cmp=True, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None)}
+      >>> attr.fields_dict(C)['y']
+      Attribute(name='y', default=NOTHING, validator=None, repr=True, cmp=True, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None)
+      >>> attr.fields_dict(C)['y'] is attr.fields(C).y
+      True
+
 
 .. autofunction:: attr.has
 
    For example:
 
    .. doctest::
 
       >>> @attr.s
@@ -407,17 +375,17 @@ Converters
 .. autofunction:: attr.converters.optional
 
    For example:
 
    .. doctest::
 
       >>> @attr.s
       ... class C(object):
-      ...     x = attr.ib(convert=attr.converters.optional(int))
+      ...     x = attr.ib(converter=attr.converters.optional(int))
       >>> C(None)
       C(x=None)
       >>> C(42)
       C(x=42)
 
 
 Deprecated APIs
 ---------------
--- a/testing/web-platform/tests/tools/third_party/attrs/docs/contributing.rst
+++ b/testing/web-platform/tests/tools/third_party/attrs/docs/contributing.rst
@@ -1,5 +1,5 @@
 .. _contributing:
 
-.. include:: ../CONTRIBUTING.rst
+.. include:: ../.github/CONTRIBUTING.rst
 
-.. include:: ../CODE_OF_CONDUCT.rst
+.. include:: ../.github/CODE_OF_CONDUCT.rst
--- a/testing/web-platform/tests/tools/third_party/attrs/docs/examples.rst
+++ b/testing/web-platform/tests/tools/third_party/attrs/docs/examples.rst
@@ -110,17 +110,17 @@ This is useful in times when you want to
    ... class A(object):
    ...     a = attr.ib()
    ...     def get_a(self):
    ...         return self.a
    >>> @attr.s
    ... class B(object):
    ...     b = attr.ib()
    >>> @attr.s
-   ... class C(B, A):
+   ... class C(A, B):
    ...     c = attr.ib()
    >>> i = C(1, 2, 3)
    >>> i
    C(a=1, b=2, c=3)
    >>> i == C(1, 2, 3)
    True
    >>> i.get_a()
    1
@@ -213,18 +213,16 @@ Other times, all you want is a tuple and
    ...    c.execute("INSERT INTO foo VALUES (?, ?)", attr.astuple(foo)) #doctest: +ELLIPSIS
    ...    foo2 = Foo(*c.execute("SELECT x, y FROM foo").fetchone())
    <sqlite3.Cursor object at ...>
    <sqlite3.Cursor object at ...>
    >>> foo == foo2
    True
 
 
-
-
 Defaults
 --------
 
 Sometimes you want to have default values for your initializer.
 And sometimes you even want mutable objects as default values (ever used accidentally ``def f(arg=[])``?).
 ``attrs`` has you covered in both cases:
 
 .. doctest::
@@ -277,37 +275,36 @@ The method receives the partially initia
    ...     y = attr.ib()
    ...     @y.default
    ...     def name_does_not_matter(self):
    ...         return self.x + 1
    >>> C()
    C(x=1, y=2)
 
 
+And since the case of ``attr.ib(default=attr.Factory(f))`` is so common, ``attrs`` also comes with syntactic sugar for it:
+
+.. doctest::
+
+   >>> @attr.s
+   ... class C(object):
+   ...     x = attr.ib(factory=list)
+   >>> C()
+   C(x=[])
+
 .. _examples_validators:
 
 Validators
 ----------
 
 Although your initializers should do as little as possible (ideally: just initialize your instance according to the arguments!), it can come in handy to do some kind of validation on the arguments.
 
 ``attrs`` offers two ways to define validators for each attribute and it's up to you to choose which one suites better your style and project.
 
-
-Decorator
-~~~~~~~~~
-
-The more straightforward way is by using the attribute's ``validator`` method as a decorator.
-The method has to accept three arguments:
-
-#. the *instance* that's being validated (aka ``self``),
-#. the *attribute* that it's validating, and finally
-#. the *value* that is passed for it.
-
-If the value does not pass the validator's standards, it just raises an appropriate exception.
+You can use a decorator:
 
 .. doctest::
 
    >>> @attr.s
    ... class C(object):
    ...     x = attr.ib()
    ...     @x.validator
    ...     def check(self, attribute, value):
@@ -315,25 +312,17 @@ If the value does not pass the validator
    ...             raise ValueError("x must be smaller or equal to 42")
    >>> C(42)
    C(x=42)
    >>> C(43)
    Traceback (most recent call last):
       ...
    ValueError: x must be smaller or equal to 42
 
-
-Callables
-~~~~~~~~~
-
-If you want to re-use your validators, you should have a look at the ``validator`` argument to :func:`attr.ib()`.
-
-It takes either a callable or a list of callables (usually functions) and treats them as validators that receive the same arguments as with the decorator approach.
-
-Since the validators runs *after* the instance is initialized, you can refer to other attributes while validating:
+ ...or a callable...
 
 .. doctest::
 
    >>> def x_smaller_than_y(instance, attribute, value):
    ...     if value >= instance.y:
    ...         raise ValueError("'x' has to be smaller than 'y'!")
    >>> @attr.s
    ... class C(object):
@@ -342,109 +331,72 @@ Since the validators runs *after* the in
    ...     y = attr.ib()
    >>> C(x=3, y=4)
    C(x=3, y=4)
    >>> C(x=4, y=3)
    Traceback (most recent call last):
       ...
    ValueError: 'x' has to be smaller than 'y'!
 
-This example also shows of some syntactic sugar for using the :func:`attr.validators.and_` validator: if you pass a list, all validators have to pass.
-
-``attrs`` won't intercept your changes to those attributes but you can always call :func:`attr.validate` on any instance to verify that it's still valid:
+...or both at once:
 
 .. doctest::
 
-   >>> i = C(4, 5)
-   >>> i.x = 5  # works, no magic here
-   >>> attr.validate(i)
+   >>> @attr.s
+   ... class C(object):
+   ...     x = attr.ib(validator=attr.validators.instance_of(int))
+   ...     @x.validator
+   ...     def fits_byte(self, attribute, value):
+   ...         if not 0 <= value < 256:
+   ...             raise ValueError("value out of bounds")
+   >>> C(128)
+   C(x=128)
+   >>> C("128")
    Traceback (most recent call last):
       ...
-   ValueError: 'x' has to be smaller than 'y'!
+   TypeError: ("'x' must be <class 'int'> (got '128' that is a <class 'str'>).", Attribute(name='x', default=NOTHING, validator=[<instance_of validator for type <class 'int'>>, <function fits_byte at 0x10fd7a0d0>], repr=True, cmp=True, hash=True, init=True, metadata=mappingproxy({}), type=None, converter=one), <class 'int'>, '128')
+   >>> C(256)
+   Traceback (most recent call last):
+      ...
+   ValueError: value out of bounds
+
 
 ``attrs`` ships with a bunch of validators, make sure to :ref:`check them out <api_validators>` before writing your own:
 
 .. doctest::
 
    >>> @attr.s
    ... class C(object):
    ...     x = attr.ib(validator=attr.validators.instance_of(int))
    >>> C(42)
    C(x=42)
    >>> C("42")
    Traceback (most recent call last):
       ...
    TypeError: ("'x' must be <type 'int'> (got '42' that is a <type 'str'>).", Attribute(name='x', default=NOTHING, factory=NOTHING, validator=<instance_of validator for type <type 'int'>>, type=None), <type 'int'>, '42')
 
-Of course you can mix and match the two approaches at your convenience:
-
-.. doctest::
-
-   >>> @attr.s
-   ... class C(object):
-   ...     x = attr.ib(validator=attr.validators.instance_of(int))
-   ...     @x.validator
-   ...     def fits_byte(self, attribute, value):
-   ...         if not 0 < value < 256:
-   ...             raise ValueError("value out of bounds")
-   >>> C(128)
-   C(x=128)
-   >>> C("128")
-   Traceback (most recent call last):
-      ...
-   TypeError: ("'x' must be <class 'int'> (got '128' that is a <class 'str'>).", Attribute(name='x', default=NOTHING, validator=[<instance_of validator for type <class 'int'>>, <function fits_byte at 0x10fd7a0d0>], repr=True, cmp=True, hash=True, init=True, convert=None, metadata=mappingproxy({}), type=None), <class 'int'>, '128')
-   >>> C(256)
-   Traceback (most recent call last):
-      ...
-   ValueError: value out of bounds
-
-And finally you can disable validators globally:
-
-   >>> attr.set_run_validators(False)
-   >>> C("128")
-   C(x='128')
-   >>> attr.set_run_validators(True)
-   >>> C("128")
-   Traceback (most recent call last):
-      ...
-   TypeError: ("'x' must be <class 'int'> (got '128' that is a <class 'str'>).", Attribute(name='x', default=NOTHING, validator=[<instance_of validator for type <class 'int'>>, <function fits_byte at 0x10fd7a0d0>], repr=True, cmp=True, hash=True, init=True, convert=None, metadata=mappingproxy({}), type=None), <class 'int'>, '128')
+Check out :ref:`validators` for more details.
 
 
 Conversion
 ----------
 
-Attributes can have a ``convert`` function specified, which will be called with the attribute's passed-in value to get a new value to use.
+Attributes can have a ``converter`` function specified, which will be called with the attribute's passed-in value to get a new value to use.
 This can be useful for doing type-conversions on values that you don't want to force your callers to do.
 
 .. doctest::
 
     >>> @attr.s
     ... class C(object):
-    ...     x = attr.ib(convert=int)
+    ...     x = attr.ib(converter=int)
     >>> o = C("1")
     >>> o.x
     1
 
-Converters are run *before* validators, so you can use validators to check the final form of the value.
-
-.. doctest::
-
-    >>> def validate_x(instance, attribute, value):
-    ...     if value < 0:
-    ...         raise ValueError("x must be be at least 0.")
-    >>> @attr.s
-    ... class C(object):
-    ...     x = attr.ib(convert=int, validator=validate_x)
-    >>> o = C("0")
-    >>> o.x
-    0
-    >>> C("-1")
-    Traceback (most recent call last):
-        ...
-    ValueError: x must be be at least 0.
+Check out :ref:`converters` for more details.
 
 
 .. _metadata:
 
 Metadata
 --------
 
 All ``attrs`` attributes may include arbitrary metadata in the form of a read-only dictionary.
@@ -504,96 +456,40 @@ If you don't mind annotating *all* attri
    <class 'str'>
    >>> attr.fields(AutoC).bar.type
    typing.Any
    >>> AutoC()
    AutoC(l=[], x=1, foo='every attrib needs a type if auto_attribs=True', bar=None)
    >>> AutoC.cls_var
    5
 
+The generated ``__init__`` method will have an attribute called ``__annotations__`` that contains this type information.
 
 .. warning::
 
    ``attrs`` itself doesn't have any features that work on top of type metadata *yet*.
    However it's useful for writing your own validators or serialization frameworks.
 
 
 .. _slots:
 
 Slots
 -----
 
-By default, instances of classes have a dictionary for attribute storage.
-This wastes space for objects having very few data attributes.
-The space consumption can become significant when creating large numbers of instances.
-
-Normal Python classes can avoid using a separate dictionary for each instance of a class by `defining <https://docs.python.org/3/reference/datamodel.html#slots>`_ ``__slots__``.
-For ``attrs`` classes it's enough to set ``slots=True``:
+:term:`Slotted classes` have a bunch of advantages on CPython.
+Defining ``__slots__`` by hand is tedious, in ``attrs`` it's just a matter of passing ``slots=True``:
 
 .. doctest::
 
    >>> @attr.s(slots=True)
    ... class Coordinates(object):
    ...     x = attr.ib()
    ...     y = attr.ib()
 
 
-.. note::
-
-    ``attrs`` slot classes can inherit from other classes just like non-slot classes, but some of the benefits of slot classes are lost if you do that.
-    If you must inherit from other classes, try to inherit only from other slot classes.
-
-Slot classes are a little different than ordinary, dictionary-backed classes:
-
-- Assigning to a non-existent attribute of an instance will result in an ``AttributeError`` being raised.
-  Depending on your needs, this might be a good thing since it will let you catch typos early.
-  This is not the case if your class inherits from any non-slot classes.
-
-  .. doctest::
-
-     >>> @attr.s(slots=True)
-     ... class Coordinates(object):
-     ...     x = attr.ib()
-     ...     y = attr.ib()
-     ...
-     >>> c = Coordinates(x=1, y=2)
-     >>> c.z = 3
-     Traceback (most recent call last):
-         ...
-     AttributeError: 'Coordinates' object has no attribute 'z'
-
-- Since non-slot classes cannot be turned into slot classes after they have been created, ``attr.s(slots=True)`` will *replace* the class it is applied to with a copy.
-  In almost all cases this isn't a problem, but we mention it for the sake of completeness.
-
-  * One notable problem is that certain metaclass features like ``__init_subclass__`` do not work with slot classes.
-
-- Using :mod:`pickle` with slot classes requires pickle protocol 2 or greater.
-  Python 2 uses protocol 0 by default so the protocol needs to be specified.
-  Python 3 uses protocol 3 by default.
-  You can support protocol 0 and 1 by implementing :meth:`__getstate__ <object.__getstate__>` and :meth:`__setstate__ <object.__setstate__>` methods yourself.
-  Those methods are created for frozen slot classes because they won't pickle otherwise.
-  `Think twice <https://www.youtube.com/watch?v=7KnfGDajDQw>`_ before using :mod:`pickle` though.
-
-- As always with slot classes, you must specify a ``__weakref__`` slot if you wish for the class to be weak-referenceable.
-  Here's how it looks using ``attrs``:
-
-  .. doctest::
-
-    >>> import weakref
-    >>> @attr.s(slots=True)
-    ... class C(object):
-    ...     __weakref__ = attr.ib(init=False, hash=False, repr=False, cmp=False)
-    ...     x = attr.ib()
-    >>> c = C(1)
-    >>> weakref.ref(c)
-    <weakref at 0x...; to 'C' at 0x...>
-
-All in all, setting ``slots=True`` is usually a very good idea.
-
-
 Immutability
 ------------
 
 Sometimes you have instances that shouldn't be changed after instantiation.
 Immutability is especially popular in functional programming and is generally a very good thing.
 If you'd like to enforce it, ``attrs`` will try to help:
 
 .. doctest::
--- a/testing/web-platform/tests/tools/third_party/attrs/docs/extending.rst
+++ b/testing/web-platform/tests/tools/third_party/attrs/docs/extending.rst
@@ -12,17 +12,17 @@ So it is fairly simple to build your own
 
    >>> import attr
    >>> def print_attrs(cls):
    ...     print(cls.__attrs_attrs__)
    >>> @print_attrs
    ... @attr.s
    ... class C(object):
    ...     a = attr.ib()
-   (Attribute(name='a', default=NOTHING, validator=None, repr=True, cmp=True, hash=None, init=True, convert=None, metadata=mappingproxy({}), type=None),)
+   (Attribute(name='a', default=NOTHING, validator=None, repr=True, cmp=True, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None),)
 
 
 .. warning::
 
    The :func:`attr.s` decorator **must** be applied first because it puts ``__attrs_attrs__`` in place!
    That means that is has to come *after* your decorator because::
 
       @a
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/tools/third_party/attrs/docs/glossary.rst
@@ -0,0 +1,63 @@
+Glossary
+========
+
+.. glossary::
+
+   dict classes
+      A regular class whose attributes are stored in the ``__dict__`` attribute of every single instance.
+      This is quite wasteful especially for objects with very few data attributes and the space consumption can become significant when creating large numbers of instances.
+
+      This is the type of class you get by default both with and without ``attrs``.
+
+   slotted classes
+      A class that has no ``__dict__`` attribute and `defines <https://docs.python.org/3/reference/datamodel.html#slots>`_ its attributes in a ``__slots__`` attribute instead.
+      In ``attrs``, they are created by passing ``slots=True`` to ``@attr.s``.
+
+      Their main advantage is that they use less memory on CPython [#pypy]_.
+
+      However they also come with a bunch of possibly surprising gotchas:
+
+      - Slotted classes don't allow for any other attribute to be set except for those defined in one of the class' hierarchies ``__slots__``:
+
+        .. doctest::
+
+          >>> import attr
+          >>> @attr.s(slots=True)
+          ... class Coordinates(object):
+          ...     x = attr.ib()
+          ...     y = attr.ib()
+          ...
+          >>> c = Coordinates(x=1, y=2)
+          >>> c.z = 3
+          Traceback (most recent call last):
+              ...
+          AttributeError: 'Coordinates' object has no attribute 'z'
+
+      - Slotted classes can inherit from other classes just like non-slotted classes, but some of the benefits of slotted classes are lost if you do that.
+        If you must inherit from other classes, try to inherit only from other slot classes.
+
+      - Using :mod:`pickle` with slotted classes requires pickle protocol 2 or greater.
+        Python 2 uses protocol 0 by default so the protocol needs to be specified.
+        Python 3 uses protocol 3 by default.
+        You can support protocol 0 and 1 by implementing :meth:`__getstate__ <object.__getstate__>` and :meth:`__setstate__ <object.__setstate__>` methods yourself.
+        Those methods are created for frozen slotted classes because they won't pickle otherwise.
+        `Think twice <https://www.youtube.com/watch?v=7KnfGDajDQw>`_ before using :mod:`pickle` though.
+
+      - As always with slotted classes, you must specify a ``__weakref__`` slot if you wish for the class to be weak-referenceable.
+        Here's how it looks using ``attrs``:
+
+        .. doctest::
+
+          >>> import weakref
+          >>> @attr.s(slots=True)
+          ... class C(object):
+          ...     __weakref__ = attr.ib(init=False, hash=False, repr=False, cmp=False)
+          ...     x = attr.ib()
+          >>> c = C(1)
+          >>> weakref.ref(c)
+          <weakref at 0x...; to 'C' at 0x...>
+      - Since it's currently impossible to make a class slotted after it's created, ``attrs`` has to replace your class with a new one.
+        While it tries to do that as graciously as possible, certain metaclass features like ``__init_subclass__`` do not work with slotted classes.
+
+
+.. [#pypy] On PyPy, there is no memory advantage in using slotted classes.
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/tools/third_party/attrs/docs/hashing.rst
@@ -0,0 +1,56 @@
+Hashing
+=======
+
+.. warning::
+
+   The overarching theme is to never set the ``@attr.s(hash=X)`` parameter yourself.
+   Leave it at ``None`` which means that ``attrs`` will do the right thing for you, depending on the other parameters:
+
+   - If you want to make objects hashable by value: use ``@attr.s(frozen=True)``.
+   - If you want hashing and comparison by object identity: use ``@attr.s(cmp=False)``
+
+   Setting ``hash`` yourself can have unexpected consequences so we recommend to tinker with it only if you know exactly what you're doing.
+
+Under certain circumstances, it's necessary for objects to be *hashable*.
+For example if you want to put them into a :class:`set` or if you want to use them as keys in a :class:`dict`.
+
+The *hash* of an object is an integer that represents the contents of an object.
+It can be obtained by calling :func:`hash` on an object and is implemented by writing a ``__hash__`` method for your class.
+
+``attrs`` will happily write a ``__hash__`` method you [#fn1]_, however it will *not* do so by default.
+Because according to the definition_ from the official Python docs, the returned hash has to fullfil certain constraints:
+
+#. Two objects that are equal, **must** have the same hash.
+   This means that if ``x == y``, it *must* follow that ``hash(x) == hash(y)``.
+
+   By default, Python classes are compared *and* hashed by their :func:`id`.
+   That means that every instance of a class has a different hash, no matter what attributes it carries.
+
+   It follows that the moment you (or ``attrs``) change the way equality is handled by implementing ``__eq__`` which is based on attribute values, this constraint is broken.
+   For that reason Python 3 will make a class that has customized equality unhashable.
+   Python 2 on the other hand will happily let you shoot your foot off.
+   Unfortunately ``attrs`` currently mimics Python 2's behavior for backward compatibility reasons if you set ``hash=False``.
+
+   The *correct way* to achieve hashing by id is to set ``@attr.s(cmp=False)``.
+   Setting ``@attr.s(hash=False)`` (that implies ``cmp=True``) is almost certainly a *bug*.
+
+#. If two object are not equal, their hash **should** be different.
+
+   While this isn't a requirement from a standpoint of correctness, sets and dicts become less effective if there are a lot of identical hashes.
+   The worst case is when all objects have the same hash which turns a set into a list.
+
+#. The hash of an object **must not** change.
+
+   If you create a class with ``@attr.s(frozen=True)`` this is fullfilled by definition, therefore ``attrs`` will write a ``__hash__`` function for you automatically.
+   You can also force it to write one with ``hash=True`` but then it's *your* responsibility to make sure that the object is not mutated.
+
+   This point is the reason why mutable structures like lists, dictionaries, or sets aren't hashable while immutable ones like tuples or frozensets are:
+   point 1 and 2 require that the hash changes with the contents but point 3 forbids it.
+
+For a more thorough explanation of this topic, please refer to this blog post: `Python Hashes and Equality`_.
+
+
+.. [#fn1] The hash is computed by hashing a tuple that consists of an unique id for the class plus all attribute values.
+
+.. _definition: https://docs.python.org/3/glossary.html#term-hashable
+.. _`Python Hashes and Equality`: https://hynek.me/articles/hashes-and-equality/
--- a/testing/web-platform/tests/tools/third_party/attrs/docs/how-does-it-work.rst
+++ b/testing/web-platform/tests/tools/third_party/attrs/docs/how-does-it-work.rst
@@ -12,17 +12,17 @@ But its **declarative** approach combine
 
 Once you apply the ``@attr.s`` decorator to a class, ``attrs`` searches the class object for instances of ``attr.ib``\ s.
 Internally they're a representation of the data passed into ``attr.ib`` along with a counter to preserve the order of the attributes.
 
 In order to ensure that sub-classing works as you'd expect it to work, ``attrs`` also walks the class hierarchy and collects the attributes of all super-classes.
 Please note that ``attrs`` does *not* call ``super()`` *ever*.
 It will write dunder methods to work on *all* of those attributes which also has performance benefits due to fewer function calls.
 
-Once ``attrs`` knows what attributes it has to work on, it writes the requested dunder methods and -- depending on whether you wish to have ``__slots__`` -- creates a new class for you (``slots=True``) or attaches them to the original class (``slots=False``).
+Once ``attrs`` knows what attributes it has to work on, it writes the requested dunder methods and -- depending on whether you wish to have a :term:`dict <dict classes>` or :term:`slotted <slotted classes>` class -- creates a new class for you (``slots=True``) or attaches them to the original class (``slots=False``).
 While creating new classes is more elegant, we've run into several edge cases surrounding metaclasses that make it impossible to go this route unconditionally.
 
 To be very clear: if you define a class with a single attribute  without a default value, the generated ``__init__`` will look *exactly* how you'd expect:
 
 .. doctest::
 
    >>> import attr, inspect
    >>> @attr.s
@@ -48,17 +48,34 @@ This **static** approach was very much a
 
 .. _how-frozen:
 
 Immutability
 ------------
 
 In order to give you immutability, ``attrs`` will attach a ``__setattr__`` method to your class that raises a :exc:`attr.exceptions.FrozenInstanceError` whenever anyone tries to set an attribute.
 
-To circumvent that ourselves in ``__init__``, ``attrs`` uses (an aggressively cached) :meth:`object.__setattr__` to set your attributes.
+Depending on whether of not a class is a dict class or a slots class, ``attrs`` uses a different technique to circumvent that limitation in the ``__init__`` method.
+
+Once constructed, frozen instances don't differ in any way from regular ones except that you cannot change its attributes.
+
+
+Dict Classes
+++++++++++++
+
+Dict classes -- i.e. regular classes -- simply assign the value directly into the class' eponymous ``__dict__`` (and there's nothing we can do to stop the user to do the same).
+
+The performance impact is negligible.
+
+
+Slots Classes
++++++++++++++
+
+Slots classes are more complicated.
+Here it uses (an aggressively cached) :meth:`object.__setattr__` to set your attributes.
 This is (still) slower than a plain assignment:
 
 .. code-block:: none
 
   $ pyperf timeit --rigorous \
         -s "import attr; C = attr.make_class('C', ['x', 'y', 'z'], slots=True)" \
         "C(1, 2, 3)"
   ........................................
@@ -69,11 +86,15 @@ This is (still) slower than a plain assi
         "C(1, 2, 3)"
   ........................................
   Median +- std dev: 676 ns +- 16 ns
 
 So on a standard notebook the difference is about 300 nanoseconds (1 second is 1,000,000,000 nanoseconds).
 It's certainly something you'll feel in a hot loop but shouldn't matter in normal code.
 Pick what's more important to you.
 
-****
+
+Summary
++++++++
 
-Once constructed, frozen instances don't differ in any way from regular ones except that you cannot change its attributes.
+You should avoid to instantiate lots of frozen slotted classes (i.e. ``@attr.s(slots=True, frozen=True)``) in performance-critical code.
+
+Frozen dict classes have barely a performance impact, unfrozen slotted classes are even *faster* than unfrozen dict classes (i.e. regular classes).
--- a/testing/web-platform/tests/tools/third_party/attrs/docs/index.rst
+++ b/testing/web-platform/tests/tools/third_party/attrs/docs/index.rst
@@ -22,25 +22,31 @@ The recommended installation method is `
 The next three steps should bring you up and running in no time:
 
 - :doc:`overview` will show you a simple example of ``attrs`` in action and introduce you to its philosophy.
   Afterwards, you can start writing your own classes, understand what drives ``attrs``'s design, and know what ``@attr.s`` and ``attr.ib()`` stand for.
 - :doc:`examples` will give you a comprehensive tour of ``attrs``'s features.
   After reading, you will know about our advanced features and how to use them.
 - Finally :doc:`why` gives you a rundown of potential alternatives and why we think ``attrs`` is superior.
   Yes, we've heard about ``namedtuple``\ s!
+- If at any point you get confused by some terminology, please check out our :doc:`glossary`.
 
 
 If you need any help while getting started, feel free to use the ``python-attrs`` tag on `StackOverflow <https://stackoverflow.com/questions/tagged/python-attrs>`_ and someone will surely help you out!
 
 
 Day-to-Day Usage
 ================
 
 - Once you're comfortable with the concepts, our :doc:`api` contains all information you need to use ``attrs`` to its fullest.
+- Instance initialization is one of ``attrs`` key feature areas.
+  Our goal is to relieve you from writing as much code as possible.
+  :doc:`init` gives you an overview what ``attrs`` has to offer and explains some related philosophies we believe in.
+- If you want to put objects into sets or use them as keys in dictionaries, they have to be hashable.
+  The simplest way to do that is to use frozen classes, but the topic is more complex than it seems and :doc:`hashing` will give you a primer on what to look out for.
 - ``attrs`` is built for extension from the ground up.
   :doc:`extending` will show you the affordances it offers and how to make it a building block of your own projects.
 
 
 .. include:: ../README.rst
    :start-after: -testimonials-
    :end-before: -end-
 
@@ -63,18 +69,21 @@ Full Table of Contents
 ======================
 
 .. toctree::
    :maxdepth: 2
 
    overview
    why
    examples
+   init
+   hashing
    api
    extending
    how-does-it-work
+   glossary
 
 
 Indices and tables
 ==================
 
 * :ref:`genindex`
 * :ref:`search`
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/tools/third_party/attrs/docs/init.rst
@@ -0,0 +1,354 @@
+Initialization
+==============
+
+In Python, instance intialization happens in the ``__init__`` method.
+Generally speaking, you should keep as little logic as possible in it, and you should think about what the class needs and not how it is going to be instantiated.
+
+Passing complex objects into ``__init__`` and then using them to derive data for the class unnecessarily couples your new class with the old class which makes it harder to test and also will cause problems later.
+
+So assuming you use an ORM and want to extract 2D points from a row object, do not write code like this::
+
+    class Point(object):
+        def __init__(self, database_row):
+            self.x = database_row.x
+            self.y = database_row.y
+
+    pt = Point(row)
+
+Instead, write a :func:`classmethod` that will extract it for you::
+
+   @attr.s
+   class Point(object):
+       x = attr.ib()
+       y = attr.ib()
+
+       @classmethod
+       def from_row(cls, row):
+           return cls(row.x, row.y)
+
+   pt = Point.from_row(row)
+
+Now you can instantiate ``Point``\ s without creating fake row objects in your tests and you can have as many smart creation helpers as you want, in case more data sources appear.
+
+For similar reasons, we strongly discourage from patterns like::
+
+   pt = Point(**row.attributes)
+
+which couples your classes to the data model.
+Try to design your classes in a way that is clean and convenient to use -- not based on your database format.
+The database format can change anytime and you're stuck with a bad class design that is hard to change.
+Embrace classmethods as a filter between reality and what's best for you to work with.
+
+If you look for object serialization, there's a bunch of projects listed on our ``attrs`` extensions `Wiki page`_.
+Some of them even support nested schemas.
+
+
+Private Attributes
+------------------
+
+One thing people tend to find confusing is the treatment of private attributes that start with an underscore.
+``attrs`` follows the doctrine that `there is no such thing as a private argument`_ and strips the underscores from the name when writing the ``__init__`` method signature:
+
+.. doctest::
+
+   >>> import inspect, attr
+   >>> @attr.s
+   ... class C(object):
+   ...    _x = attr.ib()
+   >>> inspect.signature(C.__init__)
+   <Signature (self, x) -> None>
+
+There really isn't a right or wrong, it's a matter of taste.
+But it's important to be aware of it because it can lead to surprising syntax errors:
+
+.. doctest::
+
+   >>> @attr.s
+   ... class C(object):
+   ...    _1 = attr.ib()
+   Traceback (most recent call last):
+      ...
+   SyntaxError: invalid syntax
+
+In this case a valid attribute name ``_1`` got transformed into an invalid argument name ``1``.
+
+
+Defaults
+--------
+
+Sometimes you don't want to pass all attribute values to a class.
+And sometimes, certain attributes aren't even intended to be passed but you want to allow for customization anyways for easier testing.
+
+This is when default values come into play:
+
+.. doctest::
+
+   >>> import attr
+   >>> @attr.s
+   ... class C(object):
+   ...     a = attr.ib(default=42)
+   ...     b = attr.ib(default=attr.Factory(list))
+   ...     c = attr.ib(factory=list)  # syntactic sugar for above
+   ...     d = attr.ib()
+   ...     @d.default
+   ...     def _any_name_except_a_name_of_an_attribute(self):
+   ...        return {}
+   >>> C()
+   C(a=42, b=[], c=[], d={})
+
+It's important that the decorated method -- or any other method or property! -- doesn't have the same name as the attribute, otherwise it would overwrite the attribute definition.
+
+
+Please note that as with function and method signatures, ``default=[]`` will *not* do what you may think it might do:
+
+.. doctest::
+
+   >>> @attr.s
+   ... class C(object):
+   ...     x = attr.ib(default=[])
+   >>> i = C()
+   >>> j = C()
+   >>> i.x.append(42)
+   >>> j.x
+   [42]
+
+
+This is why ``attrs`` comes with factory options.
+
+.. warning::
+
+   Please note that the decorator based defaults have one gotcha:
+   they are executed when the attribute is set, that means depending on the order of attributes, the ``self`` object may not be fully initialized when they're called.
+
+   Therefore you should use ``self`` as little as possible.
+
+   Even the smartest of us can `get confused`_ by what happens if you pass partially initialized objects around.
+
+
+ .. _validators:
+
+Validators
+----------
+
+Another thing that definitely *does* belong into ``__init__`` is checking the resulting instance for invariants.
+This is why ``attrs`` has the concept of validators.
+
+
+Decorator
+~~~~~~~~~
+
+The most straightforward way is using the attribute's ``validator`` method as a decorator.
+
+The method has to accept three arguments:
+
+#. the *instance* that's being validated (aka ``self``),
+#. the *attribute* that it's validating, and finally
+#. the *value* that is passed for it.
+
+If the value does not pass the validator's standards, it just raises an appropriate exception.
+
+   >>> @attr.s
+   ... class C(object):
+   ...     x = attr.ib()
+   ...     @x.validator
+   ...     def _check_x(self, attribute, value):
+   ...         if value > 42:
+   ...             raise ValueError("x must be smaller or equal to 42")
+   >>> C(42)
+   C(x=42)
+   >>> C(43)
+   Traceback (most recent call last):
+      ...
+   ValueError: x must be smaller or equal to 42
+
+Again, it's important that the decorated method doesn't have the same name as the attribute.
+
+
+Callables
+~~~~~~~~~
+
+If you want to re-use your validators, you should have a look at the ``validator`` argument to :func:`attr.ib()`.
+
+It takes either a callable or a list of callables (usually functions) and treats them as validators that receive the same arguments as with the decorator approach.
+
+Since the validators runs *after* the instance is initialized, you can refer to other attributes while validating:
+
+.. doctest::
+
+   >>> def x_smaller_than_y(instance, attribute, value):
+   ...     if value >= instance.y:
+   ...         raise ValueError("'x' has to be smaller than 'y'!")
+   >>> @attr.s
+   ... class C(object):
+   ...     x = attr.ib(validator=[attr.validators.instance_of(int),
+   ...                            x_smaller_than_y])
+   ...     y = attr.ib()
+   >>> C(x=3, y=4)
+   C(x=3, y=4)
+   >>> C(x=4, y=3)
+   Traceback (most recent call last):
+      ...
+   ValueError: 'x' has to be smaller than 'y'!
+
+This example also shows of some syntactic sugar for using the :func:`attr.validators.and_` validator: if you pass a list, all validators have to pass.
+
+``attrs`` won't intercept your changes to those attributes but you can always call :func:`attr.validate` on any instance to verify that it's still valid:
+
+.. doctest::
+
+   >>> i = C(4, 5)
+   >>> i.x = 5  # works, no magic here
+   >>> attr.validate(i)
+   Traceback (most recent call last):
+      ...
+   ValueError: 'x' has to be smaller than 'y'!
+
+``attrs`` ships with a bunch of validators, make sure to :ref:`check them out <api_validators>` before writing your own:
+
+.. doctest::
+
+   >>> @attr.s
+   ... class C(object):
+   ...     x = attr.ib(validator=attr.validators.instance_of(int))
+   >>> C(42)
+   C(x=42)
+   >>> C("42")
+   Traceback (most recent call last):
+      ...
+   TypeError: ("'x' must be <type 'int'> (got '42' that is a <type 'str'>).", Attribute(name='x', default=NOTHING, factory=NOTHING, validator=<instance_of validator for type <type 'int'>>, type=None), <type 'int'>, '42')
+
+Of course you can mix and match the two approaches at your convenience.
+If you define validators both ways for an attribute, they are both ran:
+
+.. doctest::
+
+   >>> @attr.s
+   ... class C(object):
+   ...     x = attr.ib(validator=attr.validators.instance_of(int))
+   ...     @x.validator
+   ...     def fits_byte(self, attribute, value):
+   ...         if not 0 <= value < 256:
+   ...             raise ValueError("value out of bounds")
+   >>> C(128)
+   C(x=128)
+   >>> C("128")
+   Traceback (most recent call last):
+      ...
+   TypeError: ("'x' must be <class 'int'> (got '128' that is a <class 'str'>).", Attribute(name='x', default=NOTHING, validator=[<instance_of validator for type <class 'int'>>, <function fits_byte at 0x10fd7a0d0>], repr=True, cmp=True, hash=True, init=True, metadata=mappingproxy({}), type=None, converter=one), <class 'int'>, '128')
+   >>> C(256)
+   Traceback (most recent call last):
+      ...
+   ValueError: value out of bounds
+
+And finally you can disable validators globally:
+
+   >>> attr.set_run_validators(False)
+   >>> C("128")
+   C(x='128')
+   >>> attr.set_run_validators(True)
+   >>> C("128")
+   Traceback (most recent call last):
+      ...
+   TypeError: ("'x' must be <class 'int'> (got '128' that is a <class 'str'>).", Attribute(name='x', default=NOTHING, validator=[<instance_of validator for type <class 'int'>>, <function fits_byte at 0x10fd7a0d0>], repr=True, cmp=True, hash=True, init=True, metadata=mappingproxy({}), type=None, converter=None), <class 'int'>, '128')
+
+
+.. _converters:
+
+Converters
+----------
+
+Finally, sometimes you may want to normalize the values coming in.
+For that ``attrs`` comes with converters.
+
+Attributes can have a ``converter`` function specified, which will be called with the attribute's passed-in value to get a new value to use.
+This can be useful for doing type-conversions on values that you don't want to force your callers to do.
+
+.. doctest::
+
+    >>> @attr.s
+    ... class C(object):
+    ...     x = attr.ib(converter=int)
+    >>> o = C("1")
+    >>> o.x
+    1
+
+Converters are run *before* validators, so you can use validators to check the final form of the value.
+
+.. doctest::
+
+    >>> def validate_x(instance, attribute, value):
+    ...     if value < 0:
+    ...         raise ValueError("x must be at least 0.")
+    >>> @attr.s
+    ... class C(object):
+    ...     x = attr.ib(converter=int, validator=validate_x)
+    >>> o = C("0")
+    >>> o.x
+    0
+    >>> C("-1")
+    Traceback (most recent call last):
+        ...
+    ValueError: x must be at least 0.
+
+
+Arguably, you can abuse converters as one-argument validators:
+
+.. doctest::
+
+   >>> C("x")
+   Traceback (most recent call last):
+       ...
+   ValueError: invalid literal for int() with base 10: 'x'
+
+
+Post-Init Hook
+--------------
+
+Generally speaking, the moment you think that you need finer control over how your class is instantiated than what ``attrs`` offers, it's usually best to use a classmethod factory or to apply the `builder pattern <https://en.wikipedia.org/wiki/Builder_pattern>`_.
+
+However, sometimes you need to do that one quick thing after your class is initialized.
+And for that ``attrs`` offers the ``__attrs_post_init__`` hook that is automatically detected and run after ``attrs`` is done initializing your instance:
+
+.. doctest::
+
+   >>> @attr.s
+   ... class C(object):
+   ...     x = attr.ib()
+   ...     y = attr.ib(init=False)
+   ...     def __attrs_post_init__(self):
+   ...         self.y = self.x + 1
+   >>> C(1)
+   C(x=1, y=2)
+
+Please note that you can't directly set attributes on frozen classes:
+
+.. doctest::
+
+   >>> @attr.s(frozen=True)
+   ... class FrozenBroken(object):
+   ...     x = attr.ib()
+   ...     y = attr.ib(init=False)
+   ...     def __attrs_post_init__(self):
+   ...         self.y = self.x + 1
+   >>> FrozenBroken(1)
+   Traceback (most recent call last):
+      ...
+   attr.exceptions.FrozenInstanceError: can't set attribute
+
+If you need to set attributes on a frozen class, you'll have to resort to the :ref:`same trick <how-frozen>` as ``attrs`` and use :meth:`object.__setattr__`:
+
+.. doctest::
+
+   >>> @attr.s(frozen=True)
+   ... class Frozen(object):
+   ...     x = attr.ib()
+   ...     y = attr.ib(init=False)
+   ...     def __attrs_post_init__(self):
+   ...         object.__setattr__(self, "y", self.x + 1)
+   >>> Frozen(1)
+   Frozen(x=1, y=2)
+
+
+.. _`Wiki page`: https://github.com/python-attrs/attrs/wiki/Extensions-to-attrs
+.. _`get confused`: https://github.com/python-attrs/attrs/issues/289
+.. _`there is no such thing as a private argument`: https://github.com/hynek/characteristic/issues/6
--- a/testing/web-platform/tests/tools/third_party/attrs/docs/why.rst
+++ b/testing/web-platform/tests/tools/third_party/attrs/docs/why.rst
@@ -130,51 +130,71 @@ With ``attrs`` your users won't notice a
 .. [#iter] :func:`attr.astuple` can be used to get that behavior in ``attrs`` on *explicit demand*.
 .. [#immutable] ``attrs`` offers *optional* immutability through the ``frozen`` keyword.
 .. [#perf] Although ``attrs`` would serve you just as well!
            Since both employ the same method of writing and compiling Python code for you, the performance penalty is negligible at worst and in some cases ``attrs`` is even faster if you use ``slots=True`` (which is generally a good idea anyway).
 
 .. _behaving like a tuple: https://docs.python.org/3/tutorial/datastructures.html#tuples-and-sequences
 
 
+…Data Classes?
+--------------
+
+`PEP 557 <https://www.python.org/dev/peps/pep-0557/>`_ added Data Classes to `Python 3.7 <https://docs.python.org/3.7/whatsnew/3.7.html#pep-557-data-classes>`_ that resemble ``attrs`` in many ways.
+
+They are the result of the Python community's `wish <https://mail.python.org/pipermail/python-ideas/2017-May/045618.html>`_ to have an easier way to write classes in the standard library that doesn't carry the problems of ``namedtuple``\ s.
+To that end, ``attrs`` and its developers were involved in the PEP process and while we may disagree with some minor decisions that have been made, it's a fine library and if it stops you from abusing ``namedtuple``\ s, they are a huge win.
+
+Nevertheless, there are still reasons to prefer ``attrs`` over Data Classes whose relevancy depends on your circumstances:
+
+- ``attrs`` supports all maintream Python versions, including CPython 2.7 and PyPy.
+- Data Classes are intentionally less powerful than ``attrs``.
+  There is a long list of features that were sacrificed for the sake of simplicity and while the most obvious ones are validators, converters, and ``__slots__``, it permeates throughout all APIs.
+
+  On the other hand, Data Classes currently do not offer any significant feature that ``attrs`` doesn't already have.
+- ``attrs`` can and will move faster.
+  We are not bound to any release schedules and we have a clear deprecation policy.
+
+  One of the `reasons <https://www.python.org/dev/peps/pep-0557/#why-not-just-use-attrs>`_ to not vendor ``attrs`` in the standard library was to not impede ``attrs``'s future developement.
+
+
+
 …dicts?
 -------
 
 Dictionaries are not for fixed fields.
 
 If you have a dict, it maps something to something else.
 You should be able to add and remove values.
 
-
-
 ``attrs`` lets you be specific about those expectations; a dictionary does not.
 It gives you a named entity (the class) in your code, which lets you explain in other places whether you take a parameter of that class or return a value of that class.
 
 In other words: if your dict has a fixed and known set of keys, it is an object, not a hash.
 So if you never iterate over the keys of a dict, you should use a proper class.
 
 
 …hand-written classes?
 ----------------------
 
-While we're fans of all things artisanal, writing the same nine methods all over again doesn't qualify for me.
+While we're fans of all things artisanal, writing the same nine methods again and again doesn't qualify.
 I usually manage to get some typos inside and there's simply more code that can break and thus has to be tested.
 
 To bring it into perspective, the equivalent of
 
 .. doctest::
 
    >>> @attr.s
    ... class SmartClass(object):
    ...    a = attr.ib()
    ...    b = attr.ib()
    >>> SmartClass(1, 2)
    SmartClass(a=1, b=2)
 
-is
+is roughly
 
 .. doctest::
 
    >>> class ArtisanalClass(object):
    ...     def __init__(self, a, b):
    ...         self.a = a
    ...         self.b = b
    ...
@@ -214,17 +234,17 @@ is
    ...
    ...     def __ge__(self, other):
    ...         if other.__class__ is self.__class__:
    ...             return (self.a, self.b) >= (other.a, other.b)
    ...         else:
    ...             return NotImplemented
    ...
    ...     def __hash__(self):
-   ...         return hash((self.a, self.b))
+   ...         return hash((self.__class__, self.a, self.b))
    >>> ArtisanalClass(a=1, b=2)
    ArtisanalClass(a=1, b=2)
 
 which is quite a mouthful and it doesn't even use any of ``attrs``'s more advanced features like validators or defaults values.
 Also: no tests whatsoever.
 And who will guarantee you, that you don't accidentally flip the ``<`` in your tenth implementation of ``__gt__``?
 
 It also should be noted that ``attrs`` is not an all-or-nothing solution.
--- a/testing/web-platform/tests/tools/third_party/attrs/pyproject.toml
+++ b/testing/web-platform/tests/tools/third_party/attrs/pyproject.toml
@@ -1,12 +1,13 @@
 [tool.towncrier]
     package = "attr"
     package_dir = "src"
     filename = "CHANGELOG.rst"
+    template = "changelog.d/towncrier_template.rst"
     issue_format = "`#{issue} <https://github.com/python-attrs/attrs/issues/{issue}>`_"
     directory = "changelog.d"
     title_format = "{version} ({project_date})"
     underlines = ["-", "^"]
 
     [[tool.towncrier.section]]
         path = ""
 
--- a/testing/web-platform/tests/tools/third_party/attrs/setup.py
+++ b/testing/web-platform/tests/tools/third_party/attrs/setup.py
@@ -19,21 +19,37 @@ CLASSIFIERS = [
     "Operating System :: OS Independent",
     "Programming Language :: Python",
     "Programming Language :: Python :: 2",
     "Programming Language :: Python :: 2.7",
     "Programming Language :: Python :: 3",
     "Programming Language :: Python :: 3.4",
     "Programming Language :: Python :: 3.5",
     "Programming Language :: Python :: 3.6",
+    "Programming Language :: Python :: 3.7",
     "Programming Language :: Python :: Implementation :: CPython",
     "Programming Language :: Python :: Implementation :: PyPy",
     "Topic :: Software Development :: Libraries :: Python Modules",
 ]
 INSTALL_REQUIRES = []
+EXTRAS_REQUIRE = {
+    "docs": [
+        "sphinx",
+        "zope.interface",
+    ],
+    "tests": [
+        "coverage",
+        "hypothesis",
+        "pympler",
+        "pytest",
+        "six",
+        "zope.interface",
+    ],
+}
+EXTRAS_REQUIRE["dev"] = EXTRAS_REQUIRE["tests"] + EXTRAS_REQUIRE["docs"]
 
 ###############################################################################
 
 HERE = os.path.abspath(os.path.dirname(__file__))
 
 
 def read(*parts):
     """
@@ -87,9 +103,10 @@ if __name__ == "__main__":
         maintainer_email=find_meta("email"),
         keywords=KEYWORDS,
         long_description=LONG,
         packages=PACKAGES,
         package_dir={"": "src"},
         zip_safe=False,
         classifiers=CLASSIFIERS,
         install_requires=INSTALL_REQUIRES,
+        extras_require=EXTRAS_REQUIRE,
     )
--- a/testing/web-platform/tests/tools/third_party/attrs/src/attr/__init__.py
+++ b/testing/web-platform/tests/tools/third_party/attrs/src/attr/__init__.py
@@ -1,21 +1,22 @@
 from __future__ import absolute_import, division, print_function
 
 from functools import partial
 
 from . import converters, exceptions, filters, validators
 from ._config import get_run_validators, set_run_validators
 from ._funcs import asdict, assoc, astuple, evolve, has
 from ._make import (
-    NOTHING, Attribute, Factory, attrib, attrs, fields, make_class, validate
+    NOTHING, Attribute, Factory, attrib, attrs, fields, fields_dict,
+    make_class, validate
 )
 
 
-__version__ = "17.4.0.dev0"
+__version__ = "18.1.0"
 
 __title__ = "attrs"
 __description__ = "Classes Without Boilerplate"
 __uri__ = "http://www.attrs.org/"
 __doc__ = __description__ + " <" + __uri__ + ">"
 
 __author__ = "Hynek Schlawack"
 __email__ = "hs@ox.cx"
@@ -38,16 +39,17 @@ dataclass = partial(attrs, auto_attribs=
     "attr",
     "attrib",
     "attributes",
     "attrs",
     "converters",
     "evolve",
     "exceptions",
     "fields",
+    "fields_dict",
     "filters",
     "get_run_validators",
     "has",
     "ib",
     "make_class",
     "s",
     "set_run_validators",
     "validate",
--- a/testing/web-platform/tests/tools/third_party/attrs/src/attr/_compat.py
+++ b/testing/web-platform/tests/tools/third_party/attrs/src/attr/_compat.py
@@ -5,16 +5,23 @@ import sys
 import types
 import warnings
 
 
 PY2 = sys.version_info[0] == 2
 PYPY = platform.python_implementation() == "PyPy"
 
 
+if PYPY or sys.version_info[:2] >= (3, 6):
+    ordered_dict = dict
+else:
+    from collections import OrderedDict
+    ordered_dict = OrderedDict
+
+
 if PY2:
     from UserDict import IterableUserDict
 
     # We 'bundle' isclass instead of using inspect as importing inspect is
     # fairly expensive (order of 10-15 ms for a modern machine in 2016)
     def isclass(klass):
         return isinstance(klass, (type, types.ClassType))
 
@@ -82,25 +89,22 @@ else:
 
     def iteritems(d):
         return d.items()
 
     def metadata_proxy(d):
         return types.MappingProxyType(dict(d))
 
 
-def import_ctypes():  # pragma: nocover
+def import_ctypes():
     """
     Moved into a function for testability.
     """
-    try:
-        import ctypes
-        return ctypes
-    except ImportError:
-        return None
+    import ctypes
+    return ctypes
 
 
 if not PY2:
     def just_warn(*args, **kw):
         """
         We only warn on Python 3 because we are not aware of any concrete
         consequences of not setting the cell on Python 2.
         """
@@ -121,19 +125,22 @@ else:
 def make_set_closure_cell():
     """
     Moved into a function for testability.
     """
     if PYPY:  # pragma: no cover
         def set_closure_cell(cell, value):
             cell.__setstate__((value,))
     else:
-        ctypes = import_ctypes()
-        if ctypes is not None:
+        try:
+            ctypes = import_ctypes()
+
             set_closure_cell = ctypes.pythonapi.PyCell_Set
             set_closure_cell.argtypes = (ctypes.py_object, ctypes.py_object)
             set_closure_cell.restype = ctypes.c_int
-        else:
+        except Exception:
+            # We try best effort to set the cell, but sometimes it's not
+            # possible.  For example on Jython or on GAE.
             set_closure_cell = just_warn
     return set_closure_cell
 
 
 set_closure_cell = make_set_closure_cell()
--- a/testing/web-platform/tests/tools/third_party/attrs/src/attr/_make.py
+++ b/testing/web-platform/tests/tools/third_party/attrs/src/attr/_make.py
@@ -1,29 +1,35 @@
 from __future__ import absolute_import, division, print_function
 
 import hashlib
 import linecache
 import sys
+import threading
+import warnings
 
 from operator import itemgetter
 
 from . import _config
-from ._compat import PY2, isclass, iteritems, metadata_proxy, set_closure_cell
+from ._compat import (
+    PY2, isclass, iteritems, metadata_proxy, ordered_dict, set_closure_cell
+)
 from .exceptions import (
     DefaultAlreadySetError, FrozenInstanceError, NotAnAttrsClassError,
     UnannotatedAttributeError
 )
 
 
 # This is used at least twice, so cache it here.
 _obj_setattr = object.__setattr__
-_init_convert_pat = "__attr_convert_{}"
+_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({})
 
 
 class _Nothing(object):
     """
     Sentinel class to indicate the lack of a value when ``None`` is ambiguous.
 
     All instances of `_Nothing` are equal.
@@ -39,28 +45,29 @@ class _Nothing(object):
 
     def __ne__(self, other):
         return not self == other
 
     def __repr__(self):
         return "NOTHING"
 
     def __hash__(self):
-        return 0xdeadbeef
+        return 0xc0ffee
 
 
 NOTHING = _Nothing()
 """
 Sentinel to indicate the lack of a value when ``None`` is ambiguous.
 """
 
 
 def attrib(default=NOTHING, validator=None,
            repr=True, cmp=True, hash=None, init=True,
-           convert=None, metadata={}, type=None):
+           convert=None, metadata=None, type=None, converter=None,
+           factory=None):
     """
     Create a new attribute on a class.
 
     ..  warning::
 
         Does *not* do anything unless the class is also decorated with
         :func:`attr.s`!
 
@@ -75,78 +82,117 @@ def attrib(default=NOTHING, validator=No
         If a default is not set (or set manually to ``attr.NOTHING``), a value
         *must* be supplied when instantiating; otherwise a :exc:`TypeError`
         will be raised.
 
         The default can also be set using decorator notation as shown below.
 
     :type default: Any value.
 
+    :param callable factory: Syntactic sugar for
+        ``default=attr.Factory(callable)``.
+
     :param validator: :func:`callable` that is called by ``attrs``-generated
         ``__init__`` methods after the instance has been initialized.  They
         receive the initialized instance, the :class:`Attribute`, and the
         passed value.
 
         The return value is *not* inspected so the validator has to throw an
         exception itself.
 
         If a ``list`` is passed, its items are treated as validators and must
         all pass.
 
         Validators can be globally disabled and re-enabled using
         :func:`get_run_validators`.
 
         The validator can also be set using decorator notation as shown below.
 
-    :type validator: ``callable`` or a ``list`` of ``callable``\ s.
+    :type validator: ``callable`` or a ``list`` of ``callable``\\ s.
 
     :param bool repr: Include this attribute in the generated ``__repr__``
         method.
     :param bool cmp: Include this attribute in the generated comparison methods
         (``__eq__`` et al).
     :param hash: Include this attribute in the generated ``__hash__``
         method.  If ``None`` (default), mirror *cmp*'s value.  This is the
         correct behavior according the Python spec.  Setting this value to
         anything else than ``None`` is *discouraged*.
     :type hash: ``bool`` or ``None``
     :param bool init: Include this attribute in the generated ``__init__``
         method.  It is possible to set this to ``False`` and set a default
         value.  In that case this attributed is unconditionally initialized
         with the specified default value or factory.
-    :param callable convert: :func:`callable` that is called by
-        ``attrs``-generated ``__init__`` methods to convert attribute's value
+    :param callable converter: :func:`callable` that is called by
+        ``attrs``-generated ``__init__`` methods to converter attribute's value
         to the desired format.  It is given the passed-in value, and the
         returned value will be used as the new value of the attribute.  The
         value is converted before being passed to the validator, if any.
     :param metadata: An arbitrary mapping, to be used by third-party
         components.  See :ref:`extending_metadata`.
     :param type: The type of the attribute.  In Python 3.6 or greater, the
         preferred method to specify the type is using a variable annotation
         (see `PEP 526 <https://www.python.org/dev/peps/pep-0526/>`_).
         This argument is provided for backward compatibility.
         Regardless of the approach used, the type will be stored on
         ``Attribute.type``.
 
-    ..  versionchanged:: 17.1.0 *validator* can be a ``list`` now.
-    ..  versionchanged:: 17.1.0
-        *hash* is ``None`` and therefore mirrors *cmp* by default.
-    ..  versionadded:: 17.3.0 *type*
+    .. versionadded:: 15.2.0 *convert*
+    .. versionadded:: 16.3.0 *metadata*
+    .. versionchanged:: 17.1.0 *validator* can be a ``list`` now.
+    .. versionchanged:: 17.1.0
+       *hash* is ``None`` and therefore mirrors *cmp* by default.
+    .. versionadded:: 17.3.0 *type*
+    .. deprecated:: 17.4.0 *convert*
+    .. versionadded:: 17.4.0 *converter* as a replacement for the deprecated
+       *convert* to achieve consistency with other noun-based arguments.
+    .. versionadded:: 18.1.0
+       ``factory=f`` is syntactic sugar for ``default=attr.Factory(f)``.
     """
     if hash is not None and hash is not True and hash is not False:
         raise TypeError(
             "Invalid value for hash.  Must be True, False, or None."
         )
+
+    if convert is not None:
+        if converter is not None:
+            raise RuntimeError(
+                "Can't pass both `convert` and `converter`.  "
+                "Please use `converter` only."
+            )
+        warnings.warn(
+            "The `convert` argument is deprecated in favor of `converter`.  "
+            "It will be removed after 2019/01.",
+            DeprecationWarning, stacklevel=2
+        )
+        converter = convert
+
+    if factory is not None:
+        if default is not NOTHING:
+            raise ValueError(
+                "The `default` and `factory` arguments are mutually "
+                "exclusive."
+            )
+        if not callable(factory):
+            raise ValueError(
+                "The `factory` argument must be a callable."
+            )
+        default = Factory(factory)
+
+    if metadata is None:
+        metadata = {}
+
     return _CountingAttr(
         default=default,
         validator=validator,
         repr=repr,
         cmp=cmp,
         hash=hash,
         init=init,
-        convert=convert,
+        converter=converter,
         metadata=metadata,
         type=type,
     )
 
 
 def _make_attr_tuple_class(cls_name, attr_names):
     """
     Create a tuple subclass to hold `Attribute`s for an `attrs` class.
@@ -173,29 +219,31 @@ def _make_attr_tuple_class(cls_name, att
     globs = {"itemgetter": itemgetter}
     eval(compile("\n".join(attr_class_template), "", "exec"), globs)
     return globs[attr_class_name]
 
 
 # Tuple class for extracted attributes from a class definition.
 # `super_attrs` is a subset of `attrs`.
 _Attributes = _make_attr_tuple_class("_Attributes", [
-    "attrs",        # all attributes to build dunder methods for
-    "super_attrs",  # attributes that have been inherited from super classes
+    "attrs",            # all attributes to build dunder methods for
+    "super_attrs",      # attributes that have been inherited
+    "super_attrs_map",  # map inherited attributes to their originating classes
 ])
 
 
 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):
     """
     Get annotations for *cls*.
     """
     anns = getattr(cls, "__annotations__", None)
     if anns is None:
@@ -204,33 +252,43 @@ def _get_annotations(cls):
     # Verify that the annotations aren't merely inherited.
     for super_cls in cls.__mro__[1:]:
         if anns is getattr(super_cls, "__annotations__", None):
             return {}
 
     return anns
 
 
+def _counter_getter(e):
+    """
+    Key function for sorting to avoid re-creating a lambda for every class.
+    """
+    return e[1].counter
+
+
 def _transform_attrs(cls, these, auto_attribs):
     """
     Transform all `_CountingAttr`s on a class into `Attribute`s.
 
     If *these* is passed, use that and don't look for them on the class.
 
     Return an `_Attributes`.
     """
     cd = cls.__dict__
     anns = _get_annotations(cls)
 
     if these is not None:
-        ca_list = sorted((
+        ca_list = [
             (name, ca)
             for name, ca
             in iteritems(these)
-        ), key=lambda e: e[1].counter)
+        ]
+
+        if not isinstance(these, ordered_dict):
+            ca_list.sort(key=_counter_getter)
     elif auto_attribs is True:
         ca_names = {
             name
             for name, attr
             in cd.items()
             if isinstance(attr, _CountingAttr)
         }
         ca_list = []
@@ -259,54 +317,44 @@ def _transform_attrs(cls, these, auto_at
     else:
         ca_list = sorted((
             (name, attr)
             for name, attr
             in cd.items()
             if isinstance(attr, _CountingAttr)
         ), key=lambda e: e[1].counter)
 
-    non_super_attrs = [
+    own_attrs = [
         Attribute.from_counting_attr(
             name=attr_name,
             ca=ca,
             type=anns.get(attr_name),
         )
         for attr_name, ca
         in ca_list
     ]
 
-    # Walk *down* the MRO for attributes.  While doing so, we collect the names
-    # of attributes we've seen in `take_attr_names` and ignore their
-    # redefinitions deeper in the hierarchy.
     super_attrs = []
-    taken_attr_names = {a.name: a for a in non_super_attrs}
+    super_attr_map = {}  # A dictionary of superattrs to their classes.
+    taken_attr_names = {a.name: a for a in own_attrs}
+
+    # Traverse the MRO and collect attributes.
     for super_cls in cls.__mro__[1:-1]:
         sub_attrs = getattr(super_cls, "__attrs_attrs__", None)
         if sub_attrs is not None:
-            # We iterate over sub_attrs backwards so we can reverse the whole
-            # list in the end and get all attributes in the order they have
-            # been defined.
-            for a in reversed(sub_attrs):
+            for a in sub_attrs:
                 prev_a = taken_attr_names.get(a.name)
+                # Only add an attribute if it hasn't been defined before.  This
+                # allows for overwriting attribute definitions by subclassing.
                 if prev_a is None:
                     super_attrs.append(a)
                     taken_attr_names[a.name] = a
-                elif prev_a == a:
-                    # This happens thru multiple inheritance.  We don't want
-                    # to favor attributes that are further down in the tree
-                    # so we move them to the back.
-                    super_attrs.remove(a)
-                    super_attrs.append(a)
+                    super_attr_map[a.name] = super_cls
 
-    # Now reverse the list, such that the attributes are sorted by *descending*
-    # age.  IOW: the oldest attribute definition is at the head of the list.
-    super_attrs.reverse()
-
-    attr_names = [a.name for a in super_attrs + non_super_attrs]
+    attr_names = [a.name for a in super_attrs + own_attrs]
 
     AttrsClass = _make_attr_tuple_class(cls.__name__, attr_names)
 
     attrs = AttrsClass(
         super_attrs + [
             Attribute.from_counting_attr(
                 name=attr_name,
                 ca=ca,
@@ -325,17 +373,17 @@ def _transform_attrs(cls, these, auto_at
                 "default value or factory.  Attribute in question: {a!r}"
                 .format(a=a)
             )
         elif had_default is False and \
                 a.default is not NOTHING and \
                 a.init is not False:
             had_default = True
 
-    return _Attributes((attrs, super_attrs))
+    return _Attributes((attrs, super_attrs, super_attr_map))
 
 
 def _frozen_setattrs(self, name, value):
     """
     Attached to frozen classes as __setattr__.
     """
     raise FrozenInstanceError()
 
@@ -348,30 +396,34 @@ def _frozen_delattrs(self, name):
 
 
 class _ClassBuilder(object):
     """
     Iteratively build *one* class.
     """
     __slots__ = (
         "_cls", "_cls_dict", "_attrs", "_super_names", "_attr_names", "_slots",
-        "_frozen", "_has_post_init",
+        "_frozen", "_has_post_init", "_delete_attribs", "_super_attr_map",
     )
 
     def __init__(self, cls, these, slots, frozen, auto_attribs):
-        attrs, super_attrs = _transform_attrs(cls, these, auto_attribs)
+        attrs, super_attrs, super_map = _transform_attrs(
+            cls, these, auto_attribs
+        )
 
         self._cls = cls
         self._cls_dict = dict(cls.__dict__) if slots else {}
         self._attrs = attrs
         self._super_names = set(a.name for a in super_attrs)
+        self._super_attr_map = super_map
         self._attr_names = tuple(a.name for a in attrs)
         self._slots = slots
         self._frozen = frozen or _has_frozen_superclass(cls)
         self._has_post_init = bool(getattr(cls, "__attrs_post_init__", False))
+        self._delete_attribs = not bool(these)
 
         self._cls_dict["__attrs_attrs__"] = self._attrs
 
         if frozen:
             self._cls_dict["__setattr__"] = _frozen_setattrs
             self._cls_dict["__delattr__"] = _frozen_delattrs
 
     def __repr__(self):
@@ -391,20 +443,21 @@ class _ClassBuilder(object):
     def _patch_original_class(self):
         """
         Apply accumulated methods and return the class.
         """
         cls = self._cls
         super_names = self._super_names
 
         # Clean class of attribute definitions (`attr.ib()`s).
-        for name in self._attr_names:
-            if name not in super_names and \
-                    getattr(cls, name, None) is not None:
-                delattr(cls, name)
+        if self._delete_attribs:
+            for name in self._attr_names:
+                if name not in super_names and \
+                        getattr(cls, name, None) is not None:
+                    delattr(cls, name)
 
         # Attach our dunder methods.
         for name, value in self._cls_dict.items():
             setattr(cls, name, value)
 
         return cls
 
     def _create_slots_class(self):
@@ -425,30 +478,36 @@ class _ClassBuilder(object):
             for name in self._attr_names
             if name not in super_names
         )
 
         qualname = getattr(self._cls, "__qualname__", None)
         if qualname is not None:
             cd["__qualname__"] = qualname
 
-        attr_names = tuple(self._attr_names)
+        # __weakref__ is not writable.
+        state_attr_names = tuple(
+            an for an in self._attr_names if an != "__weakref__"
+        )
 
         def slots_getstate(self):
             """
             Automatically created by attrs.
             """
-            return tuple(getattr(self, name) for name in attr_names)
+            return tuple(
+                getattr(self, name)
+                for name in state_attr_names
+            )
 
         def slots_setstate(self, state):
             """
             Automatically created by attrs.
             """
             __bound_setattr = _obj_setattr.__get__(self, Attribute)
-            for name, value in zip(attr_names, state):
+            for name, value in zip(state_attr_names, state):
                 __bound_setattr(name, value)
 
         # slots and frozen require __getstate__/__setstate__ to work
         cd["__getstate__"] = slots_getstate
         cd["__setstate__"] = slots_setstate
 
         # Create new class based on old class and our methods.
         cls = type(self._cls)(
@@ -475,69 +534,108 @@ class _ClassBuilder(object):
                 continue
             for cell in closure_cells:
                 if cell.cell_contents is self._cls:
                     set_closure_cell(cell, cls)
 
         return cls
 
     def add_repr(self, ns):
-        self._cls_dict["__repr__"] = _make_repr(self._attrs, ns=ns)
+        self._cls_dict["__repr__"] = self._add_method_dunders(
+            _make_repr(self._attrs, ns=ns)
+        )
         return self
 
     def add_str(self):
-        repr_ = self._cls_dict.get("__repr__")
-        if repr_ is None:
+        repr = self._cls_dict.get("__repr__")
+        if repr is None:
             raise ValueError(
                 "__str__ can only be generated if a __repr__ exists."
             )
 
-        self._cls_dict["__str__"] = repr_
+        def __str__(self):
+            return self.__repr__()
+
+        self._cls_dict["__str__"] = self._add_method_dunders(__str__)
         return self
 
     def make_unhashable(self):
         self._cls_dict["__hash__"] = None
         return self
 
     def add_hash(self):
-        self._cls_dict["__hash__"] = _make_hash(self._attrs)
+        self._cls_dict["__hash__"] = self._add_method_dunders(
+            _make_hash(self._attrs)
+        )
+
         return self
 
     def add_init(self):
-        self._cls_dict["__init__"] = _make_init(
-            self._attrs,
-            self._has_post_init,
-            self._frozen,
+        self._cls_dict["__init__"] = self._add_method_dunders(
+            _make_init(
+                self._attrs,
+                self._has_post_init,
+                self._frozen,
+                self._slots,
+                self._super_attr_map,
+            )
         )
+
         return self
 
     def add_cmp(self):
         cd = self._cls_dict
 
         cd["__eq__"], cd["__ne__"], cd["__lt__"], cd["__le__"], cd["__gt__"], \
-            cd["__ge__"] = _make_cmp(self._attrs)
+            cd["__ge__"] = (
+                self._add_method_dunders(meth)
+                for meth in _make_cmp(self._attrs)
+            )
 
         return self
 
+    def _add_method_dunders(self, method):
+        """
+        Add __module__ and __qualname__ to a *method* if possible.
+        """
+        try:
+            method.__module__ = self._cls.__module__
+        except AttributeError:
+            pass
+
+        try:
+            method.__qualname__ = ".".join(
+                (self._cls.__qualname__, method.__name__,)
+            )
+        except AttributeError:
+            pass
+
+        return method
+
 
 def attrs(maybe_cls=None, these=None, repr_ns=None,
           repr=True, cmp=True, hash=None, init=True,
           slots=False, frozen=False, str=False, auto_attribs=False):
     r"""
     A class decorator that adds `dunder
     <https://wiki.python.org/moin/DunderAlias>`_\ -methods according to the
     specified attributes using :func:`attr.ib` or the *these* argument.
 
     :param these: A dictionary of name to :func:`attr.ib` mappings.  This is
         useful to avoid the definition of your attributes within the class body
         because you can't (e.g. if you want to add ``__repr__`` methods to
         Django models) or don't want to.
 
         If *these* is not ``None``, ``attrs`` will *not* search the class body
-        for attributes.
+        for attributes and will *not* remove any attributes from it.
+
+        If *these* is an ordered dict (:class:`dict` on Python 3.6+,
+        :class:`collections.OrderedDict` otherwise), the order is deduced from
+        the order of the attributes inside *these*.  Otherwise the order
+        of the definition of the attributes is used.
 
     :type these: :class:`dict` of :class:`str` to :func:`attr.ib`
 
     :param str repr_ns: When using nested classes, there's no way in Python 2
         to automatically detect that.  Therefore it's possible to set the
         namespace explicitly for a more meaningful ``repr`` output.
     :param bool repr: Create a ``__repr__`` method with a human readable
         representation of ``attrs`` attributes..
@@ -608,23 +706,26 @@ def attrs(maybe_cls=None, these=None, re
         value becomes the default value like if it were passed using
         ``attr.ib(default=42)``.  Passing an instance of :class:`Factory` also
         works as expected.
 
         Attributes annotated as :data:`typing.ClassVar` are **ignored**.
 
         .. _`PEP 526`: https://www.python.org/dev/peps/pep-0526/
 
-    ..  versionadded:: 16.0.0 *slots*
-    ..  versionadded:: 16.1.0 *frozen*
-    ..  versionadded:: 16.3.0 *str*, and support for ``__attrs_post_init__``.
-    ..  versionchanged::
-            17.1.0 *hash* supports ``None`` as value which is also the default
-            now.
+    .. versionadded:: 16.0.0 *slots*
+    .. versionadded:: 16.1.0 *frozen*
+    .. versionadded:: 16.3.0 *str*
+    .. versionadded:: 16.3.0 Support for ``__attrs_post_init__``.
+    .. versionchanged:: 17.1.0
+       *hash* supports ``None`` as value which is also the default now.
     .. versionadded:: 17.3.0 *auto_attribs*
+    .. versionchanged:: 18.1.0
+       If *these* is passed, no attributes are deleted from the class body.
+    .. versionchanged:: 18.1.0 If *these* is ordered, the order is retained.
     """
     def wrap(cls):
         if getattr(cls, "__class__", None) is None:
             raise TypeError("attrs only works with new-style classes.")
 
         builder = _ClassBuilder(cls, these, slots, frozen, auto_attribs)
 
         if repr is True:
@@ -696,17 +797,17 @@ def _attrs_to_tuple(obj, attrs):
 
 def _make_hash(attrs):
     attrs = tuple(
         a
         for a in attrs
         if a.hash is True or (a.hash is None and a.cmp is True)
     )
 
-    # We cache the generated init methods for the same kinds of attributes.
+    # We cache the generated hash methods for the same kinds of attributes.
     sha1 = hashlib.sha1()
     sha1.update(repr(attrs).encode("utf-8"))
     unique_filename = "<attrs generated hash %s>" % (sha1.hexdigest(),)
     type_hash = hash(unique_filename)
     lines = [
         "def __hash__(self):",
         "    return hash((",
         "        %d," % (type_hash,),
@@ -737,160 +838,216 @@ def _make_hash(attrs):
 def _add_hash(cls, attrs):
     """
     Add a hash method to *cls*.
     """
     cls.__hash__ = _make_hash(attrs)
     return cls
 
 
+def __ne__(self, other):
+    """
+    Check equality and either forward a NotImplemented or return the result
+    negated.
+    """
+    result = self.__eq__(other)
+    if result is NotImplemented:
+        return NotImplemented
+
+    return not result
+
+
 def _make_cmp(attrs):
     attrs = [a for a in attrs if a.cmp]
 
+    # We cache the generated eq methods for the same kinds of attributes.
+    sha1 = hashlib.sha1()
+    sha1.update(repr(attrs).encode("utf-8"))
+    unique_filename = "<attrs generated eq %s>" % (sha1.hexdigest(),)
+    lines = [
+        "def __eq__(self, other):",
+        "    if other.__class__ is not self.__class__:",
+        "        return NotImplemented",
+    ]
+    # We can't just do a big self.x = other.x and... clause due to
+    # irregularities like nan == nan is false but (nan,) == (nan,) is true.
+    if attrs:
+        lines.append("    return  (")
+        others = [
+            "    ) == (",
+        ]
+        for a in attrs:
+            lines.append("        self.%s," % (a.name,))
+            others.append("        other.%s," % (a.name,))
+
+        lines += others + ["    )"]
+    else:
+        lines.append("    return True")
+
+    script = "\n".join(lines)
+    globs = {}
+    locs = {}
+    bytecode = compile(script, unique_filename, "exec")
+    eval(bytecode, globs, locs)
+
+    # In order of debuggers like PDB being able to step through the code,
+    # we add a fake linecache entry.
+    linecache.cache[unique_filename] = (
+        len(script),
+        None,
+        script.splitlines(True),
+        unique_filename,
+    )
+    eq = locs["__eq__"]
+    ne = __ne__
+
     def attrs_to_tuple(obj):
         """
         Save us some typing.
         """
         return _attrs_to_tuple(obj, attrs)
 
-    def eq(self, other):
-        """
-        Automatically created by attrs.
-        """
-        if other.__class__ is self.__class__:
-            return attrs_to_tuple(self) == attrs_to_tuple(other)
-        else:
-            return NotImplemented
-
-    def ne(self, other):
-        """
-        Automatically created by attrs.
-        """
-        result = eq(self, other)
-        if result is NotImplemented:
-            return NotImplemented
-        else:
-            return not result
-
-    def lt(self, other):
+    def __lt__(self, other):
         """
         Automatically created by attrs.
         """
         if isinstance(other, self.__class__):
             return attrs_to_tuple(self) < attrs_to_tuple(other)
         else:
             return NotImplemented
 
-    def le(self, other):
+    def __le__(self, other):
         """
         Automatically created by attrs.
         """
         if isinstance(other, self.__class__):
             return attrs_to_tuple(self) <= attrs_to_tuple(other)
         else:
             return NotImplemented
 
-    def gt(self, other):
+    def __gt__(self, other):
         """
         Automatically created by attrs.
         """
         if isinstance(other, self.__class__):
             return attrs_to_tuple(self) > attrs_to_tuple(other)
         else:
             return NotImplemented
 
-    def ge(self, other):
+    def __ge__(self, other):
         """
         Automatically created by attrs.
         """
         if isinstance(other, self.__class__):
             return attrs_to_tuple(self) >= attrs_to_tuple(other)
         else:
             return NotImplemented
 
-    return eq, ne, lt, le, gt, ge
+    return eq, ne, __lt__, __le__, __gt__, __ge__
 
 
 def _add_cmp(cls, attrs=None):
     """
     Add comparison methods to *cls*.
     """
     if attrs is None:
         attrs = cls.__attrs_attrs__
 
     cls.__eq__, cls.__ne__, cls.__lt__, cls.__le__, cls.__gt__, cls.__ge__ = \
         _make_cmp(attrs)
 
     return cls
 
 
+_already_repring = threading.local()
+
+
 def _make_repr(attrs, ns):
     """
     Make a repr method for *attr_names* adding *ns* to the full name.
     """
     attr_names = tuple(
         a.name
         for a in attrs
         if a.repr
     )
 
-    def repr_(self):
+    def __repr__(self):
         """
         Automatically created by attrs.
         """
+        try:
+            working_set = _already_repring.working_set
+        except AttributeError:
+            working_set = set()
+            _already_repring.working_set = working_set
+
+        if id(self) in working_set:
+            return "..."
         real_cls = self.__class__
         if ns is None:
             qualname = getattr(real_cls, "__qualname__", None)
             if qualname is not None:
                 class_name = qualname.rsplit(">.", 1)[-1]
             else:
                 class_name = real_cls.__name__
         else:
             class_name = ns + "." + real_cls.__name__
 
-        return "{0}({1})".format(
-            class_name,
-            ", ".join(
-                name + "=" + repr(getattr(self, name))
-                for name in attr_names
-            )
-        )
-    return repr_
+        # Since 'self' remains on the stack (i.e.: strongly referenced) for the
+        # duration of this call, it's safe to depend on id(...) stability, and
+        # not need to track the instance and therefore worry about properties
+        # like weakref- or hash-ability.
+        working_set.add(id(self))
+        try:
+            result = [class_name, "("]
+            first = True
+            for name in attr_names:
+                if first:
+                    first = False
+                else:
+                    result.append(", ")
+                result.extend((name, "=", repr(getattr(self, name, NOTHING))))
+            return "".join(result) + ")"
+        finally:
+            working_set.remove(id(self))
+    return __repr__
 
 
 def _add_repr(cls, ns=None, attrs=None):
     """
     Add a repr method to *cls*.
     """
     if attrs is None:
         attrs = cls.__attrs_attrs__
 
-    repr_ = _make_repr(attrs, ns)
-    cls.__repr__ = repr_
+    cls.__repr__ = _make_repr(attrs, ns)
     return cls
 
 
-def _make_init(attrs, post_init, frozen):
+def _make_init(attrs, post_init, frozen, slots, super_attr_map):
     attrs = [
         a
         for a in attrs
         if a.init or a.default is not NOTHING
     ]
 
     # We cache the generated init methods for the same kinds of attributes.
     sha1 = hashlib.sha1()
     sha1.update(repr(attrs).encode("utf-8"))
     unique_filename = "<attrs generated init {0}>".format(
         sha1.hexdigest()
     )
 
-    script, globs = _attrs_to_init_script(
+    script, globs, annotations = _attrs_to_init_script(
         attrs,
         frozen,
+        slots,
         post_init,
+        super_attr_map,
     )
     locs = {}
     bytecode = compile(script, unique_filename, "exec")
     attr_dict = dict((a.name, a) for a in attrs)
     globs.update({
         "NOTHING": NOTHING,
         "attr_dict": attr_dict,
     })
@@ -904,34 +1061,38 @@ def _make_init(attrs, post_init, frozen)
     # we add a fake linecache entry.
     linecache.cache[unique_filename] = (
         len(script),
         None,
         script.splitlines(True),
         unique_filename,
     )
 
-    return locs["__init__"]
+    __init__ = locs["__init__"]
+    __init__.__annotations__ = annotations
+    return __init__
 
 
 def _add_init(cls, frozen):
     """
     Add a __init__ method to *cls*.  If *frozen* is True, make it immutable.
     """
     cls.__init__ = _make_init(
         cls.__attrs_attrs__,
         getattr(cls, "__attrs_post_init__", False),
         frozen,
+        _is_slot_cls(cls),
+        {},
     )
     return cls
 
 
 def fields(cls):
     """
-    Returns the tuple of ``attrs`` attributes for a class.
+    Return the tuple of ``attrs`` attributes for a class.
 
     The tuple also allows accessing the fields by their names (see below for
     examples).
 
     :param type cls: Class to introspect.
 
     :raise TypeError: If *cls* is not a class.
     :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
@@ -947,16 +1108,44 @@ def fields(cls):
     attrs = getattr(cls, "__attrs_attrs__", None)
     if attrs is None:
         raise NotAnAttrsClassError(
             "{cls!r} is not an attrs-decorated class.".format(cls=cls)
         )
     return attrs
 
 
+def fields_dict(cls):
+    """
+    Return an ordered dictionary of ``attrs`` attributes for a class, whose
+    keys are the attribute names.
+
+    :param type cls: Class to introspect.
+
+    :raise TypeError: If *cls* is not a class.
+    :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
+        class.
+
+    :rtype: an ordered dict where keys are attribute names and values are
+        :class:`attr.Attribute`\\ s. This will be a :class:`dict` if it's
+        naturally ordered like on Python 3.6+ or an
+        :class:`~collections.OrderedDict` otherwise.
+
+    .. versionadded:: 18.1.0
+    """
+    if not isclass(cls):
+        raise TypeError("Passed object must be a class.")
+    attrs = getattr(cls, "__attrs_attrs__", None)
+    if attrs is None:
+        raise NotAnAttrsClassError(
+            "{cls!r} is not an attrs-decorated class.".format(cls=cls)
+        )
+    return ordered_dict(((a.name, a) for a in attrs))
+
+
 def validate(inst):
     """
     Validate all attributes on *inst* that have a validator.
 
     Leaves all exceptions through.
 
     :param inst: Instance of a class with ``attrs`` attributes.
     """
@@ -964,150 +1153,216 @@ def validate(inst):
         return
 
     for a in fields(inst.__class__):
         v = a.validator
         if v is not None:
             v(inst, a, getattr(inst, a.name))
 
 
-def _attrs_to_init_script(attrs, frozen, post_init):
+def _is_slot_cls(cls):
+    return "__slots__" in cls.__dict__
+
+
+def _is_slot_attr(a_name, super_attr_map):
+    """
+    Check if the attribute name comes from a slot class.
+    """
+    return a_name in super_attr_map and _is_slot_cls(super_attr_map[a_name])
+
+
+def _attrs_to_init_script(attrs, frozen, slots, post_init, super_attr_map):
     """
     Return a script of an initializer for *attrs* and a dict of globals.
 
     The globals are expected by the generated script.
 
-     If *frozen* is True, we cannot set the attributes directly so we use
+    If *frozen* is True, we cannot set the attributes directly so we use
     a cached ``object.__setattr__``.
     """
     lines = []
+    any_slot_ancestors = any(
+        _is_slot_attr(a.name, super_attr_map)
+        for a in attrs
+    )
     if frozen is True:
-        lines.append(
-            # Circumvent the __setattr__ descriptor to save one lookup per
-            # assignment.
-            "_setattr = _cached_setattr.__get__(self, self.__class__)"
-        )
+        if slots is True:
+            lines.append(
+                # Circumvent the __setattr__ descriptor to save one lookup per
+                # assignment.
+                "_setattr = _cached_setattr.__get__(self, self.__class__)"
+            )
+
+            def fmt_setter(attr_name, value_var):
+                return "_setattr('%(attr_name)s', %(value_var)s)" % {
+                    "attr_name": attr_name,
+                    "value_var": value_var,
+                }
 
-        def fmt_setter(attr_name, value_var):
-            return "_setattr('%(attr_name)s', %(value_var)s)" % {
-                "attr_name": attr_name,
-                "value_var": value_var,
-            }
+            def fmt_setter_with_converter(attr_name, value_var):
+                conv_name = _init_converter_pat.format(attr_name)
+                return "_setattr('%(attr_name)s', %(conv)s(%(value_var)s))" % {
+                    "attr_name": attr_name,
+                    "value_var": value_var,
+                    "conv": conv_name,
+                }
+        else:
+            # Dict frozen classes assign directly to __dict__.
+            # But only if the attribute doesn't come from an ancestor slot
+            # class.
+            lines.append(
+                "_inst_dict = self.__dict__"
+            )
+            if any_slot_ancestors:
+                lines.append(
+                    # Circumvent the __setattr__ descriptor to save one lookup
+                    # per assignment.
+                    "_setattr = _cached_setattr.__get__(self, self.__class__)"
+                )
 
-        def fmt_setter_with_converter(attr_name, value_var):
-            conv_name = _init_convert_pat.format(attr_name)
-            return "_setattr('%(attr_name)s', %(conv)s(%(value_var)s))" % {
-                "attr_name": attr_name,
-                "value_var": value_var,
-                "conv": conv_name,
-            }
+            def fmt_setter(attr_name, value_var):
+                if _is_slot_attr(attr_name, super_attr_map):
+                    res = "_setattr('%(attr_name)s', %(value_var)s)" % {
+                        "attr_name": attr_name,
+                        "value_var": value_var,
+                    }
+                else:
+                    res = "_inst_dict['%(attr_name)s'] = %(value_var)s" % {
+                        "attr_name": attr_name,
+                        "value_var": value_var,
+                    }
+                return res
+
+            def fmt_setter_with_converter(attr_name, value_var):
+                conv_name = _init_converter_pat.format(attr_name)
+                if _is_slot_attr(attr_name, super_attr_map):
+                    tmpl = "_setattr('%(attr_name)s', %(c)s(%(value_var)s))"
+                else:
+                    tmpl = "_inst_dict['%(attr_name)s'] = %(c)s(%(value_var)s)"
+                return tmpl % {
+                    "attr_name": attr_name,
+                    "value_var": value_var,
+                    "c": conv_name,
+                }
     else:
+        # Not frozen.
         def fmt_setter(attr_name, value):
             return "self.%(attr_name)s = %(value)s" % {
                 "attr_name": attr_name,
                 "value": value,
             }
 
         def fmt_setter_with_converter(attr_name, value_var):
-            conv_name = _init_convert_pat.format(attr_name)
+            conv_name = _init_converter_pat.format(attr_name)
             return "self.%(attr_name)s = %(conv)s(%(value_var)s)" % {
                 "attr_name": attr_name,
                 "value_var": value_var,
                 "conv": conv_name,
             }
 
     args = []
     attrs_to_validate = []
 
     # This is a dictionary of names to validator and converter callables.
     # Injecting this into __init__ globals lets us avoid lookups.
     names_for_globals = {}
+    annotations = {'return': None}
 
     for a in attrs:
         if a.validator:
             attrs_to_validate.append(a)
         attr_name = a.name
         arg_name = a.name.lstrip("_")
         has_factory = isinstance(a.default, Factory)
         if has_factory and a.default.takes_self:
             maybe_self = "self"
         else:
             maybe_self = ""
         if a.init is False:
             if has_factory:
                 init_factory_name = _init_factory_pat.format(a.name)
-                if a.convert is not None:
+                if a.converter is not None:
                     lines.append(fmt_setter_with_converter(
                         attr_name,
                         init_factory_name + "({0})".format(maybe_self)))
-                    conv_name = _init_convert_pat.format(a.name)
-                    names_for_globals[conv_name] = a.convert
+                    conv_name = _init_converter_pat.format(a.name)
+                    names_for_globals[conv_name] = a.converter
                 else:
                     lines.append(fmt_setter(
                         attr_name,
                         init_factory_name + "({0})".format(maybe_self)
                     ))
                 names_for_globals[init_factory_name] = a.default.factory
             else:
-                if a.convert is not None:
+                if a.converter is not None:
                     lines.append(fmt_setter_with_converter(
                         attr_name,
                         "attr_dict['{attr_name}'].default"
                         .format(attr_name=attr_name)
                     ))
-                    conv_name = _init_convert_pat.format(a.name)
-                    names_for_globals[conv_name] = a.convert
+                    conv_name = _init_converter_pat.format(a.name)
+                    names_for_globals[conv_name] = a.converter
                 else:
                     lines.append(fmt_setter(
                         attr_name,
                         "attr_dict['{attr_name}'].default"
                         .format(attr_name=attr_name)
                     ))
         elif a.default is not NOTHING and not has_factory:
             args.append(
                 "{arg_name}=attr_dict['{attr_name}'].default".format(
                     arg_name=arg_name,
                     attr_name=attr_name,
                 )
             )
-            if a.convert is not None:
+            if a.converter is not None:
                 lines.append(fmt_setter_with_converter(attr_name, arg_name))
-                names_for_globals[_init_convert_pat.format(a.name)] = a.convert
+                names_for_globals[_init_converter_pat.format(a.name)] = (
+                    a.converter
+                )
             else:
                 lines.append(fmt_setter(attr_name, arg_name))
         elif has_factory:
             args.append("{arg_name}=NOTHING".format(arg_name=arg_name))
             lines.append("if {arg_name} is not NOTHING:"
                          .format(arg_name=arg_name))
             init_factory_name = _init_factory_pat.format(a.name)
-            if a.convert is not None:
-                lines.append("    " + fmt_setter_with_converter(attr_name,
-                                                                arg_name))
+            if a.converter is not None:
+                lines.append("    " + fmt_setter_with_converter(
+                    attr_name, arg_name
+                ))
                 lines.append("else:")
                 lines.append("    " + fmt_setter_with_converter(
                     attr_name,
                     init_factory_name + "({0})".format(maybe_self)
                 ))
-                names_for_globals[_init_convert_pat.format(a.name)] = a.convert
+                names_for_globals[_init_converter_pat.format(a.name)] = (
+                    a.converter
+                )
             else:
                 lines.append("    " + fmt_setter(attr_name, arg_name))
                 lines.append("else:")
                 lines.append("    " + fmt_setter(
                     attr_name,
                     init_factory_name + "({0})".format(maybe_self)
                 ))
             names_for_globals[init_factory_name] = a.default.factory
         else:
             args.append(arg_name)
-            if a.convert is not None:
+            if a.converter is not None:
                 lines.append(fmt_setter_with_converter(attr_name, arg_name))
-                names_for_globals[_init_convert_pat.format(a.name)] = a.convert
+                names_for_globals[_init_converter_pat.format(a.name)] = (
+                    a.converter
+                )
             else:
                 lines.append(fmt_setter(attr_name, arg_name))
 
+        if a.init is True and a.converter is None and a.type is not None:
+            annotations[arg_name] = a.type
+
     if attrs_to_validate:  # we can skip this if there are no validators.
         names_for_globals["_config"] = _config
         lines.append("if _config._run_validators is True:")
         for a in attrs_to_validate:
             val_name = "__attr_validator_{}".format(a.name)
             attr_name = "__attr_{}".format(a.name)
             lines.append("    {}(self, {}, self.{})".format(
                 val_name, attr_name, a.name))
@@ -1117,71 +1372,101 @@ def _attrs_to_init_script(attrs, frozen,
         lines.append("self.__attrs_post_init__()")
 
     return """\
 def __init__(self, {args}):
     {lines}
 """.format(
         args=", ".join(args),
         lines="\n    ".join(lines) if lines else "pass",
-    ), names_for_globals
+    ), names_for_globals, annotations
 
 
 class Attribute(object):
     """
     *Read-only* representation of an attribute.
 
     :attribute name: The name of the attribute.
 
     Plus *all* arguments of :func:`attr.ib`.
+
+    For the version history of the fields, see :func:`attr.ib`.
     """
     __slots__ = (
         "name", "default", "validator", "repr", "cmp", "hash", "init",
-        "convert", "metadata", "type"
+        "metadata", "type", "converter",
     )
 
     def __init__(self, name, default, validator, repr, cmp, hash, init,
-                 convert=None, metadata=None, type=None):
+                 convert=None, metadata=None, type=None, converter=None):
         # Cache this descriptor here to speed things up later.
         bound_setattr = _obj_setattr.__get__(self, Attribute)
 
+        # Despite the big red warning, people *do* instantiate `Attribute`
+        # themselves.
+        if convert is not None:
+            if converter is not None:
+                raise RuntimeError(
+                    "Can't pass both `convert` and `converter`.  "
+                    "Please use `converter` only."
+                )
+            warnings.warn(
+                "The `convert` argument is deprecated in favor of `converter`."
+                "  It will be removed after 2019/01.",
+                DeprecationWarning, stacklevel=2
+            )
+            converter = convert
+
         bound_setattr("name", name)
         bound_setattr("default", default)
         bound_setattr("validator", validator)
         bound_setattr("repr", repr)
         bound_setattr("cmp", cmp)
         bound_setattr("hash", hash)
         bound_setattr("init", init)
-        bound_setattr("convert", convert)
-        bound_setattr("metadata", (metadata_proxy(metadata) if metadata
-                                   else _empty_metadata_singleton))
+        bound_setattr("converter", converter)
+        bound_setattr("metadata", (
+            metadata_proxy(metadata) if metadata
+            else _empty_metadata_singleton
+        ))
         bound_setattr("type", type)
 
     def __setattr__(self, name, value):
         raise FrozenInstanceError()
 
+    @property
+    def convert(self):
+        warnings.warn(
+            "The `convert` attribute is deprecated in favor of `converter`.  "
+            "It will be removed after 2019/01.",
+            DeprecationWarning, stacklevel=2,
+        )
+        return self.converter
+
     @classmethod
     def from_counting_attr(cls, name, ca, type=None):
         # type holds the annotated value. deal with conflicts:
         if type is None:
             type = ca.type
         elif ca.type is not None:
             raise ValueError(
                 "Type annotation and type argument cannot both be present"
             )
         inst_dict = {
             k: getattr(ca, k)
             for k
             in Attribute.__slots__
             if k not in (
-                "name", "validator", "default", "type"
-            )  # exclude methods
+                "name", "validator", "default", "type", "convert",
+            )  # exclude methods and deprecated alias
         }
-        return cls(name=name, validator=ca._validator, default=ca._default,
-                   type=type, **inst_dict)
+        return cls(
+            name=name, validator=ca._validator, default=ca._default, type=type,
+            **inst_dict
+        )
 
     # Don't use _add_pickle since fields(Attribute) doesn't work
     def __getstate__(self):
         """
         Play nice with pickle.
         """
         return tuple(getattr(self, name) if name != "metadata"
                      else dict(self.metadata)
@@ -1195,62 +1480,65 @@ class Attribute(object):
         for name, value in zip(self.__slots__, state):
             if name != "metadata":
                 bound_setattr(name, value)
             else:
                 bound_setattr(name, metadata_proxy(value) if value else
                               _empty_metadata_singleton)
 
 
-_a = [Attribute(name=name, default=NOTHING, validator=None,
-                repr=True, cmp=True, hash=(name != "metadata"), init=True)
-      for name in Attribute.__slots__]
+_a = [
+    Attribute(name=name, default=NOTHING, validator=None,
+              repr=True, cmp=True, hash=(name != "metadata"), init=True)
+    for name in Attribute.__slots__
+    if name != "convert"  # XXX: remove once `convert` is gone
+]
 
 Attribute = _add_hash(
     _add_cmp(_add_repr(Attribute, attrs=_a), attrs=_a),
     attrs=[a for a in _a if a.hash]
 )
 
 
 class _CountingAttr(object):
     """
     Intermediate representation of attributes that uses a counter to preserve
     the order in which the attributes have been defined.
 
     *Internal* data structure of the attrs library.  Running into is most
     likely the result of a bug like a forgotten `@attr.s` decorator.
     """
     __slots__ = ("counter", "_default", "repr", "cmp", "hash", "init",
-                 "metadata", "_validator", "convert", "type")
+                 "metadata", "_validator", "converter", "type")
     __attrs_attrs__ = tuple(
         Attribute(name=name, default=NOTHING, validator=None,
                   repr=True, cmp=True, hash=True, init=True)
         for name
         in ("counter", "_default", "repr", "cmp", "hash", "init",)
     ) + (
         Attribute(name="metadata", default=None, validator=None,
                   repr=True, cmp=True, hash=False, init=True),
     )
     cls_counter = 0
 
-    def __init__(self, default, validator, repr, cmp, hash, init, convert,
+    def __init__(self, default, validator, repr, cmp, hash, init, converter,
                  metadata, type):
         _CountingAttr.cls_counter += 1
         self.counter = _CountingAttr.cls_counter
         self._default = default
         # If validator is a list/tuple, wrap it using helper validator.
         if validator and isinstance(validator, (list, tuple)):
             self._validator = and_(*validator)
         else:
             self._validator = validator
         self.repr = repr
         self.cmp = cmp
         self.hash = hash
         self.init = init
-        self.convert = convert
+        self.converter = converter
         self.metadata = metadata
         self.type = type
 
     def validator(self, meth):
         """
         Decorator that adds *meth* to the list of validators.
 
         Returns *meth* unchanged.
@@ -1315,26 +1603,32 @@ def make_class(name, attrs, bases=(objec
     """
     A quick way to create a new class called *name* with *attrs*.
 
     :param name: The name for the new class.
     :type name: str
 
     :param attrs: A list of names or a dictionary of mappings of names to
         attributes.
+
+        If *attrs* is a list or an ordered dict (:class:`dict` on Python 3.6+,
+        :class:`collections.OrderedDict` otherwise), the order is deduced from
+        the order of the names or attributes inside *attrs*.  Otherwise the
+        order of the definition of the attributes is used.
     :type attrs: :class:`list` or :class:`dict`
 
     :param tuple bases: Classes that the new class will subclass.
 
     :param attributes_arguments: Passed unmodified to :func:`attr.s`.
 
     :return: A new class with *attrs*.
     :rtype: type
 
-    ..  versionadded:: 17.1.0 *bases*
+    .. versionadded:: 17.1.0 *bases*
+    .. versionchanged:: 18.1.0 If *attrs* is ordered, the order is retained.
     """
     if isinstance(attrs, dict):
         cls_dict = attrs
     elif isinstance(attrs, (list, tuple)):
         cls_dict = dict((a, attrib()) for a in attrs)
     else:
         raise TypeError("attrs argument must be a dict or a list.")
 
--- a/testing/web-platform/tests/tools/third_party/attrs/src/attr/filters.py
+++ b/testing/web-platform/tests/tools/third_party/attrs/src/attr/filters.py
@@ -18,34 +18,34 @@ def _split_what(what):
     )
 
 
 def include(*what):
     """
     Whitelist *what*.
 
     :param what: What to whitelist.
-    :type what: :class:`list` of :class:`type` or :class:`attr.Attribute`\ s
+    :type what: :class:`list` of :class:`type` or :class:`attr.Attribute`\\ s
 
     :rtype: :class:`callable`
     """
     cls, attrs = _split_what(what)
 
     def include_(attribute, value):
         return value.__class__ in cls or attribute in attrs
 
     return include_
 
 
 def exclude(*what):
     """
     Blacklist *what*.
 
     :param what: What to blacklist.
-    :type what: :class:`list` of classes or :class:`attr.Attribute`\ s.
+    :type what: :class:`list` of classes or :class:`attr.Attribute`\\ s.
 
     :rtype: :class:`callable`
     """
     cls, attrs = _split_what(what)
 
     def exclude_(attribute, value):
         return value.__class__ not in cls and attribute not in attrs
 
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/tools/third_party/attrs/tests/strategies.py
@@ -0,0 +1,203 @@
+"""
+Testing strategies for Hypothesis-based tests.
+"""
+
+import keyword
+import string
+
+from collections import OrderedDict
+
+from hypothesis import strategies as st
+
+import attr
+
+from .utils import make_class
+
+
+def gen_attr_names():
+    """
+    Generate names for attributes, 'a'...'z', then 'aa'...'zz'.
+
+    ~702 different attribute names should be enough in practice.
+
+    Some short strings (such as 'as') are keywords, so we skip them.
+    """
+    lc = string.ascii_lowercase
+    for c in lc:
+        yield c
+    for outer in lc:
+        for inner in lc:
+            res = outer + inner
+            if keyword.iskeyword(res):
+                continue
+            yield outer + inner
+
+
+def maybe_underscore_prefix(source):
+    """
+    A generator to sometimes prepend an underscore.
+    """
+    to_underscore = False
+    for val in source:
+        yield val if not to_underscore else '_' + val
+        to_underscore = not to_underscore
+
+
+def _create_hyp_class(attrs):
+    """
+    A helper function for Hypothesis to generate attrs classes.
+    """
+    return make_class(
+        "HypClass", dict(zip(gen_attr_names(), attrs))
+    )
+
+
+def _create_hyp_nested_strategy(simple_class_strategy):
+    """
+    Create a recursive attrs class.
+
+    Given a strategy for building (simpler) classes, create and return
+    a strategy for building classes that have as an attribute: either just
+    the simpler class, a list of simpler classes, a tuple of simpler classes,
+    an ordered dict or a dict mapping the string "cls" to a simpler class.
+    """
+    # Use a tuple strategy to combine simple attributes and an attr class.
+    def just_class(tup):
+        combined_attrs = list(tup[0])
+        combined_attrs.append(attr.ib(default=attr.Factory(tup[1])))
+        return _create_hyp_class(combined_attrs)
+
+    def list_of_class(tup):
+        default = attr.Factory(lambda: [tup[1]()])
+        combined_attrs = list(tup[0])
+        combined_attrs.append(attr.ib(default=default))
+        return _create_hyp_class(combined_attrs)
+
+    def tuple_of_class(tup):
+        default = attr.Factory(lambda: (tup[1](),))
+        combined_attrs = list(tup[0])
+        combined_attrs.append(attr.ib(default=default))
+        return _create_hyp_class(combined_attrs)
+
+    def dict_of_class(tup):
+        default = attr.Factory(lambda: {"cls": tup[1]()})
+        combined_attrs = list(tup[0])
+        combined_attrs.append(attr.ib(default=default))
+        return _create_hyp_class(combined_attrs)
+
+    def ordereddict_of_class(tup):
+        default = attr.Factory(lambda: OrderedDict([("cls", tup[1]())]))
+        combined_attrs = list(tup[0])
+        combined_attrs.append(attr.ib(default=default))
+        return _create_hyp_class(combined_attrs)
+
+    # A strategy producing tuples of the form ([list of attributes], <given
+    # class strategy>).
+    attrs_and_classes = st.tuples(list_of_attrs, simple_class_strategy)
+
+    return st.one_of(attrs_and_classes.map(just_class),
+                     attrs_and_classes.map(list_of_class),
+                     attrs_and_classes.map(tuple_of_class),
+                     attrs_and_classes.map(dict_of_class),
+                     attrs_and_classes.map(ordereddict_of_class))
+
+
+bare_attrs = st.builds(attr.ib, default=st.none())
+int_attrs = st.integers().map(lambda i: attr.ib(default=i))
+str_attrs = st.text().map(lambda s: attr.ib(default=s))
+float_attrs = st.floats().map(lambda f: attr.ib(default=f))
+dict_attrs = (st.dictionaries(keys=st.text(), values=st.integers())
+              .map(lambda d: attr.ib(default=d)))
+
+simple_attrs_without_metadata = (bare_attrs | int_attrs | str_attrs |
+                                 float_attrs | dict_attrs)
+
+
+@st.composite
+def simple_attrs_with_metadata(draw):
+    """
+    Create a simple attribute with arbitrary metadata.
+    """
+    c_attr = draw(simple_attrs)
+    keys = st.booleans() | st.binary() | st.integers() | st.text()
+    vals = st.booleans() | st.binary() | st.integers() | st.text()
+    metadata = draw(st.dictionaries(
+        keys=keys, values=vals, min_size=1, max_size=5))
+
+    return attr.ib(
+        default=c_attr._default,
+        validator=c_attr._validator,
+        repr=c_attr.repr,
+        cmp=c_attr.cmp,
+        hash=c_attr.hash,
+        init=c_attr.init,
+        metadata=metadata,
+        type=None,
+        converter=c_attr.converter,
+    )
+
+
+simple_attrs = simple_attrs_without_metadata | simple_attrs_with_metadata()
+
+# Python functions support up to 255 arguments.
+list_of_attrs = st.lists(simple_attrs, max_size=9)
+
+
+@st.composite
+def simple_classes(draw, slots=None, frozen=None, private_attrs=None):
+    """
+    A strategy that generates classes with default non-attr attributes.
+
+    For example, this strategy might generate a class such as:
+
+    @attr.s(slots=True, frozen=True)
+    class HypClass:
+        a = attr.ib(default=1)
+        _b = attr.ib(default=None)
+        c = attr.ib(default='text')
+        _d = attr.ib(default=1.0)
+        c = attr.ib(default={'t': 1})
+
+    By default, all combinations of slots and frozen classes will be generated.
+    If `slots=True` is passed in, only slots classes will be generated, and
+    if `slots=False` is passed in, no slot classes will be generated. The same
+    applies to `frozen`.
+
+    By default, some attributes will be private (i.e. prefixed with an
+    underscore). If `private_attrs=True` is passed in, all attributes will be
+    private, and if `private_attrs=False`, no attributes will be private.
+    """
+    attrs = draw(list_of_attrs)
+    frozen_flag = draw(st.booleans()) if frozen is None else frozen
+    slots_flag = draw(st.booleans()) if slots is None else slots
+
+    if private_attrs is None:
+        attr_names = maybe_underscore_prefix(gen_attr_names())
+    elif private_attrs is True:
+        attr_names = ('_' + n for n in gen_attr_names())
+    elif private_attrs is False:
+        attr_names = gen_attr_names()
+
+    cls_dict = dict(zip(attr_names, attrs))
+    post_init_flag = draw(st.booleans())
+    if post_init_flag:
+        def post_init(self):
+            pass
+        cls_dict["__attrs_post_init__"] = post_init
+
+    return make_class(
+        "HypClass",
+        cls_dict,
+        slots=slots_flag,
+        frozen=frozen_flag,
+    )
+
+
+# st.recursive works by taking a base strategy (in this case, simple_classes)
+# and a special function.  This function receives a strategy, and returns
+# another strategy (building on top of the base strategy).
+nested_classes = st.recursive(
+    simple_classes(),
+    _create_hyp_nested_strategy,
+    max_leaves=10
+)
--- a/testing/web-platform/tests/tools/third_party/attrs/tests/test_annotations.py
+++ b/testing/web-platform/tests/tools/third_party/attrs/tests/test_annotations.py
@@ -6,16 +6,17 @@ Python 3.6+ only.
 
 import types
 import typing
 
 import pytest
 
 import attr
 
+from attr._make import _classvar_prefixes
 from attr.exceptions import UnannotatedAttributeError
 
 
 class TestAnnotations:
     """
     Tests for types derived from variable annotations (PEP-526).
     """
 
@@ -27,16 +28,21 @@ class TestAnnotations:
         class C:
             x: int = attr.ib()
             y = attr.ib(type=str)
             z = attr.ib()
 
         assert int is attr.fields(C).x.type
         assert str is attr.fields(C).y.type
         assert None is attr.fields(C).z.type
+        assert C.__init__.__annotations__ == {
+            'x': int,
+            'y': str,
+            'return': None,
+        }
 
     def test_catches_basic_type_conflict(self):
         """
         Raises ValueError if type is specified both ways.
         """
         with pytest.raises(ValueError) as e:
             @attr.s
             class C:
@@ -52,27 +58,36 @@ class TestAnnotations:
         """
         @attr.s
         class C:
             x: typing.List[int] = attr.ib()
             y = attr.ib(type=typing.Optional[str])
 
         assert typing.List[int] is attr.fields(C).x.type
         assert typing.Optional[str] is attr.fields(C).y.type
+        assert C.__init__.__annotations__ == {
+            'x': typing.List[int],
+            'y': typing.Optional[str],
+            'return': None,
+        }
 
     def test_only_attrs_annotations_collected(self):
         """
         Annotations that aren't set to an attr.ib are ignored.
         """
         @attr.s
         class C:
             x: typing.List[int] = attr.ib()
             y: int
 
         assert 1 == len(attr.fields(C))
+        assert C.__init__.__annotations__ == {
+            'x': typing.List[int],
+            'return': None,
+        }
 
     @pytest.mark.parametrize("slots", [True, False])
     def test_auto_attribs(self, slots):
         """
         If *auto_attribs* is True, bare annotations are collected too.
         Defaults work and class variables are ignored.
         """
         @attr.s(auto_attribs=True, slots=slots)
@@ -110,16 +125,25 @@ class TestAnnotations:
 
             assert 2 == i.y
         else:
             assert isinstance(C.y, types.MemberDescriptorType)
 
             i.y = 23
             assert 23 == i.y
 
+        assert C.__init__.__annotations__ == {
+            'a': int,
+            'x': typing.List[int],
+            'y': int,
+            'z': int,
+            'foo': typing.Any,
+            'return': None,
+        }
+
     @pytest.mark.parametrize("slots", [True, False])
     def test_auto_attribs_unannotated(self, slots):
         """
         Unannotated `attr.ib`s raise an error.
         """
         with pytest.raises(UnannotatedAttributeError) as e:
             @attr.s(slots=slots, auto_attribs=True)
             class C:
@@ -149,8 +173,57 @@ class TestAnnotations:
             b: int = 2
 
         @attr.s(slots=slots, auto_attribs=True)
         class C(A):
             pass
 
         assert "B(a=1, b=2)" == repr(B())
         assert "C(a=1)" == repr(C())
+
+        assert A.__init__.__annotations__ == {
+            'a': int,
+            'return': None,
+        }
+        assert B.__init__.__annotations__ == {
+            'a': int,
+            'b': int,
+            'return': None,
+        }
+        assert C.__init__.__annotations__ == {
+            'a': int,
+            'return': None,
+        }
+
+    def test_converter_annotations(self):
+        """
+        Attributes with converters don't have annotations.
+        """
+
+        @attr.s(auto_attribs=True)
+        class A:
+            a: int = attr.ib(converter=int)
+
+        assert A.__init__.__annotations__ == {'return': None}
+
+    @pytest.mark.parametrize("slots", [True, False])
+    @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: classvar + '[int]' = 23
+            a: 'int'
+            x: 'typing.List[int]' = attr.Factory(list)
+            y: 'int' = 2
+            z: 'int' = attr.ib(default=3)
+            foo: 'typing.Any' = None
+
+        assert C.__init__.__annotations__ == {
+            'a': 'int',
+            'x': 'typing.List[int]',
+            'y': 'int',
+            'z': 'int',
+            'foo': 'typing.Any',
+            'return': None,
+        }
--- a/testing/web-platform/tests/tools/third_party/attrs/tests/test_dark_magic.py
+++ b/testing/web-platform/tests/tools/third_party/attrs/tests/test_dark_magic.py
@@ -375,8 +375,41 @@ class TestDarkMagic(object):
             d = attr.ib(default=3.14)
 
         @attr.s
         class E(D):
             y = attr.ib(default=3)
             z = attr.ib(default=4)
 
         assert "E(c=100, b=23, a=42, x=2, d=3.14, y=3, z=4)" == repr(E())
+
+    @pytest.mark.parametrize("base_slots", [True, False])
+    @pytest.mark.parametrize("sub_slots", [True, False])
+    @pytest.mark.parametrize("base_frozen", [True, False])
+    @pytest.mark.parametrize("sub_frozen", [True, False])
+    @pytest.mark.parametrize("base_converter", [True, False])
+    @pytest.mark.parametrize("sub_converter", [True, False])
+    def test_frozen_slots_combo(self, base_slots, sub_slots, base_frozen,
+                                sub_frozen, base_converter, sub_converter):
+        """
+        A class with a single attribute, inheriting from another class
+        with a single attribute.
+        """
+
+        @attr.s(frozen=base_frozen, slots=base_slots)
+        class Base(object):
+            a = attr.ib(converter=int if base_converter else None)
+
+        @attr.s(frozen=sub_frozen, slots=sub_slots)
+        class Sub(Base):
+            b = attr.ib(converter=int if sub_converter else None)
+
+        i = Sub("1", "2")
+
+        assert i.a == (1 if base_converter else "1")
+        assert i.b == (2 if sub_converter else "2")
+
+        if base_frozen or sub_frozen:
+            with pytest.raises(FrozenInstanceError):
+                i.a = "2"
+
+            with pytest.raises(FrozenInstanceError):
+                i.b = "3"
--- a/testing/web-platform/tests/tools/third_party/attrs/tests/test_dunders.py
+++ b/testing/web-platform/tests/tools/third_party/attrs/tests/test_dunders.py
@@ -182,29 +182,53 @@ class TestAddRepr(object):
 
     @pytest.mark.parametrize("cls", [ReprC, ReprCSlots])
     def test_repr_works(self, cls):
         """
         repr returns a sensible value.
         """
         assert "C(a=1, b=2)" == repr(cls(1, 2))
 
+    def test_infinite_recursion(self):
+        """
+        In the presence of a cyclic graph, repr will emit an ellipsis and not
+        raise an exception.
+        """
+        @attr.s
+        class Cycle(object):
+            value = attr.ib(default=7)
+            cycle = attr.ib(default=None)
+
+        cycle = Cycle()
+        cycle.cycle = cycle
+        assert "Cycle(value=7, cycle=...)" == repr(cycle)
+
     def test_underscores(self):
         """
         repr does not strip underscores.
         """
         class C(object):
             __attrs_attrs__ = [simple_attr("_x")]
 
         C = _add_repr(C)
         i = C()
         i._x = 42
 
         assert "C(_x=42)" == repr(i)
 
+    def test_repr_uninitialized_member(self):
+        """
+        repr signals unset attributes
+        """
+        C = make_class("C", {
+            "a": attr.ib(init=False),
+        })
+
+        assert "C(a=NOTHING)" == repr(C())
+
     @given(add_str=booleans(), slots=booleans())
     def test_str(self, add_str, slots):
         """
         If str is True, it returns the same as repr.
 
         This only makes sense when subclassing a class with an poor __str__
         (like Exceptions).
         """
--- a/testing/web-platform/tests/tools/third_party/attrs/tests/test_funcs.py
+++ b/testing/web-platform/tests/tools/third_party/attrs/tests/test_funcs.py
@@ -3,27 +3,27 @@ Tests for `attr._funcs`.
 """
 
 from __future__ import absolute_import, division, print_function
 
 from collections import Mapping, OrderedDict, Sequence
 
 import pytest
 
+from hypothesis import HealthCheck, assume, given, settings
 from hypothesis import strategies as st
-from hypothesis import HealthCheck, assume, given, settings
 
 import attr
 
 from attr import asdict, assoc, astuple, evolve, fields, has
 from attr._compat import TYPE
 from attr.exceptions import AttrsAttributeNotFoundError
 from attr.validators import instance_of
 
-from .utils import nested_classes, simple_classes
+from .strategies import nested_classes, simple_classes
 
 
 MAPPING_TYPES = (dict, OrderedDict)
 SEQUENCE_TYPES = (list, tuple)
 
 
 class TestAsDict(object):
     """
--- a/testing/web-platform/tests/tools/third_party/attrs/tests/test_make.py
+++ b/testing/web-platform/tests/tools/third_party/attrs/tests/test_make.py
@@ -1,38 +1,42 @@
 """
 Tests for `attr._make`.
 """
 
 from __future__ import absolute_import, division, print_function
 
+import copy
 import inspect
+import itertools
 import sys
 
 from operator import attrgetter
 
 import pytest
 
 from hypothesis import given
 from hypothesis.strategies import booleans, integers, lists, sampled_from, text
 
 import attr
 
 from attr import _config
-from attr._compat import PY2
+from attr._compat import PY2, ordered_dict
 from attr._make import (
     Attribute, Factory, _AndValidator, _Attributes, _ClassBuilder,
-    _CountingAttr, _transform_attrs, and_, fields, make_class, validate
+    _CountingAttr, _transform_attrs, and_, fields, fields_dict, make_class,
+    validate
 )
 from attr.exceptions import DefaultAlreadySetError, NotAnAttrsClassError
 
-from .utils import (
-    gen_attr_names, list_of_attrs, simple_attr, simple_attrs,
+from .strategies import (
+    gen_attr_names, list_of_attrs, simple_attrs, simple_attrs_with_metadata,
     simple_attrs_without_metadata, simple_classes
 )
+from .utils import simple_attr
 
 
 attrs_st = simple_attrs.map(lambda c: Attribute.from_counting_attr("name", c))
 
 
 class TestCountingAttr(object):
     """
     Tests for `attr`.
@@ -115,16 +119,76 @@ class TestCountingAttr(object):
 
         @a.default
         def f(self):
             pass
 
         assert Factory(f, True) == a._default
 
 
+class TestAttribute(object):
+    """
+    Tests for `attr.Attribute`.
+    """
+    def test_deprecated_convert_argument(self):
+        """
+        Using *convert* raises a DeprecationWarning and sets the converter
+        field.
+        """
+        def conv(v):
+            return v
+
+        with pytest.warns(DeprecationWarning) as wi:
+            a = Attribute(
+                "a", True, True, True, True, True, True, convert=conv
+            )
+        w = wi.pop()
+
+        assert conv == a.converter
+        assert (
+            "The `convert` argument is deprecated in favor of `converter`.  "
+            "It will be removed after 2019/01.",
+        ) == w.message.args
+        assert __file__ == w.filename
+
+    def test_deprecated_convert_attribute(self):
+        """
+        If Attribute.convert is accessed, a DeprecationWarning is raised.
+        """
+        def conv(v):
+            return v
+
+        a = simple_attr("a", converter=conv)
+        with pytest.warns(DeprecationWarning) as wi:
+            convert = a.convert
+        w = wi.pop()
+
+        assert conv is convert is a.converter
+        assert (
+            "The `convert` attribute is deprecated in favor of `converter`.  "
+            "It will be removed after 2019/01.",
+        ) == w.message.args
+        assert __file__ == w.filename
+
+    def test_convert_converter(self):
+        """
+        A TypeError is raised if both *convert* and *converter* are passed.
+        """
+        with pytest.raises(RuntimeError) as ei:
+            Attribute(
+                "a", True, True, True, True, True, True,
+                convert=lambda v: v, converter=lambda v: v,
+            )
+
+        assert (
+            "Can't pass both `convert` and `converter`.  "
+            "Please use `converter` only.",
+        ) == ei.value.args
+
+
 def make_tc():
     class TransformC(object):
         z = attr.ib()
         y = attr.ib()
         x = attr.ib()
         a = 42
     return TransformC
 
@@ -142,36 +206,36 @@ class TestTransformAttrs(object):
 
         assert None is getattr(C, "__attrs_attrs__", None)
 
     def test_normal(self):
         """
         Transforms every `_CountingAttr` and leaves others (a) be.
         """
         C = make_tc()
-        attrs, _, = _transform_attrs(C, None, False)
+        attrs, _, _ = _transform_attrs(C, None, False)
 
         assert ["z", "y", "x"] == [a.name for a in attrs]
 
     def test_empty(self):
         """
         No attributes works as expected.
         """
         @attr.s
         class C(object):
             pass
 
-        assert _Attributes(((), [])) == _transform_attrs(C, None, False)
+        assert _Attributes(((), [], {})) == _transform_attrs(C, None, False)
 
     def test_transforms_to_attribute(self):
         """
         All `_CountingAttr`s are transformed into `Attribute`s.
         """
         C = make_tc()
-        attrs, super_attrs = _transform_attrs(C, None, False)
+        attrs, super_attrs, _ = _transform_attrs(C, None, False)
 
         assert [] == super_attrs
         assert 3 == len(attrs)
         assert all(isinstance(a, Attribute) for a in attrs)
 
     def test_conflicting_defaults(self):
         """
         Raises `ValueError` if attributes with defaults are followed by
@@ -182,37 +246,62 @@ class TestTransformAttrs(object):
             y = attr.ib()
 
         with pytest.raises(ValueError) as e:
             _transform_attrs(C, None, False)
         assert (
             "No mandatory attributes allowed after an attribute with a "
             "default value or factory.  Attribute in question: Attribute"
             "(name='y', default=NOTHING, validator=None, repr=True, "
-            "cmp=True, hash=None, init=True, convert=None, "
-            "metadata=mappingproxy({}), type=None)",
+            "cmp=True, hash=None, init=True, metadata=mappingproxy({}), "
+            "type=None, converter=None)",
         ) == e.value.args
 
     def test_these(self):
         """
         If these is passed, use it and ignore body and super classes.
         """
         class Base(object):
             z = attr.ib()
 
         class C(Base):
             y = attr.ib()
 
-        attrs, super_attrs = _transform_attrs(C, {"x": attr.ib()}, False)
+        attrs, super_attrs, _ = _transform_attrs(C, {"x": attr.ib()}, False)
 
         assert [] == super_attrs
         assert (
             simple_attr("x"),
         ) == attrs
 
+    def test_these_leave_body(self):
+        """
+        If these is passed, no attributes are removed from the body.
+        """
+        @attr.s(init=False, these={"x": attr.ib()})
+        class C(object):
+            x = 5
+
+        assert 5 == C().x
+        assert "C(x=5)" == repr(C())
+
+    def test_these_ordered(self):
+        """
+        If these is passed ordered attrs, their order respect instead of the
+        counter.
+        """
+        b = attr.ib(default=2)
+        a = attr.ib(default=1)
+
+        @attr.s(these=ordered_dict([("a", a), ("b", b)]))
+        class C(object):
+            pass
+
+        assert "C(a=1, b=2)" == repr(C())
+
     def test_multiple_inheritance(self):
         """
         Order of attributes doesn't get mixed up by multiple inheritance.
 
         See #285
         """
         @attr.s
         class A(object):
@@ -230,17 +319,17 @@ class TestTransformAttrs(object):
             c2 = attr.ib(default="c2")
 
         @attr.s
         class D(A):
             d1 = attr.ib(default="d1")
             d2 = attr.ib(default="d2")
 
         @attr.s
-        class E(D, C):
+        class E(C, D):
             e1 = attr.ib(default="e1")
             e2 = attr.ib(default="e2")
 
         assert (
             "E(a1='a1', a2='a2', b1='b1', b2='b2', c1='c1', c2='c2', d1='d1', "
             "d2='d2', e1='e1', e2='e2')"
         ) == repr(E())
 
@@ -432,16 +521,45 @@ class TestAttributes(object):
         @attr.s(slots=slots)
         class C(object):
             x = attr.ib()
 
         x = getattr(C, "x", None)
 
         assert not isinstance(x, _CountingAttr)
 
+    def test_factory_sugar(self):
+        """
+        Passing factory=f is syntactic sugar for passing default=Factory(f).
+        """
+        @attr.s
+        class C(object):
+            x = attr.ib(factory=list)
+
+        assert Factory(list) == attr.fields(C).x.default
+
+    def test_sugar_factory_mutex(self):
+        """
+        Passing both default and factory raises ValueError.
+        """
+        with pytest.raises(ValueError, match="mutually exclusive"):
+            @attr.s
+            class C(object):
+                x = attr.ib(factory=list, default=Factory(list))
+
+    def test_sugar_callable(self):
+        """
+        Factory has to be a callable to prevent people from passing Factory
+        into it.
+        """
+        with pytest.raises(ValueError, match="must be a callable"):
+            @attr.s
+            class C(object):
+                x = attr.ib(factory=Factory(list))
+
 
 @attr.s
 class GC(object):
     @attr.s
     class D(object):
         pass
 
 
@@ -532,16 +650,28 @@ class TestMakeClass(object):
         """
         `make_class()` does not fail when `sys._getframe()` is not available.
         """
         monkeypatch.delattr(sys, '_getframe')
         C = make_class("C", ["x"])
 
         assert 1 == len(C.__attrs_attrs__)
 
+    def test_make_class_ordered(self):
+        """
+        If `make_class()` is passed ordered attrs, their order is respected
+        instead of the counter.
+        """
+        b = attr.ib(default=2)
+        a = attr.ib(default=1)
+
+        C = attr.make_class("C", ordered_dict([("a", a), ("b", b)]))
+
+        assert "C(a=1, b=2)" == repr(C())
+
 
 class TestFields(object):
     """
     Tests for `fields`.
     """
     def test_instance(self, C):
         """
         Raises `TypeError` on non-classes.
@@ -552,16 +682,17 @@ class TestFields(object):
         assert "Passed object must be a class." == e.value.args[0]
 
     def test_handler_non_attrs_class(self, C):
         """
         Raises `ValueError` if passed a non-``attrs`` instance.
         """
         with pytest.raises(NotAnAttrsClassError) as e:
             fields(object)
+
         assert (
             "{o!r} is not an attrs-decorated class.".format(o=object)
         ) == e.value.args[0]
 
     @given(simple_classes())
     def test_fields(self, C):
         """
         Returns a list of `Attribute`a.
@@ -572,59 +703,96 @@ class TestFields(object):
     def test_fields_properties(self, C):
         """
         Fields returns a tuple with properties.
         """
         for attribute in fields(C):
             assert getattr(fields(C), attribute.name) is attribute
 
 
-class TestConvert(object):
+class TestFieldsDict(object):
+    """
+    Tests for `fields_dict`.
+    """
+    def test_instance(self, C):
+        """
+        Raises `TypeError` on non-classes.
+        """
+        with pytest.raises(TypeError) as e:
+            fields_dict(C(1, 2))
+
+        assert "Passed object must be a class." == e.value.args[0]
+
+    def test_handler_non_attrs_class(self, C):
+        """
+        Raises `ValueError` if passed a non-``attrs`` instance.
+        """
+        with pytest.raises(NotAnAttrsClassError) as e:
+            fields_dict(object)
+
+        assert (
+            "{o!r} is not an attrs-decorated class.".format(o=object)
+        ) == e.value.args[0]
+
+    @given(simple_classes())
+    def test_fields_dict(self, C):
+        """
+        Returns an ordered dict of ``{attribute_name: Attribute}``.
+        """
+        d = fields_dict(C)
+
+        assert isinstance(d, ordered_dict)
+        assert list(fields(C)) == list(d.values())
+        assert [a.name for a in fields(C)] == [field_name for field_name in d]
+
+
+class TestConverter(object):
     """
     Tests for attribute conversion.
     """
     def test_convert(self):
         """
-        Return value of convert is used as the attribute's value.
+        Return value of converter is used as the attribute's value.
         """
         C = make_class("C", {
-            "x": attr.ib(convert=lambda v: v + 1),
+            "x": attr.ib(converter=lambda v: v + 1),
             "y": attr.ib(),
         })
         c = C(1, 2)
 
         assert c.x == 2
         assert c.y == 2
 
     @given(integers(), booleans())
     def test_convert_property(self, val, init):
         """
         Property tests for attributes with convert.
         """
         C = make_class("C", {
             "y": attr.ib(),
-            "x": attr.ib(init=init, default=val, convert=lambda v: v + 1),
+            "x": attr.ib(init=init, default=val, converter=lambda v: v + 1),
         })
         c = C(2)
 
         assert c.x == val + 1
         assert c.y == 2
 
     @given(integers(), booleans())
     def test_convert_factory_property(self, val, init):
         """
         Property tests for attributes with convert, and a factory default.
         """
-        C = make_class("C", {
-            "y": attr.ib(),
-            "x": attr.ib(
+        C = make_class("C", ordered_dict([
+            ("y", attr.ib()),
+            ("x", attr.ib(
                 init=init,
                 default=Factory(lambda: val),
-                convert=lambda v: v + 1),
-        })
+                converter=lambda v: v + 1
+            )),
+        ]))
         c = C(2)
 
         assert c.x == val + 1
         assert c.y == 2
 
     def test_factory_takes_self(self):
         """
         If takes_self on factories is True, self is passed.
@@ -648,31 +816,70 @@ class TestConvert(object):
     def test_convert_before_validate(self):
         """
         Validation happens after conversion.
         """
         def validator(inst, attr, val):
             raise RuntimeError("foo")
         C = make_class(
             "C", {
-                "x": attr.ib(validator=validator, convert=lambda v: 1 / 0),
+                "x": attr.ib(validator=validator, converter=lambda v: 1 / 0),
                 "y": attr.ib(),
             })
         with pytest.raises(ZeroDivisionError):
             C(1, 2)
 
     def test_frozen(self):
         """
         Converters circumvent immutability.
         """
         C = make_class("C", {
-            "x": attr.ib(convert=lambda v: int(v)),
+            "x": attr.ib(converter=lambda v: int(v)),
         }, frozen=True)
         C("1")
 
+    def test_deprecated_convert(self):
+        """
+        Using *convert* raises a DeprecationWarning and sets the converter
+        field.
+        """
+        def conv(v):
+            return v
+
+        with pytest.warns(DeprecationWarning) as wi:
+            @attr.s
+            class C(object):
+                x = attr.ib(convert=conv)
+
+            convert = fields(C).x.convert
+
+        assert 2 == len(wi.list)
+        w = wi.pop()
+
+        assert conv == fields(C).x.converter == convert
+        assert (
+            "The `convert` argument is deprecated in favor of `converter`.  "
+            "It will be removed after 2019/01.",
+        ) == w.message.args
+        assert __file__ == w.filename
+
+    def test_convert_converter(self):
+        """
+        A TypeError is raised if both *convert* and *converter* are passed.
+        """
+        with pytest.raises(RuntimeError) as ei:
+            @attr.s
+            class C(object):
+                x = attr.ib(convert=lambda v: v, converter=lambda v: v)
+
+        assert (
+            "Can't pass both `convert` and `converter`.  "
+            "Please use `converter` only.",
+        ) == ei.value.args
+
 
 class TestValidate(object):
     """
     Tests for `validate`.
     """
     def test_success(self):
         """
         If the validator succeeds, nothing gets raised.
@@ -818,16 +1025,49 @@ class TestMetadata(object):
     def test_empty_metadata_singleton(self, list_of_attrs):
         """
         All empty metadata attributes share the same empty metadata dict.
         """
         C = make_class("C", dict(zip(gen_attr_names(), list_of_attrs)))
         for a in fields(C)[1:]:
             assert a.metadata is fields(C)[0].metadata
 
+    @given(lists(simple_attrs_without_metadata, min_size=2, max_size=5))
+    def test_empty_countingattr_metadata_independent(self, list_of_attrs):
+        """
+        All empty metadata attributes are independent before ``@attr.s``.
+        """
+        for x, y in itertools.combinations(list_of_attrs, 2):
+            assert x.metadata is not y.metadata
+
+    @given(lists(simple_attrs_with_metadata(), min_size=2, max_size=5))
+    def test_not_none_metadata(self, list_of_attrs):
+        """
+        Non-empty metadata attributes exist as fields after ``@attr.s``.
+        """
+        C = make_class("C", dict(zip(gen_attr_names(), list_of_attrs)))
+
+        assert len(fields(C)) > 0
+
+        for cls_a, raw_a in zip(fields(C), list_of_attrs):
+            assert cls_a.metadata != {}
+            assert cls_a.metadata == raw_a.metadata
+
+    def test_metadata(self):
+        """
+        If metadata that is not None is passed, it is used.
+
+        This is necessary for coverage because the previous test is
+        hypothesis-based.
+        """
+        md = {}
+        a = attr.ib(metadata=md)
+
+        assert md is a.metadata
+
 
 class TestClassBuilder(object):
     """
     Tests for `_ClassBuilder`.
     """
     def test_repr_str(self):
         """
         Trying to add a `__str__` without having a `__repr__` raises a
@@ -859,8 +1099,67 @@ class TestClassBuilder(object):
             x = attr.ib()
 
         b = _ClassBuilder(C, None, True, True, False)
 
         cls = b.add_cmp().add_hash().add_init().add_repr("ns").add_str() \
             .build_class()
 
         assert "ns.C(x=1)" == repr(cls(1))
+
+    @pytest.mark.parametrize("meth_name", [
+        "__init__", "__hash__", "__repr__", "__str__",
+        "__eq__", "__ne__", "__lt__", "__le__", "__gt__", "__ge__",
+    ])
+    def test_attaches_meta_dunders(self, meth_name):
+        """
+        Generated methods have correct __module__, __name__, and __qualname__
+        attributes.
+        """
+        @attr.s(hash=True, str=True)
+        class C(object):
+            def organic(self):
+                pass
+
+        meth = getattr(C, meth_name)
+
+        assert meth_name == meth.__name__
+        assert C.organic.__module__ == meth.__module__
+        if not PY2:
+            organic_prefix = C.organic.__qualname__.rsplit(".", 1)[0]
+            assert organic_prefix + "." + meth_name == meth.__qualname__
+
+    def test_handles_missing_meta_on_class(self):
+        """
+        If the class hasn't a __module__ or __qualname__, the method hasn't
+        either.
+        """
+        class C(object):
+            pass
+
+        b = _ClassBuilder(
+            C, these=None, slots=False, frozen=False, auto_attribs=False,
+        )
+        b._cls = {}  # no __module__; no __qualname__
+
+        def fake_meth(self):
+            pass
+
+        fake_meth.__module__ = "42"
+        fake_meth.__qualname__ = "23"
+
+        rv = b._add_method_dunders(fake_meth)
+
+        assert "42" == rv.__module__ == fake_meth.__module__
+        assert "23" == rv.__qualname__ == fake_meth.__qualname__
+
+    def test_weakref_setstate(self):
+        """
+        __weakref__ is not set on in setstate because it's not writable in
+        slots classes.
+        """
+        @attr.s(slots=True)
+        class C(object):
+            __weakref__ = attr.ib(
+                init=False, hash=False, repr=False, cmp=False
+            )
+
+        assert C() == copy.deepcopy(C())
--- a/testing/web-platform/tests/tools/third_party/attrs/tests/test_validators.py
+++ b/testing/web-platform/tests/tools/third_party/attrs/tests/test_validators.py
@@ -4,18 +4,18 @@ Tests for `attr.validators`.
 
 from __future__ import absolute_import, division, print_function
 
 import pytest
 import zope.interface
 
 import attr
 
+from attr import has
 from attr import validators as validator_module
-from attr import has
 from attr._compat import TYPE
 from attr.validators import and_, in_, instance_of, optional, provides
 
 from .utils import simple_attr
 
 
 class TestInstanceOf(object):
     """
--- a/testing/web-platform/tests/tools/third_party/attrs/tests/utils.py
+++ b/testing/web-platform/tests/tools/third_party/attrs/tests/utils.py
@@ -1,23 +1,14 @@
 """
 Common helper functions for tests.
 """
 
 from __future__ import absolute_import, division, print_function
 
-import keyword
-import string
-
-from collections import OrderedDict
-
-from hypothesis import strategies as st
-
-import attr
-
 from attr import Attribute
 from attr._make import NOTHING, make_class
 
 
 def simple_class(cmp=False, repr=False, hash=False, str=False, slots=False,
                  frozen=False):
     """
     Return a new simple class.
@@ -25,23 +16,23 @@ def simple_class(cmp=False, repr=False, 
     return make_class(
         "C", ["a", "b"],
         cmp=cmp, repr=repr, hash=hash, init=True, slots=slots, str=str,
         frozen=frozen,
     )
 
 
 def simple_attr(name, default=NOTHING, validator=None, repr=True,
-                cmp=True, hash=None, init=True):
+                cmp=True, hash=None, init=True, converter=None):
     """
     Return an attribute with a name and no other bells and whistles.
     """
     return Attribute(
         name=name, default=default, validator=validator, repr=repr,
-        cmp=cmp, hash=hash, init=init
+        cmp=cmp, hash=hash, init=init, converter=converter,
     )
 
 
 class TestSimpleClass(object):
     """
     Tests for the testing helper function `make_class`.
     """
     def test_returns_class(self):
@@ -50,188 +41,8 @@ class TestSimpleClass(object):
         """
         assert type is simple_class().__class__
 
     def returns_distinct_classes(self):
         """
         Each call returns a completely new class.
         """
         assert simple_class() is not simple_class()
-
-
-def gen_attr_names():
-    """
-    Generate names for attributes, 'a'...'z', then 'aa'...'zz'.
-
-    ~702 different attribute names should be enough in practice.
-
-    Some short strings (such as 'as') are keywords, so we skip them.
-    """
-    lc = string.ascii_lowercase
-    for c in lc:
-        yield c
-    for outer in lc:
-        for inner in lc:
-            res = outer + inner
-            if keyword.iskeyword(res):
-                continue
-            yield outer + inner
-
-
-def maybe_underscore_prefix(source):
-    """
-    A generator to sometimes prepend an underscore.
-    """
-    to_underscore = False
-    for val in source:
-        yield val if not to_underscore else '_' + val
-        to_underscore = not to_underscore
-
-
-def _create_hyp_class(attrs):
-    """
-    A helper function for Hypothesis to generate attrs classes.
-    """
-    return make_class(
-        "HypClass", dict(zip(gen_attr_names(), attrs))
-    )
-
-
-def _create_hyp_nested_strategy(simple_class_strategy):
-    """
-    Create a recursive attrs class.
-
-    Given a strategy for building (simpler) classes, create and return
-    a strategy for building classes that have as an attribute: either just
-    the simpler class, a list of simpler classes, a tuple of simpler classes,
-    an ordered dict or a dict mapping the string "cls" to a simpler class.
-    """
-    # Use a tuple strategy to combine simple attributes and an attr class.
-    def just_class(tup):
-        combined_attrs = list(tup[0])
-        combined_attrs.append(attr.ib(default=attr.Factory(tup[1])))
-        return _create_hyp_class(combined_attrs)
-
-    def list_of_class(tup):
-        default = attr.Factory(lambda: [tup[1]()])
-        combined_attrs = list(tup[0])
-        combined_attrs.append(attr.ib(default=default))
-        return _create_hyp_class(combined_attrs)
-
-    def tuple_of_class(tup):
-        default = attr.Factory(lambda: (tup[1](),))
-        combined_attrs = list(tup[0])
-        combined_attrs.append(attr.ib(default=default))
-        return _create_hyp_class(combined_attrs)
-
-    def dict_of_class(tup):
-        default = attr.Factory(lambda: {"cls": tup[1]()})
-        combined_attrs = list(tup[0])
-        combined_attrs.append(attr.ib(default=default))
-        return _create_hyp_class(combined_attrs)
-
-    def ordereddict_of_class(tup):
-        default = attr.Factory(lambda: OrderedDict([("cls", tup[1]())]))
-        combined_attrs = list(tup[0])
-        combined_attrs.append(attr.ib(default=default))
-        return _create_hyp_class(combined_attrs)
-
-    # A strategy producing tuples of the form ([list of attributes], <given
-    # class strategy>).
-    attrs_and_classes = st.tuples(list_of_attrs, simple_class_strategy)
-
-    return st.one_of(attrs_and_classes.map(just_class),
-                     attrs_and_classes.map(list_of_class),
-                     attrs_and_classes.map(tuple_of_class),
-                     attrs_and_classes.map(dict_of_class),
-                     attrs_and_classes.map(ordereddict_of_class))
-
-
-bare_attrs = st.just(attr.ib(default=None))
-int_attrs = st.integers().map(lambda i: attr.ib(default=i))
-str_attrs = st.text().map(lambda s: attr.ib(default=s))
-float_attrs = st.floats().map(lambda f: attr.ib(default=f))
-dict_attrs = (st.dictionaries(keys=st.text(), values=st.integers())
-              .map(lambda d: attr.ib(default=d)))
-
-simple_attrs_without_metadata = (bare_attrs | int_attrs | str_attrs |
-                                 float_attrs | dict_attrs)
-
-
-@st.composite
-def simple_attrs_with_metadata(draw):
-    """
-    Create a simple attribute with arbitrary metadata.
-    """
-    c_attr = draw(simple_attrs)
-    keys = st.booleans() | st.binary() | st.integers() | st.text()
-    vals = st.booleans() | st.binary() | st.integers() | st.text()
-    metadata = draw(st.dictionaries(keys=keys, values=vals))
-
-    return attr.ib(c_attr._default, c_attr._validator, c_attr.repr,
-                   c_attr.cmp, c_attr.hash, c_attr.init, c_attr.convert,
-                   metadata)
-
-
-simple_attrs = simple_attrs_without_metadata | simple_attrs_with_metadata()
-
-# Python functions support up to 255 arguments.
-list_of_attrs = st.lists(simple_attrs, average_size=3, max_size=9)
-
-
-@st.composite
-def simple_classes(draw, slots=None, frozen=None, private_attrs=None):
-    """
-    A strategy that generates classes with default non-attr attributes.
-
-    For example, this strategy might generate a class such as:
-
-    @attr.s(slots=True, frozen=True)
-    class HypClass:
-        a = attr.ib(default=1)
-        _b = attr.ib(default=None)
-        c = attr.ib(default='text')
-        _d = attr.ib(default=1.0)
-        c = attr.ib(default={'t': 1})
-
-    By default, all combinations of slots and frozen classes will be generated.
-    If `slots=True` is passed in, only slots classes will be generated, and
-    if `slots=False` is passed in, no slot classes will be generated. The same
-    applies to `frozen`.
-
-    By default, some attributes will be private (i.e. prefixed with an
-    underscore). If `private_attrs=True` is passed in, all attributes will be
-    private, and if `private_attrs=False`, no attributes will be private.
-    """
-    attrs = draw(list_of_attrs)
-    frozen_flag = draw(st.booleans()) if frozen is None else frozen
-    slots_flag = draw(st.booleans()) if slots is None else slots
-
-    if private_attrs is None:
-        attr_names = maybe_underscore_prefix(gen_attr_names())
-    elif private_attrs is True:
-        attr_names = ('_' + n for n in gen_attr_names())
-    elif private_attrs is False:
-        attr_names = gen_attr_names()
-
-    cls_dict = dict(zip(attr_names, attrs))
-    post_init_flag = draw(st.booleans())
-    if post_init_flag:
-        def post_init(self):
-            pass
-        cls_dict["__attrs_post_init__"] = post_init
-
-    return make_class(
-        "HypClass",
-        cls_dict,
-        slots=slots_flag,
-        frozen=frozen_flag,
-    )
-
-
-# st.recursive works by taking a base strategy (in this case, simple_classes)
-# and a special function.  This function receives a strategy, and returns
-# another strategy (building on top of the base strategy).
-nested_classes = st.recursive(
-    simple_classes(),
-    _create_hyp_nested_strategy,
-    max_leaves=10
-)
--- a/testing/web-platform/tests/tools/third_party/attrs/tox.ini
+++ b/testing/web-platform/tests/tools/third_party/attrs/tox.ini
@@ -2,65 +2,70 @@
 envlist = isort,py27,py34,py35,py36,pypy,pypy3,flake8,manifest,docs,readme,changelog,coverage-report
 
 
 [testenv]
 # Prevent random setuptools/pip breakages like
 # https://github.com/pypa/setuptools/issues/1042 from breaking our builds.
 setenv =
     VIRTUALENV_NO_DOWNLOAD=1
-deps = -rdev-requirements.txt
+extras = tests
 commands = python -m pytest {posargs}
 
 
 [testenv:py27]
-deps = -rdev-requirements.txt
+extras = tests
 commands = coverage run --parallel -m pytest {posargs}
 
 
 [testenv:py36]
-deps = -rdev-requirements.txt
+# Python 3.6+ has a number of compile-time warnings on invalid string escapes.
+# PYTHONWARNINGS=d and --no-compile below make them visible during the Tox run.
+install_command = pip install --no-compile {opts} {packages}
+setenv =
+    PYTHONWARNINGS=d
+extras = tests
 commands = coverage run --parallel -m pytest {posargs}
 
 
 # Uses default basepython otherwise reporting doesn't work on Travis where
 # Python 3.6 is only available in 3.6 jobs.
 [testenv:coverage-report]
 deps = coverage
 skip_install = true
 commands =
     coverage combine
     coverage report
 
 
 [testenv:flake8]
 basepython = python3.6
+extras = tests
 # Needs a full install so isort can determine own/foreign imports.
 deps =
-    -rdev-requirements.txt
     flake8
     flake8-isort
 commands = flake8 src tests setup.py conftest.py docs/conf.py
 
 
 [testenv:isort]
 basepython = python3.6
+extras = tests
 # Needs a full install so isort can determine own/foreign imports.
 deps =
-    -rdev-requirements.txt
     isort
 commands =
     isort --recursive setup.py conftest.py src tests
 
 
 [testenv:docs]
 basepython = python3.6
 setenv =
     PYTHONHASHSEED = 0
-deps = -rdocs-requirements.txt
+extras = docs
 commands =
     sphinx-build -W -b html -d {envtmpdir}/doctrees docs docs/_build/html
     sphinx-build -W -b doctest -d {envtmpdir}/doctrees docs docs/_build/html
     python -m doctest README.rst
 
 
 [testenv:manifest]
 basepython = python3.6