• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

zopefoundation / ZODB / 5706069709

pending completion
5706069709

push

github

web-flow
Drop support for Python < 3.7 (#386)

* Bumped version for breaking release.

* Drop support for Python 2.7, 3.5, 3.6.

---------

Co-authored-by: Jens Vagelpohl <jens@plyp.com>

2877 of 4050 branches covered (71.04%)

554 of 554 new or added lines in 89 files covered. (100.0%)

13324 of 15914 relevant lines covered (83.73%)

0.84 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

79.19
/src/ZODB/tests/testSerialize.py
1
##############################################################################
2
#
3
# Copyright (c) 2004 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
##############################################################################
14
import doctest
1✔
15
import sys
1✔
16
import unittest
1✔
17
from io import BytesIO
1✔
18

19
from persistent import Persistent
1✔
20
from persistent.wref import WeakRef
1✔
21

22
import ZODB.tests.util
1✔
23
from ZODB import serialize
1✔
24
from ZODB._compat import PersistentUnpickler
1✔
25
from ZODB._compat import Pickler
1✔
26
from ZODB._compat import _protocol
1✔
27

28

29
class PersistentObject(Persistent):
1✔
30
    pass
1✔
31

32

33
class ClassWithNewargs(int):
1✔
34
    def __new__(cls, value):
1✔
35
        return int.__new__(cls, value)
1✔
36

37
    def __getnewargs__(self):
1✔
38
        return int(self),
×
39

40

41
class ClassWithoutNewargs:
1✔
42
    def __init__(self, value):
1✔
43
        self.value = value
×
44

45

46
def make_pickle(ob):
1✔
47
    sio = BytesIO()
1✔
48
    p = Pickler(sio, _protocol)
1✔
49
    p.dump(ob)
1✔
50
    return sio.getvalue()
1✔
51

52

53
def _factory(conn, module_name, name):
1✔
54
    return globals()[name]
1✔
55

56

57
class SerializerTestCase(unittest.TestCase):
1✔
58

59
    # old format:  (module, name), None
60
    old_style_without_newargs = make_pickle(
1✔
61
        ((__name__, "ClassWithoutNewargs"), None))
62

63
    # old format:  (module, name), argtuple
64
    old_style_with_newargs = make_pickle(
1✔
65
        ((__name__, "ClassWithNewargs"), (1,)))
66

67
    # new format:  klass
68
    new_style_without_newargs = make_pickle(
1✔
69
        ClassWithoutNewargs)
70

71
    # new format:  klass, argtuple
72
    new_style_with_newargs = make_pickle(
1✔
73
        (ClassWithNewargs, (1,)))
74

75
    def test_getClassName(self):
1✔
76
        r = serialize.ObjectReader(factory=_factory)
1✔
77
        eq = self.assertEqual
1✔
78
        eq(r.getClassName(self.old_style_with_newargs),
1✔
79
           __name__ + ".ClassWithNewargs")
80
        eq(r.getClassName(self.new_style_with_newargs),
1✔
81
           __name__ + ".ClassWithNewargs")
82
        eq(r.getClassName(self.old_style_without_newargs),
1✔
83
           __name__ + ".ClassWithoutNewargs")
84
        eq(r.getClassName(self.new_style_without_newargs),
1✔
85
           __name__ + ".ClassWithoutNewargs")
86

87
    def test_getGhost(self):
1✔
88
        # Use a TestObjectReader since we need _get_class() to be
89
        # implemented; otherwise this is just a BaseObjectReader.
90

91
        class TestObjectReader(serialize.ObjectReader):
1✔
92
            # A production object reader would optimize this, but we
93
            # don't need to in a test
94
            def _get_class(self, module, name):
1✔
95
                __import__(module)
1✔
96
                return getattr(sys.modules[module], name)
1✔
97

98
        r = TestObjectReader(factory=_factory)
1✔
99
        g = r.getGhost(self.old_style_with_newargs)
1✔
100
        self.assertTrue(isinstance(g, ClassWithNewargs))
1✔
101
        self.assertEqual(g, 1)
1✔
102
        g = r.getGhost(self.old_style_without_newargs)
1✔
103
        self.assertTrue(isinstance(g, ClassWithoutNewargs))
1✔
104
        g = r.getGhost(self.new_style_with_newargs)
1✔
105
        self.assertTrue(isinstance(g, ClassWithNewargs))
1✔
106
        g = r.getGhost(self.new_style_without_newargs)
1✔
107
        self.assertTrue(isinstance(g, ClassWithoutNewargs))
1✔
108

109
    def test_myhasattr(self):
1✔
110

111
        class OldStyle:
1✔
112
            bar = "bar"
1✔
113

114
            def __getattr__(self, name):
1✔
115
                if name == "error":
1✔
116
                    raise ValueError("whee!")
1✔
117
                else:
118
                    raise AttributeError(name)
1✔
119

120
        class NewStyle:
1✔
121
            bar = "bar"
1✔
122

123
            def _raise(self):
1✔
124
                raise ValueError("whee!")
1✔
125
            error = property(_raise)
1✔
126

127
        self.assertRaises(ValueError,
1✔
128
                          serialize.myhasattr, OldStyle(), "error")
129
        self.assertRaises(ValueError,
1✔
130
                          serialize.myhasattr, NewStyle(), "error")
131
        self.assertTrue(serialize.myhasattr(OldStyle(), "bar"))
1✔
132
        self.assertTrue(serialize.myhasattr(NewStyle(), "bar"))
1✔
133
        self.assertTrue(not serialize.myhasattr(OldStyle(), "rat"))
1✔
134
        self.assertTrue(not serialize.myhasattr(NewStyle(), "rat"))
1✔
135

136
    def test_persistent_id_noload(self):
1✔
137
        # make sure we can noload weak references and other list-based
138
        # references like we expect.
139
        o = PersistentObject()
1✔
140
        o._p_oid = b'abcd'
1✔
141

142
        top = PersistentObject()
1✔
143
        top._p_oid = b'efgh'
1✔
144
        top.ref = WeakRef(o)
1✔
145

146
        pickle = serialize.ObjectWriter().serialize(top)
1✔
147
        # Make sure the persistent id is pickled using the 'C',
148
        # SHORT_BINBYTES opcode:
149
        self.assertTrue(b'C\x04abcd' in pickle)
1✔
150

151
        refs = []
1✔
152
        u = PersistentUnpickler(None, refs.append, BytesIO(pickle))
1✔
153
        u.noload()
1✔
154
        u.noload()
1✔
155

156
        self.assertEqual(refs, [['w', (b'abcd',)]])
1✔
157

158
    def test_protocol_3_binary_handling(self):
1✔
159
        from ZODB.serialize import _protocol
1✔
160
        self.assertEqual(3, _protocol)  # Yeah, whitebox
1✔
161
        o = PersistentObject()
1✔
162
        o._p_oid = b'o'
1✔
163
        o.o = PersistentObject()
1✔
164
        o.o._p_oid = b'o.o'
1✔
165
        pickle = serialize.ObjectWriter().serialize(o)
1✔
166

167
        # Make sure the persistent id is pickled using the 'C',
168
        # SHORT_BINBYTES opcode:
169
        self.assertTrue(b'C\x03o.o' in pickle)
1✔
170

171

172
class SerializerFunctestCase(unittest.TestCase):
1✔
173

174
    def setUp(self):
1✔
175
        import tempfile
1✔
176
        self._tempdir = tempfile.mkdtemp(suffix='serializerfunc')
1✔
177

178
    def tearDown(self):
1✔
179
        import shutil
1✔
180
        shutil.rmtree(self._tempdir)
1✔
181

182
    def test_funky_datetime_serialization(self):
1✔
183
        import os
1✔
184
        import subprocess
1✔
185
        fqn = os.path.join(self._tempdir, 'Data.fs')
1✔
186
        prep_args = [sys.executable, '-c',
1✔
187
                     'from ZODB.tests.testSerialize import _functest_prep; '
188
                     '_functest_prep(%s)' % repr(fqn)]
189
        # buildout doesn't arrange for the sys.path to be exported,
190
        # so force it ourselves
191
        environ = os.environ.copy()
1✔
192
        sys_path = sys.path
1✔
193
        environ['PYTHONPATH'] = os.pathsep.join(sys_path)
1✔
194
        subprocess.check_call(prep_args, env=environ)
1✔
195
        load_args = [sys.executable, '-c',
1✔
196
                     'from ZODB.tests.testSerialize import _functest_load; '
197
                     '_functest_load(%s)' % repr(fqn)]
198
        subprocess.call(load_args, env=environ)
1✔
199

200

201
def _working_failing_datetimes():
1✔
202
    import datetime
×
203
    WORKING = datetime.datetime(5375, 12, 31, 23, 59, 59)
×
204
    # Any date after 5375 A.D. appears to trigger this bug.
205
    FAILING = datetime.datetime(5376, 12, 31, 23, 59, 59)
×
206
    return WORKING, FAILING
×
207

208

209
def _functest_prep(fqn):
1✔
210
    # Prepare the database with a BTree which won't deserialize
211
    # if the bug is present.
212
    # run in separate process)
213
    import transaction
×
214
    from BTrees.OOBTree import OOBTree
×
215

216
    from ZODB import DB
×
217
    WORKING, FAILING = _working_failing_datetimes()
×
218
    db = DB(fqn)
×
219
    conn = db.open()
×
220
    try:
×
221
        root = conn.root()
×
222
        tree = root['tree'] = OOBTree()
×
223
        tree[WORKING] = 'working'
×
224
        tree[FAILING] = 'failing'
×
225
        transaction.commit()
×
226
    finally:  # Windoze
227
        conn.close()
×
228
        db.close()
×
229

230

231
def _functest_load(fqn):
1✔
232
    # Open the database and attempt to deserialize the tree
233
    # (run in separate process)
234
    from ZODB import DB
×
235
    WORKING, FAILING = _working_failing_datetimes()
×
236
    db = DB(fqn)
×
237
    conn = db.open()
×
238
    try:
×
239
        root = conn.root()
×
240
        tree = root['tree']
×
241
        assert tree[WORKING] == 'working'
×
242
        assert tree[FAILING] == 'failing'
×
243
    finally:  # Windoze
244
        conn.close()
×
245
        db.close()
×
246

247

248
def test_suite():
1✔
249
    return unittest.TestSuite((
1✔
250
        unittest.defaultTestLoader.loadTestsFromTestCase(SerializerTestCase),
251
        unittest.defaultTestLoader.loadTestsFromTestCase(
252
            SerializerFunctestCase),
253
        doctest.DocTestSuite("ZODB.serialize",
254
                             checker=ZODB.tests.util.checker),
255
    ))
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc