diff --git a/Lib/test/test_ast_jy.py b/Lib/test/test_ast_jy.py index 7f027bd96..2dbe7991f 100644 --- a/Lib/test/test_ast_jy.py +++ b/Lib/test/test_ast_jy.py @@ -146,10 +146,31 @@ def test_empty_init(self): #ast.stmt() #ast.unaryop() +class TestAstList(unittest.TestCase): + """Supplementary tests for org.python.core.AstList""" + + def test_concat(self): + # Issue gh-356 Java IndexOOB error on append + body = ast.parse('x=1; global y; y+=x*2').body # _ast.AstList + p = [ast.Pass(), ast.Break()] # PyList + x = body + p + self.assertEqual(x[-2:], p) + + def test_append(self): + # Issue gh-356 Java IndexOOB error on append + body = ast.parse('x=1; global y; y+=x*2').body # _ast.AstList + p = [ast.Pass(), ast.Break()] # PyList + body += p + self.assertEqual(body[-2:], p) + + #============================================================================== def test_main(verbose=None): - test_classes = [TestCompile] + test_classes = [ + TestCompile, + TestAstList + ] test_support.run_unittest(*test_classes) if __name__ == "__main__": diff --git a/NEWS b/NEWS index 3fd6cc8dc..ad08795a1 100644 --- a/NEWS +++ b/NEWS @@ -25,13 +25,14 @@ New Features to the classpath on top of jython.jar without name conflict. In many cases this should suffice to retain workability of a legacy setup if needed. - - Upgrade of jffi to 1.3.14 eliminates the need for sun.misc.Unsafe, which is + - Upgrade of jffi to 1.3.15 eliminates the need for sun.misc.Unsafe, which is deprecated for removal in Java 26. See https://github.com/jnr/jffi/issues/165 Jython 2.7.5a1 Bugs fixed - [ GH-404 ] Windows code page 65001 (UTF-8) not supported by Jython - [ GH-384 ] Console encoding inferred incorrectly on Java 21 - [ GH-382 ] Java EE Servlet Namespace Has Been Changed From javax.servlet to jakarta.servlet + - [ GH-356 ] _ast.astlist.__add__ leads to Java exception - [ GH-84 ] PyServlet Will Need To Use The jakarta.servlet Namespace #84 ============================================================================== diff --git a/README.md b/README.md index ce5fd6198..c7692df2c 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Jython: Python for the Java Platform -[![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.python/jython-standalone/badge.svg)](https://search.maven.org/artifact/org.python/jython-standalone/) +[![Maven Central](https://maven-badges.sml.io/maven-central/org.python/jython-standalone/badge.svg)](https://search.maven.org/artifact/org.python/jython-standalone/) [![Javadocs](https://www.javadoc.io/badge/org.python/jython-standalone.svg)](https://www.javadoc.io/doc/org.python/jython-standalone) [![APIdia](https://apidia.net/java/Jython/badge.svg)](https://apidia.net/java/Jython) diff --git a/src/org/python/core/AstList.java b/src/org/python/core/AstList.java index 6a0218dd3..fe3f1aa46 100644 --- a/src/org/python/core/AstList.java +++ b/src/org/python/core/AstList.java @@ -1,8 +1,7 @@ /* - * I don't like that this is in org.python.core, but PySequence has package - * private methods that I want to override. Hopefully we will clean up the - * PyList hierarchy in the future, and then maybe we will find a more sensible - * place for this class. + * I don't like that this is in org.python.core, but PySequence has package private methods that I + * want to override. Hopefully we will clean up the PyList hierarchy in the future, and then maybe + * we will find a more sensible place for this class. */ package org.python.core; @@ -27,26 +26,32 @@ public class AstList extends PySequence implements Cloneable, List, Traverseproc private final static PyString[] fields = new PyString[0]; @ExposedGet(name = "_fields") - public PyString[] get_fields() { return fields; } - + public PyString[] get_fields() { + return fields; + } /** The underlying Java List. */ + @SuppressWarnings("rawtypes") private List data; private AstAdapter adapter; + @SuppressWarnings("rawtypes") public AstList() { this(TYPE, new ArrayList(), null); } + @SuppressWarnings("rawtypes") public AstList(List data) { this(TYPE, data, null); } + @SuppressWarnings("rawtypes") public AstList(List data, AstAdapter adapter) { this(TYPE, data, adapter); } + @SuppressWarnings("rawtypes") public AstList(PyType type, List data, AstAdapter adapter) { super(TYPE); if (data == null) { @@ -104,7 +109,7 @@ final void astlist___setitem__(PyObject o, PyObject def) { @ExposedMethod final PyObject astlist___getitem__(PyObject o) { PyObject ret = seq___finditem__(o); - if(ret == null) { + if (ret == null) { throw Py.IndexError("index out of range: " + o); } return ret; @@ -127,7 +132,7 @@ final PyObject astlist___getslice__(PyObject start, PyObject stop, PyObject step @ExposedMethod(defaults = "null") final void astlist___setslice__(PyObject start, PyObject stop, PyObject step, PyObject value) { - if(value == null) { + if (value == null) { value = step; step = null; } @@ -139,6 +144,7 @@ final void astlist___delslice__(PyObject start, PyObject stop, PyObject step) { seq___delslice__(start, stop, step); } + @Override public PyObject __imul__(PyObject o) { return astlist___imul__(o); } @@ -172,6 +178,7 @@ final PyObject astlist___imul__(PyObject o) { return this; } + @Override public PyObject __mul__(PyObject o) { return astlist___mul__(o); } @@ -184,6 +191,7 @@ final PyObject astlist___mul__(PyObject o) { return repeat(o.asIndex(Py.OverflowError)); } + @Override public PyObject __rmul__(PyObject o) { return astlist___rmul__(o); } @@ -196,6 +204,7 @@ final PyObject astlist___rmul__(PyObject o) { return repeat(o.asIndex(Py.OverflowError)); } + @Override public PyObject __iadd__(PyObject other) { return astlist___iadd__(other); } @@ -221,29 +230,33 @@ final PyObject astlist___iadd__(PyObject o) { return this; } + @Override public PyObject __add__(PyObject other) { return astlist___add__(other); } + @SuppressWarnings("rawtypes") @ExposedMethod(type = MethodType.BINARY) final PyObject astlist___add__(PyObject o) { AstList sum = null; Object oList = o.__tojava__(List.class); - if(oList != Py.NoConversion && oList != null) { + if (oList != Py.NoConversion && oList != null) { List otherList = (List) oList; sum = new AstList(); sum.extend(this); - for(Iterator i = otherList.iterator(); i.hasNext();) { + for (Iterator i = otherList.iterator(); i.hasNext();) { sum.add(i.next()); } } return sum; } + @Override public PyObject __radd__(PyObject o) { return astlist___radd__(o); } + @SuppressWarnings("rawtypes") @ExposedMethod(type = MethodType.BINARY) final PyObject astlist___radd__(PyObject o) { PyList sum = null; @@ -256,6 +269,7 @@ final PyObject astlist___radd__(PyObject o) { return sum; } + @Override public int __len__() { return data.size(); } @@ -280,6 +294,7 @@ final void astlist_append(PyObject o) { data.add(o); } + @Override public Object clone() { return new AstList(this); } @@ -287,8 +302,8 @@ public Object clone() { @ExposedMethod final int astlist_count(PyObject value) { int count = 0; - for(Object o : data) { - if(o.equals(value)) { + for (Object o : data) { + if (o.equals(value)) { count++; } } @@ -334,34 +349,35 @@ private int _index(PyObject o, String message, int start, int stop) { // Follow Python 2.3+ behavior int validStop = boundToSequence(stop); int validStart = boundToSequence(start); - for(int i = validStart; i < validStop && i < size(); i++) { - if(data.get(i).equals(o)) { + for (int i = validStart; i < validStop && i < size(); i++) { + if (data.get(i).equals(o)) { return i; } } throw Py.ValueError(message); } + @Override protected void del(int i) { data.remove(i); } protected void delRange(int start, int stop, int step) { - if(step >= 1) { - for(int i = start; i < stop; i += step) { + if (step >= 1) { + for (int i = start; i < stop; i += step) { remove(i); i--; stop--; } - } else if(step < 0) { - for(int i = start; i >= 0 && i >= stop; i += step) { + } else if (step < 0) { + for (int i = start; i >= 0 && i >= stop; i += step) { remove(i); } } } @ExposedMethod - final void astlist_extend(PyObject iterable){ + final void astlist_extend(PyObject iterable) { int length = size(); setslice(length, length, 1, iterable); } @@ -371,18 +387,19 @@ public void extend(PyObject iterable) { } @SuppressWarnings({ "rawtypes", "unchecked" }) + @Override protected PyObject getslice(int start, int stop, int step) { - if(step > 0 && stop < start) { + if (step > 0 && stop < start) { stop = start; } int n = sliceLength(start, stop, step); List newList = data.subList(start, stop); - if(step == 1) { + if (step == 1) { newList = data.subList(start, stop); return new AstList(newList, adapter); } int j = 0; - for(int i = start; j < n; i += step) { + for (int i = start; j < n; i += step) { newList.set(j, data.get(i)); j++; } @@ -396,17 +413,17 @@ public void insert(int index, PyObject o) { @SuppressWarnings("unchecked") @ExposedMethod final void astlist_insert(int index, PyObject o) { - if(index < 0) { + if (index < 0) { index = Math.max(0, size() + index); } - if(index > size()) { + if (index > size()) { index = size(); } data.add(index, o); } @ExposedMethod - final void astlist_remove(PyObject value){ + final void astlist_remove(PyObject value) { del(_index(value, "astlist.remove(x): x not in list", 0, size())); } @@ -434,7 +451,7 @@ public PyObject pop(int n) { @ExposedMethod(defaults = "-1") final PyObject astlist_pop(int n) { if (adapter == null) { - return (PyObject)data.remove(n); + return (PyObject) data.remove(n); } Object element = data.remove(n); return adapter.ast2py(element); @@ -442,6 +459,7 @@ final PyObject astlist_pop(int n) { } @SuppressWarnings("unchecked") + @Override protected PyObject repeat(int count) { if (count < 0) { count = 0; @@ -454,22 +472,24 @@ protected PyObject repeat(int count) { @SuppressWarnings("rawtypes") List newList = new ArrayList(); - for(int i = 0; i < count; i++) { + for (int i = 0; i < count; i++) { newList.addAll(data); } return new AstList(newList); } + @SuppressWarnings("rawtypes") + @Override protected void setslice(int start, int stop, int step, PyObject value) { - if(stop < start) { + if (stop < start) { stop = start; } if (value instanceof PySequence) { PySequence sequence = (PySequence) value; setslicePySequence(start, stop, step, sequence); } else if (value instanceof List) { - List list = (List)value.__tojava__(List.class); - if(list != null && list != Py.NoConversion) { + List list = (List) value.__tojava__(List.class); + if (list != null && list != Py.NoConversion) { setsliceList(start, stop, step, list); } } else { @@ -479,17 +499,22 @@ protected void setslice(int start, int stop, int step, PyObject value) { protected void setslicePySequence(int start, int stop, int step, PySequence value) { if (step != 0) { - if(value == this) { + if (value == this) { PyList newseq = new PyList(); PyObject iter = value.__iter__(); - for(PyObject item = null; (item = iter.__iternext__()) != null;) { + for (PyObject item = null; (item = iter.__iternext__()) != null;) { newseq.append(item); } value = newseq; } - int n = value.__len__(); + int size = __len__(), n = value.__len__(); for (int i = 0, j = start; i < n; i++, j += step) { - pyset(j, value.pyget(i)); + PyObject item = value.pyget(i); + if (j < size) { + pyset(j, item); + } else { + pyadd(item); + } } } } @@ -500,7 +525,7 @@ protected void setsliceList(int start, int stop, int step, @SuppressWarnings("ra throw Py.TypeError("setslice with java.util.List and step != 1 not supported yet"); } int n = value.size(); - for(int i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { data.add(i + start, value.get(i)); } } @@ -519,11 +544,13 @@ protected void setsliceIterable(int start, int stop, int step, PyObject value) { } @SuppressWarnings("unchecked") + @Override public void add(int index, Object element) { data.add(index, element); } @SuppressWarnings("unchecked") + @Override public boolean add(Object o) { return data.add(o); } @@ -538,10 +565,12 @@ public boolean addAll(@SuppressWarnings("rawtypes") Collection c) { return data.addAll(c); } + @Override public void clear() { data.clear(); } + @Override public boolean contains(Object o) { return data.contains(o); } @@ -551,30 +580,40 @@ public boolean containsAll(@SuppressWarnings("rawtypes") Collection c) { return data.containsAll(c); } + @Override public Object get(int index) { return data.get(index); } + @Override public int indexOf(Object o) { return data.indexOf(o); } + @Override public boolean isEmpty() { return data.isEmpty(); } + @SuppressWarnings("rawtypes") + @Override public Iterator iterator() { return data.iterator(); } + @Override public int lastIndexOf(Object o) { return data.lastIndexOf(o); } + @SuppressWarnings("rawtypes") + @Override public ListIterator listIterator() { return data.listIterator(); } + @SuppressWarnings("rawtypes") + @Override public ListIterator listIterator(int index) { return data.listIterator(index); } @@ -590,14 +629,16 @@ public void pyadd(int index, PyObject element) { data.add(index, element); } + @Override public PyObject pyget(int index) { if (adapter == null) { - return (PyObject)data.get(index); + return (PyObject) data.get(index); } return adapter.ast2py(data.get(index)); } @SuppressWarnings("unchecked") + @Override public void pyset(int index, PyObject element) { if (adapter == null) { data.set(index, element); @@ -607,10 +648,12 @@ public void pyset(int index, PyObject element) { } } + @Override public Object remove(int index) { return data.remove(index); } + @Override public boolean remove(Object o) { return data.remove(o); } @@ -630,39 +673,47 @@ public Object set(int index, Object element) { return data.set(index, element); } + @Override public int size() { return data.size(); } + @SuppressWarnings("rawtypes") + @Override public List subList(int fromIndex, int toIndex) { return data.subList(fromIndex, toIndex); } + @Override public Object[] toArray() { return data.toArray(); } @SuppressWarnings("unchecked") + @Override public Object[] toArray(Object[] a) { return data.toArray(a); } + @SuppressWarnings("rawtypes") + @Override public Object __tojava__(Class c) { - if(c.isInstance(this)) { + if (c.isInstance(this)) { return this; } return Py.NoConversion; } - /* Traverseproc implementation */ @Override public int traverse(Visitproc visit, Object arg) { int retVal; - for (Object ob: data) { + for (Object ob : data) { if (ob instanceof PyObject) { retVal = visit.visit((PyObject) ob, arg); - if (retVal != 0) return retVal; + if (retVal != 0) { + return retVal; + } } } return 0;