give the names of missing positional or keyword-only arguments (closes #12356)
diff --git a/Lib/inspect.py b/Lib/inspect.py
index aa4c30f..80802e4 100644
--- a/Lib/inspect.py
+++ b/Lib/inspect.py
@@ -918,10 +918,24 @@
         specs.append(formatvarkw(varkw) + formatvalue(locals[varkw]))
     return '(' + ', '.join(specs) + ')'
 
-def _positional_error(f_name, args, kwonly, varargs, defcount, given, values):
+def _missing_arguments(f_name, argnames, pos, values):
+    names = [repr(name) for name in argnames if name not in values]
+    missing = len(names)
+    if missing == 1:
+        s = names[0]
+    elif missing == 2:
+        s = "{} and {}".format(*names)
+    else:
+        tail = ", {} and {}".format(names[-2:])
+        del names[-2:]
+        s = ", ".join(names) + tail
+    raise TypeError("%s() missing %i required %s argument%s: %s" %
+                    (f_name, missing,
+                      "positional" if pos else "keyword-only",
+                      "" if missing == 1 else "s", s))
+
+def _too_many(f_name, args, kwonly, varargs, defcount, given, values):
     atleast = len(args) - defcount
-    if given is None:
-        given = len([arg for arg in args if arg in values])
     kwonly_given = len([arg for arg in kwonly if arg in values])
     if varargs:
         plural = atleast != 1
@@ -980,22 +994,25 @@
                             (f_name, kw))
         arg2value[kw] = value
     if num_pos > num_args and not varargs:
-        _positional_error(f_name, args, kwonlyargs, varargs, num_defaults,
-                          num_pos, arg2value)
+        _too_many(f_name, args, kwonlyargs, varargs, num_defaults,
+                   num_pos, arg2value)
     if num_pos < num_args:
-        for arg in args[:num_args - num_defaults]:
+        req = args[:num_args - num_defaults]
+        for arg in req:
             if arg not in arg2value:
-                _positional_error(f_name, args, kwonlyargs, varargs,
-                                  num_defaults, None, arg2value)
+                _missing_arguments(f_name, req, True, arg2value)
         for i, arg in enumerate(args[num_args - num_defaults:]):
             if arg not in arg2value:
                 arg2value[arg] = defaults[i]
+    missing = 0
     for kwarg in kwonlyargs:
         if kwarg not in arg2value:
-            if kwarg not in kwonlydefaults:
-                raise TypeError("%s() requires keyword-only argument %r" %
-                                (f_name, kwarg))
-            arg2value[kwarg] = kwonlydefaults[kwarg]
+            if kwarg in kwonlydefaults:
+                arg2value[kwarg] = kwonlydefaults[kwarg]
+            else:
+                missing += 1
+    if missing:
+        _missing_arguments(f_name, kwonlyargs, False, arg2value)
     return arg2value
 
 # -------------------------------------------------- stack frame extraction
diff --git a/Lib/test/test_extcall.py b/Lib/test/test_extcall.py
index 49d5441..6b6c12d 100644
--- a/Lib/test/test_extcall.py
+++ b/Lib/test/test_extcall.py
@@ -66,17 +66,17 @@
     >>> g()
     Traceback (most recent call last):
       ...
-    TypeError: g() takes at least 1 positional argument but 0 were given
+    TypeError: g() missing 1 required positional argument: 'x'
 
     >>> g(*())
     Traceback (most recent call last):
       ...
-    TypeError: g() takes at least 1 positional argument but 0 were given
+    TypeError: g() missing 1 required positional argument: 'x'
 
     >>> g(*(), **{})
     Traceback (most recent call last):
       ...
-    TypeError: g() takes at least 1 positional argument but 0 were given
+    TypeError: g() missing 1 required positional argument: 'x'
 
     >>> g(1)
     1 () {}
@@ -263,91 +263,80 @@
     >>> f(**x)
     1 2
 
-Some additional tests about positional argument errors:
+Too many arguments:
 
-    >>> def f(a, b):
-    ...    pass
-    >>> f(b=1)
-    Traceback (most recent call last):
-      ...
-    TypeError: f() takes 2 positional arguments but 1 was given
-
-    >>> def f(a):
-    ...    pass
-    >>> f(6, a=4, *(1, 2, 3))
-    Traceback (most recent call last):
-      ...
-    TypeError: f() got multiple values for argument 'a'
-    >>> def f(a, *, kw):
-    ...    pass
-    >>> f(6, 4, kw=4)
-    Traceback (most recent call last):
-      ...
-    TypeError: f() takes 1 positional argument but 2 positional arguments (and 1 keyword-only argument) were given
-
-    >>> def f(a):
-    ...    pass
-    >>> f()
-    Traceback (most recent call last):
-      ...
-    TypeError: f() takes 1 positional argument but 0 were given
-
-    >>> def f(a, b):
-    ...    pass
+    >>> def f(): pass
     >>> f(1)
     Traceback (most recent call last):
       ...
