File: _ioTest.java

package info (click to toggle)
jython 2.7.3%2Brepack1-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 62,820 kB
  • sloc: python: 641,384; java: 306,981; xml: 2,066; sh: 514; ansic: 126; makefile: 77
file content (324 lines) | stat: -rw-r--r-- 11,867 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
/* Copyright (c)2013 Jython Developers */
package org.python.modules._io;

import static org.junit.Assert.*;
import static org.junit.matchers.JUnitMatchers.*;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;

import org.junit.Before;
import org.junit.Test;
import org.python.core.Py;
import org.python.core.PyException;
import org.python.core.PyFile;
import org.python.core.PyObject;
import org.python.core.PyStringMap;
import org.python.core.PySystemState;
import org.python.core.io.RawIOBase;
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 {

    // Some file names to use
    private final String FILE1 = "$test_1_tmp";
    private final String FILE2 = "$test_2_tmp";
    private final String FILE3 = "$test_3_tmp";

    // We need the interpreter to be initialised for these tests.
    PySystemState systemState;
    PyStringMap dict;
    PythonInterpreter interp;

    /**
     * Initialisation called before each test.
     *
     * @throws java.lang.Exception
     */
    @Before
    public void setUp() throws Exception {
        // Initialise a Jython interpreter
        systemState = Py.getSystemState();
        dict = new PyStringMap();
        interp = new PythonInterpreter(dict, systemState);
    }

    /**
     * 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 = _jyio.UnsupportedOperation("Message from _ioTest");
        PyObject type = pye.type;
        String repr = type.toString();
        assertEquals("Class name", "<class '_io.UnsupportedOperation'>", 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(_jyio.UnsupportedOperation, e.type);
        }
    }

    /** Check <code>PyFile().fileno()</code> is acceptable to <code>_io.open()</code> */
    @Test
    public void openPyFileByFileno() throws IOException {
        PySystemState sys = Py.getSystemState();
        PyFile file = new PyFile(FILE1, "w", 1);
        openByFilenoTest(file, "wb");
    }

    /** Check <code>PyFile(OutputStream).fileno()</code> is acceptable to <code>_io.open()</code> */
    @Test
    public void openPyFileOStreamByFileno() throws IOException {
        PySystemState sys = Py.getSystemState();
        OutputStream ostream = new FileOutputStream(FILE1);
        PyFile file = new PyFile(ostream);
        openByFilenoTest(file, "wb");
    }

    /** Check <code>sys.stdin.fileno()</code> is acceptable to <code>_io.open()</code> */
    @Test
    public void openStdinByFileno() throws IOException {
        PySystemState sys = Py.getSystemState();
        openByFilenoTest(sys.stdin, "rb");
    }

    /** Check <code>sys.stdout.fileno()</code> is acceptable to <code>_io.open()</code> */
    @Test
    public void openStdoutByFileno() throws IOException {
        PySystemState sys = Py.getSystemState();
        openByFilenoTest(sys.stdout, "wb");
    }

    /** Check <code>sys.stderr.fileno()</code> is acceptable to <code>_io.open()</code> */
    @Test
    public void openStderrByFileno() throws IOException {
        PySystemState sys = Py.getSystemState();
        openByFilenoTest(sys.stderr, "wb");
    }

    /**
     * Test opening by a "file descriptor" obtained from another file or stream. The main purpose is
     * to test that the return from <code>fileno()</code> is acceptable to <code>_io.open()</code>.
     *
     * @param file anything with a "fileno" function
     * @param mode mode string "rb" etc.
     * @throws IOException
     */
    public void openByFilenoTest(PyObject file, String mode) throws IOException {
        PyObject pyfd = file.invoke("fileno");
        RawIOBase fd = (RawIOBase)pyfd.__tojava__(RawIOBase.class);
        PyObject[] args = new PyObject[] {pyfd, Py.newString(mode), Py.False};
        String[] kwds = {"closefd"};
        PyObject file2 = _jyio.open(args, kwds);
        file2.invoke("close");
    }

    /**
     * Test automatic closing of files when the interpreter finally exits. Done correctly, text
     * written to any kind of file-like object should be flushed to disk and the file closed when
     * the PySystemState is torn down, which happens during JVM exit. We don't here (can't?) test
     * through JVM shutdown, but we test it using the same mechanism that a JVM shutdown invokes.
     *
     * @throws IOException
     */
    @Test
    public void closeNeglectedFiles() throws IOException {

        // File names
        final String F = FILE1;     // Raw file
        final String FB = FILE2;    // Buffered file
        final String FT = FILE3;    // Test file

        String expText = "Line 1\nLine 2\nLine 3.";     // Note: all ascii, but with new lines
        byte[] expBytes = expText.getBytes();
        String escapedText = expText.replace("\n", "\\n");

        // The approach is to open and write files in Python, then bin the interpreter
        interp.exec("import io\n" + //
                "u = u'" + escapedText + "'\n" +    //
                "b = b'" + escapedText + "'\n"      //
        );

        // This should get us an io.FileIO (unbuffered binary file) called f
        interp.exec("f = io.open('" + F + "', 'wb', 0)");
        PyIOBase pyf = (PyIOBase)interp.get("f");
        assertNotNull(pyf);

        // This should get us an io.BufferedWriter (buffered binary file) called fb
        interp.exec("fb = io.open('" + FB + "', 'wb')");
        PyIOBase pyfb = (PyIOBase)interp.get("fb");
        assertNotNull(pyfb);

        // This should get us an io.TextIOWrapper (buffered text file) called ft
        interp.exec("ft = io.open('" + FT + "', 'w', encoding='ascii')");
        PyIOBase pyft = (PyIOBase)interp.get("ft");
        assertNotNull(pyft);

        // Write the bytes test material to each file but don't close it
        interp.exec("f.write(b)");
        interp.exec("fb.write(b)");
        interp.exec("ft.write(u)");

        // Now bin the interpreter. (Is there a more realistic way?)
        interp.cleanup();

        // Check file itself for closure using package-visible attribute
        assertTrue(pyf.__closed);
        assertTrue(pyfb.__closed);
        assertTrue(pyft.__closed);

        // If they were not closed properly not all bytes will reach the files.
        checkFileContent(F, expBytes, true);
        checkFileContent(FB, expBytes, true);

        // Expect that TextIOWrapper should have adjusted the line separator
        checkFileContent(FT, newlineFix(expText), true);
    }

    /**
     * Test automatic closing of PyFiles when the interpreter finally exits. This repeats
     * {@link #closeNeglectedFiles()} but for the py2k flavour of file.
     *
     * @throws IOException
     */
    @Test
    public void closeNeglectedPyFiles() throws IOException {

        // File names
        final String F = FILE1;     // Raw file
        final String FB = FILE2;    // Buffered file
        final String FT = FILE3;    // Test file

        String expText = "Line 1\nLine 2\nLine 3.";
        byte[] expBytes = expText.getBytes();
        String escapedText = expText.replace("\n", "\\n");

        // The approach is to open and write files in Python, then bin the interpreter
        interp.exec("import io\n" + //
                "u = u'" + escapedText + "'\n" +    //
                "b = b'" + escapedText + "'\n"      //
        );

        // This should get us an unbuffered binary PyFile called f
        interp.exec("f = open('" + F + "', 'wb', 0)");
        PyFile pyf = (PyFile)interp.get("f");
        assertNotNull(pyf);
        RawIOBase r = (RawIOBase)pyf.fileno().__tojava__(RawIOBase.class);

        // This should get us a buffered binary PyFile called fb
        interp.exec("fb = open('" + FB + "', 'wb')");
        PyFile pyfb = (PyFile)interp.get("fb");
        assertNotNull(pyfb);
        RawIOBase rb = (RawIOBase)pyfb.fileno().__tojava__(RawIOBase.class);

        // This should get us an buffered text PyFile called ft
        interp.exec("ft = open('" + FT + "', 'w')");
        PyFile pyft = (PyFile)interp.get("ft");
        assertNotNull(pyft);
        RawIOBase rt = (RawIOBase)pyft.fileno().__tojava__(RawIOBase.class);

        // Write the bytes test material to each file but don't close it
        interp.exec("f.write(b)");
        interp.exec("fb.write(b)");
        interp.exec("ft.write(u)");

        // Now bin the interpreter. (Is there a more realistic way?)
        interp.cleanup();

        // The PyFile itself is not closed but the underlying stream should be
        assertTrue(r.closed());
        assertTrue(rb.closed());
        assertTrue(rt.closed());

        // If they were not closed properly not all bytes will reach the files.
        checkFileContent(F, expBytes, true);
        checkFileContent(FB, expBytes, true);

        // Expect that TextIOWrapper should have adjusted the line separator
        checkFileContent(FT, newlineFix(expText), true);
    }

    /**
     * Check the file contains the bytes we expect and optionally <b>delete the file</b>. If it was
     * not closed properly (layers in the right order and a flush) not all bytes will have reached
     * the files.
     *
     * @param name of file
     * @param expBytes expected
     * @param delete the file if true
     * @throws IOException if cannot open/read
     */
    private static void checkFileContent(String name, byte[] expBytes, boolean delete)
            throws IOException {
        // Open and read
        byte[] r = new byte[2 * expBytes.length];
        File f = new File(name);
        FileInputStream in = new FileInputStream(f);
        int n = in.read(r);
        in.close();

        // Check as expected
        String msg = "Bytes read from " + name;
        assertEquals(msg, expBytes.length, n);
        byte[] resBytes = Arrays.copyOf(r, n);
        assertArrayEquals(msg, expBytes, resBytes);

        // Delete the file
        if (delete) {
            f.delete();
        }
    }

    /**
     * Replace "\n" characters by the system-defined newline sequence and return as bytes.
     *
     * @param expText to translate
     * @return result as bytes
     */
    private static byte[] newlineFix(String expText) {
        String newline = System.getProperty("line.separator");
        return expText.replace("\n", newline).getBytes();
    }
}