Skip to content

gh-122245: move checks for writes and shadowing of __debug__ to symtable #122246

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jul 26, 2024
Merged
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
5 changes: 5 additions & 0 deletions Doc/whatsnew/3.14.rst
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ Other Language Changes
command line option. For example, ``python -O -c 'assert await 1'``
now produces a :exc:`SyntaxError`. (Contributed by Jelle Zijlstra in :gh:`121637`.)

* Writes to ``__debug__`` are now detected even if the code is optimized
away by the :option:`-O` command line option. For example,
``python -O -c 'assert (__debug__ := 1)'`` now produces a
:exc:`SyntaxError`. (Contributed by Irit Katriel in :gh:`122245`.)

* Added class methods :meth:`float.from_number` and :meth:`complex.from_number`
to convert a number to :class:`float` or :class:`complex` type correspondingly.
They raise an error if the argument is a string.
Expand Down
79 changes: 79 additions & 0 deletions Lib/test/test_syntax.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,18 @@
Traceback (most recent call last):
SyntaxError: cannot assign to __debug__

>>> def __debug__(): pass
Traceback (most recent call last):
SyntaxError: cannot assign to __debug__

>>> async def __debug__(): pass
Traceback (most recent call last):
SyntaxError: cannot assign to __debug__

>>> class __debug__: pass
Traceback (most recent call last):
SyntaxError: cannot assign to __debug__

>>> del __debug__
Traceback (most recent call last):
SyntaxError: cannot delete __debug__
Expand Down Expand Up @@ -786,6 +798,9 @@
>>> __debug__: int
Traceback (most recent call last):
SyntaxError: cannot assign to __debug__
>>> x.__debug__: int
Traceback (most recent call last):
SyntaxError: cannot assign to __debug__
>>> f(a=)
Traceback (most recent call last):
SyntaxError: expected argument value expression
Expand Down Expand Up @@ -1182,6 +1197,24 @@
Traceback (most recent call last):
SyntaxError: expected ':'

>>> match x:
... case a, __debug__, b:
... pass
Traceback (most recent call last):
SyntaxError: cannot assign to __debug__

>>> match x:
... case a, b, *__debug__:
... pass
Traceback (most recent call last):
SyntaxError: cannot assign to __debug__

>>> match x:
... case Foo(a, __debug__=1, b=2):
... pass
Traceback (most recent call last):
SyntaxError: cannot assign to __debug__

>>> if x = 3:
... pass
Traceback (most recent call last):
Expand Down Expand Up @@ -1275,6 +1308,15 @@
Traceback (most recent call last):
SyntaxError: expected 'except' or 'finally' block

Custom error message for __debug__ as exception variable

>>> try:
... pass
... except TypeError as __debug__:
... pass
Traceback (most recent call last):
SyntaxError: cannot assign to __debug__

Custom error message for try block mixing except and except*

>>> try:
Expand Down Expand Up @@ -1522,6 +1564,19 @@
Traceback (most recent call last):
IndentationError: expected an indented block after class definition on line 1

>>> class C(__debug__=42): ...
Traceback (most recent call last):
SyntaxError: cannot assign to __debug__

>>> class Meta(type):
... def __new__(*args, **kwargs):
... pass

>>> class C(metaclass=Meta, __debug__=42):
... pass
Traceback (most recent call last):
SyntaxError: cannot assign to __debug__

>>> match something:
... pass
Traceback (most recent call last):
Expand Down Expand Up @@ -1708,6 +1763,26 @@
Traceback (most recent call last):
SyntaxError: Did you mean to use 'from ... import ...' instead?

>>> import __debug__
Traceback (most recent call last):
SyntaxError: cannot assign to __debug__

>>> import a as __debug__
Traceback (most recent call last):
SyntaxError: cannot assign to __debug__

>>> import a.b.c as __debug__
Traceback (most recent call last):
SyntaxError: cannot assign to __debug__

