Bug 1748737: Bump attrs to be compatible with mochitest draft
authorMitchell Hentges <mhentges@mozilla.com>
Fri, 21 Jan 2022 18:22:12 +0000
changeset 4182096 af229dbd26e0704d91a7e6323d9d82edc714e2f1
parent 4182095 8eca736bbb995b7283e56b2e1fc87de6cc0142d3
child 4182097 5dca8a6d25797b09dd80eaca73d6c5d82d0f3c05
push id771939
push userreviewbot
push dateFri, 21 Jan 2022 18:22:37 +0000
treeherdertry@5dca8a6d2579 [default view] [failures only]
bugs1748737
milestone98.0a1
Bug 1748737: Bump attrs to be compatible with mochitest Summary: A bunch of modern packages (`pytest`, `twisted`, `automat`) all need `attrs==19.2.0` (or newer). We _could_ bump `attrs` all the way to the modern `21.4.0` version, but I'd like to defer that upgrade risk, since there's a lot of backwards-incompatible changes and deprecations. So, lightly bump it to `19.2.0`. As part of bumping it, `pytest` is no longer compatible. The earliest candidate that seems to be compatible is `pytest` 4.6.6, which boasts in its release notes that it's resolved some deprecation warnings against `attrs>=19.2.0`. Once `pytest` was bumped, it needed a newer version of `pluggy`, which itself has dependencies. Since we're using hashes in `tox_requirements.txt`, all dependencies needed to be hashed as well. Differential Revision: https://phabricator.services.mozilla.com/D135178 Depends on D136537 Test Plan: Reviewers: Subscribers: Bug #: 1748737 Differential Diff: PHID-DIFF-6e2sxupbvd4225mt2st7
build/python-test_virtualenv_packages.txt
third_party/python/attrs/attr/__init__.py
third_party/python/attrs/attr/__init__.pyi
third_party/python/attrs/attr/_compat.py
third_party/python/attrs/attr/_funcs.py
third_party/python/attrs/attr/_make.py
third_party/python/attrs/attr/_version.py
third_party/python/attrs/attr/_version.pyi
third_party/python/attrs/attr/converters.py
third_party/python/attrs/attr/exceptions.py
third_party/python/attrs/attr/exceptions.pyi
third_party/python/attrs/attr/filters.py
third_party/python/attrs/attr/validators.py
third_party/python/attrs/attr/validators.pyi
third_party/python/attrs/attrs-19.1.0.dist-info/LICENSE
third_party/python/attrs/attrs-19.1.0.dist-info/METADATA
third_party/python/attrs/attrs-19.1.0.dist-info/RECORD
third_party/python/attrs/attrs-19.1.0.dist-info/WHEEL
third_party/python/attrs/attrs-19.1.0.dist-info/top_level.txt
third_party/python/attrs/attrs-19.2.0.dist-info/LICENSE
third_party/python/attrs/attrs-19.2.0.dist-info/METADATA
third_party/python/attrs/attrs-19.2.0.dist-info/RECORD
third_party/python/attrs/attrs-19.2.0.dist-info/WHEEL
third_party/python/attrs/attrs-19.2.0.dist-info/top_level.txt
third_party/python/requirements.in
third_party/python/requirements.txt
tools/lint/tox/tox_requirements.txt
--- a/build/python-test_virtualenv_packages.txt
+++ b/build/python-test_virtualenv_packages.txt
@@ -1,2 +1,2 @@
 vendored:third_party/python/glean_parser
-pypi:pytest==3.6.2
+pypi:pytest==4.6.6
--- a/third_party/python/attrs/attr/__init__.py
+++ b/third_party/python/attrs/attr/__init__.py
@@ -11,19 +11,21 @@ from ._make import (
     Factory,
     attrib,
     attrs,
     fields,
     fields_dict,
     make_class,
     validate,
 )
+from ._version import VersionInfo
 
 
-__version__ = "19.1.0"
+__version__ = "19.2.0"
+__version_info__ = VersionInfo._from_version_string(__version__)
 
 __title__ = "attrs"
 __description__ = "Classes Without Boilerplate"
 __url__ = "https://www.attrs.org/"
 __uri__ = __url__
 __doc__ = __description__ + " <" + __uri__ + ">"
 
 __author__ = "Hynek Schlawack"
@@ -32,16 +34,17 @@ from ._make import (
 __license__ = "MIT"
 __copyright__ = "Copyright (c) 2015 Hynek Schlawack"
 
 
 s = attributes = attrs
 ib = attr = attrib
 dataclass = partial(attrs, auto_attribs=True)  # happy Easter ;)
 
+
 __all__ = [
     "Attribute",
     "Factory",
     "NOTHING",
     "asdict",
     "assoc",
     "astuple",
     "attr",
--- a/third_party/python/attrs/attr/__init__.pyi
+++ b/third_party/python/attrs/attr/__init__.pyi
@@ -15,22 +15,37 @@ from typing import (
 )
 
 # `import X as X` is required to make these public
 from . import exceptions as exceptions
 from . import filters as filters
 from . import converters as converters
 from . import validators as validators
 
+from ._version import VersionInfo
+
+__version__: str
+__version_info__: VersionInfo
+__title__: str
+__description__: str
+__url__: str
+__uri__: str
+__author__: str
+__email__: str
+__license__: str
+__copyright__: str
+
 _T = TypeVar("_T")
 _C = TypeVar("_C", bound=type)
 
 _ValidatorType = Callable[[Any, Attribute[_T], _T], Any]
 _ConverterType = Callable[[Any], _T]
 _FilterType = Callable[[Attribute[_T], _T], bool]
+_ReprType = Callable[[Any], str]
+_ReprArgType = Union[bool, _ReprType]
 # FIXME: in reality, if multiple validators are passed they must be in a list or tuple,
 # but those are invariant and so would prevent subtypes of _ValidatorType from working
 # when passed in a list or tuple.
 _ValidatorArgType = Union[_ValidatorType[_T], Sequence[_ValidatorType[_T]]]
 
 # _make --
 
 NOTHING: object
@@ -44,28 +59,26 @@ def Factory(
     factory: Union[Callable[[Any], _T], Callable[[], _T]],
     takes_self: bool = ...,
 ) -> _T: ...
 
 class Attribute(Generic[_T]):
     name: str
     default: Optional[_T]
     validator: Optional[_ValidatorType[_T]]
-    repr: bool
+    repr: _ReprArgType
     cmp: bool
+    eq: bool
+    order: bool
     hash: Optional[bool]
     init: bool
     converter: Optional[_ConverterType[_T]]
     metadata: Dict[Any, Any]
     type: Optional[Type[_T]]
     kw_only: bool
-    def __lt__(self, x: Attribute[_T]) -> bool: ...
-    def __le__(self, x: Attribute[_T]) -> bool: ...
-    def __gt__(self, x: Attribute[_T]) -> bool: ...
-    def __ge__(self, x: Attribute[_T]) -> bool: ...
 
 # NOTE: We had several choices for the annotation to use for type arg:
 # 1) Type[_T]
 #   - Pros: Handles simple cases correctly
 #   - Cons: Might produce less informative errors in the case of conflicting TypeVars
 #   e.g. `attr.ib(default='bad', type=int)`
 # 2) Callable[..., _T]
 #   - Pros: Better error messages than #1 for conflicting TypeVars
@@ -84,113 +97,121 @@ class Attribute(Generic[_T]):
 # This makes this type of assignments possible:
 #     x: int = attr(8)
 #
 # This form catches explicit None or no default but with no other arguments returns Any.
 @overload
 def attrib(
     default: None = ...,
     validator: None = ...,
-    repr: bool = ...,
-    cmp: bool = ...,
+    repr: _ReprArgType = ...,
+    cmp: Optional[bool] = ...,
     hash: Optional[bool] = ...,
     init: bool = ...,
-    convert: None = ...,
     metadata: Optional[Mapping[Any, Any]] = ...,
     type: None = ...,
     converter: None = ...,
     factory: None = ...,
     kw_only: bool = ...,
+    eq: Optional[bool] = ...,
+    order: Optional[bool] = ...,
 ) -> Any: ...
 
 # This form catches an explicit None or no default and infers the type from the other arguments.
 @overload
 def attrib(
     default: None = ...,
     validator: Optional[_ValidatorArgType[_T]] = ...,
-    repr: bool = ...,
-    cmp: bool = ...,
+    repr: _ReprArgType = ...,
+    cmp: Optional[bool] = ...,
     hash: Optional[bool] = ...,
     init: bool = ...,
-    convert: Optional[_ConverterType[_T]] = ...,
     metadata: Optional[Mapping[Any, Any]] = ...,
     type: Optional[Type[_T]] = ...,
     converter: Optional[_ConverterType[_T]] = ...,
     factory: Optional[Callable[[], _T]] = ...,
     kw_only: bool = ...,
+    eq: Optional[bool] = ...,
+    order: Optional[bool] = ...,
 ) -> _T: ...
 
 # This form catches an explicit default argument.
 @overload
 def attrib(
     default: _T,
     validator: Optional[_ValidatorArgType[_T]] = ...,
-    repr: bool = ...,
-    cmp: bool = ...,
+    repr: _ReprArgType = ...,
+    cmp: Optional[bool] = ...,
     hash: Optional[bool] = ...,
     init: bool = ...,
-    convert: Optional[_ConverterType[_T]] = ...,
     metadata: Optional[Mapping[Any, Any]] = ...,
     type: Optional[Type[_T]] = ...,
     converter: Optional[_ConverterType[_T]] = ...,
     factory: Optional[Callable[[], _T]] = ...,
     kw_only: bool = ...,
+    eq: Optional[bool] = ...,
+    order: Optional[bool] = ...,
 ) -> _T: ...
 
 # This form covers type=non-Type: e.g. forward references (str), Any
 @overload
 def attrib(
     default: Optional[_T] = ...,
     validator: Optional[_ValidatorArgType[_T]] = ...,
-    repr: bool = ...,
-    cmp: bool = ...,
+    repr: _ReprArgType = ...,
+    cmp: Optional[bool] = ...,
     hash: Optional[bool] = ...,
     init: bool = ...,
-    convert: Optional[_ConverterType[_T]] = ...,
     metadata: Optional[Mapping[Any, Any]] = ...,
     type: object = ...,
     converter: Optional[_ConverterType[_T]] = ...,
     factory: Optional[Callable[[], _T]] = ...,
     kw_only: bool = ...,
+    eq: Optional[bool] = ...,
+    order: Optional[bool] = ...,
 ) -> Any: ...
 @overload
 def attrs(
     maybe_cls: _C,
     these: Optional[Dict[str, Any]] = ...,
     repr_ns: Optional[str] = ...,
     repr: bool = ...,
-    cmp: bool = ...,
+    cmp: Optional[bool] = ...,
     hash: Optional[bool] = ...,
     init: bool = ...,
     slots: bool = ...,
     frozen: bool = ...,
     weakref_slot: bool = ...,
     str: bool = ...,
     auto_attribs: bool = ...,
     kw_only: bool = ...,
     cache_hash: bool = ...,
     auto_exc: bool = ...,
+    eq: Optional[bool] = ...,
+    order: Optional[bool] = ...,
 ) -> _C: ...
 @overload
 def attrs(
     maybe_cls: None = ...,
     these: Optional[Dict[str, Any]] = ...,
     repr_ns: Optional[str] = ...,
     repr: bool = ...,
-    cmp: bool = ...,
+    cmp: Optional[bool] = ...,
     hash: Optional[bool] = ...,
     init: bool = ...,
     slots: bool = ...,
     frozen: bool = ...,
     weakref_slot: bool = ...,
     str: bool = ...,
     auto_attribs: bool = ...,
     kw_only: bool = ...,
     cache_hash: bool = ...,
     auto_exc: bool = ...,
+    eq: Optional[bool] = ...,
+    order: Optional[bool] = ...,
 ) -> Callable[[_C], _C]: ...
 
 # TODO: add support for returning NamedTuple from the mypy plugin
 class _Fields(Tuple[Attribute[Any], ...]):
     def __getattr__(self, name: str) -> Attribute[Any]: ...
 
 def fields(cls: type) -> _Fields: ...
 def fields_dict(cls: type) -> Dict[str, Attribute[Any]]: ...
@@ -199,27 +220,29 @@ def validate(inst: Any) -> None: ...
 # TODO: add support for returning a proper attrs class from the mypy plugin
 # we use Any instead of _CountingAttr so that e.g. `make_class('Foo', [attr.ib()])` is valid
 def make_class(
     name: str,
     attrs: Union[List[str], Tuple[str, ...], Dict[str, Any]],
     bases: Tuple[type, ...] = ...,
     repr_ns: Optional[str] = ...,
     repr: bool = ...,
-    cmp: bool = ...,
+    cmp: Optional[bool] = ...,
     hash: Optional[bool] = ...,
     init: bool = ...,
     slots: bool = ...,
     frozen: bool = ...,
     weakref_slot: bool = ...,
     str: bool = ...,
     auto_attribs: bool = ...,
     kw_only: bool = ...,
     cache_hash: bool = ...,
     auto_exc: bool = ...,
+    eq: Optional[bool] = ...,
+    order: Optional[bool] = ...,
 ) -> type: ...
 
 # _funcs --
 
 # TODO: add support for returning TypedDict from the mypy plugin
 # FIXME: asdict/astuple do not honor their factory args.  waiting on one of these:
 # https://github.com/python/mypy/issues/4236
 # https://github.com/python/typing/issues/253
--- a/third_party/python/attrs/attr/_compat.py
+++ b/third_party/python/attrs/attr/_compat.py
@@ -15,17 +15,17 @@ if PYPY or sys.version_info[:2] >= (3, 6
 else:
     from collections import OrderedDict
 
     ordered_dict = OrderedDict
 
 
 if PY2:
     from UserDict import IterableUserDict
-    from collections import Mapping, Sequence  # noqa
+    from collections import Mapping, Sequence
 
     # 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))
 
     # TYPE is used in exceptions, repr(int) is different on Python 2 and 3.
     TYPE = "type"
@@ -101,17 +101,18 @@ else:  # Python 3 and later.
     from collections.abc import Mapping, Sequence  # noqa
 
     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.
         """
         warnings.warn(
-            "Missing ctypes.  Some features like bare super() or accessing "
+            "Running interpreter doesn't sufficiently support code object "
+            "introspection.  Some features like bare super() or accessing "
             "__class__ will not work with slotted classes.",
             RuntimeWarning,
             stacklevel=2,
         )
 
     def isclass(klass):
         return isinstance(klass, type)
 
@@ -119,41 +120,111 @@ else:  # Python 3 and later.
 
     def iteritems(d):
         return d.items()
 
     def metadata_proxy(d):
         return types.MappingProxyType(dict(d))
 
 
-def import_ctypes():
-    """
-    Moved into a function for testability.
+def make_set_closure_cell():
+    """Return a function of two arguments (cell, value) which sets
+    the value stored in the closure cell `cell` to `value`.
     """
-    import ctypes
-
-    return ctypes
-
-
-def make_set_closure_cell():
-    """
-    Moved into a function for testability.
-    """
+    # pypy makes this easy. (It also supports the logic below, but
+    # why not do the easy/fast thing?)
     if PYPY:  # pragma: no cover
 
         def set_closure_cell(cell, value):
             cell.__setstate__((value,))
 
-    else:
-        try:
-            ctypes = import_ctypes()
+        return set_closure_cell
+
+    # Otherwise gotta do it the hard way.
+
+    # Create a function that will set its first cellvar to `value`.
+    def set_first_cellvar_to(value):
+        x = value
+        return
+
+        # This function will be eliminated as dead code, but
+        # not before its reference to `x` forces `x` to be
+        # represented as a closure cell rather than a local.
+        def force_x_to_be_a_cell():  # pragma: no cover
+            return x
+
+    try:
+        # Extract the code object and make sure our assumptions about
+        # the closure behavior are correct.
+        if PY2:
+            co = set_first_cellvar_to.func_code
+        else:
+            co = set_first_cellvar_to.__code__
+        if co.co_cellvars != ("x",) or co.co_freevars != ():
+            raise AssertionError  # pragma: no cover
 
-            set_closure_cell = ctypes.pythonapi.PyCell_Set
-            set_closure_cell.argtypes = (ctypes.py_object, ctypes.py_object)
-            set_closure_cell.restype = ctypes.c_int
-        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
+        # Convert this code object to a code object that sets the
+        # function's first _freevar_ (not cellvar) to the argument.
+        if sys.version_info >= (3, 8):
+            # CPython 3.8+ has an incompatible CodeType signature
+            # (added a posonlyargcount argument) but also added
+            # CodeType.replace() to do this without counting parameters.
+            set_first_freevar_code = co.replace(
+                co_cellvars=co.co_freevars, co_freevars=co.co_cellvars
+            )
+        else:
+            args = [co.co_argcount]
+            if not PY2:
+                args.append(co.co_kwonlyargcount)
+            args.extend(
+                [
+                    co.co_nlocals,
+                    co.co_stacksize,
+                    co.co_flags,
+                    co.co_code,
+                    co.co_consts,
+                    co.co_names,
+                    co.co_varnames,
+                    co.co_filename,
+                    co.co_name,
+                    co.co_firstlineno,
+                    co.co_lnotab,
+                    # These two arguments are reversed:
+                    co.co_cellvars,
+                    co.co_freevars,
+                ]
+            )
+            set_first_freevar_code = types.CodeType(*args)
+
+        def set_closure_cell(cell, value):
+            # Create a function using the set_first_freevar_code,
+            # whose first closure cell is `cell`. Calling it will
+            # change the value of that cell.
+            setter = types.FunctionType(
+                set_first_freevar_code, {}, "setter", (), (cell,)
+            )
+            # And call it to set the cell.
+            setter(value)
+
+        # Make sure it works on this interpreter:
+        def make_func_with_cell():
+            x = None
+
+            def func():
+                return x  # pragma: no cover
+
+            return func
+
+        if PY2:
+            cell = make_func_with_cell().func_closure[0]
+        else:
+            cell = make_func_with_cell().__closure__[0]
+        set_closure_cell(cell, 100)
+        if cell.cell_contents != 100:
+            raise AssertionError  # pragma: no cover
+
+    except Exception:
+        return just_warn
+    else:
+        return set_closure_cell
 
 
 set_closure_cell = make_set_closure_cell()
--- a/third_party/python/attrs/attr/_funcs.py
+++ b/third_party/python/attrs/attr/_funcs.py
@@ -19,17 +19,17 @@ def asdict(
 
     Optionally recurse into other ``attrs``-decorated classes.
 
     :param inst: Instance of an ``attrs``-decorated class.
     :param bool recurse: Recurse into classes that are also
         ``attrs``-decorated.
     :param callable filter: A callable whose return code determines whether an
         attribute or element is included (``True``) or dropped (``False``).  Is
-        called with the :class:`attr.Attribute` as the first argument and the
+        called with the `attr.Attribute` as the first argument and the
         value as the second argument.
     :param callable dict_factory: A callable to produce dictionaries from.  For
         example, to produce ordered dictionaries instead of normal Python
         dictionaries, pass in ``collections.OrderedDict``.
     :param bool retain_collection_types: Do not convert to ``list`` when
         encountering an attribute whose type is ``tuple`` or ``set``.  Only
         meaningful if ``recurse`` is ``True``.
 
@@ -125,17 +125,17 @@ def astuple(
 
     Optionally recurse into other ``attrs``-decorated classes.
 
     :param inst: Instance of an ``attrs``-decorated class.
     :param bool recurse: Recurse into classes that are also
         ``attrs``-decorated.
     :param callable filter: A callable whose return code determines whether an
         attribute or element is included (``True``) or dropped (``False``).  Is
-        called with the :class:`attr.Attribute` as the first argument and the
+        called with the `attr.Attribute` as the first argument and the
         value as the second argument.
     :param callable tuple_factory: A callable to produce tuples from.  For
         example, to produce lists instead of tuples.
     :param bool retain_collection_types: Do not convert to ``list``
         or ``dict`` when encountering an attribute which type is
         ``tuple``, ``dict`` or ``set``.  Only meaningful if ``recurse`` is
         ``True``.
 
@@ -214,17 +214,17 @@ def astuple(
 
 def has(cls):
     """
     Check whether *cls* is a class with ``attrs`` attributes.
 
     :param type cls: Class to introspect.
     :raise TypeError: If *cls* is not a class.
 
-    :rtype: :class:`bool`
+    :rtype: bool
     """
     return getattr(cls, "__attrs_attrs__", None) is not None
 
 
 def assoc(inst, **changes):
     """
     Copy *inst* and apply *changes*.
 
@@ -234,17 +234,17 @@ def assoc(inst, **changes):
     :return: A copy of inst with *changes* incorporated.
 
     :raise attr.exceptions.AttrsAttributeNotFoundError: If *attr_name* couldn't
         be found on *cls*.
     :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
         class.
 
     ..  deprecated:: 17.1.0
-        Use :func:`evolve` instead.
+        Use `evolve` instead.
     """
     import warnings
 
     warnings.warn(
         "assoc is deprecated and will be removed after 2018/01.",
         DeprecationWarning,
         stacklevel=2,
     )
--- a/third_party/python/attrs/attr/_make.py
+++ b/third_party/python/attrs/attr/_make.py
@@ -1,15 +1,15 @@
 from __future__ import absolute_import, division, print_function
 
 import copy
-import hashlib
 import linecache
 import sys
 import threading
+import uuid
 import warnings
 
 from operator import itemgetter
 
 from . import _config
 from ._compat import (
     PY2,
     isclass,
@@ -37,16 +37,19 @@ from .exceptions import (
 _classvar_prefixes = ("typing.ClassVar", "t.ClassVar", "ClassVar")
 # we don't use a double-underscore prefix because that triggers
 # name mangling when trying to create a slot for the field
 # (when slots=True)
 _hash_cache_field = "_attrs_cached_hash"
 
 _empty_metadata_singleton = metadata_proxy({})
 
+# Unique object for unequivocal getattr() defaults.
+_sentinel = object()
+
 
 class _Nothing(object):
     """
     Sentinel class to indicate the lack of a value when ``None`` is ambiguous.
 
     ``_Nothing`` is a singleton. There is only ever one of it.
     """
 
@@ -66,137 +69,141 @@ NOTHING = _Nothing()
 Sentinel to indicate the lack of a value when ``None`` is ambiguous.
 """
 
 
 def attrib(
     default=NOTHING,
     validator=None,
     repr=True,
-    cmp=True,
+    cmp=None,
     hash=None,
     init=True,
-    convert=None,
     metadata=None,
     type=None,
     converter=None,
     factory=None,
     kw_only=False,
+    eq=None,
+    order=None,
 ):
     """
     Create a new attribute on a class.
 
     ..  warning::
 
         Does *not* do anything unless the class is also decorated with
-        :func:`attr.s`!
+        `attr.s`!
 
     :param default: A value that is used if an ``attrs``-generated ``__init__``
         is used and no value is passed while instantiating or the attribute is
         excluded using ``init=False``.
 
-        If the value is an instance of :class:`Factory`, its callable will be
+        If the value is an instance of `Factory`, its callable will be
         used to construct a new value (useful for mutable data types like lists
         or dicts).
 
         If a default is not set (or set manually to ``attr.NOTHING``), a value
-        *must* be supplied when instantiating; otherwise a :exc:`TypeError`
+        *must* be supplied when instantiating; otherwise a `TypeError`
         will be raised.
 
         The default can also be set using decorator notation as shown below.
 
-    :type default: Any value.
+    :type default: Any value
 
     :param callable factory: Syntactic sugar for
         ``default=attr.Factory(callable)``.
 
-    :param validator: :func:`callable` that is called by ``attrs``-generated
+    :param validator: `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
+        receive the initialized instance, the `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`.
+        `get_run_validators`.
 
         The validator can also be set using decorator notation as shown below.
 
     :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 repr: Include this attribute in the generated ``__repr__``
+        method. If ``True``, include the attribute; if ``False``, omit it. By
+        default, the built-in ``repr()`` function is used. To override how the
+        attribute value is formatted, pass a ``callable`` that takes a single
+        value and returns a string. Note that the resulting string is used
+        as-is, i.e. it will be used directly *instead* of calling ``repr()``
+        (the default).
+    :type repr: a ``bool`` or a ``callable`` to use a custom function.
+    :param bool eq: If ``True`` (default), include this attribute in the
+        generated ``__eq__`` and ``__ne__`` methods that check two instances
+        for equality.
+    :param bool order: If ``True`` (default), include this attributes in the
+        generated ``__lt__``, ``__le__``, ``__gt__`` and ``__ge__`` methods.
+    :param bool cmp: Setting to ``True`` is equivalent to setting ``eq=True,
+        order=True``. Deprecated in favor of *eq* and *order*.
     :param hash: Include this attribute in the generated ``__hash__``
-        method.  If ``None`` (default), mirror *cmp*'s value.  This is the
+        method.  If ``None`` (default), mirror *eq*'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 converter: :func:`callable` that is called by
+    :param callable converter: `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`.
+        components.  See `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``.
 
         Please note that ``attrs`` doesn't do anything with this metadata by
         itself. You can use it as part of your own code or for
-        :doc:`static type checking <types>`.
+        `static type checking <types>`.
     :param kw_only: Make this attribute keyword-only (Python 3+)
         in the generated ``__init__`` (if ``init`` is ``False``, this
         parameter is ignored).
 
     .. 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.
+       *hash* is ``None`` and therefore mirrors *eq* 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)``.
     .. versionadded:: 18.2.0 *kw_only*
+    .. versionchanged:: 19.2.0 *convert* keyword argument removed
+    .. versionchanged:: 19.2.0 *repr* also accepts a custom callable.
+    .. deprecated:: 19.2.0 *cmp* Removal on or after 2021-06-01.
+    .. versionadded:: 19.2.0 *eq* and *order*
     """
+    eq, order = _determine_eq_order(cmp, eq, order)
+
     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.")
@@ -204,23 +211,25 @@ def attrib(
 
     if metadata is None:
         metadata = {}
 
     return _CountingAttr(
         default=default,
         validator=validator,
         repr=repr,
-        cmp=cmp,
+        cmp=None,
         hash=hash,
         init=init,
         converter=converter,
         metadata=metadata,
         type=type,
         kw_only=kw_only,
+        eq=eq,
+        order=order,
     )
 
 
 def _make_attr_tuple_class(cls_name, attr_names):
     """
     Create a tuple subclass to hold `Attribute`s for an `attrs` class.
 
     The subclass is a bare tuple with properties for names.
@@ -380,48 +389,30 @@ def _transform_attrs(cls, these, auto_at
     AttrsClass = _make_attr_tuple_class(cls.__name__, attr_names)
 
     if kw_only:
         own_attrs = [a._assoc(kw_only=True) for a in own_attrs]
         base_attrs = [a._assoc(kw_only=True) for a in base_attrs]
 
     attrs = AttrsClass(base_attrs + own_attrs)
 
+    # Mandatory vs non-mandatory attr order only matters when they are part of
+    # the __init__ signature and when they aren't kw_only (which are moved to
+    # the end and can be mandatory or non-mandatory in any order, as they will
+    # be specified as keyword args anyway). Check the order of those attrs:
     had_default = False
-    was_kw_only = False
-    for a in attrs:
-        if (
-            was_kw_only is False
-            and had_default is True
-            and a.default is NOTHING
-            and a.init is True
-            and a.kw_only is False
-        ):
+    for a in (a for a in attrs if a.init is not False and a.kw_only is False):
+        if had_default is True and a.default is NOTHING:
             raise ValueError(
                 "No mandatory attributes allowed after an attribute with a "
                 "default value or factory.  Attribute in question: %r" % (a,)
             )
-        elif (
-            had_default is False
-            and a.default is not NOTHING
-            and a.init is not False
-            and
-            # Keyword-only attributes without defaults can be specified
-            # after keyword-only attributes with defaults.
-            a.kw_only is False
-        ):
+
+        if had_default is False and a.default is not NOTHING:
             had_default = True
-        if was_kw_only is True and a.kw_only is False and a.init is True:
-            raise ValueError(
-                "Non keyword-only attributes are not allowed after a "
-                "keyword-only attribute (unless they are init=False).  "
-                "Attribute in question: {a!r}".format(a=a)
-            )
-        if was_kw_only is False and a.init is True and a.kw_only is True:
-            was_kw_only = True
 
     return _Attributes((attrs, base_attrs, base_attr_map))
 
 
 def _frozen_setattrs(self, name, value):
     """
     Attached to frozen classes as __setattr__.
     """
@@ -513,17 +504,17 @@ class _ClassBuilder(object):
         cls = self._cls
         base_names = self._base_names
 
         # Clean class of attribute definitions (`attr.ib()`s).
         if self._delete_attribs:
             for name in self._attr_names:
                 if (
                     name not in base_names
-                    and getattr(cls, name, None) is not None
+                    and getattr(cls, name, _sentinel) != _sentinel
                 ):
                     try:
                         delattr(cls, name)
                     except AttributeError:
                         # This can happen if a base class defines a class
                         # variable and we want to set an attribute with the
                         # same name by using only a type annotation.
                         pass
@@ -671,44 +662,57 @@ class _ClassBuilder(object):
 
     def make_unhashable(self):
         self._cls_dict["__hash__"] = None
         return self
 
     def add_hash(self):
         self._cls_dict["__hash__"] = self._add_method_dunders(
             _make_hash(
-                self._attrs, frozen=self._frozen, cache_hash=self._cache_hash
+                self._cls,
+                self._attrs,
+                frozen=self._frozen,
+                cache_hash=self._cache_hash,
             )
         )
 
         return self
 
     def add_init(self):
         self._cls_dict["__init__"] = self._add_method_dunders(
             _make_init(
+                self._cls,
                 self._attrs,
                 self._has_post_init,
                 self._frozen,
                 self._slots,
                 self._cache_hash,
                 self._base_attr_map,
                 self._is_exc,
             )
         )
 
         return self
 
-    def add_cmp(self):
+    def add_eq(self):
         cd = self._cls_dict
 
-        cd["__eq__"], cd["__ne__"], cd["__lt__"], cd["__le__"], cd[
-            "__gt__"
-        ], cd["__ge__"] = (
-            self._add_method_dunders(meth) for meth in _make_cmp(self._attrs)
+        cd["__eq__"], cd["__ne__"] = (
+            self._add_method_dunders(meth)
+            for meth in _make_eq(self._cls, self._attrs)
+        )
+
+        return self
+
+    def add_order(self):
+        cd = self._cls_dict
+
+        cd["__lt__"], cd["__le__"], cd["__gt__"], cd["__ge__"] = (
+            self._add_method_dunders(meth)
+            for meth in _make_order(self._cls, self._attrs)
         )
 
         return self
 
     def _add_method_dunders(self, method):
         """
         Add __module__ and __qualname__ to a *method* if possible.
         """
@@ -722,150 +726,196 @@ class _ClassBuilder(object):
                 (self._cls.__qualname__, method.__name__)
             )
         except AttributeError:
             pass
 
         return method
 
 
+_CMP_DEPRECATION = (
+    "The usage of `cmp` is deprecated and will be removed on or after "
+    "2021-06-01.  Please use `eq` and `order` instead."
+)
+
+
+def _determine_eq_order(cmp, eq, order):
+    """
+    Validate the combination of *cmp*, *eq*, and *order*. Derive the effective
+    values of eq and order.
+    """
+    if cmp is not None and any((eq is not None, order is not None)):
+        raise ValueError("Don't mix `cmp` with `eq' and `order`.")
+
+    # cmp takes precedence due to bw-compatibility.
+    if cmp is not None:
+        warnings.warn(_CMP_DEPRECATION, DeprecationWarning, stacklevel=3)
+
+        return cmp, cmp
+
+    # If left None, equality is on and ordering mirrors equality.
+    if eq is None:
+        eq = True
+
+    if order is None:
+        order = eq
+
+    if eq is False and order is True:
+        raise ValueError("`order` can only be True if `eq` is True too.")
+
+    return eq, order
+
+
 def attrs(
     maybe_cls=None,
     these=None,
     repr_ns=None,
     repr=True,
-    cmp=True,
+    cmp=None,
     hash=None,
     init=True,
     slots=False,
     frozen=False,
     weakref_slot=True,
     str=False,
     auto_attribs=False,
     kw_only=False,
     cache_hash=False,
     auto_exc=False,
+    eq=None,
+    order=None,
 ):
     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.
+    specified attributes using `attr.ib` or the *these* argument.
 
-    :param these: A dictionary of name to :func:`attr.ib` mappings.  This is
+    :param these: A dictionary of name to `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 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
+        If *these* is an ordered dict (`dict` on Python 3.6+,
+        `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`
+    :type these: `dict` of `str` to `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..
     :param bool str: Create a ``__str__`` method that is identical to
         ``__repr__``.  This is usually not necessary except for
-        :class:`Exception`\ s.
-    :param bool cmp: Create ``__eq__``, ``__ne__``, ``__lt__``, ``__le__``,
-        ``__gt__``, and ``__ge__`` methods that compare the class as if it were
-        a tuple of its ``attrs`` attributes.  But the attributes are *only*
-        compared, if the types of both classes are *identical*!
+        `Exception`\ s.
+    :param bool eq: If ``True`` or ``None`` (default), add ``__eq__`` and
+        ``__ne__`` methods that check two instances for equality.
+
+        They compare the instances as if they were tuples of their ``attrs``
+        attributes, but only iff the types of both classes are *identical*!
+    :type eq: `bool` or `None`
+    :param bool order: If ``True``, add ``__lt__``, ``__le__``, ``__gt__``,
+        and ``__ge__`` methods that behave like *eq* above and allow instances
+        to be ordered. If ``None`` (default) mirror value of *eq*.
+    :type order: `bool` or `None`
+    :param cmp: Setting to ``True`` is equivalent to setting ``eq=True,
+        order=True``. Deprecated in favor of *eq* and *order*, has precedence
+        over them for backward-compatibility though. Must not be mixed with
+        *eq* or *order*.
+    :type cmp: `bool` or `None`
     :param hash: If ``None`` (default), the ``__hash__`` method is generated
-        according how *cmp* and *frozen* are set.
+        according how *eq* and *frozen* are set.
 
         1. If *both* are True, ``attrs`` will generate a ``__hash__`` for you.
-        2. If *cmp* is True and *frozen* is False, ``__hash__`` will be set to
+        2. If *eq* is True and *frozen* is False, ``__hash__`` will be set to
            None, marking it unhashable (which it is).
-        3. If *cmp* is False, ``__hash__`` will be left untouched meaning the
+        3. If *eq* is False, ``__hash__`` will be left untouched meaning the
            ``__hash__`` method of the base class will be used (if base class is
            ``object``, this means it will fall back to id-based hashing.).
 
         Although not recommended, you can decide for yourself and force
         ``attrs`` to create one (e.g. if the class is immutable even though you
         didn't freeze it programmatically) by passing ``True`` or not.  Both of
         these cases are rather special and should be used carefully.
 
-        See the `Python documentation \
-        <https://docs.python.org/3/reference/datamodel.html#object.__hash__>`_
-        and the `GitHub issue that led to the default behavior \
-        <https://github.com/python-attrs/attrs/issues/136>`_ for more details.
+        See our documentation on `hashing`, Python's documentation on
+        `object.__hash__`, and the `GitHub issue that led to the default \
+        behavior <https://github.com/python-attrs/attrs/issues/136>`_ for more
+        details.
     :type hash: ``bool`` or ``None``
     :param bool init: Create a ``__init__`` method that initializes the
         ``attrs`` attributes.  Leading underscores are stripped for the
         argument name.  If a ``__attrs_post_init__`` method exists on the
         class, it will be called after the class is fully initialized.
-    :param bool slots: Create a slots_-style class that's more
-        memory-efficient.  See :ref:`slots` for further ramifications.
+    :param bool slots: Create a `slotted class <slotted classes>` that's more
+        memory-efficient.
     :param bool frozen: Make instances immutable after initialization.  If
         someone attempts to modify a frozen instance,
-        :exc:`attr.exceptions.FrozenInstanceError` is raised.
+        `attr.exceptions.FrozenInstanceError` is raised.
 
         Please note:
 
             1. This is achieved by installing a custom ``__setattr__`` method
                on your class so you can't implement an own one.
 
             2. True immutability is impossible in Python.
 
-            3. This *does* have a minor a runtime performance :ref:`impact
+            3. This *does* have a minor a runtime performance `impact
                <how-frozen>` when initializing new instances.  In other words:
                ``__init__`` is slightly slower with ``frozen=True``.
 
             4. If a class is frozen, you cannot modify ``self`` in
                ``__attrs_post_init__`` or a self-written ``__init__``. You can
                circumvent that limitation by using
                ``object.__setattr__(self, "attribute_name", value)``.
 
-        ..  _slots: https://docs.python.org/3/reference/datamodel.html#slots
     :param bool weakref_slot: Make instances weak-referenceable.  This has no
         effect unless ``slots`` is also enabled.
     :param bool auto_attribs: If True, collect `PEP 526`_-annotated attributes
         (Python 3.6 and later only) from the class body.
 
         In this case, you **must** annotate every field.  If ``attrs``
-        encounters a field that is set to an :func:`attr.ib` but lacks a type
-        annotation, an :exc:`attr.exceptions.UnannotatedAttributeError` is
+        encounters a field that is set to an `attr.ib` but lacks a type
+        annotation, an `attr.exceptions.UnannotatedAttributeError` is
         raised.  Use ``field_name: typing.Any = attr.ib(...)`` if you don't
         want to set a type.
 
         If you assign a value to those attributes (e.g. ``x: int = 42``), that
         value becomes the default value like if it were passed using
-        ``attr.ib(default=42)``.  Passing an instance of :class:`Factory` also
+        ``attr.ib(default=42)``.  Passing an instance of `Factory` also
         works as expected.
 
-        Attributes annotated as :data:`typing.ClassVar` are **ignored**.
+        Attributes annotated as `typing.ClassVar`, and attributes that are
+        neither annotated nor set to an `attr.ib` are **ignored**.
 
         .. _`PEP 526`: https://www.python.org/dev/peps/pep-0526/
     :param bool kw_only: Make all attributes keyword-only (Python 3+)
         in the generated ``__init__`` (if ``init`` is ``False``, this
         parameter is ignored).
     :param bool cache_hash: Ensure that the object's hash code is computed
         only once and stored on the object.  If this is set to ``True``,
         hashing must be either explicitly or implicitly enabled for this
         class.  If the hash code is cached, avoid any reassignments of
         fields involved in hash code computation or mutations of the objects
         those fields point to after object creation.  If such changes occur,
         the behavior of the object's hash code is undefined.
-    :param bool auto_exc: If the class subclasses :class:`BaseException`
+    :param bool auto_exc: If the class subclasses `BaseException`
         (which implicitly includes any subclass of any exception), the
         following happens to behave like a well-behaved Python exceptions
         class:
 
-        - the values for *cmp* and *hash* are ignored and the instances compare
-          and hash by the instance's ids (N.B. ``attrs`` will *not* remove
-          existing implementations of ``__hash__`` or the equality methods. It
-          just won't add own ones.),
+        - the values for *eq*, *order*, and *hash* are ignored and the
+          instances compare and hash by the instance's ids (N.B. ``attrs`` will
+          *not* remove existing implementations of ``__hash__`` or the equality
+          methods. It just won't add own ones.),
         - all attributes that are either passed into ``__init__`` or have a
           default value are additionally available as a tuple in the ``args``
           attribute,
         - the value of *str* is ignored leaving ``__str__`` to base classes.
 
     .. versionadded:: 16.0.0 *slots*
     .. versionadded:: 16.1.0 *frozen*
     .. versionadded:: 16.3.0 *str*
@@ -874,23 +924,29 @@ def attrs(
        *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.
     .. versionadded:: 18.2.0 *weakref_slot*
     .. deprecated:: 18.2.0
        ``__lt__``, ``__le__``, ``__gt__``, and ``__ge__`` now raise a
-       :class:`DeprecationWarning` if the classes compared are subclasses of
+       `DeprecationWarning` if the classes compared are subclasses of
        each other. ``__eq`` and ``__ne__`` never tried to compared subclasses
        to each other.
+    .. versionchanged:: 19.2.0
+       ``__lt__``, ``__le__``, ``__gt__``, and ``__ge__`` now do not consider
+       subclasses comparable anymore.
     .. versionadded:: 18.2.0 *kw_only*
     .. versionadded:: 18.2.0 *cache_hash*
     .. versionadded:: 19.1.0 *auto_exc*
+    .. deprecated:: 19.2.0 *cmp* Removal on or after 2021-06-01.
+    .. versionadded:: 19.2.0 *eq* and *order*
     """
+    eq, order = _determine_eq_order(cmp, eq, order)
 
     def wrap(cls):
 
         if getattr(cls, "__class__", None) is None:
             raise TypeError("attrs only works with new-style classes.")
 
         is_exc = auto_exc is True and issubclass(cls, BaseException)
 
@@ -905,38 +961,40 @@ def attrs(
             cache_hash,
             is_exc,
         )
 
         if repr is True:
             builder.add_repr(repr_ns)
         if str is True:
             builder.add_str()
-        if cmp is True and not is_exc:
-            builder.add_cmp()
+        if eq is True and not is_exc:
+            builder.add_eq()
+        if order is True and not is_exc:
+            builder.add_order()
 
         if hash is not True and hash is not False and hash is not None:
             # Can't use `hash in` because 1 == True for example.
             raise TypeError(
                 "Invalid value for hash.  Must be True, False, or None."
             )
-        elif hash is False or (hash is None and cmp is False):
+        elif hash is False or (hash is None and eq is False) or is_exc:
+            # Don't do anything. Should fall back to __object__'s __hash__
+            # which is by id.
             if cache_hash:
                 raise TypeError(
                     "Invalid value for cache_hash.  To use hash caching,"
                     " hashing must be either explicitly or implicitly "
                     "enabled."
                 )
-        elif (
-            hash is True
-            or (hash is None and cmp is True and frozen is True)
-            and is_exc is False
-        ):
+        elif hash is True or (hash is None and eq is True and frozen is True):
+            # Build a __hash__ if told so, or if it's safe.
             builder.add_hash()
         else:
+            # Raise TypeError on attempts to hash.
             if cache_hash:
                 raise TypeError(
                     "Invalid value for cache_hash.  To use hash caching,"
                     " hashing must be either explicitly or implicitly "
                     "enabled."
                 )
             builder.make_unhashable()
 
@@ -992,29 +1050,54 @@ else:
 
 def _attrs_to_tuple(obj, attrs):
     """
     Create a tuple of all values of *obj*'s *attrs*.
     """
     return tuple(getattr(obj, a.name) for a in attrs)
 
 
-def _make_hash(attrs, frozen, cache_hash):
+def _generate_unique_filename(cls, func_name):
+    """
+    Create a "filename" suitable for a function being generated.
+    """
+    unique_id = uuid.uuid4()
+    extra = ""
+    count = 1
+
+    while True:
+        unique_filename = "<attrs generated {0} {1}.{2}{3}>".format(
+            func_name,
+            cls.__module__,
+            getattr(cls, "__qualname__", cls.__name__),
+            extra,
+        )
+        # To handle concurrency we essentially "reserve" our spot in
+        # the linecache with a dummy line.  The caller can then
+        # set this value correctly.
+        cache_line = (1, None, (str(unique_id),), unique_filename)
+        if (
+            linecache.cache.setdefault(unique_filename, cache_line)
+            == cache_line
+        ):
+            return unique_filename
+
+        # Looks like this spot is taken. Try again.
+        count += 1
+        extra = "-{0}".format(count)
+
+
+def _make_hash(cls, attrs, frozen, cache_hash):
     attrs = tuple(
-        a
-        for a in attrs
-        if a.hash is True or (a.hash is None and a.cmp is True)
+        a for a in attrs if a.hash is True or (a.hash is None and a.eq is True)
     )
 
     tab = "        "
 
-    # 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(),)
+    unique_filename = _generate_unique_filename(cls, "hash")
     type_hash = hash(unique_filename)
 
     method_lines = ["def __hash__(self):"]
 
     def append_hash_computation_lines(prefix, indent):
         """
         Generate the code for actually computing the hash code.
         Below this will either be returned directly or used to compute
@@ -1061,45 +1144,36 @@ def _make_hash(attrs, frozen, cache_hash
 
     return locs["__hash__"]
 
 
 def _add_hash(cls, attrs):
     """
     Add a hash method to *cls*.
     """
-    cls.__hash__ = _make_hash(attrs, frozen=False, cache_hash=False)
+    cls.__hash__ = _make_hash(cls, attrs, frozen=False, cache_hash=False)
     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
 
 
-WARNING_CMP_ISINSTANCE = (
-    "Comparision of subclasses using __%s__ is deprecated and will be removed "
-    "in 2019."
-)
-
+def _make_eq(cls, attrs):
+    attrs = [a for a in attrs if a.eq]
 
-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(),)
+    unique_filename = _generate_unique_filename(cls, "eq")
     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:
@@ -1122,102 +1196,95 @@ def _make_cmp(attrs):
     # 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__
+    return locs["__eq__"], __ne__
+
+
+def _make_order(cls, attrs):
+    attrs = [a for a in attrs if a.order]
 
     def attrs_to_tuple(obj):
         """
         Save us some typing.
         """
         return _attrs_to_tuple(obj, attrs)
 
     def __lt__(self, other):
         """
         Automatically created by attrs.
         """
-        if isinstance(other, self.__class__):
-            if other.__class__ is not self.__class__:
-                warnings.warn(
-                    WARNING_CMP_ISINSTANCE % ("lt",), DeprecationWarning
-                )
+        if other.__class__ is self.__class__:
             return attrs_to_tuple(self) < attrs_to_tuple(other)
-        else:
-            return NotImplemented
+
+        return NotImplemented
 
     def __le__(self, other):
         """
         Automatically created by attrs.
         """
-        if isinstance(other, self.__class__):
-            if other.__class__ is not self.__class__:
-                warnings.warn(
-                    WARNING_CMP_ISINSTANCE % ("le",), DeprecationWarning
-                )
+        if other.__class__ is self.__class__:
             return attrs_to_tuple(self) <= attrs_to_tuple(other)
-        else:
-            return NotImplemented
+
+        return NotImplemented
 
     def __gt__(self, other):
         """
         Automatically created by attrs.
         """
-        if isinstance(other, self.__class__):
-            if other.__class__ is not self.__class__:
-                warnings.warn(
-                    WARNING_CMP_ISINSTANCE % ("gt",), DeprecationWarning
-                )
+        if other.__class__ is self.__class__:
             return attrs_to_tuple(self) > attrs_to_tuple(other)
-        else:
-            return NotImplemented
+
+        return NotImplemented
 
     def __ge__(self, other):
         """
         Automatically created by attrs.
         """
-        if isinstance(other, self.__class__):
-            if other.__class__ is not self.__class__:
-                warnings.warn(
-                    WARNING_CMP_ISINSTANCE % ("ge",), DeprecationWarning
-                )
+        if other.__class__ is self.__class__:
             return attrs_to_tuple(self) >= attrs_to_tuple(other)
-        else:
-            return NotImplemented
 
-    return eq, ne, __lt__, __le__, __gt__, __ge__
+        return NotImplemented
+
+    return __lt__, __le__, __gt__, __ge__
 
 
-def _add_cmp(cls, attrs=None):
+def _add_eq(cls, attrs=None):
     """
-    Add comparison methods to *cls*.
+    Add equality methods to *cls* with *attrs*.
     """
     if attrs is None:
         attrs = cls.__attrs_attrs__
 
-    cls.__eq__, cls.__ne__, cls.__lt__, cls.__le__, cls.__gt__, cls.__ge__ = _make_cmp(  # noqa
-        attrs
-    )
+    cls.__eq__, cls.__ne__ = _make_eq(cls, 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.
+    Make a repr method that includes relevant *attrs*, adding *ns* to the full
+    name.
     """
-    attr_names = tuple(a.name for a in attrs if a.repr)
+
+    # Figure out which attributes to include, and which function to use to
+    # format them. The a.repr value can be either bool or a custom callable.
+    attr_names_with_reprs = tuple(
+        (a.name, repr if a.repr is True else a.repr)
+        for a in attrs
+        if a.repr is not False
+    )
 
     def __repr__(self):
         """
         Automatically created by attrs.
         """
         try:
             working_set = _already_repring.working_set
         except AttributeError:
@@ -1239,22 +1306,24 @@ def _make_repr(attrs, ns):
         # 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:
+            for name, attr_repr in attr_names_with_reprs:
                 if first:
                     first = False
                 else:
                     result.append(", ")
-                result.extend((name, "=", repr(getattr(self, name, NOTHING))))
+                result.extend(
+                    (name, "=", attr_repr(getattr(self, name, NOTHING)))
+                )
             return "".join(result) + ")"
         finally:
             working_set.remove(id(self))
 
     return __repr__
 
 
 def _add_repr(cls, ns=None, attrs=None):
@@ -1264,24 +1333,21 @@ def _add_repr(cls, ns=None, attrs=None):
     if attrs is None:
         attrs = cls.__attrs_attrs__
 
     cls.__repr__ = _make_repr(attrs, ns)
     return cls
 
 
 def _make_init(
-    attrs, post_init, frozen, slots, cache_hash, base_attr_map, is_exc
+    cls, attrs, post_init, frozen, slots, cache_hash, base_attr_map, is_exc
 ):
     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())
+    unique_filename = _generate_unique_filename(cls, "init")
 
     script, globs, annotations = _attrs_to_init_script(
         attrs, frozen, slots, post_init, cache_hash, base_attr_map, is_exc
     )
     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})
@@ -1316,17 +1382,17 @@ def fields(cls):
     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``
         class.
 
-    :rtype: tuple (with name accessors) of :class:`attr.Attribute`
+    :rtype: tuple (with name accessors) of `attr.Attribute`
 
     ..  versionchanged:: 16.2.0 Returned tuple allows accessing the fields
         by name.
     """
     if not isclass(cls):
         raise TypeError("Passed object must be a class.")
     attrs = getattr(cls, "__attrs_attrs__", None)
     if attrs is None:
@@ -1343,17 +1409,17 @@ def fields_dict(cls):
 
     :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
+        `attr.Attribute`\\ s. This will be a `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)
@@ -1673,74 +1739,66 @@ def __init__(self, {args}):
 
 
 class Attribute(object):
     """
     *Read-only* representation of an attribute.
 
     :attribute name: The name of the attribute.
 
-    Plus *all* arguments of :func:`attr.ib`.
+    Plus *all* arguments of `attr.ib` (except for ``factory``
+    which is only syntactic sugar for ``default=Factory(...)``.
 
-    For the version history of the fields, see :func:`attr.ib`.
+    For the version history of the fields, see `attr.ib`.
     """
 
     __slots__ = (
         "name",
         "default",
         "validator",
         "repr",
-        "cmp",
+        "eq",
+        "order",
         "hash",
         "init",
         "metadata",
         "type",
         "converter",
         "kw_only",
     )
 
     def __init__(
         self,
         name,
         default,
         validator,
         repr,
-        cmp,
+        cmp,  # XXX: unused, remove along with other cmp code.
         hash,
         init,
-        convert=None,
         metadata=None,
         type=None,
         converter=None,
         kw_only=False,
+        eq=None,
+        order=None,
     ):
+        eq, order = _determine_eq_order(cmp, eq, order)
+
         # 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("eq", eq)
+        bound_setattr("order", order)
         bound_setattr("hash", hash)
         bound_setattr("init", init)
         bound_setattr("converter", converter)
         bound_setattr(
             "metadata",
             (
                 metadata_proxy(metadata)
                 if metadata
@@ -1748,26 +1806,16 @@ class Attribute(object):
             ),
         )
         bound_setattr("type", type)
         bound_setattr("kw_only", kw_only)
 
     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"
@@ -1776,27 +1824,36 @@ class Attribute(object):
             k: getattr(ca, k)
             for k in Attribute.__slots__
             if k
             not in (
                 "name",
                 "validator",
                 "default",
                 "type",
-                "convert",
             )  # exclude methods and deprecated alias
         }
         return cls(
             name=name,
             validator=ca._validator,
             default=ca._default,
             type=type,
+            cmp=None,
             **inst_dict
         )
 
+    @property
+    def cmp(self):
+        """
+        Simulate the presence of a cmp attribute and warn.
+        """
+        warnings.warn(_CMP_DEPRECATION, DeprecationWarning, stacklevel=2)
+
+        return self.eq and self.order
+
     # Don't use attr.assoc since fields(Attribute) doesn't work
     def _assoc(self, **changes):
         """
         Copy *self* and apply *changes*.
         """
         new = copy.copy(self)
 
         new._setattrs(changes.items())
@@ -1834,26 +1891,27 @@ class Attribute(object):
 
 
 _a = [
     Attribute(
         name=name,
         default=NOTHING,
         validator=None,
         repr=True,
-        cmp=True,
+        cmp=None,
+        eq=True,
+        order=False,
         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),
+    _add_eq(_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.
@@ -1861,74 +1919,90 @@ class _CountingAttr(object):
     *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",
+        "eq",
+        "order",
         "hash",
         "init",
         "metadata",
         "_validator",
         "converter",
         "type",
         "kw_only",
     )
     __attrs_attrs__ = tuple(
         Attribute(
             name=name,
             default=NOTHING,
             validator=None,
             repr=True,
-            cmp=True,
+            cmp=None,
             hash=True,
             init=True,
             kw_only=False,
+            eq=True,
+            order=False,
         )
-        for name in ("counter", "_default", "repr", "cmp", "hash", "init")
+        for name in (
+            "counter",
+            "_default",
+            "repr",
+            "eq",
+            "order",
+            "hash",
+            "init",
+        )
     ) + (
         Attribute(
             name="metadata",
             default=None,
             validator=None,
             repr=True,
-            cmp=True,
+            cmp=None,
             hash=False,
             init=True,
             kw_only=False,
+            eq=True,
+            order=False,
         ),
     )
     cls_counter = 0
 
     def __init__(
         self,
         default,
         validator,
         repr,
-        cmp,
+        cmp,  # XXX: unused, remove along with cmp
         hash,
         init,
         converter,
         metadata,
         type,
         kw_only,
+        eq,
+        order,
     ):
         _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.eq = eq
+        self.order = order
         self.hash = hash
         self.init = init
         self.converter = converter
         self.metadata = metadata
         self.type = type
         self.kw_only = kw_only
 
     def validator(self, meth):
@@ -1958,25 +2032,25 @@ class _CountingAttr(object):
         if self._default is not NOTHING:
             raise DefaultAlreadySetError()
 
         self._default = Factory(meth, takes_self=True)
 
         return meth
 
 
-_CountingAttr = _add_cmp(_add_repr(_CountingAttr))
+_CountingAttr = _add_eq(_add_repr(_CountingAttr))
 
 
 @attrs(slots=True, init=False, hash=True)
 class Factory(object):
     """
     Stores a factory callable.
 
-    If passed as the default value to :func:`attr.ib`, the factory is used to
+    If passed as the default value to `attr.ib`, the factory is used to
     generate a new value.
 
     :param callable factory: A callable that takes either none or exactly one
         mandatory positional argument depending on *takes_self*.
     :param bool takes_self: Pass the partially initialized instance that is
         being initialized as a positional argument.
 
     .. versionadded:: 17.1.0  *takes_self*
@@ -1999,25 +2073,25 @@ 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
+        If *attrs* is a list or an ordered dict (`dict` on Python 3.6+,
+        `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`
+    :type attrs: `list` or `dict`
 
     :param tuple bases: Classes that the new class will subclass.
 
-    :param attributes_arguments: Passed unmodified to :func:`attr.s`.
+    :param attributes_arguments: Passed unmodified to `attr.s`.
 
     :return: A new class with *attrs*.
     :rtype: type
 
     .. versionadded:: 17.1.0 *bases*
     .. versionchanged:: 18.1.0 If *attrs* is ordered, the order is retained.
     """
     if isinstance(attrs, dict):
@@ -2039,16 +2113,24 @@ def make_class(name, attrs, bases=(objec
     # defined for arguments greater than 0 (IronPython).
     try:
         type_.__module__ = sys._getframe(1).f_globals.get(
             "__name__", "__main__"
         )
     except (AttributeError, ValueError):
         pass
 
+    # We do it here for proper warnings with meaningful stacklevel.
+    cmp = attributes_arguments.pop("cmp", None)
+    attributes_arguments["eq"], attributes_arguments[
+        "order"
+    ] = _determine_eq_order(
+        cmp, attributes_arguments.get("eq"), attributes_arguments.get("order")
+    )
+
     return _attrs(these=cls_dict, **attributes_arguments)(type_)
 
 
 # These are required by within this module so we define them here and merely
 # import into .validators.
 
 
 @attrs(slots=True, hash=True)
new file mode 100644
--- /dev/null
+++ b/third_party/python/attrs/attr/_version.py
@@ -0,0 +1,85 @@
+from __future__ import absolute_import, division, print_function
+
+from functools import total_ordering
+
+from ._funcs import astuple
+from ._make import attrib, attrs
+
+
+@total_ordering
+@attrs(eq=False, order=False, slots=True, frozen=True)
+class VersionInfo(object):
+    """
+    A version object that can be compared to tuple of length 1--4:
+
+    >>> attr.VersionInfo(19, 1, 0, "final")  <= (19, 2)
+    True
+    >>> attr.VersionInfo(19, 1, 0, "final") < (19, 1, 1)
+    True
+    >>> vi = attr.VersionInfo(19, 2, 0, "final")
+    >>> vi < (19, 1, 1)
+    False
+    >>> vi < (19,)
+    False
+    >>> vi == (19, 2,)
+    True
+    >>> vi == (19, 2, 1)
+    False
+
+    .. versionadded:: 19.2
+    """
+
+    year = attrib(type=int)
+    minor = attrib(type=int)
+    micro = attrib(type=int)
+    releaselevel = attrib(type=str)
+
+    @classmethod
+    def _from_version_string(cls, s):
+        """
+        Parse *s* and return a _VersionInfo.
+        """
+        v = s.split(".")
+        if len(v) == 3:
+            v.append("final")
+
+        return cls(
+            year=int(v[0]), minor=int(v[1]), micro=int(v[2]), releaselevel=v[3]
+        )
+
+    def _ensure_tuple(self, other):
+        """
+        Ensure *other* is a tuple of a valid length.
+
+        Returns a possibly transformed *other* and ourselves as a tuple of
+        the same length as *other*.
+        """
+
+        if self.__class__ is other.__class__:
+            other = astuple(other)
+
+        if not isinstance(other, tuple):
+            raise NotImplementedError
+
+        if not (1 <= len(other) <= 4):
+            raise NotImplementedError
+
+        return astuple(self)[: len(other)], other
+
+    def __eq__(self, other):
+        try:
+            us, them = self._ensure_tuple(other)
+        except NotImplementedError:
+            return NotImplemented
+
+        return us == them
+
+    def __lt__(self, other):
+        try:
+            us, them = self._ensure_tuple(other)
+        except NotImplementedError:
+            return NotImplemented
+
+        # Since alphabetically "dev0" < "final" < "post1" < "post2", we don't
+        # have to do anything special with releaselevel for now.
+        return us < them
new file mode 100644
--- /dev/null
+++ b/third_party/python/attrs/attr/_version.pyi
@@ -0,0 +1,9 @@
+class VersionInfo:
+    @property
+    def year(self) -> int: ...
+    @property
+    def minor(self) -> int: ...
+    @property
+    def micro(self) -> int: ...
+    @property
+    def releaselevel(self) -> str: ...
--- a/third_party/python/attrs/attr/converters.py
+++ b/third_party/python/attrs/attr/converters.py
@@ -27,24 +27,24 @@ def optional(converter):
 
 
 def default_if_none(default=NOTHING, factory=None):
     """
     A converter that allows to replace ``None`` values by *default* or the
     result of *factory*.
 
     :param default: Value to be used if ``None`` is passed. Passing an instance
-       of :class:`attr.Factory` is supported, however the ``takes_self`` option
+       of `attr.Factory` is supported, however the ``takes_self`` option
        is *not*.
     :param callable factory: A callable that takes not parameters whose result
        is used if ``None`` is passed.
 
     :raises TypeError: If **neither** *default* or *factory* is passed.
     :raises TypeError: If **both** *default* and *factory* are passed.
-    :raises ValueError: If an instance of :class:`attr.Factory` is passed with
+    :raises ValueError: If an instance of `attr.Factory` is passed with
        ``takes_self=True``.
 
     .. versionadded:: 18.2.0
     """
     if default is NOTHING and factory is None:
         raise TypeError("Must pass either `default` or `factory`.")
 
     if default is not NOTHING and factory is not None:
--- a/third_party/python/attrs/attr/exceptions.py
+++ b/third_party/python/attrs/attr/exceptions.py
@@ -1,17 +1,17 @@
 from __future__ import absolute_import, division, print_function
 
 
 class FrozenInstanceError(AttributeError):
     """
     A frozen/immutable instance has been attempted to be modified.
 
     It mirrors the behavior of ``namedtuples`` by using the same error message
-    and subclassing :exc:`AttributeError`.
+    and subclassing `AttributeError`.
 
     .. versionadded:: 16.1.0
     """
 
     msg = "can't set attribute"
     args = [msg]
 
 
@@ -50,8 +50,25 @@ class UnannotatedAttributeError(RuntimeE
 
 
 class PythonTooOldError(RuntimeError):
     """
     An ``attrs`` feature requiring a more recent python version has been used.
 
     .. versionadded:: 18.2.0
     """
+
+
+class NotCallableError(TypeError):
+    """
+    A ``attr.ib()`` requiring a callable has been set with a value
+    that is not callable.
+
+    .. versionadded:: 19.2.0
+    """
+
+    def __init__(self, msg, value):
+        super(TypeError, self).__init__(msg, value)
+        self.msg = msg
+        self.value = value
+
+    def __str__(self):
+        return str(self.msg)
--- a/third_party/python/attrs/attr/exceptions.pyi
+++ b/third_party/python/attrs/attr/exceptions.pyi
@@ -1,7 +1,15 @@
+from typing import Any
+
 class FrozenInstanceError(AttributeError):
     msg: str = ...
 
 class AttrsAttributeNotFoundError(ValueError): ...
 class NotAnAttrsClassError(ValueError): ...
 class DefaultAlreadySetError(RuntimeError): ...
 class UnannotatedAttributeError(RuntimeError): ...
+class PythonTooOldError(RuntimeError): ...
+
+class NotCallableError(TypeError):
+    msg: str = ...
+    value: Any = ...
+    def __init__(self, msg: str, value: Any) -> None: ...
--- a/third_party/python/attrs/attr/filters.py
+++ b/third_party/python/attrs/attr/filters.py
@@ -1,10 +1,10 @@
 """
-Commonly useful filters for :func:`attr.asdict`.
+Commonly useful filters for `attr.asdict`.
 """
 
 from __future__ import absolute_import, division, print_function
 
 from ._compat import isclass
 from ._make import Attribute
 
 
@@ -18,35 +18,35 @@ 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: `list` of `type` or `attr.Attribute`\\ s
 
-    :rtype: :class:`callable`
+    :rtype: `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: `list` of classes or `attr.Attribute`\\ s.
 
-    :rtype: :class:`callable`
+    :rtype: `callable`
     """
     cls, attrs = _split_what(what)
 
     def exclude_(attribute, value):
         return value.__class__ not in cls and attribute not in attrs
 
     return exclude_
--- a/third_party/python/attrs/attr/validators.py
+++ b/third_party/python/attrs/attr/validators.py
@@ -1,18 +1,31 @@
 """
 Commonly useful validators.
 """
 
 from __future__ import absolute_import, division, print_function
 
+import re
+
 from ._make import _AndValidator, and_, attrib, attrs
+from .exceptions import NotCallableError
 
 
-__all__ = ["and_", "in_", "instance_of", "optional", "provides"]
+__all__ = [
+    "and_",
+    "deep_iterable",
+    "deep_mapping",
+    "in_",
+    "instance_of",
+    "is_callable",
+    "matches_re",
+    "optional",
+    "provides",
+]
 
 
 @attrs(repr=False, slots=True, hash=True)
 class _InstanceOfValidator(object):
     type = attrib()
 
     def __call__(self, inst, attr, value):
         """
@@ -35,30 +48,102 @@ class _InstanceOfValidator(object):
     def __repr__(self):
         return "<instance_of validator for type {type!r}>".format(
             type=self.type
         )
 
 
 def instance_of(type):
     """
-    A validator that raises a :exc:`TypeError` if the initializer is called
+    A validator that raises a `TypeError` if the initializer is called
     with a wrong type for this particular attribute (checks are performed using
-    :func:`isinstance` therefore it's also valid to pass a tuple of types).
+    `isinstance` therefore it's also valid to pass a tuple of types).
 
     :param type: The type to check for.
     :type type: type or tuple of types
 
     :raises TypeError: With a human readable error message, the attribute
-        (of type :class:`attr.Attribute`), the expected type, and the value it
+        (of type `attr.Attribute`), the expected type, and the value it
         got.
     """
     return _InstanceOfValidator(type)
 
 
+@attrs(repr=False, frozen=True)
+class _MatchesReValidator(object):
+    regex = attrib()
+    flags = attrib()
+    match_func = attrib()
+
+    def __call__(self, inst, attr, value):
+        """
+        We use a callable class to be able to change the ``__repr__``.
+        """
+        if not self.match_func(value):
+            raise ValueError(
+                "'{name}' must match regex {regex!r}"
+                " ({value!r} doesn't)".format(
+                    name=attr.name, regex=self.regex.pattern, value=value
+                ),
+                attr,
+                self.regex,
+                value,
+            )
+
+    def __repr__(self):
+        return "<matches_re validator for pattern {regex!r}>".format(
+            regex=self.regex
+        )
+
+
+def matches_re(regex, flags=0, func=None):
+    r"""
+    A validator that raises `ValueError` if the initializer is called
+    with a string that doesn't match *regex*.
+
+    :param str regex: a regex string to match against
+    :param int flags: flags that will be passed to the underlying re function
+        (default 0)
+    :param callable func: which underlying `re` function to call (options
+        are `re.fullmatch`, `re.search`, `re.match`, default
+        is ``None`` which means either `re.fullmatch` or an emulation of
+        it on Python 2). For performance reasons, they won't be used directly
+        but on a pre-`re.compile`\ ed pattern.
+
+    .. versionadded:: 19.2.0
+    """
+    fullmatch = getattr(re, "fullmatch", None)
+    valid_funcs = (fullmatch, None, re.search, re.match)
+    if func not in valid_funcs:
+        raise ValueError(
+            "'func' must be one of %s."
+            % (
+                ", ".join(
+                    sorted(
+                        e and e.__name__ or "None" for e in set(valid_funcs)
+                    )
+                ),
+            )
+        )
+
+    pattern = re.compile(regex, flags)
+    if func is re.match:
+        match_func = pattern.match
+    elif func is re.search:
+        match_func = pattern.search
+    else:
+        if fullmatch:
+            match_func = pattern.fullmatch
+        else:
+            pattern = re.compile(r"(?:{})\Z".format(regex), flags)
+            match_func = pattern.match
+
+    return _MatchesReValidator(pattern, flags, match_func)
+
+
 @attrs(repr=False, slots=True, hash=True)
 class _ProvidesValidator(object):
     interface = attrib()
 
     def __call__(self, inst, attr, value):
         """
         We use a callable class to be able to change the ``__repr__``.
         """
@@ -76,25 +161,25 @@ class _ProvidesValidator(object):
     def __repr__(self):
         return "<provides validator for interface {interface!r}>".format(
             interface=self.interface
         )
 
 
 def provides(interface):
     """
-    A validator that raises a :exc:`TypeError` if the initializer is called
+    A validator that raises a `TypeError` if the initializer is called
     with an object that does not provide the requested *interface* (checks are
     performed using ``interface.providedBy(value)`` (see `zope.interface
     <https://zopeinterface.readthedocs.io/en/latest/>`_).
 
     :param zope.interface.Interface interface: The interface to check for.
 
     :raises TypeError: With a human readable error message, the attribute
-        (of type :class:`attr.Attribute`), the expected interface, and the
+        (of type `attr.Attribute`), the expected interface, and the
         value it got.
     """
     return _ProvidesValidator(interface)
 
 
 @attrs(repr=False, slots=True, hash=True)
 class _OptionalValidator(object):
     validator = attrib()
@@ -114,17 +199,17 @@ class _OptionalValidator(object):
 def optional(validator):
     """
     A validator that makes an attribute optional.  An optional attribute is one
     which can be set to ``None`` in addition to satisfying the requirements of
     the sub-validator.
 
     :param validator: A validator (or a list of validators) that is used for
         non-``None`` values.
-    :type validator: callable or :class:`list` of callables.
+    :type validator: callable or `list` of callables.
 
     .. versionadded:: 15.1.0
     .. versionchanged:: 17.1.0 *validator* can be a list of validators.
     """
     if isinstance(validator, list):
         return _OptionalValidator(_AndValidator(validator))
     return _OptionalValidator(validator)
 
@@ -149,54 +234,65 @@ class _InValidator(object):
     def __repr__(self):
         return "<in_ validator with options {options!r}>".format(
             options=self.options
         )
 
 
 def in_(options):
     """
-    A validator that raises a :exc:`ValueError` if the initializer is called
+    A validator that raises a `ValueError` if the initializer is called
     with a value that does not belong in the options provided.  The check is
     performed using ``value in options``.
 
     :param options: Allowed options.
-    :type options: list, tuple, :class:`enum.Enum`, ...
+    :type options: list, tuple, `enum.Enum`, ...
 
     :raises ValueError: With a human readable error message, the attribute (of
-       type :class:`attr.Attribute`), the expected options, and the value it
+       type `attr.Attribute`), the expected options, and the value it
        got.
 
     .. versionadded:: 17.1.0
     """
     return _InValidator(options)
 
 
 @attrs(repr=False, slots=False, hash=True)
 class _IsCallableValidator(object):
     def __call__(self, inst, attr, value):
         """
         We use a callable class to be able to change the ``__repr__``.
         """
         if not callable(value):
-            raise TypeError("'{name}' must be callable".format(name=attr.name))
+            message = (
+                "'{name}' must be callable "
+                "(got {value!r} that is a {actual!r})."
+            )
+            raise NotCallableError(
+                msg=message.format(
+                    name=attr.name, value=value, actual=value.__class__
+                ),
+                value=value,
+            )
 
     def __repr__(self):
         return "<is_callable validator>"
 
 
 def is_callable():
     """
-    A validator that raises a :class:`TypeError` if the initializer is called
-    with a value for this particular attribute that is not callable.
+    A validator that raises a `attr.exceptions.NotCallableError` if the
+    initializer is called with a value for this particular attribute
+    that is not callable.
 
     .. versionadded:: 19.1.0
 
-    :raises TypeError: With a human readable error message containing the
-        attribute (of type :class:`attr.Attribute`) name.
+    :raises `attr.exceptions.NotCallableError`: With a human readable error
+        message containing the attribute (`attr.Attribute`) name,
+        and the value it got.
     """
     return _IsCallableValidator()
 
 
 @attrs(repr=False, slots=True, hash=True)
 class _DeepIterable(object):
     member_validator = attrib(validator=is_callable())
     iterable_validator = attrib(
--- a/third_party/python/attrs/attr/validators.pyi
+++ b/third_party/python/attrs/attr/validators.pyi
@@ -1,24 +1,66 @@
-from typing import Container, List, Union, TypeVar, Type, Any, Optional, Tuple
+from typing import (
+    Container,
+    List,
+    Union,
+    TypeVar,
+    Type,
+    Any,
+    Optional,
+    Tuple,
+    Iterable,
+    Mapping,
+    Callable,
+    Match,
+    AnyStr,
+    overload,
+)
 from . import _ValidatorType
 
 _T = TypeVar("_T")
+_T1 = TypeVar("_T1")
+_T2 = TypeVar("_T2")
+_T3 = TypeVar("_T3")
+_I = TypeVar("_I", bound=Iterable)
+_K = TypeVar("_K")
+_V = TypeVar("_V")
+_M = TypeVar("_M", bound=Mapping)
 
+# To be more precise on instance_of use some overloads.
+# If there are more than 3 items in the tuple then we fall back to Any
+@overload
+def instance_of(type: Type[_T]) -> _ValidatorType[_T]: ...
+@overload
+def instance_of(type: Tuple[Type[_T]]) -> _ValidatorType[_T]: ...
+@overload
 def instance_of(
-    type: Union[Tuple[Type[_T], ...], Type[_T]]
-) -> _ValidatorType[_T]: ...
+    type: Tuple[Type[_T1], Type[_T2]]
+) -> _ValidatorType[Union[_T1, _T2]]: ...
+@overload
+def instance_of(
+    type: Tuple[Type[_T1], Type[_T2], Type[_T3]]
+) -> _ValidatorType[Union[_T1, _T2, _T3]]: ...
+@overload
+def instance_of(type: Tuple[type, ...]) -> _ValidatorType[Any]: ...
 def provides(interface: Any) -> _ValidatorType[Any]: ...
 def optional(
     validator: Union[_ValidatorType[_T], List[_ValidatorType[_T]]]
 ) -> _ValidatorType[Optional[_T]]: ...
 def in_(options: Container[_T]) -> _ValidatorType[_T]: ...
 def and_(*validators: _ValidatorType[_T]) -> _ValidatorType[_T]: ...
+def matches_re(
+    regex: AnyStr,
+    flags: int = ...,
+    func: Optional[
+        Callable[[AnyStr, AnyStr, int], Optional[Match[AnyStr]]]
+    ] = ...,
+) -> _ValidatorType[AnyStr]: ...
 def deep_iterable(
     member_validator: _ValidatorType[_T],
-    iterable_validator: Optional[_ValidatorType[_T]],
-) -> _ValidatorType[_T]: ...
+    iterable_validator: Optional[_ValidatorType[_I]] = ...,
+) -> _ValidatorType[_I]: ...
 def deep_mapping(
-    key_validator: _ValidatorType[_T],
-    value_validator: _ValidatorType[_T],
-    mapping_validator: Optional[_ValidatorType[_T]],
-) -> _ValidatorType[_T]: ...
+    key_validator: _ValidatorType[_K],
+    value_validator: _ValidatorType[_V],
+    mapping_validator: Optional[_ValidatorType[_M]] = ...,
+) -> _ValidatorType[_M]: ...
 def is_callable() -> _ValidatorType[_T]: ...
deleted file mode 100644
--- a/third_party/python/attrs/attrs-19.1.0.dist-info/METADATA
+++ /dev/null
@@ -1,249 +0,0 @@
-Metadata-Version: 2.1
-Name: attrs
-Version: 19.1.0
-Summary: Classes Without Boilerplate
-Home-page: https://www.attrs.org/
-Author: Hynek Schlawack
-Author-email: hs@ox.cx
-Maintainer: Hynek Schlawack
-Maintainer-email: hs@ox.cx
-License: MIT
-Project-URL: Documentation, https://www.attrs.org/
-Project-URL: Bug Tracker, https://github.com/python-attrs/attrs/issues
-Project-URL: Source Code, https://github.com/python-attrs/attrs
-Keywords: class,attribute,boilerplate
-Platform: UNKNOWN
-Classifier: Development Status :: 5 - Production/Stable
-Classifier: Intended Audience :: Developers
-Classifier: Natural Language :: English
-Classifier: License :: OSI Approved :: MIT License
-Classifier: Operating System :: OS Independent
-Classifier: Programming Language :: Python
-Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 2.7
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.4
-Classifier: Programming Language :: Python :: 3.5
-Classifier: Programming Language :: Python :: 3.6
-Classifier: Programming Language :: Python :: 3.7
-Classifier: Programming Language :: Python :: Implementation :: CPython
-Classifier: Programming Language :: Python :: Implementation :: PyPy
-Classifier: Topic :: Software Development :: Libraries :: Python Modules
-Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*
-Provides-Extra: dev
-Requires-Dist: coverage ; extra == 'dev'
-Requires-Dist: hypothesis ; extra == 'dev'
-Requires-Dist: pympler ; extra == 'dev'
-Requires-Dist: pytest ; extra == 'dev'
-Requires-Dist: six ; extra == 'dev'
-Requires-Dist: zope.interface ; extra == 'dev'
-Requires-Dist: sphinx ; extra == 'dev'
-Requires-Dist: pre-commit ; extra == 'dev'
-Provides-Extra: docs
-Requires-Dist: sphinx ; extra == 'docs'
-Requires-Dist: zope.interface ; extra == 'docs'
-Provides-Extra: tests
-Requires-Dist: coverage ; extra == 'tests'
-Requires-Dist: hypothesis ; extra == 'tests'
-Requires-Dist: pympler ; extra == 'tests'
-Requires-Dist: pytest ; extra == 'tests'
-Requires-Dist: six ; extra == 'tests'
-Requires-Dist: zope.interface ; extra == 'tests'
-
-.. image:: https://www.attrs.org/en/latest/_static/attrs_logo.png
-   :alt: attrs Logo
-
-======================================
-``attrs``: Classes Without Boilerplate
-======================================
-
-.. image:: https://readthedocs.org/projects/attrs/badge/?version=stable
-   :target: https://www.attrs.org/en/stable/?badge=stable
-   :alt: Documentation Status
-
-.. image:: https://travis-ci.org/python-attrs/attrs.svg?branch=master
-   :target: https://travis-ci.org/python-attrs/attrs
-   :alt: CI Status
-
-.. image:: https://codecov.io/github/python-attrs/attrs/branch/master/graph/badge.svg
-   :target: https://codecov.io/github/python-attrs/attrs
-   :alt: Test Coverage
-
-.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
-   :target: https://github.com/ambv/black
-   :alt: Code style: black
-
-.. teaser-begin
-
-``attrs`` is the Python package that will bring back the **joy** of **writing classes** by relieving you from the drudgery of implementing object protocols (aka `dunder <https://nedbatchelder.com/blog/200605/dunder.html>`_ methods).
-
-Its main goal is to help you to write **concise** and **correct** software without slowing down your code.
-
-.. -spiel-end-
-
-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(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,
-- a nice human-readable ``__repr__``,
-- a complete set of comparison methods,
-- an initializer,
-- and much more,
-
-*without* writing dull boilerplate code again and again and *without* runtime performance penalties.
-
-On Python 3.6 and later, you can often even drop the calls to ``attr.ib()`` by using `type annotations <https://www.attrs.org/en/latest/types.html>`_.
-
-This gives you the power to use actual classes with actual types in your code instead of confusing ``tuple``\ s or `confusingly behaving <https://www.attrs.org/en/stable/why.html#namedtuples>`_ ``namedtuple``\ s.
-Which in turn encourages you to write *small classes* that do `one thing well <https://www.destroyallsoftware.com/talks/boundaries>`_.
-Never again violate the `single responsibility principle <https://en.wikipedia.org/wiki/Single_responsibility_principle>`_ just because implementing ``__init__`` et al is a painful drag.
-
-
-.. -testimonials-
-
-Testimonials
-============
-
-**Amber Hawkie Brown**, Twisted Release Manager and Computer Owl:
-
-  Writing a fully-functional class using attrs takes me less time than writing this testimonial.
-
-
-**Glyph Lefkowitz**, creator of `Twisted <https://twistedmatrix.com/>`_, `Automat <https://pypi.org/project/Automat/>`_, and other open source software, in `The One Python Library Everyone Needs <https://glyph.twistedmatrix.com/2016/08/attrs.html>`_:
-
-  I’m looking forward to is being able to program in Python-with-attrs everywhere.
-  It exerts a subtle, but positive, design influence in all the codebases I’ve see it used in.
-
-
-**Kenneth Reitz**, author of `Requests <http://www.python-requests.org/>`_ and Developer Advocate at DigitalOcean, (`on paper no less <https://twitter.com/hynek/status/866817877650751488>`_!):
-
-  attrs—classes for humans.  I like it.
-
-
-**Łukasz Langa**, prolific CPython core developer and Production Engineer at Facebook:
-
-  I'm increasingly digging your attr.ocity. Good job!
-
-
-.. -end-
-
-.. -project-information-
-
-Getting Help
-============
-
-Please use the ``python-attrs`` tag on `StackOverflow <https://stackoverflow.com/questions/tagged/python-attrs>`_ to get help.
-
-Answering questions of your fellow developers is also great way to help the project!
-
-
-Project Information
-===================
-
-``attrs`` is released under the `MIT <https://choosealicense.com/licenses/mit/>`_ license,
-its documentation lives at `Read the Docs <https://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.
-
-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 <https://www.attrs.org/en/latest/contributing.html>`_ to get you started!
-
-
-Release Information
-===================
-
-19.1.0 (2019-03-03)
--------------------
-
-Backward-incompatible Changes
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-- Fixed a bug where deserialized objects with ``cache_hash=True`` could have incorrect hash code values.
-  This change breaks classes with ``cache_hash=True`` when a custom ``__setstate__`` is present.
-  An exception will be thrown when applying the ``attrs`` annotation to such a class.
-  This limitation is tracked in issue `#494 <https://github.com/python-attrs/attrs/issues/494>`_.
-  `#482 <https://github.com/python-attrs/attrs/issues/482>`_
-
-
-Changes
-^^^^^^^
-
-- Add ``is_callable``, ``deep_iterable``, and ``deep_mapping`` validators.
-
-  * ``is_callable``: validates that a value is callable
-  * ``deep_iterable``: Allows recursion down into an iterable,
-    applying another validator to every member in the iterable
-    as well as applying an optional validator to the iterable itself.
-  * ``deep_mapping``: Allows recursion down into the items in a mapping object,
-    applying a key validator and a value validator to the key and value in every item.
-    Also applies an optional validator to the mapping object itself.
-
-  You can find them in the ``attr.validators`` package.
-  `#425 <https://github.com/python-attrs/attrs/issues/425>`_
-- Fixed stub files to prevent errors raised by mypy's ``disallow_any_generics = True`` option.
-  `#443 <https://github.com/python-attrs/attrs/issues/443>`_
-- Attributes with ``init=False`` now can follow after ``kw_only=True`` attributes.
-  `#450 <https://github.com/python-attrs/attrs/issues/450>`_
-- ``attrs`` now has first class support for defining exception classes.
-
-  If you define a class using ``@attr.s(auto_exc=True)`` and subclass an exception, the class will behave like a well-behaved exception class including an appropriate ``__str__`` method, and all attributes additionally available in an ``args`` attribute.
-  `#500 <https://github.com/python-attrs/attrs/issues/500>`_
-- Clarified documentation for hashing to warn that hashable objects should be deeply immutable (in their usage, even if this is not enforced).
-  `#503 <https://github.com/python-attrs/attrs/issues/503>`_
-
-`Full changelog <https://www.attrs.org/en/stable/changelog.html>`_.
-
-Credits
-=======
-
-``attrs`` is written and maintained by `Hynek Schlawack <https://hynek.me/>`_.
-
-The development is kindly supported by `Variomedia AG <https://www.variomedia.de/>`_.
-
-A full list of contributors can be found in `GitHub's overview <https://github.com/python-attrs/attrs/graphs/contributors>`_.
-
-It’s the spiritual successor of `characteristic <https://characteristic.readthedocs.io/>`_ and aspires to fix some of it clunkiness and unfortunate decisions.
-Both were inspired by Twisted’s `FancyEqMixin <https://twistedmatrix.com/documents/current/api/twisted.python.util.FancyEqMixin.html>`_ but both are implemented using class decorators because `subclassing is bad for you <https://www.youtube.com/watch?v=3MNVP9-hglc>`_, m’kay?
-
-
deleted file mode 100644
--- a/third_party/python/attrs/attrs-19.1.0.dist-info/RECORD
+++ /dev/null
@@ -1,20 +0,0 @@
-attr/__init__.py,sha256=3XomfUfit8bVVEmSf1bRhLnRMPKauPbzFqPUnVRPgXw,1244
-attr/__init__.pyi,sha256=OON4rNWdgL69frd_WdrxtuQe8CEczl3aFpgifFeESN8,7769
-attr/_compat.py,sha256=GcjqWHrwUWGVCbDKY7twYt-Rr_4nPJqBnfrf5SeHsIY,4583
-attr/_config.py,sha256=_KvW0mQdH2PYjHc0YfIUaV_o2pVfM7ziMEYTxwmEhOA,514
-attr/_funcs.py,sha256=7v3MNMHdOUP2NkiLPwEiWAorBs3uNQq5Rn70Odr5uqo,9725
-attr/_make.py,sha256=be1PmzR8EDGfVA2Cx6ljsTIuXRxW2tEWPpTqtQXde0Y,68317
-attr/converters.py,sha256=SFPiz6-hAs2pw3kn7SzkBcdpE9AjW8iT9wjpe2eLDrQ,2155
-attr/converters.pyi,sha256=wAhCoOT1MFV8t323rpD87O7bxQ8CYLTPiBQd-29BieI,351
-attr/exceptions.py,sha256=N0WQfKvBVd4GWgDxTbFScg4ajy7-HlyvXiwlSQBA0jA,1272
-attr/exceptions.pyi,sha256=sq7TbBEGGSf81uFXScW9_aO62vd0v6LAvqz0a8Hrsxw,257
-attr/filters.py,sha256=s6NrcRWJKlCQauPEH0S4lmgFwlCdUQcHKcNkDHpptN4,1153
-attr/filters.pyi,sha256=xDpmKQlFdssgxGa5tsl1ADh_3zwAwAT4vUhd8h-8-Tk,214
-attr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
-attr/validators.py,sha256=ZAf_y5wNHyq2Rdlin_fwplQnU2u5wZnvmYJq1JddPtM,8750
-attr/validators.pyi,sha256=p2xr2ob8RaKW3PqlKDrQQVAyl8ZH4pNdlZzWXapGPjk,897
-attrs-19.1.0.dist-info/LICENSE,sha256=v2WaKLSSQGAvVrvfSQy-LsUJsVuY-Z17GaUsdA4yeGM,1082
-attrs-19.1.0.dist-info/METADATA,sha256=5yXp3BTFGRkY2hQDs18h-2dT7xnSlExRUfxvujCtHTE,10275
-attrs-19.1.0.dist-info/WHEEL,sha256=_wJFdOYk7i3xxT8ElOkUJvOdOvfNGbR9g-bf6UQT6sU,110
-attrs-19.1.0.dist-info/top_level.txt,sha256=tlRYMddkRlKPqJ96wP2_j9uEsmcNHgD2SbuWd4CzGVU,5
-attrs-19.1.0.dist-info/RECORD,,
deleted file mode 100644
--- a/third_party/python/attrs/attrs-19.1.0.dist-info/WHEEL
+++ /dev/null
@@ -1,6 +0,0 @@
-Wheel-Version: 1.0
-Generator: bdist_wheel (0.32.3)
-Root-Is-Purelib: true
-Tag: py2-none-any
-Tag: py3-none-any
-
rename from third_party/python/attrs/attrs-19.1.0.dist-info/LICENSE
rename to third_party/python/attrs/attrs-19.2.0.dist-info/LICENSE
new file mode 100644
--- /dev/null
+++ b/third_party/python/attrs/attrs-19.2.0.dist-info/METADATA
@@ -0,0 +1,284 @@
+Metadata-Version: 2.1
+Name: attrs
+Version: 19.2.0
+Summary: Classes Without Boilerplate
+Home-page: https://www.attrs.org/
+Author: Hynek Schlawack
+Author-email: hs@ox.cx
+Maintainer: Hynek Schlawack
+Maintainer-email: hs@ox.cx
+License: MIT
+Project-URL: Documentation, https://www.attrs.org/
+Project-URL: Bug Tracker, https://github.com/python-attrs/attrs/issues
+Project-URL: Source Code, https://github.com/python-attrs/attrs
+Keywords: class,attribute,boilerplate
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: Natural Language :: English
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*
+Description-Content-Type: text/x-rst
+Provides-Extra: azure-pipelines
+Requires-Dist: coverage ; extra == 'azure-pipelines'
+Requires-Dist: hypothesis ; extra == 'azure-pipelines'
+Requires-Dist: pympler ; extra == 'azure-pipelines'
+Requires-Dist: pytest (>=4.3.0) ; extra == 'azure-pipelines'
+Requires-Dist: six ; extra == 'azure-pipelines'
+Requires-Dist: zope.interface ; extra == 'azure-pipelines'
+Requires-Dist: pytest-azurepipelines ; extra == 'azure-pipelines'
+Provides-Extra: dev
+Requires-Dist: coverage ; extra == 'dev'
+Requires-Dist: hypothesis ; extra == 'dev'
+Requires-Dist: pympler ; extra == 'dev'
+Requires-Dist: pytest (>=4.3.0) ; extra == 'dev'
+Requires-Dist: six ; extra == 'dev'
+Requires-Dist: zope.interface ; extra == 'dev'
+Requires-Dist: sphinx ; extra == 'dev'
+Requires-Dist: pre-commit ; extra == 'dev'
+Provides-Extra: docs
+Requires-Dist: sphinx ; extra == 'docs'
+Requires-Dist: zope.interface ; extra == 'docs'
+Provides-Extra: tests
+Requires-Dist: coverage ; extra == 'tests'
+Requires-Dist: hypothesis ; extra == 'tests'
+Requires-Dist: pympler ; extra == 'tests'
+Requires-Dist: pytest (>=4.3.0) ; extra == 'tests'
+Requires-Dist: six ; extra == 'tests'
+Requires-Dist: zope.interface ; extra == 'tests'
+
+.. image:: https://www.attrs.org/en/latest/_static/attrs_logo.png
+   :alt: attrs Logo
+
+======================================
+``attrs``: Classes Without Boilerplate
+======================================
+
+.. image:: https://readthedocs.org/projects/attrs/badge/?version=stable
+   :target: https://www.attrs.org/en/stable/?badge=stable
+   :alt: Documentation Status
+
+.. image:: https://attrs.visualstudio.com/attrs/_apis/build/status/python-attrs.attrs?branchName=master
+   :target: https://attrs.visualstudio.com/attrs/_build/latest?definitionId=1&branchName=master
+   :alt: CI Status
+
+.. image:: https://codecov.io/github/python-attrs/attrs/branch/master/graph/badge.svg
+   :target: https://codecov.io/github/python-attrs/attrs
+   :alt: Test Coverage
+
+.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
+   :target: https://github.com/psf/black
+   :alt: Code style: black
+
+.. teaser-begin
+
+``attrs`` is the Python package that will bring back the **joy** of **writing classes** by relieving you from the drudgery of implementing object protocols (aka `dunder <https://nedbatchelder.com/blog/200605/dunder.html>`_ methods).
+
+Its main goal is to help you to write **concise** and **correct** software without slowing down your code.
+
+.. -spiel-end-
+
+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(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,
+- a nice human-readable ``__repr__``,
+- a complete set of comparison methods (equality and ordering),
+- an initializer,
+- and much more,
+
+*without* writing dull boilerplate code again and again and *without* runtime performance penalties.
+
+On Python 3.6 and later, you can often even drop the calls to ``attr.ib()`` by using `type annotations <https://www.attrs.org/en/latest/types.html>`_.
+
+This gives you the power to use actual classes with actual types in your code instead of confusing ``tuple``\ s or `confusingly behaving <https://www.attrs.org/en/stable/why.html#namedtuples>`_ ``namedtuple``\ s.
+Which in turn encourages you to write *small classes* that do `one thing well <https://www.destroyallsoftware.com/talks/boundaries>`_.
+Never again violate the `single responsibility principle <https://en.wikipedia.org/wiki/Single_responsibility_principle>`_ just because implementing ``__init__`` et al is a painful drag.
+
+
+.. -testimonials-
+
+Testimonials
+============
+
+**Amber Hawkie Brown**, Twisted Release Manager and Computer Owl:
+
+  Writing a fully-functional class using attrs takes me less time than writing this testimonial.
+
+
+**Glyph Lefkowitz**, creator of `Twisted <https://twistedmatrix.com/>`_, `Automat <https://pypi.org/project/Automat/>`_, and other open source software, in `The One Python Library Everyone Needs <https://glyph.twistedmatrix.com/2016/08/attrs.html>`_:
+
+  I’m looking forward to is being able to program in Python-with-attrs everywhere.
+  It exerts a subtle, but positive, design influence in all the codebases I’ve see it used in.
+
+
+**Kenneth Reitz**, creator of `Requests <https://github.com/psf/requests>`_ (`on paper no less <https://twitter.com/hynek/status/866817877650751488>`_!):
+
+  attrs—classes for humans.  I like it.
+
+
+**Łukasz Langa**, creator of `Black <https://github.com/psf/black>`_, prolific Python core developer, and release manager for Python 3.8 and 3.9:
+
+  I'm increasingly digging your attr.ocity. Good job!
+
+
+.. -end-
+
+.. -project-information-
+
+Getting Help
+============
+
+Please use the ``python-attrs`` tag on `StackOverflow <https://stackoverflow.com/questions/tagged/python-attrs>`_ to get help.
+
+Answering questions of your fellow developers is also great way to help the project!
+
+
+Project Information
+===================
+
+``attrs`` is released under the `MIT <https://choosealicense.com/licenses/mit/>`_ license,
+its documentation lives at `Read the Docs <https://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.
+
+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 <https://www.attrs.org/en/latest/contributing.html>`_ to get you started!
+
+
+Release Information
+===================
+
+19.2.0 (2019-10-01)
+-------------------
+
+Backward-incompatible Changes
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+- Removed deprecated ``Attribute`` attribute ``convert`` per scheduled removal on 2019/1.
+  This planned deprecation is tracked in issue `#307 <https://github.com/python-attrs/attrs/issues/307>`_.
+  `#504 <https://github.com/python-attrs/attrs/issues/504>`_
+- ``__lt__``, ``__le__``, ``__gt__``, and ``__ge__`` do not consider subclasses comparable anymore.
+
+  This has been deprecated since 18.2.0 and was raising a ``DeprecationWarning`` for over a year.
+  `#570 <https://github.com/python-attrs/attrs/issues/570>`_
+
+
+Deprecations
+^^^^^^^^^^^^
+
+- The ``cmp`` argument to ``attr.s()`` and ``attr.ib()`` is now deprecated.
+
+  Please use ``eq`` to add equality methods (``__eq__`` and ``__ne__``) and ``order`` to add ordering methods (``__lt__``, ``__le__``, ``__gt__``, and ``__ge__``) instead – just like with `dataclasses <https://docs.python.org/3/library/dataclasses.html>`_.
+
+  Both are effectively ``True`` by default but it's enough to set ``eq=False`` to disable both at once.
+  Passing ``eq=False, order=True`` explicitly will raise a ``ValueError`` though.
+
+  Since this is arguably a deeper backward-compatibility break, it will have an extended deprecation period until 2021-06-01.
+  After that day, the ``cmp`` argument will be removed.
+
+  ``attr.Attribute`` also isn't orderable anymore.
+  `#574 <https://github.com/python-attrs/attrs/issues/574>`_
+
+
+Changes
+^^^^^^^
+
+- Updated ``attr.validators.__all__`` to include new validators added in `#425`_.
+  `#517 <https://github.com/python-attrs/attrs/issues/517>`_
+- Slotted classes now use a pure Python mechanism to rewrite the ``__class__`` cell when rebuilding the class, so ``super()`` works even on environments where ``ctypes`` is not installed.
+  `#522 <https://github.com/python-attrs/attrs/issues/522>`_
+- When collecting attributes using ``@attr.s(auto_attribs=True)``, attributes with a default of ``None`` are now deleted too.
+  `#523 <https://github.com/python-attrs/attrs/issues/523>`_,
+  `#556 <https://github.com/python-attrs/attrs/issues/556>`_
+- Fixed ``attr.validators.deep_iterable()`` and ``attr.validators.deep_mapping()`` type stubs.
+  `#533 <https://github.com/python-attrs/attrs/issues/533>`_
+- ``attr.validators.is_callable()`` validator now raises an exception ``attr.exceptions.NotCallableError``, a subclass of ``TypeError``, informing the received value.
+  `#536 <https://github.com/python-attrs/attrs/issues/536>`_
+- ``@attr.s(auto_exc=True)`` now generates classes that are hashable by ID, as the documentation always claimed it would.
+  `#543 <https://github.com/python-attrs/attrs/issues/543>`_,
+  `#563 <https://github.com/python-attrs/attrs/issues/563>`_
+- Added ``attr.validators.matches_re()`` that checks string attributes whether they match a regular expression.
+  `#552 <https://github.com/python-attrs/attrs/issues/552>`_
+- Keyword-only attributes (``kw_only=True``) and attributes that are excluded from the ``attrs``'s ``__init__`` (``init=False``) now can appear before mandatory attributes.
+  `#559 <https://github.com/python-attrs/attrs/issues/559>`_
+- The fake filename for generated methods is now more stable.
+  It won't change when you restart the process.
+  `#560 <https://github.com/python-attrs/attrs/issues/560>`_
+- The value passed to ``@attr.ib(repr=…)`` can now be either a boolean (as before) or a callable.
+  That callable must return a string and is then used for formatting the attribute by the generated ``__repr__()`` method.
+  `#568 <https://github.com/python-attrs/attrs/issues/568>`_
+- Added ``attr.__version_info__`` that can be used to reliably check the version of ``attrs`` and write forward- and backward-compatible code.
+  Please check out the `section on deprecated APIs <http://www.attrs.org/en/stable/api.html#deprecated-apis>`_ on how to use it.
+  `#580 <https://github.com/python-attrs/attrs/issues/580>`_
+
+ .. _`#425`: https://github.com/python-attrs/attrs/issues/425
+
+`Full changelog <https://www.attrs.org/en/stable/changelog.html>`_.
+
+Credits
+=======
+
+``attrs`` is written and maintained by `Hynek Schlawack <https://hynek.me/>`_.
+
+The development is kindly supported by `Variomedia AG <https://www.variomedia.de/>`_.
+
+A full list of contributors can be found in `GitHub's overview <https://github.com/python-attrs/attrs/graphs/contributors>`_.
+
+It’s the spiritual successor of `characteristic <https://characteristic.readthedocs.io/>`_ and aspires to fix some of it clunkiness and unfortunate decisions.
+Both were inspired by Twisted’s `FancyEqMixin <https://twistedmatrix.com/documents/current/api/twisted.python.util.FancyEqMixin.html>`_ but both are implemented using class decorators because `subclassing is bad for you <https://www.youtube.com/watch?v=3MNVP9-hglc>`_, m’kay?
+
+
new file mode 100644
--- /dev/null
+++ b/third_party/python/attrs/attrs-19.2.0.dist-info/RECORD
@@ -0,0 +1,22 @@
+attr/__init__.py,sha256=nRvEecOWLaJsMraOK89f4hMYThTUZHWj1B0jl249M-0,1344
+attr/__init__.pyi,sha256=5AVtEEzK-g3HO1SUll44hTL8LFoM8TYD7Gn9vEMFGzk,8252
+attr/_compat.py,sha256=-pJtdtqgCg0K6rH_BWf3wKuTum58GD-WWPclQQ2SUaU,7326
+attr/_config.py,sha256=_KvW0mQdH2PYjHc0YfIUaV_o2pVfM7ziMEYTxwmEhOA,514
+attr/_funcs.py,sha256=unAJfNGSTOzxyFzkj7Rs3O1bfsQodmXyir9uZKen-vY,9696
+attr/_make.py,sha256=4pdTus8d4OkitzlwytTPP7TNLZK6pVIoKg6KdAZMwYQ,70804
+attr/_version.py,sha256=azMi1lNelb3cJvvYUMXsXVbUANkRzbD5IEiaXVpeVr4,2162
+attr/_version.pyi,sha256=x_M3L3WuB7r_ULXAWjx959udKQ4HLB8l-hsc1FDGNvk,209
+attr/converters.py,sha256=5QJRYSXE8G7PW0289y_SPwvvZIcw-nJIuBlfYVdB4BQ,2141
+attr/converters.pyi,sha256=wAhCoOT1MFV8t323rpD87O7bxQ8CYLTPiBQd-29BieI,351
+attr/exceptions.py,sha256=hbhOa3b4W8_mRrbj3FsMTR4Bt5xzbJs5xaFTWn8s6h4,1635
+attr/exceptions.pyi,sha256=4zuaJyl2axxWbqnZgxo_2oTpPNbyowEw3A4hqV5PmAc,458
+attr/filters.py,sha256=weDxwATsa69T_0bPVjiM1fGsciAMQmwhY5G8Jm5BxuI,1098
+attr/filters.pyi,sha256=xDpmKQlFdssgxGa5tsl1ADh_3zwAwAT4vUhd8h-8-Tk,214
+attr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+attr/validators.py,sha256=8AsxgdDgh3sGPseiUIMPGcTr6PvaDYfH3AK46tsvs8U,11460
+attr/validators.pyi,sha256=vZgsJqUwrJevh4v_Hd7_RSXqDrBctE6-3AEZ7uYKodo,1868
+attrs-19.2.0.dist-info/LICENSE,sha256=v2WaKLSSQGAvVrvfSQy-LsUJsVuY-Z17GaUsdA4yeGM,1082
+attrs-19.2.0.dist-info/METADATA,sha256=qPqvhqvovqyvpsQebMPTXsOi8pv2xuzUDkUzAxz-wvM,12750
+attrs-19.2.0.dist-info/WHEEL,sha256=8zNYZbwQSXoB9IfXOjPfeNwvAsALAjffgk27FqvCWbo,110
+attrs-19.2.0.dist-info/top_level.txt,sha256=tlRYMddkRlKPqJ96wP2_j9uEsmcNHgD2SbuWd4CzGVU,5
+attrs-19.2.0.dist-info/RECORD,,
new file mode 100644
--- /dev/null
+++ b/third_party/python/attrs/attrs-19.2.0.dist-info/WHEEL
@@ -0,0 +1,6 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.33.6)
+Root-Is-Purelib: true
+Tag: py2-none-any
+Tag: py3-none-any
+
rename from third_party/python/attrs/attrs-19.1.0.dist-info/top_level.txt
rename to third_party/python/attrs/attrs-19.2.0.dist-info/top_level.txt
--- a/third_party/python/requirements.in
+++ b/third_party/python/requirements.in
@@ -1,10 +1,10 @@
 appdirs==1.4.4
-attrs==19.1.0
+attrs==19.2.0
 blessings==1.7
 cbor2==4.0.1
 # Though we don't depend on colorama directly, we need to explicitly
 # define it here because it's needed by other dependencies on
 # Windows systems.
 colorama==0.4.4
 compare-locales==8.2.1
 cookies==2.2.1
--- a/third_party/python/requirements.txt
+++ b/third_party/python/requirements.txt
@@ -45,19 +45,19 @@ appdirs==1.4.4 \
     #   glean-parser
     #   taskcluster-taskgraph
 async-timeout==3.0.1 \
     --hash=sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f \
     --hash=sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3
     # via
     #   aiohttp
     #   taskcluster
-attrs==19.1.0 \
-    --hash=sha256:69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79 \
-    --hash=sha256:f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399
+attrs==19.2.0 \
+    --hash=sha256:ec20e7a4825331c1b5ebf261d111e16fa9612c1f7a5e1f884f12bd53a664dfd2 \
+    --hash=sha256:f913492e1663d3c36f502e5e9ba6cd13cf19d7fab50aa13239e420fef95e1396
     # via
     #   -r requirements-mach-vendor-python.in
     #   aiohttp
     #   jsonschema
     #   mozilla-version
     #   taskcluster-taskgraph
 blessings==1.7 \
     --hash=sha256:98e5854d805f50a5b58ac2333411b0482516a8210f23f43308baeb58d77c157d \
--- a/tools/lint/tox/tox_requirements.txt
+++ b/tools/lint/tox/tox_requirements.txt
@@ -1,4 +1,7 @@
-pluggy==0.6.0 --hash=sha256:e160a7fcf25762bb60efc7e171d4497ff1d8d2d75a3d0df7a21b76821ecbf5c5
+pluggy==0.13.1 --hash=sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d
+importlib-metadata==0.23 --hash=sha256:d5f18a79777f3aa179c145737780282e27b508fc8fd688cb17c7a813e8bd39af
+more-itertools==7.2.0 --hash=sha256:92b8c4b06dac4f0611c0729b2f2ede52b2e1bac1ab48f089c7ddc12e26bb60c4
+zipp==0.6.0 --hash=sha256:f06903e9f1f43b12d371004b4ac7b06ab39a44adc747266928ae6debfa7b3335
 py==1.5.4 --hash=sha256:e31fb2767eb657cbde86c454f02e99cb846d3cd9d61b318525140214fdc0e98e
 tox==2.7.0 --hash=sha256:0f37ea637ead4a5bbae91531b0bf8fd327c7152e20255e5960ee180598228d21
 virtualenv==15.1.0 --hash=sha256:39d88b533b422825d644087a21e78c45cf5af0ef7a99a1fc9fbb7b481e5c85b0