Skip to content

Commit 776e8b3

Browse files
committed
[GR-12099] Implement zipimporter module
PullRequest: graalpython/263
2 parents de01f4d + 4d67ce7 commit 776e8b3

File tree

16 files changed

+1156
-5
lines changed

16 files changed

+1156
-5
lines changed
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
# Copyright (c) 2018, Oracle and/or its affiliates.
2+
# Copyright (c) 2013, Regents of the University of California
3+
#
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
26+
27+
import sys
28+
import os
29+
import unittest
30+
import time
31+
import importlib
32+
33+
import zipimport
34+
from zipimport import ZipImportError
35+
36+
from test import support
37+
from zipfile import ZipFile, ZipInfo, ZIP_STORED, ZIP_DEFLATED
38+
39+
test_src = """\
40+
def get_name():
41+
return __name__
42+
def get_file():
43+
return __file__
44+
"""
45+
46+
ZIP_FILE_NAME = 'testzipfile.zip'
47+
DIR_PATH = os.path.dirname(os.path.realpath(__file__))
48+
ZIP_PATH = os.path.join(DIR_PATH, ZIP_FILE_NAME)
49+
ZIP_ABS_PATH = os.path.abspath(ZIP_PATH);
50+
51+
class ZipImportBaseTestCase(unittest.TestCase):
52+
53+
def setUp(self):
54+
zipimport._zip_directory_cache.clear()
55+
self.path = sys.path[:]
56+
self.meta_path = sys.meta_path[:]
57+
self.path_hooks = sys.path_hooks[:]
58+
sys.path_importer_cache.clear()
59+
self.modules_before = support.modules_setup()
60+
61+
def tearDown(self):
62+
sys.path[:] = self.path
63+
sys.meta_path[:] = self.meta_path
64+
sys.path_hooks[:] = self.path_hooks
65+
sys.path_importer_cache.clear()
66+
support.modules_cleanup(*self.modules_before)
67+
68+
class BasicZipImportTests(ZipImportBaseTestCase):
69+
70+
def setUp(self):
71+
ZipImportBaseTestCase.setUp(self)
72+
self.z = zipimport.zipimporter(ZIP_PATH)
73+
74+
def test_zipimporter_attribute(self):
75+
self.assertTrue(self.z.prefix == "")
76+
self.assertTrue(self.z.archive == ZIP_ABS_PATH)
77+
self.assertTrue(type(self.z._files) is dict)
78+
self.assertTrue(self.z._files["MyTestModule.py"] is not None)
79+
self.assertTrue(self.z._files["empty.txt"] is not None)
80+
self.assertTrue(self.z._files["packageA/moduleC.py"] is not None)
81+
self.assertTrue(self.z._files["cesta/moduleA.py"] is not None)
82+
83+
def test_create_zipimport_from_string(self):
84+
zipimport._zip_directory_cache.clear()
85+
z = zipimport.zipimporter(ZIP_PATH)
86+
self.assertTrue(zipimport._zip_directory_cache[ZIP_ABS_PATH] is not None)
87+
88+
def test_create_zipimport_from_bytes(self):
89+
zipimport._zip_directory_cache.clear()
90+
a = bytes(ZIP_PATH, 'UTF-8')
91+
z = zipimport.zipimporter(a)
92+
self.assertTrue(zipimport._zip_directory_cache[os.path.abspath(ZIP_PATH)] is not None)
93+
94+
def test_create_zipimport_from_pathlike(self):
95+
class MyPath():
96+
def __init__(self, path):
97+
self.value = path
98+
def __fspath__(self):
99+
return self.value
100+
101+
zipimport._zip_directory_cache.clear()
102+
mp = MyPath(ZIP_PATH)
103+
if (sys.version_info.major >= 3 and sys.version_info.minor >= 6):
104+
z = zipimport.zipimporter(mp)
105+
self.assertTrue(zipimport._zip_directory_cache[os.path.abspath(ZIP_PATH)] is not None)
106+
107+
def test_zipimporter_find_module(self):
108+
self.assertTrue(self.z is self.z.find_module("MyTestModule"))
109+
self.assertTrue(self.z is self.z.find_module("packageA"))
110+
self.assertTrue(self.z is self.z.find_module("packageA/moduleC"))
111+
self.assertTrue(None is self.z.find_module("packageA.moduleC"))
112+
self.assertTrue(self.z is self.z.find_module("cesta/moduleA"))
113+
114+
def test_zipimporter_get_code(self):
115+
self.assertTrue(self.z.get_code("MyTestModule").co_filename.endswith("MyTestModule.py"))
116+
self.assertTrue(self.z.get_code("packageA").co_filename.endswith("packageA/__init__.py"))
117+
self.assertTrue(self.z.get_code("packageA/moduleC").co_filename.endswith("packageA/moduleC.py"))
118+
self.assertTrue(self.z.get_code("cesta/moduleA").co_filename.endswith("cesta/moduleA.py"))
119+
self.assertRaises(ZipImportError, self.z.get_code, "wrongname")
120+
self.assertRaises(ZipImportError, self.z.get_code, "")
121+
122+
def test_zipimporter_get_data(self):
123+
self.assertTrue(type(self.z.get_data("MyTestModule.py")) is bytes)
124+
self.assertTrue(type(self.z.get_data("packageA/moduleC.py")) is bytes)
125+
self.assertTrue(type(self.z.get_data("cesta/moduleA.py")) is bytes)
126+
self.assertRaises(OSError, self.z.get_data, "")
127+
self.assertRaises(OSError, self.z.get_data, "MyTestModule")
128+
self.assertRaises(OSError, self.z.get_data, "packageA")
129+
self.assertTrue(type(self.z.get_data(ZIP_ABS_PATH + "/MyTestModule.py")) is bytes)
130+
self.assertTrue(type(self.z.get_data(ZIP_ABS_PATH + "/packageA/moduleC.py")) is bytes)
131+
self.assertTrue(type(self.z.get_data(ZIP_ABS_PATH + "/cesta/moduleA.py")) is bytes)
132+
self.assertTrue(type(self.z.get_data(ZIP_ABS_PATH + "/read.me")) is bytes)
133+
self.assertTrue(type(self.z.get_data("empty.txt")) is bytes)
134+
self.assertRaises(OSError, self.z.get_data, "/empty.txt")
135+
136+
def test_zipimporter_get_filename(self):
137+
self.assertEqual(self.z.get_filename("packageA"), ZIP_ABS_PATH + "/packageA/__init__.py")
138+
self.assertEqual(self.z.get_filename("MyTestModule"), ZIP_ABS_PATH + "/MyTestModule.py")
139+
self.assertRaises(ZipImportError, self.z.get_filename, "empty.txt")
140+
self.assertRaises(ZipImportError, self.z.get_filename, "empty")
141+
142+
def test_zipimporter_get_source(self):
143+
self.assertTrue(type(self.z.get_source("MyTestModule")) is str)
144+
self.assertTrue(type(self.z.get_source("packageA")) is str)
145+
self.assertTrue(type(self.z.get_source("packageA/moduleC")) is str)
146+
self.assertRaises(ZipImportError, self.z.get_source, "packageA.moduleC")
147+
148+
def test_zipimporter_is_package(self):
149+
self.assertTrue(self.z.is_package("packageA"))
150+
self.assertFalse(self.z.is_package("MyTestModule"))
151+
self.assertFalse(self.z.is_package("packageA/moduleC"))
152+
self.assertRaises(ZipImportError, self.z.is_package, "empty")
153+
self.assertRaises(ZipImportError, self.z.is_package, "cesta")
154+
self.assertRaises(ZipImportError, self.z.is_package, "packageA.moduleC")
155+
156+
def test_zipimporter_load_module(self):
157+
self.assertTrue(self.z.load_module("MyTestModule").__loader__ is self.z)
158+
self.assertTrue(self.z.load_module("packageA").__loader__ is self.z)
159+
self.assertTrue(self.z.load_module("packageA/moduleC").__loader__ is self.z)
160+
self.assertTrue(self.z.load_module("cesta/moduleA").__loader__ is self.z)
161+
self.assertRaises(ZipImportError, self.z.load_module, "packageA.moduleC")
162+
163+
class ZipImportWithPrefixTests(ZipImportBaseTestCase):
164+
165+
def setUp(self):
166+
ZipImportBaseTestCase.setUp(self)
167+
self.z = zipimport.zipimporter(ZIP_PATH + "/cesta")
168+
169+
def tearDown(self):
170+
zipimport._zip_directory_cache.clear()
171+
172+
def test_zipimporter_with_prefix_attribute(self):
173+
self.assertTrue(self.z.prefix == "cesta/")
174+
self.assertTrue(self.z.archive == ZIP_ABS_PATH)
175+
self.assertTrue(type(self.z._files) is dict)
176+
self.assertTrue(self.z._files["MyTestModule.py"] is not None)
177+
self.assertTrue(self.z._files["empty.txt"] is not None)
178+
self.assertTrue(self.z._files["packageA/moduleC.py"] is not None)
179+
self.assertTrue(self.z._files["cesta/moduleA.py"] is not None)
180+
181+
def test_zipimporter_with_prefix_find_module(self):
182+
self.assertTrue(None is self.z.find_module("MyTestModule"))
183+
self.assertTrue(None is self.z.find_module("packageA"))
184+
self.assertTrue(None is self.z.find_module("packageA/moduleC"))
185+
self.assertTrue(None is self.z.find_module("packageA.moduleC"))
186+
self.assertTrue(self.z is self.z.find_module("moduleA"))
187+
188+
class ImportTests(ZipImportBaseTestCase):
189+
190+
def setUp(self):
191+
ZipImportBaseTestCase.setUp(self)
192+
sys.path.insert(0, ZIP_PATH)
193+
194+
def test_module_import(self):
195+
m = importlib.import_module("MyTestModule")
196+
self.assertTrue (m.get_file() == ZIP_ABS_PATH + "/MyTestModule.py")
197+
p = importlib.import_module("packageA.moduleC")
198+
self.assertTrue (p.get_file() == ZIP_ABS_PATH + "/packageA/moduleC.py")
Binary file not shown.

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@
8383
import com.oracle.graal.python.builtins.modules.TruffleCextBuiltins;
8484
import com.oracle.graal.python.builtins.modules.UnicodeDataModuleBuiltins;
8585
import com.oracle.graal.python.builtins.modules.WeakRefModuleBuiltins;
86+
import com.oracle.graal.python.builtins.modules.ZipImportModuleBuiltins;
8687
import com.oracle.graal.python.builtins.objects.array.ArrayBuiltins;
8788
import com.oracle.graal.python.builtins.objects.bool.BoolBuiltins;
8889
import com.oracle.graal.python.builtins.objects.bytes.ByteArrayBuiltins;
@@ -145,6 +146,7 @@
145146
import com.oracle.graal.python.builtins.objects.type.PythonBuiltinClass;
146147
import com.oracle.graal.python.builtins.objects.type.PythonClass;
147148
import com.oracle.graal.python.builtins.objects.type.TypeBuiltins;
149+
import com.oracle.graal.python.builtins.objects.zipimporter.ZipImporterBuiltins;
148150
import com.oracle.graal.python.runtime.PythonContext;
149151
import com.oracle.graal.python.runtime.PythonCore;
150152
import com.oracle.graal.python.runtime.PythonOptions;
@@ -209,7 +211,8 @@ private static final String[] initializeCoreFiles() {
209211
"function",
210212
"_sysconfig",
211213
"_socket",
212-
"_thread"));
214+
"_thread",
215+
"zipimport"));
213216

