Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Doc/library/bdb.rst
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ The :mod:`bdb` module also defines two classes:
``(frame, lineno)`` tuple. The return string contains:

* The canonical filename which contains the frame.
* The function name or ``"<lambda>"``.
* The function name.
* The input arguments.
* The return value.
* The line of code (if it exists).
Expand Down
3 changes: 1 addition & 2 deletions Doc/library/pickle.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1256,8 +1256,7 @@ The following option is accepted:

.. [#] Don't confuse this with the :mod:`marshal` module

.. [#] This is why :keyword:`lambda` functions cannot be pickled: all
:keyword:`!lambda` functions share the same name: ``<lambda>``.
.. [#] This is why :keyword:`lambda` functions cannot be pickled.

.. [#] The exception raised will likely be an :exc:`ImportError` or an
:exc:`AttributeError` but it could be something else.
Expand Down
2 changes: 1 addition & 1 deletion Doc/library/unittest.mock.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2684,7 +2684,7 @@ we try to call it incorrectly::
>>> req = request.Request()
Traceback (most recent call last):
...
TypeError: <lambda>() takes at least 2 arguments (1 given)
TypeError: missing a required argument: 'url'

The spec also applies to instantiated classes (i.e. the return value of
specced mocks)::
Expand Down
11 changes: 11 additions & 0 deletions Doc/whatsnew/3.15.rst
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,17 @@ Other language changes
not only integers or floats, although this does not improve precision.
(Contributed by Serhiy Storchaka in :gh:`67795`.)

* The repr and the name of :keyword:`lambda` functions will now contain
the signature and body expression. For example::

>>> f = lambda x, y=1: x+y
>>> f
<lambda x, y=1: x + y at 0x7f437cd27890>
>>> f.__name__
'<lambda x, y=1: x + y>'

(Contributed by Serhiy Storchaka in :issue:`32489`.)


New modules
===========
Expand Down
1 change: 1 addition & 0 deletions Lib/test/test_compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,7 @@ def test_compile_ast(self):
['<ifblock>', """if True:\n pass\n"""],
['<forblock>', """for n in [1, 2, 3]:\n print(n)\n"""],
['<deffunc>', """def foo():\n pass\nfoo()\n"""],
['<lambda>', """f = lambda x: x+1"""],
[fname, fcontents],
]

Expand Down
4 changes: 2 additions & 2 deletions Lib/test/test_docxmlrpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,8 @@ def test_lambda(self):
self.client.request("GET", "/")
response = self.client.getresponse()

self.assertIn((b'<dl><dt><a name="-&lt;lambda&gt;"><strong>'
b'&lt;lambda&gt;</strong></a>(x, y)</dt></dl>'),
self.assertIn((b'<dl><dt><a name="-&lt;lambda x, y: x - y&gt;"><strong>'
b'&lt;lambda x, y: x - y&gt;</strong></a>(x, y)</dt></dl>'),
response.read())

@make_request_and_skipIf(sys.flags.optimize >= 2,
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_funcattrs.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ def test___qualname__(self):
self.assertEqual(FuncAttrsTest.setUp.__qualname__, 'FuncAttrsTest.setUp')
self.assertEqual(global_function.__qualname__, 'global_function')
self.assertEqual(global_function().__qualname__,
'global_function.<locals>.<lambda>')
'global_function.<locals>.<lambda: inner_function>')
self.assertEqual(global_function()().__qualname__,
'global_function.<locals>.inner_function')
self.assertEqual(global_function()()().__qualname__,
Expand Down
4 changes: 2 additions & 2 deletions Lib/test/test_reprlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,8 +202,8 @@ def test_instance(self):
self.assertIn(s.find("..."), [12, 13])

def test_lambda(self):
r = repr(lambda x: x)
self.assertStartsWith(r, "<function ReprTests.test_lambda.<locals>.<lambda")
r = repr(lambda x, y=1: x+y)
self.assertStartsWith(r, "<lambda x, y=1: x + y ")
# XXX anonymous functions? see func_repr

def test_builtin_function(self):
Expand Down
4 changes: 2 additions & 2 deletions Lib/test/test_unittest/test_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -1544,11 +1544,11 @@ def Stub():

def testAssertNotRaisesRegex(self):
self.assertRaisesRegex(
self.failureException, '^Exception not raised by <lambda>$',
self.failureException, '^Exception not raised by <lambda: None>$',
self.assertRaisesRegex, Exception, re.compile('x'),
lambda: None)
self.assertRaisesRegex(
self.failureException, '^Exception not raised by <lambda>$',
self.failureException, '^Exception not raised by <lambda: None>$',
self.assertRaisesRegex, Exception, 'x',
lambda: None)
# Custom message
Expand Down
9 changes: 6 additions & 3 deletions Lib/tkinter/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1700,15 +1700,18 @@ def _register(self, func, subst=None, needcleanup=1):
be executed. An optional function SUBST can
be given which will be executed before FUNC."""
f = CallWrapper(func, subst, self).__call__
name = repr(id(f))
try:
func = func.__func__
except AttributeError:
pass
try:
name = name + func.__name__
name = func.__name__
except AttributeError:
pass
name = ''
else:
if not re.fullmatch('[a-zA-Z0-9_]+', name):
name = ''
name = repr(id(f)) + name
self.tk.createcommand(name, f)
if needcleanup:
if self._tclCommands is None:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add signature and body expression to the repr of lambda.
12 changes: 12 additions & 0 deletions Objects/funcobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -1165,6 +1165,18 @@ static PyObject*
func_repr(PyObject *self)
{
PyFunctionObject *op = _PyFunction_CAST(self);
PyObject *name = op->func_name;
Py_ssize_t len = PyUnicode_GET_LENGTH(name);
if (len > 2
&& PyUnicode_READ_CHAR(name, 0) == '<'
&& PyUnicode_READ_CHAR(name, len-1) == '>')
{
PyObject *repr = PyUnicode_Substring(name, 0, len-1);
if (repr) {
Py_SETREF(repr, PyUnicode_FromFormat("%U at %p>", repr, op));
}
return repr;
}
return PyUnicode_FromFormat("<function %U at %p>",
op->func_qualname, op);
}
Expand Down
18 changes: 14 additions & 4 deletions Python/codegen.c
Original file line number Diff line number Diff line change
Expand Up @@ -2022,10 +2022,20 @@ codegen_lambda(compiler *c, expr_ty e)
.u_posonlyargcount = asdl_seq_LEN(args->posonlyargs),
.u_kwonlyargcount = asdl_seq_LEN(args->kwonlyargs),
};
_Py_DECLARE_STR(anon_lambda, "<lambda>");
RETURN_IF_ERROR(
codegen_enter_scope(c, &_Py_STR(anon_lambda), COMPILE_SCOPE_LAMBDA,
(void *)e, e->lineno, NULL, &umd));

PyObject *exprstr = _PyAST_ExprAsUnicode(e);
if (!exprstr) {
return ERROR;
}
PyObject *name = PyUnicode_FromFormat("<%U>", exprstr);
Py_DECREF(exprstr);
if (!name) {
return ERROR;
}
int rc = codegen_enter_scope(c, name, COMPILE_SCOPE_LAMBDA,
(void *)e, e->lineno, NULL, &umd);
Py_DECREF(name);
RETURN_IF_ERROR(rc);

assert(!SYMTABLE_ENTRY(c)->ste_has_docstring);

Expand Down
Loading