From jython-checkins at python.org Sat Dec 1 00:30:03 2012 From: jython-checkins at python.org (jeff.allen) Date: Sat, 1 Dec 2012 00:30:03 +0100 (CET) Subject: [Jython-checkins] =?utf-8?q?jython=3A_Buffer_api=3A_eliminate_Buf?= =?utf-8?q?ferPointer_as_class?= Message-ID: <3YCsJq6Pq2zMt4@mail.python.org> http://hg.python.org/jython/rev/58f8ff3a6216 changeset: 6888:58f8ff3a6216 user: Jeff Allen date: Fri Nov 30 22:05:43 2012 +0000 summary: Buffer api: eliminate BufferPointer as class Improvements associated with the attempt to make java.nio.ByteBuffer part of the buffer API. The BufferPointer class is now PyBuffer.Pointer and only appears as a return type. Internals of BaseBuffer flattened and the implementation of Sliced1DBuffer made slicker with range-checking in the constructor. JUnit tests (tidied a bit) all pass. files: src/org/python/core/BufferPointer.java | 41 -- src/org/python/core/PyBuffer.java | 48 ++- src/org/python/core/buffer/BaseBuffer.java | 157 +++------ src/org/python/core/buffer/SimpleBuffer.java | 123 +++++-- src/org/python/core/buffer/SimpleStringBuffer.java | 45 +- src/org/python/core/buffer/SimpleWritableBuffer.java | 62 +-- src/org/python/core/buffer/Strided1DBuffer.java | 149 +++++--- src/org/python/core/buffer/Strided1DWritableBuffer.java | 43 +- src/org/python/core/buffer/ZeroByteBuffer.java | 26 +- src/org/python/core/io/TextIOBase.java | 4 +- tests/java/org/python/core/PyBufferTest.java | 101 ++---- 11 files changed, 399 insertions(+), 400 deletions(-) diff --git a/src/org/python/core/BufferPointer.java b/src/org/python/core/BufferPointer.java deleted file mode 100644 --- a/src/org/python/core/BufferPointer.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.python.core; - -/** - * A class that references a specified byte[] array and an offset in it to be treated - * as "index zero", for use in the buffer API. This class simply bundles together a reference to an - * array and a particular offset within that array. It is used by the Jython buffer API roughly - * where the CPython buffer API uses a C (char *) pointer. - */ -public class BufferPointer { - - /** - * Reference to the backing array. Usually this is the actual storage exported by a Python - * object. In some contexts the consumer will be entitled to make changes to the contents of - * this array, and in others, not. See {@link PyBuffer#isReadonly()}. - */ - public final byte[] storage; - /** Starting position within the array for index calculations: "index zero". */ - public final int offset; - - /** - * Refer to an offset in the given array. - * - * @param storage array at reference - * @param offset index of the first byte - */ - public BufferPointer(byte[] storage, int offset) { - // No checks: keep it simple - this.storage = storage; - this.offset = offset; - } - - /** - * Refer to the whole of a byte array. - * - * @param storage array at reference - */ - public BufferPointer(byte[] storage) { - this.storage = storage; - this.offset = 0; - } -} \ No newline at end of file diff --git a/src/org/python/core/PyBuffer.java b/src/org/python/core/PyBuffer.java --- a/src/org/python/core/PyBuffer.java +++ b/src/org/python/core/PyBuffer.java @@ -220,7 +220,7 @@ * In a simple buffer backed by a contiguous byte array, the result is a strided PyBuffer on the * same storage but where the offset is adjusted by s and the stride is as supplied. If * the current buffer is already strided and/or has an item size larger than single bytes, the - * new offset, size and stride will be translated from the arguments given, through this + * new start index, length and stride will be translated from the arguments given, through this * buffer's stride and item size. The consumer always expresses start and * strides in terms of the abstract view of this buffer. * @@ -234,6 +234,33 @@ // Direct access to actual storage // + + /** + * A class that references a byte[] array and a particular offset within it, as the + * return type for methods that give direct access to byte-oriented data exported by a Python + * object. In some contexts the consumer will be entitled to make changes to the contents of + * this array, and in others not. See {@link PyBuffer#isReadonly()}. It is used by the Jython + * buffer API roughly where the CPython buffer API uses a C (char *) pointer. + */ + public static class Pointer { + + /** Reference to the array holding the bytes. */ + public byte[] storage; + /** Starting position within the array for the data being pointed to. */ + public int offset; + + /** + * Construct a reference to the given array and offset. + * + * @param storage array at reference + * @param offset index of the reference byte + */ + public Pointer(byte[] storage, int offset) { + this.storage = storage; + this.offset = offset; + } + } + /** * Return a structure describing the slice of a byte array that holds the data being exported to * the consumer. For a one-dimensional contiguous buffer, assuming the following client code @@ -242,7 +269,7 @@ *
      * PyBuffer a = obj.getBuffer();
      * int itemsize = a.getItemsize();
-     * BufferPointer b = a.getBuf();
+     * PyBuffer.Pointer b = a.getBuf();
      * 
* * the item with index k is in the array b.storage at index @@ -250,14 +277,14 @@ * inclusive. And if itemsize==1, the item is simply the byte * b.storage[b.offset + k] *

- * If the buffer is multidimensional or non-contiguous, b.storage[b.offset] is - * still the (first byte of) the item at index [0] or [0,...,0]. However, it is necessary to - * navigate b using the shape, strides and maybe + * If the buffer is multidimensional or non-contiguous, storage[offset] is still + * the (first byte of) the item at index [0] or [0,...,0]. However, it is necessary to navigate + * b.storage using the shape, strides and maybe * suboffsets provided by the API. * * @return structure defining the byte[] slice that is the shared data */ - BufferPointer getBuf(); + PyBuffer.Pointer getBuf(); /** * Return a structure describing the slice of a byte array that points to a single item from the @@ -268,7 +295,7 @@ * int k = ... ; * PyBuffer a = obj.getBuffer(); * int itemsize = a.getItemsize(); - * BufferPointer b = a.getPointer(k); + * PyBuffer.Pointer b = a.getPointer(k); * * * the item with index k is in the array b.storage at index @@ -282,7 +309,7 @@ * @param index in the buffer to position the pointer * @return structure defining the byte[] slice that is the shared data */ - BufferPointer getPointer(int index); + PyBuffer.Pointer getPointer(int index); /** * Return a structure describing the slice of a byte array that points to a single item from the @@ -295,7 +322,7 @@ * // ... calculation that assigns i, j, k * PyBuffer a = obj.getBuffer(); * int itemsize = a.getItemsize(); - * BufferPointer b = a.getPointer(i,j,k); + * PyBuffer.Pointer b = a.getPointer(i,j,k); * * * the item with index [i,j,k] is in the array b.storage at index @@ -313,7 +340,7 @@ * @param indices multidimensional index at which to position the pointer * @return structure defining the byte[] slice that is the shared data */ - BufferPointer getPointer(int... indices); + PyBuffer.Pointer getPointer(int... indices); // Inherited from PyBUF and belonging here // @@ -345,4 +372,5 @@ */ @Override public String toString(); + } diff --git a/src/org/python/core/buffer/BaseBuffer.java b/src/org/python/core/buffer/BaseBuffer.java --- a/src/org/python/core/buffer/BaseBuffer.java +++ b/src/org/python/core/buffer/BaseBuffer.java @@ -1,6 +1,5 @@ package org.python.core.buffer; -import org.python.core.BufferPointer; import org.python.core.BufferProtocol; import org.python.core.Py; import org.python.core.PyBUF; @@ -47,6 +46,7 @@ * filled (difference from CPython). This value is returned by {@link #getShape()}. */ protected int[] shape; + /** * Step sizes in the underlying buffer essential to correct translation of an index (or indices) * into an index into the storage. The strides array should always be created and @@ -54,11 +54,24 @@ * CPython). This value is returned by {@link #getStrides()}. */ protected int[] strides; + /** - * Reference to a structure that wraps the underlying storage that the exporter is sharing with - * the consumer. + * Reference to the underlying byte[] storage that the exporter is sharing with the + * consumer. The data need not occupy the whole array: in the constructor of a particular type + * of buffer, the exporter usually indicates an offset to the first significant byte and length + * (contiguous cases) or the index in storage that should be treated as the item + * with index zero (retrieved say by {@link #byteAt(int)}). */ - protected BufferPointer buf; + protected byte[] storage; + + /** + * Absolute index in storage of item[0]. In one dimension, for a + * positive stride this is equal to the offset of the first byte used in + * {@link #storage}, and for a negative stride it is the last. In an N-dimensional + * buffer with strides of mixed sign, it could be anywhere in the data. + */ + protected int index0; + /** * Count the number of times {@link #release()} must be called before actual release actions * need to take place. Equivalently, this is the number of calls to @@ -114,16 +127,10 @@ * exported, not the flags that form the consumer's request. The buffer will be read-only unless * {@link PyBUF#WRITABLE} is set in the feature flags. {@link PyBUF#FORMAT} is implicitly added * to the feature flags. The navigation arrays are all null, awaiting action by the sub-class - * constructor. To complete initialisation, the sub-class normally must assign: {@link #buf}, - * {@link #shape}, and {@link #strides}, and call {@link #checkRequestFlags(int)} passing the - * consumer's request flags. - * - *

-     * this.buf = buf;                  // Wraps exported data
-     * this.shape = shape;              // Array of dimensions of exported data (in units)
-     * this.strides = strides;          // Byte offset between successive items in each dimension
-     * checkRequestFlags(flags);        // Check request is compatible with type
-     * 
+ * constructor. To complete initialisation, the sub-class normally must assign: the buffer ( + * {@link #storage}, {@link #index0}), and the navigation arrays ({@link #shape}, + * {@link #strides}), and call {@link #checkRequestFlags(int)} passing the consumer's request + * flags. * * @param featureFlags bit pattern that specifies the actual features allowed/required */ @@ -215,15 +222,21 @@ return shape; } + /** + * {@inheritDoc} + *

+ * The default implementation in BaseBuffer deals with the general one-dimensional + * case, with any item size and stride. + */ @Override public int getLen() { - // Correct if one-dimensional bytes. Override with itemsize*product(shape). - return shape[0]; + // Correct if one-dimensional. Override with itemsize*product(shape). + return shape[0] * getItemsize(); } @Override public byte byteAt(int index) throws IndexOutOfBoundsException { - return buf.storage[calcIndex(index)]; + return storage[calcIndex(index)]; } @Override @@ -236,7 +249,7 @@ if (isReadonly()) { throw notWritable(); } - buf.storage[calcIndex(index)] = value; + storage[calcIndex(index)] = value; } /** @@ -248,12 +261,12 @@ */ protected int calcIndex(int index) throws IndexOutOfBoundsException { // Treat as one-dimensional - return buf.offset + index * getStrides()[0]; + return index0 + index * getStrides()[0]; } @Override public byte byteAt(int... indices) throws IndexOutOfBoundsException { - return buf.storage[calcIndex(indices)]; + return storage[calcIndex(indices)]; } @Override @@ -266,29 +279,29 @@ if (isReadonly()) { throw notWritable(); } - buf.storage[calcIndex(indices)] = value; + storage[calcIndex(indices)] = value; } /** * Convert a multi-dimensional item index (if we are not using indirection) to an absolute byte * index in the actual storage array being shared by the exporter. The purpose of this method is * to allow a sub-class to define, in one place, an indexing calculation that maps the index as - * provided by the consumer into an index in the storage as seen by the buffer. + * provided by the consumer into an index in the storage known to the buffer. *

- * In the usual case where the storage is referenced via the BufferPointer member - * {@link #buf}, the buffer implementation may use buf.storage[calcIndex(i)] to - * reference the (first byte of) the item x[i]. This is what the default implementation of - * accessors in BaseBuffer will do. In the simplest cases, this is fairly - * inefficient, and an implementation will override the accessors to in-line the calculation. - * The default implementation here is suited to N-dimensional arrays. + * In the usual case where the storage is referenced via the {@link #storage} and + * {@link #index0} members, the buffer implementation may use storage[calcIndex(i)] + * to reference the (first byte of) the item x[i]. This is what the default implementation of + * accessors in BaseBuffer will do. In the simplest cases, calling + * calcIndex may be an overhead to avoid, and an implementation will specialise the + * accessors. The default implementation here is suited to N-dimensional arrays. * * @param indices of the item from the consumer * @return index relative to item x[0,...,0] in actual storage */ protected int calcIndex(int... indices) throws IndexOutOfBoundsException { final int N = checkDimension(indices); - // In general: buf.offset + sum(k=0,N-1) indices[k]*strides[k] - int index = buf.offset; + // In general: index0 + sum(k=0,N-1) indices[k]*strides[k] + int index = index0; if (N > 0) { int[] strides = getStrides(); for (int k = 0; k < N; k++) { @@ -332,13 +345,13 @@ // Strategy depends on whether items are laid end-to-end contiguously or there are gaps if (skip == 0) { // stride == itemsize: straight copy of contiguous bytes - System.arraycopy(buf.storage, s, dest, d, length * itemsize); + System.arraycopy(storage, s, dest, d, length * itemsize); } else if (itemsize == 1) { // Non-contiguous copy: single byte items int limit = s + length * stride; for (; s < limit; s += stride) { - dest[d++] = buf.storage[s]; + dest[d++] = storage[s]; } } else { @@ -347,7 +360,7 @@ for (; s < limit; s += skip) { int t = s + itemsize; while (s < t) { - dest[d++] = buf.storage[s++]; + dest[d++] = storage[s++]; } } } @@ -381,13 +394,13 @@ // Strategy depends on whether items are laid end-to-end or there are gaps if (skip == 0) { // Straight copy of contiguous bytes - System.arraycopy(src, srcPos, buf.storage, d, length * itemsize); + System.arraycopy(src, srcPos, storage, d, length * itemsize); } else if (itemsize == 1) { // Non-contiguous copy: single byte items int limit = d + length * stride; for (; d != limit; d += stride) { - buf.storage[d] = src[s++]; + storage[d] = src[s++]; } } else { @@ -396,7 +409,7 @@ for (; d != limit; d += skip) { int t = d + itemsize; while (d < t) { - buf.storage[d++] = src[s++]; + storage[d++] = src[s++]; } } } @@ -430,21 +443,21 @@ // Strategy depends on whether items are laid end-to-end or there are gaps if (stride == itemsize) { // Straight copy to contiguous bytes - src.copyTo(buf.storage, d); + src.copyTo(storage, d); } else if (itemsize == 1) { // Non-contiguous copy: single byte items int limit = d + src.getLen() * stride; for (; d != limit; d += stride) { - buf.storage[d] = src.byteAt(s++); + storage[d] = src.byteAt(s++); } } else { // Non-contiguous copy: each time, copy itemsize bytes then skip int limit = d + src.getShape()[0] * stride; for (; d != limit; d += stride) { - BufferPointer srcItem = src.getPointer(s++); - System.arraycopy(srcItem.storage, srcItem.offset, buf.storage, d, itemsize); + Pointer srcItem = src.getPointer(s++); + System.arraycopy(srcItem.storage, srcItem.offset, storage, d, itemsize); } } @@ -521,18 +534,18 @@ // @Override public PyBuffer getBufferSlice(int flags, int start, int length, int stride) {} @Override - public BufferPointer getBuf() { - return buf; + public Pointer getBuf() { + return new Pointer(storage, index0); } @Override - public BufferPointer getPointer(int index) { - return new BufferPointer(buf.storage, calcIndex(index)); + public Pointer getPointer(int index) throws IndexOutOfBoundsException { + return new Pointer(storage, calcIndex(index)); } @Override - public BufferPointer getPointer(int... indices) { - return new BufferPointer(buf.storage, calcIndex(indices)); + public Pointer getPointer(int... indices) throws IndexOutOfBoundsException { + return new Pointer(storage, calcIndex(indices)); } @Override @@ -620,58 +633,6 @@ } /** - * Convenience method for checking arguments to slice formation, that the start and end elements - * are within the buffer. An exception is raised if start<0 or - * start+length>shape[0]. This logic is correct for one-dimensional arrays (of - * any item size) and stride. In the context we use this, length is guaranteed by - * the acller to be non-negative. - * - * @param start index to check - * @param length number of elements in slice (must be >0) - * @throws IndexOutOfBoundsException - */ - protected void checkSlice(int start, int length) throws IndexOutOfBoundsException { - // Between the last element of the slice and the end of the buffer there are ... - int margin = shape[0] - start - length; - if ((start | margin) < 0) { - throw new IndexOutOfBoundsException("invalid slice of buffer"); - } - } - - /** - * Convenience method for checking arguments to slice formation, that the start and end elements - * are within the buffer. An exception is raised if either of start or - * end=start+(length-1)*stride+1 is <0 or >shape[0] - * . This logic is correct for one-dimensional arrays (of any item size) and current stride. - * Note that the parameter stride is in terms of this biuffer's indexing. In the context we use - * this, length is guaranteed by the acller to be non-negative. - * - * @param start index to check - * @param length number of elements in slice (must be >0) - * @param stride in buffer elements between items of the slice - * @throws IndexOutOfBoundsException - */ - protected void checkSlice(int start, int length, int stride) throws IndexOutOfBoundsException { - /* - * Simpler to check if we know which is the smaller of the two ends, which depends on the - * sign of the stride. - */ - int lo, hi; - if (stride > 0) { - lo = start; - hi = start + (length - 1) * stride + 1; - } else { - hi = start; - lo = start + (length - 1) * stride + 1; - } - // Between the last element of the slice and the end of the buffer there are ... - int margin = shape[0] - hi; - if ((lo | margin) < 0) { - throw new IndexOutOfBoundsException("invalid slice of buffer"); - } - } - - /** * The toString() method of a buffer reproduces the values in the buffer (as unsigned integers) * as the character codes of a String. */ diff --git a/src/org/python/core/buffer/SimpleBuffer.java b/src/org/python/core/buffer/SimpleBuffer.java --- a/src/org/python/core/buffer/SimpleBuffer.java +++ b/src/org/python/core/buffer/SimpleBuffer.java @@ -1,6 +1,5 @@ package org.python.core.buffer; -import org.python.core.BufferPointer; import org.python.core.PyBuffer; import org.python.core.PyException; import org.python.core.util.StringUtil; @@ -17,16 +16,10 @@ /** * Provide an instance of SimpleBuffer with navigation variables partly - * initialised, for sub-class use. One-dimensional arrays without slicing are C- and - * F-contiguous. To complete initialisation, the sub-class normally must assign: {@link #buf} - * and {@link #shape}[0], and call {@link #checkRequestFlags(int)} passing the consumer's - * request flags. - * - *

-     * this.buf = buf;              // Wraps exported data
-     * this.shape[0] = n;           // Number of units in exported data
-     * checkRequestFlags(flags);    // Check request is compatible with type
-     * 
+ * initialised, for sub-class use. One-dimensional arrays without strides are C- and + * F-contiguous. To complete initialisation, the sub-class must normally assign the buffer ( + * {@link #storage}, {@link #index0}), and the navigation ({@link #shape} array), and then call + * {@link #checkRequestFlags(int)} passing the consumer's request flags. */ protected SimpleBuffer() { super(CONTIGUITY | SIMPLE); @@ -37,22 +30,70 @@ } /** + * Provide an instance of SimpleBuffer with navigation variables initialised, for + * sub-class use. The buffer ({@link #storage}, {@link #index0}), and the {@link #shape} array + * will be initialised from the arguments (which are checked for range). The {@link #strides} is + * set for (one-byte) unit stride. Only the call to {@link #checkRequestFlags(int)}, passing the + * consumer's request flags really remains for the sub-class constructor to do. + * + *
+     * super(storage, index0, size);
+     * checkRequestFlags(flags);        // Check request is compatible with type
+     * 
+ * + * @param storage the array of bytes storing the implementation of the exporting object + * @param index0 offset where the data starts in that array (item[0]) + * @param size the number of bytes occupied + * @throws NullPointerException if storage is null + * @throws ArrayIndexOutOfBoundsException if index0 and size are + * inconsistent with storage.length + */ + public SimpleBuffer(byte[] storage, int index0, int size) throws PyException, + ArrayIndexOutOfBoundsException { + this(); + this.storage = storage; // Exported data + this.index0 = index0; // Index to be treated as item[0] + this.shape[0] = size; // Number of items in exported data + + // Check arguments using the "all non-negative" trick + if ((index0 | size | storage.length - (index0 + size)) < 0) { + throw new ArrayIndexOutOfBoundsException(); + } + } + + /** * Provide an instance of SimpleBuffer, on a slice of a byte array, meeting the * consumer's expectations as expressed in the flags argument, which is checked * against the capabilities of the buffer type. * * @param flags consumer requirements * @param storage the array of bytes storing the implementation of the exporting object - * @param offset where the data starts in that array (item[0]) + * @param index0 offset where the data starts in that array (item[0]) * @param size the number of bytes occupied + * @throws NullPointerException if storage is null + * @throws ArrayIndexOutOfBoundsException if index0 and size are + * inconsistent with storage.length * @throws PyException (BufferError) when expectations do not correspond with the type */ - public SimpleBuffer(int flags, byte[] storage, int offset, int size) throws PyException { + public SimpleBuffer(int flags, byte[] storage, int index0, int size) throws PyException, + ArrayIndexOutOfBoundsException, NullPointerException { + this(storage, index0, size); // Construct checked SimpleBuffer + checkRequestFlags(flags); // Check request is compatible with type + } + + /** + * Provide an instance of SimpleBuffer, on the entirety of a byte array, with + * navigation variables initialised, for sub-class use. The buffer ( {@link #storage}, + * {@link #index0}), and the navigation ({@link #shape} array) will be initialised from the + * array argument. + * + * @param storage the array of bytes storing the implementation of the exporting object + * @throws NullPointerException if storage is null + */ + public SimpleBuffer(byte[] storage) throws NullPointerException { this(); - // Wrap the exported data on a BufferPointer object - this.buf = new BufferPointer(storage, offset); - this.shape[0] = size; // Number of units in exported data - checkRequestFlags(flags); // Check request is compatible with type + this.storage = storage; // Exported data (index0=0 from initialisation) + this.shape[0] = storage.length; // Number of units in whole array } /** @@ -62,10 +103,12 @@ * * @param flags consumer requirements * @param storage the array of bytes storing the implementation of the exporting object + * @throws NullPointerException if storage is null * @throws PyException (BufferError) when expectations do not correspond with the type */ - public SimpleBuffer(int flags, byte[] storage) throws PyException { - this(flags, storage, 0, storage.length); + public SimpleBuffer(int flags, byte[] storage) throws PyException, NullPointerException { + this(storage); // Construct SimpleBuffer on whole array + checkRequestFlags(flags); // Check request is compatible with type } @Override @@ -80,9 +123,21 @@ * one-dimension. */ @Override + public int getLen() { + // Simplify for one-dimensional contiguous bytes + return shape[0]; + } + + /** + * {@inheritDoc} + *

+ * SimpleBuffer provides an implementation optimised for contiguous bytes in + * one-dimension. + */ + @Override public byte byteAt(int index) throws IndexOutOfBoundsException { // Implement directly: a bit quicker than the default - return buf.storage[buf.offset + index]; + return storage[index0 + index]; } /** @@ -94,12 +149,12 @@ @Override public int intAt(int index) throws IndexOutOfBoundsException { // Implement directly: a bit quicker than the default - return 0xff & buf.storage[buf.offset + index]; + return 0xff & storage[index0 + index]; } @Override protected int calcIndex(int index) throws IndexOutOfBoundsException { - return buf.offset + index; + return index0 + index; } /** @@ -130,18 +185,16 @@ @Override public void copyTo(int srcIndex, byte[] dest, int destPos, int length) throws IndexOutOfBoundsException { - System.arraycopy(buf.storage, buf.offset + srcIndex, dest, destPos, length); + System.arraycopy(storage, index0 + srcIndex, dest, destPos, length); } @Override public PyBuffer getBufferSlice(int flags, int start, int length) { if (length > 0) { - // Check the arguments define a slice within this buffer - checkSlice(start, length); // Translate relative to underlying buffer - int compIndex0 = buf.offset + start; + int compIndex0 = index0 + start; // Create the slice from the sub-range of the buffer - return new SimpleView(getRoot(), flags, buf.storage, compIndex0, length); + return new SimpleView(getRoot(), flags, storage, compIndex0, length); } else { // Special case for length==0 where above logic would fail. Efficient too. return new ZeroByteBuffer.View(getRoot(), flags); @@ -165,23 +218,21 @@ return getBufferSlice(flags, start, length); } else { - // Check the arguments define a slice within this buffer - checkSlice(start, length, stride); // Translate relative to underlying buffer - int compIndex0 = buf.offset + start; + int compIndex0 = index0 + start; // Construct a view, taking a lock on the root object (this or this.root) - return new Strided1DBuffer.SlicedView(getRoot(), flags, buf.storage, compIndex0, - length, stride); + return new Strided1DBuffer.SlicedView(getRoot(), flags, storage, compIndex0, length, + stride); } } @Override - public BufferPointer getPointer(int index) { - return new BufferPointer(buf.storage, buf.offset + index); + public Pointer getPointer(int index) throws IndexOutOfBoundsException { + return new Pointer(storage, index0 + index); } @Override - public BufferPointer getPointer(int... indices) { + public Pointer getPointer(int... indices) throws IndexOutOfBoundsException { checkDimension(indices.length); return getPointer(indices[0]); } @@ -189,7 +240,7 @@ @Override public String toString() { // For contiguous bytes in one dimension we can avoid the intAt() calls - return StringUtil.fromBytes(buf.storage, buf.offset, shape[0]); + return StringUtil.fromBytes(storage, index0, shape[0]); } /** diff --git a/src/org/python/core/buffer/SimpleStringBuffer.java b/src/org/python/core/buffer/SimpleStringBuffer.java --- a/src/org/python/core/buffer/SimpleStringBuffer.java +++ b/src/org/python/core/buffer/SimpleStringBuffer.java @@ -1,13 +1,12 @@ package org.python.core.buffer; -import org.python.core.BufferPointer; import org.python.core.PyBuffer; import org.python.core.util.StringUtil; /** * Buffer API that appears to be a one-dimensional array of one-byte items providing read-only API, * but which is actually backed by a Java String. Some of the buffer API absolutely needs access to - * the data as a byte array (those parts that involve a {@link BufferPointer} result), and therefore + * the data as a byte array (those parts that involve a {@link PyBuffer.Pointer} result), and therefore * this class must create a byte array from the String for them. However, it defers creation of a * byte array until that part of the API is actually used. Where possible, this class overrides * those methods in SimpleBuffer that would otherwise access the byte array attribute to use the @@ -29,7 +28,6 @@ * @param flags consumer requirements */ public SimpleStringBuffer(int flags, String bufString) { - super(); // Save the backing string this.bufString = bufString; shape[0] = bufString.length(); @@ -101,6 +99,7 @@ *

* The SimpleStringBuffer implementation creates an actual byte buffer. */ + @Override public PyBuffer getBufferSlice(int flags, int start, int length, int stride) { if (stride == 1) { // Unstrided slice of simple buffer is itself simple @@ -113,43 +112,51 @@ } /** - * {@inheritDoc} - *

- * This method creates an actual byte buffer from the String if none yet exists. + * This method creates an actual byte array from the underlying String if none yet exists. */ - @Override - public BufferPointer getBuf() { - if (buf == null) { - // We can't avoid creating buf any longer - buf = new BufferPointer(StringUtil.toBytes(bufString)); + private void ensureHaveBytes() { + if (storage == null) { + // We can't avoid creating the byte array any longer (index0 already correct) + storage = StringUtil.toBytes(bufString); } - return buf; } /** * {@inheritDoc} *

- * This method creates an actual byte buffer from the String if none yet exists. + * This method creates an actual byte array from the underlying String if none yet exists. */ @Override - public BufferPointer getPointer(int index) { - getBuf(); // Ensure buffer created + public Pointer getBuf() { + ensureHaveBytes(); + return super.getBuf(); + } + + /** + * {@inheritDoc} + *

+ * This method creates an actual byte array from the underlying String if none yet exists. + */ + @Override + public Pointer getPointer(int index) { + ensureHaveBytes(); return super.getPointer(index); } /** * {@inheritDoc} *

- * This method creates an actual byte buffer from the String if none yet exists. + * This method creates an actual byte array from the underlying String if none yet exists. */ @Override - public BufferPointer getPointer(int... indices) { - getBuf(); // Ensure buffer created + public Pointer getPointer(int... indices) { + ensureHaveBytes(); return super.getPointer(indices); } /** - * The toString() method of a SimpleStringBuffer simply produces the underlying String. + * The toString() method of a SimpleStringBuffer simply produces the + * underlying String. */ @Override public String toString() { diff --git a/src/org/python/core/buffer/SimpleWritableBuffer.java b/src/org/python/core/buffer/SimpleWritableBuffer.java --- a/src/org/python/core/buffer/SimpleWritableBuffer.java +++ b/src/org/python/core/buffer/SimpleWritableBuffer.java @@ -1,6 +1,5 @@ package org.python.core.buffer; -import org.python.core.BufferPointer; import org.python.core.PyBuffer; import org.python.core.PyException; @@ -10,35 +9,36 @@ public class SimpleWritableBuffer extends SimpleBuffer { /** - * Provide an instance of SimpleWritableBuffer, on a slice of a byte array, meeting the consumer's expectations - * as expressed in the flags argument, which is checked against the capabilities of - * the buffer type. + * Provide an instance of SimpleWritableBuffer, on a slice of a byte array, meeting + * the consumer's expectations as expressed in the flags argument, which is checked + * against the capabilities of the buffer type. * * @param flags consumer requirements * @param storage the array of bytes storing the implementation of the exporting object - * @param offset where the data starts in that array (item[0]) + * @param index0 offset where the data starts in that array (item[0]) * @param size the number of bytes occupied * @throws PyException (BufferError) when expectations do not correspond with the type */ - public SimpleWritableBuffer(int flags, byte[] storage, int offset, int size) throws PyException { + public SimpleWritableBuffer(int flags, byte[] storage, int index0, int size) + throws PyException, NullPointerException { + super(storage, index0, size); // Construct checked SimpleBuffer addFeatureFlags(WRITABLE); - // Wrap the exported data on a BufferPointer object - this.buf = new BufferPointer(storage, offset); - this.shape[0] = size; // Number of units in exported data - checkRequestFlags(flags); // Check request is compatible with type + checkRequestFlags(flags); // Check request is compatible with type } /** - * Provide an instance of SimpleWritableBuffer, on the entirety of a byte array, meeting the consumer's expectations - * as expressed in the flags argument, which is checked against the capabilities of - * the buffer type. + * Provide an instance of SimpleWritableBuffer, on the entirety of a byte array, + * meeting the consumer's expectations as expressed in the flags argument, which is + * checked against the capabilities of the buffer type. * * @param flags consumer requirements * @param storage the array of bytes storing the implementation of the exporting object * @throws PyException (BufferError) when expectations do not correspond with the type */ - public SimpleWritableBuffer(int flags, byte[] storage) throws PyException { - this(flags, storage, 0, storage.length); + public SimpleWritableBuffer(int flags, byte[] storage) throws PyException, NullPointerException { + super(storage); // Construct SimpleBuffer on whole array + addFeatureFlags(WRITABLE); + checkRequestFlags(flags); // Check request is compatible with type } @Override @@ -55,7 +55,7 @@ @Override public void storeAt(byte value, int index) { // Implement directly and don't ask whether read-only - buf.storage[buf.offset + index] = value; + storage[index0 + index] = value; } /** @@ -78,7 +78,7 @@ */ @Override public void copyFrom(byte[] src, int srcPos, int destIndex, int length) { - System.arraycopy(src, srcPos, buf.storage, buf.offset + destIndex, length); + System.arraycopy(src, srcPos, storage, index0 + destIndex, length); } /** @@ -89,13 +89,11 @@ */ @Override public void copyFrom(PyBuffer src) throws IndexOutOfBoundsException, PyException { - if (src.getLen() != getLen()) { throw differentStructure(); } - // Get the source to deliver efficiently to our byte storage - src.copyTo(buf.storage, buf.offset); + src.copyTo(storage, index0); } /** @@ -107,12 +105,10 @@ @Override public PyBuffer getBufferSlice(int flags, int start, int length) { if (length > 0) { - // Check the arguments define a slice within this buffer - checkSlice(start, length); // Translate relative to underlying buffer - int compIndex0 = buf.offset + start; + int compIndex0 = index0 + start; // Create the slice from the sub-range of the buffer - return new SimpleView(getRoot(), flags, buf.storage, compIndex0, length); + return new SimpleView(getRoot(), flags, storage, compIndex0, length); } else { // Special case for length==0 where above logic would fail. Efficient too. return new ZeroByteBuffer.View(getRoot(), flags); @@ -133,19 +129,17 @@ return getBufferSlice(flags, start, length); } else { - // Check the arguments define a slice within this buffer - checkSlice(start, length, stride); // Translate relative to underlying buffer - int compIndex0 = buf.offset + start; + int compIndex0 = index0 + start; // Construct a view, taking a lock on the root object (this or this.root) - return new Strided1DWritableBuffer.SlicedView(getRoot(), flags, buf.storage, - compIndex0, length, stride); + return new Strided1DWritableBuffer.SlicedView(getRoot(), flags, storage, compIndex0, + length, stride); } } /** - * A SimpleWritableBuffer.SimpleView represents a contiguous subsequence of - * another SimpleWritableBuffer. + * A SimpleWritableBuffer.SimpleView represents a contiguous subsequence of another + * SimpleWritableBuffer. */ static class SimpleView extends SimpleWritableBuffer { @@ -158,12 +152,12 @@ * @param root buffer which will be acquired and must be released ultimately * @param flags the request flags of the consumer that requested the slice * @param storage the array of bytes storing the implementation of the exporting object - * @param offset where the data starts in that array (item[0]) + * @param index0 offset where the data starts in that array (item[0]) * @param size the number of bytes occupied */ - public SimpleView(PyBuffer root, int flags, byte[] storage, int offset, int size) { + public SimpleView(PyBuffer root, int flags, byte[] storage, int index0, int size) { // Create a new SimpleBuffer on the buffer passed in (part of the root) - super(flags, storage, offset, size); + super(flags, storage, index0, size); // Get a lease on the root PyBuffer this.root = root.getBuffer(FULL_RO); } diff --git a/src/org/python/core/buffer/Strided1DBuffer.java b/src/org/python/core/buffer/Strided1DBuffer.java --- a/src/org/python/core/buffer/Strided1DBuffer.java +++ b/src/org/python/core/buffer/Strided1DBuffer.java @@ -1,26 +1,25 @@ package org.python.core.buffer; -import org.python.core.BufferPointer; import org.python.core.PyBuffer; import org.python.core.PyException; /** * Read-only buffer API over a one-dimensional array of one-byte items, that are evenly-spaced in a - * storage array. The buffer has a buf property in the usual way, designating a slice - * (or all) of a byte array, but also a stride property (equal to - * getStrides()[0]). + * storage array. The buffer has storage, index0 and length + * properties in the usual way, designating a slice (or all) of a byte array, but also a + * stride property (equal to getStrides()[0]). *

- * Let this underlying buffer be the byte array u(i) for i=a..a+N, let x be the + * Let this underlying buffer be the byte array u(i) for i=0..N-1, let x be the * Strided1DBuffer, and let the stride be p. The storage works as follows. * Designate by x(j), for j=0..L-1, the byte at index j, that is, the byte - * retrieved by x.byteAt(j). Then, - *

+ * retrieved by x.byteAt(j). Then, we store x(j) at u(a+pj), that is, + * x(0) is at u(a). When we construct such a buffer, we have to supply a = + * index0, L = length, and p = stride as the + * constructor arguments. The last item in the slice x(L-1) is stored at u(a+p(L-1)). + * If p<0 and L>1, this will be to the left of u(a), so the constructor + * argument index0 is not then the low index of the range occupied by the data. Clearly both these + * indexes must be in the range 0 to N-1 inclusive, a rule enforced by the constructors + * (unless L=0, when it is assumed no array access will take place). *

* The class may be used by exporters to create a strided slice (e.g. to export the diagonal of a * matrix) and in particular by other buffers to create strided slices of themselves, such as to @@ -38,13 +37,14 @@ /** * Provide an instance of Strided1DBuffer with navigation variables partly * initialised, for sub-class use. To complete initialisation, the sub-class normally must - * assign: {@link #buf}, {@link #shape}[0], and {@link #stride}, and call - * {@link #checkRequestFlags(int)} passing the consumer's request flags. + * assign the navigational properties and call {@link #checkRequestFlags(int)} passing the + * consumer's request flags. * *

-     * this.buf = buf;              // Wraps exported data
-     * setStride(stride);           // Stride, shape[0] and index0 all set consistently
-     * checkRequestFlags(flags);    // Check request is compatible with type
+     * this.storage = storage;          // Exported data
+     * this.index0 = index0;            // Index to be treated as item[0]
+     * this.shape[0] = length;          // Number of items in exported data
+     * this.stride = stride;            // Between items
      * 
* * The pre-defined {@link #strides} field remains null until {@link #getStrides} is @@ -59,38 +59,87 @@ } /** + * Provide an instance of Strided1DBuffer with navigation variables initialised, + * for sub-class use. The buffer ( {@link #storage}, {@link #index0}), and the navigation ( + * {@link #shape} array and {@link #stride}) will be initialised from the arguments (which are + * checked for range). + * + * @param storage raw byte array containing exported data + * @param index0 index into storage of item[0] + * @param length number of items in the slice + * @param stride in between successive elements of the new PyBuffer + * @throws NullPointerException if storage is null + * @throws ArrayIndexOutOfBoundsException if index0, length and + * stride are inconsistent with storage.length + */ + public Strided1DBuffer(byte[] storage, int index0, int length, int stride) + throws ArrayIndexOutOfBoundsException, NullPointerException { + this(); + this.storage = storage; // Exported data + this.index0 = index0; // Index to be treated as item[0] + this.shape[0] = length; // Number of items in exported data + this.stride = stride; // Between items + + if (length == 0) { + // Nothing to check as we'll make no accesses + addFeatureFlags(CONTIGUITY); + + } else { + // Need to check lowest and highest index against array + int lo, hi; + + if (stride == 1) { + lo = index0; // First byte of item[0] + hi = index0 + length; // Last byte of item[L-1] + 1 + addFeatureFlags(CONTIGUITY); + + } else if (stride > 1) { + lo = index0; // First byte of item[0] + hi = index0 + (length - 1) * stride + 1; // Last byte of item[L-1] + 1 + + } else { + hi = index0 + 1; // Last byte of item[0] + 1 + lo = index0 + (length - 1) * stride; // First byte of item[L-1] + } + + // Check indices using "all non-negative" trick + if ((length | lo | (storage.length - lo) | hi | (storage.length - hi)) < 0) { + throw new ArrayIndexOutOfBoundsException(); + } + } + } + + /** * Provide an instance of Strided1DBuffer on a particular array of bytes specifying * a starting index, the number of items in the result, and a byte-indexing stride. The result * of byteAt(i) will be equal to storage[index0+stride*i] (whatever - * the sign of stride>0), valid for 0<=i<length. + * the sign of stride), valid for 0<=i<length. The constructor + * checks that all these indices lie within the storage array (unless + * length=0). *

* The constructed PyBuffer meets the consumer's expectations as expressed in the * flags argument, or an exception will be thrown if these are incompatible with * the type (e.g. the consumer does not specify that it understands the strides array). Note * that the actual range in the storage array, the lowest and highest index, is not * explicitly passed, but is implicit in index0, length and - * stride. The caller is responsible for checking these fall within the array, or - * the sub-range the caller is allowed to use. + * stride. The constructor checks that these indices lie within the + * storage array (unless length=0). * * @param flags consumer requirements * @param storage raw byte array containing exported data * @param index0 index into storage of item[0] * @param length number of items in the slice * @param stride in between successive elements of the new PyBuffer + * @throws NullPointerException if storage is null + * @throws ArrayIndexOutOfBoundsException if index0, length and + * stride are inconsistent with storage.length * @throws PyException (BufferError) when expectations do not correspond with the type */ public Strided1DBuffer(int flags, byte[] storage, int index0, int length, int stride) - throws PyException { - // Arguments programme the object directly - this(); - this.shape[0] = length; - this.buf = new BufferPointer(storage, index0); - this.stride = stride; - if (stride == 1) { - // Really this is a simple buffer - addFeatureFlags(CONTIGUITY); - } + throws ArrayIndexOutOfBoundsException, NullPointerException, PyException { + this(storage, index0, length, stride); checkRequestFlags(flags); // Check request is compatible with type + } @Override @@ -100,12 +149,12 @@ @Override public byte byteAt(int index) throws IndexOutOfBoundsException { - return buf.storage[buf.offset + index * stride]; + return storage[index0 + index * stride]; } @Override protected int calcIndex(int index) throws IndexOutOfBoundsException { - return buf.offset + index * stride; + return index0 + index * stride; } @Override @@ -123,19 +172,19 @@ public void copyTo(int srcIndex, byte[] dest, int destPos, int length) throws IndexOutOfBoundsException { // Data is here in the buffers - int s = buf.offset + srcIndex * stride; + int s = index0 + srcIndex * stride; int d = destPos; // Strategy depends on whether items are laid end-to-end contiguously or there are gaps if (stride == 1) { // stride == itemsize: straight copy of contiguous bytes - System.arraycopy(buf.storage, s, dest, d, length); + System.arraycopy(storage, s, dest, d, length); } else { // Non-contiguous copy: single byte items int limit = s + length * stride; for (; s != limit; s += stride) { - dest[d++] = buf.storage[s]; + dest[d++] = storage[s]; } } } @@ -147,30 +196,18 @@ * one dimension. In that case, x(i) = u(r+ip) for i = 0..L-1 where u is the * underlying buffer, and r, p and L are the start, stride and length with * which x was created from u. Thus y(k) = u(r+sp+kmp), that is, the - * composite offset is r+sp and the composite stride is mp. + * composite index0 is r+sp and the composite stride is + * mp. */ @Override public PyBuffer getBufferSlice(int flags, int start, int length, int stride) { if (length > 0) { - int compStride; - - if (stride == 1) { - // Check the arguments define a slice within this buffer - checkSlice(start, length); - // Composite stride is same as original stride - compStride = this.stride; - } else { - // Check the arguments define a slice within this buffer - checkSlice(start, length, stride); - // Composite stride is product - compStride = this.stride * stride; - } - // Translate start relative to underlying buffer - int compIndex0 = buf.offset + start * this.stride; + int compStride = this.stride * stride; + int compIndex0 = index0 + start * this.stride; // Construct a view, taking a lock on the root object (this or this.root) - return new SlicedView(getRoot(), flags, buf.storage, compIndex0, length, compStride); + return new SlicedView(getRoot(), flags, storage, compIndex0, length, compStride); } else { // Special case for length==0 where above logic would fail. Efficient too. @@ -179,12 +216,12 @@ } @Override - public BufferPointer getPointer(int index) { - return new BufferPointer(buf.storage, buf.offset + index * stride); + public Pointer getPointer(int index) { + return new Pointer(storage, index0 + index * stride); } @Override - public BufferPointer getPointer(int... indices) { + public Pointer getPointer(int... indices) { // BaseBuffer implementation can be simplified since if indices.length!=1 we error. checkDimension(indices.length); return getPointer(indices[0]); diff --git a/src/org/python/core/buffer/Strided1DWritableBuffer.java b/src/org/python/core/buffer/Strided1DWritableBuffer.java --- a/src/org/python/core/buffer/Strided1DWritableBuffer.java +++ b/src/org/python/core/buffer/Strided1DWritableBuffer.java @@ -1,6 +1,5 @@ package org.python.core.buffer; -import org.python.core.BufferPointer; import org.python.core.PyBuffer; import org.python.core.PyException; @@ -30,19 +29,14 @@ * @param index0 index into storage of item[0] * @param length number of items in the slice * @param stride in between successive elements of the new PyBuffer + * @throws NullPointerException if storage is null + * @throws ArrayIndexOutOfBoundsException if index0, length and + * stride are inconsistent with storage.length * @throws PyException (BufferError) when expectations do not correspond with the type */ public Strided1DWritableBuffer(int flags, byte[] storage, int index0, int length, int stride) - throws PyException { - // Arguments programme the object directly - // this(); - this.shape[0] = length; - this.buf = new BufferPointer(storage, index0); - this.stride = stride; - if (stride == 1) { - // Really this is a simple buffer - addFeatureFlags(CONTIGUITY); - } + throws ArrayIndexOutOfBoundsException, NullPointerException, PyException { + super(storage, index0, length, stride); addFeatureFlags(WRITABLE); checkRequestFlags(flags); // Check request is compatible with type } @@ -54,7 +48,7 @@ @Override public void storeAt(byte value, int index) throws IndexOutOfBoundsException, PyException { - buf.storage[buf.offset + index * stride] = value; + storage[index0 + index * stride] = value; } /** @@ -67,18 +61,18 @@ // Data is here in the buffers int s = srcPos; - int d = buf.offset + destIndex * stride; + int d = index0 + destIndex * stride; // Strategy depends on whether items are laid end-to-end or there are gaps if (stride == 1) { // Straight copy of contiguous bytes - System.arraycopy(src, srcPos, buf.storage, d, length); + System.arraycopy(src, srcPos, storage, d, length); } else { // Non-contiguous copy: single byte items int limit = d + length * stride; for (; d != limit; d += stride) { - buf.storage[d] = src[s++]; + storage[d] = src[s++]; } } } @@ -93,24 +87,11 @@ public PyBuffer getBufferSlice(int flags, int start, int length, int stride) { if (length > 0) { - int compStride; - - if (stride == 1) { - // Check the arguments define a slice within this buffer - checkSlice(start, length); - // Composite stride is same as original stride - compStride = this.stride; - } else { - // Check the arguments define a slice within this buffer - checkSlice(start, length, stride); - // Composite stride is product - compStride = this.stride * stride; - } - // Translate start relative to underlying buffer - int compIndex0 = buf.offset + start * this.stride; + int compStride= this.stride * stride; + int compIndex0 = index0 + start * this.stride; // Construct a view, taking a lock on the root object (this or this.root) - return new SlicedView(getRoot(), flags, buf.storage, compIndex0, length, compStride); + return new SlicedView(getRoot(), flags, storage, compIndex0, length, compStride); } else { // Special case for length==0 where above logic would fail. Efficient too. diff --git a/src/org/python/core/buffer/ZeroByteBuffer.java b/src/org/python/core/buffer/ZeroByteBuffer.java --- a/src/org/python/core/buffer/ZeroByteBuffer.java +++ b/src/org/python/core/buffer/ZeroByteBuffer.java @@ -1,6 +1,5 @@ package org.python.core.buffer; -import org.python.core.BufferPointer; import org.python.core.PyBuffer; import org.python.core.PyException; @@ -15,8 +14,8 @@ */ public class ZeroByteBuffer extends BaseBuffer { - /** Shared instance of a zero-length buffer. */ - private static final BufferPointer EMPTY_BUF = new BufferPointer(new byte[0]); + /** Shared instance of a zero-length storage. */ + private static final byte[] EMPTY = new byte[0]; /** Array containing a single zero for the length */ protected static final int[] SHAPE = {0}; @@ -33,7 +32,7 @@ */ public ZeroByteBuffer(int flags, boolean readonly) throws PyException { super(CONTIGUITY | SIMPLE | (readonly ? 0 : WRITABLE)); - this.buf = EMPTY_BUF; // Wraps empty array + this.storage = EMPTY; // Empty array this.shape = SHAPE; // {0} this.strides = SimpleBuffer.SIMPLE_STRIDES; // {1} checkRequestFlags(flags); @@ -60,7 +59,7 @@ protected int calcIndex(int... indices) throws IndexOutOfBoundsException { // Bootless dimension check takes precedence (for consistency with other buffers) checkDimension(indices); - // This causes all access to the bytes in to throw (since BaseBuffer calls it). + // This causes all access to the bytes to throw (since BaseBuffer calls it). throw new IndexOutOfBoundsException(); } @@ -81,7 +80,7 @@ */ @Override public void copyTo(int srcIndex, byte[] dest, int destPos, int length) - throws IndexOutOfBoundsException, PyException { + throws IndexOutOfBoundsException, PyException { // Nothing to copy } @@ -91,7 +90,7 @@ */ @Override public void copyFrom(byte[] src, int srcPos, int destIndex, int length) - throws IndexOutOfBoundsException, PyException { + throws IndexOutOfBoundsException, PyException { if (length > 0) { throw new IndexOutOfBoundsException(); } @@ -132,6 +131,17 @@ } /** + * {@inheritDoc} + *

+ * The implementation in ZeroByteBuffer efficiently returns an empty buffer. + */ + @Override + public Pointer getBuf() { + // Has to be new because the client is allowed to manipulate the contents. + return new Pointer(EMPTY, 0); + } + + /** * For a ZeroByteBuffer, it's the empty string. */ @Override @@ -175,5 +185,5 @@ // We have to release the root too if ours was final. root.release(); } - } + } } diff --git a/src/org/python/core/io/TextIOBase.java b/src/org/python/core/io/TextIOBase.java --- a/src/org/python/core/io/TextIOBase.java +++ b/src/org/python/core/io/TextIOBase.java @@ -5,7 +5,6 @@ import java.io.OutputStream; import java.nio.ByteBuffer; -import org.python.core.BufferPointer; import org.python.core.BufferProtocol; import org.python.core.Py; import org.python.core.PyArray; @@ -13,7 +12,6 @@ import org.python.core.PyBuffer; import org.python.core.PyObject; import org.python.core.PyString; -import org.python.core.util.StringUtil; /** * Base class for text I/O. @@ -98,7 +96,7 @@ /** * Read into the given PyObject that implements the Jython buffer API (with write access) or is * a PyArray. - * + * * @param buf a PyObject compatible with the buffer API * @return the amount of data read as an int */ diff --git a/tests/java/org/python/core/PyBufferTest.java b/tests/java/org/python/core/PyBufferTest.java --- a/tests/java/org/python/core/PyBufferTest.java +++ b/tests/java/org/python/core/PyBufferTest.java @@ -41,7 +41,7 @@ */ public class PyBufferTest extends TestCase { - /** Control amount of output. Instance variable so can be adjusted temporarily per test. */ + /** Control amount of output. Instance variable so can be adjusted temporarily in test. */ protected int verbosity = 0; /** @@ -514,7 +514,7 @@ // Check changed part of destination assertBytesEqual("copyTo(slice) incorrect", test.material.bytes, srcIndex, - actual, destPos, length); + length, actual, destPos); if (destPos > 0) { assertEquals("data before destination", BLANK, actual[destPos - 1]); } @@ -538,7 +538,7 @@ // Check changed part of destination assertBytesEqual("copyTo(slice) incorrect", test.material.bytes, srcIndex, - actual, destPos, length); + length, actual, destPos); if (destPos > 0) { assertEquals("data before destination", BLANK, actual[destPos - 1]); } @@ -860,25 +860,6 @@ } /** - * Check that reusable PyBuffer is re-used, and that non-reusable PyBuffer is not re-used. - * - * @param subject - */ - private void checkReusable(BufferProtocol subject, PyBuffer previous, PyBuffer latest) { - assertNotNull("Re-used PyBuffer reference null", latest); - if (subject instanceof PyByteArray) { - // Re-use prohibited because might have resized while released - assertFalse("PyByteArray buffer reused unexpectedly", latest == previous); - } else if (subject instanceof TestableExporter && !((TestableExporter)subject).reusable) { - // Special test case where re-use prohibited - assertFalse("PyBuffer reused unexpectedly", latest == previous); - } else { - // Other types of TestableExporter and PyString all re-use - assertTrue("PyBuffer not re-used as expected", latest == previous); - } - } - - /** * Test method for {@link org.python.core.PyBuffer#getBufferSlice(int, int, int, int)}. */ public void testGetBufferSliceWithStride() { @@ -968,7 +949,7 @@ int flags = test.readonly ? PyBUF.SIMPLE : PyBUF.SIMPLE + PyBUF.WRITABLE; PyBuffer view = test.subject.getBuffer(flags); - BufferPointer bp = view.getBuf(); + PyBuffer.Pointer bp = view.getBuf(); assertBytesEqual("buffer does not match reference", test.material.bytes, bp); } else { @@ -977,7 +958,7 @@ PyBuffer view = test.subject.getBuffer(flags); stride = view.getStrides()[0]; // Just possibly != test.strides when length<=1 - BufferPointer bp = view.getBuf(); + PyBuffer.Pointer bp = view.getBuf(); assertBytesEqual("buffer does not match reference", test.material.bytes, bp, stride); } @@ -1004,9 +985,8 @@ } // Get pointer and check contents for correct data - BufferPointer bp = view.getPointer(i); - int stride = view.getStrides()[0]; - assertBytesEqual("getPointer value", exp, bp, stride); + PyBuffer.Pointer bp = view.getPointer(i); + assertBytesEqual("getPointer value", exp, bp); } } } @@ -1031,11 +1011,10 @@ exp[j] = bytes[p + j]; } - // Get pointer and check contents for correct data + // Get pointer and check contents for data matching exp index[0] = i; - BufferPointer bp = view.getPointer(index); - assertBytesEqual("getPointer value", exp, bp.storage, bp.offset); -// assertEquals("getPointer size wrong", itemsize, bp.size); + PyBuffer.Pointer bp = view.getPointer(index); + assertBytesEqual("getPointer value", exp, bp); } // Check 2D index throws @@ -1225,12 +1204,6 @@ return false; } - /** - * Determine whether this object permits it's buffers to re-animate themselves. If not, a - * call to getBuffer on a released buffer should not return the same buffer. - */ - public boolean reusable = true; - } /** @@ -1271,15 +1244,12 @@ } /** - * A class to act as an exporter that uses the SimpleBuffer. This permits testing abstracted - * from the Jython interpreter. - *

- * The exporter shares a single exported buffer between all consumers and needs to take any - * action immediately when that buffer is finally released. You are most likely to use this - * approach with an exporting object type that modifies its behaviour while there are active - * exports, but where it is worth avoiding the cost of duplicate buffers. This is the case with - * PyByteArray, which prohibits operations that would resize it, while there are outstanding - * exports. + * A class to act as an exporter that uses the SimpleBuffer. The exporter shares a single + * exported buffer between all consumers and needs to take any action immediately when that + * buffer is finally released. You are most likely to use this approach with an exporting object + * type that modifies its behaviour while there are active exports, but where it is worth + * avoiding the cost of duplicate buffers. This is the case with PyByteArray, which prohibits + * operations that would resize it, while there are outstanding exports. */ static class SimpleWritableExporter extends TestableExporter { @@ -1292,7 +1262,6 @@ */ public SimpleWritableExporter(byte[] storage) { this.storage = storage; - reusable = false; } @Override @@ -1469,7 +1438,7 @@ * @param expected expected byte array * @param bp result to test */ - static void assertBytesEqual(String message, byte[] expected, BufferPointer bp) { + static void assertBytesEqual(String message, byte[] expected, PyBuffer.Pointer bp) { assertBytesEqual(message, expected, bp, 1); } @@ -1482,19 +1451,24 @@ * @param bp result to test * @param stride in the storage array */ - static void assertBytesEqual(String message, byte[] expected, BufferPointer bp, int stride) { - assertBytesEqual(message, expected, 0, bp.storage, bp.offset, expected.length, stride); + static void assertBytesEqual(String message, byte[] expected, PyBuffer.Pointer bp, int stride) { + assertBytesEqual(message, expected, 0, expected.length, bp.storage, bp.offset, stride); } /** * Customised assert method comparing a buffer pointer to a byte array, usually the one from * ByteMaterial. * + * @param message to issue on failure * @param expected expected byte array - * @param bp result to test + * @param expectedStart where to start the comparison in expected + * @param n number of bytes to test + * @param bb result to test + * @param stride in the storage array */ - static void assertBytesEqual(byte[] expected, BufferPointer bp) { - assertBytesEqual("", expected, bp); + static void assertBytesEqual(String message, byte[] expected, int expectedStart, int n, + PyBuffer.Pointer bp, int stride) { + assertBytesEqual(message, expected, expectedStart, n, bp.storage, bp.offset, stride); } /** @@ -1506,8 +1480,8 @@ * @param actual result to test */ static void assertBytesEqual(String message, byte[] expected, byte[] actual) { - assertEquals(message, expected.length, actual.length); - assertBytesEqual(message, expected, 0, actual, 0, expected.length, 1); + assertEquals(message + " (array size)", expected.length, actual.length); + assertBytesEqual(message, expected, 0, expected.length, actual, 0, 1); } /** @@ -1520,7 +1494,7 @@ * @param actualStart where to start the comparison in actual */ static void assertBytesEqual(String message, byte[] expected, byte[] actual, int actualStart) { - assertBytesEqual(message, expected, 0, actual, actualStart, expected.length, 1); + assertBytesEqual(message, expected, 0, expected.length, actual, actualStart, 1); } /** @@ -1530,14 +1504,13 @@ * @param message to issue on failure * @param expected expected byte array * @param expectedStart where to start the comparison in expected + * @param n number of bytes to test * @param actual result to test * @param actualStart where to start the comparison in actual - * @param n number of bytes to test */ - static void assertBytesEqual(String message, byte[] expected, int expectedStart, byte[] actual, - int actualStart, int n) { - - assertBytesEqual(message, expected, expectedStart, actual, actualStart, n, 1); + static void assertBytesEqual(String message, byte[] expected, int expectedStart, int n, + byte[] actual, int actualStart) { + assertBytesEqual(message, expected, expectedStart, n, actual, actualStart, 1); } /** @@ -1547,13 +1520,13 @@ * @param message to issue on failure * @param expected expected byte array * @param expectedStart where to start the comparison in expected + * @param n number of bytes to test * @param actual result to test * @param actualStart where to start the comparison in actual - * @param n number of bytes to test * @param stride spacing of bytes in actual array */ - static void assertBytesEqual(String message, byte[] expected, int expectedStart, byte[] actual, - int actualStart, int n, int stride) { + static void assertBytesEqual(String message, byte[] expected, int expectedStart, int n, + byte[] actual, int actualStart, int stride) { if (actualStart < 0) { fail(message + " (start<0 in result)"); -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Sun Dec 9 21:29:33 2012 From: jython-checkins at python.org (jeff.allen) Date: Sun, 9 Dec 2012 21:29:33 +0100 (CET) Subject: [Jython-checkins] =?utf-8?q?jython=3A_Update_Lib/=5Fjyio=2Epy_fro?= =?utf-8?q?m_CPython_2=2E7_version=2E?= Message-ID: <3YKJtP65YZzRVP@mail.python.org> http://hg.python.org/jython/rev/cc88e7077ebc changeset: 6889:cc88e7077ebc user: Jeff Allen date: Wed Dec 05 08:22:41 2012 +0000 summary: Update Lib/_jyio.py from CPython 2.7 version. Formerly 2.6-ish _pyio was used as the basis. (This causes many small changes at once.) Some changes to io.py now ideas clearer on how we shall replace _jyio progressively with _io module. files: Lib/_jyio.py | 585 +++++++++++++++++++++++--------------- Lib/io.py | 19 +- 2 files changed, 353 insertions(+), 251 deletions(-) diff --git a/Lib/_jyio.py b/Lib/_jyio.py --- a/Lib/_jyio.py +++ b/Lib/_jyio.py @@ -1,96 +1,74 @@ """ -XXX: This is actually io.py pulled from CPython 2.6 with the addition of some _ -onto the names of types. Eventually we should implement this stuff in Java. +This is based on _pyio.py from CPython 2.7 which is Python implementation of +the io module. The upgrade from a 2.6-ish version accounts for the large +number of changes made all at once. -The _jyio module provides the Python interfaces to stream handling. The -builtin open function is defined in this module. +It is here to stand in for classes that should be provided by the _io module. +In CPython 2.7, when client code imports io, that module imports a set of +classes from _io and re-exports them as its own. In Jython, io.py imports +those things from _jyio, which in turn imports from _io those so far +implemented in Java. _jyio implements the rest here using nearly the same +code as _pyio. -At the top of the I/O hierarchy is the abstract base class _IOBase. It -defines the basic interface to a stream. Note, however, that there is no -separation between reading and writing to streams; implementations are -allowed to throw an IOError if they do not support a given operation. +Some classes have gained an underscore to match their _io module names: +_IOBase, _RawIOBase, _BufferedIOBase, _TextIOBase. -Extending _IOBase is _RawIOBase which deals simply with the reading and -writing of raw bytes to a stream. FileIO subclasses _RawIOBase to provide -an interface to OS files. +As Jython implements more and more of _io in Java, the Python implementations here +will progressively be replaced with imports from _io. Eventually we should implement +all this in Java, remove this module and revert io.py to its CPython original. +""" -_BufferedIOBase deals with buffering on a raw byte stream (_RawIOBase). Its -subclasses, BufferedWriter, BufferedReader, and BufferedRWPair buffer -streams that are readable, writable, and both respectively. -BufferedRandom provides a buffered interface to random access -streams. BytesIO is a simple stream of in-memory bytes. +from __future__ import (print_function, unicode_literals) -Another _IOBase subclass, _TextIOBase, deals with the encoding and decoding -of streams into text. TextIOWrapper, which extends it, is a buffered text -interface to a buffered raw stream (`_BufferedIOBase`). Finally, StringIO -is a in-memory stream for text. - -Argument names are not part of the specification, and only the arguments -of open() are intended to be used as keyword arguments. - -data: - -DEFAULT_BUFFER_SIZE - - An int containing the default buffer size used by the module's buffered - I/O classes. open() uses the file's blksize (as obtained by os.stat) if - possible. -""" -# New I/O library conforming to PEP 3116. - -# This is a prototype; hopefully eventually some of this will be -# reimplemented in C. - -# XXX edge cases when switching between reading/writing -# XXX need to support 1 meaning line-buffered -# XXX whenever an argument is None, use the default value -# XXX read/write ops should check readable/writable -# XXX buffered readinto should work with arbitrary buffer objects -# XXX use incremental encoder for text output, at least for UTF-16 and UTF-8-SIG -# XXX check writable, readable and seekable in appropriate places -from __future__ import print_function -from __future__ import unicode_literals - -__author__ = ("Guido van Rossum , " - "Mike Verdone , " - "Mark Russell ") - -__all__ = ["BlockingIOError", "open", "_IOBase", "_RawIOBase", "FileIO", - "BytesIO", "StringIO", "_BufferedIOBase", - "BufferedReader", "BufferedWriter", "BufferedRWPair", - "BufferedRandom", "_TextIOBase", "TextIOWrapper"] +import _io # for some diagnostic code import os import abc import codecs -import _io -import threading +import warnings +import errno +# Import thread instead of threading to reduce startup cost +try: + from thread import allocate_lock as Lock +except ImportError: + from dummy_thread import allocate_lock as Lock + +#import io +#from io import (__all__, SEEK_SET, SEEK_CUR, SEEK_END) +from errno import EINTR + +__metaclass__ = type # open() uses st_blksize whenever we can DEFAULT_BUFFER_SIZE = 8 * 1024 # bytes -# py3k has only new style classes -__metaclass__ = type +# NOTE: Base classes defined here are registered with the "official" ABCs +# defined in io.py. We don't use real inheritance though, because we don't +# want to inherit the C implementations. + class BlockingIOError(IOError): """Exception raised when I/O would block on a non-blocking I/O stream.""" def __init__(self, errno, strerror, characters_written=0): - IOError.__init__(self, errno, strerror) + super(IOError, self).__init__(errno, strerror) + if not isinstance(characters_written, (int, long)): + raise TypeError("characters_written must be a integer") self.characters_written = characters_written -def open(file, mode="r", buffering=None, encoding=None, errors=None, +def open(file, mode="r", buffering=-1, + encoding=None, errors=None, newline=None, closefd=True): - r"""Open file and return a stream. If the file cannot be opened, an IOError is - raised. - file is either a string giving the name (and the path if the file - isn't in the current working directory) of the file to be opened or an - integer file descriptor of the file to be wrapped. (If a file - descriptor is given, it is closed when the returned I/O object is - closed, unless closefd is set to False.) + r"""Open file and return a stream. Raise IOError upon failure. + + file is either a text or byte string giving the name (and the path + if the file isn't in the current working directory) of the file to + be opened or an integer file descriptor of the file to be + wrapped. (If a file descriptor is given, it is closed when the + returned I/O object is closed, unless closefd is set to False.) mode is an optional string that specifies the mode in which the file is opened. It defaults to 'r' which means open for reading in text @@ -135,7 +113,7 @@ * Binary files are buffered in fixed-size chunks; the size of the buffer is chosen using a heuristic trying to determine the underlying device's - "block size" and falling back on `_jyio.DEFAULT_BUFFER_SIZE`. + "block size" and falling back on `io.DEFAULT_BUFFER_SIZE`. On many systems, the buffer will typically be 4096 or 8192 bytes long. * "Interactive" text files (files for which isatty() returns True) @@ -191,11 +169,11 @@ opened in a text mode, and for bytes a BytesIO can be used like a file opened in a binary mode. """ - if not isinstance(file, (basestring, int)): + if not isinstance(file, (basestring, int, long)): raise TypeError("invalid file: %r" % file) if not isinstance(mode, basestring): raise TypeError("invalid mode: %r" % mode) - if buffering is not None and not isinstance(buffering, int): + if not isinstance(buffering, (int, long)): raise TypeError("invalid buffering: %r" % buffering) if encoding is not None and not isinstance(encoding, basestring): raise TypeError("invalid encoding: %r" % encoding) @@ -232,8 +210,6 @@ (appending and "a" or "") + (updating and "+" or ""), closefd) - if buffering is None: - buffering = -1 line_buffering = False if buffering == 1 or buffering < 0 and raw.isatty(): buffering = -1 @@ -267,24 +243,26 @@ text.mode = mode return text -class _DocDescriptor: + +class DocDescriptor: """Helper for builtins.open.__doc__ """ def __get__(self, obj, typ): return ( - "open(file, mode='r', buffering=None, encoding=None, " + "open(file, mode='r', buffering=-1, encoding=None, " "errors=None, newline=None, closefd=True)\n\n" + open.__doc__) + class OpenWrapper: """Wrapper for builtins.open Trick so that open won't become a bound method when stored - as a class variable (as dumbdbm does). + as a class variable (as dbm.dumb does). See initstdio() in Python/pythonrun.c. """ - __doc__ = _DocDescriptor() + __doc__ = DocDescriptor() def __new__(cls, *args, **kwargs): return open(*args, **kwargs) @@ -294,7 +272,8 @@ pass -class _IOBase(object): +class _IOBase: + __metaclass__ = abc.ABCMeta """The abstract base class for all I/O classes, acting on streams of bytes. There is no public constructor. @@ -320,14 +299,12 @@ stream. _IOBase also supports the :keyword:`with` statement. In this example, - fp is closed after the suite of the with statment is complete: + fp is closed after the suite of the with statement is complete: with open('spam.txt', 'r') as fp: fp.write('Spam and eggs!') """ - __metaclass__ = abc.ABCMeta - ### Internal ### def _unsupported(self, name): @@ -337,7 +314,7 @@ ### Positioning ### - def seek(self, pos, whence = 0): + def seek(self, pos, whence=0): """Change stream position. Change the stream position to byte offset offset. offset is @@ -356,7 +333,7 @@ """Return current stream position.""" return self.seek(0, 1) - def truncate(self, pos = None): + def truncate(self, pos=None): """Truncate file to size bytes. Size defaults to the current IO position as reported by tell(). Return @@ -371,9 +348,7 @@ This is not implemented for read-only and non-blocking streams. """ - if self.__closed: - raise ValueError("flush of closed file") - #self._checkClosed() + self._checkClosed() # XXX Should this return the number of bytes written??? __closed = False @@ -492,7 +467,7 @@ ### Readline[s] and writelines ### - def readline(self, limit = -1): + def readline(self, limit=-1): r"""Read and return a line from the stream. If limit is specified, at most limit bytes will be read. @@ -501,7 +476,7 @@ files, the newlines argument to open can be used to select the line terminator(s) recognized. """ - self._checkClosed() + # For backwards compatibility, a (slowish) readline(). if hasattr(self, "peek"): def nreadahead(): readahead = self.peek(1) @@ -516,7 +491,7 @@ return 1 if limit is None: limit = -1 - if not isinstance(limit, (int, long)): + elif not isinstance(limit, (int, long)): raise TypeError("limit must be an integer") res = bytearray() while limit < 0 or len(res) < limit: @@ -545,11 +520,9 @@ lines will be read if the total size (in bytes/characters) of all lines so far exceeds hint. """ - if hint is None: - hint = -1 - if not isinstance(hint, (int, long)): - raise TypeError("hint must be an integer") - if hint <= 0: + if hint is not None and not isinstance(hint, (int, long)): + raise TypeError("integer or None expected") + if hint is None or hint <= 0: return list(self) n = 0 lines = [] @@ -580,10 +553,10 @@ # primitive operation, but that would lead to nasty recursion in case # a subclass doesn't implement either.) - def read(self, n = -1): + def read(self, n=-1): """Read and return up to n bytes. - Returns an empty bytes array on EOF, or None if the object is + Returns an empty bytes object on EOF, or None if the object is set not to block and has no data to read. """ if n is None: @@ -592,6 +565,8 @@ return self.readall() b = bytearray(n.__index__()) n = self.readinto(b) + if n is None: + return None del b[n:] return bytes(b) @@ -603,13 +578,17 @@ if not data: break res += data - return bytes(res) + if res: + return bytes(res) + else: + # b'' or None + return data def readinto(self, b): """Read up to len(b) bytes into b. Returns number of bytes read (0 for EOF), or None if the object - is set not to block as has no data to read. + is set not to block and has no data to read. """ self._unsupported("readinto") @@ -621,27 +600,7 @@ self._unsupported("write") -class FileIO(_io.FileIO, _RawIOBase): - - """Raw I/O implementation for OS files.""" - - #XXX: have to read between these mangled CPython lines! - # This multiply inherits from FileIO and _RawIOBase to make - # isinstance(_io.FileIO(), _io._RawIOBase) return True without requiring - # that _io.FileIO inherits from _io._RawIOBase (which would be hard - # to do since fileio.c is written in C). - - def __init__(self, name, mode="r", closefd=True): - _io.FileIO.__init__(self, name, mode, closefd) - self._name = name - - def close(self): - _io.FileIO.close(self) - _RawIOBase.close(self) - - @property - def name(self): - return self._name +from _io import FileIO class _BufferedIOBase(_IOBase): @@ -661,7 +620,7 @@ implementation, but wrap one. """ - def read(self, n = None): + def read(self, n=None): """Read and return up to n bytes. If the argument is omitted, None, or negative, reads and @@ -681,6 +640,10 @@ """ self._unsupported("read") + def read1(self, n=None): + """Read up to n bytes with at most one read() system call.""" + self._unsupported("read1") + def readinto(self, b): """Read up to len(b) bytes into b. @@ -715,6 +678,15 @@ """ self._unsupported("write") + def detach(self): + """ + Separate the underlying raw stream from the buffer and return it. + + After the raw stream has been detached, the buffer is in an unusable + state. + """ + self._unsupported("detach") + class _BufferedIOMixin(_BufferedIOBase): @@ -726,15 +698,21 @@ """ def __init__(self, raw): - self.raw = raw + self._raw = raw ### Positioning ### def seek(self, pos, whence=0): - return self.raw.seek(pos, whence) + new_position = self.raw.seek(pos, whence) + if new_position < 0: + raise IOError("seek() returned an invalid position") + return new_position def tell(self): - return self.raw.tell() + pos = self.raw.tell() + if pos < 0: + raise IOError("tell() returned an invalid position") + return pos def truncate(self, pos=None): # Flush the stream. We're mixing buffered I/O with lower-level I/O, @@ -751,12 +729,25 @@ ### Flush and close ### def flush(self): + if self.closed: + raise ValueError("flush of closed file") self.raw.flush() def close(self): - if not self.closed: - self.flush() - self.raw.close() + if self.raw is not None and not self.closed: + try: + # may raise BlockingIOError or BrokenPipeError etc + self.flush() + finally: + self.raw.close() + + def detach(self): + if self.raw is None: + raise ValueError("raw stream already detached") + self.flush() + raw = self._raw + self._raw = None + return raw ### Inquiries ### @@ -770,6 +761,10 @@ return self.raw.writable() @property + def raw(self): + return self._raw + + @property def closed(self): return self.raw.closed @@ -781,6 +776,15 @@ def mode(self): return self.raw.mode + def __repr__(self): + clsname = self.__class__.__name__ + try: + name = self.name + except AttributeError: + return "<_pyio.{0}>".format(clsname) + else: + return "<_pyio.{0} name={1!r}>".format(clsname, name) + ### Lower-level APIs ### def fileno(self): @@ -790,19 +794,22 @@ return self.raw.isatty() -class _BytesIO(_BufferedIOBase): +class BytesIO(_BufferedIOBase): """Buffered I/O implementation using an in-memory bytes buffer.""" - # XXX More docs - def __init__(self, initial_bytes=None): buf = bytearray() if initial_bytes is not None: - buf += bytearray(initial_bytes) + buf.extend(initial_bytes) self._buffer = buf self._pos = 0 + def __getstate__(self): + if self.closed: + raise ValueError("__getstate__ on closed file") + return self.__dict__.copy() + def getvalue(self): """Return the bytes value (contents) of the buffer """ @@ -816,7 +823,8 @@ if n is None: n = -1 if not isinstance(n, (int, long)): - raise TypeError("argument must be an integer") + raise TypeError("integer argument expected, got {0!r}".format( + type(n))) if n < 0: n = len(self._buffer) if len(self._buffer) <= self._pos: @@ -827,7 +835,7 @@ return bytes(b) def read1(self, n): - """this is the same as read. + """This is the same as read. """ return self.read(n) @@ -855,7 +863,7 @@ try: pos.__index__ except AttributeError: - raise TypeError("an integer is required") # from err + raise TypeError("an integer is required") if whence == 0: if pos < 0: raise ValueError("negative seek position %r" % (pos,)) @@ -897,16 +905,6 @@ def seekable(self): return True -# Use the faster implementation of BytesIO if available -try: - import _bytesio - - class BytesIO(_bytesio._BytesIO, _BufferedIOBase): - __doc__ = _bytesio._BytesIO.__doc__ - -except ImportError: - BytesIO = _BytesIO - class BufferedReader(_BufferedIOMixin): @@ -922,11 +920,15 @@ def __init__(self, raw, buffer_size=DEFAULT_BUFFER_SIZE): """Create a new buffered reader using the given readable raw IO object. """ - raw._checkReadable() + if not raw.readable(): + raise IOError('"raw" argument must be readable.') + _BufferedIOMixin.__init__(self, raw) + if buffer_size <= 0: + raise ValueError("invalid buffer size") self.buffer_size = buffer_size self._reset_read_buf() - self._read_lock = threading.Lock() + self._read_lock = Lock() def _reset_read_buf(self): self._read_buf = b"" @@ -940,6 +942,8 @@ mode. If n is negative, read until EOF or until read() would block. """ + if n is not None and n < -1: + raise ValueError("invalid number of bytes to read") with self._read_lock: return self._read_unlocked(n) @@ -956,7 +960,12 @@ current_size = 0 while True: # Read until EOF or until read() would block. - chunk = self.raw.read() + try: + chunk = self.raw.read() + except IOError as e: + if e.errno != EINTR: + raise + continue if chunk in empty_values: nodata_val = chunk break @@ -975,7 +984,12 @@ chunks = [buf[pos:]] wanted = max(self.buffer_size, n) while avail < n: - chunk = self.raw.read(wanted) + try: + chunk = self.raw.read(wanted) + except IOError as e: + if e.errno != EINTR: + raise + continue if chunk in empty_values: nodata_val = chunk break @@ -1002,9 +1016,16 @@ def _peek_unlocked(self, n=0): want = min(n, self.buffer_size) have = len(self._read_buf) - self._read_pos - if have < want: + if have < want or have <= 0: to_read = self.buffer_size - have - current = self.raw.read(to_read) + while True: + try: + current = self.raw.read(to_read) + except IOError as e: + if e.errno != EINTR: + raise + continue + break if current: self._read_buf = self._read_buf[self._read_pos:] + current self._read_pos = 0 @@ -1014,7 +1035,9 @@ """Reads up to n bytes, with at most one read() system call.""" # Returns up to n bytes. If at least one byte is buffered, we # only return buffered bytes. Otherwise, we do one raw read. - if n <= 0: + if n < 0: + raise ValueError("number of bytes to read must be positive") + if n == 0: return b"" with self._read_lock: self._peek_unlocked(1) @@ -1022,13 +1045,15 @@ min(n, len(self._read_buf) - self._read_pos)) def tell(self): - return self.raw.tell() - len(self._read_buf) + self._read_pos + return _BufferedIOMixin.tell(self) - len(self._read_buf) + self._read_pos def seek(self, pos, whence=0): + if not (0 <= whence <= 2): + raise ValueError("invalid whence value") with self._read_lock: if whence == 1: pos -= len(self._read_buf) - self._read_pos - pos = self.raw.seek(pos, whence) + pos = _BufferedIOMixin.seek(self, pos, whence) self._reset_read_buf() return pos @@ -1039,20 +1064,25 @@ The constructor creates a BufferedWriter for the given writeable raw stream. If the buffer_size is not given, it defaults to - DEAFULT_BUFFER_SIZE. If max_buffer_size is omitted, it defaults to - twice the buffer size. + DEFAULT_BUFFER_SIZE. """ + _warning_stack_offset = 2 + def __init__(self, raw, buffer_size=DEFAULT_BUFFER_SIZE, max_buffer_size=None): - raw._checkWritable() + if not raw.writable(): + raise IOError('"raw" argument must be writable.') + _BufferedIOMixin.__init__(self, raw) + if buffer_size <= 0: + raise ValueError("invalid buffer size") + if max_buffer_size is not None: + warnings.warn("max_buffer_size is deprecated", DeprecationWarning, + self._warning_stack_offset) self.buffer_size = buffer_size - self.max_buffer_size = (2*buffer_size - if max_buffer_size is None - else max_buffer_size) self._write_buf = bytearray() - self._write_lock = threading.Lock() + self._write_lock = Lock() def write(self, b): if self.closed: @@ -1063,13 +1093,9 @@ # XXX we can implement some more tricks to try and avoid # partial writes if len(self._write_buf) > self.buffer_size: - # We're full, so let's pre-flush the buffer - try: - self._flush_unlocked() - except BlockingIOError as e: - # We can't accept anything else. - # XXX Why not just let the exception pass through? - raise BlockingIOError(e.errno, e.strerror, 0) + # We're full, so let's pre-flush the buffer. (This may + # raise BlockingIOError with characters_written == 0.) + self._flush_unlocked() before = len(self._write_buf) self._write_buf.extend(b) written = len(self._write_buf) - before @@ -1077,12 +1103,13 @@ try: self._flush_unlocked() except BlockingIOError as e: - if len(self._write_buf) > self.max_buffer_size: - # We've hit max_buffer_size. We have to accept a - # partial write and cut back our buffer. - overage = len(self._write_buf) - self.max_buffer_size - self._write_buf = self._write_buf[:self.max_buffer_size] - raise BlockingIOError(e.errno, e.strerror, overage) + if len(self._write_buf) > self.buffer_size: + # We've hit the buffer_size. We have to accept a partial + # write and cut back our buffer. + overage = len(self._write_buf) - self.buffer_size + written -= overage + self._write_buf = self._write_buf[:self.buffer_size] + raise BlockingIOError(e.errno, e.strerror, written) return written def truncate(self, pos=None): @@ -1093,33 +1120,39 @@ return self.raw.truncate(pos) def flush(self): - if self.closed: - raise ValueError("flush of closed file") with self._write_lock: self._flush_unlocked() def _flush_unlocked(self): if self.closed: raise ValueError("flush of closed file") - written = 0 - try: - while self._write_buf: + while self._write_buf: + try: n = self.raw.write(self._write_buf) - del self._write_buf[:n] - written += n - except BlockingIOError as e: - n = e.characters_written + except BlockingIOError: + raise RuntimeError("self.raw should implement _RawIOBase: it " + "should not raise BlockingIOError") + except IOError as e: + if e.errno != EINTR: + raise + continue + if n is None: + raise BlockingIOError( + errno.EAGAIN, + "write could not complete without blocking", 0) + if n > len(self._write_buf) or n < 0: + raise IOError("write() returned incorrect number of bytes") del self._write_buf[:n] - written += n - raise BlockingIOError(e.errno, e.strerror, written) def tell(self): - return self.raw.tell() + len(self._write_buf) + return _BufferedIOMixin.tell(self) + len(self._write_buf) def seek(self, pos, whence=0): + if not (0 <= whence <= 2): + raise ValueError("invalid whence") with self._write_lock: self._flush_unlocked() - return self.raw.seek(pos, whence) + return _BufferedIOMixin.seek(self, pos, whence) class BufferedRWPair(_BufferedIOBase): @@ -1132,8 +1165,7 @@ reader and writer are _RawIOBase objects that are readable and writeable respectively. If the buffer_size is omitted it defaults to - DEFAULT_BUFFER_SIZE. The max_buffer_size (for the buffered writer) - defaults to twice the buffer size. + DEFAULT_BUFFER_SIZE. """ # XXX The usefulness of this (compared to having two separate IO @@ -1145,10 +1177,17 @@ The arguments are two RawIO instances. """ - reader._checkReadable() - writer._checkWritable() + if max_buffer_size is not None: + warnings.warn("max_buffer_size is deprecated", DeprecationWarning, 2) + + if not reader.readable(): + raise IOError('"reader" argument must be readable.') + + if not writer.writable(): + raise IOError('"writer" argument must be writable.') + self.reader = BufferedReader(reader, buffer_size) - self.writer = BufferedWriter(writer, buffer_size, max_buffer_size) + self.writer = BufferedWriter(writer, buffer_size) def read(self, n=None): if n is None: @@ -1194,10 +1233,11 @@ The constructor creates a reader and writer for a seekable stream, raw, given in the first argument. If the buffer_size is omitted it - defaults to DEFAULT_BUFFER_SIZE. The max_buffer_size (for the buffered - writer) defaults to twice the buffer size. + defaults to DEFAULT_BUFFER_SIZE. """ + _warning_stack_offset = 3 + def __init__(self, raw, buffer_size=DEFAULT_BUFFER_SIZE, max_buffer_size=None): raw._checkSeekable() @@ -1205,21 +1245,25 @@ BufferedWriter.__init__(self, raw, buffer_size, max_buffer_size) def seek(self, pos, whence=0): + if not (0 <= whence <= 2): + raise ValueError("invalid whence") self.flush() - # First do the raw seek, then empty the read buffer, so that - # if the raw seek fails, we don't lose buffered data forever. - if self._read_buf and whence == 1: + if self._read_buf: # Undo read ahead. with self._read_lock: self.raw.seek(self._read_pos - len(self._read_buf), 1) + # First do the raw seek, then empty the read buffer, so that + # if the raw seek fails, we don't lose buffered data forever. pos = self.raw.seek(pos, whence) with self._read_lock: self._reset_read_buf() + if pos < 0: + raise IOError("seek() returned invalid position") return pos def tell(self): if self._write_buf: - return self.raw.tell() + len(self._write_buf) + return BufferedWriter.tell(self) else: return BufferedReader.tell(self) @@ -1265,7 +1309,7 @@ are immutable. There is no public constructor. """ - def read(self, n = -1): + def read(self, n=-1): """Read at most n characters from stream. Read from underlying buffer until we have n characters or we hit EOF. @@ -1277,7 +1321,7 @@ """Write string s to stream.""" self._unsupported("write") - def truncate(self, pos = None): + def truncate(self, pos=None): """Truncate size to pos.""" self._unsupported("truncate") @@ -1288,6 +1332,15 @@ """ self._unsupported("readline") + def detach(self): + """ + Separate the underlying buffer from the _TextIOBase and return it. + + After the underlying buffer has been detached, the TextIO is in an + unusable state. + """ + self._unsupported("detach") + @property def encoding(self): """Subclasses should override.""" @@ -1303,13 +1356,20 @@ """ return None + @property + def errors(self): + """Error setting of the decoder or encoder. + + Subclasses should override.""" + return None + class IncrementalNewlineDecoder(codecs.IncrementalDecoder): - """Codec used when reading a file in universal newlines mode. - It wraps another incremental decoder, translating \\r\\n and \\r into \\n. - It also records the types of newlines encountered. - When used with translate=False, it ensures that the newline sequence is - returned in one piece. + r"""Codec used when reading a file in universal newlines mode. It wraps + another incremental decoder, translating \r\n and \r into \n. It also + records the types of newlines encountered. When used with + translate=False, it ensures that the newline sequence is returned in + one piece. """ def __init__(self, decoder, translate, errors='strict'): codecs.IncrementalDecoder.__init__(self, errors=errors) @@ -1320,7 +1380,10 @@ def decode(self, input, final=False): # decode input (with the eventual \r from a previous pass) - output = self.decoder.decode(input, final=final) + if self.decoder is None: + output = input + else: + output = self.decoder.decode(input, final=final) if self.pendingcr and (output or final): output = "\r" + output self.pendingcr = False @@ -1347,7 +1410,11 @@ return output def getstate(self): - buf, flag = self.decoder.getstate() + if self.decoder is None: + buf = b"" + flag = 0 + else: + buf, flag = self.decoder.getstate() flag <<= 1 if self.pendingcr: flag |= 1 @@ -1356,12 +1423,14 @@ def setstate(self, state): buf, flag = state self.pendingcr = bool(flag & 1) - self.decoder.setstate((buf, flag >> 1)) + if self.decoder is not None: + self.decoder.setstate((buf, flag >> 1)) def reset(self): self.seennl = 0 self.pendingcr = False - self.decoder.reset() + if self.decoder is not None: + self.decoder.reset() _LF = 1 _CR = 2 @@ -1404,25 +1473,22 @@ write contains a newline character. """ - _CHUNK_SIZE = 128 + _CHUNK_SIZE = 2048 def __init__(self, buffer, encoding=None, errors=None, newline=None, line_buffering=False): + if newline is not None and not isinstance(newline, basestring): + raise TypeError("illegal newline type: %r" % (type(newline),)) if newline not in (None, "", "\n", "\r", "\r\n"): raise ValueError("illegal newline value: %r" % (newline,)) if encoding is None: try: - encoding = os.device_encoding(buffer.fileno()) - except (AttributeError, UnsupportedOperation): - pass - if encoding is None: - try: - import locale - except ImportError: - # Importing locale may fail if Python is being built - encoding = "ascii" - else: - encoding = locale.getpreferredencoding() + import locale + except ImportError: + # Importing locale may fail if Python is being built + encoding = "ascii" + else: + encoding = locale.getpreferredencoding() if not isinstance(encoding, basestring): raise ValueError("invalid encoding: %r" % encoding) @@ -1433,7 +1499,7 @@ if not isinstance(errors, basestring): raise ValueError("invalid errors: %r" % errors) - self.buffer = buffer + self._buffer = buffer self._line_buffering = line_buffering self._encoding = encoding self._errors = errors @@ -1467,6 +1533,15 @@ # - "bytes_..." for integer variables that count input bytes # - "chars_..." for integer variables that count decoded characters + def __repr__(self): + try: + name = self.name + except AttributeError: + return "<_pyio.TextIOWrapper encoding='{0}'>".format(self.encoding) + else: + return "<_pyio.TextIOWrapper name={0!r} encoding='{1}'>".format( + name, self.encoding) + @property def encoding(self): return self._encoding @@ -1479,6 +1554,10 @@ def line_buffering(self): return self._line_buffering + @property + def buffer(self): + return self._buffer + def seekable(self): return self._seekable @@ -1493,7 +1572,7 @@ self._telling = self._seekable def close(self): - if not self.closed: + if self.buffer is not None and not self.closed: self.flush() self.buffer.close() @@ -1572,12 +1651,13 @@ def _read_chunk(self): """ Read and decode the next chunk of data from the BufferedReader. + """ - The return value is True unless EOF was reached. The decoded string - is placed in self._decoded_chars (replacing its previous value). - The entire input chunk is sent to the decoder, though some of it - may remain buffered in the decoder, yet to be converted. - """ + # The return value is True unless EOF was reached. The decoded + # string is placed in self._decoded_chars (replacing its previous + # value). The entire input chunk is sent to the decoder, though + # some of it may remain buffered in the decoder, yet to be + # converted. if self._decoder is None: raise ValueError("no decoder") @@ -1687,6 +1767,14 @@ pos = self.tell() return self.buffer.truncate(pos) + def detach(self): + if self.buffer is None: + raise ValueError("buffer is already detached") + self.flush() + buffer = self._buffer + self._buffer = None + return buffer + def seek(self, cookie, whence=0): if self.closed: raise ValueError("tell on closed file") @@ -1727,7 +1815,9 @@ self._snapshot = None # Restore the decoder to its state from the safe start point. - if self._decoder or dec_flags or chars_to_skip: + if cookie == 0 and self._decoder: + self._decoder.reset() + elif self._decoder or dec_flags or chars_to_skip: self._decoder = self._decoder or self._get_decoder() self._decoder.setstate((b'', dec_flags)) self._snapshot = (dec_flags, b'') @@ -1758,6 +1848,7 @@ return cookie def read(self, n=None): + self._checkReadable() if n is None: n = -1 decoder = self._decoder or self._get_decoder() @@ -1795,14 +1886,16 @@ raise ValueError("read from closed file") if limit is None: limit = -1 - if not isinstance(limit, (int, long)): + elif not isinstance(limit, (int, long)): raise TypeError("limit must be an integer") # Grab all the decoded text (we will rewind any extra bits later). line = self._get_decoded_chars() start = 0 - decoder = self._decoder or self._get_decoder() + # Make the decoder if it doesn't already exist. + if not self._decoder: + self._get_decoder() pos = endpos = None while True: @@ -1857,8 +1950,7 @@ endpos = limit # reached length limit break - # No line ending seen yet - get more data - more_line = '' + # No line ending seen yet - get more data' while self._read_chunk(): if self._decoded_chars: break @@ -1881,18 +1973,18 @@ def newlines(self): return self._decoder.newlines if self._decoder else None + class StringIO(TextIOWrapper): + """Text I/O implementation using an in-memory buffer. - """An in-memory stream for text. The initial_value argument sets the - value of object. The other arguments are like those of TextIOWrapper's - constructor. + The initial_value argument sets the value of object. The newline + argument is like the one of TextIOWrapper's constructor. """ - def __init__(self, initial_value="", encoding="utf-8", - errors="strict", newline="\n"): + def __init__(self, initial_value="", newline="\n"): super(StringIO, self).__init__(BytesIO(), - encoding=encoding, - errors=errors, + encoding="utf-8", + errors="strict", newline=newline) # Issue #5645: make universal newlines semantics the same as in the # C version, even under Windows. @@ -1907,3 +1999,20 @@ def getvalue(self): self.flush() return self.buffer.getvalue().decode(self._encoding, self._errors) + + def __repr__(self): + # TextIOWrapper tells the encoding in its repr. In StringIO, + # that's a implementation detail. + return object.__repr__(self) + + @property + def errors(self): + return None + + @property + def encoding(self): + return None + + def detach(self): + # This doesn't make sense on StringIO. + self._unsupported("detach") diff --git a/Lib/io.py b/Lib/io.py --- a/Lib/io.py +++ b/Lib/io.py @@ -1,8 +1,8 @@ # XXX Temporary addition to Jython while we use _jyio.py in place of _io. # This module will stand in place of the lib-python io.py. The idea is -# gradually to switch, in this module, between _jyio and _io as classes -# are implemented in _io in Java. In the end, we delete this and _jyio.py, -# and go back to using lib-python's io.py +# gradually to switch, in _jyio, between Python implementation there and +# Java implementations imported from _io as classes in Java. In the end, +# we delete this and _jyio.py, and go back to using lib-python's io.py """The io module provides the Python interfaces to stream handling. The builtin open function is defined in this module. @@ -63,24 +63,17 @@ "UnsupportedOperation", "SEEK_SET", "SEEK_CUR", "SEEK_END"] -import _jyio import abc -# Gradually shorten this list +# For the time being, import everything via _jyio instead of from _io directly +import _jyio from _jyio import (DEFAULT_BUFFER_SIZE, BlockingIOError, UnsupportedOperation, open, - #FileIO, + FileIO, BytesIO, StringIO, BufferedReader, BufferedWriter, BufferedRWPair, BufferedRandom, IncrementalNewlineDecoder, TextIOWrapper) -# Gradually lengthen this list -from _io import (FileIO,) -#from _io import (DEFAULT_BUFFER_SIZE, BlockingIOError, UnsupportedOperation, -# open, FileIO, BytesIO, StringIO, BufferedReader, -# BufferedWriter, BufferedRWPair, BufferedRandom, -# IncrementalNewlineDecoder, TextIOWrapper) - OpenWrapper = _jyio.open # for compatibility with _pyio # for seek() -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Sun Dec 9 21:29:35 2012 From: jython-checkins at python.org (jeff.allen) Date: Sun, 9 Dec 2012 21:29:35 +0100 (CET) Subject: [Jython-checkins] =?utf-8?q?jython=3A_Add_Java_=5Fio=2EUnsupporte?= =?utf-8?q?dOperation?= Message-ID: <3YKJtR2Zg1zRW7@mail.python.org> http://hg.python.org/jython/rev/092e145a18f2 changeset: 6890:092e145a18f2 user: Jeff Allen date: Wed Dec 05 19:39:12 2012 +0000 summary: Add Java _io.UnsupportedOperation files: Lib/_jyio.py | 5 +- src/org/python/modules/_io/_io.java | 60 ++++++- tests/java/org/python/modules/_io/_ioTest.java | 84 ++++++++++ 3 files changed, 143 insertions(+), 6 deletions(-) diff --git a/Lib/_jyio.py b/Lib/_jyio.py --- a/Lib/_jyio.py +++ b/Lib/_jyio.py @@ -20,7 +20,7 @@ from __future__ import (print_function, unicode_literals) -import _io # for some diagnostic code +import _io # Java implementations to replace this module import os import abc @@ -268,8 +268,7 @@ return open(*args, **kwargs) -class UnsupportedOperation(ValueError, IOError): - pass +from _io import UnsupportedOperation class _IOBase: diff --git a/src/org/python/modules/_io/_io.java b/src/org/python/modules/_io/_io.java --- a/src/org/python/modules/_io/_io.java +++ b/src/org/python/modules/_io/_io.java @@ -1,24 +1,78 @@ -/* Copyright (c) Jython Developers */ +/* Copyright (c)2012 Jython Developers */ package org.python.modules._io; import org.python.core.ClassDictInit; +import org.python.core.Py; +import org.python.core.PyException; import org.python.core.PyObject; import org.python.core.PyString; +import org.python.core.PyStringMap; +import org.python.core.PyType; +import org.python.core.imp; /** - * The Python _fileio module. + * The Python _io module. */ public class _io implements ClassDictInit { - public static final PyString __doc__ = new PyString("Fast implementation of io.FileIO."); + public static final PyString __doc__ = new PyString("Java implementation of _io."); + /** + * This method is called when the module is loaded, to populate the namespace (dictionary) of + * the module. The dictionary has been initialised at this point reflectively from the methods + * of this class and this method nulls those entries that ought not to be exposed. + * + * @param dict namespace of the module + */ public static void classDictInit(PyObject dict) { dict.__setitem__("__name__", new PyString("_io")); dict.__setitem__("__doc__", __doc__); dict.__setitem__("FileIO", PyFileIO.TYPE); + // Define UnsupportedOperation exception by constructing the type + + PyObject exceptions = imp.load("exceptions"); + PyObject ValueError = exceptions.__getattr__("ValueError"); + PyObject IOError = exceptions.__getattr__("IOError"); + // Equivalent to class UnsupportedOperation(ValueError, IOError) : pass + // UnsupportedOperation = makeException(dict, "UnsupportedOperation", ValueError, IOError); + // XXX Work-around: slots not properly initialised unless IOError comes first + UnsupportedOperation = makeException(dict, "UnsupportedOperation", IOError, ValueError); + // Hide from Python dict.__setitem__("classDictInit", null); + dict.__setitem__("makeException", null); + } + + /** A Python class for the UnsupportedOperation exception. */ + public static PyType UnsupportedOperation; + + /** + * A function that returns a {@link PyException}, which is a Java exception suitable for + * throwing, and that will be raised as an UnsupportedOperation Python exception. + * + * @param message text message parameter to the Python exception + * @return nascent UnsupportedOperation Python exception + */ + public static PyException UnsupportedOperation(String message) { + return new PyException(UnsupportedOperation, message); + } + + /** + * Convenience method for constructing a type object of a Python exception, named as given, and + * added to the namespace of the "_io" module. + * + * @param dict module dictionary + * @param excname name of the exception + * @param bases one or more bases (superclasses) + * @return the constructed exception type + */ + private static PyType makeException(PyObject dict, String excname, PyObject... bases) { + PyStringMap classDict = new PyStringMap(); + classDict.__setitem__("__module__", Py.newString("_io")); + PyType type = (PyType)Py.makeClass(excname, bases, classDict); + dict.__setitem__(excname, type); + return type; } } diff --git a/tests/java/org/python/modules/_io/_ioTest.java b/tests/java/org/python/modules/_io/_ioTest.java new file mode 100644 --- /dev/null +++ b/tests/java/org/python/modules/_io/_ioTest.java @@ -0,0 +1,84 @@ +/* Copyright (c)2012 Jython Developers */ +package org.python.modules._io; + +import static org.junit.Assert.*; +import static org.junit.matchers.JUnitMatchers.*; + +import org.junit.Before; +import org.junit.Test; +import org.python.core.PyException; +import org.python.core.PyObject; +import org.python.util.PythonInterpreter; + +/** + * Tests of specific methods in the Python _io module (org.python.modules._io._io). There is an + * extensive regression test in Lib/test/test_io.py, but that is quite complex. This test case + * exists to exercise selected functionality in isolation. + */ +public class _ioTest { + + /** We need the interpreter to be initialised for these tests **/ + PythonInterpreter interp; + + /** + * Initialisation called before each test. + * + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + + // Initialise a Jython interpreter + interp = new PythonInterpreter(); + + } + + /** + * Test importing the _io module into the global namespace of {@link #interp}. + */ + @Test + public void moduleImport() { + interp.exec("import _io"); + PyObject _io = interp.get("_io"); + org.junit.Assert.assertNotNull(_io); + } + + /** + * Test raising a Python _io.UnsupportedOperation from Java code directly. + */ + @Test + public void javaRaiseUnsupportedOperation() { + + // Built-in modules seem not to initialise until we actually use an interpreter + interp.exec("import io"); + + // There should be a helper function + PyException pye = _io.UnsupportedOperation("Message from _ioTest"); + PyObject type = pye.type; + String repr = type.toString(); + assertEquals("Class name", "", repr); + + // Raise from Java into Python and catch it in a variable: _IOBase.fileno() raises it + interp.exec("try :\n io.IOBase().fileno()\n" + "except Exception as e:\n pass"); + PyObject e = interp.get("e"); + + String m = e.toString(); + assertThat(m, both(containsString("UnsupportedOperation")).and(containsString("fileno"))); + + } + + /** + * Test raising a Python _io.UnsupportedOperation from Python code into Java. + */ + @Test + public void pythonRaiseUnsupportedOperation() { + interp.exec("import _io"); + try { + interp.exec("raise _io.UnsupportedOperation()"); + fail("_io.UnsupportedOperation not raised when expected"); + } catch (PyException e) { + assertEquals(_io.UnsupportedOperation, e.type); + } + } + +} -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Sun Dec 9 21:29:36 2012 From: jython-checkins at python.org (jeff.allen) Date: Sun, 9 Dec 2012 21:29:36 +0100 (CET) Subject: [Jython-checkins] =?utf-8?q?jython=3A_Add_skip_to_test=5Fio_for_P?= =?utf-8?q?osix_signals_on_NT?= Message-ID: <3YKJtS4sYDzRVP@mail.python.org> http://hg.python.org/jython/rev/061ca4e0f423 changeset: 6891:061ca4e0f423 user: Jeff Allen date: Wed Dec 05 20:09:04 2012 +0000 summary: Add skip to test_io for Posix signals on NT These tests should still run on Unix, but that needs to be verified. test_io scores: fail/error/skip = 6/58/99. files: Lib/test/test_io.py | 4 +++- 1 files changed, 3 insertions(+), 1 deletions(-) diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -2723,7 +2723,9 @@ io = pyio - at unittest.skipIf(os.name == 'nt', 'POSIX signals required for this test.') + at unittest.skipIf(os.name == 'nt' or + (sys.platform[:4] == 'java' and os._name == 'nt'), + 'POSIX signals required for this test.') class SignalsTest(unittest.TestCase): def setUp(self): -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Sun Dec 9 21:29:38 2012 From: jython-checkins at python.org (jeff.allen) Date: Sun, 9 Dec 2012 21:29:38 +0100 (CET) Subject: [Jython-checkins] =?utf-8?q?jython=3A_=5Fio=2Eopen=28=29_in_Java?= Message-ID: <3YKJtV3fhYzRVS@mail.python.org> http://hg.python.org/jython/rev/3cce7c6141a3 changeset: 6892:3cce7c6141a3 user: Jeff Allen date: Thu Dec 06 07:29:57 2012 +0000 summary: _io.open() in Java Eliminates Python implementation in _jyio of open(). the Java implementation does not yet support file descriptors (neither int nor Jython-style). Failures in test_io slightly up at fail/error/skip = 8/58/99 files: Lib/_jyio.py | 217 +--------- Lib/test/test_io.py | 3 - src/org/python/modules/_io/OpenMode.java | 242 ++++++++++ src/org/python/modules/_io/PyFileIO.java | 20 +- src/org/python/modules/_io/_io.java | 253 ++++++++++- 5 files changed, 506 insertions(+), 229 deletions(-) diff --git a/Lib/_jyio.py b/Lib/_jyio.py --- a/Lib/_jyio.py +++ b/Lib/_jyio.py @@ -40,11 +40,10 @@ __metaclass__ = type # open() uses st_blksize whenever we can -DEFAULT_BUFFER_SIZE = 8 * 1024 # bytes +from _io import DEFAULT_BUFFER_SIZE # NOTE: Base classes defined here are registered with the "official" ABCs -# defined in io.py. We don't use real inheritance though, because we don't -# want to inherit the C implementations. +# defined in io.py. class BlockingIOError(IOError): @@ -58,217 +57,7 @@ self.characters_written = characters_written -def open(file, mode="r", buffering=-1, - encoding=None, errors=None, - newline=None, closefd=True): - - r"""Open file and return a stream. Raise IOError upon failure. - - file is either a text or byte string giving the name (and the path - if the file isn't in the current working directory) of the file to - be opened or an integer file descriptor of the file to be - wrapped. (If a file descriptor is given, it is closed when the - returned I/O object is closed, unless closefd is set to False.) - - mode is an optional string that specifies the mode in which the file - is opened. It defaults to 'r' which means open for reading in text - mode. Other common values are 'w' for writing (truncating the file if - it already exists), and 'a' for appending (which on some Unix systems, - means that all writes append to the end of the file regardless of the - current seek position). In text mode, if encoding is not specified the - encoding used is platform dependent. (For reading and writing raw - bytes use binary mode and leave encoding unspecified.) The available - modes are: - - ========= =============================================================== - Character Meaning - --------- --------------------------------------------------------------- - 'r' open for reading (default) - 'w' open for writing, truncating the file first - 'a' open for writing, appending to the end of the file if it exists - 'b' binary mode - 't' text mode (default) - '+' open a disk file for updating (reading and writing) - 'U' universal newline mode (for backwards compatibility; unneeded - for new code) - ========= =============================================================== - - The default mode is 'rt' (open for reading text). For binary random - access, the mode 'w+b' opens and truncates the file to 0 bytes, while - 'r+b' opens the file without truncation. - - Python distinguishes between files opened in binary and text modes, - even when the underlying operating system doesn't. Files opened in - binary mode (appending 'b' to the mode argument) return contents as - bytes objects without any decoding. In text mode (the default, or when - 't' is appended to the mode argument), the contents of the file are - returned as strings, the bytes having been first decoded using a - platform-dependent encoding or using the specified encoding if given. - - buffering is an optional integer used to set the buffering policy. - Pass 0 to switch buffering off (only allowed in binary mode), 1 to select - line buffering (only usable in text mode), and an integer > 1 to indicate - the size of a fixed-size chunk buffer. When no buffering argument is - given, the default buffering policy works as follows: - - * Binary files are buffered in fixed-size chunks; the size of the buffer - is chosen using a heuristic trying to determine the underlying device's - "block size" and falling back on `io.DEFAULT_BUFFER_SIZE`. - On many systems, the buffer will typically be 4096 or 8192 bytes long. - - * "Interactive" text files (files for which isatty() returns True) - use line buffering. Other text files use the policy described above - for binary files. - - encoding is the name of the encoding used to decode or encode the - file. This should only be used in text mode. The default encoding is - platform dependent, but any encoding supported by Python can be - passed. See the codecs module for the list of supported encodings. - - errors is an optional string that specifies how encoding errors are to - be handled---this argument should not be used in binary mode. Pass - 'strict' to raise a ValueError exception if there is an encoding error - (the default of None has the same effect), or pass 'ignore' to ignore - errors. (Note that ignoring encoding errors can lead to data loss.) - See the documentation for codecs.register for a list of the permitted - encoding error strings. - - newline controls how universal newlines works (it only applies to text - mode). It can be None, '', '\n', '\r', and '\r\n'. It works as - follows: - - * On input, if newline is None, universal newlines mode is - enabled. Lines in the input can end in '\n', '\r', or '\r\n', and - these are translated into '\n' before being returned to the - caller. If it is '', universal newline mode is enabled, but line - endings are returned to the caller untranslated. If it has any of - the other legal values, input lines are only terminated by the given - string, and the line ending is returned to the caller untranslated. - - * On output, if newline is None, any '\n' characters written are - translated to the system default line separator, os.linesep. If - newline is '', no translation takes place. If newline is any of the - other legal values, any '\n' characters written are translated to - the given string. - - If closefd is False, the underlying file descriptor will be kept open - when the file is closed. This does not work when a file name is given - and must be True in that case. - - open() returns a file object whose type depends on the mode, and - through which the standard file operations such as reading and writing - are performed. When open() is used to open a file in a text mode ('w', - 'r', 'wt', 'rt', etc.), it returns a TextIOWrapper. When used to open - a file in a binary mode, the returned class varies: in read binary - mode, it returns a BufferedReader; in write binary and append binary - modes, it returns a BufferedWriter, and in read/write mode, it returns - a BufferedRandom. - - It is also possible to use a string or bytearray as a file for both - reading and writing. For strings StringIO can be used like a file - opened in a text mode, and for bytes a BytesIO can be used like a file - opened in a binary mode. - """ - if not isinstance(file, (basestring, int, long)): - raise TypeError("invalid file: %r" % file) - if not isinstance(mode, basestring): - raise TypeError("invalid mode: %r" % mode) - if not isinstance(buffering, (int, long)): - raise TypeError("invalid buffering: %r" % buffering) - if encoding is not None and not isinstance(encoding, basestring): - raise TypeError("invalid encoding: %r" % encoding) - if errors is not None and not isinstance(errors, basestring): - raise TypeError("invalid errors: %r" % errors) - modes = set(mode) - if modes - set("arwb+tU") or len(mode) > len(modes): - raise ValueError("invalid mode: %r" % mode) - reading = "r" in modes - writing = "w" in modes - appending = "a" in modes - updating = "+" in modes - text = "t" in modes - binary = "b" in modes - if "U" in modes: - if writing or appending: - raise ValueError("can't use U and writing mode at once") - reading = True - if text and binary: - raise ValueError("can't have text and binary mode at once") - if reading + writing + appending > 1: - raise ValueError("can't have read/write/append mode at once") - if not (reading or writing or appending): - raise ValueError("must have exactly one of read/write/append mode") - if binary and encoding is not None: - raise ValueError("binary mode doesn't take an encoding argument") - if binary and errors is not None: - raise ValueError("binary mode doesn't take an errors argument") - if binary and newline is not None: - raise ValueError("binary mode doesn't take a newline argument") - raw = FileIO(file, - (reading and "r" or "") + - (writing and "w" or "") + - (appending and "a" or "") + - (updating and "+" or ""), - closefd) - line_buffering = False - if buffering == 1 or buffering < 0 and raw.isatty(): - buffering = -1 - line_buffering = True - if buffering < 0: - buffering = DEFAULT_BUFFER_SIZE - try: - bs = os.fstat(raw.fileno()).st_blksize - except (os.error, AttributeError): - pass - else: - if bs > 1: - buffering = bs - if buffering < 0: - raise ValueError("invalid buffering size") - if buffering == 0: - if binary: - return raw - raise ValueError("can't have unbuffered text I/O") - if updating: - buffer = BufferedRandom(raw, buffering) - elif writing or appending: - buffer = BufferedWriter(raw, buffering) - elif reading: - buffer = BufferedReader(raw, buffering) - else: - raise ValueError("unknown mode: %r" % mode) - if binary: - return buffer - text = TextIOWrapper(buffer, encoding, errors, newline, line_buffering) - text.mode = mode - return text - - -class DocDescriptor: - """Helper for builtins.open.__doc__ - """ - def __get__(self, obj, typ): - return ( - "open(file, mode='r', buffering=-1, encoding=None, " - "errors=None, newline=None, closefd=True)\n\n" + - open.__doc__) - - -class OpenWrapper: - """Wrapper for builtins.open - - Trick so that open won't become a bound method when stored - as a class variable (as dbm.dumb does). - - See initstdio() in Python/pythonrun.c. - """ - __doc__ = DocDescriptor() - - def __new__(cls, *args, **kwargs): - return open(*args, **kwargs) - - -from _io import UnsupportedOperation +from _io import (open, UnsupportedOperation) class _IOBase: diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -2951,9 +2951,6 @@ py_io_ns.update((x.__name__, globs["Py" + x.__name__]) for x in mocks) # Avoid turning open into a bound method. py_io_ns["open"] = pyio.OpenWrapper - # XXX: While we use _jyio.py, the same trick is necessary for it too - import _jyio # XXX - c_io_ns["open"] = _jyio.OpenWrapper # XXX for test in tests: if test.__name__.startswith("C"): for name, obj in c_io_ns.items(): diff --git a/src/org/python/modules/_io/OpenMode.java b/src/org/python/modules/_io/OpenMode.java new file mode 100644 --- /dev/null +++ b/src/org/python/modules/_io/OpenMode.java @@ -0,0 +1,242 @@ +package org.python.modules._io; + +import org.python.core.Py; +import org.python.core.PyException; + +/** + * An object able to check a file access mode provided as a String and represent it as boolean + * attributes and in a normalised form. Such a string is the the mode argument of the several open() + * functions available in Python and certain constructors for streams-like objects. + */ +public class OpenMode { + + /** Original string supplied as the mode */ + public final String originalModeString; + + /** Whether this file is opened for reading ('r') */ + public boolean reading; + + /** Whether this file is opened for writing ('w') */ + public boolean writing; + + /** Whether this file is opened in appending mode ('a') */ + public boolean appending; + + /** Whether this file is opened for updating ('+') */ + public boolean updating; + + /** Whether this file is opened in binary mode ('b') */ + public boolean binary; + + /** Whether this file is opened in text mode ('t') */ + public boolean text; + + /** Whether this file is opened in universal newlines mode ('U') */ + public boolean universal; + + /** Whether the mode contained some other symbol from the allowed ones */ + public boolean other; + + /** Set true when any invalid symbol or combination is discovered */ + public boolean invalid; + + /** + * Error message describing the way in which the mode is invalid, or null if no problem has been + * found. This field may be set by the constructor (in the case of duplicate or unrecognised + * mode letters), by the {@link #isValid()} method, or by client code. A non-null value will + * cause {@link #isValid()} to return false. + */ + public String message; + + /** + * Decode the given string to an OpenMode object, checking for duplicate or unrecognised mode + * letters. Valid letters are those in "rwa+btU". Errors in the mode string do not raise an + * exception, they simply generate an appropriate error message in {@link #message}. After + * construction, a client should always call {@link #isValid()} to complete validity checks. + * + * @param mode + */ + public OpenMode(String mode) { + + originalModeString = mode; + int n = mode.length(); + boolean duplicate = false; + + for (int i = 0; i < n; i++) { + char c = mode.charAt(i); + + switch (c) { + case 'r': + duplicate = reading; + reading = true; + break; + case 'w': + duplicate = writing; + writing = true; + break; + case 'a': + duplicate = appending; + appending = true; + break; + case '+': + duplicate = updating; + updating = true; + break; + case 't': + duplicate = text; + text = true; + break; + case 'b': + duplicate = binary; + binary = true; + break; + case 'U': + duplicate = universal; + universal = true; + break; + default: + other = true; + } + + // duplicate is set iff c was encountered previously */ + if (duplicate) { + invalid = true; + break; + } + } + + } + + /** + * Adjust and validate the flags decoded from the mode string. The method affects the flags + * where the presence of one flag implies another, then if the {@link #invalid} flag is not + * already true, it checks the validity of the flags against combinations allowed + * by the Python io.open() function. In the case of a violation, it sets the + * invalid flag, and sets {@link #message} to a descriptive message. The point of + * the qualification "if the invalid flag is not already true" is that + * the message should always describe the first problem discovered. If left blank, as in fact + * the constructor does, it will be filled by the generic message when {@link #checkValid()} is + * finally called. Clients may override this method (by sub-classing) to express the validation + * correct in their context. + *

+ * The invalid combinations enforced here are those for the "raw" (ie non-text) file types: + *

+ * See also {@link #validate(String, String, String)} for additional checks relevant to text + * files. + */ + public void validate() { + + // Implications + reading |= universal; + + // Standard tests + if (!invalid) { + if (universal && (writing || appending)) { + message = "can't use U and writing mode at once"; + } else if (text && binary) { + message = "can't have text and binary mode at once"; + } else if (reading && writing || appending && (reading || writing)) { + message = "must have exactly one of read/write/append mode"; + } + invalid |= (message != null); + } + } + + /** + * Perform additional validation of the flags relevant to text files. If {@link #invalid} is not + * already true, and the mode includes {@link #binary}, then all the arguments to + * this call must be null. If the criterion is not met, then on return from the + * method, invalid==true and {@link #message} is set to a standard error message. + * This is the standard additional validation applicable to text files. (By "standard" we mean + * the test and messages that CPython io.open uses.) + * + * @param encoding argument to open() + * @param errors argument to open() + * @param newline argument to open() + */ + public void validate(String encoding, String errors, String newline) { + + // If the basic tests passed and binary mode is set one check text arguments null + if (!invalid && binary) { + if (encoding != null) { + message = "binary mode doesn't take an encoding argument"; + } else if (errors != null) { + message = "binary mode doesn't take an errors argument"; + } else if (newline != null) { + message = "binary mode doesn't take a newline argument"; + } + invalid = (message != null); + } + } + + /** + * Call {@link #validate()} and raise an exception if the mode string is not valid, + * as signalled by either {@link #invalid} + * or {@link #other} being true after that call. If no more specific message has been assigned in + * {@link #message}, report the original mode string. + * + * @throws PyException (ValueError) if the mode string was invalid. + */ + public void checkValid() throws PyException { + + // Actually peform the check + validate(); + + // The 'other' flag reports alien symbols in the original mode string + invalid |= other; + + // Finally, if invalid, report this as an error + if (invalid) { + if (message == null) { + // Duplicates discovered in the constructor or invalid symbols + message = String.format("invalid mode: '%.20s'", originalModeString); + } + throw Py.ValueError(message); + } + } + + public String rawmode() { + StringBuilder m = new StringBuilder(2); + if (appending) { + m.append('a'); + } else if (writing) { + m.append('w'); + } else { + m.append('r'); + } + if (updating) { + m.append('+'); + } + return m.toString(); + } + + @Override + public String toString() { + StringBuilder m = new StringBuilder(4); + if (appending) { + m.append('a'); + } else if (writing) { + m.append('w'); + } else { + m.append('r'); + } + if (updating) { + m.append('+'); + } + if (text) { + m.append('t'); + } else if (binary) { + m.append('b'); + } + if (universal) { + m.append('U'); + } + return m.toString(); + } + +} diff --git a/src/org/python/modules/_io/PyFileIO.java b/src/org/python/modules/_io/PyFileIO.java --- a/src/org/python/modules/_io/PyFileIO.java +++ b/src/org/python/modules/_io/PyFileIO.java @@ -1,4 +1,4 @@ -/* Copyright (c) Jython Developers */ +/* Copyright (c)2012 Jython Developers */ package org.python.modules._io; import java.nio.ByteBuffer; @@ -71,9 +71,10 @@ String mode = ap.getString(1, "r"); boolean closefd = Py.py2boolean(ap.getPyObject(2, Py.True)); // TODO: make this work with file channels so closefd=False can be used - if (!closefd) - throw Py.ValueError("Cannot use closefd=False with file name"); - + if (!closefd) { + throw Py.ValueError("Cannot use closefd=False with file name"); + } + FileIO___init__((PyString)name, mode, closefd); closer = new Closer(file, Py.getSystemState()); } @@ -83,7 +84,7 @@ this.name = name; this.mode = mode; this.closefd = closefd; - this.file = new FileIO((PyString) name, mode.replaceAll("b", "")); + this.file = new FileIO(name, mode.replaceAll("b", "")); } private String parseMode(String mode) { @@ -141,8 +142,9 @@ @ExposedMethod(doc = "True if file supports random-access.") final boolean FileIO_seekable() { - if (seekable == null) - seekable = file.seek(0, 0) >= 0; + if (seekable == null) { + seekable = file.seek(0, 0) >= 0; + } return seekable; } @@ -158,8 +160,9 @@ @ExposedMethod(defaults = {"null"}, doc = BuiltinDocs.file_truncate_doc) final PyObject FileIO_truncate(PyObject position) { - if (position == null) + if (position == null) { return Py.java2py(FileIO_truncate()); + } return Py.java2py(FileIO_truncate(position.asLong())); } @@ -311,6 +314,7 @@ } /** For closing as part of a shutdown process */ + @Override public Void call() { file.close(); sys = null; diff --git a/src/org/python/modules/_io/_io.java b/src/org/python/modules/_io/_io.java --- a/src/org/python/modules/_io/_io.java +++ b/src/org/python/modules/_io/_io.java @@ -1,22 +1,23 @@ /* Copyright (c)2012 Jython Developers */ package org.python.modules._io; +import org.python.core.ArgParser; import org.python.core.ClassDictInit; import org.python.core.Py; import org.python.core.PyException; +import org.python.core.PyInteger; import org.python.core.PyObject; import org.python.core.PyString; import org.python.core.PyStringMap; import org.python.core.PyType; import org.python.core.imp; +import org.python.core.io.IOBase; /** - * The Python _io module. + * The Python _io module implemented in Java. */ public class _io implements ClassDictInit { - public static final PyString __doc__ = new PyString("Java implementation of _io."); - /** * This method is called when the module is loaded, to populate the namespace (dictionary) of * the module. The dictionary has been initialised at this point reflectively from the methods @@ -26,7 +27,8 @@ */ public static void classDictInit(PyObject dict) { dict.__setitem__("__name__", new PyString("_io")); - dict.__setitem__("__doc__", __doc__); + dict.__setitem__("__doc__", new PyString(__doc__)); + dict.__setitem__("DEFAULT_BUFFER_SIZE", DEFAULT_BUFFER_SIZE); dict.__setitem__("FileIO", PyFileIO.TYPE); // Define UnsupportedOperation exception by constructing the type @@ -75,4 +77,247 @@ return type; } + /** Default buffer size obtained from {@link IOBase#DEFAULT_BUFFER_SIZE}. */ + private static final int _DEFAULT_BUFFER_SIZE = IOBase.DEFAULT_BUFFER_SIZE; + + /** Default buffer size for export. */ + public static final PyInteger DEFAULT_BUFFER_SIZE = new PyInteger(_DEFAULT_BUFFER_SIZE); + + /** + * Open file and return a stream. Raise IOError upon failure. This is a port to Java of the + * CPython _io.open (Modules/_io/_iomodule.c) following the same logic, but expressed with the + * benefits of Java syntax. + * + * @param args array of arguments from Python call via Jython framework + * @param kwds array of keywords from Python call via Jython framework + * @return the stream object + */ + public static PyObject open(PyObject[] args, String[] kwds) { + + // Get the arguments to variables + ArgParser ap = new ArgParser("open", args, kwds, openKwds, 1); + PyObject file = ap.getPyObject(0); + String m = ap.getString(1, "r"); + int buffering = ap.getInt(2, -1); + final String encoding = ap.getString(3, null); + final String errors = ap.getString(4, null); + final String newline = ap.getString(5, null); + boolean closefd = Py.py2boolean(ap.getPyObject(6, Py.True)); + + // Decode the mode string + OpenMode mode = new OpenMode(m) { + + @Override + public void validate() { + super.validate(); + validate(encoding, errors, newline); + } + }; + + mode.checkValid(); + + int line_buffering; + + /* + * Create the Raw file stream. Let the constructor deal with the variants and argument + * checking. + */ + // XXX open() doesn't yet support file descriptors or the "digested" mode + PyFileIO raw = new PyFileIO(file.toString(), mode.rawmode(), closefd); + + // XXX Can this work: boolean isatty = raw.isatty() ? Or maybe: + // PyObject res = PyObject_CallMethod(raw, "isatty", NULL); + boolean isatty = false; + + /* + * Work out a felicitous buffer size + */ + if (buffering == 1 || (buffering < 0 && isatty)) { + buffering = -1; + line_buffering = 1; + } else { + line_buffering = 0; + } + + if (buffering < 0) { + // Try to establish the default buffer size for this file using the OS. + buffering = _DEFAULT_BUFFER_SIZE; + // PyObject res = PyObject_CallMethod(raw, "fileno", NULL); + // if (fstat(fileno, &st) >= 0 && st.st_blksize > 1) + // buffering = st.st_blksize; + } + + if (buffering < 0) { + throw Py.ValueError("invalid buffering size"); + } + + // If not buffering, return the raw file object + if (buffering == 0) { + if (!mode.binary) { + throw Py.ValueError("can't have unbuffered text I/O"); + } + return raw; + } + + // We are buffering, so wrap raw into a buffered file + PyObject bufferType = null; + PyObject io = imp.load("io"); + + if (mode.updating) { + bufferType = io.__getattr__("BufferedRandom"); + } else if (mode.writing || mode.appending) { + bufferType = io.__getattr__("BufferedWriter"); + } else if (mode.reading) { + bufferType = io.__getattr__("BufferedReader"); + } else { + // Can it really still go wrong? I don't think so. + throw Py.ValueError(String.format("unknown mode: '%s'", mode.originalModeString)); + } + + PyInteger pyBuffering = new PyInteger(buffering); + PyObject buffer = bufferType.__call__(raw, pyBuffering); + + // If binary, return the buffered file + if (mode.binary) { + return buffer; + } + + /* We are opening in text mode, so wrap buffer into a TextIOWrapper */ + PyObject textType = io.__getattr__("TextIOWrapper"); + PyObject[] textArgs = + {buffer, ap.getPyObject(3, Py.None), ap.getPyObject(4, Py.None), + ap.getPyObject(5, Py.None), Py.newInteger(line_buffering)}; + PyObject wrapper = textType.__call__(textArgs); + + return wrapper; + } + + private static final String[] openKwds = {"file", "mode", "buffering", "encoding", "errors", + "newline", "closefd"}; + + public static final String __doc__ = + "The io module provides the Python interfaces to stream handling. The\n" + + "builtin open function is defined in this module.\n" + "\n" + + "At the top of the I/O hierarchy is the abstract base class IOBase. It\n" + + "defines the basic interface to a stream. Note, however, that there is no\n" + + "seperation between reading and writing to streams; implementations are\n" + + "allowed to throw an IOError if they do not support a given operation.\n" + + "\n" + + "Extending IOBase is RawIOBase which deals simply with the reading and\n" + + "writing of raw bytes to a stream. FileIO subclasses RawIOBase to provide\n" + + "an interface to OS files.\n" + "\n" + + "BufferedIOBase deals with buffering on a raw byte stream (RawIOBase). Its\n" + + "subclasses, BufferedWriter, BufferedReader, and BufferedRWPair buffer\n" + + "streams that are readable, writable, and both respectively.\n" + + "BufferedRandom provides a buffered interface to random access\n" + + "streams. BytesIO is a simple stream of in-memory bytes.\n" + "\n" + + "Another IOBase subclass, TextIOBase, deals with the encoding and decoding\n" + + "of streams into text. TextIOWrapper, which extends it, is a buffered text\n" + + "interface to a buffered raw stream (`BufferedIOBase`). Finally, StringIO\n" + + "is a in-memory stream for text.\n" + "\n" + + "Argument names are not part of the specification, and only the arguments\n" + + "of open() are intended to be used as keyword arguments.\n"; + +// + "\n" +// + "data:\n" +// + "\n" +// + "DEFAULT_BUFFER_SIZE\n" +// + "\n" +// + " An int containing the default buffer size used by the module's buffered\n" +// + " I/O classes. open() uses the file's blksize (as obtained by os.stat) if\n" +// + " possible.\n"; + + public static final String __doc__open = + "Open file and return a stream. Raise IOError upon failure.\n" + "\n" + + "file is either a text or byte string giving the name (and the path\n" + + "if the file isn't in the current working directory) of the file to\n" + + "be opened or an integer file descriptor of the file to be\n" + + "wrapped. (If a file descriptor is given, it is closed when the\n" + + "returned I/O object is closed, unless closefd is set to False.)\n" + "\n" + + "mode is an optional string that specifies the mode in which the file\n" + + "is opened. It defaults to 'r' which means open for reading in text\n" + + "mode. Other common values are 'w' for writing (truncating the file if\n" + + "it already exists), and 'a' for appending (which on some Unix systems,\n" + + "means that all writes append to the end of the file regardless of the\n" + + "current seek position). In text mode, if encoding is not specified the\n" + + "encoding used is platform dependent. (For reading and writing raw\n" + + "bytes use binary mode and leave encoding unspecified.) The available\n" + + "modes are:\n" + "\n" + + "========= ===============================================================\n" + + "Character Meaning\n" + + "--------- ---------------------------------------------------------------\n" + + "'r' open for reading (default)\n" + + "'w' open for writing, truncating the file first\n" + + "'a' open for writing, appending to the end of the file if it exists\n" + + "'b' binary mode\n" + "'t' text mode (default)\n" + + "'+' open a disk file for updating (reading and writing)\n" + + "'U' universal newline mode (for backwards compatibility; unneeded\n" + + " for new code)\n" + + "========= ===============================================================\n" + + "\n" + + "The default mode is 'rt' (open for reading text). For binary random\n" + + "access, the mode 'w+b' opens and truncates the file to 0 bytes, while\n" + + "'r+b' opens the file without truncation.\n" + "\n" + + "Python distinguishes between files opened in binary and text modes,\n" + + "even when the underlying operating system doesn't. Files opened in\n" + + "binary mode (appending 'b' to the mode argument) return contents as\n" + + "bytes objects without any decoding. In text mode (the default, or when\n" + + "'t' is appended to the mode argument), the contents of the file are\n" + + "returned as strings, the bytes having been first decoded using a\n" + + "platform-dependent encoding or using the specified encoding if given.\n" + + "\n" + "buffering is an optional integer used to set the buffering policy.\n" + + "Pass 0 to switch buffering off (only allowed in binary mode), 1 to select\n" + + "line buffering (only usable in text mode), and an integer > 1 to indicate\n" + + "the size of a fixed-size chunk buffer. When no buffering argument is\n" + + "given, the default buffering policy works as follows:\n" + "\n" + + "* Binary files are buffered in fixed-size chunks; the size of the buffer\n" + + " is chosen using a heuristic trying to determine the underlying device's\n" + + " \"block size\" and falling back on `io.DEFAULT_BUFFER_SIZE`.\n" + + " On many systems, the buffer will typically be 4096 or 8192 bytes long.\n" + + "\n" + + "* \"Interactive\" text files (files for which isatty() returns True)\n" + + " use line buffering. Other text files use the policy described above\n" + + " for binary files.\n" + "\n" + + "encoding is the name of the encoding used to decode or encode the\n" + + "file. This should only be used in text mode. The default encoding is\n" + + "platform dependent, but any encoding supported by Python can be\n" + + "passed. See the codecs module for the list of supported encodings.\n" + + "\n" + + "errors is an optional string that specifies how encoding errors are to\n" + + "be handled---this argument should not be used in binary mode. Pass\n" + + "'strict' to raise a ValueError exception if there is an encoding error\n" + + "(the default of None has the same effect), or pass 'ignore' to ignore\n" + + "errors. (Note that ignoring encoding errors can lead to data loss.)\n" + + "See the documentation for codecs.register for a list of the permitted\n" + + "encoding error strings.\n" + "\n" + + "newline controls how universal newlines works (it only applies to text\n" + + "mode). It can be None, '', '\\n', '\\r', and '\\r\\n'. It works as\n" + + "follows:\n" + "\n" + + "* On input, if newline is None, universal newlines mode is\n" + + " enabled. Lines in the input can end in '\\n', '\\r', or '\\r\\n', and\n" + + " these are translated into '\\n' before being returned to the\n" + + " caller. If it is '', universal newline mode is enabled, but line\n" + + " endings are returned to the caller untranslated. If it has any of\n" + + " the other legal values, input lines are only terminated by the given\n" + + " string, and the line ending is returned to the caller untranslated.\n" + + "\n" + "* On output, if newline is None, any '\\n' characters written are\n" + + " translated to the system default line separator, os.linesep. If\n" + + " newline is '', no translation takes place. If newline is any of the\n" + + " other legal values, any '\\n' characters written are translated to\n" + + " the given string.\n" + "\n" + + "If closefd is False, the underlying file descriptor will be kept open\n" + + "when the file is closed. This does not work when a file name is given\n" + + "and must be True in that case.\n" + "\n" + + "open() returns a file object whose type depends on the mode, and\n" + + "through which the standard file operations such as reading and writing\n" + + "are performed. When open() is used to open a file in a text mode ('w',\n" + + "'r', 'wt', 'rt', etc.), it returns a TextIOWrapper. When used to open\n" + + "a file in a binary mode, the returned class varies: in read binary\n" + + "mode, it returns a BufferedReader; in write binary and append binary\n" + + "modes, it returns a BufferedWriter, and in read/write mode, it returns\n" + + "a BufferedRandom.\n" + "\n" + + "It is also possible to use a string or bytearray as a file for both\n" + + "reading and writing. For strings StringIO can be used like a file\n" + + "opened in a text mode, and for bytes a BytesIO can be used like a file\n" + + "opened in a binary mode.\n"; } -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Sun Dec 9 21:29:40 2012 From: jython-checkins at python.org (jeff.allen) Date: Sun, 9 Dec 2012 21:29:40 +0100 (CET) Subject: [Jython-checkins] =?utf-8?q?jython=3A_Implement_=5Fio=2E=5FIOBase?= =?utf-8?q?_in_Java?= Message-ID: <3YKJtX5CZmzRW2@mail.python.org> http://hg.python.org/jython/rev/4c44912ee03f changeset: 6893:4c44912ee03f user: Jeff Allen date: Sat Dec 08 16:31:29 2012 +0000 summary: Implement _io._IOBase in Java There is now an implementation in org.python.modules._io.PyIOBase.java. Score in test_io.py is slightly up again at fail/error/skip = 12/58/99, but this will come down in later rounds when PyFileIO finally joins the new class hierarchy. files: CoreExposed.includes | 1 + Lib/_jyio.py | 285 +- Lib/test/test_io.py | 5 +- src/org/python/modules/_io/Closer.java | 67 + src/org/python/modules/_io/PyIOBase.java | 812 +++++ src/org/python/modules/_io/PyIOBaseDerived.java | 1538 ++++++++++ src/org/python/modules/_io/_io.java | 2 + src/templates/_io._IOBase.derived | 210 + src/templates/mappings | 1 + 9 files changed, 2643 insertions(+), 278 deletions(-) diff --git a/CoreExposed.includes b/CoreExposed.includes --- a/CoreExposed.includes +++ b/CoreExposed.includes @@ -59,6 +59,7 @@ org/python/modules/_csv/PyDialect.class org/python/modules/_csv/PyReader.class org/python/modules/_csv/PyWriter.class +org/python/modules/_io/PyIOBase.class org/python/modules/_io/PyFileIO.class org/python/modules/_functools/PyPartial.class org/python/modules/_hashlib$Hash.class diff --git a/Lib/_jyio.py b/Lib/_jyio.py --- a/Lib/_jyio.py +++ b/Lib/_jyio.py @@ -57,274 +57,7 @@ self.characters_written = characters_written -from _io import (open, UnsupportedOperation) - - -class _IOBase: - __metaclass__ = abc.ABCMeta - - """The abstract base class for all I/O classes, acting on streams of - bytes. There is no public constructor. - - This class provides dummy implementations for many methods that - derived classes can override selectively; the default implementations - represent a file that cannot be read, written or seeked. - - Even though _IOBase does not declare read, readinto, or write because - their signatures will vary, implementations and clients should - consider those methods part of the interface. Also, implementations - may raise a IOError when operations they do not support are called. - - The basic type used for binary data read from or written to a file is - bytes. bytearrays are accepted too, and in some cases (such as - readinto) needed. Text I/O classes work with str data. - - Note that calling any method (even inquiries) on a closed stream is - undefined. Implementations may raise IOError in this case. - - _IOBase (and its subclasses) support the iterator protocol, meaning - that an _IOBase object can be iterated over yielding the lines in a - stream. - - _IOBase also supports the :keyword:`with` statement. In this example, - fp is closed after the suite of the with statement is complete: - - with open('spam.txt', 'r') as fp: - fp.write('Spam and eggs!') - """ - - ### Internal ### - - def _unsupported(self, name): - """Internal: raise an exception for unsupported operations.""" - raise UnsupportedOperation("%s.%s() not supported" % - (self.__class__.__name__, name)) - - ### Positioning ### - - def seek(self, pos, whence=0): - """Change stream position. - - Change the stream position to byte offset offset. offset is - interpreted relative to the position indicated by whence. Values - for whence are: - - * 0 -- start of stream (the default); offset should be zero or positive - * 1 -- current stream position; offset may be negative - * 2 -- end of stream; offset is usually negative - - Return the new absolute position. - """ - self._unsupported("seek") - - def tell(self): - """Return current stream position.""" - return self.seek(0, 1) - - def truncate(self, pos=None): - """Truncate file to size bytes. - - Size defaults to the current IO position as reported by tell(). Return - the new size. - """ - self._unsupported("truncate") - - ### Flush and close ### - - def flush(self): - """Flush write buffers, if applicable. - - This is not implemented for read-only and non-blocking streams. - """ - self._checkClosed() - # XXX Should this return the number of bytes written??? - - __closed = False - - def close(self): - """Flush and close the IO object. - - This method has no effect if the file is already closed. - """ - if not self.__closed: - self.flush() - self.__closed = True - - def __del__(self): - """Destructor. Calls close().""" - # The try/except block is in case this is called at program - # exit time, when it's possible that globals have already been - # deleted, and then the close() call might fail. Since - # there's nothing we can do about such failures and they annoy - # the end users, we suppress the traceback. - try: - self.close() - except: - pass - - ### Inquiries ### - - def seekable(self): - """Return whether object supports random access. - - If False, seek(), tell() and truncate() will raise IOError. - This method may need to do a test seek(). - """ - return False - - def _checkSeekable(self, msg=None): - """Internal: raise an IOError if file is not seekable - """ - if not self.seekable(): - raise IOError("File or stream is not seekable." - if msg is None else msg) - - - def readable(self): - """Return whether object was opened for reading. - - If False, read() will raise IOError. - """ - return False - - def _checkReadable(self, msg=None): - """Internal: raise an IOError if file is not readable - """ - if not self.readable(): - raise IOError("File or stream is not readable." - if msg is None else msg) - - def writable(self): - """Return whether object was opened for writing. - - If False, write() and truncate() will raise IOError. - """ - return False - - def _checkWritable(self, msg=None): - """Internal: raise an IOError if file is not writable - """ - if not self.writable(): - raise IOError("File or stream is not writable." - if msg is None else msg) - - @property - def closed(self): - """closed: bool. True iff the file has been closed. - - For backwards compatibility, this is a property, not a predicate. - """ - return self.__closed - - def _checkClosed(self, msg=None): - """Internal: raise an ValueError if file is closed - """ - if self.closed: - raise ValueError("I/O operation on closed file." - if msg is None else msg) - - ### Context manager ### - - def __enter__(self): - """Context management protocol. Returns self.""" - self._checkClosed() - return self - - def __exit__(self, *args): - """Context management protocol. Calls close()""" - self.close() - - ### Lower-level APIs ### - - # XXX Should these be present even if unimplemented? - - def fileno(self): - """Returns underlying file descriptor if one exists. - - An IOError is raised if the IO object does not use a file descriptor. - """ - self._unsupported("fileno") - - def isatty(self): - """Return whether this is an 'interactive' stream. - - Return False if it can't be determined. - """ - self._checkClosed() - return False - - ### Readline[s] and writelines ### - - def readline(self, limit=-1): - r"""Read and return a line from the stream. - - If limit is specified, at most limit bytes will be read. - - The line terminator is always b'\n' for binary files; for text - files, the newlines argument to open can be used to select the line - terminator(s) recognized. - """ - # For backwards compatibility, a (slowish) readline(). - if hasattr(self, "peek"): - def nreadahead(): - readahead = self.peek(1) - if not readahead: - return 1 - n = (readahead.find(b"\n") + 1) or len(readahead) - if limit >= 0: - n = min(n, limit) - return n - else: - def nreadahead(): - return 1 - if limit is None: - limit = -1 - elif not isinstance(limit, (int, long)): - raise TypeError("limit must be an integer") - res = bytearray() - while limit < 0 or len(res) < limit: - b = self.read(nreadahead()) - if not b: - break - res += b - if res.endswith(b"\n"): - break - return bytes(res) - - def __iter__(self): - self._checkClosed() - return self - - def next(self): - line = self.readline() - if not line: - raise StopIteration - return line - - def readlines(self, hint=None): - """Return a list of lines from the stream. - - hint can be specified to control the number of lines read: no more - lines will be read if the total size (in bytes/characters) of all - lines so far exceeds hint. - """ - if hint is not None and not isinstance(hint, (int, long)): - raise TypeError("integer or None expected") - if hint is None or hint <= 0: - return list(self) - n = 0 - lines = [] - for line in self: - lines.append(line) - n += len(line) - if n >= hint: - break - return lines - - def writelines(self, lines): - self._checkClosed() - for line in lines: - self.write(line) +from _io import (open, UnsupportedOperation, _IOBase) class _RawIOBase(_IOBase): @@ -569,9 +302,9 @@ try: name = self.name except AttributeError: - return "<_pyio.{0}>".format(clsname) + return "<_jyio.{0}>".format(clsname) else: - return "<_pyio.{0} name={1!r}>".format(clsname, name) + return "<_jyio.{0} name={1!r}>".format(clsname, name) ### Lower-level APIs ### @@ -1325,9 +1058,9 @@ try: name = self.name except AttributeError: - return "<_pyio.TextIOWrapper encoding='{0}'>".format(self.encoding) + return "<_jyio.TextIOWrapper encoding='{0}'>".format(self.encoding) else: - return "<_pyio.TextIOWrapper name={0!r} encoding='{1}'>".format( + return "<_jyio.TextIOWrapper name={0!r} encoding='{1}'>".format( name, self.encoding) @property @@ -1361,12 +1094,12 @@ def close(self): if self.buffer is not None and not self.closed: - self.flush() + # Jython difference: flush and close via super. + # Sets __closed for quick _checkClosed(). + super(TextIOWrapper, self).close() self.buffer.close() - @property - def closed(self): - return self.buffer.closed + # Jython difference: @property closed(self) inherited from _IOBase.__closed @property def name(self): diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -37,8 +37,9 @@ from test import test_support as support import codecs -import io # C implementation of io -import _pyio as pyio # Python implementation of io +import io # subject of test +import _io # compiled implementation of io +import _pyio as pyio # Python implementation of io try: import threading except ImportError: diff --git a/src/org/python/modules/_io/Closer.java b/src/org/python/modules/_io/Closer.java new file mode 100644 --- /dev/null +++ b/src/org/python/modules/_io/Closer.java @@ -0,0 +1,67 @@ +/* Copyright (c)2012 Jython Developers */ +package org.python.modules._io; + +import java.lang.ref.WeakReference; +import java.util.concurrent.Callable; + +import org.python.core.PyObject; +import org.python.core.PySystemState; + +/** + * A class to take responsibility for closing a stream on JVM shutdown. On creation a Closer adds + * itself to a list that will be run by PySystemState on shutdown and it maintains a reference to + * its client PyIOBase. The client (assuming it kept a reference to the Closer) may dismiss the + * closer at any time: normally this will be when the client stream is closed directly, or + * implicitly through {@link PyIOBase#__exit__(PyObject, PyObject, PyObject)}. A possibility is that + * the JVM exits before an close() is called. In that case, {@link Closer#call()} will + * be called, and the Closer will call the client's close operation. + *

+ * In case the close operation is overridden so that it does not dismiss the closer, it is useful to + * have the client's finalize dismiss the closer. If not, the Closer will remain registered and this + * will keep the PyIOBase object alive in the JVM, which is mostly harmless. + */ +/* + * Adapted from PyFile, but significantly different to call the overridden close operation of the + * client itself, rather than just the underlying file. + */ +class Closer implements Callable { + + /** The client in need of closing. */ + private final WeakReference client; + + /** Interpreter state that will call {@link #call()} on shutdown. */ + private PySystemState sys; + + public Closer(C toClose, PySystemState sys) { + this.client = new WeakReference(toClose); + this.sys = sys; + sys.registerCloser(this); + } + + /** + * Tell the Closer that its services are no longer required. This unhooks it from the shutdown + * list. Repeated calls are allowed but only the first has any effect. + */ + public synchronized void dismiss() { + if (sys != null) { + sys.unregisterCloser(this); + sys = null; + } + } + + /** + * Called as part of a shutdown process. If the client is still (weakly) reachable, invoke its + * "close" method. + */ + @Override + public synchronized Void call() { + // This will prevent a call to dismiss() manipulating the caller's list of closers + sys = null; + // Call close on the client (if it still exists) + C toClose = client.get(); + if (toClose != null) { + toClose.invoke("close"); + } + return null; + } +} \ No newline at end of file diff --git a/src/org/python/modules/_io/PyIOBase.java b/src/org/python/modules/_io/PyIOBase.java new file mode 100644 --- /dev/null +++ b/src/org/python/modules/_io/PyIOBase.java @@ -0,0 +1,812 @@ +/* Copyright (c)2012 Jython Developers */ +package org.python.modules._io; + +import org.python.core.BufferProtocol; +import org.python.core.Py; +import org.python.core.PyArray; +import org.python.core.PyBUF; +import org.python.core.PyBuffer; +import org.python.core.PyByteArray; +import org.python.core.PyException; +import org.python.core.PyList; +import org.python.core.PyNewWrapper; +import org.python.core.PyObject; +import org.python.core.PyString; +import org.python.core.PyStringMap; +import org.python.core.PyType; +import org.python.core.PyUnicode; +import org.python.core.buffer.SimpleStringBuffer; +import org.python.core.io.FileIO; +import org.python.expose.ExposedGet; +import org.python.expose.ExposedMethod; +import org.python.expose.ExposedNew; +import org.python.expose.ExposedType; + +/** + * The Python module _io._IOBase, on which the io module depends directly. + *

+ * Implementation note: The code is based heavily on the Jython 2.6-ish + * _fileio.PyFileIO, the purely Java-accessible {@link org.python.core.io.IOBase} (both + * Philip Jenvey's work), and the Python implementation in Lib/_pyio. We do not simply + * delegate to the implementation in {@link org.python.core.io} because of the need to override + * parts of the implementation in Python subclasses. A call to {@link #close()}, for example, is + * required to call {@link #flush()}, but if delegated to the pure Java implementation would not + * call the version of flush() overridden in a Python sub-class of + * _io._IOBase. Equally, the use made by PyRawIOBase.read(int) of + * readinto(bytearray) would not pick up the version of readinto defined + * in Python. + */ + at ExposedType(name = "_io._IOBase", doc = PyIOBase.doc) +public class PyIOBase extends PyObject { + + public static final PyType TYPE = PyType.fromClass(PyIOBase.class); + + /** The ioDelegate's closer object; ensures the stream is closed at shutdown */ + private Closer closer; + + protected PyIOBase() { + this(TYPE); + } + + protected PyIOBase(PyType subtype) { + super(subtype); + closer = new Closer(this, Py.getSystemState()); + } + + /** + * Provide a dictionary in the object, mainly so that methods can be overridden at instance + * level, mainly, in fact, so that the test test_flush_error_on_close passes. + */ + @ExposedGet + protected PyObject __dict__ = new PyStringMap(); + + @Override + public PyObject fastGetDict() { + return __dict__; + } + + @ExposedNew + static PyObject _IOBase___new__(PyNewWrapper new_, boolean init, PyType subtype, + PyObject[] args, String[] keywords) { + if (new_.for_type == subtype) { + // We only want an _io._IOBase, so the constructor does it all + return new PyIOBase(); + } else { + // We want some sub-class of it (in which __init__ will be called by the caller) + return new PyIOBaseDerived(subtype); + } + } + + /** + * Convenience method returning a PyException(_io.UnsupportedOperation), ripe for throwing, and + * naming the unsupported operation. Like many similar methods, this does not actually throw + * (raise) the exception: it should be thrown from the place where the problem is. + * + * @param op name of operation + * @return the exception for throwing + */ + protected PyException unsupported(String op) { + String fmt = "%s.%s() not supported"; + String msg = String.format(fmt, getType().fastGetName(), op); + return _io.UnsupportedOperation(msg); + } + + @ExposedMethod(doc = "Internal: raise an exception for unsupported operations.") + final void _IOBase__unsupported(String name) { + throw unsupported(name); + } + + /** + * Position the read or write pointer at a given byte offset pos relative to a + * position indicated by whence. + *

+ * + * @param pos relative to the specified point + * @param whence 0=from start, 1=from here, 2=from end + * @return the new current position + */ + public long seek(long pos, int whence) { + return _IOBase_seek(pos, whence); + } + + public final long seek(long pos) { + return seek(pos, 0); + } + + @ExposedMethod(defaults = "0", doc = seek_doc) + final long _IOBase_seek(long pos, int whence) { + throw unsupported("seek"); + } + + /** + * Get the current stream position. + * + * @return stream position + */ + public long tell() { + return _IOBase_tell(); + } + + @ExposedMethod(doc = tell_doc) + final long _IOBase_tell() { + return seek(0, 1); + } + + /** + * Truncate file to size bytes. + * + * @param size requested for stream + * @return the new size + */ + public long truncate(long size) { + return _IOBase_truncate(null); // Just raises an exception anyway + } + + /** + * Truncate file to size bytes to the current position (as reported by + * tell()). + * + * @param size requested for stream (or null for default) + * @return the new size + */ + public long truncate() { + return _IOBase_truncate(null); + } + + @ExposedMethod(defaults = "null", doc = truncate_doc) + final long _IOBase_truncate(PyObject size) { + throw unsupported("truncate"); + } + + /** + * Flush write buffers, or no-op for read-only and non-blocking streams. Irrespective of the + * concrete type of the i/o object, locally-buffered write data is written downstream. Whether + * the downstream in object is also flushed depends upon the specific type of this object. + */ + public void flush() { + _IOBase_flush(); + } + + @ExposedMethod(doc = flush_doc) + final void _IOBase_flush() {} + + /** + * True if the object is closed to further client operations. It is the state accessed by + * closed and checked by {@link #_checkClosed()}. It may be set without making it + * an error (for this object and its subclasses) to access some downstream object, notably in + * the implementation of close(). + */ + /* + * This is the analogue of the IS_CLOSED(self) macro and iobase_closed_get() in CPython + * modules/_io/iobase.c. That function checks for the *existence* of an attribute + * __IOBase_closed. You can find this exposed in CPython. _pyio.IOBase exposes _IOBase__closed + * (aka "__closed") for a similar purpose (note different name). + */ + @ExposedGet(name = "closed", doc = closed_doc) + protected boolean __closed; + + /** + * Close the stream. If closed already, this is a no-op. + */ + public void close() { + _IOBase_close(); + } + + @ExposedMethod(doc = close_doc) + final void _IOBase_close() { + if (!__closed) { + // Cancel the wake-up call + closer.dismiss(); + // Close should invoke (possibly overridden) flush here. + invoke("flush"); + // Prevent further access from upstream + __closed = true; + } + } + + /** + * Is the stream capable of positioning the read/write pointer? + * + * @return True if may be positioned + */ + public boolean seekable() { + return _IOBase_seekable(); + } + + @ExposedMethod(doc = seekable_doc) + final boolean _IOBase_seekable() { + return false; + } + + /** + * Raise an error if the pointer of underlying IO stream is not capable of being + * positioned. + * + * @param msg optional custom message + */ + public void _checkSeekable(String msg) { + _IOBase__checkSeekable(msg); + } + + /** + * Raise an error if the pointer of underlying IO stream is not capable of being + * positioned. + */ + public final void _checkSeekable() { + _checkSeekable(null); + } + + @ExposedMethod(defaults = "null") + final void _IOBase__checkSeekable(String msg) { + if (!invoke("seekable").__nonzero__()) { + throw tailoredIOError(msg, "seek"); + } + } + + /** + * Is the stream readable? + * + * @return true if readable + */ + public boolean readable() { + return _IOBase_readable(); + } + + @ExposedMethod(doc = readable_doc) + final boolean _IOBase_readable() { + return false; + } + + /** + * Raise an error if the underlying IO stream is not readable. + * + * @param msg optional custom message + */ + public void _checkReadable(String msg) { + _IOBase__checkReadable(msg); + } + + /** + * Raise an error if the underlying IO stream is not readable. + */ + public final void _checkReadable() { + _checkReadable(null); + } + + @ExposedMethod(defaults = "null") + final void _IOBase__checkReadable(String msg) { + if (!invoke("readable").__nonzero__()) { + throw tailoredIOError(msg, "read"); + } + } + + /** + * Is the stream writable? + * + * @return true if writable + */ + public boolean writable() { + return _IOBase_writable(); + } + + @ExposedMethod(doc = writable_doc) + final boolean _IOBase_writable() { + return false; + } + + /** + * Raise an error if the underlying IO stream is not writable. + * + * @param msg optional custom message + */ + public void _checkWritable(String msg) { + _IOBase__checkWritable(msg); + } + + /** + * Raise an error if the underlying IO stream is not writable. + */ + public final void _checkWritable() { + _checkWritable(null); + } + + @ExposedMethod(defaults = "null") + final void _IOBase__checkWritable(String msg) { + if (!invoke("writable").__nonzero__()) { + throw tailoredIOError(msg, "writ"); + } + } + + /** + * Is the stream closed against further client operations? + * + * @return true if closed + */ + public final boolean closed() { + return __closed; + } + + /** + * Raise an error if the underlying IO stream is closed. (Note opposite sense from + * {@link #_checkSeekable}, etc.. + * + * @param msg optional custom message + */ + public void _checkClosed(String msg) { + _IOBase__checkClosed(msg); + } + + public final void _checkClosed() { + _checkClosed(null); + } + + @ExposedMethod(defaults = "null") + final void _IOBase__checkClosed(String msg) { + if (closed()) { + throw Py.ValueError(msg != null ? msg : "I/O operation on closed file"); + } + } + + /** + * Called at the start of a context-managed suite (supporting the with clause). + * + * @return this object + */ + public PyObject __enter__() { + return _IOBase___enter__(); + } + + @ExposedMethod(names = {"__enter__", "__iter__"}) + final PyObject _IOBase___enter__() { + _checkClosed(); + return this; + } + + /** + * Called at the end of a context-managed suite (supporting the with clause), and + * will normally close the stream. + * + * @return false + */ + public boolean __exit__(PyObject type, PyObject value, PyObject traceback) { + return _IOBase___exit__(type, value, traceback); + } + + @ExposedMethod + final boolean _IOBase___exit__(PyObject type, PyObject value, PyObject traceback) { + invoke("close"); + return false; + } + + /** + * Return a file descriptor for the stream. A CPython file descriptor is an int, but this is not + * the natural choice in Jython, since Java has no such convention of using integers. File + * descriptors should be passed around opaquely, so their actual type is irrelevant, as long as + * (say) {@link _io#open(PyObject[], String[])} accepts the type that {@link FileIO#fileno()} + * returns. + * + * @return a file descriptor (as opaque object) + */ + public PyObject fileno() { + return _IOBase_fileno(); + } + + @ExposedMethod(doc = fileno_doc) + final PyObject _IOBase_fileno() { + throw unsupported("fileno"); + } + + /** + * Is the stream known to be an interactive console? This relies on the ability of the + * underlying stream to know, which is not always possible. + * + * @return true if a console: false if not or we can't tell + */ + public boolean isatty() { + return _IOBase_isatty(); + } + + @ExposedMethod(doc = isatty_doc) + final boolean _IOBase_isatty() { + _checkClosed(); + return false; + } + + /** + * Return one line of text (bytes terminates by '\n'), or the specified number of + * bytes, or the whole stream, whichever is shortest. + * + * @param limit maximum number of bytes (<0 means no limit) + * @return the line (or fragment) + */ + public PyObject readline(int limit) { + return _readline(limit); + } + + /** + * Return one line of text (bytes terminates by '\n'), or the whole stream, + * whichever is shorter. + * + * @return the line (or fragment) + */ + public PyObject readline() { + return _readline(-1); + } + + @ExposedMethod(defaults = "null", doc = readline_doc) + final PyObject _IOBase_readline(PyObject limit) { + if (limit == null || limit==Py.None) { + return _readline(-1); + } else if (limit.isIndex()) { + return _readline(limit.asInt()); + } else { + throw Py.TypeError("limit must be an integer"); + } + } + + private PyObject _readline(int limit) { + + // Either null, or a thing we can __call__() + PyObject peekMethod = __findattr__("peek"); + // Either an error, or a thing we can __call__() + PyObject readMethod = __getattr__("read"); + + // We'll count down from the provided limit here + int remainingLimit = (limit) >= 0 ? limit : Integer.MAX_VALUE; + + /* + * We have two almost independent versions of the implementation, depending on whether we + * have a peek() method available. + */ + if (peekMethod != null) { + /* + * This type/object has some kind of read-ahead buffer that we can scan for end of line, + * and refill as required. We are prepared to make a list of the fragments and join them + * at the end, but often there is only one. + */ + PyList list = null; + PyObject curr = Py.EmptyString; + + while (remainingLimit > 0) { + + // We hope to get away with just one pass of the loop, but if not ... + if (curr.__nonzero__()) { + // We have some previous bytes that must be added to the list + if (list == null) { + // ... be first we need a list to add them to. + list = new PyList(); + } + list.add(curr); + } + + /* + * peek() returns a str of bytes from the buffer (if any), doing at most one read to + * refill, all without advancing the pointer, or it returns None (in vacuous + * non-blocking read). + */ + PyObject peekResult = peekMethod.__call__(Py.One); + if (peekResult.__nonzero__()) { + + // Get a look at the bytes themselves + PyBuffer peekBuffer = readablePyBuffer(peekResult); + + try { + /* + * Scan forwards in the peek buffer to see if there is an end-of-line. Most + * frequently this succeeds. The number of bytes to scan is limited to the + * (remaining) limit value and the peek-buffer length. + */ + int p = 0, nr = peekBuffer.getLen(); + if (nr > remainingLimit) { + nr = remainingLimit; + } + + while (p < nr) { + if (peekBuffer.byteAt(p++) == '\n') { + remainingLimit = p; // Engineer an exit from the outer loop + break; + } + } + + /* + * The the next p bytes should be added to the line we are building, and the + * pointer advanced: that's a read(). + */ + curr = readMethod.__call__(Py.newInteger(p)); + remainingLimit -= p; + + } finally { + // We must let go of the buffer we were given + peekBuffer.release(); + } + + } else { + // peekResult was vacuous: we must be finished + curr = Py.EmptyString; + remainingLimit = 0; + } + } + + if (list == null) { + // We went through the loop at most once and the entire answer is in curr + return curr; + + } else { + // Previous reads are in the list: return them all stitched together + if (curr.__nonzero__()) { + list.add(curr); + } + return Py.EmptyString.join(list); + } + + } else { + /* + * We don't have a peek method, so we'll be reading one byte at a time. + */ + PyByteArray res = new PyByteArray(); + + while (remainingLimit > 0) { + + /* + * peek() returns a str of bytes from the buffer (if any), doing at most one read to + * refill, all without advancing the pointer, or it returns None (in vacuous + * non-blocking read). + */ + PyObject curr = readMethod.__call__(Py.One); + + if (curr.__nonzero__()) { + if (curr instanceof PyString) { + // Should be one-character string holding a byte + char c = ((PyString)curr).getString().charAt(0); + if (c == '\n') { + remainingLimit = 0; // Engineer an exit from the outer loop + } + res.append((byte)c); + } else { + String fmt = "read() should have returned a bytes object, not '%.200s'"; + throw Py.IOError(String.format(fmt, curr.getType().fastGetName())); + } + } else { + remainingLimit = 0; // Exit the outer loop + } + + } + + return res.__str__(); + } + + } + + @Override + public PyObject __iter__() { + _checkClosed(); + // Not like this, in spite of what base comment says, because file *is* an iterator + // return new PySequenceIter(this); + return this; + } + + // _IOBase___iter__ = _IOBase___enter__ + + @Override + public PyObject __iternext__() { + PyObject line = invoke("readline"); + return (!line.__nonzero__()) ? null : line; + } + + /** + * Read a stream as a sequence of lines. + * + * @param hint stop reading lines after this many bytes (if not EOF first) + * @return list containing the lines read + */ + public PyObject readlines(PyObject hint) { + return _IOBase_readlines(hint); + } + + @ExposedMethod(defaults = "null", doc = readlines_doc) + final PyObject _IOBase_readlines(PyObject hint) { + + int h = 0; + + if (hint==null || hint == Py.None) { + return new PyList(this); + + } else if (!hint.isIndex()) { + throw Py.TypeError("integer or None expected"); + + } else if ((h = hint.asIndex()) <= 0) { + return new PyList(this); + + } else { + int n = 0; + PyList lines = new PyList(); + + for (PyObject line : this.asIterable()) { + lines.append(line); + n += line.__len__(); + if (n >= h) { + break; + } + } + return lines; + } + } + + /** + * Write an iterable sequence of strings to the stream. + * + * @param lines + */ + public void writelines(PyObject lines) { + _IOBase_writelines(lines); + } + + @ExposedMethod(doc = writelines_doc) + final void _IOBase_writelines(PyObject lines) { + _checkClosed(); + // Either an error, or a thing we can __call__() + PyObject writeMethod = __getattr__("write"); + + for (PyObject line : lines.asIterable()) { + writeMethod.__call__(line); + } + } + + @Override + protected void finalize() throws Throwable { + closer.dismiss(); + invoke("close"); + super.finalize(); + } + + /** Convenience method providing the exception in the _checkWhatever() methods. */ + private static PyException tailoredIOError(String msg, String oper) { + if (msg == null) { + return Py.IOError("File or stream is not " + oper + "able."); + } else { + return Py.IOError(msg); + } + } + + /** + * Construct a PyBuffer on a given object suitable for writing to the underlying stream. The + * buffer returned will be navigable as a 1D contiguous sequence of bytes. + * + * @param obj to be wrapped and presented as a buffer + * @return a 1D contiguous PyBuffer of bytes + * @throws PyException (BufferError) if object has buffer API, but is not 1D contiguous bytes + * @throws PyException (TypeError) if object not convertible to a byte array + */ + protected static PyBuffer readablePyBuffer(PyObject obj) throws PyException { + if (obj instanceof BufferProtocol) { + return ((BufferProtocol)obj).getBuffer(PyBUF.SIMPLE); + } else { + // Something else we can view as a String? + String s; + if (obj instanceof PyUnicode) { + s = ((PyUnicode)obj).encode(); + } else if (obj instanceof PyArray) { + s = ((PyArray)obj).tostring(); + } else { + // None of the above: complain + String fmt = "object must be string or buffer, not %.100s"; + throw Py.TypeError(String.format(fmt, obj.getType().fastGetName())); + } + return new SimpleStringBuffer(PyBUF.SIMPLE, s); + } + } + + /** + * Construct a PyBuffer on a given object suitable for reading into from the underlying stream. + * The buffer returned will be navigable as a 1D contiguous writable sequence of bytes. + * + * @param obj to be wrapped and presented as a buffer + * @return a 1D contiguous PyBuffer of bytes + * @throws PyException (BufferError) if object has buffer API, but is not 1D contiguous bytes + * @throws PyException (TypeError) if object not convertible to a byte array + */ + protected static PyBuffer writablePyBuffer(PyObject obj) throws PyException { + if (obj instanceof BufferProtocol) { + return ((BufferProtocol)obj).getBuffer(PyBUF.WRITABLE); + } else { + // Can't be a buffer: complain + String fmt = "object must be read-write buffer, not %.100s"; + throw Py.TypeError(String.format(fmt, obj.getType().fastGetName())); + } + } + + /* + * Documentation strings: public where they might be useful to a subclass. + */ + public static final String seek_doc = "Change stream position.\n" + "\n" + + "Change the stream position to byte offset offset. offset is\n" + + "interpreted relative to the position indicated by whence. Values\n" + + "for whence are:\n" + "\n" + + "* 0 -- start of stream (the default); offset should be zero or positive\n" + + "* 1 -- current stream position; offset may be negative\n" + + "* 2 -- end of stream; offset is usually negative\n" + "\n" + + "Return the new absolute position."; + + public static final String tell_doc = "Return current stream position."; + + public static final String truncate_doc = "Truncate file to size bytes.\n" + "\n" + + "File pointer is left unchanged. Size defaults to the current IO\n" + + "position as reported by tell(). Returns the new size."; + + public static final String flush_doc = "Flush write buffers, if applicable.\n" + "\n" + + "This is not implemented for read-only and non-blocking streams."; + + // public static final String _flush_self_doc = + // "Flush write buffers (if any) into the downstream object\n" + // + "without flushing that object. The base implementations of\n" + // + "flush() and close() will call this method to move any\n" + // + "buffered write-data down to the next i/o object in the\n" + // + "stack before flushing or closing that downstream object.\n" + // + "A sub-class may override this method if it defines buffered\n" + // + "state. Generally sub-classes and clients should not call\n" + // + "this method directly."; + + public static final String close_doc = "Flush and close the IO object.\n" + "\n" + + "This method has no effect if the file is already closed."; + + public static final String closed_doc = "True if the stream is closed.\n"; + + public static final String seekable_doc = "Return whether object supports random access.\n" + + "\n" + "If False, seek(), tell() and truncate() will raise IOError.\n" + + "This method may need to do a test seek()."; + + public static final String readable_doc = "Return whether object was opened for reading.\n" + + "\n" + "If False, read() will raise IOError."; + + public static final String writable_doc = "Return whether object was opened for writing.\n" + + "\n" + "If False, read() will raise IOError."; + + public static final String fileno_doc = "Returns underlying file descriptor if one exists.\n" + + "\n" + "An IOError is raised if the IO object does not use a file descriptor.\n"; + + public static final String isatty_doc = "Return whether this is an 'interactive' stream.\n" + + "\n" + "Return False if it can't be determined.\n"; + + public static final String readline_doc = "Read and return a line from the stream.\n" + "\n" + + "If limit is specified, at most limit bytes will be read.\n" + "\n" + + "The line terminator is always b'\n' for binary files; for text\n" + + "files, the newlines argument to open can be used to select the line\n" + + "terminator(s) recognized.\n"; + + public static final String readlines_doc = "Return a list of lines from the stream.\n" + "\n" + + "hint can be specified to control the number of lines read: no more\n" + + "lines will be read if the total size (in bytes/characters) of all\n" + + "lines so far exceeds hint."; + + public static final String writelines_doc = + "Write a list of lines to the stream. Line separators are not added,\n" + + "so it is usual for each of the lines provided to have a line separator\n" + + "at the end."; + + static final String doc = "The abstract base class for all I/O classes, acting on streams of\n" + + "bytes. There is no public constructor.\n" + "\n" + + "This class provides dummy implementations for many methods that\n" + + "derived classes can override selectively; the default implementations\n" + + "represent a file that cannot be read, written or seeked.\n" + "\n" + + "Even though IOBase does not declare read, readinto, or write because\n" + + "their signatures will vary, implementations and clients should\n" + + "consider those methods part of the interface. Also, implementations\n" + + "may raise a IOError when operations they do not support are called.\n" + "\n" + + "The basic type used for binary data read from or written to a file is\n" + + "bytes. bytearrays are accepted too, and in some cases (such as\n" + + "readinto) needed. Text I/O classes work with str data.\n" + "\n" + + "Note that calling any method (even inquiries) on a closed stream is\n" + + "undefined. Implementations may raise IOError in this case.\n" + "\n" + + "IOBase (and its subclasses) support the iterator protocol, meaning\n" + + "that an IOBase object can be iterated over yielding the lines in a\n" + "stream.\n" + + "\n" + "IOBase also supports the :keyword:`with` statement. In this example,\n" + + "fp is closed after the suite of the with statement is complete:\n" + "\n" + + "with open('spam.txt', 'r') as fp:\n" + " fp.write('Spam and eggs!')\n"; + +} diff --git a/src/org/python/modules/_io/PyIOBaseDerived.java b/src/org/python/modules/_io/PyIOBaseDerived.java new file mode 100644 --- /dev/null +++ b/src/org/python/modules/_io/PyIOBaseDerived.java @@ -0,0 +1,1538 @@ +/* Generated file, do not modify. See jython/src/templates/gderived.py. */ +package org.python.modules._io; + +import java.io.Serializable; + +import org.python.core.Deriveds; +import org.python.core.Py; +import org.python.core.PyBoolean; +import org.python.core.PyComplex; +import org.python.core.PyException; +import org.python.core.PyFloat; +import org.python.core.PyInteger; +import org.python.core.PyLong; +import org.python.core.PyObject; +import org.python.core.PySequenceIter; +import org.python.core.PySlice; +import org.python.core.PyString; +import org.python.core.PyTuple; +import org.python.core.PyType; +import org.python.core.PyUnicode; +import org.python.core.Slotted; +import org.python.core.ThreadState; + +public class PyIOBaseDerived extends PyIOBase implements Slotted { + + @Override + public PyObject getSlot(int index) { + return slots[index]; + } + + @Override + public void setSlot(int index, PyObject value) { + slots[index] = value; + } + + private PyObject[] slots; + + public PyIOBaseDerived(PyType subtype) { + super(subtype); + slots = new PyObject[subtype.getNumSlots()]; + } + + @Override + public PyString __str__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__str__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(); + if (res instanceof PyString) { + return (PyString)res; + } + throw Py.TypeError("__str__" + " returned non-" + "string" + " (type " + + res.getType().fastGetName() + ")"); + } + return super.__str__(); + } + + @Override + public PyString __repr__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__repr__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(); + if (res instanceof PyString) { + return (PyString)res; + } + throw Py.TypeError("__repr__" + " returned non-" + "string" + " (type " + + res.getType().fastGetName() + ")"); + } + return super.__repr__(); + } + + @Override + public PyString __hex__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__hex__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(); + if (res instanceof PyString) { + return (PyString)res; + } + throw Py.TypeError("__hex__" + " returned non-" + "string" + " (type " + + res.getType().fastGetName() + ")"); + } + return super.__hex__(); + } + + @Override + public PyString __oct__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__oct__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(); + if (res instanceof PyString) { + return (PyString)res; + } + throw Py.TypeError("__oct__" + " returned non-" + "string" + " (type " + + res.getType().fastGetName() + ")"); + } + return super.__oct__(); + } + + @Override + public PyFloat __float__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__float__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(); + if (res instanceof PyFloat) { + return (PyFloat)res; + } + throw Py.TypeError("__float__" + " returned non-" + "float" + " (type " + + res.getType().fastGetName() + ")"); + } + return super.__float__(); + } + + @Override + public PyComplex __complex__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__complex__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(); + if (res instanceof PyComplex) { + return (PyComplex)res; + } + throw Py.TypeError("__complex__" + " returned non-" + "complex" + " (type " + + res.getType().fastGetName() + ")"); + } + return super.__complex__(); + } + + @Override + public PyObject __pos__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__pos__"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(); + } + return super.__pos__(); + } + + @Override + public PyObject __neg__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__neg__"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(); + } + return super.__neg__(); + } + + @Override + public PyObject __abs__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__abs__"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(); + } + return super.__abs__(); + } + + @Override + public PyObject __invert__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__invert__"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(); + } + return super.__invert__(); + } + + @Override + public PyObject __reduce__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__reduce__"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(); + } + return super.__reduce__(); + } + + @Override + public PyObject __dir__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__dir__"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(); + } + return super.__dir__(); + } + + @Override + public PyObject __add__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__add__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__add__(other); + } + + @Override + public PyObject __radd__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__radd__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__radd__(other); + } + + @Override + public PyObject __sub__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__sub__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__sub__(other); + } + + @Override + public PyObject __rsub__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__rsub__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__rsub__(other); + } + + @Override + public PyObject __mul__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__mul__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__mul__(other); + } + + @Override + public PyObject __rmul__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__rmul__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__rmul__(other); + } + + @Override + public PyObject __div__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__div__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__div__(other); + } + + @Override + public PyObject __rdiv__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__rdiv__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__rdiv__(other); + } + + @Override + public PyObject __floordiv__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__floordiv__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__floordiv__(other); + } + + @Override + public PyObject __rfloordiv__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__rfloordiv__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__rfloordiv__(other); + } + + @Override + public PyObject __truediv__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__truediv__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__truediv__(other); + } + + @Override + public PyObject __rtruediv__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__rtruediv__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__rtruediv__(other); + } + + @Override + public PyObject __mod__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__mod__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__mod__(other); + } + + @Override + public PyObject __rmod__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__rmod__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__rmod__(other); + } + + @Override + public PyObject __divmod__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__divmod__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__divmod__(other); + } + + @Override + public PyObject __rdivmod__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__rdivmod__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__rdivmod__(other); + } + + @Override + public PyObject __rpow__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__rpow__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__rpow__(other); + } + + @Override + public PyObject __lshift__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__lshift__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__lshift__(other); + } + + @Override + public PyObject __rlshift__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__rlshift__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__rlshift__(other); + } + + @Override + public PyObject __rshift__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__rshift__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__rshift__(other); + } + + @Override + public PyObject __rrshift__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__rrshift__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__rrshift__(other); + } + + @Override + public PyObject __and__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__and__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__and__(other); + } + + @Override + public PyObject __rand__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__rand__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__rand__(other); + } + + @Override + public PyObject __or__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__or__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__or__(other); + } + + @Override + public PyObject __ror__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__ror__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__ror__(other); + } + + @Override + public PyObject __xor__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__xor__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__xor__(other); + } + + @Override + public PyObject __rxor__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__rxor__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__rxor__(other); + } + + @Override + public PyObject __lt__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__lt__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__lt__(other); + } + + @Override + public PyObject __le__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__le__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__le__(other); + } + + @Override + public PyObject __gt__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__gt__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__gt__(other); + } + + @Override + public PyObject __ge__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__ge__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__ge__(other); + } + + @Override + public PyObject __eq__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__eq__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__eq__(other); + } + + @Override + public PyObject __ne__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__ne__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__ne__(other); + } + + @Override + public PyObject __format__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__format__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__format__(other); + } + + @Override + public PyObject __iadd__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__iadd__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__iadd__(other); + } + + @Override + public PyObject __isub__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__isub__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__isub__(other); + } + + @Override + public PyObject __imul__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__imul__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__imul__(other); + } + + @Override + public PyObject __idiv__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__idiv__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__idiv__(other); + } + + @Override + public PyObject __ifloordiv__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__ifloordiv__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__ifloordiv__(other); + } + + @Override + public PyObject __itruediv__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__itruediv__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__itruediv__(other); + } + + @Override + public PyObject __imod__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__imod__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__imod__(other); + } + + @Override + public PyObject __ipow__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__ipow__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__ipow__(other); + } + + @Override + public PyObject __ilshift__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__ilshift__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__ilshift__(other); + } + + @Override + public PyObject __irshift__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__irshift__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__irshift__(other); + } + + @Override + public PyObject __iand__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__iand__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__iand__(other); + } + + @Override + public PyObject __ior__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__ior__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__ior__(other); + } + + @Override + public PyObject __ixor__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__ixor__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__ixor__(other); + } + + @Override + public PyObject __int__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__int__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(); + if (res instanceof PyInteger || res instanceof PyLong) { + return res; + } + throw Py.TypeError("__int__" + " should return an integer"); + } + return super.__int__(); + } + + @Override + public PyObject __long__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__long__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(); + if (res instanceof PyLong || res instanceof PyInteger) { + return res; + } + throw Py.TypeError("__long__" + " returned non-" + "long" + " (type " + + res.getType().fastGetName() + ")"); + } + return super.__long__(); + } + + @Override + public int hashCode() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__hash__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(); + if (res instanceof PyInteger) { + return ((PyInteger)res).getValue(); + } else if (res instanceof PyLong) { + return ((PyLong)res).getValue().intValue(); + } + throw Py.TypeError("__hash__ should return a int"); + } + if (self_type.lookup("__eq__") != null || self_type.lookup("__cmp__") != null) { + throw Py.TypeError(String.format("unhashable type: '%.200s'", getType().fastGetName())); + } + return super.hashCode(); + } + + @Override + public PyUnicode __unicode__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__unicode__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(); + if (res instanceof PyUnicode) { + return (PyUnicode)res; + } + if (res instanceof PyString) { + return new PyUnicode((PyString)res); + } + throw Py.TypeError("__unicode__" + " should return a " + "unicode"); + } + return super.__unicode__(); + } + + @Override + public int __cmp__(PyObject other) { + PyType self_type = getType(); + PyObject[] where_type = new PyObject[1]; + PyObject impl = self_type.lookup_where("__cmp__", where_type); + // Full Compatibility with CPython __cmp__: + // If the derived type don't override __cmp__, the + // *internal* super().__cmp__ should be called, not the + // exposed one. The difference is that the exposed __cmp__ + // throws a TypeError if the argument is an instance of the same type. + if (impl == null || where_type[0] == TYPE || Py.isSubClass(TYPE, where_type[0])) { + return super.__cmp__(other); + } + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return -2; + } + int c = res.asInt(); + return c < 0 ? -1 : c > 0 ? 1 : 0; + } + + @Override + public boolean __nonzero__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__nonzero__"); + if (impl == null) { + impl = self_type.lookup("__len__"); + if (impl == null) { + return super.__nonzero__(); + } + } + PyObject o = impl.__get__(this, self_type).__call__(); + Class c = o.getClass(); + if (c != PyInteger.class && c != PyBoolean.class) { + throw Py.TypeError(String.format("__nonzero__ should return bool or int, returned %s", + self_type.getName())); + } + return o.__nonzero__(); + } + + @Override + public boolean __contains__(PyObject o) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__contains__"); + if (impl == null) { + return super.__contains__(o); + } + return impl.__get__(this, self_type).__call__(o).__nonzero__(); + } + + @Override + public int __len__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__len__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(); + if (res instanceof PyInteger) { + return ((PyInteger)res).getValue(); + } + throw Py.TypeError("__len__ should return a int"); + } + return super.__len__(); + } + + @Override + public PyObject __iter__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__iter__"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(); + } + impl = self_type.lookup("__getitem__"); + if (impl == null) { + return super.__iter__(); + } + return new PySequenceIter(this); + } + + @Override + public PyObject __iternext__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("next"); + if (impl != null) { + try { + return impl.__get__(this, self_type).__call__(); + } catch (PyException exc) { + if (exc.match(Py.StopIteration)) { + return null; + } + throw exc; + } + } + return super.__iternext__(); // ??? + } + + @Override + public PyObject __finditem__(PyObject key) { // ??? + PyType self_type = getType(); + PyObject impl = self_type.lookup("__getitem__"); + if (impl != null) { + try { + return impl.__get__(this, self_type).__call__(key); + } catch (PyException exc) { + if (exc.match(Py.LookupError)) { + return null; + } + throw exc; + } + } + return super.__finditem__(key); + } + + @Override + public PyObject __finditem__(int key) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__getitem__"); + if (impl != null) { + try { + return impl.__get__(this, self_type).__call__(new PyInteger(key)); + } catch (PyException exc) { + if (exc.match(Py.LookupError)) { + return null; + } + throw exc; + } + } + return super.__finditem__(key); + } + + @Override + public PyObject __getitem__(PyObject key) { + // Same as __finditem__, without swallowing LookupErrors. This allows + // __getitem__ implementations written in Python to raise custom + // exceptions (such as subclasses of KeyError). + // + // We are forced to duplicate the code, instead of defining __finditem__ + // in terms of __getitem__. That's because PyObject defines __getitem__ + // in terms of __finditem__. Therefore, we would end with an infinite + // loop when self_type.lookup("__getitem__") returns null: + // + // __getitem__ -> super.__getitem__ -> __finditem__ -> __getitem__ + // + // By duplicating the (short) lookup and call code, we are safe, because + // the call chains will be: + // + // __finditem__ -> super.__finditem__ + // + // __getitem__ -> super.__getitem__ -> __finditem__ -> super.__finditem__ + + PyType self_type = getType(); + PyObject impl = self_type.lookup("__getitem__"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(key); + } + return super.__getitem__(key); + } + + @Override + public void __setitem__(PyObject key, PyObject value) { // ??? + PyType self_type = getType(); + PyObject impl = self_type.lookup("__setitem__"); + if (impl != null) { + impl.__get__(this, self_type).__call__(key, value); + return; + } + super.__setitem__(key, value); + } + + @Override + public PyObject __getslice__(PyObject start, PyObject stop, PyObject step) { // ??? + if (step != null) { + return __getitem__(new PySlice(start, stop, step)); + } + PyType self_type = getType(); + PyObject impl = self_type.lookup("__getslice__"); + if (impl != null) { + PyObject[] indices = PySlice.indices2(this, start, stop); + return impl.__get__(this, self_type).__call__(indices[0], indices[1]); + } + return super.__getslice__(start, stop, step); + } + + @Override + public void __setslice__(PyObject start, PyObject stop, PyObject step, PyObject value) { + if (step != null) { + __setitem__(new PySlice(start, stop, step), value); + return; + } + PyType self_type = getType(); + PyObject impl = self_type.lookup("__setslice__"); + if (impl != null) { + PyObject[] indices = PySlice.indices2(this, start, stop); + impl.__get__(this, self_type).__call__(indices[0], indices[1], value); + return; + } + super.__setslice__(start, stop, step, value); + } + + @Override + public void __delslice__(PyObject start, PyObject stop, PyObject step) { + if (step != null) { + __delitem__(new PySlice(start, stop, step)); + return; + } + PyType self_type = getType(); + PyObject impl = self_type.lookup("__delslice__"); + if (impl != null) { + PyObject[] indices = PySlice.indices2(this, start, stop); + impl.__get__(this, self_type).__call__(indices[0], indices[1]); + return; + } + super.__delslice__(start, stop, step); + } + + @Override + public void __delitem__(PyObject key) { // ??? + PyType self_type = getType(); + PyObject impl = self_type.lookup("__delitem__"); + if (impl != null) { + impl.__get__(this, self_type).__call__(key); + return; + } + super.__delitem__(key); + } + + @Override + public PyObject __call__(PyObject args[], String keywords[]) { + ThreadState ts = Py.getThreadState(); + if (ts.recursion_depth++ > ts.systemState.getrecursionlimit()) { + throw Py.RuntimeError("maximum __call__ recursion depth exceeded"); + } + try { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__call__"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(args, keywords); + } + return super.__call__(args, keywords); + } finally { + --ts.recursion_depth; + } + } + + @Override + public PyObject __findattr_ex__(String name) { + return Deriveds.__findattr_ex__(this, name); + } + + @Override + public void __setattr__(String name, PyObject value) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__setattr__"); + if (impl != null) { + impl.__get__(this, self_type).__call__(PyString.fromInterned(name), value); + return; + } + super.__setattr__(name, value); + } + + @Override + public void __delattr__(String name) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__delattr__"); + if (impl != null) { + impl.__get__(this, self_type).__call__(PyString.fromInterned(name)); + return; + } + super.__delattr__(name); + } + + @Override + public PyObject __get__(PyObject obj, PyObject type) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__get__"); + if (impl != null) { + if (obj == null) { + obj = Py.None; + } + if (type == null) { + type = Py.None; + } + return impl.__get__(this, self_type).__call__(obj, type); + } + return super.__get__(obj, type); + } + + @Override + public void __set__(PyObject obj, PyObject value) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__set__"); + if (impl != null) { + impl.__get__(this, self_type).__call__(obj, value); + return; + } + super.__set__(obj, value); + } + + @Override + public void __delete__(PyObject obj) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__delete__"); + if (impl != null) { + impl.__get__(this, self_type).__call__(obj); + return; + } + super.__delete__(obj); + } + + @Override + public PyObject __pow__(PyObject other, PyObject modulo) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__pow__"); + if (impl != null) { + PyObject res; + if (modulo == null) { + res = impl.__get__(this, self_type).__call__(other); + } else { + res = impl.__get__(this, self_type).__call__(other, modulo); + } + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__pow__(other, modulo); + } + + @Override + public void dispatch__init__(PyObject[] args, String[] keywords) { + Deriveds.dispatch__init__(this, args, keywords); + } + + @Override + public PyObject __index__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__index__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(); + if (res instanceof PyInteger || res instanceof PyLong) { + return res; + } + throw Py.TypeError(String.format("__index__ returned non-(int,long) (type %s)", res + .getType().fastGetName())); + } + return super.__index__(); + } + + @Override + public Object __tojava__(Class c) { + // If we are not being asked by the "default" conversion to java, then + // we can provide this as the result, as long as it is a instance of the + // specified class. Without this, derived.__tojava__(PyObject.class) + // would broke. (And that's not pure speculation: PyReflectedFunction's + // ReflectedArgs asks for things like that). + if ((c != Object.class) && (c != Serializable.class) && (c.isInstance(this))) { + return this; + } + // Otherwise, we call the derived __tojava__, if it exists: + PyType self_type = getType(); + PyObject impl = self_type.lookup("__tojava__"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(Py.java2py(c)).__tojava__(Object.class); + } + return super.__tojava__(c); + } + + @Override + public Object __coerce_ex__(PyObject o) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__coerce__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(o); + if (res == Py.NotImplemented) { + return Py.None; + } + if (!(res instanceof PyTuple)) { + throw Py.TypeError("__coerce__ didn't return a 2-tuple"); + } + return ((PyTuple)res).getArray(); + } + return super.__coerce_ex__(o); + } + + @Override + public PyObject __enter__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__enter__"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(); + } + return super.__enter__(); + } + + @Override + public PyObject fileno() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("fileno"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(); + } + return super.fileno(); + } + + // Hand-crafted in _io._IOBase.derived + + @Override + public long seek(long pos, int whence) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("seek"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(Py.newLong(pos), Py.newInteger(whence)) + .asLong(); + } else { + return super.seek(pos, whence); + } + } + + @Override + public long tell() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("tell"); + if (impl != null) { + return impl.__get__(this, self_type).__call__().asLong(); + } else { + return super.tell(); + } + } + + @Override + public long truncate(long size) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("truncate"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(Py.newLong(size)).asLong(); + } else { + return super.truncate(size); + } + } + + @Override + public long truncate() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("truncate"); + if (impl != null) { + return impl.__get__(this, self_type).__call__().asLong(); + } else { + return super.truncate(); + } + } + + @Override + public void flush() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("flush"); + if (impl != null) { + impl.__get__(this, self_type).__call__(); + } else { + super.flush(); + } + } + + @Override + public void close() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("close"); + if (impl != null) { + impl.__get__(this, self_type).__call__(); + } else { + super.close(); + } + } + + @Override + public boolean seekable() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("seekable"); + if (impl != null) { + return impl.__get__(this, self_type).__call__().__nonzero__(); + } else { + return super.seekable(); + } + } + + @Override + public void _checkSeekable(String msg) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("_checkSeekable"); + if (impl != null) { + PyObject pymsg = msg == null ? Py.None : new PyString(msg); + impl.__get__(this, self_type).__call__(pymsg); + } else { + super._checkSeekable(msg); + } + } + + @Override + public boolean readable() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("readable"); + if (impl != null) { + return impl.__get__(this, self_type).__call__().__nonzero__(); + } else { + return super.readable(); + } + } + + @Override + public void _checkReadable(String msg) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("_checkReadable"); + if (impl != null) { + PyObject pymsg = msg == null ? Py.None : new PyString(msg); + impl.__get__(this, self_type).__call__(pymsg); + } else { + super._checkReadable(msg); + } + } + + @Override + public boolean writable() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("writable"); + if (impl != null) { + return impl.__get__(this, self_type).__call__().__nonzero__(); + } else { + return super.writable(); + } + } + + @Override + public void _checkWritable(String msg) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("_checkWritable"); + if (impl != null) { + PyObject pymsg = msg == null ? Py.None : new PyString(msg); + impl.__get__(this, self_type).__call__(pymsg); + } else { + super._checkWritable(msg); + } + } + + // Note that closed is a property not a predicate, so no derived method. + + @Override + public void _checkClosed(String msg) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("_checkClosed"); + if (impl != null) { + PyObject pymsg = msg == null ? Py.None : new PyString(msg); + impl.__get__(this, self_type).__call__(pymsg); + } else { + super._checkClosed(msg); + } + } + + @Override + public boolean __exit__(PyObject type, PyObject value, PyObject traceback) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__exit__"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(type, value, traceback).__nonzero__(); + } else { + return super.__exit__(type, value, traceback); + } + } + + @Override + public boolean isatty() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("isatty"); + if (impl != null) { + return impl.__get__(this, self_type).__call__().__nonzero__(); + } else { + return super.isatty(); + } + } + + @Override + public PyObject readline() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("readline"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(Py.None); + } else { + return super.readline(); + } + } + + @Override + public PyObject readline(int limit) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("readline"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(Py.newInteger(limit)); + } else { + return super.readline(limit); + } + } + + @Override + public PyObject readlines(PyObject hint) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("readlines"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(hint); + return res; + } else { + return super.readlines(hint); + } + } + + @Override + public void writelines(PyObject lines) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("writelines"); + if (impl != null) { + impl.__get__(this, self_type).__call__(lines); + } else { + super.writelines(lines); + } + } + +} diff --git a/src/org/python/modules/_io/_io.java b/src/org/python/modules/_io/_io.java --- a/src/org/python/modules/_io/_io.java +++ b/src/org/python/modules/_io/_io.java @@ -29,6 +29,8 @@ dict.__setitem__("__name__", new PyString("_io")); dict.__setitem__("__doc__", new PyString(__doc__)); dict.__setitem__("DEFAULT_BUFFER_SIZE", DEFAULT_BUFFER_SIZE); + + dict.__setitem__("_IOBase", PyIOBase.TYPE); dict.__setitem__("FileIO", PyFileIO.TYPE); // Define UnsupportedOperation exception by constructing the type diff --git a/src/templates/_io._IOBase.derived b/src/templates/_io._IOBase.derived new file mode 100644 --- /dev/null +++ b/src/templates/_io._IOBase.derived @@ -0,0 +1,210 @@ +base_class: PyIOBase +want_dict: false +ctr: +no_toString: true +incl: object +unary1: __enter__ +unary1: fileno + +rest: + // Hand-crafted in _io._IOBase.derived + + public long seek(long pos, int whence) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("seek"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(Py.newLong(pos), Py.newInteger(whence)) + .asLong(); + } else { + return super.seek(pos, whence); + } + } + + public long tell() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("tell"); + if (impl != null) { + return impl.__get__(this, self_type).__call__().asLong(); + } else { + return super.tell(); + } + } + + public long truncate(long size) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("truncate"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(Py.newLong(size)).asLong(); + } else { + return super.truncate(size); + } + } + + public long truncate() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("truncate"); + if (impl != null) { + return impl.__get__(this, self_type).__call__().asLong(); + } else { + return super.truncate(); + } + } + + public void flush() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("flush"); + if (impl != null) { + impl.__get__(this, self_type).__call__(); + } else { + super.flush(); + } + } + + public void close() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("close"); + if (impl != null) { + impl.__get__(this, self_type).__call__(); + } else { + super.close(); + } + } + + public boolean seekable() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("seekable"); + if (impl != null) { + return impl.__get__(this, self_type).__call__().__nonzero__(); + } else { + return super.seekable(); + } + } + + public void _checkSeekable(String msg) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("_checkSeekable"); + if (impl != null) { + PyObject pymsg = msg == null ? Py.None : new PyString(msg); + impl.__get__(this, self_type).__call__(pymsg); + } else { + super._checkSeekable(msg); + } + } + + public boolean readable() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("readable"); + if (impl != null) { + return impl.__get__(this, self_type).__call__().__nonzero__(); + } else { + return super.readable(); + } + } + + public void _checkReadable(String msg) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("_checkReadable"); + if (impl != null) { + PyObject pymsg = msg == null ? Py.None : new PyString(msg); + impl.__get__(this, self_type).__call__(pymsg); + } else { + super._checkReadable(msg); + } + } + + public boolean writable() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("writable"); + if (impl != null) { + return impl.__get__(this, self_type).__call__().__nonzero__(); + } else { + return super.writable(); + } + } + + public void _checkWritable(String msg) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("_checkWritable"); + if (impl != null) { + PyObject pymsg = msg == null ? Py.None : new PyString(msg); + impl.__get__(this, self_type).__call__(pymsg); + } else { + super._checkWritable(msg); + } + } + + // Note that closed is a property not a predicate, so no derived method. + + public void _checkClosed(String msg) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("_checkClosed"); + if (impl != null) { + PyObject pymsg = msg == null ? Py.None : new PyString(msg); + impl.__get__(this, self_type).__call__(pymsg); + } else { + super._checkClosed(msg); + } + } + + public boolean __exit__(PyObject type, PyObject value, PyObject traceback) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__exit__"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(type, value, traceback).__nonzero__(); + } else { + return super.__exit__(type, value, traceback); + } + } + + public boolean isatty() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("isatty"); + if (impl != null) { + return impl.__get__(this, self_type).__call__().__nonzero__(); + } else { + return super.isatty(); + } + } + + public PyObject readline() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("readline"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(Py.None); + } else { + return super.readline(); + } + } + + public PyObject readline(int limit) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("readline"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(Py.newInteger(limit)); + } else { + return super.readline(limit); + } + } + + public PyObject readlines(PyObject hint) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("readlines"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(hint); + return res; + } else { + return super.readlines(hint); + } + } + + public void writelines(PyObject lines) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("writelines"); + if (impl != null) { + impl.__get__(this, self_type).__call__(lines); + } else { + super.writelines(lines); + } + } + + diff --git a/src/templates/mappings b/src/templates/mappings --- a/src/templates/mappings +++ b/src/templates/mappings @@ -6,6 +6,7 @@ # will map str.expose to org.python.core.PyString. Many classes will have 2 # lines; one for their expose, and one for their derived class. +_io._IOBase.derived:org.python.modules._io.PyIOBaseDerived BaseException.derived:org.python.core.PyBaseExceptionDerived ClasspathPyImporter.derived:org.python.core.ClasspathPyImporterDerived PyFileIO.derived:org.python.modules._io.PyFileIODerived -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Sun Dec 9 21:29:42 2012 From: jython-checkins at python.org (jeff.allen) Date: Sun, 9 Dec 2012 21:29:42 +0100 (CET) Subject: [Jython-checkins] =?utf-8?q?jython=3A_Implement_=5Fio=2E=5FRawIOB?= =?utf-8?q?ase_in_Java?= Message-ID: <3YKJtZ5HdQzRWS@mail.python.org> http://hg.python.org/jython/rev/3782427e3a6d changeset: 6894:3782427e3a6d user: Jeff Allen date: Sat Dec 08 18:02:19 2012 +0000 summary: Implement _io._RawIOBase in Java There is now an implementation in org.python.modules._io.PyRawIOBase.java. Score in test_io.py is unchanged at fail/error/skip = 12/58/99. files: CoreExposed.includes | 1 + Lib/_jyio.py | 66 +- src/org/python/modules/_io/PyRawIOBase.java | 206 + src/org/python/modules/_io/PyRawIOBaseDerived.java | 1583 ++++++++++ src/org/python/modules/_io/_io.java | 1 + src/templates/_io._RawIOBase.derived | 39 + src/templates/mappings | 3 +- 7 files changed, 1833 insertions(+), 66 deletions(-) diff --git a/CoreExposed.includes b/CoreExposed.includes --- a/CoreExposed.includes +++ b/CoreExposed.includes @@ -60,6 +60,7 @@ org/python/modules/_csv/PyReader.class org/python/modules/_csv/PyWriter.class org/python/modules/_io/PyIOBase.class +org/python/modules/_io/PyRawIOBase.class org/python/modules/_io/PyFileIO.class org/python/modules/_functools/PyPartial.class org/python/modules/_hashlib$Hash.class diff --git a/Lib/_jyio.py b/Lib/_jyio.py --- a/Lib/_jyio.py +++ b/Lib/_jyio.py @@ -57,71 +57,7 @@ self.characters_written = characters_written -from _io import (open, UnsupportedOperation, _IOBase) - - -class _RawIOBase(_IOBase): - - """Base class for raw binary I/O.""" - - # The read() method is implemented by calling readinto(); derived - # classes that want to support read() only need to implement - # readinto() as a primitive operation. In general, readinto() can be - # more efficient than read(). - - # (It would be tempting to also provide an implementation of - # readinto() in terms of read(), in case the latter is a more suitable - # primitive operation, but that would lead to nasty recursion in case - # a subclass doesn't implement either.) - - def read(self, n=-1): - """Read and return up to n bytes. - - Returns an empty bytes object on EOF, or None if the object is - set not to block and has no data to read. - """ - if n is None: - n = -1 - if n < 0: - return self.readall() - b = bytearray(n.__index__()) - n = self.readinto(b) - if n is None: - return None - del b[n:] - return bytes(b) - - def readall(self): - """Read until EOF, using multiple read() call.""" - res = bytearray() - while True: - data = self.read(DEFAULT_BUFFER_SIZE) - if not data: - break - res += data - if res: - return bytes(res) - else: - # b'' or None - return data - - def readinto(self, b): - """Read up to len(b) bytes into b. - - Returns number of bytes read (0 for EOF), or None if the object - is set not to block and has no data to read. - """ - self._unsupported("readinto") - - def write(self, b): - """Write the given buffer to the IO stream. - - Returns the number of bytes written, which may be less than len(b). - """ - self._unsupported("write") - - -from _io import FileIO +from _io import (open, UnsupportedOperation, _IOBase, _RawIOBase, FileIO) class _BufferedIOBase(_IOBase): diff --git a/src/org/python/modules/_io/PyRawIOBase.java b/src/org/python/modules/_io/PyRawIOBase.java new file mode 100644 --- /dev/null +++ b/src/org/python/modules/_io/PyRawIOBase.java @@ -0,0 +1,206 @@ +/* Copyright (c)2012 Jython Developers */ +package org.python.modules._io; + +import org.python.core.Py; +import org.python.core.PyBUF; +import org.python.core.PyBuffer; +import org.python.core.PyByteArray; +import org.python.core.PyList; +import org.python.core.PyLong; +import org.python.core.PyNewWrapper; +import org.python.core.PyObject; +import org.python.core.PyString; +import org.python.core.PyType; +import org.python.expose.ExposedMethod; +import org.python.expose.ExposedNew; +import org.python.expose.ExposedType; + +/** + * An implementation of Python _io._RawIOBase mirroring the arrangement of methods in + * the CPython version. + */ + at ExposedType(name = "_io._RawIOBase", doc = PyRawIOBase.doc, base = PyIOBase.class) +public class PyRawIOBase extends PyIOBase { + + public static final PyType TYPE = PyType.fromClass(PyRawIOBase.class); + + public PyRawIOBase() { + this(TYPE); + } + + public PyRawIOBase(PyType subtype) { + super(subtype); + } + + @ExposedNew + static PyObject _RawIOBase__new__(PyNewWrapper new_, boolean init, PyType subtype, + PyObject[] args, String[] keywords) { + if (new_.for_type == subtype) { + // We only want an _io._RawIOBase, so the constructor does it all + return new PyRawIOBase(); + } else { + // We want some sub-class of it (in which __init__ will be called by the caller) + return new PyRawIOBaseDerived(subtype); + } + } + + /** + * The read() method is implemented by calling readinto(); derived classes that want to support + * read() only need to implement readinto() as a primitive operation. In general, readinto() can + * be more efficient than read(). + * + * @param n number of bytes to read (if possible) + * @return a PyString holding the bytes read or Py.None (when a non-blocking source + * is not ready with further data) + */ + public PyObject read(int n) { + return _RawIOBase_read(n); + } + + /* + * CPython comment: (It would be tempting to also provide an implementation of readinto() in + * terms of read(), in case the latter is a more suitable primitive operation, but that would + * lead to nasty recursion in case a subclass doesn't implement either.) + */ + @ExposedMethod(defaults = "-1", doc = read_doc) + final PyObject _RawIOBase_read(int n) { + + if (n < 0) { + // This is really a request to read the whole stream + return invoke("readall"); + + } else { + // Allocate a buffer big enough to satisfy the request + PyByteArray b = new PyByteArray(n); + + // Read up to that much using the (possibly overridden) readinto() method + PyObject m = invoke("readinto", b); + + if (m.isIndex()) { + // It returned the actual count of bytes read + int count = m.asIndex(); + PyBuffer view = b.getBuffer(PyBUF.FULL_RO); + // We can forget view.release() as the bytearray b is garbage outside this method. + + // Did we get all the bytes we expected? + if (count < n) { + // No, so swap the view for a slice of just the data we actually read. + view = view.getBufferSlice(PyBUF.FULL_RO, 0, count); + } + + // Make a str from that view + return new PyString(view.toString()); + + } else { + // It must have returned None (signalling a vacuous read of non-blocking stream) + return m; + } + + } + } + + /** + * Read until end of file, using multiple read() operations on the underlying + * stream. If the first read() returns None (only possible in the case + * of a non-blocking stream), this method returns None. + * + * @return a PyString holding the bytes read or Py.None (when a non-blocking source + * is not ready with further data) + */ + public PyObject readall() { + return _RawIOBase_readall(); + } + + @ExposedMethod(doc = readall_doc) + final synchronized PyObject _RawIOBase_readall() { + + // Get reference to the (possibly overridden) read() method + PyObject readMethod = __getattr__("read"); + + // Quite often, a single read operation will do the trick + PyObject prev = readMethod.__call__(_io.DEFAULT_BUFFER_SIZE); + + if (!prev.__nonzero__()) { + // Nothing on the first read: that means we're done + return prev; + + } else { + // Try a second read + PyObject curr = readMethod.__call__(_io.DEFAULT_BUFFER_SIZE); + if (!curr.__nonzero__()) { + // Nothing on the second read: the result is just the first one + return prev; + + } else { + // Remembering more than one thing is hard: we're going to need a list + PyList list = new PyList(); + list.add(prev); + + // Accumulate the current read result and get another, until we run out of bytes. + do { + list.add(curr); + curr = readMethod.__call__(_io.DEFAULT_BUFFER_SIZE); + } while (curr.__nonzero__()); + + // Stitch it all together + return Py.EmptyString.join(list); + } + } + } + + /** + * Read up to len(b) bytes into bytearray b and return the number of + * bytes read. If the object is in non-blocking mode and no bytes are available, + * None is returned."; + * + * @param b byte array to try to fill + * @return number of bytes actually read or Py.None (when a non-blocking source is + * not ready with further data) + */ + public PyObject readinto(PyObject b) { + return _RawIOBase_readinto(b); + } + + @ExposedMethod(doc = readinto_doc) + final synchronized PyLong _RawIOBase_readinto(PyObject b) { + throw unsupported("readinto"); + } + + /** + * Write the given bytes or bytearray object to the underlying raw stream and return the number + * of bytes written. + * + * @param b buffer of bytes to be written + * @return + */ + public PyObject write(PyObject b) { + return _RawIOBase_write(b); + } + + @ExposedMethod(doc = write_doc) + final PyLong _RawIOBase_write(PyObject b) { + throw unsupported("write"); + } + + /* + * Documentation strings: public where they might be useful to a subclass. + */ + public static final String read_doc = "Read up to n bytes from the object and return them.\n" + + "As a convenience, if n is unspecified or -1, readall() is called."; + + public static final String readall_doc = + "Read and return all the bytes from the stream until EOF, using multiple\n" + + "calls to the stream if necessary."; + + public static final String readinto_doc = + "Read up to len(b) bytes into bytearray b and return the number of bytes read.\n" + + "If the object is in non-blocking mode and no bytes are available,\n" + + "None is returned."; + + public static final String write_doc = + "Write the given bytes or bytearray object, b, to the underlying raw\n" + + "stream and return the number of bytes written."; + + static final String doc = "Base class for raw binary I/O."; + +} diff --git a/src/org/python/modules/_io/PyRawIOBaseDerived.java b/src/org/python/modules/_io/PyRawIOBaseDerived.java new file mode 100644 --- /dev/null +++ b/src/org/python/modules/_io/PyRawIOBaseDerived.java @@ -0,0 +1,1583 @@ +/* Generated file, do not modify. See jython/src/templates/gderived.py. */ +package org.python.modules._io; + +import java.io.Serializable; + +import org.python.core.Deriveds; +import org.python.core.Py; +import org.python.core.PyBoolean; +import org.python.core.PyComplex; +import org.python.core.PyException; +import org.python.core.PyFloat; +import org.python.core.PyInteger; +import org.python.core.PyLong; +import org.python.core.PyObject; +import org.python.core.PySequenceIter; +import org.python.core.PySlice; +import org.python.core.PyString; +import org.python.core.PyTuple; +import org.python.core.PyType; +import org.python.core.PyUnicode; +import org.python.core.Slotted; +import org.python.core.ThreadState; + +public class PyRawIOBaseDerived extends PyRawIOBase implements Slotted { + + @Override + public PyObject getSlot(int index) { + return slots[index]; + } + + @Override + public void setSlot(int index, PyObject value) { + slots[index] = value; + } + + private PyObject[] slots; + + public PyRawIOBaseDerived(PyType subtype) { + super(subtype); + slots = new PyObject[subtype.getNumSlots()]; + } + + @Override + public PyString __str__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__str__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(); + if (res instanceof PyString) { + return (PyString)res; + } + throw Py.TypeError("__str__" + " returned non-" + "string" + " (type " + + res.getType().fastGetName() + ")"); + } + return super.__str__(); + } + + @Override + public PyString __repr__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__repr__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(); + if (res instanceof PyString) { + return (PyString)res; + } + throw Py.TypeError("__repr__" + " returned non-" + "string" + " (type " + + res.getType().fastGetName() + ")"); + } + return super.__repr__(); + } + + @Override + public PyString __hex__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__hex__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(); + if (res instanceof PyString) { + return (PyString)res; + } + throw Py.TypeError("__hex__" + " returned non-" + "string" + " (type " + + res.getType().fastGetName() + ")"); + } + return super.__hex__(); + } + + @Override + public PyString __oct__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__oct__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(); + if (res instanceof PyString) { + return (PyString)res; + } + throw Py.TypeError("__oct__" + " returned non-" + "string" + " (type " + + res.getType().fastGetName() + ")"); + } + return super.__oct__(); + } + + @Override + public PyFloat __float__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__float__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(); + if (res instanceof PyFloat) { + return (PyFloat)res; + } + throw Py.TypeError("__float__" + " returned non-" + "float" + " (type " + + res.getType().fastGetName() + ")"); + } + return super.__float__(); + } + + @Override + public PyComplex __complex__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__complex__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(); + if (res instanceof PyComplex) { + return (PyComplex)res; + } + throw Py.TypeError("__complex__" + " returned non-" + "complex" + " (type " + + res.getType().fastGetName() + ")"); + } + return super.__complex__(); + } + + @Override + public PyObject __pos__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__pos__"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(); + } + return super.__pos__(); + } + + @Override + public PyObject __neg__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__neg__"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(); + } + return super.__neg__(); + } + + @Override + public PyObject __abs__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__abs__"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(); + } + return super.__abs__(); + } + + @Override + public PyObject __invert__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__invert__"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(); + } + return super.__invert__(); + } + + @Override + public PyObject __reduce__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__reduce__"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(); + } + return super.__reduce__(); + } + + @Override + public PyObject __dir__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__dir__"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(); + } + return super.__dir__(); + } + + @Override + public PyObject __add__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__add__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__add__(other); + } + + @Override + public PyObject __radd__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__radd__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__radd__(other); + } + + @Override + public PyObject __sub__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__sub__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__sub__(other); + } + + @Override + public PyObject __rsub__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__rsub__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__rsub__(other); + } + + @Override + public PyObject __mul__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__mul__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__mul__(other); + } + + @Override + public PyObject __rmul__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__rmul__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__rmul__(other); + } + + @Override + public PyObject __div__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__div__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__div__(other); + } + + @Override + public PyObject __rdiv__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__rdiv__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__rdiv__(other); + } + + @Override + public PyObject __floordiv__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__floordiv__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__floordiv__(other); + } + + @Override + public PyObject __rfloordiv__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__rfloordiv__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__rfloordiv__(other); + } + + @Override + public PyObject __truediv__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__truediv__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__truediv__(other); + } + + @Override + public PyObject __rtruediv__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__rtruediv__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__rtruediv__(other); + } + + @Override + public PyObject __mod__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__mod__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__mod__(other); + } + + @Override + public PyObject __rmod__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__rmod__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__rmod__(other); + } + + @Override + public PyObject __divmod__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__divmod__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__divmod__(other); + } + + @Override + public PyObject __rdivmod__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__rdivmod__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__rdivmod__(other); + } + + @Override + public PyObject __rpow__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__rpow__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__rpow__(other); + } + + @Override + public PyObject __lshift__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__lshift__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__lshift__(other); + } + + @Override + public PyObject __rlshift__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__rlshift__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__rlshift__(other); + } + + @Override + public PyObject __rshift__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__rshift__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__rshift__(other); + } + + @Override + public PyObject __rrshift__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__rrshift__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__rrshift__(other); + } + + @Override + public PyObject __and__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__and__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__and__(other); + } + + @Override + public PyObject __rand__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__rand__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__rand__(other); + } + + @Override + public PyObject __or__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__or__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__or__(other); + } + + @Override + public PyObject __ror__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__ror__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__ror__(other); + } + + @Override + public PyObject __xor__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__xor__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__xor__(other); + } + + @Override + public PyObject __rxor__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__rxor__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__rxor__(other); + } + + @Override + public PyObject __lt__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__lt__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__lt__(other); + } + + @Override + public PyObject __le__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__le__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__le__(other); + } + + @Override + public PyObject __gt__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__gt__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__gt__(other); + } + + @Override + public PyObject __ge__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__ge__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__ge__(other); + } + + @Override + public PyObject __eq__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__eq__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__eq__(other); + } + + @Override + public PyObject __ne__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__ne__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__ne__(other); + } + + @Override + public PyObject __format__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__format__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__format__(other); + } + + @Override + public PyObject __iadd__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__iadd__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__iadd__(other); + } + + @Override + public PyObject __isub__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__isub__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__isub__(other); + } + + @Override + public PyObject __imul__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__imul__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__imul__(other); + } + + @Override + public PyObject __idiv__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__idiv__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__idiv__(other); + } + + @Override + public PyObject __ifloordiv__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__ifloordiv__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__ifloordiv__(other); + } + + @Override + public PyObject __itruediv__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__itruediv__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__itruediv__(other); + } + + @Override + public PyObject __imod__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__imod__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__imod__(other); + } + + @Override + public PyObject __ipow__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__ipow__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__ipow__(other); + } + + @Override + public PyObject __ilshift__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__ilshift__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__ilshift__(other); + } + + @Override + public PyObject __irshift__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__irshift__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__irshift__(other); + } + + @Override + public PyObject __iand__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__iand__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__iand__(other); + } + + @Override + public PyObject __ior__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__ior__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__ior__(other); + } + + @Override + public PyObject __ixor__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__ixor__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__ixor__(other); + } + + @Override + public PyObject __int__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__int__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(); + if (res instanceof PyInteger || res instanceof PyLong) { + return res; + } + throw Py.TypeError("__int__" + " should return an integer"); + } + return super.__int__(); + } + + @Override + public PyObject __long__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__long__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(); + if (res instanceof PyLong || res instanceof PyInteger) { + return res; + } + throw Py.TypeError("__long__" + " returned non-" + "long" + " (type " + + res.getType().fastGetName() + ")"); + } + return super.__long__(); + } + + @Override + public int hashCode() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__hash__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(); + if (res instanceof PyInteger) { + return ((PyInteger)res).getValue(); + } else if (res instanceof PyLong) { + return ((PyLong)res).getValue().intValue(); + } + throw Py.TypeError("__hash__ should return a int"); + } + if (self_type.lookup("__eq__") != null || self_type.lookup("__cmp__") != null) { + throw Py.TypeError(String.format("unhashable type: '%.200s'", getType().fastGetName())); + } + return super.hashCode(); + } + + @Override + public PyUnicode __unicode__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__unicode__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(); + if (res instanceof PyUnicode) { + return (PyUnicode)res; + } + if (res instanceof PyString) { + return new PyUnicode((PyString)res); + } + throw Py.TypeError("__unicode__" + " should return a " + "unicode"); + } + return super.__unicode__(); + } + + @Override + public int __cmp__(PyObject other) { + PyType self_type = getType(); + PyObject[] where_type = new PyObject[1]; + PyObject impl = self_type.lookup_where("__cmp__", where_type); + // Full Compatibility with CPython __cmp__: + // If the derived type don't override __cmp__, the + // *internal* super().__cmp__ should be called, not the + // exposed one. The difference is that the exposed __cmp__ + // throws a TypeError if the argument is an instance of the same type. + if (impl == null || where_type[0] == TYPE || Py.isSubClass(TYPE, where_type[0])) { + return super.__cmp__(other); + } + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return -2; + } + int c = res.asInt(); + return c < 0 ? -1 : c > 0 ? 1 : 0; + } + + @Override + public boolean __nonzero__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__nonzero__"); + if (impl == null) { + impl = self_type.lookup("__len__"); + if (impl == null) { + return super.__nonzero__(); + } + } + PyObject o = impl.__get__(this, self_type).__call__(); + Class c = o.getClass(); + if (c != PyInteger.class && c != PyBoolean.class) { + throw Py.TypeError(String.format("__nonzero__ should return bool or int, returned %s", + self_type.getName())); + } + return o.__nonzero__(); + } + + @Override + public boolean __contains__(PyObject o) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__contains__"); + if (impl == null) { + return super.__contains__(o); + } + return impl.__get__(this, self_type).__call__(o).__nonzero__(); + } + + @Override + public int __len__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__len__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(); + if (res instanceof PyInteger) { + return ((PyInteger)res).getValue(); + } + throw Py.TypeError("__len__ should return a int"); + } + return super.__len__(); + } + + @Override + public PyObject __iter__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__iter__"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(); + } + impl = self_type.lookup("__getitem__"); + if (impl == null) { + return super.__iter__(); + } + return new PySequenceIter(this); + } + + @Override + public PyObject __iternext__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("next"); + if (impl != null) { + try { + return impl.__get__(this, self_type).__call__(); + } catch (PyException exc) { + if (exc.match(Py.StopIteration)) { + return null; + } + throw exc; + } + } + return super.__iternext__(); // ??? + } + + @Override + public PyObject __finditem__(PyObject key) { // ??? + PyType self_type = getType(); + PyObject impl = self_type.lookup("__getitem__"); + if (impl != null) { + try { + return impl.__get__(this, self_type).__call__(key); + } catch (PyException exc) { + if (exc.match(Py.LookupError)) { + return null; + } + throw exc; + } + } + return super.__finditem__(key); + } + + @Override + public PyObject __finditem__(int key) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__getitem__"); + if (impl != null) { + try { + return impl.__get__(this, self_type).__call__(new PyInteger(key)); + } catch (PyException exc) { + if (exc.match(Py.LookupError)) { + return null; + } + throw exc; + } + } + return super.__finditem__(key); + } + + @Override + public PyObject __getitem__(PyObject key) { + // Same as __finditem__, without swallowing LookupErrors. This allows + // __getitem__ implementations written in Python to raise custom + // exceptions (such as subclasses of KeyError). + // + // We are forced to duplicate the code, instead of defining __finditem__ + // in terms of __getitem__. That's because PyObject defines __getitem__ + // in terms of __finditem__. Therefore, we would end with an infinite + // loop when self_type.lookup("__getitem__") returns null: + // + // __getitem__ -> super.__getitem__ -> __finditem__ -> __getitem__ + // + // By duplicating the (short) lookup and call code, we are safe, because + // the call chains will be: + // + // __finditem__ -> super.__finditem__ + // + // __getitem__ -> super.__getitem__ -> __finditem__ -> super.__finditem__ + + PyType self_type = getType(); + PyObject impl = self_type.lookup("__getitem__"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(key); + } + return super.__getitem__(key); + } + + @Override + public void __setitem__(PyObject key, PyObject value) { // ??? + PyType self_type = getType(); + PyObject impl = self_type.lookup("__setitem__"); + if (impl != null) { + impl.__get__(this, self_type).__call__(key, value); + return; + } + super.__setitem__(key, value); + } + + @Override + public PyObject __getslice__(PyObject start, PyObject stop, PyObject step) { // ??? + if (step != null) { + return __getitem__(new PySlice(start, stop, step)); + } + PyType self_type = getType(); + PyObject impl = self_type.lookup("__getslice__"); + if (impl != null) { + PyObject[] indices = PySlice.indices2(this, start, stop); + return impl.__get__(this, self_type).__call__(indices[0], indices[1]); + } + return super.__getslice__(start, stop, step); + } + + @Override + public void __setslice__(PyObject start, PyObject stop, PyObject step, PyObject value) { + if (step != null) { + __setitem__(new PySlice(start, stop, step), value); + return; + } + PyType self_type = getType(); + PyObject impl = self_type.lookup("__setslice__"); + if (impl != null) { + PyObject[] indices = PySlice.indices2(this, start, stop); + impl.__get__(this, self_type).__call__(indices[0], indices[1], value); + return; + } + super.__setslice__(start, stop, step, value); + } + + @Override + public void __delslice__(PyObject start, PyObject stop, PyObject step) { + if (step != null) { + __delitem__(new PySlice(start, stop, step)); + return; + } + PyType self_type = getType(); + PyObject impl = self_type.lookup("__delslice__"); + if (impl != null) { + PyObject[] indices = PySlice.indices2(this, start, stop); + impl.__get__(this, self_type).__call__(indices[0], indices[1]); + return; + } + super.__delslice__(start, stop, step); + } + + @Override + public void __delitem__(PyObject key) { // ??? + PyType self_type = getType(); + PyObject impl = self_type.lookup("__delitem__"); + if (impl != null) { + impl.__get__(this, self_type).__call__(key); + return; + } + super.__delitem__(key); + } + + @Override + public PyObject __call__(PyObject args[], String keywords[]) { + ThreadState ts = Py.getThreadState(); + if (ts.recursion_depth++ > ts.systemState.getrecursionlimit()) { + throw Py.RuntimeError("maximum __call__ recursion depth exceeded"); + } + try { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__call__"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(args, keywords); + } + return super.__call__(args, keywords); + } finally { + --ts.recursion_depth; + } + } + + @Override + public PyObject __findattr_ex__(String name) { + return Deriveds.__findattr_ex__(this, name); + } + + @Override + public void __setattr__(String name, PyObject value) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__setattr__"); + if (impl != null) { + impl.__get__(this, self_type).__call__(PyString.fromInterned(name), value); + return; + } + super.__setattr__(name, value); + } + + @Override + public void __delattr__(String name) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__delattr__"); + if (impl != null) { + impl.__get__(this, self_type).__call__(PyString.fromInterned(name)); + return; + } + super.__delattr__(name); + } + + @Override + public PyObject __get__(PyObject obj, PyObject type) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__get__"); + if (impl != null) { + if (obj == null) { + obj = Py.None; + } + if (type == null) { + type = Py.None; + } + return impl.__get__(this, self_type).__call__(obj, type); + } + return super.__get__(obj, type); + } + + @Override + public void __set__(PyObject obj, PyObject value) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__set__"); + if (impl != null) { + impl.__get__(this, self_type).__call__(obj, value); + return; + } + super.__set__(obj, value); + } + + @Override + public void __delete__(PyObject obj) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__delete__"); + if (impl != null) { + impl.__get__(this, self_type).__call__(obj); + return; + } + super.__delete__(obj); + } + + @Override + public PyObject __pow__(PyObject other, PyObject modulo) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__pow__"); + if (impl != null) { + PyObject res; + if (modulo == null) { + res = impl.__get__(this, self_type).__call__(other); + } else { + res = impl.__get__(this, self_type).__call__(other, modulo); + } + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__pow__(other, modulo); + } + + @Override + public void dispatch__init__(PyObject[] args, String[] keywords) { + Deriveds.dispatch__init__(this, args, keywords); + } + + @Override + public PyObject __index__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__index__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(); + if (res instanceof PyInteger || res instanceof PyLong) { + return res; + } + throw Py.TypeError(String.format("__index__ returned non-(int,long) (type %s)", res + .getType().fastGetName())); + } + return super.__index__(); + } + + @Override + public Object __tojava__(Class c) { + // If we are not being asked by the "default" conversion to java, then + // we can provide this as the result, as long as it is a instance of the + // specified class. Without this, derived.__tojava__(PyObject.class) + // would broke. (And that's not pure speculation: PyReflectedFunction's + // ReflectedArgs asks for things like that). + if ((c != Object.class) && (c != Serializable.class) && (c.isInstance(this))) { + return this; + } + // Otherwise, we call the derived __tojava__, if it exists: + PyType self_type = getType(); + PyObject impl = self_type.lookup("__tojava__"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(Py.java2py(c)).__tojava__(Object.class); + } + return super.__tojava__(c); + } + + @Override + public Object __coerce_ex__(PyObject o) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__coerce__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(o); + if (res == Py.NotImplemented) { + return Py.None; + } + if (!(res instanceof PyTuple)) { + throw Py.TypeError("__coerce__ didn't return a 2-tuple"); + } + return ((PyTuple)res).getArray(); + } + return super.__coerce_ex__(o); + } + + @Override + public PyObject __enter__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__enter__"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(); + } + return super.__enter__(); + } + + @Override + public PyObject fileno() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("fileno"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(); + } + return super.fileno(); + } + + // Hand-crafted in _io._IOBase.derived + + @Override + public long seek(long pos, int whence) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("seek"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(Py.newLong(pos), Py.newInteger(whence)) + .asLong(); + } else { + return super.seek(pos, whence); + } + } + + @Override + public long tell() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("tell"); + if (impl != null) { + return impl.__get__(this, self_type).__call__().asLong(); + } else { + return super.tell(); + } + } + + @Override + public long truncate(long size) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("truncate"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(Py.newLong(size)).asLong(); + } else { + return super.truncate(size); + } + } + + @Override + public long truncate() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("truncate"); + if (impl != null) { + return impl.__get__(this, self_type).__call__().asLong(); + } else { + return super.truncate(); + } + } + + @Override + public void flush() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("flush"); + if (impl != null) { + impl.__get__(this, self_type).__call__(); + } else { + super.flush(); + } + } + + @Override + public void close() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("close"); + if (impl != null) { + impl.__get__(this, self_type).__call__(); + } else { + super.close(); + } + } + + @Override + public boolean seekable() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("seekable"); + if (impl != null) { + return impl.__get__(this, self_type).__call__().__nonzero__(); + } else { + return super.seekable(); + } + } + + @Override + public void _checkSeekable(String msg) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("_checkSeekable"); + if (impl != null) { + PyObject pymsg = msg == null ? Py.None : new PyString(msg); + impl.__get__(this, self_type).__call__(pymsg); + } else { + super._checkSeekable(msg); + } + } + + @Override + public boolean readable() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("readable"); + if (impl != null) { + return impl.__get__(this, self_type).__call__().__nonzero__(); + } else { + return super.readable(); + } + } + + @Override + public void _checkReadable(String msg) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("_checkReadable"); + if (impl != null) { + PyObject pymsg = msg == null ? Py.None : new PyString(msg); + impl.__get__(this, self_type).__call__(pymsg); + } else { + super._checkReadable(msg); + } + } + + @Override + public boolean writable() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("writable"); + if (impl != null) { + return impl.__get__(this, self_type).__call__().__nonzero__(); + } else { + return super.writable(); + } + } + + @Override + public void _checkWritable(String msg) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("_checkWritable"); + if (impl != null) { + PyObject pymsg = msg == null ? Py.None : new PyString(msg); + impl.__get__(this, self_type).__call__(pymsg); + } else { + super._checkWritable(msg); + } + } + + // Note that closed is a property not a predicate, so no derived method. + + @Override + public void _checkClosed(String msg) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("_checkClosed"); + if (impl != null) { + PyObject pymsg = msg == null ? Py.None : new PyString(msg); + impl.__get__(this, self_type).__call__(pymsg); + } else { + super._checkClosed(msg); + } + } + + @Override + public boolean __exit__(PyObject type, PyObject value, PyObject traceback) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__exit__"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(type, value, traceback).__nonzero__(); + } else { + return super.__exit__(type, value, traceback); + } + } + + @Override + public boolean isatty() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("isatty"); + if (impl != null) { + return impl.__get__(this, self_type).__call__().__nonzero__(); + } else { + return super.isatty(); + } + } + + @Override + public PyObject readline() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("readline"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(Py.None); + } else { + return super.readline(); + } + } + + @Override + public PyObject readline(int limit) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("readline"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(Py.newInteger(limit)); + } else { + return super.readline(limit); + } + } + + @Override + public PyObject readlines(PyObject hint) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("readlines"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(hint); + return res; + } else { + return super.readlines(hint); + } + } + + @Override + public void writelines(PyObject lines) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("writelines"); + if (impl != null) { + impl.__get__(this, self_type).__call__(lines); + } else { + super.writelines(lines); + } + } + + @Override + public PyObject readall() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("readall"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(); + } + return super.readall(); + } + + // Hand-crafted in _io._RawIOBase.derived + + @Override + public PyObject read(int n) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("read"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(Py.newInteger(n)); + } else { + return super.read(n); + } + } + + @Override + public PyObject readinto(PyObject b) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("readinto"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(b); + } else { + return super.readinto(b); + } + } + + @Override + public PyObject write(PyObject b) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("write"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(b); + } else { + return super.write(b); + } + } + +} diff --git a/src/org/python/modules/_io/_io.java b/src/org/python/modules/_io/_io.java --- a/src/org/python/modules/_io/_io.java +++ b/src/org/python/modules/_io/_io.java @@ -31,6 +31,7 @@ dict.__setitem__("DEFAULT_BUFFER_SIZE", DEFAULT_BUFFER_SIZE); dict.__setitem__("_IOBase", PyIOBase.TYPE); + dict.__setitem__("_RawIOBase", PyRawIOBase.TYPE); dict.__setitem__("FileIO", PyFileIO.TYPE); // Define UnsupportedOperation exception by constructing the type diff --git a/src/templates/_io._RawIOBase.derived b/src/templates/_io._RawIOBase.derived new file mode 100644 --- /dev/null +++ b/src/templates/_io._RawIOBase.derived @@ -0,0 +1,39 @@ +base_class: PyRawIOBase +want_dict: false +ctr: +no_toString: true +incl: _io._IOBase +unary1: readall + +rest: + // Hand-crafted in _io._RawIOBase.derived + + public PyObject read(int n) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("read"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(Py.newInteger(n)); + } else { + return super.read(n); + } + } + + public PyObject readinto(PyObject b) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("readinto"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(b); + } else { + return super.readinto(b); + } + } + + public PyObject write(PyObject b) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("write"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(b); + } else { + return super.write(b); + } + } diff --git a/src/templates/mappings b/src/templates/mappings --- a/src/templates/mappings +++ b/src/templates/mappings @@ -6,7 +6,6 @@ # will map str.expose to org.python.core.PyString. Many classes will have 2 # lines; one for their expose, and one for their derived class. -_io._IOBase.derived:org.python.modules._io.PyIOBaseDerived BaseException.derived:org.python.core.PyBaseExceptionDerived ClasspathPyImporter.derived:org.python.core.ClasspathPyImporterDerived PyFileIO.derived:org.python.modules._io.PyFileIODerived @@ -148,3 +147,5 @@ op_Sub.derived:org.python.antlr.op.SubDerived op_UAdd.derived:org.python.antlr.op.UAddDerived op_USub.derived:org.python.antlr.op.USubDerived +_io._IOBase.derived:org.python.modules._io.PyIOBaseDerived +_io._RawIOBase.derived:org.python.modules._io.PyRawIOBaseDerived -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Sun Dec 9 21:29:44 2012 From: jython-checkins at python.org (jeff.allen) Date: Sun, 9 Dec 2012 21:29:44 +0100 (CET) Subject: [Jython-checkins] =?utf-8?q?jython=3A_Make_=5Fio=2EPyFileIO_inher?= =?utf-8?q?it_=5Fio=2EPyRawFileIO?= Message-ID: <3YKJtc1XByzRVP@mail.python.org> http://hg.python.org/jython/rev/eab0ca95f176 changeset: 6895:eab0ca95f176 user: Jeff Allen date: Sat Dec 08 22:09:29 2012 +0000 summary: Make _io.PyFileIO inherit _io.PyRawFileIO Now PyFileIO is part of the _io hierarchy, it also adopts the Closer that PyIOBase provides, in place of its own, and the base class' approach to close and flush. test_io errors are lower (as promised) at fail/error/skip = 19/20/99. files: src/org/python/modules/_io/PyFileIO.java | 114 ++++------ 1 files changed, 42 insertions(+), 72 deletions(-) diff --git a/src/org/python/modules/_io/PyFileIO.java b/src/org/python/modules/_io/PyFileIO.java --- a/src/org/python/modules/_io/PyFileIO.java +++ b/src/org/python/modules/_io/PyFileIO.java @@ -2,7 +2,6 @@ package org.python.modules._io; import java.nio.ByteBuffer; -import java.util.concurrent.Callable; import org.python.core.ArgParser; import org.python.core.BaseBytes; @@ -13,7 +12,6 @@ import org.python.core.PyJavaType; import org.python.core.PyObject; import org.python.core.PyString; -import org.python.core.PySystemState; import org.python.core.PyType; import org.python.core.PyUnicode; import org.python.core.io.FileIO; @@ -23,8 +21,8 @@ import org.python.expose.ExposedNew; import org.python.expose.ExposedType; - at ExposedType(name = "_io.FileIO") -public class PyFileIO extends PyObject { + at ExposedType(name = "_io.FileIO", base = PyRawIOBase.class) +public class PyFileIO extends PyRawIOBase { public static final PyType TYPE = PyType.fromClass(PyFileIO.class); @@ -39,9 +37,6 @@ @ExposedGet(doc = BuiltinDocs.file_mode_doc) public String mode; - /** The file's closer object; ensures the file is closed at shutdown */ - private Closer closer; - public PyFileIO() { super(TYPE); } @@ -76,7 +71,6 @@ } FileIO___init__((PyString)name, mode, closefd); - closer = new Closer(file, Py.getSystemState()); } private void FileIO___init__(PyString name, String mode, boolean closefd) { @@ -107,20 +101,26 @@ + "b" + (updating ? "+" : ""); } - @ExposedMethod(doc = BuiltinDocs.file_close_doc) + /** + * Close the underlying file only if closefd was specified as (or defaulted to) + * True. + */ + @Override + public void close() { + FileIO_close(); + } + + @ExposedMethod final synchronized void FileIO_close() { - if (closer != null) { - closer.close(); - closer = null; - } else { + // Close this object to further input (also calls flush) + super.close(); + // Now close downstream (if required to) + if (closefd) { file.close(); } } - public void close() { - FileIO_close(); - } - + @Override public boolean readable() { return FileIO_readable(); } @@ -136,6 +136,7 @@ return Py.java2py(file.seek(pos, how)); } + @Override public boolean seekable() { return FileIO_seekable(); } @@ -154,34 +155,41 @@ return file.tell(); } + @Override public long tell() { return FileIO_tell(); } - @ExposedMethod(defaults = {"null"}, doc = BuiltinDocs.file_truncate_doc) - final PyObject FileIO_truncate(PyObject position) { - if (position == null) { - return Py.java2py(FileIO_truncate()); - } - return Py.java2py(FileIO_truncate(position.asLong())); + @Override + public long truncate() { + return _truncate(); } - final synchronized long FileIO_truncate(long position) { - return file.truncate(position); + @Override + public long truncate(long size) { + return _truncate(size); } - public long truncate(long position) { - return FileIO_truncate(position); + @ExposedMethod(defaults = "null", doc = truncate_doc) + final long FileIO_truncate(PyObject size) { + return (size != null) ? _truncate(size.asLong()) : _truncate(); } - final synchronized long FileIO_truncate() { - return file.truncate(file.tell()); + /** Common to FileIO_truncate(null) and truncate(). */ + private final long _truncate() { + synchronized (file) { + return file.truncate(file.tell()); + } } - public void truncate() { - FileIO_truncate(); + /** Common to FileIO_truncate(size) and truncate(size). */ + private final long _truncate(long size) { + synchronized (file) { + return file.truncate(size); + } } + @Override public boolean isatty() { return FileIO_isatty(); } @@ -191,6 +199,7 @@ return file.isatty(); } + @Override public boolean writable() { return FileIO_writable(); } @@ -200,6 +209,7 @@ return file.writable(); } + @Override public PyObject fileno() { return FileIO_fileno(); } @@ -216,6 +226,7 @@ return new PyString(StringUtil.fromBytes(buf)); } + @Override public PyString read(int size) { return FileIO_read(size); } @@ -281,45 +292,4 @@ return file.closed(); } - /** - * XXX update docs - A mechanism to make sure PyFiles are closed on exit. On creation Closer adds itself - * to a list of Closers that will be run by PyFileCloser on JVM shutdown. When a - * PyFile's close or finalize methods are called, PyFile calls its Closer.close which - * clears Closer out of the shutdown queue. - * - * We use a regular object here rather than WeakReferences and their ilk as they may - * be collected before the shutdown hook runs. There's no guarantee that finalize will - * be called during shutdown, so we can't use it. It's vital that this Closer has no - * reference to the PyFile it's closing so the PyFile remains garbage collectable. - */ - private static class Closer implements Callable { - - /** - * The underlying file - */ - private final FileIO file; - private PySystemState sys; - - public Closer(FileIO file, PySystemState sys) { - this.file = file; - this.sys = sys; - sys.registerCloser(this); - } - - /** For closing directly */ - public void close() { - sys.unregisterCloser(this); - file.close(); - sys = null; - } - - /** For closing as part of a shutdown process */ - @Override - public Void call() { - file.close(); - sys = null; - return null; - } - - } } -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Sun Dec 9 21:29:46 2012 From: jython-checkins at python.org (jeff.allen) Date: Sun, 9 Dec 2012 21:29:46 +0100 (CET) Subject: [Jython-checkins] =?utf-8?q?jython=3A_Renaming_and_formatting_aro?= =?utf-8?q?und_=5Fio=2EFileIO?= Message-ID: <3YKJtf2tyszRVP@mail.python.org> http://hg.python.org/jython/rev/b0d03cd1dd44 changeset: 6896:b0d03cd1dd44 user: Jeff Allen date: Sat Dec 08 23:19:29 2012 +0000 summary: Renaming and formatting around _io.FileIO src/templates/PyFileIO.derived renamed _io.FileIO.derived after its object. Harmless renaming of private member, and some code formatting for compliance. No effect on test scores. files: src/org/python/modules/_io/PyFileIO.java | 82 +- src/org/python/modules/_io/PyFileIODerived.java | 2470 +++++---- src/templates/PyFileIO.derived | 0 src/templates/mappings | 2 +- 4 files changed, 1388 insertions(+), 1166 deletions(-) diff --git a/src/org/python/modules/_io/PyFileIO.java b/src/org/python/modules/_io/PyFileIO.java --- a/src/org/python/modules/_io/PyFileIO.java +++ b/src/org/python/modules/_io/PyFileIO.java @@ -26,12 +26,12 @@ public static final PyType TYPE = PyType.fromClass(PyFileIO.class); - private FileIO file; - private PyObject name; - private Boolean seekable; + private FileIO ioDelegate; // XXX final? + private PyObject name; + private Boolean seekable; @ExposedGet - public boolean closefd; + public boolean closefd; /** The mode string */ @ExposedGet(doc = BuiltinDocs.file_mode_doc) @@ -46,22 +46,23 @@ } public PyFileIO(String name, String mode, boolean closefd) { - this(); + this(); FileIO___init__(Py.newString(name), mode, closefd); } public PyFileIO(String name, String mode) { - this(name, mode, true); + this(name, mode, true); } @ExposedNew @ExposedMethod(doc = BuiltinDocs.file___init___doc) final void FileIO___init__(PyObject[] args, String[] kwds) { - ArgParser ap = new ArgParser("file", args, kwds, new String[] {"name", "mode", "closefd"}, 1); + ArgParser ap = + new ArgParser("file", args, kwds, new String[] {"name", "mode", "closefd"}, 1); PyObject name = ap.getPyObject(0); if (!(name instanceof PyString)) { throw Py.TypeError("coercing to Unicode: need string, '" + name.getType().fastGetName() - + "' type found"); + + "' type found"); } String mode = ap.getString(1, "r"); boolean closefd = Py.py2boolean(ap.getPyObject(2, Py.True)); @@ -74,11 +75,11 @@ } private void FileIO___init__(PyString name, String mode, boolean closefd) { - mode = parseMode(mode); + mode = parseMode(mode); this.name = name; this.mode = mode; this.closefd = closefd; - this.file = new FileIO(name, mode.replaceAll("b", "")); + this.ioDelegate = new FileIO(name, mode.replaceAll("b", "")); } private String parseMode(String mode) { @@ -89,7 +90,7 @@ String origMode = mode; if ("rwa".indexOf(mode.charAt(0)) == -1) { throw Py.ValueError("mode string must begin with one of 'r', 'w', 'a' or 'U', not '" - + origMode + "'"); + + origMode + "'"); } boolean reading = mode.contains("r"); @@ -97,13 +98,13 @@ boolean appending = mode.contains("a"); boolean updating = mode.contains("+"); - return (reading ? "r" : "") + (writing ? "w" : "") + (appending ? "a" : "") - + "b" + (updating ? "+" : ""); + return (reading ? "r" : "") + (writing ? "w" : "") + (appending ? "a" : "") + "b" + + (updating ? "+" : ""); } /** - * Close the underlying file only if closefd was specified as (or defaulted to) - * True. + * Close the underlying ioDelegate only if closefd was specified as (or defaulted + * to) True. */ @Override public void close() { @@ -116,7 +117,7 @@ super.close(); // Now close downstream (if required to) if (closefd) { - file.close(); + ioDelegate.close(); } } @@ -127,13 +128,13 @@ @ExposedMethod(doc = "True if file was opened in a read mode.") final boolean FileIO_readable() { - return file.readable(); + return ioDelegate.readable(); } @ExposedMethod(defaults = {"0"}, doc = BuiltinDocs.file_seek_doc) final synchronized PyObject FileIO_seek(long pos, int how) { checkClosed(); - return Py.java2py(file.seek(pos, how)); + return Py.java2py(ioDelegate.seek(pos, how)); } @Override @@ -143,16 +144,16 @@ @ExposedMethod(doc = "True if file supports random-access.") final boolean FileIO_seekable() { - if (seekable == null) { - seekable = file.seek(0, 0) >= 0; + if (seekable == null) { + seekable = ioDelegate.seek(0, 0) >= 0; } - return seekable; + return seekable; } @ExposedMethod(doc = BuiltinDocs.file_tell_doc) final synchronized long FileIO_tell() { checkClosed(); - return file.tell(); + return ioDelegate.tell(); } @Override @@ -177,15 +178,15 @@ /** Common to FileIO_truncate(null) and truncate(). */ private final long _truncate() { - synchronized (file) { - return file.truncate(file.tell()); + synchronized (ioDelegate) { + return ioDelegate.truncate(ioDelegate.tell()); } } /** Common to FileIO_truncate(size) and truncate(size). */ private final long _truncate(long size) { - synchronized (file) { - return file.truncate(size); + synchronized (ioDelegate) { + return ioDelegate.truncate(size); } } @@ -196,7 +197,7 @@ @ExposedMethod(doc = BuiltinDocs.file_isatty_doc) final boolean FileIO_isatty() { - return file.isatty(); + return ioDelegate.isatty(); } @Override @@ -206,7 +207,7 @@ @ExposedMethod(doc = "True if file was opened in a write mode.") final boolean FileIO_writable() { - return file.writable(); + return ioDelegate.writable(); } @Override @@ -216,13 +217,13 @@ @ExposedMethod(doc = BuiltinDocs.file_fileno_doc) final PyObject FileIO_fileno() { - return PyJavaType.wrapJavaObject(file.fileno()); + return PyJavaType.wrapJavaObject(ioDelegate.fileno()); } @ExposedMethod(defaults = {"-1"}, doc = BuiltinDocs.file_read_doc) final synchronized PyString FileIO_read(int size) { checkClosed(); - ByteBuffer buf = file.read(size); + ByteBuffer buf = ioDelegate.read(size); return new PyString(StringUtil.fromBytes(buf)); } @@ -233,7 +234,7 @@ @ExposedMethod(doc = BuiltinDocs.file_read_doc) final synchronized PyString FileIO_readall() { - return FileIO_read(-1); + return FileIO_read(-1); } /** @@ -243,30 +244,31 @@ if (obj instanceof PyUnicode) { return ((PyUnicode)obj).encode(); } else if (obj instanceof PyString) { - return ((PyString) obj).getString(); + return ((PyString)obj).getString(); } else if (obj instanceof PyArray) { return ((PyArray)obj).tostring(); } else if (obj instanceof BaseBytes) { return StringUtil.fromBytes((BaseBytes)obj); } if (message == null) { - message = String.format("argument 1 must be string or buffer, not %.200s", - obj.getType().fastGetName()); + message = + String.format("argument 1 must be string or buffer, not %.200s", obj.getType() + .fastGetName()); } throw Py.TypeError(message); } @ExposedMethod(doc = BuiltinDocs.file_write_doc) final PyObject FileIO_write(PyObject obj) { - String writable = asWritable(obj, null); - byte[] bytes = StringUtil.toBytes(writable); + String writable = asWritable(obj, null); + byte[] bytes = StringUtil.toBytes(writable); int written = write(ByteBuffer.wrap(bytes)); - return new PyInteger(written); + return new PyInteger(written); } final synchronized int write(ByteBuffer buf) { checkClosed(); - return file.write(buf); + return ioDelegate.write(buf); } @ExposedMethod(names = {"__str__", "__repr__"}, doc = BuiltinDocs.file___str___doc) @@ -284,12 +286,12 @@ } private void checkClosed() { - file.checkClosed(); + ioDelegate.checkClosed(); } @ExposedGet(name = "closed", doc = BuiltinDocs.file_closed_doc) public boolean getClosed() { - return file.closed(); + return ioDelegate.closed(); } } diff --git a/src/org/python/modules/_io/PyFileIODerived.java b/src/org/python/modules/_io/PyFileIODerived.java --- a/src/org/python/modules/_io/PyFileIODerived.java +++ b/src/org/python/modules/_io/PyFileIODerived.java @@ -1,1125 +1,1345 @@ -/* Generated file, do not modify. See jython/src/templates/gderived.py. */ -package org.python.modules._io; - -import java.io.Serializable; -import org.python.core.*; - -public class PyFileIODerived extends PyFileIO implements Slotted { - - public PyObject getSlot(int index) { - return slots[index]; - } - - public void setSlot(int index,PyObject value) { - slots[index]=value; - } - - private PyObject[]slots; - - private PyObject dict; - - public PyObject fastGetDict() { - return dict; - } - - public PyObject getDict() { - return dict; - } - - public void setDict(PyObject newDict) { - if (newDict instanceof PyStringMap||newDict instanceof PyDictionary) { - dict=newDict; - } else { - throw Py.TypeError("__dict__ must be set to a Dictionary "+newDict.getClass().getName()); - } - } - - public void delDict() { - // deleting an object's instance dict makes it grow a new one - dict=new PyStringMap(); - } - - public PyFileIODerived(PyType subtype) { - super(subtype); - slots=new PyObject[subtype.getNumSlots()]; - dict=subtype.instDict(); - } - - public PyString __str__() { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__str__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(); - if (res instanceof PyString) - return(PyString)res; - throw Py.TypeError("__str__"+" returned non-"+"string"+" (type "+res.getType().fastGetName()+")"); - } - return super.__str__(); - } - - public PyString __repr__() { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__repr__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(); - if (res instanceof PyString) - return(PyString)res; - throw Py.TypeError("__repr__"+" returned non-"+"string"+" (type "+res.getType().fastGetName()+")"); - } - return super.__repr__(); - } - - public PyString __hex__() { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__hex__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(); - if (res instanceof PyString) - return(PyString)res; - throw Py.TypeError("__hex__"+" returned non-"+"string"+" (type "+res.getType().fastGetName()+")"); - } - return super.__hex__(); - } - - public PyString __oct__() { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__oct__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(); - if (res instanceof PyString) - return(PyString)res; - throw Py.TypeError("__oct__"+" returned non-"+"string"+" (type "+res.getType().fastGetName()+")"); - } - return super.__oct__(); - } - - public PyFloat __float__() { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__float__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(); - if (res instanceof PyFloat) - return(PyFloat)res; - throw Py.TypeError("__float__"+" returned non-"+"float"+" (type "+res.getType().fastGetName()+")"); - } - return super.__float__(); - } - - public PyComplex __complex__() { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__complex__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(); - if (res instanceof PyComplex) - return(PyComplex)res; - throw Py.TypeError("__complex__"+" returned non-"+"complex"+" (type "+res.getType().fastGetName()+")"); - } - return super.__complex__(); - } - - public PyObject __pos__() { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__pos__"); - if (impl!=null) - return impl.__get__(this,self_type).__call__(); - return super.__pos__(); - } - - public PyObject __neg__() { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__neg__"); - if (impl!=null) - return impl.__get__(this,self_type).__call__(); - return super.__neg__(); - } - - public PyObject __abs__() { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__abs__"); - if (impl!=null) - return impl.__get__(this,self_type).__call__(); - return super.__abs__(); - } - - public PyObject __invert__() { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__invert__"); - if (impl!=null) - return impl.__get__(this,self_type).__call__(); - return super.__invert__(); - } - - public PyObject __reduce__() { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__reduce__"); - if (impl!=null) - return impl.__get__(this,self_type).__call__(); - return super.__reduce__(); - } - - public PyObject __dir__() { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__dir__"); - if (impl!=null) - return impl.__get__(this,self_type).__call__(); - return super.__dir__(); - } - - public PyObject __add__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__add__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__add__(other); - } - - public PyObject __radd__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__radd__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__radd__(other); - } - - public PyObject __sub__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__sub__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__sub__(other); - } - - public PyObject __rsub__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__rsub__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__rsub__(other); - } - - public PyObject __mul__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__mul__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__mul__(other); - } - - public PyObject __rmul__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__rmul__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__rmul__(other); - } - - public PyObject __div__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__div__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__div__(other); - } - - public PyObject __rdiv__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__rdiv__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__rdiv__(other); - } - - public PyObject __floordiv__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__floordiv__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__floordiv__(other); - } - - public PyObject __rfloordiv__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__rfloordiv__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__rfloordiv__(other); - } - - public PyObject __truediv__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__truediv__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__truediv__(other); - } - - public PyObject __rtruediv__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__rtruediv__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__rtruediv__(other); - } - - public PyObject __mod__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__mod__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__mod__(other); - } - - public PyObject __rmod__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__rmod__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__rmod__(other); - } - - public PyObject __divmod__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__divmod__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__divmod__(other); - } - - public PyObject __rdivmod__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__rdivmod__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__rdivmod__(other); - } - - public PyObject __rpow__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__rpow__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__rpow__(other); - } - - public PyObject __lshift__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__lshift__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__lshift__(other); - } - - public PyObject __rlshift__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__rlshift__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__rlshift__(other); - } - - public PyObject __rshift__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__rshift__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__rshift__(other); - } - - public PyObject __rrshift__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__rrshift__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__rrshift__(other); - } - - public PyObject __and__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__and__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__and__(other); - } - - public PyObject __rand__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__rand__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__rand__(other); - } - - public PyObject __or__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__or__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__or__(other); - } - - public PyObject __ror__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__ror__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__ror__(other); - } - - public PyObject __xor__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__xor__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__xor__(other); - } - - public PyObject __rxor__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__rxor__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__rxor__(other); - } - - public PyObject __lt__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__lt__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__lt__(other); - } - - public PyObject __le__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__le__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__le__(other); - } - - public PyObject __gt__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__gt__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__gt__(other); - } - - public PyObject __ge__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__ge__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__ge__(other); - } - - public PyObject __eq__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__eq__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__eq__(other); - } - - public PyObject __ne__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__ne__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__ne__(other); - } - - public PyObject __iadd__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__iadd__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__iadd__(other); - } - - public PyObject __isub__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__isub__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__isub__(other); - } - - public PyObject __imul__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__imul__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__imul__(other); - } - - public PyObject __idiv__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__idiv__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__idiv__(other); - } - - public PyObject __ifloordiv__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__ifloordiv__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__ifloordiv__(other); - } - - public PyObject __itruediv__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__itruediv__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__itruediv__(other); - } - - public PyObject __imod__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__imod__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__imod__(other); - } - - public PyObject __ipow__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__ipow__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__ipow__(other); - } - - public PyObject __ilshift__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__ilshift__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__ilshift__(other); - } - - public PyObject __irshift__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__irshift__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__irshift__(other); - } - - public PyObject __iand__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__iand__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__iand__(other); - } - - public PyObject __ior__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__ior__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__ior__(other); - } - - public PyObject __ixor__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__ixor__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__ixor__(other); - } - - public PyObject __int__() { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__int__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(); - if (res instanceof PyInteger||res instanceof PyLong) - return res; - throw Py.TypeError("__int__"+" should return an integer"); - } - return super.__int__(); - } - - public PyObject __long__() { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__long__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(); - if (res instanceof PyLong||res instanceof PyInteger) - return res; - throw Py.TypeError("__long__"+" returned non-"+"long"+" (type "+res.getType().fastGetName()+")"); - } - return super.__long__(); - } - - public int hashCode() { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__hash__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(); - if (res instanceof PyInteger) { - return((PyInteger)res).getValue(); - } else - if (res instanceof PyLong) { - return((PyLong)res).getValue().intValue(); - } - throw Py.TypeError("__hash__ should return a int"); - } - if (self_type.lookup("__eq__")!=null||self_type.lookup("__cmp__")!=null) { - throw Py.TypeError(String.format("unhashable type: '%.200s'",getType().fastGetName())); - } - return super.hashCode(); - } - - public PyUnicode __unicode__() { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__unicode__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(); - if (res instanceof PyUnicode) - return(PyUnicode)res; - if (res instanceof PyString) - return new PyUnicode((PyString)res); - throw Py.TypeError("__unicode__"+" should return a "+"unicode"); - } - return super.__unicode__(); - } - - public int __cmp__(PyObject other) { - PyType self_type=getType(); - PyObject[]where_type=new PyObject[1]; - PyObject impl=self_type.lookup_where("__cmp__",where_type); - // Full Compatibility with CPython __cmp__: - // If the derived type don't override __cmp__, the - // *internal* super().__cmp__ should be called, not the - // exposed one. The difference is that the exposed __cmp__ - // throws a TypeError if the argument is an instance of the same type. - if (impl==null||where_type[0]==TYPE||Py.isSubClass(TYPE,where_type[0])) { - return super.__cmp__(other); - } - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) { - return-2; - } - int c=res.asInt(); - return c<0?-1:c>0?1:0; - } - - public boolean __nonzero__() { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__nonzero__"); - if (impl==null) { - impl=self_type.lookup("__len__"); - if (impl==null) - return super.__nonzero__(); - } - PyObject o=impl.__get__(this,self_type).__call__(); - Class c=o.getClass(); - if (c!=PyInteger.class&&c!=PyBoolean.class) { - throw Py.TypeError(String.format("__nonzero__ should return bool or int, returned %s",self_type.getName())); - } - return o.__nonzero__(); - } - - public boolean __contains__(PyObject o) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__contains__"); - if (impl==null) - return super.__contains__(o); - return impl.__get__(this,self_type).__call__(o).__nonzero__(); - } - - public int __len__() { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__len__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(); - if (res instanceof PyInteger) - return((PyInteger)res).getValue(); - throw Py.TypeError("__len__ should return a int"); - } - return super.__len__(); - } - - public PyObject __iter__() { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__iter__"); - if (impl!=null) - return impl.__get__(this,self_type).__call__(); - impl=self_type.lookup("__getitem__"); - if (impl==null) - return super.__iter__(); - return new PySequenceIter(this); - } - - public PyObject __iternext__() { - PyType self_type=getType(); - PyObject impl=self_type.lookup("next"); - if (impl!=null) { - try { - return impl.__get__(this,self_type).__call__(); - } catch (PyException exc) { - if (exc.match(Py.StopIteration)) - return null; - throw exc; - } - } - return super.__iternext__(); // ??? - } - - public PyObject __finditem__(PyObject key) { // ??? - PyType self_type=getType(); - PyObject impl=self_type.lookup("__getitem__"); - if (impl!=null) - try { - return impl.__get__(this,self_type).__call__(key); - } catch (PyException exc) { - if (exc.match(Py.LookupError)) - return null; - throw exc; - } - return super.__finditem__(key); - } - - public PyObject __finditem__(int key) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__getitem__"); - if (impl!=null) - try { - return impl.__get__(this,self_type).__call__(new PyInteger(key)); - } catch (PyException exc) { - if (exc.match(Py.LookupError)) - return null; - throw exc; - } - return super.__finditem__(key); - } - - public PyObject __getitem__(PyObject key) { - // Same as __finditem__, without swallowing LookupErrors. This allows - // __getitem__ implementations written in Python to raise custom - // exceptions (such as subclasses of KeyError). - // - // We are forced to duplicate the code, instead of defining __finditem__ - // in terms of __getitem__. That's because PyObject defines __getitem__ - // in terms of __finditem__. Therefore, we would end with an infinite - // loop when self_type.lookup("__getitem__") returns null: - // - // __getitem__ -> super.__getitem__ -> __finditem__ -> __getitem__ - // - // By duplicating the (short) lookup and call code, we are safe, because - // the call chains will be: - // - // __finditem__ -> super.__finditem__ - // - // __getitem__ -> super.__getitem__ -> __finditem__ -> super.__finditem__ - - PyType self_type=getType(); - PyObject impl=self_type.lookup("__getitem__"); - if (impl!=null) - return impl.__get__(this,self_type).__call__(key); - return super.__getitem__(key); - } - - public void __setitem__(PyObject key,PyObject value) { // ??? - PyType self_type=getType(); - PyObject impl=self_type.lookup("__setitem__"); - if (impl!=null) { - impl.__get__(this,self_type).__call__(key,value); - return; - } - super.__setitem__(key,value); - } - - public PyObject __getslice__(PyObject start,PyObject stop,PyObject step) { // ??? - if (step!=null) { - return __getitem__(new PySlice(start,stop,step)); - } - PyType self_type=getType(); - PyObject impl=self_type.lookup("__getslice__"); - if (impl!=null) { - PyObject[]indices=PySlice.indices2(this,start,stop); - return impl.__get__(this,self_type).__call__(indices[0],indices[1]); - } - return super.__getslice__(start,stop,step); - } - - public void __setslice__(PyObject start,PyObject stop,PyObject step,PyObject value) { - if (step!=null) { - __setitem__(new PySlice(start,stop,step),value); - return; - } - PyType self_type=getType(); - PyObject impl=self_type.lookup("__setslice__"); - if (impl!=null) { - PyObject[]indices=PySlice.indices2(this,start,stop); - impl.__get__(this,self_type).__call__(indices[0],indices[1],value); - return; - } - super.__setslice__(start,stop,step,value); - } - - public void __delslice__(PyObject start,PyObject stop,PyObject step) { - if (step!=null) { - __delitem__(new PySlice(start,stop,step)); - return; - } - PyType self_type=getType(); - PyObject impl=self_type.lookup("__delslice__"); - if (impl!=null) { - PyObject[]indices=PySlice.indices2(this,start,stop); - impl.__get__(this,self_type).__call__(indices[0],indices[1]); - return; - } - super.__delslice__(start,stop,step); - } - - public void __delitem__(PyObject key) { // ??? - PyType self_type=getType(); - PyObject impl=self_type.lookup("__delitem__"); - if (impl!=null) { - impl.__get__(this,self_type).__call__(key); - return; - } - super.__delitem__(key); - } - - public PyObject __call__(PyObject args[],String keywords[]) { - ThreadState ts=Py.getThreadState(); - if (ts.recursion_depth++>ts.systemState.getrecursionlimit()) - throw Py.RuntimeError("maximum __call__ recursion depth exceeded"); - try { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__call__"); - if (impl!=null) - return impl.__get__(this,self_type).__call__(args,keywords); - return super.__call__(args,keywords); - } finally { - --ts.recursion_depth; - } - } - - public PyObject __findattr_ex__(String name) { - return Deriveds.__findattr_ex__(this,name); - } - - public void __setattr__(String name,PyObject value) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__setattr__"); - if (impl!=null) { - impl.__get__(this,self_type).__call__(PyString.fromInterned(name),value); - return; - } - super.__setattr__(name,value); - } - - public void __delattr__(String name) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__delattr__"); - if (impl!=null) { - impl.__get__(this,self_type).__call__(PyString.fromInterned(name)); - return; - } - super.__delattr__(name); - } - - public PyObject __get__(PyObject obj,PyObject type) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__get__"); - if (impl!=null) { - if (obj==null) - obj=Py.None; - if (type==null) - type=Py.None; - return impl.__get__(this,self_type).__call__(obj,type); - } - return super.__get__(obj,type); - } - - public void __set__(PyObject obj,PyObject value) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__set__"); - if (impl!=null) { - impl.__get__(this,self_type).__call__(obj,value); - return; - } - super.__set__(obj,value); - } - - public void __delete__(PyObject obj) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__delete__"); - if (impl!=null) { - impl.__get__(this,self_type).__call__(obj); - return; - } - super.__delete__(obj); - } - - public PyObject __pow__(PyObject other,PyObject modulo) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__pow__"); - if (impl!=null) { - PyObject res; - if (modulo==null) { - res=impl.__get__(this,self_type).__call__(other); - } else { - res=impl.__get__(this,self_type).__call__(other,modulo); - } - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__pow__(other,modulo); - } - - public void dispatch__init__(PyObject[]args,String[]keywords) { - Deriveds.dispatch__init__(this,args,keywords); - } - - public PyObject __index__() { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__index__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(); - if (res instanceof PyInteger||res instanceof PyLong) { - return res; - } - throw Py.TypeError(String.format("__index__ returned non-(int,long) (type %s)",res.getType().fastGetName())); - } - return super.__index__(); - } - - public Object __tojava__(Class c) { - // If we are not being asked by the "default" conversion to java, then - // we can provide this as the result, as long as it is a instance of the - // specified class. Without this, derived.__tojava__(PyObject.class) - // would broke. (And that's not pure speculation: PyReflectedFunction's - // ReflectedArgs asks for things like that). - if ((c!=Object.class)&&(c!=Serializable.class)&&(c.isInstance(this))) { - return this; - } - // Otherwise, we call the derived __tojava__, if it exists: - PyType self_type=getType(); - PyObject impl=self_type.lookup("__tojava__"); - if (impl!=null) - return impl.__get__(this,self_type).__call__(Py.java2py(c)).__tojava__(Object.class); - return super.__tojava__(c); - } - - public Object __coerce_ex__(PyObject o) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__coerce__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(o); - if (res==Py.NotImplemented) - return Py.None; - if (!(res instanceof PyTuple)) - throw Py.TypeError("__coerce__ didn't return a 2-tuple"); - return((PyTuple)res).getArray(); - } - return super.__coerce_ex__(o); - } - - public String toString() { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__repr__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(); - if (!(res instanceof PyString)) - throw Py.TypeError("__repr__ returned non-string (type "+res.getType().fastGetName()+")"); - return((PyString)res).toString(); - } - return super.toString(); - } - -} +/* Generated file, do not modify. See jython/src/templates/gderived.py. */ +package org.python.modules._io; + +import java.io.Serializable; + +import org.python.core.Deriveds; +import org.python.core.Py; +import org.python.core.PyBoolean; +import org.python.core.PyComplex; +import org.python.core.PyDictionary; +import org.python.core.PyException; +import org.python.core.PyFloat; +import org.python.core.PyInteger; +import org.python.core.PyLong; +import org.python.core.PyObject; +import org.python.core.PySequenceIter; +import org.python.core.PySlice; +import org.python.core.PyString; +import org.python.core.PyStringMap; +import org.python.core.PyTuple; +import org.python.core.PyType; +import org.python.core.PyUnicode; +import org.python.core.Slotted; +import org.python.core.ThreadState; + +public class PyFileIODerived extends PyFileIO implements Slotted { + + @Override + public PyObject getSlot(int index) { + return slots[index]; + } + + @Override + public void setSlot(int index, PyObject value) { + slots[index] = value; + } + + private PyObject[] slots; + + private PyObject dict; + + @Override + public PyObject fastGetDict() { + return dict; + } + + @Override + public PyObject getDict() { + return dict; + } + + @Override + public void setDict(PyObject newDict) { + if (newDict instanceof PyStringMap || newDict instanceof PyDictionary) { + dict = newDict; + } else { + throw Py.TypeError("__dict__ must be set to a Dictionary " + + newDict.getClass().getName()); + } + } + + @Override + public void delDict() { + // deleting an object's instance dict makes it grow a new one + dict = new PyStringMap(); + } + + public PyFileIODerived(PyType subtype) { + super(subtype); + slots = new PyObject[subtype.getNumSlots()]; + dict = subtype.instDict(); + } + + @Override + public PyString __str__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__str__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(); + if (res instanceof PyString) { + return (PyString)res; + } + throw Py.TypeError("__str__" + " returned non-" + "string" + " (type " + + res.getType().fastGetName() + ")"); + } + return super.__str__(); + } + + @Override + public PyString __repr__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__repr__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(); + if (res instanceof PyString) { + return (PyString)res; + } + throw Py.TypeError("__repr__" + " returned non-" + "string" + " (type " + + res.getType().fastGetName() + ")"); + } + return super.__repr__(); + } + + @Override + public PyString __hex__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__hex__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(); + if (res instanceof PyString) { + return (PyString)res; + } + throw Py.TypeError("__hex__" + " returned non-" + "string" + " (type " + + res.getType().fastGetName() + ")"); + } + return super.__hex__(); + } + + @Override + public PyString __oct__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__oct__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(); + if (res instanceof PyString) { + return (PyString)res; + } + throw Py.TypeError("__oct__" + " returned non-" + "string" + " (type " + + res.getType().fastGetName() + ")"); + } + return super.__oct__(); + } + + @Override + public PyFloat __float__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__float__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(); + if (res instanceof PyFloat) { + return (PyFloat)res; + } + throw Py.TypeError("__float__" + " returned non-" + "float" + " (type " + + res.getType().fastGetName() + ")"); + } + return super.__float__(); + } + + @Override + public PyComplex __complex__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__complex__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(); + if (res instanceof PyComplex) { + return (PyComplex)res; + } + throw Py.TypeError("__complex__" + " returned non-" + "complex" + " (type " + + res.getType().fastGetName() + ")"); + } + return super.__complex__(); + } + + @Override + public PyObject __pos__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__pos__"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(); + } + return super.__pos__(); + } + + @Override + public PyObject __neg__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__neg__"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(); + } + return super.__neg__(); + } + + @Override + public PyObject __abs__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__abs__"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(); + } + return super.__abs__(); + } + + @Override + public PyObject __invert__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__invert__"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(); + } + return super.__invert__(); + } + + @Override + public PyObject __reduce__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__reduce__"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(); + } + return super.__reduce__(); + } + + @Override + public PyObject __dir__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__dir__"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(); + } + return super.__dir__(); + } + + @Override + public PyObject __add__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__add__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__add__(other); + } + + @Override + public PyObject __radd__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__radd__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__radd__(other); + } + + @Override + public PyObject __sub__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__sub__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__sub__(other); + } + + @Override + public PyObject __rsub__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__rsub__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__rsub__(other); + } + + @Override + public PyObject __mul__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__mul__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__mul__(other); + } + + @Override + public PyObject __rmul__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__rmul__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__rmul__(other); + } + + @Override + public PyObject __div__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__div__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__div__(other); + } + + @Override + public PyObject __rdiv__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__rdiv__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__rdiv__(other); + } + + @Override + public PyObject __floordiv__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__floordiv__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__floordiv__(other); + } + + @Override + public PyObject __rfloordiv__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__rfloordiv__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__rfloordiv__(other); + } + + @Override + public PyObject __truediv__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__truediv__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__truediv__(other); + } + + @Override + public PyObject __rtruediv__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__rtruediv__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__rtruediv__(other); + } + + @Override + public PyObject __mod__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__mod__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__mod__(other); + } + + @Override + public PyObject __rmod__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__rmod__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__rmod__(other); + } + + @Override + public PyObject __divmod__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__divmod__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__divmod__(other); + } + + @Override + public PyObject __rdivmod__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__rdivmod__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__rdivmod__(other); + } + + @Override + public PyObject __rpow__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__rpow__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__rpow__(other); + } + + @Override + public PyObject __lshift__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__lshift__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__lshift__(other); + } + + @Override + public PyObject __rlshift__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__rlshift__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__rlshift__(other); + } + + @Override + public PyObject __rshift__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__rshift__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__rshift__(other); + } + + @Override + public PyObject __rrshift__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__rrshift__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__rrshift__(other); + } + + @Override + public PyObject __and__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__and__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__and__(other); + } + + @Override + public PyObject __rand__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__rand__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__rand__(other); + } + + @Override + public PyObject __or__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__or__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__or__(other); + } + + @Override + public PyObject __ror__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__ror__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__ror__(other); + } + + @Override + public PyObject __xor__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__xor__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__xor__(other); + } + + @Override + public PyObject __rxor__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__rxor__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__rxor__(other); + } + + @Override + public PyObject __lt__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__lt__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__lt__(other); + } + + @Override + public PyObject __le__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__le__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__le__(other); + } + + @Override + public PyObject __gt__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__gt__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__gt__(other); + } + + @Override + public PyObject __ge__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__ge__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__ge__(other); + } + + @Override + public PyObject __eq__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__eq__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__eq__(other); + } + + @Override + public PyObject __ne__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__ne__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__ne__(other); + } + + @Override + public PyObject __format__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__format__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__format__(other); + } + + @Override + public PyObject __iadd__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__iadd__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__iadd__(other); + } + + @Override + public PyObject __isub__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__isub__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__isub__(other); + } + + @Override + public PyObject __imul__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__imul__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__imul__(other); + } + + @Override + public PyObject __idiv__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__idiv__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__idiv__(other); + } + + @Override + public PyObject __ifloordiv__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__ifloordiv__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__ifloordiv__(other); + } + + @Override + public PyObject __itruediv__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__itruediv__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__itruediv__(other); + } + + @Override + public PyObject __imod__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__imod__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__imod__(other); + } + + @Override + public PyObject __ipow__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__ipow__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__ipow__(other); + } + + @Override + public PyObject __ilshift__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__ilshift__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__ilshift__(other); + } + + @Override + public PyObject __irshift__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__irshift__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__irshift__(other); + } + + @Override + public PyObject __iand__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__iand__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__iand__(other); + } + + @Override + public PyObject __ior__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__ior__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__ior__(other); + } + + @Override + public PyObject __ixor__(PyObject other) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__ixor__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__ixor__(other); + } + + @Override + public PyObject __int__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__int__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(); + if (res instanceof PyInteger || res instanceof PyLong) { + return res; + } + throw Py.TypeError("__int__" + " should return an integer"); + } + return super.__int__(); + } + + @Override + public PyObject __long__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__long__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(); + if (res instanceof PyLong || res instanceof PyInteger) { + return res; + } + throw Py.TypeError("__long__" + " returned non-" + "long" + " (type " + + res.getType().fastGetName() + ")"); + } + return super.__long__(); + } + + @Override + public int hashCode() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__hash__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(); + if (res instanceof PyInteger) { + return ((PyInteger)res).getValue(); + } else if (res instanceof PyLong) { + return ((PyLong)res).getValue().intValue(); + } + throw Py.TypeError("__hash__ should return a int"); + } + if (self_type.lookup("__eq__") != null || self_type.lookup("__cmp__") != null) { + throw Py.TypeError(String.format("unhashable type: '%.200s'", getType().fastGetName())); + } + return super.hashCode(); + } + + @Override + public PyUnicode __unicode__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__unicode__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(); + if (res instanceof PyUnicode) { + return (PyUnicode)res; + } + if (res instanceof PyString) { + return new PyUnicode((PyString)res); + } + throw Py.TypeError("__unicode__" + " should return a " + "unicode"); + } + return super.__unicode__(); + } + + @Override + public int __cmp__(PyObject other) { + PyType self_type = getType(); + PyObject[] where_type = new PyObject[1]; + PyObject impl = self_type.lookup_where("__cmp__", where_type); + // Full Compatibility with CPython __cmp__: + // If the derived type don't override __cmp__, the + // *internal* super().__cmp__ should be called, not the + // exposed one. The difference is that the exposed __cmp__ + // throws a TypeError if the argument is an instance of the same type. + if (impl == null || where_type[0] == TYPE || Py.isSubClass(TYPE, where_type[0])) { + return super.__cmp__(other); + } + PyObject res = impl.__get__(this, self_type).__call__(other); + if (res == Py.NotImplemented) { + return -2; + } + int c = res.asInt(); + return c < 0 ? -1 : c > 0 ? 1 : 0; + } + + @Override + public boolean __nonzero__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__nonzero__"); + if (impl == null) { + impl = self_type.lookup("__len__"); + if (impl == null) { + return super.__nonzero__(); + } + } + PyObject o = impl.__get__(this, self_type).__call__(); + Class c = o.getClass(); + if (c != PyInteger.class && c != PyBoolean.class) { + throw Py.TypeError(String.format("__nonzero__ should return bool or int, returned %s", + self_type.getName())); + } + return o.__nonzero__(); + } + + @Override + public boolean __contains__(PyObject o) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__contains__"); + if (impl == null) { + return super.__contains__(o); + } + return impl.__get__(this, self_type).__call__(o).__nonzero__(); + } + + @Override + public int __len__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__len__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(); + if (res instanceof PyInteger) { + return ((PyInteger)res).getValue(); + } + throw Py.TypeError("__len__ should return a int"); + } + return super.__len__(); + } + + @Override + public PyObject __iter__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__iter__"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(); + } + impl = self_type.lookup("__getitem__"); + if (impl == null) { + return super.__iter__(); + } + return new PySequenceIter(this); + } + + @Override + public PyObject __iternext__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("next"); + if (impl != null) { + try { + return impl.__get__(this, self_type).__call__(); + } catch (PyException exc) { + if (exc.match(Py.StopIteration)) { + return null; + } + throw exc; + } + } + return super.__iternext__(); // ??? + } + + @Override + public PyObject __finditem__(PyObject key) { // ??? + PyType self_type = getType(); + PyObject impl = self_type.lookup("__getitem__"); + if (impl != null) { + try { + return impl.__get__(this, self_type).__call__(key); + } catch (PyException exc) { + if (exc.match(Py.LookupError)) { + return null; + } + throw exc; + } + } + return super.__finditem__(key); + } + + @Override + public PyObject __finditem__(int key) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__getitem__"); + if (impl != null) { + try { + return impl.__get__(this, self_type).__call__(new PyInteger(key)); + } catch (PyException exc) { + if (exc.match(Py.LookupError)) { + return null; + } + throw exc; + } + } + return super.__finditem__(key); + } + + @Override + public PyObject __getitem__(PyObject key) { + // Same as __finditem__, without swallowing LookupErrors. This allows + // __getitem__ implementations written in Python to raise custom + // exceptions (such as subclasses of KeyError). + // + // We are forced to duplicate the code, instead of defining __finditem__ + // in terms of __getitem__. That's because PyObject defines __getitem__ + // in terms of __finditem__. Therefore, we would end with an infinite + // loop when self_type.lookup("__getitem__") returns null: + // + // __getitem__ -> super.__getitem__ -> __finditem__ -> __getitem__ + // + // By duplicating the (short) lookup and call code, we are safe, because + // the call chains will be: + // + // __finditem__ -> super.__finditem__ + // + // __getitem__ -> super.__getitem__ -> __finditem__ -> super.__finditem__ + + PyType self_type = getType(); + PyObject impl = self_type.lookup("__getitem__"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(key); + } + return super.__getitem__(key); + } + + @Override + public void __setitem__(PyObject key, PyObject value) { // ??? + PyType self_type = getType(); + PyObject impl = self_type.lookup("__setitem__"); + if (impl != null) { + impl.__get__(this, self_type).__call__(key, value); + return; + } + super.__setitem__(key, value); + } + + @Override + public PyObject __getslice__(PyObject start, PyObject stop, PyObject step) { // ??? + if (step != null) { + return __getitem__(new PySlice(start, stop, step)); + } + PyType self_type = getType(); + PyObject impl = self_type.lookup("__getslice__"); + if (impl != null) { + PyObject[] indices = PySlice.indices2(this, start, stop); + return impl.__get__(this, self_type).__call__(indices[0], indices[1]); + } + return super.__getslice__(start, stop, step); + } + + @Override + public void __setslice__(PyObject start, PyObject stop, PyObject step, PyObject value) { + if (step != null) { + __setitem__(new PySlice(start, stop, step), value); + return; + } + PyType self_type = getType(); + PyObject impl = self_type.lookup("__setslice__"); + if (impl != null) { + PyObject[] indices = PySlice.indices2(this, start, stop); + impl.__get__(this, self_type).__call__(indices[0], indices[1], value); + return; + } + super.__setslice__(start, stop, step, value); + } + + @Override + public void __delslice__(PyObject start, PyObject stop, PyObject step) { + if (step != null) { + __delitem__(new PySlice(start, stop, step)); + return; + } + PyType self_type = getType(); + PyObject impl = self_type.lookup("__delslice__"); + if (impl != null) { + PyObject[] indices = PySlice.indices2(this, start, stop); + impl.__get__(this, self_type).__call__(indices[0], indices[1]); + return; + } + super.__delslice__(start, stop, step); + } + + @Override + public void __delitem__(PyObject key) { // ??? + PyType self_type = getType(); + PyObject impl = self_type.lookup("__delitem__"); + if (impl != null) { + impl.__get__(this, self_type).__call__(key); + return; + } + super.__delitem__(key); + } + + @Override + public PyObject __call__(PyObject args[], String keywords[]) { + ThreadState ts = Py.getThreadState(); + if (ts.recursion_depth++ > ts.systemState.getrecursionlimit()) { + throw Py.RuntimeError("maximum __call__ recursion depth exceeded"); + } + try { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__call__"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(args, keywords); + } + return super.__call__(args, keywords); + } finally { + --ts.recursion_depth; + } + } + + @Override + public PyObject __findattr_ex__(String name) { + return Deriveds.__findattr_ex__(this, name); + } + + @Override + public void __setattr__(String name, PyObject value) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__setattr__"); + if (impl != null) { + impl.__get__(this, self_type).__call__(PyString.fromInterned(name), value); + return; + } + super.__setattr__(name, value); + } + + @Override + public void __delattr__(String name) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__delattr__"); + if (impl != null) { + impl.__get__(this, self_type).__call__(PyString.fromInterned(name)); + return; + } + super.__delattr__(name); + } + + @Override + public PyObject __get__(PyObject obj, PyObject type) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__get__"); + if (impl != null) { + if (obj == null) { + obj = Py.None; + } + if (type == null) { + type = Py.None; + } + return impl.__get__(this, self_type).__call__(obj, type); + } + return super.__get__(obj, type); + } + + @Override + public void __set__(PyObject obj, PyObject value) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__set__"); + if (impl != null) { + impl.__get__(this, self_type).__call__(obj, value); + return; + } + super.__set__(obj, value); + } + + @Override + public void __delete__(PyObject obj) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__delete__"); + if (impl != null) { + impl.__get__(this, self_type).__call__(obj); + return; + } + super.__delete__(obj); + } + + @Override + public PyObject __pow__(PyObject other, PyObject modulo) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__pow__"); + if (impl != null) { + PyObject res; + if (modulo == null) { + res = impl.__get__(this, self_type).__call__(other); + } else { + res = impl.__get__(this, self_type).__call__(other, modulo); + } + if (res == Py.NotImplemented) { + return null; + } + return res; + } + return super.__pow__(other, modulo); + } + + @Override + public void dispatch__init__(PyObject[] args, String[] keywords) { + Deriveds.dispatch__init__(this, args, keywords); + } + + @Override + public PyObject __index__() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__index__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(); + if (res instanceof PyInteger || res instanceof PyLong) { + return res; + } + throw Py.TypeError(String.format("__index__ returned non-(int,long) (type %s)", res + .getType().fastGetName())); + } + return super.__index__(); + } + + @Override + public Object __tojava__(Class c) { + // If we are not being asked by the "default" conversion to java, then + // we can provide this as the result, as long as it is a instance of the + // specified class. Without this, derived.__tojava__(PyObject.class) + // would broke. (And that's not pure speculation: PyReflectedFunction's + // ReflectedArgs asks for things like that). + if ((c != Object.class) && (c != Serializable.class) && (c.isInstance(this))) { + return this; + } + // Otherwise, we call the derived __tojava__, if it exists: + PyType self_type = getType(); + PyObject impl = self_type.lookup("__tojava__"); + if (impl != null) { + return impl.__get__(this, self_type).__call__(Py.java2py(c)).__tojava__(Object.class); + } + return super.__tojava__(c); + } + + @Override + public Object __coerce_ex__(PyObject o) { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__coerce__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(o); + if (res == Py.NotImplemented) { + return Py.None; + } + if (!(res instanceof PyTuple)) { + throw Py.TypeError("__coerce__ didn't return a 2-tuple"); + } + return ((PyTuple)res).getArray(); + } + return super.__coerce_ex__(o); + } + + @Override + public String toString() { + PyType self_type = getType(); + PyObject impl = self_type.lookup("__repr__"); + if (impl != null) { + PyObject res = impl.__get__(this, self_type).__call__(); + if (!(res instanceof PyString)) { + throw Py.TypeError("__repr__ returned non-string (type " + + res.getType().fastGetName() + ")"); + } + return ((PyString)res).toString(); + } + return super.toString(); + } + +} diff --git a/src/templates/PyFileIO.derived b/src/templates/_io.FileIO.derived rename from src/templates/PyFileIO.derived rename to src/templates/_io.FileIO.derived diff --git a/src/templates/mappings b/src/templates/mappings --- a/src/templates/mappings +++ b/src/templates/mappings @@ -8,7 +8,6 @@ BaseException.derived:org.python.core.PyBaseExceptionDerived ClasspathPyImporter.derived:org.python.core.ClasspathPyImporterDerived -PyFileIO.derived:org.python.modules._io.PyFileIODerived array.derived:org.python.core.PyArrayDerived bytearray.derived:org.python.core.PyByteArrayDerived classmethod.derived:org.python.core.PyClassMethodDerived @@ -149,3 +148,4 @@ op_USub.derived:org.python.antlr.op.USubDerived _io._IOBase.derived:org.python.modules._io.PyIOBaseDerived _io._RawIOBase.derived:org.python.modules._io.PyRawIOBaseDerived +_io.FileIO.derived:org.python.modules._io.PyFileIODerived -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Sun Dec 9 21:29:47 2012 From: jython-checkins at python.org (jeff.allen) Date: Sun, 9 Dec 2012 21:29:47 +0100 (CET) Subject: [Jython-checkins] =?utf-8?q?jython=3A_Implement_=5Fio=2EPyFileIO?= =?utf-8?q?=2Ereadinto=28=29_and_inherit_read_methods_in_place_of_local?= Message-ID: <3YKJtg6HvszRVP@mail.python.org> http://hg.python.org/jython/rev/580ce865cfb1 changeset: 6897:580ce865cfb1 user: Jeff Allen date: Sun Dec 09 18:38:21 2012 +0000 summary: Implement _io.PyFileIO.readinto() and inherit read methods in place of local implementation. This is part of the progressive change to the hierarchical implementation with delegation. Score in test_io.py is a little lower at fail/error/skip = 19/18/99. files: src/org/python/modules/_io/PyFileIO.java | 190 ++++++---- src/org/python/modules/_io/PyIOBase.java | 27 +- src/org/python/modules/_io/_io.java | 9 - 3 files changed, 122 insertions(+), 104 deletions(-) diff --git a/src/org/python/modules/_io/PyFileIO.java b/src/org/python/modules/_io/PyFileIO.java --- a/src/org/python/modules/_io/PyFileIO.java +++ b/src/org/python/modules/_io/PyFileIO.java @@ -8,8 +8,10 @@ import org.python.core.BuiltinDocs; import org.python.core.Py; import org.python.core.PyArray; +import org.python.core.PyBuffer; import org.python.core.PyInteger; import org.python.core.PyJavaType; +import org.python.core.PyLong; import org.python.core.PyObject; import org.python.core.PyString; import org.python.core.PyType; @@ -102,6 +104,95 @@ + (updating ? "+" : ""); } + /* + * =========================================================================================== + * Exposed methods in the order they appear in CPython's fileio.c method table + * =========================================================================================== + */ + + // _RawIOBase.read is correct for us + // _RawIOBase.readall is correct for us + + @Override + public PyObject readinto(PyObject buf) { + return FileIO_readinto(buf); + } + + @ExposedMethod(doc = readinto_doc) + final PyLong FileIO_readinto(PyObject buf) { + // Check we can do this + _checkClosed(); + _checkReadable(); + // Perform the operation through a buffer view on the object + PyBuffer pybuf = writablePyBuffer(buf); + try { + PyBuffer.Pointer bp = pybuf.getBuf(); + ByteBuffer byteBuffer = ByteBuffer.wrap(bp.storage, bp.offset, pybuf.getLen()); + int count; + synchronized (ioDelegate) { + count = ioDelegate.readinto(byteBuffer); + } + return new PyLong(count); + } finally { + // Must unlock the PyBuffer view from client's object + pybuf.release(); + } + } + + @Override + public PyObject write(PyObject buf) { + return FileIO_write(buf); + } + + @ExposedMethod(doc = write_doc) + final PyLong FileIO_write(PyObject obj) { + _checkWritable(); + // Get or synthesise a buffer API on the object to be written + PyBuffer pybuf = readablePyBuffer(obj); + try { + // Access the data as a java.nio.ByteBuffer [pos:limit] within possibly larger array + PyBuffer.Pointer bp = pybuf.getBuf(); + ByteBuffer byteBuffer = ByteBuffer.wrap(bp.storage, bp.offset, pybuf.getLen()); + int count; + synchronized (ioDelegate) { + count = ioDelegate.write(byteBuffer); + } + return new PyLong(count); + } finally { + // Even if that went badly, we should release the lock on the client buffer + pybuf.release(); + } + } + + @Override + public long truncate() { + return _truncate(); + } + + @Override + public long truncate(long size) { + return _truncate(size); + } + + @ExposedMethod(defaults = "null", doc = truncate_doc) + final long FileIO_truncate(PyObject size) { + return (size != null) ? _truncate(size.asLong()) : _truncate(); + } + + /** Common to FileIO_truncate(null) and truncate(). */ + private final long _truncate() { + synchronized (ioDelegate) { + return ioDelegate.truncate(ioDelegate.tell()); + } + } + + /** Common to FileIO_truncate(size) and truncate(size). */ + private final long _truncate(long size) { + synchronized (ioDelegate) { + return ioDelegate.truncate(size); + } + } + /** * Close the underlying ioDelegate only if closefd was specified as (or defaulted * to) True. @@ -133,7 +224,7 @@ @ExposedMethod(defaults = {"0"}, doc = BuiltinDocs.file_seek_doc) final synchronized PyObject FileIO_seek(long pos, int how) { - checkClosed(); + _checkClosed(); return Py.java2py(ioDelegate.seek(pos, how)); } @@ -152,7 +243,7 @@ @ExposedMethod(doc = BuiltinDocs.file_tell_doc) final synchronized long FileIO_tell() { - checkClosed(); + _checkClosed(); return ioDelegate.tell(); } @@ -162,35 +253,6 @@ } @Override - public long truncate() { - return _truncate(); - } - - @Override - public long truncate(long size) { - return _truncate(size); - } - - @ExposedMethod(defaults = "null", doc = truncate_doc) - final long FileIO_truncate(PyObject size) { - return (size != null) ? _truncate(size.asLong()) : _truncate(); - } - - /** Common to FileIO_truncate(null) and truncate(). */ - private final long _truncate() { - synchronized (ioDelegate) { - return ioDelegate.truncate(ioDelegate.tell()); - } - } - - /** Common to FileIO_truncate(size) and truncate(size). */ - private final long _truncate(long size) { - synchronized (ioDelegate) { - return ioDelegate.truncate(size); - } - } - - @Override public boolean isatty() { return FileIO_isatty(); } @@ -220,55 +282,20 @@ return PyJavaType.wrapJavaObject(ioDelegate.fileno()); } - @ExposedMethod(defaults = {"-1"}, doc = BuiltinDocs.file_read_doc) - final synchronized PyString FileIO_read(int size) { - checkClosed(); - ByteBuffer buf = ioDelegate.read(size); - return new PyString(StringUtil.fromBytes(buf)); + // fileio.c has no flush(), but why not, when there is fdflush()? + // And it is a no-op for Jython io.FileIO, but why when there is FileChannel.force()? + @Override + public void flush() { + FileIO_flush(); } - @Override - public PyString read(int size) { - return FileIO_read(size); - } - - @ExposedMethod(doc = BuiltinDocs.file_read_doc) - final synchronized PyString FileIO_readall() { - return FileIO_read(-1); - } - - /** - * Return a String for writing to the underlying file from obj. - */ - private String asWritable(PyObject obj, String message) { - if (obj instanceof PyUnicode) { - return ((PyUnicode)obj).encode(); - } else if (obj instanceof PyString) { - return ((PyString)obj).getString(); - } else if (obj instanceof PyArray) { - return ((PyArray)obj).tostring(); - } else if (obj instanceof BaseBytes) { - return StringUtil.fromBytes((BaseBytes)obj); + @ExposedMethod(doc = "Flush write buffers.") + final void FileIO_flush() { + if (writable()) { + // Check for *downstream* close. (Locally, closed means "closed to client actions".) + ioDelegate.checkClosed(); + ioDelegate.flush(); } - if (message == null) { - message = - String.format("argument 1 must be string or buffer, not %.200s", obj.getType() - .fastGetName()); - } - throw Py.TypeError(message); - } - - @ExposedMethod(doc = BuiltinDocs.file_write_doc) - final PyObject FileIO_write(PyObject obj) { - String writable = asWritable(obj, null); - byte[] bytes = StringUtil.toBytes(writable); - int written = write(ByteBuffer.wrap(bytes)); - return new PyInteger(written); - } - - final synchronized int write(ByteBuffer buf) { - checkClosed(); - return ioDelegate.write(buf); } @ExposedMethod(names = {"__str__", "__repr__"}, doc = BuiltinDocs.file___str___doc) @@ -285,13 +312,4 @@ return FileIO_toString(); } - private void checkClosed() { - ioDelegate.checkClosed(); - } - - @ExposedGet(name = "closed", doc = BuiltinDocs.file_closed_doc) - public boolean getClosed() { - return ioDelegate.closed(); - } - } diff --git a/src/org/python/modules/_io/PyIOBase.java b/src/org/python/modules/_io/PyIOBase.java --- a/src/org/python/modules/_io/PyIOBase.java +++ b/src/org/python/modules/_io/PyIOBase.java @@ -201,12 +201,21 @@ @ExposedMethod(doc = close_doc) final void _IOBase_close() { if (!__closed) { - // Cancel the wake-up call - closer.dismiss(); - // Close should invoke (possibly overridden) flush here. - invoke("flush"); - // Prevent further access from upstream - __closed = true; + /* + * The downstream file (file descriptor) will sometimes have been closed by another + * client. This should raise an error for us, (since data potentially in our buffers + * will be discarded) but we still must end up closed. the local close comes after the + * flush, in case operations within flush() test for "the wrong kind of closed". + */ + try { + // Cancel the wake-up call + closer.dismiss(); + // Close should invoke (possibly overridden) flush here. + invoke("flush"); + } finally { + // Become closed to further client operations (other than certain enquiries) + __closed = true; + } } } @@ -441,7 +450,7 @@ @ExposedMethod(defaults = "null", doc = readline_doc) final PyObject _IOBase_readline(PyObject limit) { - if (limit == null || limit==Py.None) { + if (limit == null || limit == Py.None) { return _readline(-1); } else if (limit.isIndex()) { return _readline(limit.asInt()); @@ -587,7 +596,7 @@ public PyObject __iter__() { _checkClosed(); // Not like this, in spite of what base comment says, because file *is* an iterator - // return new PySequenceIter(this); + // return new PySequenceIter(this); return this; } @@ -614,7 +623,7 @@ int h = 0; - if (hint==null || hint == Py.None) { + if (hint == null || hint == Py.None) { return new PyList(this); } else if (!hint.isIndex()) { diff --git a/src/org/python/modules/_io/_io.java b/src/org/python/modules/_io/_io.java --- a/src/org/python/modules/_io/_io.java +++ b/src/org/python/modules/_io/_io.java @@ -221,15 +221,6 @@ + "Argument names are not part of the specification, and only the arguments\n" + "of open() are intended to be used as keyword arguments.\n"; -// + "\n" -// + "data:\n" -// + "\n" -// + "DEFAULT_BUFFER_SIZE\n" -// + "\n" -// + " An int containing the default buffer size used by the module's buffered\n" -// + " I/O classes. open() uses the file's blksize (as obtained by os.stat) if\n" -// + " possible.\n"; - public static final String __doc__open = "Open file and return a stream. Raise IOError upon failure.\n" + "\n" + "file is either a text or byte string giving the name (and the path\n" -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Sun Dec 9 21:29:49 2012 From: jython-checkins at python.org (jeff.allen) Date: Sun, 9 Dec 2012 21:29:49 +0100 (CET) Subject: [Jython-checkins] =?utf-8?q?jython=3A_Fix_return_value_of_io=2EFi?= =?utf-8?q?leIO=2Ereadinto=28=29_so_EOF=3D0_as_contract=2E?= Message-ID: <3YKJtj1K6fzRW9@mail.python.org> http://hg.python.org/jython/rev/2ccd73a00a86 changeset: 6898:2ccd73a00a86 user: Jeff Allen date: Sun Dec 09 19:46:05 2012 +0000 summary: Fix return value of io.FileIO.readinto() so EOF=0 as contract. test_io scores now fail/error/skip = 17/18/99 files: src/org/python/core/io/FileIO.java | 10 ++++++---- 1 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/org/python/core/io/FileIO.java b/src/org/python/core/io/FileIO.java --- a/src/org/python/core/io/FileIO.java +++ b/src/org/python/core/io/FileIO.java @@ -241,15 +241,16 @@ checkClosed(); checkReadable(); try { - return fileChannel.read(buf); + int n = fileChannel.read(buf); + return n > 0 ? n : 0; } catch (IOException ioe) { throw Py.IOError(ioe); } } /** - * Read bytes into each of the specified ByteBuffers via scatter - * i/o. + * Read bytes into each of the specified ByteBuffers via scatter i/o. Returns number of bytes + * read (0 for EOF). * * @param bufs {@inheritDoc} * @return {@inheritDoc} @@ -259,7 +260,8 @@ checkClosed(); checkReadable(); try { - return fileChannel.read(bufs); + long n = fileChannel.read(bufs); + return n > 0L ? n : 0L; } catch (IOException ioe) { throw Py.IOError(ioe); } -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Sun Dec 16 01:20:03 2012 From: jython-checkins at python.org (jeff.allen) Date: Sun, 16 Dec 2012 01:20:03 +0100 (CET) Subject: [Jython-checkins] =?utf-8?q?jython=3A_Support_file_descriptor_in_?= =?utf-8?q?=5Fio=2Eopen_and_=5Fio=2EFileIO?= Message-ID: <3YP5jb455gzNn6@mail.python.org> http://hg.python.org/jython/rev/537524cf052e changeset: 6899:537524cf052e user: Jeff Allen date: Tue Dec 11 23:21:04 2012 +0000 summary: Support file descriptor in _io.open and _io.FileIO Replaced the constructors in PyFileIO to accept Jython-style fds. Corresponding changes to open() and some tests. Also, a lot of fun with mode strings. test_io.py now scores fail/error/skip = 15/15/99. files: Lib/test/test_fileio.py | 5 +- Lib/test/test_io.py | 5 +- src/org/python/modules/_io/OpenMode.java | 50 +- src/org/python/modules/_io/PyFileIO.java | 201 +- src/org/python/modules/_io/PyFileIODerived.java | 1299 +++++---- src/org/python/modules/_io/_io.java | 12 +- src/templates/_io.FileIO.derived | 7 +- 7 files changed, 954 insertions(+), 625 deletions(-) diff --git a/Lib/test/test_fileio.py b/Lib/test/test_fileio.py --- a/Lib/test/test_fileio.py +++ b/Lib/test/test_fileio.py @@ -335,11 +335,10 @@ def testInvalidFd(self): if is_jython: - self.assertRaises(TypeError, _FileIO, -10) - self.assertRaises(TypeError, _FileIO, make_bad_fd()) + self.assertRaises(TypeError, _FileIO, -10) # file descriptor not int in Jython else: self.assertRaises(ValueError, _FileIO, -10) - self.assertRaises(OSError, _FileIO, make_bad_fd()) + self.assertRaises(OSError, _FileIO, make_bad_fd()) if sys.platform == 'win32': import msvcrt self.assertRaises(IOError, msvcrt.get_osfhandle, make_bad_fd()) diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -2547,9 +2547,8 @@ f = self.open(support.TESTFN, "w+") self.assertEqual(f.mode, "w+") - #If next doesn't matter - does it matter it doesn't work in Jython? - #self.assertEqual(f.buffer.mode, "rb+") # Does it really matter? - #self.assertEqual(f.buffer.raw.mode, "rb+") + self.assertEqual(f.buffer.mode, "rb+") # Does it really matter? + self.assertEqual(f.buffer.raw.mode, "rb+") g = self.open(f.fileno(), "wb", closefd=False) self.assertEqual(g.mode, "wb") diff --git a/src/org/python/modules/_io/OpenMode.java b/src/org/python/modules/_io/OpenMode.java --- a/src/org/python/modules/_io/OpenMode.java +++ b/src/org/python/modules/_io/OpenMode.java @@ -40,6 +40,12 @@ /** Set true when any invalid symbol or combination is discovered */ public boolean invalid; + /** Set true when stream must be readable = reading | updating */ + public boolean readable; + + /** Set true when stream must be writable = writing | updating | appending */ + public boolean writable; + /** * Error message describing the way in which the mode is invalid, or null if no problem has been * found. This field may be set by the constructor (in the case of duplicate or unrecognised @@ -133,6 +139,8 @@ // Implications reading |= universal; + readable = reading | updating; + writable = writing | updating | appending; // Standard tests if (!invalid) { @@ -175,10 +183,9 @@ } /** - * Call {@link #validate()} and raise an exception if the mode string is not valid, - * as signalled by either {@link #invalid} - * or {@link #other} being true after that call. If no more specific message has been assigned in - * {@link #message}, report the original mode string. + * Call {@link #validate()} and raise an exception if the mode string is not valid, as signalled + * by either {@link #invalid} or {@link #other} being true after that call. If no + * more specific message has been assigned in {@link #message}, report the original mode string. * * @throws PyException (ValueError) if the mode string was invalid. */ @@ -200,7 +207,29 @@ } } - public String rawmode() { + /** + * The mode string that a raw file should claim to have, when initialised with the present mode. + * Note that this is not the same as the open mode because it omits the text-based attributes, + * even if set, and always asserts it is binary. + * + * @return "rb", "rb+", or "wb". + */ + public String raw() { + if (readable) { + return writable ? "rb+" : "rb"; + } else { + return "wb"; + } + } + + /** + * The mode string we need when constructing a FileIO initialised with the present + * mode. Note that this is not the same as the full open mode because it omits the text-based + * attributes, and not the same as {@link #raw()}. + * + * @return "r", "w", or "a" with optional "+". + */ + public String forFileIO() { StringBuilder m = new StringBuilder(2); if (appending) { m.append('a'); @@ -215,6 +244,17 @@ return m.toString(); } + /** + * The mode string that a text file should claim to have, when initialised with the present + * mode. Note that this only contains text-based attributes. Since mode 't' has no effect, + * except to produce an error if specified with 'b', we don't reproduce it. + * + * @return "", or "U". + */ + public String text() { + return universal ? "U" : ""; + } + @Override public String toString() { StringBuilder m = new StringBuilder(4); diff --git a/src/org/python/modules/_io/PyFileIO.java b/src/org/python/modules/_io/PyFileIO.java --- a/src/org/python/modules/_io/PyFileIO.java +++ b/src/org/python/modules/_io/PyFileIO.java @@ -2,107 +2,172 @@ package org.python.modules._io; import java.nio.ByteBuffer; +import java.nio.channels.Channel; +import java.nio.channels.FileChannel; import org.python.core.ArgParser; -import org.python.core.BaseBytes; import org.python.core.BuiltinDocs; import org.python.core.Py; -import org.python.core.PyArray; import org.python.core.PyBuffer; -import org.python.core.PyInteger; import org.python.core.PyJavaType; import org.python.core.PyLong; +import org.python.core.PyNewWrapper; import org.python.core.PyObject; import org.python.core.PyString; import org.python.core.PyType; import org.python.core.PyUnicode; import org.python.core.io.FileIO; -import org.python.core.util.StringUtil; +import org.python.core.io.RawIOBase; import org.python.expose.ExposedGet; import org.python.expose.ExposedMethod; import org.python.expose.ExposedNew; import org.python.expose.ExposedType; +import com.kenai.constantine.platform.Errno; + @ExposedType(name = "_io.FileIO", base = PyRawIOBase.class) public class PyFileIO extends PyRawIOBase { public static final PyType TYPE = PyType.fromClass(PyFileIO.class); - private FileIO ioDelegate; // XXX final? - private PyObject name; + /** The FileIO to which we delegate operations not complete locally. */ + private final FileIO ioDelegate; + + /** The name of the file */ + @ExposedGet(doc = BuiltinDocs.file_name_doc) + protected PyObject name; + private Boolean seekable; + /** Whether to close the underlying stream on closing this object. */ @ExposedGet - public boolean closefd; + public final boolean closefd; - /** The mode string */ - @ExposedGet(doc = BuiltinDocs.file_mode_doc) - public String mode; + /** The mode as given to the constructor */ + private OpenMode openMode; + @ExposedGet(doc = BuiltinDocs.file_mode_doc) // and as a PyString + public PyString mode() { return new PyString(openMode.raw()); } + private static final PyString defaultMode = new PyString("r"); - public PyFileIO() { - super(TYPE); + /** + * Construct an open _io.FileIO starting with an object that may be a file name or + * a file descriptor (actually a {@link RawIOBase}). Only the relevant flags within the parsed + * mode object are consulted (so that flags meaningful to this sub-class need not be processed + * out). + * + * @param fd on which this should be constructed + * @param mode type of access specified + * @param closefd if false, do not close fd on call to + * close() + */ + public PyFileIO(PyObject file, OpenMode mode, boolean closefd) { + this(TYPE, file, mode, closefd); } - public PyFileIO(PyType subType) { - super(subType); + /** + * Construct an open _io.FileIO for a sub-class constructor starting with an object + * that may be a file name or a file descriptor (actually a {@link RawIOBase}). Only the + * relevant flags within the parsed mode object are consulted (so that flags meaningful to this + * sub-class need not be processed out). + * + * @param subtype for which construction is occurring + * @param file on which this should be constructed + * @param mode type of access specified + * @param closefd if false, do not close file on call to + * close() + */ + public PyFileIO(PyType subtype, PyObject file, OpenMode mode, boolean closefd) { + super(subtype); + this.ioDelegate = getFileIO(file, mode, closefd); + this.closefd = closefd; + this.name = file; + this.openMode = mode; } - public PyFileIO(String name, String mode, boolean closefd) { - this(); - FileIO___init__(Py.newString(name), mode, closefd); + /** + * Helper function that turns the arguments of the most general constructor, or + * __new__, into a {@link FileIO}. This places the logic of those several + * operations in one place. + *

+ * In many cases (such as construction from a file name, the FileIO is a newly-opened file. When + * the file object passed in is a file descriptor, the FileIO may be created to wrap that + * existing stream. + * + * @param file name or descriptor + * @param mode parsed file open mode + * @param closefd must be true if file is in fact a name (checked, not used) + * @return + */ + private static FileIO getFileIO(PyObject file, OpenMode mode, boolean closefd) { + + if (file instanceof PyString) { + // Open a file by name + if (!closefd) { + throw Py.ValueError("Cannot use closefd=False with file name"); + } + String name = file.asString(); + return new FileIO(name, mode.forFileIO()); + + } else { + /* + * Build an _io.FileIO from an existing "file descriptor", which we may or may not want + * closed at the end. A CPython file descriptor is an int, but this is not the natural + * choice in Jython, and file descriptors should be treated as opaque. + */ + Object fd = file.__tojava__(Object.class); + if (fd instanceof RawIOBase) { + // It is the "Jython file descriptor" from which we can get a channel. + /* + * If the argument were a FileIO, could we return it directly? I think not: there + * would be a problem with close and closefd=False since it is not the PyFileIO that + * keeps state. + */ + Channel channel = ((RawIOBase)fd).getChannel(); + if (channel instanceof FileChannel) { + if (channel.isOpen()){ + FileChannel fc = (FileChannel)channel; + return new FileIO(fc, mode.forFileIO()); + } else { + // File not open (we have to check as FileIO doesn't) + throw Py.OSError(Errno.EBADF); + } + } + } + // The file was a type we don't know how to use + throw Py.TypeError(String.format("invalid file: %s", file.__repr__().asString())); + } } - public PyFileIO(String name, String mode) { - this(name, mode, true); + private static final String[] openArgs = {"file", "mode", "closefd"}; + + /** + * Create a {@link PyFileIO} and its FileIO delegate from the arguments. + */ + @ExposedNew + static PyObject FileIO___new__(PyNewWrapper new_, boolean init, PyType subtype, + PyObject[] args, String[] keywords) { + ArgParser ap = new ArgParser("FileIO", args, keywords, openArgs, 1); + PyObject file = ap.getPyObject(0); + PyObject m = ap.getPyObject(1, defaultMode); + boolean closefd = Py.py2boolean(ap.getPyObject(2, Py.True)); + + // Decode the mode string and check it + OpenMode mode = new OpenMode(m.asString()) { + + { + invalid |= universal | text; // These other modes are invalid + } + }; + mode.checkValid(); + + if (subtype == TYPE) { + return new PyFileIO(subtype, file, mode, closefd); + } else { + return new PyFileIODerived(subtype, file, mode, closefd); + } + } - @ExposedNew - @ExposedMethod(doc = BuiltinDocs.file___init___doc) - final void FileIO___init__(PyObject[] args, String[] kwds) { - ArgParser ap = - new ArgParser("file", args, kwds, new String[] {"name", "mode", "closefd"}, 1); - PyObject name = ap.getPyObject(0); - if (!(name instanceof PyString)) { - throw Py.TypeError("coercing to Unicode: need string, '" + name.getType().fastGetName() - + "' type found"); - } - String mode = ap.getString(1, "r"); - boolean closefd = Py.py2boolean(ap.getPyObject(2, Py.True)); - // TODO: make this work with file channels so closefd=False can be used - if (!closefd) { - throw Py.ValueError("Cannot use closefd=False with file name"); - } - - FileIO___init__((PyString)name, mode, closefd); - } - - private void FileIO___init__(PyString name, String mode, boolean closefd) { - mode = parseMode(mode); - this.name = name; - this.mode = mode; - this.closefd = closefd; - this.ioDelegate = new FileIO(name, mode.replaceAll("b", "")); - } - - private String parseMode(String mode) { - if (mode.length() == 0) { - throw Py.ValueError("empty mode string"); - } - - String origMode = mode; - if ("rwa".indexOf(mode.charAt(0)) == -1) { - throw Py.ValueError("mode string must begin with one of 'r', 'w', 'a' or 'U', not '" - + origMode + "'"); - } - - boolean reading = mode.contains("r"); - boolean writing = mode.contains("w"); - boolean appending = mode.contains("a"); - boolean updating = mode.contains("+"); - - return (reading ? "r" : "") + (writing ? "w" : "") + (appending ? "a" : "") + "b" - + (updating ? "+" : ""); - } /* * =========================================================================================== @@ -302,9 +367,9 @@ final String FileIO_toString() { if (name instanceof PyUnicode) { String escapedName = PyString.encode_UnicodeEscape(name.toString(), false); - return String.format("<_io.FileIO name='%s', mode='%s'>", escapedName, mode); + return String.format("<_io.FileIO name='%s', mode='%s'>", escapedName, mode()); } - return String.format("<_io.FileIO name='%s', mode='%s'>", name, mode); + return String.format("<_io.FileIO name='%s', mode='%s'>", name, mode()); } @Override diff --git a/src/org/python/modules/_io/PyFileIODerived.java b/src/org/python/modules/_io/PyFileIODerived.java --- a/src/org/python/modules/_io/PyFileIODerived.java +++ b/src/org/python/modules/_io/PyFileIODerived.java @@ -1,4 +1,4 @@ -/* Generated file, do not modify. See jython/src/templates/gderived.py. */ +/* Generated file, do not modify. See jython/src/templates/gderived.py. */ package org.python.modules._io; import java.io.Serializable; @@ -7,7 +7,6 @@ import org.python.core.Py; import org.python.core.PyBoolean; import org.python.core.PyComplex; -import org.python.core.PyDictionary; import org.python.core.PyException; import org.python.core.PyFloat; import org.python.core.PyInteger; @@ -16,7 +15,6 @@ import org.python.core.PySequenceIter; import org.python.core.PySlice; import org.python.core.PyString; -import org.python.core.PyStringMap; import org.python.core.PyTuple; import org.python.core.PyType; import org.python.core.PyUnicode; @@ -31,203 +29,168 @@ } @Override - public void setSlot(int index, PyObject value) { - slots[index] = value; + public void setSlot(int index,PyObject value) { + slots[index]=value; } - private PyObject[] slots; + private PyObject[]slots; - private PyObject dict; - - @Override - public PyObject fastGetDict() { - return dict; - } - - @Override - public PyObject getDict() { - return dict; - } - - @Override - public void setDict(PyObject newDict) { - if (newDict instanceof PyStringMap || newDict instanceof PyDictionary) { - dict = newDict; - } else { - throw Py.TypeError("__dict__ must be set to a Dictionary " - + newDict.getClass().getName()); - } - } - - @Override - public void delDict() { - // deleting an object's instance dict makes it grow a new one - dict = new PyStringMap(); - } - - public PyFileIODerived(PyType subtype) { - super(subtype); - slots = new PyObject[subtype.getNumSlots()]; - dict = subtype.instDict(); + public PyFileIODerived(PyType subtype,PyObject file,OpenMode mode,boolean closefd) { + super(subtype,file,mode,closefd); + slots=new PyObject[subtype.getNumSlots()]; } @Override public PyString __str__() { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__str__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(); + PyType self_type=getType(); + PyObject impl=self_type.lookup("__str__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(); if (res instanceof PyString) { - return (PyString)res; + return(PyString)res; } - throw Py.TypeError("__str__" + " returned non-" + "string" + " (type " - + res.getType().fastGetName() + ")"); + throw Py.TypeError("__str__"+" returned non-"+"string"+" (type "+res.getType().fastGetName()+")"); } return super.__str__(); } @Override public PyString __repr__() { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__repr__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(); + PyType self_type=getType(); + PyObject impl=self_type.lookup("__repr__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(); if (res instanceof PyString) { - return (PyString)res; + return(PyString)res; } - throw Py.TypeError("__repr__" + " returned non-" + "string" + " (type " - + res.getType().fastGetName() + ")"); + throw Py.TypeError("__repr__"+" returned non-"+"string"+" (type "+res.getType().fastGetName()+")"); } return super.__repr__(); } @Override public PyString __hex__() { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__hex__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(); + PyType self_type=getType(); + PyObject impl=self_type.lookup("__hex__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(); if (res instanceof PyString) { - return (PyString)res; + return(PyString)res; } - throw Py.TypeError("__hex__" + " returned non-" + "string" + " (type " - + res.getType().fastGetName() + ")"); + throw Py.TypeError("__hex__"+" returned non-"+"string"+" (type "+res.getType().fastGetName()+")"); } return super.__hex__(); } @Override public PyString __oct__() { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__oct__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(); + PyType self_type=getType(); + PyObject impl=self_type.lookup("__oct__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(); if (res instanceof PyString) { - return (PyString)res; + return(PyString)res; } - throw Py.TypeError("__oct__" + " returned non-" + "string" + " (type " - + res.getType().fastGetName() + ")"); + throw Py.TypeError("__oct__"+" returned non-"+"string"+" (type "+res.getType().fastGetName()+")"); } return super.__oct__(); } @Override public PyFloat __float__() { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__float__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(); + PyType self_type=getType(); + PyObject impl=self_type.lookup("__float__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(); if (res instanceof PyFloat) { - return (PyFloat)res; + return(PyFloat)res; } - throw Py.TypeError("__float__" + " returned non-" + "float" + " (type " - + res.getType().fastGetName() + ")"); + throw Py.TypeError("__float__"+" returned non-"+"float"+" (type "+res.getType().fastGetName()+")"); } return super.__float__(); } @Override public PyComplex __complex__() { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__complex__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(); + PyType self_type=getType(); + PyObject impl=self_type.lookup("__complex__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(); if (res instanceof PyComplex) { - return (PyComplex)res; + return(PyComplex)res; } - throw Py.TypeError("__complex__" + " returned non-" + "complex" + " (type " - + res.getType().fastGetName() + ")"); + throw Py.TypeError("__complex__"+" returned non-"+"complex"+" (type "+res.getType().fastGetName()+")"); } return super.__complex__(); } @Override public PyObject __pos__() { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__pos__"); - if (impl != null) { - return impl.__get__(this, self_type).__call__(); + PyType self_type=getType(); + PyObject impl=self_type.lookup("__pos__"); + if (impl!=null) { + return impl.__get__(this,self_type).__call__(); } return super.__pos__(); } @Override public PyObject __neg__() { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__neg__"); - if (impl != null) { - return impl.__get__(this, self_type).__call__(); + PyType self_type=getType(); + PyObject impl=self_type.lookup("__neg__"); + if (impl!=null) { + return impl.__get__(this,self_type).__call__(); } return super.__neg__(); } @Override public PyObject __abs__() { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__abs__"); - if (impl != null) { - return impl.__get__(this, self_type).__call__(); + PyType self_type=getType(); + PyObject impl=self_type.lookup("__abs__"); + if (impl!=null) { + return impl.__get__(this,self_type).__call__(); } return super.__abs__(); } @Override public PyObject __invert__() { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__invert__"); - if (impl != null) { - return impl.__get__(this, self_type).__call__(); + PyType self_type=getType(); + PyObject impl=self_type.lookup("__invert__"); + if (impl!=null) { + return impl.__get__(this,self_type).__call__(); } return super.__invert__(); } @Override public PyObject __reduce__() { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__reduce__"); - if (impl != null) { - return impl.__get__(this, self_type).__call__(); + PyType self_type=getType(); + PyObject impl=self_type.lookup("__reduce__"); + if (impl!=null) { + return impl.__get__(this,self_type).__call__(); } return super.__reduce__(); } @Override public PyObject __dir__() { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__dir__"); - if (impl != null) { - return impl.__get__(this, self_type).__call__(); + PyType self_type=getType(); + PyObject impl=self_type.lookup("__dir__"); + if (impl!=null) { + return impl.__get__(this,self_type).__call__(); } return super.__dir__(); } @Override public PyObject __add__(PyObject other) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__add__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(other); - if (res == Py.NotImplemented) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__add__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { return null; } return res; @@ -237,11 +200,11 @@ @Override public PyObject __radd__(PyObject other) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__radd__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(other); - if (res == Py.NotImplemented) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__radd__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { return null; } return res; @@ -251,11 +214,11 @@ @Override public PyObject __sub__(PyObject other) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__sub__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(other); - if (res == Py.NotImplemented) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__sub__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { return null; } return res; @@ -265,11 +228,11 @@ @Override public PyObject __rsub__(PyObject other) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__rsub__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(other); - if (res == Py.NotImplemented) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__rsub__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { return null; } return res; @@ -279,11 +242,11 @@ @Override public PyObject __mul__(PyObject other) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__mul__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(other); - if (res == Py.NotImplemented) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__mul__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { return null; } return res; @@ -293,11 +256,11 @@ @Override public PyObject __rmul__(PyObject other) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__rmul__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(other); - if (res == Py.NotImplemented) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__rmul__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { return null; } return res; @@ -307,11 +270,11 @@ @Override public PyObject __div__(PyObject other) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__div__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(other); - if (res == Py.NotImplemented) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__div__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { return null; } return res; @@ -321,11 +284,11 @@ @Override public PyObject __rdiv__(PyObject other) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__rdiv__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(other); - if (res == Py.NotImplemented) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__rdiv__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { return null; } return res; @@ -335,11 +298,11 @@ @Override public PyObject __floordiv__(PyObject other) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__floordiv__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(other); - if (res == Py.NotImplemented) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__floordiv__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { return null; } return res; @@ -349,11 +312,11 @@ @Override public PyObject __rfloordiv__(PyObject other) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__rfloordiv__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(other); - if (res == Py.NotImplemented) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__rfloordiv__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { return null; } return res; @@ -363,11 +326,11 @@ @Override public PyObject __truediv__(PyObject other) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__truediv__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(other); - if (res == Py.NotImplemented) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__truediv__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { return null; } return res; @@ -377,11 +340,11 @@ @Override public PyObject __rtruediv__(PyObject other) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__rtruediv__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(other); - if (res == Py.NotImplemented) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__rtruediv__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { return null; } return res; @@ -391,11 +354,11 @@ @Override public PyObject __mod__(PyObject other) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__mod__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(other); - if (res == Py.NotImplemented) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__mod__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { return null; } return res; @@ -405,11 +368,11 @@ @Override public PyObject __rmod__(PyObject other) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__rmod__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(other); - if (res == Py.NotImplemented) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__rmod__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { return null; } return res; @@ -419,11 +382,11 @@ @Override public PyObject __divmod__(PyObject other) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__divmod__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(other); - if (res == Py.NotImplemented) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__divmod__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { return null; } return res; @@ -433,11 +396,11 @@ @Override public PyObject __rdivmod__(PyObject other) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__rdivmod__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(other); - if (res == Py.NotImplemented) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__rdivmod__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { return null; } return res; @@ -447,11 +410,11 @@ @Override public PyObject __rpow__(PyObject other) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__rpow__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(other); - if (res == Py.NotImplemented) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__rpow__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { return null; } return res; @@ -461,11 +424,11 @@ @Override public PyObject __lshift__(PyObject other) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__lshift__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(other); - if (res == Py.NotImplemented) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__lshift__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { return null; } return res; @@ -475,11 +438,11 @@ @Override public PyObject __rlshift__(PyObject other) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__rlshift__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(other); - if (res == Py.NotImplemented) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__rlshift__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { return null; } return res; @@ -489,11 +452,11 @@ @Override public PyObject __rshift__(PyObject other) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__rshift__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(other); - if (res == Py.NotImplemented) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__rshift__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { return null; } return res; @@ -503,11 +466,11 @@ @Override public PyObject __rrshift__(PyObject other) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__rrshift__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(other); - if (res == Py.NotImplemented) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__rrshift__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { return null; } return res; @@ -517,11 +480,11 @@ @Override public PyObject __and__(PyObject other) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__and__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(other); - if (res == Py.NotImplemented) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__and__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { return null; } return res; @@ -531,11 +494,11 @@ @Override public PyObject __rand__(PyObject other) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__rand__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(other); - if (res == Py.NotImplemented) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__rand__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { return null; } return res; @@ -545,11 +508,11 @@ @Override public PyObject __or__(PyObject other) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__or__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(other); - if (res == Py.NotImplemented) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__or__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { return null; } return res; @@ -559,11 +522,11 @@ @Override public PyObject __ror__(PyObject other) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__ror__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(other); - if (res == Py.NotImplemented) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__ror__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { return null; } return res; @@ -573,11 +536,11 @@ @Override public PyObject __xor__(PyObject other) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__xor__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(other); - if (res == Py.NotImplemented) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__xor__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { return null; } return res; @@ -587,11 +550,11 @@ @Override public PyObject __rxor__(PyObject other) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__rxor__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(other); - if (res == Py.NotImplemented) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__rxor__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { return null; } return res; @@ -601,11 +564,11 @@ @Override public PyObject __lt__(PyObject other) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__lt__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(other); - if (res == Py.NotImplemented) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__lt__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { return null; } return res; @@ -615,11 +578,11 @@ @Override public PyObject __le__(PyObject other) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__le__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(other); - if (res == Py.NotImplemented) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__le__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { return null; } return res; @@ -629,11 +592,11 @@ @Override public PyObject __gt__(PyObject other) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__gt__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(other); - if (res == Py.NotImplemented) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__gt__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { return null; } return res; @@ -643,11 +606,11 @@ @Override public PyObject __ge__(PyObject other) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__ge__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(other); - if (res == Py.NotImplemented) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__ge__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { return null; } return res; @@ -657,11 +620,11 @@ @Override public PyObject __eq__(PyObject other) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__eq__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(other); - if (res == Py.NotImplemented) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__eq__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { return null; } return res; @@ -671,11 +634,11 @@ @Override public PyObject __ne__(PyObject other) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__ne__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(other); - if (res == Py.NotImplemented) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__ne__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { return null; } return res; @@ -685,11 +648,11 @@ @Override public PyObject __format__(PyObject other) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__format__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(other); - if (res == Py.NotImplemented) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__format__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { return null; } return res; @@ -699,11 +662,11 @@ @Override public PyObject __iadd__(PyObject other) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__iadd__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(other); - if (res == Py.NotImplemented) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__iadd__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { return null; } return res; @@ -713,11 +676,11 @@ @Override public PyObject __isub__(PyObject other) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__isub__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(other); - if (res == Py.NotImplemented) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__isub__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { return null; } return res; @@ -727,11 +690,11 @@ @Override public PyObject __imul__(PyObject other) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__imul__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(other); - if (res == Py.NotImplemented) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__imul__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { return null; } return res; @@ -741,11 +704,11 @@ @Override public PyObject __idiv__(PyObject other) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__idiv__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(other); - if (res == Py.NotImplemented) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__idiv__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { return null; } return res; @@ -755,11 +718,11 @@ @Override public PyObject __ifloordiv__(PyObject other) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__ifloordiv__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(other); - if (res == Py.NotImplemented) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__ifloordiv__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { return null; } return res; @@ -769,11 +732,11 @@ @Override public PyObject __itruediv__(PyObject other) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__itruediv__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(other); - if (res == Py.NotImplemented) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__itruediv__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { return null; } return res; @@ -783,11 +746,11 @@ @Override public PyObject __imod__(PyObject other) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__imod__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(other); - if (res == Py.NotImplemented) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__imod__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { return null; } return res; @@ -797,11 +760,11 @@ @Override public PyObject __ipow__(PyObject other) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__ipow__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(other); - if (res == Py.NotImplemented) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__ipow__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { return null; } return res; @@ -811,11 +774,11 @@ @Override public PyObject __ilshift__(PyObject other) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__ilshift__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(other); - if (res == Py.NotImplemented) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__ilshift__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { return null; } return res; @@ -825,11 +788,11 @@ @Override public PyObject __irshift__(PyObject other) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__irshift__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(other); - if (res == Py.NotImplemented) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__irshift__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { return null; } return res; @@ -839,11 +802,11 @@ @Override public PyObject __iand__(PyObject other) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__iand__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(other); - if (res == Py.NotImplemented) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__iand__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { return null; } return res; @@ -853,11 +816,11 @@ @Override public PyObject __ior__(PyObject other) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__ior__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(other); - if (res == Py.NotImplemented) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__ior__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { return null; } return res; @@ -867,11 +830,11 @@ @Override public PyObject __ixor__(PyObject other) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__ixor__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(other); - if (res == Py.NotImplemented) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__ixor__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { return null; } return res; @@ -881,127 +844,126 @@ @Override public PyObject __int__() { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__int__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(); - if (res instanceof PyInteger || res instanceof PyLong) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__int__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(); + if (res instanceof PyInteger||res instanceof PyLong) { return res; } - throw Py.TypeError("__int__" + " should return an integer"); + throw Py.TypeError("__int__"+" should return an integer"); } return super.__int__(); } @Override public PyObject __long__() { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__long__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(); - if (res instanceof PyLong || res instanceof PyInteger) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__long__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(); + if (res instanceof PyLong||res instanceof PyInteger) { return res; } - throw Py.TypeError("__long__" + " returned non-" + "long" + " (type " - + res.getType().fastGetName() + ")"); + throw Py.TypeError("__long__"+" returned non-"+"long"+" (type "+res.getType().fastGetName()+")"); } return super.__long__(); } @Override public int hashCode() { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__hash__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(); + PyType self_type=getType(); + PyObject impl=self_type.lookup("__hash__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(); if (res instanceof PyInteger) { - return ((PyInteger)res).getValue(); - } else if (res instanceof PyLong) { - return ((PyLong)res).getValue().intValue(); - } + return((PyInteger)res).getValue(); + } else + if (res instanceof PyLong) { + return((PyLong)res).getValue().intValue(); + } throw Py.TypeError("__hash__ should return a int"); } - if (self_type.lookup("__eq__") != null || self_type.lookup("__cmp__") != null) { - throw Py.TypeError(String.format("unhashable type: '%.200s'", getType().fastGetName())); + if (self_type.lookup("__eq__")!=null||self_type.lookup("__cmp__")!=null) { + throw Py.TypeError(String.format("unhashable type: '%.200s'",getType().fastGetName())); } return super.hashCode(); } @Override public PyUnicode __unicode__() { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__unicode__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(); + PyType self_type=getType(); + PyObject impl=self_type.lookup("__unicode__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(); if (res instanceof PyUnicode) { - return (PyUnicode)res; + return(PyUnicode)res; } if (res instanceof PyString) { return new PyUnicode((PyString)res); } - throw Py.TypeError("__unicode__" + " should return a " + "unicode"); + throw Py.TypeError("__unicode__"+" should return a "+"unicode"); } return super.__unicode__(); } @Override public int __cmp__(PyObject other) { - PyType self_type = getType(); - PyObject[] where_type = new PyObject[1]; - PyObject impl = self_type.lookup_where("__cmp__", where_type); + PyType self_type=getType(); + PyObject[]where_type=new PyObject[1]; + PyObject impl=self_type.lookup_where("__cmp__",where_type); // Full Compatibility with CPython __cmp__: // If the derived type don't override __cmp__, the // *internal* super().__cmp__ should be called, not the // exposed one. The difference is that the exposed __cmp__ // throws a TypeError if the argument is an instance of the same type. - if (impl == null || where_type[0] == TYPE || Py.isSubClass(TYPE, where_type[0])) { + if (impl==null||where_type[0]==TYPE||Py.isSubClass(TYPE,where_type[0])) { return super.__cmp__(other); } - PyObject res = impl.__get__(this, self_type).__call__(other); - if (res == Py.NotImplemented) { - return -2; + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { + return-2; } - int c = res.asInt(); - return c < 0 ? -1 : c > 0 ? 1 : 0; + int c=res.asInt(); + return c<0?-1:c>0?1:0; } @Override public boolean __nonzero__() { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__nonzero__"); - if (impl == null) { - impl = self_type.lookup("__len__"); - if (impl == null) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__nonzero__"); + if (impl==null) { + impl=self_type.lookup("__len__"); + if (impl==null) { return super.__nonzero__(); } } - PyObject o = impl.__get__(this, self_type).__call__(); - Class c = o.getClass(); - if (c != PyInteger.class && c != PyBoolean.class) { - throw Py.TypeError(String.format("__nonzero__ should return bool or int, returned %s", - self_type.getName())); + PyObject o=impl.__get__(this,self_type).__call__(); + Class c=o.getClass(); + if (c!=PyInteger.class&&c!=PyBoolean.class) { + throw Py.TypeError(String.format("__nonzero__ should return bool or int, returned %s",self_type.getName())); } return o.__nonzero__(); } @Override public boolean __contains__(PyObject o) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__contains__"); - if (impl == null) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__contains__"); + if (impl==null) { return super.__contains__(o); } - return impl.__get__(this, self_type).__call__(o).__nonzero__(); + return impl.__get__(this,self_type).__call__(o).__nonzero__(); } @Override public int __len__() { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__len__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(); + PyType self_type=getType(); + PyObject impl=self_type.lookup("__len__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(); if (res instanceof PyInteger) { - return ((PyInteger)res).getValue(); + return((PyInteger)res).getValue(); } throw Py.TypeError("__len__ should return a int"); } @@ -1010,13 +972,13 @@ @Override public PyObject __iter__() { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__iter__"); - if (impl != null) { - return impl.__get__(this, self_type).__call__(); + PyType self_type=getType(); + PyObject impl=self_type.lookup("__iter__"); + if (impl!=null) { + return impl.__get__(this,self_type).__call__(); } - impl = self_type.lookup("__getitem__"); - if (impl == null) { + impl=self_type.lookup("__getitem__"); + if (impl==null) { return super.__iter__(); } return new PySequenceIter(this); @@ -1024,11 +986,11 @@ @Override public PyObject __iternext__() { - PyType self_type = getType(); - PyObject impl = self_type.lookup("next"); - if (impl != null) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("next"); + if (impl!=null) { try { - return impl.__get__(this, self_type).__call__(); + return impl.__get__(this,self_type).__call__(); } catch (PyException exc) { if (exc.match(Py.StopIteration)) { return null; @@ -1041,11 +1003,11 @@ @Override public PyObject __finditem__(PyObject key) { // ??? - PyType self_type = getType(); - PyObject impl = self_type.lookup("__getitem__"); - if (impl != null) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__getitem__"); + if (impl!=null) { try { - return impl.__get__(this, self_type).__call__(key); + return impl.__get__(this,self_type).__call__(key); } catch (PyException exc) { if (exc.match(Py.LookupError)) { return null; @@ -1058,11 +1020,11 @@ @Override public PyObject __finditem__(int key) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__getitem__"); - if (impl != null) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__getitem__"); + if (impl!=null) { try { - return impl.__get__(this, self_type).__call__(new PyInteger(key)); + return impl.__get__(this,self_type).__call__(new PyInteger(key)); } catch (PyException exc) { if (exc.match(Py.LookupError)) { return null; @@ -1084,7 +1046,7 @@ // in terms of __finditem__. Therefore, we would end with an infinite // loop when self_type.lookup("__getitem__") returns null: // - // __getitem__ -> super.__getitem__ -> __finditem__ -> __getitem__ + // __getitem__ -> super.__getitem__ -> __finditem__ -> __getitem__ // // By duplicating the (short) lookup and call code, we are safe, because // the call chains will be: @@ -1093,95 +1055,95 @@ // // __getitem__ -> super.__getitem__ -> __finditem__ -> super.__finditem__ - PyType self_type = getType(); - PyObject impl = self_type.lookup("__getitem__"); - if (impl != null) { - return impl.__get__(this, self_type).__call__(key); + PyType self_type=getType(); + PyObject impl=self_type.lookup("__getitem__"); + if (impl!=null) { + return impl.__get__(this,self_type).__call__(key); } return super.__getitem__(key); } @Override - public void __setitem__(PyObject key, PyObject value) { // ??? - PyType self_type = getType(); - PyObject impl = self_type.lookup("__setitem__"); - if (impl != null) { - impl.__get__(this, self_type).__call__(key, value); + public void __setitem__(PyObject key,PyObject value) { // ??? + PyType self_type=getType(); + PyObject impl=self_type.lookup("__setitem__"); + if (impl!=null) { + impl.__get__(this,self_type).__call__(key,value); return; } - super.__setitem__(key, value); + super.__setitem__(key,value); } @Override - public PyObject __getslice__(PyObject start, PyObject stop, PyObject step) { // ??? - if (step != null) { - return __getitem__(new PySlice(start, stop, step)); + public PyObject __getslice__(PyObject start,PyObject stop,PyObject step) { // ??? + if (step!=null) { + return __getitem__(new PySlice(start,stop,step)); } - PyType self_type = getType(); - PyObject impl = self_type.lookup("__getslice__"); - if (impl != null) { - PyObject[] indices = PySlice.indices2(this, start, stop); - return impl.__get__(this, self_type).__call__(indices[0], indices[1]); + PyType self_type=getType(); + PyObject impl=self_type.lookup("__getslice__"); + if (impl!=null) { + PyObject[]indices=PySlice.indices2(this,start,stop); + return impl.__get__(this,self_type).__call__(indices[0],indices[1]); } - return super.__getslice__(start, stop, step); + return super.__getslice__(start,stop,step); } @Override - public void __setslice__(PyObject start, PyObject stop, PyObject step, PyObject value) { - if (step != null) { - __setitem__(new PySlice(start, stop, step), value); + public void __setslice__(PyObject start,PyObject stop,PyObject step,PyObject value) { + if (step!=null) { + __setitem__(new PySlice(start,stop,step),value); return; } - PyType self_type = getType(); - PyObject impl = self_type.lookup("__setslice__"); - if (impl != null) { - PyObject[] indices = PySlice.indices2(this, start, stop); - impl.__get__(this, self_type).__call__(indices[0], indices[1], value); + PyType self_type=getType(); + PyObject impl=self_type.lookup("__setslice__"); + if (impl!=null) { + PyObject[]indices=PySlice.indices2(this,start,stop); + impl.__get__(this,self_type).__call__(indices[0],indices[1],value); return; } - super.__setslice__(start, stop, step, value); + super.__setslice__(start,stop,step,value); } @Override - public void __delslice__(PyObject start, PyObject stop, PyObject step) { - if (step != null) { - __delitem__(new PySlice(start, stop, step)); + public void __delslice__(PyObject start,PyObject stop,PyObject step) { + if (step!=null) { + __delitem__(new PySlice(start,stop,step)); return; } - PyType self_type = getType(); - PyObject impl = self_type.lookup("__delslice__"); - if (impl != null) { - PyObject[] indices = PySlice.indices2(this, start, stop); - impl.__get__(this, self_type).__call__(indices[0], indices[1]); + PyType self_type=getType(); + PyObject impl=self_type.lookup("__delslice__"); + if (impl!=null) { + PyObject[]indices=PySlice.indices2(this,start,stop); + impl.__get__(this,self_type).__call__(indices[0],indices[1]); return; } - super.__delslice__(start, stop, step); + super.__delslice__(start,stop,step); } @Override public void __delitem__(PyObject key) { // ??? - PyType self_type = getType(); - PyObject impl = self_type.lookup("__delitem__"); - if (impl != null) { - impl.__get__(this, self_type).__call__(key); + PyType self_type=getType(); + PyObject impl=self_type.lookup("__delitem__"); + if (impl!=null) { + impl.__get__(this,self_type).__call__(key); return; } super.__delitem__(key); } @Override - public PyObject __call__(PyObject args[], String keywords[]) { - ThreadState ts = Py.getThreadState(); - if (ts.recursion_depth++ > ts.systemState.getrecursionlimit()) { + public PyObject __call__(PyObject args[],String keywords[]) { + ThreadState ts=Py.getThreadState(); + if (ts.recursion_depth++>ts.systemState.getrecursionlimit()) { throw Py.RuntimeError("maximum __call__ recursion depth exceeded"); } try { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__call__"); - if (impl != null) { - return impl.__get__(this, self_type).__call__(args, keywords); + PyType self_type=getType(); + PyObject impl=self_type.lookup("__call__"); + if (impl!=null) { + return impl.__get__(this,self_type).__call__(args,keywords); } - return super.__call__(args, keywords); + return super.__call__(args,keywords); } finally { --ts.recursion_depth; } @@ -1189,104 +1151,103 @@ @Override public PyObject __findattr_ex__(String name) { - return Deriveds.__findattr_ex__(this, name); + return Deriveds.__findattr_ex__(this,name); } @Override - public void __setattr__(String name, PyObject value) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__setattr__"); - if (impl != null) { - impl.__get__(this, self_type).__call__(PyString.fromInterned(name), value); + public void __setattr__(String name,PyObject value) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__setattr__"); + if (impl!=null) { + impl.__get__(this,self_type).__call__(PyString.fromInterned(name),value); return; } - super.__setattr__(name, value); + super.__setattr__(name,value); } @Override public void __delattr__(String name) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__delattr__"); - if (impl != null) { - impl.__get__(this, self_type).__call__(PyString.fromInterned(name)); + PyType self_type=getType(); + PyObject impl=self_type.lookup("__delattr__"); + if (impl!=null) { + impl.__get__(this,self_type).__call__(PyString.fromInterned(name)); return; } super.__delattr__(name); } @Override - public PyObject __get__(PyObject obj, PyObject type) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__get__"); - if (impl != null) { - if (obj == null) { - obj = Py.None; + public PyObject __get__(PyObject obj,PyObject type) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__get__"); + if (impl!=null) { + if (obj==null) { + obj=Py.None; } - if (type == null) { - type = Py.None; + if (type==null) { + type=Py.None; } - return impl.__get__(this, self_type).__call__(obj, type); + return impl.__get__(this,self_type).__call__(obj,type); } - return super.__get__(obj, type); + return super.__get__(obj,type); } @Override - public void __set__(PyObject obj, PyObject value) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__set__"); - if (impl != null) { - impl.__get__(this, self_type).__call__(obj, value); + public void __set__(PyObject obj,PyObject value) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__set__"); + if (impl!=null) { + impl.__get__(this,self_type).__call__(obj,value); return; } - super.__set__(obj, value); + super.__set__(obj,value); } @Override public void __delete__(PyObject obj) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__delete__"); - if (impl != null) { - impl.__get__(this, self_type).__call__(obj); + PyType self_type=getType(); + PyObject impl=self_type.lookup("__delete__"); + if (impl!=null) { + impl.__get__(this,self_type).__call__(obj); return; } super.__delete__(obj); } @Override - public PyObject __pow__(PyObject other, PyObject modulo) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__pow__"); - if (impl != null) { + public PyObject __pow__(PyObject other,PyObject modulo) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__pow__"); + if (impl!=null) { PyObject res; - if (modulo == null) { - res = impl.__get__(this, self_type).__call__(other); + if (modulo==null) { + res=impl.__get__(this,self_type).__call__(other); } else { - res = impl.__get__(this, self_type).__call__(other, modulo); + res=impl.__get__(this,self_type).__call__(other,modulo); } - if (res == Py.NotImplemented) { + if (res==Py.NotImplemented) { return null; } return res; } - return super.__pow__(other, modulo); + return super.__pow__(other,modulo); } @Override - public void dispatch__init__(PyObject[] args, String[] keywords) { - Deriveds.dispatch__init__(this, args, keywords); + public void dispatch__init__(PyObject[]args,String[]keywords) { + Deriveds.dispatch__init__(this,args,keywords); } @Override public PyObject __index__() { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__index__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(); - if (res instanceof PyInteger || res instanceof PyLong) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__index__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(); + if (res instanceof PyInteger||res instanceof PyLong) { return res; } - throw Py.TypeError(String.format("__index__ returned non-(int,long) (type %s)", res - .getType().fastGetName())); + throw Py.TypeError(String.format("__index__ returned non-(int,long) (type %s)",res.getType().fastGetName())); } return super.__index__(); } @@ -1298,48 +1259,316 @@ // specified class. Without this, derived.__tojava__(PyObject.class) // would broke. (And that's not pure speculation: PyReflectedFunction's // ReflectedArgs asks for things like that). - if ((c != Object.class) && (c != Serializable.class) && (c.isInstance(this))) { + if ((c!=Object.class)&&(c!=Serializable.class)&&(c.isInstance(this))) { return this; } // Otherwise, we call the derived __tojava__, if it exists: - PyType self_type = getType(); - PyObject impl = self_type.lookup("__tojava__"); - if (impl != null) { - return impl.__get__(this, self_type).__call__(Py.java2py(c)).__tojava__(Object.class); + PyType self_type=getType(); + PyObject impl=self_type.lookup("__tojava__"); + if (impl!=null) { + return impl.__get__(this,self_type).__call__(Py.java2py(c)).__tojava__(Object.class); } return super.__tojava__(c); } @Override public Object __coerce_ex__(PyObject o) { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__coerce__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(o); - if (res == Py.NotImplemented) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__coerce__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(o); + if (res==Py.NotImplemented) { return Py.None; } if (!(res instanceof PyTuple)) { throw Py.TypeError("__coerce__ didn't return a 2-tuple"); } - return ((PyTuple)res).getArray(); + return((PyTuple)res).getArray(); } return super.__coerce_ex__(o); } @Override - public String toString() { - PyType self_type = getType(); - PyObject impl = self_type.lookup("__repr__"); - if (impl != null) { - PyObject res = impl.__get__(this, self_type).__call__(); - if (!(res instanceof PyString)) { - throw Py.TypeError("__repr__ returned non-string (type " - + res.getType().fastGetName() + ")"); - } - return ((PyString)res).toString(); + public PyObject __enter__() { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__enter__"); + if (impl!=null) { + return impl.__get__(this,self_type).__call__(); } - return super.toString(); + return super.__enter__(); + } + + @Override + public PyObject fileno() { + PyType self_type=getType(); + PyObject impl=self_type.lookup("fileno"); + if (impl!=null) { + return impl.__get__(this,self_type).__call__(); + } + return super.fileno(); + } + + // Hand-crafted in _io._IOBase.derived + + @Override + public long seek(long pos,int whence) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("seek"); + if (impl!=null) { + return impl.__get__(this,self_type).__call__(Py.newLong(pos),Py.newInteger(whence)).asLong(); + } else { + return super.seek(pos,whence); + } + } + + @Override + public long tell() { + PyType self_type=getType(); + PyObject impl=self_type.lookup("tell"); + if (impl!=null) { + return impl.__get__(this,self_type).__call__().asLong(); + } else { + return super.tell(); + } + } + + @Override + public long truncate(long size) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("truncate"); + if (impl!=null) { + return impl.__get__(this,self_type).__call__(Py.newLong(size)).asLong(); + } else { + return super.truncate(size); + } + } + + @Override + public long truncate() { + PyType self_type=getType(); + PyObject impl=self_type.lookup("truncate"); + if (impl!=null) { + return impl.__get__(this,self_type).__call__().asLong(); + } else { + return super.truncate(); + } + } + + @Override + public void flush() { + PyType self_type=getType(); + PyObject impl=self_type.lookup("flush"); + if (impl!=null) { + impl.__get__(this,self_type).__call__(); + } else { + super.flush(); + } + } + + @Override + public void close() { + PyType self_type=getType(); + PyObject impl=self_type.lookup("close"); + if (impl!=null) { + impl.__get__(this,self_type).__call__(); + } else { + super.close(); + } + } + + @Override + public boolean seekable() { + PyType self_type=getType(); + PyObject impl=self_type.lookup("seekable"); + if (impl!=null) { + return impl.__get__(this,self_type).__call__().__nonzero__(); + } else { + return super.seekable(); + } + } + + @Override + public void _checkSeekable(String msg) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("_checkSeekable"); + if (impl!=null) { + PyObject pymsg=msg==null?Py.None:new PyString(msg); + impl.__get__(this,self_type).__call__(pymsg); + } else { + super._checkSeekable(msg); + } + } + + @Override + public boolean readable() { + PyType self_type=getType(); + PyObject impl=self_type.lookup("readable"); + if (impl!=null) { + return impl.__get__(this,self_type).__call__().__nonzero__(); + } else { + return super.readable(); + } + } + + @Override + public void _checkReadable(String msg) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("_checkReadable"); + if (impl!=null) { + PyObject pymsg=msg==null?Py.None:new PyString(msg); + impl.__get__(this,self_type).__call__(pymsg); + } else { + super._checkReadable(msg); + } + } + + @Override + public boolean writable() { + PyType self_type=getType(); + PyObject impl=self_type.lookup("writable"); + if (impl!=null) { + return impl.__get__(this,self_type).__call__().__nonzero__(); + } else { + return super.writable(); + } + } + + @Override + public void _checkWritable(String msg) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("_checkWritable"); + if (impl!=null) { + PyObject pymsg=msg==null?Py.None:new PyString(msg); + impl.__get__(this,self_type).__call__(pymsg); + } else { + super._checkWritable(msg); + } + } + + // Note that closed is a property not a predicate, so no derived method. + + @Override + public void _checkClosed(String msg) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("_checkClosed"); + if (impl!=null) { + PyObject pymsg=msg==null?Py.None:new PyString(msg); + impl.__get__(this,self_type).__call__(pymsg); + } else { + super._checkClosed(msg); + } + } + + @Override + public boolean __exit__(PyObject type,PyObject value,PyObject traceback) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__exit__"); + if (impl!=null) { + return impl.__get__(this,self_type).__call__(type,value,traceback).__nonzero__(); + } else { + return super.__exit__(type,value,traceback); + } + } + + @Override + public boolean isatty() { + PyType self_type=getType(); + PyObject impl=self_type.lookup("isatty"); + if (impl!=null) { + return impl.__get__(this,self_type).__call__().__nonzero__(); + } else { + return super.isatty(); + } + } + + @Override + public PyObject readline() { + PyType self_type=getType(); + PyObject impl=self_type.lookup("readline"); + if (impl!=null) { + return impl.__get__(this,self_type).__call__(Py.None); + } else { + return super.readline(); + } + } + + @Override + public PyObject readline(int limit) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("readline"); + if (impl!=null) { + return impl.__get__(this,self_type).__call__(Py.newInteger(limit)); + } else { + return super.readline(limit); + } + } + + @Override + public PyObject readlines(PyObject hint) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("readlines"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(hint); + return res; + } else { + return super.readlines(hint); + } + } + + @Override + public void writelines(PyObject lines) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("writelines"); + if (impl!=null) { + impl.__get__(this,self_type).__call__(lines); + } else { + super.writelines(lines); + } + } + + @Override + public PyObject readall() { + PyType self_type=getType(); + PyObject impl=self_type.lookup("readall"); + if (impl!=null) { + return impl.__get__(this,self_type).__call__(); + } + return super.readall(); + } + + // Hand-crafted in _io._RawIOBase.derived + + @Override + public PyObject read(int n) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("read"); + if (impl!=null) { + return impl.__get__(this,self_type).__call__(Py.newInteger(n)); + } else { + return super.read(n); + } + } + + @Override + public PyObject readinto(PyObject b) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("readinto"); + if (impl!=null) { + return impl.__get__(this,self_type).__call__(b); + } else { + return super.readinto(b); + } + } + + @Override + public PyObject write(PyObject b) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("write"); + if (impl!=null) { + return impl.__get__(this,self_type).__call__(b); + } else { + return super.write(b); + } } } diff --git a/src/org/python/modules/_io/_io.java b/src/org/python/modules/_io/_io.java --- a/src/org/python/modules/_io/_io.java +++ b/src/org/python/modules/_io/_io.java @@ -125,8 +125,7 @@ * Create the Raw file stream. Let the constructor deal with the variants and argument * checking. */ - // XXX open() doesn't yet support file descriptors or the "digested" mode - PyFileIO raw = new PyFileIO(file.toString(), mode.rawmode(), closefd); + PyFileIO raw = new PyFileIO(file, mode, closefd); // XXX Can this work: boolean isatty = raw.isatty() ? Or maybe: // PyObject res = PyObject_CallMethod(raw, "isatty", NULL); @@ -168,13 +167,10 @@ if (mode.updating) { bufferType = io.__getattr__("BufferedRandom"); - } else if (mode.writing || mode.appending) { + } else if (mode.writable) { // = writing || appending bufferType = io.__getattr__("BufferedWriter"); - } else if (mode.reading) { + } else { // = reading bufferType = io.__getattr__("BufferedReader"); - } else { - // Can it really still go wrong? I don't think so. - throw Py.ValueError(String.format("unknown mode: '%s'", mode.originalModeString)); } PyInteger pyBuffering = new PyInteger(buffering); @@ -191,7 +187,7 @@ {buffer, ap.getPyObject(3, Py.None), ap.getPyObject(4, Py.None), ap.getPyObject(5, Py.None), Py.newInteger(line_buffering)}; PyObject wrapper = textType.__call__(textArgs); - + wrapper.__setattr__("mode", new PyString(m)); return wrapper; } diff --git a/src/templates/_io.FileIO.derived b/src/templates/_io.FileIO.derived --- a/src/templates/_io.FileIO.derived +++ b/src/templates/_io.FileIO.derived @@ -1,4 +1,5 @@ base_class: PyFileIO -want_dict: true -ctr: -incl: object +want_dict: false +ctr: PyObject file, OpenMode mode, boolean closefd +no_toString: true +incl: _io._RawIOBase -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Sun Dec 16 01:20:05 2012 From: jython-checkins at python.org (jeff.allen) Date: Sun, 16 Dec 2012 01:20:05 +0100 (CET) Subject: [Jython-checkins] =?utf-8?q?jython=3A_Partly_treat_ValueError/IOE?= =?utf-8?q?rror_confusion_in_=5Fio=2EFileIO?= Message-ID: <3YP5jd20bwzRhR@mail.python.org> http://hg.python.org/jython/rev/4bfdc027aad2 changeset: 6900:4bfdc027aad2 user: Jeff Allen date: Thu Dec 13 16:31:05 2012 +0000 summary: Partly treat ValueError/IOError confusion in _io.FileIO Reworked the tests for closed, readable and writable in PyFileIO to be more like CPython. Corresponding changes to some tests. More fun with mode strings. test_io.py now scores fail/error/skip = 12/15/99, and test_fileio 0/0/1 (but with some concessions to Jython non-conformance still in place). files: Lib/_jyio.py | 5 +- Lib/test/test_fileio.py | 5 +- src/org/python/modules/_io/OpenMode.java | 23 - src/org/python/modules/_io/PyFileIO.java | 154 ++++++++-- src/org/python/modules/_io/PyIOBase.java | 43 ++- src/org/python/modules/_io/_io.java | 2 +- 6 files changed, 160 insertions(+), 72 deletions(-) diff --git a/Lib/_jyio.py b/Lib/_jyio.py --- a/Lib/_jyio.py +++ b/Lib/_jyio.py @@ -193,8 +193,9 @@ def close(self): if self.raw is not None and not self.closed: try: - # may raise BlockingIOError or BrokenPipeError etc - self.flush() + # Jython difference: call super.close() which manages "closed to client" state, + # and calls flush(), which may raise BlockingIOError or BrokenPipeError etc. + super(_BufferedIOBase, self).close() finally: self.raw.close() diff --git a/Lib/test/test_fileio.py b/Lib/test/test_fileio.py --- a/Lib/test/test_fileio.py +++ b/Lib/test/test_fileio.py @@ -427,10 +427,7 @@ self.assertEqual(w.warnings, []) self.assertRaises(TypeError, _FileIO, []) self.assertEqual(w.warnings, []) - if is_jython: - self.assertRaises(IOError, _FileIO, "/some/invalid/name", "rt") - else: - self.assertRaises(ValueError, _FileIO, "/some/invalid/name", "rt") + self.assertRaises(ValueError, _FileIO, "/some/invalid/name", "rt") self.assertEqual(w.warnings, []) def test_main(): diff --git a/src/org/python/modules/_io/OpenMode.java b/src/org/python/modules/_io/OpenMode.java --- a/src/org/python/modules/_io/OpenMode.java +++ b/src/org/python/modules/_io/OpenMode.java @@ -40,12 +40,6 @@ /** Set true when any invalid symbol or combination is discovered */ public boolean invalid; - /** Set true when stream must be readable = reading | updating */ - public boolean readable; - - /** Set true when stream must be writable = writing | updating | appending */ - public boolean writable; - /** * Error message describing the way in which the mode is invalid, or null if no problem has been * found. This field may be set by the constructor (in the case of duplicate or unrecognised @@ -139,8 +133,6 @@ // Implications reading |= universal; - readable = reading | updating; - writable = writing | updating | appending; // Standard tests if (!invalid) { @@ -208,21 +200,6 @@ } /** - * The mode string that a raw file should claim to have, when initialised with the present mode. - * Note that this is not the same as the open mode because it omits the text-based attributes, - * even if set, and always asserts it is binary. - * - * @return "rb", "rb+", or "wb". - */ - public String raw() { - if (readable) { - return writable ? "rb+" : "rb"; - } else { - return "wb"; - } - } - - /** * The mode string we need when constructing a FileIO initialised with the present * mode. Note that this is not the same as the full open mode because it omits the text-based * attributes, and not the same as {@link #raw()}. diff --git a/src/org/python/modules/_io/PyFileIO.java b/src/org/python/modules/_io/PyFileIO.java --- a/src/org/python/modules/_io/PyFileIO.java +++ b/src/org/python/modules/_io/PyFileIO.java @@ -9,6 +9,7 @@ import org.python.core.BuiltinDocs; import org.python.core.Py; import org.python.core.PyBuffer; +import org.python.core.PyException; import org.python.core.PyJavaType; import org.python.core.PyLong; import org.python.core.PyNewWrapper; @@ -37,7 +38,24 @@ @ExposedGet(doc = BuiltinDocs.file_name_doc) protected PyObject name; - private Boolean seekable; + /* + * Implementation note: CPython fileio does not use the base-class, possibly overridden, + * readable(), writable() and seekable(). Instead it sets local variables for readable and + * writable using the open mode, and returns these as readable() and writable(), while using + * them internally. The local variable seekable (and seekable()) is worked out from a one-time + * trial seek. + */ + /** Set true when stream must be readable = reading | updating */ + private boolean readable; + + /** Set true when stream must be writable = writing | updating | appending */ + private boolean writable; + + /** Set true when we have made the seekable test */ + private boolean seekableKnown; + + /** Set true when stream is seekable */ + private boolean seekable; /** Whether to close the underlying stream on closing this object. */ @ExposedGet @@ -45,8 +63,11 @@ /** The mode as given to the constructor */ private OpenMode openMode; - @ExposedGet(doc = BuiltinDocs.file_mode_doc) // and as a PyString - public PyString mode() { return new PyString(openMode.raw()); } + + /** The mode as a PyString based on readable and writable */ + @ExposedGet(doc = BuiltinDocs.file_mode_doc) + public final PyString mode; + private static final PyString defaultMode = new PyString("r"); /** @@ -82,6 +103,16 @@ this.closefd = closefd; this.name = file; this.openMode = mode; + + readable = mode.reading | mode.updating; + writable = mode.writing | mode.updating | mode.appending; + + // The mode string of a raw file always asserts it is binary: "rb", "rb+", or "wb". + if (readable) { + this.mode = new PyString(writable ? "rb+" : "rb"); + } else { + this.mode = new PyString("wb"); + } } /** @@ -124,9 +155,9 @@ */ Channel channel = ((RawIOBase)fd).getChannel(); if (channel instanceof FileChannel) { - if (channel.isOpen()){ - FileChannel fc = (FileChannel)channel; - return new FileIO(fc, mode.forFileIO()); + if (channel.isOpen()) { + FileChannel fc = (FileChannel)channel; + return new FileIO(fc, mode.forFileIO()); } else { // File not open (we have to check as FileIO doesn't) throw Py.OSError(Errno.EBADF); @@ -146,6 +177,7 @@ @ExposedNew static PyObject FileIO___new__(PyNewWrapper new_, boolean init, PyType subtype, PyObject[] args, String[] keywords) { + ArgParser ap = new ArgParser("FileIO", args, keywords, openArgs, 1); PyObject file = ap.getPyObject(0); PyObject m = ap.getPyObject(1, defaultMode); @@ -168,7 +200,6 @@ } - /* * =========================================================================================== * Exposed methods in the order they appear in CPython's fileio.c method table @@ -185,9 +216,11 @@ @ExposedMethod(doc = readinto_doc) final PyLong FileIO_readinto(PyObject buf) { - // Check we can do this - _checkClosed(); - _checkReadable(); + + if (!readable) { // ... (or closed) + throw tailoredValueError("read"); + } + // Perform the operation through a buffer view on the object PyBuffer pybuf = writablePyBuffer(buf); try { @@ -198,6 +231,7 @@ count = ioDelegate.readinto(byteBuffer); } return new PyLong(count); + } finally { // Must unlock the PyBuffer view from client's object pybuf.release(); @@ -211,7 +245,11 @@ @ExposedMethod(doc = write_doc) final PyLong FileIO_write(PyObject obj) { - _checkWritable(); + + if (!writable) { // ... (or closed) + throw tailoredValueError("writ"); + } + // Get or synthesise a buffer API on the object to be written PyBuffer pybuf = readablePyBuffer(obj); try { @@ -223,6 +261,7 @@ count = ioDelegate.write(byteBuffer); } return new PyLong(count); + } finally { // Even if that went badly, we should release the lock on the client buffer pybuf.release(); @@ -246,6 +285,9 @@ /** Common to FileIO_truncate(null) and truncate(). */ private final long _truncate() { + if (!writable) { // ... (or closed) + throw tailoredValueError("writ"); + } synchronized (ioDelegate) { return ioDelegate.truncate(ioDelegate.tell()); } @@ -253,6 +295,9 @@ /** Common to FileIO_truncate(size) and truncate(size). */ private final long _truncate(long size) { + if (!writable) { // ... (or closed) + throw tailoredValueError("writ"); + } synchronized (ioDelegate) { return ioDelegate.truncate(size); } @@ -275,21 +320,42 @@ if (closefd) { ioDelegate.close(); } + // This saves us doing two tests for each action (when the file is open) + readable = false; + writable = false; } @Override - public boolean readable() { + public boolean readable() throws PyException { return FileIO_readable(); } - @ExposedMethod(doc = "True if file was opened in a read mode.") + @ExposedMethod(doc = readable_doc) final boolean FileIO_readable() { - return ioDelegate.readable(); + if (__closed) { + throw closedValueError(); + } + return readable; + } + + @Override + public boolean writable() throws PyException { + return FileIO_writable(); + } + + @ExposedMethod(doc = writable_doc) + final boolean FileIO_writable() { + if (__closed) { + throw closedValueError(); + } + return writable; } @ExposedMethod(defaults = {"0"}, doc = BuiltinDocs.file_seek_doc) final synchronized PyObject FileIO_seek(long pos, int how) { - _checkClosed(); + if (__closed) { + throw closedValueError(); + } return Py.java2py(ioDelegate.seek(pos, how)); } @@ -300,15 +366,21 @@ @ExposedMethod(doc = "True if file supports random-access.") final boolean FileIO_seekable() { - if (seekable == null) { + if (__closed) { + throw closedValueError(); + } + if (!seekableKnown) { seekable = ioDelegate.seek(0, 0) >= 0; + seekableKnown = true; } return seekable; } @ExposedMethod(doc = BuiltinDocs.file_tell_doc) final synchronized long FileIO_tell() { - _checkClosed(); + if (__closed) { + throw closedValueError(); + } return ioDelegate.tell(); } @@ -322,28 +394,24 @@ return FileIO_isatty(); } - @ExposedMethod(doc = BuiltinDocs.file_isatty_doc) + @ExposedMethod(doc = isatty_doc) final boolean FileIO_isatty() { + if (__closed) { + throw closedValueError(); + } return ioDelegate.isatty(); } @Override - public boolean writable() { - return FileIO_writable(); - } - - @ExposedMethod(doc = "True if file was opened in a write mode.") - final boolean FileIO_writable() { - return ioDelegate.writable(); - } - - @Override public PyObject fileno() { return FileIO_fileno(); } @ExposedMethod(doc = BuiltinDocs.file_fileno_doc) final PyObject FileIO_fileno() { + if (__closed) { + throw closedValueError(); + } return PyJavaType.wrapJavaObject(ioDelegate.fileno()); } @@ -367,9 +435,9 @@ final String FileIO_toString() { if (name instanceof PyUnicode) { String escapedName = PyString.encode_UnicodeEscape(name.toString(), false); - return String.format("<_io.FileIO name='%s', mode='%s'>", escapedName, mode()); + return String.format("<_io.FileIO name='%s', mode='%s'>", escapedName, mode); } - return String.format("<_io.FileIO name='%s', mode='%s'>", name, mode()); + return String.format("<_io.FileIO name='%s', mode='%s'>", name, mode); } @Override @@ -377,4 +445,30 @@ return FileIO_toString(); } + /** + * Convenience method providing the exception when an method requires the file to be open, and + * it isn't. + * + * @return ValueError to throw + */ + private PyException closedValueError() { + return Py.ValueError("I/O operation on closed file"); + } + + /** + * Convenience method providing the exception when an method requires the file to be open, + * readable or writable, and it isn't. If the file is closed, return the message for that, + * otherwise, one about reading or writing. + * + * @param action type of operation not valid ("read" or "writ" in practice). + * @return ValueError to throw + */ + private PyException tailoredValueError(String action) { + if (action == null || __closed) { + return closedValueError(); + } else { + return Py.ValueError("File not open for " + action + "ing"); + } + } + } diff --git a/src/org/python/modules/_io/PyIOBase.java b/src/org/python/modules/_io/PyIOBase.java --- a/src/org/python/modules/_io/PyIOBase.java +++ b/src/org/python/modules/_io/PyIOBase.java @@ -223,13 +223,14 @@ * Is the stream capable of positioning the read/write pointer? * * @return True if may be positioned + * @throws PyException(ValueError) if the object is closed to client operations */ - public boolean seekable() { + public boolean seekable() throws PyException { return _IOBase_seekable(); } @ExposedMethod(doc = seekable_doc) - final boolean _IOBase_seekable() { + final boolean _IOBase_seekable() throws PyException { return false; } @@ -238,6 +239,8 @@ * positioned. * * @param msg optional custom message + * @throws PyException(ValueError) if the object is closed to client operations + * @throws PyException(IOError) if the stream is not capable of being positioned. */ public void _checkSeekable(String msg) { _IOBase__checkSeekable(msg); @@ -246,6 +249,9 @@ /** * Raise an error if the pointer of underlying IO stream is not capable of being * positioned. + * + * @throws PyException(ValueError) if the object is closed to client operations + * @throws PyException(IOError) if the stream is not capable of being positioned. */ public final void _checkSeekable() { _checkSeekable(null); @@ -262,13 +268,14 @@ * Is the stream readable? * * @return true if readable + * @throws PyException(ValueError) if the object is closed to client operations */ - public boolean readable() { + public boolean readable() throws PyException { return _IOBase_readable(); } @ExposedMethod(doc = readable_doc) - final boolean _IOBase_readable() { + final boolean _IOBase_readable() throws PyException { return false; } @@ -276,6 +283,8 @@ * Raise an error if the underlying IO stream is not readable. * * @param msg optional custom message + * @throws PyException(ValueError) if the object is closed to client operations + * @throws PyException(IOError) if the stream is not readable. */ public void _checkReadable(String msg) { _IOBase__checkReadable(msg); @@ -283,6 +292,9 @@ /** * Raise an error if the underlying IO stream is not readable. + * + * @throws PyException(ValueError) if the object is closed to client operations + * @throws PyException(IOError) if the stream is not readable. */ public final void _checkReadable() { _checkReadable(null); @@ -299,13 +311,14 @@ * Is the stream writable? * * @return true if writable + * @throws PyException(ValueError) if the object is closed to client operations */ - public boolean writable() { + public boolean writable() throws PyException { return _IOBase_writable(); } @ExposedMethod(doc = writable_doc) - final boolean _IOBase_writable() { + final boolean _IOBase_writable() throws PyException { return false; } @@ -313,20 +326,25 @@ * Raise an error if the underlying IO stream is not writable. * * @param msg optional custom message + * @throws PyException(ValueError) if the object is closed to client operations + * @throws PyException(IOError) if the stream is not writable. */ - public void _checkWritable(String msg) { + public void _checkWritable(String msg) throws PyException { _IOBase__checkWritable(msg); } /** * Raise an error if the underlying IO stream is not writable. + * + * @throws PyException(ValueError) if the object is closed to client operations + * @throws PyException(IOError) if the stream is not writable. */ - public final void _checkWritable() { + public final void _checkWritable() throws PyException { _checkWritable(null); } @ExposedMethod(defaults = "null") - final void _IOBase__checkWritable(String msg) { + final void _IOBase__checkWritable(String msg) throws PyException { if (!invoke("writable").__nonzero__()) { throw tailoredIOError(msg, "writ"); } @@ -346,17 +364,18 @@ * {@link #_checkSeekable}, etc.. * * @param msg optional custom message + * @throws PyException(ValueError) if the object is closed to client operations */ - public void _checkClosed(String msg) { + public void _checkClosed(String msg) throws PyException { _IOBase__checkClosed(msg); } - public final void _checkClosed() { + public final void _checkClosed() throws PyException { _checkClosed(null); } @ExposedMethod(defaults = "null") - final void _IOBase__checkClosed(String msg) { + final void _IOBase__checkClosed(String msg) throws PyException { if (closed()) { throw Py.ValueError(msg != null ? msg : "I/O operation on closed file"); } diff --git a/src/org/python/modules/_io/_io.java b/src/org/python/modules/_io/_io.java --- a/src/org/python/modules/_io/_io.java +++ b/src/org/python/modules/_io/_io.java @@ -167,7 +167,7 @@ if (mode.updating) { bufferType = io.__getattr__("BufferedRandom"); - } else if (mode.writable) { // = writing || appending + } else if (mode.writing || mode.appending) { bufferType = io.__getattr__("BufferedWriter"); } else { // = reading bufferType = io.__getattr__("BufferedReader"); -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Sun Dec 16 01:20:06 2012 From: jython-checkins at python.org (jeff.allen) Date: Sun, 16 Dec 2012 01:20:06 +0100 (CET) Subject: [Jython-checkins] =?utf-8?q?jython=3A_Implement_conformant_=5Fio?= =?utf-8?b?LkZpbGVJTy5fX3JlcHJfXw==?= Message-ID: <3YP5jf5D3rzNn6@mail.python.org> http://hg.python.org/jython/rev/46e2e4f64981 changeset: 6901:46e2e4f64981 user: Jeff Allen date: Thu Dec 13 23:11:03 2012 +0000 summary: Implement conformant _io.FileIO.__repr__ Improved __repr__, __str__ implementation so it passes test.test_fileio.AutoFileTests.testRepr, and removed skip of that test. Also catch BufferError in and convert to TypeError in _io._IOBase support for i/o. Some re-ordering in PyFileIO.java. test_io score unchanged. files: Lib/test/test_fileio.py | 1 - src/org/python/modules/_io/PyFileIO.java | 114 +++++----- src/org/python/modules/_io/PyIOBase.java | 29 ++- 3 files changed, 79 insertions(+), 65 deletions(-) diff --git a/Lib/test/test_fileio.py b/Lib/test/test_fileio.py --- a/Lib/test/test_fileio.py +++ b/Lib/test/test_fileio.py @@ -89,7 +89,6 @@ self.assertEqual(self.f.readline(None), b"hi\n") self.assertEqual(self.f.readlines(None), [b"bye\n", b"abc"]) - @unittest.skipIf(is_jython, "FIXME: unicode+slightly different in Jython") def testRepr(self): self.assertEqual(repr(self.f), "<_io.FileIO name=%r mode='%s'>" % (self.f.name, self.f.mode)) diff --git a/src/org/python/modules/_io/PyFileIO.java b/src/org/python/modules/_io/PyFileIO.java --- a/src/org/python/modules/_io/PyFileIO.java +++ b/src/org/python/modules/_io/PyFileIO.java @@ -61,9 +61,6 @@ @ExposedGet public final boolean closefd; - /** The mode as given to the constructor */ - private OpenMode openMode; - /** The mode as a PyString based on readable and writable */ @ExposedGet(doc = BuiltinDocs.file_mode_doc) public final PyString mode; @@ -102,7 +99,6 @@ this.ioDelegate = getFileIO(file, mode, closefd); this.closefd = closefd; this.name = file; - this.openMode = mode; readable = mode.reading | mode.updating; writable = mode.writing | mode.updating | mode.appending; @@ -269,6 +265,23 @@ } @Override + public long seek(long pos, int whence) { + return FileIO_seek(pos, whence); + } + + @ExposedMethod(defaults = "0", doc = seek_doc) + final long FileIO_seek(long pos, int whence) { + if (__closed) { + throw closedValueError(); + } + synchronized (ioDelegate) { + return ioDelegate.seek(pos, whence); + } + } + + // _IOBase.tell() is correct for us + + @Override public long truncate() { return _truncate(); } @@ -326,6 +339,23 @@ } @Override + public boolean seekable() { + return FileIO_seekable(); + } + + @ExposedMethod(doc = seekable_doc) + final boolean FileIO_seekable() { + if (__closed) { + throw closedValueError(); + } + if (!seekableKnown) { + seekable = ioDelegate.seek(0, 0) >= 0; + seekableKnown = true; + } + return seekable; + } + + @Override public boolean readable() throws PyException { return FileIO_readable(); } @@ -351,47 +381,14 @@ return writable; } - @ExposedMethod(defaults = {"0"}, doc = BuiltinDocs.file_seek_doc) - final synchronized PyObject FileIO_seek(long pos, int how) { - if (__closed) { - throw closedValueError(); - } - return Py.java2py(ioDelegate.seek(pos, how)); + @Override + public PyObject fileno() { + return FileIO_fileno(); } - @Override - public boolean seekable() { - return FileIO_seekable(); - } - - @ExposedMethod(doc = "True if file supports random-access.") - final boolean FileIO_seekable() { - if (__closed) { - throw closedValueError(); - } - if (!seekableKnown) { - seekable = ioDelegate.seek(0, 0) >= 0; - seekableKnown = true; - } - return seekable; - } - - @ExposedMethod(doc = BuiltinDocs.file_tell_doc) - final synchronized long FileIO_tell() { - if (__closed) { - throw closedValueError(); - } - return ioDelegate.tell(); - } - - @Override - public long tell() { - return FileIO_tell(); - } - - @Override - public boolean isatty() { - return FileIO_isatty(); + @ExposedMethod(doc = fileno_doc) + final PyObject FileIO_fileno() { + return PyJavaType.wrapJavaObject(ioDelegate.fileno()); } @ExposedMethod(doc = isatty_doc) @@ -402,19 +399,6 @@ return ioDelegate.isatty(); } - @Override - public PyObject fileno() { - return FileIO_fileno(); - } - - @ExposedMethod(doc = BuiltinDocs.file_fileno_doc) - final PyObject FileIO_fileno() { - if (__closed) { - throw closedValueError(); - } - return PyJavaType.wrapJavaObject(ioDelegate.fileno()); - } - // fileio.c has no flush(), but why not, when there is fdflush()? // And it is a no-op for Jython io.FileIO, but why when there is FileChannel.force()? @Override @@ -431,18 +415,24 @@ } } - @ExposedMethod(names = {"__str__", "__repr__"}, doc = BuiltinDocs.file___str___doc) + @ExposedMethod(names = {"__str__", "__repr__"}, doc = BuiltinDocs.object___str___doc) final String FileIO_toString() { - if (name instanceof PyUnicode) { - String escapedName = PyString.encode_UnicodeEscape(name.toString(), false); - return String.format("<_io.FileIO name='%s', mode='%s'>", escapedName, mode); + if (closed()) { + return "<_io.FileIO [closed]>"; + } else if (name instanceof PyString) { + String xname = name.asString(); + if (name instanceof PyUnicode) { + xname = PyString.encode_UnicodeEscape(xname, false); + } + return String.format("<_io.FileIO name='%s' mode='%s'>", xname, mode); + } else { + return String.format("<_io.FileIO fd=%s mode='%s'>", fileno(), mode); } - return String.format("<_io.FileIO name='%s', mode='%s'>", name, mode); } @Override public String toString() { - return FileIO_toString(); + return FileIO_toString().toString(); } /** diff --git a/src/org/python/modules/_io/PyIOBase.java b/src/org/python/modules/_io/PyIOBase.java --- a/src/org/python/modules/_io/PyIOBase.java +++ b/src/org/python/modules/_io/PyIOBase.java @@ -115,6 +115,13 @@ return _IOBase_seek(pos, whence); } + /** + * Position the read or write pointer at a given byte offset pos relative to the + * start. + * + * @param pos relative to the start + * @return the new current position + */ public final long seek(long pos) { return seek(pos, 0); } @@ -713,7 +720,16 @@ */ protected static PyBuffer readablePyBuffer(PyObject obj) throws PyException { if (obj instanceof BufferProtocol) { - return ((BufferProtocol)obj).getBuffer(PyBUF.SIMPLE); + try { + return ((BufferProtocol)obj).getBuffer(PyBUF.SIMPLE); + } catch (PyException pye) { + if (pye.match(Py.BufferError)) { + // If we can't get a buffer on the object, say it's the wrong type + throw Py.TypeError(String.format("(BufferError) %s", pye.getMessage())); + } else { + throw pye; + } + } } else { // Something else we can view as a String? String s; @@ -741,7 +757,16 @@ */ protected static PyBuffer writablePyBuffer(PyObject obj) throws PyException { if (obj instanceof BufferProtocol) { - return ((BufferProtocol)obj).getBuffer(PyBUF.WRITABLE); + try { + return ((BufferProtocol)obj).getBuffer(PyBUF.WRITABLE); + } catch (PyException pye) { + if (pye.match(Py.BufferError)) { + // If we can't get a buffer on the object, say it's the wrong type + throw Py.TypeError(String.format("(BufferError) %s", pye.getMessage())); + } else { + throw pye; + } + } } else { // Can't be a buffer: complain String fmt = "object must be read-write buffer, not %.100s"; -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Sun Dec 16 01:20:08 2012 From: jython-checkins at python.org (jeff.allen) Date: Sun, 16 Dec 2012 01:20:08 +0100 (CET) Subject: [Jython-checkins] =?utf-8?q?jython=3A_Improve_test=5Ffileio_respo?= =?utf-8?q?nse_to_errors?= Message-ID: <3YP5jh1TtNzRhT@mail.python.org> http://hg.python.org/jython/rev/31ec07bf1be0 changeset: 6902:31ec07bf1be0 user: Jeff Allen date: Sat Dec 15 01:04:24 2012 +0000 summary: Improve test_fileio response to errors The test was bad at deleting the working test file when a test failed, and this would cause other tests to fail confusingly. It now cleans up properly. files: Lib/test/test_fileio.py | 149 +++++++++++++-------------- 1 files changed, 73 insertions(+), 76 deletions(-) diff --git a/Lib/test/test_fileio.py b/Lib/test/test_fileio.py --- a/Lib/test/test_fileio.py +++ b/Lib/test/test_fileio.py @@ -103,10 +103,10 @@ self.assertTrue(not f.isatty()) self.assertTrue(not f.closed) #self.assertEqual(f.name, TESTFN) - self.assertRaises(ValueError, f.read, 10) # Open for reading + self.assertRaises(ValueError, f.read, 10) # Open for writing f.close() self.assertTrue(f.closed) - f = _FileIO(TESTFN, 'r') + f = self.f = _FileIO(TESTFN, 'r') self.assertRaises(TypeError, f.readinto, "") self.assertTrue(not f.closed) f.close() @@ -255,66 +255,72 @@ f.readinto(a) class OtherFileTests(unittest.TestCase): + # file tests for which a test file is not created but cleaned up + # This introduced by Jython, to prevent the cascade of errors when + # a test exits leaving an open file. Also a CPython problem. + + def setUp(self): + self.f = None + + def tearDown(self): + if self.f: + self.f.close() + if os.path.exists(TESTFN): + os.remove(TESTFN) def testAbles(self): - try: - f = _FileIO(TESTFN, "w") - self.assertEqual(f.readable(), False) - self.assertEqual(f.writable(), True) - self.assertEqual(f.seekable(), True) - f.close() - f = _FileIO(TESTFN, "r") - self.assertEqual(f.readable(), True) - self.assertEqual(f.writable(), False) - self.assertEqual(f.seekable(), True) - f.close() + f = self.f = _FileIO(TESTFN, "w") + self.assertEqual(f.readable(), False) + self.assertEqual(f.writable(), True) + self.assertEqual(f.seekable(), True) + f.close() - f = _FileIO(TESTFN, "a+") - self.assertEqual(f.readable(), True) - self.assertEqual(f.writable(), True) - self.assertEqual(f.seekable(), True) - self.assertEqual(f.isatty(), False) - f.close() + f = self.f = _FileIO(TESTFN, "r") + self.assertEqual(f.readable(), True) + self.assertEqual(f.writable(), False) + self.assertEqual(f.seekable(), True) + f.close() - if sys.platform != "win32": - try: - f = _FileIO("/dev/tty", "a") - except EnvironmentError: - # When run in a cron job there just aren't any - # ttys, so skip the test. This also handles other - # OS'es that don't support /dev/tty. - pass - else: - self.assertEqual(f.readable(), False) - self.assertEqual(f.writable(), True) - if sys.platform != "darwin" and \ - 'bsd' not in sys.platform and \ - not sys.platform.startswith('sunos'): - # Somehow /dev/tty appears seekable on some BSDs - self.assertEqual(f.seekable(), False) - self.assertEqual(f.isatty(), True) - f.close() - finally: - os.unlink(TESTFN) + f = self.f = _FileIO(TESTFN, "a+") + self.assertEqual(f.readable(), True) + self.assertEqual(f.writable(), True) + self.assertEqual(f.seekable(), True) + self.assertEqual(f.isatty(), False) + f.close() + + if sys.platform != "win32": + try: + f = self.f = _FileIO("/dev/tty", "a") + except EnvironmentError: + # When run in a cron job there just aren't any + # ttys, so skip the test. This also handles other + # OS'es that don't support /dev/tty. + pass + else: + self.assertEqual(f.readable(), False) + self.assertEqual(f.writable(), True) + if sys.platform != "darwin" and \ + 'bsd' not in sys.platform and \ + not sys.platform.startswith('sunos'): + # Somehow /dev/tty appears seekable on some BSDs + self.assertEqual(f.seekable(), False) + self.assertEqual(f.isatty(), True) @unittest.skipIf(is_jython, "FIXME: not working in Jython") def testModeStrings(self): # check invalid mode strings for mode in ("", "aU", "wU+", "rw", "rt"): try: - f = _FileIO(TESTFN, mode) + f = self.f = _FileIO(TESTFN, mode) except ValueError: pass else: - f.close() self.fail('%r is an invalid file mode' % mode) def testUnicodeOpen(self): # verify repr works for unicode too - f = _FileIO(str(TESTFN), "w") - f.close() - os.unlink(TESTFN) + f = self.f = _FileIO(str(TESTFN), "w") def testBytesOpen(self): # Opening a bytes filename @@ -323,14 +329,12 @@ except UnicodeEncodeError: # Skip test return - f = _FileIO(fn, "w") - try: - f.write(b"abc") - f.close() - with open(TESTFN, "rb") as f: - self.assertEqual(f.read(), b"abc") - finally: - os.unlink(TESTFN) + f = self.f = _FileIO(fn, "w") + f.write(b"abc") + f.close() + with open(TESTFN, "rb") as f: + self.f = f + self.assertEqual(f.read(), b"abc") def testInvalidFd(self): if is_jython: @@ -346,7 +350,7 @@ # verify that we get a sensible error message for bad mode argument bad_mode = "qwerty" try: - f = _FileIO(TESTFN, bad_mode) + f = self.f = _FileIO(TESTFN, bad_mode) except ValueError as msg: if msg.args[0] != 0: s = str(msg) @@ -359,7 +363,7 @@ self.fail("no error for invalid mode: %s" % bad_mode) def testTruncate(self): - f = _FileIO(TESTFN, 'w') + f = self.f = _FileIO(TESTFN, 'w') f.write(bytes(bytearray(range(10)))) self.assertEqual(f.tell(), 10) f.truncate(5) @@ -375,11 +379,11 @@ def bug801631(): # SF bug # "file.truncate fault on windows" - f = _FileIO(TESTFN, 'w') + f = self.f = _FileIO(TESTFN, 'w') f.write(bytes(range(11))) f.close() - f = _FileIO(TESTFN,'r+') + f = self.f = _FileIO(TESTFN,'r+') data = f.read(5) if data != bytes(range(5)): self.fail("Read on file opened for update failed %r" % data) @@ -395,28 +399,21 @@ if size != 5: self.fail("File size after ftruncate wrong %d" % size) - try: - bug801631() - finally: - os.unlink(TESTFN) + # Test for bug 801631 + bug801631() def testAppend(self): - try: - f = open(TESTFN, 'wb') - f.write(b'spam') - f.close() - f = open(TESTFN, 'ab') - f.write(b'eggs') - f.close() - f = open(TESTFN, 'rb') - d = f.read() - f.close() - self.assertEqual(d, b'spameggs') - finally: - try: - os.unlink(TESTFN) - except: - pass + + f = self.f = open(TESTFN, 'wb') + f.write(b'spam') + f.close() + f = self.f = open(TESTFN, 'ab') + f.write(b'eggs') + f.close() + f = self.f = open(TESTFN, 'rb') + d = f.read() + f.close() + self.assertEqual(d, b'spameggs') def testInvalidInit(self): self.assertRaises(TypeError, _FileIO, "1", 0, 0) -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Sun Dec 16 01:20:09 2012 From: jython-checkins at python.org (jeff.allen) Date: Sun, 16 Dec 2012 01:20:09 +0100 (CET) Subject: [Jython-checkins] =?utf-8?q?jython=3A_=5Fio=2EFileIO_implement_na?= =?utf-8?q?me_attribute?= Message-ID: <3YP5jj4nQnzNn6@mail.python.org> http://hg.python.org/jython/rev/73ed9bffcac5 changeset: 6903:73ed9bffcac5 user: Jeff Allen date: Sat Dec 15 11:40:22 2012 +0000 summary: _io.FileIO implement name attribute Also impacts the behaviour of repr() and necessitates a change to construction. "name" is implemented in the dictionary. test_fileio scores unchanged at fail/error/skip = 12/15/99, test_fileio errors down at 0/3/3. files: src/org/python/modules/_io/PyFileIO.java | 64 ++++++----- src/org/python/modules/_io/PyIOBase.java | 8 +- 2 files changed, 38 insertions(+), 34 deletions(-) diff --git a/src/org/python/modules/_io/PyFileIO.java b/src/org/python/modules/_io/PyFileIO.java --- a/src/org/python/modules/_io/PyFileIO.java +++ b/src/org/python/modules/_io/PyFileIO.java @@ -32,11 +32,7 @@ public static final PyType TYPE = PyType.fromClass(PyFileIO.class); /** The FileIO to which we delegate operations not complete locally. */ - private final FileIO ioDelegate; - - /** The name of the file */ - @ExposedGet(doc = BuiltinDocs.file_name_doc) - protected PyObject name; + private FileIO ioDelegate; /* * Implementation note: CPython fileio does not use the base-class, possibly overridden, @@ -58,11 +54,11 @@ private boolean seekable; /** Whether to close the underlying stream on closing this object. */ - @ExposedGet + @ExposedGet(doc = "True if the file descriptor will be closed") public final boolean closefd; /** The mode as a PyString based on readable and writable */ - @ExposedGet(doc = BuiltinDocs.file_mode_doc) + @ExposedGet(doc = "String giving the file mode: 'rb', 'rb+', or 'wb'") public final PyString mode; private static final PyString defaultMode = new PyString("r"); @@ -73,7 +69,7 @@ * mode object are consulted (so that flags meaningful to this sub-class need not be processed * out). * - * @param fd on which this should be constructed + * @param file path or descriptor on which this should be constructed * @param mode type of access specified * @param closefd if false, do not close fd on call to * close() @@ -89,16 +85,15 @@ * sub-class need not be processed out). * * @param subtype for which construction is occurring - * @param file on which this should be constructed + * @param file path or descriptor on which this should be constructed * @param mode type of access specified * @param closefd if false, do not close file on call to * close() */ public PyFileIO(PyType subtype, PyObject file, OpenMode mode, boolean closefd) { super(subtype); - this.ioDelegate = getFileIO(file, mode, closefd); + setDelegate(file, mode, closefd); this.closefd = closefd; - this.name = file; readable = mode.reading | mode.updating; writable = mode.writing | mode.updating | mode.appending; @@ -113,8 +108,10 @@ /** * Helper function that turns the arguments of the most general constructor, or - * __new__, into a {@link FileIO}. This places the logic of those several - * operations in one place. + * __new__, into a {@link FileIO}, assigned to {@link #ioDelegate}. It enforces + * rules on {@link #closefd} and the type of object that may be a file descriptor, and assigns + * the name attribute to the string name or the file descriptor (see Python docs + * for io.FileIO.name). This places the logic of those several operations in one place. *

* In many cases (such as construction from a file name, the FileIO is a newly-opened file. When * the file object passed in is a file descriptor, the FileIO may be created to wrap that @@ -123,17 +120,15 @@ * @param file name or descriptor * @param mode parsed file open mode * @param closefd must be true if file is in fact a name (checked, not used) - * @return */ - private static FileIO getFileIO(PyObject file, OpenMode mode, boolean closefd) { + private void setDelegate(PyObject file, OpenMode mode, boolean closefd) { if (file instanceof PyString) { // Open a file by name if (!closefd) { throw Py.ValueError("Cannot use closefd=False with file name"); } - String name = file.asString(); - return new FileIO(name, mode.forFileIO()); + ioDelegate = new FileIO((PyString)file, mode.forFileIO()); } else { /* @@ -142,27 +137,33 @@ * choice in Jython, and file descriptors should be treated as opaque. */ Object fd = file.__tojava__(Object.class); + if (fd instanceof RawIOBase) { // It is the "Jython file descriptor" from which we can get a channel. /* - * If the argument were a FileIO, could we return it directly? I think not: there - * would be a problem with close and closefd=False since it is not the PyFileIO that - * keeps state. + * If the argument were a FileIO, could we assign it directly to ioDelegate? I think + * not: there would be a problem with close and closefd=False since it is not the + * PyFileIO that keeps state. */ Channel channel = ((RawIOBase)fd).getChannel(); if (channel instanceof FileChannel) { if (channel.isOpen()) { FileChannel fc = (FileChannel)channel; - return new FileIO(fc, mode.forFileIO()); + ioDelegate = new FileIO(fc, mode.forFileIO()); } else { // File not open (we have to check as FileIO doesn't) throw Py.OSError(Errno.EBADF); } } + + } else { + // The file was a type we don't know how to use + throw Py.TypeError(String.format("invalid file: %s", file.__repr__().asString())); } - // The file was a type we don't know how to use - throw Py.TypeError(String.format("invalid file: %s", file.__repr__().asString())); } + + // The name is either the textual name or a file descriptor (see Python docs) + fastGetDict().__setitem__("name", file); } private static final String[] openArgs = {"file", "mode", "closefd"}; @@ -419,14 +420,17 @@ final String FileIO_toString() { if (closed()) { return "<_io.FileIO [closed]>"; - } else if (name instanceof PyString) { - String xname = name.asString(); - if (name instanceof PyUnicode) { - xname = PyString.encode_UnicodeEscape(xname, false); + } else { + PyObject name = fastGetDict().__finditem__("name"); + if (name != null && (name instanceof PyString)) { + String xname = name.asString(); + if (name instanceof PyUnicode) { + xname = PyString.encode_UnicodeEscape(xname, false); + } + return String.format("<_io.FileIO name='%s' mode='%s'>", xname, mode); + } else { + return String.format("<_io.FileIO fd=%s mode='%s'>", fileno(), mode); } - return String.format("<_io.FileIO name='%s' mode='%s'>", xname, mode); - } else { - return String.format("<_io.FileIO fd=%s mode='%s'>", fileno(), mode); } } diff --git a/src/org/python/modules/_io/PyIOBase.java b/src/org/python/modules/_io/PyIOBase.java --- a/src/org/python/modules/_io/PyIOBase.java +++ b/src/org/python/modules/_io/PyIOBase.java @@ -54,14 +54,14 @@ } /** - * Provide a dictionary in the object, mainly so that methods can be overridden at instance - * level, mainly, in fact, so that the test test_flush_error_on_close passes. + * Provide a dictionary in the object, so that methods and attributes may be overridden at instance + * level. */ @ExposedGet - protected PyObject __dict__ = new PyStringMap(); + protected PyStringMap __dict__ = new PyStringMap(); @Override - public PyObject fastGetDict() { + public PyStringMap fastGetDict() { return __dict__; } -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Sun Dec 16 01:20:11 2012 From: jython-checkins at python.org (jeff.allen) Date: Sun, 16 Dec 2012 01:20:11 +0100 (CET) Subject: [Jython-checkins] =?utf-8?q?jython=3A_Make_attributes_closed=2C_m?= =?utf-8?q?ode_readonly_in_=5Fio=2EFileIO=2E?= Message-ID: <3YP5jl0zvQzNn6@mail.python.org> http://hg.python.org/jython/rev/9968367fa217 changeset: 6904:9968367fa217 user: Jeff Allen date: Sat Dec 15 23:59:45 2012 +0000 summary: Make attributes closed, mode readonly in _io.FileIO. Removed some skps and other suppression of Jython test failures in test_fileio. test_io scores unchanged at fail/error/skip = 12/15/99, test_fileio down at 0/1/1, but with concessions to Jython ValueError issue still in place. files: Lib/test/test_fileio.py | 22 ++++--- src/org/python/modules/_io/OpenMode.java | 17 +++++- src/org/python/modules/_io/PyFileIO.java | 5 + src/org/python/modules/_io/PyIOBase.java | 28 +++++++-- src/org/python/modules/_io/PyRawIOBase.java | 22 ++++++- 5 files changed, 74 insertions(+), 20 deletions(-) diff --git a/Lib/test/test_fileio.py b/Lib/test/test_fileio.py --- a/Lib/test/test_fileio.py +++ b/Lib/test/test_fileio.py @@ -66,10 +66,9 @@ self.assertEqual(f.closed, False) # verify the attributes are readonly - #XXX: not read only in Jython? - ###for attr in 'mode', 'closed': - ### self.assertRaises((AttributeError, TypeError), - ### setattr, f, attr, 'oops') + for attr in 'mode', 'closed': + self.assertRaises((AttributeError, TypeError), + setattr, f, attr, 'oops') def testReadinto(self): # verify readinto @@ -112,9 +111,9 @@ f.close() self.assertTrue(f.closed) - def testMethods(self): - methods = ['fileno', 'isatty', 'read', 'readinto', - 'seek', 'tell', 'truncate', 'write', 'seekable', + # These methods all accept a call with 0 arguments + methods = ['fileno', 'isatty', 'read', + 'tell', 'truncate', 'seekable', 'readable', 'writable'] if sys.platform.startswith('atheos'): methods.remove('truncate') @@ -127,6 +126,13 @@ # should raise on closed file self.assertRaises(ValueError, method) + # These other methods should be tested using a specific call + # in case the test for number of arguments comes first. + b = bytearray() + self.assertRaises(ValueError, self.f.readinto, b ) + self.assertRaises(ValueError, self.f.seek, 0) + self.assertRaises(ValueError, self.f.write, b ) + def testOpendir(self): # Issue 3703: opening a directory should fill the errno # Windows always returns "[Errno 13]: Permission denied @@ -188,7 +194,6 @@ return wrapper - @unittest.skipIf(is_jython, "FIXME: not working in Jython") @ClosedFDRaises def testErrnoOnClose(self, f): f.close() @@ -307,7 +312,6 @@ self.assertEqual(f.seekable(), False) self.assertEqual(f.isatty(), True) - @unittest.skipIf(is_jython, "FIXME: not working in Jython") def testModeStrings(self): # check invalid mode strings for mode in ("", "aU", "wU+", "rw", "rt"): diff --git a/src/org/python/modules/_io/OpenMode.java b/src/org/python/modules/_io/OpenMode.java --- a/src/org/python/modules/_io/OpenMode.java +++ b/src/org/python/modules/_io/OpenMode.java @@ -140,8 +140,21 @@ message = "can't use U and writing mode at once"; } else if (text && binary) { message = "can't have text and binary mode at once"; - } else if (reading && writing || appending && (reading || writing)) { - message = "must have exactly one of read/write/append mode"; + } else { + // How many of r/U, w and a were given? + int rwa = 0; + if (reading) { + rwa += 1; + } + if (writing) { + rwa += 1; + } + if (appending) { + rwa += 1; + } + if (rwa != 1) { + message = "must have exactly one of read/write/append mode"; + } } invalid |= (message != null); } diff --git a/src/org/python/modules/_io/PyFileIO.java b/src/org/python/modules/_io/PyFileIO.java --- a/src/org/python/modules/_io/PyFileIO.java +++ b/src/org/python/modules/_io/PyFileIO.java @@ -22,6 +22,7 @@ import org.python.expose.ExposedGet; import org.python.expose.ExposedMethod; import org.python.expose.ExposedNew; +import org.python.expose.ExposedSet; import org.python.expose.ExposedType; import com.kenai.constantine.platform.Errno; @@ -60,6 +61,10 @@ /** The mode as a PyString based on readable and writable */ @ExposedGet(doc = "String giving the file mode: 'rb', 'rb+', or 'wb'") public final PyString mode; + @ExposedSet(name="mode") + public final void mode_readonly(PyString value) { + readonlyAttributeError("mode"); + } private static final PyString defaultMode = new PyString("r"); diff --git a/src/org/python/modules/_io/PyIOBase.java b/src/org/python/modules/_io/PyIOBase.java --- a/src/org/python/modules/_io/PyIOBase.java +++ b/src/org/python/modules/_io/PyIOBase.java @@ -20,6 +20,7 @@ import org.python.expose.ExposedGet; import org.python.expose.ExposedMethod; import org.python.expose.ExposedNew; +import org.python.expose.ExposedSet; import org.python.expose.ExposedType; /** @@ -197,6 +198,10 @@ */ @ExposedGet(name = "closed", doc = closed_doc) protected boolean __closed; + @ExposedSet(name="closed") + public final void closed_readonly(boolean value) { + readonlyAttributeError("closed"); + } /** * Close the stream. If closed already, this is a no-op. @@ -481,7 +486,7 @@ } else if (limit.isIndex()) { return _readline(limit.asInt()); } else { - throw Py.TypeError("limit must be an integer"); + throw tailoredTypeError("integer limit", limit); } } @@ -653,7 +658,7 @@ return new PyList(this); } else if (!hint.isIndex()) { - throw Py.TypeError("integer or None expected"); + throw tailoredTypeError("integer or None", hint); } else if ((h = hint.asIndex()) <= 0) { return new PyList(this); @@ -739,8 +744,7 @@ s = ((PyArray)obj).tostring(); } else { // None of the above: complain - String fmt = "object must be string or buffer, not %.100s"; - throw Py.TypeError(String.format(fmt, obj.getType().fastGetName())); + throw tailoredTypeError("read-write buffer", obj); } return new SimpleStringBuffer(PyBUF.SIMPLE, s); } @@ -769,11 +773,23 @@ } } else { // Can't be a buffer: complain - String fmt = "object must be read-write buffer, not %.100s"; - throw Py.TypeError(String.format(fmt, obj.getType().fastGetName())); + throw tailoredTypeError("read-write buffer", obj); } } + /** + * Convenience method providing the exception when an argument is not the expected type. + * The format is "type argument expected, got type(arg)." + * + * @param type of thing expected (or could any text) + * @param arg argument provided from which actual type will be reported + * @return TypeError to throw + */ + protected static PyException tailoredTypeError(String type, PyObject arg){ + return Py.TypeError(String.format("%s argument expected, got %.100s.", + type, arg.getType().fastGetName())); + } + /* * Documentation strings: public where they might be useful to a subclass. */ diff --git a/src/org/python/modules/_io/PyRawIOBase.java b/src/org/python/modules/_io/PyRawIOBase.java --- a/src/org/python/modules/_io/PyRawIOBase.java +++ b/src/org/python/modules/_io/PyRawIOBase.java @@ -54,7 +54,7 @@ * is not ready with further data) */ public PyObject read(int n) { - return _RawIOBase_read(n); + return _read(n); } /* @@ -62,8 +62,24 @@ * terms of read(), in case the latter is a more suitable primitive operation, but that would * lead to nasty recursion in case a subclass doesn't implement either.) */ - @ExposedMethod(defaults = "-1", doc = read_doc) - final PyObject _RawIOBase_read(int n) { + @ExposedMethod(defaults = "null", doc = read_doc) + final PyObject _RawIOBase_read(PyObject n) { + if (n == null || n == Py.None) { + return _read(-1); + } else if (n.isIndex()) { + return _read(n.asInt()); + } else { + throw tailoredTypeError("integer", n); + } + } + + /** + * Implementation of the read() method once the argument has been reduced to an int. + * @param n number of bytes to read (if possible) + * @return a PyString holding the bytes read or Py.None (when a non-blocking source + * is not ready with further data) + */ + private PyObject _read(int n) { if (n < 0) { // This is really a request to read the whole stream -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Sat Dec 22 20:52:02 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Sat, 22 Dec 2012 20:52:02 +0100 (CET) Subject: [Jython-checkins] =?utf-8?q?jython=3A_Initial_implementation_of_c?= =?utf-8?q?ustomizable_proxymaker_with_annotation_support_and?= Message-ID: <3YTHR64Fs7zNsc@mail.python.org> http://hg.python.org/jython/rev/06c3db38b42b changeset: 6905:06c3db38b42b parent: 6872:19e37832c394 user: Darjus Loktevic date: Wed Oct 10 21:47:33 2012 -0700 summary: Initial implementation of customizable proxymaker with annotation support and reference implementation in tests. Remaining: * constructor signatures * more tests files: src/org/python/compiler/ClassFile.java | 89 ++ src/org/python/compiler/Code.java | 2 +- src/org/python/compiler/JavaMaker.java | 7 +- src/org/python/compiler/ProxyCodeHelpers.java | 338 ++++++++ src/org/python/compiler/ProxyMaker.java | 418 ++++----- src/org/python/core/MakeProxies.java | 52 +- src/org/python/util/ProxyCompiler.java | 32 + tests/java/org/python/compiler/custom_proxymaker/ClassAnnotationTest.java | 36 + tests/java/org/python/compiler/custom_proxymaker/ConstructorSignatureTest.java | 33 + tests/java/org/python/compiler/custom_proxymaker/CustomAnnotation.java | 30 + tests/java/org/python/compiler/custom_proxymaker/JUnitTest.java | 18 + tests/java/org/python/compiler/custom_proxymaker/MethodSignatureTest.java | 77 + tests/java/org/python/compiler/custom_proxymaker/MiniClampMaker.java | 182 ++++ tests/python/custom_proxymaker/annotated_class.py | 18 + tests/python/custom_proxymaker/clamp.py | 5 + tests/python/custom_proxymaker/constructor_signatures.py | 32 + tests/python/custom_proxymaker/junit_test.py | 28 + tests/python/custom_proxymaker/method_signatures.py | 41 + 18 files changed, 1207 insertions(+), 231 deletions(-) diff --git a/src/org/python/compiler/ClassFile.java b/src/org/python/compiler/ClassFile.java --- a/src/org/python/compiler/ClassFile.java +++ b/src/org/python/compiler/ClassFile.java @@ -7,14 +7,18 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; import org.python.core.imp; +import org.python.compiler.ProxyCodeHelpers.AnnotationDescr; public class ClassFile { @@ -27,6 +31,7 @@ String[] interfaces; List methodVisitors; List fieldVisitors; + List annotationVisitors; public static String fixName(String n) { if (n.indexOf('.') == -1) @@ -37,6 +42,34 @@ } return new String(c); } + + + public static void visitAnnotations(AnnotationVisitor av, Map fields) { + for (Entryfield: fields.entrySet()) { + visitAnnotation(av, field.getKey(), field.getValue()); + } + } + + // See org.objectweb.asm.AnnotationVisitor for details + // TODO Support annotation annotations and annotation array annotations + public static void visitAnnotation(AnnotationVisitor av, String fieldName, Object fieldValue) { + Class fieldValueClass = fieldValue.getClass(); + + if (fieldValue instanceof Class) { + av.visit(fieldName, Type.getType((Class)fieldValue)); + } else if (fieldValueClass.isEnum()) { + av.visitEnum(fieldName, ProxyCodeHelpers.mapType(fieldValueClass), fieldValue.toString()); + } else if (fieldValue instanceof List) { + AnnotationVisitor arrayVisitor = av.visitArray(fieldName); + List fieldList = (List)fieldValue; + for (Object arrayField: fieldList) { + visitAnnotation(arrayVisitor, null, arrayField); + } + arrayVisitor.visitEnd(); + } else { + av.visit(fieldName, fieldValue); + } + } public ClassFile(String name) { this(name, "java/lang/Object", Opcodes.ACC_SYNCHRONIZED | Opcodes.ACC_PUBLIC, @@ -56,6 +89,7 @@ cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); methodVisitors = Collections.synchronizedList(new ArrayList()); fieldVisitors = Collections.synchronizedList(new ArrayList()); + annotationVisitors = Collections.synchronizedList(new ArrayList()); } public void setSource(String name) { @@ -77,7 +111,54 @@ methodVisitors.add(pmv); return pmv; } + public Code addMethod(String name, String type, int access, String[] exceptions) + throws IOException + { + MethodVisitor mv = cw.visitMethod(access, name, type, null, exceptions); + Code pmv = new Code(mv, type, access); + methodVisitors.add(pmv); + return pmv; + } + + public Code addMethod(String name, String type, int access, String[] exceptions, + AnnotationDescr[]methodAnnotationDescrs, AnnotationDescr[][] parameterAnnotationDescrs) + throws IOException + { + MethodVisitor mv = cw.visitMethod(access, name, type, null, exceptions); + // method annotations + for (AnnotationDescr ad: methodAnnotationDescrs) { + AnnotationVisitor av = mv.visitAnnotation(ad.getName(), true); + if (ad.hasFields()) { + visitAnnotations(av, ad.getFields()); + } + av.visitEnd(); + } + + // parameter annotations + for (int i = 0; i < parameterAnnotationDescrs.length; i++) { + for (AnnotationDescr ad: parameterAnnotationDescrs[i]) { + AnnotationVisitor av = mv.visitParameterAnnotation(i, ad.getName(), true); + if (ad.hasFields()) { + visitAnnotations(av, ad.getFields()); + } + av.visitEnd(); + } + } + + Code pmv = new Code(mv, type, access); + methodVisitors.add(pmv); + return pmv; + } + + public void addClassAnnotation(AnnotationDescr annotationDescr) { + AnnotationVisitor av = cw.visitAnnotation(annotationDescr.getName(), true); + if (annotationDescr.hasFields()) { + visitAnnotations(av, annotationDescr.getFields()); + } + annotationVisitors.add(av); + } + public void addField(String name, String type, int access) throws IOException { @@ -103,6 +184,12 @@ } } + public void endClassAnnotations() { + for (AnnotationVisitor av: annotationVisitors) { + av.visitEnd(); + } + } + public void write(OutputStream stream) throws IOException { cw.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, this.name, null, this.superclass, interfaces); AnnotationVisitor av = cw.visitAnnotation("Lorg/python/compiler/APIVersion;", true); @@ -118,6 +205,7 @@ if (sfilename != null) { cw.visitSource(sfilename, null); } + endClassAnnotations(); endFields(); endMethods(); @@ -129,4 +217,5 @@ //debug(baos); baos.close(); } + } diff --git a/src/org/python/compiler/Code.java b/src/org/python/compiler/Code.java --- a/src/org/python/compiler/Code.java +++ b/src/org/python/compiler/Code.java @@ -10,7 +10,7 @@ import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; -class Code extends MethodVisitor implements Opcodes { +public class Code extends MethodVisitor implements Opcodes { MethodVisitor mv; String sig; String locals[]; diff --git a/src/org/python/compiler/JavaMaker.java b/src/org/python/compiler/JavaMaker.java --- a/src/org/python/compiler/JavaMaker.java +++ b/src/org/python/compiler/JavaMaker.java @@ -32,11 +32,8 @@ int access) throws Exception { /* Need a fancy constructor for the Java side of things */ Code code = classfile.addMethod("", sig, access); - callSuper(code, "", name, parameters, null, sig); - code.visitVarInsn(ALOAD, 0); - getArgs(code, parameters); - code.visitMethodInsn(INVOKEVIRTUAL, classfile.name, "__initProxy__", makeSig("V", $objArr)); - code.visitInsn(RETURN); + callSuper(code, "", name, parameters, Void.TYPE, false); + callInitProxy(parameters, code); } @Override diff --git a/src/org/python/compiler/ProxyCodeHelpers.java b/src/org/python/compiler/ProxyCodeHelpers.java new file mode 100644 --- /dev/null +++ b/src/org/python/compiler/ProxyCodeHelpers.java @@ -0,0 +1,338 @@ +package org.python.compiler; + + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.Map; +import java.util.Map.Entry; + +import org.objectweb.asm.Type; +import org.python.core.Py; +import org.python.core.PyMethod; +import org.python.core.PyObject; +import org.python.core.PyProxy; +import org.python.core.PyReflectedFunction; +import org.python.util.Generic; + +/* + * Various constants and methods for generating Proxy code + */ +public class ProxyCodeHelpers { + public static final int tBoolean=0; + public static final int tByte=1; + public static final int tShort=2; + public static final int tInteger=3; + public static final int tLong=4; + public static final int tFloat=5; + public static final int tDouble=6; + public static final int tCharacter=7; + public static final int tVoid=8; + public static final int tOther=9; + public static final int tNone=10; + + public static Map, Integer> types = fillTypes(); + + public static Map, Integer> fillTypes() { + Map, Integer> typeMap = Generic.map(); + typeMap.put(Boolean.TYPE, tBoolean); + typeMap.put(Byte.TYPE, tByte); + typeMap.put(Short.TYPE, tShort); + typeMap.put(Integer.TYPE, tInteger); + typeMap.put(Long.TYPE, tLong); + typeMap.put(Float.TYPE, tFloat); + typeMap.put(Double.TYPE, tDouble); + typeMap.put(Character.TYPE, tCharacter); + typeMap.put(Void.TYPE, tVoid); + return typeMap; + } + + public static int getType(Class c) { + if (c == null) { + return tNone; + } + Object i = types.get(c); + if (i == null) { + return tOther; + } else { + return ((Integer)i); + } + } + + /** + * Retrieves name from the PyObject in proxy if it's defined in + * Python. This is a specialized helper function for internal PyProxy use. + */ + public static PyObject findPython(PyProxy proxy, String name) { + PyObject o = proxy._getPyInstance(); + if (o == null) { + proxy.__initProxy__(new Object[0]); + o = proxy._getPyInstance(); + } + PyObject ret = o.__findattr__(name); + if (ret instanceof PyMethod) { + PyMethod meth = ((PyMethod)ret); + if (meth.__func__ instanceof PyReflectedFunction) { + PyReflectedFunction func = (PyReflectedFunction)meth.__func__; + if (func.nargs > 0 && proxy.getClass() == func.argslist[0].declaringClass) { + // This function is the default return for the proxy type if the Python instance + // hasn't returned something of its own from __findattr__, so do the standard + // Java call on this + return null; + } + } + } + Py.setSystemState(proxy._getPySystemState()); + return ret; + } + + public static String mapClass(Class c) { + String name = c.getName(); + int index = name.indexOf("."); + if (index == -1) { + return name; + } + StringBuffer buf = new StringBuffer(name.length()); + int last_index = 0; + while (index != -1) { + buf.append(name.substring(last_index, index)); + buf.append("/"); + last_index = index+1; + index = name.indexOf(".", last_index); + } + buf.append(name.substring(last_index, name.length())); + return buf.toString(); + } + + public static String mapType(Class type) { + if (type.isArray()) + return "["+mapType(type.getComponentType()); + + switch (getType(type)) { + case tByte: return "B"; + case tCharacter: return "C"; + case tDouble: return "D"; + case tFloat: return "F"; + case tInteger: return "I"; + case tLong: return "J"; + case tShort: return "S"; + case tBoolean: return "Z"; + case tVoid: return "V"; + default: + return "L"+mapClass(type)+";"; + } + } + + public static String makeSig(Class ret, Class... sig) { + String[] mapped = new String[sig.length]; + for (int i = 0; i < mapped.length; i++) { + mapped[i] = mapType(sig[i]); + } + return makeSig(mapType(ret), mapped); + } + + public static String makeSig(String returnType, String... parameterTypes) { + StringBuilder buf = new StringBuilder("("); + for (String param : parameterTypes) { + buf.append(param); + } + return buf.append(')').append(returnType).toString(); + } + + public static void doReturn(Code code, Class type) throws Exception { + switch (getType(type)) { + case tNone: + break; + case tCharacter: + case tBoolean: + case tByte: + case tShort: + case tInteger: + code.ireturn(); + break; + case tLong: + code.lreturn(); + break; + case tFloat: + code.freturn(); + break; + case tDouble: + code.dreturn(); + break; + case tVoid: + code.return_(); + break; + default: + code.areturn(); + break; + } + } + + public static void doNullReturn(Code code, Class type) throws Exception { + switch (getType(type)) { + case tNone: + break; + case tCharacter: + case tBoolean: + case tByte: + case tShort: + case tInteger: + code.iconst_0(); + code.ireturn(); + break; + case tLong: + code.lconst_0(); + code.lreturn(); + break; + case tFloat: + code.fconst_0(); + code.freturn(); + break; + case tDouble: + code.dconst_0(); + code.dreturn(); + break; + case tVoid: + code.return_(); + break; + default: + code.aconst_null(); + code.areturn(); + break; + } + } + + public static String[] mapClasses(Class[] classes) { + String[] mapped = new String[classes.length]; + for (int i = 0; i < mapped.length; i++) { + mapped[i] = mapClass(classes[i]); + } + return mapped; + } + + public static String[] mapExceptions(Class[] classes) { + String[] exceptionTypes = new String[classes.length]; + for (int i = 0; i < classes.length; i++) { + // Exceptions are represented by their internal names + exceptionTypes[i] = Type.getType(classes[i]).getInternalName(); + } + return exceptionTypes; + } + + public static class MethodDescr { + + public final Class returnType; + + public final String name; + + public final Class[] parameters; + public final Class[] exceptions; + public final Map methodAnnotations; + public final Map[] parameterAnnotations; + + public MethodDescr(Method m) { + this(m.getName(), m.getReturnType(), m.getParameterTypes(), m.getExceptionTypes(), null, null); + } + + public MethodDescr(String name, + Class returnType, + Class[] parameters, + Class[] exceptions) { + this.name = name; + this.returnType = returnType; + this.parameters = parameters; + this.exceptions = exceptions; + this.methodAnnotations = null; + this.parameterAnnotations = null; + } + + public MethodDescr(String name, + Class returnType, + Class[] parameters, + Class[] exceptions, + Map methodAnnotations, + Map[] parameterAnnotations) { + this.name = name; + this.returnType = returnType; + this.parameters = parameters; + this.exceptions = exceptions; + this.methodAnnotations = methodAnnotations; + this.parameterAnnotations = parameterAnnotations; + } + + @Override + public int hashCode() { + return name.hashCode() + parameters.length; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof MethodDescr)) { + return false; + } + MethodDescr oDescr = (MethodDescr)obj; + if (!name.equals(oDescr.name) || parameters.length != oDescr.parameters.length) { + return false; + } + for (int i = 0; i < parameters.length; i++) { + if (!parameters[i].equals(oDescr.parameters[i])) { + return false; + } + } + return true; + } + } + + public static class ConstructorDescr extends MethodDescr { + + public ConstructorDescr(Constructor cons) { + this(cons.getParameterTypes(), cons.getExceptionTypes()); + } + + public ConstructorDescr(Class[] parameters, Class[] exceptions) { + super("", Void.TYPE, parameters, exceptions); + } + } + + public static class AnnotationDescr { + public final Class annotation; + public final Map fields; + + public AnnotationDescr(Classannotation) { + this.annotation = annotation; + this.fields = null; + } + + public AnnotationDescr(Classannotation, Map fields) { + this.annotation = annotation; + this.fields = fields; + } + + public boolean hasFields() { + if (fields == null) { + return false; + } + return true; + } + + public String getName() { + return mapType(annotation); + } + + public Map getFields() { + return fields; + } + + @Override + public int hashCode() { + if (hasFields()) { + int hash = annotation.hashCode(); + for (Entry field: fields.entrySet()) { + hash += field.getKey().hashCode() + field.getValue().hashCode(); + } + return hash; + } else { + return annotation.hashCode(); + } + } + } +} diff --git a/src/org/python/compiler/ProxyMaker.java b/src/org/python/compiler/ProxyMaker.java --- a/src/org/python/compiler/ProxyMaker.java +++ b/src/org/python/compiler/ProxyMaker.java @@ -5,89 +5,18 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.util.Map; import java.util.Set; import org.objectweb.asm.Label; import org.objectweb.asm.Opcodes; import org.python.core.Py; -import org.python.core.PyMethod; -import org.python.core.PyObject; -import org.python.core.PyProxy; -import org.python.core.PyReflectedFunction; import org.python.util.Generic; -public class ProxyMaker implements ClassConstants, Opcodes + +public class ProxyMaker extends ProxyCodeHelpers implements ClassConstants, Opcodes { - public static final int tBoolean=0; - public static final int tByte=1; - public static final int tShort=2; - public static final int tInteger=3; - public static final int tLong=4; - public static final int tFloat=5; - public static final int tDouble=6; - public static final int tCharacter=7; - public static final int tVoid=8; - public static final int tOther=9; - public static final int tNone=10; - - public static Map, Integer> types = fillTypes(); - - public static Map, Integer> fillTypes() { - Map, Integer> typeMap = Generic.map(); - typeMap.put(Boolean.TYPE, tBoolean); - typeMap.put(Byte.TYPE, tByte); - typeMap.put(Short.TYPE, tShort); - typeMap.put(Integer.TYPE, tInteger); - typeMap.put(Long.TYPE, tLong); - typeMap.put(Float.TYPE, tFloat); - typeMap.put(Double.TYPE, tDouble); - typeMap.put(Character.TYPE, tCharacter); - typeMap.put(Void.TYPE, tVoid); - return typeMap; - } - - public static int getType(Class c) { - if (c == null) { - return tNone; - } - Object i = types.get(c); - if (i == null) { - return tOther; - } else { - return ((Integer)i); - } - } - - /** - * Retrieves name from the PyObject in proxy if it's defined in - * Python. This is a specialized helper function for internal PyProxy use. - */ - public static PyObject findPython(PyProxy proxy, String name) { - PyObject o = proxy._getPyInstance(); - if (o == null) { - proxy.__initProxy__(new Object[0]); - o = proxy._getPyInstance(); - } - PyObject ret = o.__findattr__(name); - if (ret instanceof PyMethod) { - PyMethod meth = ((PyMethod)ret); - if (meth.__func__ instanceof PyReflectedFunction) { - PyReflectedFunction func = (PyReflectedFunction)meth.__func__; - if (func.nargs > 0 && proxy.getClass() == func.argslist[0].declaringClass) { - // This function is the default return for the proxy type if the Python instance - // hasn't returned something of its own from __findattr__, so do the standard - // Java call on this - return null; - } - } - } - Py.setSystemState(proxy._getPySystemState()); - return ret; - } - - Class superclass; - Class[] interfaces; + protected final Class superclass; + protected final Class[] interfaces; Set names; Set supernames = Generic.set(); public ClassFile classfile; @@ -137,134 +66,17 @@ this.interfaces = interfaces; } - public static String mapClass(Class c) { - String name = c.getName(); - int index = name.indexOf("."); - if (index == -1) { - return name; - } - StringBuffer buf = new StringBuffer(name.length()); - int last_index = 0; - while (index != -1) { - buf.append(name.substring(last_index, index)); - buf.append("/"); - last_index = index+1; - index = name.indexOf(".", last_index); - } - buf.append(name.substring(last_index, name.length())); - return buf.toString(); - } - - public static String mapType(Class type) { - if (type.isArray()) - return "["+mapType(type.getComponentType()); - - switch (getType(type)) { - case tByte: return "B"; - case tCharacter: return "C"; - case tDouble: return "D"; - case tFloat: return "F"; - case tInteger: return "I"; - case tLong: return "J"; - case tShort: return "S"; - case tBoolean: return "Z"; - case tVoid: return "V"; - default: - return "L"+mapClass(type)+";"; - } - } - - public static String makeSig(Class ret, Class... sig) { - String[] mapped = new String[sig.length]; - for (int i = 0; i < mapped.length; i++) { - mapped[i] = mapType(sig[i]); - } - return makeSig(mapType(ret), mapped); - } - - public static String makeSig(String returnType, String... parameterTypes) { - StringBuilder buf = new StringBuilder("("); - for (String param : parameterTypes) { - buf.append(param); - } - return buf.append(')').append(returnType).toString(); - } - - public void doConstants() throws Exception { Code code = classfile.addMethod("", makeSig("V"), Modifier.STATIC); code.return_(); } - public static void doReturn(Code code, Class type) throws Exception { - switch (getType(type)) { - case tNone: - break; - case tCharacter: - case tBoolean: - case tByte: - case tShort: - case tInteger: - code.ireturn(); - break; - case tLong: - code.lreturn(); - break; - case tFloat: - code.freturn(); - break; - case tDouble: - code.dreturn(); - break; - case tVoid: - code.return_(); - break; - default: - code.areturn(); - break; - } - } - - public static void doNullReturn(Code code, Class type) throws Exception { - switch (getType(type)) { - case tNone: - break; - case tCharacter: - case tBoolean: - case tByte: - case tShort: - case tInteger: - code.iconst_0(); - code.ireturn(); - break; - case tLong: - code.lconst_0(); - code.lreturn(); - break; - case tFloat: - code.fconst_0(); - code.freturn(); - break; - case tDouble: - code.dconst_0(); - code.dreturn(); - break; - case tVoid: - code.return_(); - break; - default: - code.aconst_null(); - code.areturn(); - break; - } - } - public void callSuper(Code code, String name, String superclass, Class[] parameters, Class ret, - String sig) throws Exception { + boolean doReturn) throws Exception { code.aload(0); int local_index; @@ -297,9 +109,11 @@ break; } } - code.invokespecial(superclass, name, sig); + code.invokespecial(superclass, name, makeSig(ret, parameters)); - doReturn(code, ret); + if (doReturn) { + doReturn(code, ret); + } } public void doJavaCall(Code code, String name, String type, @@ -471,6 +285,52 @@ public void addMethod(Method method, int access) throws Exception { + addMethod(method.getName(), method.getReturnType(), method.getParameterTypes(), + method.getExceptionTypes(), access, method.getDeclaringClass()); + } + + /** + * Adds a method of the given name to the class being implemented. If + * declaringClass is null, the generated method will expect to find an object of + * the method's name in the Python object and call it. If it isn't null, if an object is found + * in the Python object, it'll be called. Otherwise the superclass will be called. No checking + * is done to guarantee that the superclass has a method with the same signature. + */ + public void addMethod(String name, + Class ret, + Class[] parameters, + Class[] exceptions, + int access, + Class declaringClass) throws Exception { + addMethod(name, name, ret, parameters, exceptions, access, declaringClass, null, null); + } + + + /** + * Generates and adds a proxy method to the proxy class + * + * @param name: name of the java method + * @param pyName: name of the python method to which the java method + * proxies (useful for clamped objects) + * + * @param ret: return type + * @param parameters: parameter types + * @param exceptions: throwable exception types + * @param access + * @param declaringClass + * @param methodAnnotations: method annotations + * @param parameterAnnotations: parameter annotations + * @throws Exception + */ + public void addMethod(String name, + String pyName, + Class ret, + Class[] parameters, + Class[] exceptions, + int access, + Class declaringClass, + AnnotationDescr[] methodAnnotations, + AnnotationDescr[][]parameterAnnotations) throws Exception { boolean isAbstract = false; if (Modifier.isAbstract(access)) { @@ -478,17 +338,20 @@ isAbstract = true; } - Class[] parameters = method.getParameterTypes(); - Class ret = method.getReturnType(); String sig = makeSig(ret, parameters); + String[] exceptionTypes = mapExceptions(exceptions); - String name = method.getName(); names.add(name); - Code code = classfile.addMethod(name, sig, access); + Code code = null; + if (methodAnnotations != null && parameterAnnotations != null) { + code = classfile.addMethod(name, sig, access, exceptionTypes, methodAnnotations, parameterAnnotations); + } else { + code = classfile.addMethod(name, sig, access, exceptionTypes); + } code.aload(0); - code.ldc(name); + code.ldc(pyName); if (!isAbstract) { int tmp = code.getLocal("org/python/core/PyObject"); @@ -500,12 +363,12 @@ Label callPython = new Label(); code.ifnonnull(callPython); - String superClass = mapClass(method.getDeclaringClass()); + String superClass = mapClass(declaringClass); - callSuper(code, name, superClass, parameters, ret, sig); + callSuper(code, name, superClass, parameters, ret, true); code.label(callPython); code.aload(tmp); - callMethod(code, name, parameters, ret, method.getExceptionTypes()); + callMethod(code, name, parameters, ret, exceptions); addSuperMethod("super__"+name, name, superClass, parameters, ret, sig, access); @@ -515,13 +378,37 @@ code.dup(); Label returnNull = new Label(); code.ifnull(returnNull); - callMethod(code, name, parameters, ret, method.getExceptionTypes()); + callMethod(code, name, parameters, ret, exceptions); code.label(returnNull); code.pop(); doNullReturn(code, ret); } } + + /** + * A constructor that is also a method (!) + */ + public void addConstructorMethodCode(String pyName, + Class[] parameters, + Class[] exceptions, + int access, + Class declaringClass, + Code code) throws Exception { + code.aload(0); + code.ldc(pyName); + + int tmp = code.getLocal("org/python/core/PyObject"); + code.invokestatic("org/python/compiler/ProxyMaker", "findPython", + makeSig($pyObj, $pyProxy, $str)); + code.astore(tmp); + code.aload(tmp); + callMethod(code, "", parameters, Void.TYPE, exceptions); + +// addSuperMethod("super__"+name, name, superClass, parameters, +// ret, sig, access); + } + private String methodString(Method m) { StringBuffer buf = new StringBuffer(m.getName()); buf.append(":"); @@ -579,7 +466,7 @@ String sig, int access) throws Exception { Code code = classfile.addMethod("", sig, access); - callSuper(code, "", name, parameters, Void.TYPE, sig); + callSuper(code, "", name, parameters, Void.TYPE, true); } public void addConstructors(Class c) throws Exception { @@ -600,6 +487,10 @@ addConstructor(name, parameters, Void.TYPE, makeSig(Void.TYPE, parameters), access); } } + + protected void addClassAnnotation(AnnotationDescr annotation) { + classfile.addClassAnnotation(annotation); + } // Super methods are added for the following three reasons: // @@ -654,7 +545,7 @@ } supernames.add(methodName); Code code = classfile.addMethod(methodName, sig, access); - callSuper(code, superName, declClass, parameters, ret, sig); + callSuper(code, superName, declClass, parameters, ret, true); } public void addProxy() throws Exception { @@ -728,20 +619,123 @@ classfile = new ClassFile(myClass, mapClass(superclass), access); addProxy(); - addConstructors(superclass); + visitConstructors(); classfile.addInterface("org/python/core/PyProxy"); + + visitClassAnnotations(); + visitMethods(); + doConstants(); + addClassDictInit(); + } + + /** + * Visits all methods declared on the given class and classes in its inheritance hierarchy. + * Methods visible to subclasses are added to seen. + */ + protected void visitMethods(Class klass) throws Exception { + for (Method method : klass.getDeclaredMethods()) { + int access = method.getModifiers(); + if (Modifier.isStatic(access) || Modifier.isPrivate(access)) { + continue; + } - Set seenmethods = Generic.set(); - addMethods(superclass, seenmethods); + if (Modifier.isNative(access)) { + access = access & ~Modifier.NATIVE; + } + + if (Modifier.isProtected(access)) { + access = (access & ~Modifier.PROTECTED) | Modifier.PUBLIC; + if (Modifier.isFinal(access)) { + addSuperMethod(method, access); + continue; + } + } else if (Modifier.isFinal(access)) { + continue; + } else if (!Modifier.isPublic(access)) { + continue; // package protected by process of elimination; we can't override + } + addMethod(method, access); + } + + Class superClass = klass.getSuperclass(); + if (superClass != null) { + visitMethods(superClass); + } + + for (Class iface : klass.getInterfaces()) { + visitMethods(iface); + } + } + + /** + * Called for every method on the proxy's superclass and interfaces that can be overriden by the + * proxy class. If the proxy wants to perform Python lookup and calling for the method, + * {@link #addMethod(Method)} should be called. For abstract methods, addMethod must be called. + */ + protected void visitMethod(Method method) throws Exception { + addMethod(method, method.getModifiers()); + } + + protected void visitMethods() throws Exception { + visitMethods(superclass); for (Class iface : interfaces) { if (iface.isAssignableFrom(superclass)) { Py.writeWarning("compiler", "discarding redundant interface: " + iface.getName()); continue; } classfile.addInterface(mapClass(iface)); - addMethods(iface, seenmethods); + visitMethods(iface); } - doConstants(); - addClassDictInit(); } + + /** Adds a constructor that calls through to superclass. */ + protected void addConstructor(Class[] parameters, int access) throws Exception { + String sig = makeSig(Void.TYPE, parameters); + Code code = classfile.addMethod("", sig, access); + callSuper(code, "", mapClass(superclass), parameters, Void.TYPE, true); + } + + /** + * Called for every constructor on the proxy's superclass that can be overridden by + * the proxy class. + */ + protected void visitConstructor(Constructor constructor) throws Exception { + /* Need a fancy constructor for the Java side of things */ + callInitProxy(constructor.getParameterTypes(), addOpenConstructor(constructor)); + } + + /** + * Adds a constructor that calls through to the superclass constructor with the same signature + * and leaves the returned Code open for more operations. The caller of this method must add a + * return to the Code. + */ + protected Code addOpenConstructor(Constructor constructor) throws Exception { + String sig = makeSig(Void.TYPE, constructor.getParameterTypes()); + Code code = classfile.addMethod("", sig, constructor.getModifiers()); + callSuper(code, "", mapClass(superclass), constructor.getParameterTypes(), Void.TYPE, true); + return code; + } + + /** + * Calls __initProxy__ on this class with the given types of parameters, which must be + * available as arguments to the currently called method in the order of the parameters. + */ + protected void callInitProxy(Class[] parameters, Code code) throws Exception { + code.visitVarInsn(ALOAD, 0); + getArgs(code, parameters); + code.visitMethodInsn(INVOKEVIRTUAL, classfile.name, "__initProxy__", makeSig("V", $objArr)); + code.visitInsn(RETURN); + } + + /** + * Visits constructors from this proxy's superclass. + */ + protected void visitConstructors() throws Exception { + addConstructors(superclass); + } + + protected void visitClassAnnotations() throws Exception { + // ProxyMaker itself does nothing with class annotations for now + } + } diff --git a/src/org/python/core/MakeProxies.java b/src/org/python/core/MakeProxies.java --- a/src/org/python/core/MakeProxies.java +++ b/src/org/python/core/MakeProxies.java @@ -6,6 +6,7 @@ import java.util.ArrayList; import java.util.List; +import org.python.compiler.APIVersion; import org.python.compiler.AdapterMaker; import org.python.compiler.JavaMaker; @@ -50,27 +51,52 @@ public static synchronized Class makeProxy(Class superclass, List> vinterfaces, String className, String proxyName, PyObject dict) { + JavaMaker javaMaker = null; + + // check if a Jython annotation exists and if yes, the class is already a Jython Proxy + APIVersion apiVersion = superclass.getAnnotation(org.python.compiler.APIVersion.class); + if (apiVersion != null) { + // TODO validate versions, maybe use a different annotation + return superclass; + } + Class[] interfaces = vinterfaces.toArray(new Class[vinterfaces.size()]); String fullProxyName = proxyPrefix + proxyName + "$" + proxyNumber++; String pythonModuleName; - PyObject mn = dict.__finditem__("__module__"); - if (mn == null) { - pythonModuleName = "foo"; + PyObject module = dict.__finditem__("__module__"); + if (module == null) { + pythonModuleName = "foo"; // FIXME Really, module name foo? } else { - pythonModuleName = (String) mn.__tojava__(String.class); + pythonModuleName = (String) module.__tojava__(String.class); } - JavaMaker jm = new JavaMaker(superclass, - interfaces, - className, - pythonModuleName, - fullProxyName, - dict); + + // Grab the proxy maker from the class if it exists, and if it does, use the proxy class + // name from the maker + PyObject customProxyMaker = dict.__finditem__("__proxymaker__"); + if (customProxyMaker != null) { + if (module == null) { + throw Py.TypeError("Classes using __proxymaker__ must define __module__"); + } + PyObject[] args = Py.javas2pys(superclass, interfaces, className, pythonModuleName, fullProxyName, dict); + javaMaker = Py.tojava(customProxyMaker.__call__(args), JavaMaker.class); + // TODO Full proxy name + } + + if (javaMaker == null) { + javaMaker = new JavaMaker(superclass, + interfaces, + className, + pythonModuleName, + fullProxyName, + dict); + } + try { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - jm.build(bytes); - Py.saveClassFile(fullProxyName, bytes); + javaMaker.build(bytes); + Py.saveClassFile(javaMaker.myClass, bytes); - return makeClass(superclass, vinterfaces, jm.myClass, bytes); + return makeClass(superclass, vinterfaces, javaMaker.myClass, bytes); } catch (Exception exc) { throw Py.JavaError(exc); } diff --git a/src/org/python/util/ProxyCompiler.java b/src/org/python/util/ProxyCompiler.java new file mode 100644 --- /dev/null +++ b/src/org/python/util/ProxyCompiler.java @@ -0,0 +1,32 @@ +package org.python.util; + +import java.util.Properties; + +import org.python.core.PySystemState; + +public class ProxyCompiler { + /** + * Compiles the python file by loading it + * + * FIXME: this is quite hackish right now. It basically starts a whole interpreter + * and set's proxyDebugDirectory as the destination for the compiled proxy class + * + * @param filename: python filename to exec + * @param destDir: destination directory for the proxy classes + */ + public static void compile(String filename, String destDir) { + Properties props = new Properties(System.getProperties()); + props.setProperty(PySystemState.PYTHON_CACHEDIR_SKIP, "true"); + PySystemState.initialize(props, null); + PythonInterpreter interp = new PythonInterpreter(); + + String origProxyDir = org.python.core.Options.proxyDebugDirectory; + try { + org.python.core.Options.proxyDebugDirectory = destDir; + interp.execfile(filename); + } finally { + org.python.core.Options.proxyDebugDirectory = origProxyDir; + } + } + +} diff --git a/tests/java/org/python/compiler/custom_proxymaker/ClassAnnotationTest.java b/tests/java/org/python/compiler/custom_proxymaker/ClassAnnotationTest.java new file mode 100644 --- /dev/null +++ b/tests/java/org/python/compiler/custom_proxymaker/ClassAnnotationTest.java @@ -0,0 +1,36 @@ +package org.python.compiler.custom_proxymaker; + +/* + * Test support for Python class annotations + */ + +import static org.junit.Assert.*; + +import org.junit.*; +import org.python.util.ProxyCompiler; + +public class ClassAnnotationTest { + + Class proxy; + + @Before + public void setUp() throws Exception { + ProxyCompiler.compile("tests/python/custom_proxymaker/annotated_class.py", "build/classes"); + proxy = Class.forName("custom_proxymaker.tests.AnnotatedInputStream"); + } + + @Test + public void hasClassAnnotation() { + // Just by "finding" it we satisfy the test. + @SuppressWarnings("unused") + Deprecated deprecatedAnnotation = proxy.getAnnotation(Deprecated.class); + } + + @Test + public void hasCustomAnnotationWithFields() throws Exception { + CustomAnnotation customAnnotation = proxy.getAnnotation(CustomAnnotation.class); + assertEquals("Darusik", customAnnotation.createdBy()); + assertEquals(CustomAnnotation.Priority.LOW, customAnnotation.priority()); + assertArrayEquals(new String[] {"Darjus", "Darjunia"}, customAnnotation.changedBy()); + } +} diff --git a/tests/java/org/python/compiler/custom_proxymaker/ConstructorSignatureTest.java b/tests/java/org/python/compiler/custom_proxymaker/ConstructorSignatureTest.java new file mode 100644 --- /dev/null +++ b/tests/java/org/python/compiler/custom_proxymaker/ConstructorSignatureTest.java @@ -0,0 +1,33 @@ +package org.python.compiler.custom_proxymaker; + +/* + * Tests constructor signatures + */ + +import static org.junit.Assert.*; + +import java.lang.reflect.*; + +import org.junit.*; +import org.python.util.ProxyCompiler; + +import java.awt.Container; +import javax.swing.BoxLayout; + +public class ConstructorSignatureTest { + Class proxy; + + @Before + public void setUp() throws Exception { + ProxyCompiler.compile("tests/python/custom_proxymaker/constructor_signatures.py", "build/classes"); + proxy = Class.forName("custom_proxymaker.tests.ConstructorSignatures"); + } + + @Ignore // Constructor signatures are not working yet + @Test + @SuppressWarnings("unused") + public void returnsVoid() throws Exception { + Constructor constructor = proxy.getConstructor(new Class[] {Container.class, Integer.TYPE}); + constructor.newInstance(new Container(), BoxLayout.X_AXIS); + } +} diff --git a/tests/java/org/python/compiler/custom_proxymaker/CustomAnnotation.java b/tests/java/org/python/compiler/custom_proxymaker/CustomAnnotation.java new file mode 100644 --- /dev/null +++ b/tests/java/org/python/compiler/custom_proxymaker/CustomAnnotation.java @@ -0,0 +1,30 @@ +package org.python.compiler.custom_proxymaker; + +/** + * This Annotation contains most of the possible annotation fields, + * used to test the annotation part of custom proxymaker + */ + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + + + at Documented + at Retention(RetentionPolicy.RUNTIME) + at Target({ElementType.TYPE,ElementType.METHOD, + ElementType.CONSTRUCTOR,ElementType.ANNOTATION_TYPE, + ElementType.PACKAGE,ElementType.FIELD,ElementType.LOCAL_VARIABLE}) + at Inherited +public @interface CustomAnnotation { + public enum Priority { LOW, MEDIUM, HIGH } + String value(); + String[] changedBy() default ""; + Priority[] priorities(); + Priority priority() default Priority.MEDIUM; + String createdBy() default "Darjus Loktevic"; + String lastChanged() default "08/06/2012"; +} diff --git a/tests/java/org/python/compiler/custom_proxymaker/JUnitTest.java b/tests/java/org/python/compiler/custom_proxymaker/JUnitTest.java new file mode 100644 --- /dev/null +++ b/tests/java/org/python/compiler/custom_proxymaker/JUnitTest.java @@ -0,0 +1,18 @@ +package org.python.compiler.custom_proxymaker; + +/* + * This test tests that we can create JUnit 4 tests in Python and that JUnit's own + * reflection system picks our annotations and runs the underlying code + */ + +import org.junit.Test; +import org.junit.runner.JUnitCore; +import org.python.util.ProxyCompiler; + +public class JUnitTest { + @Test + public void testMethodSignatures() throws Exception { + ProxyCompiler.compile("tests/python/custom_proxymaker/junit_test.py", "build/classes"); + JUnitCore.runClasses(Class.forName("custom_proxymaker.tests.JUnitTest")); + } +} diff --git a/tests/java/org/python/compiler/custom_proxymaker/MethodSignatureTest.java b/tests/java/org/python/compiler/custom_proxymaker/MethodSignatureTest.java new file mode 100644 --- /dev/null +++ b/tests/java/org/python/compiler/custom_proxymaker/MethodSignatureTest.java @@ -0,0 +1,77 @@ +package org.python.compiler.custom_proxymaker; + +/* + * Tests support for various combinations of method signatures + */ + +import static org.junit.Assert.*; + +import java.lang.reflect.*; + +import org.junit.Before; +import org.junit.Test; +import org.python.util.ProxyCompiler; + +public class MethodSignatureTest { + Class proxy; + + @Before + public void setUp() throws Exception { + ProxyCompiler.compile("tests/python/custom_proxymaker/method_signatures.py", "build/classes"); + proxy = Class.forName("custom_proxymaker.tests.MethodSignatures"); + } + + @Test + public void methodThrows() throws Exception { + Method method = proxy.getMethod("throwsException"); + assertArrayEquals(new Class[] {RuntimeException.class}, method.getExceptionTypes()); + } + + @Test + public void returnsVoid() throws Exception { + Method method = proxy.getMethod("throwsException"); + assertEquals(Void.TYPE, method.getReturnType()); + } + + @Test + public void returnsLong() throws Exception { + Method method = proxy.getMethod("returnsLong"); + assertEquals(Long.TYPE, method.getReturnType()); + } + + @Test + public void returnsObject() throws Exception { + Method method = proxy.getMethod("returnsObject"); + assertEquals(Object.class, method.getReturnType()); + } + + @Test + public void returnsArray() throws Exception { + Method method = proxy.getMethod("returnsArray"); + Object compareType = Array.newInstance(Long.TYPE, 0); + assertEquals(compareType.getClass(), method.getReturnType()); + } + + @Test + public void returnsArrayObj() throws Exception { + Method method = proxy.getMethod("returnsArrayObj"); + Object compareType = Array.newInstance(Object.class, 0); + assertEquals(compareType.getClass(), method.getReturnType()); + } + + @Test + @SuppressWarnings("unused") + public void acceptsString() throws Exception { + Class[] partypes = new Class[] {String.class}; + Method method = proxy.getMethod("acceptsString", partypes); + } + + @Test + @SuppressWarnings("unused") + public void acceptsArray() throws Exception { + Object compareType = Array.newInstance(Long.TYPE, 0); + Class[] partypes = new Class[] {compareType.getClass()}; + Method method = proxy.getMethod("acceptsArray", partypes); + } + +} diff --git a/tests/java/org/python/compiler/custom_proxymaker/MiniClampMaker.java b/tests/java/org/python/compiler/custom_proxymaker/MiniClampMaker.java new file mode 100644 --- /dev/null +++ b/tests/java/org/python/compiler/custom_proxymaker/MiniClampMaker.java @@ -0,0 +1,182 @@ +package org.python.compiler.custom_proxymaker; + +/* + * This is a bare bones implementation of ClampMaker. It's goal is to be a "reference" + * implementation for the features that are provided by customizable ProxyMaker + */ + +import java.lang.reflect.Constructor; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; + +import org.python.compiler.Code; +import org.python.compiler.JavaMaker; +import org.python.core.Py; +import org.python.core.PyDictionary; +import org.python.core.PyObject; +import org.python.core.__builtin__; +import org.python.util.Generic; + +public class MiniClampMaker extends JavaMaker { + + private final Map methodsToAdd = Generic.map(); + private final Map constructorsToAdd = Generic.map(); + private AnnotationDescr[] classAnnotations = new AnnotationDescr[]{}; + + private static AnnotationDescr[] extractAnnotation(PyDictionary dict) { + List annotationDescrs = Generic.list(); + for (PyObject annotationIter: dict.iteritems().asIterable()) { + PyObject annotationClass = annotationIter.__getitem__(0); + PyObject annotationFields = annotationIter.__getitem__(1); + AnnotationDescr annotationDescr = null; + if (annotationFields == Py.None) { + annotationDescr = new AnnotationDescr(Py.tojava(annotationClass, Class.class)); + } else { + Map fields = Generic.map(); + for (PyObject item: ((PyDictionary)annotationFields).iteritems().asIterable()) { + fields.put(Py.tojava(item.__getitem__(0), String.class), Py.tojava(item.__getitem__(1), Object.class)); + } + annotationDescr = new AnnotationDescr(Py.tojava(annotationClass, Class.class), fields); + } + annotationDescrs.add(annotationDescr); + } + return (AnnotationDescr[]) annotationDescrs.toArray(new AnnotationDescr[annotationDescrs.size()]); + } + + public MiniClampMaker(Class superclass, + Class[] interfaces, + String pythonClass, + String pythonModule, + String myClass, + PyObject methods) { + super(superclass, interfaces, pythonClass, pythonModule, myClass, methods); + + // if we find __java_package__, override the default proxy naming with + // __java_package__ + .pythonClass + PyObject javaPackage = methods.__finditem__("__java_package__"); + if (javaPackage != null) { + String newMyClass = new String((String)javaPackage.__tojava__(String.class)); + newMyClass += "." + pythonClass; + this.myClass = newMyClass; + } + + + PyObject clampAttr = Py.newString("_clamp"); + for (PyObject pykey : methods.asIterable()) { + String key = Py.tojava(pykey, String.class); + PyObject value = methods.__finditem__(key); + PyObject clampObj = __builtin__.getattr(value, clampAttr, Py.None); + if (clampObj == Py.None) { + continue; + } + String name = (String)clampObj.__getattr__("name").__tojava__(String.class); + if (name.equals("__init__")) { + constructorsToAdd.put(key, clampObj); + } else { + methodsToAdd.put(key, clampObj); + } + } + PyObject pyAnnotations = methods.__finditem__("_class_annotations"); + if (pyAnnotations != null) { + classAnnotations = extractAnnotation((PyDictionary)pyAnnotations); + } + + } + + @Override + protected void visitClassAnnotations() throws Exception { + for (AnnotationDescr annotation: classAnnotations) { + addClassAnnotation(annotation); + } + } + + @Override + protected void visitConstructors() throws Exception { + + Set> superConstructors = Generic.set(); + for (Constructor constructor: superclass.getDeclaredConstructors()) { + superConstructors.add(constructor); + } + + for (Entry meth : constructorsToAdd.entrySet()) { + Constructor superToCall = null; + String pyName = meth.getKey(); + PyObject clampObj = meth.getValue(); + + Class[] thrownClasses = Py.tojava(clampObj.__getattr__("throws"), Class[].class); + Class[] parameterClasses = Py.tojava(clampObj.__getattr__("argtypes"), Class[].class); + + if (clampObj.__findattr__("super_constructor") != null) { + superToCall = (Constructor)clampObj.__getattr__("super_constructor").__tojava__(Constructor.class); + } else { // try to find a matching super constructor + try { + superToCall = superclass.getDeclaredConstructor(parameterClasses); + } catch (NoSuchMethodException err) { + // FIXME need a fancy constructor finder + superToCall = superConstructors.iterator().next(); + } + } + + for (Constructor constructor: superConstructors) { + if (Arrays.equals(constructor.getParameterTypes(), superToCall.getParameterTypes())) { + superConstructors.remove(constructor); + } + } + + AnnotationDescr[] methodAnnotations = extractAnnotation((PyDictionary)clampObj.__getattr__("method_annotations")); + PyObject[] parameterAnnotationObjs = (PyObject[])clampObj.__getattr__("parameter_annotations").__tojava__(PyObject[].class); + + AnnotationDescr[][]parameterAnnotations = new AnnotationDescr[parameterAnnotationObjs.length][]; + for (int i = 0; i", fullsig, Modifier.PUBLIC, mappedExceptions, methodAnnotations, parameterAnnotations); + callSuper(code, "", mapClass(superclass), superToCall.getParameterTypes(), Void.TYPE, false); + // instead of calling the proxy, we construct the full method code + + addConstructorMethodCode(pyName, superToCall.getParameterTypes(), thrownClasses, Modifier.PUBLIC, superclass, code); + + } + + // the non-overwritten constructors + for (Constructor constructor: superConstructors) { + addConstructor(constructor.getParameterTypes(), Modifier.PUBLIC); + } + } + + @Override + protected void visitMethods () throws Exception { + for (Entry meth : methodsToAdd.entrySet()) { + PyObject clampObj = meth.getValue(); + String methodName = (String)clampObj.__getattr__("name").__tojava__(String.class); + Class returnClass = (Class)clampObj.__getattr__("returntype").__tojava__(Class.class); + Class[] thrownClasses = Py.tojava(clampObj.__getattr__("throws"), Class[].class); + Class[] parameterClasses = Py.tojava(clampObj.__getattr__("argtypes"), Class[].class); + AnnotationDescr[] methodAnnotations = extractAnnotation((PyDictionary)clampObj.__getattr__("method_annotations")); + PyObject[] parameterAnnotationObjs = (PyObject[])clampObj.__getattr__("parameter_annotations").__tojava__(PyObject[].class); + + AnnotationDescr[][]parameterAnnotations = new AnnotationDescr[parameterAnnotationObjs.length][]; + for (int i = 0; i)returnClass, parameterClasses, thrownClasses, + Modifier.PUBLIC, superclass, methodAnnotations, parameterAnnotations); + + } + } +} + diff --git a/tests/python/custom_proxymaker/annotated_class.py b/tests/python/custom_proxymaker/annotated_class.py new file mode 100644 --- /dev/null +++ b/tests/python/custom_proxymaker/annotated_class.py @@ -0,0 +1,18 @@ +import java.io.BufferedInputStream + +from java.lang import Deprecated + +from org.python.compiler.custom_proxymaker import MiniClampMaker +from org.python.compiler.custom_proxymaker import CustomAnnotation + +class AnnotatedInputStream(java.io.BufferedInputStream): + __proxymaker__ = MiniClampMaker + __java_package__ = 'custom_proxymaker.tests' + + _class_annotations = {CustomAnnotation: + {'createdBy': 'Darusik', + 'priority': CustomAnnotation.Priority.LOW, + 'changedBy': ['Darjus', 'Darjunia']}, + Deprecated:None} + + diff --git a/tests/python/custom_proxymaker/clamp.py b/tests/python/custom_proxymaker/clamp.py new file mode 100644 --- /dev/null +++ b/tests/python/custom_proxymaker/clamp.py @@ -0,0 +1,5 @@ +from collections import namedtuple + +# just a helper +ClampMethod = namedtuple('ClampMethod', 'name returntype argtypes throws ' + 'method_annotations parameter_annotations') diff --git a/tests/python/custom_proxymaker/constructor_signatures.py b/tests/python/custom_proxymaker/constructor_signatures.py new file mode 100644 --- /dev/null +++ b/tests/python/custom_proxymaker/constructor_signatures.py @@ -0,0 +1,32 @@ +from java.lang import (Void, String, Integer, Long) +from javax.swing import BoxLayout +from java.awt import Container + +from org.python.compiler.custom_proxymaker import MiniClampMaker + +import sys +sys.path.append('tests/python/custom_proxymaker/') +from clamp import ClampMethod + +class ConstructorSignatures(BoxLayout): + __proxymaker__ = MiniClampMaker + __java_package__ = 'custom_proxymaker.tests' + +# def __init__(self, val): +# super(ConstructorSignatures, self).__init__(Container(), BoxLayout.X_AXIS) +# print val + + def __jinit__(self, one, two): + # super(ConstructorSignatures, self).__init__(Container(), BoxLayout.X_AXIS) + print one, two + + __jinit__._clamp = ClampMethod('__init__', Void.TYPE, [Container, Integer.TYPE], [], {}, [{}]) + + def test(self): + return 1 + test._clamp = ClampMethod('test', Long.TYPE, [], [], {}, [{}]) + + def toString(self): + return self.__class__.__name__ + toString._clamp = ClampMethod('toString', String, [], [], {}, [{}]) + diff --git a/tests/python/custom_proxymaker/junit_test.py b/tests/python/custom_proxymaker/junit_test.py new file mode 100644 --- /dev/null +++ b/tests/python/custom_proxymaker/junit_test.py @@ -0,0 +1,28 @@ +from org.junit.Assert import assertEquals +from org.junit import Test +from java.lang import (Object, Void, Long) +from java.lang import Exception as JavaException +import sys +import time + +from org.python.compiler.custom_proxymaker import MiniClampMaker + +sys.path.append('tests/python/custom_proxymaker/') +from clamp import ClampMethod + +class JUnitTest(Object): + __proxymaker__ = MiniClampMaker + __java_package__ = 'custom_proxymaker.tests' + + + def testAddition(self): + assertEquals(4, 1 + 3) + testAddition._clamp = ClampMethod('testAddition',Void.TYPE,[],[],{Test:None},[{}]) + + def testJavaException(self): + raise JavaException() + testJavaException._clamp = ClampMethod('testJavaException', Void.TYPE, [], [JavaException], {Test:{'expected':JavaException}}, [{}]) + + def testTimeout(self): + time.sleep(0.1) + testTimeout._clamp = ClampMethod('testTimeout', Void.TYPE, [], [], {Test:{'timeout':Long(1000)}}, [{}]) diff --git a/tests/python/custom_proxymaker/method_signatures.py b/tests/python/custom_proxymaker/method_signatures.py new file mode 100644 --- /dev/null +++ b/tests/python/custom_proxymaker/method_signatures.py @@ -0,0 +1,41 @@ +from java.lang import (Class, Object, Void, String, Long) +from java.lang import RuntimeException + +from org.python.compiler.custom_proxymaker import MiniClampMaker + +import sys +sys.path.append('tests/python/custom_proxymaker/') +from clamp import ClampMethod + +class MethodSignatures(Object): + __proxymaker__ = MiniClampMaker + __java_package__ = 'custom_proxymaker.tests' + + def throwsException(self): + pass + throwsException._clamp = ClampMethod('throwsException', Void.TYPE, [], [RuntimeException], {}, [{}]) + + def returnsLong(self): + return 2 + returnsLong._clamp = ClampMethod('returnsLong', Long.TYPE, [], [], {}, [{}]) + + def returnsObject(self): + return Object() + returnsObject._clamp = ClampMethod('returnsObject', Object, [], [], {}, [{}]) + + def returnsArray(self): + return [1,2,3] + returnsArray._clamp = ClampMethod('returnsArray', Class.forName('[J'), [], [], {}, [{}]) + + def returnsArrayObj(self): + return [1,2,3] + returnsArrayObj._clamp = ClampMethod('returnsArrayObj', Class.forName('[Ljava.lang.Object;'), [], [], {}, [{}]) + + def acceptsString(self, arg): + pass + acceptsString._clamp = ClampMethod('acceptsString', Void.TYPE, [String], [], {}, [{}]) + + def acceptsArray(self, arg): + pass + acceptsArray._clamp = ClampMethod('acceptsArray', Void.TYPE, [Class.forName('[J')], [], {}, [{}]) + \ No newline at end of file -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Sat Dec 22 20:52:03 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Sat, 22 Dec 2012 20:52:03 +0100 (CET) Subject: [Jython-checkins] =?utf-8?q?jython=3A_Implemented_feedback_from_F?= =?utf-8?q?rank=27s_review_on_customizable_proxymaker?= Message-ID: <3YTHR76NT5zNsc@mail.python.org> http://hg.python.org/jython/rev/98189c8cc1a1 changeset: 6906:98189c8cc1a1 user: Darjus Loktevic date: Fri Oct 12 18:10:48 2012 -0700 summary: Implemented feedback from Frank's review on customizable proxymaker files: src/org/python/compiler/ProxyMaker.java | 3 --- tests/java/org/python/compiler/custom_proxymaker/MiniClampMaker.java | 2 +- tests/python/custom_proxymaker/annotated_class.py | 10 +++++----- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/org/python/compiler/ProxyMaker.java b/src/org/python/compiler/ProxyMaker.java --- a/src/org/python/compiler/ProxyMaker.java +++ b/src/org/python/compiler/ProxyMaker.java @@ -404,9 +404,6 @@ code.aload(tmp); callMethod(code, "", parameters, Void.TYPE, exceptions); - -// addSuperMethod("super__"+name, name, superClass, parameters, -// ret, sig, access); } private String methodString(Method m) { diff --git a/tests/java/org/python/compiler/custom_proxymaker/MiniClampMaker.java b/tests/java/org/python/compiler/custom_proxymaker/MiniClampMaker.java --- a/tests/java/org/python/compiler/custom_proxymaker/MiniClampMaker.java +++ b/tests/java/org/python/compiler/custom_proxymaker/MiniClampMaker.java @@ -80,7 +80,7 @@ methodsToAdd.put(key, clampObj); } } - PyObject pyAnnotations = methods.__finditem__("_class_annotations"); + PyObject pyAnnotations = methods.__finditem__("_clamp_class_annotations"); if (pyAnnotations != null) { classAnnotations = extractAnnotation((PyDictionary)pyAnnotations); } diff --git a/tests/python/custom_proxymaker/annotated_class.py b/tests/python/custom_proxymaker/annotated_class.py --- a/tests/python/custom_proxymaker/annotated_class.py +++ b/tests/python/custom_proxymaker/annotated_class.py @@ -9,10 +9,10 @@ __proxymaker__ = MiniClampMaker __java_package__ = 'custom_proxymaker.tests' - _class_annotations = {CustomAnnotation: - {'createdBy': 'Darusik', - 'priority': CustomAnnotation.Priority.LOW, - 'changedBy': ['Darjus', 'Darjunia']}, - Deprecated:None} + _clamp_class_annotations = {CustomAnnotation: + {'createdBy': 'Darusik', + 'priority': CustomAnnotation.Priority.LOW, + 'changedBy': ['Darjus', 'Darjunia']}, + Deprecated:None} -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Sat Dec 22 20:52:05 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Sat, 22 Dec 2012 20:52:05 +0100 (CET) Subject: [Jython-checkins] =?utf-8?q?jython=3A_Fix_the_fact_that_superclas?= =?utf-8?q?s_can_be_null_in_makeProxy?= Message-ID: <3YTHR91LxmzQsY@mail.python.org> http://hg.python.org/jython/rev/4fea52cd30e2 changeset: 6907:4fea52cd30e2 user: Darjus Loktevic date: Mon Oct 15 22:59:47 2012 -0700 summary: Fix the fact that superclass can be null in makeProxy files: src/org/python/core/MakeProxies.java | 10 ++++++---- 1 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/org/python/core/MakeProxies.java b/src/org/python/core/MakeProxies.java --- a/src/org/python/core/MakeProxies.java +++ b/src/org/python/core/MakeProxies.java @@ -54,10 +54,12 @@ JavaMaker javaMaker = null; // check if a Jython annotation exists and if yes, the class is already a Jython Proxy - APIVersion apiVersion = superclass.getAnnotation(org.python.compiler.APIVersion.class); - if (apiVersion != null) { - // TODO validate versions, maybe use a different annotation - return superclass; + if (superclass != null) { + APIVersion apiVersion = superclass.getAnnotation(org.python.compiler.APIVersion.class); + if (apiVersion != null) { + // TODO validate versions, maybe use a different annotation + return superclass; + } } Class[] interfaces = vinterfaces.toArray(new Class[vinterfaces.size()]); -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Sat Dec 22 20:52:06 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Sat, 22 Dec 2012 20:52:06 +0100 (CET) Subject: [Jython-checkins] =?utf-8?q?jython=3A_Make_sure_ProxyMaker_genera?= =?utf-8?q?tes_only_one_method_per_method+signature_combination=2E?= Message-ID: <3YTHRB3XwGzNpw@mail.python.org> http://hg.python.org/jython/rev/4cea1e0ecd1f changeset: 6908:4cea1e0ecd1f user: Darjus Loktevic date: Wed Oct 24 00:25:25 2012 -0700 summary: Make sure ProxyMaker generates only one method per method+signature combination. The new ProxyMaker allows generating methods with the same name but multiple signatures so the old method of tracking just the name was not sufficient and i actually omitted it in the original request. files: src/org/python/compiler/ProxyMaker.java | 9 ++++++++- 1 files changed, 8 insertions(+), 1 deletions(-) diff --git a/src/org/python/compiler/ProxyMaker.java b/src/org/python/compiler/ProxyMaker.java --- a/src/org/python/compiler/ProxyMaker.java +++ b/src/org/python/compiler/ProxyMaker.java @@ -19,6 +19,7 @@ protected final Class[] interfaces; Set names; Set supernames = Generic.set(); + Set namesAndSigs; // name+signature pairs public ClassFile classfile; /** The name of the class to build. */ public String myClass; @@ -332,7 +333,7 @@ AnnotationDescr[] methodAnnotations, AnnotationDescr[][]parameterAnnotations) throws Exception { boolean isAbstract = false; - + if (Modifier.isAbstract(access)) { access = access & ~Modifier.ABSTRACT; isAbstract = true; @@ -342,6 +343,11 @@ String[] exceptionTypes = mapExceptions(exceptions); names.add(name); + + // make sure we have only one name + signature pair available per method + if (!namesAndSigs.add(name + sig)) { + return; + } Code code = null; if (methodAnnotations != null && parameterAnnotations != null) { @@ -608,6 +614,7 @@ public void build() throws Exception { names = Generic.set(); + namesAndSigs = Generic.set(); int access = superclass.getModifiers(); if ((access & Modifier.FINAL) != 0) { throw new InstantiationException("can't subclass final class"); -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Sat Dec 22 20:52:07 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Sat, 22 Dec 2012 20:52:07 +0100 (CET) Subject: [Jython-checkins] =?utf-8?q?jython=3A_Remove_the_Proxy_caching=2C?= =?utf-8?q?_seems_to_be_interfering_with_ProxyMaker_classes_that?= Message-ID: <3YTHRC5zXCzNqZ@mail.python.org> http://hg.python.org/jython/rev/3121e1a9b99d changeset: 6909:3121e1a9b99d user: Darjus Loktevic date: Sun Nov 18 17:15:16 2012 -0800 summary: Remove the Proxy caching, seems to be interfering with ProxyMaker classes that are themselves subclasses of ProxyMaker classes files: src/org/python/core/MakeProxies.java | 9 --------- 1 files changed, 0 insertions(+), 9 deletions(-) diff --git a/src/org/python/core/MakeProxies.java b/src/org/python/core/MakeProxies.java --- a/src/org/python/core/MakeProxies.java +++ b/src/org/python/core/MakeProxies.java @@ -53,15 +53,6 @@ PyObject dict) { JavaMaker javaMaker = null; - // check if a Jython annotation exists and if yes, the class is already a Jython Proxy - if (superclass != null) { - APIVersion apiVersion = superclass.getAnnotation(org.python.compiler.APIVersion.class); - if (apiVersion != null) { - // TODO validate versions, maybe use a different annotation - return superclass; - } - } - Class[] interfaces = vinterfaces.toArray(new Class[vinterfaces.size()]); String fullProxyName = proxyPrefix + proxyName + "$" + proxyNumber++; String pythonModuleName; -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Sat Dec 22 20:52:09 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Sat, 22 Dec 2012 20:52:09 +0100 (CET) Subject: [Jython-checkins] =?utf-8?q?jython=3A_Move_the_method+signature_c?= =?utf-8?q?hecking_into_visitMethods=2E?= Message-ID: <3YTHRF1346zNpw@mail.python.org> http://hg.python.org/jython/rev/a310960970b3 changeset: 6910:a310960970b3 user: Darjus Loktevic date: Sun Nov 18 17:43:51 2012 -0800 summary: Move the method+signature checking into visitMethods. files: src/org/python/compiler/ProxyMaker.java | 50 ++++++------ 1 files changed, 26 insertions(+), 24 deletions(-) diff --git a/src/org/python/compiler/ProxyMaker.java b/src/org/python/compiler/ProxyMaker.java --- a/src/org/python/compiler/ProxyMaker.java +++ b/src/org/python/compiler/ProxyMaker.java @@ -343,11 +343,6 @@ String[] exceptionTypes = mapExceptions(exceptions); names.add(name); - - // make sure we have only one name + signature pair available per method - if (!namesAndSigs.add(name + sig)) { - return; - } Code code = null; if (methodAnnotations != null && parameterAnnotations != null) { @@ -638,27 +633,34 @@ */ protected void visitMethods(Class klass) throws Exception { for (Method method : klass.getDeclaredMethods()) { - int access = method.getModifiers(); - if (Modifier.isStatic(access) || Modifier.isPrivate(access)) { - continue; - } + + + // make sure we have only one name + signature pair available per method + if (!namesAndSigs.add(methodString(method))) { + continue; + } - if (Modifier.isNative(access)) { - access = access & ~Modifier.NATIVE; - } + int access = method.getModifiers(); + if (Modifier.isStatic(access) || Modifier.isPrivate(access)) { + continue; + } - if (Modifier.isProtected(access)) { - access = (access & ~Modifier.PROTECTED) | Modifier.PUBLIC; - if (Modifier.isFinal(access)) { - addSuperMethod(method, access); - continue; - } - } else if (Modifier.isFinal(access)) { - continue; - } else if (!Modifier.isPublic(access)) { - continue; // package protected by process of elimination; we can't override - } - addMethod(method, access); + if (Modifier.isNative(access)) { + access = access & ~Modifier.NATIVE; + } + + if (Modifier.isProtected(access)) { + access = (access & ~Modifier.PROTECTED) | Modifier.PUBLIC; + if (Modifier.isFinal(access)) { + addSuperMethod(method, access); + continue; + } + } else if (Modifier.isFinal(access)) { + continue; + } else if (!Modifier.isPublic(access)) { + continue; // package protected by process of elimination; we can't override + } + addMethod(method, access); } Class superClass = klass.getSuperclass(); -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Sat Dec 22 20:52:11 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Sat, 22 Dec 2012 20:52:11 +0100 (CET) Subject: [Jython-checkins] =?utf-8?q?jython_=28merge_default_-=3E_default?= =?utf-8?q?=29=3A_Initial_implementation_of_customizable_proxymaker_with_a?= =?utf-8?q?nnotation_support_and?= Message-ID: <3YTHRH13B1zNpw@mail.python.org> http://hg.python.org/jython/rev/f8224fee2886 changeset: 6911:f8224fee2886 parent: 6904:9968367fa217 parent: 6910:a310960970b3 user: Darjus Loktevic date: Sat Dec 22 11:51:49 2012 -0800 summary: Initial implementation of customizable proxymaker with annotation support and reference implementation in tests. files: src/org/python/compiler/ClassFile.java | 89 ++ src/org/python/compiler/Code.java | 2 +- src/org/python/compiler/JavaMaker.java | 7 +- src/org/python/compiler/ProxyCodeHelpers.java | 338 +++++++ src/org/python/compiler/ProxyMaker.java | 426 +++++----- src/org/python/core/MakeProxies.java | 45 +- src/org/python/util/ProxyCompiler.java | 32 + tests/java/org/python/compiler/custom_proxymaker/ClassAnnotationTest.java | 36 + tests/java/org/python/compiler/custom_proxymaker/ConstructorSignatureTest.java | 33 + tests/java/org/python/compiler/custom_proxymaker/CustomAnnotation.java | 30 + tests/java/org/python/compiler/custom_proxymaker/JUnitTest.java | 18 + tests/java/org/python/compiler/custom_proxymaker/MethodSignatureTest.java | 77 + tests/java/org/python/compiler/custom_proxymaker/MiniClampMaker.java | 182 ++++ tests/python/custom_proxymaker/annotated_class.py | 18 + tests/python/custom_proxymaker/clamp.py | 5 + tests/python/custom_proxymaker/constructor_signatures.py | 32 + tests/python/custom_proxymaker/junit_test.py | 28 + tests/python/custom_proxymaker/method_signatures.py | 41 + 18 files changed, 1207 insertions(+), 232 deletions(-) diff --git a/src/org/python/compiler/ClassFile.java b/src/org/python/compiler/ClassFile.java --- a/src/org/python/compiler/ClassFile.java +++ b/src/org/python/compiler/ClassFile.java @@ -7,14 +7,18 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; import org.python.core.imp; +import org.python.compiler.ProxyCodeHelpers.AnnotationDescr; public class ClassFile { @@ -27,6 +31,7 @@ String[] interfaces; List methodVisitors; List fieldVisitors; + List annotationVisitors; public static String fixName(String n) { if (n.indexOf('.') == -1) @@ -37,6 +42,34 @@ } return new String(c); } + + + public static void visitAnnotations(AnnotationVisitor av, Map fields) { + for (Entryfield: fields.entrySet()) { + visitAnnotation(av, field.getKey(), field.getValue()); + } + } + + // See org.objectweb.asm.AnnotationVisitor for details + // TODO Support annotation annotations and annotation array annotations + public static void visitAnnotation(AnnotationVisitor av, String fieldName, Object fieldValue) { + Class fieldValueClass = fieldValue.getClass(); + + if (fieldValue instanceof Class) { + av.visit(fieldName, Type.getType((Class)fieldValue)); + } else if (fieldValueClass.isEnum()) { + av.visitEnum(fieldName, ProxyCodeHelpers.mapType(fieldValueClass), fieldValue.toString()); + } else if (fieldValue instanceof List) { + AnnotationVisitor arrayVisitor = av.visitArray(fieldName); + List fieldList = (List)fieldValue; + for (Object arrayField: fieldList) { + visitAnnotation(arrayVisitor, null, arrayField); + } + arrayVisitor.visitEnd(); + } else { + av.visit(fieldName, fieldValue); + } + } public ClassFile(String name) { this(name, "java/lang/Object", Opcodes.ACC_SYNCHRONIZED | Opcodes.ACC_PUBLIC, @@ -56,6 +89,7 @@ cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); methodVisitors = Collections.synchronizedList(new ArrayList()); fieldVisitors = Collections.synchronizedList(new ArrayList()); + annotationVisitors = Collections.synchronizedList(new ArrayList()); } public void setSource(String name) { @@ -77,7 +111,54 @@ methodVisitors.add(pmv); return pmv; } + public Code addMethod(String name, String type, int access, String[] exceptions) + throws IOException + { + MethodVisitor mv = cw.visitMethod(access, name, type, null, exceptions); + Code pmv = new Code(mv, type, access); + methodVisitors.add(pmv); + return pmv; + } + + public Code addMethod(String name, String type, int access, String[] exceptions, + AnnotationDescr[]methodAnnotationDescrs, AnnotationDescr[][] parameterAnnotationDescrs) + throws IOException + { + MethodVisitor mv = cw.visitMethod(access, name, type, null, exceptions); + // method annotations + for (AnnotationDescr ad: methodAnnotationDescrs) { + AnnotationVisitor av = mv.visitAnnotation(ad.getName(), true); + if (ad.hasFields()) { + visitAnnotations(av, ad.getFields()); + } + av.visitEnd(); + } + + // parameter annotations + for (int i = 0; i < parameterAnnotationDescrs.length; i++) { + for (AnnotationDescr ad: parameterAnnotationDescrs[i]) { + AnnotationVisitor av = mv.visitParameterAnnotation(i, ad.getName(), true); + if (ad.hasFields()) { + visitAnnotations(av, ad.getFields()); + } + av.visitEnd(); + } + } + + Code pmv = new Code(mv, type, access); + methodVisitors.add(pmv); + return pmv; + } + + public void addClassAnnotation(AnnotationDescr annotationDescr) { + AnnotationVisitor av = cw.visitAnnotation(annotationDescr.getName(), true); + if (annotationDescr.hasFields()) { + visitAnnotations(av, annotationDescr.getFields()); + } + annotationVisitors.add(av); + } + public void addField(String name, String type, int access) throws IOException { @@ -103,6 +184,12 @@ } } + public void endClassAnnotations() { + for (AnnotationVisitor av: annotationVisitors) { + av.visitEnd(); + } + } + public void write(OutputStream stream) throws IOException { cw.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, this.name, null, this.superclass, interfaces); AnnotationVisitor av = cw.visitAnnotation("Lorg/python/compiler/APIVersion;", true); @@ -118,6 +205,7 @@ if (sfilename != null) { cw.visitSource(sfilename, null); } + endClassAnnotations(); endFields(); endMethods(); @@ -129,4 +217,5 @@ //debug(baos); baos.close(); } + } diff --git a/src/org/python/compiler/Code.java b/src/org/python/compiler/Code.java --- a/src/org/python/compiler/Code.java +++ b/src/org/python/compiler/Code.java @@ -10,7 +10,7 @@ import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; -class Code extends MethodVisitor implements Opcodes { +public class Code extends MethodVisitor implements Opcodes { MethodVisitor mv; String sig; String locals[]; diff --git a/src/org/python/compiler/JavaMaker.java b/src/org/python/compiler/JavaMaker.java --- a/src/org/python/compiler/JavaMaker.java +++ b/src/org/python/compiler/JavaMaker.java @@ -32,11 +32,8 @@ int access) throws Exception { /* Need a fancy constructor for the Java side of things */ Code code = classfile.addMethod("", sig, access); - callSuper(code, "", name, parameters, null, sig); - code.visitVarInsn(ALOAD, 0); - getArgs(code, parameters); - code.visitMethodInsn(INVOKEVIRTUAL, classfile.name, "__initProxy__", makeSig("V", $objArr)); - code.visitInsn(RETURN); + callSuper(code, "", name, parameters, Void.TYPE, false); + callInitProxy(parameters, code); } @Override diff --git a/src/org/python/compiler/ProxyCodeHelpers.java b/src/org/python/compiler/ProxyCodeHelpers.java new file mode 100644 --- /dev/null +++ b/src/org/python/compiler/ProxyCodeHelpers.java @@ -0,0 +1,338 @@ +package org.python.compiler; + + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.Map; +import java.util.Map.Entry; + +import org.objectweb.asm.Type; +import org.python.core.Py; +import org.python.core.PyMethod; +import org.python.core.PyObject; +import org.python.core.PyProxy; +import org.python.core.PyReflectedFunction; +import org.python.util.Generic; + +/* + * Various constants and methods for generating Proxy code + */ +public class ProxyCodeHelpers { + public static final int tBoolean=0; + public static final int tByte=1; + public static final int tShort=2; + public static final int tInteger=3; + public static final int tLong=4; + public static final int tFloat=5; + public static final int tDouble=6; + public static final int tCharacter=7; + public static final int tVoid=8; + public static final int tOther=9; + public static final int tNone=10; + + public static Map, Integer> types = fillTypes(); + + public static Map, Integer> fillTypes() { + Map, Integer> typeMap = Generic.map(); + typeMap.put(Boolean.TYPE, tBoolean); + typeMap.put(Byte.TYPE, tByte); + typeMap.put(Short.TYPE, tShort); + typeMap.put(Integer.TYPE, tInteger); + typeMap.put(Long.TYPE, tLong); + typeMap.put(Float.TYPE, tFloat); + typeMap.put(Double.TYPE, tDouble); + typeMap.put(Character.TYPE, tCharacter); + typeMap.put(Void.TYPE, tVoid); + return typeMap; + } + + public static int getType(Class c) { + if (c == null) { + return tNone; + } + Object i = types.get(c); + if (i == null) { + return tOther; + } else { + return ((Integer)i); + } + } + + /** + * Retrieves name from the PyObject in proxy if it's defined in + * Python. This is a specialized helper function for internal PyProxy use. + */ + public static PyObject findPython(PyProxy proxy, String name) { + PyObject o = proxy._getPyInstance(); + if (o == null) { + proxy.__initProxy__(new Object[0]); + o = proxy._getPyInstance(); + } + PyObject ret = o.__findattr__(name); + if (ret instanceof PyMethod) { + PyMethod meth = ((PyMethod)ret); + if (meth.__func__ instanceof PyReflectedFunction) { + PyReflectedFunction func = (PyReflectedFunction)meth.__func__; + if (func.nargs > 0 && proxy.getClass() == func.argslist[0].declaringClass) { + // This function is the default return for the proxy type if the Python instance + // hasn't returned something of its own from __findattr__, so do the standard + // Java call on this + return null; + } + } + } + Py.setSystemState(proxy._getPySystemState()); + return ret; + } + + public static String mapClass(Class c) { + String name = c.getName(); + int index = name.indexOf("."); + if (index == -1) { + return name; + } + StringBuffer buf = new StringBuffer(name.length()); + int last_index = 0; + while (index != -1) { + buf.append(name.substring(last_index, index)); + buf.append("/"); + last_index = index+1; + index = name.indexOf(".", last_index); + } + buf.append(name.substring(last_index, name.length())); + return buf.toString(); + } + + public static String mapType(Class type) { + if (type.isArray()) + return "["+mapType(type.getComponentType()); + + switch (getType(type)) { + case tByte: return "B"; + case tCharacter: return "C"; + case tDouble: return "D"; + case tFloat: return "F"; + case tInteger: return "I"; + case tLong: return "J"; + case tShort: return "S"; + case tBoolean: return "Z"; + case tVoid: return "V"; + default: + return "L"+mapClass(type)+";"; + } + } + + public static String makeSig(Class ret, Class... sig) { + String[] mapped = new String[sig.length]; + for (int i = 0; i < mapped.length; i++) { + mapped[i] = mapType(sig[i]); + } + return makeSig(mapType(ret), mapped); + } + + public static String makeSig(String returnType, String... parameterTypes) { + StringBuilder buf = new StringBuilder("("); + for (String param : parameterTypes) { + buf.append(param); + } + return buf.append(')').append(returnType).toString(); + } + + public static void doReturn(Code code, Class type) throws Exception { + switch (getType(type)) { + case tNone: + break; + case tCharacter: + case tBoolean: + case tByte: + case tShort: + case tInteger: + code.ireturn(); + break; + case tLong: + code.lreturn(); + break; + case tFloat: + code.freturn(); + break; + case tDouble: + code.dreturn(); + break; + case tVoid: + code.return_(); + break; + default: + code.areturn(); + break; + } + } + + public static void doNullReturn(Code code, Class type) throws Exception { + switch (getType(type)) { + case tNone: + break; + case tCharacter: + case tBoolean: + case tByte: + case tShort: + case tInteger: + code.iconst_0(); + code.ireturn(); + break; + case tLong: + code.lconst_0(); + code.lreturn(); + break; + case tFloat: + code.fconst_0(); + code.freturn(); + break; + case tDouble: + code.dconst_0(); + code.dreturn(); + break; + case tVoid: + code.return_(); + break; + default: + code.aconst_null(); + code.areturn(); + break; + } + } + + public static String[] mapClasses(Class[] classes) { + String[] mapped = new String[classes.length]; + for (int i = 0; i < mapped.length; i++) { + mapped[i] = mapClass(classes[i]); + } + return mapped; + } + + public static String[] mapExceptions(Class[] classes) { + String[] exceptionTypes = new String[classes.length]; + for (int i = 0; i < classes.length; i++) { + // Exceptions are represented by their internal names + exceptionTypes[i] = Type.getType(classes[i]).getInternalName(); + } + return exceptionTypes; + } + + public static class MethodDescr { + + public final Class returnType; + + public final String name; + + public final Class[] parameters; + public final Class[] exceptions; + public final Map methodAnnotations; + public final Map[] parameterAnnotations; + + public MethodDescr(Method m) { + this(m.getName(), m.getReturnType(), m.getParameterTypes(), m.getExceptionTypes(), null, null); + } + + public MethodDescr(String name, + Class returnType, + Class[] parameters, + Class[] exceptions) { + this.name = name; + this.returnType = returnType; + this.parameters = parameters; + this.exceptions = exceptions; + this.methodAnnotations = null; + this.parameterAnnotations = null; + } + + public MethodDescr(String name, + Class returnType, + Class[] parameters, + Class[] exceptions, + Map methodAnnotations, + Map[] parameterAnnotations) { + this.name = name; + this.returnType = returnType; + this.parameters = parameters; + this.exceptions = exceptions; + this.methodAnnotations = methodAnnotations; + this.parameterAnnotations = parameterAnnotations; + } + + @Override + public int hashCode() { + return name.hashCode() + parameters.length; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof MethodDescr)) { + return false; + } + MethodDescr oDescr = (MethodDescr)obj; + if (!name.equals(oDescr.name) || parameters.length != oDescr.parameters.length) { + return false; + } + for (int i = 0; i < parameters.length; i++) { + if (!parameters[i].equals(oDescr.parameters[i])) { + return false; + } + } + return true; + } + } + + public static class ConstructorDescr extends MethodDescr { + + public ConstructorDescr(Constructor cons) { + this(cons.getParameterTypes(), cons.getExceptionTypes()); + } + + public ConstructorDescr(Class[] parameters, Class[] exceptions) { + super("", Void.TYPE, parameters, exceptions); + } + } + + public static class AnnotationDescr { + public final Class annotation; + public final Map fields; + + public AnnotationDescr(Classannotation) { + this.annotation = annotation; + this.fields = null; + } + + public AnnotationDescr(Classannotation, Map fields) { + this.annotation = annotation; + this.fields = fields; + } + + public boolean hasFields() { + if (fields == null) { + return false; + } + return true; + } + + public String getName() { + return mapType(annotation); + } + + public Map getFields() { + return fields; + } + + @Override + public int hashCode() { + if (hasFields()) { + int hash = annotation.hashCode(); + for (Entry field: fields.entrySet()) { + hash += field.getKey().hashCode() + field.getValue().hashCode(); + } + return hash; + } else { + return annotation.hashCode(); + } + } + } +} diff --git a/src/org/python/compiler/ProxyMaker.java b/src/org/python/compiler/ProxyMaker.java --- a/src/org/python/compiler/ProxyMaker.java +++ b/src/org/python/compiler/ProxyMaker.java @@ -5,91 +5,21 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.util.Map; import java.util.Set; import org.objectweb.asm.Label; import org.objectweb.asm.Opcodes; import org.python.core.Py; -import org.python.core.PyMethod; -import org.python.core.PyObject; -import org.python.core.PyProxy; -import org.python.core.PyReflectedFunction; import org.python.util.Generic; -public class ProxyMaker implements ClassConstants, Opcodes + +public class ProxyMaker extends ProxyCodeHelpers implements ClassConstants, Opcodes { - public static final int tBoolean=0; - public static final int tByte=1; - public static final int tShort=2; - public static final int tInteger=3; - public static final int tLong=4; - public static final int tFloat=5; - public static final int tDouble=6; - public static final int tCharacter=7; - public static final int tVoid=8; - public static final int tOther=9; - public static final int tNone=10; - - public static Map, Integer> types = fillTypes(); - - public static Map, Integer> fillTypes() { - Map, Integer> typeMap = Generic.map(); - typeMap.put(Boolean.TYPE, tBoolean); - typeMap.put(Byte.TYPE, tByte); - typeMap.put(Short.TYPE, tShort); - typeMap.put(Integer.TYPE, tInteger); - typeMap.put(Long.TYPE, tLong); - typeMap.put(Float.TYPE, tFloat); - typeMap.put(Double.TYPE, tDouble); - typeMap.put(Character.TYPE, tCharacter); - typeMap.put(Void.TYPE, tVoid); - return typeMap; - } - - public static int getType(Class c) { - if (c == null) { - return tNone; - } - Object i = types.get(c); - if (i == null) { - return tOther; - } else { - return ((Integer)i); - } - } - - /** - * Retrieves name from the PyObject in proxy if it's defined in - * Python. This is a specialized helper function for internal PyProxy use. - */ - public static PyObject findPython(PyProxy proxy, String name) { - PyObject o = proxy._getPyInstance(); - if (o == null) { - proxy.__initProxy__(new Object[0]); - o = proxy._getPyInstance(); - } - PyObject ret = o.__findattr__(name); - if (ret instanceof PyMethod) { - PyMethod meth = ((PyMethod)ret); - if (meth.__func__ instanceof PyReflectedFunction) { - PyReflectedFunction func = (PyReflectedFunction)meth.__func__; - if (func.nargs > 0 && proxy.getClass() == func.argslist[0].declaringClass) { - // This function is the default return for the proxy type if the Python instance - // hasn't returned something of its own from __findattr__, so do the standard - // Java call on this - return null; - } - } - } - Py.setSystemState(proxy._getPySystemState()); - return ret; - } - - Class superclass; - Class[] interfaces; + protected final Class superclass; + protected final Class[] interfaces; Set names; Set supernames = Generic.set(); + Set namesAndSigs; // name+signature pairs public ClassFile classfile; /** The name of the class to build. */ public String myClass; @@ -137,134 +67,17 @@ this.interfaces = interfaces; } - public static String mapClass(Class c) { - String name = c.getName(); - int index = name.indexOf("."); - if (index == -1) { - return name; - } - StringBuffer buf = new StringBuffer(name.length()); - int last_index = 0; - while (index != -1) { - buf.append(name.substring(last_index, index)); - buf.append("/"); - last_index = index+1; - index = name.indexOf(".", last_index); - } - buf.append(name.substring(last_index, name.length())); - return buf.toString(); - } - - public static String mapType(Class type) { - if (type.isArray()) - return "["+mapType(type.getComponentType()); - - switch (getType(type)) { - case tByte: return "B"; - case tCharacter: return "C"; - case tDouble: return "D"; - case tFloat: return "F"; - case tInteger: return "I"; - case tLong: return "J"; - case tShort: return "S"; - case tBoolean: return "Z"; - case tVoid: return "V"; - default: - return "L"+mapClass(type)+";"; - } - } - - public static String makeSig(Class ret, Class... sig) { - String[] mapped = new String[sig.length]; - for (int i = 0; i < mapped.length; i++) { - mapped[i] = mapType(sig[i]); - } - return makeSig(mapType(ret), mapped); - } - - public static String makeSig(String returnType, String... parameterTypes) { - StringBuilder buf = new StringBuilder("("); - for (String param : parameterTypes) { - buf.append(param); - } - return buf.append(')').append(returnType).toString(); - } - - public void doConstants() throws Exception { Code code = classfile.addMethod("", makeSig("V"), Modifier.STATIC); code.return_(); } - public static void doReturn(Code code, Class type) throws Exception { - switch (getType(type)) { - case tNone: - break; - case tCharacter: - case tBoolean: - case tByte: - case tShort: - case tInteger: - code.ireturn(); - break; - case tLong: - code.lreturn(); - break; - case tFloat: - code.freturn(); - break; - case tDouble: - code.dreturn(); - break; - case tVoid: - code.return_(); - break; - default: - code.areturn(); - break; - } - } - - public static void doNullReturn(Code code, Class type) throws Exception { - switch (getType(type)) { - case tNone: - break; - case tCharacter: - case tBoolean: - case tByte: - case tShort: - case tInteger: - code.iconst_0(); - code.ireturn(); - break; - case tLong: - code.lconst_0(); - code.lreturn(); - break; - case tFloat: - code.fconst_0(); - code.freturn(); - break; - case tDouble: - code.dconst_0(); - code.dreturn(); - break; - case tVoid: - code.return_(); - break; - default: - code.aconst_null(); - code.areturn(); - break; - } - } - public void callSuper(Code code, String name, String superclass, Class[] parameters, Class ret, - String sig) throws Exception { + boolean doReturn) throws Exception { code.aload(0); int local_index; @@ -297,9 +110,11 @@ break; } } - code.invokespecial(superclass, name, sig); + code.invokespecial(superclass, name, makeSig(ret, parameters)); - doReturn(code, ret); + if (doReturn) { + doReturn(code, ret); + } } public void doJavaCall(Code code, String name, String type, @@ -471,24 +286,73 @@ public void addMethod(Method method, int access) throws Exception { + addMethod(method.getName(), method.getReturnType(), method.getParameterTypes(), + method.getExceptionTypes(), access, method.getDeclaringClass()); + } + + /** + * Adds a method of the given name to the class being implemented. If + * declaringClass is null, the generated method will expect to find an object of + * the method's name in the Python object and call it. If it isn't null, if an object is found + * in the Python object, it'll be called. Otherwise the superclass will be called. No checking + * is done to guarantee that the superclass has a method with the same signature. + */ + public void addMethod(String name, + Class ret, + Class[] parameters, + Class[] exceptions, + int access, + Class declaringClass) throws Exception { + addMethod(name, name, ret, parameters, exceptions, access, declaringClass, null, null); + } + + + /** + * Generates and adds a proxy method to the proxy class + * + * @param name: name of the java method + * @param pyName: name of the python method to which the java method + * proxies (useful for clamped objects) + * + * @param ret: return type + * @param parameters: parameter types + * @param exceptions: throwable exception types + * @param access + * @param declaringClass + * @param methodAnnotations: method annotations + * @param parameterAnnotations: parameter annotations + * @throws Exception + */ + public void addMethod(String name, + String pyName, + Class ret, + Class[] parameters, + Class[] exceptions, + int access, + Class declaringClass, + AnnotationDescr[] methodAnnotations, + AnnotationDescr[][]parameterAnnotations) throws Exception { boolean isAbstract = false; - + if (Modifier.isAbstract(access)) { access = access & ~Modifier.ABSTRACT; isAbstract = true; } - Class[] parameters = method.getParameterTypes(); - Class ret = method.getReturnType(); String sig = makeSig(ret, parameters); + String[] exceptionTypes = mapExceptions(exceptions); - String name = method.getName(); names.add(name); - Code code = classfile.addMethod(name, sig, access); + Code code = null; + if (methodAnnotations != null && parameterAnnotations != null) { + code = classfile.addMethod(name, sig, access, exceptionTypes, methodAnnotations, parameterAnnotations); + } else { + code = classfile.addMethod(name, sig, access, exceptionTypes); + } code.aload(0); - code.ldc(name); + code.ldc(pyName); if (!isAbstract) { int tmp = code.getLocal("org/python/core/PyObject"); @@ -500,12 +364,12 @@ Label callPython = new Label(); code.ifnonnull(callPython); - String superClass = mapClass(method.getDeclaringClass()); + String superClass = mapClass(declaringClass); - callSuper(code, name, superClass, parameters, ret, sig); + callSuper(code, name, superClass, parameters, ret, true); code.label(callPython); code.aload(tmp); - callMethod(code, name, parameters, ret, method.getExceptionTypes()); + callMethod(code, name, parameters, ret, exceptions); addSuperMethod("super__"+name, name, superClass, parameters, ret, sig, access); @@ -515,13 +379,34 @@ code.dup(); Label returnNull = new Label(); code.ifnull(returnNull); - callMethod(code, name, parameters, ret, method.getExceptionTypes()); + callMethod(code, name, parameters, ret, exceptions); code.label(returnNull); code.pop(); doNullReturn(code, ret); } } + + /** + * A constructor that is also a method (!) + */ + public void addConstructorMethodCode(String pyName, + Class[] parameters, + Class[] exceptions, + int access, + Class declaringClass, + Code code) throws Exception { + code.aload(0); + code.ldc(pyName); + + int tmp = code.getLocal("org/python/core/PyObject"); + code.invokestatic("org/python/compiler/ProxyMaker", "findPython", + makeSig($pyObj, $pyProxy, $str)); + code.astore(tmp); + code.aload(tmp); + callMethod(code, "", parameters, Void.TYPE, exceptions); + } + private String methodString(Method m) { StringBuffer buf = new StringBuffer(m.getName()); buf.append(":"); @@ -579,7 +464,7 @@ String sig, int access) throws Exception { Code code = classfile.addMethod("", sig, access); - callSuper(code, "", name, parameters, Void.TYPE, sig); + callSuper(code, "", name, parameters, Void.TYPE, true); } public void addConstructors(Class c) throws Exception { @@ -600,6 +485,10 @@ addConstructor(name, parameters, Void.TYPE, makeSig(Void.TYPE, parameters), access); } } + + protected void addClassAnnotation(AnnotationDescr annotation) { + classfile.addClassAnnotation(annotation); + } // Super methods are added for the following three reasons: // @@ -654,7 +543,7 @@ } supernames.add(methodName); Code code = classfile.addMethod(methodName, sig, access); - callSuper(code, superName, declClass, parameters, ret, sig); + callSuper(code, superName, declClass, parameters, ret, true); } public void addProxy() throws Exception { @@ -720,6 +609,7 @@ public void build() throws Exception { names = Generic.set(); + namesAndSigs = Generic.set(); int access = superclass.getModifiers(); if ((access & Modifier.FINAL) != 0) { throw new InstantiationException("can't subclass final class"); @@ -728,20 +618,130 @@ classfile = new ClassFile(myClass, mapClass(superclass), access); addProxy(); - addConstructors(superclass); + visitConstructors(); classfile.addInterface("org/python/core/PyProxy"); + + visitClassAnnotations(); + visitMethods(); + doConstants(); + addClassDictInit(); + } + + /** + * Visits all methods declared on the given class and classes in its inheritance hierarchy. + * Methods visible to subclasses are added to seen. + */ + protected void visitMethods(Class klass) throws Exception { + for (Method method : klass.getDeclaredMethods()) { + + + // make sure we have only one name + signature pair available per method + if (!namesAndSigs.add(methodString(method))) { + continue; + } - Set seenmethods = Generic.set(); - addMethods(superclass, seenmethods); + int access = method.getModifiers(); + if (Modifier.isStatic(access) || Modifier.isPrivate(access)) { + continue; + } + + if (Modifier.isNative(access)) { + access = access & ~Modifier.NATIVE; + } + + if (Modifier.isProtected(access)) { + access = (access & ~Modifier.PROTECTED) | Modifier.PUBLIC; + if (Modifier.isFinal(access)) { + addSuperMethod(method, access); + continue; + } + } else if (Modifier.isFinal(access)) { + continue; + } else if (!Modifier.isPublic(access)) { + continue; // package protected by process of elimination; we can't override + } + addMethod(method, access); + } + + Class superClass = klass.getSuperclass(); + if (superClass != null) { + visitMethods(superClass); + } + + for (Class iface : klass.getInterfaces()) { + visitMethods(iface); + } + } + + /** + * Called for every method on the proxy's superclass and interfaces that can be overriden by the + * proxy class. If the proxy wants to perform Python lookup and calling for the method, + * {@link #addMethod(Method)} should be called. For abstract methods, addMethod must be called. + */ + protected void visitMethod(Method method) throws Exception { + addMethod(method, method.getModifiers()); + } + + protected void visitMethods() throws Exception { + visitMethods(superclass); for (Class iface : interfaces) { if (iface.isAssignableFrom(superclass)) { Py.writeWarning("compiler", "discarding redundant interface: " + iface.getName()); continue; } classfile.addInterface(mapClass(iface)); - addMethods(iface, seenmethods); + visitMethods(iface); } - doConstants(); - addClassDictInit(); } + + /** Adds a constructor that calls through to superclass. */ + protected void addConstructor(Class[] parameters, int access) throws Exception { + String sig = makeSig(Void.TYPE, parameters); + Code code = classfile.addMethod("", sig, access); + callSuper(code, "", mapClass(superclass), parameters, Void.TYPE, true); + } + + /** + * Called for every constructor on the proxy's superclass that can be overridden by + * the proxy class. + */ + protected void visitConstructor(Constructor constructor) throws Exception { + /* Need a fancy constructor for the Java side of things */ + callInitProxy(constructor.getParameterTypes(), addOpenConstructor(constructor)); + } + + /** + * Adds a constructor that calls through to the superclass constructor with the same signature + * and leaves the returned Code open for more operations. The caller of this method must add a + * return to the Code. + */ + protected Code addOpenConstructor(Constructor constructor) throws Exception { + String sig = makeSig(Void.TYPE, constructor.getParameterTypes()); + Code code = classfile.addMethod("", sig, constructor.getModifiers()); + callSuper(code, "", mapClass(superclass), constructor.getParameterTypes(), Void.TYPE, true); + return code; + } + + /** + * Calls __initProxy__ on this class with the given types of parameters, which must be + * available as arguments to the currently called method in the order of the parameters. + */ + protected void callInitProxy(Class[] parameters, Code code) throws Exception { + code.visitVarInsn(ALOAD, 0); + getArgs(code, parameters); + code.visitMethodInsn(INVOKEVIRTUAL, classfile.name, "__initProxy__", makeSig("V", $objArr)); + code.visitInsn(RETURN); + } + + /** + * Visits constructors from this proxy's superclass. + */ + protected void visitConstructors() throws Exception { + addConstructors(superclass); + } + + protected void visitClassAnnotations() throws Exception { + // ProxyMaker itself does nothing with class annotations for now + } + } diff --git a/src/org/python/core/MakeProxies.java b/src/org/python/core/MakeProxies.java --- a/src/org/python/core/MakeProxies.java +++ b/src/org/python/core/MakeProxies.java @@ -6,6 +6,7 @@ import java.util.ArrayList; import java.util.List; +import org.python.compiler.APIVersion; import org.python.compiler.AdapterMaker; import org.python.compiler.JavaMaker; @@ -50,27 +51,45 @@ public static synchronized Class makeProxy(Class superclass, List> vinterfaces, String className, String proxyName, PyObject dict) { + JavaMaker javaMaker = null; + Class[] interfaces = vinterfaces.toArray(new Class[vinterfaces.size()]); String fullProxyName = proxyPrefix + proxyName + "$" + proxyNumber++; String pythonModuleName; - PyObject mn = dict.__finditem__("__module__"); - if (mn == null) { - pythonModuleName = "foo"; + PyObject module = dict.__finditem__("__module__"); + if (module == null) { + pythonModuleName = "foo"; // FIXME Really, module name foo? } else { - pythonModuleName = (String) mn.__tojava__(String.class); + pythonModuleName = (String) module.__tojava__(String.class); } - JavaMaker jm = new JavaMaker(superclass, - interfaces, - className, - pythonModuleName, - fullProxyName, - dict); + + // Grab the proxy maker from the class if it exists, and if it does, use the proxy class + // name from the maker + PyObject customProxyMaker = dict.__finditem__("__proxymaker__"); + if (customProxyMaker != null) { + if (module == null) { + throw Py.TypeError("Classes using __proxymaker__ must define __module__"); + } + PyObject[] args = Py.javas2pys(superclass, interfaces, className, pythonModuleName, fullProxyName, dict); + javaMaker = Py.tojava(customProxyMaker.__call__(args), JavaMaker.class); + // TODO Full proxy name + } + + if (javaMaker == null) { + javaMaker = new JavaMaker(superclass, + interfaces, + className, + pythonModuleName, + fullProxyName, + dict); + } + try { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - jm.build(bytes); - Py.saveClassFile(fullProxyName, bytes); + javaMaker.build(bytes); + Py.saveClassFile(javaMaker.myClass, bytes); - return makeClass(superclass, vinterfaces, jm.myClass, bytes); + return makeClass(superclass, vinterfaces, javaMaker.myClass, bytes); } catch (Exception exc) { throw Py.JavaError(exc); } diff --git a/src/org/python/util/ProxyCompiler.java b/src/org/python/util/ProxyCompiler.java new file mode 100644 --- /dev/null +++ b/src/org/python/util/ProxyCompiler.java @@ -0,0 +1,32 @@ +package org.python.util; + +import java.util.Properties; + +import org.python.core.PySystemState; + +public class ProxyCompiler { + /** + * Compiles the python file by loading it + * + * FIXME: this is quite hackish right now. It basically starts a whole interpreter + * and set's proxyDebugDirectory as the destination for the compiled proxy class + * + * @param filename: python filename to exec + * @param destDir: destination directory for the proxy classes + */ + public static void compile(String filename, String destDir) { + Properties props = new Properties(System.getProperties()); + props.setProperty(PySystemState.PYTHON_CACHEDIR_SKIP, "true"); + PySystemState.initialize(props, null); + PythonInterpreter interp = new PythonInterpreter(); + + String origProxyDir = org.python.core.Options.proxyDebugDirectory; + try { + org.python.core.Options.proxyDebugDirectory = destDir; + interp.execfile(filename); + } finally { + org.python.core.Options.proxyDebugDirectory = origProxyDir; + } + } + +} diff --git a/tests/java/org/python/compiler/custom_proxymaker/ClassAnnotationTest.java b/tests/java/org/python/compiler/custom_proxymaker/ClassAnnotationTest.java new file mode 100644 --- /dev/null +++ b/tests/java/org/python/compiler/custom_proxymaker/ClassAnnotationTest.java @@ -0,0 +1,36 @@ +package org.python.compiler.custom_proxymaker; + +/* + * Test support for Python class annotations + */ + +import static org.junit.Assert.*; + +import org.junit.*; +import org.python.util.ProxyCompiler; + +public class ClassAnnotationTest { + + Class proxy; + + @Before + public void setUp() throws Exception { + ProxyCompiler.compile("tests/python/custom_proxymaker/annotated_class.py", "build/classes"); + proxy = Class.forName("custom_proxymaker.tests.AnnotatedInputStream"); + } + + @Test + public void hasClassAnnotation() { + // Just by "finding" it we satisfy the test. + @SuppressWarnings("unused") + Deprecated deprecatedAnnotation = proxy.getAnnotation(Deprecated.class); + } + + @Test + public void hasCustomAnnotationWithFields() throws Exception { + CustomAnnotation customAnnotation = proxy.getAnnotation(CustomAnnotation.class); + assertEquals("Darusik", customAnnotation.createdBy()); + assertEquals(CustomAnnotation.Priority.LOW, customAnnotation.priority()); + assertArrayEquals(new String[] {"Darjus", "Darjunia"}, customAnnotation.changedBy()); + } +} diff --git a/tests/java/org/python/compiler/custom_proxymaker/ConstructorSignatureTest.java b/tests/java/org/python/compiler/custom_proxymaker/ConstructorSignatureTest.java new file mode 100644 --- /dev/null +++ b/tests/java/org/python/compiler/custom_proxymaker/ConstructorSignatureTest.java @@ -0,0 +1,33 @@ +package org.python.compiler.custom_proxymaker; + +/* + * Tests constructor signatures + */ + +import static org.junit.Assert.*; + +import java.lang.reflect.*; + +import org.junit.*; +import org.python.util.ProxyCompiler; + +import java.awt.Container; +import javax.swing.BoxLayout; + +public class ConstructorSignatureTest { + Class proxy; + + @Before + public void setUp() throws Exception { + ProxyCompiler.compile("tests/python/custom_proxymaker/constructor_signatures.py", "build/classes"); + proxy = Class.forName("custom_proxymaker.tests.ConstructorSignatures"); + } + + @Ignore // Constructor signatures are not working yet + @Test + @SuppressWarnings("unused") + public void returnsVoid() throws Exception { + Constructor constructor = proxy.getConstructor(new Class[] {Container.class, Integer.TYPE}); + constructor.newInstance(new Container(), BoxLayout.X_AXIS); + } +} diff --git a/tests/java/org/python/compiler/custom_proxymaker/CustomAnnotation.java b/tests/java/org/python/compiler/custom_proxymaker/CustomAnnotation.java new file mode 100644 --- /dev/null +++ b/tests/java/org/python/compiler/custom_proxymaker/CustomAnnotation.java @@ -0,0 +1,30 @@ +package org.python.compiler.custom_proxymaker; + +/** + * This Annotation contains most of the possible annotation fields, + * used to test the annotation part of custom proxymaker + */ + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + + + at Documented + at Retention(RetentionPolicy.RUNTIME) + at Target({ElementType.TYPE,ElementType.METHOD, + ElementType.CONSTRUCTOR,ElementType.ANNOTATION_TYPE, + ElementType.PACKAGE,ElementType.FIELD,ElementType.LOCAL_VARIABLE}) + at Inherited +public @interface CustomAnnotation { + public enum Priority { LOW, MEDIUM, HIGH } + String value(); + String[] changedBy() default ""; + Priority[] priorities(); + Priority priority() default Priority.MEDIUM; + String createdBy() default "Darjus Loktevic"; + String lastChanged() default "08/06/2012"; +} diff --git a/tests/java/org/python/compiler/custom_proxymaker/JUnitTest.java b/tests/java/org/python/compiler/custom_proxymaker/JUnitTest.java new file mode 100644 --- /dev/null +++ b/tests/java/org/python/compiler/custom_proxymaker/JUnitTest.java @@ -0,0 +1,18 @@ +package org.python.compiler.custom_proxymaker; + +/* + * This test tests that we can create JUnit 4 tests in Python and that JUnit's own + * reflection system picks our annotations and runs the underlying code + */ + +import org.junit.Test; +import org.junit.runner.JUnitCore; +import org.python.util.ProxyCompiler; + +public class JUnitTest { + @Test + public void testMethodSignatures() throws Exception { + ProxyCompiler.compile("tests/python/custom_proxymaker/junit_test.py", "build/classes"); + JUnitCore.runClasses(Class.forName("custom_proxymaker.tests.JUnitTest")); + } +} diff --git a/tests/java/org/python/compiler/custom_proxymaker/MethodSignatureTest.java b/tests/java/org/python/compiler/custom_proxymaker/MethodSignatureTest.java new file mode 100644 --- /dev/null +++ b/tests/java/org/python/compiler/custom_proxymaker/MethodSignatureTest.java @@ -0,0 +1,77 @@ +package org.python.compiler.custom_proxymaker; + +/* + * Tests support for various combinations of method signatures + */ + +import static org.junit.Assert.*; + +import java.lang.reflect.*; + +import org.junit.Before; +import org.junit.Test; +import org.python.util.ProxyCompiler; + +public class MethodSignatureTest { + Class proxy; + + @Before + public void setUp() throws Exception { + ProxyCompiler.compile("tests/python/custom_proxymaker/method_signatures.py", "build/classes"); + proxy = Class.forName("custom_proxymaker.tests.MethodSignatures"); + } + + @Test + public void methodThrows() throws Exception { + Method method = proxy.getMethod("throwsException"); + assertArrayEquals(new Class[] {RuntimeException.class}, method.getExceptionTypes()); + } + + @Test + public void returnsVoid() throws Exception { + Method method = proxy.getMethod("throwsException"); + assertEquals(Void.TYPE, method.getReturnType()); + } + + @Test + public void returnsLong() throws Exception { + Method method = proxy.getMethod("returnsLong"); + assertEquals(Long.TYPE, method.getReturnType()); + } + + @Test + public void returnsObject() throws Exception { + Method method = proxy.getMethod("returnsObject"); + assertEquals(Object.class, method.getReturnType()); + } + + @Test + public void returnsArray() throws Exception { + Method method = proxy.getMethod("returnsArray"); + Object compareType = Array.newInstance(Long.TYPE, 0); + assertEquals(compareType.getClass(), method.getReturnType()); + } + + @Test + public void returnsArrayObj() throws Exception { + Method method = proxy.getMethod("returnsArrayObj"); + Object compareType = Array.newInstance(Object.class, 0); + assertEquals(compareType.getClass(), method.getReturnType()); + } + + @Test + @SuppressWarnings("unused") + public void acceptsString() throws Exception { + Class[] partypes = new Class[] {String.class}; + Method method = proxy.getMethod("acceptsString", partypes); + } + + @Test + @SuppressWarnings("unused") + public void acceptsArray() throws Exception { + Object compareType = Array.newInstance(Long.TYPE, 0); + Class[] partypes = new Class[] {compareType.getClass()}; + Method method = proxy.getMethod("acceptsArray", partypes); + } + +} diff --git a/tests/java/org/python/compiler/custom_proxymaker/MiniClampMaker.java b/tests/java/org/python/compiler/custom_proxymaker/MiniClampMaker.java new file mode 100644 --- /dev/null +++ b/tests/java/org/python/compiler/custom_proxymaker/MiniClampMaker.java @@ -0,0 +1,182 @@ +package org.python.compiler.custom_proxymaker; + +/* + * This is a bare bones implementation of ClampMaker. It's goal is to be a "reference" + * implementation for the features that are provided by customizable ProxyMaker + */ + +import java.lang.reflect.Constructor; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; + +import org.python.compiler.Code; +import org.python.compiler.JavaMaker; +import org.python.core.Py; +import org.python.core.PyDictionary; +import org.python.core.PyObject; +import org.python.core.__builtin__; +import org.python.util.Generic; + +public class MiniClampMaker extends JavaMaker { + + private final Map methodsToAdd = Generic.map(); + private final Map constructorsToAdd = Generic.map(); + private AnnotationDescr[] classAnnotations = new AnnotationDescr[]{}; + + private static AnnotationDescr[] extractAnnotation(PyDictionary dict) { + List annotationDescrs = Generic.list(); + for (PyObject annotationIter: dict.iteritems().asIterable()) { + PyObject annotationClass = annotationIter.__getitem__(0); + PyObject annotationFields = annotationIter.__getitem__(1); + AnnotationDescr annotationDescr = null; + if (annotationFields == Py.None) { + annotationDescr = new AnnotationDescr(Py.tojava(annotationClass, Class.class)); + } else { + Map fields = Generic.map(); + for (PyObject item: ((PyDictionary)annotationFields).iteritems().asIterable()) { + fields.put(Py.tojava(item.__getitem__(0), String.class), Py.tojava(item.__getitem__(1), Object.class)); + } + annotationDescr = new AnnotationDescr(Py.tojava(annotationClass, Class.class), fields); + } + annotationDescrs.add(annotationDescr); + } + return (AnnotationDescr[]) annotationDescrs.toArray(new AnnotationDescr[annotationDescrs.size()]); + } + + public MiniClampMaker(Class superclass, + Class[] interfaces, + String pythonClass, + String pythonModule, + String myClass, + PyObject methods) { + super(superclass, interfaces, pythonClass, pythonModule, myClass, methods); + + // if we find __java_package__, override the default proxy naming with + // __java_package__ + .pythonClass + PyObject javaPackage = methods.__finditem__("__java_package__"); + if (javaPackage != null) { + String newMyClass = new String((String)javaPackage.__tojava__(String.class)); + newMyClass += "." + pythonClass; + this.myClass = newMyClass; + } + + + PyObject clampAttr = Py.newString("_clamp"); + for (PyObject pykey : methods.asIterable()) { + String key = Py.tojava(pykey, String.class); + PyObject value = methods.__finditem__(key); + PyObject clampObj = __builtin__.getattr(value, clampAttr, Py.None); + if (clampObj == Py.None) { + continue; + } + String name = (String)clampObj.__getattr__("name").__tojava__(String.class); + if (name.equals("__init__")) { + constructorsToAdd.put(key, clampObj); + } else { + methodsToAdd.put(key, clampObj); + } + } + PyObject pyAnnotations = methods.__finditem__("_clamp_class_annotations"); + if (pyAnnotations != null) { + classAnnotations = extractAnnotation((PyDictionary)pyAnnotations); + } + + } + + @Override + protected void visitClassAnnotations() throws Exception { + for (AnnotationDescr annotation: classAnnotations) { + addClassAnnotation(annotation); + } + } + + @Override + protected void visitConstructors() throws Exception { + + Set> superConstructors = Generic.set(); + for (Constructor constructor: superclass.getDeclaredConstructors()) { + superConstructors.add(constructor); + } + + for (Entry meth : constructorsToAdd.entrySet()) { + Constructor superToCall = null; + String pyName = meth.getKey(); + PyObject clampObj = meth.getValue(); + + Class[] thrownClasses = Py.tojava(clampObj.__getattr__("throws"), Class[].class); + Class[] parameterClasses = Py.tojava(clampObj.__getattr__("argtypes"), Class[].class); + + if (clampObj.__findattr__("super_constructor") != null) { + superToCall = (Constructor)clampObj.__getattr__("super_constructor").__tojava__(Constructor.class); + } else { // try to find a matching super constructor + try { + superToCall = superclass.getDeclaredConstructor(parameterClasses); + } catch (NoSuchMethodException err) { + // FIXME need a fancy constructor finder + superToCall = superConstructors.iterator().next(); + } + } + + for (Constructor constructor: superConstructors) { + if (Arrays.equals(constructor.getParameterTypes(), superToCall.getParameterTypes())) { + superConstructors.remove(constructor); + } + } + + AnnotationDescr[] methodAnnotations = extractAnnotation((PyDictionary)clampObj.__getattr__("method_annotations")); + PyObject[] parameterAnnotationObjs = (PyObject[])clampObj.__getattr__("parameter_annotations").__tojava__(PyObject[].class); + + AnnotationDescr[][]parameterAnnotations = new AnnotationDescr[parameterAnnotationObjs.length][]; + for (int i = 0; i", fullsig, Modifier.PUBLIC, mappedExceptions, methodAnnotations, parameterAnnotations); + callSuper(code, "", mapClass(superclass), superToCall.getParameterTypes(), Void.TYPE, false); + // instead of calling the proxy, we construct the full method code + + addConstructorMethodCode(pyName, superToCall.getParameterTypes(), thrownClasses, Modifier.PUBLIC, superclass, code); + + } + + // the non-overwritten constructors + for (Constructor constructor: superConstructors) { + addConstructor(constructor.getParameterTypes(), Modifier.PUBLIC); + } + } + + @Override + protected void visitMethods () throws Exception { + for (Entry meth : methodsToAdd.entrySet()) { + PyObject clampObj = meth.getValue(); + String methodName = (String)clampObj.__getattr__("name").__tojava__(String.class); + Class returnClass = (Class)clampObj.__getattr__("returntype").__tojava__(Class.class); + Class[] thrownClasses = Py.tojava(clampObj.__getattr__("throws"), Class[].class); + Class[] parameterClasses = Py.tojava(clampObj.__getattr__("argtypes"), Class[].class); + AnnotationDescr[] methodAnnotations = extractAnnotation((PyDictionary)clampObj.__getattr__("method_annotations")); + PyObject[] parameterAnnotationObjs = (PyObject[])clampObj.__getattr__("parameter_annotations").__tojava__(PyObject[].class); + + AnnotationDescr[][]parameterAnnotations = new AnnotationDescr[parameterAnnotationObjs.length][]; + for (int i = 0; i)returnClass, parameterClasses, thrownClasses, + Modifier.PUBLIC, superclass, methodAnnotations, parameterAnnotations); + + } + } +} + diff --git a/tests/python/custom_proxymaker/annotated_class.py b/tests/python/custom_proxymaker/annotated_class.py new file mode 100644 --- /dev/null +++ b/tests/python/custom_proxymaker/annotated_class.py @@ -0,0 +1,18 @@ +import java.io.BufferedInputStream + +from java.lang import Deprecated + +from org.python.compiler.custom_proxymaker import MiniClampMaker +from org.python.compiler.custom_proxymaker import CustomAnnotation + +class AnnotatedInputStream(java.io.BufferedInputStream): + __proxymaker__ = MiniClampMaker + __java_package__ = 'custom_proxymaker.tests' + + _clamp_class_annotations = {CustomAnnotation: + {'createdBy': 'Darusik', + 'priority': CustomAnnotation.Priority.LOW, + 'changedBy': ['Darjus', 'Darjunia']}, + Deprecated:None} + + diff --git a/tests/python/custom_proxymaker/clamp.py b/tests/python/custom_proxymaker/clamp.py new file mode 100644 --- /dev/null +++ b/tests/python/custom_proxymaker/clamp.py @@ -0,0 +1,5 @@ +from collections import namedtuple + +# just a helper +ClampMethod = namedtuple('ClampMethod', 'name returntype argtypes throws ' + 'method_annotations parameter_annotations') diff --git a/tests/python/custom_proxymaker/constructor_signatures.py b/tests/python/custom_proxymaker/constructor_signatures.py new file mode 100644 --- /dev/null +++ b/tests/python/custom_proxymaker/constructor_signatures.py @@ -0,0 +1,32 @@ +from java.lang import (Void, String, Integer, Long) +from javax.swing import BoxLayout +from java.awt import Container + +from org.python.compiler.custom_proxymaker import MiniClampMaker + +import sys +sys.path.append('tests/python/custom_proxymaker/') +from clamp import ClampMethod + +class ConstructorSignatures(BoxLayout): + __proxymaker__ = MiniClampMaker + __java_package__ = 'custom_proxymaker.tests' + +# def __init__(self, val): +# super(ConstructorSignatures, self).__init__(Container(), BoxLayout.X_AXIS) +# print val + + def __jinit__(self, one, two): + # super(ConstructorSignatures, self).__init__(Container(), BoxLayout.X_AXIS) + print one, two + + __jinit__._clamp = ClampMethod('__init__', Void.TYPE, [Container, Integer.TYPE], [], {}, [{}]) + + def test(self): + return 1 + test._clamp = ClampMethod('test', Long.TYPE, [], [], {}, [{}]) + + def toString(self): + return self.__class__.__name__ + toString._clamp = ClampMethod('toString', String, [], [], {}, [{}]) + diff --git a/tests/python/custom_proxymaker/junit_test.py b/tests/python/custom_proxymaker/junit_test.py new file mode 100644 --- /dev/null +++ b/tests/python/custom_proxymaker/junit_test.py @@ -0,0 +1,28 @@ +from org.junit.Assert import assertEquals +from org.junit import Test +from java.lang import (Object, Void, Long) +from java.lang import Exception as JavaException +import sys +import time + +from org.python.compiler.custom_proxymaker import MiniClampMaker + +sys.path.append('tests/python/custom_proxymaker/') +from clamp import ClampMethod + +class JUnitTest(Object): + __proxymaker__ = MiniClampMaker + __java_package__ = 'custom_proxymaker.tests' + + + def testAddition(self): + assertEquals(4, 1 + 3) + testAddition._clamp = ClampMethod('testAddition',Void.TYPE,[],[],{Test:None},[{}]) + + def testJavaException(self): + raise JavaException() + testJavaException._clamp = ClampMethod('testJavaException', Void.TYPE, [], [JavaException], {Test:{'expected':JavaException}}, [{}]) + + def testTimeout(self): + time.sleep(0.1) + testTimeout._clamp = ClampMethod('testTimeout', Void.TYPE, [], [], {Test:{'timeout':Long(1000)}}, [{}]) diff --git a/tests/python/custom_proxymaker/method_signatures.py b/tests/python/custom_proxymaker/method_signatures.py new file mode 100644 --- /dev/null +++ b/tests/python/custom_proxymaker/method_signatures.py @@ -0,0 +1,41 @@ +from java.lang import (Class, Object, Void, String, Long) +from java.lang import RuntimeException + +from org.python.compiler.custom_proxymaker import MiniClampMaker + +import sys +sys.path.append('tests/python/custom_proxymaker/') +from clamp import ClampMethod + +class MethodSignatures(Object): + __proxymaker__ = MiniClampMaker + __java_package__ = 'custom_proxymaker.tests' + + def throwsException(self): + pass + throwsException._clamp = ClampMethod('throwsException', Void.TYPE, [], [RuntimeException], {}, [{}]) + + def returnsLong(self): + return 2 + returnsLong._clamp = ClampMethod('returnsLong', Long.TYPE, [], [], {}, [{}]) + + def returnsObject(self): + return Object() + returnsObject._clamp = ClampMethod('returnsObject', Object, [], [], {}, [{}]) + + def returnsArray(self): + return [1,2,3] + returnsArray._clamp = ClampMethod('returnsArray', Class.forName('[J'), [], [], {}, [{}]) + + def returnsArrayObj(self): + return [1,2,3] + returnsArrayObj._clamp = ClampMethod('returnsArrayObj', Class.forName('[Ljava.lang.Object;'), [], [], {}, [{}]) + + def acceptsString(self, arg): + pass + acceptsString._clamp = ClampMethod('acceptsString', Void.TYPE, [String], [], {}, [{}]) + + def acceptsArray(self, arg): + pass + acceptsArray._clamp = ClampMethod('acceptsArray', Void.TYPE, [Class.forName('[J')], [], {}, [{}]) + \ No newline at end of file -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Tue Dec 25 02:05:17 2012 From: jython-checkins at python.org (jeff.allen) Date: Tue, 25 Dec 2012 02:05:17 +0100 (CET) Subject: [Jython-checkins] =?utf-8?q?jython=3A_Formatting_change_only_to_P?= =?utf-8?q?yArray?= Message-ID: <3YVfHd6KBMzRpn@mail.python.org> http://hg.python.org/jython/rev/e98037676920 changeset: 6912:e98037676920 parent: 6904:9968367fa217 user: Jeff Allen date: Tue Dec 18 23:15:41 2012 +0000 summary: Formatting change only to PyArray ...because I'm going to make some minor changes and at some point I *will* auto-format. There's a lot in this file that does not match the coding standard (optional braces, spacing, line width). Fixed here so as not to confuse the substantive change set I expect to follow. test_array passes (ok with 12 skips). files: src/org/python/core/PyArray.java | 590 ++++++++---------- 1 files changed, 266 insertions(+), 324 deletions(-) diff --git a/src/org/python/core/PyArray.java b/src/org/python/core/PyArray.java --- a/src/org/python/core/PyArray.java +++ b/src/org/python/core/PyArray.java @@ -22,8 +22,7 @@ /** * A wrapper class around native java arrays. * - * Instances of PyArray are created either by java functions or directly by the - * jarray module. + * Instances of PyArray are created either by java functions or directly by the jarray module. *

* See also the jarray module. */ @@ -74,15 +73,16 @@ @ExposedNew static final PyObject array_new(PyNewWrapper new_, boolean init, PyType subtype, - PyObject[] args, String[] keywords) { + PyObject[] args, String[] keywords) { if (new_.for_type != subtype && keywords.length > 0) { int argc = args.length - keywords.length; PyObject[] justArgs = new PyObject[argc]; System.arraycopy(args, 0, justArgs, 0, argc); args = justArgs; } - ArgParser ap = new ArgParser("array", args, Py.NoKeywords, new String[] {"typecode", "initializer"}, - 1); + ArgParser ap = + new ArgParser("array", args, Py.NoKeywords, + new String[] {"typecode", "initializer"}, 1); ap.noKeywords(); PyObject obj = ap.getPyObject(0); PyObject initial = ap.getPyObject(1, null); @@ -99,7 +99,8 @@ type = ((PyJavaType)obj).getProxyType(); typecode = type.getName(); } else { - throw Py.TypeError("array() argument 1 must be char, not " + obj.getType().fastGetName()); + throw Py.TypeError("array() argument 1 must be char, not " + + obj.getType().fastGetName()); } PyArray self; @@ -121,9 +122,8 @@ self.fromstring(initial.toString()); } else if ("u".equals(typecode)) { if (initial instanceof PyUnicode) { - self.extendArray(((PyUnicode) initial).toCodePoints()); - } - else { + self.extendArray(((PyUnicode)initial).toCodePoints()); + } else { self.extendUnicodeIter(initial); } } else { @@ -149,20 +149,18 @@ array.typecode = Character.toString(typecode); return array; } - + public static Class array_class(Class type) { - return Array.newInstance(type, 0).getClass(); + return Array.newInstance(type, 0).getClass(); } /** - * Create a PyArray storing ctype types and being initialised - * with initialiser. + * Create a PyArray storing ctype types and being initialised with + * init. * - * @param init - * an initialiser for the array - can be PyString or PySequence - * (including PyArray) or iterable type. - * @param ctype - * Class type of the elements stored in the array. + * @param init an initialiser for the array - can be PyString or PySequence (including PyArray) + * or iterable type. + * @param ctype Class type of the elements stored in the array. * @return a new PyArray */ public static PyArray array(PyObject init, Class ctype) { @@ -220,7 +218,7 @@ @ExposedMethod final PyObject array___getitem__(PyObject o) { PyObject ret = seq___finditem__(o); - if(ret == null) { + if (ret == null) { throw Py.IndexError("index out of range: " + o); } return ret; @@ -316,8 +314,8 @@ PyArray otherArr = (PyArray)other; if (!otherArr.typecode.equals(this.typecode)) { - throw Py.TypeError("can only append arrays of the same type, expected '" - + this.type + ", found " + otherArr.type); + throw Py.TypeError("can only append arrays of the same type, expected '" + this.type + + ", found " + otherArr.type); } delegate.appendArray(otherArr.delegate.copyArray()); return this; @@ -331,8 +329,7 @@ /** * Adds (appends) two PyArrays together * - * @param other - * a PyArray to be added to the instance + * @param other a PyArray to be added to the instance * @return the result of the addition as a new PyArray instance */ @ExposedMethod(type = MethodType.BINARY) @@ -344,7 +341,7 @@ PyArray otherArr = (PyArray)other; if (!otherArr.typecode.equals(this.typecode)) { throw Py.TypeError("can only append arrays of the same type, expected '" + this.type - + ", found " + otherArr.type); + + ", found " + otherArr.type); } PyArray ret = new PyArray(this); ret.delegate.appendArray(otherArr.delegate.copyArray()); @@ -379,7 +376,7 @@ } if (__len__() > 0) { return new PyTuple(getType(), new PyTuple(Py.newString(typecode), - Py.newString(tostring())), dict); + Py.newString(tostring())), dict); } else { return new PyTuple(getType(), new PyTuple(Py.newString(typecode)), dict); } @@ -402,20 +399,21 @@ } private String encodeTypecode(String typecode) { - if (typecode.length() > 1) return typecode; - else return "'" + typecode + "'"; + if (typecode.length() > 1) { + return typecode; + } else { + return "'" + typecode + "'"; + } } /** * - * @param c - * target Class for the conversion + * @param c target Class for the conversion * @return Java object converted to required class type if possible. */ @Override public Object __tojava__(Class c) { - if(c == Object.class - || (c.isArray() && c.getComponentType().isAssignableFrom(type))) { + if (c == Object.class || (c.isArray() && c.getComponentType().isAssignableFrom(type))) { if (delegate.capacity != delegate.size) { // when unboxing, need to shrink the array first, otherwise incorrect // results to Java @@ -424,8 +422,9 @@ return data; } } - if(c.isInstance(this)) + if (c.isInstance(this)) { return this; + } return Py.NoConversion; } @@ -436,7 +435,7 @@ private static int getCodePoint(PyObject obj) { if (obj instanceof PyUnicode) { - PyUnicode u = (PyUnicode) obj; + PyUnicode u = (PyUnicode)obj; int[] codepoints = u.toCodePoints(); if (codepoints.length == 1) { return codepoints[0]; @@ -445,21 +444,17 @@ throw Py.TypeError("array item must be unicode character"); } - // relax to allow mixing with PyString, integers private static int getCodePointOrInt(PyObject obj) { if (obj instanceof PyUnicode) { - PyUnicode u = (PyUnicode) obj; + PyUnicode u = (PyUnicode)obj; return u.toCodePoints()[0]; - } - else if (obj instanceof PyString) { - PyString s = (PyString) obj; + } else if (obj instanceof PyString) { + PyString s = (PyString)obj; return s.toString().charAt(0); - } - else if (obj.__nonzero__()) { + } else if (obj.__nonzero__()) { return obj.asInt(); - } - else { + } else { return -1; } } @@ -467,8 +462,7 @@ /** * Append new value x to the end of the array. * - * @param value - * item to be appended to the array + * @param value item to be appended to the array */ public void append(PyObject value) { @@ -495,17 +489,15 @@ } } - @ExposedMethod public void array_byteswap() { byteswap(); } /** - * "Byteswap" all items of the array. This is only supported for values - * which are 1, 2, 4, or 8 bytes in size; for other types of values, - * RuntimeError is raised. It is useful when reading data from a file - * written on a machine with a different byte order. + * "Byteswap" all items of the array. This is only supported for values which are 1, 2, 4, or 8 + * bytes in size; for other types of values, RuntimeError is raised. It is useful when reading + * data from a file written on a machine with a different byte order. */ public void byteswap() { if (getStorageSize() == 0 || "u".equals(typecode)) { @@ -515,7 +507,7 @@ } /** - * Implementation of Cloneable interface. + * Implementation of Cloneable interface. * * @return copy of current PyArray */ @@ -526,8 +518,7 @@ /** * Converts a character code for the array type to a Java Class. - *

- * + *

* The following character codes and their native types are supported:
* * @@ -567,17 +558,15 @@ * * *
double
- *

+ *

* - * @param type - * character code for the array type - * + * @param type character code for the array type * @return Class of the native type */ // promote B, H, I (unsigned int) to next larger size public static Class char2class(char type) throws PyIgnoreMethodTag { - switch(type){ + switch (type) { case 'z': return Boolean.TYPE; case 'b': @@ -610,24 +599,25 @@ } private static String class2char(Class cls) { - if(cls.equals(Boolean.TYPE)) + if (cls.equals(Boolean.TYPE)) { return "z"; - else if(cls.equals(Character.TYPE)) + } else if (cls.equals(Character.TYPE)) { return "c"; - else if(cls.equals(Byte.TYPE)) + } else if (cls.equals(Byte.TYPE)) { return "b"; - else if(cls.equals(Short.TYPE)) + } else if (cls.equals(Short.TYPE)) { return "h"; - else if(cls.equals(Integer.TYPE)) + } else if (cls.equals(Integer.TYPE)) { return "i"; - else if(cls.equals(Long.TYPE)) + } else if (cls.equals(Long.TYPE)) { return "l"; - else if(cls.equals(Float.TYPE)) + } else if (cls.equals(Float.TYPE)) { return "f"; - else if(cls.equals(Double.TYPE)) + } else if (cls.equals(Double.TYPE)) { return "d"; - else + } else { return cls.getName(); + } } @ExposedMethod @@ -652,11 +642,11 @@ } return iCount; } + /** * Return the number of occurrences of x in the array. * - * @param value - * instances of the value to be counted + * @param value instances of the value to be counted * @return number of time value was found in the array. */ public PyInteger count(PyObject value) { @@ -664,10 +654,9 @@ } /** - * Delete the element at position i from the array + * Delete the element at position i from the array * - * @param i - * index of the item to be deleted from the array + * @param i index of the item to be deleted from the array */ @Override protected void del(int i) { @@ -677,12 +666,10 @@ } /** - * Delete the slice defined by start to stop from the array. + * Delete the slice defined by start to stop from the array. * - * @param start - * starting index of slice - * @param stop - * finishing index of slice + * @param start starting index of slice + * @param stop finishing index of slice */ @Override protected void delRange(int start, int stop) { @@ -690,49 +677,43 @@ } @ExposedMethod - public final void array_extend(PyObject iterable){ + public final void array_extend(PyObject iterable) { extendInternal(iterable); } /** - * Append items from iterable to the end of the array. If - * iterable is another array, it must have exactly the same type code; if - * not, TypeError will be raised. If iterable is not an array, it must be - * iterable and its elements must be the right type to be appended to the - * array. Changed in version 2.4: Formerly, the argument could only be - * another array. + * Append items from iterable to the end of the array. If iterable is another + * array, it must have exactly the same type code; if not, TypeError will be raised. If iterable + * is not an array, it must be iterable and its elements must be the right type to be appended + * to the array. Changed in version 2.4: Formerly, the argument could only be another array. * - * @param iterable - * iterable object used to extend the array + * @param iterable iterable object used to extend the array */ public void extend(PyObject iterable) { extendInternal(iterable); } /** - * Internal extend function, provides basic interface for extending arrays. - * Handles specific cases of iterable being PyStrings or - * PyArrays. Default behaviour is to defer to + * Internal extend function, provides basic interface for extending arrays. Handles specific + * cases of iterable being PyStrings or PyArrays. Default behaviour is to defer to * {@link #extendInternalIter(PyObject) extendInternalIter } * - * @param iterable - * object of type PyString, PyArray or any object that can be - * iterated over. + * @param iterable object of type PyString, PyArray or any object that can be iterated over. */ private void extendInternal(PyObject iterable) { if (iterable instanceof PyUnicode) { if ("u".equals(typecode)) { extendUnicodeIter(iterable); - } else if ("c".equals(typecode)){ + } else if ("c".equals(typecode)) { throw Py.TypeError("array item must be char"); } else { throw Py.TypeError("an integer is required"); } } else if (iterable instanceof PyString) { - fromstring(((PyString) iterable).toString()); + fromstring(((PyString)iterable).toString()); } else if (iterable instanceof PyArray) { - PyArray source = (PyArray) iterable; + PyArray source = (PyArray)iterable; if (!source.typecode.equals(typecode)) { throw Py.TypeError("can only extend with array of same kind"); } @@ -745,13 +726,12 @@ /** * Internal extend function to process iterable objects. * - * @param iterable - * any object that can be iterated over. + * @param iterable any object that can be iterated over. */ private void extendInternalIter(PyObject iterable) { // iterable object without a length property - cannot presize the // array, so append each item - if(iterable.__findattr__("__len__") == null) { + if (iterable.__findattr__("__len__") == null) { for (PyObject item : iterable.asIterable()) { append(item); } @@ -770,7 +750,7 @@ for (PyObject item : iterable.asIterable()) { PyUnicode uitem; try { - uitem = (PyUnicode) item; + uitem = (PyUnicode)item; } catch (ClassCastException e) { throw Py.TypeError("Type not compatible with array type"); } @@ -792,22 +772,19 @@ } @ExposedMethod - public final void array_fromfile(PyObject f, int count){ + public final void array_fromfile(PyObject f, int count) { fromfile(f, count); } /** - * Read count items (as machine values) from the file object - * f and append them to the end of the array. If less than - * count items are available, EOFError is raised, but the items - * that were available are still inserted into the array. f must - * be a real built-in file object; something else with a read() method won't + * Read count items (as machine values) from the file object f and + * append them to the end of the array. If less than count items are available, + * EOFError is raised, but the items that were available are still inserted into the array. + * f must be a real built-in file object; something else with a read() method won't * do. * - * @param f - * Python builtin file object to retrieve data - * @param count - * number of array elements to read + * @param f Python builtin file object to retrieve data + * @param count number of array elements to read */ public void fromfile(PyObject f, int count) { // check for arg1 as file object @@ -820,35 +797,33 @@ // load whatever was collected into the array fromstring(buffer); // check for underflow - if(buffer.length() < readbytes) { + if (buffer.length() < readbytes) { int readcount = buffer.length() / getStorageSize(); - throw Py.EOFError("not enough items in file. " - + Integer.toString(count) + " requested, " - + Integer.toString(readcount) + " actually read"); + throw Py.EOFError("not enough items in file. " + Integer.toString(count) + + " requested, " + Integer.toString(readcount) + " actually read"); } } @ExposedMethod - public final void array_fromlist(PyObject obj){ + public final void array_fromlist(PyObject obj) { fromlist(obj); } /** - * Append items from the list. This is equivalent to "for x in list: - * a.append(x)"except that if there is a type error, the array is unchanged. + * Append items from the list. This is equivalent to "for x in list: a.append(x)"except that if + * there is a type error, the array is unchanged. * - * @param obj - * input list object that will be appended to the array + * @param obj input list object that will be appended to the array */ public void fromlist(PyObject obj) { - if(!(obj instanceof PyList)) { + if (!(obj instanceof PyList)) { throw Py.TypeError("arg must be list"); } // store the current size of the internal array int size = delegate.getSize(); try { extendInternalIter(obj); - } catch(PyException e) { + } catch (PyException e) { // trap any exception - any error invalidates the whole list delegate.setSize(size); // re-throw @@ -857,11 +832,9 @@ } /** - * Generic stream reader to read the entire contents of a stream into the - * array. + * Generic stream reader to read the entire contents of a stream into the array. * - * @param is - * InputStream to source the data from + * @param is InputStream to source the data from * * @return number of primitives successfully read * @@ -873,21 +846,18 @@ } /** - * Generic stream reader to read count primitive types from a - * stream into the array. + * Generic stream reader to read count primitive types from a stream into the + * array. * - * @param is - * InputStream to source the data from - * @param count - * number of primitive types to read from the stream + * @param is InputStream to source the data from + * @param count number of primitive types to read from the stream * * @return number of primitives successfully read * * @throws IOException * @throws EOFException */ - private int fromStream(InputStream is, int count) throws IOException, - EOFException { + private int fromStream(InputStream is, int count) throws IOException, EOFException { DataInputStream dis = new DataInputStream(is); // current number of items present int origsize = delegate.getSize(); @@ -924,7 +894,7 @@ break; case 'c': for (int i = 0; i < count; i++, index++) { - Array.setChar(data, index, (char) (dis.readByte() & 0xff)); + Array.setChar(data, index, (char)(dis.readByte() & 0xff)); delegate.size++; } break; @@ -987,28 +957,26 @@ } /** - * Appends items from the string, interpreting the string as an array of - * machine values (as if it had been read from a file using the - * {@link #fromfile(PyObject, int) fromfile()} method). + * Appends items from the string, interpreting the string as an array of machine values (as if + * it had been read from a file using the {@link #fromfile(PyObject, int) fromfile()} method). * - * @param input - * string of bytes containing array data + * @param input string of bytes containing array data */ @ExposedMethod final void array_fromstring(String input) { int itemsize = getStorageSize(); int strlen = input.length(); - if((strlen % itemsize) != 0) { + if ((strlen % itemsize) != 0) { throw Py.ValueError("string length not a multiple of item size"); } ByteArrayInputStream bis = new ByteArrayInputStream(StringUtil.toBytes(input)); int origsize = delegate.getSize(); try { fromStream(bis); - } catch(EOFException e) { + } catch (EOFException e) { // stubbed catch for fromStream throws throw Py.EOFError("not enough items in string"); - } catch(IOException e) { + } catch (IOException e) { // discard anything successfully loaded delegate.setSize(origsize); throw Py.IOError(e); @@ -1031,10 +999,9 @@ } /** - * Get the element at position i from the array + * Get the element at position i from the array * - * @param i - * index of the item to be retrieved from the array + * @param i index of the item to be retrieved from the array */ @Override protected PyObject pyget(int i) { @@ -1055,18 +1022,13 @@ /** * Getter for the storage size of the array's type. - *

- * - * The sizes returned by this method represent the number of bytes used to - * store the type. In the case of streams, this is the number of bytes - * written to, or read from a stream. For memory this value is the - * minimum number of bytes required to store the type. - *

- * - * This method is used by other methods to define read/write quanta from - * strings and streams. - *

- * + *

+ * The sizes returned by this method represent the number of bytes used to store the type. In + * the case of streams, this is the number of bytes written to, or read from a stream. For + * memory this value is the minimum number of bytes required to store the type. + *

+ * This method is used by other methods to define read/write quanta from strings and streams. + *

* Values returned are:
* * @@ -1111,23 +1073,24 @@ */ @ExposedGet(name = "itemsize") public int getItemsize() { - if(type.isPrimitive()) { - if(type == Boolean.TYPE) + if (type.isPrimitive()) { + if (type == Boolean.TYPE) { return 1; - else if(type == Byte.TYPE) + } else if (type == Byte.TYPE) { return 1; - else if(type == Character.TYPE) + } else if (type == Character.TYPE) { return 1; - else if(type == Short.TYPE) + } else if (type == Short.TYPE) { return 2; - else if(type == Integer.TYPE) + } else if (type == Integer.TYPE) { return 4; - else if(type == Long.TYPE) + } else if (type == Long.TYPE) { return 8; - else if(type == Float.TYPE) + } else if (type == Float.TYPE) { return 4; - else if(type == Double.TYPE) + } else if (type == Double.TYPE) { return 8; + } } // return something here... could be a calculated size? return 0; @@ -1171,15 +1134,12 @@ } /** - * Retrieve a slice from the array specified by the start, - * stop and step. + * Retrieve a slice from the array specified by the start, stop and + * step. * - * @param start - * start index of the slice - * @param stop - * stop index of the slice - * @param step - * stepping increment of the slice + * @param start start index of the slice + * @param stop stop index of the slice + * @param step stepping increment of the slice * @return A new PyArray object containing the described slice */ @Override @@ -1202,9 +1162,8 @@ } /** - * Getter for the type code of the array. - * {@link #char2class(char) char2class} describes the possible type codes - * and their meaning. + * Getter for the type code of the array. {@link #char2class(char) char2class} describes the + * possible type codes and their meaning. * * @return single character type code for the array */ @@ -1214,33 +1173,31 @@ } @ExposedMethod - public final int array_index(PyObject value){ + public final int array_index(PyObject value) { int index = indexInternal(value); - if(index != -1) + if (index != -1) { return index; - throw Py.ValueError("array.index(" + value + "): " + value - + " not found in array"); + } + throw Py.ValueError("array.index(" + value + "): " + value + " not found in array"); } /** - * Return the smallest i such that i is the index of - * the first occurrence of value in the array. + * Return the smallest i such that i is the index of the first occurrence of + * value in the array. * - * @param value - * value to find the index of - * @return index of the first occurance of value + * @param value value to find the index of + * @return index of the first occurrence of value */ public PyObject index(PyObject value) { return Py.newInteger(array_index(value)); } /** - * Return the smallest i such that i is the index of - * the first occurrence of value in the array. + * Return the smallest i such that i is the index of the first occurrence of + * value in the array. * - * @param value - * value to find the index of - * @return index of the first occurance of value + * @param value value to find the index of + * @return index of the first occurrence of value */ private int indexInternal(PyObject value) { // note: cpython does not raise type errors based on item type @@ -1264,19 +1221,16 @@ } @ExposedMethod - public final void array_insert(int index, PyObject value){ + public final void array_insert(int index, PyObject value) { insert(index, value); } /** - * Insert a new item with value value in the array before - * position index. Negative values are treated as being relative - * to the end of the array. + * Insert a new item with value value in the array before position + * index. Negative values are treated as being relative to the end of the array. * - * @param index - * insert position - * @param value - * value to be inserted into array + * @param index insert position + * @param value value to be inserted into array */ public void insert(int index, PyObject value) { index = boundToSequence(index); @@ -1291,8 +1245,12 @@ } } - @ExposedMethod(defaults="-1") - public final PyObject array_pop(int i){ + /** + * Removes the item at the index i from the array and returns it. The optional + * argument defaults to -1, so that by default the last item is removed and returned. + */ + @ExposedMethod(defaults = "-1") + public final PyObject array_pop(int i) { PyObject val = pop(i); if ("u".equals(typecode)) { return new PyUnicode(val.asInt()); @@ -1301,21 +1259,16 @@ } /** - * Removes the item with the index index from the array and - * returns it. The optional argument defaults to -1, so that by default the - * last item is removed and returned. + * Removes the last item from the array and return it. */ public PyObject pop() { return pop(-1); } /** - * Removes the item with the index index from the array and - * returns it. The optional argument defaults to -1, so that by default the - * last item is removed and returned. + * Removes the item with the index index from the array and returns it. * - * @param index - * array location to be popped from the array + * @param index array location to be popped from the array * @return array element popped from index */ public PyObject pop(int index) { @@ -1332,33 +1285,29 @@ } @ExposedMethod - public final void array_remove(PyObject value){ + public final void array_remove(PyObject value) { remove(value); } /** - * Remove the first occurrence of value from the array. + * Remove the first occurrence of value from the array. * - * @param value - * array value to be removed + * @param value array value to be removed */ public void remove(PyObject value) { int index = indexInternal(value); - if(index != -1) { + if (index != -1) { delegate.remove(index); return; } - throw Py.ValueError("array.remove(" + value + "): " + value - + " not found in array"); + throw Py.ValueError("array.remove(" + value + "): " + value + " not found in array"); } /** - * Repeat the array count times. + * Repeat the array count times. * - * @param count - * number of times to repeat the array - * @return A new PyArray object containing the source object repeated - * count times. + * @param count number of times to repeat the array + * @return A new PyArray object containing the source object repeated count times. */ @Override protected PyObject repeat(int count) { @@ -1366,45 +1315,39 @@ PyArray ret = new PyArray(type, 0); // XXX: ret.typecode = typecode; - for(int i = 0; i < count; i++) { + for (int i = 0; i < count; i++) { ret.delegate.appendArray(arraycopy); } return ret; } @ExposedMethod - public final void array_reverse(){ + public final void array_reverse() { reverse(); } /** * Reverse the elements in the array - * */ public void reverse() { // build a new reversed array and set this.data to it when done Object array = Array.newInstance(type, Array.getLength(data)); - for(int i = 0, lastIndex = delegate.getSize() - 1; i <= lastIndex; i++) { + for (int i = 0, lastIndex = delegate.getSize() - 1; i <= lastIndex; i++) { Array.set(array, lastIndex - i, Array.get(data, i)); } data = array; } /** - * Set an element in the array - the index needs to exist, this method does - * not automatically extend the array. See - * {@link AbstractArray#setSize(int) AbstractArray.setSize()} or - * {@link AbstractArray#ensureCapacity(int) AbstractArray.ensureCapacity()} - * for ways to extend capacity. - *

+ * Set an element in the array - the index needs to exist, this method does not automatically + * extend the array. See {@link AbstractArray#setSize(int) AbstractArray.setSize()} or + * {@link AbstractArray#ensureCapacity(int) AbstractArray.ensureCapacity()} for ways to extend + * capacity. + *

+ * This code specifically checks for overflows of the integral types: byte, short, int and long. * - * This code specifically checks for overflows of the integral types: byte, - * short, int and long. - * - * @param i - * index of the element to be set - * @param value - * value to set the element to + * @param i index of the element to be set + * @param value value to set the element to */ public void set(int i, PyObject value) { pyset(i, value); @@ -1417,64 +1360,63 @@ return; } - if(type == Byte.TYPE) { + if (type == Byte.TYPE) { long val; try { val = ((Long)value.__tojava__(Long.TYPE)).longValue(); - } catch(ClassCastException e) { + } catch (ClassCastException e) { throw Py.TypeError("Type not compatible with array type"); } - if(val < (isSigned() ? 0 : Byte.MIN_VALUE)) { + if (val < (isSigned() ? 0 : Byte.MIN_VALUE)) { throw Py.OverflowError("value too small for " + type.getName()); - } else if(val > Byte.MAX_VALUE) { + } else if (val > Byte.MAX_VALUE) { throw Py.OverflowError("value too large for " + type.getName()); } - } else if(type == Short.TYPE) { + } else if (type == Short.TYPE) { long val; try { val = ((Long)value.__tojava__(Long.TYPE)).longValue(); - } catch(ClassCastException e) { + } catch (ClassCastException e) { throw Py.TypeError("Type not compatible with array type"); } - if(val < (isSigned() ? 0 : Short.MIN_VALUE)) { + if (val < (isSigned() ? 0 : Short.MIN_VALUE)) { throw Py.OverflowError("value too small for " + type.getName()); - } else if(val > Short.MAX_VALUE) { + } else if (val > Short.MAX_VALUE) { throw Py.OverflowError("value too large for " + type.getName()); } - } else if(type == Integer.TYPE) { + } else if (type == Integer.TYPE) { long val; try { val = ((Long)value.__tojava__(Long.TYPE)).longValue(); - } catch(ClassCastException e) { + } catch (ClassCastException e) { throw Py.TypeError("Type not compatible with array type"); } - if(val < (isSigned() ? 0 : Integer.MIN_VALUE)) { + if (val < (isSigned() ? 0 : Integer.MIN_VALUE)) { throw Py.OverflowError("value too small for " + type.getName()); - } else if(val > Integer.MAX_VALUE) { + } else if (val > Integer.MAX_VALUE) { throw Py.OverflowError("value too large for " + type.getName()); } - } else if(type == Long.TYPE) { + } else if (type == Long.TYPE) { if (isSigned() && value instanceof PyInteger) { if (((PyInteger)value).getValue() < 0) { throw Py.OverflowError("value too small for " + type.getName()); } } else if (value instanceof PyLong) { - ((PyLong)value).getLong(isSigned() ? 0 : Long.MIN_VALUE, - Long.MAX_VALUE); + ((PyLong)value).getLong(isSigned() ? 0 : Long.MIN_VALUE, Long.MAX_VALUE); } else { Object o; try { o = value.__tojava__(Long.TYPE); - } catch(ClassCastException e) { + } catch (ClassCastException e) { throw Py.TypeError("Type not compatible with array type"); } - if(o == Py.NoConversion) { + if (o == Py.NoConversion) { throw Py.TypeError("Type not compatible with array type"); } } } Object o = Py.tojava(value, type); - if(o == Py.NoConversion) { + if (o == Py.NoConversion) { throw Py.TypeError("Type not compatible with array type"); } Array.set(data, i, o); @@ -1502,43 +1444,40 @@ } /** - * Sets a slice of the array. value can be a string (for - * byte and char types) or PyArray. If a - * PyArray, its type must be convertible into the type of the target - * PyArray. + * Sets a slice of the array. value can be a string (for byte and + * char types) or PyArray. If a PyArray, its type must be convertible into the type + * of the target PyArray. * - * @param start - * start index of the delete slice - * @param stop - * end index of the delete slice - * @param step - * stepping increment of the slice + * @param start start index of the delete slice + * @param stop end index of the delete slice + * @param step stepping increment of the slice */ @Override protected void setslice(int start, int stop, int step, PyObject value) { if (stop < start) { stop = start; } - if(type == Character.TYPE && value instanceof PyString) { + if (type == Character.TYPE && value instanceof PyString) { char[] chars = null; // if (value instanceof PyString) { - if(step != 1) { + if (step != 1) { throw Py.ValueError("invalid bounds for setting from string"); } chars = value.toString().toCharArray(); delegate.replaceSubArray(start, stop, chars, 0, chars.length); } else { - if(value instanceof PyString && type == Byte.TYPE) { + if (value instanceof PyString && type == Byte.TYPE) { byte[] chars = ((PyString)value).toBytes(); - if(chars.length == stop - start && step == 1) { + if (chars.length == stop - start && step == 1) { System.arraycopy(chars, 0, data, start, chars.length); } else { throw Py.ValueError("invalid bounds for setting from string"); } - } else if(value instanceof PyArray) { + } else if (value instanceof PyArray) { PyArray array = (PyArray)value; if (!array.typecode.equals(typecode)) { - throw Py.TypeError("bad argument type for built-in operation|" + array.typecode + "|" + typecode); + throw Py.TypeError("bad argument type for built-in operation|" + array.typecode + + "|" + typecode); } if (step == 1) { Object arrayDelegate; @@ -1548,11 +1487,12 @@ arrayDelegate = array.delegate.getArray(); } try { - delegate.replaceSubArray(start, stop, arrayDelegate, 0, array.delegate.getSize()); - } catch(IllegalArgumentException e) { + delegate.replaceSubArray(start, stop, arrayDelegate, 0, + array.delegate.getSize()); + } catch (IllegalArgumentException e) { throw Py.TypeError("Slice typecode '" + array.typecode - + "' is not compatible with this array (typecode '" - + this.typecode + "')"); + + "' is not compatible with this array (typecode '" + this.typecode + + "')"); } } else if (step > 1) { int len = array.__len__(); @@ -1570,26 +1510,25 @@ } } else { throw Py.TypeError(String.format("can only assign array (not \"%.200s\") to array " - + "slice", value.getType().fastGetName())); + + "slice", value.getType().fastGetName())); } } } @ExposedMethod - public final void array_tofile(PyObject f){ + public final void array_tofile(PyObject f) { tofile(f); } @ExposedMethod - public void array_write(PyObject f){ + public void array_write(PyObject f) { tofile(f); } /** - * Write all items (as machine values) to the file object f. + * Write all items (as machine values) to the file object f. * - * @param f - * Python builtin file object to write data + * @param f Python builtin file object to write data */ public void tofile(PyObject f) { if (!(f instanceof PyFile)) { @@ -1625,11 +1564,10 @@ } /** - * Generic stream writer to write the entire contents of the array to the - * stream as primitive types. + * Generic stream writer to write the entire contents of the array to the stream as primitive + * types. * - * @param os - * OutputStream to sink the array data to + * @param os OutputStream to sink the array data to * * @return number of primitives successfully written * @@ -1639,57 +1577,70 @@ DataOutputStream dos = new DataOutputStream(os); switch (typecode.charAt(0)) { case 'z': - for(int i = 0; i < delegate.getSize(); i++) + for (int i = 0; i < delegate.getSize(); i++) { dos.writeBoolean(Array.getBoolean(data, i)); + } break; case 'b': - for(int i = 0; i < delegate.getSize(); i++) + for (int i = 0; i < delegate.getSize(); i++) { dos.writeByte(Array.getByte(data, i)); + } break; case 'B': - for(int i = 0; i < delegate.getSize(); i++) + for (int i = 0; i < delegate.getSize(); i++) { dos.writeByte(signedByte(Array.getShort(data, i))); + } break; case 'u': // use 32-bit integers since we want UCS-4 storage - for(int i = 0; i < delegate.getSize(); i++) + for (int i = 0; i < delegate.getSize(); i++) { dos.writeInt(Array.getInt(data, i)); + } break; case 'c': - for(int i = 0; i < delegate.getSize(); i++) + for (int i = 0; i < delegate.getSize(); i++) { dos.writeByte((byte)Array.getChar(data, i)); + } break; case 'h': - for(int i = 0; i < delegate.getSize(); i++) + for (int i = 0; i < delegate.getSize(); i++) { dos.writeShort(Array.getShort(data, i)); + } break; case 'H': - for(int i = 0; i < delegate.getSize(); i++) + for (int i = 0; i < delegate.getSize(); i++) { dos.writeShort(signedShort(Array.getInt(data, i))); + } break; case 'i': - for(int i = 0; i < delegate.getSize(); i++) + for (int i = 0; i < delegate.getSize(); i++) { dos.writeInt(Array.getInt(data, i)); + } break; case 'I': - for(int i = 0; i < delegate.getSize(); i++) + for (int i = 0; i < delegate.getSize(); i++) { dos.writeInt(signedInt(Array.getLong(data, i))); + } break; case 'l': - for(int i = 0; i < delegate.getSize(); i++) + for (int i = 0; i < delegate.getSize(); i++) { dos.writeLong(Array.getLong(data, i)); + } break; case 'L': // faking it - for(int i = 0; i < delegate.getSize(); i++) + for (int i = 0; i < delegate.getSize(); i++) { dos.writeLong(Array.getLong(data, i)); + } break; case 'f': - for(int i = 0; i < delegate.getSize(); i++) + for (int i = 0; i < delegate.getSize(); i++) { dos.writeFloat(Array.getFloat(data, i)); + } break; case 'd': - for(int i = 0; i < delegate.getSize(); i++) + for (int i = 0; i < delegate.getSize(); i++) { dos.writeDouble(Array.getDouble(data, i)); + } break; } return dos.size(); @@ -1698,23 +1649,19 @@ private static byte signedByte(short x) { if (x >= 128 && x < 256) { return (byte)(x - 256); - } - else if (x >= 0) { + } else if (x >= 0) { return (byte)x; - } - else { + } else { throw Py.ValueError("invalid storage"); } } - private static short signedShort(int x) { + private static short signedShort(int x) { if (x >= 32768 && x < 65536) { return (short)(x - 65536); - } - else if (x >= 0) { + } else if (x >= 0) { return (short)x; - } - else { + } else { throw Py.ValueError("invalid storage"); } } @@ -1722,11 +1669,9 @@ private static int signedInt(long x) { if (x >= 2147483648L && x < 4294967296L) { return (int)(x - 4294967296L); - } - else if (x >= 0) { + } else if (x >= 0) { return (int)x; - } - else { + } else { throw Py.ValueError("invalid storage"); } } @@ -1734,17 +1679,15 @@ private static short unsignedByte(byte x) { if (x < 0) { return (short)(x + 256); - } - else { + } else { return x; } } - private static int unsignedShort(short x) { + private static int unsignedShort(short x) { if (x < 0) { return x + 65536; - } - else { + } else { return x; } } @@ -1752,28 +1695,27 @@ private static long unsignedInt(int x) { if (x < 0) { return x + 4294967296L; - } - else { + } else { return x; } } @ExposedMethod - public final PyObject array_tostring(){ + public final PyObject array_tostring() { return new PyString(tostring()); } /** - * Convert the array to an array of machine values and return the string - * representation (the same sequence of bytes that would be written to a - * file by the {@link #tofile(PyObject) tofile()} method.) + * Convert the array to an array of machine values and return the string representation (the + * same sequence of bytes that would be written to a file by the {@link #tofile(PyObject) + * tofile()} method.) */ public String tostring() { ByteArrayOutputStream bos = new ByteArrayOutputStream(); try { toStream(bos); - } catch(IOException e) { + } catch (IOException e) { throw Py.IOError(e); } return StringUtil.fromBytes(bos.toByteArray()); @@ -1785,12 +1727,12 @@ } int len = delegate.getSize(); int[] codepoints = new int[len]; - for(int i = 0; i < len; i++) + for (int i = 0; i < len; i++) { codepoints[i] = Array.getInt(data, i); + } return new String(codepoints, 0, codepoints.length); } - @ExposedMethod public final PyObject array_tounicode() { return new PyUnicode(tounicode()); -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Tue Dec 25 02:05:19 2012 From: jython-checkins at python.org (jeff.allen) Date: Tue, 25 Dec 2012 02:05:19 +0100 (CET) Subject: [Jython-checkins] =?utf-8?q?jython=3A_=5Fio=2EFileIO=3A_support_r?= =?utf-8?q?eadinto/write_array=2Earray?= Message-ID: <3YVfHg3y8qzRqH@mail.python.org> http://hg.python.org/jython/rev/b2523e388129 changeset: 6913:b2523e388129 user: Jeff Allen date: Thu Dec 20 17:06:22 2012 +0000 summary: _io.FileIO: support readinto/write array.array Make PyArray.tostream public and add a suitable public fromstream and use them from PyFileIO. test_io now scores fail/error/skip = 11/15/99, test_fileio = 0/0/1 and test_array = 0/0/12 (unchanged). files: Lib/_jyio.py | 6 +- src/org/python/core/PyArray.java | 236 ++++++---- src/org/python/modules/_io/PyFileIO.java | 93 ++- 3 files changed, 209 insertions(+), 126 deletions(-) diff --git a/Lib/_jyio.py b/Lib/_jyio.py --- a/Lib/_jyio.py +++ b/Lib/_jyio.py @@ -27,6 +27,7 @@ import codecs import warnings import errno +import array # Import thread instead of threading to reduce startup cost try: from thread import allocate_lock as Lock @@ -555,7 +556,10 @@ # raise BlockingIOError with characters_written == 0.) self._flush_unlocked() before = len(self._write_buf) - self._write_buf.extend(b) + if isinstance(b, array.array): # _pyio.py version fails on array.array + self._write_buf.extend(b.tostring()) # Jython version works (while needed) + else: + self._write_buf.extend(b) written = len(self._write_buf) - before if len(self._write_buf) > self.buffer_size: try: diff --git a/src/org/python/core/PyArray.java b/src/org/python/core/PyArray.java --- a/src/org/python/core/PyArray.java +++ b/src/org/python/core/PyArray.java @@ -858,100 +858,146 @@ * @throws EOFException */ private int fromStream(InputStream is, int count) throws IOException, EOFException { - DataInputStream dis = new DataInputStream(is); - // current number of items present + + // Current number of items present int origsize = delegate.getSize(); - // position to start inserting into - int index = origsize; - // create capacity for 'count' items - delegate.ensureCapacity(index + count); - if (type.isPrimitive()) { - switch (typecode.charAt(0)) { - case 'z': - for (int i = 0; i < count; i++, index++) { - Array.setBoolean(data, index, dis.readBoolean()); - delegate.size++; - } - break; - case 'b': - for (int i = 0; i < count; i++, index++) { - Array.setByte(data, index, dis.readByte()); - delegate.size++; - } - break; - case 'B': - for (int i = 0; i < count; i++, index++) { - Array.setShort(data, index, unsignedByte(dis.readByte())); - delegate.size++; - } - break; - case 'u': - // use 32-bit integers since we want UCS-4 storage - for (int i = 0; i < count; i++, index++) { - Array.setInt(data, index, dis.readInt()); - delegate.size++; - } - break; - case 'c': - for (int i = 0; i < count; i++, index++) { - Array.setChar(data, index, (char)(dis.readByte() & 0xff)); - delegate.size++; - } - break; - case 'h': - for (int i = 0; i < count; i++, index++) { - Array.setShort(data, index, dis.readShort()); - delegate.size++; - } - break; - case 'H': - for (int i = 0; i < count; i++, index++) { - Array.setInt(data, index, unsignedShort(dis.readShort())); - delegate.size++; - } - break; - case 'i': - for (int i = 0; i < count; i++, index++) { - Array.setInt(data, index, dis.readInt()); - delegate.size++; - } - break; - case 'I': - for (int i = 0; i < count; i++, index++) { - Array.setLong(data, index, unsignedInt(dis.readInt())); - delegate.size++; - } - break; - case 'l': - for (int i = 0; i < count; i++, index++) { - Array.setLong(data, index, dis.readLong()); - delegate.size++; - } - break; - case 'L': // faking it - for (int i = 0; i < count; i++, index++) { - Array.setLong(data, index, dis.readLong()); - delegate.size++; - } - break; - case 'f': - for (int i = 0; i < count; i++, index++) { - Array.setFloat(data, index, dis.readFloat()); - delegate.size++; - } - break; - case 'd': - for (int i = 0; i < count; i++, index++) { - Array.setDouble(data, index, dis.readDouble()); - delegate.size++; - } - break; - } - } - dis.close(); - return (index - origsize); + + // Reserve capacity for 'count' items + delegate.setSize(origsize + count); + + // Read into the array, after the current contents, up to new size (or EOF thrown) + int n = fromStream(is, origsize, delegate.getSize(), true); + return n - origsize; } + /** + * Read primitive values from a stream into the array without resizing. Data is read until the + * array is filled or the stream runs out. If the stream does not contain a whole number of + * items (possible if the item size is not one byte), the behaviour in respect of the final + * partial item and straem position is not defined. + * + * @param is InputStream to source the data from + * @return number of primitives successfully read + * @throws IOException reflecting I/O errors during reading + */ + public int fillFromStream(InputStream is) throws IOException { + return fromStream(is, 0, delegate.size, false); + } + + /** + * Helper for reading primitive values from a stream into a slice of the array. Data is read + * until the array slice is filled or the stream runs out. The purpose of the method is to + * concentrate in one place the manipulation of bytes into the several primitive element types + * on behalf of {@link #fillFromStream(InputStream)} etc.. Since different read methods respond + * differently to it, the caller must specify whether the exhaustion of the stream (EOF) should + * be treated as an error or not. If the stream does not contain a whole number of items + * (possible if the item size is not one byte), the behaviour in respect of the final partial + * item and stream position is not defined. + * + * @param dis data stream source for the values + * @param index first element index to read + * @param limit first element index not to read + * @param eofIsError if true, treat EOF as expected way to end + * @return index of first element not read (=limit, if not ended by EOF) + * @throws IOException reflecting I/O errors during reading + * @throws EOFException if stream ends before read is satisfied and eofIsError is true + */ + private int fromStream(InputStream is, int index, int limit, boolean eofIsError) + throws IOException, EOFException { + + // We need a wrapper capable of encoding the data + DataInputStream dis = new DataInputStream(is); + + try { + // We have to deal with each primitive type as a distinct case + if (type.isPrimitive()) { + switch (typecode.charAt(0)) { + case 'z': + for (; index < limit; index++) { + Array.setBoolean(data, index, dis.readBoolean()); + } + break; + case 'b': + for (; index < limit; index++) { + Array.setByte(data, index, dis.readByte()); + } + break; + case 'B': + for (; index < limit; index++) { + Array.setShort(data, index, unsignedByte(dis.readByte())); + } + break; + case 'u': + // use 32-bit integers since we want UCS-4 storage + for (; index < limit; index++) { + Array.setInt(data, index, dis.readInt()); + } + break; + case 'c': + for (; index < limit; index++) { + Array.setChar(data, index, (char)(dis.readByte() & 0xff)); + } + break; + case 'h': + for (; index < limit; index++) { + Array.setShort(data, index, dis.readShort()); + } + break; + case 'H': + for (; index < limit; index++) { + Array.setInt(data, index, unsignedShort(dis.readShort())); + } + break; + case 'i': + for (; index < limit; index++) { + Array.setInt(data, index, dis.readInt()); + } + break; + case 'I': + for (; index < limit; index++) { + Array.setLong(data, index, unsignedInt(dis.readInt())); + } + break; + case 'l': + for (; index < limit; index++) { + Array.setLong(data, index, dis.readLong()); + } + break; + case 'L': // faking it + for (; index < limit; index++) { + Array.setLong(data, index, dis.readLong()); + } + break; + case 'f': + for (; index < limit; index++) { + Array.setFloat(data, index, dis.readFloat()); + } + break; + case 'd': + for (; index < limit; index++) { + Array.setDouble(data, index, dis.readDouble()); + } + break; + } + } + + } catch (EOFException eof) { + if (eofIsError) { + throw eof; + } + // EOF = end of reading: excess odd bytes read inside dis.readXXX() discarded + } + + // index points to the first element *not* written + return index; + } + + /** + * Appends items from the string, interpreting the string as an array of machine values (as if + * it had been read from a file using the {@link #fromfile(PyObject, int) fromfile()} method). + * + * @param input string of bytes containing array data + */ public void fromstring(String input) { array_fromstring(input); } @@ -1568,12 +1614,10 @@ * types. * * @param os OutputStream to sink the array data to - * - * @return number of primitives successfully written - * + * @return number of bytes successfully written * @throws IOException */ - private int toStream(OutputStream os) throws IOException { + public int toStream(OutputStream os) throws IOException { DataOutputStream dos = new DataOutputStream(os); switch (typecode.charAt(0)) { case 'z': @@ -1643,7 +1687,7 @@ } break; } - return dos.size(); + return dos.size(); // bytes written } private static byte signedByte(short x) { diff --git a/src/org/python/modules/_io/PyFileIO.java b/src/org/python/modules/_io/PyFileIO.java --- a/src/org/python/modules/_io/PyFileIO.java +++ b/src/org/python/modules/_io/PyFileIO.java @@ -1,13 +1,20 @@ /* Copyright (c)2012 Jython Developers */ package org.python.modules._io; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.nio.ByteBuffer; import java.nio.channels.Channel; +import java.nio.channels.Channels; import java.nio.channels.FileChannel; +import java.nio.channels.ReadableByteChannel; +import java.nio.channels.WritableByteChannel; import org.python.core.ArgParser; import org.python.core.BuiltinDocs; import org.python.core.Py; +import org.python.core.PyArray; import org.python.core.PyBuffer; import org.python.core.PyException; import org.python.core.PyJavaType; @@ -61,7 +68,8 @@ /** The mode as a PyString based on readable and writable */ @ExposedGet(doc = "String giving the file mode: 'rb', 'rb+', or 'wb'") public final PyString mode; - @ExposedSet(name="mode") + + @ExposedSet(name = "mode") public final void mode_readonly(PyString value) { readonlyAttributeError("mode"); } @@ -218,26 +226,41 @@ @ExposedMethod(doc = readinto_doc) final PyLong FileIO_readinto(PyObject buf) { - + int count; if (!readable) { // ... (or closed) throw tailoredValueError("read"); } - // Perform the operation through a buffer view on the object - PyBuffer pybuf = writablePyBuffer(buf); - try { - PyBuffer.Pointer bp = pybuf.getBuf(); - ByteBuffer byteBuffer = ByteBuffer.wrap(bp.storage, bp.offset, pybuf.getLen()); - int count; - synchronized (ioDelegate) { - count = ioDelegate.readinto(byteBuffer); + if (buf instanceof PyArray) { + // Special case: PyArray knows how to read into itself + PyArray a = (PyArray)buf; + + try { + ReadableByteChannel ch = ioDelegate.getChannel(); + InputStream is = Channels.newInputStream(ch); + count = a.fillFromStream(is); + count *= a.getItemsize(); + } catch (IOException ioe) { + throw Py.IOError(ioe); } - return new PyLong(count); - } finally { - // Must unlock the PyBuffer view from client's object - pybuf.release(); + } else { + // Perform the operation through a buffer view on the object + PyBuffer pybuf = writablePyBuffer(buf); + + try { + PyBuffer.Pointer bp = pybuf.getBuf(); + ByteBuffer byteBuffer = ByteBuffer.wrap(bp.storage, bp.offset, pybuf.getLen()); + synchronized (ioDelegate) { + count = ioDelegate.readinto(byteBuffer); + } + } finally { + // Must unlock the PyBuffer view from client's object + pybuf.release(); + } } + + return new PyLong(count); } @Override @@ -246,28 +269,40 @@ } @ExposedMethod(doc = write_doc) - final PyLong FileIO_write(PyObject obj) { - + final PyLong FileIO_write(PyObject buf) { + int count; if (!writable) { // ... (or closed) throw tailoredValueError("writ"); } - // Get or synthesise a buffer API on the object to be written - PyBuffer pybuf = readablePyBuffer(obj); - try { - // Access the data as a java.nio.ByteBuffer [pos:limit] within possibly larger array - PyBuffer.Pointer bp = pybuf.getBuf(); - ByteBuffer byteBuffer = ByteBuffer.wrap(bp.storage, bp.offset, pybuf.getLen()); - int count; - synchronized (ioDelegate) { - count = ioDelegate.write(byteBuffer); + if (buf instanceof PyArray) { + // Special case: PyArray knows how to write itself + try { + WritableByteChannel ch = ioDelegate.getChannel(); + OutputStream os = Channels.newOutputStream(ch); + count = ((PyArray)buf).toStream(os); + } catch (IOException ioe) { + throw Py.IOError(ioe); } - return new PyLong(count); - } finally { - // Even if that went badly, we should release the lock on the client buffer - pybuf.release(); + } else { + // Get or synthesise a buffer API on the object to be written + PyBuffer pybuf = readablePyBuffer(buf); + + try { + // Access the data as a java.nio.ByteBuffer [pos:limit] within possibly larger array + PyBuffer.Pointer bp = pybuf.getBuf(); + ByteBuffer byteBuffer = ByteBuffer.wrap(bp.storage, bp.offset, pybuf.getLen()); + synchronized (ioDelegate) { + count = ioDelegate.write(byteBuffer); + } + } finally { + // Even if that went badly, we should release the lock on the client buffer + pybuf.release(); + } } + + return new PyLong(count); } @Override -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Tue Dec 25 02:05:21 2012 From: jython-checkins at python.org (jeff.allen) Date: Tue, 25 Dec 2012 02:05:21 +0100 (CET) Subject: [Jython-checkins] =?utf-8?q?jython=3A_Checking_and_raising_errors?= =?utf-8?q?_in_=5Fio_module?= Message-ID: <3YVfHj0G2YzRpv@mail.python.org> http://hg.python.org/jython/rev/044d655970b8 changeset: 6914:044d655970b8 user: Jeff Allen date: Sun Dec 23 16:49:35 2012 +0000 summary: Checking and raising errors in _io module Changes to _jyio.py mostly aimed at greater conformity with test.test_io where copied _pyio code seems not to pass. Introduce _checkInitialized mirroring CPython code. Last skip removed from test_bufio.py so we can revert to lib-python version. test_io now scores fail/error/skip = 2/17/99. files: Lib/_jyio.py | 45 ++++++- Lib/test/test_bufio.py | 80 ------------ Lib/test/test_io.py | 9 +- src/org/python/modules/_io/PyFileIO.java | 2 +- 4 files changed, 49 insertions(+), 87 deletions(-) diff --git a/Lib/_jyio.py b/Lib/_jyio.py --- a/Lib/_jyio.py +++ b/Lib/_jyio.py @@ -156,6 +156,7 @@ """ def __init__(self, raw): + self._ok = False # Jython: subclass __init__ must set when state valid self._raw = raw ### Positioning ### @@ -211,21 +212,31 @@ ### Inquiries ### def seekable(self): + self._checkInitialized() # Jython: to forbid use in an invalid state return self.raw.seekable() def readable(self): + self._checkInitialized() # Jython: to forbid use in an invalid state return self.raw.readable() def writable(self): + self._checkInitialized() # Jython: to forbid use in an invalid state return self.raw.writable() @property def raw(self): return self._raw - @property - def closed(self): - return self.raw.closed + # Jython difference: @property closed(self) inherited from _IOBase.__closed + + # Jython difference: emulate C implementation CHECK_INITIALIZED. This is for + # compatibility, to pass test.test_io.CTextIOWrapperTest.test_initialization. + def _checkInitialized(self): + if not self._ok: + if self.raw is None: + raise ValueError("raw stream has been detached") + else: + raise ValueError("I/O operation on uninitialized object") @property def name(self): @@ -388,6 +399,7 @@ self.buffer_size = buffer_size self._reset_read_buf() self._read_lock = Lock() + self._ok = True # Jython: to enable use now in a valid state def _reset_read_buf(self): self._read_buf = b"" @@ -401,6 +413,7 @@ mode. If n is negative, read until EOF or until read() would block. """ + self._checkReadable() # Jython: to forbid use in an invalid state if n is not None and n < -1: raise ValueError("invalid number of bytes to read") with self._read_lock: @@ -494,6 +507,7 @@ """Reads up to n bytes, with at most one read() system call.""" # Returns up to n bytes. If at least one byte is buffered, we # only return buffered bytes. Otherwise, we do one raw read. + self._checkReadable() # Jython: to forbid use in an invalid state if n < 0: raise ValueError("number of bytes to read must be positive") if n == 0: @@ -542,8 +556,10 @@ self.buffer_size = buffer_size self._write_buf = bytearray() self._write_lock = Lock() + self._ok = True # Jython: to enable use now in a valid state def write(self, b): + self._checkWritable() # Jython: to forbid use in an invalid state if self.closed: raise ValueError("write to closed file") if isinstance(b, unicode): @@ -588,6 +604,7 @@ def _flush_unlocked(self): if self.closed: raise ValueError("flush of closed file") + self._checkWritable() # Jython: to forbid use in an invalid state while self._write_buf: try: n = self.raw.write(self._write_buf) @@ -939,6 +956,7 @@ def __init__(self, buffer, encoding=None, errors=None, newline=None, line_buffering=False): + self._ok = False # Jython: to forbid use in an invalid state if newline is not None and not isinstance(newline, basestring): raise TypeError("illegal newline type: %r" % (type(newline),)) if newline not in (None, "", "\n", "\r", "\r\n"): @@ -977,6 +995,8 @@ self._snapshot = None # info for reconstructing decoder state self._seekable = self._telling = self.buffer.seekable() + self._ok = True # Jython: to enable use now in a valid state + if self._seekable and self.writable(): position = self.buffer.tell() if position != 0: @@ -1021,15 +1041,23 @@ return self._buffer def seekable(self): + self._checkInitialized() # Jython: to forbid use in an invalid state + self._checkClosed() # Jython: compatibility with C implementation return self._seekable def readable(self): + self._checkInitialized() # Jython: to forbid use in an invalid state + self._checkClosed() # Jython: compatibility with C implementation return self.buffer.readable() def writable(self): + self._checkInitialized() # Jython: to forbid use in an invalid state + self._checkClosed() # Jython: compatibility with C implementation return self.buffer.writable() def flush(self): + self._checkInitialized() # Jython: to forbid use in an invalid state + self._checkClosed() # Jython: compatibility with C implementation self.buffer.flush() self._telling = self._seekable @@ -1042,6 +1070,15 @@ # Jython difference: @property closed(self) inherited from _IOBase.__closed + # Jython difference: emulate C implementation CHECK_INITIALIZED. This is for + # compatibility, to pass test.test_io.CTextIOWrapperTest.test_initialization. + def _checkInitialized(self): + if not self._ok: + if self.buffer is None: + raise ValueError("underlying buffer has been detached") + else: + raise ValueError("I/O operation on uninitialized object") + @property def name(self): return self.buffer.name @@ -1053,6 +1090,7 @@ return self.buffer.isatty() def write(self, s): + self._checkWritable() # Jython: to forbid use in an invalid state if self.closed: raise ValueError("write to closed file") if not isinstance(s, unicode): @@ -1233,6 +1271,7 @@ if self.buffer is None: raise ValueError("buffer is already detached") self.flush() + self._ok = False # Jython: to forbid use in an invalid state buffer = self._buffer self._buffer = None return buffer diff --git a/Lib/test/test_bufio.py b/Lib/test/test_bufio.py deleted file mode 100644 --- a/Lib/test/test_bufio.py +++ /dev/null @@ -1,80 +0,0 @@ -import unittest -from test import test_support as support - -import io # C implementation. -import _pyio as pyio # Python implementation. - -# Simple test to ensure that optimizations in the IO library deliver the -# expected results. For best testing, run this under a debug-build Python too -# (to exercise asserts in the C code). - -lengths = list(range(1, 257)) + [512, 1000, 1024, 2048, 4096, 8192, 10000, - 16384, 32768, 65536, 1000000] - -class BufferSizeTest(unittest.TestCase): - def try_one(self, s): - # Write s + "\n" + s to file, then open it and ensure that successive - # .readline()s deliver what we wrote. - - # Ensure we can open TESTFN for writing. - support.unlink(support.TESTFN) - - # Since C doesn't guarantee we can write/read arbitrary bytes in text - # files, use binary mode. - f = self.open(support.TESTFN, "wb") - try: - # write once with \n and once without - f.write(s) - f.write(b"\n") - f.write(s) - f.close() - f = open(support.TESTFN, "rb") - line = f.readline() - self.assertEqual(line, s + b"\n") - line = f.readline() - self.assertEqual(line, s) - line = f.readline() - self.assertTrue(not line) # Must be at EOF - f.close() - finally: - support.unlink(support.TESTFN) - - def drive_one(self, pattern): - for length in lengths: - # Repeat string 'pattern' as often as needed to reach total length - # 'length'. Then call try_one with that string, a string one larger - # than that, and a string one smaller than that. Try this with all - # small sizes and various powers of 2, so we exercise all likely - # stdio buffer sizes, and "off by one" errors on both sides. - q, r = divmod(length, len(pattern)) - teststring = pattern * q + pattern[:r] - self.assertEqual(len(teststring), length) - self.try_one(teststring) - self.try_one(teststring + b"x") - self.try_one(teststring[:-1]) - - def test_primepat(self): - # A pattern with prime length, to avoid simple relationships with - # stdio buffer sizes. - self.drive_one(b"1234567890\00\01\02\03\04\05\06") - - def test_nullpat(self): - self.drive_one(bytes(1000)) - - - at unittest.skipIf(support.is_jython, "FIXME: Not working on Jython") -class CBufferSizeTest(BufferSizeTest): - open = io.open - -class PyBufferSizeTest(BufferSizeTest): - open = staticmethod(pyio.open) - -class BuiltinBufferSizeTest(BufferSizeTest): - open = open - - -def test_main(): - support.run_unittest(CBufferSizeTest, PyBufferSizeTest, BuiltinBufferSizeTest) - -if __name__ == "__main__": - test_main() diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -2355,13 +2355,16 @@ class CTextIOWrapperTest(TextIOWrapperTest): def test_initialization(self): - r = self.BytesIO(b"\xc3\xa9\n\n") + # Use ascii material so decoding does not raise ValueError + r = self.BytesIO(b"red\nherring\n") b = self.BufferedReader(r, 1000) t = self.TextIOWrapper(b) + # Jython note: Invalid __init__ calls also leave t unreadable (in C + # implementation but not pure python _pyio). self.assertRaises(TypeError, t.__init__, b, newline=42) - self.assertRaises(ValueError, t.read) + self.assertRaises(ValueError, t.read) # Check t unreadable self.assertRaises(ValueError, t.__init__, b, newline='xyzzy') - self.assertRaises(ValueError, t.read) + self.assertRaises(ValueError, t.read) # Check t unreadable @unittest.skipIf(support.is_jython, "GC nondeterministic in Jython") def test_garbage_collection(self): diff --git a/src/org/python/modules/_io/PyFileIO.java b/src/org/python/modules/_io/PyFileIO.java --- a/src/org/python/modules/_io/PyFileIO.java +++ b/src/org/python/modules/_io/PyFileIO.java @@ -390,7 +390,7 @@ throw closedValueError(); } if (!seekableKnown) { - seekable = ioDelegate.seek(0, 0) >= 0; + seekable = ioDelegate.seek(0, 1) >= 0; // Trial seek seekableKnown = true; } return seekable; -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Tue Dec 25 02:05:22 2012 From: jython-checkins at python.org (jeff.allen) Date: Tue, 25 Dec 2012 02:05:22 +0100 (CET) Subject: [Jython-checkins] =?utf-8?q?jython=3A_Remove_skips_from_test/test?= =?utf-8?b?X2lvLnB5?= Message-ID: <3YVfHk2PmwzRpv@mail.python.org> http://hg.python.org/jython/rev/a46ab727b67e changeset: 6915:a46ab727b67e user: Jeff Allen date: Sun Dec 23 22:57:51 2012 +0000 summary: Remove skips from test/test_io.py Removed all the FIXME skips, leaving only those related to GC, and on Windows the Posix and large file test skips. None of the reinstated test fails. Score fail/error/skip = 2/17/63 files: Lib/test/test_io.py | 10 ---------- 1 files changed, 0 insertions(+), 10 deletions(-) diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -718,7 +718,6 @@ self.assertTrue(s.startswith("Exception IOError: "), s) self.assertTrue(s.endswith(" ignored"), s) - @unittest.skipIf(support.is_jython, "Not working in Jython") def test_repr(self): raw = self.MockRawIO() b = self.tp(raw) @@ -745,7 +744,6 @@ b.close() self.assertRaises(ValueError, b.flush) - @unittest.skipIf(support.is_jython, "FIXME: not working in Jython") def test_readonly_attributes(self): raw = self.MockRawIO() buf = self.tp(raw) @@ -811,7 +809,6 @@ self.assertEqual(bufio.readinto(b), 0) self.assertEqual(b, b"gf") - @unittest.skipIf(support.is_jython, "FIXME: hangs in Jython") def test_readlines(self): def bufio(): rawio = self.MockRawIO((b"abc\n", b"d\n", b"ef")) @@ -840,7 +837,6 @@ # this is mildly implementation-dependent self.assertEqual(rawio.read_history, raw_read_sizes) - @unittest.skipIf(support.is_jython, "FIXME: not working in Jython") def test_read_non_blocking(self): # Inject some None's in there to simulate EWOULDBLOCK rawio = self.MockRawIO((b"abc", b"d", None, b"efg", None, None, None)) @@ -1191,7 +1187,6 @@ finally: support.unlink(support.TESTFN) - @unittest.skipIf(support.is_jython, "FIXME: not working in Jython") def test_misbehaved_io(self): rawio = self.MisbehavedRawIO() bufio = self.tp(rawio, 5) @@ -1199,7 +1194,6 @@ self.assertRaises(IOError, bufio.tell) self.assertRaises(IOError, bufio.write, b"abcdef") - @unittest.skipIf(support.is_jython, "FIXME: not working in Jython") def test_max_buffer_size_deprecation(self): with support.check_warnings(("max_buffer_size is deprecated", DeprecationWarning)): @@ -1287,7 +1281,6 @@ pair = self.tp(self.BytesIO(b"abc"), self.MockRawIO()) self.assertEqual(pair.read(None), b"abc") - @unittest.skipIf(support.is_jython, "FIXME: hangs in Jython") def test_readlines(self): pair = lambda: self.tp(self.BytesIO(b"abc\ndef\nh"), self.MockRawIO()) self.assertEqual(pair().readlines(), [b"abc\n", b"def\n", b"h"]) @@ -1799,7 +1792,6 @@ self.assertEqual(r.getvalue(), b"howdy") self.assertRaises(ValueError, t.detach) - @unittest.skipIf(support.is_jython, "Not working in Jython") def test_repr(self): raw = self.BytesIO("hello".encode("utf-8")) b = self.BufferedReader(raw) @@ -2176,7 +2168,6 @@ self.assertEqual(f.read(), data * 2) self.assertEqual(buf.getvalue(), (data * 2).encode(encoding)) - @unittest.skipIf(support.is_jython, "FIXME: not working in Jython") def test_unreadable(self): class UnReadable(self.BytesIO): def readable(self): @@ -2345,7 +2336,6 @@ txt.close() self.assertRaises(ValueError, txt.flush) - @unittest.skipIf(support.is_jython, "FIXME: not working in Jython") def test_readonly_attributes(self): txt = self.TextIOWrapper(self.BytesIO(self.testdata), encoding="ascii") buf = self.BytesIO(self.testdata) -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Tue Dec 25 02:05:23 2012 From: jython-checkins at python.org (jeff.allen) Date: Tue, 25 Dec 2012 02:05:23 +0100 (CET) Subject: [Jython-checkins] =?utf-8?q?jython=3A_PyIOBase_made_iterable?= Message-ID: <3YVfHl680czRqN@mail.python.org> http://hg.python.org/jython/rev/e5c55803473f changeset: 6916:e5c55803473f user: Jeff Allen date: Mon Dec 24 19:49:03 2012 +0000 summary: PyIOBase made iterable Implemented _io._IOBase.next. Also added skip to test_misbehaved_io_read() as this tests a particular CPython implementation detail. test.test_io scores fail/error/skip = 0/16/65 files: Lib/test/test_io.py | 4 + src/org/python/modules/_io/PyIOBase.java | 51 ++++++++--- 2 files changed, 42 insertions(+), 13 deletions(-) diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -958,11 +958,15 @@ self.assertRaises(ValueError, bufio.__init__, rawio, buffer_size=-1) self.assertRaises(ValueError, bufio.read) + @support.cpython_only def test_misbehaved_io_read(self): rawio = self.MisbehavedRawIO((b"abc", b"d", b"efg")) bufio = self.tp(rawio) # _pyio.BufferedReader seems to implement reading different, so that # checking this is not so easy. + # Jython adds: not raised in _jyio.py stand-in. Maybe in eventual Java version. + # CPython raises "raw readinto() returned invalid length" here: + # http://hg.python.org/cpython/file/8527427914a2/Modules/_io/bufferedio.c#l1298 self.assertRaises(IOError, bufio.read, 10) @unittest.skipIf(support.is_jython, "GC nondeterministic in Jython") diff --git a/src/org/python/modules/_io/PyIOBase.java b/src/org/python/modules/_io/PyIOBase.java --- a/src/org/python/modules/_io/PyIOBase.java +++ b/src/org/python/modules/_io/PyIOBase.java @@ -55,8 +55,8 @@ } /** - * Provide a dictionary in the object, so that methods and attributes may be overridden at instance - * level. + * Provide a dictionary in the object, so that methods and attributes may be overridden at + * instance level. */ @ExposedGet protected PyStringMap __dict__ = new PyStringMap(); @@ -198,7 +198,8 @@ */ @ExposedGet(name = "closed", doc = closed_doc) protected boolean __closed; - @ExposedSet(name="closed") + + @ExposedSet(name = "closed") public final void closed_readonly(boolean value) { readonlyAttributeError("closed"); } @@ -594,9 +595,8 @@ while (remainingLimit > 0) { /* - * peek() returns a str of bytes from the buffer (if any), doing at most one read to - * refill, all without advancing the pointer, or it returns None (in vacuous - * non-blocking read). + * read() returns a str of one byte, doing at most one read to refill, or it returns + * None (in vacuous non-blocking read). */ PyObject curr = readMethod.__call__(Py.One); @@ -623,11 +623,14 @@ } + /** + * Return an iterator on which next may be repeatedly called to produce (usually) + * lines from this stream or file. + */ @Override public PyObject __iter__() { _checkClosed(); - // Not like this, in spite of what base comment says, because file *is* an iterator - // return new PySequenceIter(this); + // The object *is* an iterator so return itself return this; } @@ -640,6 +643,28 @@ } /** + * May be called repeatedly to produce (usually) lines from this stream or file. + * + * @return next line from the stream or file + * @throws PyException(StopIteration) when iteration has reached a natural conclusion + * @throws PyException(ValueError) if the file or stream is closed + * @throws PyException(IOError) reflecting an I/O error in during the read + */ + public PyObject next() throws PyException { + return _IOBase_next(); + } + + @ExposedMethod(doc = "x.__next__() <==> next(x)") + final PyObject _IOBase_next() throws PyException { + // Implement directly. Calling __iternext__() fails when PyIOBaseDerived is considered. + PyObject line = invoke("readline"); + if (!line.__nonzero__()) { + throw Py.StopIteration(""); + } + return line; + } + + /** * Read a stream as a sequence of lines. * * @param hint stop reading lines after this many bytes (if not EOF first) @@ -778,16 +803,16 @@ } /** - * Convenience method providing the exception when an argument is not the expected type. - * The format is "type argument expected, got type(arg)." + * Convenience method providing the exception when an argument is not the expected type. The + * format is "type argument expected, got type(arg)." * * @param type of thing expected (or could any text) * @param arg argument provided from which actual type will be reported * @return TypeError to throw */ - protected static PyException tailoredTypeError(String type, PyObject arg){ - return Py.TypeError(String.format("%s argument expected, got %.100s.", - type, arg.getType().fastGetName())); + protected static PyException tailoredTypeError(String type, PyObject arg) { + return Py.TypeError(String.format("%s argument expected, got %.100s.", type, arg.getType() + .fastGetName())); } /* -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Tue Dec 25 02:05:26 2012 From: jython-checkins at python.org (jeff.allen) Date: Tue, 25 Dec 2012 02:05:26 +0100 (CET) Subject: [Jython-checkins] =?utf-8?q?jython_=28merge_default_-=3E_default?= =?utf-8?q?=29=3A_Merge_io_work_with_trunk?= Message-ID: <3YVfHp09qBzRpv@mail.python.org> http://hg.python.org/jython/rev/481f7d06d470 changeset: 6917:481f7d06d470 parent: 6916:e5c55803473f parent: 6911:f8224fee2886 user: Jeff Allen date: Tue Dec 25 00:47:34 2012 +0000 summary: Merge io work with trunk files: src/org/python/compiler/ClassFile.java | 89 ++ src/org/python/compiler/Code.java | 2 +- src/org/python/compiler/JavaMaker.java | 7 +- src/org/python/compiler/ProxyCodeHelpers.java | 338 +++++++ src/org/python/compiler/ProxyMaker.java | 426 +++++----- src/org/python/core/MakeProxies.java | 45 +- src/org/python/util/ProxyCompiler.java | 32 + tests/java/org/python/compiler/custom_proxymaker/ClassAnnotationTest.java | 36 + tests/java/org/python/compiler/custom_proxymaker/ConstructorSignatureTest.java | 33 + tests/java/org/python/compiler/custom_proxymaker/CustomAnnotation.java | 30 + tests/java/org/python/compiler/custom_proxymaker/JUnitTest.java | 18 + tests/java/org/python/compiler/custom_proxymaker/MethodSignatureTest.java | 77 + tests/java/org/python/compiler/custom_proxymaker/MiniClampMaker.java | 182 ++++ tests/python/custom_proxymaker/annotated_class.py | 18 + tests/python/custom_proxymaker/clamp.py | 5 + tests/python/custom_proxymaker/constructor_signatures.py | 32 + tests/python/custom_proxymaker/junit_test.py | 28 + tests/python/custom_proxymaker/method_signatures.py | 41 + 18 files changed, 1207 insertions(+), 232 deletions(-) diff --git a/src/org/python/compiler/ClassFile.java b/src/org/python/compiler/ClassFile.java --- a/src/org/python/compiler/ClassFile.java +++ b/src/org/python/compiler/ClassFile.java @@ -7,14 +7,18 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; import org.python.core.imp; +import org.python.compiler.ProxyCodeHelpers.AnnotationDescr; public class ClassFile { @@ -27,6 +31,7 @@ String[] interfaces; List methodVisitors; List fieldVisitors; + List annotationVisitors; public static String fixName(String n) { if (n.indexOf('.') == -1) @@ -37,6 +42,34 @@ } return new String(c); } + + + public static void visitAnnotations(AnnotationVisitor av, Map fields) { + for (Entryfield: fields.entrySet()) { + visitAnnotation(av, field.getKey(), field.getValue()); + } + } + + // See org.objectweb.asm.AnnotationVisitor for details + // TODO Support annotation annotations and annotation array annotations + public static void visitAnnotation(AnnotationVisitor av, String fieldName, Object fieldValue) { + Class fieldValueClass = fieldValue.getClass(); + + if (fieldValue instanceof Class) { + av.visit(fieldName, Type.getType((Class)fieldValue)); + } else if (fieldValueClass.isEnum()) { + av.visitEnum(fieldName, ProxyCodeHelpers.mapType(fieldValueClass), fieldValue.toString()); + } else if (fieldValue instanceof List) { + AnnotationVisitor arrayVisitor = av.visitArray(fieldName); + List fieldList = (List)fieldValue; + for (Object arrayField: fieldList) { + visitAnnotation(arrayVisitor, null, arrayField); + } + arrayVisitor.visitEnd(); + } else { + av.visit(fieldName, fieldValue); + } + } public ClassFile(String name) { this(name, "java/lang/Object", Opcodes.ACC_SYNCHRONIZED | Opcodes.ACC_PUBLIC, @@ -56,6 +89,7 @@ cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); methodVisitors = Collections.synchronizedList(new ArrayList()); fieldVisitors = Collections.synchronizedList(new ArrayList()); + annotationVisitors = Collections.synchronizedList(new ArrayList()); } public void setSource(String name) { @@ -77,7 +111,54 @@ methodVisitors.add(pmv); return pmv; } + public Code addMethod(String name, String type, int access, String[] exceptions) + throws IOException + { + MethodVisitor mv = cw.visitMethod(access, name, type, null, exceptions); + Code pmv = new Code(mv, type, access); + methodVisitors.add(pmv); + return pmv; + } + + public Code addMethod(String name, String type, int access, String[] exceptions, + AnnotationDescr[]methodAnnotationDescrs, AnnotationDescr[][] parameterAnnotationDescrs) + throws IOException + { + MethodVisitor mv = cw.visitMethod(access, name, type, null, exceptions); + // method annotations + for (AnnotationDescr ad: methodAnnotationDescrs) { + AnnotationVisitor av = mv.visitAnnotation(ad.getName(), true); + if (ad.hasFields()) { + visitAnnotations(av, ad.getFields()); + } + av.visitEnd(); + } + + // parameter annotations + for (int i = 0; i < parameterAnnotationDescrs.length; i++) { + for (AnnotationDescr ad: parameterAnnotationDescrs[i]) { + AnnotationVisitor av = mv.visitParameterAnnotation(i, ad.getName(), true); + if (ad.hasFields()) { + visitAnnotations(av, ad.getFields()); + } + av.visitEnd(); + } + } + + Code pmv = new Code(mv, type, access); + methodVisitors.add(pmv); + return pmv; + } + + public void addClassAnnotation(AnnotationDescr annotationDescr) { + AnnotationVisitor av = cw.visitAnnotation(annotationDescr.getName(), true); + if (annotationDescr.hasFields()) { + visitAnnotations(av, annotationDescr.getFields()); + } + annotationVisitors.add(av); + } + public void addField(String name, String type, int access) throws IOException { @@ -103,6 +184,12 @@ } } + public void endClassAnnotations() { + for (AnnotationVisitor av: annotationVisitors) { + av.visitEnd(); + } + } + public void write(OutputStream stream) throws IOException { cw.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, this.name, null, this.superclass, interfaces); AnnotationVisitor av = cw.visitAnnotation("Lorg/python/compiler/APIVersion;", true); @@ -118,6 +205,7 @@ if (sfilename != null) { cw.visitSource(sfilename, null); } + endClassAnnotations(); endFields(); endMethods(); @@ -129,4 +217,5 @@ //debug(baos); baos.close(); } + } diff --git a/src/org/python/compiler/Code.java b/src/org/python/compiler/Code.java --- a/src/org/python/compiler/Code.java +++ b/src/org/python/compiler/Code.java @@ -10,7 +10,7 @@ import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; -class Code extends MethodVisitor implements Opcodes { +public class Code extends MethodVisitor implements Opcodes { MethodVisitor mv; String sig; String locals[]; diff --git a/src/org/python/compiler/JavaMaker.java b/src/org/python/compiler/JavaMaker.java --- a/src/org/python/compiler/JavaMaker.java +++ b/src/org/python/compiler/JavaMaker.java @@ -32,11 +32,8 @@ int access) throws Exception { /* Need a fancy constructor for the Java side of things */ Code code = classfile.addMethod("", sig, access); - callSuper(code, "", name, parameters, null, sig); - code.visitVarInsn(ALOAD, 0); - getArgs(code, parameters); - code.visitMethodInsn(INVOKEVIRTUAL, classfile.name, "__initProxy__", makeSig("V", $objArr)); - code.visitInsn(RETURN); + callSuper(code, "", name, parameters, Void.TYPE, false); + callInitProxy(parameters, code); } @Override diff --git a/src/org/python/compiler/ProxyCodeHelpers.java b/src/org/python/compiler/ProxyCodeHelpers.java new file mode 100644 --- /dev/null +++ b/src/org/python/compiler/ProxyCodeHelpers.java @@ -0,0 +1,338 @@ +package org.python.compiler; + + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.Map; +import java.util.Map.Entry; + +import org.objectweb.asm.Type; +import org.python.core.Py; +import org.python.core.PyMethod; +import org.python.core.PyObject; +import org.python.core.PyProxy; +import org.python.core.PyReflectedFunction; +import org.python.util.Generic; + +/* + * Various constants and methods for generating Proxy code + */ +public class ProxyCodeHelpers { + public static final int tBoolean=0; + public static final int tByte=1; + public static final int tShort=2; + public static final int tInteger=3; + public static final int tLong=4; + public static final int tFloat=5; + public static final int tDouble=6; + public static final int tCharacter=7; + public static final int tVoid=8; + public static final int tOther=9; + public static final int tNone=10; + + public static Map, Integer> types = fillTypes(); + + public static Map, Integer> fillTypes() { + Map, Integer> typeMap = Generic.map(); + typeMap.put(Boolean.TYPE, tBoolean); + typeMap.put(Byte.TYPE, tByte); + typeMap.put(Short.TYPE, tShort); + typeMap.put(Integer.TYPE, tInteger); + typeMap.put(Long.TYPE, tLong); + typeMap.put(Float.TYPE, tFloat); + typeMap.put(Double.TYPE, tDouble); + typeMap.put(Character.TYPE, tCharacter); + typeMap.put(Void.TYPE, tVoid); + return typeMap; + } + + public static int getType(Class c) { + if (c == null) { + return tNone; + } + Object i = types.get(c); + if (i == null) { + return tOther; + } else { + return ((Integer)i); + } + } + + /** + * Retrieves name from the PyObject in proxy if it's defined in + * Python. This is a specialized helper function for internal PyProxy use. + */ + public static PyObject findPython(PyProxy proxy, String name) { + PyObject o = proxy._getPyInstance(); + if (o == null) { + proxy.__initProxy__(new Object[0]); + o = proxy._getPyInstance(); + } + PyObject ret = o.__findattr__(name); + if (ret instanceof PyMethod) { + PyMethod meth = ((PyMethod)ret); + if (meth.__func__ instanceof PyReflectedFunction) { + PyReflectedFunction func = (PyReflectedFunction)meth.__func__; + if (func.nargs > 0 && proxy.getClass() == func.argslist[0].declaringClass) { + // This function is the default return for the proxy type if the Python instance + // hasn't returned something of its own from __findattr__, so do the standard + // Java call on this + return null; + } + } + } + Py.setSystemState(proxy._getPySystemState()); + return ret; + } + + public static String mapClass(Class c) { + String name = c.getName(); + int index = name.indexOf("."); + if (index == -1) { + return name; + } + StringBuffer buf = new StringBuffer(name.length()); + int last_index = 0; + while (index != -1) { + buf.append(name.substring(last_index, index)); + buf.append("/"); + last_index = index+1; + index = name.indexOf(".", last_index); + } + buf.append(name.substring(last_index, name.length())); + return buf.toString(); + } + + public static String mapType(Class type) { + if (type.isArray()) + return "["+mapType(type.getComponentType()); + + switch (getType(type)) { + case tByte: return "B"; + case tCharacter: return "C"; + case tDouble: return "D"; + case tFloat: return "F"; + case tInteger: return "I"; + case tLong: return "J"; + case tShort: return "S"; + case tBoolean: return "Z"; + case tVoid: return "V"; + default: + return "L"+mapClass(type)+";"; + } + } + + public static String makeSig(Class ret, Class... sig) { + String[] mapped = new String[sig.length]; + for (int i = 0; i < mapped.length; i++) { + mapped[i] = mapType(sig[i]); + } + return makeSig(mapType(ret), mapped); + } + + public static String makeSig(String returnType, String... parameterTypes) { + StringBuilder buf = new StringBuilder("("); + for (String param : parameterTypes) { + buf.append(param); + } + return buf.append(')').append(returnType).toString(); + } + + public static void doReturn(Code code, Class type) throws Exception { + switch (getType(type)) { + case tNone: + break; + case tCharacter: + case tBoolean: + case tByte: + case tShort: + case tInteger: + code.ireturn(); + break; + case tLong: + code.lreturn(); + break; + case tFloat: + code.freturn(); + break; + case tDouble: + code.dreturn(); + break; + case tVoid: + code.return_(); + break; + default: + code.areturn(); + break; + } + } + + public static void doNullReturn(Code code, Class type) throws Exception { + switch (getType(type)) { + case tNone: + break; + case tCharacter: + case tBoolean: + case tByte: + case tShort: + case tInteger: + code.iconst_0(); + code.ireturn(); + break; + case tLong: + code.lconst_0(); + code.lreturn(); + break; + case tFloat: + code.fconst_0(); + code.freturn(); + break; + case tDouble: + code.dconst_0(); + code.dreturn(); + break; + case tVoid: + code.return_(); + break; + default: + code.aconst_null(); + code.areturn(); + break; + } + } + + public static String[] mapClasses(Class[] classes) { + String[] mapped = new String[classes.length]; + for (int i = 0; i < mapped.length; i++) { + mapped[i] = mapClass(classes[i]); + } + return mapped; + } + + public static String[] mapExceptions(Class[] classes) { + String[] exceptionTypes = new String[classes.length]; + for (int i = 0; i < classes.length; i++) { + // Exceptions are represented by their internal names + exceptionTypes[i] = Type.getType(classes[i]).getInternalName(); + } + return exceptionTypes; + } + + public static class MethodDescr { + + public final Class returnType; + + public final String name; + + public final Class[] parameters; + public final Class[] exceptions; + public final Map methodAnnotations; + public final Map[] parameterAnnotations; + + public MethodDescr(Method m) { + this(m.getName(), m.getReturnType(), m.getParameterTypes(), m.getExceptionTypes(), null, null); + } + + public MethodDescr(String name, + Class returnType, + Class[] parameters, + Class[] exceptions) { + this.name = name; + this.returnType = returnType; + this.parameters = parameters; + this.exceptions = exceptions; + this.methodAnnotations = null; + this.parameterAnnotations = null; + } + + public MethodDescr(String name, + Class returnType, + Class[] parameters, + Class[] exceptions, + Map methodAnnotations, + Map[] parameterAnnotations) { + this.name = name; + this.returnType = returnType; + this.parameters = parameters; + this.exceptions = exceptions; + this.methodAnnotations = methodAnnotations; + this.parameterAnnotations = parameterAnnotations; + } + + @Override + public int hashCode() { + return name.hashCode() + parameters.length; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof MethodDescr)) { + return false; + } + MethodDescr oDescr = (MethodDescr)obj; + if (!name.equals(oDescr.name) || parameters.length != oDescr.parameters.length) { + return false; + } + for (int i = 0; i < parameters.length; i++) { + if (!parameters[i].equals(oDescr.parameters[i])) { + return false; + } + } + return true; + } + } + + public static class ConstructorDescr extends MethodDescr { + + public ConstructorDescr(Constructor cons) { + this(cons.getParameterTypes(), cons.getExceptionTypes()); + } + + public ConstructorDescr(Class[] parameters, Class[] exceptions) { + super("", Void.TYPE, parameters, exceptions); + } + } + + public static class AnnotationDescr { + public final Class annotation; + public final Map fields; + + public AnnotationDescr(Classannotation) { + this.annotation = annotation; + this.fields = null; + } + + public AnnotationDescr(Classannotation, Map fields) { + this.annotation = annotation; + this.fields = fields; + } + + public boolean hasFields() { + if (fields == null) { + return false; + } + return true; + } + + public String getName() { + return mapType(annotation); + } + + public Map getFields() { + return fields; + } + + @Override + public int hashCode() { + if (hasFields()) { + int hash = annotation.hashCode(); + for (Entry field: fields.entrySet()) { + hash += field.getKey().hashCode() + field.getValue().hashCode(); + } + return hash; + } else { + return annotation.hashCode(); + } + } + } +} diff --git a/src/org/python/compiler/ProxyMaker.java b/src/org/python/compiler/ProxyMaker.java --- a/src/org/python/compiler/ProxyMaker.java +++ b/src/org/python/compiler/ProxyMaker.java @@ -5,91 +5,21 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.util.Map; import java.util.Set; import org.objectweb.asm.Label; import org.objectweb.asm.Opcodes; import org.python.core.Py; -import org.python.core.PyMethod; -import org.python.core.PyObject; -import org.python.core.PyProxy; -import org.python.core.PyReflectedFunction; import org.python.util.Generic; -public class ProxyMaker implements ClassConstants, Opcodes + +public class ProxyMaker extends ProxyCodeHelpers implements ClassConstants, Opcodes { - public static final int tBoolean=0; - public static final int tByte=1; - public static final int tShort=2; - public static final int tInteger=3; - public static final int tLong=4; - public static final int tFloat=5; - public static final int tDouble=6; - public static final int tCharacter=7; - public static final int tVoid=8; - public static final int tOther=9; - public static final int tNone=10; - - public static Map, Integer> types = fillTypes(); - - public static Map, Integer> fillTypes() { - Map, Integer> typeMap = Generic.map(); - typeMap.put(Boolean.TYPE, tBoolean); - typeMap.put(Byte.TYPE, tByte); - typeMap.put(Short.TYPE, tShort); - typeMap.put(Integer.TYPE, tInteger); - typeMap.put(Long.TYPE, tLong); - typeMap.put(Float.TYPE, tFloat); - typeMap.put(Double.TYPE, tDouble); - typeMap.put(Character.TYPE, tCharacter); - typeMap.put(Void.TYPE, tVoid); - return typeMap; - } - - public static int getType(Class c) { - if (c == null) { - return tNone; - } - Object i = types.get(c); - if (i == null) { - return tOther; - } else { - return ((Integer)i); - } - } - - /** - * Retrieves name from the PyObject in proxy if it's defined in - * Python. This is a specialized helper function for internal PyProxy use. - */ - public static PyObject findPython(PyProxy proxy, String name) { - PyObject o = proxy._getPyInstance(); - if (o == null) { - proxy.__initProxy__(new Object[0]); - o = proxy._getPyInstance(); - } - PyObject ret = o.__findattr__(name); - if (ret instanceof PyMethod) { - PyMethod meth = ((PyMethod)ret); - if (meth.__func__ instanceof PyReflectedFunction) { - PyReflectedFunction func = (PyReflectedFunction)meth.__func__; - if (func.nargs > 0 && proxy.getClass() == func.argslist[0].declaringClass) { - // This function is the default return for the proxy type if the Python instance - // hasn't returned something of its own from __findattr__, so do the standard - // Java call on this - return null; - } - } - } - Py.setSystemState(proxy._getPySystemState()); - return ret; - } - - Class superclass; - Class[] interfaces; + protected final Class superclass; + protected final Class[] interfaces; Set names; Set supernames = Generic.set(); + Set namesAndSigs; // name+signature pairs public ClassFile classfile; /** The name of the class to build. */ public String myClass; @@ -137,134 +67,17 @@ this.interfaces = interfaces; } - public static String mapClass(Class c) { - String name = c.getName(); - int index = name.indexOf("."); - if (index == -1) { - return name; - } - StringBuffer buf = new StringBuffer(name.length()); - int last_index = 0; - while (index != -1) { - buf.append(name.substring(last_index, index)); - buf.append("/"); - last_index = index+1; - index = name.indexOf(".", last_index); - } - buf.append(name.substring(last_index, name.length())); - return buf.toString(); - } - - public static String mapType(Class type) { - if (type.isArray()) - return "["+mapType(type.getComponentType()); - - switch (getType(type)) { - case tByte: return "B"; - case tCharacter: return "C"; - case tDouble: return "D"; - case tFloat: return "F"; - case tInteger: return "I"; - case tLong: return "J"; - case tShort: return "S"; - case tBoolean: return "Z"; - case tVoid: return "V"; - default: - return "L"+mapClass(type)+";"; - } - } - - public static String makeSig(Class ret, Class... sig) { - String[] mapped = new String[sig.length]; - for (int i = 0; i < mapped.length; i++) { - mapped[i] = mapType(sig[i]); - } - return makeSig(mapType(ret), mapped); - } - - public static String makeSig(String returnType, String... parameterTypes) { - StringBuilder buf = new StringBuilder("("); - for (String param : parameterTypes) { - buf.append(param); - } - return buf.append(')').append(returnType).toString(); - } - - public void doConstants() throws Exception { Code code = classfile.addMethod("", makeSig("V"), Modifier.STATIC); code.return_(); } - public static void doReturn(Code code, Class type) throws Exception { - switch (getType(type)) { - case tNone: - break; - case tCharacter: - case tBoolean: - case tByte: - case tShort: - case tInteger: - code.ireturn(); - break; - case tLong: - code.lreturn(); - break; - case tFloat: - code.freturn(); - break; - case tDouble: - code.dreturn(); - break; - case tVoid: - code.return_(); - break; - default: - code.areturn(); - break; - } - } - - public static void doNullReturn(Code code, Class type) throws Exception { - switch (getType(type)) { - case tNone: - break; - case tCharacter: - case tBoolean: - case tByte: - case tShort: - case tInteger: - code.iconst_0(); - code.ireturn(); - break; - case tLong: - code.lconst_0(); - code.lreturn(); - break; - case tFloat: - code.fconst_0(); - code.freturn(); - break; - case tDouble: - code.dconst_0(); - code.dreturn(); - break; - case tVoid: - code.return_(); - break; - default: - code.aconst_null(); - code.areturn(); - break; - } - } - public void callSuper(Code code, String name, String superclass, Class[] parameters, Class ret, - String sig) throws Exception { + boolean doReturn) throws Exception { code.aload(0); int local_index; @@ -297,9 +110,11 @@ break; } } - code.invokespecial(superclass, name, sig); + code.invokespecial(superclass, name, makeSig(ret, parameters)); - doReturn(code, ret); + if (doReturn) { + doReturn(code, ret); + } } public void doJavaCall(Code code, String name, String type, @@ -471,24 +286,73 @@ public void addMethod(Method method, int access) throws Exception { + addMethod(method.getName(), method.getReturnType(), method.getParameterTypes(), + method.getExceptionTypes(), access, method.getDeclaringClass()); + } + + /** + * Adds a method of the given name to the class being implemented. If + * declaringClass is null, the generated method will expect to find an object of + * the method's name in the Python object and call it. If it isn't null, if an object is found + * in the Python object, it'll be called. Otherwise the superclass will be called. No checking + * is done to guarantee that the superclass has a method with the same signature. + */ + public void addMethod(String name, + Class ret, + Class[] parameters, + Class[] exceptions, + int access, + Class declaringClass) throws Exception { + addMethod(name, name, ret, parameters, exceptions, access, declaringClass, null, null); + } + + + /** + * Generates and adds a proxy method to the proxy class + * + * @param name: name of the java method + * @param pyName: name of the python method to which the java method + * proxies (useful for clamped objects) + * + * @param ret: return type + * @param parameters: parameter types + * @param exceptions: throwable exception types + * @param access + * @param declaringClass + * @param methodAnnotations: method annotations + * @param parameterAnnotations: parameter annotations + * @throws Exception + */ + public void addMethod(String name, + String pyName, + Class ret, + Class[] parameters, + Class[] exceptions, + int access, + Class declaringClass, + AnnotationDescr[] methodAnnotations, + AnnotationDescr[][]parameterAnnotations) throws Exception { boolean isAbstract = false; - + if (Modifier.isAbstract(access)) { access = access & ~Modifier.ABSTRACT; isAbstract = true; } - Class[] parameters = method.getParameterTypes(); - Class ret = method.getReturnType(); String sig = makeSig(ret, parameters); + String[] exceptionTypes = mapExceptions(exceptions); - String name = method.getName(); names.add(name); - Code code = classfile.addMethod(name, sig, access); + Code code = null; + if (methodAnnotations != null && parameterAnnotations != null) { + code = classfile.addMethod(name, sig, access, exceptionTypes, methodAnnotations, parameterAnnotations); + } else { + code = classfile.addMethod(name, sig, access, exceptionTypes); + } code.aload(0); - code.ldc(name); + code.ldc(pyName); if (!isAbstract) { int tmp = code.getLocal("org/python/core/PyObject"); @@ -500,12 +364,12 @@ Label callPython = new Label(); code.ifnonnull(callPython); - String superClass = mapClass(method.getDeclaringClass()); + String superClass = mapClass(declaringClass); - callSuper(code, name, superClass, parameters, ret, sig); + callSuper(code, name, superClass, parameters, ret, true); code.label(callPython); code.aload(tmp); - callMethod(code, name, parameters, ret, method.getExceptionTypes()); + callMethod(code, name, parameters, ret, exceptions); addSuperMethod("super__"+name, name, superClass, parameters, ret, sig, access); @@ -515,13 +379,34 @@ code.dup(); Label returnNull = new Label(); code.ifnull(returnNull); - callMethod(code, name, parameters, ret, method.getExceptionTypes()); + callMethod(code, name, parameters, ret, exceptions); code.label(returnNull); code.pop(); doNullReturn(code, ret); } } + + /** + * A constructor that is also a method (!) + */ + public void addConstructorMethodCode(String pyName, + Class[] parameters, + Class[] exceptions, + int access, + Class declaringClass, + Code code) throws Exception { + code.aload(0); + code.ldc(pyName); + + int tmp = code.getLocal("org/python/core/PyObject"); + code.invokestatic("org/python/compiler/ProxyMaker", "findPython", + makeSig($pyObj, $pyProxy, $str)); + code.astore(tmp); + code.aload(tmp); + callMethod(code, "", parameters, Void.TYPE, exceptions); + } + private String methodString(Method m) { StringBuffer buf = new StringBuffer(m.getName()); buf.append(":"); @@ -579,7 +464,7 @@ String sig, int access) throws Exception { Code code = classfile.addMethod("", sig, access); - callSuper(code, "", name, parameters, Void.TYPE, sig); + callSuper(code, "", name, parameters, Void.TYPE, true); } public void addConstructors(Class c) throws Exception { @@ -600,6 +485,10 @@ addConstructor(name, parameters, Void.TYPE, makeSig(Void.TYPE, parameters), access); } } + + protected void addClassAnnotation(AnnotationDescr annotation) { + classfile.addClassAnnotation(annotation); + } // Super methods are added for the following three reasons: // @@ -654,7 +543,7 @@ } supernames.add(methodName); Code code = classfile.addMethod(methodName, sig, access); - callSuper(code, superName, declClass, parameters, ret, sig); + callSuper(code, superName, declClass, parameters, ret, true); } public void addProxy() throws Exception { @@ -720,6 +609,7 @@ public void build() throws Exception { names = Generic.set(); + namesAndSigs = Generic.set(); int access = superclass.getModifiers(); if ((access & Modifier.FINAL) != 0) { throw new InstantiationException("can't subclass final class"); @@ -728,20 +618,130 @@ classfile = new ClassFile(myClass, mapClass(superclass), access); addProxy(); - addConstructors(superclass); + visitConstructors(); classfile.addInterface("org/python/core/PyProxy"); + + visitClassAnnotations(); + visitMethods(); + doConstants(); + addClassDictInit(); + } + + /** + * Visits all methods declared on the given class and classes in its inheritance hierarchy. + * Methods visible to subclasses are added to seen. + */ + protected void visitMethods(Class klass) throws Exception { + for (Method method : klass.getDeclaredMethods()) { + + + // make sure we have only one name + signature pair available per method + if (!namesAndSigs.add(methodString(method))) { + continue; + } - Set seenmethods = Generic.set(); - addMethods(superclass, seenmethods); + int access = method.getModifiers(); + if (Modifier.isStatic(access) || Modifier.isPrivate(access)) { + continue; + } + + if (Modifier.isNative(access)) { + access = access & ~Modifier.NATIVE; + } + + if (Modifier.isProtected(access)) { + access = (access & ~Modifier.PROTECTED) | Modifier.PUBLIC; + if (Modifier.isFinal(access)) { + addSuperMethod(method, access); + continue; + } + } else if (Modifier.isFinal(access)) { + continue; + } else if (!Modifier.isPublic(access)) { + continue; // package protected by process of elimination; we can't override + } + addMethod(method, access); + } + + Class superClass = klass.getSuperclass(); + if (superClass != null) { + visitMethods(superClass); + } + + for (Class iface : klass.getInterfaces()) { + visitMethods(iface); + } + } + + /** + * Called for every method on the proxy's superclass and interfaces that can be overriden by the + * proxy class. If the proxy wants to perform Python lookup and calling for the method, + * {@link #addMethod(Method)} should be called. For abstract methods, addMethod must be called. + */ + protected void visitMethod(Method method) throws Exception { + addMethod(method, method.getModifiers()); + } + + protected void visitMethods() throws Exception { + visitMethods(superclass); for (Class iface : interfaces) { if (iface.isAssignableFrom(superclass)) { Py.writeWarning("compiler", "discarding redundant interface: " + iface.getName()); continue; } classfile.addInterface(mapClass(iface)); - addMethods(iface, seenmethods); + visitMethods(iface); } - doConstants(); - addClassDictInit(); } + + /** Adds a constructor that calls through to superclass. */ + protected void addConstructor(Class[] parameters, int access) throws Exception { + String sig = makeSig(Void.TYPE, parameters); + Code code = classfile.addMethod("", sig, access); + callSuper(code, "", mapClass(superclass), parameters, Void.TYPE, true); + } + + /** + * Called for every constructor on the proxy's superclass that can be overridden by + * the proxy class. + */ + protected void visitConstructor(Constructor constructor) throws Exception { + /* Need a fancy constructor for the Java side of things */ + callInitProxy(constructor.getParameterTypes(), addOpenConstructor(constructor)); + } + + /** + * Adds a constructor that calls through to the superclass constructor with the same signature + * and leaves the returned Code open for more operations. The caller of this method must add a + * return to the Code. + */ + protected Code addOpenConstructor(Constructor constructor) throws Exception { + String sig = makeSig(Void.TYPE, constructor.getParameterTypes()); + Code code = classfile.addMethod("", sig, constructor.getModifiers()); + callSuper(code, "", mapClass(superclass), constructor.getParameterTypes(), Void.TYPE, true); + return code; + } + + /** + * Calls __initProxy__ on this class with the given types of parameters, which must be + * available as arguments to the currently called method in the order of the parameters. + */ + protected void callInitProxy(Class[] parameters, Code code) throws Exception { + code.visitVarInsn(ALOAD, 0); + getArgs(code, parameters); + code.visitMethodInsn(INVOKEVIRTUAL, classfile.name, "__initProxy__", makeSig("V", $objArr)); + code.visitInsn(RETURN); + } + + /** + * Visits constructors from this proxy's superclass. + */ + protected void visitConstructors() throws Exception { + addConstructors(superclass); + } + + protected void visitClassAnnotations() throws Exception { + // ProxyMaker itself does nothing with class annotations for now + } + } diff --git a/src/org/python/core/MakeProxies.java b/src/org/python/core/MakeProxies.java --- a/src/org/python/core/MakeProxies.java +++ b/src/org/python/core/MakeProxies.java @@ -6,6 +6,7 @@ import java.util.ArrayList; import java.util.List; +import org.python.compiler.APIVersion; import org.python.compiler.AdapterMaker; import org.python.compiler.JavaMaker; @@ -50,27 +51,45 @@ public static synchronized Class makeProxy(Class superclass, List> vinterfaces, String className, String proxyName, PyObject dict) { + JavaMaker javaMaker = null; + Class[] interfaces = vinterfaces.toArray(new Class[vinterfaces.size()]); String fullProxyName = proxyPrefix + proxyName + "$" + proxyNumber++; String pythonModuleName; - PyObject mn = dict.__finditem__("__module__"); - if (mn == null) { - pythonModuleName = "foo"; + PyObject module = dict.__finditem__("__module__"); + if (module == null) { + pythonModuleName = "foo"; // FIXME Really, module name foo? } else { - pythonModuleName = (String) mn.__tojava__(String.class); + pythonModuleName = (String) module.__tojava__(String.class); } - JavaMaker jm = new JavaMaker(superclass, - interfaces, - className, - pythonModuleName, - fullProxyName, - dict); + + // Grab the proxy maker from the class if it exists, and if it does, use the proxy class + // name from the maker + PyObject customProxyMaker = dict.__finditem__("__proxymaker__"); + if (customProxyMaker != null) { + if (module == null) { + throw Py.TypeError("Classes using __proxymaker__ must define __module__"); + } + PyObject[] args = Py.javas2pys(superclass, interfaces, className, pythonModuleName, fullProxyName, dict); + javaMaker = Py.tojava(customProxyMaker.__call__(args), JavaMaker.class); + // TODO Full proxy name + } + + if (javaMaker == null) { + javaMaker = new JavaMaker(superclass, + interfaces, + className, + pythonModuleName, + fullProxyName, + dict); + } + try { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - jm.build(bytes); - Py.saveClassFile(fullProxyName, bytes); + javaMaker.build(bytes); + Py.saveClassFile(javaMaker.myClass, bytes); - return makeClass(superclass, vinterfaces, jm.myClass, bytes); + return makeClass(superclass, vinterfaces, javaMaker.myClass, bytes); } catch (Exception exc) { throw Py.JavaError(exc); } diff --git a/src/org/python/util/ProxyCompiler.java b/src/org/python/util/ProxyCompiler.java new file mode 100644 --- /dev/null +++ b/src/org/python/util/ProxyCompiler.java @@ -0,0 +1,32 @@ +package org.python.util; + +import java.util.Properties; + +import org.python.core.PySystemState; + +public class ProxyCompiler { + /** + * Compiles the python file by loading it + * + * FIXME: this is quite hackish right now. It basically starts a whole interpreter + * and set's proxyDebugDirectory as the destination for the compiled proxy class + * + * @param filename: python filename to exec + * @param destDir: destination directory for the proxy classes + */ + public static void compile(String filename, String destDir) { + Properties props = new Properties(System.getProperties()); + props.setProperty(PySystemState.PYTHON_CACHEDIR_SKIP, "true"); + PySystemState.initialize(props, null); + PythonInterpreter interp = new PythonInterpreter(); + + String origProxyDir = org.python.core.Options.proxyDebugDirectory; + try { + org.python.core.Options.proxyDebugDirectory = destDir; + interp.execfile(filename); + } finally { + org.python.core.Options.proxyDebugDirectory = origProxyDir; + } + } + +} diff --git a/tests/java/org/python/compiler/custom_proxymaker/ClassAnnotationTest.java b/tests/java/org/python/compiler/custom_proxymaker/ClassAnnotationTest.java new file mode 100644 --- /dev/null +++ b/tests/java/org/python/compiler/custom_proxymaker/ClassAnnotationTest.java @@ -0,0 +1,36 @@ +package org.python.compiler.custom_proxymaker; + +/* + * Test support for Python class annotations + */ + +import static org.junit.Assert.*; + +import org.junit.*; +import org.python.util.ProxyCompiler; + +public class ClassAnnotationTest { + + Class proxy; + + @Before + public void setUp() throws Exception { + ProxyCompiler.compile("tests/python/custom_proxymaker/annotated_class.py", "build/classes"); + proxy = Class.forName("custom_proxymaker.tests.AnnotatedInputStream"); + } + + @Test + public void hasClassAnnotation() { + // Just by "finding" it we satisfy the test. + @SuppressWarnings("unused") + Deprecated deprecatedAnnotation = proxy.getAnnotation(Deprecated.class); + } + + @Test + public void hasCustomAnnotationWithFields() throws Exception { + CustomAnnotation customAnnotation = proxy.getAnnotation(CustomAnnotation.class); + assertEquals("Darusik", customAnnotation.createdBy()); + assertEquals(CustomAnnotation.Priority.LOW, customAnnotation.priority()); + assertArrayEquals(new String[] {"Darjus", "Darjunia"}, customAnnotation.changedBy()); + } +} diff --git a/tests/java/org/python/compiler/custom_proxymaker/ConstructorSignatureTest.java b/tests/java/org/python/compiler/custom_proxymaker/ConstructorSignatureTest.java new file mode 100644 --- /dev/null +++ b/tests/java/org/python/compiler/custom_proxymaker/ConstructorSignatureTest.java @@ -0,0 +1,33 @@ +package org.python.compiler.custom_proxymaker; + +/* + * Tests constructor signatures + */ + +import static org.junit.Assert.*; + +import java.lang.reflect.*; + +import org.junit.*; +import org.python.util.ProxyCompiler; + +import java.awt.Container; +import javax.swing.BoxLayout; + +public class ConstructorSignatureTest { + Class proxy; + + @Before + public void setUp() throws Exception { + ProxyCompiler.compile("tests/python/custom_proxymaker/constructor_signatures.py", "build/classes"); + proxy = Class.forName("custom_proxymaker.tests.ConstructorSignatures"); + } + + @Ignore // Constructor signatures are not working yet + @Test + @SuppressWarnings("unused") + public void returnsVoid() throws Exception { + Constructor constructor = proxy.getConstructor(new Class[] {Container.class, Integer.TYPE}); + constructor.newInstance(new Container(), BoxLayout.X_AXIS); + } +} diff --git a/tests/java/org/python/compiler/custom_proxymaker/CustomAnnotation.java b/tests/java/org/python/compiler/custom_proxymaker/CustomAnnotation.java new file mode 100644 --- /dev/null +++ b/tests/java/org/python/compiler/custom_proxymaker/CustomAnnotation.java @@ -0,0 +1,30 @@ +package org.python.compiler.custom_proxymaker; + +/** + * This Annotation contains most of the possible annotation fields, + * used to test the annotation part of custom proxymaker + */ + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + + + at Documented + at Retention(RetentionPolicy.RUNTIME) + at Target({ElementType.TYPE,ElementType.METHOD, + ElementType.CONSTRUCTOR,ElementType.ANNOTATION_TYPE, + ElementType.PACKAGE,ElementType.FIELD,ElementType.LOCAL_VARIABLE}) + at Inherited +public @interface CustomAnnotation { + public enum Priority { LOW, MEDIUM, HIGH } + String value(); + String[] changedBy() default ""; + Priority[] priorities(); + Priority priority() default Priority.MEDIUM; + String createdBy() default "Darjus Loktevic"; + String lastChanged() default "08/06/2012"; +} diff --git a/tests/java/org/python/compiler/custom_proxymaker/JUnitTest.java b/tests/java/org/python/compiler/custom_proxymaker/JUnitTest.java new file mode 100644 --- /dev/null +++ b/tests/java/org/python/compiler/custom_proxymaker/JUnitTest.java @@ -0,0 +1,18 @@ +package org.python.compiler.custom_proxymaker; + +/* + * This test tests that we can create JUnit 4 tests in Python and that JUnit's own + * reflection system picks our annotations and runs the underlying code + */ + +import org.junit.Test; +import org.junit.runner.JUnitCore; +import org.python.util.ProxyCompiler; + +public class JUnitTest { + @Test + public void testMethodSignatures() throws Exception { + ProxyCompiler.compile("tests/python/custom_proxymaker/junit_test.py", "build/classes"); + JUnitCore.runClasses(Class.forName("custom_proxymaker.tests.JUnitTest")); + } +} diff --git a/tests/java/org/python/compiler/custom_proxymaker/MethodSignatureTest.java b/tests/java/org/python/compiler/custom_proxymaker/MethodSignatureTest.java new file mode 100644 --- /dev/null +++ b/tests/java/org/python/compiler/custom_proxymaker/MethodSignatureTest.java @@ -0,0 +1,77 @@ +package org.python.compiler.custom_proxymaker; + +/* + * Tests support for various combinations of method signatures + */ + +import static org.junit.Assert.*; + +import java.lang.reflect.*; + +import org.junit.Before; +import org.junit.Test; +import org.python.util.ProxyCompiler; + +public class MethodSignatureTest { + Class proxy; + + @Before + public void setUp() throws Exception { + ProxyCompiler.compile("tests/python/custom_proxymaker/method_signatures.py", "build/classes"); + proxy = Class.forName("custom_proxymaker.tests.MethodSignatures"); + } + + @Test + public void methodThrows() throws Exception { + Method method = proxy.getMethod("throwsException"); + assertArrayEquals(new Class[] {RuntimeException.class}, method.getExceptionTypes()); + } + + @Test + public void returnsVoid() throws Exception { + Method method = proxy.getMethod("throwsException"); + assertEquals(Void.TYPE, method.getReturnType()); + } + + @Test + public void returnsLong() throws Exception { + Method method = proxy.getMethod("returnsLong"); + assertEquals(Long.TYPE, method.getReturnType()); + } + + @Test + public void returnsObject() throws Exception { + Method method = proxy.getMethod("returnsObject"); + assertEquals(Object.class, method.getReturnType()); + } + + @Test + public void returnsArray() throws Exception { + Method method = proxy.getMethod("returnsArray"); + Object compareType = Array.newInstance(Long.TYPE, 0); + assertEquals(compareType.getClass(), method.getReturnType()); + } + + @Test + public void returnsArrayObj() throws Exception { + Method method = proxy.getMethod("returnsArrayObj"); + Object compareType = Array.newInstance(Object.class, 0); + assertEquals(compareType.getClass(), method.getReturnType()); + } + + @Test + @SuppressWarnings("unused") + public void acceptsString() throws Exception { + Class[] partypes = new Class[] {String.class}; + Method method = proxy.getMethod("acceptsString", partypes); + } + + @Test + @SuppressWarnings("unused") + public void acceptsArray() throws Exception { + Object compareType = Array.newInstance(Long.TYPE, 0); + Class[] partypes = new Class[] {compareType.getClass()}; + Method method = proxy.getMethod("acceptsArray", partypes); + } + +} diff --git a/tests/java/org/python/compiler/custom_proxymaker/MiniClampMaker.java b/tests/java/org/python/compiler/custom_proxymaker/MiniClampMaker.java new file mode 100644 --- /dev/null +++ b/tests/java/org/python/compiler/custom_proxymaker/MiniClampMaker.java @@ -0,0 +1,182 @@ +package org.python.compiler.custom_proxymaker; + +/* + * This is a bare bones implementation of ClampMaker. It's goal is to be a "reference" + * implementation for the features that are provided by customizable ProxyMaker + */ + +import java.lang.reflect.Constructor; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; + +import org.python.compiler.Code; +import org.python.compiler.JavaMaker; +import org.python.core.Py; +import org.python.core.PyDictionary; +import org.python.core.PyObject; +import org.python.core.__builtin__; +import org.python.util.Generic; + +public class MiniClampMaker extends JavaMaker { + + private final Map methodsToAdd = Generic.map(); + private final Map constructorsToAdd = Generic.map(); + private AnnotationDescr[] classAnnotations = new AnnotationDescr[]{}; + + private static AnnotationDescr[] extractAnnotation(PyDictionary dict) { + List annotationDescrs = Generic.list(); + for (PyObject annotationIter: dict.iteritems().asIterable()) { + PyObject annotationClass = annotationIter.__getitem__(0); + PyObject annotationFields = annotationIter.__getitem__(1); + AnnotationDescr annotationDescr = null; + if (annotationFields == Py.None) { + annotationDescr = new AnnotationDescr(Py.tojava(annotationClass, Class.class)); + } else { + Map fields = Generic.map(); + for (PyObject item: ((PyDictionary)annotationFields).iteritems().asIterable()) { + fields.put(Py.tojava(item.__getitem__(0), String.class), Py.tojava(item.__getitem__(1), Object.class)); + } + annotationDescr = new AnnotationDescr(Py.tojava(annotationClass, Class.class), fields); + } + annotationDescrs.add(annotationDescr); + } + return (AnnotationDescr[]) annotationDescrs.toArray(new AnnotationDescr[annotationDescrs.size()]); + } + + public MiniClampMaker(Class superclass, + Class[] interfaces, + String pythonClass, + String pythonModule, + String myClass, + PyObject methods) { + super(superclass, interfaces, pythonClass, pythonModule, myClass, methods); + + // if we find __java_package__, override the default proxy naming with + // __java_package__ + .pythonClass + PyObject javaPackage = methods.__finditem__("__java_package__"); + if (javaPackage != null) { + String newMyClass = new String((String)javaPackage.__tojava__(String.class)); + newMyClass += "." + pythonClass; + this.myClass = newMyClass; + } + + + PyObject clampAttr = Py.newString("_clamp"); + for (PyObject pykey : methods.asIterable()) { + String key = Py.tojava(pykey, String.class); + PyObject value = methods.__finditem__(key); + PyObject clampObj = __builtin__.getattr(value, clampAttr, Py.None); + if (clampObj == Py.None) { + continue; + } + String name = (String)clampObj.__getattr__("name").__tojava__(String.class); + if (name.equals("__init__")) { + constructorsToAdd.put(key, clampObj); + } else { + methodsToAdd.put(key, clampObj); + } + } + PyObject pyAnnotations = methods.__finditem__("_clamp_class_annotations"); + if (pyAnnotations != null) { + classAnnotations = extractAnnotation((PyDictionary)pyAnnotations); + } + + } + + @Override + protected void visitClassAnnotations() throws Exception { + for (AnnotationDescr annotation: classAnnotations) { + addClassAnnotation(annotation); + } + } + + @Override + protected void visitConstructors() throws Exception { + + Set> superConstructors = Generic.set(); + for (Constructor constructor: superclass.getDeclaredConstructors()) { + superConstructors.add(constructor); + } + + for (Entry meth : constructorsToAdd.entrySet()) { + Constructor superToCall = null; + String pyName = meth.getKey(); + PyObject clampObj = meth.getValue(); + + Class[] thrownClasses = Py.tojava(clampObj.__getattr__("throws"), Class[].class); + Class[] parameterClasses = Py.tojava(clampObj.__getattr__("argtypes"), Class[].class); + + if (clampObj.__findattr__("super_constructor") != null) { + superToCall = (Constructor)clampObj.__getattr__("super_constructor").__tojava__(Constructor.class); + } else { // try to find a matching super constructor + try { + superToCall = superclass.getDeclaredConstructor(parameterClasses); + } catch (NoSuchMethodException err) { + // FIXME need a fancy constructor finder + superToCall = superConstructors.iterator().next(); + } + } + + for (Constructor constructor: superConstructors) { + if (Arrays.equals(constructor.getParameterTypes(), superToCall.getParameterTypes())) { + superConstructors.remove(constructor); + } + } + + AnnotationDescr[] methodAnnotations = extractAnnotation((PyDictionary)clampObj.__getattr__("method_annotations")); + PyObject[] parameterAnnotationObjs = (PyObject[])clampObj.__getattr__("parameter_annotations").__tojava__(PyObject[].class); + + AnnotationDescr[][]parameterAnnotations = new AnnotationDescr[parameterAnnotationObjs.length][]; + for (int i = 0; i", fullsig, Modifier.PUBLIC, mappedExceptions, methodAnnotations, parameterAnnotations); + callSuper(code, "", mapClass(superclass), superToCall.getParameterTypes(), Void.TYPE, false); + // instead of calling the proxy, we construct the full method code + + addConstructorMethodCode(pyName, superToCall.getParameterTypes(), thrownClasses, Modifier.PUBLIC, superclass, code); + + } + + // the non-overwritten constructors + for (Constructor constructor: superConstructors) { + addConstructor(constructor.getParameterTypes(), Modifier.PUBLIC); + } + } + + @Override + protected void visitMethods () throws Exception { + for (Entry meth : methodsToAdd.entrySet()) { + PyObject clampObj = meth.getValue(); + String methodName = (String)clampObj.__getattr__("name").__tojava__(String.class); + Class returnClass = (Class)clampObj.__getattr__("returntype").__tojava__(Class.class); + Class[] thrownClasses = Py.tojava(clampObj.__getattr__("throws"), Class[].class); + Class[] parameterClasses = Py.tojava(clampObj.__getattr__("argtypes"), Class[].class); + AnnotationDescr[] methodAnnotations = extractAnnotation((PyDictionary)clampObj.__getattr__("method_annotations")); + PyObject[] parameterAnnotationObjs = (PyObject[])clampObj.__getattr__("parameter_annotations").__tojava__(PyObject[].class); + + AnnotationDescr[][]parameterAnnotations = new AnnotationDescr[parameterAnnotationObjs.length][]; + for (int i = 0; i)returnClass, parameterClasses, thrownClasses, + Modifier.PUBLIC, superclass, methodAnnotations, parameterAnnotations); + + } + } +} + diff --git a/tests/python/custom_proxymaker/annotated_class.py b/tests/python/custom_proxymaker/annotated_class.py new file mode 100644 --- /dev/null +++ b/tests/python/custom_proxymaker/annotated_class.py @@ -0,0 +1,18 @@ +import java.io.BufferedInputStream + +from java.lang import Deprecated + +from org.python.compiler.custom_proxymaker import MiniClampMaker +from org.python.compiler.custom_proxymaker import CustomAnnotation + +class AnnotatedInputStream(java.io.BufferedInputStream): + __proxymaker__ = MiniClampMaker + __java_package__ = 'custom_proxymaker.tests' + + _clamp_class_annotations = {CustomAnnotation: + {'createdBy': 'Darusik', + 'priority': CustomAnnotation.Priority.LOW, + 'changedBy': ['Darjus', 'Darjunia']}, + Deprecated:None} + + diff --git a/tests/python/custom_proxymaker/clamp.py b/tests/python/custom_proxymaker/clamp.py new file mode 100644 --- /dev/null +++ b/tests/python/custom_proxymaker/clamp.py @@ -0,0 +1,5 @@ +from collections import namedtuple + +# just a helper +ClampMethod = namedtuple('ClampMethod', 'name returntype argtypes throws ' + 'method_annotations parameter_annotations') diff --git a/tests/python/custom_proxymaker/constructor_signatures.py b/tests/python/custom_proxymaker/constructor_signatures.py new file mode 100644 --- /dev/null +++ b/tests/python/custom_proxymaker/constructor_signatures.py @@ -0,0 +1,32 @@ +from java.lang import (Void, String, Integer, Long) +from javax.swing import BoxLayout +from java.awt import Container + +from org.python.compiler.custom_proxymaker import MiniClampMaker + +import sys +sys.path.append('tests/python/custom_proxymaker/') +from clamp import ClampMethod + +class ConstructorSignatures(BoxLayout): + __proxymaker__ = MiniClampMaker + __java_package__ = 'custom_proxymaker.tests' + +# def __init__(self, val): +# super(ConstructorSignatures, self).__init__(Container(), BoxLayout.X_AXIS) +# print val + + def __jinit__(self, one, two): + # super(ConstructorSignatures, self).__init__(Container(), BoxLayout.X_AXIS) + print one, two + + __jinit__._clamp = ClampMethod('__init__', Void.TYPE, [Container, Integer.TYPE], [], {}, [{}]) + + def test(self): + return 1 + test._clamp = ClampMethod('test', Long.TYPE, [], [], {}, [{}]) + + def toString(self): + return self.__class__.__name__ + toString._clamp = ClampMethod('toString', String, [], [], {}, [{}]) + diff --git a/tests/python/custom_proxymaker/junit_test.py b/tests/python/custom_proxymaker/junit_test.py new file mode 100644 --- /dev/null +++ b/tests/python/custom_proxymaker/junit_test.py @@ -0,0 +1,28 @@ +from org.junit.Assert import assertEquals +from org.junit import Test +from java.lang import (Object, Void, Long) +from java.lang import Exception as JavaException +import sys +import time + +from org.python.compiler.custom_proxymaker import MiniClampMaker + +sys.path.append('tests/python/custom_proxymaker/') +from clamp import ClampMethod + +class JUnitTest(Object): + __proxymaker__ = MiniClampMaker + __java_package__ = 'custom_proxymaker.tests' + + + def testAddition(self): + assertEquals(4, 1 + 3) + testAddition._clamp = ClampMethod('testAddition',Void.TYPE,[],[],{Test:None},[{}]) + + def testJavaException(self): + raise JavaException() + testJavaException._clamp = ClampMethod('testJavaException', Void.TYPE, [], [JavaException], {Test:{'expected':JavaException}}, [{}]) + + def testTimeout(self): + time.sleep(0.1) + testTimeout._clamp = ClampMethod('testTimeout', Void.TYPE, [], [], {Test:{'timeout':Long(1000)}}, [{}]) diff --git a/tests/python/custom_proxymaker/method_signatures.py b/tests/python/custom_proxymaker/method_signatures.py new file mode 100644 --- /dev/null +++ b/tests/python/custom_proxymaker/method_signatures.py @@ -0,0 +1,41 @@ +from java.lang import (Class, Object, Void, String, Long) +from java.lang import RuntimeException + +from org.python.compiler.custom_proxymaker import MiniClampMaker + +import sys +sys.path.append('tests/python/custom_proxymaker/') +from clamp import ClampMethod + +class MethodSignatures(Object): + __proxymaker__ = MiniClampMaker + __java_package__ = 'custom_proxymaker.tests' + + def throwsException(self): + pass + throwsException._clamp = ClampMethod('throwsException', Void.TYPE, [], [RuntimeException], {}, [{}]) + + def returnsLong(self): + return 2 + returnsLong._clamp = ClampMethod('returnsLong', Long.TYPE, [], [], {}, [{}]) + + def returnsObject(self): + return Object() + returnsObject._clamp = ClampMethod('returnsObject', Object, [], [], {}, [{}]) + + def returnsArray(self): + return [1,2,3] + returnsArray._clamp = ClampMethod('returnsArray', Class.forName('[J'), [], [], {}, [{}]) + + def returnsArrayObj(self): + return [1,2,3] + returnsArrayObj._clamp = ClampMethod('returnsArrayObj', Class.forName('[Ljava.lang.Object;'), [], [], {}, [{}]) + + def acceptsString(self, arg): + pass + acceptsString._clamp = ClampMethod('acceptsString', Void.TYPE, [String], [], {}, [{}]) + + def acceptsArray(self, arg): + pass + acceptsArray._clamp = ClampMethod('acceptsArray', Void.TYPE, [Class.forName('[J')], [], {}, [{}]) + \ No newline at end of file -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Thu Dec 27 05:31:45 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Thu, 27 Dec 2012 05:31:45 +0100 (CET) Subject: [Jython-checkins] =?utf-8?q?jython=3A_Switched_to_CPython=27s_Ele?= =?utf-8?q?mentTree=2Epy_since_our_workaround_no_longer_appears?= Message-ID: <3YWymx05WtzQkQ@mail.python.org> http://hg.python.org/jython/rev/570786c5ba8f changeset: 6918:570786c5ba8f user: Frank Wierzbicki date: Wed Dec 26 20:31:25 2012 -0800 summary: Switched to CPython's ElementTree.py since our workaround no longer appears necessary. See http://bugs.jython.org/issue1479 and http://bugs.python.org/issue7334 files: Lib/xml/etree/ElementTree.py | 1273 --------------------- Lib/xml/etree/cElementTree.py | 15 +- 2 files changed, 8 insertions(+), 1280 deletions(-) diff --git a/Lib/xml/etree/ElementTree.py b/Lib/xml/etree/ElementTree.py deleted file mode 100644 --- a/Lib/xml/etree/ElementTree.py +++ /dev/null @@ -1,1273 +0,0 @@ -# -# ElementTree -# $Id: ElementTree.py 2326 2005-03-17 07:45:21Z fredrik $ -# -# light-weight XML support for Python 1.5.2 and later. -# -# history: -# 2001-10-20 fl created (from various sources) -# 2001-11-01 fl return root from parse method -# 2002-02-16 fl sort attributes in lexical order -# 2002-04-06 fl TreeBuilder refactoring, added PythonDoc markup -# 2002-05-01 fl finished TreeBuilder refactoring -# 2002-07-14 fl added basic namespace support to ElementTree.write -# 2002-07-25 fl added QName attribute support -# 2002-10-20 fl fixed encoding in write -# 2002-11-24 fl changed default encoding to ascii; fixed attribute encoding -# 2002-11-27 fl accept file objects or file names for parse/write -# 2002-12-04 fl moved XMLTreeBuilder back to this module -# 2003-01-11 fl fixed entity encoding glitch for us-ascii -# 2003-02-13 fl added XML literal factory -# 2003-02-21 fl added ProcessingInstruction/PI factory -# 2003-05-11 fl added tostring/fromstring helpers -# 2003-05-26 fl added ElementPath support -# 2003-07-05 fl added makeelement factory method -# 2003-07-28 fl added more well-known namespace prefixes -# 2003-08-15 fl fixed typo in ElementTree.findtext (Thomas Dartsch) -# 2003-09-04 fl fall back on emulator if ElementPath is not installed -# 2003-10-31 fl markup updates -# 2003-11-15 fl fixed nested namespace bug -# 2004-03-28 fl added XMLID helper -# 2004-06-02 fl added default support to findtext -# 2004-06-08 fl fixed encoding of non-ascii element/attribute names -# 2004-08-23 fl take advantage of post-2.1 expat features -# 2005-02-01 fl added iterparse implementation -# 2005-03-02 fl fixed iterparse support for pre-2.2 versions -# -# Copyright (c) 1999-2005 by Fredrik Lundh. All rights reserved. -# -# fredrik at pythonware.com -# http://www.pythonware.com -# -# -------------------------------------------------------------------- -# The ElementTree toolkit is -# -# Copyright (c) 1999-2005 by Fredrik Lundh -# -# By obtaining, using, and/or copying this software and/or its -# associated documentation, you agree that you have read, understood, -# and will comply with the following terms and conditions: -# -# Permission to use, copy, modify, and distribute this software and -# its associated documentation for any purpose and without fee is -# hereby granted, provided that the above copyright notice appears in -# all copies, and that both that copyright notice and this permission -# notice appear in supporting documentation, and that the name of -# Secret Labs AB or the author not be used in advertising or publicity -# pertaining to distribution of the software without specific, written -# prior permission. -# -# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD -# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT- -# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR -# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY -# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, -# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS -# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE -# OF THIS SOFTWARE. -# -------------------------------------------------------------------- - -# Licensed to PSF under a Contributor Agreement. -# See http://www.python.org/2.4/license for licensing details. - -__all__ = [ - # public symbols - "Comment", - "dump", - "Element", "ElementTree", - "fromstring", - "iselement", "iterparse", - "parse", - "PI", "ProcessingInstruction", - "QName", - "SubElement", - "tostring", - "TreeBuilder", - "VERSION", "XML", - "XMLParser", "XMLTreeBuilder", - ] - -## -# The Element type is a flexible container object, designed to -# store hierarchical data structures in memory. The type can be -# described as a cross between a list and a dictionary. -#

-# Each element has a number of properties associated with it: -#

    -#
  • a tag. This is a string identifying what kind of data -# this element represents (the element type, in other words).
  • -#
  • a number of attributes, stored in a Python dictionary.
  • -#
  • a text string.
  • -#
  • an optional tail string.
  • -#
  • a number of child elements, stored in a Python sequence
  • -#
-# -# To create an element instance, use the {@link #Element} or {@link -# #SubElement} factory functions. -#

-# The {@link #ElementTree} class can be used to wrap an element -# structure, and convert it from and to XML. -## - -import string, sys, re - -class _SimpleElementPath: - # emulate pre-1.2 find/findtext/findall behaviour - def find(self, element, tag): - for elem in element: - if elem.tag == tag: - return elem - return None - def findtext(self, element, tag, default=None): - for elem in element: - if elem.tag == tag: - return elem.text or "" - return default - def findall(self, element, tag): - if tag[:3] == ".//": - return element.getiterator(tag[3:]) - result = [] - for elem in element: - if elem.tag == tag: - result.append(elem) - return result - -try: - import ElementPath -except ImportError: - # FIXME: issue warning in this case? - ElementPath = _SimpleElementPath() - -# TODO: add support for custom namespace resolvers/default namespaces -# TODO: add improved support for incremental parsing - -VERSION = "1.2.6" - -## -# Internal element class. This class defines the Element interface, -# and provides a reference implementation of this interface. -#

-# You should not create instances of this class directly. Use the -# appropriate factory functions instead, such as {@link #Element} -# and {@link #SubElement}. -# -# @see Element -# @see SubElement -# @see Comment -# @see ProcessingInstruction - -class _ElementInterface: - # text...tail - - ## - # (Attribute) Element tag. - - tag = None - - ## - # (Attribute) Element attribute dictionary. Where possible, use - # {@link #_ElementInterface.get}, - # {@link #_ElementInterface.set}, - # {@link #_ElementInterface.keys}, and - # {@link #_ElementInterface.items} to access - # element attributes. - - attrib = None - - ## - # (Attribute) Text before first subelement. This is either a - # string or the value None, if there was no text. - - text = None - - ## - # (Attribute) Text after this element's end tag, but before the - # next sibling element's start tag. This is either a string or - # the value None, if there was no text. - - tail = None # text after end tag, if any - - def __init__(self, tag, attrib): - self.tag = tag - self.attrib = attrib - self._children = [] - - def __repr__(self): - return "" % (self.tag, id(self)) - - ## - # Creates a new element object of the same type as this element. - # - # @param tag Element tag. - # @param attrib Element attributes, given as a dictionary. - # @return A new element instance. - - def makeelement(self, tag, attrib): - return Element(tag, attrib) - - ## - # Returns the number of subelements. - # - # @return The number of subelements. - - def __len__(self): - return len(self._children) - - ## - # Returns the given subelement. - # - # @param index What subelement to return. - # @return The given subelement. - # @exception IndexError If the given element does not exist. - - def __getitem__(self, index): - return self._children[index] - - ## - # Replaces the given subelement. - # - # @param index What subelement to replace. - # @param element The new element value. - # @exception IndexError If the given element does not exist. - # @exception AssertionError If element is not a valid object. - - def __setitem__(self, index, element): - assert iselement(element) - self._children[index] = element - - ## - # Deletes the given subelement. - # - # @param index What subelement to delete. - # @exception IndexError If the given element does not exist. - - def __delitem__(self, index): - del self._children[index] - - ## - # Returns a list containing subelements in the given range. - # - # @param start The first subelement to return. - # @param stop The first subelement that shouldn't be returned. - # @return A sequence object containing subelements. - - def __getslice__(self, start, stop): - return self._children[start:stop] - - ## - # Replaces a number of subelements with elements from a sequence. - # - # @param start The first subelement to replace. - # @param stop The first subelement that shouldn't be replaced. - # @param elements A sequence object with zero or more elements. - # @exception AssertionError If a sequence member is not a valid object. - - def __setslice__(self, start, stop, elements): - for element in elements: - assert iselement(element) - self._children[start:stop] = list(elements) - - ## - # Deletes a number of subelements. - # - # @param start The first subelement to delete. - # @param stop The first subelement to leave in there. - - def __delslice__(self, start, stop): - del self._children[start:stop] - - ## - # Adds a subelement to the end of this element. - # - # @param element The element to add. - # @exception AssertionError If a sequence member is not a valid object. - - def append(self, element): - assert iselement(element) - self._children.append(element) - - ## - # Inserts a subelement at the given position in this element. - # - # @param index Where to insert the new subelement. - # @exception AssertionError If the element is not a valid object. - - def insert(self, index, element): - assert iselement(element) - self._children.insert(index, element) - - ## - # Removes a matching subelement. Unlike the find methods, - # this method compares elements based on identity, not on tag - # value or contents. - # - # @param element What element to remove. - # @exception ValueError If a matching element could not be found. - # @exception AssertionError If the element is not a valid object. - - def remove(self, element): - assert iselement(element) - self._children.remove(element) - - ## - # Returns all subelements. The elements are returned in document - # order. - # - # @return A list of subelements. - # @defreturn list of Element instances - - def getchildren(self): - return self._children - - ## - # Finds the first matching subelement, by tag name or path. - # - # @param path What element to look for. - # @return The first matching element, or None if no element was found. - # @defreturn Element or None - - def find(self, path): - return ElementPath.find(self, path) - - ## - # Finds text for the first matching subelement, by tag name or path. - # - # @param path What element to look for. - # @param default What to return if the element was not found. - # @return The text content of the first matching element, or the - # default value no element was found. Note that if the element - # has is found, but has no text content, this method returns an - # empty string. - # @defreturn string - - def findtext(self, path, default=None): - return ElementPath.findtext(self, path, default) - - ## - # Finds all matching subelements, by tag name or path. - # - # @param path What element to look for. - # @return A list or iterator containing all matching elements, - # in document order. - # @defreturn list of Element instances - - def findall(self, path): - return ElementPath.findall(self, path) - - ## - # Resets an element. This function removes all subelements, clears - # all attributes, and sets the text and tail attributes to None. - - def clear(self): - self.attrib.clear() - self._children = [] - self.text = self.tail = None - - ## - # Gets an element attribute. - # - # @param key What attribute to look for. - # @param default What to return if the attribute was not found. - # @return The attribute value, or the default value, if the - # attribute was not found. - # @defreturn string or None - - def get(self, key, default=None): - return self.attrib.get(key, default) - - ## - # Sets an element attribute. - # - # @param key What attribute to set. - # @param value The attribute value. - - def set(self, key, value): - self.attrib[key] = value - - ## - # Gets a list of attribute names. The names are returned in an - # arbitrary order (just like for an ordinary Python dictionary). - # - # @return A list of element attribute names. - # @defreturn list of strings - - def keys(self): - return self.attrib.keys() - - ## - # Gets element attributes, as a sequence. The attributes are - # returned in an arbitrary order. - # - # @return A list of (name, value) tuples for all attributes. - # @defreturn list of (string, string) tuples - - def items(self): - return self.attrib.items() - - ## - # Creates a tree iterator. The iterator loops over this element - # and all subelements, in document order, and returns all elements - # with a matching tag. - #

- # If the tree structure is modified during iteration, the result - # is undefined. - # - # @param tag What tags to look for (default is to return all elements). - # @return A list or iterator containing all the matching elements. - # @defreturn list or iterator - - def getiterator(self, tag=None): - nodes = [] - if tag == "*": - tag = None - if tag is None or self.tag == tag: - nodes.append(self) - for node in self._children: - nodes.extend(node.getiterator(tag)) - return nodes - -# compatibility -_Element = _ElementInterface - -## -# Element factory. This function returns an object implementing the -# standard Element interface. The exact class or type of that object -# is implementation dependent, but it will always be compatible with -# the {@link #_ElementInterface} class in this module. -#

-# The element name, attribute names, and attribute values can be -# either 8-bit ASCII strings or Unicode strings. -# -# @param tag The element name. -# @param attrib An optional dictionary, containing element attributes. -# @param **extra Additional attributes, given as keyword arguments. -# @return An element instance. -# @defreturn Element - -def Element(tag, attrib={}, **extra): - attrib = attrib.copy() - attrib.update(extra) - return _ElementInterface(tag, attrib) - -## -# Subelement factory. This function creates an element instance, and -# appends it to an existing element. -#

-# The element name, attribute names, and attribute values can be -# either 8-bit ASCII strings or Unicode strings. -# -# @param parent The parent element. -# @param tag The subelement name. -# @param attrib An optional dictionary, containing element attributes. -# @param **extra Additional attributes, given as keyword arguments. -# @return An element instance. -# @defreturn Element - -def SubElement(parent, tag, attrib={}, **extra): - attrib = attrib.copy() - attrib.update(extra) - element = parent.makeelement(tag, attrib) - parent.append(element) - return element - -## -# Comment element factory. This factory function creates a special -# element that will be serialized as an XML comment. -#

-# The comment string can be either an 8-bit ASCII string or a Unicode -# string. -# -# @param text A string containing the comment string. -# @return An element instance, representing a comment. -# @defreturn Element - -def Comment(text=None): - element = Element(Comment) - element.text = text - return element - -## -# PI element factory. This factory function creates a special element -# that will be serialized as an XML processing instruction. -# -# @param target A string containing the PI target. -# @param text A string containing the PI contents, if any. -# @return An element instance, representing a PI. -# @defreturn Element - -def ProcessingInstruction(target, text=None): - element = Element(ProcessingInstruction) - element.text = target - if text: - element.text = element.text + " " + text - return element - -PI = ProcessingInstruction - -## -# QName wrapper. This can be used to wrap a QName attribute value, in -# order to get proper namespace handling on output. -# -# @param text A string containing the QName value, in the form {uri}local, -# or, if the tag argument is given, the URI part of a QName. -# @param tag Optional tag. If given, the first argument is interpreted as -# an URI, and this argument is interpreted as a local name. -# @return An opaque object, representing the QName. - -class QName: - def __init__(self, text_or_uri, tag=None): - if tag: - text_or_uri = "{%s}%s" % (text_or_uri, tag) - self.text = text_or_uri - def __str__(self): - return self.text - def __hash__(self): - return hash(self.text) - def __cmp__(self, other): - if isinstance(other, QName): - return cmp(self.text, other.text) - return cmp(self.text, other) - -## -# ElementTree wrapper class. This class represents an entire element -# hierarchy, and adds some extra support for serialization to and from -# standard XML. -# -# @param element Optional root element. -# @keyparam file Optional file handle or name. If given, the -# tree is initialized with the contents of this XML file. - -class ElementTree: - - def __init__(self, element=None, file=None): - assert element is None or iselement(element) - self._root = element # first node - if file: - self.parse(file) - - ## - # Gets the root element for this tree. - # - # @return An element instance. - # @defreturn Element - - def getroot(self): - return self._root - - ## - # Replaces the root element for this tree. This discards the - # current contents of the tree, and replaces it with the given - # element. Use with care. - # - # @param element An element instance. - - def _setroot(self, element): - assert iselement(element) - self._root = element - - ## - # Loads an external XML document into this element tree. - # - # @param source A file name or file object. - # @param parser An optional parser instance. If not given, the - # standard {@link XMLTreeBuilder} parser is used. - # @return The document root element. - # @defreturn Element - - def parse(self, source, parser=None): - managed_file = not hasattr(source, "read") - if managed_file: - source = open(source, "rb") - try: - if not parser: - parser = XMLTreeBuilder() - while 1: - data = source.read(32768) - if not data: - break - parser.feed(data) - self._root = parser.close() - return self._root - finally: - if managed_file: - source.close() - - ## - # Creates a tree iterator for the root element. The iterator loops - # over all elements in this tree, in document order. - # - # @param tag What tags to look for (default is to return all elements) - # @return An iterator. - # @defreturn iterator - - def getiterator(self, tag=None): - assert self._root is not None - return self._root.getiterator(tag) - - ## - # Finds the first toplevel element with given tag. - # Same as getroot().find(path). - # - # @param path What element to look for. - # @return The first matching element, or None if no element was found. - # @defreturn Element or None - - def find(self, path): - assert self._root is not None - if path[:1] == "/": - path = "." + path - return self._root.find(path) - - ## - # Finds the element text for the first toplevel element with given - # tag. Same as getroot().findtext(path). - # - # @param path What toplevel element to look for. - # @param default What to return if the element was not found. - # @return The text content of the first matching element, or the - # default value no element was found. Note that if the element - # has is found, but has no text content, this method returns an - # empty string. - # @defreturn string - - def findtext(self, path, default=None): - assert self._root is not None - if path[:1] == "/": - path = "." + path - return self._root.findtext(path, default) - - ## - # Finds all toplevel elements with the given tag. - # Same as getroot().findall(path). - # - # @param path What element to look for. - # @return A list or iterator containing all matching elements, - # in document order. - # @defreturn list of Element instances - - def findall(self, path): - assert self._root is not None - if path[:1] == "/": - path = "." + path - return self._root.findall(path) - - ## - # Writes the element tree to a file, as XML. - # - # @param file A file name, or a file object opened for writing. - # @param encoding Optional output encoding (default is US-ASCII). - - def write(self, file, encoding="us-ascii"): - assert self._root is not None - managed_file = not hasattr(file, "write") - if managed_file: - file = open(file, "wb") - try: - if not encoding: - encoding = "us-ascii" - elif encoding != "utf-8" and encoding != "us-ascii": - file.write("\n" % encoding) - self._write(file, self._root, encoding, {}) - finally: - if managed_file: - file.close() - - def _write(self, file, node, encoding, namespaces): - # write XML to file - tag = node.tag - if tag is Comment: - file.write("" % _escape_cdata(node.text, encoding)) - elif tag is ProcessingInstruction: - file.write("" % _escape_cdata(node.text, encoding)) - else: - items = node.items() - xmlns_items = [] # new namespaces in this scope - try: - if isinstance(tag, QName) or tag[:1] == "{": - tag, xmlns = fixtag(tag, namespaces) - if xmlns: xmlns_items.append(xmlns) - except TypeError: - _raise_serialization_error(tag) - file.write("<" + _encode(tag, encoding)) - if items or xmlns_items: - items.sort() # lexical order - for k, v in items: - try: - if isinstance(k, QName) or k[:1] == "{": - k, xmlns = fixtag(k, namespaces) - if xmlns: xmlns_items.append(xmlns) - except TypeError: - _raise_serialization_error(k) - try: - if isinstance(v, QName): - v, xmlns = fixtag(v, namespaces) - if xmlns: xmlns_items.append(xmlns) - except TypeError: - _raise_serialization_error(v) - file.write(" %s=\"%s\"" % (_encode(k, encoding), - _escape_attrib(v, encoding))) - for k, v in xmlns_items: - file.write(" %s=\"%s\"" % (_encode(k, encoding), - _escape_attrib(v, encoding))) - if node.text or len(node): - file.write(">") - if node.text: - file.write(_escape_cdata(node.text, encoding)) - for n in node: - self._write(file, n, encoding, namespaces) - file.write("") - else: - file.write(" />") - for k, v in xmlns_items: - del namespaces[v] - if node.tail: - file.write(_escape_cdata(node.tail, encoding)) - -# -------------------------------------------------------------------- -# helpers - -## -# Checks if an object appears to be a valid element object. -# -# @param An element instance. -# @return A true value if this is an element object. -# @defreturn flag - -def iselement(element): - # FIXME: not sure about this; might be a better idea to look - # for tag/attrib/text attributes - return isinstance(element, _ElementInterface) or hasattr(element, "tag") - -## -# Writes an element tree or element structure to sys.stdout. This -# function should be used for debugging only. -#

-# The exact output format is implementation dependent. In this -# version, it's written as an ordinary XML file. -# -# @param elem An element tree or an individual element. - -def dump(elem): - # debugging - if not isinstance(elem, ElementTree): - elem = ElementTree(elem) - elem.write(sys.stdout) - tail = elem.getroot().tail - if not tail or tail[-1] != "\n": - sys.stdout.write("\n") - -def _encode(s, encoding): - try: - return s.encode(encoding) - except AttributeError: - return s # 1.5.2: assume the string uses the right encoding - -if sys.version[:3] == "1.5": - _escape = re.compile(r"[&<>\"\x80-\xff]+") # 1.5.2 -else: - _escape = re.compile(eval(r'u"[&<>\"\u0080-\uffff]+"')) - -_escape_map = { - "&": "&", - "<": "<", - ">": ">", - '"': """, -} - -_namespace_map = { - # "well-known" namespace prefixes - "http://www.w3.org/XML/1998/namespace": "xml", - "http://www.w3.org/1999/xhtml": "html", - "http://www.w3.org/1999/02/22-rdf-syntax-ns#": "rdf", - "http://schemas.xmlsoap.org/wsdl/": "wsdl", -} - -def _raise_serialization_error(text): - raise TypeError( - "cannot serialize %r (type %s)" % (text, type(text).__name__) - ) - -def _encode_entity(text, pattern=_escape): - # map reserved and non-ascii characters to numerical entities - def escape_entities(m, map=_escape_map): - out = [] - append = out.append - for char in m.group(): - text = map.get(char) - if text is None: - text = "&#%d;" % ord(char) - append(text) - return string.join(out, "") - try: - return _encode(pattern.sub(escape_entities, text), "ascii") - except TypeError: - _raise_serialization_error(text) - -# -# the following functions assume an ascii-compatible encoding -# (or "utf-16") - -def _escape_cdata(text, encoding=None, replace=string.replace): - # escape character data - try: - if encoding: - try: - text = _encode(text, encoding) - except UnicodeError: - return _encode_entity(text) - text = replace(text, "&", "&") - text = replace(text, "<", "<") - text = replace(text, ">", ">") - return text - except (TypeError, AttributeError): - _raise_serialization_error(text) - -def _escape_attrib(text, encoding=None, replace=string.replace): - # escape attribute value - try: - if encoding: - try: - text = _encode(text, encoding) - except UnicodeError: - return _encode_entity(text) - text = replace(text, "&", "&") - text = replace(text, "'", "'") # FIXME: overkill - text = replace(text, "\"", """) - text = replace(text, "<", "<") - text = replace(text, ">", ">") - return text - except (TypeError, AttributeError): - _raise_serialization_error(text) - -def fixtag(tag, namespaces): - # given a decorated tag (of the form {uri}tag), return prefixed - # tag and namespace declaration, if any - if isinstance(tag, QName): - tag = tag.text - namespace_uri, tag = string.split(tag[1:], "}", 1) - prefix = namespaces.get(namespace_uri) - if prefix is None: - prefix = _namespace_map.get(namespace_uri) - if prefix is None: - prefix = "ns%d" % len(namespaces) - namespaces[namespace_uri] = prefix - if prefix == "xml": - xmlns = None - else: - xmlns = ("xmlns:%s" % prefix, namespace_uri) - else: - xmlns = None - return "%s:%s" % (prefix, tag), xmlns - -## -# Parses an XML document into an element tree. -# -# @param source A filename or file object containing XML data. -# @param parser An optional parser instance. If not given, the -# standard {@link XMLTreeBuilder} parser is used. -# @return An ElementTree instance - -def parse(source, parser=None): - tree = ElementTree() - tree.parse(source, parser) - return tree - -## -# Parses an XML document into an element tree incrementally, and reports -# what's going on to the user. -# -# @param source A filename or file object containing XML data. -# @param events A list of events to report back. If omitted, only "end" -# events are reported. -# @return A (event, elem) iterator. - -class iterparse: - - def __init__(self, source, events=None): - self._managed_file = not hasattr(source, "read") - if self._managed_file: - source = open(source, "rb") - self._file = source - self._events = [] - self._index = 0 - self.root = self._root = None - self._parser = XMLTreeBuilder() - # wire up the parser for event reporting - parser = self._parser._parser - append = self._events.append - if events is None: - events = ["end"] - for event in events: - if event == "start": - try: - parser.ordered_attributes = 1 - parser.specified_attributes = 1 - def handler(tag, attrib_in, event=event, append=append, - start=self._parser._start_list): - append((event, start(tag, attrib_in))) - parser.StartElementHandler = handler - except AttributeError: - def handler(tag, attrib_in, event=event, append=append, - start=self._parser._start): - append((event, start(tag, attrib_in))) - parser.StartElementHandler = handler - elif event == "end": - def handler(tag, event=event, append=append, - end=self._parser._end): - append((event, end(tag))) - parser.EndElementHandler = handler - elif event == "start-ns": - def handler(prefix, uri, event=event, append=append): - try: - uri = _encode(uri, "ascii") - except UnicodeError: - pass - append((event, (prefix or "", uri))) - parser.StartNamespaceDeclHandler = handler - elif event == "end-ns": - def handler(prefix, event=event, append=append): - append((event, None)) - parser.EndNamespaceDeclHandler = handler - - def next(self): - while 1: - try: - item = self._events[self._index] - except IndexError: - if self._parser is None: - self.root = self._root - try: - raise StopIteration - except NameError: - raise IndexError - # load event buffer - del self._events[:] - self._index = 0 - data = self._file.read(16384) - if data: - self._parser.feed(data) - else: - self._root = self._parser.close() - self._parser = None - if self._managed_file: - self._file.close() - else: - self._index = self._index + 1 - return item - - try: - iter - def __iter__(self): - return self - except NameError: - def __getitem__(self, index): - return self.next() - -## -# Parses an XML document from a string constant. This function can -# be used to embed "XML literals" in Python code. -# -# @param source A string containing XML data. -# @return An Element instance. -# @defreturn Element - -def XML(text): - parser = XMLTreeBuilder() - parser.feed(text) - return parser.close() - -## -# Parses an XML document from a string constant, and also returns -# a dictionary which maps from element id:s to elements. -# -# @param source A string containing XML data. -# @return A tuple containing an Element instance and a dictionary. -# @defreturn (Element, dictionary) - -def XMLID(text): - parser = XMLTreeBuilder() - parser.feed(text) - tree = parser.close() - ids = {} - for elem in tree.getiterator(): - id = elem.get("id") - if id: - ids[id] = elem - return tree, ids - -## -# Parses an XML document from a string constant. Same as {@link #XML}. -# -# @def fromstring(text) -# @param source A string containing XML data. -# @return An Element instance. -# @defreturn Element - -fromstring = XML - -## -# Generates a string representation of an XML element, including all -# subelements. -# -# @param element An Element instance. -# @return An encoded string containing the XML data. -# @defreturn string - -def tostring(element, encoding=None): - class dummy: - pass - data = [] - file = dummy() - file.write = data.append - ElementTree(element).write(file, encoding) - return string.join(data, "") - -## -# Generic element structure builder. This builder converts a sequence -# of {@link #TreeBuilder.start}, {@link #TreeBuilder.data}, and {@link -# #TreeBuilder.end} method calls to a well-formed element structure. -#

-# You can use this class to build an element structure using a custom XML -# parser, or a parser for some other XML-like format. -# -# @param element_factory Optional element factory. This factory -# is called to create new Element instances, as necessary. - -class TreeBuilder: - - def __init__(self, element_factory=None): - self._data = [] # data collector - self._elem = [] # element stack - self._last = None # last element - self._tail = None # true if we're after an end tag - if element_factory is None: - element_factory = _ElementInterface - self._factory = element_factory - - ## - # Flushes the parser buffers, and returns the toplevel documen - # element. - # - # @return An Element instance. - # @defreturn Element - - def close(self): - assert len(self._elem) == 0, "missing end tags" - assert self._last != None, "missing toplevel element" - return self._last - - def _flush(self): - if self._data: - if self._last is not None: - text = string.join(self._data, "") - if self._tail: - assert self._last.tail is None, "internal error (tail)" - self._last.tail = text - else: - assert self._last.text is None, "internal error (text)" - self._last.text = text - self._data = [] - - ## - # Adds text to the current element. - # - # @param data A string. This should be either an 8-bit string - # containing ASCII text, or a Unicode string. - - def data(self, data): - self._data.append(data) - - ## - # Opens a new element. - # - # @param tag The element name. - # @param attrib A dictionary containing element attributes. - # @return The opened element. - # @defreturn Element - - def start(self, tag, attrs): - self._flush() - self._last = elem = self._factory(tag, attrs) - if self._elem: - self._elem[-1].append(elem) - self._elem.append(elem) - self._tail = 0 - return elem - - ## - # Closes the current element. - # - # @param tag The element name. - # @return The closed element. - # @defreturn Element - - def end(self, tag): - self._flush() - self._last = self._elem.pop() - assert self._last.tag == tag,\ - "end tag mismatch (expected %s, got %s)" % ( - self._last.tag, tag) - self._tail = 1 - return self._last - -## -# Element structure builder for XML source data, based on the -# expat parser. -# -# @keyparam target Target object. If omitted, the builder uses an -# instance of the standard {@link #TreeBuilder} class. -# @keyparam html Predefine HTML entities. This flag is not supported -# by the current implementation. -# @see #ElementTree -# @see #TreeBuilder - -class XMLTreeBuilder: - - def __init__(self, html=0, target=None): - try: - from xml.parsers import expat - except ImportError: - raise ImportError( - "No module named expat; use SimpleXMLTreeBuilder instead" - ) - self._parser = parser = expat.ParserCreate(None, "}") - if target is None: - target = TreeBuilder() - self._target = target - self._names = {} # name memo cache - # callbacks - parser.DefaultHandlerExpand = self._default - parser.StartElementHandler = self._start - parser.EndElementHandler = self._end - parser.CharacterDataHandler = self._data - # let expat do the buffering, if supported - try: - self._parser.buffer_text = 1 - except AttributeError: - pass - # use new-style attribute handling, if supported - try: - self._parser.ordered_attributes = 1 - self._parser.specified_attributes = 1 - parser.StartElementHandler = self._start_list - except AttributeError: - pass - encoding = None - if not parser.returns_unicode: - encoding = "utf-8" - # target.xml(encoding, None) - self._doctype = None - self.entity = {} - - def _fixtext(self, text): - # convert text string to ascii, if possible - try: - return _encode(text, "ascii") - except UnicodeError: - return text - - def _fixname(self, key): - # expand qname, and convert name string to ascii, if possible - try: - name = self._names[key] - except KeyError: - name = key - if "}" in name: - name = "{" + name - self._names[key] = name = self._fixtext(name) - return name - - def _start(self, tag, attrib_in): - fixname = self._fixname - tag = fixname(tag) - attrib = {} - for key, value in attrib_in.items(): - attrib[fixname(key)] = self._fixtext(value) - return self._target.start(tag, attrib) - - def _start_list(self, tag, attrib_in): - fixname = self._fixname - tag = fixname(tag) - attrib = {} - if attrib_in: - for i in range(0, len(attrib_in), 2): - attrib[fixname(attrib_in[i])] = self._fixtext(attrib_in[i+1]) - return self._target.start(tag, attrib) - - def _data(self, text): - return self._target.data(self._fixtext(text)) - - def _end(self, tag): - return self._target.end(self._fixname(tag)) - - def _default(self, text): - prefix = text[:1] - if prefix == "&": - # deal with undefined entities - try: - self._target.data(self.entity[text[1:-1]]) - except KeyError: - from xml.parsers import expat - raise expat.error( - "undefined entity %s: line %d, column %d" % - (text, self._parser.ErrorLineNumber, - self._parser.ErrorColumnNumber) - ) - elif prefix == "<" and text[:9] == "": - self._doctype = None - return - text = string.strip(text) - if not text: - return - self._doctype.append(text) - n = len(self._doctype) - if n > 2: - type = self._doctype[1] - if type == "PUBLIC" and n == 4: - name, type, pubid, system = self._doctype - elif type == "SYSTEM" and n == 3: - name, type, system = self._doctype - pubid = None - else: - return - if pubid: - pubid = pubid[1:-1] - self.doctype(name, pubid, system[1:-1]) - self._doctype = None - - ## - # Handles a doctype declaration. - # - # @param name Doctype name. - # @param pubid Public identifier. - # @param system System identifier. - - def doctype(self, name, pubid, system): - pass - - ## - # Feeds data to the parser. - # - # @param data Encoded data. - - def feed(self, data): - self._parser.Parse(data, 0) - - ## - # Finishes feeding data to the parser. - # - # @return An element structure. - # @defreturn Element - - def close(self): - self._parser.Parse("", 1) # end of data - tree = self._target.close() - del self._target, self._parser # get rid of circular references - return tree - -# compatibility -XMLParser = XMLTreeBuilder diff --git a/Lib/xml/etree/cElementTree.py b/Lib/xml/etree/cElementTree.py --- a/Lib/xml/etree/cElementTree.py +++ b/Lib/xml/etree/cElementTree.py @@ -1,10 +1,11 @@ # make an exact copy of ElementTree's namespace here to support even # private API usage from xml.etree.ElementTree import ( - Comment, Element, ElementPath, ElementTree, PI, ProcessingInstruction, - QName, SubElement, TreeBuilder, VERSION, XML, XMLID, XMLParser, - XMLTreeBuilder, _Element, _ElementInterface, _SimpleElementPath, - __all__, __doc__, __file__, __name__, _encode, _encode_entity, - _escape, _escape_attrib, _escape_cdata, _escape_map, _namespace_map, - _raise_serialization_error, dump, fixtag, fromstring, iselement, - iterparse, parse, re, string, sys, tostring) + Comment, Element, ElementPath, ElementTree, PI, ParseError, + ProcessingInstruction, QName, SubElement, TreeBuilder, VERSION, XML, XMLID, + XMLParser, XMLTreeBuilder, _Element, _ElementInterface, _SimpleElementPath, + __all__, __doc__, __file__, __name__, __package__, _encode, + _escape_attrib, _escape_cdata, _namespace_map, + _raise_serialization_error, dump, fromstring, fromstringlist, + iselement, iterparse, parse, re, register_namespace, sys, tostring, + tostringlist) -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Thu Dec 27 21:07:13 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Thu, 27 Dec 2012 21:07:13 +0100 (CET) Subject: [Jython-checkins] =?utf-8?q?jython=3A_from=3A?= Message-ID: <3YXMXK6bVrzP0X@mail.python.org> http://hg.python.org/jython/rev/9b75982e2f22 changeset: 6919:9b75982e2f22 user: Frank Wierzbicki date: Thu Dec 27 11:57:19 2012 -0800 summary: from: http://hg.python.org/cpython/Lib/test/test_sort.py at 22db03646d9b files: Lib/test/test_sort.py | 204 ++++++++++++++--------------- 1 files changed, 98 insertions(+), 106 deletions(-) diff --git a/Lib/test/test_sort.py b/Lib/test/test_sort.py --- a/Lib/test/test_sort.py +++ b/Lib/test/test_sort.py @@ -1,7 +1,9 @@ -from test.test_support import verbose +from test import test_support import random -from UserList import UserList +import sys +import unittest +verbose = test_support.verbose nerrors = 0 def check(tag, expected, raw, compare=None): @@ -36,91 +38,89 @@ nerrors += 1 return -# Try a variety of sizes at and around powers of 2, and at powers of 10. -sizes = [0] -for power in range(1, 10): - n = 2 ** power - sizes.extend(range(n-1, n+2)) -sizes.extend([10, 100, 1000]) +class TestBase(unittest.TestCase): + def testStressfully(self): + # Try a variety of sizes at and around powers of 2, and at powers of 10. + sizes = [0] + for power in range(1, 10): + n = 2 ** power + sizes.extend(range(n-1, n+2)) + sizes.extend([10, 100, 1000]) -class Complains(object): - maybe_complain = True + class Complains(object): + maybe_complain = True - def __init__(self, i): - self.i = i + def __init__(self, i): + self.i = i - def __lt__(self, other): - if Complains.maybe_complain and random.random() < 0.001: + def __lt__(self, other): + if Complains.maybe_complain and random.random() < 0.001: + if verbose: + print " complaining at", self, other + raise RuntimeError + return self.i < other.i + + def __repr__(self): + return "Complains(%d)" % self.i + + class Stable(object): + def __init__(self, key, i): + self.key = key + self.index = i + + def __cmp__(self, other): + return cmp(self.key, other.key) + __hash__ = None # Silence Py3k warning + + def __repr__(self): + return "Stable(%d, %d)" % (self.key, self.index) + + for n in sizes: + x = range(n) if verbose: - print " complaining at", self, other - raise RuntimeError - return self.i < other.i + print "Testing size", n - def __repr__(self): - return "Complains(%d)" % self.i + s = x[:] + check("identity", x, s) -class Stable(object): - def __init__(self, key, i): - self.key = key - self.index = i + s = x[:] + s.reverse() + check("reversed", x, s) - def __cmp__(self, other): - return cmp(self.key, other.key) + s = x[:] + random.shuffle(s) + check("random permutation", x, s) - def __repr__(self): - return "Stable(%d, %d)" % (self.key, self.index) + y = x[:] + y.reverse() + s = x[:] + check("reversed via function", y, s, lambda a, b: cmp(b, a)) -for n in sizes: - x = range(n) - if verbose: - print "Testing size", n + if verbose: + print " Checking against an insane comparison function." + print " If the implementation isn't careful, this may segfault." + s = x[:] + s.sort(lambda a, b: int(random.random() * 3) - 1) + check("an insane function left some permutation", x, s) - s = x[:] - check("identity", x, s) + x = [Complains(i) for i in x] + s = x[:] + random.shuffle(s) + Complains.maybe_complain = True + it_complained = False + try: + s.sort() + except RuntimeError: + it_complained = True + if it_complained: + Complains.maybe_complain = False + check("exception during sort left some permutation", x, s) - s = x[:] - s.reverse() - check("reversed", x, s) - - s = x[:] - random.shuffle(s) - check("random permutation", x, s) - - y = x[:] - y.reverse() - s = x[:] - check("reversed via function", y, s, lambda a, b: cmp(b, a)) - - if verbose: - print " Checking against an insane comparison function." - print " If the implementation isn't careful, this may segfault." - s = x[:] - s.sort(lambda a, b: int(random.random() * 3) - 1) - check("an insane function left some permutation", x, s) - - x = [Complains(i) for i in x] - s = x[:] - random.shuffle(s) - Complains.maybe_complain = True - it_complained = False - try: - s.sort() - except RuntimeError: - it_complained = True - if it_complained: - Complains.maybe_complain = False - check("exception during sort left some permutation", x, s) - - s = [Stable(random.randrange(10), i) for i in xrange(n)] - augmented = [(e, e.index) for e in s] - augmented.sort() # forced stable because ties broken by index - x = [e for e, i in augmented] # a stable sort of s - check("stability", x, s) - - -import unittest -from test import test_support -import sys + s = [Stable(random.randrange(10), i) for i in xrange(n)] + augmented = [(e, e.index) for e in s] + augmented.sort() # forced stable because ties broken by index + x = [e for e, i in augmented] # a stable sort of s + check("stability", x, s) #============================================================================== @@ -185,7 +185,7 @@ def test_stability(self): data = [(random.randrange(100), i) for i in xrange(200)] copy = data[:] - data.sort(key=lambda (x,y): x) # sort on the random first field + data.sort(key=lambda x: x[0]) # sort on the random first field copy.sort() # sort using both fields self.assertEqual(data, copy) # should get the same result @@ -207,19 +207,16 @@ # Verify that the wrapper has been removed data = range(-2,2) dup = data[:] - self.assertRaises(ZeroDivisionError, data.sort, None, lambda x: 1/x) + self.assertRaises(ZeroDivisionError, data.sort, None, lambda x: 1 // x) self.assertEqual(data, dup) - # for jython, we have a different storage mechanism for this in our - # implementation of MergeState; given that this is likely to go away, - # this doesn't seem so important -# def test_key_with_mutation(self): -# data = range(10) -# def k(x): -# del data[:] -# data[:] = range(20) -# return x -# self.assertRaises(ValueError, data.sort, key=k) + def test_key_with_mutation(self): + data = range(10) + def k(x): + del data[:] + data[:] = range(20) + return x + self.assertRaises(ValueError, data.sort, key=k) def test_key_with_mutating_del(self): data = range(10) @@ -272,29 +269,24 @@ def test_main(verbose=None): test_classes = ( + TestBase, TestDecorateSortUndecorate, TestBugs, ) - # In the following test cases, class obj, which has function that changes - # the data upon which sort is invoked, is passed for "key" argument. - # It can not be checked if that function changes data as long as it is - # invoked(e.g. __del__ in SortKiller). so these are currently commented out. - del TestDecorateSortUndecorate.test_key_with_mutating_del - del TestDecorateSortUndecorate.test_key_with_mutating_del_and_exception - # + with test_support.check_py3k_warnings( + ("the cmp argument is not supported", DeprecationWarning)): + test_support.run_unittest(*test_classes) - test_support.run_unittest(*test_classes) - - # verify reference counting - if verbose and hasattr(sys, "gettotalrefcount"): - import gc - counts = [None] * 5 - for i in xrange(len(counts)): - test_support.run_unittest(*test_classes) - gc.collect() - counts[i] = sys.gettotalrefcount() - print counts + # verify reference counting + if verbose and hasattr(sys, "gettotalrefcount"): + import gc + counts = [None] * 5 + for i in xrange(len(counts)): + test_support.run_unittest(*test_classes) + gc.collect() + counts[i] = sys.gettotalrefcount() + print counts if __name__ == "__main__": test_main(verbose=True) -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Thu Dec 27 21:07:15 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Thu, 27 Dec 2012 21:07:15 +0100 (CET) Subject: [Jython-checkins] =?utf-8?q?jython=3A_Skipping_tests_that_where_p?= =?utf-8?q?reviously_skipped=2E?= Message-ID: <3YXMXM1c86zNyK@mail.python.org> http://hg.python.org/jython/rev/8bc3fb357491 changeset: 6920:8bc3fb357491 user: Frank Wierzbicki date: Thu Dec 27 12:07:08 2012 -0800 summary: Skipping tests that where previously skipped. files: Lib/test/test_sort.py | 19 +++++++++++++++++-- 1 files changed, 17 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_sort.py b/Lib/test/test_sort.py --- a/Lib/test/test_sort.py +++ b/Lib/test/test_sort.py @@ -39,6 +39,8 @@ return class TestBase(unittest.TestCase): + @unittest.skipIf(test_support.is_jython, + "FIXME: find the part that is too much for Jython.") def testStressfully(self): # Try a variety of sizes at and around powers of 2, and at powers of 10. sizes = [0] @@ -209,7 +211,12 @@ dup = data[:] self.assertRaises(ZeroDivisionError, data.sort, None, lambda x: 1 // x) self.assertEqual(data, dup) - + + # for jython, we have a different storage mechanism for this in our + # implementation of MergeState; given that this is likely to go away, + # this doesn't seem so important + @unittest.skipIf(test_support.is_jython, + "Jython has a different implementation of MergeSort") def test_key_with_mutation(self): data = range(10) def k(x): @@ -217,7 +224,11 @@ data[:] = range(20) return x self.assertRaises(ValueError, data.sort, key=k) - + + # The function passed to the "key" argument changes the data upon which + # sort is invoked. It can not be checked if that function changes data as + # long as it is invoked(e.g. __del__ in SortKiller). so skipping for now. + @unittest.skipIf(test_support.is_jython, "Doesn't work for Jython") def test_key_with_mutating_del(self): data = range(10) class SortKiller(object): @@ -228,6 +239,10 @@ data[:] = range(20) self.assertRaises(ValueError, data.sort, key=SortKiller) + # The function passed to the "key" argument changes the data upon which + # sort is invoked. It can not be checked if that function changes data as + # long as it is invoked(e.g. __del__ in SortKiller). so skipping for now. + @unittest.skipIf(test_support.is_jython, "Doesn't work for Jython") def test_key_with_mutating_del_and_exception(self): data = range(10) ## dup = data[:] -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Mon Dec 31 15:22:06 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Mon, 31 Dec 2012 15:22:06 +0100 (CET) Subject: [Jython-checkins] =?utf-8?q?jython=3A_Skip_failing_range_tests_fo?= =?utf-8?q?r_now=2E?= Message-ID: <3YZghG3B54zSCC@mail.python.org> http://hg.python.org/jython/rev/6a9a29884e2f changeset: 6921:6a9a29884e2f user: Frank Wierzbicki date: Mon Dec 31 06:21:58 2012 -0800 summary: Skip failing range tests for now. files: Lib/test/test_xrange.py | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-) diff --git a/Lib/test/test_xrange.py b/Lib/test/test_xrange.py --- a/Lib/test/test_xrange.py +++ b/Lib/test/test_xrange.py @@ -1,6 +1,7 @@ # Python test set -- built-in functions import test.test_support, unittest +from test.test_support import is_jython import sys import pickle import itertools @@ -95,6 +96,7 @@ self.assertEqual(len(r), sys.maxint) self.assertRaises(OverflowError, xrange, -sys.maxint-1, sys.maxint, 2) + @unittest.skipIf(is_jython, "FIXME: pickling range not working in Jython") def test_pickling(self): testcases = [(13,), (0, 11), (-22, 10), (20, 3, -1), (13, 21, 3), (-2, 2, 2)] @@ -104,6 +106,7 @@ self.assertEqual(list(pickle.loads(pickle.dumps(r, proto))), list(r)) + @unittest.skipIf(is_jython, "FIXME: range iter not working in Jython") def test_range_iterators(self): # see issue 7298 limits = [base + jiggle -- Repository URL: http://hg.python.org/jython