Skip to content

Commit e9c09fa

Browse files
committed
[GR-12615] Create minimal implementation of zlib module.
PullRequest: graalpython/289
2 parents 5c466eb + d8edc07 commit e9c09fa

File tree

9 files changed

+787
-31
lines changed

9 files changed

+787
-31
lines changed

graalpython/com.oracle.graal.python.test/src/tests/test_zipimport.py

Lines changed: 3 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,8 @@
11
# Copyright (c) 2018, Oracle and/or its affiliates.
2-
# Copyright (c) 2013, Regents of the University of California
2+
# Copyright (C) 1996-2017 Python Software Foundation
33
#
4-
# All rights reserved.
5-
#
6-
# Redistribution and use in source and binary forms, with or without modification, are
7-
# permitted provided that the following conditions are met:
8-
#
9-
# 1. Redistributions of source code must retain the above copyright notice, this list of
10-
# conditions and the following disclaimer.
11-
# 2. Redistributions in binary form must reproduce the above copyright notice, this list of
12-
# conditions and the following disclaimer in the documentation and/or other materials provided
13-
# with the distribution.
14-
#
15-
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
16-
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
17-
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
18-
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19-
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
20-
# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
21-
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
22-
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
23-
# OF THE POSSIBILITY OF SUCH DAMAGE.
24-
# Qunaibit 02/05/2014
25-
# With Statement
4+
# Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
5+
266

