Skip to content

Commit 4e2b84a

Browse files
Commit
1 parent 55a44cc commit 4e2b84a

File tree

6 files changed

+83
-38
lines changed

6 files changed

+83
-38
lines changed

Lib/inspect.py

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1381,7 +1381,7 @@ def convert(name, locals=locals,
13811381
specs.append(formatvarkw(varkw) + formatvalue(locals[varkw]))
13821382
return '(' + ', '.join(specs) + ')'
13831383

1384-
def _missing_arguments(f_name, argnames, pos, values):
1384+
def _missing_arguments(f_name, argnames, pos, values, *, has_posonly=False):
13851385
names = [repr(name) for name in argnames if name not in values]
13861386
missing = len(names)
13871387
if missing == 1:
@@ -1392,10 +1392,18 @@ def _missing_arguments(f_name, argnames, pos, values):
13921392
tail = ", {} and {}".format(*names[-2:])
13931393
del names[-2:]
13941394
s = ", ".join(names) + tail
1395-
raise TypeError("%s() missing %i required %s argument%s: %s" %
1396-
(f_name, missing,
1397-
"positional" if pos else "keyword-only",
1398-
"" if missing == 1 else "s", s))
1395+
1396+
if pos:
1397+
qualifier = "positional" if has_posonly else ""
1398+
else:
1399+
qualifier = "keyword-only"
1400+
if qualifier:
1401+
raise TypeError("%s() missing %i required %s argument%s: %s" %
1402+
(f_name, missing, qualifier,
1403+
"" if missing == 1 else "s", s))
1404+
else:
1405+
raise TypeError("%s() missing %i required argument%s: %s" %
1406+
(f_name, missing, "" if missing == 1 else "s", s))
13991407

14001408
def _too_many(f_name, args, kwonly, varargs, defcount, given, values):
14011409
atleast = len(args) - defcount
@@ -1429,7 +1437,9 @@ def getcallargs(func, /, *positional, **named):
14291437
f_name = func.__name__
14301438
arg2value = {}
14311439

1432-
1440+
num_posonly = 0
1441+
if hasattr(func, '__code__'):
1442+
num_posonly = func.__code__.co_posonlyargcount
14331443
if ismethod(func) and func.__self__ is not None:
14341444
# implicit 'self' (or 'cls' for classmethods) argument
14351445
positional = (func.__self__,) + positional
@@ -1463,7 +1473,9 @@ def getcallargs(func, /, *positional, **named):
14631473
req = args[:num_args - num_defaults]
14641474
for arg in req:
14651475
if arg not in arg2value:
1466-
_missing_arguments(f_name, req, True, arg2value)
1476+
missing_posonly = any(i < num_posonly for i, name in enumerate(args)
1477+
if name in req and name not in arg2value)
1478+
_missing_arguments(f_name, req, True, arg2value, has_posonly=missing_posonly)
14671479
for i, arg in enumerate(args[num_args - num_defaults:]):
14681480
if arg not in arg2value:
14691481
arg2value[arg] = defaults[i]
@@ -1475,7 +1487,7 @@ def getcallargs(func, /, *positional, **named):
14751487
else:
14761488
missing += 1
14771489
if missing:
1478-
_missing_arguments(f_name, kwonlyargs, False, arg2value)
1490+
_missing_arguments(f_name, kwonlyargs, False, arg2value, has_posonly=False)
14791491
return arg2value
14801492

14811493
ClosureVars = namedtuple('ClosureVars', 'nonlocals globals builtins unbound')

Lib/test/test_call.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -920,7 +920,7 @@ def check_raises_type_error(self, message):
920920
self.assertEqual(str(cm.exception), message)
921921

922922
def test_missing_arguments(self):
923-
msg = "A.method_two_args() missing 1 required positional argument: 'y'"
923+
msg = "A.method_two_args() missing 1 required argument: 'y'"
924924
with self.check_raises_type_error(msg):
925925
A().method_two_args("x")
926926

Lib/test/test_dataclasses/__init__.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,7 @@ class C:
534534

535535
with self.assertRaisesRegex(TypeError,
536536
r"__init__\(\) missing 1 required "
537-
"positional argument: 'x'"):
537+
"argument: 'x'"):
538538
C()
539539

540540
def test_field_default(self):
@@ -935,7 +935,7 @@ class C:
935935
x: int=field(default=MISSING)
936936
with self.assertRaisesRegex(TypeError,
937937
r'__init__\(\) missing 1 required '
938-
'positional argument'):
938+
'argument'):
939939
C()
940940
self.assertNotIn('x', C.__dict__)
941941

@@ -944,7 +944,7 @@ class D:
944944
x: int
945945
with self.assertRaisesRegex(TypeError,
946946
r'__init__\(\) missing 1 required '
947-
'positional argument'):
947+
'argument'):
948948
D()
949949
self.assertNotIn('x', D.__dict__)
950950

@@ -957,7 +957,7 @@ class C:
957957
x: int=field(default_factory=MISSING)
958958
with self.assertRaisesRegex(TypeError,
959959
r'__init__\(\) missing 1 required '
960-
'positional argument'):
960+
'argument'):
961961
C()
962962
self.assertNotIn('x', C.__dict__)
963963

@@ -966,7 +966,7 @@ class D:
966966
x: int=field(default=MISSING, default_factory=MISSING)
967967
with self.assertRaisesRegex(TypeError,
968968
r'__init__\(\) missing 1 required '
969-
'positional argument'):
969+
'argument'):
970970
D()
971971
self.assertNotIn('x', D.__dict__)
972972

@@ -3260,7 +3260,7 @@ class C:
32603260
# also have a default value (of type
32613261
# types.MemberDescriptorType).
32623262
with self.assertRaisesRegex(TypeError,
3263-
r"__init__\(\) missing 1 required positional argument: 'x'"):
3263+
r"__init__\(\) missing 1 required argument: 'x'"):
32643264
C()
32653265

32663266
# We can create an instance, and assign to x.
@@ -3784,7 +3784,7 @@ def __init_subclass__(cls, arg):
37843784

37853785
with self.assertRaisesRegex(
37863786
TypeError,
3787-
"missing 1 required positional argument: 'arg'",
3787+
"missing 1 required argument: 'arg'",
37883788
):
37893789
@dataclass(slots=True)
37903790
class WithWrongSuper(WrongSuper, arg=1):
@@ -4018,7 +4018,7 @@ def __set__(self, instance: Any, value: int) -> None:
40184018
class C:
40194019
i: D = D()
40204020

4021-
with self.assertRaisesRegex(TypeError, 'missing 1 required positional argument'):
4021+
with self.assertRaisesRegex(TypeError, 'missing 1 required argument'):
40224022
c = C()
40234023

40244024
class TestStringAnnotations(unittest.TestCase):
@@ -4227,7 +4227,7 @@ class Base2:
42274227
C = make_dataclass('C',
42284228
[('y', int)],
42294229
bases=(Base1, Base2))
4230-
with self.assertRaisesRegex(TypeError, 'required positional'):
4230+
with self.assertRaisesRegex(TypeError, 'required argument'):
42314231
c = C(2)
42324232
c = C(1, 2)
42334233
self.assertIsInstance(c, C)

Lib/test/test_extcall.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -111,17 +111,17 @@
111111
>>> g()
112112
Traceback (most recent call last):
113113
...
114-
TypeError: g() missing 1 required positional argument: 'x'
114+
TypeError: g() missing 1 required argument: 'x'
115115
116116
>>> g(*())
117117
Traceback (most recent call last):
118118
...
119-
TypeError: g() missing 1 required positional argument: 'x'
119+
TypeError: g() missing 1 required argument: 'x'
120120
121121
>>> g(*(), **{})
122122
Traceback (most recent call last):
123123
...
124-
TypeError: g() missing 1 required positional argument: 'x'
124+
TypeError: g() missing 1 required argument: 'x'
125125
126126
>>> g(1)
127127
1 () {}
@@ -505,27 +505,27 @@
505505
>>> f()
506506
Traceback (most recent call last):
507507
...
508-
TypeError: f() missing 1 required positional argument: 'a'
508+
TypeError: f() missing 1 required argument: 'a'
509509
>>> def f(a, b): pass
510510
>>> f()
511511
Traceback (most recent call last):
512512
...
513-
TypeError: f() missing 2 required positional arguments: 'a' and 'b'
513+
TypeError: f() missing 2 required arguments: 'a' and 'b'
514514
>>> def f(a, b, c): pass
515515
>>> f()
516516
Traceback (most recent call last):
517517
...
518-
TypeError: f() missing 3 required positional arguments: 'a', 'b', and 'c'
518+
TypeError: f() missing 3 required arguments: 'a', 'b', and 'c'
519519
>>> def f(a, b, c, d, e): pass
520520
>>> f()
521521
Traceback (most recent call last):
522522
...
523-
TypeError: f() missing 5 required positional arguments: 'a', 'b', 'c', 'd', and 'e'
523+
TypeError: f() missing 5 required arguments: 'a', 'b', 'c', 'd', and 'e'
524524
>>> def f(a, b=4, c=5, d=5): pass
525525
>>> f(c=12, b=9)
526526
Traceback (most recent call last):
527527
...
528-
TypeError: f() missing 1 required positional argument: 'a'
528+
TypeError: f() missing 1 required argument: 'a'
529529
530530
Same with keyword only args:
531531