>>> from a import __debug__
Traceback (most recent call last):
SyntaxError: cannot assign to __debug__

>>> from a import b as __debug__
Traceback (most recent call last):
SyntaxError: cannot assign to __debug__

# Check that we dont raise the "trailing comma" error if there is more
# input to the left of the valid part that we parsed.

Expand Down Expand Up @@ -2186,6 +2261,10 @@ def f(x: *b)
...
SyntaxError: yield expression cannot be used within a type alias

>>> type __debug__ = int
Traceback (most recent call last):
SyntaxError: cannot assign to __debug__

Comment on lines +2264 to +2267
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
>>> type __debug__ = int
Traceback (most recent call last):
SyntaxError: cannot assign to __debug__
>>> type __debug__ = int
Traceback (most recent call last):
SyntaxError: cannot assign to __debug__
>>> class A[__debug__]: pass
Traceback (most recent call last):
SyntaxError: cannot assign to __debug__

In a similar vein

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, should have waited a bit longer. I'll add this.

>>> class A[T]((x := 3)): ...
Traceback (most recent call last):
...
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Detection of writes to ``__debug__`` is moved from the compiler's codegen
stage to the symtable. This means that these errors now detected even in
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
stage to the symtable. This means that these errors now detected even in
stage to the symtable. This means that these errors are now detected even in

code that is optimized away before codegen (such as assertions with the
:option:`-O` command line option.)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
:option:`-O` command line option.)
:option:`-O` command line option).

76 changes: 0 additions & 76 deletions Python/compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -1954,55 +1954,6 @@ compiler_default_arguments(struct compiler *c, location loc,
return funcflags;
}

static bool
forbidden_name(struct compiler *c, location loc, identifier name,
expr_context_ty ctx)
{
if (ctx == Store && _PyUnicode_EqualToASCIIString(name, "__debug__")) {
compiler_error(c, loc, "cannot assign to __debug__");
return true;
}
if (ctx == Del && _PyUnicode_EqualToASCIIString(name, "__debug__")) {
compiler_error(c, loc, "cannot delete __debug__");
return true;
}
return false;
}

static int
compiler_check_debug_one_arg(struct compiler *c, arg_ty arg)
{
if (arg != NULL) {
if (forbidden_name(c, LOC(arg), arg->arg, Store)) {
return ERROR;
}
}
return SUCCESS;
}

static int
compiler_check_debug_args_seq(struct compiler *c, asdl_arg_seq *args)
{
if (args != NULL) {
for (Py_ssize_t i = 0, n = asdl_seq_LEN(args); i < n; i++) {
RETURN_IF_ERROR(
compiler_check_debug_one_arg(c, asdl_seq_GET(args, i)));
}
}
return SUCCESS;
}

static int
compiler_check_debug_args(struct compiler *c, arguments_ty args)
{
RETURN_IF_ERROR(compiler_check_debug_args_seq(c, args->posonlyargs));
RETURN_IF_ERROR(compiler_check_debug_args_seq(c, args->args));
RETURN_IF_ERROR(compiler_check_debug_one_arg(c, args->vararg));
RETURN_IF_ERROR(compiler_check_debug_args_seq(c, args->kwonlyargs));
RETURN_IF_ERROR(compiler_check_debug_one_arg(c, args->kwarg));
return SUCCESS;
}

static int
wrap_in_stopiteration_handler(struct compiler *c)
{
Expand Down Expand Up @@ -2267,7 +2218,6 @@ compiler_function(struct compiler *c, stmt_ty s, int is_async)
type_params = s->v.FunctionDef.type_params;
}

RETURN_IF_ERROR(compiler_check_debug_args(c, args));
RETURN_IF_ERROR(compiler_decorators(c, decos));

firstlineno = s->lineno;
Expand Down Expand Up @@ -2910,8 +2860,6 @@ compiler_lambda(struct compiler *c, expr_ty e)
arguments_ty args = e->v.Lambda.args;
assert(e->kind == Lambda_kind);