277
import sys
288
import os
Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
# Copyright (c) 2018, Oracle and/or its affiliates.
2+
# Copyright (C) 1996-2017 Python Software Foundation
3+
#
4+
# Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
5+
6+
7+
import unittest
8+
import zlib
9+
import binascii
10+
import re
11+
import sys
12+
13+
pintNumber = 98765432109876543210
14+
longNumber = 9876543210
15+
16+
class MyIntObject:
17+
def __init__(self, value):
18+
self.value = value
19+
def __int__(self):
20+
return self.value
21+
22+
class MyIndexObject:
23+
def __init__(self, value):
24+
self.value = value
25+
def __index__(self):
26+
return self.value
27+
28+
class CustomInt:
29+
def __int__(self):
30+
return 100
31+
32+
class ChecksumTests(unittest.TestCase):
33+
34+
# checksum test cases
35+
def test_crc32start(self):
36+
self.assertEqual(zlib.crc32(b""), zlib.crc32(b"", 0))
37+
self.assertTrue(zlib.crc32(b"abc", 0xffffffff))
38+
39+
def test_crc32empty(self):
40+
self.assertEqual(zlib.crc32(b"", 0), 0)
41+
self.assertEqual(zlib.crc32(b"", 1), 1)
42+
self.assertEqual(zlib.crc32(b"", 432), 432)
43+
self.assertEqual(zlib.crc32(b"", -1), 4294967295)
44+
self.assertEqual(zlib.crc32(b"", longNumber), 1286608618)
45+
self.assertEqual(zlib.crc32(b"", pintNumber), 3844505322)
46+
self.assertEqual(zlib.crc32(b"", MyIntObject(10)), 10)
47+
48+
def test_adler32start(self):
49+
self.assertEqual(zlib.adler32(b""), zlib.adler32(b"", 1))
50+
self.assertTrue(zlib.adler32(b"abc", 0xffffffff))
51+
52+
def test_adler32empty(self):
53+
self.assertEqual(zlib.adler32(b"", 0), 0)
54+
self.assertEqual(zlib.adler32(b"", 1), 1)
55+
self.assertEqual(zlib.adler32(b"", 432), 432)
56+
self.assertEqual(zlib.adler32(b"", longNumber), 1286608618)
57+
self.assertEqual(zlib.adler32(b"", pintNumber), 3844505322)
58+
self.assertEqual(zlib.adler32(b"", MyIntObject(10)), 10)
59+
60+
def test_penguins(self):
61+
self.assertEqual(zlib.crc32(b"penguin", 0), 0x0e5c1a120)
62+
self.assertEqual(zlib.crc32(b"penguin", 1), 0x43b6aa94)
63+
self.assertEqual(zlib.adler32(b"penguin", 0), 0x0bcf02f6)
64+
self.assertEqual(zlib.adler32(b"penguin", 1), 0x0bd602f7)
65+
self.assertEqual(zlib.crc32(b"penguin"), zlib.crc32(b"penguin", 0))
66+
self.assertEqual(zlib.adler32(b"penguin"),zlib.adler32(b"penguin",1))
67+
68+
def test_crc32_adler32_unsigned(self):
69+
foo = b'abcdefghijklmnop'
70+
# explicitly test signed behavior
71+
self.assertEqual(zlib.crc32(foo), 2486878355)
72+
self.assertEqual(zlib.crc32(b'spam'), 1138425661)
73+
self.assertEqual(zlib.adler32(foo+foo), 3573550353)
74+
self.assertEqual(zlib.adler32(b'spam'), 72286642)
75+
76+
def test_same_as_binascii_crc32(self):
77+
foo = b'abcdefghijklmnop'
78+
crc = 2486878355
79+
self.assertEqual(binascii.crc32(foo), crc)
80+
self.assertEqual(zlib.crc32(foo), crc)
81+
self.assertEqual(binascii.crc32(b'spam'), zlib.crc32(b'spam'))
82+
83+
def test_wrong_inputs(self):
84+
self.assertRaises(TypeError, zlib.crc32, 10)
85+
self.assertRaises(TypeError, zlib.crc32, 'ahoj')
86+
self.assertRaises(TypeError, zlib.crc32, b'ahoj', MyIndexObject(10))
87+
self.assertRaises(TypeError, zlib.adler32, 10)
88+
self.assertRaises(TypeError, zlib.adler32, 'ahoj')
89+
self.assertRaises(TypeError, zlib.adler32, b'ahoj', MyIndexObject(10))
90+
91+
class BaseCompressTestCase(object):
92+
def check_big_compress_buffer(self, size, compress_func):
93+
_1M = 1024 * 1024
94+
# Generate 10 MiB worth of random, and expand it by repeating it.
95+
# The assumption is that zlib's memory is not big enough to exploit
96+
# such spread out redundancy.
97+
data = b''.join([random.getrandbits(8 * _1M).to_bytes(_1M, 'little')
98+
for i in range(10)])
99+
data = data * (size // len(data) + 1)
100+
try:
101+
compress_func(data)
102+
finally:
103+
# Release memory
104+
data = None
105+
106+
def check_big_decompress_buffer(self, size, decompress_func):
107+
data = b'x' * size
108+
try:
109+
compressed = zlib.compress(data, 1)
110+
finally:
111+
# Release memory
112+
data = None
113+
data = decompress_func(compressed)
114+
# Sanity check
115+
try:
116+
self.assertEqual(len(data), size)
117+
self.assertEqual(len(data.strip(b'x')), 0)
118+
finally:
119+
data = None
120+
121+
def assertRaisesRegex(self, expected_exception, expected_regex, function,
122+
*args, **kwargs):
123+
"""Asserts that the message in a raised exception matches a regex.
124+
125+
Args:
126+
expected_exception: Exception class expected to be raised.
127+
expected_regex: Regex (re.Pattern object or string) expected
128+
to be found in error message.
129+
args: Function to be called and extra positional args.
130+
kwargs: Extra kwargs.
131+
msg: Optional message used in case of failure. Can only be used
132+
when assertRaisesRegex is used as a context manager.
133+
"""
134+
try:
135+
function(*args, **kwargs)
136+
except expected_exception as e:
137+
if not re.compile(expected_regex).search(str(e)):
138+
assert False, "exception message '%r' does not match '%r'" % (e, expected_regex)
139+
else:
140+
assert False, "expected '%r' to raise '%r'" % (self.function, exc_type)
141+
142+
class CompressTests(BaseCompressTestCase, unittest.TestCase):
143+
# Test compression in one go (whole message compression)
144+
def test_speech(self):
145+
x = zlib.compress(HAMLET_SCENE)
146+
self.assertEqual(zlib.decompress(x), HAMLET_SCENE)
147+
148+
def test_keywords(self):
149+
if (sys.version_info.major >= 3 and sys.version_info.minor >= 6):
150+
# the keywords were added later
151+
x = zlib.compress(HAMLET_SCENE, level=3)
152+
self.assertEqual(zlib.decompress(x), HAMLET_SCENE)
153+
with self.assertRaises(TypeError):
154+
zlib.compress(data=HAMLET_SCENE, level=3)
155+
self.assertEqual(zlib.decompress(x,
156+
wbits=zlib.MAX_WBITS,
157+
bufsize=zlib.DEF_BUF_SIZE),
158+
HAMLET_SCENE)
159+
160+
def test_speech128(self):
161+
# compress more data
162+
data = HAMLET_SCENE * 128
163+
x = zlib.compress(data)
164+
self.assertEqual(zlib.compress(bytearray(data)), x)
165+
for ob in x, bytearray(x):
166+
self.assertEqual(zlib.decompress(ob), data)
167+
168+
def test_incomplete_stream(self):
169+
170+
# A useful error message is given
171+
x = zlib.compress(HAMLET_SCENE)
172+
self.assertRaisesRegex(zlib.error,
173+
"Error -5 while decompressing data: incomplete or truncated stream",
174+
zlib.decompress, x[:-1])
175+
176+
def test_custom_bufsize(self):
177+
data = HAMLET_SCENE * 10
178+
compressed = zlib.compress(data, 1)
179+
self.assertEqual(zlib.decompress(compressed, 15, CustomInt()), data)
180+
181+
HAMLET_SCENE = b"""
182+
LAERTES
183+
184+
O, fear me not.
185+
I stay too long: but here my father comes.
186+
187+
Enter POLONIUS
188+
189+
A double blessing is a double grace,
190+
Occasion smiles upon a second leave.
191+
192+
LORD POLONIUS
193+
194+
Yet here, Laertes! aboard, aboard, for shame!
195+
The wind sits in the shoulder of your sail,
196+
And you are stay'd for. There; my blessing with thee!
197+
And these few precepts in thy memory
198+
See thou character. Give thy thoughts no tongue,
199+
Nor any unproportioned thought his act.
200+
Be thou familiar, but by no means vulgar.
201+
Those friends thou hast, and their adoption tried,
202+
Grapple them to thy soul with hoops of steel;
203+
But do not dull thy palm with entertainment
204+
Of each new-hatch'd, unfledged comrade. Beware
205+
Of entrance to a quarrel, but being in,
206+
Bear't that the opposed may beware of thee.
207+
Give every man thy ear, but few thy voice;
208+
Take each man's censure, but reserve thy judgment.
209+
Costly thy habit as thy purse can buy,
210+
But not express'd in fancy; rich, not gaudy;
211+
For the apparel oft proclaims the man,
212+
And they in France of the best rank and station
213+
Are of a most select and generous chief in that.
214+
Neither a borrower nor a lender be;
215+
For loan oft loses both itself and friend,
216+
And borrowing dulls the edge of husbandry.
217+
This above all: to thine ownself be true,
218+
And it must follow, as the night the day,
219+
Thou canst not then be false to any man.
220+
Farewell: my blessing season this in thee!
221+
222+
LAERTES
223+
224+
Most humbly do I take my leave, my lord.
225+
226+
LORD POLONIUS
227+
228+
The time invites you; go; your servants tend.
229+
230+
LAERTES
231+
232+
Farewell, Ophelia; and remember well
233+
What I have said to you.
234+
235+
OPHELIA
236+
237+
'Tis in my memory lock'd,
238+
And you yourself shall keep the key of it.
239+
240+
LAERTES
241+
242+
Farewell.
243+
"""

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@
8585
import com.oracle.graal.python.builtins.modules.UnicodeDataModuleBuiltins;
8686
import com.oracle.graal.python.builtins.modules.WeakRefModuleBuiltins;
8787
import com.oracle.graal.python.builtins.modules.ZipImportModuleBuiltins;
88+
import com.oracle.graal.python.builtins.modules.ZLibModuleBuiltins;
8889
import com.oracle.graal.python.builtins.objects.array.ArrayBuiltins;
8990
import com.oracle.graal.python.builtins.objects.bool.BoolBuiltins;
9091
import com.oracle.graal.python.builtins.objects.bytes.ByteArrayBuiltins;
@@ -313,7 +314,8 @@ private static final PythonBuiltins[] initializeBuiltins() {
313314
new PyExpatModuleBuiltins(),
314315
new SysConfigModuleBuiltins(),
315316
new ZipImporterBuiltins(),
316-
new ZipImportModuleBuiltins()));
317+
new ZipImportModuleBuiltins(),
318+
new ZLibModuleBuiltins()));
317319
if (!TruffleOptions.AOT) {
318320
ServiceLoader<PythonBuiltins> providers = ServiceLoader.load(PythonBuiltins.class);
319321
for (PythonBuiltins builtin : providers) {

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ public enum PythonBuiltinClassType implements LazyPythonClass {
137137
ProcessLookupError("ProcessLookupError", "builtins"),
138138
TimeoutError("TimeoutError", "builtins"),
139139
ZipImportError("ZipImportError", "zipimport"),
140+
ZLibError("error", "zlib"),
140141

141142
// todo: all OS errors
142143

@@ -267,6 +268,7 @@ public Shape getInstanceShape() {
267268
ProcessLookupError.base = OSError;
268269
TimeoutError.base = OSError;
269270
ZipImportError.base = ImportError;
271+
ZLibError.base = Exception;
270272

271273
ReferenceError.base = Exception;
272274
RuntimeError.base = Exception;

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ErrnoModuleBuiltins.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,5 +192,9 @@ public void initialize(PythonCore core) {
192192
builtinConstants.put("ENOTRECOVERABLE", 131);
193193
builtinConstants.put("ERFKILL", 132);
194194
builtinConstants.put("EHWPOISON", 133);
195+
196+
// added manually
197+
builtinConstants.put("EAGAIN", 11);
198+
builtinConstants.put("EWOULDBLOCK", 11);
195199
}
196200
}

0 commit comments

Comments
 (0)