Added support for keyword-only arguments on Python 3+ [rebase] (#411)

* Added support for keyword-only attributes. Closes #106, and closes #38

(Rebases #281)

Co-authored-by: Alex Ford <fordas@uw.edu>

* Add `attr.s`-level `kw_only` flag.

Add `kw_only` flag to `attr.s` decorator, indicating that all class
attributes should be keyword-only in __init__.

Minor updates to internal interface of `Attribute` to support
evolution of attributes to `kw_only` in class factory.

Expand examples with `attr.s` level kw_only.

* Add `kw_only` to type stubs.

* Update changelog for rebased PR.

Hear ye, hear ye. A duplicate PR is born.

* Tidy docs from review.

* Tidy code from review.

* Add explicit tests of PY2 kw_only SyntaxError behavior.

* Add `PythonToOldError`, raise for kw_only on PY2.

* `Attribute._evolve` to `Attribute._assoc`.
diff --git a/changelog.d/281.change.rst b/changelog.d/281.change.rst
new file mode 100644
index 0000000..e942d18
--- /dev/null
+++ b/changelog.d/281.change.rst
@@ -0,0 +1,2 @@
+Added ``kw_only`` arguments to ``attr.ib`` and ``attr.s```, and a corresponding ``kw_only`` attribute to ``attr.Attribute``.
+This change makes it possible to have a generated ``__init__`` with keyword-only arguments on Python 3, relaxing the required ordering of default and non-default valued attributes.
diff --git a/changelog.d/411.change.rst b/changelog.d/411.change.rst
new file mode 100644
index 0000000..e942d18
--- /dev/null
+++ b/changelog.d/411.change.rst
@@ -0,0 +1,2 @@
+Added ``kw_only`` arguments to ``attr.ib`` and ``attr.s```, and a corresponding ``kw_only`` attribute to ``attr.Attribute``.
+This change makes it possible to have a generated ``__init__`` with keyword-only arguments on Python 3, relaxing the required ordering of default and non-default valued attributes.
diff --git a/docs/api.rst b/docs/api.rst
index 665fe9f..ba3bc18 100644
--- a/docs/api.rst
+++ b/docs/api.rst
@@ -90,7 +90,7 @@
       ... class C(object):
       ...     x = attr.ib()
       >>> attr.fields(C).x
-      Attribute(name='x', default=NOTHING, validator=None, repr=True, cmp=True, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None)
+      Attribute(name='x', default=NOTHING, validator=None, repr=True, cmp=True, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None, kw_only=False)
 
 
 .. autofunction:: attr.make_class
@@ -161,9 +161,9 @@
       ...     x = attr.ib()
       ...     y = attr.ib()
       >>> attr.fields(C)
-      (Attribute(name='x', default=NOTHING, validator=None, repr=True, cmp=True, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None), Attribute(name='y', default=NOTHING, validator=None, repr=True, cmp=True, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None))
+      (Attribute(name='x', default=NOTHING, validator=None, repr=True, cmp=True, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None, kw_only=False), Attribute(name='y', default=NOTHING, validator=None, repr=True, cmp=True, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None, kw_only=False))
       >>> attr.fields(C)[1]
-      Attribute(name='y', default=NOTHING, validator=None, repr=True, cmp=True, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None)
+      Attribute(name='y', default=NOTHING, validator=None, repr=True, cmp=True, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None, kw_only=False)
       >>> attr.fields(C).y is attr.fields(C)[1]
       True
 
@@ -178,9 +178,9 @@
       ...     x = attr.ib()
       ...     y = attr.ib()
       >>> attr.fields_dict(C)
-      {'x': Attribute(name='x', default=NOTHING, validator=None, repr=True, cmp=True, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None), 'y': Attribute(name='y', default=NOTHING, validator=None, repr=True, cmp=True, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None)}
+      {'x': Attribute(name='x', default=NOTHING, validator=None, repr=True, cmp=True, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None, kw_only=False), 'y': Attribute(name='y', default=NOTHING, validator=None, repr=True, cmp=True, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None, kw_only=False)}
       >>> attr.fields_dict(C)['y']
-      Attribute(name='y', default=NOTHING, validator=None, repr=True, cmp=True, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None)
+      Attribute(name='y', default=NOTHING, validator=None, repr=True, cmp=True, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None, kw_only=False)
       >>> attr.fields_dict(C)['y'] is attr.fields(C).y
       True
 
@@ -275,7 +275,7 @@
       >>> attr.validate(i)
       Traceback (most recent call last):
          ...
-      TypeError: ("'x' must be <type 'int'> (got '1' that is a <type 'str'>).", Attribute(name='x', default=NOTHING, validator=<instance_of validator for type <type 'int'>>, repr=True, cmp=True, hash=None, init=True, type=None), <type 'int'>, '1')
+      TypeError: ("'x' must be <type 'int'> (got '1' that is a <type 'str'>).", Attribute(name='x', default=NOTHING, validator=<instance_of validator for type <type 'int'>>, repr=True, cmp=True, hash=None, init=True, type=None, kw_only=False), <type 'int'>, '1')
 
 
 Validators can be globally disabled if you want to run them only in development and tests but not in production because you fear their performance impact:
@@ -308,11 +308,11 @@
       >>> C("42")
       Traceback (most recent call last):
          ...
-      TypeError: ("'x' must be <type 'int'> (got '42' that is a <type 'str'>).", Attribute(name='x', default=NOTHING, validator=<instance_of validator for type <type 'int'>>, type=None), <type 'int'>, '42')
+      TypeError: ("'x' must be <type 'int'> (got '42' that is a <type 'str'>).", Attribute(name='x', default=NOTHING, validator=<instance_of validator for type <type 'int'>>, type=None, kw_only=False), <type 'int'>, '42')
       >>> C(None)
       Traceback (most recent call last):
          ...
-      TypeError: ("'x' must be <type 'int'> (got None that is a <type 'NoneType'>).", Attribute(name='x', default=NOTHING, validator=<instance_of validator for type <type 'int'>>, repr=True, cmp=True, hash=None, init=True, type=None), <type 'int'>, None)
+      TypeError: ("'x' must be <type 'int'> (got None that is a <type 'NoneType'>).", Attribute(name='x', default=NOTHING, validator=<instance_of validator for type <type 'int'>>, repr=True, cmp=True, hash=None, init=True, type=None, kw_only=False), <type 'int'>, None)
 
 .. autofunction:: attr.validators.in_
 
@@ -364,7 +364,7 @@
       >>> C("42")
       Traceback (most recent call last):
          ...
-      TypeError: ("'x' must be <type 'int'> (got '42' that is a <type 'str'>).", Attribute(name='x', default=NOTHING, validator=<instance_of validator for type <type 'int'>>, type=None), <type 'int'>, '42')
+      TypeError: ("'x' must be <type 'int'> (got '42' that is a <type 'str'>).", Attribute(name='x', default=NOTHING, validator=<instance_of validator for type <type 'int'>>, type=None, kw_only=False), <type 'int'>, '42')
       >>> C(None)
       C(x=None)
 
diff --git a/docs/examples.rst b/docs/examples.rst
index dfd6bf9..656d969 100644
--- a/docs/examples.rst
+++ b/docs/examples.rst
@@ -145,6 +145,73 @@
 On Python 3 it overrides the implicit detection.
 
 
+Keyword-only Attributes
+~~~~~~~~~~~~~~~~~~~~~~~
+
+When using ``attrs`` on Python 3, you can also add `keyword-only <https://docs.python.org/3/glossary.html#keyword-only-parameter>`_ attributes:
+
+.. doctest::
+
+    >>> @attr.s
+    ... class A:
+    ...     a = attr.ib(kw_only=True)
+    >>> A()
+    Traceback (most recent call last):
+      ...
+    TypeError: A() missing 1 required keyword-only argument: 'a'
+    >>> A(a=1)
+    A(a=1)
+
+``kw_only`` may also be specified at via ``attr.s``, and will apply to all attributes:
+
+.. doctest::
+
+    >>> @attr.s(kw_only=True)
+    ... class A:
+    ...     a = attr.ib()
+    ...     b = attr.ib()
+    >>> A(1, 2)
+    Traceback (most recent call last):
+      ...
+    TypeError: __init__() takes 1 positional argument but 3 were given
+    >>> A(a=1, b=2)
+    A(a=1, b=2)
+
+
+
+If you create an attribute with ``init=False``, the ``kw_only`` argument is ignored.
+
+Keyword-only attributes allow subclasses to add attributes without default values, even if the base class defines attributes with default values:
+
+.. doctest::
+
+    >>> @attr.s
+    ... class A:
+    ...     a = attr.ib(default=0)
+    >>> @attr.s
+    ... class B(A):
+    ...     b = attr.ib(kw_only=True)
+    >>> B(b=1)
+    B(a=0, b=1)
+    >>> B()
+    Traceback (most recent call last):
+      ...
+    TypeError: B() missing 1 required keyword-only argument: 'b'
+
+If you don't set ``kw_only=True``, then there's is no valid attribute ordering and you'll get an error:
+
+.. doctest::
+
+    >>> @attr.s
+    ... class A:
+    ...     a = attr.ib(default=0)
+    >>> @attr.s
+    ... class B(A):
+    ...     b = attr.ib()
+    Traceback (most recent call last):
+      ...
+    ValueError: No mandatory attributes allowed after an attribute with a default value or factory.  Attribute in question: Attribute(name='b', default=NOTHING, validator=None, repr=True, cmp=True, hash=None, init=True, convert=None, metadata=mappingproxy({}), type=None, kw_only=False)
+
 .. _asdict:
 
 Converting to Collections Types
@@ -352,7 +419,7 @@
    >>> C("128")
    Traceback (most recent call last):
       ...
-   TypeError: ("'x' must be <class 'int'> (got '128' that is a <class 'str'>).", Attribute(name='x', default=NOTHING, validator=[<instance_of validator for type <class 'int'>>, <function fits_byte at 0x10fd7a0d0>], repr=True, cmp=True, hash=True, init=True, metadata=mappingproxy({}), type=None, converter=one), <class 'int'>, '128')
+   TypeError: ("'x' must be <class 'int'> (got '128' that is a <class 'str'>).", Attribute(name='x', default=NOTHING, validator=[<instance_of validator for type <class 'int'>>, <function fits_byte at 0x10fd7a0d0>], repr=True, cmp=True, hash=True, init=True, metadata=mappingproxy({}), type=None, converter=one, kw_only=False), <class 'int'>, '128')
    >>> C(256)
    Traceback (most recent call last):
       ...
@@ -371,7 +438,7 @@
    >>> C("42")
    Traceback (most recent call last):
       ...
-   TypeError: ("'x' must be <type 'int'> (got '42' that is a <type 'str'>).", Attribute(name='x', default=NOTHING, factory=NOTHING, validator=<instance_of validator for type <type 'int'>>, type=None), <type 'int'>, '42')
+   TypeError: ("'x' must be <type 'int'> (got '42' that is a <type 'str'>).", Attribute(name='x', default=NOTHING, factory=NOTHING, validator=<instance_of validator for type <type 'int'>>, type=None, kw_only=False), <type 'int'>, '42')
 
 Check out :ref:`validators` for more details.
 
diff --git a/docs/extending.rst b/docs/extending.rst
index 77f3f64..11f2a74 100644
--- a/docs/extending.rst
+++ b/docs/extending.rst
@@ -17,7 +17,7 @@
    ... @attr.s
    ... class C(object):
    ...     a = attr.ib()
-   (Attribute(name='a', default=NOTHING, validator=None, repr=True, cmp=True, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None),)
+   (Attribute(name='a', default=NOTHING, validator=None, repr=True, cmp=True, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None, kw_only=False),)
 
 
 .. warning::
diff --git a/src/attr/__init__.pyi b/src/attr/__init__.pyi
index c8b4c65..34376a2 100644
--- a/src/attr/__init__.pyi
+++ b/src/attr/__init__.pyi
@@ -56,6 +56,7 @@
     converter: Optional[_ConverterType[_T]]
     metadata: Dict[Any, Any]
     type: Optional[Type[_T]]
+    kw_only: bool
     def __lt__(self, x: Attribute) -> bool: ...
     def __le__(self, x: Attribute) -> bool: ...
     def __gt__(self, x: Attribute) -> bool: ...
@@ -99,6 +100,7 @@
     type: None = ...,
     converter: None = ...,
     factory: None = ...,
+    kw_only: bool = ...,
 ) -> Any: ...
 
 # This form catches an explicit None or no default and infers the type from the other arguments.
@@ -115,6 +117,7 @@
     type: Optional[Type[_T]] = ...,
     converter: Optional[_ConverterType[_T]] = ...,
     factory: Optional[Callable[[], _T]] = ...,
+    kw_only: bool = ...,
 ) -> _T: ...
 
 # This form catches an explicit default argument.
