File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -1195,7 +1195,15 @@ def load_build(self):
11951195 if isinstance (state , tuple ) and len (state ) == 2 :
11961196 state , slotstate = state
11971197 if state :
1198- inst .__dict__ .update (state )
1198+ d = inst .__dict__
1199+ intern = sys .intern
1200+ try :
1201+ for k , v in state .items ():
1202+ d [intern (k )] = v
1203+ # keys in state don't have to be strings
1204+ # don't blow up, but don't go out of our way
1205+ except TypeError :
1206+ d .update (state )
11991207 if slotstate :
12001208 for k , v in slotstate .items ():
12011209 setattr (inst , k , v )
Original file line number Diff line number Diff line change @@ -932,6 +932,20 @@ def test_many_puts_and_gets(self):
932932 "Failed protocol %d: %r != %r"
933933 % (proto , obj , loaded ))
934934
935+ def test_attribute_name_interning (self ):
936+ # Test that attribute names of pickled objects are interned when
937+ # unpickling.
938+ for proto in protocols :
939+ x = C ()
940+ x .foo = 42
941+ x .bar = "hello"
942+ s = self .dumps (x , proto )
943+ y = self .loads (s )
944+ x_keys = sorted (x .__dict__ )
945+ y_keys = sorted (y .__dict__ )
946+ for x_key , y_key in zip (x_keys , y_keys ):
947+ self .assertIs (x_key , y_key )
948+
935949# Test classes for reduce_ex
936950
937951class REX_one (object ):
Original file line number Diff line number Diff line change @@ -109,6 +109,10 @@ Installation
109109Library
110110-------
111111
112+ - Issue #5084: unpickling now interns the attribute names of pickled objects,
113+ saving memory and avoiding growth in size of subsequent pickles. Proposal
114+ and original patch by Jake McGuire.
115+
112116- The json module now works exclusively with str and not bytes.
113117
114118- Issue #3959: The ipaddr module has been added to the standard library.
Original file line number Diff line number Diff line change @@ -4020,6 +4020,8 @@ load_build(UnpicklerObject *self)
40204020 /* Set inst.__dict__ from the state dict (if any). */
40214021 if (state != Py_None ) {
40224022 PyObject * dict ;
4023+ PyObject * d_key , * d_value ;
4024+ Py_ssize_t i ;
40234025
40244026 if (!PyDict_Check (state )) {
40254027 PyErr_SetString (UnpicklingError , "state is not a dictionary" );
@@ -4029,7 +4031,19 @@ load_build(UnpicklerObject *self)
40294031 if (dict == NULL )
40304032 goto error ;
40314033
4032- PyDict_Update (dict , state );
4034+ i = 0 ;
4035+ while (PyDict_Next (state , & i , & d_key , & d_value )) {
4036+ /* normally the keys for instance attributes are
4037+ interned. we should try to do that here. */
4038+ Py_INCREF (d_key );
4039+ if (PyUnicode_CheckExact (d_key ))
4040+ PyUnicode_InternInPlace (& d_key );
4041+ if (PyObject_SetItem (dict , d_key , d_value ) < 0 ) {
4042+ Py_DECREF (d_key );
4043+ goto error ;
4044+ }
4045+ Py_DECREF (d_key );
4046+ }
40334047 Py_DECREF (dict );
40344048 }
40354049
You can’t perform that action at this time.
0 commit comments