From 9510e4a9f8503421c9f589e99e697aa5f3b89b69 Mon Sep 17 00:00:00 2001 From: Alexandre Vassalotti Date: Sun, 11 May 2008 08:25:28 +0000 Subject: Added module stub for copy_reg renaming in 3.0. Renamed copy_reg to copyreg in the standard library, to avoid spurious warnings and ease later merging to py3k branch. Public documentation remains intact. --- Lib/copy.py | 2 +- Lib/copy_reg.py | 205 ++-------------------------------------------- Lib/copyreg.py | 201 +++++++++++++++++++++++++++++++++++++++++++++ Lib/idlelib/rpc.py | 6 +- Lib/os.py | 2 +- Lib/pickle.py | 6 +- Lib/re.py | 4 +- Lib/test/pickletester.py | 18 ++-- Lib/test/regrtest.py | 10 +-- Lib/test/test___all__.py | 2 +- Lib/test/test_copy.py | 6 +- Lib/test/test_copy_reg.py | 121 --------------------------- Lib/test/test_copyreg.py | 121 +++++++++++++++++++++++++++ Modules/cPickle.c | 32 ++++---- Modules/parsermodule.c | 2 +- Objects/typeobject.c | 44 +++++----- 16 files changed, 395 insertions(+), 387 deletions(-) create mode 100644 Lib/copyreg.py delete mode 100644 Lib/test/test_copy_reg.py create mode 100644 Lib/test/test_copyreg.py diff --git a/Lib/copy.py b/Lib/copy.py index 3f2033f..55ea660 100644 --- a/Lib/copy.py +++ b/Lib/copy.py @@ -49,7 +49,7 @@ __getstate__() and __setstate__(). See the documentation for module """ import types -from copy_reg import dispatch_table +from copyreg import dispatch_table class Error(Exception): pass diff --git a/Lib/copy_reg.py b/Lib/copy_reg.py index db17150..700698c 100644 --- a/Lib/copy_reg.py +++ b/Lib/copy_reg.py @@ -1,201 +1,8 @@ -"""Helper to provide extensibility for pickle/cPickle. +import sys +from warnings import warnpy3k -This is only useful to add pickle support for extension types defined in -C, not for instances of user-defined classes. -""" +warnpy3k("the copy_reg module has been renamed " + "to 'copyreg' in Python 3.0", stacklevel=2) -from types import ClassType as _ClassType - -__all__ = ["pickle", "constructor", - "add_extension", "remove_extension", "clear_extension_cache"] - -dispatch_table = {} - -def pickle(ob_type, pickle_function, constructor_ob=None): - if type(ob_type) is _ClassType: - raise TypeError("copy_reg is not intended for use with classes") - - if not hasattr(pickle_function, '__call__'): - raise TypeError("reduction functions must be callable") - dispatch_table[ob_type] = pickle_function - - # The constructor_ob function is a vestige of safe for unpickling. - # There is no reason for the caller to pass it anymore. - if constructor_ob is not None: - constructor(constructor_ob) - -def constructor(object): - if not hasattr(object, '__call__'): - raise TypeError("constructors must be callable") - -# Example: provide pickling support for complex numbers. - -try: - complex -except NameError: - pass -else: - - def pickle_complex(c): - return complex, (c.real, c.imag) - - pickle(complex, pickle_complex, complex) - -# Support for pickling new-style objects - -def _reconstructor(cls, base, state): - if base is object: - obj = object.__new__(cls) - else: - obj = base.__new__(cls, state) - if base.__init__ != object.__init__: - base.__init__(obj, state) - return obj - -_HEAPTYPE = 1<<9 - -# Python code for object.__reduce_ex__ for protocols 0 and 1 - -def _reduce_ex(self, proto): - assert proto < 2 - for base in self.__class__.__mro__: - if hasattr(base, '__flags__') and not base.__flags__ & _HEAPTYPE: - break - else: - base = object # not really reachable - if base is object: - state = None - else: - if base is self.__class__: - raise TypeError, "can't pickle %s objects" % base.__name__ - state = base(self) - args = (self.__class__, base, state) - try: - getstate = self.__getstate__ - except AttributeError: - if getattr(self, "__slots__", None): - raise TypeError("a class that defines __slots__ without " - "defining __getstate__ cannot be pickled") - try: - dict = self.__dict__ - except AttributeError: - dict = None - else: - dict = getstate() - if dict: - return _reconstructor, args, dict - else: - return _reconstructor, args - -# Helper for __reduce_ex__ protocol 2 - -def __newobj__(cls, *args): - return cls.__new__(cls, *args) - -def _slotnames(cls): - """Return a list of slot names for a given class. - - This needs to find slots defined by the class and its bases, so we - can't simply return the __slots__ attribute. We must walk down - the Method Resolution Order and concatenate the __slots__ of each - class found there. (This assumes classes don't modify their - __slots__ attribute to misrepresent their slots after the class is - defined.) - """ - - # Get the value from a cache in the class if possible - names = cls.__dict__.get("__slotnames__") - if names is not None: - return names - - # Not cached -- calculate the value - names = [] - if not hasattr(cls, "__slots__"): - # This class has no slots - pass - else: - # Slots found -- gather slot names from all base classes - for c in cls.__mro__: - if "__slots__" in c.__dict__: - slots = c.__dict__['__slots__'] - # if class has a single slot, it can be given as a string - if isinstance(slots, basestring): - slots = (slots,) - for name in slots: - # special descriptors - if name in ("__dict__", "__weakref__"): - continue - # mangled names - elif name.startswith('__') and not name.endswith('__'): - names.append('_%s%s' % (c.__name__, name)) - else: - names.append(name) - - # Cache the outcome in the class if at all possible - try: - cls.__slotnames__ = names - except: - pass # But don't die if we can't - - return names - -# A registry of extension codes. This is an ad-hoc compression -# mechanism. Whenever a global reference to , is about -# to be pickled, the (, ) tuple is looked up here to see -# if it is a registered extension code for it. Extension codes are -# universal, so that the meaning of a pickle does not depend on -# context. (There are also some codes reserved for local use that -# don't have this restriction.) Codes are positive ints; 0 is -# reserved. - -_extension_registry = {} # key -> code -_inverted_registry = {} # code -> key -_extension_cache = {} # code -> object -# Don't ever rebind those names: cPickle grabs a reference to them when -# it's initialized, and won't see a rebinding. - -def add_extension(module, name, code): - """Register an extension code.""" - code = int(code) - if not 1 <= code <= 0x7fffffff: - raise ValueError, "code out of range" - key = (module, name) - if (_extension_registry.get(key) == code and - _inverted_registry.get(code) == key): - return # Redundant registrations are benign - if key in _extension_registry: - raise ValueError("key %s is already registered with code %s" % - (key, _extension_registry[key])) - if code in _inverted_registry: - raise ValueError("code %s is already in use for key %s" % - (code, _inverted_registry[code])) - _extension_registry[key] = code - _inverted_registry[code] = key - -def remove_extension(module, name, code): - """Unregister an extension code. For testing only.""" - key = (module, name) - if (_extension_registry.get(key) != code or - _inverted_registry.get(code) != key): - raise ValueError("key %s is not registered with code %s" % - (key, code)) - del _extension_registry[key] - del _inverted_registry[code] - if code in _extension_cache: - del _extension_cache[code] - -def clear_extension_cache(): - _extension_cache.clear() - -# Standard extension code assignments - -# Reserved ranges - -# First Last Count Purpose -# 1 127 127 Reserved for Python standard library -# 128 191 64 Reserved for Zope -# 192 239 48 Reserved for 3rd parties -# 240 255 16 Reserved for private use (will never be assigned) -# 256 Inf Inf Reserved for future assignment - -# Extension codes are assigned by the Python Software Foundation. +import copyreg +sys.modules[__name__] = copyreg diff --git a/Lib/copyreg.py b/Lib/copyreg.py new file mode 100644 index 0000000..db17150 --- /dev/null +++ b/Lib/copyreg.py @@ -0,0 +1,201 @@ +"""Helper to provide extensibility for pickle/cPickle. + +This is only useful to add pickle support for extension types defined in +C, not for instances of user-defined classes. +""" + +from types import ClassType as _ClassType + +__all__ = ["pickle", "constructor", + "add_extension", "remove_extension", "clear_extension_cache"] + +dispatch_table = {} + +def pickle(ob_type, pickle_function, constructor_ob=None): + if type(ob_type) is _ClassType: + raise TypeError("copy_reg is not intended for use with classes") + + if not hasattr(pickle_function, '__call__'): + raise TypeError("reduction functions must be callable") + dispatch_table[ob_type] = pickle_function + + # The constructor_ob function is a vestige of safe for unpickling. + # There is no reason for the caller to pass it anymore. + if constructor_ob is not None: + constructor(constructor_ob) + +def constructor(object): + if not hasattr(object, '__call__'): + raise TypeError("constructors must be callable") + +# Example: provide pickling support for complex numbers. + +try: + complex +except NameError: + pass +else: + + def pickle_complex(c): + return complex, (c.real, c.imag) + + pickle(complex, pickle_complex, complex) + +# Support for pickling new-style objects + +def _reconstructor(cls, base, state): + if base is object: + obj = object.__new__(cls) + else: + obj = base.__new__(cls, state) + if base.__init__ != object.__init__: + base.__init__(obj, state) + return obj + +_HEAPTYPE = 1<<9 + +# Python code for object.__reduce_ex__ for protocols 0 and 1 + +def _reduce_ex(self, proto): + assert proto < 2 + for base in self.__class__.__mro__: + if hasattr(base, '__flags__') and not base.__flags__ & _HEAPTYPE: + break + else: + base = object # not really reachable + if base is object: + state = None + else: + if base is self.__class__: + raise TypeError, "can't pickle %s objects" % base.__name__ + state = base(self) + args = (self.__class__, base, state) + try: + getstate = self.__getstate__ + except AttributeError: + if getattr(self, "__slots__", None): + raise TypeError("a class that defines __slots__ without " + "defining __getstate__ cannot be pickled") + try: + dict = self.__dict__ + except AttributeError: + dict = None + else: + dict = getstate() + if dict: + return _reconstructor, args, dict + else: + return _reconstructor, args + +# Helper for __reduce_ex__ protocol 2 + +def __newobj__(cls, *args): + return cls.__new__(cls, *args) + +def _slotnames(cls): + """Return a list of slot names for a given class. + + This needs to find slots defined by the class and its bases, so we + can't simply return the __slots__ attribute. We must walk down + the Method Resolution Order and concatenate the __slots__ of each + class found there. (This assumes classes don't modify their + __slots__ attribute to misrepresent their slots after the class is + defined.) + """ + + # Get the value from a cache in the class if possible + names = cls.__dict__.get("__slotnames__") + if names is not None: + return names + + # Not cached -- calculate the value + names = [] + if not hasattr(cls, "__slots__"): + # This class has no slots + pass + else: + # Slots found -- gather slot names from all base classes + for c in cls.__mro__: + if "__slots__" in c.__dict__: + slots = c.__dict__['__slots__'] + # if class has a single slot, it can be given as a string + if isinstance(slots, basestring): + slots = (slots,) + for name in slots: + # special descriptors + if name in ("__dict__", "__weakref__"): + continue + # mangled names + elif name.startswith('__') and not name.endswith('__'): + names.append('_%s%s' % (c.__name__, name)) + else: + names.append(name) + + # Cache the outcome in the class if at all possible + try: + cls.__slotnames__ = names + except: + pass # But don't die if we can't + + return names + +# A registry of extension codes. This is an ad-hoc compression +# mechanism. Whenever a global reference to , is about +# to be pickled, the (, ) tuple is looked up here to see +# if it is a registered extension code for it. Extension codes are +# universal, so that the meaning of a pickle does not depend on +# context. (There are also some codes reserved for local use that +# don't have this restriction.) Codes are positive ints; 0 is +# reserved. + +_extension_registry = {} # key -> code +_inverted_registry = {} # code -> key +_extension_cache = {} # code -> object +# Don't ever rebind those names: cPickle grabs a reference to them when +# it's initialized, and won't see a rebinding. + +def add_extension(module, name, code): + """Register an extension code.""" + code = int(code) + if not 1 <= code <= 0x7fffffff: + raise ValueError, "code out of range" + key = (module, name) + if (_extension_registry.get(key) == code and + _inverted_registry.get(code) == key): + return # Redundant registrations are benign + if key in _extension_registry: + raise ValueError("key %s is already registered with code %s" % + (key, _extension_registry[key])) + if code in _inverted_registry: + raise ValueError("code %s is already in use for key %s" % + (code, _inverted_registry[code])) + _extension_registry[key] = code + _inverted_registry[code] = key + +def remove_extension(module, name, code): + """Unregister an extension code. For testing only.""" + key = (module, name) + if (_extension_registry.get(key) != code or + _inverted_registry.get(code) != key): + raise ValueError("key %s is not registered with code %s" % + (key, code)) + del _extension_registry[key] + del _inverted_registry[code] + if code in _extension_cache: + del _extension_cache[code] + +def clear_extension_cache(): + _extension_cache.clear() + +# Standard extension code assignments + +# Reserved ranges + +# First Last Count Purpose +# 1 127 127 Reserved for Python standard library +# 128 191 64 Reserved for Zope +# 192 239 48 Reserved for 3rd parties +# 240 255 16 Reserved for private use (will never be assigned) +# 256 Inf Inf Reserved for future assignment + +# Extension codes are assigned by the Python Software Foundation. diff --git a/Lib/idlelib/rpc.py b/Lib/idlelib/rpc.py index 3bac6a3..52a2eaf 100644 --- a/Lib/idlelib/rpc.py +++ b/Lib/idlelib/rpc.py @@ -37,7 +37,7 @@ import cPickle as pickle import threading import Queue import traceback -import copy_reg +import copyreg import types import marshal @@ -60,8 +60,8 @@ def pickle_code(co): # assert isinstance(fn, type.FunctionType) # return repr(fn) -copy_reg.pickle(types.CodeType, pickle_code, unpickle_code) -# copy_reg.pickle(types.FunctionType, pickle_function, unpickle_function) +copyreg.pickle(types.CodeType, pickle_code, unpickle_code) +# copyreg.pickle(types.FunctionType, pickle_function, unpickle_function) BUFSIZE = 8*1024 LOCALHOST = '127.0.0.1' diff --git a/Lib/os.py b/Lib/os.py index 6dc4bf3..dc2e5fd 100644 --- a/Lib/os.py +++ b/Lib/os.py @@ -729,7 +729,7 @@ if _exists("fork"): return p.stdin, p.stdout __all__.append("popen4") -import copy_reg as _copy_reg +import copyreg as _copy_reg def _make_stat_result(tup, dict): return stat_result(tup, dict) diff --git a/Lib/pickle.py b/Lib/pickle.py index 02a1b1d..1e91eef 100644 --- a/Lib/pickle.py +++ b/Lib/pickle.py @@ -27,8 +27,8 @@ Misc variables: __version__ = "$Revision$" # Code version from types import * -from copy_reg import dispatch_table -from copy_reg import _extension_registry, _inverted_registry, _extension_cache +from copyreg import dispatch_table +from copyreg import _extension_registry, _inverted_registry, _extension_cache import marshal import sys import struct @@ -295,7 +295,7 @@ class Pickler: self.save_global(obj) return - # Check copy_reg.dispatch_table + # Check copyreg.dispatch_table reduce = dispatch_table.get(t) if reduce: rv = reduce(obj) diff --git a/Lib/re.py b/Lib/re.py index 1d9c987..3f4f02c 100644 --- a/Lib/re.py +++ b/Lib/re.py @@ -278,12 +278,12 @@ def _subx(pattern, template): # register myself for pickling -import copy_reg +import copyreg def _pickle(p): return _compile, (p.pattern, p.flags) -copy_reg.pickle(_pattern_type, _pickle, _compile) +copyreg.pickle(_pattern_type, _pickle, _compile) # -------------------------------------------------------------------- # experimental stuff (see python-dev discussions for details) diff --git a/Lib/test/pickletester.py b/Lib/test/pickletester.py index e1bc078..ed3b626 100644 --- a/Lib/test/pickletester.py +++ b/Lib/test/pickletester.py @@ -2,7 +2,7 @@ import unittest import pickle import cPickle import pickletools -import copy_reg +import copyreg from test.test_support import TestFailed, have_unicode, TESTFN, \ run_with_locale @@ -44,21 +44,21 @@ class ExtensionSaver: # there is one). def __init__(self, code): self.code = code - if code in copy_reg._inverted_registry: - self.pair = copy_reg._inverted_registry[code] - copy_reg.remove_extension(self.pair[0], self.pair[1], code) + if code in copyreg._inverted_registry: + self.pair = copyreg._inverted_registry[code] + copyreg.remove_extension(self.pair[0], self.pair[1], code) else: self.pair = None # Restore previous registration for code. def restore(self): code = self.code - curpair = copy_reg._inverted_registry.get(code) + curpair = copyreg._inverted_registry.get(code) if curpair is not None: - copy_reg.remove_extension(curpair[0], curpair[1], code) + copyreg.remove_extension(curpair[0], curpair[1], code) pair = self.pair if pair is not None: - copy_reg.add_extension(pair[0], pair[1], code) + copyreg.add_extension(pair[0], pair[1], code) class C: def __cmp__(self, other): @@ -690,14 +690,14 @@ class AbstractPickleTests(unittest.TestCase): self.assertEqual(B(x), B(y), detail) self.assertEqual(x.__dict__, y.__dict__, detail) - # Register a type with copy_reg, with extension code extcode. Pickle + # Register a type with copyreg, with extension code extcode. Pickle # an object of that type. Check that the resulting pickle uses opcode # (EXT[124]) under proto 2, and not in proto 1. def produce_global_ext(self, extcode, opcode): e = ExtensionSaver(extcode) try: - copy_reg.add_extension(__name__, "MyList", extcode) + copyreg.add_extension(__name__, "MyList", extcode) x = MyList([1, 2, 3]) x.foo = 42 x.bar = "hello" diff --git a/Lib/test/regrtest.py b/Lib/test/regrtest.py index 7aa22ed..85df78e 100755 --- a/Lib/test/regrtest.py +++ b/Lib/test/regrtest.py @@ -629,7 +629,7 @@ def cleanup_test_droppings(testname, verbose): def dash_R(the_module, test, indirect_test, huntrleaks): # This code is hackish and inelegant, but it seems to do the job. - import copy_reg, _abcoll, io + import copyreg, _abcoll, io if not hasattr(sys, 'gettotalrefcount'): raise Exception("Tracking reference leaks requires a debug build " @@ -637,7 +637,7 @@ def dash_R(the_module, test, indirect_test, huntrleaks): # Save current values for dash_R_cleanup() to restore. fs = warnings.filters[:] - ps = copy_reg.dispatch_table.copy() + ps = copyreg.dispatch_table.copy() pic = sys.path_importer_cache.copy() abcs = {} modules = _abcoll, io @@ -677,7 +677,7 @@ def dash_R(the_module, test, indirect_test, huntrleaks): refrep.close() def dash_R_cleanup(fs, ps, pic, abcs): - import gc, copy_reg + import gc, copyreg import _strptime, linecache dircache = test_support.import_module('dircache', deprecated=True) import urlparse, urllib, urllib2, mimetypes, doctest @@ -691,8 +691,8 @@ def dash_R_cleanup(fs, ps, pic, abcs): # Restore some original values. warnings.filters[:] = fs - copy_reg.dispatch_table.clear() - copy_reg.dispatch_table.update(ps) + copyreg.dispatch_table.clear() + copyreg.dispatch_table.update(ps) sys.path_importer_cache.clear() sys.path_importer_cache.update(pic) diff --git a/Lib/test/test___all__.py b/Lib/test/test___all__.py index 1076c61..9c5e1f3 100644 --- a/Lib/test/test___all__.py +++ b/Lib/test/test___all__.py @@ -61,7 +61,7 @@ class AllTest(unittest.TestCase): self.check_all("commands") self.check_all("compileall") self.check_all("copy") - self.check_all("copy_reg") + self.check_all("copyreg") self.check_all("csv") self.check_all("dbhash") self.check_all("decimal") diff --git a/Lib/test/test_copy.py b/Lib/test/test_copy.py index d2899bd..8645ba1 100644 --- a/Lib/test/test_copy.py +++ b/Lib/test/test_copy.py @@ -1,7 +1,7 @@ """Unit tests for the copy module.""" import copy -import copy_reg +import copyreg import unittest from test import test_support @@ -42,7 +42,7 @@ class TestCopy(unittest.TestCase): return (C, (obj.foo,)) x = C(42) self.assertRaises(TypeError, copy.copy, x) - copy_reg.pickle(C, pickle_C, C) + copyreg.pickle(C, pickle_C, C) y = copy.copy(x) def test_copy_reduce_ex(self): @@ -215,7 +215,7 @@ class TestCopy(unittest.TestCase): return (C, (obj.foo,)) x = C(42) self.assertRaises(TypeError, copy.deepcopy, x) - copy_reg.pickle(C, pickle_C, C) + copyreg.pickle(C, pickle_C, C) y = copy.deepcopy(x) def test_deepcopy_reduce_ex(self): diff --git a/Lib/test/test_copy_reg.py b/Lib/test/test_copy_reg.py deleted file mode 100644 index c3d3964..0000000 --- a/Lib/test/test_copy_reg.py +++ /dev/null @@ -1,121 +0,0 @@ -import copy_reg -import unittest - -from test import test_support -from test.pickletester import ExtensionSaver - -class C: - pass - - -class WithoutSlots(object): - pass - -class WithWeakref(object): - __slots__ = ('__weakref__',) - -class WithPrivate(object): - __slots__ = ('__spam',) - -class WithSingleString(object): - __slots__ = 'spam' - -class WithInherited(WithSingleString): - __slots__ = ('eggs',) - - -class CopyRegTestCase(unittest.TestCase): - - def test_class(self): - self.assertRaises(TypeError, copy_reg.pickle, - C, None, None) - - def test_noncallable_reduce(self): - self.assertRaises(TypeError, copy_reg.pickle, - type(1), "not a callable") - - def test_noncallable_constructor(self): - self.assertRaises(TypeError, copy_reg.pickle, - type(1), int, "not a callable") - - def test_bool(self): - import copy - self.assertEquals(True, copy.copy(True)) - - def test_extension_registry(self): - mod, func, code = 'junk1 ', ' junk2', 0xabcd - e = ExtensionSaver(code) - try: - # Shouldn't be in registry now. - self.assertRaises(ValueError, copy_reg.remove_extension, - mod, func, code) - copy_reg.add_extension(mod, func, code) - # Should be in the registry. - self.assert_(copy_reg._extension_registry[mod, func] == code) - self.assert_(copy_reg._inverted_registry[code] == (mod, func)) - # Shouldn't be in the cache. - self.assert_(code not in copy_reg._extension_cache) - # Redundant registration should be OK. - copy_reg.add_extension(mod, func, code) # shouldn't blow up - # Conflicting code. - self.assertRaises(ValueError, copy_reg.add_extension, - mod, func, code + 1) - self.assertRaises(ValueError, copy_reg.remove_extension, - mod, func, code + 1) - # Conflicting module name. - self.assertRaises(ValueError, copy_reg.add_extension, - mod[1:], func, code ) - self.assertRaises(ValueError, copy_reg.remove_extension, - mod[1:], func, code ) - # Conflicting function name. - self.assertRaises(ValueError, copy_reg.add_extension, - mod, func[1:], code) - self.assertRaises(ValueError, copy_reg.remove_extension, - mod, func[1:], code) - # Can't remove one that isn't registered at all. - if code + 1 not in copy_reg._inverted_registry: - self.assertRaises(ValueError, copy_reg.remove_extension, - mod[1:], func[1:], code + 1) - - finally: - e.restore() - - # Shouldn't be there anymore. - self.assert_((mod, func) not in copy_reg._extension_registry) - # The code *may* be in copy_reg._extension_registry, though, if - # we happened to pick on a registered code. So don't check for - # that. - - # Check valid codes at the limits. - for code in 1, 0x7fffffff: - e = ExtensionSaver(code) - try: - copy_reg.add_extension(mod, func, code) - copy_reg.remove_extension(mod, func, code) - finally: - e.restore() - - # Ensure invalid codes blow up. - for code in -1, 0, 0x80000000L: - self.assertRaises(ValueError, copy_reg.add_extension, - mod, func, code) - - def test_slotnames(self): - self.assertEquals(copy_reg._slotnames(WithoutSlots), []) - self.assertEquals(copy_reg._slotnames(WithWeakref), []) - expected = ['_WithPrivate__spam'] - self.assertEquals(copy_reg._slotnames(WithPrivate), expected) - self.assertEquals(copy_reg._slotnames(WithSingleString), ['spam']) - expected = ['eggs', 'spam'] - expected.sort() - result = copy_reg._slotnames(WithInherited) - result.sort() - self.assertEquals(result, expected) - - -def test_main(): - test_support.run_unittest(CopyRegTestCase) - - -if __name__ == "__main__": - test_main() diff --git a/Lib/test/test_copyreg.py b/Lib/test/test_copyreg.py new file mode 100644 index 0000000..0b42128 --- /dev/null +++ b/Lib/test/test_copyreg.py @@ -0,0 +1,121 @@ +import copyreg +import unittest + +from test import test_support +from test.pickletester import ExtensionSaver + +class C: + pass + + +class WithoutSlots(object): + pass + +class WithWeakref(object): + __slots__ = ('__weakref__',) + +class WithPrivate(object): + __slots__ = ('__spam',) + +class WithSingleString(object): + __slots__ = 'spam' + +class WithInherited(WithSingleString): + __slots__ = ('eggs',) + + +class CopyRegTestCase(unittest.TestCase): + + def test_class(self): + self.assertRaises(TypeError, copyreg.pickle, + C, None, None) + + def test_noncallable_reduce(self): + self.assertRaises(TypeError, copyreg.pickle, + type(1), "not a callable") + + def test_noncallable_constructor(self): + self.assertRaises(TypeError, copyreg.pickle, + type(1), int, "not a callable") + + def test_bool(self): + import copy + self.assertEquals(True, copy.copy(True)) + + def test_extension_registry(self): + mod, func, code = 'junk1 ', ' junk2', 0xabcd + e = ExtensionSaver(code) + try: + # Shouldn't be in registry now. + self.assertRaises(ValueError, copyreg.remove_extension, + mod, func, code) + copyreg.add_extension(mod, func, code) + # Should be in the registry. + self.assert_(copyreg._extension_registry[mod, func] == code) + self.assert_(copyreg._inverted_registry[code] == (mod, func)) + # Shouldn't be in the cache. + self.assert_(code not in copyreg._extension_cache) + # Redundant registration should be OK. + copyreg.add_extension(mod, func, code) # shouldn't blow up + # Conflicting code. + self.assertRaises(ValueError, copyreg.add_extension, + mod, func, code + 1) + self.assertRaises(ValueError, copyreg.remove_extension, + mod, func, code + 1) + # Conflicting module name. + self.assertRaises(ValueError, copyreg.add_extension, + mod[1:], func, code ) + self.assertRaises(ValueError, copyreg.remove_extension, + mod[1:], func, code ) + # Conflicting function name. + self.assertRaises(ValueError, copyreg.add_extension, + mod, func[1:], code) + self.assertRaises(ValueError, copyreg.remove_extension, + mod, func[1:], code) + # Can't remove one that isn't registered at all. + if code + 1 not in copyreg._inverted_registry: + self.assertRaises(ValueError, copyreg.remove_extension, + mod[1:], func[1:], code + 1) + + finally: + e.restore() + + # Shouldn't be there anymore. + self.assert_((mod, func) not in copyreg._extension_registry) + # The code *may* be in copyreg._extension_registry, though, if + # we happened to pick on a registered code. So don't check for + # that. + + # Check valid codes at the limits. + for code in 1, 0x7fffffff: + e = ExtensionSaver(code) + try: + copyreg.add_extension(mod, func, code) + copyreg.remove_extension(mod, func, code) + finally: + e.restore() + + # Ensure invalid codes blow up. + for code in -1, 0, 0x80000000L: + self.assertRaises(ValueError, copyreg.add_extension, + mod, func, code) + + def test_slotnames(self): + self.assertEquals(copyreg._slotnames(WithoutSlots), []) + self.assertEquals(copyreg._slotnames(WithWeakref), []) + expected = ['_WithPrivate__spam'] + self.assertEquals(copyreg._slotnames(WithPrivate), expected) + self.assertEquals(copyreg._slotnames(WithSingleString), ['spam']) + expected = ['eggs', 'spam'] + expected.sort() + result = copyreg._slotnames(WithInherited) + result.sort() + self.assertEquals(result, expected) + + +def test_main(): + test_support.run_unittest(CopyRegTestCase) + + +if __name__ == "__main__": + test_main() diff --git a/Modules/cPickle.c b/Modules/cPickle.c index b9b95c8..1dd35be 100644 --- a/Modules/cPickle.c +++ b/Modules/cPickle.c @@ -105,18 +105,18 @@ static PyObject *BadPickleGet; /* As the name says, an empty tuple. */ static PyObject *empty_tuple; -/* copy_reg.dispatch_table, {type_object: pickling_function} */ +/* copyreg.dispatch_table, {type_object: pickling_function} */ static PyObject *dispatch_table; /* For EXT[124] opcodes. */ -/* copy_reg._extension_registry, {(module_name, function_name): code} */ +/* copyreg._extension_registry, {(module_name, function_name): code} */ static PyObject *extension_registry; -/* copy_reg._inverted_registry, {code: (module_name, function_name)} */ +/* copyreg._inverted_registry, {code: (module_name, function_name)} */ static PyObject *inverted_registry; -/* copy_reg._extension_cache, {code: object} */ +/* copyreg._extension_cache, {code: object} */ static PyObject *extension_cache; -/* For looking up name pairs in copy_reg._extension_registry. */ +/* For looking up name pairs in copyreg._extension_registry. */ static PyObject *two_tuple; static PyObject *__class___str, *__getinitargs___str, *__dict___str, @@ -124,7 +124,7 @@ static PyObject *__class___str, *__getinitargs___str, *__dict___str, *__reduce_ex___str, *write_str, *append_str, *read_str, *readline_str, *__main___str, - *copy_reg_str, *dispatch_table_str; + *copyreg_str, *dispatch_table_str; /************************************************************************* Internal Data type for pickle data. */ @@ -2477,7 +2477,7 @@ save(Picklerobject *self, PyObject *args, int pers_save) } /* Get a reduction callable, and call it. This may come from - * copy_reg.dispatch_table, the object's __reduce_ex__ method, + * copyreg.dispatch_table, the object's __reduce_ex__ method, * or the object's __reduce__ method. */ __reduce__ = PyDict_GetItem(dispatch_table, (PyObject *)type); @@ -2856,7 +2856,7 @@ newPicklerobject(PyObject *file, int proto) if (PyEval_GetRestricted()) { /* Restricted execution, get private tables */ - PyObject *m = PyImport_Import(copy_reg_str); + PyObject *m = PyImport_Import(copyreg_str); if (m == NULL) goto err; @@ -5566,7 +5566,7 @@ static struct PyMethodDef cPickle_methods[] = { static int init_stuff(PyObject *module_dict) { - PyObject *copy_reg, *t, *r; + PyObject *copyreg, *t, *r; #define INIT_STR(S) if (!( S ## _str=PyString_InternFromString(#S))) return -1; @@ -5588,30 +5588,30 @@ init_stuff(PyObject *module_dict) INIT_STR(append); INIT_STR(read); INIT_STR(readline); - INIT_STR(copy_reg); + INIT_STR(copyreg); INIT_STR(dispatch_table); - if (!( copy_reg = PyImport_ImportModule("copy_reg"))) + if (!( copyreg = PyImport_ImportModule("copyreg"))) return -1; /* This is special because we want to use a different one in restricted mode. */ - dispatch_table = PyObject_GetAttr(copy_reg, dispatch_table_str); + dispatch_table = PyObject_GetAttr(copyreg, dispatch_table_str); if (!dispatch_table) return -1; - extension_registry = PyObject_GetAttrString(copy_reg, + extension_registry = PyObject_GetAttrString(copyreg, "_extension_registry"); if (!extension_registry) return -1; - inverted_registry = PyObject_GetAttrString(copy_reg, + inverted_registry = PyObject_GetAttrString(copyreg, "_inverted_registry"); if (!inverted_registry) return -1; - extension_cache = PyObject_GetAttrString(copy_reg, + extension_cache = PyObject_GetAttrString(copyreg, "_extension_cache"); if (!extension_cache) return -1; - Py_DECREF(copy_reg); + Py_DECREF(copyreg); if (!(empty_tuple = PyTuple_New(0))) return -1; diff --git a/Modules/parsermodule.c b/Modules/parsermodule.c index 6e52343..02a1e48 100644 --- a/Modules/parsermodule.c +++ b/Modules/parsermodule.c @@ -3284,7 +3284,7 @@ initparser(void) * If this fails, the import of this module will fail because an * exception will be raised here; should we clear the exception? */ - copyreg = PyImport_ImportModuleNoBlock("copy_reg"); + copyreg = PyImport_ImportModuleNoBlock("copyreg"); if (copyreg != NULL) { PyObject *func, *pickler; diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 54ad714..4dfd39e 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -3058,31 +3058,31 @@ static PyGetSetDef object_getsets[] = { /* Stuff to implement __reduce_ex__ for pickle protocols >= 2. - We fall back to helpers in copy_reg for: + We fall back to helpers in copyreg for: - pickle protocols < 2 - calculating the list of slot names (done only once per class) - the __newobj__ function (which is used as a token but never called) */ static PyObject * -import_copy_reg(void) +import_copyreg(void) { - static PyObject *copy_reg_str; + static PyObject *copyreg_str; - if (!copy_reg_str) { - copy_reg_str = PyString_InternFromString("copy_reg"); - if (copy_reg_str == NULL) + if (!copyreg_str) { + copyreg_str = PyString_InternFromString("copyreg"); + if (copyreg_str == NULL) return NULL; } - return PyImport_Import(copy_reg_str); + return PyImport_Import(copyreg_str); } static PyObject * slotnames(PyObject *cls) { PyObject *clsdict; - PyObject *copy_reg; + PyObject *copyreg; PyObject *slotnames; if (!PyType_Check(cls)) { @@ -3097,12 +3097,12 @@ slotnames(PyObject *cls) return slotnames; } - copy_reg = import_copy_reg(); - if (copy_reg == NULL) + copyreg = import_copyreg(); + if (copyreg == NULL) return NULL; - slotnames = PyObject_CallMethod(copy_reg, "_slotnames", "O", cls); - Py_DECREF(copy_reg); + slotnames = PyObject_CallMethod(copyreg, "_slotnames", "O", cls); + Py_DECREF(copyreg); if (slotnames != NULL && slotnames != Py_None && !PyList_Check(slotnames)) @@ -3123,7 +3123,7 @@ reduce_2(PyObject *obj) PyObject *args = NULL, *args2 = NULL; PyObject *getstate = NULL, *state = NULL, *names = NULL; PyObject *slots = NULL, *listitems = NULL, *dictitems = NULL; - PyObject *copy_reg = NULL, *newobj = NULL, *res = NULL; + PyObject *copyreg = NULL, *newobj = NULL, *res = NULL; Py_ssize_t i, n; cls = PyObject_GetAttrString(obj, "__class__"); @@ -3218,10 +3218,10 @@ reduce_2(PyObject *obj) goto end; } - copy_reg = import_copy_reg(); - if (copy_reg == NULL) + copyreg = import_copyreg(); + if (copyreg == NULL) goto end; - newobj = PyObject_GetAttrString(copy_reg, "__newobj__"); + newobj = PyObject_GetAttrString(copyreg, "__newobj__"); if (newobj == NULL) goto end; @@ -3248,7 +3248,7 @@ reduce_2(PyObject *obj) Py_XDECREF(names); Py_XDECREF(listitems); Py_XDECREF(dictitems); - Py_XDECREF(copy_reg); + Py_XDECREF(copyreg); Py_XDECREF(newobj); return res; } @@ -3271,17 +3271,17 @@ reduce_2(PyObject *obj) static PyObject * _common_reduce(PyObject *self, int proto) { - PyObject *copy_reg, *res; + PyObject *copyreg, *res; if (proto >= 2) return reduce_2(self); - copy_reg = import_copy_reg(); - if (!copy_reg) + copyreg = import_copyreg(); + if (!copyreg) return NULL; - res = PyEval_CallMethod(copy_reg, "_reduce_ex", "(Oi)", self, proto); - Py_DECREF(copy_reg); + res = PyEval_CallMethod(copyreg, "_reduce_ex", "(Oi)", self, proto); + Py_DECREF(copyreg); return res; } -- cgit v0.12