@@ -131,6 +134,7 @@
     type: Optional[Type[_T]] = ...,
     converter: Optional[_ConverterType[_T]] = ...,
     factory: Optional[Callable[[], _T]] = ...,
+    kw_only: bool = ...,
 ) -> _T: ...
 
 # This form covers type=non-Type: e.g. forward references (str), Any
@@ -147,6 +151,7 @@
     type: object = ...,
     converter: Optional[_ConverterType[_T]] = ...,
     factory: Optional[Callable[[], _T]] = ...,
+    kw_only: bool = ...,
 ) -> Any: ...
 @overload
 def attrs(
@@ -161,6 +166,7 @@
     frozen: bool = ...,
     str: bool = ...,
     auto_attribs: bool = ...,
+    kw_only: bool = ...,
 ) -> _C: ...
 @overload
 def attrs(
@@ -175,6 +181,7 @@
     frozen: bool = ...,
     str: bool = ...,
     auto_attribs: bool = ...,
+    kw_only: bool = ...,
 ) -> Callable[[_C], _C]: ...
 
 # TODO: add support for returning NamedTuple from the mypy plugin
@@ -200,6 +207,7 @@
     frozen: bool = ...,
     str: bool = ...,
     auto_attribs: bool = ...,
+    kw_only: bool = ...,
 ) -> type: ...
 
 # _funcs --
