Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

gh-104374: Remove access to class scopes for inlined comprehensions#104528

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
JelleZijlstra merged 10 commits intopython:mainfromJelleZijlstra:classcomp2
May 18, 2023
Merged
Show file tree
Hide file tree
Changes from1 commit
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
21 changes: 19 additions & 2 deletionsLib/test/test_listcomps.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -200,7 +200,7 @@ def f():
y = [g for x in [1]]
"""
outputs = {"y": [2]}
self._check_in_scopes(code, outputs)
self._check_in_scopes(code, outputs, scopes=["module", "function"])

def test_inner_cell_shadows_outer_redefined(self):
code = """
Expand DownExpand Up@@ -328,7 +328,7 @@ def test_nested_2(self):
y = [x for [x ** x for x in range(x)][x - 1] in l]
"""
outputs = {"y": [3, 3, 3]}
self._check_in_scopes(code, outputs)
self._check_in_scopes(code, outputs, scopes=["module", "function"])

def test_nested_3(self):
code = """
Expand DownExpand Up@@ -379,6 +379,23 @@ def f():
with self.assertRaises(UnboundLocalError):
f()

def test_unbound_local_in_class_scope(self):
class X:
y = 1
with self.assertRaises(NameError):
[x + y for x in range(2)]

def test_comprehension_in_class_scope(self):
code = """
y = 1
class X:
y = 2
vals = [(x, y) for x in range(2)]
vals = X.vals
"""
self._check_in_scopes(code, {"vals": [(0, 1), (1, 1)]},
scopes=["module", "fucntion"])


__test__ = {'doctests' : doctests}

Expand Down
22 changes: 17 additions & 5 deletionsPython/compile.c
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -388,6 +388,8 @@ struct compiler_unit {
instr_sequence u_instr_sequence; /* codegen output */

int u_nfblocks;
int u_in_inlined_comp;

struct fblockinfo u_fblock[CO_MAXBLOCKS];

_PyCompile_CodeUnitMetadata u_metadata;
Expand DownExpand Up@@ -1290,6 +1292,7 @@ compiler_enter_scope(struct compiler *c, identifier name,
}

u->u_nfblocks = 0;
u->u_in_inlined_comp = 0;
u->u_metadata.u_firstlineno = lineno;
u->u_metadata.u_consts = PyDict_New();
if (!u->u_metadata.u_consts) {
Expand DownExpand Up@@ -4137,7 +4140,7 @@ compiler_nameop(struct compiler *c, location loc,
case OP_DEREF:
switch (ctx) {
case Load:
if (c->u->u_ste->ste_type == ClassBlock) {
if (c->u->u_ste->ste_type == ClassBlock && !c->u->u_in_inlined_comp) {
op = LOAD_FROM_DICT_OR_DEREF;
// First load the locals
if (codegen_addop_noarg(INSTR_SEQUENCE(c), LOAD_LOCALS, loc) < 0) {
Expand DownExpand Up@@ -4188,7 +4191,12 @@ compiler_nameop(struct compiler *c, location loc,
break;
case OP_NAME:
switch (ctx) {
case Load: op = LOAD_NAME; break;
case Load:
op = (c->u->u_ste->ste_type == ClassBlock
&& c->u->u_in_inlined_comp)
? LOAD_GLOBAL
: LOAD_NAME;
break;
case Store: op = STORE_NAME; break;
case Del: op = DELETE_NAME; break;
}
Expand DownExpand Up@@ -5413,6 +5421,7 @@ push_inlined_comprehension_state(struct compiler *c, location loc,
PySTEntryObject *entry,
inlined_comprehension_state *state)
{
c->u->u_in_inlined_comp++;
// iterate over names bound in the comprehension and ensure we isolate
// them from the outer scope as needed
PyObject *k, *v;
Expand All@@ -5424,7 +5433,7 @@ push_inlined_comprehension_state(struct compiler *c, location loc,
// at all; DEF_LOCAL | DEF_NONLOCAL can occur in the case of an
// assignment expression to a nonlocal in the comprehension, these don't
// need handling here since they shouldn't be isolated
if (symbol & DEF_LOCAL && !(symbol & DEF_NONLOCAL)) {
if ((symbol & DEF_LOCAL && !(symbol & DEF_NONLOCAL)) || c->u->u_ste->ste_type == ClassBlock) {
if (!_PyST_IsFunctionLike(c->u->u_ste)) {
// non-function scope: override this name to use fast locals
PyObject *orig = PyDict_GetItem(c->u->u_metadata.u_fasthidden, k);
Expand All@@ -5444,10 +5453,12 @@ push_inlined_comprehension_state(struct compiler *c, location loc,
}
}
long scope = (symbol >> SCOPE_OFFSET) & SCOPE_MASK;
if (scope == FREE && c->u->u_ste->ste_type == ClassBlock) {
dict_add_o(c->u->u_metadata.u_freevars, k);
}
PyObject *outv = PyDict_GetItemWithError(c->u->u_ste->ste_symbols, k);
if (outv == NULL) {
assert(PyErr_Occurred());
return ERROR;
outv = _PyLong_GetZero();
}
assert(PyLong_Check(outv));
long outsc = (PyLong_AS_LONG(outv) >> SCOPE_OFFSET) & SCOPE_MASK;
Expand DownExpand Up@@ -5521,6 +5532,7 @@ static int
pop_inlined_comprehension_state(struct compiler *c, location loc,
inlined_comprehension_state state)
{
c->u->u_in_inlined_comp--;
PyObject *k, *v;
Py_ssize_t pos = 0;
if (state.temp_symbols) {
Expand Down
5 changes: 3 additions & 2 deletionsPython/symtable.c
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -674,8 +674,9 @@ inline_comprehension(PySTEntryObject *ste, PySTEntryObject *comp,
}

// free vars in comprehension that are locals in outer scope can
// now simply be locals, unless they are free in comp children
if (!is_free_in_any_child(comp, k)) {
// now simply be locals, unless they are free in comp children,
// or if the outer scope is a class block
if (!is_free_in_any_child(comp, k) && ste->ste_type != ClassBlock) {
if (PySet_Discard(comp_free, k) < 0) {
return 0;
}
Expand Down

[8]ページ先頭

©2009-2025 Movatter.jp