Skip to content

Commit addbbe8

Browse files
committed
[GR-27305] Initial support for matplotlib.
PullRequest: graalpython/1425
2 parents 32eff74 + d4ef36b commit addbbe8

File tree

20 files changed

+1421
-132
lines changed

20 files changed

+1421
-132
lines changed

graalpython/com.oracle.graal.python.cext/include/pyconfig.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2017, 2020, 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
@@ -62,6 +62,13 @@
6262
#define SIZEOF_VOID_P __SIZEOF_POINTER__
6363
#define SIZEOF_WCHAR_T __SIZEOF_WCHAR_T__
6464
#define SIZEOF__BOOL 1
65+
66+
/* Define if your compiler supports function prototype */
67+
#define HAVE_PROTOTYPES 1
68+
69+
/* Define to 1 if you have the ANSI C header files. */
70+
#define STDC_HEADERS 1
71+
6572
# ifndef UINT_MAX
6673
#define UINT_MAX __UINT32_MAX__
6774
#endif

graalpython/com.oracle.graal.python.cext/setup.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959

6060
verbosity = '--verbose' if sys.flags.verbose else '--quiet'
6161
darwin_native = sys.platform == "darwin" and __graalpython__.platform_id == "native"
62-
relative_rpath = "@loader_path" if darwin_native else r"\$ORIGIN"
62+
relative_rpath = "@loader_path" if darwin_native else r"$ORIGIN"
6363
so_ext = get_config_var("EXT_SUFFIX")
6464
SOABI = get_config_var("SOABI")
6565
is_managed = 'managed' in SOABI
@@ -164,9 +164,10 @@ def _single_compile(obj):
164164
logger.debug("Compiling {!s}".format(src))
165165
self._compile(obj, src, ext, cc_args, extra_postargs, pp_opts)
166166

