mirror of
https://github.com/python/cpython.git
synced 2025-11-13 07:26:31 +00:00
Subclasses of string can no longer be interned. The semantics of
interning were not clear here -- a subclass could be mutable, for example -- and had bugs. Explicitly interning a subclass of string via intern() will raise a TypeError. Internal operations that attempt to intern a string subclass will have no effect. Added a few tests to test_builtin that includes the old buggy code and verifies that calls like PyObject_SetAttr() don't fail. Perhaps these tests should have gone in test_string.
This commit is contained in:
parent
cbd81556bb
commit
4c989ddc9c
4 changed files with 40 additions and 22 deletions
|
|
@ -608,6 +608,23 @@ class BuiltinTest(unittest.TestCase):
|
||||||
s2 = s.swapcase().swapcase()
|
s2 = s.swapcase().swapcase()
|
||||||
self.assert_(intern(s2) is s)
|
self.assert_(intern(s2) is s)
|
||||||
|
|
||||||
|
# Subclasses of string can't be interned, because they
|
||||||
|
# provide too much opportunity for insane things to happen.
|
||||||
|
# We don't want them in the interned dict and if they aren't
|
||||||
|
# actually interned, we don't want to create the appearance
|
||||||
|
# that they are by allowing intern() to succeeed.
|
||||||
|
class S(str):
|
||||||
|
def __hash__(self):
|
||||||
|
return 123
|
||||||
|
|
||||||
|
self.assertRaises(TypeError, intern, S("abc"))
|
||||||
|
|
||||||
|
# It's still safe to pass these strings to routines that
|
||||||
|
# call intern internally, e.g. PyObject_SetAttr().
|
||||||
|
s = S("abc")
|
||||||
|
setattr(s, s, s)
|
||||||
|
self.assertEqual(getattr(s, s), s)
|
||||||
|
|
||||||
def test_iter(self):
|
def test_iter(self):
|
||||||
self.assertRaises(TypeError, iter)
|
self.assertRaises(TypeError, iter)
|
||||||
self.assertRaises(TypeError, iter, 42, 42)
|
self.assertRaises(TypeError, iter, 42, 42)
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,12 @@ What's New in Python 2.4 alpha 3?
|
||||||
Core and builtins
|
Core and builtins
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
Subclasses of string can no longer be interned. The semantics of
|
||||||
|
interning were not clear here -- a subclass could be mutable, for
|
||||||
|
example -- and had bugs. Explicitly interning a subclass of string
|
||||||
|
via intern() will raise a TypeError. Internal operations that attempt
|
||||||
|
to intern a string subclass will have no effect.
|
||||||
|
|
||||||
Extension modules
|
Extension modules
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4313,6 +4313,10 @@ PyString_InternInPlace(PyObject **p)
|
||||||
PyObject *t;
|
PyObject *t;
|
||||||
if (s == NULL || !PyString_Check(s))
|
if (s == NULL || !PyString_Check(s))
|
||||||
Py_FatalError("PyString_InternInPlace: strings only please!");
|
Py_FatalError("PyString_InternInPlace: strings only please!");
|
||||||
|
/* If it's a string subclass, we don't really know what putting
|
||||||
|
it in the interned dict might do. */
|
||||||
|
if (!PyString_CheckExact(s))
|
||||||
|
return;
|
||||||
if (PyString_CHECK_INTERNED(s))
|
if (PyString_CHECK_INTERNED(s))
|
||||||
return;
|
return;
|
||||||
if (interned == NULL) {
|
if (interned == NULL) {
|
||||||
|
|
@ -4322,36 +4326,22 @@ PyString_InternInPlace(PyObject **p)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((t = PyDict_GetItem(interned, (PyObject *)s)) != NULL) {
|
t = PyDict_GetItem(interned, (PyObject *)s);
|
||||||
|
if (t) {
|
||||||
Py_INCREF(t);
|
Py_INCREF(t);
|
||||||
Py_DECREF(*p);
|
Py_DECREF(*p);
|
||||||
*p = t;
|
*p = t;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/* Ensure that only true string objects appear in the intern dict */
|
|
||||||
if (!PyString_CheckExact(s)) {
|
|
||||||
t = PyString_FromStringAndSize(PyString_AS_STRING(s),
|
|
||||||
PyString_GET_SIZE(s));
|
|
||||||
if (t == NULL) {
|
|
||||||
PyErr_Clear();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
t = (PyObject*) s;
|
|
||||||
Py_INCREF(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (PyDict_SetItem(interned, t, t) == 0) {
|
if (PyDict_SetItem(interned, s, s) < 0) {
|
||||||
/* The two references in interned are not counted by
|
PyErr_Clear();
|
||||||
refcnt. The string deallocator will take care of this */
|
|
||||||
((PyObject *)t)->ob_refcnt-=2;
|
|
||||||
PyString_CHECK_INTERNED(t) = SSTATE_INTERNED_MORTAL;
|
|
||||||
Py_DECREF(*p);
|
|
||||||
*p = t;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Py_DECREF(t);
|
/* The two references in interned are not counted by refcnt.
|
||||||
PyErr_Clear();
|
The string deallocator will take care of this */
|
||||||
|
(*p)->ob_refcnt -= 2;
|
||||||
|
PyString_CHECK_INTERNED(s) = SSTATE_INTERNED_MORTAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
||||||
|
|
@ -1035,6 +1035,11 @@ builtin_intern(PyObject *self, PyObject *args)
|
||||||
PyObject *s;
|
PyObject *s;
|
||||||
if (!PyArg_ParseTuple(args, "S:intern", &s))
|
if (!PyArg_ParseTuple(args, "S:intern", &s))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
if (!PyString_CheckExact(s)) {
|
||||||
|
PyErr_SetString(PyExc_TypeError,
|
||||||
|
"can't intern subclass of string");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
Py_INCREF(s);
|
Py_INCREF(s);
|
||||||
PyString_InternInPlace(&s);
|
PyString_InternInPlace(&s);
|
||||||
return s;
|
return s;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue