diff options
author | Mark Shannon <mark@hotpy.org> | 2022-03-04 11:31:29 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-03-04 11:31:29 (GMT) |
commit | 03c2a36b2bd2d4469160d1607619ee144175d753 (patch) | |
tree | 828a98bada6b84ea722ac104da3b7e891f44d4cd | |
parent | 8f31bf46980956c735dd18f9914f3e7144e87c77 (diff) | |
download | cpython-03c2a36b2bd2d4469160d1607619ee144175d753.zip cpython-03c2a36b2bd2d4469160d1607619ee144175d753.tar.gz cpython-03c2a36b2bd2d4469160d1607619ee144175d753.tar.bz2 |
bpo-46903: Handle str-subclasses in virtual instance dictionaries. (GH-31658)
-rw-r--r-- | Include/internal/pycore_code.h | 1 | ||||
-rw-r--r-- | Lib/test/test_unicode.py | 24 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Core and Builtins/2022-03-03-12-02-41.bpo-46903.OzgaFZ.rst | 2 | ||||
-rw-r--r-- | Objects/dictobject.c | 28 | ||||
-rw-r--r-- | Python/specialize.c | 1 |
5 files changed, 46 insertions, 10 deletions
diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index 25c31a1..2e03358 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -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 { diff --git a/Lib/test/test_unicode.py b/Lib/test/test_unicode.py index 8e4e648..e97f971 100644 --- a/Lib/test/test_unicode.py +++ b/Lib/test/test_unicode.py @@ -3044,6 +3044,30 @@ class StringModuleTest(unittest.TestCase): ]]) 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() diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-03-03-12-02-41.bpo-46903.OzgaFZ.rst b/Misc/NEWS.d/next/Core and Builtins/2022-03-03-12-02-41.bpo-46903.OzgaFZ.rst new file mode 100644 index 0000000..f6120ef --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-03-03-12-02-41.bpo-46903.OzgaFZ.rst @@ -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 diff --git a/Objects/dictobject.c b/Objects/dictobject.c index d8bf164..635a738 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -5427,23 +5427,26 @@ 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); + Py_ssize_t ix = DKIX_EMPTY; + if (PyUnicode_CheckExact(name)) { + ix = insert_into_dictkeys(keys, name); + } if (ix == DKIX_EMPTY) { - if (value == NULL) { - PyErr_SetObject(PyExc_AttributeError, name); - return -1; - } #ifdef Py_STATS - if (shared_keys_usable_size(keys) == SHARED_KEYS_MAX_SIZE) { - OBJECT_STAT_INC(dict_materialized_too_big); + 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_new_key); + OBJECT_STAT_INC(dict_materialized_str_subclass); } #endif PyObject *dict = make_dict_from_instance_attributes(keys, values); @@ -5452,7 +5455,12 @@ _PyObject_StoreInstanceAttribute(PyObject *obj, PyDictValues *values, } *_PyObject_ValuesPointer(obj) = NULL; *_PyObject_ManagedDictPointer(obj) = dict; - return PyDict_SetItem(dict, name, value); + if (value == NULL) { + return PyDict_DelItem(dict, name); + } + else { + return PyDict_SetItem(dict, name, value); + } } PyObject *old_value = values->values[ix]; Py_XINCREF(value); diff --git a/Python/specialize.c b/Python/specialize.c index 66dce8c..912b9e2 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -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 |