Skip to content

Commit 93313f8

Browse files
committed
[GR-34605] Fixes for Pillow
PullRequest: graalpython/2101
2 parents 03613ea + f0a7f29 commit 93313f8

39 files changed

+657
-339
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ language runtime. The main focus is on user-observable behavior of the engine.
55

66
## Version 22.1.0
77
* String conversion (`__str__`) now calls `toString` for Java objects and `toDisplayString` interop message for foreign objects.
8-
* Improved compatibility with PyPI package `lxml`
8+
* Improved compatibility with PyPI packages `lxml`, `pytz`, `Pillow`
99

1010
## Version 22.0.0
1111
* Added support for `pyexpat` module.

graalpython/com.oracle.graal.python.cext/src/capi.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -42,6 +42,7 @@
4242

4343
void *PY_TRUFFLE_CEXT;
4444
void *PY_BUILTIN;
45+
void *PY_SYS;
4546
void *Py_NoValue;
4647

4748
void*(*pytruffle_decorate_function)(void *fun0, void* fun1);
@@ -72,6 +73,7 @@ __attribute__((constructor (__COUNTER__)))
7273
static void initialize_upcall_functions() {
7374
PY_TRUFFLE_CEXT = (void*)polyglot_eval("python", "import python_cext\npython_cext");
7475
PY_BUILTIN = (void*)polyglot_eval("python", "import builtins\nbuiltins");
76+
PY_SYS = (void*)polyglot_eval("python", "import sys\nsys");
7577

7678
pytruffle_decorate_function = ((void*(*)(void *fun0, void* fun1))polyglot_get_member(PY_TRUFFLE_CEXT, polyglot_from_string("PyTruffle_Decorate_Function", SRC_CS)));
7779

@@ -322,13 +324,18 @@ static void initialize_bufferprocs() {
322324
polyglot_invoke(PY_TRUFFLE_CEXT, "PyTruffle_SetBufferProcs", native_to_java((PyObject*)&PyMemoryView_Type), (getbufferproc)memoryview_getbuffer, (releasebufferproc)memoryview_releasebuffer);
323325
}
324326

327+
static void initialize_filesystemencoding() {
328+
Py_FileSystemDefaultEncoding = (const char *)to_sulong(polyglot_invoke(PY_SYS, "getfilesystemencoding"));
329+
}
330+
325331
__attribute__((constructor (20000)))
326332
static void initialize_capi() {
327333
// initialize global variables like '_Py_NoneStruct', etc.
328334
initialize_globals();
329335
initialize_exceptions();
330336
initialize_hashes();
331337
initialize_bufferprocs();
338+
initialize_filesystemencoding();
332339
}
333340

334341
// Workaround: use 'uint64' to avoid conversion to an LLVM boxed primitive such

graalpython/com.oracle.graal.python.cext/src/longobject.c

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -164,11 +164,10 @@ PyObject * PyLong_FromUnsignedLongLong(unsigned long long n) {
164164
return _jls_PyLong_FromLongLong(n, 0);
165165
}
166166

167-
typedef PyObject* (*fromVoidPtr_fun_t)(void*);
168-
UPCALL_TYPED_ID(PyLong_FromVoidPtr, fromVoidPtr_fun_t);
167+
typedef PyObject* (*fromVoidPtr_fun_t)(void*, int32_t);
169168
PyObject * PyLong_FromVoidPtr(void *p) {
170169
// directly do the upcall to avoid a cast to primitive and reference counting
171-
return _jls_PyLong_FromVoidPtr(p);
170+
return ((fromVoidPtr_fun_t)_jls_PyLong_FromLongLong)(p, 0);
172171
}
173172

174173
UPCALL_ID(PyLong_AsVoidPtr);
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* The Universal Permissive License (UPL), Version 1.0
6+
*
7+
* Subject to the condition set forth below, permission is hereby granted to any
8+
* person obtaining a copy of this software, associated documentation and/or
9+
* data (collectively the "Software"), free of charge and under any and all
10+
* copyright rights in the Software, and any and all patent rights owned or
11+
* freely licensable by each licensor hereunder covering either (i) the
12+
* unmodified Software as contributed to or provided by such licensor, or (ii)
13+
* the Larger Works (as defined below), to deal in both
14+
*
15+
* (a) the Software, and
16+
*
17+
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
18+
* one is included with the Software each a "Larger Work" to which the Software
19+
* is contributed by such licensors),
20+
*
21+
* without restriction, including without limitation the rights to copy, create
22+
* derivative works of, display, perform, and distribute the Software and make,
23+
* use, sell, offer for sale, import, export, have made, and have sold the
24+
* Software and the Larger Work(s), and to sublicense the foregoing rights on
25+
* either these or other terms.
26+
*
27+
* This license is subject to the following condition:
28+
*
29+
* The above copyright notice and either this complete permission notice or at a
30+
* minimum a reference to the UPL must be included in all copies or substantial
31+
* portions of the Software.
32+
*
33+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39+
* SOFTWARE.
40+
*/
41+
#include "capi.h"
42+
#include "fileobject.h"
43+
44+
const char *Py_FileSystemDefaultEncoding = NULL;

graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/parser/BasicTests.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -705,6 +705,16 @@ public void try11() throws Exception {
705705
" print(err)");
706706
}
707707

708+
@Test
709+
public void tryPrint() throws Exception {
710+
checkScopeAndTree(
711+
"try:\n" +
712+
" 1\n" +
713+
"except:\n" +
714+
" pass",
715+
PythonParser.ParserMode.Statement);
716+
}
717+
708718
@Test
709719
public void tuple01() throws Exception {
710720
checkTreeResult("(1, 2, 3)");
@@ -924,6 +934,14 @@ public void with06() throws Exception {
924934
" pass");
925935
}
926936

937+
@Test
938+
public void withPrint() throws Exception {
939+
checkScopeAndTree(
940+
"with A():\n" +
941+
" 1",
942+
PythonParser.ParserMode.Statement);
943+
}
944+
927945
@Test
928946
public void spaceEnd() throws Exception {
929947
checkTreeResult("x=5 ");

graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_method.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
1+
# Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved.
22
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
33
#
44
# The Universal Permissive License (UPL), Version 1.0
@@ -43,6 +43,15 @@
4343
__dir__ = __file__.rpartition("/")[0]
4444

4545

46+
def assert_raises(err, fn, *args, **kwargs):
47+
raised = False
48+
try:
49+
fn(*args, **kwargs)
50+
except err:
51+
raised = True
52+
assert raised
53+
54+
4655
def _reference_classmethod(args):
4756
if isinstance(args[0], type(list.append)):
4857
return classmethod(args[0])()
@@ -103,6 +112,16 @@ def test_methods(self):
103112
assert obj.meth_static_varargs(1, 2, 3) == (None, (1, 2, 3))
104113
assert obj.meth_static_varargs_keywords(1, 2, 3, a=1, b=2) == (None, (1, 2, 3), {'a': 1, 'b': 2})
105114

115+
assert_raises(TypeError, obj.meth_noargs, 1)
116+
assert_raises(TypeError, obj.meth_o)
117+
assert_raises(TypeError, obj.meth_o, 1, 2)
118+
assert_raises(TypeError, obj.meth_class_noargs, 1)
119+
assert_raises(TypeError, obj.meth_class_o)
120+
assert_raises(TypeError, obj.meth_class_o, 1, 2)
121+
assert_raises(TypeError, obj.meth_static_noargs, 1)
122+
assert_raises(TypeError, obj.meth_static_o)
123+
assert_raises(TypeError, obj.meth_static_o, 1, 2)
124+
106125

107126
class TestPyMethod(CPyExtTestCase):
108127
def compile_module(self, name):

graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_mmap.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
1+
# Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
22
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
33
#
44
# The Universal Permissive License (UPL), Version 1.0
@@ -85,3 +85,30 @@ def compile_module(self, name):
8585
callfunction="get_mmap_buf",
8686
cmpfunc=unhandled_error_compare
8787
)
88+
89+
# Exercises conversion to native and copying from the actual mmap pointer
90+
test_buffer_memcpy = CPyExtFunction(
91+
lambda args: b"hello, world",
92+
lambda: (
93+
(create_and_map_file(),),
94+
),
95+
code="""
96+
static PyObject* get_mmap_buf(PyObject* mmapObj) {
97+
Py_buffer buf;
98+
Py_ssize_t len, i;
99+
char* data = NULL;
100+
if (PyObject_GetBuffer(mmapObj, &buf, PyBUF_SIMPLE)) {
101+
return NULL;
102+
}
103+
len = buf.len;
104+
data = (char*) malloc(sizeof(char)*len);
105+
memcpy(data, buf.buf, len);
106+
return PyBytes_FromStringAndSize(data, len);
107+
}
108+
""",
109+
resultspec="O",
110+
argspec='O',
111+
arguments=["PyObject* mmapObj"],
112+
callfunction="get_mmap_buf",
113+
cmpfunc=unhandled_error_compare
114+
)

graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_modsupport.py

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved.
1+
# Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved.
22
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
33
#
44
# The Universal Permissive License (UPL), Version 1.0
@@ -61,15 +61,25 @@ def _reference_typecheck(args, expected_type):
6161

6262

