Skip to content

Commit f0b4ebb

Browse files
committed
[GR-12099] Implement zipimporter module.
Initial implementation
1 parent 85c1e74 commit f0b4ebb

File tree

13 files changed

+1104
-2
lines changed

13 files changed

+1104
-2
lines changed
Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
# Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
2+
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3+
#
4+
# The Universal Permissive License (UPL), Version 1.0
5+
#
6+
# Subject to the condition set forth below, permission is hereby granted to any
7+
# person obtaining a copy of this software, associated documentation and/or
8+
# data (collectively the "Software"), free of charge and under any and all
9+
# copyright rights in the Software, and any and all patent rights owned or
10+
# freely licensable by each licensor hereunder covering either (i) the
11+
# unmodified Software as contributed to or provided by such licensor, or (ii)
12+
# the Larger Works (as defined below), to deal in both
13+
#
14+
# (a) the Software, and
15+
#
16+
# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
17+
# one is included with the Software each a "Larger Work" to which the Software
18+
# is contributed by such licensors),
19+
#
20+
# without restriction, including without limitation the rights to copy, create
21+
# derivative works of, display, perform, and distribute the Software and make,
22+
# use, sell, offer for sale, import, export, have made, and have sold the
23+
# Software and the Larger Work(s), and to sublicense the foregoing rights on
24+
# either these or other terms.
25+
#
26+
# This license is subject to the following condition:
27+
#
28+
# The above copyright notice and either this complete permission notice or at a
29+
# minimum a reference to the UPL must be included in all copies or substantial
30+
# portions of the Software.
31+
#
32+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
33+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
34+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
35+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
36+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
37+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
38+
# SOFTWARE.
39+
40+
import sys
41+
import os
42+
import unittest
43+
import time
44+
import importlib
45+
46+
import zipimport
47+
from zipimport import ZipImportError
48+
49+
50+
from test import support
51+
from zipfile import ZipFile, ZipInfo, ZIP_STORED, ZIP_DEFLATED
52+
53+
54+
test_src = """\
55+
def get_name():
56+
return __name__
57+
def get_file():
58+
return __file__
59+
"""
60+
61+
ZIP_FILE_NAME = 'testzipfile.zip'
62+
DIR_PATH = os.path.dirname(os.path.realpath(__file__))
63+
ZIP_PATH = os.path.join(DIR_PATH, ZIP_FILE_NAME)
64+
ZIP_ABS_PATH = os.path.abspath(ZIP_PATH);
65+
66+
67+
class ZipImportBaseTestCase(unittest.TestCase):
68+
69+
def setUp(self):
70+
zipimport._zip_directory_cache.clear()
71+
self.path = sys.path[:]
72+
self.meta_path = sys.meta_path[:]
73+
self.path_hooks = sys.path_hooks[:]
74+
sys.path_importer_cache.clear()
75+
self.modules_before = support.modules_setup()
76+
77+
def tearDown(self):
78+
sys.path[:] = self.path
79+
sys.meta_path[:] = self.meta_path
80+
sys.path_hooks[:] = self.path_hooks
81+
sys.path_importer_cache.clear()
82+
support.modules_cleanup(*self.modules_before)
83+
84+
class BasicZipImportTests(ZipImportBaseTestCase):
85+
86+
def setUp(self):
87+
ZipImportBaseTestCase.setUp(self)
88+
self.z = zipimport.zipimporter(ZIP_PATH)
89+
90+
91+
def test_zipimporter_attribute(self):
92+
self.assertTrue(self.z.prefix == "")
93+
self.assertTrue(self.z.archive == ZIP_ABS_PATH)
94+
self.assertTrue(type(self.z._files) is dict)
95+
self.assertTrue(self.z._files["MyTestModule.py"] is not None)
96+
self.assertTrue(self.z._files["empty.txt"] is not None)
97+
self.assertTrue(self.z._files["packageA/moduleC.py"] is not None)
98+
self.assertTrue(self.z._files["cesta/moduleA.py"] is not None)
99+
100+
def test_create_zipimport_from_string(self):
101+
zipimport._zip_directory_cache.clear()
102+
z = zipimport.zipimporter(ZIP_PATH)
103+
self.assertTrue(zipimport._zip_directory_cache[ZIP_ABS_PATH] is not None)
104+
105+
def test_create_zipimport_from_bytes(self):
106+
zipimport._zip_directory_cache.clear()
107+
a = bytes(ZIP_PATH, 'UTF-8')
108+
z = zipimport.zipimporter(a)
109+
self.assertTrue(zipimport._zip_directory_cache[os.path.abspath(ZIP_PATH)] is not None)
110+
111+
def test_create_zipimport_from_pathlike(self):
112+
class MyPath():
113+
def __init__(self, path):
114+
self.value = path
115+
def __fspath__(self):
116+
return self.value
117+
118+
zipimport._zip_directory_cache.clear()
119+
mp = MyPath(ZIP_PATH)
120+
z = zipimport.zipimporter(mp)
121+
self.assertTrue(zipimport._zip_directory_cache[os.path.abspath(ZIP_PATH)] is not None)
122+
123+
def test_zipimporter_find_module(self):
124+
self.assertTrue(self.z is self.z.find_module("MyTestModule"))
125+
self.assertTrue(self.z is self.z.find_module("packageA"))
126+
self.assertTrue(self.z is self.z.find_module("packageA/moduleC"))
127+
self.assertTrue(None is self.z.find_module("packageA.moduleC"))
128+
self.assertTrue(self.z is self.z.find_module("cesta/moduleA"))
129+
130+
def test_zipimporter_get_code(self):
131+
self.assertTrue(self.z.get_code("MyTestModule").co_filename.endswith("MyTestModule.py"))
132+
self.assertTrue(self.z.get_code("packageA").co_filename.endswith("packageA/__init__.py"))
133+
self.assertTrue(self.z.get_code("packageA/moduleC").co_filename.endswith("packageA/moduleC.py"))
134+
self.assertTrue(self.z.get_code("cesta/moduleA").co_filename.endswith("cesta/moduleA.py"))
135+
self.assertRaises(ZipImportError, self.z.get_code, "wrongname")
136+
self.assertRaises(ZipImportError, self.z.get_code, "")
137+
138+
def test_zipimporter_get_data(self):
139+
self.assertTrue(type(self.z.get_data("MyTestModule.py")) is bytes)
140+
self.assertTrue(type(self.z.get_data("packageA/moduleC.py")) is bytes)
141+
self.assertTrue(type(self.z.get_data("cesta/moduleA.py")) is bytes)
142+
self.assertRaises(OSError, self.z.get_data, "")
143+
self.assertRaises(OSError, self.z.get_data, "MyTestModule")
144+
self.assertRaises(OSError, self.z.get_data, "packageA")
145+
self.assertTrue(type(self.z.get_data(ZIP_ABS_PATH + "/MyTestModule.py")) is bytes)
146+
self.assertTrue(type(self.z.get_data(ZIP_ABS_PATH + "/packageA/moduleC.py")) is bytes)
147+
self.assertTrue(type(self.z.get_data(ZIP_ABS_PATH + "/cesta/moduleA.py")) is bytes)
148+
self.assertTrue(type(self.z.get_data(ZIP_ABS_PATH + "/read.me")) is bytes)
149+
self.assertTrue(type(self.z.get_data("empty.txt")) is bytes)
150+
self.assertRaises(OSError, self.z.get_data, "/empty.txt")
151+
152+
def test_zipimporter_get_filename(self):
153+
self.assertEqual(self.z.get_filename("packageA"), ZIP_ABS_PATH + "/packageA/__init__.py")
154+
self.assertEqual(self.z.get_filename("MyTestModule"), ZIP_ABS_PATH + "/MyTestModule.py")
155+
self.assertRaises(ZipImportError, self.z.get_filename, "empty.txt")
156+
self.assertRaises(ZipImportError, self.z.get_filename, "empty")
157+
158+
def test_zipimporter_get_source(self):
159+
self.assertTrue(type(self.z.get_source("MyTestModule")) is str)
160+
self.assertTrue(type(self.z.get_source("packageA")) is str)
161+
self.assertTrue(type(self.z.get_source("packageA/moduleC")) is str)
162+
self.assertRaises(ZipImportError, self.z.get_source, "packageA.moduleC")
163+
164+
def test_zipimporter_is_package(self):
165+
self.assertTrue(self.z.is_package("packageA"))
166+
self.assertFalse(self.z.is_package("MyTestModule"))
167+
self.assertFalse(self.z.is_package("packageA/moduleC"))
168+
self.assertRaises(ZipImportError, self.z.is_package, "empty")
169+
self.assertRaises(ZipImportError, self.z.is_package, "cesta")
170+
self.assertRaises(ZipImportError, self.z.is_package, "packageA.moduleC")
171+
172+
def test_zipimporter_load_module(self):
173+
self.assertTrue(self.z.load_module("MyTestModule").__loader__ is self.z)
174+
self.assertTrue(self.z.load_module("packageA").__loader__ is self.z)
175+
self.assertTrue(self.z.load_module("packageA/moduleC").__loader__ is self.z)
176+
self.assertTrue(self.z.load_module("cesta/moduleA").__loader__ is self.z)
177+
self.assertRaises(ZipImportError, self.z.load_module, "packageA.moduleC")
178+
179+
class ZipImportWithPrefixTests(ZipImportBaseTestCase):
180+
181+
def setUp(self):
182+
ZipImportBaseTestCase.setUp(self)
183+
self.z = zipimport.zipimporter(ZIP_PATH + "/cesta")
184+
185+
def tearDown(self):
186+
zipimport._zip_directory_cache.clear()
187+
188+
def test_zipimporter_with_prefix_attribute(self):
189+
self.assertTrue(self.z.prefix == "cesta/")
190+
self.assertTrue(self.z.archive == ZIP_ABS_PATH)
191+
self.assertTrue(type(self.z._files) is dict)
192+
self.assertTrue(self.z._files["MyTestModule.py"] is not None)
193+
self.assertTrue(self.z._files["empty.txt"] is not None)
194+
self.assertTrue(self.z._files["packageA/moduleC.py"] is not None)
195+
self.assertTrue(self.z._files["cesta/moduleA.py"] is not None)
196+
197+
def test_zipimporter_with_prefix_find_module(self):
198+
self.assertTrue(None is self.z.find_module("MyTestModule"))
199+
self.assertTrue(None is self.z.find_module("packageA"))
200+
self.assertTrue(None is self.z.find_module("packageA/moduleC"))
201+
self.assertTrue(None is self.z.find_module("packageA.moduleC"))
202+
self.assertTrue(self.z is self.z.find_module("moduleA"))
203+
204+
class ImportTests(ZipImportBaseTestCase):
205+
206+
def setUp(self):
207+
ZipImportBaseTestCase.setUp(self)
208+
sys.path.insert(0, ZIP_PATH)
209+
210+
def test_module_import(self):
211+
m = importlib.import_module("MyTestModule")
212+
self.assertTrue (m.get_file() == ZIP_ABS_PATH + "/MyTestModule.py")
213+
p = importlib.import_module("packageA.moduleC")
214+
self.assertTrue (p.get_file() == ZIP_ABS_PATH + "/packageA/moduleC.py")
215+
216+

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@
8181
import com.oracle.graal.python.builtins.modules.TruffleCextBuiltins;
8282
import com.oracle.graal.python.builtins.modules.UnicodeDataModuleBuiltins;
8383
import com.oracle.graal.python.builtins.modules.WeakRefModuleBuiltins;
84+
import com.oracle.graal.python.builtins.modules.ZipImportModuleBuiltins;
8485
import com.oracle.graal.python.builtins.objects.array.ArrayBuiltins;
8586
import com.oracle.graal.python.builtins.objects.bool.BoolBuiltins;
8687
import com.oracle.graal.python.builtins.objects.bytes.ByteArrayBuiltins;
@@ -140,6 +141,7 @@
140141
import com.oracle.graal.python.builtins.objects.type.PythonBuiltinClass;
141142
import com.oracle.graal.python.builtins.objects.type.PythonClass;
142143
import com.oracle.graal.python.builtins.objects.type.TypeBuiltins;
144+
import com.oracle.graal.python.builtins.objects.zipimporter.ZipImporterBuiltins;
143145
import com.oracle.graal.python.runtime.PythonContext;
144146
import com.oracle.graal.python.runtime.PythonCore;
145147
import com.oracle.graal.python.runtime.PythonOptions;
@@ -200,6 +202,7 @@ public final class Python3Core implements PythonCore {
200202
"_sre",
201203
"function",
202204
"_socket",
205+
"zipimport",
203206
};
204207