-    TypeError: f() takes 2 positional arguments but 1 was given
-
-    >>> def f(a, *b):
-    ...    pass
-    >>> f()
+    TypeError: f() takes 0 positional arguments but 1 was given
+    >>> def f(a): pass
+    >>> f(1, 2)
     Traceback (most recent call last):
       ...
-    TypeError: f() takes at least 1 positional argument but 0 were given
-
-    >>> def f(a, *, kw=4):
-    ...    pass
-    >>> f(kw=4)
+    TypeError: f() takes 1 positional argument but 2 were given
+    >>> def f(a, b=1): pass
+    >>> f(1, 2, 3)
     Traceback (most recent call last):
       ...
-    TypeError: f() takes 1 positional argument but 0 positional arguments (and 1 keyword-only argument) were given
-
-    >>> def f(a, b=2):
-    ...    pass
-    >>> f()
-    Traceback (most recent call last):
-      ...
-    TypeError: f() takes from 1 to 2 positional arguments but 0 were given
-
-    >>> def f(a, *b):
-    ...    pass
-    >>> f()
-    Traceback (most recent call last):
-      ...
-    TypeError: f() takes at least 1 positional argument but 0 were given
-
-    >>> def f(*, kw):
-    ...    pass
-    >>> f(3, kw=4)
+    TypeError: f() takes from 1 to 2 positional arguments but 3 were given
+    >>> def f(*, kw): pass
+    >>> f(1, kw=3)
     Traceback (most recent call last):
       ...
     TypeError: f() takes 0 positional arguments but 1 positional argument (and 1 keyword-only argument) were given
+    >>> def f(*, kw, b): pass
+    >>> f(1, 2, 3, b=3, kw=3)
+    Traceback (most recent call last):
+      ...
+    TypeError: f() takes 0 positional arguments but 3 positional arguments (and 2 keyword-only arguments) were given
+    >>> def f(a, b=2, *, kw): pass
+    >>> f(2, 3, 4, kw=4)
+    Traceback (most recent call last):
+      ...
+    TypeError: f() takes from 1 to 2 positional arguments but 3 positional arguments (and 1 keyword-only argument) were given
 
-    >>> def f(a, c=3, *b, kw):
-    ...    pass
+Too few and missing arguments:
+
+    >>> def f(a): pass
     >>> f()
     Traceback (most recent call last):
-     ...
-    TypeError: f() takes at least 1 positional argument but 0 were given
-    >>> f(kw=3)
+      ...
+    TypeError: f() missing 1 required positional argument: 'a'
+    >>> def f(a, b): pass
+    >>> f()
     Traceback (most recent call last):
-     ...
-    TypeError: f() takes at least 1 positional argument but 0 positional arguments (and 1 keyword-only argument) were given
-    >>> f(kw=3, c=4)
+      ...
+    TypeError: f() missing 2 required positional arguments: 'a' and 'b'
+    >>> def f(a, b, c): pass
+    >>> f()
     Traceback (most recent call last):
-     ...
-    TypeError: f() takes at least 1 positional argument but 1 positional argument (and 1 keyword-only argument) were given
+      ...
+    TypeError: f() missing 3 required positional arguments: 'a', 'b', and 'c'
+    >>> def f(a, b, c, d, e): pass
+    >>> f()
+    Traceback (most recent call last):
+      ...
+    TypeError: f() missing 5 required positional arguments: 'a', 'b', 'c', 'd', and 'e'
+    >>> def f(a, b=4, c=5, d=5): pass
+    >>> f(c=12, b=9)
+    Traceback (most recent call last):
+      ...
+    TypeError: f() missing 1 required positional argument: 'a'
+
+Same with keyword only args:
+
+    >>> def f(*, w): pass
+    >>> f()
+    Traceback (most recent call last):
+      ...
+    TypeError: f() missing 1 required keyword-only argument: 'w'
+    >>> def f(*, a, b, c, d, e): pass
+    >>> f()
+    Traceback (most recent call last):
+      ...
+    TypeError: f() missing 5 required keyword-only arguments: 'a', 'b', 'c', 'd', and 'e'
+
 """
 
 import sys