Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 03c2a36

Browse files
authored
bpo-46903: Handle str-subclasses in virtual instance dictionaries. (GH-31658)
1 parent 8f31bf4 commit 03c2a36

5 files changed

Lines changed: 46 additions & 10 deletions

File tree

Include/internal/pycore_code.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,7 @@ typedef struct _object_stats {
398398
uint64_t dict_materialized_on_request;
399399
uint64_t dict_materialized_new_key;
400400
uint64_t dict_materialized_too_big;
401+
uint64_t dict_materialized_str_subclass;
401402
} ObjectStats;
402403

403404
typedef struct _stats {

Lib/test/test_unicode.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3044,6 +3044,30 @@ def split(name):
30443044
]])
30453045
self.assertRaises(TypeError, _string.formatter_field_name_split, 1)
30463046

3047+
def test_str_subclass_attr(self):
3048+
3049+
name = StrSubclass("name")
3050+
name2 = StrSubclass("name2")
3051+
class Bag:
3052+
pass
3053+
3054+
o = Bag()
3055+
with self.assertRaises(AttributeError):
3056+
delattr(o, name)
3057+
setattr(o, name, 1)
3058+
self.assertEquals(o.name, 1)
3059+
o.name = 2
3060+
self.assertEquals(list(o.__dict__), [name])
3061+
3062+
with self.assertRaises(AttributeError):
3063+
delattr(o, name2)
3064+
with self.assertRaises(AttributeError):
3065+
del o.name2
3066+
setattr(o, name2, 3)
3067+
self.assertEquals(o.name2, 3)
3068+
o.name2 = 4
3069+
self.assertEquals(list(o.__dict__), [name, name2])
3070+
30473071

30483072
if __name__ == "__main__":
30493073
unittest.main()
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Make sure that str subclasses can be used as attribute names for instances
2+
with virtual dictionaries. Fixes regression in 3.11alpha

Objects/dictobject.c

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5427,23 +5427,26 @@ int
54275427
_PyObject_StoreInstanceAttribute(PyObject *obj, PyDictValues *values,
54285428
PyObject *name, PyObject *value)
54295429
{
5430-
assert(PyUnicode_CheckExact(name));
54315430
PyDictKeysObject *keys = CACHED_KEYS(Py_TYPE(obj));
54325431
assert(keys != NULL);
54335432
assert(values != NULL);
54345433
assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
5435-
Py_ssize_t ix = insert_into_dictkeys(keys, name);
5434+
Py_ssize_t ix = DKIX_EMPTY;
5435+
if (PyUnicode_CheckExact(name)) {
5436+
ix = insert_into_dictkeys(keys, name);
5437+
}
54365438
if (ix == DKIX_EMPTY) {
5437-
if (value == NULL) {
5438-
PyErr_SetObject(PyExc_AttributeError, name);
5439-
return -1;
5440-
}
54415439
#ifdef Py_STATS
5442-
if (shared_keys_usable_size(keys) == SHARED_KEYS_MAX_SIZE) {
5443-
OBJECT_STAT_INC(dict_materialized_too_big);
5440+
if (PyUnicode_CheckExact(name)) {
5441+
if (shared_keys_usable_size(keys) == SHARED_KEYS_MAX_SIZE) {
5442+
OBJECT_STAT_INC(dict_materialized_too_big);
5443+
}
5444+
else {
5445+
OBJECT_STAT_INC(dict_materialized_new_key);
5446+
}
54445447
}
54455448
else {
5446-
OBJECT_STAT_INC(dict_materialized_new_key);
5449+
OBJECT_STAT_INC(dict_materialized_str_subclass);
54475450
}
54485451
#endif
54495452
PyObject *dict = make_dict_from_instance_attributes(keys, values);
@@ -5452,7 +5455,12 @@ _PyObject_StoreInstanceAttribute(PyObject *obj, PyDictValues *values,
54525455
}
54535456
*_PyObject_ValuesPointer(obj) = NULL;
54545457
*_PyObject_ManagedDictPointer(obj) = dict;
5455-
return PyDict_SetItem(dict, name, value);
5458+
if (value == NULL) {
5459+
return PyDict_DelItem(dict, name);
5460+
}
5461+
else {
5462+
return PyDict_SetItem(dict, name, value);
5463+
}
54565464
}
54575465
PyObject *old_value = values->values[ix];
54585466
Py_XINCREF(value);

Python/specialize.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ print_object_stats(FILE *out, ObjectStats *stats)
221221
fprintf(out, "Object materialize dict (on request): %" PRIu64 "\n", stats->dict_materialized_on_request);
222222
fprintf(out, "Object materialize dict (new key): %" PRIu64 "\n", stats->dict_materialized_new_key);
223223
fprintf(out, "Object materialize dict (too big): %" PRIu64 "\n", stats->dict_materialized_too_big);
224+
fprintf(out, "Object materialize dict (str subclass): %" PRIu64 "\n", stats->dict_materialized_str_subclass);
224225
}
225226

226227
static void

0 commit comments

Comments
 (0)