205208
private final PythonBuiltins[] builtins;
@@ -289,7 +292,9 @@ private static final PythonBuiltins[] initializeBuiltins() {
289292
new PosixSubprocessModuleBuiltins(),
290293
new CtypesModuleBuiltins(),
291294
new ReadlineModuleBuiltins(),
292-
new PyExpatModuleBuiltins()));
295+
new PyExpatModuleBuiltins(),
296+
new ZipImporterBuiltins(),
297+
new ZipImportModuleBuiltins()));
293298
if (!TruffleOptions.AOT) {
294299
ServiceLoader<PythonBuiltins> providers = ServiceLoader.load(PythonBuiltins.class);
295300
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
PSocket("socket", "_socket"),
9091

@@ -130,6 +131,7 @@ public enum PythonBuiltinClassType implements LazyPythonClass {
130131
PermissionError("PermissionError", "builtins"),
131132
ProcessLookupError("ProcessLookupError", "builtins"),
132133
TimeoutError("TimeoutError", "builtins"),
134+
ZipImportError("ZipImportError", "zipimport"),
133135

134136
// todo: all OS errors
135137

@@ -259,6 +261,7 @@ public Shape getInstanceShape() {
259261
PermissionError.base = OSError;
260262
ProcessLookupError.base = OSError;
261263
TimeoutError.base = OSError;
264+
ZipImportError.base = ImportError;
262265

263266
ReferenceError.base = Exception;
264267
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
@@ -162,6 +162,7 @@ public void initialize(PythonCore core) {
162162
builtinConstants.put("NotImplemented", PNotImplemented.NOT_IMPLEMENTED);
163163
}
164164

165+
@TypeSystemReference(PythonArithmeticTypes.class)
165166
protected abstract static class CreateByteOrByteArrayNode extends PythonBuiltinNode {
166167
@Child private IsIndexNode isIndexNode;
167168
@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)