6363
def _reference_parse_O(args):
64-
assert isinstance(args[0], tuple)
65-
assert isinstance(args[1], dict)
66-
if args[0]:
64+
if not isinstance(args[0], tuple) or not isinstance(args[1], dict):
65+
raise SystemError
66+
if len(args[0]) == 1:
6767
return args[0][0]
6868
elif "arg0" in args[1]:
6969
return args[1]["arg0"]
7070
raise TypeError
7171

7272

73+
def _reference_parse_tuple(args):
74+
try:
75+
t = args[0][0]
76+
if len(t) != 2:
77+
raise TypeError
78+
return t[0], t[1]
79+
except Exception:
80+
raise TypeError
81+
82+
7383
class Indexable:
7484
def __int__(self):
7585
return 456
@@ -78,6 +88,26 @@ def __index__(self):
7888
return 123
7989

8090

91+
class MySeq:
92+
def __len__(self):
93+
return 2
94+
95+
def __getitem__(self, item):
96+
if item == 0:
97+
return 'x'
98+
elif item == 1:
99+
return 'y'
100+
else:
101+
raise IndexError
102+
103+
104+
class BadSeq:
105+
def __len__(self):
106+
return 2
107+
108+
def __getitem__(self, item):
109+
raise IndexError
110+
81111
class TestModsupport(CPyExtTestCase):
82112
def compile_module(self, name):
83113
type(self).mro()[1].__dict__["test_%s" % name].create_module(name)
@@ -130,6 +160,9 @@ def compile_module(self, name):
130160
(tuple(), {"arg0": 'helloworld'}),
131161
(tuple(), dict()),
132162
(tuple(), {"arg1": 'helloworld'}),
163+
(1, dict()),
164+
(tuple(), 1),
165+
(("a", "excess"), dict()),
133166
),
134167
code='''
135168
static PyObject* wrap_PyArg_ParseTupleAndKeywords(PyObject* argTuple, PyObject* kwargs) {
@@ -149,6 +182,34 @@ def compile_module(self, name):
149182
cmpfunc=unhandled_error_compare
150183
)
151184

185+
test_parseargs_tuple = CPyExtFunction(
186+
_reference_parse_tuple,
187+
lambda: (
188+
((("a", "b"),),),
189+
((["a", "b"],),),
190+
((MySeq(),),),
191+
((["a"],),),
192+
((["a", "b", "c"],),),
193+
((1,),),
194+
((BadSeq(),),),
195+
),
196+
code='''
197+
static PyObject* wrap_PyArg_ParseTuple(PyObject* argTuple) {
198+
PyObject* a = NULL;
199+
PyObject* b = NULL;
200+
if (PyArg_ParseTuple(argTuple, "(OO)", &a, &b) == 0) {
201+
return NULL;
202+
}
203+
return Py_BuildValue("(OO)", a, b);
204+
}
205+
''',
206+
resultspec="O",
207+
argspec="O",
208+
arguments=["PyObject* argTuple"],
209+
callfunction="wrap_PyArg_ParseTuple",
210+
cmpfunc=unhandled_error_compare
211+
)
212+
152213
test_parseargs_O_conv = CPyExtFunction(
153214
lambda args: True if args[0][0] else False,
154215
lambda: (
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Scope: []
2+
Kind: Module
3+
FrameDescriptor: Empty
4+
CellVars: Empty
5+
FreeVars: Empty
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
ModuleRootNode Name: <module> SourceSection: [0,23]`try:↵ 1↵except:↵ p...`
2+
Signature: varArgs=False, varKeywordArgs=False, noArguments=True, positionalOnly=True, requiresKeywordArgs=False
3+
FreeVars: None
4+
NeedsCellFrame: False
5+
FrameDescriptor: Empty
6+
Documentation: None
7+
InnerRootNode SourceSection: [0,23]`try:↵ 1↵except:↵ p...`
8+
ExpressionWithSideEffect SourceSection: [0,23]`try:↵ 1↵except:↵ p...`
9+
Expression:
10+
EmptyNode SourceSection: None
11+
SideEffect:
12+
TryFinallyNode SourceSection: [0,23]`try:↵ 1↵except:↵ p...`
13+
TryExceptNode SourceSection: None
14+
ExpressionStatementNode SourceSection: None
15+
PrintExpressionNode SourceSection: None
16+
GetAttributeNode SourceSection: None
17+
GetFixedAttributeNodeGen SourceSection: None
18+
Key: displayhook
19+
LookupAndCallNonReversibleBinaryNodeGen SourceSection: None
20+
Op: __getattribute__
21+
CallNodeGen SourceSection: None
22+
IntegerLiteralNode SourceSection: [7,8]`1`
23+
Value: 1
24+
ExceptNode SourceSection: None
25+
ExpressionStatementNode SourceSection: [19,23]`pass`
26+
EmptyNode SourceSection: [19,23]`pass`
27+
BlockNode SourceSection: None

0 commit comments

Comments
 (0)