[Jython-checkins] jython (merge 2.5 -> default): merge w/2.5: Fixing issue 1754: changing the implementation of the modjy

alan.kennedy jython-checkins at python.org
Sun Nov 4 15:37:31 CET 2012


http://hg.python.org/jython/rev/7ebb51401f54
changeset:   6885:7ebb51401f54
parent:      6883:4b061dc82e97
parent:      6884:4c3155645812
user:        Alan Kennedy <alan at xhaus.com>
date:        Sun Nov 04 14:32:26 2012 +0000
summary:
  merge w/2.5: Fixing issue 1754: changing the implementation of the modjy sgi.input object

files:
  Lib/modjy/modjy_input.py                                   |  167 ++++++++++
  Lib/modjy/modjy_wsgi.py                                    |    5 +-
  tests/modjy/java/com/xhaus/modjy/ModjyTestPostData.java    |    4 +-
  tests/modjy/java/com/xhaus/modjy/ModjyTestWSGIStreams.java |   47 ++-
  tests/modjy/test_apps_dir/stream_tests.py                  |    8 +
  5 files changed, 223 insertions(+), 8 deletions(-)


diff --git a/Lib/modjy/modjy_input.py b/Lib/modjy/modjy_input.py
new file mode 100644
--- /dev/null
+++ b/Lib/modjy/modjy_input.py
@@ -0,0 +1,167 @@
+###
+#
+# Copyright Alan Kennedy.
+#
+# You may contact the copyright holder at this uri:
+#
+# http://www.xhaus.com/contact/modjy
+#
+# The licence under which this code is released is the Apache License v2.0.
+#
+# The terms and conditions of this license are listed in a file contained
+# in the distribution that also contained this file, under the name
+# LICENSE.txt.
+#
+# You may also read a copy of the license at the following web address.
+#
+# http://modjy.xhaus.com/LICENSE.txt
+#
+###
+
+#
+# This code adapted from the socket._fileobject class
+#
+
+import jarray
+
+class modjy_input_object(object):
+
+    def __init__(self, servlet_inputstream, bufsize=8192):
+        self.istream = servlet_inputstream
+        self.buffer_size = bufsize
+        self.buffer = ""
+
+    def istream_read(self, n):
+        data = jarray.zeros(n, 'b')
+        m = self.istream.read(data)
+        if m == -1: # indicates EOF has been reached, so we just return the empty string
+            return ""
+        elif m <= 0:
+            return ""
+        if m < n:
+            data = data[:m]
+        return data.tostring()
+
+    def read(self, size=-1):
+        data = self.buffer
+        if size < 0:
+            # Read until EOF
+            buffers = []
+            if data:
+                buffers.append(data)
+            self.buffer = ""
+            recv_size = self.buffer_size
+            while True:
+                data = self.istream_read(recv_size)
+                if not data:
+                    break
+                buffers.append(data)
+            return "".join(buffers)
+        else:
+            # Read until size bytes or EOF seen, whichever comes first
+            buf_len = len(data)
+            if buf_len >= size:
+                self.buffer = data[size:]
+                return data[:size]
+            buffers = []
+            if data:
+                buffers.append(data)
+            self.buffer = ""
+            while True:
+                left = size - buf_len
+                recv_size = max(self.buffer_size, left)
+                data = self.istream_read(recv_size)
+                if not data:
+                    break
+                buffers.append(data)
+                n = len(data)
+                if n >= left:
+                    self.buffer = data[left:]
+                    buffers[-1] = data[:left]
+                    break
+                buf_len += n
+            return "".join(buffers)
+
+    def readline(self, size=-1):
+        data = self.buffer
+        if size < 0:
+            # Read until \n or EOF, whichever comes first
+            nl = data.find('\n')
+            if nl >= 0:
+                nl += 1
+                self.buffer = data[nl:]
+                return data[:nl]
+            buffers = []
+            if data:
+                buffers.append(data)
+            self.buffer = ""
+            while True:
+                data = self.istream_read(self.buffer_size)
+                if not data:
+                    break
+                buffers.append(data)
+                nl = data.find('\n')
+                if nl >= 0:
+                    nl += 1
+                    self.buffer = data[nl:]
+                    buffers[-1] = data[:nl]
+                    break
+            return "".join(buffers)
+        else:
+            # Read until size bytes or \n or EOF seen, whichever comes first
+            nl = data.find('\n', 0, size)
+            if nl >= 0:
+                nl += 1
+                self.buffer = data[nl:]
+                return data[:nl]
+            buf_len = len(data)
+            if buf_len >= size:
+                self.buffer = data[size:]
+                return data[:size]
+            buffers = []
+            if data:
+                buffers.append(data)
+            self.buffer = ""
+            while True:
+                data = self.istream_read(self.buffer_size)
+                if not data:
+                    break
+                buffers.append(data)
+                left = size - buf_len
+                nl = data.find('\n', 0, left)
+                if nl >= 0:
+                    nl += 1
+                    self.buffer = data[nl:]
+                    buffers[-1] = data[:nl]
+                    break
+                n = len(data)
+                if n >= left:
+                    self.buffer = data[left:]
+                    buffers[-1] = data[:left]
+                    break
+                buf_len += n
+            return "".join(buffers)
+
+    def readlines(self, sizehint=0):
+        total = 0
+        list = []
+        while True:
+            line = self.readline()
+            if not line:
+                break
+            list.append(line)
+            total += len(line)
+            if sizehint and total >= sizehint:
+                break
+        return list
+
+    # Iterator protocols
+
+    def __iter__(self):
+        return self
+
+    def next(self):
+        line = self.readline()
+        if not line:
+            raise StopIteration
+        return line
diff --git a/Lib/modjy/modjy_wsgi.py b/Lib/modjy/modjy_wsgi.py
--- a/Lib/modjy/modjy_wsgi.py
+++ b/Lib/modjy/modjy_wsgi.py
@@ -28,6 +28,7 @@
     create_py_file = PyFile
 
 from modjy_exceptions import *
+from modjy_input import modjy_input_object
 
 server_name = "modjy"
 server_param_prefix = "%s.param" % server_name
@@ -125,8 +126,8 @@
 
     def set_wsgi_streams(self, req, resp, dict):
         try:
-            dict["wsgi.input"]  = create_py_file(req.getInputStream(), "rb")
-            dict["wsgi.errors"] =  create_py_file(System.err)
+            dict["wsgi.input"]  = modjy_input_object(req.getInputStream())
+            dict["wsgi.errors"] = create_py_file(System.err)
         except IOException, iox:
             raise ModjyIOException(iox)
 
diff --git a/tests/modjy/java/com/xhaus/modjy/ModjyTestPostData.java b/tests/modjy/java/com/xhaus/modjy/ModjyTestPostData.java
--- a/tests/modjy/java/com/xhaus/modjy/ModjyTestPostData.java
+++ b/tests/modjy/java/com/xhaus/modjy/ModjyTestPostData.java
@@ -27,7 +27,7 @@
         setAppFile("post_data_tests.py");
     }
 
-    public void doHeaderTest(String appName, String postData) throws Exception {
+    public void doPostTest(String appName, String postData) throws Exception {
         postDataTestSetUp();
         setMethod("POST");
         setAppName(appName);
@@ -40,7 +40,7 @@
     public void testPostDataLineEndsNotTranslated() throws Exception {
     	String testData = "this\r\ndata\r\ncontains\r\ncarriage\r\nreturns\r\n";
     	String expectedData = "'"+testData.replace("\r", "\\r").replace("\n", "\\n")+"'";
-        doHeaderTest("test_return_post_data", testData);
+        doPostTest("test_return_post_data", testData);
         assertEquals("Wrong post data returned >>" + getOutput() + "<< != >>"+expectedData+"<<", expectedData, getOutput());
     }
 
diff --git a/tests/modjy/java/com/xhaus/modjy/ModjyTestWSGIStreams.java b/tests/modjy/java/com/xhaus/modjy/ModjyTestWSGIStreams.java
--- a/tests/modjy/java/com/xhaus/modjy/ModjyTestWSGIStreams.java
+++ b/tests/modjy/java/com/xhaus/modjy/ModjyTestWSGIStreams.java
@@ -63,6 +63,14 @@
         doInputTest(appName, bodyContent, expectedContent, expectedLength, 0);
     }
 
+    protected String buildStringWithContents(int count, String contents) {
+    	StringBuilder builder = new StringBuilder();
+    	for (int i = 0 ; i < count ; i++) {
+            builder.append(contents);
+        };
+        return builder.toString();
+    }
+
     public void testEmptyInput() throws Exception {
         doInputTest("test_read_input_stream", "", "", 0);
     }
@@ -81,6 +89,14 @@
         }
     }
 
