FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

gh-115775: Compiler adds __static_attributes__ field to classes by iritkatriel · Pull Request #115913 · python/cpython · GitHub

/ cpython Public
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension .c  (1) .h  (4) .py  (7) .rst  (1) All 4 file types selected
Viewed files
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Unified
Split
Hide whitespace
Diff view
Unified
Split
Hide whitespace

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Include/internal/pycore_global_strings.h
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ struct _Py_global_strings {
STRUCT_FOR_ID(__slotnames__)
STRUCT_FOR_ID(__slots__)
STRUCT_FOR_ID(__spec__)
STRUCT_FOR_ID(__static_attributes__)
STRUCT_FOR_ID(__str__)
STRUCT_FOR_ID(__sub__)
STRUCT_FOR_ID(__subclasscheck__)
Expand Down
1 change: 1 addition & 0 deletions Include/internal/pycore_runtime_init_generated.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Include/internal/pycore_unicodeobject_generated.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Lib/enum.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -2018,7 +2018,8 @@ def _test_simple_enum(checked_enum, simple_enum):
+ list(simple_enum._member_map_.keys())
)
for key in set(checked_keys + simple_keys):
if key in ('__module__', '_member_map_', '_value2member_map_', '__doc__'):
if key in ('__module__', '_member_map_', '_value2member_map_', '__doc__',
'__static_attributes__'):
# keys known to be different, or very long
continue
elif key in member_names:
Expand Down
3 changes: 2 additions & 1 deletion Lib/pydoc.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,8 @@ def visiblename(name, all=None, obj=None):
if name in {'__author__', '__builtins__', '__cached__', '__credits__',
'__date__', '__doc__', '__file__', '__spec__',
'__loader__', '__module__', '__name__', '__package__',
'__path__', '__qualname__', '__slots__', '__version__'}:
'__path__', '__qualname__', '__slots__', '__version__',
'__static_attributes__'}:
return 0
# Private names are hidden, but special names are displayed.
if name.startswith('__') and name.endswith('__'): return 1
Expand Down
58 changes: 58 additions & 0 deletions Lib/test/test_compile.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -1960,6 +1960,64 @@ def test_load_super_attr(self):
)


class TestExpectedAttributes(unittest.TestCase):

def test_basic(self):
class C:
def f(self):
self.a = self.b = 42

self.assertIsInstance(C.__static_attributes__, tuple)
self.assertEqual(sorted(C.__static_attributes__), ['a', 'b'])

def test_nested_function(self):
class C:
def f(self):
self.x = 1
self.y = 2
self.x = 3 # check deduplication

def g(self, obj):
self.y = 4
self.z = 5

def h(self, a):
self.u = 6
self.v = 7

obj.self = 8

self.assertEqual(sorted(C.__static_attributes__), ['u', 'v', 'x', 'y', 'z'])

def test_nested_class(self):
class C:
def f(self):
self.x = 42
self.y = 42

class D:
def g(self):
self.y = 42
self.z = 42

self.assertEqual(sorted(C.__static_attributes__), ['x', 'y'])
self.assertEqual(sorted(C.D.__static_attributes__), ['y', 'z'])

def test_subclass(self):
class C:
def f(self):
self.x = 42
self.y = 42

class D(C):
def g(self):
self.y = 42
self.z = 42

self.assertEqual(sorted(C.__static_attributes__), ['x', 'y'])
self.assertEqual(sorted(D.__static_attributes__), ['y', 'z'])


class TestExpressionStackSize(unittest.TestCase):
# These tests check that the computed stack size for a code object
# stays within reasonable bounds (see issue #21523 for an example
Expand Down
8 changes: 5 additions & 3 deletions Lib/test/test_descr.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -5080,7 +5080,8 @@ def test_iter_keys(self):
keys = list(it)
keys.sort()
self.assertEqual(keys, ['__dict__', '__doc__', '__module__',
'__weakref__', 'meth'])
'__static_attributes__', '__weakref__',
'meth'])

@unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(),
'trace function introduces __local__')
Expand All @@ -5089,7 +5090,7 @@ def test_iter_values(self):
it = self.C.__dict__.values()
self.assertNotIsInstance(it, list)
values = list(it)
self.assertEqual(len(values), 5)
self.assertEqual(len(values), 6)

@unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(),
'trace function introduces __local__')
Expand All @@ -5100,7 +5101,8 @@ def test_iter_items(self):
keys = [item[0] for item in it]
keys.sort()
self.assertEqual(keys, ['__dict__', '__doc__', '__module__',
'__weakref__', 'meth'])
'__static_attributes__', '__weakref__',
'meth'])

def test_dict_type_with_metaclass(self):
# Testing type of __dict__ when metaclass set...
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_io.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -1160,7 +1160,7 @@ class APIMismatchTest(unittest.TestCase):
def test_RawIOBase_io_in_pyio_match(self):
"""Test that pyio RawIOBase class has all c RawIOBase methods"""
mismatch = support.detect_api_mismatch(pyio.RawIOBase, io.RawIOBase,
ignore=('__weakref__',))
ignore=('__weakref__', '__static_attributes__'))
self.assertEqual(mismatch, set(), msg='Python RawIOBase does not have all C RawIOBase methods')

