1##############################################################################
2#
3# Copyright (c) 2013 Zope Foundation and Contributors.
4# All Rights Reserved.
5#
6# This software is subject to the provisions of the Zope Public License,
7# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
8# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11# FOR A PARTICULAR PURPOSE
12#
13##############################################################################
14import sys
15from six import PY3
16
17IS_JYTHON = sys.platform.startswith('java')
18
19_protocol = 3
20from zodbpickle import binary
21
22if not PY3:
23    # Python 2.x
24    # PyPy's cPickle doesn't have noload, and noload is broken in Python 2.7,
25    # so we need zodbpickle.
26    # Get the fastest working version we can (PyPy has no fastpickle)
27    try:
28        import zodbpickle.fastpickle as cPickle
29    except ImportError:
30        import zodbpickle.pickle as cPickle
31    Pickler = cPickle.Pickler
32    Unpickler = cPickle.Unpickler
33    dump = cPickle.dump
34    dumps = cPickle.dumps
35    loads = cPickle.loads
36    HIGHEST_PROTOCOL = cPickle.HIGHEST_PROTOCOL
37    IMPORT_MAPPING = {}
38    NAME_MAPPING = {}
39    FILESTORAGE_MAGIC = b"FS21"
40else:
41    # Python 3.x: can't use stdlib's pickle because
42    # http://bugs.python.org/issue6784
43    import zodbpickle.pickle
44    HIGHEST_PROTOCOL = 3
45    from _compat_pickle import IMPORT_MAPPING, NAME_MAPPING
46
47    class Pickler(zodbpickle.pickle.Pickler):
48        def __init__(self, f, protocol=None):
49            super(Pickler, self).__init__(f, protocol)
50
51    class Unpickler(zodbpickle.pickle.Unpickler):
52        def __init__(self, f):
53            super(Unpickler, self).__init__(f)
54
55        # Py3: Python 3 doesn't allow assignments to find_global,
56        # instead, find_class can be overridden
57
58        find_global = None
59
60        def find_class(self, modulename, name):
61            if self.find_global is None:
62                return super(Unpickler, self).find_class(modulename, name)
63            return self.find_global(modulename, name)
64
65    def dump(o, f, protocol=None):
66        return zodbpickle.pickle.dump(o, f, protocol)
67
68    def dumps(o, protocol=None):
69        return zodbpickle.pickle.dumps(o, protocol)
70
71    def loads(s):
72        return zodbpickle.pickle.loads(s, encoding='ASCII', errors='bytes')
73    FILESTORAGE_MAGIC = b"FS30"
74
75
76def PersistentPickler(persistent_id, *args, **kwargs):
77    """
78    Returns a :class:`Pickler` that will use the given ``persistent_id``
79    to get persistent IDs. The remainder of the arguments are passed to the
80    Pickler itself.
81
82    This covers the differences between Python 2 and 3 and PyPy/zodbpickle.
83    """
84    p = Pickler(*args, **kwargs)
85    if not PY3:
86        p.inst_persistent_id = persistent_id
87
88    # PyPy uses a python implementation of cPickle/zodbpickle in both Python 2
89    # and Python 3. We can't really detect inst_persistent_id as its
90    # a magic attribute that's not readable, but it doesn't hurt to
91    # simply always assign to persistent_id also
92    p.persistent_id = persistent_id
93    return p
94
95def PersistentUnpickler(find_global, load_persistent, *args, **kwargs):
96    """
97    Returns a :class:`Unpickler` that will use the given `find_global` function
98    to locate classes, and the given `load_persistent` function to load
99    objects from a persistent id.
100
101    This covers the differences between Python 2 and 3 and PyPy/zodbpickle.
102    """
103    unpickler = Unpickler(*args, **kwargs)
104    if find_global is not None:
105        unpickler.find_global = find_global
106        try:
107            unpickler.find_class = find_global # PyPy, zodbpickle, the non-c-accelerated version
108        except AttributeError:
109            pass
110    if load_persistent is not None:
111        unpickler.persistent_load = load_persistent
112
113    return unpickler
114
115
116try:
117    # XXX: why not just import BytesIO from io?
118    from cStringIO import StringIO as BytesIO
119except ImportError:
120    # Python 3.x
121    from io import BytesIO
122
123
124try:
125    # Python 3.x
126    from base64 import decodebytes, encodebytes
127except ImportError:
128    # Python 2.x
129    from base64 import decodestring as decodebytes, encodestring as encodebytes
130
131
132# Python 3.x: ``hasattr()`` swallows only AttributeError.
133def py2_hasattr(obj, name):
134    try:
135        getattr(obj, name)
136    except:
137        return False
138    return True
139
140
141try:
142    # Py2: simply reexport the builtin
143    long = long
144except NameError:
145    # Py3
146    long = int
147    INT_TYPES = (int,)
148else:
149    INT_TYPES = (int, long)
150
151
152try:
153    TEXT = unicode
154except NameError: #pragma NO COVER Py3k
155    TEXT = str
156
157def ascii_bytes(x):
158    if isinstance(x, TEXT):
159        x = x.encode('ascii')
160    return x
161