+    public void testAsciiInputWithReadSizeLargeInput() throws Exception {
+        String one_k = buildStringWithContents(1024, "X");
+        String testData = buildStringWithContents(64, one_k);
+        for (int i = 64; i <= 65536; i=i*2) {
+            doInputTest("test_read_input_stream", testData, testData, testData.length(), i);
+        }
+    }
+
     public void testAsciiInputReadline() throws Exception {
         doInputTest("test_readline_input_stream", "Hello\nWorld!\n", "Hello\n", 6);
         doInputTest("test_readline_input_stream", "Hello", "Hello", 5);
@@ -88,17 +104,40 @@
 
     public void testAsciiInputReadlineWithSize() throws Exception {
         // Let's test this: although PEP-333 says it's not supported, modjy can do it
-        doInputTest("test_readline_input_stream", "Hello\nWorld!\n", "Hello", 5, 5);
+        doInputTest("test_readline_input_stream", "Hello\nWorld!\n", "Hello\n", 6, 6);
+    }
+
+    public void testAsciiInputReadlineWithSizeLessThanOneLine() throws Exception {
+        // Test where the size given is too small to actually reach a line end and
+        // a truncated line should be returned.
+        doInputTest("test_readline_input_stream", "Hello\nWorld!\n", "Hel", 3, 3);
+    }
+
+    public void testAsciiInputReadlineWithSizeLongerThanOneLine() throws Exception {
+        // Test where the size given is long enough to take in a whole line 
+        // and part of the next line.
+        doInputTest("test_readline_input_stream", "Hello\nWorld!\n", "Hello\n", 6, 10);
+    }
+
+    public void testAsciiInputReadlineWithSizeLongerThanTwoLines() throws Exception {
+        // Test where the size given is long enough to take in a whole line 
+        // and part of the next line.
+        doInputTest("test_readline_input_stream", "Hello\nWorld!\n", "Hello\n", 6, 32);
     }
 
     public void testAsciiInputWithReadlines() throws Exception {
+        doInputTest("test_readlines_input_stream", "Hello", "Hello", 5);
+        doInputTest("test_readlines_input_stream", "Hello\n", "Hello\n", 6);
         doInputTest("test_readlines_input_stream", "Hello\nWorld!\n", "Hello\n$World!\n", 14);
-        doInputTest("test_readlines_input_stream", "Hello", "Hello", 5);
     }
 
     public void testAsciiInputWithReadlinesWithHint() throws Exception {
-    // Let's leave this for now
-    // doInputTest("test_readlines_input_stream", "Hello\nWorld!\n", "Hello\n", 6, 5);
+        doInputTest("test_readlines_input_stream", "Hello\nWorld!\n", "Hello\n", 6, 5);
+        doInputTest("test_readlines_input_stream", "Hello\nWorld!\n", "Hello\n$World!\n", 14, 32);
+    }
+
+    public void testInputIterator() throws Exception {
+        doInputTest("test_iter_input_stream", "Hello\nWorld!\n", "Hello\n$World!\n", 14);
     }
 
     public void testError() throws Exception {
diff --git a/tests/modjy/test_apps_dir/stream_tests.py b/tests/modjy/test_apps_dir/stream_tests.py
--- a/tests/modjy/test_apps_dir/stream_tests.py
+++ b/tests/modjy/test_apps_dir/stream_tests.py
@@ -83,6 +83,14 @@
     writer(repr(output_dict))
     return []
 
+def test_iter_input_stream(environ, start_response):
+    writer = start_response("200 OK", [])
+    wsgi_input = environ['wsgi.input']
+    data = "$".join([line for line in wsgi_input])
+    output_dict = {'data': data}
+    writer(repr(output_dict))
+    return []
+
 def test_error_stream(environ, start_response):
     writer = start_response("200 OK", [])
     wsgi_errors = environ['wsgi.errors']

-- 
Repository URL: http://hg.python.org/jython


More information about the Jython-checkins mailing list