Skip to content

Raise consistent NameError in ForwardRef.evaluate() #135646

Description

@Viicos

Feature or enhancement

Proposal:

The new ForwardRef.evaluate() implementation had a fast path introduced in #124337 to not call eval():

ifarg.isidentifier() andnotkeyword.iskeyword(arg):
ifarginlocals:
returnlocals[arg]
elifarginglobals:
returnglobals[arg]
elifhasattr(builtins, arg):
returngetattr(builtins, arg)
elifis_forwardref_format:
returnself
else:
raiseNameError(arg)

However, when the NameError is raised, the format is different from the one raised by eval():

The exception message is defined as:

#defineNAME_ERROR_MSG "name '%.200s' is not defined"

and raised this way:

cpython/Python/ceval.c

Lines 3339 to 3351 in cb39410

_PyErr_Format(tstate, exc, format_str, obj_str);
if (exc==PyExc_NameError) {
// Include the name in the NameError exceptions to offer suggestions later.
PyObject*exc=PyErr_GetRaisedException();
if (PyErr_GivenExceptionMatches(exc, PyExc_NameError)) {
if (((PyNameErrorObject*)exc)->name==NULL) {
// We do not care if this fails because we are going to restore the
// NameError anyway.
(void)PyObject_SetAttr(exc, &_Py_ID(name), obj);
}
}
PyErr_SetRaisedException(exc);

To have consistent exceptions raised, would it be possible to change the Python fast path implementation to match the C eval code?

diff --git a/Lib/annotationlib.py b/Lib/annotationlib.py
index 731817a9973..c83a1573ccd 100644
--- a/Lib/annotationlib.py+++ b/Lib/annotationlib.py@@ -27,6 +27,9 @@ class Format(enum.IntEnum):
_sentinel = object()
+# Following `NAME_ERROR_MSG` in `ceval_macros.h`:+_NAME_ERROR_MSG = "name '{name:.200}' is not defined"+
# Slots shared by ForwardRef and _Stringifier. The __forward__ names must be
# preserved for compatibility with the old typing.ForwardRef class. The remaining
@@ -184,7 +187,7 @@ def evaluate(
elif is_forwardref_format:
return self
else:
- raise NameError(arg)+ raise NameError(_NAME_ERROR_MSG.format(name=arg), name=arg)
else:
code = self.__forward_code__
try:

This requires _NAME_ERROR_MSG to be in sync with the one from ceval_macros.h.

Or at least, the NameError should have its name property set (especially as type stubs shows NameError.name as str currently).

cc @JelleZijlstra.

Has this already been discussed elsewhere?

This is a minor feature, which does not need previous discussion elsewhere

Links to previous discussion of this feature:

No response

Linked PRs

Metadata

Metadata

Assignees

No one assigned

    Labels

    type-featureA feature request or enhancement

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions