diff --git a/README.rst b/README.rst index d3f7b3ac12..ba26453a5e 100644 --- a/README.rst +++ b/README.rst @@ -1,4 +1,5 @@ -.. image:: http://pybee.org/project/projects/bridges/voc/voc-72.png +.. image:: http://pybee.org/project/projects/bridges/voc/voc.png + :height: 72px :target: https://pybee.org/voc VOC diff --git a/python/android/python/platform/AndroidPlatform.java b/python/android/python/platform/AndroidPlatform.java index 163d35988b..af2fef5aa5 100644 --- a/python/android/python/platform/AndroidPlatform.java +++ b/python/android/python/platform/AndroidPlatform.java @@ -4,17 +4,37 @@ public class AndroidPlatform implements python.Platform { - public AndroidPlatform() {} + private org.python.stdlib._io.TextIOWrapper _stderr; + private org.python.stdlib._io.TextIOWrapper _stdout; + private org.python.stdlib._io.TextIOWrapper _stdin; + + public AndroidPlatform() { + _stderr = new org.python.stdlib._io.TextIOWrapper(new LogStream(android.util.Log.ERROR, "Python", 1024)); + _stdout = new org.python.stdlib._io.TextIOWrapper(new LogStream(android.util.Log.INFO, "Python", 1024)); + // _stdin = new org.python.stdlib._io.TextIOWrapper(System.in); + } public long clock() { return Debug.threadCpuTimeNanos(); } public void debug(java.lang.String msg) { - android.util.Log.d("VOC", msg); + android.util.Log.d("Python", msg); } public void debug(java.lang.String msg, java.lang.Object obj) { - android.util.Log.d("VOC", msg + " " + obj); + android.util.Log.d("Python", msg + " " + obj); + } + + public org.python.Object stderr() { + return _stderr; + } + + public org.python.Object stdout() { + return _stdout; + } + + public org.python.Object stdin() { + return _stdin; } -} \ No newline at end of file +} diff --git a/python/android/python/platform/LogStream.java b/python/android/python/platform/LogStream.java new file mode 100644 index 0000000000..60665f1ee7 --- /dev/null +++ b/python/android/python/platform/LogStream.java @@ -0,0 +1,30 @@ +package python.platform; + + +public class LogStream extends java.io.OutputStream { + protected java.lang.String tag; + protected byte[] buf; + protected int size; + protected int count; + protected int priority; + + public LogStream(int priority, java.lang.String tag, int size) { + this.tag = tag; + this.priority = priority; + this.size = size; + + buf = new byte[size]; + } + + public void write(int b) { + if (b != '\n') { + buf[count] = (byte) b; + count++; + } + + if (b == '\n' || count == size) { + android.util.Log.println(priority, tag, new java.lang.String(buf, 0, count)); + count = 0; + } + } +} \ No newline at end of file diff --git a/python/common/org/Python.java b/python/common/org/Python.java index 2f64075191..851918bf68 100644 --- a/python/common/org/Python.java +++ b/python/common/org/Python.java @@ -185,6 +185,63 @@ public static java.lang.String typeName(java.lang.Class cls) { return name; } + /** + * Add the contents of a list to the varargs to be used in a function call. + */ + public static org.python.Object [] addToArgs(org.python.Object [] args, org.python.Object varargs) { + java.util.List temp_list = new java.util.ArrayList(); + try { + org.python.Iterable iter = varargs.__iter__(); + while (true) { + org.python.Object item = iter.__next__(); + temp_list.add(item); + } + } catch (org.python.exceptions.StopIteration e) {} + + java.lang.Object [] va_list = temp_list.toArray(); + org.python.Object [] arg_list = new org.python.Object [args.length + va_list.length]; + + System.arraycopy(args, 0, arg_list, 0, args.length); + System.arraycopy(va_list, 0, arg_list, args.length, va_list.length); + + return arg_list; + } + + /** + * Add the contents of a dictionary to the keyword arguments to be used + * in a function call. + * + * Returns the updated kwargs Map. + * + * If any of the keys in the varkwargs dictionary aren't strings, + * a TypeError is raised. + */ + public static java.util.Map addToKwargs(java.util.Map kwargs, org.python.Object kwvarargs, java.lang.String func_name) { + // FIXME: Once we have dictionary iterators, we should use an iteration-based + // rollout, rather than casting to Dict. + // try { + // org.python.Iterable iter = varkwargs.__iter__(); + // while (true) { + // org.python.Object key = iter.__next__() + // java.lang.String key_string = ((org.python.types.Str) key).value; + // org.python.Object key + // kwargs.put(key_string, value); + // } + // } catch (ClassCastException e) { + // throw new org.python.exceptions.TypeError(func_name + "() keywords must be strings"); + // } catch (org.python.exceptions.StopIteration e) {} + + for (java.util.Map.Entry entry : ((org.python.types.Dict) kwvarargs).value.entrySet()) { + try { + kwargs.put(((org.python.types.Str) entry.getKey()).value, entry.getValue()); + } catch (ClassCastException e) { + throw new org.python.exceptions.TypeError(func_name + "() keywords must be strings"); + } + } + return kwargs; + } + + @org.python.Method( __doc__ = "__import__(name, globals=None, locals=None, fromlist=(), level=0) -> module" + "\n" + @@ -1331,12 +1388,7 @@ public static org.python.Object pow(org.python.Object x, org.python.Object y, or kwonlyargs={"file", "sep", "end", "flush"} ) public static void print(org.python.types.Tuple value, org.python.Object file, org.python.Object sep, org.python.Object end, org.python.Object flush) { - if (file == null) { - // file = System.out; - } - java.util.List valueArgs = value.value; - StringBuilder buffer = new StringBuilder(); for (int i = 0; i < valueArgs.size(); i++) { @@ -1355,8 +1407,27 @@ public static void print(org.python.types.Tuple value, org.python.Object file, o } else { buffer.append(end); } - System.out.print(buffer.toString()); - // file.write(buffer.toString()); + + if (file == null) { + file = python.sys.__init__.stdout; + } + + org.python.Object content = new org.python.types.Str(buffer.toString()); + org.python.Object write_method = file.__getattribute__("write"); + try { + ((org.python.Callable) write_method).invoke(new org.python.Object [] { content }, null); + } catch (java.lang.ClassCastException e) { + throw new org.python.exceptions.TypeError("'" + write_method.typeName() + "' object is not callable"); + } + + if (flush != null && ((org.python.types.Bool) flush.__bool__()).value) { + org.python.Object flush_method = file.__getattribute__("flush"); + try { + ((org.python.Callable) flush_method).invoke(null, null); + } catch (java.lang.ClassCastException e) { + throw new org.python.exceptions.TypeError("'" + flush_method.typeName() + "' object is not callable"); + } + } } @org.python.Method( diff --git a/python/common/org/python/stdlib/_io/TextIOWrapper.java b/python/common/org/python/stdlib/_io/TextIOWrapper.java new file mode 100644 index 0000000000..a8210c826d --- /dev/null +++ b/python/common/org/python/stdlib/_io/TextIOWrapper.java @@ -0,0 +1,112 @@ +package org.python.stdlib._io; + +public class TextIOWrapper extends org.python.types.Object { + public org.python.Object __VOC__; + + java.io.InputStream input; + java.io.OutputStream output; + + public TextIOWrapper(java.io.InputStream istream) { + super(org.python.types.Type.Origin.BUILTIN, null); + this.input = istream; + } + + public TextIOWrapper(java.io.OutputStream ostream) { + super(org.python.types.Type.Origin.BUILTIN, null); + this.output = ostream; + } + + // @org.python.Method( + // __doc__ = "Create and return a new object. See help(type) for accurate signature." + // ) + // public org.python.Object __new__(org.python.Object klass) { + // org.python.types.Type cls = (org.python.types.Type) super.__new__(klass); + // System.out.println("__NEW__ TextIOWrapper"); + // return cls; + // } + + // '_CHUNK_SIZE', + // '__class__', + // '__del__', + // '__delattr__', + // '__dict__', + // '__dir__', + // '__doc__', + // '__enter__', + // '__eq__', + // '__exit__', + // '__format__', + // '__ge__', + // '__getattribute__', + // '__getstate__', + // '__gt__', + // '__hash__', + // '__init__', + // '__iter__', + // '__le__', + // '__lt__', + // '__ne__', + // '__new__', + // '__next__', + // '__reduce__', + // '__reduce_ex__', + // '__repr__', + // '__setattr__', + // '__sizeof__', + // '__str__', + // '__subclasshook__', + // '_checkClosed', + // '_checkReadable', + // '_checkSeekable', + // '_checkWritable', + // '_finalizing', + // 'buffer', + // 'close', + // 'closed', + // 'detach', + // 'encoding', + // 'errors', + // 'fileno', + + @org.python.Method( + __doc__ = "" + ) + public org.python.Object flush() { + try { + output.flush(); + } catch (java.io.IOException e) { + throw new org.python.exceptions.RuntimeError("Unable to flush output: " + e.toString()); + } + return org.python.types.NoneType.NONE; + } + + // 'isatty', + // 'line_buffering', + // 'name', + // 'newlines', + // 'read', + // 'readable', + // 'readline', + // 'readlines', + // 'seek', + // 'seekable', + // 'tell', + // 'truncate', + // 'writable', + + @org.python.Method( + __doc__ = "", + args = {"content"} + ) + public org.python.Object write(org.python.Object content) { + try { + output.write(content.toString().getBytes("UTF-8")); + } catch (java.io.IOException e) { + throw new org.python.exceptions.RuntimeError("Unable to write output: " + e.toString()); + } + return org.python.types.NoneType.NONE; + } + + // 'writelines' + +}; diff --git a/python/common/org/python/types/ByteArray.java b/python/common/org/python/types/ByteArray.java index c01ff7ea21..98ccd154e2 100644 --- a/python/common/org/python/types/ByteArray.java +++ b/python/common/org/python/types/ByteArray.java @@ -424,7 +424,7 @@ public org.python.Object __mod__(org.python.Object other) { if (other instanceof org.python.types.List || other instanceof org.python.types.Range || other instanceof org.python.types.Dict) { int i; for (i=0; i < this.value.length; i++) { - if (this.value[0] == 0) break; + if (this.value[0] == 0) break; } byte[] bytes = new byte[i]; System.arraycopy(this.value, 0, bytes, 0, i); diff --git a/python/common/org/python/types/Closure.java b/python/common/org/python/types/Closure.java index c99d1c2d94..c3895753f2 100644 --- a/python/common/org/python/types/Closure.java +++ b/python/common/org/python/types/Closure.java @@ -1,7 +1,6 @@ package org.python.types; public class Closure extends org.python.types.Object { - public java.util.List default_args; public java.util.Map default_kwargs; diff --git a/python/common/org/python/types/Complex.java b/python/common/org/python/types/Complex.java index 09e36e9e9c..304a8d6710 100644 --- a/python/common/org/python/types/Complex.java +++ b/python/common/org/python/types/Complex.java @@ -1,7 +1,6 @@ package org.python.types; public class Complex extends org.python.types.Object { - public org.python.types.Float real; public org.python.types.Float imag; diff --git a/python/common/org/python/types/Dict.java b/python/common/org/python/types/Dict.java index 19f7cb515b..de20fe44f1 100644 --- a/python/common/org/python/types/Dict.java +++ b/python/common/org/python/types/Dict.java @@ -256,6 +256,7 @@ public void __delitem__(org.python.Object item) { __doc__ = "" ) public org.python.Iterable __iter__() { + // FIXME: Once this is implemented, update org.Python.addToKwargs() throw new org.python.exceptions.NotImplementedError("dict.__iter__() has not been implemented."); } diff --git a/python/common/org/python/types/Function.java b/python/common/org/python/types/Function.java index c56741e294..e746c2de33 100644 --- a/python/common/org/python/types/Function.java +++ b/python/common/org/python/types/Function.java @@ -167,28 +167,38 @@ public org.python.Object __get__(org.python.Object instance, org.python.Object k // // TODO: This doesn't have to be so - we *could* introspect argument names. // throw new org.python.exceptions.RuntimeError("Cannot use kwargs to invoke a native Java method."); // } - // System.out.println("CODE " + this.code + " "); - - int pos_count = (int) this.code.co_argcount.value; - int keyword_only_count = (int) this.code.co_kwonlyargcount.value; + int argcount = (int) this.code.co_argcount.value; + int kwonlyargcount = (int) this.code.co_kwonlyargcount.value; int flags = (int) this.code.co_flags.value; java.util.List varnames = this.code.co_varnames.value; - int first_arg; - int first_default; - // System.out.println("counts " + pos_count + " " + keyword_only_count + " " + flags); - - int n_args = pos_count + keyword_only_count; + int first_arg = 0; + int has_varargs = 0; + int has_varkwargs = 0; + // System.out.println("Instance = " + instance); + // System.out.println("method:" + method); + // System.out.println("args:" + args.length); + // System.out.println("kwargs:" + kwargs); + // System.out.println("argcount = " + argcount); + // System.out.println("kwonlyargcount = " + kwonlyargcount); + + int n_args = argcount + kwonlyargcount; if ((flags & CO_VARARGS) != 0) { + // System.out.println("HAS VARARGS"); n_args += 1; + has_varargs = 1; } if ((flags & CO_VARKEYWORDS) != 0) { + // System.out.println("HAS VARKEYWORDS"); n_args += 1; + has_varkwargs = 1; } - first_default = pos_count - this.default_args.size(); - // System.out.println("nargs " + n_args); + int required_args = argcount - this.default_args.size(); + // System.out.println("nargs = " + n_args); + // System.out.println("first default = " + required_args); + // If there are genuinely *no* arguments - not even self - return null; if (n_args == 0) { return null; } @@ -197,44 +207,76 @@ public org.python.Object __get__(org.python.Object instance, org.python.Object k // If this is an instance, the first argument will be self; we don't // need to pass this to the Java function. - if (instance == null || !java.lang.reflect.Modifier.isStatic(method.getModifiers())) { - first_arg = 0; - } else { + if (instance != null && java.lang.reflect.Modifier.isStatic(method.getModifiers())) { + // System.out.println("CALL USING INSTANCE"); first_arg = 1; adjusted[0] = instance; + // System.out.println(" aARG 0: " + instance); } - for (int i = first_arg; i < pos_count; i++) { - java.lang.String varname = ((org.python.types.Str) varnames.get(i)).value; - if (i < args.length + first_arg) { - adjusted[i] = args[i - first_arg]; - org.python.Object value = kwargs.remove(varname); - if (value != null) { - throw new org.python.exceptions.TypeError(this.name + "() got multiple values for argument '" + varname + "'"); + // System.out.println("First arg = " + first_arg); + // Populate the positional args. + for (int i = 0; i < argcount - first_arg; i++) { + if (i < args.length) { + // System.out.println(" b" + (i + first_arg)); + adjusted[i + first_arg] = args[i]; + // System.out.println(" bARG " + (i + first_arg) + ": " + args[i]); + if (kwargs != null) { + java.lang.String varname = ((org.python.types.Str) varnames.get(i)).value; + org.python.Object value = kwargs.remove(varname); + if (value != null) { + throw new org.python.exceptions.TypeError(this.name + "() got multiple values for argument '" + varname + "'"); + } } } else { - org.python.Object value = kwargs.remove(varname); + // Use a default argument. They might be specified as a kwarg. + // System.out.println(" c" + i); + org.python.Object value = null; + if (kwargs != null) { + java.lang.String varname = ((org.python.types.Str) varnames.get(i)).value; + value = kwargs.remove(varname); + } if (value == null) { - value = this.default_args.get(i - first_default); + value = this.default_args.get(i - required_args); } adjusted[i] = value; + // System.out.println(" cARG " + i + ": " + value); } } + // Create a tuple for the varargs if ((flags & CO_VARARGS) != 0) { + // System.out.println("Handle varargs"); // Construct Python tuple object org.python.types.Tuple tuple = new org.python.types.Tuple( - java.util.Arrays.asList(java.util.Arrays.copyOfRange(args, pos_count, args.length))); + java.util.Arrays.asList(java.util.Arrays.copyOfRange(args, argcount, args.length))); - adjusted[pos_count] = tuple; + adjusted[argcount] = tuple; + // System.out.println(" dARG " + argcount + ": " + tuple); } + + // Populate the kwonly args + for (int i = 0; i < kwonlyargcount; i++) { + java.lang.String varname = ((org.python.types.Str) varnames.get(argcount + has_varargs + i)).value; + // System.out.println(" e" + (argcount + has_varargs + i) + " " + varname); + org.python.Object value = kwargs.remove(varname); + if (value == null) { + value = this.default_kwargs.get(varname); + } + adjusted[argcount + has_varargs + i] = value; + // System.out.println(" eARG " + (argcount + has_varargs + i) + ": " + value); + } + // Add remaining kwargs to kwargs argument if we have one. if ((flags & CO_VARKEYWORDS) != 0) { + // System.out.println("Handle varkwargs = " + kwargs); org.python.types.Dict kwargDict = new org.python.types.Dict(); - for (java.util.Map.Entry entry : kwargs.entrySet()) { - kwargDict.__setitem__(new org.python.types.Str(entry.getKey()), entry.getValue()); + for (java.util.Map.Entry entry : kwargs.entrySet()) { + // System.out.println("Add KWARG" + entry.getKey()); + kwargDict.__setitem__(new org.python.types.Str(entry.getKey()), entry.getValue()); } adjusted[adjusted.length - 1] = kwargDict; + // System.out.println(" fARG " + (adjusted.length - 1) + ": " + kwargDict); } return adjusted; diff --git a/python/common/org/python/types/Method.java b/python/common/org/python/types/Method.java index 85372495be..c8e86c8ddc 100644 --- a/python/common/org/python/types/Method.java +++ b/python/common/org/python/types/Method.java @@ -41,6 +41,7 @@ public org.python.types.Str __repr__() { } public org.python.Object invoke(org.python.Object [] args, java.util.Map kwargs) { + // System.out.println("METHOD Invocation: " + this.im_self); return this.im_func.invoke(this.im_self, args, kwargs); } } diff --git a/python/common/org/python/types/NoneType.java b/python/common/org/python/types/NoneType.java index a82d628763..7b1816513e 100644 --- a/python/common/org/python/types/NoneType.java +++ b/python/common/org/python/types/NoneType.java @@ -130,7 +130,7 @@ public boolean __setattr_null(java.lang.String name, org.python.Object value) { __doc__ = "" ) public org.python.Object __round__(org.python.Object ndigits) { - - throw new org.python.exceptions.TypeError("type NoneType doesn't define __round__ method"); + + throw new org.python.exceptions.TypeError("type NoneType doesn't define __round__ method"); } } diff --git a/python/common/org/python/types/Tuple.java b/python/common/org/python/types/Tuple.java index 6bc095bbd9..9ca31316b5 100644 --- a/python/common/org/python/types/Tuple.java +++ b/python/common/org/python/types/Tuple.java @@ -255,7 +255,7 @@ public org.python.types.List __dir__() { __doc__ = "" ) public org.python.types.Int __len__() { - throw new org.python.exceptions.NotImplementedError("__len__() has not been implemented."); + return new org.python.types.Int(this.value.size()); } @org.python.Method( @@ -437,8 +437,8 @@ public org.python.Object index(org.python.Object item, org.python.Object start, __doc__ = "" ) public org.python.Object __round__(org.python.Object ndigits) { - - throw new org.python.exceptions.TypeError("type tuple doesn't define __round__ method"); - + + throw new org.python.exceptions.TypeError("type tuple doesn't define __round__ method"); + } } diff --git a/python/common/org/python/types/Type.java b/python/common/org/python/types/Type.java index fb72f32651..695daa9fec 100644 --- a/python/common/org/python/types/Type.java +++ b/python/common/org/python/types/Type.java @@ -26,7 +26,9 @@ public static org.python.types.Type pythonType(java.lang.Class java_class) { // Any type implementing org.python.Object is a Python type; // otherwise, wrap it as a native Java type. if (org.python.Object.class.isAssignableFrom(java_class)) { - if (java_class.getName().startsWith("org.python.types.")) { + // if (org.python.Builtin.class.isAssignableFrom(java_class)) { + if (java_class.getName().startsWith("org.python.types") + || java_class.getName().startsWith("org.python.stdlib")) { python_type = new org.python.types.Type(Origin.BUILTIN, java_class); } else { python_type = new org.python.types.Type(Origin.PYTHON, java_class); diff --git a/python/common/python/Platform.java b/python/common/python/Platform.java index a7b75f61d3..3b433ed3cd 100644 --- a/python/common/python/Platform.java +++ b/python/common/python/Platform.java @@ -9,4 +9,10 @@ public interface Platform { public void debug(java.lang.String msg); public void debug(java.lang.String msg, java.lang.Object obj); + + public org.python.Object stderr(); + + public org.python.Object stdout(); + + public org.python.Object stdin(); } \ No newline at end of file diff --git a/python/common/python/_io/__init__.java b/python/common/python/_io/__init__.java new file mode 100644 index 0000000000..0c03c2d68f --- /dev/null +++ b/python/common/python/_io/__init__.java @@ -0,0 +1,36 @@ +package python._io; + + +public class __init__ extends org.python.types.Module { + @org.python.Method( + __doc__ = "Create and return a new object. See help(type) for accurate signature." + ) + public org.python.Object __new__(org.python.Object klass) { + org.python.types.Type cls = (org.python.types.Type) super.__new__(klass); + return cls; + } + + //'BlockingIOError', + //'BufferedRWPair', + //'BufferedRandom', + //'BufferedReader', + //'BufferedWriter', + //'BytesIO', + //'DEFAULT_BUFFER_SIZE', + //'FileIO', + //'IncrementalNewlineDecoder', + //'StringIO', + //'TextIOWrapper', + //'UnsupportedOperation', + //'_BufferedIOBase', + //'_IOBase', + //'_RawIOBase', + //'_TextIOBase', + //'__doc__', + //'__loader__', + //'__name__', + //'__package__', + //'__spec__', + //'open'] + +}; diff --git a/python/common/python/sys/__init__.java b/python/common/python/sys/__init__.java index 4ff8aeebbc..b1f2aa28ec 100644 --- a/python/common/python/sys/__init__.java +++ b/python/common/python/sys/__init__.java @@ -2,6 +2,16 @@ public class __init__ extends org.python.types.Module { + static { + stdout = python.platform.__init__.impl.stdout(); + stderr = python.platform.__init__.impl.stderr(); + stdin = python.platform.__init__.impl.stdin(); + + __stdout__ = python.platform.__init__.impl.stdout(); + __stderr__ = python.platform.__init__.impl.stderr(); + __stdin__ = python.platform.__init__.impl.stdin(); + } + @org.python.Method( __doc__ = "Create and return a new object. See help(type) for accurate signature." ) @@ -49,11 +59,14 @@ public static org.python.Object __excepthook__(java.util.List // __spec__ - // __stderr__ + @org.python.Attribute() + public static org.python.Object __stderr__; - // __stdin__ + @org.python.Attribute() + public static org.python.Object __stdin__; - // __stdout__ + @org.python.Attribute() + public static org.python.Object __stdout__; @org.python.Method( __doc__ = "" diff --git a/python/java/python/platform/JavaPlatform.java b/python/java/python/platform/JavaPlatform.java index 1244a272ea..8d6e527402 100644 --- a/python/java/python/platform/JavaPlatform.java +++ b/python/java/python/platform/JavaPlatform.java @@ -5,7 +5,15 @@ public class JavaPlatform implements python.Platform { - public JavaPlatform() {} + private org.python.stdlib._io.TextIOWrapper _stderr; + private org.python.stdlib._io.TextIOWrapper _stdout; + private org.python.stdlib._io.TextIOWrapper _stdin; + + public JavaPlatform() { + _stderr = new org.python.stdlib._io.TextIOWrapper(System.err); + _stdout = new org.python.stdlib._io.TextIOWrapper(System.out); + _stdin = new org.python.stdlib._io.TextIOWrapper(System.in); + } public long clock() { ThreadMXBean tmxb = ManagementFactory.getThreadMXBean(); @@ -20,4 +28,15 @@ public void debug(java.lang.String msg, java.lang.Object obj) { System.out.println("DEBUG " + msg + ": " + obj); } + public org.python.Object stderr() { + return _stderr; + } + + public org.python.Object stdout() { + return _stdout; + } + + public org.python.Object stdin() { + return _stdin; + } } \ No newline at end of file diff --git a/setup.cfg b/setup.cfg index fdb7aadd63..f965c07765 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,3 @@ -[bdist_wheel] -universal=1 - [isort] combine_as_imports = true default_section = THIRDPARTY diff --git a/setup.py b/setup.py index bfa84878b2..9d38916715 100644 --- a/setup.py +++ b/setup.py @@ -1,10 +1,8 @@ #!/usr/bin/env python3 - - import sys -if sys.version_info[:3] < (3, 4): - raise SystemExit("You need Python 3.4+") +if sys.version_info[:2] != (3, 4): + raise SystemExit("VOC requires Python 3.4+") import io import re @@ -45,6 +43,7 @@ 'License :: OSI Approved :: BSD License', 'Operating System :: OS Independent', 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3 :: Only', 'Topic :: Software Development', 'Topic :: Utilities', diff --git a/tests/builtins/test_len.py b/tests/builtins/test_len.py index dcdf23105c..71c3e7f32b 100644 --- a/tests/builtins/test_len.py +++ b/tests/builtins/test_len.py @@ -14,5 +14,4 @@ class BuiltinLenFunctionTests(BuiltinFunctionTestCase, TranspileTestCase): 'test_class', 'test_complex', 'test_frozenset', - 'test_tuple', ] diff --git a/tests/builtins/test_print.py b/tests/builtins/test_print.py index f024869e3b..f9b541195c 100644 --- a/tests/builtins/test_print.py +++ b/tests/builtins/test_print.py @@ -2,7 +2,58 @@ class PrintTests(TranspileTestCase): - pass + def test_fileobj(self): + self.assertCodeExecution(""" + class FileLikeObject: + def __init__(self): + self.buffer = '' + + def write(self, content): + self.buffer = self.buffer + (content * 2) + + out = FileLikeObject() + + print('hello', 'world', file=out) + print('goodbye', 'world', file=out) + print() + """) + + def test_sep(self): + self.assertCodeExecution(""" + print('hello world', 'goodbye world', sep='-') + print() + """) + + def test_end(self): + self.assertCodeExecution(""" + print('hello world', 'goodbye world', end='-') + print() + """) + + def test_flush(self): + self.assertCodeExecution(""" + print('hello world', 'goodbye world', flush=True) + print() + """) + + def test_combined(self): + self.assertCodeExecution(""" + class FileLikeObject: + def __init__(self): + self.buffer = '' + + def write(self, content): + self.buffer = self.buffer + (content * 2) + + def flush(self): + self.buffer = self.buffer + '<<<' + + out = FileLikeObject() + + print('hello', 'world', sep='*', end='-', file=out, flush=True) + print('goodbye', 'world', file=out, sep='-', end='*') + print() + """) class BuiltinPrintFunctionTests(BuiltinFunctionTestCase, TranspileTestCase): diff --git a/tests/structures/test_function.py b/tests/structures/test_function.py index 9875bfdde8..97436c614a 100644 --- a/tests/structures/test_function.py +++ b/tests/structures/test_function.py @@ -161,7 +161,6 @@ def myfunc(**kwargs): print('Done.') """, run_in_function=False) - @expectedFailure def test_call_function_kw(self): self.assertCodeExecution(""" def myfunc(**kwargs): @@ -175,7 +174,6 @@ def myfunc(**kwargs): print('Done.') """, run_in_function=False) - @expectedFailure def test_call_function_var_kw(self): self.assertCodeExecution(""" def myfunc(*args, **kwargs): @@ -191,7 +189,6 @@ def myfunc(*args, **kwargs): print('Done.') """, run_in_function=False) - @expectedFailure def test_call_function_var(self): self.assertCodeExecution(""" def myfunc(*args): diff --git a/voc/python/ast.py b/voc/python/ast.py index 2df4f451b8..c99f69c912 100644 --- a/voc/python/ast.py +++ b/voc/python/ast.py @@ -1583,7 +1583,6 @@ def visit_Call(self, node): ) for i, arg in enumerate(node.args): - self.context.add_opcodes( JavaOpcodes.DUP(), ICONST_val(i), @@ -1593,17 +1592,13 @@ def visit_Call(self, node): JavaOpcodes.AASTORE(), ) - # FIXME - # if node.starargs is not None: - # for arg in node.starargs: - # self.context.add_opcodes( - # JavaOpcodes.DUP(), - # ICONST_val(i), - # ) - # self.visit(arg) - # self.context.add_opcodes( - # JavaOpcodes.AASTORE(), - # ) + if node.starargs is not None: + # Evaluate the starargs + self.visit(node.starargs) + + self.context.add_opcodes( + JavaOpcodes.INVOKESTATIC('org/Python', 'addToArgs', '([Lorg/python/Object;Lorg/python/Object;)[Lorg/python/Object;'), + ) # Create and populate the map of kwargs to pass to invoke(). self.context.add_opcodes( @@ -1623,10 +1618,15 @@ def visit_Call(self, node): JavaOpcodes.POP() ) - # FIXME - # if node.kwargs is not None: - # for arg in node.kwargs: - # self.visit(arg) + if node.kwargs is not None: + # Evaluate the kwargs + self.visit(node.kwargs) + + # Add all the kwargs to the kwargs dict. + self.context.add_opcodes( + JavaOpcodes.LDC_W(node.func.id), + JavaOpcodes.INVOKESTATIC('org/Python', 'addToKwargs', '(Ljava/util/Map;Lorg/python/Object;Ljava/lang/String;)Ljava/util/Map;'), + ) # Set up the stack and invoke the callable self.context.add_opcodes(