Lib/test/test_positional_only_arg.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ def f(a, b, /):
142142
def test_positional_only_and_arg_invalid_calls(self):
143143
def f(a, b, /, c):
144144
pass
145-
with self.assertRaisesRegex(TypeError, r"f\(\) missing 1 required positional argument: 'c'"):
145+
with self.assertRaisesRegex(TypeError, r"f\(\) missing 1 required argument: 'c'"):
146146
f(1, 2)
147147
with self.assertRaisesRegex(TypeError, r"f\(\) missing 2 required positional arguments: 'b' and 'c'"):
148148
f(1)
@@ -170,7 +170,7 @@ def f(a, b, /, c, *, d, e):
170170
f(1, 2, 3, e=2)
171171
with self.assertRaisesRegex(TypeError, r"missing 2 required keyword-only arguments: 'd' and 'e'"):
172172
f(1, 2, 3)
173-
with self.assertRaisesRegex(TypeError, r"f\(\) missing 1 required positional argument: 'c'"):
173+
with self.assertRaisesRegex(TypeError, r"f\(\) missing 1 required argument: 'c'"):
174174
f(1, 2)
175175
with self.assertRaisesRegex(TypeError, r"f\(\) missing 2 required positional arguments: 'b' and 'c'"):
176176
f(1)
@@ -278,7 +278,7 @@ def g(x2,/,y2):
278278
return g
279279

280280
self.assertEqual(f(1,2)(3,4), 10)
281-
with self.assertRaisesRegex(TypeError, r"g\(\) missing 1 required positional argument: 'y2'"):
281+
with self.assertRaisesRegex(TypeError, r"g\(\) missing 1 required argument: 'y2'"):
282282
f(1,2)(3)
283283
with self.assertRaisesRegex(TypeError, r"g\(\) takes 2 positional arguments but 3 were given"):
284284
f(1,2)(3,4,5)
@@ -296,7 +296,7 @@ def g(x2,/,y2):
296296
return g
297297

298298
self.assertEqual(f(1,2)(3,4), 10)
299-
with self.assertRaisesRegex(TypeError, r"g\(\) missing 1 required positional argument: 'y2'"):
299+
with self.assertRaisesRegex(TypeError, r"g\(\) missing 1 required argument: 'y2'"):
300300
f(1,2)(3)
301301
with self.assertRaisesRegex(TypeError, r"g\(\) takes 2 positional arguments but 3 were given"):
302302
f(1,2)(3,4,5)

Python/ceval.c

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1322,13 +1322,22 @@ format_missing(PyThreadState *tstate, const char *kind,
13221322
}
13231323
if (name_str == NULL)
13241324
return;
1325-
_PyErr_Format(tstate, PyExc_TypeError,
1326-
"%U() missing %i required %s argument%s: %U",
1327-
qualname,
1328-
len,
1329-
kind,
1330-
len == 1 ? "" : "s",
1331-
name_str);
1325+
if (kind[0] == '\0') {
1326+
_PyErr_Format(tstate, PyExc_TypeError,
1327+
"%U() missing %i required argument%s: %U",
1328+
qualname,
1329+
len,
1330+
len == 1 ? "" : "s",
1331+
name_str);
1332+
} else {
1333+
_PyErr_Format(tstate, PyExc_TypeError,
1334+
"%U() missing %i required %s argument%s: %U",
1335+
qualname,
1336+
len,
1337+
kind,
1338+
len == 1 ? "" : "s",
1339+
name_str);
1340+
}
13321341
Py_DECREF(name_str);
13331342
}
13341343

@@ -1340,9 +1349,33 @@ missing_arguments(PyThreadState *tstate, PyCodeObject *co,
13401349
Py_ssize_t i, j = 0;
13411350
Py_ssize_t start, end;
13421351
int positional = (defcount != -1);
1343-
const char *kind = positional ? "positional" : "keyword-only";
1352+
const char *kind = "";
13441353
PyObject *missing_names;
13451354

1355+
if (positional) {
1356+
int has_posonly_missing = 0;
1357+
1358+
start = 0;
1359+
end = co->co_argcount - defcount;
1360+
1361+
for (i = start; i < end; i++) {
1362+
if (PyStackRef_IsNull(localsplus[i])) {
1363+
if (i < co->co_posonlyargcount) {
1364+
has_posonly_missing = 1;
1365+
break;
1366+
}
1367+
}
1368+
}
1369+
1370+
if (has_posonly_missing) {
1371+
kind = "positional";
1372+
} else {
1373+
kind = "";
1374+
}
1375+
} else {
1376+
kind = "keyword-only";
1377+
}
1378+
13461379
/* Compute the names of the arguments that are missing. */
13471380
missing_names = PyList_New(missing);
13481381
if (missing_names == NULL)

0 commit comments

Comments
 (0)