def test_RawIOBase_pyio_in_io_match(self):
Expand Down
8 changes: 5 additions & 3 deletions Lib/test/test_metaclass.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@
d['foo'] = 4
d['foo'] = 42
d['bar'] = 123
d['__static_attributes__'] = ()
>>>

Use a metaclass that doesn't derive from type.
Expand All @@ -182,12 +183,12 @@
... b = 24
...
meta: C ()
ns: [('__module__', 'test.test_metaclass'), ('__qualname__', 'C'), ('a', 42), ('b', 24)]
ns: [('__module__', 'test.test_metaclass'), ('__qualname__', 'C'), ('__static_attributes__', ()), ('a', 42), ('b', 24)]
kw: []
>>> type(C) is dict
True
>>> print(sorted(C.items()))
[('__module__', 'test.test_metaclass'), ('__qualname__', 'C'), ('a', 42), ('b', 24)]
[('__module__', 'test.test_metaclass'), ('__qualname__', 'C'), ('__static_attributes__', ()), ('a', 42), ('b', 24)]
>>>

And again, with a __prepare__ attribute.
Expand All @@ -208,8 +209,9 @@
d['a'] = 1
d['a'] = 2
d['b'] = 3
d['__static_attributes__'] = ()
meta: C ()
ns: [('__module__', 'test.test_metaclass'), ('__qualname__', 'C'), ('a', 2), ('b', 3)]
ns: [('__module__', 'test.test_metaclass'), ('__qualname__', 'C'), ('__static_attributes__', ()), ('a', 2), ('b', 3)]
kw: [('other', 'booh')]
>>>

Expand Down
2 changes: 1 addition & 1 deletion Lib/typing.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -1717,7 +1717,7 @@ class _TypingEllipsis:
'__abstractmethods__', '__annotations__', '__dict__', '__doc__',
'__init__', '__module__', '__new__', '__slots__',
'__subclasshook__', '__weakref__', '__class_getitem__',
'__match_args__',
'__match_args__', '__static_attributes__',
})

# These special attributes will be not collected as protocol members.
Expand Down
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Compiler populates the new ``__static_attributes__`` field on a class with
the names of attributes of this class which are accessed through self.X from
any function in its body.
54 changes: 53 additions & 1 deletion Python/compile.c
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,8 @@ struct compiler_unit {

int u_scope_type;

PyObject *u_private; /* for private name mangling */
PyObject *u_private; /* for private name mangling */
PyObject *u_static_attributes; /* for class: attributes accessed via self.X */

instr_sequence u_instr_sequence; /* codegen output */

Expand Down Expand Up @@ -690,9 +691,26 @@ compiler_unit_free(struct compiler_unit *u)
Py_CLEAR(u->u_metadata.u_cellvars);
Py_CLEAR(u->u_metadata.u_fasthidden);
Py_CLEAR(u->u_private);
Py_CLEAR(u->u_static_attributes);
PyMem_Free(u);
}

static struct compiler_unit *
get_class_compiler_unit(struct compiler *c)
{
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;
}
}
return NULL;
}

static int
compiler_set_qualname(struct compiler *c)
{
Expand Down Expand Up @@ -1336,6 +1354,16 @@ compiler_enter_scope(struct compiler *c, identifier name,
}

u->u_private = NULL;
if (scope_type == COMPILER_SCOPE_CLASS) {
u->u_static_attributes = PySet_New(0);
if (!u->u_static_attributes) {
compiler_unit_free(u);
return ERROR;
}
}
else {
u->u_static_attributes = NULL;
}

/* Push the old compiler_unit on the stack. */
if (c->u) {
Expand Down Expand Up @@ -2517,6 +2545,18 @@ compiler_class_body(struct compiler *c, stmt_ty s, int firstlineno)
compiler_exit_scope(c);
return ERROR;
}
assert(c->u->u_static_attributes);
PyObject *static_attributes = PySequence_Tuple(c->u->u_static_attributes);
if (static_attributes == NULL) {
compiler_exit_scope(c);
return ERROR;
}
ADDOP_LOAD_CONST(c, NO_LOCATION, static_attributes);
Py_CLEAR(static_attributes);
if (compiler_nameop(c, NO_LOCATION, &_Py_ID(__static_attributes__), Store) < 0) {
compiler_exit_scope(c);
return ERROR;
}
/* The following code is artificial */
/* Set __classdictcell__ if necessary */
if (c->u->u_ste->ste_needs_classdict) {
Expand Down Expand Up @@ -2657,6 +2697,7 @@ compiler_class(struct compiler *c, stmt_ty s)
s->v.ClassDef.keywords));

PyCodeObject *co = optimize_and_assemble(c, 0);

compiler_exit_scope(c);
if (co == NULL) {
return ERROR;
Expand Down Expand Up @@ -6246,6 +6287,17 @@ 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));
}
}
VISIT(c, expr, e->v.Attribute.value);
loc = LOC(e);
loc = update_start_location_to_match_attr(c, loc, e);
Expand Down

Back | FazBrowse Home | New Git URL