diff --git a/src/attr/_make.py b/src/attr/_make.py
index a8d9c70..71c0f23 100644
--- a/src/attr/_make.py
+++ b/src/attr/_make.py
@@ -1,5 +1,6 @@
 from __future__ import absolute_import, division, print_function
 
+import copy
 import hashlib
 import linecache
 import sys
@@ -21,6 +22,7 @@
     DefaultAlreadySetError,
     FrozenInstanceError,
     NotAnAttrsClassError,
+    PythonTooOldError,
     UnannotatedAttributeError,
 )
 
@@ -79,6 +81,7 @@
     type=None,
     converter=None,
     factory=None,
+    kw_only=False,
 ):
     """
     Create a new attribute on a class.
@@ -151,6 +154,9 @@
         This argument is provided for backward compatibility.
         Regardless of the approach used, the type will be stored on
         ``Attribute.type``.
+    :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*
@@ -163,6 +169,7 @@
        *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*
     """
     if hash is not None and hash is not True and hash is not False:
         raise TypeError(
@@ -206,6 +213,7 @@
         converter=converter,
         metadata=metadata,
         type=type,
+        kw_only=kw_only,
     )
 
 
@@ -285,7 +293,7 @@
     return e[1].counter
 
 
-def _transform_attrs(cls, these, auto_attribs):
+def _transform_attrs(cls, these, auto_attribs, kw_only):
     """
     Transform all `_CountingAttr`s on a class into `Attribute`s.
 
@@ -368,19 +376,22 @@
 
     AttrsClass = _make_attr_tuple_class(cls.__name__, attr_names)
 
-    attrs = AttrsClass(
-        super_attrs
-        + [
-            Attribute.from_counting_attr(
-                name=attr_name, ca=ca, type=anns.get(attr_name)
-            )
-            for attr_name, ca in ca_list
-        ]
-    )
+    if kw_only:
+        own_attrs = [a._assoc(kw_only=True) for a in own_attrs]
+        super_attrs = [a._assoc(kw_only=True) for a in super_attrs]
+
+    attrs = AttrsClass(super_attrs + own_attrs)
 
     had_default = False
+    was_kw_only = False
     for a in attrs:
-        if had_default is True and a.default is NOTHING and a.init is True:
+        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
+        ):
             raise ValueError(
                 "No mandatory attributes allowed after an attribute with a "
                 "default value or factory.  Attribute in question: %r" % (a,)
@@ -389,8 +400,21 @@
             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
         ):
             had_default = True
+        if was_kw_only is True and a.kw_only is False:
+            raise ValueError(
+                "Non keyword-only attributes are not allowed after a "
+                "keyword-only attribute.  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, super_attrs, super_attr_map))
 
@@ -427,9 +451,9 @@
         "_super_attr_map",
     )
 
-    def __init__(self, cls, these, slots, frozen, auto_attribs):
+    def __init__(self, cls, these, slots, frozen, auto_attribs, kw_only):
         attrs, super_attrs, super_map = _transform_attrs(
-            cls, these, auto_attribs
+            cls, these, auto_attribs, kw_only
         )
 
         self._cls = cls
@@ -639,6 +663,7 @@
     frozen=False,
     str=False,
     auto_attribs=False,
+    kw_only=False,
 ):
     r"""
     A class decorator that adds `dunder
@@ -736,6 +761,10 @@
         Attributes annotated as :data:`typing.ClassVar` 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).
+
 
     .. versionadded:: 16.0.0 *slots*
     .. versionadded:: 16.1.0 *frozen*
@@ -752,13 +781,16 @@
        :class:`DeprecationWarning` if the classes compared are subclasses of
        each other. ``__eq`` and ``__ne__`` never tried to compared subclasses
        to each other.
+    .. versionadded:: 18.2.0 *kw_only*
     """
 
     def wrap(cls):
         if getattr(cls, "__class__", None) is None:
             raise TypeError("attrs only works with new-style classes.")
 
-        builder = _ClassBuilder(cls, these, slots, frozen, auto_attribs)
+        builder = _ClassBuilder(
+            cls, these, slots, frozen, auto_attribs, kw_only
+        )
 
         if repr is True:
             builder.add_repr(repr_ns)
@@ -1298,6 +1330,7 @@
             }
 
     args = []
+    kw_only_args = []
     attrs_to_validate = []
 
     # This is a dictionary of names to validator and converter callables.
@@ -1357,11 +1390,13 @@
                         )
                     )
         elif a.default is not NOTHING and not has_factory:
-            args.append(
-                "{arg_name}=attr_dict['{attr_name}'].default".format(
-                    arg_name=arg_name, attr_name=attr_name
-                )
+            arg = "{arg_name}=attr_dict['{attr_name}'].default".format(
+                arg_name=arg_name, attr_name=attr_name
             )
+            if a.kw_only:
+                kw_only_args.append(arg)
+            else:
+                args.append(arg)
             if a.converter is not None:
                 lines.append(fmt_setter_with_converter(attr_name, arg_name))
                 names_for_globals[
@@ -1370,7 +1405,11 @@
             else:
                 lines.append(fmt_setter(attr_name, arg_name))
         elif has_factory:
-            args.append("{arg_name}=NOTHING".format(arg_name=arg_name))
+            arg = "{arg_name}=NOTHING".format(arg_name=arg_name)
+            if a.kw_only:
+                kw_only_args.append(arg)
+            else:
+                args.append(arg)
             lines.append(
                 "if {arg_name} is not NOTHING:".format(arg_name=arg_name)
             )
@@ -1402,7 +1441,10 @@
                 )
             names_for_globals[init_factory_name] = a.default.factory
         else:
-            args.append(arg_name)
+            if a.kw_only:
+                kw_only_args.append(arg_name)
+            else:
+                args.append(arg_name)
             if a.converter is not None:
                 lines.append(fmt_setter_with_converter(attr_name, arg_name))
                 names_for_globals[
@@ -1428,13 +1470,23 @@
     if post_init:
         lines.append("self.__attrs_post_init__()")
 
+    args = ", ".join(args)
+    if kw_only_args:
+        if PY2:
+            raise PythonTooOldError(
+                "Keyword-only arguments only work on Python 3 and later."
+            )
+
+        args += "{leading_comma}*, {kw_only_args}".format(
+            leading_comma=", " if args else "",
+            kw_only_args=", ".join(kw_only_args),
+        )
     return (
         """\
 def __init__(self, {args}):
     {lines}
 """.format(
-            args=", ".join(args),
-            lines="\n    ".join(lines) if lines else "pass",
+            args=args, lines="\n    ".join(lines) if lines else "pass"
         ),
         names_for_globals,
         annotations,
@@ -1463,6 +1515,7 @@
         "metadata",
         "type",
         "converter",
+        "kw_only",
     )
 
     def __init__(
@@ -1478,6 +1531,7 @@
         metadata=None,
         type=None,
         converter=None,
+        kw_only=False,
     ):
         # Cache this descriptor here to speed things up later.
         bound_setattr = _obj_setattr.__get__(self, Attribute)
@@ -1515,6 +1569,7 @@
             ),
         )
         bound_setattr("type", type)
+        bound_setattr("kw_only", kw_only)
 
     def __setattr__(self, name, value):
         raise FrozenInstanceError()
@@ -1558,6 +1613,17 @@
             **inst_dict
         )
 
+    # 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())
+
+        return new
+
     # Don't use _add_pickle since fields(Attribute) doesn't work
     def __getstate__(self):
         """
@@ -1572,8 +1638,11 @@
         """
         Play nice with pickle.
         """
+        self._setattrs(zip(self.__slots__, state))
+
+    def _setattrs(self, name_values_pairs):
         bound_setattr = _obj_setattr.__get__(self, Attribute)
-        for name, value in zip(self.__slots__, state):
+        for name, value in name_values_pairs:
             if name != "metadata":
                 bound_setattr(name, value)
             else:
@@ -1625,6 +1694,7 @@
         "_validator",
         "converter",
         "type",
+        "kw_only",
     )
     __attrs_attrs__ = tuple(
         Attribute(
@@ -1635,6 +1705,7 @@
             cmp=True,
             hash=True,
             init=True,
+            kw_only=False,
         )
         for name in ("counter", "_default", "repr", "cmp", "hash", "init")
     ) + (
@@ -1646,6 +1717,7 @@
             cmp=True,
             hash=False,
             init=True,
+            kw_only=False,
         ),
     )
     cls_counter = 0
@@ -1661,6 +1733,7 @@
         converter,
         metadata,
         type,
+        kw_only,
     ):
         _CountingAttr.cls_counter += 1
         self.counter = _CountingAttr.cls_counter
@@ -1677,6 +1750,7 @@
         self.converter = converter
         self.metadata = metadata
         self.type = type
+        self.kw_only = kw_only
 
     def validator(self, meth):
         """
diff --git a/src/attr/exceptions.py b/src/attr/exceptions.py
index 1a3229f..b12e41e 100644
--- a/src/attr/exceptions.py
+++ b/src/attr/exceptions.py
@@ -47,3 +47,11 @@
 
     .. versionadded:: 17.3.0
     """
+
+
+class PythonTooOldError(RuntimeError):
+    """
+    An ``attrs`` feature requiring a more recent python version has been used.
+
+    .. versionadded:: 18.2.0
+    """
diff --git a/tests/test_annotations.py b/tests/test_annotations.py
index 34807a4..fee45d1 100644
--- a/tests/test_annotations.py
+++ b/tests/test_annotations.py
@@ -229,3 +229,24 @@
             "foo": "typing.Any",
             "return": None,
         }
+
+    def test_keyword_only_auto_attribs(self):
+        """
+        `kw_only` propagates to attributes defined via `auto_attribs`.
+        """
+
+        @attr.s(auto_attribs=True, kw_only=True)
+        class C:
+            x: int
+            y: int
+
+        with pytest.raises(TypeError):
+            C(0, 1)
+
+        with pytest.raises(TypeError):
+            C(x=0)
+
+        c = C(x=0, y=1)
+
+        assert c.x == 0
+        assert c.y == 1
diff --git a/tests/test_make.py b/tests/test_make.py
index 20d13b6..0302cb5 100644
--- a/tests/test_make.py
+++ b/tests/test_make.py
@@ -35,7 +35,11 @@
     make_class,
     validate,
 )
-from attr.exceptions import DefaultAlreadySetError, NotAnAttrsClassError
+from attr.exceptions import (
+    DefaultAlreadySetError,
+    NotAnAttrsClassError,
+    PythonTooOldError,
+)
 
 from .strategies import (
     gen_attr_names,
@@ -229,7 +233,7 @@
         Doesn't attach __attrs_attrs__ to the class anymore.
         """
         C = make_tc()
-        _transform_attrs(C, None, False)
+        _transform_attrs(C, None, False, False)
 
         assert None is getattr(C, "__attrs_attrs__", None)
 
@@ -238,7 +242,7 @@
         Transforms every `_CountingAttr` and leaves others (a) be.
         """
         C = make_tc()
-        attrs, _, _ = _transform_attrs(C, None, False)
+        attrs, _, _ = _transform_attrs(C, None, False, False)
 
         assert ["z", "y", "x"] == [a.name for a in attrs]
 
@@ -251,14 +255,16 @@
         class C(object):
             pass
 
-        assert _Attributes(((), [], {})) == _transform_attrs(C, None, False)
+        assert _Attributes(((), [], {})) == _transform_attrs(
+            C, None, False, False
+        )
 
     def test_transforms_to_attribute(self):
         """
         All `_CountingAttr`s are transformed into `Attribute`s.
         """
         C = make_tc()
-        attrs, super_attrs, _ = _transform_attrs(C, None, False)
+        attrs, super_attrs, _ = _transform_attrs(C, None, False, False)
 
         assert [] == super_attrs
         assert 3 == len(attrs)
@@ -275,15 +281,46 @@
             y = attr.ib()
 
         with pytest.raises(ValueError) as e:
-            _transform_attrs(C, None, False)
+            _transform_attrs(C, None, False, False)
         assert (
             "No mandatory attributes allowed after an attribute with a "
             "default value or factory.  Attribute in question: Attribute"
             "(name='y', default=NOTHING, validator=None, repr=True, "
             "cmp=True, hash=None, init=True, metadata=mappingproxy({}), "
-            "type=None, converter=None)",
+            "type=None, converter=None, kw_only=False)",
         ) == e.value.args
 
+    def test_kw_only(self):
+        """
+        Converts all attributes, including superclass attributes, if `kw_only`
+        is provided. Therefore, `kw_only` allows attributes with defaults to
+        preceed mandatory attributes.
+
+        Updates in the subclass *don't* affect the superclass attributes.
+        """
+
+        @attr.s
+        class B(object):
+            b = attr.ib()
+
+        for b_a in B.__attrs_attrs__:
+            assert b_a.kw_only is False
+
+        class C(B):
+            x = attr.ib(default=None)
+            y = attr.ib()
+
+        attrs, super_attrs, _ = _transform_attrs(C, None, False, True)
+
+        assert len(attrs) == 3
+        assert len(super_attrs) == 1
+
+        for a in attrs:
+            assert a.kw_only is True
+
+        for b_a in B.__attrs_attrs__:
+            assert b_a.kw_only is False
+
     def test_these(self):
         """
         If these is passed, use it and ignore body and super classes.
@@ -295,7 +332,9 @@
         class C(Base):
             y = attr.ib()
 
-        attrs, super_attrs, _ = _transform_attrs(C, {"x": attr.ib()}, False)
+        attrs, super_attrs, _ = _transform_attrs(
+            C, {"x": attr.ib()}, False, False
+        )
 
         assert [] == super_attrs
         assert (simple_attr("x"),) == attrs
@@ -594,6 +633,182 @@
                 x = attr.ib(factory=Factory(list))
 
 
+@pytest.mark.skipif(PY2, reason="keyword-only arguments are PY3-only.")
+class TestKeywordOnlyAttributes(object):
+    """
+    Tests for keyword-only attributes.
+    """
+
+    def test_adds_keyword_only_arguments(self):
+        """
+        Attributes can be added as keyword-only.
+        """
+
+        @attr.s
+        class C(object):
+            a = attr.ib()
+            b = attr.ib(default=2, kw_only=True)
+            c = attr.ib(kw_only=True)
+            d = attr.ib(default=attr.Factory(lambda: 4), kw_only=True)
+
+        c = C(1, c=3)
+
+        assert c.a == 1
+        assert c.b == 2
+        assert c.c == 3
+        assert c.d == 4
+
+    def test_ignores_kw_only_when_init_is_false(self):
+        """
+        Specifying ``kw_only=True`` when ``init=False`` is essentially a no-op.
+        """
+
+        @attr.s
+        class C(object):
+            x = attr.ib(init=False, default=0, kw_only=True)
+            y = attr.ib()
+
+        c = C(1)
+
+        assert c.x == 0
+        assert c.y == 1
+
+    def test_keyword_only_attributes_presence(self):
+        """
+        Raises `TypeError` when keyword-only arguments are
+        not specified.
+        """
+
+        @attr.s
+        class C(object):
+            x = attr.ib(kw_only=True)
+
+        with pytest.raises(TypeError) as e:
+            C()
+
+        assert (
+            "missing 1 required keyword-only argument: 'x'"
+        ) in e.value.args[0]
+
+    def test_conflicting_keyword_only_attributes(self):
+        """
+        Raises `ValueError` if keyword-only attributes are followed by
+        regular (non keyword-only) attributes.
+        """
+
+        class C(object):
+            x = attr.ib(kw_only=True)
+            y = attr.ib()
+
+        with pytest.raises(ValueError) as e:
+            _transform_attrs(C, None, False, False)
+
+        assert (
+            "Non keyword-only attributes are not allowed after a "
+            "keyword-only attribute.  Attribute in question: Attribute"
+            "(name='y', default=NOTHING, validator=None, repr=True, "
+            "cmp=True, hash=None, init=True, metadata=mappingproxy({}), "
+            "type=None, converter=None, kw_only=False)",
+        ) == e.value.args
+
+    def test_keyword_only_attributes_allow_subclassing(self):
+        """
+        Subclass can define keyword-only attributed without defaults,
+        when the base class has attributes with defaults.
+        """
+
+        @attr.s
+        class Base(object):
+            x = attr.ib(default=0)
+
+        @attr.s
+        class C(Base):
+            y = attr.ib(kw_only=True)
+
+        c = C(y=1)
+
+        assert c.x == 0
+        assert c.y == 1
+
+    def test_keyword_only_class_level(self):
+        """
+        `kw_only` can be provided at the attr.s level, converting all
+        attributes to `kw_only.`
+        """
+
+        @attr.s(kw_only=True)
+        class C:
+            x = attr.ib()
+            y = attr.ib(kw_only=True)
+
+        with pytest.raises(TypeError):
+            C(0, y=1)
+
+        c = C(x=0, y=1)
+
+        assert c.x == 0
+        assert c.y == 1
+
+    def test_keyword_only_class_level_subclassing(self):
+        """
+        Subclass `kw_only` propagates to attrs inherited from the base,
+        allowing non-default following default.
+        """
+
+        @attr.s
+        class Base(object):
+            x = attr.ib(default=0)
+
+        @attr.s(kw_only=True)
+        class C(Base):
+            y = attr.ib()
+
+        with pytest.raises(TypeError):
+            C(1)
+
+        c = C(x=0, y=1)
+
+        assert c.x == 0
+        assert c.y == 1
+
+
+@pytest.mark.skipif(not PY2, reason="PY2-specific keyword-only error behavior")
+class TestKeywordOnlyAttributesOnPy2(object):
+    """
+    Tests for keyword-only attribute behavior on py2.
+    """
+
+    def test_syntax_error(self):
+        """
+        Keyword-only attributes raise Syntax error on ``__init__`` generation.
+        """
+
+        with pytest.raises(PythonTooOldError):
+
+            @attr.s(kw_only=True)
+            class ClassLevel(object):
+                a = attr.ib()
+
+        with pytest.raises(PythonTooOldError):
+
+            @attr.s()
+            class AttrLevel(object):
+                a = attr.ib(kw_only=True)
+
+    def test_no_init(self):
+        """
+        Keyworld-only is a no-op, not any error, if ``init=false``.
+        """
+
+        @attr.s(kw_only=True, init=False)
+        class ClassLevel(object):
+            a = attr.ib()
+
+        @attr.s(init=False)
+        class AttrLevel(object):
+            a = attr.ib(kw_only=True)
+
+
 @attr.s
 class GC(object):
     @attr.s
@@ -1153,7 +1368,7 @@
         class C(object):
             pass
 
-        b = _ClassBuilder(C, None, True, True, False)
+        b = _ClassBuilder(C, None, True, True, False, False)
 
         assert "<_ClassBuilder(cls=C)>" == repr(b)
 
@@ -1165,7 +1380,7 @@
         class C(object):
             x = attr.ib()
 
-        b = _ClassBuilder(C, None, True, True, False)
+        b = _ClassBuilder(C, None, True, True, False, False)
 
         cls = (
             b.add_cmp()
@@ -1222,7 +1437,12 @@
             pass
 
         b = _ClassBuilder(
-            C, these=None, slots=False, frozen=False, auto_attribs=False
+            C,
+            these=None,
+            slots=False,
+            frozen=False,
+            auto_attribs=False,
+            kw_only=False,
         )
         b._cls = {}  # no __module__; no __qualname__
 
diff --git a/tests/utils.py b/tests/utils.py
index baf7331..230726c 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -36,6 +36,7 @@
     hash=None,
     init=True,
     converter=None,
+    kw_only=False,
 ):
     """
     Return an attribute with a name and no other bells and whistles.
@@ -49,6 +50,7 @@
         hash=hash,
         init=init,
         converter=converter,
+        kw_only=False,
     )