Hi all, I read this paper today about common mistakes that Python beginners make:
https://www.researchgate.net/publication/307088989_Some_Trouble_with_Transparency_An_Analysis_of_Student_Errors_with_Object-oriented_Python The most common one by far is forgetting the "self" parameter in the method definition (which also still happens to me regularly). The error message is not particularly enlightening, if you don't quite understand the explicit self in Python. So I wonder whether we should print a better error message, something like this: $ cat m.py class A(object): def f(x): return self.x A().f(1) $ pypy m.py Traceback (application-level): File "m.py", line 4 in <module> A().f(1) TypeError: f() takes exactly 1 argument (2 given). Did you forget 'self' in the function definition? It's a bit the question how clever we would like this to be to reduce false positives, see the attached patch for a very simple approach. Anyone have opinions? Cheers, Carl Friedrich
diff -r 42797eb41b78 pypy/interpreter/argument.py --- a/pypy/interpreter/argument.py Mon Sep 26 10:05:13 2016 +0200 +++ b/pypy/interpreter/argument.py Tue Sep 27 19:00:02 2016 +0200 @@ -466,16 +466,18 @@ plural = "" else: plural = "s" + selfmsg = msg2 = "" if has_kwarg or num_kwds > 0: msg2 = " non-keyword" - else: - msg2 = "" - msg = "takes %s %d%s argument%s (%d given)" % ( + if num_args == n + 1 and self.signature.argnames[0] != "self": + selfmsg = ". Did you forget 'self' in the function definition?" + msg = "takes %s %d%s argument%s (%d given)%s" % ( msg1, n, msg2, plural, - num_args) + num_args, + selfmsg) return msg class ArgErrMultipleValues(ArgErr): diff -r 42797eb41b78 pypy/interpreter/test/test_argument.py --- a/pypy/interpreter/test/test_argument.py Mon Sep 26 10:05:13 2016 +0200 +++ b/pypy/interpreter/test/test_argument.py Tue Sep 27 19:00:02 2016 +0200 @@ -573,13 +573,17 @@ s = err.getmsg() assert s == "takes exactly 1 argument (0 given)" + sig = Signature(['self', 'b'], None, None) + err = ArgErrCount(3, 0, sig, [], 0) + s = err.getmsg() + assert s == "takes exactly 2 arguments (3 given)" sig = Signature(['a', 'b'], None, None) err = ArgErrCount(3, 0, sig, [], 0) s = err.getmsg() - assert s == "takes exactly 2 arguments (3 given)" + assert s == "takes exactly 2 arguments (3 given). Did you forget 'self' in the function definition?" err = ArgErrCount(3, 0, sig, ['a'], 0) s = err.getmsg() - assert s == "takes at most 2 arguments (3 given)" + assert s == "takes at most 2 arguments (3 given). Did you forget 'self' in the function definition?" sig = Signature(['a', 'b'], '*', None) err = ArgErrCount(1, 0, sig, [], 1) @@ -592,7 +596,7 @@ sig = Signature(['a'], None, '**') err = ArgErrCount(2, 1, sig, [], 0) s = err.getmsg() - assert s == "takes exactly 1 non-keyword argument (2 given)" + assert s == "takes exactly 1 non-keyword argument (2 given). Did you forget 'self' in the function definition?" err = ArgErrCount(0, 1, sig, [], 1) s = err.getmsg() assert s == "takes exactly 1 non-keyword argument (0 given)" @@ -605,7 +609,7 @@ sig = Signature(['a'], None, '**') err = ArgErrCount(2, 1, sig, ['a'], 0) s = err.getmsg() - assert s == "takes at most 1 non-keyword argument (2 given)" + assert s == "takes at most 1 non-keyword argument (2 given). Did you forget 'self' in the function definition?" def test_bad_type_for_star(self): space = self.space @@ -662,9 +666,9 @@ exc = raises(TypeError, (lambda a, b: 0), 1, 2, 3, a=1) assert exc.value.message == "<lambda>() takes exactly 2 arguments (4 given)" exc = raises(TypeError, (lambda a, b=1: 0), 1, 2, 3, a=1) - assert exc.value.message == "<lambda>() takes at most 2 non-keyword arguments (3 given)" + assert exc.value.message == "<lambda>() takes at most 2 non-keyword arguments (3 given). Did you forget 'self' in the function definition?" exc = raises(TypeError, (lambda a, b=1, **kw: 0), 1, 2, 3) - assert exc.value.message == "<lambda>() takes at most 2 non-keyword arguments (3 given)" + assert exc.value.message == "<lambda>() takes at most 2 non-keyword arguments (3 given). Did you forget 'self' in the function definition?" exc = raises(TypeError, (lambda a, b, c=3, **kw: 0), 1) assert exc.value.message == "<lambda>() takes at least 2 arguments (1 given)" exc = raises(TypeError, (lambda a, b, **kw: 0), 1)
_______________________________________________ pypy-dev mailing list pypy-dev@python.org https://mail.python.org/mailman/listinfo/pypy-dev