Skip to content

Commit 92f5a27

Browse files
committed
bpo-33007: Name-mangled private methods don't roundtrip when pickling
1 parent 568fb0f commit 92f5a27

File tree

3 files changed

+30
-1
lines changed

3 files changed

+30
-1
lines changed

Lib/test/pickletester.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,15 @@ def __reduce__(self):
150150
# Shouldn't support the recursion itself
151151
return K, (self.value,)
152152

153+
154+
class L:
155+
def __init__(self):
156+
self.attr = self.__foo
157+
158+
def __foo(self):
159+
return 42
160+
161+
153162
import __main__
154163
__main__.C = C
155164
C.__module__ = "__main__"
@@ -2249,6 +2258,13 @@ def test_many_puts_and_gets(self):
22492258
loaded = self.loads(dumped)
22502259
self.assert_is_copy(obj, loaded)
22512260

2261+
def test_mangled_private_methods_roundtrip(self):
2262+
obj = L()
2263+
for proto in protocols:
2264+
with self.subTest(proto=proto):
2265+
loaded = self.loads(self.dumps(obj, proto))
2266+
self.assertEqual(loaded.attr(), 42)
2267+
22522268
def test_attribute_name_interning(self):
22532269
# Test that attribute names of pickled objects are interned when
22542270
# unpickling.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
The :mod:`pickle` module now properly handles name-mangled private methods.

Objects/classobject.c

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,20 @@ method_reduce(PyMethodObject *im, PyObject *Py_UNUSED(ignored))
128128
if (funcname == NULL) {
129129
return NULL;
130130
}
131+
PyObject *name = _PyObject_GetAttrId((PyObject *)Py_TYPE(self),
132+
&PyId___name__);
133+
if (name == NULL) {
134+
Py_DECREF(funcname);
135+
return NULL;
136+
}
137+
PyObject *mangled = _Py_Mangle(name, funcname);
138+
Py_DECREF(name);
139+
Py_DECREF(funcname);
140+
if (mangled == NULL) {
141+
return NULL;
142+
}
131143
return Py_BuildValue("N(ON)", _PyEval_GetBuiltinId(&PyId_getattr),
132-
self, funcname);
144+
self, mangled);
133145
}
134146

135147
static PyMethodDef method_methods[] = {

0 commit comments

Comments
 (0)