RETURN_IF_ERROR(compiler_check_debug_args(c, args));

location loc = LOC(e);
funcflags = compiler_default_arguments(c, loc, args);
if (funcflags == -1) {
Expand Down Expand Up @@ -4086,10 +4034,6 @@ compiler_nameop(struct compiler *c, location loc,
!_PyUnicode_EqualToASCIIString(name, "True") &&
!_PyUnicode_EqualToASCIIString(name, "False"));

if (forbidden_name(c, loc, name, ctx)) {
return ERROR;
}

mangled = compiler_maybe_mangle(c, name);
if (!mangled) {
return ERROR;
Expand Down Expand Up @@ -4878,10 +4822,6 @@ validate_keywords(struct compiler *c, asdl_keyword_seq *keywords)
if (key->arg == NULL) {
continue;
}
location loc = LOC(key);
if (forbidden_name(c, loc, key->arg, Store)) {
return ERROR;
}
for (Py_ssize_t j = i + 1; j < nkeywords; j++) {
keyword_ty other = ((keyword_ty)asdl_seq_GET(keywords, j));
if (other->arg && !PyUnicode_Compare(key->arg, other->arg)) {
Expand Down Expand Up @@ -6135,9 +6075,6 @@ compiler_visit_expr(struct compiler *c, expr_ty e)
ADDOP_NAME(c, loc, LOAD_ATTR, e->v.Attribute.attr, names);
break;
case Store:
if (forbidden_name(c, loc, e->v.Attribute.attr, e->v.Attribute.ctx)) {
return ERROR;
}
ADDOP_NAME(c, loc, STORE_ATTR, e->v.Attribute.attr, names);
break;
case Del:
Expand Down Expand Up @@ -6331,9 +6268,6 @@ compiler_annassign(struct compiler *c, stmt_ty s)
}
switch (targ->kind) {
case Name_kind:
if (forbidden_name(c, loc, targ->v.Name.id, Store)) {
return ERROR;
}
/* If we have a simple name in a module or class, store annotation. */
if (s->v.AnnAssign.simple &&
(c->u->u_scope_type == COMPILER_SCOPE_MODULE ||
Expand Down Expand Up @@ -6365,9 +6299,6 @@ compiler_annassign(struct compiler *c, stmt_ty s)
}
break;
case Attribute_kind:
if (forbidden_name(c, loc, targ->v.Attribute.attr, Store)) {
return ERROR;
}
if (!s->v.AnnAssign.value &&
check_ann_expr(c, targ->v.Attribute.value) < 0) {
return ERROR;
Expand Down Expand Up @@ -6631,9 +6562,6 @@ pattern_helper_store_name(struct compiler *c, location loc,
ADDOP(c, loc, POP_TOP);
return SUCCESS;
}
if (forbidden_name(c, loc, n, Store)) {
return ERROR;
}
// Can't assign to the same name twice:
int duplicate = PySequence_Contains(pc->stores, n);
RETURN_IF_ERROR(duplicate);
Expand Down Expand Up @@ -6791,10 +6719,6 @@ validate_kwd_attrs(struct compiler *c, asdl_identifier_seq *attrs, asdl_pattern_
Py_ssize_t nattrs = asdl_seq_LEN(attrs);
for (Py_ssize_t i = 0; i < nattrs; i++) {
identifier attr = ((identifier)asdl_seq_GET(attrs, i));
location loc = LOC((pattern_ty) asdl_seq_GET(patterns, i));
if (forbidden_name(c, loc, attr, Store)) {
return ERROR;
}
for (Py_ssize_t j = i + 1; j < nattrs; j++) {
identifier other = ((identifier)asdl_seq_GET(attrs, j));
if (!PyUnicode_Compare(attr, other)) {
Expand Down
Loading
Loading