Skip to content

Commit

Permalink
pythongh-122445: populate only modified fields in __static_attributes…
Browse files Browse the repository at this point in the history
…__ (python#122446)

(cherry picked from commit 498376d)
  • Loading branch information
iritkatriel committed Aug 2, 2024
1 parent b20893b commit dbcd947
Show file tree
Hide file tree
Showing 5 changed files with 22 additions and 18 deletions.
2 changes: 1 addition & 1 deletion Doc/reference/datamodel.rst
Original file line number Diff line number Diff line change
Expand Up @@ -996,7 +996,7 @@ Special attributes:
a :ref:`generic class <generic-classes>`.

:attr:`~class.__static_attributes__`
A tuple containing names of attributes of this class which are accessed
A tuple containing names of attributes of this class which are assigned
through ``self.X`` from any function in its body.

:attr:`__firstlineno__`
Expand Down
2 changes: 1 addition & 1 deletion Doc/whatsnew/3.13.rst
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ Improved Error Messages
TypeError: split() got an unexpected keyword argument 'max_split'. Did you mean 'maxsplit'?

* Classes have a new :attr:`~class.__static_attributes__` attribute, populated by the compiler,
with a tuple of names of attributes of this class which are accessed
with a tuple of names of attributes of this class which are assigned
through ``self.X`` from any function in its body. (Contributed by Irit Katriel
in :gh:`115775`.)

Expand Down
5 changes: 4 additions & 1 deletion Lib/test/test_compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -2056,12 +2056,15 @@ def test_lambda_return_position(self):
self.assertLessEqual(end_col, code_end)


class TestExpectedAttributes(unittest.TestCase):
class TestStaticAttributes(unittest.TestCase):

def test_basic(self):
class C:
def f(self):
self.a = self.b = 42
# read fields are not included
self.f()
self.arr[3]

self.assertIsInstance(C.__static_attributes__, tuple)
self.assertEqual(sorted(C.__static_attributes__), ['a', 'b'])
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add only fields which are modified via self.* to :attr:`~class.__static_attributes__`.
30 changes: 15 additions & 15 deletions Python/compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -589,20 +589,30 @@ compiler_unit_free(struct compiler_unit *u)
PyMem_Free(u);
}

static struct compiler_unit *
get_class_compiler_unit(struct compiler *c)
static int
compiler_maybe_add_static_attribute_to_class(struct compiler *c, expr_ty e)
{
assert(e->kind == Attribute_kind);
expr_ty attr_value = e->v.Attribute.value;
if (attr_value->kind != Name_kind ||
e->v.Attribute.ctx != Store ||
!_PyUnicode_EqualToASCIIString(attr_value->v.Name.id, "self"))
{
return SUCCESS;
}
Py_ssize_t stack_size = PyList_GET_SIZE(c->c_stack);
for (Py_ssize_t i = stack_size - 1; i >= 0; i--) {
PyObject *capsule = PyList_GET_ITEM(c->c_stack, i);
struct compiler_unit *u = (struct compiler_unit *)PyCapsule_GetPointer(
capsule, CAPSULE_NAME);
assert(u);
if (u->u_scope_type == COMPILER_SCOPE_CLASS) {
return u;
assert(u->u_static_attributes);
RETURN_IF_ERROR(PySet_Add(u->u_static_attributes, e->v.Attribute.attr));
break;
}
}
return NULL;
return SUCCESS;
}

static int
Expand Down Expand Up @@ -6283,17 +6293,7 @@ compiler_visit_expr1(struct compiler *c, expr_ty e)
ADDOP(c, loc, NOP);
return SUCCESS;
}
if (e->v.Attribute.value->kind == Name_kind &&
_PyUnicode_EqualToASCIIString(e->v.Attribute.value->v.Name.id, "self"))
{
struct compiler_unit *class_u = get_class_compiler_unit(c);
if (class_u != NULL) {
assert(class_u->u_scope_type == COMPILER_SCOPE_CLASS);
assert(class_u->u_static_attributes);
RETURN_IF_ERROR(
PySet_Add(class_u->u_static_attributes, e->v.Attribute.attr));
}
}
RETURN_IF_ERROR(compiler_maybe_add_static_attribute_to_class(c, e));
VISIT(c, expr, e->v.Attribute.value);
loc = LOC(e);
loc = update_start_location_to_match_attr(c, loc, e);
Expand Down

0 comments on commit dbcd947

Please sign in to comment.