summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Shannon <mark@hotpy.org>2022-03-04 11:31:29 (GMT)
committerGitHub <noreply@github.com>2022-03-04 11:31:29 (GMT)
commit03c2a36b2bd2d4469160d1607619ee144175d753 (patch)
tree828a98bada6b84ea722ac104da3b7e891f44d4cd
parent8f31bf46980956c735dd18f9914f3e7144e87c77 (diff)
downloadcpython-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.h1
-rw-r--r--Lib/test/test_unicode.py24
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2022-03-03-12-02-41.bpo-46903.OzgaFZ.rst2
-rw-r--r--Objects/dictobject.c28
-rw-r--r--Python/specialize.c1
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