Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 5aac4e6

Browse files
committed
Move _better_reduce from copy.py to copy_reg.py, and also use it in
pickle.py, where it makes save_newobj() unnecessary. Tests pass.
1 parent e7ee17c commit 5aac4e6

3 files changed

Lines changed: 83 additions & 163 deletions

File tree

Lib/copy.py

Lines changed: 1 addition & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ class instances).
5151
# XXX need to support copy_reg here too...
5252

5353
import types
54-
from pickle import _slotnames
54+
from copy_reg import _better_reduce
5555

5656
class Error(Exception):
5757
pass
@@ -89,46 +89,6 @@ def copy(x):
8989
else:
9090
y = copierfunction(x)
9191
return y
92-
93-
def __newobj__(cls, *args):
94-
return cls.__new__(cls, *args)
95-
96-
def _better_reduce(obj):
97-
cls = obj.__class__
98-
getnewargs = getattr(obj, "__getnewargs__", None)
99-
if getnewargs:
100-
args = getnewargs()
101-
else:
102-
args = ()
103-
getstate = getattr(obj, "__getstate__", None)
104-
if getstate:
105-
try:
106-
state = getstate()
107-
except TypeError, err:
108-
# XXX Catch generic exception caused by __slots__
109-
if str(err) != ("a class that defines __slots__ "
110-
"without defining __getstate__ "
111-
"cannot be pickled"):
112-
raise # Not that specific exception
113-
getstate = None
114-
if not getstate:
115-
state = getattr(obj, "__dict__", None)
116-
names = _slotnames(cls)
117-
if names:
118-
slots = {}
119-
nil = []
120-
for name in names:
121-
value = getattr(obj, name, nil)
122-
if value is not nil:
123-
slots[name] = value
124-
if slots:
125-
state = (state, slots)
126-
listitems = dictitems = None
127-
if isinstance(obj, list):
128-
listitems = iter(obj)
129-
elif isinstance(obj, dict):
130-
dictitems = obj.iteritems()
131-
return __newobj__, (cls,) + args, state, listitems, dictitems
13292

13393

13494
_copy_dispatch = d = {}

Lib/copy_reg.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,84 @@ def _reduce(self):
6969
else:
7070
return _reconstructor, args
7171

72+
# A better version of _reduce, used by copy and pickle protocol 2
73+
74+
def __newobj__(cls, *args):
75+
return cls.__new__(cls, *args)
76+
77+
def _better_reduce(obj):
78+
cls = obj.__class__
79+
getnewargs = getattr(obj, "__getnewargs__", None)
80+
if getnewargs:
81+
args = getnewargs()
82+
else:
83+
args = ()
84+
getstate = getattr(obj, "__getstate__", None)
85+
if getstate:
86+
try:
87+
state = getstate()
88+
except TypeError, err:
89+
# XXX Catch generic exception caused by __slots__
90+
if str(err) != ("a class that defines __slots__ "
91+
"without defining __getstate__ "
92+
"cannot be pickled"):
93+
raise # Not that specific exception
94+
getstate = None
95+
if not getstate:
96+
state = getattr(obj, "__dict__", None)
97+
names = _slotnames(cls)
98+
if names:
99+
slots = {}
100+
nil = []
101+
for name in names:
102+
value = getattr(obj, name, nil)
103+
if value is not nil:
104+
slots[name] = value
105+
if slots:
106+
state = (state, slots)
107+
listitems = dictitems = None
108+
if isinstance(obj, list):
109+
listitems = iter(obj)
110+
elif isinstance(obj, dict):
111+
dictitems = obj.iteritems()
112+
return __newobj__, (cls,) + args, state, listitems, dictitems
113+
114+
def _slotnames(cls):
115+
"""Return a list of slot names for a given class.
116+
117+
This needs to find slots defined by the class and its bases, so we
118+
can't simply return the __slots__ attribute. We must walk down
119+
the Method Resolution Order and concatenate the __slots__ of each
120+
class found there. (This assumes classes don't modify their
121+
__slots__ attribute to misrepresent their slots after the class is
122+
defined.)
123+
"""
124+
125+
# Get the value from a cache in the class if possible
126+
names = cls.__dict__.get("__slotnames__")
127+
if names is not None:
128+
return names
129+
130+
# Not cached -- calculate the value
131+
names = []
132+
if not hasattr(cls, "__slots__"):
133+
# This class has no slots
134+
pass
135+
else:
136+
# Slots found -- gather slot names from all base classes
137+
for c in cls.__mro__:
138+
if "__slots__" in c.__dict__:
139+
names += [name for name in c.__dict__["__slots__"]
140+
if name not in ("__dict__", "__weakref__")]
141+
142+
# Cache the outcome in the class if at all possible
143+
try:
144+
cls.__slotnames__ = names
145+
except:
146+
pass # But don't die if we can't
147+
148+
return names
149+
72150
# A registry of extension codes. This is an ad-hoc compression
73151
# mechanism. Whenever a global reference to <module>, <name> is about
74152
# to be pickled, the (<module>, <name>) tuple is looked up here to see

Lib/pickle.py

Lines changed: 4 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
__version__ = "$Revision$" # Code version
2828

2929
from types import *
30-
from copy_reg import dispatch_table, _reconstructor
30+
from copy_reg import dispatch_table, _reconstructor, _better_reduce
3131
from copy_reg import _extension_registry, _inverted_registry, _extension_cache
3232
import marshal
3333
import sys
@@ -310,10 +310,7 @@ def save(self, obj):
310310
if self.proto >= 2:
311311
# Protocol 2 can do better than the default __reduce__
312312
if reduce is object.__reduce__:
313-
reduce = None
314-
if not reduce:
315-
self.save_newobj(obj)
316-
return
313+
reduce = _better_reduce
317314
if not reduce:
318315
raise PicklingError("Can't pickle %r object: %r" %
319316
(t.__name__, obj))
@@ -433,86 +430,6 @@ def save_reduce(self, func, args, state=None,
433430
save(state)
434431
write(BUILD)
435432

436-
def save_newobj(self, obj):
437-
# Save a new-style class instance, using protocol 2.
438-
assert self.proto >= 2 # This only works for protocol 2
439-
t = type(obj)
440-
getnewargs = getattr(obj, "__getnewargs__", None)
441-
if getnewargs:
442-
args = getnewargs() # This better not reference obj
443-
else:
444-
args = ()
445-
446-
save = self.save
447-
write = self.write
448-
449-
self.save(t)
450-
save(args)
451-
write(NEWOBJ)
452-
self.memoize(obj)
453-
454-
if isinstance(obj, list):
455-
self._batch_appends(iter(obj))
456-
elif isinstance(obj, dict):
457-
self._batch_setitems(obj.iteritems())
458-
459-
getstate = getattr(obj, "__getstate__", None)
460-
461-
if getstate:
462-
# A class may define both __getstate__ and __getnewargs__.
463-
# If they are the same function, we ignore __getstate__.
464-
# This is for the benefit of protocols 0 and 1, which don't
465-
# use __getnewargs__. Note that the only way to make them
466-
# the same function is something like this:
467-
#
468-
# class C(object):
469-
# def __getstate__(self):
470-
# return ...
471-
# __getnewargs__ = __getstate__
472-
#
473-
# No tricks are needed to ignore __setstate__; it simply
474-
# won't be called when we don't generate BUILD.
475-
# Also note that when __getnewargs__ and __getstate__ are
476-
# the same function, we don't do the default thing of
477-
# looking for __dict__ and slots either -- it is assumed
478-
# that __getnewargs__ returns all the state there is
479-
# (which should be a safe assumption since __getstate__
480-
# returns the *same* state).
481-
if getstate == getnewargs:
482-
return
483-
484-
try:
485-
state = getstate()
486-
except TypeError, err:
487-
# XXX Catch generic exception caused by __slots__
488-
if str(err) != ("a class that defines __slots__ "
489-
"without defining __getstate__ "
490-
"cannot be pickled"):
491-
raise # Not that specific exception
492-
getstate = None
493-
494-
if not getstate:
495-
state = getattr(obj, "__dict__", None)
496-
if not state:
497-
state = None
498-
# If there are slots, the state becomes a tuple of two
499-
# items: the first item the regular __dict__ or None, and
500-
# the second a dict mapping slot names to slot values
501-
names = _slotnames(t)
502-
if names:
503-
slots = {}
504-
nil = []
505-
for name in names:
506-
value = getattr(obj, name, nil)
507-
if value is not nil:
508-
slots[name] = value
509-
if slots:
510-
state = (state, slots)
511-
512-
if state is not None:
513-
save(state)
514-
write(BUILD)
515-
516433
# Methods below this point are dispatched through the dispatch table
517434

518435
dispatch = {}
@@ -713,7 +630,8 @@ def _batch_appends(self, items):
713630
tmp = []
714631
for i in r:
715632
try:
716-
tmp.append(items.next())
633+
x = items.next()
634+
tmp.append(x)
717635
except StopIteration:
718636
items = None
719637
break
@@ -865,42 +783,6 @@ def save_global(self, obj, name=None, pack=struct.pack):
865783

866784
# Pickling helpers
867785

868-
def _slotnames(cls):
869-
"""Return a list of slot names for a given class.
870-
871-
This needs to find slots defined by the class and its bases, so we
872-
can't simply return the __slots__ attribute. We must walk down
873-
the Method Resolution Order and concatenate the __slots__ of each
874-
class found there. (This assumes classes don't modify their
875-
__slots__ attribute to misrepresent their slots after the class is
876-
defined.)
877-
"""
878-
879-
# Get the value from a cache in the class if possible
880-
names = cls.__dict__.get("__slotnames__")
881-
if names is not None:
882-
return names
883-
884-
# Not cached -- calculate the value
885-
names = []
886-
if not hasattr(cls, "__slots__"):
887-
# This class has no slots
888-
pass
889-
else:
890-
# Slots found -- gather slot names from all base classes
891-
for c in cls.__mro__:
892-
if "__slots__" in c.__dict__:
893-
names += [name for name in c.__dict__["__slots__"]
894-
if name not in ("__dict__", "__weakref__")]
895-
896-
# Cache the outcome in the class if at all possible
897-
try:
898-
cls.__slotnames__ = names
899-
except:
900-
pass # But don't die if we can't
901-
902-
return names
903-
904786
def _keep_alive(x, memo):
905787
"""Keeps a reference to the object x in the memo.
906788

0 commit comments

Comments
 (0)