167-
if len(objects) > 1:
168-
logger.debug("Compiling {} objects in parallel.".format(len(objects)))
169-
pool = SimpleThreadPool()
167+
n_objects = len(objects)
168+
if n_objects > 1:
169+
logger.debug("Compiling {} objects in parallel.".format(n_objects))
170+
pool = SimpleThreadPool(min(n_objects, os.cpu_count()))
170171
pool.start_thread_pool(_single_compile)
171172
pool.put_job(objects)
172173
pool.wait_until_finished()

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

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ PyTypeObject PyBytes_Type = PY_TRUFFLE_TYPE_WITH_ITEMSIZE("bytes", &PyType_Type,
5050

5151
typedef PyObject* (*fromStringAndSize_fun_t)(int8_t* str, int64_t sz);
5252

53+
UPCALL_ID(PyBytes_Size);
54+
Py_ssize_t PyBytes_Size(PyObject *bytes) {
55+
return UPCALL_CEXT_L(_jls_PyBytes_Size, native_to_java(bytes));
56+
}
57+
5358
UPCALL_ID(PyBytes_FromStringAndSize);
5459
UPCALL_ID(PyTruffle_Bytes_EmptyWithCapacity);
5560
PyObject* PyBytes_FromStringAndSize(const char* str, Py_ssize_t sz) {
@@ -71,22 +76,16 @@ char* PyBytes_AsString(PyObject *obj) {
7176
return (char*)(UPCALL_CEXT_NOCAST(_jls_PyTruffle_Bytes_AsString, native_to_java(obj), ERROR_MARKER));
7277
}
7378

74-
UPCALL_ID(PyBytes_AsStringCheckEmbeddedNull);
79+
UPCALL_ID(PyTruffle_Bytes_CheckEmbeddedNull);
7580
int PyBytes_AsStringAndSize(PyObject *obj, char **s, Py_ssize_t *len) {
76-
setlocale(LC_ALL, NULL);
77-
const char* encoding = nl_langinfo(CODESET);
78-
PyObject *result = UPCALL_CEXT_O(_jls_PyBytes_AsStringCheckEmbeddedNull, native_to_java(obj), polyglot_from_string(encoding, SRC_CS));
79-
if(result == NULL) {
80-
return -1;
81-
}
82-
83-
*s = (char *)as_char_pointer(result);
84-
81+
PyObject* resolved = native_to_java(obj);
82+
*s = (char*)(UPCALL_CEXT_NOCAST(_jls_PyTruffle_Bytes_AsString, resolved, ERROR_MARKER));
8583
if (len != NULL) {
86-
*len = polyglot_as_i64(polyglot_invoke(PY_TRUFFLE_CEXT, "PyTruffle_Object_LEN", native_to_java(obj)));
84+
*len = UPCALL_CEXT_L(_jls_PyBytes_Size, resolved);
85+
return 0;
86+
} else {
87+
return UPCALL_CEXT_I(_jls_PyTruffle_Bytes_CheckEmbeddedNull, resolved);
8788
}
88-
89-
return 0;
9089
}
9190

9291
PyObject * PyBytes_FromFormat(const char *format, ...) {
@@ -261,11 +260,6 @@ void PyBytes_ConcatAndDel(PyObject **bytes, PyObject *newpart) {
261260
Py_DECREF(newpart);
262261
}
263262

264-
UPCALL_ID(PyBytes_Size);
265-
Py_ssize_t PyBytes_Size(PyObject *bytes) {
266-
return UPCALL_CEXT_L(_jls_PyBytes_Size, native_to_java(bytes));
267-
}
268-
269263
int bytes_buffer_getbuffer(PyBytesObject *self, Py_buffer *view, int flags) {
270264
return PyBuffer_FillInfo(view, (PyObject*)self, (void *)self->ob_sval, Py_SIZE(self), 1, flags);
271265
}

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

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@
4242

4343
#include <stdio.h>
4444

45+
#define FLAG_SIZE_T 1
46+
4547
static int getbuffer(PyObject *arg, Py_buffer *view, const char **errmsg) {
4648
if (PyObject_GetBuffer(arg, view, PyBUF_SIMPLE) != 0) {
4749
*errmsg = "bytes-like object";
@@ -287,7 +289,7 @@ typedef struct _build_stack {
287289
struct _build_stack* prev;
288290
} build_stack;
289291

290-
MUST_INLINE static PyObject* _PyTruffle_BuildValue(const char* format, va_list va) {
292+
MUST_INLINE static PyObject* _PyTruffle_BuildValue(const char* format, va_list va, int flags) {
291293
PyObject* (*converter)(void*) = NULL;
292294
int offset = 0;
293295
char argchar[2] = {'\0'};
@@ -343,8 +345,36 @@ MUST_INLINE static PyObject* _PyTruffle_BuildValue(const char* format, va_list v
343345
}
344346
break;
345347
case 'u':
346-
fprintf(stderr, "error: unsupported format 'u'\n");
347-
break;
348+
{
349+
PyObject *v;
350+
Py_UNICODE *u = va_arg(va, Py_UNICODE *);
351+
Py_ssize_t n;
352+
if (format[format_idx + 1] == '#') {
353+
if (flags & FLAG_SIZE_T)
354+
n = va_arg(va, Py_ssize_t);
355+
else {
356+
n = va_arg(va, int);
357+
if (PyErr_WarnEx(PyExc_DeprecationWarning,
358+
"PY_SSIZE_T_CLEAN will be required for '#' formats", 1)) {
359+
return NULL;
360+
}
361+
}
362+
format_idx++;
363+
} else {
364+
n = -1;
365+
}
366+
if (u == NULL) {
367+
v = Py_None;
368+
Py_INCREF(v);
369+
}
370+
else {
371+
if (n < 0) {
372+
n = wcslen(u);
373+
}
374+
v = PyUnicode_FromWideChar(u, n);
375+
}
376+
return v;
377+
}
348378
case 'i':
349379
case 'b':
350380
case 'h':
@@ -467,12 +497,14 @@ MUST_INLINE static PyObject* _PyTruffle_BuildValue(const char* format, va_list v
467497
break;
468498
case ':':
469499
case ',':
500+
case ' ':
470501
if (v->prev == NULL) {
471502
PyErr_SetString(PyExc_SystemError, "':' without '{' in Py_BuildValue");
472503
}
473504
break;
474505
default:
475-
fprintf(stderr, "error: unsupported format starting at %d : '%s'\n", format_idx, format);
506+
PyErr_SetString(PyExc_SystemError, "bad format char passed to Py_BuildValue");
507+
return NULL;
476508
}
477509
c = format[++format_idx];
478510
}
@@ -494,18 +526,18 @@ MUST_INLINE static PyObject* _PyTruffle_BuildValue(const char* format, va_list v
494526
}
495527

496528
PyObject* Py_VaBuildValue(const char *format, va_list va) {
497-
return _Py_VaBuildValue_SizeT(format, va);
529+
return _PyTruffle_BuildValue(format, va, FLAG_SIZE_T);
498530
}
499531

500532
PyObject* _Py_VaBuildValue_SizeT(const char *format, va_list va) {
501-
return _PyTruffle_BuildValue(format, va);
533+
return _PyTruffle_BuildValue(format, va, FLAG_SIZE_T);
502534
}
503535

504536
NO_INLINE
505537
PyObject* Py_BuildValue(const char *format, ...) {
506538
va_list args;
507539
va_start(args, format);
508-
PyObject* result = _PyTruffle_BuildValue(format, args);
540+
PyObject* result = _PyTruffle_BuildValue(format, args, 0);
509541
va_end(args);
510542
return result;
511543
}
@@ -514,7 +546,7 @@ NO_INLINE
514546
PyObject* _Py_BuildValue_SizeT(const char *format, ...) {
515547
va_list args;
516548
va_start(args, format);
517-
PyObject* result = _PyTruffle_BuildValue(format, args);
549+
PyObject* result = _PyTruffle_BuildValue(format, args, FLAG_SIZE_T);
518550
va_end(args);
519551
return result;
520552
}

graalpython/com.oracle.graal.python.shell/src/com/oracle/graal/python/shell/GraalPythonMain.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -505,7 +505,7 @@ protected void launch(Builder contextBuilder) {
505505

506506
if (commandString != null || inputFile != null) {
507507
try {
508-
evalNonInteractive(context);
508+
evalNonInteractive(context, consoleHandler);
509509
rc = 0;
510510
} catch (PolyglotException e) {
511511
if (!e.isExit()) {
@@ -590,7 +590,11 @@ private static void printPythonLikeStackTrace(PolyglotException e) {
590590
System.err.println(e.getMessage());
591591
}
592592

593-
private void evalNonInteractive(Context context) throws IOException {
593+
private void evalNonInteractive(Context context, ConsoleHandler consoleHandler) throws IOException {
594+
// We need to setup the terminal even when not running the REPL because code may request
595+
// input from the terminal.
596+
setupTerminal(consoleHandler);
597+
594598
Source src;
595599
if (commandString != null) {
596600
src = Source.newBuilder(getLanguageId(), commandString, "<string>").build();
@@ -855,6 +859,14 @@ private void setupREPL(Context context, ConsoleHandler consoleHandler) {
855859

856860
}
857861

862+
private static void setupTerminal(ConsoleHandler consoleHandler) {
863+
consoleHandler.setupReader(() -> false, () -> 0, (item) -> {
864+
}, (pos) -> null, (pos, item) -> {
865+
}, (pos) -> {
866+
}, () -> {
867+
}, null);
868+
}
869+
858870
/**
859871
* Some system properties have already been read at this point, so to change them, we just
860872
* re-execute the process with the additional options.

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,3 +428,14 @@ def compile_module(self, name):
428428
cmpfunc=unhandled_error_compare
429429
)
430430

431+
test_Py_BuildValue = CPyExtFunction(
432+
lambda args: {args[1]: args[2], args[3]: args[4]},
433+
lambda: (
434+
('{O:O, O:O}', 'hello', 'world', 'foo', 'bar'),
435+
),
436+
resultspec="O",
437+
argspec="sOOOO",
438+
arguments=["char* fmt", "PyObject* a", "PyObject* b", "PyObject* c", "PyObject* d"],
439+
cmpfunc=unhandled_error_compare
440+
)
441+

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonLanguage.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,8 +208,7 @@ public NodeFactory getNodeFactory() {
208208

209209
@Override
210210
protected void finalizeContext(PythonContext context) {
211-
context.shutdownThreads();
212-
context.runShutdownHooks();
211+
context.finalizeContext();
213212
super.finalizeContext(context);
214213
}
215214

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

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
5252
import com.oracle.graal.python.builtins.PythonBuiltins;
5353
import com.oracle.graal.python.builtins.objects.PNone;
54+
import com.oracle.graal.python.builtins.objects.bytes.PBytesLike;
5455
import com.oracle.graal.python.builtins.objects.module.PythonModule;
5556
import com.oracle.graal.python.builtins.objects.object.PythonObjectLibrary;
5657
import com.oracle.graal.python.builtins.objects.str.PString;
@@ -63,6 +64,7 @@
6364
import com.oracle.graal.python.nodes.util.CastToJavaStringNode;
6465
import com.oracle.graal.python.runtime.PythonCore;
6566
import com.oracle.graal.python.runtime.exception.PythonErrorType;
67+
import com.oracle.graal.python.runtime.interop.InteropByteArray;
6668
import com.oracle.truffle.api.CompilerDirectives;
6769
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
6870
import com.oracle.truffle.api.TruffleLanguage.Env;
@@ -72,8 +74,13 @@
7274
import com.oracle.truffle.api.dsl.NodeFactory;
7375
import com.oracle.truffle.api.dsl.Specialization;
7476
import com.oracle.truffle.api.frame.VirtualFrame;
77+
import com.oracle.truffle.api.interop.InteropLibrary;
78+
import com.oracle.truffle.api.interop.InvalidArrayIndexException;
7579
import com.oracle.truffle.api.interop.TruffleObject;
80+
import com.oracle.truffle.api.interop.UnsupportedMessageException;
7681
import com.oracle.truffle.api.library.CachedLibrary;
82+
import com.oracle.truffle.api.library.ExportLibrary;
83+
import com.oracle.truffle.api.library.ExportMessage;
7784

7885
@CoreFunctions(defineModule = JavaModuleBuiltins.JAVA)
7986
public class JavaModuleBuiltins extends PythonBuiltins {
@@ -259,4 +266,92 @@ Object none(VirtualFrame frame, PythonModule mod, Object name,
259266
return lib.callObject(getAttr(frame, mod, lib), frame, name);
260267
}
261268
}
269+
270+
@Builtin(name = "as_java_byte_array", minNumOfPositionalArgs = 1)
271+
@GenerateNodeFactory
272+
abstract static class AsJavaByteArrayNode extends PythonUnaryBuiltinNode {
273+
@Specialization
274+
static Object doBytesByteStorage(PBytesLike object) {
275+
return new PUnsignedBytesWrapper(object);
276+
}
277+
278+
@Specialization(guards = "lib.isBuffer(object)", limit = "3")
279+
static Object doBuffer(Object object,
280+
@CachedLibrary("object") PythonObjectLibrary lib) {
281+
try {
282+
return new InteropByteArray(lib.getBufferBytes(object));
283+
} catch (UnsupportedMessageException e) {
284+
throw CompilerDirectives.shouldNotReachHere();
285+
}
286+
}
287+
288+
@Fallback
289+
Object doError(Object object) {
290+
throw raise(PythonBuiltinClassType.TypeError, ErrorMessages.BYTESLIKE_OBJ_REQUIRED, object);
291+
}
292+
}
293+
294+
/**
295+
* A simple wrapper object that bit-casts an integer in range {@code 0-255} to a Java
296+
* {@code byte}. This can be used to expose a bytes-like object to Java as {@code byte[]}.
297+
*/
298+
@ExportLibrary(value = InteropLibrary.class, delegateTo = "delegate")
299+
@SuppressWarnings("static-method")
300+
static final class PUnsignedBytesWrapper implements TruffleObject {
301+
final PBytesLike delegate;
302+
303+
PUnsignedBytesWrapper(PBytesLike delegate) {
304+
this.delegate = delegate;
305+
}
306+
307+
@ExportMessage
308+
boolean hasArrayElements(
309+
@CachedLibrary("this.delegate") InteropLibrary delegateLib) {
310+
return delegateLib.hasArrayElements(delegate);
311+
}
312+
313+
@ExportMessage
314+
boolean isArrayElementReadable(long index,
315+
@CachedLibrary("this.delegate") InteropLibrary delegateLib) {
316+
return delegateLib.isArrayElementReadable(delegate, index);
317+
}
318+
319+
@ExportMessage
320+
long getArraySize(
321+
@CachedLibrary("this.delegate") InteropLibrary delegateLib) throws UnsupportedMessageException {
322+
return delegateLib.getArraySize(delegate);
323+
}
324+
325+
@ExportMessage
326+
Object readArrayElement(long index,
327+
@CachedLibrary("this.delegate") InteropLibrary delegateLib,
328+
@CachedLibrary(limit = "1") InteropLibrary elementLib) throws InvalidArrayIndexException, UnsupportedMessageException {
329+
Object element = delegateLib.readArrayElement(delegate, index);
330+
if (elementLib.fitsInLong(element)) {
331+
long i = elementLib.asLong(element);
332+
if (compareUnsigned(i, Byte.MAX_VALUE) <= 0) {
333+
return (byte) i;
334+
} else if (compareUnsigned(i, 0xFF) <= 0) {
335+
return (byte) -(-i & 0xFF);
336+
}
337+
}
338+
throw CompilerDirectives.shouldNotReachHere("bytes object contains non-byte values");
339+
}
340+
341+
/**
342+
* This is taken from {@link Long#compare(long, long)}} (just to avoid a
343+
* {@code TruffleBoundary}).
344+
*/
345+
private static int compare(long x, long y) {
346+
return (x < y) ? -1 : ((x == y) ? 0 : 1);
347+
}
348+
349+
/**
350+
* This is taken from {@link Long#compareUnsigned(long, long)}} (just to avoid a
351+
* {@code TruffleBoundary}).
352+
*/
353+
private static int compareUnsigned(long x, long y) {
354+
return compare(x + Long.MIN_VALUE, y + Long.MIN_VALUE);
355+
}
356+
}
262357
}

0 commit comments

Comments
 (0)