214217
return coreFiles.toArray(new String[coreFiles.size()]);
215218
}
@@ -302,7 +305,9 @@ private static final PythonBuiltins[] initializeBuiltins() {
302305
new CtypesModuleBuiltins(),
303306
new ReadlineModuleBuiltins(),
304307
new PyExpatModuleBuiltins(),
305-
new SysConfigModuleBuiltins()));
308+
new SysConfigModuleBuiltins(),
309+
new ZipImporterBuiltins(),
310+
new ZipImportModuleBuiltins()));
306311
if (!TruffleOptions.AOT) {
307312
ServiceLoader<PythonBuiltins> providers = ServiceLoader.load(PythonBuiltins.class);
308313
for (PythonBuiltins builtin : providers) {

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ public enum PythonBuiltinClassType implements LazyPythonClass {
8585
Super("super", "builtins"),
8686
PCode("code"),
8787
PZip("zip", "builtins"),
88+
PZipImporter("zipimporter", "zipimport"),
8889
PBuffer("buffer", "builtins"),
8990
PThread("start_new_thread", "_thread"),
9091
PLock("LockType", "_thread"),
@@ -133,6 +134,7 @@ public enum PythonBuiltinClassType implements LazyPythonClass {
133134
PermissionError("PermissionError", "builtins"),
134135
ProcessLookupError("ProcessLookupError", "builtins"),
135136
TimeoutError("TimeoutError", "builtins"),
137+
ZipImportError("ZipImportError", "zipimport"),
136138

137139
// todo: all OS errors
138140

@@ -262,6 +264,7 @@ public Shape getInstanceShape() {
262264
PermissionError.base = OSError;
263265
ProcessLookupError.base = OSError;
264266
TimeoutError.base = OSError;
267+
ZipImportError.base = ImportError;
265268

266269
ReferenceError.base = Exception;
267270
RuntimeError.base = Exception;

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ public void initialize(PythonCore core) {
164164
builtinConstants.put("NotImplemented", PNotImplemented.NOT_IMPLEMENTED);
165165
}
166166

167+
@TypeSystemReference(PythonArithmeticTypes.class)
167168
protected abstract static class CreateByteOrByteArrayNode extends PythonBuiltinNode {
168169
@Child private IsIndexNode isIndexNode;
169170
@Child private CastToIndexNode castToIndexNode;

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@
116116
import com.oracle.graal.python.nodes.GraalPythonTranslationErrorNode;
117117
import com.oracle.graal.python.nodes.PGuards;
118118
import com.oracle.graal.python.nodes.SpecialMethodNames;
119+
import com.oracle.graal.python.nodes.argument.ReadArgumentNode;
119120
import com.oracle.graal.python.nodes.argument.ReadIndexedArgumentNode;
120121
import com.oracle.graal.python.nodes.argument.ReadVarArgsNode;
121122
import com.oracle.graal.python.nodes.attributes.DeleteAttributeNode;
@@ -599,6 +600,9 @@ private static Object evalNode(RootCallTarget callTarget, PythonObject globals,
599600
@GenerateNodeFactory
600601
@TypeSystemReference(PythonArithmeticTypes.class)
601602
public abstract static class CompileNode extends PythonBuiltinNode {
603+
604+
public abstract Object execute(Object source, String filename, String mode, Object kwFlags, Object kwDontInherit, Object kwOptimize);
605+
602606
@Specialization
603607
@TruffleBoundary
604608
Object compile(PBytes source, String filename, String mode, Object kwFlags, Object kwDontInherit, Object kwOptimize,
@@ -640,6 +644,10 @@ Object compile(String expression, String filename, String mode, Object kwFlags,
640644
Object compile(PCode code, String filename, String mode, Object flags, Object dontInherit, Object optimize) {
641645
return code;
642646
}
647+
648+
public static CompileNode create() {
649+
return BuiltinFunctionsFactory.CompileNodeFactory.create(new ReadArgumentNode[]{});
650+
}
643651
}
644652

645653
// delattr(object, name)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1090,7 +1090,7 @@ int system(String cmd) {
10901090
}
10911091
}
10921092

1093-
abstract static class ConvertPathlikeObjectNode extends PNodeWithContext {
1093+
public abstract static class ConvertPathlikeObjectNode extends PNodeWithContext {
10941094
@Child private LookupAndCallUnaryNode callFspathNode;
10951095
@CompilationFinal private ValueProfile resultTypeProfile;
10961096

0 commit comments

Comments
 (0)