mirror of
https://github.com/python/cpython
synced 2024-09-16 03:29:57 +00:00
bpo-46903: Handle str-subclasses in virtual instance dictionaries. (GH-31658)
This commit is contained in:
parent
8f31bf4698
commit
03c2a36b2b
|
@ -398,6 +398,7 @@ typedef struct _object_stats {
|
|||
uint64_t dict_materialized_on_request;
|
||||
uint64_t dict_materialized_new_key;
|
||||
uint64_t dict_materialized_too_big;
|
||||
uint64_t dict_materialized_str_subclass;
|
||||
} ObjectStats;
|
||||
|
||||
typedef struct _stats {
|
||||
|
|
|
@ -3044,6 +3044,30 @@ def split(name):
|
|||
]])
|
||||
self.assertRaises(TypeError, _string.formatter_field_name_split, 1)
|
||||
|
||||
def test_str_subclass_attr(self):
|
||||
|
||||
name = StrSubclass("name")
|
||||
name2 = StrSubclass("name2")
|
||||
class Bag:
|
||||
pass
|
||||
|
||||
o = Bag()
|
||||
with self.assertRaises(AttributeError):
|
||||
delattr(o, name)
|
||||
setattr(o, name, 1)
|
||||
self.assertEquals(o.name, 1)
|
||||
o.name = 2
|
||||
self.assertEquals(list(o.__dict__), [name])
|
||||
|
||||
with self.assertRaises(AttributeError):
|
||||
delattr(o, name2)
|
||||
with self.assertRaises(AttributeError):
|
||||
del o.name2
|
||||
setattr(o, name2, 3)
|
||||
self.assertEquals(o.name2, 3)
|
||||
o.name2 = 4
|
||||
self.assertEquals(list(o.__dict__), [name, name2])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
Make sure that str subclasses can be used as attribute names for instances
|
||||
with virtual dictionaries. Fixes regression in 3.11alpha
|
|
@ -5427,24 +5427,27 @@ int
|
|||
_PyObject_StoreInstanceAttribute(PyObject *obj, PyDictValues *values,
|
||||
PyObject *name, PyObject *value)
|
||||
{
|
||||
assert(PyUnicode_CheckExact(name));
|
||||
PyDictKeysObject *keys = CACHED_KEYS(Py_TYPE(obj));
|
||||
assert(keys != NULL);
|
||||
assert(values != NULL);
|
||||
assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
|
||||
Py_ssize_t ix = insert_into_dictkeys(keys, name);
|
||||
if (ix == DKIX_EMPTY) {
|
||||
if (value == NULL) {
|
||||
PyErr_SetObject(PyExc_AttributeError, name);
|
||||
return -1;
|
||||
Py_ssize_t ix = DKIX_EMPTY;
|
||||
if (PyUnicode_CheckExact(name)) {
|
||||
ix = insert_into_dictkeys(keys, name);
|
||||
}
|
||||
if (ix == DKIX_EMPTY) {
|
||||
#ifdef Py_STATS
|
||||
if (PyUnicode_CheckExact(name)) {
|
||||
if (shared_keys_usable_size(keys) == SHARED_KEYS_MAX_SIZE) {
|
||||
OBJECT_STAT_INC(dict_materialized_too_big);
|
||||
}
|
||||
else {
|
||||
OBJECT_STAT_INC(dict_materialized_new_key);
|
||||
}
|
||||
}
|
||||
else {
|
||||
OBJECT_STAT_INC(dict_materialized_str_subclass);
|
||||
}
|
||||
#endif
|
||||
PyObject *dict = make_dict_from_instance_attributes(keys, values);
|
||||
if (dict == NULL) {
|
||||
|
@ -5452,8 +5455,13 @@ _PyObject_StoreInstanceAttribute(PyObject *obj, PyDictValues *values,
|
|||
}
|
||||
*_PyObject_ValuesPointer(obj) = NULL;
|
||||
*_PyObject_ManagedDictPointer(obj) = dict;
|
||||
if (value == NULL) {
|
||||
return PyDict_DelItem(dict, name);
|
||||
}
|
||||
else {
|
||||
return PyDict_SetItem(dict, name, value);
|
||||
}
|
||||
}
|
||||
PyObject *old_value = values->values[ix];
|
||||
Py_XINCREF(value);
|
||||
values->values[ix] = value;
|
||||
|
|
|
@ -221,6 +221,7 @@ print_object_stats(FILE *out, ObjectStats *stats)
|
|||
fprintf(out, "Object materialize dict (on request): %" PRIu64 "\n", stats->dict_materialized_on_request);
|
||||
fprintf(out, "Object materialize dict (new key): %" PRIu64 "\n", stats->dict_materialized_new_key);
|
||||
fprintf(out, "Object materialize dict (too big): %" PRIu64 "\n", stats->dict_materialized_too_big);
|
||||
fprintf(out, "Object materialize dict (str subclass): %" PRIu64 "\n", stats->dict_materialized_str_subclass);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
Loading…
Reference in a new issue