From jython-checkins at python.org Wed Aug 1 10:12:29 2012 From: jython-checkins at python.org (jeff.allen) Date: Wed, 1 Aug 2012 10:12:29 +0200 (CEST) Subject: [Jython-checkins] =?utf-8?q?jython=3A_bytearray=3A_make_acceptabl?= =?utf-8?q?e_as_string_argument_to_re=2Efind=28=29_etc=2E=2E?= Message-ID: <3Wn6gx0kC0zPSw@mail.python.org> http://hg.python.org/jython/rev/37355d490a85 changeset: 6817:37355d490a85 user: Jeff Allen date: Sun Jul 29 16:23:38 2012 +0100 summary: bytearray: make acceptable as string argument to re.find() etc.. files: src/org/python/core/util/StringUtil.java | 14 +++ src/org/python/modules/sre/PatternObject.java | 39 +++++++-- 2 files changed, 44 insertions(+), 9 deletions(-) diff --git a/src/org/python/core/util/StringUtil.java b/src/org/python/core/util/StringUtil.java --- a/src/org/python/core/util/StringUtil.java +++ b/src/org/python/core/util/StringUtil.java @@ -4,8 +4,10 @@ import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; +import org.python.core.BufferPointer; import org.python.core.Py; import org.python.core.BaseBytes; +import org.python.core.PyBuffer; /** * String Utility methods. @@ -67,6 +69,18 @@ } /** + * Return a new String with chars corresponding to buf, which is a byte-oriented buffer obtained + * through the buffer API. + * + * @param buf a PyBuffer of bytes + * @return a new String corresponding to the bytes in buf + */ + public static String fromBytes(PyBuffer buf) { + BufferPointer bp = buf.getBuf(); + return fromBytes(bp.storage, bp.offset, bp.size); + } + + /** * Return a new String with chars corresponding to b. * * @param b a BaseBytes containing bytes diff --git a/src/org/python/modules/sre/PatternObject.java b/src/org/python/modules/sre/PatternObject.java --- a/src/org/python/modules/sre/PatternObject.java +++ b/src/org/python/modules/sre/PatternObject.java @@ -18,6 +18,7 @@ import java.util.*; import org.python.core.*; +import org.python.core.util.StringUtil; public class PatternObject extends PyObject { int[] code; /* link to the code string object */ @@ -362,17 +363,37 @@ _error(status); return null; } - - private static PyString extractPyString(ArgParser ap, int pos){ + + private static PyString extractPyString(ArgParser ap, int pos) { PyObject obj = ap.getPyObject(pos); - if(!(obj instanceof PyString)){ - if (obj instanceof PyArray) { - return new PyString(obj.toString()); + + if (obj instanceof PyString) { + // Easy case + return (PyString)obj; + + } else if (obj instanceof BufferProtocol) { + // Try to get a simple byte-oriented buffer + PyBuffer buf = null; + try { + buf = ((BufferProtocol)obj).getBuffer(PyBUF.SIMPLE); + // ... and treat those bytes as a PyString + String s = StringUtil.fromBytes(buf); + return new PyString(s); + } catch (Exception e) { + // Wrong kind of buffer: generic error message will do + } finally { + // If we got a buffer, we should release it + if (buf != null) { + buf.release(); + } } - throw Py.TypeError("expected str or unicode but got " + obj.getType()); + + } else if (obj instanceof PyArray) { + // PyArray can do something similar + return new PyString(obj.toString()); } - return (PyString)obj; + + // None of those things worked + throw Py.TypeError("expected string or buffer, but got " + obj.getType()); } } - - -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Wed Aug 1 10:12:30 2012 From: jython-checkins at python.org (jeff.allen) Date: Wed, 1 Aug 2012 10:12:30 +0200 (CEST) Subject: [Jython-checkins] =?utf-8?q?jython=3A_Make_bytearray_acceptable_t?= =?utf-8?b?byBmLnJlYWRpbnRvKGIpIGFuZCBmLndyaXRlKGIp?= Message-ID: <3Wn6gy45C9zPSw@mail.python.org> http://hg.python.org/jython/rev/5018fda4fc52 changeset: 6818:5018fda4fc52 user: Jeff Allen date: Sun Jul 29 21:56:01 2012 +0100 summary: Make bytearray acceptable to f.readinto(b) and f.write(b) This fixes a test failure in test.test_bytes, but I'm not at all sure it finishes the job as far as bytearray and the io module go. There is much about the design that remains a puzzle to me. files: src/org/python/core/PyFile.java | 19 ++++ src/org/python/core/io/TextIOBase.java | 56 ++++++++++---- 2 files changed, 59 insertions(+), 16 deletions(-) diff --git a/src/org/python/core/PyFile.java b/src/org/python/core/PyFile.java --- a/src/org/python/core/PyFile.java +++ b/src/org/python/core/PyFile.java @@ -22,6 +22,7 @@ import org.python.core.io.TextIOBase; import org.python.core.io.TextIOWrapper; import org.python.core.io.UniversalIOWrapper; +import org.python.core.util.StringUtil; import org.python.expose.ExposedDelete; import org.python.expose.ExposedGet; import org.python.expose.ExposedMethod; @@ -379,10 +380,28 @@ * 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 BufferProtocol) { + // Try to get a simple byte-oriented buffer + PyBuffer buf = null; + try { + buf = ((BufferProtocol)obj).getBuffer(PyBUF.SIMPLE); + return StringUtil.fromBytes(buf); + } catch (Exception e) { + // Wrong kind of buffer: generic error message will do + } finally { + // If we got a buffer, we should release it + if (buf != null) { + buf.release(); + } + } + } else if (binary && obj instanceof PyArray) { return ((PyArray)obj).tostring(); } 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,10 +5,15 @@ 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; +import org.python.core.PyBUF; +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. @@ -91,29 +96,48 @@ } /** - * Read into the given PyObject that implements the read-write - * buffer interface (currently just a PyArray). - * - * @param buf a PyObject implementing the read-write buffer interface + * 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 */ public int readinto(PyObject buf) { // This is an inefficient version of readinto: but readinto is // not recommended for use in Python 2.x anyway - if (!(buf instanceof PyArray)) { - // emulate PyArg_ParseTuple - if (buf instanceof PyString) { - throw Py.TypeError("Cannot use string as modifiable buffer"); + if (buf instanceof BufferProtocol) { + PyBuffer view = ((BufferProtocol)buf).getBuffer(PyBUF.SIMPLE); + if (view.isReadonly()) { + // More helpful than falling through to CPython message + throw Py.TypeError("cannot read into read-only " + + buf.getType().fastGetName()); + } else { + try { + // Inefficiently, we have to go via a String + String read = read(view.getLen()); + int n = read.length(); + for (int i = 0; i < n; i++) { + view.storeAt((byte)read.charAt(i), i); + } + return read.length(); + } finally { + // We should release the buffer explicitly + view.release(); + } } - throw Py.TypeError("argument 1 must be read-write buffer, not " - + buf.getType().fastGetName()); + + } else if (buf instanceof PyArray) { + PyArray array = (PyArray)buf; + String read = read(array.__len__()); + for (int i = 0; i < read.length(); i++) { + array.set(i, new PyString(read.charAt(i))); + } + return read.length(); } - PyArray array = (PyArray)buf; - String read = read(array.__len__()); - for (int i = 0; i < read.length(); i++) { - array.set(i, new PyString(read.charAt(i))); - } - return read.length(); + + // No valid alternative worked + throw Py.TypeError("argument 1 must be read-write buffer, not " + + buf.getType().fastGetName()); } /** -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Fri Aug 3 00:47:33 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Fri, 3 Aug 2012 00:47:33 +0200 (CEST) Subject: [Jython-checkins] =?utf-8?b?anl0aG9uICgyLjUpOiAjMTk1MiBfX2ltcG9y?= =?utf-8?q?t=5F=5F=28=29=3A_Handling_of_non-dict_globals_argument_incompat?= =?utf-8?q?ible_with?= Message-ID: <3Wp6391P99zPgs@mail.python.org> http://hg.python.org/jython/rev/bddb532f5a99 changeset: 6819:bddb532f5a99 branch: 2.5 parent: 6815:18db8d13c227 user: Frank Wierzbicki date: Thu Aug 02 15:10:36 2012 -0700 summary: #1952 __import__(): Handling of non-dict globals argument incompatible with CPython files: Lib/test/test_import_jy.py | 5 +++++ NEWS | 3 +++ src/org/python/core/imp.java | 2 +- 3 files changed, 9 insertions(+), 1 deletions(-) diff --git a/Lib/test/test_import_jy.py b/Lib/test/test_import_jy.py --- a/Lib/test/test_import_jy.py +++ b/Lib/test/test_import_jy.py @@ -210,6 +210,11 @@ from test.issue1811 import foo self.assertTrue(foo.issue1811.foo is foo) + def test_issue1952(self): + # CPython 2.x ignores non-dict's in second arg to __import__ + # The following threw an exception in Jython previously. + __import__("os", [], level=-1) + def test_main(): test_support.run_unittest(MislabeledImportTestCase, diff --git a/NEWS b/NEWS --- a/NEWS +++ b/NEWS @@ -3,6 +3,9 @@ Jython 2.7a1 Bugs Fixed +Jython 2.5.3rc1 + - [ 1952 ] __import__(): Handling of non-dict globals argument incompatible with CPython + Jython 2.5.3b3 Bugs Fixed - [ 1754 ] modjy does not provide appropriate wsgi.input file-like object diff --git a/src/org/python/core/imp.java b/src/org/python/core/imp.java --- a/src/org/python/core/imp.java +++ b/src/org/python/core/imp.java @@ -809,7 +809,7 @@ PyObject modules = Py.getSystemState().modules; PyObject pkgMod = null; String pkgName = null; - if (modDict != null && !(modDict instanceof PyNone)) { + if (modDict != null && modDict.isMappingType()) { pkgName = get_parent(modDict, level); pkgMod = modules.__finditem__(pkgName); if (pkgMod != null && !(pkgMod instanceof PyModule)) { -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Fri Aug 3 00:47:34 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Fri, 3 Aug 2012 00:47:34 +0200 (CEST) Subject: [Jython-checkins] =?utf-8?q?jython_=28merge_2=2E5_-=3E_default=29?= =?utf-8?b?OiBNZXJnZSAyLjUu?= Message-ID: <3Wp63B6PvWzPhf@mail.python.org> http://hg.python.org/jython/rev/b84e99394780 changeset: 6820:b84e99394780 parent: 6818:5018fda4fc52 parent: 6819:bddb532f5a99 user: Frank Wierzbicki date: Thu Aug 02 15:37:27 2012 -0700 summary: Merge 2.5. files: Lib/test/test_import_jy.py | 5 +++++ NEWS | 3 +++ src/org/python/core/imp.java | 2 +- 3 files changed, 9 insertions(+), 1 deletions(-) diff --git a/Lib/test/test_import_jy.py b/Lib/test/test_import_jy.py --- a/Lib/test/test_import_jy.py +++ b/Lib/test/test_import_jy.py @@ -210,6 +210,11 @@ from test.issue1811 import foo self.assertTrue(foo.issue1811.foo is foo) + def test_issue1952(self): + # CPython 2.x ignores non-dict's in second arg to __import__ + # The following threw an exception in Jython previously. + __import__("os", [], level=-1) + def test_main(): test_support.run_unittest(MislabeledImportTestCase, diff --git a/NEWS b/NEWS --- a/NEWS +++ b/NEWS @@ -22,6 +22,9 @@ Bugs Fixed - [ 1880 ] Sha 224 library not present in Jython +Jython 2.5.3rc1 + - [ 1952 ] __import__(): Handling of non-dict globals argument incompatible with CPython + Jython 2.5.3b3 Bugs Fixed - [ 1754 ] modjy does not provide appropriate wsgi.input file-like object diff --git a/src/org/python/core/imp.java b/src/org/python/core/imp.java --- a/src/org/python/core/imp.java +++ b/src/org/python/core/imp.java @@ -828,7 +828,7 @@ PyObject modules = Py.getSystemState().modules; PyObject pkgMod = null; String pkgName = null; - if (modDict != null && !(modDict instanceof PyNone)) { + if (modDict != null && modDict.isMappingType()) { pkgName = get_parent(modDict, level); pkgMod = modules.__finditem__(pkgName); if (pkgMod != null && !(pkgMod instanceof PyModule)) { -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Fri Aug 3 22:01:35 2012 From: jython-checkins at python.org (philip.jenvey) Date: Fri, 3 Aug 2012 22:01:35 +0200 (CEST) Subject: [Jython-checkins] =?utf-8?q?jython=3A_optimize_the_interpreter=27?= =?utf-8?q?s_stack_impl_by_using_a_plain_array?= Message-ID: <3WpfKC3XL3zNs1@mail.python.org> http://hg.python.org/jython/rev/7cebb8b5aea7 changeset: 6821:7cebb8b5aea7 user: Philip Jenvey date: Fri Aug 03 13:00:45 2012 -0700 summary: optimize the interpreter's stack impl by using a plain array fixes #1951 thanks Wei Zhang files: NEWS | 1 + src/org/python/core/PyBytecode.java | 84 ++++++++-------- 2 files changed, 44 insertions(+), 41 deletions(-) diff --git a/NEWS b/NEWS --- a/NEWS +++ b/NEWS @@ -2,6 +2,7 @@ Jython 2.7a3 Bugs Fixed + - [ 1951 ] Bytecode Interpreter stack optimization for larger arguments - [ 1921 ] compiler module broken in Jython 2.7 - [ 1920 ] Backport CO_FUTURE_PRINT_FUNCTION to Lib/compiler/pycodegen.py - [ 1914 ] Float formatting broken in many non-English locales in Jython 2.7 diff --git a/src/org/python/core/PyBytecode.java b/src/org/python/core/PyBytecode.java --- a/src/org/python/core/PyBytecode.java +++ b/src/org/python/core/PyBytecode.java @@ -255,7 +255,7 @@ @Override protected PyObject interpret(PyFrame f, ThreadState ts) { - final PyStack stack = new PyStack(); + final PyStack stack = new PyStack(co_stacksize); int next_instr = -1; int opcode; /* Current opcode */ int oparg = 0; /* Current opcode argument, if any */ @@ -1363,97 +1363,99 @@ // and capacity hints private static class PyStack { - final List stack; + final PyObject[] stack; + int top = -1; - PyStack() { - stack = new ArrayList(); + PyStack(int size) { + stack = new PyObject[size]; } PyObject top() { - return stack.get(stack.size() - 1); + return stack[top]; } PyObject top(int n) { - return stack.get(stack.size() - n); + return stack[top - n + 1]; } PyObject pop() { - return stack.remove(stack.size() - 1); + return stack[top--]; } void push(PyObject v) { - stack.add(v); + stack[++top] = v; } void set_top(PyObject v) { - stack.set(stack.size() - 1, v); + stack[top] = v; } void dup() { - stack.add(top()); + stack[top + 1] = stack[top]; + top++; } void dup(int n) { - int length = stack.size(); - for (int i = n; i > 0; i--) { - stack.add(stack.get(length - i)); + int oldTop = top; + top += n; + + for (int i = 0; i < n; i++) { + stack[top - i] = stack[oldTop - i]; } } PyObject[] popN(int n) { - int end = stack.size(); // exclusive PyObject ret[] = new PyObject[n]; - List lastN = stack.subList(end - n, end); - lastN.toArray(ret); - lastN.clear(); + top -= n; + + for (int i = 0; i < n; i++) { + ret[i] = stack[top + i + 1]; + } + return ret; } void rot2() { - int length = stack.size(); - PyObject v = stack.get(length - 1); - PyObject w = stack.get(length - 2); - stack.set(length - 1, w); - stack.set(length - 2, v); + PyObject topv = stack[top]; + stack[top] = stack[top - 1]; + stack[top - 1] = topv; } void rot3() { - int length = stack.size(); - PyObject v = stack.get(length - 1); - PyObject w = stack.get(length - 2); - PyObject x = stack.get(length - 3); - stack.set(length - 1, w); - stack.set(length - 2, x); - stack.set(length - 3, v); + PyObject v = stack[top]; + PyObject w = stack[top - 1]; + PyObject x = stack[top - 2]; + stack[top] = w; + stack[top - 1] = x; + stack[top - 2] = v; } void rot4() { - int length = stack.size(); - PyObject u = stack.get(length - 1); - PyObject v = stack.get(length - 2); - PyObject w = stack.get(length - 3); - PyObject x = stack.get(length - 4); - stack.set(length - 1, v); - stack.set(length - 2, w); - stack.set(length - 3, x); - stack.set(length - 4, u); + PyObject u = stack[top]; + PyObject v = stack[top - 1]; + PyObject w = stack[top - 2]; + PyObject x = stack[top - 3]; + stack[top] = v; + stack[top - 1] = w; + stack[top - 2] = x; + stack[top - 3] = u; } int size() { - return stack.size(); + return top + 1; } @Override public String toString() { StringBuilder buffer = new StringBuilder(); - int size = stack.size(); + int size = size(); int N = size > 4 ? 4 : size; buffer.append("["); for (int i = 0; i < N; i++) { if (i > 0) { buffer.append(", "); } - PyObject item = stack.get(size - (i + 1)); + PyObject item = stack[N - i - 1]; buffer.append(upto(item.__repr__().toString())); } if (N < size) { -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Fri Aug 3 22:01:37 2012 From: jython-checkins at python.org (philip.jenvey) Date: Fri, 3 Aug 2012 22:01:37 +0200 (CEST) Subject: [Jython-checkins] =?utf-8?q?jython=3A_these_are_now_done?= Message-ID: <3WpfKF0D7FzPlB@mail.python.org> http://hg.python.org/jython/rev/825a988dfa67 changeset: 6822:825a988dfa67 user: Philip Jenvey date: Fri Aug 03 13:01:02 2012 -0700 summary: these are now done files: src/org/python/core/PyBytecode.java | 4 +--- 1 files changed, 1 insertions(+), 3 deletions(-) diff --git a/src/org/python/core/PyBytecode.java b/src/org/python/core/PyBytecode.java --- a/src/org/python/core/PyBytecode.java +++ b/src/org/python/core/PyBytecode.java @@ -43,7 +43,7 @@ public final byte[] co_code; // widened to char to avoid signed byte issues public final PyObject[] co_consts; public final String[] co_names; - public final int co_stacksize; // XXX - use to convert PyStack to use PyObject[] instead of ArrayList + public final int co_stacksize; public final byte[] co_lnotab; private final static int CALL_FLAG_VAR = 1; private final static int CALL_FLAG_KW = 2; @@ -1359,8 +1359,6 @@ } } - // XXX - perhaps add support for max stack size (presumably from co_stacksize) - // and capacity hints private static class PyStack { final PyObject[] stack; -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Fri Aug 3 22:26:57 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Fri, 3 Aug 2012 22:26:57 +0200 (CEST) Subject: [Jython-checkins] =?utf-8?q?jython_=282=2E5=29=3A_Build_full_mave?= =?utf-8?q?n_bundles=2E?= Message-ID: <3WpftT1RXMzPkR@mail.python.org> http://hg.python.org/jython/rev/7a6dca49e49d changeset: 6823:7a6dca49e49d branch: 2.5 parent: 6819:bddb532f5a99 user: Frank Wierzbicki date: Fri Aug 03 13:20:25 2012 -0700 summary: Build full maven bundles. files: build.xml | 20 ++++++++++++++++++++ maven/build.xml | 33 ++++++++++++++++++++++++++++----- 2 files changed, 48 insertions(+), 5 deletions(-) diff --git a/build.xml b/build.xml --- a/build.xml +++ b/build.xml @@ -227,6 +227,8 @@ + + @@ -753,6 +755,20 @@ + + + + + + + + + + + + + + copy CPython LICENSE from ${checkout.dir}/python @@ -901,6 +917,8 @@ + + @@ -961,6 +979,8 @@ + + diff --git a/maven/build.xml b/maven/build.xml --- a/maven/build.xml +++ b/maven/build.xml @@ -29,6 +29,7 @@ + @@ -137,7 +138,11 @@ - + + @@ -148,9 +153,27 @@ - - - + + + + + @@ -172,7 +195,7 @@ - + -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Fri Aug 3 22:26:58 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Fri, 3 Aug 2012 22:26:58 +0200 (CEST) Subject: [Jython-checkins] =?utf-8?q?jython_=28merge_2=2E5_-=3E_default=29?= =?utf-8?b?OiBNZXJnZSAyLjUu?= Message-ID: <3WpftV5DFQzPkc@mail.python.org> http://hg.python.org/jython/rev/8c5ec7fccfb2 changeset: 6824:8c5ec7fccfb2 parent: 6822:825a988dfa67 parent: 6823:7a6dca49e49d user: Frank Wierzbicki date: Fri Aug 03 13:26:48 2012 -0700 summary: Merge 2.5. files: build.xml | 20 ++++++++++++++++++++ maven/build.xml | 33 ++++++++++++++++++++++++++++----- 2 files changed, 48 insertions(+), 5 deletions(-) diff --git a/build.xml b/build.xml --- a/build.xml +++ b/build.xml @@ -240,6 +240,8 @@ + + @@ -770,6 +772,20 @@ + + + + + + + + + + + + + + copy CPython LICENSE from ${checkout.dir}/python @@ -918,6 +934,8 @@ + + @@ -978,6 +996,8 @@ + + diff --git a/maven/build.xml b/maven/build.xml --- a/maven/build.xml +++ b/maven/build.xml @@ -29,6 +29,7 @@ + @@ -137,7 +138,11 @@ - + + @@ -148,9 +153,27 @@ - - - + + + + + @@ -172,7 +195,7 @@ - + -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Fri Aug 3 22:51:07 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Fri, 3 Aug 2012 22:51:07 +0200 (CEST) Subject: [Jython-checkins] =?utf-8?q?jython_=282=2E5=29=3A_Update_for_rc1?= =?utf-8?q?=2E?= Message-ID: <3WpgQM08gzzPlN@mail.python.org> http://hg.python.org/jython/rev/d5070c5bfb91 changeset: 6825:d5070c5bfb91 branch: 2.5 parent: 6823:7a6dca49e49d user: Frank Wierzbicki date: Fri Aug 03 13:51:00 2012 -0700 summary: Update for rc1. files: README.txt | 13 +++++++------ 1 files changed, 7 insertions(+), 6 deletions(-) diff --git a/README.txt b/README.txt --- a/README.txt +++ b/README.txt @@ -1,9 +1,10 @@ -Welcome to Jython 2.5.3b3 -======================= +Welcome to Jython 2.5.3rc1 +========================== -This is the third beta release of the 2.5.3 version of Jython -Please see the NEWS file for detailed release notes. - -The release was compiled on Ubuntu with JDK 6 and requires JDK 5 to run. +This is the first release candidate of the 2.5.3 version of Jython. +Thanks to Adconion Media Group (http://www.adconion.com/) for sponsoring this +release, and thanks to all who contribute to Jython. Please see the NEWS file +for detailed release notes. The release was compiled on Ubuntu with JDK 6 and +requires JDK 5 to run. Please try this out and report any bugs at http://bugs.jython.org. -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Fri Aug 3 23:11:22 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Fri, 3 Aug 2012 23:11:22 +0200 (CEST) Subject: [Jython-checkins] =?utf-8?q?jython_=28merge_2=2E5_-=3E_default=29?= =?utf-8?q?=3A_Block_merge_2=2E5=2E?= Message-ID: <3Wpgsk3LQNzPmS@mail.python.org> http://hg.python.org/jython/rev/f6657bf964c6 changeset: 6826:f6657bf964c6 parent: 6824:8c5ec7fccfb2 parent: 6825:d5070c5bfb91 user: Frank Wierzbicki date: Fri Aug 03 13:52:01 2012 -0700 summary: Block merge 2.5. files: -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Fri Aug 3 23:11:23 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Fri, 3 Aug 2012 23:11:23 +0200 (CEST) Subject: [Jython-checkins] =?utf-8?b?anl0aG9uICgyLjUpOiBBZGRlZCB0YWcgdjIu?= =?utf-8?q?5=2E3rc1_for_changeset_d5070c5bfb91?= Message-ID: <3Wpgsl6bsLzPmL@mail.python.org> http://hg.python.org/jython/rev/05e4374cfd0e changeset: 6827:05e4374cfd0e branch: 2.5 parent: 6825:d5070c5bfb91 user: Frank Wierzbicki date: Fri Aug 03 14:10:49 2012 -0700 summary: Added tag v2.5.3rc1 for changeset d5070c5bfb91 files: .hgtags | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -59,3 +59,4 @@ 91332231a44804192e47ca25be334bab8ac8ea7c v2.5.2 c5567b7757e7ff086bdb10006ddbd513a727a803 v2.5.3b1 bc783e270abc80c94aab8fb55aa363de0d2422bb v2.5.3b3 +d5070c5bfb914eb5f8381d70fd58316bd264f31b v2.5.3rc1 -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Fri Aug 3 23:14:51 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Fri, 3 Aug 2012 23:14:51 +0200 (CEST) Subject: [Jython-checkins] =?utf-8?q?jython_=28merge_2=2E5_-=3E_default=29?= =?utf-8?q?=3A_Update_tags=2E?= Message-ID: <3Wpgxl4gFfzNs1@mail.python.org> http://hg.python.org/jython/rev/96bc2fa235a0 changeset: 6828:96bc2fa235a0 parent: 6826:f6657bf964c6 parent: 6827:05e4374cfd0e user: Frank Wierzbicki date: Fri Aug 03 14:14:45 2012 -0700 summary: Update tags. files: .hgtags | 5 ++++- 1 files changed, 4 insertions(+), 1 deletions(-) diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -58,5 +58,8 @@ c72d5c5c968abac480c0841057797abf2d7f0018 v2.5.2 91332231a44804192e47ca25be334bab8ac8ea7c v2.5.2 c5567b7757e7ff086bdb10006ddbd513a727a803 v2.5.3b1 +bc783e270abc80c94aab8fb55aa363de0d2422bb v2.5.3b3 +d5070c5bfb914eb5f8381d70fd58316bd264f31b v2.5.3rc1 4f5e3c12edc040058d724d4469a53e8649e64420 v2.7.0a1 -ac5609677c1382f14d0e04178fe6dd4108e4c231 2.7.0a2 +ac5609677c1382f14d0e04178fe6dd4108e4c231 v2.7.0a2 + -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Fri Aug 3 23:19:36 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Fri, 3 Aug 2012 23:19:36 +0200 (CEST) Subject: [Jython-checkins] =?utf-8?q?jython_=282=2E5=29=3A_Change_status_t?= =?utf-8?q?o_gamma_=28release_candidate=29=2E?= Message-ID: <3Wph3D1DJxzPkd@mail.python.org> http://hg.python.org/jython/rev/458a07c2f0f0 changeset: 6829:458a07c2f0f0 branch: 2.5 parent: 6827:05e4374cfd0e user: Frank Wierzbicki date: Fri Aug 03 14:19:30 2012 -0700 summary: Change status to gamma (release candidate). files: build.xml | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/build.xml b/build.xml --- a/build.xml +++ b/build.xml @@ -128,7 +128,7 @@ - + -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Fri Aug 3 23:20:12 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Fri, 3 Aug 2012 23:20:12 +0200 (CEST) Subject: [Jython-checkins] =?utf-8?b?anl0aG9uICgyLjUpOiBBZGRlZCB0YWcgdjIu?= =?utf-8?q?5=2E3rc1_for_changeset_458a07c2f0f0?= Message-ID: <3Wph3w6w0pzPkd@mail.python.org> http://hg.python.org/jython/rev/aa7d621dc769 changeset: 6830:aa7d621dc769 branch: 2.5 user: Frank Wierzbicki date: Fri Aug 03 14:20:04 2012 -0700 summary: Added tag v2.5.3rc1 for changeset 458a07c2f0f0 files: .hgtags | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-) diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -60,3 +60,5 @@ c5567b7757e7ff086bdb10006ddbd513a727a803 v2.5.3b1 bc783e270abc80c94aab8fb55aa363de0d2422bb v2.5.3b3 d5070c5bfb914eb5f8381d70fd58316bd264f31b v2.5.3rc1 +d5070c5bfb914eb5f8381d70fd58316bd264f31b v2.5.3rc1 +458a07c2f0f05d6163017b893bee2a5a61489487 v2.5.3rc1 -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Tue Aug 7 19:25:18 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Tue, 7 Aug 2012 19:25:18 +0200 (CEST) Subject: [Jython-checkins] =?utf-8?q?jython_=282=2E5=29=3A_Clean_up_maven_?= =?utf-8?q?release=2E?= Message-ID: <3Ws2g23QKJzPxS@mail.python.org> http://hg.python.org/jython/rev/8fd14231e553 changeset: 6831:8fd14231e553 branch: 2.5 user: Frank Wierzbicki date: Tue Aug 07 10:25:13 2012 -0700 summary: Clean up maven release. files: maven/build.xml | 18 ++++++++++++------ maven/pom-template.xml | 7 +++++++ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/maven/build.xml b/maven/build.xml --- a/maven/build.xml +++ b/maven/build.xml @@ -139,24 +139,26 @@ - + tofile="${build.dir}/@{artifactId}-${project.version}.pom"> + + - @@ -169,10 +171,14 @@ + + + + + ---> diff --git a/maven/pom-template.xml b/maven/pom-template.xml --- a/maven/pom-template.xml +++ b/maven/pom-template.xml @@ -27,4 +27,11 @@ scm:hg:ssh://hg at hg.python.org/jython http://hg.python.org/jython + + + + fwierzbicki + Frank Wierzbicki + + -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Thu Aug 9 01:30:56 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Thu, 9 Aug 2012 01:30:56 +0200 (CEST) Subject: [Jython-checkins] =?utf-8?q?jython_=28merge_2=2E5_-=3E_default=29?= =?utf-8?b?OiBNZXJnZSAyLjUu?= Message-ID: <3WspkS3NkHzQ2k@mail.python.org> http://hg.python.org/jython/rev/381f1982162e changeset: 6832:381f1982162e parent: 6828:96bc2fa235a0 parent: 6831:8fd14231e553 user: Frank Wierzbicki date: Wed Aug 08 16:13:40 2012 -0700 summary: Merge 2.5. files: maven/build.xml | 18 ++++++++++++------ maven/pom-template.xml | 7 +++++++ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/maven/build.xml b/maven/build.xml --- a/maven/build.xml +++ b/maven/build.xml @@ -139,24 +139,26 @@ - + tofile="${build.dir}/@{artifactId}-${project.version}.pom"> + + - @@ -169,10 +171,14 @@ + + + + + ---> diff --git a/maven/pom-template.xml b/maven/pom-template.xml --- a/maven/pom-template.xml +++ b/maven/pom-template.xml @@ -27,4 +27,11 @@ scm:hg:ssh://hg at hg.python.org/jython http://hg.python.org/jython + + + + fwierzbicki + Frank Wierzbicki + + -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Thu Aug 9 01:34:23 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Thu, 9 Aug 2012 01:34:23 +0200 (CEST) Subject: [Jython-checkins] =?utf-8?q?jython_=282=2E5=29=3A_Update_NEWS=2E?= Message-ID: <3WsppR2MFdzQ2k@mail.python.org> http://hg.python.org/jython/rev/da21da0b7ed5 changeset: 6833:da21da0b7ed5 branch: 2.5 parent: 6831:8fd14231e553 user: Frank Wierzbicki date: Wed Aug 08 16:16:23 2012 -0700 summary: Update NEWS. files: NEWS | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/NEWS b/NEWS --- a/NEWS +++ b/NEWS @@ -5,6 +5,7 @@ Jython 2.5.3rc1 - [ 1952 ] __import__(): Handling of non-dict globals argument incompatible with CPython + - [ 1900 ] Python imports from Java cause some Python imports to fail Jython 2.5.3b3 Bugs Fixed -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Thu Aug 9 01:34:24 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Thu, 9 Aug 2012 01:34:24 +0200 (CEST) Subject: [Jython-checkins] =?utf-8?q?jython_=28merge_2=2E5_-=3E_default=29?= =?utf-8?b?OiBNZXJnZSAyLjUu?= Message-ID: <3WsppS589mzQ2m@mail.python.org> http://hg.python.org/jython/rev/464215a6e771 changeset: 6834:464215a6e771 parent: 6832:381f1982162e parent: 6833:da21da0b7ed5 user: Frank Wierzbicki date: Wed Aug 08 16:17:06 2012 -0700 summary: Merge 2.5. files: NEWS | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/NEWS b/NEWS --- a/NEWS +++ b/NEWS @@ -25,6 +25,7 @@ Jython 2.5.3rc1 - [ 1952 ] __import__(): Handling of non-dict globals argument incompatible with CPython + - [ 1900 ] Python imports from Java cause some Python imports to fail Jython 2.5.3b3 Bugs Fixed -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Mon Aug 13 22:53:03 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Mon, 13 Aug 2012 22:53:03 +0200 (CEST) Subject: [Jython-checkins] =?utf-8?q?jython=3A_from=3A?= Message-ID: <3Wwpzz4XLTzPrR@mail.python.org> http://hg.python.org/jython/rev/ad1d33e7aaf5 changeset: 6835:ad1d33e7aaf5 tag: 2.5.3 user: Frank Wierzbicki date: Thu Aug 09 09:39:03 2012 -0700 summary: from: http://hg.python.org/cpython/Lib/test/test_xrange.py at 22db03646d9b files: Lib/test/test_xrange.py | 143 ++++++++++++++++++++++++++++ 1 files changed, 143 insertions(+), 0 deletions(-) diff --git a/Lib/test/test_xrange.py b/Lib/test/test_xrange.py new file mode 100644 --- /dev/null +++ b/Lib/test/test_xrange.py @@ -0,0 +1,143 @@ +# Python test set -- built-in functions + +import test.test_support, unittest +import sys +import pickle +import itertools + +import warnings +warnings.filterwarnings("ignore", "integer argument expected", + DeprecationWarning, "unittest") + +# pure Python implementations (3 args only), for comparison +def pyrange(start, stop, step): + if (start - stop) // step < 0: + # replace stop with next element in the sequence of integers + # that are congruent to start modulo step. + stop += (start - stop) % step + while start != stop: + yield start + start += step + +def pyrange_reversed(start, stop, step): + stop += (start - stop) % step + return pyrange(stop - step, start - step, -step) + + +class XrangeTest(unittest.TestCase): + def assert_iterators_equal(self, xs, ys, test_id, limit=None): + # check that an iterator xs matches the expected results ys, + # up to a given limit. + if limit is not None: + xs = itertools.islice(xs, limit) + ys = itertools.islice(ys, limit) + sentinel = object() + pairs = itertools.izip_longest(xs, ys, fillvalue=sentinel) + for i, (x, y) in enumerate(pairs): + if x == y: + continue + elif x == sentinel: + self.fail('{}: iterator ended unexpectedly ' + 'at position {}; expected {}'.format(test_id, i, y)) + elif y == sentinel: + self.fail('{}: unexpected excess element {} at ' + 'position {}'.format(test_id, x, i)) + else: + self.fail('{}: wrong element at position {};' + 'expected {}, got {}'.format(test_id, i, y, x)) + + def test_xrange(self): + self.assertEqual(list(xrange(3)), [0, 1, 2]) + self.assertEqual(list(xrange(1, 5)), [1, 2, 3, 4]) + self.assertEqual(list(xrange(0)), []) + self.assertEqual(list(xrange(-3)), []) + self.assertEqual(list(xrange(1, 10, 3)), [1, 4, 7]) + self.assertEqual(list(xrange(5, -5, -3)), [5, 2, -1, -4]) + + a = 10 + b = 100 + c = 50 + + self.assertEqual(list(xrange(a, a+2)), [a, a+1]) + self.assertEqual(list(xrange(a+2, a, -1L)), [a+2, a+1]) + self.assertEqual(list(xrange(a+4, a, -2)), [a+4, a+2]) + + seq = list(xrange(a, b, c)) + self.assertIn(a, seq) + self.assertNotIn(b, seq) + self.assertEqual(len(seq), 2) + + seq = list(xrange(b, a, -c)) + self.assertIn(b, seq) + self.assertNotIn(a, seq) + self.assertEqual(len(seq), 2) + + seq = list(xrange(-a, -b, -c)) + self.assertIn(-a, seq) + self.assertNotIn(-b, seq) + self.assertEqual(len(seq), 2) + + self.assertRaises(TypeError, xrange) + self.assertRaises(TypeError, xrange, 1, 2, 3, 4) + self.assertRaises(ValueError, xrange, 1, 2, 0) + + self.assertRaises(OverflowError, xrange, 10**100, 10**101, 10**101) + + self.assertRaises(TypeError, xrange, 0, "spam") + self.assertRaises(TypeError, xrange, 0, 42, "spam") + + self.assertEqual(len(xrange(0, sys.maxint, sys.maxint-1)), 2) + + self.assertRaises(OverflowError, xrange, -sys.maxint, sys.maxint) + self.assertRaises(OverflowError, xrange, 0, 2*sys.maxint) + + r = xrange(-sys.maxint, sys.maxint, 2) + self.assertEqual(len(r), sys.maxint) + self.assertRaises(OverflowError, xrange, -sys.maxint-1, sys.maxint, 2) + + def test_pickling(self): + testcases = [(13,), (0, 11), (-22, 10), (20, 3, -1), + (13, 21, 3), (-2, 2, 2)] + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + for t in testcases: + r = xrange(*t) + self.assertEqual(list(pickle.loads(pickle.dumps(r, proto))), + list(r)) + + def test_range_iterators(self): + # see issue 7298 + limits = [base + jiggle + for M in (2**32, 2**64) + for base in (-M, -M//2, 0, M//2, M) + for jiggle in (-2, -1, 0, 1, 2)] + test_ranges = [(start, end, step) + for start in limits + for end in limits + for step in (-2**63, -2**31, -2, -1, 1, 2)] + + for start, end, step in test_ranges: + try: + iter1 = xrange(start, end, step) + except OverflowError: + pass + else: + iter2 = pyrange(start, end, step) + test_id = "xrange({}, {}, {})".format(start, end, step) + # check first 100 entries + self.assert_iterators_equal(iter1, iter2, test_id, limit=100) + + try: + iter1 = reversed(xrange(start, end, step)) + except OverflowError: + pass + else: + iter2 = pyrange_reversed(start, end, step) + test_id = "reversed(xrange({}, {}, {}))".format(start, end, step) + self.assert_iterators_equal(iter1, iter2, test_id, limit=100) + + +def test_main(): + test.test_support.run_unittest(XrangeTest) + +if __name__ == "__main__": + test_main() -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Mon Aug 13 22:53:05 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Mon, 13 Aug 2012 22:53:05 +0200 (CEST) Subject: [Jython-checkins] =?utf-8?b?anl0aG9uICgyLjUpOiBBZGRlZCB0YWcgMi41?= =?utf-8?q?=2E3_for_changeset_da21da0b7ed5?= Message-ID: <3Wwq010JfdzPrR@mail.python.org> http://hg.python.org/jython/rev/3018a43069e5 changeset: 6836:3018a43069e5 branch: 2.5 parent: 6833:da21da0b7ed5 user: Frank Wierzbicki date: Mon Aug 13 13:49:21 2012 -0700 summary: Added tag 2.5.3 for changeset da21da0b7ed5 files: .hgtags | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -62,3 +62,4 @@ d5070c5bfb914eb5f8381d70fd58316bd264f31b v2.5.3rc1 d5070c5bfb914eb5f8381d70fd58316bd264f31b v2.5.3rc1 458a07c2f0f05d6163017b893bee2a5a61489487 v2.5.3rc1 +da21da0b7ed57329924e8e111768bac6583a9bb0 2.5.3 -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Mon Aug 13 22:53:06 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Mon, 13 Aug 2012 22:53:06 +0200 (CEST) Subject: [Jython-checkins] =?utf-8?b?anl0aG9uICgyLjUpOiBQcmVwIGZvciAyLjUu?= =?utf-8?q?3_final=2E?= Message-ID: <3Wwq023BCwzPrR@mail.python.org> http://hg.python.org/jython/rev/3d2dbae23c52 changeset: 6837:3d2dbae23c52 branch: 2.5 user: Frank Wierzbicki date: Mon Aug 13 13:49:48 2012 -0700 summary: Prep for 2.5.3 final. files: README.txt | 22 ++++++++++++++++------ build.xml | 4 ++-- src/org/python/core/imp.java | 2 +- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/README.txt b/README.txt --- a/README.txt +++ b/README.txt @@ -1,10 +1,20 @@ -Welcome to Jython 2.5.3rc1 -========================== +Welcome to Jython 2.5.3! +======================== -This is the first release candidate of the 2.5.3 version of Jython. +This is the final release of Jython 2.5.3. + Thanks to Adconion Media Group (http://www.adconion.com/) for sponsoring this -release, and thanks to all who contribute to Jython. Please see the NEWS file -for detailed release notes. The release was compiled on Ubuntu with JDK 6 and -requires JDK 5 to run. +release, and thanks to all who contribute to Jython. + +This release fixes numerous bugs since the 2.5.2 release of Jython. Some +highlights include: + +* File uploads where broken in Tomcat and Jetty. +* Imports sometimes blew the stack. This was seen in SQLAlchemy for example. +* Some race conditions and threading issues where fixed. +* Several JSR 223 problems have been fixed. + +Please see the NEWS file for detailed release notes. The release was compiled +on Ubuntu with JDK 6 and requires JDK 5 to run. Please try this out and report any bugs at http://bugs.jython.org. diff --git a/build.xml b/build.xml --- a/build.xml +++ b/build.xml @@ -123,12 +123,12 @@ - + - + diff --git a/src/org/python/core/imp.java b/src/org/python/core/imp.java --- a/src/org/python/core/imp.java +++ b/src/org/python/core/imp.java @@ -26,7 +26,7 @@ private static final String UNKNOWN_SOURCEFILE = ""; - private static final int APIVersion = 32; + private static final int APIVersion = 33; public static final int NO_MTIME = -1; -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Mon Aug 13 22:53:07 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Mon, 13 Aug 2012 22:53:07 +0200 (CEST) Subject: [Jython-checkins] =?utf-8?q?jython=3A_Added_tag_2=2E5=2E3_for_cha?= =?utf-8?q?ngeset_ad1d33e7aaf5?= Message-ID: <3Wwq036MRnzPrd@mail.python.org> http://hg.python.org/jython/rev/7d5ddd7f981b changeset: 6838:7d5ddd7f981b parent: 6835:ad1d33e7aaf5 user: Frank Wierzbicki date: Mon Aug 13 13:52:33 2012 -0700 summary: Added tag 2.5.3 for changeset ad1d33e7aaf5 files: .hgtags | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-) diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -63,3 +63,5 @@ 4f5e3c12edc040058d724d4469a53e8649e64420 v2.7.0a1 ac5609677c1382f14d0e04178fe6dd4108e4c231 v2.7.0a2 +da21da0b7ed57329924e8e111768bac6583a9bb0 2.5.3 +ad1d33e7aaf55c098f925ebbc5bc49bf1a18ef1c 2.5.3 -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Mon Aug 13 22:57:52 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Mon, 13 Aug 2012 22:57:52 +0200 (CEST) Subject: [Jython-checkins] =?utf-8?b?anl0aG9uICgyLjUpOiBBZGRlZCB0YWcgMi41?= =?utf-8?q?=2E3_for_changeset_3d2dbae23c52?= Message-ID: <3Wwq5X6hDCzPqk@mail.python.org> http://hg.python.org/jython/rev/27541938411a changeset: 6839:27541938411a branch: 2.5 parent: 6837:3d2dbae23c52 user: Frank Wierzbicki date: Mon Aug 13 13:54:07 2012 -0700 summary: Added tag 2.5.3 for changeset 3d2dbae23c52 files: .hgtags | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-) diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -63,3 +63,5 @@ d5070c5bfb914eb5f8381d70fd58316bd264f31b v2.5.3rc1 458a07c2f0f05d6163017b893bee2a5a61489487 v2.5.3rc1 da21da0b7ed57329924e8e111768bac6583a9bb0 2.5.3 +ad1d33e7aaf55c098f925ebbc5bc49bf1a18ef1c 2.5.3 +3d2dbae23c5292b7f31ac4c92fa6d1afd8bd8eb2 2.5.3 -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Mon Aug 13 22:57:54 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Mon, 13 Aug 2012 22:57:54 +0200 (CEST) Subject: [Jython-checkins] =?utf-8?q?jython_=28merge_2=2E5_-=3E_default=29?= =?utf-8?q?=3A_Merge_2=2E5_-_fix_tags=2E?= Message-ID: <3Wwq5Z688szPsd@mail.python.org> http://hg.python.org/jython/rev/ed93976d71bc changeset: 6840:ed93976d71bc parent: 6838:7d5ddd7f981b parent: 6839:27541938411a user: Frank Wierzbicki date: Mon Aug 13 13:57:36 2012 -0700 summary: Merge 2.5 - fix tags. files: .hgtags | 2 -- 1 files changed, 0 insertions(+), 2 deletions(-) diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -62,6 +62,4 @@ d5070c5bfb914eb5f8381d70fd58316bd264f31b v2.5.3rc1 4f5e3c12edc040058d724d4469a53e8649e64420 v2.7.0a1 ac5609677c1382f14d0e04178fe6dd4108e4c231 v2.7.0a2 - -da21da0b7ed57329924e8e111768bac6583a9bb0 2.5.3 ad1d33e7aaf55c098f925ebbc5bc49bf1a18ef1c 2.5.3 -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Mon Aug 13 23:03:13 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Mon, 13 Aug 2012 23:03:13 +0200 (CEST) Subject: [Jython-checkins] =?utf-8?q?jython_=282=2E5=29=3A_More_tag_fixing?= =?utf-8?q?=2E?= Message-ID: <3WwqCj1gW5zPrL@mail.python.org> http://hg.python.org/jython/rev/954f20f26870 changeset: 6841:954f20f26870 branch: 2.5 parent: 6839:27541938411a user: Frank Wierzbicki date: Mon Aug 13 14:00:39 2012 -0700 summary: More tag fixing. files: .hgtags | 4 ---- 1 files changed, 0 insertions(+), 4 deletions(-) diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -59,9 +59,5 @@ 91332231a44804192e47ca25be334bab8ac8ea7c v2.5.2 c5567b7757e7ff086bdb10006ddbd513a727a803 v2.5.3b1 bc783e270abc80c94aab8fb55aa363de0d2422bb v2.5.3b3 -d5070c5bfb914eb5f8381d70fd58316bd264f31b v2.5.3rc1 -d5070c5bfb914eb5f8381d70fd58316bd264f31b v2.5.3rc1 458a07c2f0f05d6163017b893bee2a5a61489487 v2.5.3rc1 -da21da0b7ed57329924e8e111768bac6583a9bb0 2.5.3 -ad1d33e7aaf55c098f925ebbc5bc49bf1a18ef1c 2.5.3 3d2dbae23c5292b7f31ac4c92fa6d1afd8bd8eb2 2.5.3 -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Mon Aug 13 23:03:14 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Mon, 13 Aug 2012 23:03:14 +0200 (CEST) Subject: [Jython-checkins] =?utf-8?q?jython=3A_Hopefully_last_tag_fix_for_?= =?utf-8?q?this_time_around_=3A=28?= Message-ID: <3WwqCk4RX6zPrL@mail.python.org> http://hg.python.org/jython/rev/f4f057830c2b changeset: 6842:f4f057830c2b parent: 6840:ed93976d71bc user: Frank Wierzbicki date: Mon Aug 13 14:03:05 2012 -0700 summary: Hopefully last tag fix for this time around :( files: .hgtags | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -62,4 +62,4 @@ d5070c5bfb914eb5f8381d70fd58316bd264f31b v2.5.3rc1 4f5e3c12edc040058d724d4469a53e8649e64420 v2.7.0a1 ac5609677c1382f14d0e04178fe6dd4108e4c231 v2.7.0a2 -ad1d33e7aaf55c098f925ebbc5bc49bf1a18ef1c 2.5.3 +3d2dbae23c5292b7f31ac4c92fa6d1afd8bd8eb2 2.5.3 -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Mon Aug 13 23:36:04 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Mon, 13 Aug 2012 23:36:04 +0200 (CEST) Subject: [Jython-checkins] =?utf-8?b?anl0aG9uICgyLjUpOiBOb3QgdGhlIGxhc3Qs?= =?utf-8?q?_now_I_misspelled_the_tag=2E_Argh!?= Message-ID: <3Wwqxc3j2zzPqC@mail.python.org> http://hg.python.org/jython/rev/c56500f08d34 changeset: 6843:c56500f08d34 branch: 2.5 parent: 6841:954f20f26870 user: Frank Wierzbicki date: Mon Aug 13 14:33:45 2012 -0700 summary: Not the last, now I misspelled the tag. Argh! files: .hgtags | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -60,4 +60,4 @@ c5567b7757e7ff086bdb10006ddbd513a727a803 v2.5.3b1 bc783e270abc80c94aab8fb55aa363de0d2422bb v2.5.3b3 458a07c2f0f05d6163017b893bee2a5a61489487 v2.5.3rc1 -3d2dbae23c5292b7f31ac4c92fa6d1afd8bd8eb2 2.5.3 +3d2dbae23c5292b7f31ac4c92fa6d1afd8bd8eb2 v2.5.3 -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Mon Aug 13 23:36:05 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Mon, 13 Aug 2012 23:36:05 +0200 (CEST) Subject: [Jython-checkins] =?utf-8?q?jython_=28merge_2=2E5_-=3E_default=29?= =?utf-8?b?OiBNZXJnZSAyLjUu?= Message-ID: <3Wwqxd6grnzPqC@mail.python.org> http://hg.python.org/jython/rev/d7e04f80af9a changeset: 6844:d7e04f80af9a parent: 6842:f4f057830c2b parent: 6843:c56500f08d34 user: Frank Wierzbicki date: Mon Aug 13 14:34:53 2012 -0700 summary: Merge 2.5. files: .hgtags | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -62,4 +62,4 @@ d5070c5bfb914eb5f8381d70fd58316bd264f31b v2.5.3rc1 4f5e3c12edc040058d724d4469a53e8649e64420 v2.7.0a1 ac5609677c1382f14d0e04178fe6dd4108e4c231 v2.7.0a2 -3d2dbae23c5292b7f31ac4c92fa6d1afd8bd8eb2 2.5.3 +3d2dbae23c5292b7f31ac4c92fa6d1afd8bd8eb2 v2.5.3 -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Tue Aug 14 23:11:33 2012 From: jython-checkins at python.org (alan.kennedy) Date: Tue, 14 Aug 2012 23:11:33 +0200 (CEST) Subject: [Jython-checkins] =?utf-8?q?jython_=282=2E5=29=3A_Fixes_for_getad?= =?utf-8?q?drinfo?= Message-ID: <3WxRLs3dXCzQ6v@mail.python.org> http://hg.python.org/jython/rev/d56c4119fed1 changeset: 6845:d56c4119fed1 branch: 2.5 parent: 6843:c56500f08d34 user: Alan Kennedy date: Tue Aug 14 21:43:11 2012 +0100 summary: Fixes for getaddrinfo files: Lib/socket.py | 159 ++++++++++++++++++++------- Lib/test/test_socket.py | 79 +++++++++++-- 2 files changed, 181 insertions(+), 57 deletions(-) diff --git a/Lib/socket.py b/Lib/socket.py --- a/Lib/socket.py +++ b/Lib/socket.py @@ -90,7 +90,7 @@ def java_net_socketexception_handler(exc): if exc.message.startswith("Address family not supported by protocol family"): - return error(errno.EAFNOSUPPORT, 'Address family not supported by protocol family: See http://wiki.python.org/jython/NewSocketModule#IPV6addresssupport') + return error(errno.EAFNOSUPPORT, 'Address family not supported by protocol family: See http://wiki.python.org/jython/NewSocketModule#IPV6_address_support') return _unmapped_exception(exc) def would_block_error(exc=None): @@ -215,8 +215,28 @@ SOCK_SEQPACKET = 5 # not supported SOL_SOCKET = 0xFFFF -IPPROTO_TCP = 6 -IPPROTO_UDP = 17 + +IPPROTO_AH = 51 # not supported +IPPROTO_DSTOPTS = 60 # not supported +IPPROTO_ESP = 50 # not supported +IPPROTO_FRAGMENT = 44 # not supported +IPPROTO_GGP = 3 # not supported +IPPROTO_HOPOPTS = 0 # not supported +IPPROTO_ICMP = 1 # not supported +IPPROTO_ICMPV6 = 58 # not supported +IPPROTO_IDP = 22 # not supported +IPPROTO_IGMP = 2 # not supported +IPPROTO_IP = 0 +IPPROTO_IPV4 = 4 # not supported +IPPROTO_IPV6 = 41 # not supported +IPPROTO_MAX = 256 # not supported +IPPROTO_ND = 77 # not supported +IPPROTO_NONE = 59 # not supported +IPPROTO_PUP = 12 # not supported +IPPROTO_RAW = 255 # not supported +IPPROTO_ROUTING = 43 # not supported +IPPROTO_TCP = 6 +IPPROTO_UDP = 17 SO_BROADCAST = 1 SO_KEEPALIVE = 2 @@ -251,26 +271,58 @@ SO_TYPE = -1024 SO_USELOOPBACK = -2048 -__all__ = ['AF_UNSPEC', 'AF_INET', 'AF_INET6', 'AI_PASSIVE', 'SOCK_DGRAM', - 'SOCK_RAW', 'SOCK_RDM', 'SOCK_SEQPACKET', 'SOCK_STREAM', 'SOL_SOCKET', - 'SO_BROADCAST', 'SO_ERROR', 'SO_KEEPALIVE', 'SO_LINGER', 'SO_OOBINLINE', - 'SO_RCVBUF', 'SO_REUSEADDR', 'SO_SNDBUF', 'SO_TIMEOUT', 'TCP_NODELAY', - 'INADDR_ANY', 'INADDR_BROADCAST', 'IPPROTO_TCP', 'IPPROTO_UDP', - 'SocketType', 'error', 'herror', 'gaierror', 'timeout', - 'getfqdn', 'gethostbyaddr', 'gethostbyname', 'gethostname', - 'socket', 'getaddrinfo', 'getdefaulttimeout', 'setdefaulttimeout', - 'has_ipv6', 'htons', 'htonl', 'ntohs', 'ntohl', - 'SHUT_RD', 'SHUT_WR', 'SHUT_RDWR', - ] +__all__ = [ + # Families + 'AF_UNSPEC', 'AF_INET', 'AF_INET6', + # getaddrinfo and getnameinfo flags + 'AI_PASSIVE', 'AI_CANONNAME', 'AI_NUMERICHOST', 'AI_V4MAPPED', + 'AI_ALL', 'AI_ADDRCONFIG', 'AI_NUMERICSERV', 'EAI_NONAME', + 'EAI_SERVICE', 'EAI_ADDRFAMILY', + 'NI_NUMERICHOST', 'NI_NUMERICSERV', 'NI_NOFQDN', 'NI_NAMEREQD', + 'NI_DGRAM', 'NI_MAXSERV', 'NI_IDN', 'NI_IDN_ALLOW_UNASSIGNED', + 'NI_IDN_USE_STD3_ASCII_RULES', 'NI_MAXHOST', + # socket types + 'SOCK_DGRAM', 'SOCK_STREAM', 'SOCK_RAW', 'SOCK_RDM', 'SOCK_SEQPACKET', + # levels + 'SOL_SOCKET', + # protocols + 'IPPROTO_AH', 'IPPROTO_DSTOPTS', 'IPPROTO_ESP', 'IPPROTO_FRAGMENT', + 'IPPROTO_GGP', 'IPPROTO_HOPOPTS', 'IPPROTO_ICMP', 'IPPROTO_ICMPV6', + 'IPPROTO_IDP', 'IPPROTO_IGMP', 'IPPROTO_IP', 'IPPROTO_IPV4', + 'IPPROTO_IPV6', 'IPPROTO_MAX', 'IPPROTO_ND', 'IPPROTO_NONE', + 'IPPROTO_PUP', 'IPPROTO_RAW', 'IPPROTO_ROUTING', 'IPPROTO_TCP', + 'IPPROTO_UDP', + # Special hostnames + 'INADDR_ANY', 'INADDR_BROADCAST', 'IN6ADDR_ANY_INIT', + # support socket options + 'SO_BROADCAST', 'SO_KEEPALIVE', 'SO_LINGER', 'SO_OOBINLINE', + 'SO_RCVBUF', 'SO_REUSEADDR', 'SO_SNDBUF', 'SO_TIMEOUT', 'TCP_NODELAY', + # unsupported socket options + 'SO_ACCEPTCONN', 'SO_DEBUG', 'SO_DONTROUTE', 'SO_ERROR', + 'SO_EXCLUSIVEADDRUSE', 'SO_RCVLOWAT', 'SO_RCVTIMEO', 'SO_REUSEPORT', + 'SO_SNDLOWAT', 'SO_SNDTIMEO', 'SO_TYPE', 'SO_USELOOPBACK', + # functions + 'getfqdn', 'gethostname', 'gethostbyname', 'gethostbyaddr', + 'getservbyname', 'getservbyport', 'getprotobyname', 'getaddrinfo', + 'getnameinfo', 'getdefaulttimeout', 'setdefaulttimeout', 'htons', + 'htonl', 'ntohs', 'ntohl', 'inet_pton', 'inet_ntop', 'inet_aton', + 'inet_ntoa', 'create_connection', 'socket', 'ssl', + # exceptions + 'error', 'herror', 'gaierror', 'timeout', 'sslerror, + # classes + 'SocketType', + # Misc flags + 'has_ipv6', 'SHUT_RD', 'SHUT_WR', 'SHUT_RDWR', +] -def _constant_to_name(const_value): +def _constant_to_name(const_value, expected_name_starts): sock_module = sys.modules['socket'] try: for name in dir(sock_module): - if getattr(sock_module, name) is const_value and \ - (name.startswith('SO_') or name.startswith('SOL_') or \ - name.startswith('TCP_') or name.startswith('IPPROTO_')): - return name + if getattr(sock_module, name) is const_value: + for name_start in expected_name_starts: + if name.startswith(name_start): + return name return "Unknown" finally: sock_module = None @@ -320,7 +372,8 @@ return struct.pack('ii', enabled, linger_time) return result else: - raise error(errno.ENOPROTOOPT, "Socket option '%s' (level '%s') not supported on socket(%s)" % (_constant_to_name(option), _constant_to_name(level), str(self.jsocket))) + raise error(errno.ENOPROTOOPT, "Socket option '%s' (level '%s') not supported on socket(%s)" % \ + (_constant_to_name(option, ['SO_', 'TCP_']), _constant_to_name(level, ['SOL_', 'IPPROTO_']), str(self.jsocket))) def setsockopt(self, level, option, value): if (level, option) in self.options: @@ -330,7 +383,8 @@ else: getattr(self.jsocket, "set%s" % self.options[ (level, option) ])(value) else: - raise error(errno.ENOPROTOOPT, "Socket option '%s' (level '%s') not supported on socket(%s)" % (_constant_to_name(option), _constant_to_name(level), str(self.jsocket))) + raise error(errno.ENOPROTOOPT, "Socket option '%s' (level '%s') not supported on socket(%s)" % \ + (_constant_to_name(option, ['SO_', 'TCP_']), _constant_to_name(level, ['SOL_', 'IPPROTO_']), str(self.jsocket))) def close(self): self.jsocket.close() @@ -780,15 +834,18 @@ error_message = "Address must be a 2-tuple (ipv4: (host, port)) or a 4-tuple (ipv6: (host, port, flow, scope))" if not isinstance(address_object, tuple) or \ ((family == AF_INET and len(address_object) != 2) or (family == AF_INET6 and len(address_object) not in [2,4] )) or \ - not isinstance(address_object[0], basestring) or \ + not isinstance(address_object[0], (basestring, types.NoneType)) or \ not isinstance(address_object[1], (int, long)): raise TypeError(error_message) if len(address_object) == 4 and not isinstance(address_object[3], (int, long)): raise TypeError(error_message) - hostname, port = address_object[0].strip(), address_object[1] + hostname = address_object[0] + if hostname is not None: + hostname = hostname.strip() + port = address_object[1] if family == AF_INET and sock_type == SOCK_DGRAM and hostname == "": hostname = INADDR_BROADCAST - if hostname == "": + if hostname in ["", None]: if flags & AI_PASSIVE: hostname = {AF_INET: INADDR_ANY, AF_INET6: IN6ADDR_ANY_INIT}[family] else: @@ -810,9 +867,7 @@ _ipv4_addresses_only = value def _getaddrinfo_get_host(host, family, flags): - if host is None: - return host - if not isinstance(host, basestring): + if not isinstance(host, basestring) and host is not None: raise TypeError("getaddrinfo() argument 1 must be string or None") if flags & AI_NUMERICHOST: if not is_ip_address(host): @@ -845,7 +900,7 @@ int_port = int(port) return int_port % 65536 -def getaddrinfo(host, port, family=AF_INET, socktype=None, proto=0, flags=0): +def getaddrinfo(host, port, family=AF_UNSPEC, socktype=0, proto=0, flags=0): try: if _ipv4_addresses_only: family = AF_INET @@ -853,29 +908,43 @@ raise gaierror(errno.EIO, 'ai_family not supported') host = _getaddrinfo_get_host(host, family, flags) port = _getaddrinfo_get_port(port, flags) + if socktype not in [0, SOCK_DGRAM, SOCK_STREAM]: + raise error(errno.ESOCKTNOSUPPORT, "Socket type %s is not supported" % _constant_to_name(socktype, ['SOCK_'])) filter_fns = [] filter_fns.append({ AF_INET: lambda x: isinstance(x, java.net.Inet4Address), AF_INET6: lambda x: isinstance(x, java.net.Inet6Address), AF_UNSPEC: lambda x: isinstance(x, java.net.InetAddress), }[family]) - passive_mode = flags is not None and flags & AI_PASSIVE - canonname_mode = flags is not None and flags & AI_CANONNAME + if host in [None, ""]: + if flags & AI_PASSIVE: + hosts = {AF_INET: [INADDR_ANY], AF_INET6: [IN6ADDR_ANY_INIT], AF_UNSPEC: [INADDR_ANY, IN6ADDR_ANY_INIT]}[family] + else: + hosts = ["localhost"] + else: + hosts = [host] results = [] - for a in java.net.InetAddress.getAllByName(host): - if len([f for f in filter_fns if f(a)]): - family = {java.net.Inet4Address: AF_INET, java.net.Inet6Address: AF_INET6}[a.getClass()] - if passive_mode and not canonname_mode: - canonname = "" - else: - canonname = asPyString(a.getCanonicalHostName()) - if host is None and passive_mode and not canonname_mode: - sockaddr = INADDR_ANY - else: + for h in hosts: + for a in java.net.InetAddress.getAllByName(h): + if len([f for f in filter_fns if f(a)]): + family = {java.net.Inet4Address: AF_INET, java.net.Inet6Address: AF_INET6}[a.getClass()] + if flags & AI_CANONNAME: + canonname = asPyString(a.getCanonicalHostName()) + else: + canonname = "" sockaddr = asPyString(a.getHostAddress()) - # TODO: Include flowinfo and scopeid in a 4-tuple for IPv6 addresses - sock_tuple = {AF_INET : _ipv4_address_t, AF_INET6 : _ipv6_address_t}[family](sockaddr, port, a) - results.append((family, socktype, proto, canonname, sock_tuple)) + # TODO: Include flowinfo and scopeid in a 4-tuple for IPv6 addresses + sock_tuple = {AF_INET : _ipv4_address_t, AF_INET6 : _ipv6_address_t}[family](sockaddr, port, a) + if socktype == 0: + socktypes = [SOCK_DGRAM, SOCK_STREAM] + else: + socktypes = [socktype] + for result_socktype in socktypes: + result_proto = {SOCK_DGRAM: IPPROTO_UDP, SOCK_STREAM: IPPROTO_TCP}[result_socktype] + if proto in [0, result_proto]: + # The returned socket will only support the result_proto + # If this does not match the requested proto, don't return it + results.append((family, result_socktype, result_proto, canonname, sock_tuple)) return results except java.lang.Exception, jlx: raise _map_exception(jlx) @@ -1115,7 +1184,7 @@ assert not self.sock_impl assert not self.local_addr # Do the address format check - _get_jsockaddr(addr, self.family, self.type, self.proto, 0) + _get_jsockaddr(addr, self.family, self.type, self.proto, AI_PASSIVE) self.local_addr = addr def listen(self, backlog): @@ -1241,7 +1310,7 @@ assert not self.sock_impl assert not self.local_addr # Do the address format check - _get_jsockaddr(addr, self.family, self.type, self.proto, 0) + _get_jsockaddr(addr, self.family, self.type, self.proto, AI_PASSIVE) self.local_addr = addr self.sock_impl = _datagram_socket_impl(_get_jsockaddr(self.local_addr, self.family, self.type, self.proto, AI_PASSIVE), self.pending_options[ (SOL_SOCKET, SO_REUSEADDR) ]) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -281,8 +281,20 @@ def testConstantToNameMapping(self): # Testing for mission critical constants - for name in ['SOL_SOCKET', 'IPPROTO_TCP', 'IPPROTO_UDP', 'SO_BROADCAST', 'SO_KEEPALIVE', 'TCP_NODELAY', 'SO_ACCEPTCONN', 'SO_DEBUG']: - self.failUnlessEqual(socket._constant_to_name(getattr(socket, name)), name) + for name, expected_name_starts in [ + ('IPPROTO_ICMP', ['IPPROTO_']), + ('IPPROTO_TCP', ['IPPROTO_']), + ('IPPROTO_UDP', ['IPPROTO_']), + ('SO_BROADCAST', ['SO_', 'TCP_']), + ('SO_KEEPALIVE', ['SO_', 'TCP_']), + ('SO_ACCEPTCONN', ['SO_', 'TCP_']), + ('SO_DEBUG', ['SO_', 'TCP_']), + ('SOCK_DGRAM', ['SOCK_']), + ('SOCK_RAW', ['SOCK_']), + ('SOL_SOCKET', ['SOL_', 'IPPROTO_']), + ('TCP_NODELAY', ['SO_', 'TCP_']), + ]: + self.failUnlessEqual(socket._constant_to_name(getattr(socket, name), expected_name_starts), name) def testHostnameRes(self): # Testing hostname resolution mechanisms @@ -1642,6 +1654,46 @@ else: self.fail("getaddrinfo with bad family should have raised exception") + def testBadSockType(self): + for socktype in [socket.SOCK_RAW, socket.SOCK_RDM, socket.SOCK_SEQPACKET]: + try: + socket.getaddrinfo(HOST, PORT, socket.AF_UNSPEC, socktype) + except socket.error, se: + self.failUnlessEqual(se[0], errno.ESOCKTNOSUPPORT) + except Exception, x: + self.fail("getaddrinfo with bad socktype raised wrong exception: %s" % x) + else: + self.fail("getaddrinfo with bad socktype should have raised exception") + + def testBadSockTypeProtoCombination(self): + for socktype, proto in [ + (socket.SOCK_STREAM, socket.IPPROTO_UDP), + (socket.SOCK_STREAM, socket.IPPROTO_ICMP), + (socket.SOCK_DGRAM, socket.IPPROTO_TCP), + (socket.SOCK_DGRAM, socket.IPPROTO_FRAGMENT), + ]: + try: + results = socket.getaddrinfo(HOST, PORT, socket.AF_UNSPEC, socktype, proto) + self.failUnless(len(results) == 0, "getaddrinfo with bad socktype/proto combo should not have returned results") + except Exception, x: + self.fail("getaddrinfo with bad socktype/proto combo should not have raised exception") + + def testNoSockTypeWithProto(self): + for expect_results, proto in [ + (True, socket.IPPROTO_UDP), + (False, socket.IPPROTO_ICMP), + (True, socket.IPPROTO_TCP), + (False, socket.IPPROTO_FRAGMENT), + ]: + try: + results = socket.getaddrinfo(HOST, PORT, socket.AF_UNSPEC, 0, proto) + if expect_results: + self.failUnless(len(results) > 0, "getaddrinfo with no socktype and supported proto combo should have returned results") + else: + self.failUnless(len(results) == 0, "getaddrinfo with no socktype and unsupported proto combo should not have returned results") + except Exception, x: + self.fail("getaddrinfo with no socktype (un)supported proto combo should not have raised exception") + def testReturnsAreStrings(self): addrinfos = socket.getaddrinfo(HOST, PORT) for addrinfo in addrinfos: @@ -1776,12 +1828,15 @@ self.fail("getaddrinfo for unknown service name failed to raise exception") def testHostNames(self): - # None is always acceptable - for flags in [0, socket.AI_NUMERICHOST]: + # None is only acceptable if AI_NUMERICHOST is not specified + for flags, expect_exception in [(0, False), (socket.AI_NUMERICHOST, True)]: try: socket.getaddrinfo(None, 80, 0, 0, 0, flags) + if expect_exception: + self.fail("Non-numeric hostname == None should have raised exception") except Exception, x: - self.fail("hostname == None should not have raised exception: %s" % str(x)) + if not expect_exception: + self.fail("hostname == None should not have raised exception: %s" % str(x)) # Check enforcement of AI_NUMERICHOST for host in ["", " ", "localhost"]: @@ -1909,7 +1964,7 @@ (socket.AF_INET, ("localhost", 80), java.net.Inet4Address, ["127.0.0.1"]), (socket.AF_INET6, ("localhost", 80), java.net.Inet6Address, ["::1", "0:0:0:0:0:0:0:1"]), ]: - sockaddr = socket._get_jsockaddr(addr_tuple, family, None, 0, 0) + sockaddr = socket._get_jsockaddr(addr_tuple, family, 0, 0, 0) self.failUnless(isinstance(sockaddr, java.net.InetSocketAddress), "_get_jsockaddr returned wrong type: '%s'" % str(type(sockaddr))) self.failUnless(isinstance(sockaddr.address, jaddress_type), "_get_jsockaddr returned wrong address type: '%s'(family=%d)" % (str(type(sockaddr.address)), family)) self.failUnless(sockaddr.address.hostAddress in expected) @@ -1920,7 +1975,7 @@ ("localhost", 80), ("localhost", 80, 0, 0), ]: - sockaddr = socket._get_jsockaddr(addr_tuple, socket.AF_INET6, None, 0, 0) + sockaddr = socket._get_jsockaddr(addr_tuple, socket.AF_INET6, 0, 0, 0) self.failUnless(isinstance(sockaddr, java.net.InetSocketAddress), "_get_jsockaddr returned wrong type: '%s'" % str(type(sockaddr))) self.failUnless(isinstance(sockaddr.address, java.net.Inet6Address), "_get_jsockaddr returned wrong address type: '%s'" % str(type(sockaddr.address))) self.failUnless(sockaddr.address.hostAddress in ["::1", "0:0:0:0:0:0:0:1"]) @@ -1929,10 +1984,10 @@ def testSpecialHostnames(self): for family, sock_type, flags, addr_tuple, expected in [ - ( socket.AF_INET, None, 0, ("", 80), ["localhost"]), - ( socket.AF_INET, None, socket.AI_PASSIVE, ("", 80), [socket.INADDR_ANY]), - ( socket.AF_INET6, None, 0, ("", 80), ["localhost"]), - ( socket.AF_INET6, None, socket.AI_PASSIVE, ("", 80), [socket.IN6ADDR_ANY_INIT, "0:0:0:0:0:0:0:0"]), + ( socket.AF_INET, 0, 0, ("", 80), ["localhost"]), + ( socket.AF_INET, 0, socket.AI_PASSIVE, ("", 80), [socket.INADDR_ANY]), + ( socket.AF_INET6, 0, 0, ("", 80), ["localhost"]), + ( socket.AF_INET6, 0, socket.AI_PASSIVE, ("", 80), [socket.IN6ADDR_ANY_INIT, "0:0:0:0:0:0:0:0"]), ( socket.AF_INET, socket.SOCK_DGRAM, 0, ("", 80), [socket.INADDR_BROADCAST]), ]: sockaddr = socket._get_jsockaddr(addr_tuple, family, sock_type, 0, flags) @@ -1945,7 +2000,7 @@ ( socket.AF_INET6, 0, ["localhost"]), ( socket.AF_INET6, socket.AI_PASSIVE, [socket.IN6ADDR_ANY_INIT, "0:0:0:0:0:0:0:0"]), ]: - sockaddr = socket._get_jsockaddr(None, family, None, 0, flags) + sockaddr = socket._get_jsockaddr(None, family, 0, 0, flags) self.failUnless(sockaddr.hostName in expected, "_get_jsockaddr returned wrong hostname '%s' for sock tuple == None (family=%d)" % (sockaddr.hostName, family)) def testBadAddressTuples(self): -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Tue Aug 14 23:11:35 2012 From: jython-checkins at python.org (alan.kennedy) Date: Tue, 14 Aug 2012 23:11:35 +0200 (CEST) Subject: [Jython-checkins] =?utf-8?q?jython_=28merge_2=2E5_-=3E_default=29?= =?utf-8?q?=3A_merge_w/2=2E5=3A_Fixes_for_getaddrinfo?= Message-ID: <3WxRLv2s4tzQ6m@mail.python.org> http://hg.python.org/jython/rev/a423b9e08626 changeset: 6846:a423b9e08626 parent: 6844:d7e04f80af9a parent: 6845:d56c4119fed1 user: Alan Kennedy date: Tue Aug 14 22:10:15 2012 +0100 summary: merge w/2.5: Fixes for getaddrinfo files: Lib/socket.py | 159 ++++++++++++++++++++------- Lib/test/test_socket.py | 79 +++++++++++-- 2 files changed, 181 insertions(+), 57 deletions(-) diff --git a/Lib/socket.py b/Lib/socket.py --- a/Lib/socket.py +++ b/Lib/socket.py @@ -96,7 +96,7 @@ def java_net_socketexception_handler(exc): if exc.message.startswith("Address family not supported by protocol family"): return _add_exception_attrs(error(errno.EAFNOSUPPORT, - 'Address family not supported by protocol family: See http://wiki.python.org/jython/NewSocketModule#IPV6addresssupport')) + 'Address family not supported by protocol family: See http://wiki.python.org/jython/NewSocketModule#IPV6_address_support')) return _unmapped_exception(exc) def would_block_error(exc=None): @@ -220,8 +220,28 @@ SOCK_SEQPACKET = 5 # not supported SOL_SOCKET = 0xFFFF -IPPROTO_TCP = 6 -IPPROTO_UDP = 17 + +IPPROTO_AH = 51 # not supported +IPPROTO_DSTOPTS = 60 # not supported +IPPROTO_ESP = 50 # not supported +IPPROTO_FRAGMENT = 44 # not supported +IPPROTO_GGP = 3 # not supported +IPPROTO_HOPOPTS = 0 # not supported +IPPROTO_ICMP = 1 # not supported +IPPROTO_ICMPV6 = 58 # not supported +IPPROTO_IDP = 22 # not supported +IPPROTO_IGMP = 2 # not supported +IPPROTO_IP = 0 +IPPROTO_IPV4 = 4 # not supported +IPPROTO_IPV6 = 41 # not supported +IPPROTO_MAX = 256 # not supported +IPPROTO_ND = 77 # not supported +IPPROTO_NONE = 59 # not supported +IPPROTO_PUP = 12 # not supported +IPPROTO_RAW = 255 # not supported +IPPROTO_ROUTING = 43 # not supported +IPPROTO_TCP = 6 +IPPROTO_UDP = 17 SO_BROADCAST = 1 SO_KEEPALIVE = 2 @@ -256,26 +276,58 @@ SO_TYPE = -1024 SO_USELOOPBACK = -2048 -__all__ = ['AF_UNSPEC', 'AF_INET', 'AF_INET6', 'AI_PASSIVE', 'SOCK_DGRAM', - 'SOCK_RAW', 'SOCK_RDM', 'SOCK_SEQPACKET', 'SOCK_STREAM', 'SOL_SOCKET', - 'SO_BROADCAST', 'SO_ERROR', 'SO_KEEPALIVE', 'SO_LINGER', 'SO_OOBINLINE', - 'SO_RCVBUF', 'SO_REUSEADDR', 'SO_SNDBUF', 'SO_TIMEOUT', 'TCP_NODELAY', - 'INADDR_ANY', 'INADDR_BROADCAST', 'IPPROTO_TCP', 'IPPROTO_UDP', - 'SocketType', 'error', 'herror', 'gaierror', 'timeout', - 'getfqdn', 'gethostbyaddr', 'gethostbyname', 'gethostname', - 'socket', 'getaddrinfo', 'getdefaulttimeout', 'setdefaulttimeout', - 'has_ipv6', 'htons', 'htonl', 'ntohs', 'ntohl', - 'SHUT_RD', 'SHUT_WR', 'SHUT_RDWR', - ] +__all__ = [ + # Families + 'AF_UNSPEC', 'AF_INET', 'AF_INET6', + # getaddrinfo and getnameinfo flags + 'AI_PASSIVE', 'AI_CANONNAME', 'AI_NUMERICHOST', 'AI_V4MAPPED', + 'AI_ALL', 'AI_ADDRCONFIG', 'AI_NUMERICSERV', 'EAI_NONAME', + 'EAI_SERVICE', 'EAI_ADDRFAMILY', + 'NI_NUMERICHOST', 'NI_NUMERICSERV', 'NI_NOFQDN', 'NI_NAMEREQD', + 'NI_DGRAM', 'NI_MAXSERV', 'NI_IDN', 'NI_IDN_ALLOW_UNASSIGNED', + 'NI_IDN_USE_STD3_ASCII_RULES', 'NI_MAXHOST', + # socket types + 'SOCK_DGRAM', 'SOCK_STREAM', 'SOCK_RAW', 'SOCK_RDM', 'SOCK_SEQPACKET', + # levels + 'SOL_SOCKET', + # protocols + 'IPPROTO_AH', 'IPPROTO_DSTOPTS', 'IPPROTO_ESP', 'IPPROTO_FRAGMENT', + 'IPPROTO_GGP', 'IPPROTO_HOPOPTS', 'IPPROTO_ICMP', 'IPPROTO_ICMPV6', + 'IPPROTO_IDP', 'IPPROTO_IGMP', 'IPPROTO_IP', 'IPPROTO_IPV4', + 'IPPROTO_IPV6', 'IPPROTO_MAX', 'IPPROTO_ND', 'IPPROTO_NONE', + 'IPPROTO_PUP', 'IPPROTO_RAW', 'IPPROTO_ROUTING', 'IPPROTO_TCP', + 'IPPROTO_UDP', + # Special hostnames + 'INADDR_ANY', 'INADDR_BROADCAST', 'IN6ADDR_ANY_INIT', + # support socket options + 'SO_BROADCAST', 'SO_KEEPALIVE', 'SO_LINGER', 'SO_OOBINLINE', + 'SO_RCVBUF', 'SO_REUSEADDR', 'SO_SNDBUF', 'SO_TIMEOUT', 'TCP_NODELAY', + # unsupported socket options + 'SO_ACCEPTCONN', 'SO_DEBUG', 'SO_DONTROUTE', 'SO_ERROR', + 'SO_EXCLUSIVEADDRUSE', 'SO_RCVLOWAT', 'SO_RCVTIMEO', 'SO_REUSEPORT', + 'SO_SNDLOWAT', 'SO_SNDTIMEO', 'SO_TYPE', 'SO_USELOOPBACK', + # functions + 'getfqdn', 'gethostname', 'gethostbyname', 'gethostbyaddr', + 'getservbyname', 'getservbyport', 'getprotobyname', 'getaddrinfo', + 'getnameinfo', 'getdefaulttimeout', 'setdefaulttimeout', 'htons', + 'htonl', 'ntohs', 'ntohl', 'inet_pton', 'inet_ntop', 'inet_aton', + 'inet_ntoa', 'create_connection', 'socket', 'ssl', + # exceptions + 'error', 'herror', 'gaierror', 'timeout', 'sslerror, + # classes + 'SocketType', + # Misc flags + 'has_ipv6', 'SHUT_RD', 'SHUT_WR', 'SHUT_RDWR', +] -def _constant_to_name(const_value): +def _constant_to_name(const_value, expected_name_starts): sock_module = sys.modules['socket'] try: for name in dir(sock_module): - if getattr(sock_module, name) is const_value and \ - (name.startswith('SO_') or name.startswith('SOL_') or \ - name.startswith('TCP_') or name.startswith('IPPROTO_')): - return name + if getattr(sock_module, name) is const_value: + for name_start in expected_name_starts: + if name.startswith(name_start): + return name return "Unknown" finally: sock_module = None @@ -325,7 +377,8 @@ return struct.pack('ii', enabled, linger_time) return result else: - raise error(errno.ENOPROTOOPT, "Socket option '%s' (level '%s') not supported on socket(%s)" % (_constant_to_name(option), _constant_to_name(level), str(self.jsocket))) + raise error(errno.ENOPROTOOPT, "Socket option '%s' (level '%s') not supported on socket(%s)" % \ + (_constant_to_name(option, ['SO_', 'TCP_']), _constant_to_name(level, ['SOL_', 'IPPROTO_']), str(self.jsocket))) def setsockopt(self, level, option, value): if (level, option) in self.options: @@ -335,7 +388,8 @@ else: getattr(self.jsocket, "set%s" % self.options[ (level, option) ])(value) else: - raise error(errno.ENOPROTOOPT, "Socket option '%s' (level '%s') not supported on socket(%s)" % (_constant_to_name(option), _constant_to_name(level), str(self.jsocket))) + raise error(errno.ENOPROTOOPT, "Socket option '%s' (level '%s') not supported on socket(%s)" % \ + (_constant_to_name(option, ['SO_', 'TCP_']), _constant_to_name(level, ['SOL_', 'IPPROTO_']), str(self.jsocket))) def close(self): self.jsocket.close() @@ -785,15 +839,18 @@ error_message = "Address must be a 2-tuple (ipv4: (host, port)) or a 4-tuple (ipv6: (host, port, flow, scope))" if not isinstance(address_object, tuple) or \ ((family == AF_INET and len(address_object) != 2) or (family == AF_INET6 and len(address_object) not in [2,4] )) or \ - not isinstance(address_object[0], basestring) or \ + not isinstance(address_object[0], (basestring, types.NoneType)) or \ not isinstance(address_object[1], (int, long)): raise TypeError(error_message) if len(address_object) == 4 and not isinstance(address_object[3], (int, long)): raise TypeError(error_message) - hostname, port = address_object[0].strip(), address_object[1] + hostname = address_object[0] + if hostname is not None: + hostname = hostname.strip() + port = address_object[1] if family == AF_INET and sock_type == SOCK_DGRAM and hostname == "": hostname = INADDR_BROADCAST - if hostname == "": + if hostname in ["", None]: if flags & AI_PASSIVE: hostname = {AF_INET: INADDR_ANY, AF_INET6: IN6ADDR_ANY_INIT}[family] else: @@ -815,9 +872,7 @@ _ipv4_addresses_only = value def _getaddrinfo_get_host(host, family, flags): - if host is None: - return host - if not isinstance(host, basestring): + if not isinstance(host, basestring) and host is not None: raise TypeError("getaddrinfo() argument 1 must be string or None") if flags & AI_NUMERICHOST: if not is_ip_address(host): @@ -850,7 +905,7 @@ int_port = int(port) return int_port % 65536 -def getaddrinfo(host, port, family=AF_INET, socktype=None, proto=0, flags=0): +def getaddrinfo(host, port, family=AF_UNSPEC, socktype=0, proto=0, flags=0): try: if _ipv4_addresses_only: family = AF_INET @@ -858,29 +913,43 @@ raise gaierror(errno.EIO, 'ai_family not supported') host = _getaddrinfo_get_host(host, family, flags) port = _getaddrinfo_get_port(port, flags) + if socktype not in [0, SOCK_DGRAM, SOCK_STREAM]: + raise error(errno.ESOCKTNOSUPPORT, "Socket type %s is not supported" % _constant_to_name(socktype, ['SOCK_'])) filter_fns = [] filter_fns.append({ AF_INET: lambda x: isinstance(x, java.net.Inet4Address), AF_INET6: lambda x: isinstance(x, java.net.Inet6Address), AF_UNSPEC: lambda x: isinstance(x, java.net.InetAddress), }[family]) - passive_mode = flags is not None and flags & AI_PASSIVE - canonname_mode = flags is not None and flags & AI_CANONNAME + if host in [None, ""]: + if flags & AI_PASSIVE: + hosts = {AF_INET: [INADDR_ANY], AF_INET6: [IN6ADDR_ANY_INIT], AF_UNSPEC: [INADDR_ANY, IN6ADDR_ANY_INIT]}[family] + else: + hosts = ["localhost"] + else: + hosts = [host] results = [] - for a in java.net.InetAddress.getAllByName(host): - if len([f for f in filter_fns if f(a)]): - family = {java.net.Inet4Address: AF_INET, java.net.Inet6Address: AF_INET6}[a.getClass()] - if passive_mode and not canonname_mode: - canonname = "" - else: - canonname = asPyString(a.getCanonicalHostName()) - if host is None and passive_mode and not canonname_mode: - sockaddr = INADDR_ANY - else: + for h in hosts: + for a in java.net.InetAddress.getAllByName(h): + if len([f for f in filter_fns if f(a)]): + family = {java.net.Inet4Address: AF_INET, java.net.Inet6Address: AF_INET6}[a.getClass()] + if flags & AI_CANONNAME: + canonname = asPyString(a.getCanonicalHostName()) + else: + canonname = "" sockaddr = asPyString(a.getHostAddress()) - # TODO: Include flowinfo and scopeid in a 4-tuple for IPv6 addresses - sock_tuple = {AF_INET : _ipv4_address_t, AF_INET6 : _ipv6_address_t}[family](sockaddr, port, a) - results.append((family, socktype, proto, canonname, sock_tuple)) + # TODO: Include flowinfo and scopeid in a 4-tuple for IPv6 addresses + sock_tuple = {AF_INET : _ipv4_address_t, AF_INET6 : _ipv6_address_t}[family](sockaddr, port, a) + if socktype == 0: + socktypes = [SOCK_DGRAM, SOCK_STREAM] + else: + socktypes = [socktype] + for result_socktype in socktypes: + result_proto = {SOCK_DGRAM: IPPROTO_UDP, SOCK_STREAM: IPPROTO_TCP}[result_socktype] + if proto in [0, result_proto]: + # The returned socket will only support the result_proto + # If this does not match the requested proto, don't return it + results.append((family, result_socktype, result_proto, canonname, sock_tuple)) return results except java.lang.Exception, jlx: raise _map_exception(jlx) @@ -1120,7 +1189,7 @@ assert not self.sock_impl assert not self.local_addr # Do the address format check - _get_jsockaddr(addr, self.family, self.type, self.proto, 0) + _get_jsockaddr(addr, self.family, self.type, self.proto, AI_PASSIVE) self.local_addr = addr def listen(self, backlog): @@ -1246,7 +1315,7 @@ assert not self.sock_impl assert not self.local_addr # Do the address format check - _get_jsockaddr(addr, self.family, self.type, self.proto, 0) + _get_jsockaddr(addr, self.family, self.type, self.proto, AI_PASSIVE) self.local_addr = addr self.sock_impl = _datagram_socket_impl(_get_jsockaddr(self.local_addr, self.family, self.type, self.proto, AI_PASSIVE), self.pending_options[ (SOL_SOCKET, SO_REUSEADDR) ]) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -277,8 +277,20 @@ def testConstantToNameMapping(self): # Testing for mission critical constants - for name in ['SOL_SOCKET', 'IPPROTO_TCP', 'IPPROTO_UDP', 'SO_BROADCAST', 'SO_KEEPALIVE', 'TCP_NODELAY', 'SO_ACCEPTCONN', 'SO_DEBUG']: - self.failUnlessEqual(socket._constant_to_name(getattr(socket, name)), name) + for name, expected_name_starts in [ + ('IPPROTO_ICMP', ['IPPROTO_']), + ('IPPROTO_TCP', ['IPPROTO_']), + ('IPPROTO_UDP', ['IPPROTO_']), + ('SO_BROADCAST', ['SO_', 'TCP_']), + ('SO_KEEPALIVE', ['SO_', 'TCP_']), + ('SO_ACCEPTCONN', ['SO_', 'TCP_']), + ('SO_DEBUG', ['SO_', 'TCP_']), + ('SOCK_DGRAM', ['SOCK_']), + ('SOCK_RAW', ['SOCK_']), + ('SOL_SOCKET', ['SOL_', 'IPPROTO_']), + ('TCP_NODELAY', ['SO_', 'TCP_']), + ]: + self.failUnlessEqual(socket._constant_to_name(getattr(socket, name), expected_name_starts), name) def testHostnameRes(self): # Testing hostname resolution mechanisms @@ -1638,6 +1650,46 @@ else: self.fail("getaddrinfo with bad family should have raised exception") + def testBadSockType(self): + for socktype in [socket.SOCK_RAW, socket.SOCK_RDM, socket.SOCK_SEQPACKET]: + try: + socket.getaddrinfo(HOST, PORT, socket.AF_UNSPEC, socktype) + except socket.error, se: + self.failUnlessEqual(se[0], errno.ESOCKTNOSUPPORT) + except Exception, x: + self.fail("getaddrinfo with bad socktype raised wrong exception: %s" % x) + else: + self.fail("getaddrinfo with bad socktype should have raised exception") + + def testBadSockTypeProtoCombination(self): + for socktype, proto in [ + (socket.SOCK_STREAM, socket.IPPROTO_UDP), + (socket.SOCK_STREAM, socket.IPPROTO_ICMP), + (socket.SOCK_DGRAM, socket.IPPROTO_TCP), + (socket.SOCK_DGRAM, socket.IPPROTO_FRAGMENT), + ]: + try: + results = socket.getaddrinfo(HOST, PORT, socket.AF_UNSPEC, socktype, proto) + self.failUnless(len(results) == 0, "getaddrinfo with bad socktype/proto combo should not have returned results") + except Exception, x: + self.fail("getaddrinfo with bad socktype/proto combo should not have raised exception") + + def testNoSockTypeWithProto(self): + for expect_results, proto in [ + (True, socket.IPPROTO_UDP), + (False, socket.IPPROTO_ICMP), + (True, socket.IPPROTO_TCP), + (False, socket.IPPROTO_FRAGMENT), + ]: + try: + results = socket.getaddrinfo(HOST, PORT, socket.AF_UNSPEC, 0, proto) + if expect_results: + self.failUnless(len(results) > 0, "getaddrinfo with no socktype and supported proto combo should have returned results") + else: + self.failUnless(len(results) == 0, "getaddrinfo with no socktype and unsupported proto combo should not have returned results") + except Exception, x: + self.fail("getaddrinfo with no socktype (un)supported proto combo should not have raised exception") + def testReturnsAreStrings(self): addrinfos = socket.getaddrinfo(HOST, PORT) for addrinfo in addrinfos: @@ -1772,12 +1824,15 @@ self.fail("getaddrinfo for unknown service name failed to raise exception") def testHostNames(self): - # None is always acceptable - for flags in [0, socket.AI_NUMERICHOST]: + # None is only acceptable if AI_NUMERICHOST is not specified + for flags, expect_exception in [(0, False), (socket.AI_NUMERICHOST, True)]: try: socket.getaddrinfo(None, 80, 0, 0, 0, flags) + if expect_exception: + self.fail("Non-numeric hostname == None should have raised exception") except Exception, x: - self.fail("hostname == None should not have raised exception: %s" % str(x)) + if not expect_exception: + self.fail("hostname == None should not have raised exception: %s" % str(x)) # Check enforcement of AI_NUMERICHOST for host in ["", " ", "localhost"]: @@ -1905,7 +1960,7 @@ (socket.AF_INET, ("localhost", 80), java.net.Inet4Address, ["127.0.0.1"]), (socket.AF_INET6, ("localhost", 80), java.net.Inet6Address, ["::1", "0:0:0:0:0:0:0:1"]), ]: - sockaddr = socket._get_jsockaddr(addr_tuple, family, None, 0, 0) + sockaddr = socket._get_jsockaddr(addr_tuple, family, 0, 0, 0) self.failUnless(isinstance(sockaddr, java.net.InetSocketAddress), "_get_jsockaddr returned wrong type: '%s'" % str(type(sockaddr))) self.failUnless(isinstance(sockaddr.address, jaddress_type), "_get_jsockaddr returned wrong address type: '%s'(family=%d)" % (str(type(sockaddr.address)), family)) self.failUnless(sockaddr.address.hostAddress in expected) @@ -1916,7 +1971,7 @@ ("localhost", 80), ("localhost", 80, 0, 0), ]: - sockaddr = socket._get_jsockaddr(addr_tuple, socket.AF_INET6, None, 0, 0) + sockaddr = socket._get_jsockaddr(addr_tuple, socket.AF_INET6, 0, 0, 0) self.failUnless(isinstance(sockaddr, java.net.InetSocketAddress), "_get_jsockaddr returned wrong type: '%s'" % str(type(sockaddr))) self.failUnless(isinstance(sockaddr.address, java.net.Inet6Address), "_get_jsockaddr returned wrong address type: '%s'" % str(type(sockaddr.address))) self.failUnless(sockaddr.address.hostAddress in ["::1", "0:0:0:0:0:0:0:1"]) @@ -1925,10 +1980,10 @@ def testSpecialHostnames(self): for family, sock_type, flags, addr_tuple, expected in [ - ( socket.AF_INET, None, 0, ("", 80), ["localhost"]), - ( socket.AF_INET, None, socket.AI_PASSIVE, ("", 80), [socket.INADDR_ANY]), - ( socket.AF_INET6, None, 0, ("", 80), ["localhost"]), - ( socket.AF_INET6, None, socket.AI_PASSIVE, ("", 80), [socket.IN6ADDR_ANY_INIT, "0:0:0:0:0:0:0:0"]), + ( socket.AF_INET, 0, 0, ("", 80), ["localhost"]), + ( socket.AF_INET, 0, socket.AI_PASSIVE, ("", 80), [socket.INADDR_ANY]), + ( socket.AF_INET6, 0, 0, ("", 80), ["localhost"]), + ( socket.AF_INET6, 0, socket.AI_PASSIVE, ("", 80), [socket.IN6ADDR_ANY_INIT, "0:0:0:0:0:0:0:0"]), ( socket.AF_INET, socket.SOCK_DGRAM, 0, ("", 80), [socket.INADDR_BROADCAST]), ]: sockaddr = socket._get_jsockaddr(addr_tuple, family, sock_type, 0, flags) @@ -1941,7 +1996,7 @@ ( socket.AF_INET6, 0, ["localhost"]), ( socket.AF_INET6, socket.AI_PASSIVE, [socket.IN6ADDR_ANY_INIT, "0:0:0:0:0:0:0:0"]), ]: - sockaddr = socket._get_jsockaddr(None, family, None, 0, flags) + sockaddr = socket._get_jsockaddr(None, family, 0, 0, flags) self.failUnless(sockaddr.hostName in expected, "_get_jsockaddr returned wrong hostname '%s' for sock tuple == None (family=%d)" % (sockaddr.hostName, family)) def testBadAddressTuples(self): -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Fri Aug 17 18:10:09 2012 From: jython-checkins at python.org (philip.jenvey) Date: Fri, 17 Aug 2012 18:10:09 +0200 (CEST) Subject: [Jython-checkins] =?utf-8?q?jython=3A_remove_newSetFromMap_for_Ja?= =?utf-8?q?va_6=27s?= Message-ID: <3Wz8Wj3Nf2zPt2@mail.python.org> http://hg.python.org/jython/rev/9f45685d314a changeset: 6847:9f45685d314a user: Philip Jenvey date: Fri Aug 17 09:09:41 2012 -0700 summary: remove newSetFromMap for Java 6's files: src/com/ziclix/python/sql/PyConnection.java | 5 +- src/org/python/util/Generic.java | 120 +--------- 2 files changed, 3 insertions(+), 122 deletions(-) diff --git a/src/com/ziclix/python/sql/PyConnection.java b/src/com/ziclix/python/sql/PyConnection.java --- a/src/com/ziclix/python/sql/PyConnection.java +++ b/src/com/ziclix/python/sql/PyConnection.java @@ -24,7 +24,6 @@ import org.python.core.PyString; import org.python.core.PyUnicode; import org.python.core.ThreadState; -import org.python.util.Generic; import com.ziclix.python.sql.util.PyArgParser; @@ -90,10 +89,10 @@ */ public PyConnection(Connection connection) throws SQLException { this.closed = false; - cursors = Generic.newSetFromMap(new WeakHashMap()); + cursors = Collections.newSetFromMap(new WeakHashMap()); cursors = Collections.synchronizedSet(cursors); this.connection = connection; - statements = Generic.newSetFromMap(new WeakHashMap()); + statements = Collections.newSetFromMap(new WeakHashMap()); statements = Collections.synchronizedSet(statements); this.supportsTransactions = this.connection.getMetaData().supportsTransactions(); this.supportsMultipleResultSets = diff --git a/src/org/python/util/Generic.java b/src/org/python/util/Generic.java --- a/src/org/python/util/Generic.java +++ b/src/org/python/util/Generic.java @@ -91,125 +91,7 @@ * whatever this is being assigned to. */ public static Set concurrentSet() { - return newSetFromMap(Generic.concurrentMap()); - } - - /** - * Return a Set backed by the specified Map with the same ordering, concurrency and - * performance characteristics. - * - * The specified Map must be empty at the time this method is invoked. - * - * Note that this method is based on Java 6's Collections.newSetFromMap, and will be - * removed in a future version of Jython (likely 2.6) that will rely on Java 6. - * - * @param map the backing Map - * @return a Set backed by the Map - * @throws IllegalArgumentException if Map is not empty - */ - public static Set newSetFromMap(Map map) { - return new SetFromMap(map); - } - - /** - * A Set backed by a generic Map. - */ - private static class SetFromMap extends AbstractSet - implements Serializable { - - /** The backing Map. */ - private final Map map; - - /** Backing's KeySet. */ - private transient Set keySet; - - public SetFromMap(Map map) { - if (!map.isEmpty()) { - throw new IllegalArgumentException("Map is non-empty"); - } - this.map = map; - keySet = map.keySet(); - } - - @Override - public int size() { - return map.size(); - } - - @Override - public boolean isEmpty() { - return map.isEmpty(); - } - - @Override - public boolean contains(Object o) { - return map.containsKey(o); - } - - @Override - public boolean containsAll(Collection c) { - return keySet.containsAll(c); - } - - @Override - public Iterator iterator() { - return keySet.iterator(); - } - - @Override - public Object[] toArray() { - return keySet.toArray(); - } - - @Override - public T[] toArray(T[] a) { - return keySet.toArray(a); - } - - @Override - public boolean add(E e) { - return map.put(e, Boolean.TRUE) == null; - } - - @Override - public boolean remove(Object o) { - return map.remove(o) != null; - } - - @Override - public boolean removeAll(Collection c) { - return keySet.removeAll(c); - } - - @Override - public boolean retainAll(Collection c) { - return keySet.retainAll(c); - } - - @Override - public void clear() { - map.clear(); - } - - @Override - public boolean equals(Object o) { - return o == this || keySet.equals(o); - } - - @Override - public int hashCode() { - return keySet.hashCode(); - } - - @Override - public String toString() { - return keySet.toString(); - } - - private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { - in.defaultReadObject(); - keySet = map.keySet(); - } + return Collections.newSetFromMap(Generic.concurrentMap()); } } -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Tue Aug 21 00:52:35 2012 From: jython-checkins at python.org (alan.kennedy) Date: Tue, 21 Aug 2012 00:52:35 +0200 (CEST) Subject: [Jython-checkins] =?utf-8?q?jython=3A_Fixing_inheritance_on_socke?= =?utf-8?q?t_options=3A_=231309?= Message-ID: <3X19Jg5rl3zQ7V@mail.python.org> http://hg.python.org/jython/rev/cb7e31929f49 changeset: 6848:cb7e31929f49 user: Alan Kennedy date: Mon Aug 20 23:50:46 2012 +0100 summary: Fixing inheritance on socket options: #1309 files: Lib/socket.py | 28 ++++++++- Lib/test/test_socket.py | 81 +++++++++++++++++++++++----- 2 files changed, 92 insertions(+), 17 deletions(-) diff --git a/Lib/socket.py b/Lib/socket.py --- a/Lib/socket.py +++ b/Lib/socket.py @@ -413,13 +413,16 @@ (IPPROTO_TCP, TCP_NODELAY): 'TcpNoDelay', } - def __init__(self, socket=None): + def __init__(self, socket=None, pending_options=None): if socket: self.jchannel = socket.getChannel() else: self.jchannel = java.nio.channels.SocketChannel.open() self.jsocket = self.jchannel.socket() self.socketio = org.python.core.io.SocketIO(self.jchannel, 'rw') + if pending_options: + for level, optname in pending_options.keys(): + self.setsockopt(level, optname, pending_options[ (level, optname) ]) def bind(self, jsockaddr, reuse_addr): self.jsocket.setReuseAddress(reuse_addr) @@ -485,6 +488,7 @@ } def __init__(self, jsockaddr, backlog, reuse_addr): + self.pending_client_options = {} self.jchannel = java.nio.channels.ServerSocketChannel.open() self.jsocket = self.jchannel.socket() self.jsocket.setReuseAddress(reuse_addr) @@ -495,13 +499,13 @@ if self.mode in (MODE_BLOCKING, MODE_NONBLOCKING): new_cli_chan = self.jchannel.accept() if new_cli_chan is not None: - return _client_socket_impl(new_cli_chan.socket()) + return _client_socket_impl(new_cli_chan.socket(), self.pending_client_options) else: return None else: # In timeout mode now new_cli_sock = self.jsocket.accept() - return _client_socket_impl(new_cli_sock) + return _client_socket_impl(new_cli_sock, self.pending_client_options) def shutdown(self, how): # This is no-op on java, for server sockets. @@ -510,6 +514,24 @@ # later cause the user explicit close() call to fail pass + def getsockopt(self, level, option): + if self.options.has_key( (level, option) ): + return _nio_impl.getsockopt(self, level, option) + elif _client_socket_impl.options.has_key( (level, option) ): + return self.pending_client_options.get( (level, option), None) + else: + raise error(errno.ENOPROTOOPT, "Socket option '%s' (level '%s') not supported on socket(%s)" % \ + (_constant_to_name(option, ['SO_', 'TCP_']), _constant_to_name(level, ['SOL_', 'IPPROTO_']), str(self.jsocket))) + + def setsockopt(self, level, option, value): + if self.options.has_key( (level, option) ): + _nio_impl.setsockopt(self, level, option, value) + elif _client_socket_impl.options.has_key( (level, option) ): + self.pending_client_options[ (level, option) ] = value + else: + raise error(errno.ENOPROTOOPT, "Socket option '%s' (level '%s') not supported on socket(%s)" % \ + (_constant_to_name(option, ['SO_', 'TCP_']), _constant_to_name(level, ['SOL_', 'IPPROTO_']), str(self.jsocket))) + def getsockname(self): return (self.jsocket.getInetAddress().getHostAddress(), self.jsocket.getLocalPort()) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -727,8 +727,9 @@ self._testSetAndGetOption(sock, level, option, values) # now bind the socket i.e. cause the implementation socket to be created sock.bind( (HOST, PORT) ) - self.failUnlessEqual(sock.getsockopt(level, option), values[-1], \ - "Option value '(%s, %s)'='%s' did not propagate to implementation socket" % (level, option, values[-1]) ) + retrieved_option_value = sock.getsockopt(level, option) + self.failUnlessEqual(retrieved_option_value, values[-1], \ + "Option value '(%s, %s)'='%s' did not propagate to implementation socket: got %s" % (level, option, values[-1], retrieved_option_value) ) self._testSetAndGetOption(sock, level, option, values) finally: sock.close() @@ -738,6 +739,7 @@ try: # First listen on a server socket, so that the connection won't be refused. server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server_sock.bind( (HOST, PORT) ) server_sock.listen(50) # Now do the tests @@ -747,35 +749,66 @@ # First bind, so that the SO_REUSEADDR setting propagates sock.bind( (HOST, PORT+1) ) sock.connect( (HOST, PORT) ) - msg = "Option value '%s'='%s' did not propagate to implementation socket" % (option, values[-1]) + retrieved_option_value = sock.getsockopt(level, option) + msg = "Option value '%s'='%s' did not propagate to implementation socket: got %s" % (option, values[-1], retrieved_option_value) if option in (socket.SO_RCVBUF, socket.SO_SNDBUF): # NOTE: there's no guarantee that bufsize will be the # exact setsockopt value, particularly after # establishing a connection. seems it will be *at least* # the values we test (which are rather small) on # BSDs. - self.assert_(sock.getsockopt(level, option) >= values[-1], msg) + self.assert_(retrieved_option_value >= values[-1], msg) else: - self.failUnlessEqual(sock.getsockopt(level, option), values[-1], msg) + self.failUnlessEqual(retrieved_option_value, values[-1], msg) self._testSetAndGetOption(sock, level, option, values) finally: server_sock.close() if sock: sock.close() + def _testTCPClientInheritedOption(self, level, option, values): + cli_sock = accepted_sock = None + try: + server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + self._testSetAndGetOption(server_sock, level, option, values) + # now bind and listen on the socket i.e. cause the implementation socket to be created + server_sock.bind( (HOST, PORT) ) + server_sock.listen(50) + # Now create client socket to connect to server + cli_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + cli_sock.connect( (HOST, PORT) ) + accepted_sock = server_sock.accept()[0] + retrieved_option_value = accepted_sock.getsockopt(level, option) + msg = "Option value '(%s,%s)'='%s' did not propagate to accepted socket: got %s" % (level, option, values[-1], retrieved_option_value) + if option == socket.SO_RCVBUF: + # NOTE: see similar bsd/solaris workaround above + self.assert_(retrieved_option_value >= values[-1], msg) + else: + self.failUnlessEqual(retrieved_option_value, values[-1], msg) + self._testSetAndGetOption(accepted_sock, level, option, values) + finally: + server_sock.close() + if cli_sock: + cli_sock.close() + if accepted_sock: + accepted_sock.close() + def _testTCPServerOption(self, level, option, values): try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._testSetAndGetOption(sock, level, option, values) # now bind and listen on the socket i.e. cause the implementation socket to be created sock.bind( (HOST, PORT) ) sock.listen(50) - msg = "Option value '(%s,%s)'='%s' did not propagate to implementation socket" % (level, option, values[-1]) - if is_solaris and option == socket.SO_RCVBUF: + retrieved_option_value = sock.getsockopt(level, option) + msg = "Option value '(%s,%s)'='%s' did not propagate to implementation socket. Got %s" % (level, option, values[-1], retrieved_option_value) + if option == socket.SO_RCVBUF: # NOTE: see similar bsd/solaris workaround above - self.assert_(sock.getsockopt(level, option) >= values[-1], msg) + self.assert_(retrieved_option_value >= values[-1], msg) else: - self.failUnlessEqual(sock.getsockopt(level, option), values[-1], msg) + self.failUnlessEqual(retrieved_option_value, values[-1], msg) self._testSetAndGetOption(sock, level, option, values) finally: sock.close() @@ -783,8 +816,8 @@ def _testOption(self, level, option, values): for flag, func in [ (self.test_udp, self._testUDPOption), + (self.test_tcp_client, self._testTCPClientOption), (self.test_tcp_server, self._testTCPServerOption), - (self.test_tcp_client, self._testTCPClientOption), ]: if flag: func(level, option, values) @@ -798,6 +831,12 @@ else: self.fail("Setting unsupported option should have raised an exception") + def _testInheritedOption(self, level, option, values): + try: + self._testTCPClientInheritedOption(level, option, values) + except Exception, x: + self.fail("Inherited option should not have raised exception: %s" % str(x)) + class TestSupportedOptions(TestSocketOptions): def testSO_BROADCAST(self): @@ -806,44 +845,58 @@ def testSO_KEEPALIVE(self): self.test_tcp_client = 1 + self.test_tcp_server = 1 self._testOption(socket.SOL_SOCKET, socket.SO_KEEPALIVE, [0, 1]) + self._testInheritedOption(socket.SOL_SOCKET, socket.SO_KEEPALIVE, [0, 1]) def testSO_LINGER(self): self.test_tcp_client = 1 + self.test_tcp_server = 1 off = struct.pack('ii', 0, 0) on_2_seconds = struct.pack('ii', 1, 2) self._testOption(socket.SOL_SOCKET, socket.SO_LINGER, [off, on_2_seconds]) + self._testInheritedOption(socket.SOL_SOCKET, socket.SO_LINGER, [off, on_2_seconds]) def testSO_OOBINLINE(self): self.test_tcp_client = 1 + self.test_tcp_server = 1 self._testOption(socket.SOL_SOCKET, socket.SO_OOBINLINE, [0, 1]) + self._testInheritedOption(socket.SOL_SOCKET, socket.SO_OOBINLINE, [0, 1]) def testSO_RCVBUF(self): - self.test_udp = 1 + self.test_udp = 1 self.test_tcp_client = 1 self.test_tcp_server = 1 self._testOption(socket.SOL_SOCKET, socket.SO_RCVBUF, [1024, 4096, 16384]) + self._testInheritedOption(socket.SOL_SOCKET, socket.SO_RCVBUF, [1024, 4096, 16384]) def testSO_REUSEADDR(self): - self.test_udp = 1 + self.test_udp = 1 self.test_tcp_client = 1 self.test_tcp_server = 1 self._testOption(socket.SOL_SOCKET, socket.SO_REUSEADDR, [0, 1]) + self._testInheritedOption(socket.SOL_SOCKET, socket.SO_REUSEADDR, [0, 1]) def testSO_SNDBUF(self): - self.test_udp = 1 + self.test_udp = 1 self.test_tcp_client = 1 + self.test_tcp_server = 1 self._testOption(socket.SOL_SOCKET, socket.SO_SNDBUF, [1024, 4096, 16384]) + self._testInheritedOption(socket.SOL_SOCKET, socket.SO_SNDBUF, [1024, 4096, 16384]) def testSO_TIMEOUT(self): - self.test_udp = 1 + self.test_udp = 1 self.test_tcp_client = 1 self.test_tcp_server = 1 self._testOption(socket.SOL_SOCKET, socket.SO_TIMEOUT, [0, 1, 1000]) + # We don't test inheritance here because both server and client sockets have SO_TIMEOUT + # but it doesn't inherit. def testTCP_NODELAY(self): self.test_tcp_client = 1 + self.test_tcp_server = 1 self._testOption(socket.IPPROTO_TCP, socket.TCP_NODELAY, [0, 1]) + self._testInheritedOption(socket.IPPROTO_TCP, socket.TCP_NODELAY, [0, 1]) class TestUnsupportedOptions(TestSocketOptions): -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Tue Aug 21 00:54:45 2012 From: jython-checkins at python.org (alan.kennedy) Date: Tue, 21 Aug 2012 00:54:45 +0200 (CEST) Subject: [Jython-checkins] =?utf-8?q?jython=3A_NEWS_update?= Message-ID: <3X19M90qBwzQ9t@mail.python.org> http://hg.python.org/jython/rev/65906b1a45d8 changeset: 6849:65906b1a45d8 user: Alan Kennedy date: Mon Aug 20 23:53:41 2012 +0100 summary: NEWS update files: NEWS | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/NEWS b/NEWS --- a/NEWS +++ b/NEWS @@ -2,6 +2,7 @@ Jython 2.7a3 Bugs Fixed + - [ 1309 ] Server sockets do not support client options and propagate them to 'accept'ed client sockets. - [ 1951 ] Bytecode Interpreter stack optimization for larger arguments - [ 1921 ] compiler module broken in Jython 2.7 - [ 1920 ] Backport CO_FUTURE_PRINT_FUNCTION to Lib/compiler/pycodegen.py -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Thu Aug 23 01:31:52 2012 From: jython-checkins at python.org (alan.kennedy) Date: Thu, 23 Aug 2012 01:31:52 +0200 (CEST) Subject: [Jython-checkins] =?utf-8?q?jython=3A_Adding_support_for_SO=5FACC?= =?utf-8?q?EPTCONN=2C_SO=5FERROR=2C_and_SO=5FTYPE?= Message-ID: <3X2Q546zh2zQFM@mail.python.org> http://hg.python.org/jython/rev/a39725e023f2 changeset: 6850:a39725e023f2 user: Alan Kennedy date: Thu Aug 23 00:30:24 2012 +0100 summary: Adding support for SO_ACCEPTCONN, SO_ERROR, and SO_TYPE files: Lib/socket.py | 154 +++++++++++++++++---------- Lib/test/test_socket.py | 49 +++++++- 2 files changed, 137 insertions(+), 66 deletions(-) diff --git a/Lib/socket.py b/Lib/socket.py --- a/Lib/socket.py +++ b/Lib/socket.py @@ -150,13 +150,15 @@ } -def _map_exception(java_exception, circumstance=ALL): +def _map_exception(sock_object, java_exception, circumstance=ALL): mapped_exception = _exception_map.get((java_exception.__class__, circumstance)) if mapped_exception: py_exception = mapped_exception(java_exception) else: py_exception = error(-1, 'Unmapped exception: %s' % java_exception) setattr(py_exception, 'java_exception', java_exception) + if sock_object is not None: + setattr(sock_object, '_last_error', py_exception[0]) return _add_exception_attrs(py_exception) _feature_support_map = { @@ -243,16 +245,19 @@ IPPROTO_TCP = 6 IPPROTO_UDP = 17 -SO_BROADCAST = 1 -SO_KEEPALIVE = 2 -SO_LINGER = 4 -SO_OOBINLINE = 8 -SO_RCVBUF = 16 -SO_REUSEADDR = 32 -SO_SNDBUF = 64 -SO_TIMEOUT = 128 +SO_ACCEPTCONN = 1 +SO_BROADCAST = 2 +SO_ERROR = 4 +SO_KEEPALIVE = 8 +SO_LINGER = 16 +SO_OOBINLINE = 32 +SO_RCVBUF = 64 +SO_REUSEADDR = 128 +SO_SNDBUF = 256 +SO_TIMEOUT = 512 +SO_TYPE = 1024 -TCP_NODELAY = 256 +TCP_NODELAY = 2048 INADDR_ANY = "0.0.0.0" INADDR_BROADCAST = "255.255.255.255" @@ -263,18 +268,15 @@ # They are being added here so that code that refers to them # will not break with an AttributeError -SO_ACCEPTCONN = -1 -SO_DEBUG = -2 -SO_DONTROUTE = -4 -SO_ERROR = -8 -SO_EXCLUSIVEADDRUSE = -16 -SO_RCVLOWAT = -32 -SO_RCVTIMEO = -64 -SO_REUSEPORT = -128 -SO_SNDLOWAT = -256 -SO_SNDTIMEO = -512 -SO_TYPE = -1024 -SO_USELOOPBACK = -2048 +SO_DEBUG = -1 +SO_DONTROUTE = -1 +SO_EXCLUSIVEADDRUSE = -8 +SO_RCVLOWAT = -16 +SO_RCVTIMEO = -32 +SO_REUSEPORT = -64 +SO_SNDLOWAT = -128 +SO_SNDTIMEO = -256 +SO_USELOOPBACK = -512 __all__ = [ # Families @@ -692,13 +694,13 @@ try: return asPyString(java.net.InetAddress.getLocalHost().getHostName()) except java.lang.Exception, jlx: - raise _map_exception(jlx) + raise _map_exception(None, jlx) def gethostbyname(name): try: return asPyString(java.net.InetAddress.getByName(name).getHostAddress()) except java.lang.Exception, jlx: - raise _map_exception(jlx) + raise _map_exception(None, jlx) def gethostbyaddr(name): names, addrs = _gethostbyaddr(name) @@ -974,7 +976,7 @@ results.append((family, result_socktype, result_proto, canonname, sock_tuple)) return results except java.lang.Exception, jlx: - raise _map_exception(jlx) + raise _map_exception(None, jlx) def _getnameinfo_get_host(address, flags): if not isinstance(address, basestring): @@ -1057,7 +1059,7 @@ bytes.append(byte) return "".join([chr(byte) for byte in bytes]) except java.lang.Exception, jlx: - raise _map_exception(jlx) + raise _map_exception(None, jlx) def inet_ntop(family, packed_ip): try: @@ -1073,7 +1075,7 @@ ia = java.net.InetAddress.getByAddress(jByteArray) return ia.getHostAddress() except java.lang.Exception, jlx: - raise _map_exception(jlx) + raise _map_exception(None, jlx) def inet_aton(ip_string): return inet_pton(AF_INET, ip_string) @@ -1083,9 +1085,9 @@ class _nonblocking_api_mixin: - mode = MODE_BLOCKING + mode = MODE_BLOCKING reference_count = 0 - close_lock = threading.Lock() + close_lock = threading.Lock() def __init__(self): self.timeout = _defaulttimeout @@ -1122,40 +1124,52 @@ def setsockopt(self, level, optname, value): try: + self._last_error = 0 if self.sock_impl: self.sock_impl.setsockopt(level, optname, value) else: self.pending_options[ (level, optname) ] = value except java.lang.Exception, jlx: - raise _map_exception(jlx) + raise _map_exception(self, jlx) def getsockopt(self, level, optname): + # Handle "pseudo" options first + if level == SOL_SOCKET and optname == SO_TYPE: + return getattr(self, "type") + if level == SOL_SOCKET and optname == SO_ERROR: + return_value = self._last_error + self._last_error = 0 + return return_value + # Now handle "real" options try: if self.sock_impl: return self.sock_impl.getsockopt(level, optname) else: return self.pending_options.get( (level, optname), None) except java.lang.Exception, jlx: - raise _map_exception(jlx) + raise _map_exception(self, jlx) def shutdown(self, how): assert how in (SHUT_RD, SHUT_WR, SHUT_RDWR) if not self.sock_impl: raise error(errno.ENOTCONN, "Transport endpoint is not connected") try: + self._last_error = 0 self.sock_impl.shutdown(how) except java.lang.Exception, jlx: - raise _map_exception(jlx) + raise _map_exception(self, jlx) def close(self): try: + self._last_error = 0 if self.sock_impl: self.sock_impl.close() except java.lang.Exception, jlx: - raise _map_exception(jlx) + raise _map_exception(self, jlx) def getsockname(self): try: + self._last_error = 0 if self.sock_impl is None: # If the user has already bound an address, return that if self.local_addr: @@ -1165,15 +1179,16 @@ raise error(errno.EINVAL, "Invalid argument") return self.sock_impl.getsockname() except java.lang.Exception, jlx: - raise _map_exception(jlx) + raise _map_exception(self, jlx) def getpeername(self): try: + self._last_error = 0 if self.sock_impl is None: raise error(errno.ENOTCONN, "Socket is not connected") return self.sock_impl.getpeername() except java.lang.Exception, jlx: - raise _map_exception(jlx) + raise _map_exception(self, jlx) def _config(self): assert self.mode in _permitted_modes @@ -1198,16 +1213,23 @@ class _tcpsocket(_nonblocking_api_mixin): - sock_impl = None - istream = None - ostream = None - local_addr = None - server = 0 + sock_impl = None + istream = None + ostream = None + local_addr = None + server = 0 + _last_error = 0 def __init__(self): _nonblocking_api_mixin.__init__(self) + def getsockopt(self, level, optname): + if level == SOL_SOCKET and optname == SO_ACCEPTCONN: + return self.server + return _nonblocking_api_mixin.getsockopt(self, level, optname) + def bind(self, addr): + self._last_error = 0 assert not self.sock_impl assert not self.local_addr # Do the address format check @@ -1217,22 +1239,25 @@ def listen(self, backlog): "This signifies a server socket" try: + self._last_error = 0 assert not self.sock_impl self.server = 1 self.sock_impl = _server_socket_impl(_get_jsockaddr(self.local_addr, self.family, self.type, self.proto, AI_PASSIVE), backlog, self.pending_options[ (SOL_SOCKET, SO_REUSEADDR) ]) self._config() except java.lang.Exception, jlx: - raise _map_exception(jlx) + raise _map_exception(self, jlx) def accept(self): "This signifies a server socket" try: + self._last_error = 0 if not self.sock_impl: self.listen() assert self.server new_sock = self.sock_impl.accept() if not new_sock: + self._last_error = errno.EWOULDBLOCK raise would_block_error() cliconn = _tcpsocket() cliconn.pending_options[ (SOL_SOCKET, SO_REUSEADDR) ] = new_sock.jsocket.getReuseAddress() @@ -1240,10 +1265,11 @@ cliconn._setup() return cliconn, new_sock.getpeername() except java.lang.Exception, jlx: - raise _map_exception(jlx) + raise _map_exception(self, jlx) def _do_connect(self, addr): try: + self._last_error = 0 assert not self.sock_impl self.sock_impl = _client_socket_impl() if self.local_addr: # Has the socket been bound to a local address? @@ -1252,7 +1278,7 @@ self._config() # Configure timeouts, etc, now that the socket exists self.sock_impl.connect(_get_jsockaddr(addr, self.family, self.type, self.proto, 0)) except java.lang.Exception, jlx: - raise _map_exception(jlx) + raise _map_exception(self, jlx) def connect(self, addr): "This signifies a client socket" @@ -1277,6 +1303,7 @@ def recv(self, n): try: + self._last_error = 0 if not self.sock_impl: raise error(errno.ENOTCONN, 'Socket is not connected') if self.sock_impl.jchannel.isConnectionPending(): self.sock_impl.jchannel.finishConnect() @@ -1286,33 +1313,37 @@ return "" elif m <= 0: if self.mode == MODE_NONBLOCKING: + self._last_error = errno.EWOULDBLOCK raise would_block_error() return "" if m < n: data = data[:m] return data.tostring() except java.lang.Exception, jlx: - raise _map_exception(jlx) + raise _map_exception(self, jlx) def recvfrom(self, n): return self.recv(n), None def send(self, s): try: + self._last_error = 0 if not self.sock_impl: raise error(errno.ENOTCONN, 'Socket is not connected') if self.sock_impl.jchannel.isConnectionPending(): self.sock_impl.jchannel.finishConnect() numwritten = self.sock_impl.write(s) if numwritten == 0 and self.mode == MODE_NONBLOCKING: + self._last_error = errno.EWOULDBLOCK raise would_block_error() return numwritten except java.lang.Exception, jlx: - raise _map_exception(jlx) + raise _map_exception(self, jlx) sendall = send def close(self): try: + self._last_error = 0 if self.istream: self.istream.close() if self.ostream: @@ -1320,20 +1351,22 @@ if self.sock_impl: self.sock_impl.close() except java.lang.Exception, jlx: - raise _map_exception(jlx) + raise _map_exception(self, jlx) class _udpsocket(_nonblocking_api_mixin): - sock_impl = None - connected = False - local_addr = None + sock_impl = None + connected = False + local_addr = None + _last_error = 0 def __init__(self): _nonblocking_api_mixin.__init__(self) def bind(self, addr): try: + self._last_error = 0 assert not self.sock_impl assert not self.local_addr # Do the address format check @@ -1343,10 +1376,11 @@ self.pending_options[ (SOL_SOCKET, SO_REUSEADDR) ]) self._config() except java.lang.Exception, jlx: - raise _map_exception(jlx) + raise _map_exception(self, jlx) def _do_connect(self, addr): try: + self._last_error = 0 assert not self.connected, "Datagram Socket is already connected" if not self.sock_impl: self.sock_impl = _datagram_socket_impl() @@ -1354,7 +1388,7 @@ self.sock_impl.connect(_get_jsockaddr(addr, self.family, self.type, self.proto, 0)) self.connected = True except java.lang.Exception, jlx: - raise _map_exception(jlx) + raise _map_exception(self, jlx) def connect(self, addr): self._do_connect(addr) @@ -1366,6 +1400,7 @@ def sendto(self, data, p1, p2=None): try: + self._last_error = 0 if not p2: flags, addr = 0, p1 else: @@ -1377,9 +1412,10 @@ result = self.sock_impl.sendto(byte_array, _get_jsockaddr(addr, self.family, self.type, self.proto, 0), flags) return result except java.lang.Exception, jlx: - raise _map_exception(jlx) + raise _map_exception(self, jlx) def send(self, data, flags=None): + self._last_error = 0 if not self.connected: raise error(errno.ENOTCONN, "Socket is not connected") byte_array = java.lang.String(data).getBytes('iso-8859-1') return self.sock_impl.send(byte_array, flags) @@ -1393,6 +1429,7 @@ http://bugs.sun.com/view_bug.do?bug_id=6621689 """ try: + self._last_error = 0 # This is the old 2.1 behaviour #assert self.sock_impl # This is amak's preferred interpretation @@ -1403,14 +1440,15 @@ self._config() return self.sock_impl.recvfrom(num_bytes, flags) except java.lang.Exception, jlx: - raise _map_exception(jlx) + raise _map_exception(self, jlx) def recv(self, num_bytes, flags=None): if not self.sock_impl: raise error(errno.ENOTCONN, "Socket is not connected") try: + self._last_error = 0 return self.sock_impl.recv(num_bytes, flags) except java.lang.Exception, jlx: - raise _map_exception(jlx) + raise _map_exception(self, jlx) def __del__(self): self.close() @@ -1812,7 +1850,7 @@ self._in_buf = java.io.BufferedInputStream(self.ssl_sock.getInputStream()) self._out_buf = java.io.BufferedOutputStream(self.ssl_sock.getOutputStream()) except java.lang.Exception, jlx: - raise _map_exception(jlx) + raise _map_exception(self, jlx) def _make_ssl_socket(self, plain_socket, auto_close=0): java_net_socket = plain_socket._get_jsocket() @@ -1835,7 +1873,7 @@ data = data[:m] return data.tostring() except java.lang.Exception, jlx: - raise _map_exception(jlx) + raise _map_exception(self, jlx) def write(self, s): try: @@ -1843,13 +1881,13 @@ self._out_buf.flush() return len(s) except java.lang.Exception, jlx: - raise _map_exception(jlx) + raise _map_exception(self, jlx) def _get_server_cert(self): try: return self.ssl_sock.getSession().getPeerCertificates()[0] except java.lang.Exception, jlx: - raise _map_exception(jlx) + raise _map_exception(self, jlx) def server(self): cert = self._get_server_cert() diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -898,10 +898,48 @@ self._testOption(socket.IPPROTO_TCP, socket.TCP_NODELAY, [0, 1]) self._testInheritedOption(socket.IPPROTO_TCP, socket.TCP_NODELAY, [0, 1]) -class TestUnsupportedOptions(TestSocketOptions): +class TestPseudoOptions(unittest.TestCase): def testSO_ACCEPTCONN(self): - self.failUnless(hasattr(socket, 'SO_ACCEPTCONN')) + for socket_type, listen, expected_result in [ + #(socket.SOCK_STREAM, 0, 0), + #(socket.SOCK_STREAM, 1, 1), + (socket.SOCK_DGRAM, 0, Exception), + ]: + s = socket.socket(socket.AF_INET, socket_type) + if listen: + s.listen(1) + try: + result = s.getsockopt(socket.SOL_SOCKET, socket.SO_ACCEPTCONN) + if expected_result is not Exception: + self.failUnlessEqual(result, expected_result) + except socket.error, se: + if expected_result is Exception: + if se[0] != errno.ENOPROTOOPT: + self.fail("getsockopt(SO_ACCEPTCONN) on wrong socket type raised wrong exception: %s" % str(se)) + else: + self.fail("getsocket(SO_ACCEPTCONN) on valid socket type should not have raised exception: %s" % (str(se))) + + def testSO_ERROR(self): + for socket_type in [socket.SOCK_STREAM, socket.SOCK_DGRAM]: + s = socket.socket(socket.AF_INET, socket_type) + self.failUnlessEqual(s.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR), 0) + try: + # Now cause an error + s.connect(("localhost", 100000)) + self.fail("Operation '%s' that should have failed to generate SO_ERROR did not" % operation) + except socket.error, se: + so_error = s.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR) + self.failUnlessEqual(so_error, se[0]) + # Now retrieve the option again - it should be zero + self.failUnlessEqual(s.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR), 0) + + def testSO_TYPE(self): + for socket_type in [socket.SOCK_STREAM, socket.SOCK_DGRAM]: + s = socket.socket(socket.AF_INET, socket_type) + self.failUnlessEqual(s.getsockopt(socket.SOL_SOCKET, socket.SO_TYPE), socket_type) + +class TestUnsupportedOptions(TestSocketOptions): def testSO_DEBUG(self): self.failUnless(hasattr(socket, 'SO_DEBUG')) @@ -909,9 +947,6 @@ def testSO_DONTROUTE(self): self.failUnless(hasattr(socket, 'SO_DONTROUTE')) - def testSO_ERROR(self): - self.failUnless(hasattr(socket, 'SO_ERROR')) - def testSO_EXCLUSIVEADDRUSE(self): # this is an MS specific option that will not be appearing on java # http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6421091 @@ -935,9 +970,6 @@ def testSO_SNDTIMEO(self): self.failUnless(hasattr(socket, 'SO_SNDTIMEO')) - def testSO_TYPE(self): - self.failUnless(hasattr(socket, 'SO_TYPE')) - def testSO_USELOOPBACK(self): self.failUnless(hasattr(socket, 'SO_USELOOPBACK')) @@ -2429,6 +2461,7 @@ GeneralModuleTests, IPAddressTests, TestSupportedOptions, + TestPseudoOptions, TestUnsupportedOptions, BasicTCPTest, TCPServerTimeoutTest, -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Sat Aug 25 16:27:36 2012 From: jython-checkins at python.org (alan.kennedy) Date: Sat, 25 Aug 2012 16:27:36 +0200 (CEST) Subject: [Jython-checkins] =?utf-8?q?jython=3A_Coding_improvement=3A_a_muc?= =?utf-8?q?h_cleaner_method_of_tracking_last_error_on_a_socket?= Message-ID: <3X41sh5j9PzQH8@mail.python.org> http://hg.python.org/jython/rev/6579bd02d930 changeset: 6851:6579bd02d930 user: Alan Kennedy date: Sat Aug 25 15:26:10 2012 +0100 summary: Coding improvement: a much cleaner method of tracking last error on a socket files: Lib/socket.py | 108 ++++++++++++++------------- Lib/test/test_socket.py | 4 +- 2 files changed, 59 insertions(+), 53 deletions(-) diff --git a/Lib/socket.py b/Lib/socket.py --- a/Lib/socket.py +++ b/Lib/socket.py @@ -150,15 +150,13 @@ } -def _map_exception(sock_object, java_exception, circumstance=ALL): +def _map_exception(java_exception, circumstance=ALL): mapped_exception = _exception_map.get((java_exception.__class__, circumstance)) if mapped_exception: py_exception = mapped_exception(java_exception) else: py_exception = error(-1, 'Unmapped exception: %s' % java_exception) setattr(py_exception, 'java_exception', java_exception) - if sock_object is not None: - setattr(sock_object, '_last_error', py_exception[0]) return _add_exception_attrs(py_exception) _feature_support_map = { @@ -694,13 +692,13 @@ try: return asPyString(java.net.InetAddress.getLocalHost().getHostName()) except java.lang.Exception, jlx: - raise _map_exception(None, jlx) + raise _map_exception(jlx) def gethostbyname(name): try: return asPyString(java.net.InetAddress.getByName(name).getHostAddress()) except java.lang.Exception, jlx: - raise _map_exception(None, jlx) + raise _map_exception(jlx) def gethostbyaddr(name): names, addrs = _gethostbyaddr(name) @@ -976,7 +974,7 @@ results.append((family, result_socktype, result_proto, canonname, sock_tuple)) return results except java.lang.Exception, jlx: - raise _map_exception(None, jlx) + raise _map_exception(jlx) def _getnameinfo_get_host(address, flags): if not isinstance(address, basestring): @@ -1059,7 +1057,7 @@ bytes.append(byte) return "".join([chr(byte) for byte in bytes]) except java.lang.Exception, jlx: - raise _map_exception(None, jlx) + raise _map_exception(jlx) def inet_ntop(family, packed_ip): try: @@ -1075,7 +1073,7 @@ ia = java.net.InetAddress.getByAddress(jByteArray) return ia.getHostAddress() except java.lang.Exception, jlx: - raise _map_exception(None, jlx) + raise _map_exception(jlx) def inet_aton(ip_string): return inet_pton(AF_INET, ip_string) @@ -1083,6 +1081,18 @@ def inet_ntoa(packed_ip): return inet_ntop(AF_INET, packed_ip) +from functools import wraps +def raises_error(method): + @wraps(method) + def set_last_error(obj, *args, **kwargs): + try: + setattr(obj, '_last_error', 0) + return method(obj, *args, **kwargs) + except error, e: + setattr(obj, '_last_error', e[0]) + raise + return set_last_error + class _nonblocking_api_mixin: mode = MODE_BLOCKING @@ -1122,15 +1132,15 @@ def getblocking(self): return self.mode == MODE_BLOCKING + @raises_error def setsockopt(self, level, optname, value): try: - self._last_error = 0 if self.sock_impl: self.sock_impl.setsockopt(level, optname, value) else: self.pending_options[ (level, optname) ] = value except java.lang.Exception, jlx: - raise _map_exception(self, jlx) + raise _map_exception(jlx) def getsockopt(self, level, optname): # Handle "pseudo" options first @@ -1147,29 +1157,29 @@ else: return self.pending_options.get( (level, optname), None) except java.lang.Exception, jlx: - raise _map_exception(self, jlx) + raise _map_exception(jlx) + @raises_error def shutdown(self, how): assert how in (SHUT_RD, SHUT_WR, SHUT_RDWR) if not self.sock_impl: raise error(errno.ENOTCONN, "Transport endpoint is not connected") try: - self._last_error = 0 self.sock_impl.shutdown(how) except java.lang.Exception, jlx: - raise _map_exception(self, jlx) + raise _map_exception(jlx) + @raises_error def close(self): try: - self._last_error = 0 if self.sock_impl: self.sock_impl.close() except java.lang.Exception, jlx: - raise _map_exception(self, jlx) + raise _map_exception(jlx) + @raises_error def getsockname(self): try: - self._last_error = 0 if self.sock_impl is None: # If the user has already bound an address, return that if self.local_addr: @@ -1179,16 +1189,16 @@ raise error(errno.EINVAL, "Invalid argument") return self.sock_impl.getsockname() except java.lang.Exception, jlx: - raise _map_exception(self, jlx) + raise _map_exception(jlx) + @raises_error def getpeername(self): try: - self._last_error = 0 if self.sock_impl is None: raise error(errno.ENOTCONN, "Socket is not connected") return self.sock_impl.getpeername() except java.lang.Exception, jlx: - raise _map_exception(self, jlx) + raise _map_exception(jlx) def _config(self): assert self.mode in _permitted_modes @@ -1228,36 +1238,35 @@ return self.server return _nonblocking_api_mixin.getsockopt(self, level, optname) + @raises_error def bind(self, addr): - self._last_error = 0 assert not self.sock_impl assert not self.local_addr # Do the address format check _get_jsockaddr(addr, self.family, self.type, self.proto, AI_PASSIVE) self.local_addr = addr + @raises_error def listen(self, backlog): "This signifies a server socket" try: - self._last_error = 0 assert not self.sock_impl self.server = 1 self.sock_impl = _server_socket_impl(_get_jsockaddr(self.local_addr, self.family, self.type, self.proto, AI_PASSIVE), backlog, self.pending_options[ (SOL_SOCKET, SO_REUSEADDR) ]) self._config() except java.lang.Exception, jlx: - raise _map_exception(self, jlx) + raise _map_exception(jlx) + @raises_error def accept(self): "This signifies a server socket" try: - self._last_error = 0 if not self.sock_impl: self.listen() assert self.server new_sock = self.sock_impl.accept() if not new_sock: - self._last_error = errno.EWOULDBLOCK raise would_block_error() cliconn = _tcpsocket() cliconn.pending_options[ (SOL_SOCKET, SO_REUSEADDR) ] = new_sock.jsocket.getReuseAddress() @@ -1265,11 +1274,11 @@ cliconn._setup() return cliconn, new_sock.getpeername() except java.lang.Exception, jlx: - raise _map_exception(self, jlx) + raise _map_exception(jlx) + @raises_error def _do_connect(self, addr): try: - self._last_error = 0 assert not self.sock_impl self.sock_impl = _client_socket_impl() if self.local_addr: # Has the socket been bound to a local address? @@ -1278,7 +1287,7 @@ self._config() # Configure timeouts, etc, now that the socket exists self.sock_impl.connect(_get_jsockaddr(addr, self.family, self.type, self.proto, 0)) except java.lang.Exception, jlx: - raise _map_exception(self, jlx) + raise _map_exception(jlx) def connect(self, addr): "This signifies a client socket" @@ -1301,9 +1310,9 @@ self.istream = self.sock_impl.jsocket.getInputStream() self.ostream = self.sock_impl.jsocket.getOutputStream() + @raises_error def recv(self, n): try: - self._last_error = 0 if not self.sock_impl: raise error(errno.ENOTCONN, 'Socket is not connected') if self.sock_impl.jchannel.isConnectionPending(): self.sock_impl.jchannel.finishConnect() @@ -1313,37 +1322,35 @@ return "" elif m <= 0: if self.mode == MODE_NONBLOCKING: - self._last_error = errno.EWOULDBLOCK raise would_block_error() return "" if m < n: data = data[:m] return data.tostring() except java.lang.Exception, jlx: - raise _map_exception(self, jlx) + raise _map_exception(jlx) def recvfrom(self, n): return self.recv(n), None + @raises_error def send(self, s): try: - self._last_error = 0 if not self.sock_impl: raise error(errno.ENOTCONN, 'Socket is not connected') if self.sock_impl.jchannel.isConnectionPending(): self.sock_impl.jchannel.finishConnect() numwritten = self.sock_impl.write(s) if numwritten == 0 and self.mode == MODE_NONBLOCKING: - self._last_error = errno.EWOULDBLOCK raise would_block_error() return numwritten except java.lang.Exception, jlx: - raise _map_exception(self, jlx) + raise _map_exception(jlx) sendall = send + @raises_error def close(self): try: - self._last_error = 0 if self.istream: self.istream.close() if self.ostream: @@ -1351,7 +1358,7 @@ if self.sock_impl: self.sock_impl.close() except java.lang.Exception, jlx: - raise _map_exception(self, jlx) + raise _map_exception(jlx) class _udpsocket(_nonblocking_api_mixin): @@ -1364,9 +1371,9 @@ def __init__(self): _nonblocking_api_mixin.__init__(self) + @raises_error def bind(self, addr): - try: - self._last_error = 0 + try: assert not self.sock_impl assert not self.local_addr # Do the address format check @@ -1376,11 +1383,11 @@ self.pending_options[ (SOL_SOCKET, SO_REUSEADDR) ]) self._config() except java.lang.Exception, jlx: - raise _map_exception(self, jlx) + raise _map_exception(jlx) + @raises_error def _do_connect(self, addr): try: - self._last_error = 0 assert not self.connected, "Datagram Socket is already connected" if not self.sock_impl: self.sock_impl = _datagram_socket_impl() @@ -1388,7 +1395,7 @@ self.sock_impl.connect(_get_jsockaddr(addr, self.family, self.type, self.proto, 0)) self.connected = True except java.lang.Exception, jlx: - raise _map_exception(self, jlx) + raise _map_exception(jlx) def connect(self, addr): self._do_connect(addr) @@ -1398,9 +1405,9 @@ self._do_connect(addr) return 0 + @raises_error def sendto(self, data, p1, p2=None): try: - self._last_error = 0 if not p2: flags, addr = 0, p1 else: @@ -1412,14 +1419,14 @@ result = self.sock_impl.sendto(byte_array, _get_jsockaddr(addr, self.family, self.type, self.proto, 0), flags) return result except java.lang.Exception, jlx: - raise _map_exception(self, jlx) + raise _map_exception(jlx) def send(self, data, flags=None): - self._last_error = 0 if not self.connected: raise error(errno.ENOTCONN, "Socket is not connected") byte_array = java.lang.String(data).getBytes('iso-8859-1') return self.sock_impl.send(byte_array, flags) + @raises_error def recvfrom(self, num_bytes, flags=None): """ There is some disagreement as to what the behaviour should be if @@ -1429,7 +1436,6 @@ http://bugs.sun.com/view_bug.do?bug_id=6621689 """ try: - self._last_error = 0 # This is the old 2.1 behaviour #assert self.sock_impl # This is amak's preferred interpretation @@ -1440,15 +1446,15 @@ self._config() return self.sock_impl.recvfrom(num_bytes, flags) except java.lang.Exception, jlx: - raise _map_exception(self, jlx) + raise _map_exception(jlx) + @raises_error def recv(self, num_bytes, flags=None): if not self.sock_impl: raise error(errno.ENOTCONN, "Socket is not connected") try: - self._last_error = 0 return self.sock_impl.recv(num_bytes, flags) except java.lang.Exception, jlx: - raise _map_exception(self, jlx) + raise _map_exception(jlx) def __del__(self): self.close() @@ -1850,7 +1856,7 @@ self._in_buf = java.io.BufferedInputStream(self.ssl_sock.getInputStream()) self._out_buf = java.io.BufferedOutputStream(self.ssl_sock.getOutputStream()) except java.lang.Exception, jlx: - raise _map_exception(self, jlx) + raise _map_exception(jlx) def _make_ssl_socket(self, plain_socket, auto_close=0): java_net_socket = plain_socket._get_jsocket() @@ -1873,7 +1879,7 @@ data = data[:m] return data.tostring() except java.lang.Exception, jlx: - raise _map_exception(self, jlx) + raise _map_exception(jlx) def write(self, s): try: @@ -1881,13 +1887,13 @@ self._out_buf.flush() return len(s) except java.lang.Exception, jlx: - raise _map_exception(self, jlx) + raise _map_exception(jlx) def _get_server_cert(self): try: return self.ssl_sock.getSession().getPeerCertificates()[0] except java.lang.Exception, jlx: - raise _map_exception(self, jlx) + raise _map_exception(jlx) def server(self): cert = self._get_server_cert() diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -902,8 +902,8 @@ def testSO_ACCEPTCONN(self): for socket_type, listen, expected_result in [ - #(socket.SOCK_STREAM, 0, 0), - #(socket.SOCK_STREAM, 1, 1), + (socket.SOCK_STREAM, 0, 0), + (socket.SOCK_STREAM, 1, 1), (socket.SOCK_DGRAM, 0, Exception), ]: s = socket.socket(socket.AF_INET, socket_type) -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Sat Aug 25 22:49:21 2012 From: jython-checkins at python.org (alan.kennedy) Date: Sat, 25 Aug 2012 22:49:21 +0200 (CEST) Subject: [Jython-checkins] =?utf-8?q?jython=3A_Removing_old_versions_of_as?= =?utf-8?q?yncore=2Epy_and_test=5Fasyncore=2Epy?= Message-ID: <3X4BL9541yzQSZ@mail.python.org> http://hg.python.org/jython/rev/574b0d78be35 changeset: 6852:574b0d78be35 user: Alan Kennedy date: Sat Aug 25 18:38:22 2012 +0100 summary: Removing old versions of asyncore.py and test_asyncore.py files: Lib/asyncore.py | 551 ------------------- Lib/test/test_asyncore.py | 733 -------------------------- 2 files changed, 0 insertions(+), 1284 deletions(-) diff --git a/Lib/asyncore.py b/Lib/asyncore.py deleted file mode 100644 --- a/Lib/asyncore.py +++ /dev/null @@ -1,551 +0,0 @@ -# -*- Mode: Python -*- -# Id: asyncore.py,v 2.51 2000/09/07 22:29:26 rushing Exp -# Author: Sam Rushing - -# ====================================================================== -# Copyright 1996 by Sam Rushing -# -# All Rights Reserved -# -# Permission to use, copy, modify, and distribute this software and -# its documentation for any purpose and without fee is hereby -# granted, provided that the above copyright notice appear in all -# copies and that both that copyright notice and this permission -# notice appear in supporting documentation, and that the name of Sam -# Rushing not be used in advertising or publicity pertaining to -# distribution of the software without specific, written prior -# permission. -# -# SAM RUSHING DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, -# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN -# NO EVENT SHALL SAM RUSHING 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. -# ====================================================================== - -"""Basic infrastructure for asynchronous socket service clients and servers. - -There are only two ways to have a program on a single processor do "more -than one thing at a time". Multi-threaded programming is the simplest and -most popular way to do it, but there is another very different technique, -that lets you have nearly all the advantages of multi-threading, without -actually using multiple threads. it's really only practical if your program -is largely I/O bound. If your program is CPU bound, then pre-emptive -scheduled threads are probably what you really need. Network servers are -rarely CPU-bound, however. - -If your operating system supports the select() system call in its I/O -library (and nearly all do), then you can use it to juggle multiple -communication channels at once; doing other work while your I/O is taking -place in the "background." Although this strategy can seem strange and -complex, especially at first, it is in many ways easier to understand and -control than multi-threaded programming. The module documented here solves -many of the difficult problems for you, making the task of building -sophisticated high-performance network servers and clients a snap. -""" - -import select -import socket -import sys -import time - -import os -from errno import EALREADY, EINPROGRESS, EWOULDBLOCK, ECONNRESET, \ - ENOTCONN, ESHUTDOWN, EINTR, EISCONN, errorcode - -try: - socket_map -except NameError: - socket_map = {} - -class ExitNow(Exception): - pass - -def read(obj): - try: - obj.handle_read_event() - except ExitNow: - raise - except: - obj.handle_error() - -def write(obj): - try: - obj.handle_write_event() - except ExitNow: - raise - except: - obj.handle_error() - -def _exception (obj): - try: - obj.handle_expt_event() - except ExitNow: - raise - except: - obj.handle_error() - -def readwrite(obj, flags): - try: - if flags & (select.POLLIN | select.POLLPRI): - obj.handle_read_event() - if flags & select.POLLOUT: - obj.handle_write_event() - if flags & (select.POLLERR | select.POLLHUP | select.POLLNVAL): - obj.handle_expt_event() - except ExitNow: - raise - except: - obj.handle_error() - -def poll(timeout=0.0, map=None): - if map is None: - map = socket_map - if map: - r = []; w = []; e = [] - for fd, obj in map.items(): - is_r = obj.readable() - is_w = obj.writable() - if is_r: - r.append(fd) - if is_w: - w.append(fd) - if is_r or is_w: - e.append(fd) - if [] == r == w == e: - time.sleep(timeout) - else: - try: - r, w, e = select.select(r, w, e, timeout) - except select.error, err: - if err[0] != EINTR: - raise - else: - return - - for fd in r: - obj = map.get(fd) - if obj is None: - continue - read(obj) - - for fd in w: - obj = map.get(fd) - if obj is None: - continue - write(obj) - - for fd in e: - obj = map.get(fd) - if obj is None: - continue - _exception(obj) - -def poll2(timeout=0.0, map=None): - # Use the poll() support added to the select module in Python 2.0 - if map is None: - map = socket_map - if timeout is not None: - # timeout is in milliseconds - timeout = int(timeout*1000) - pollster = select.poll() - if map: - for fd, obj in map.items(): - flags = 0 - if obj.readable(): - flags |= select.POLLIN | select.POLLPRI - if obj.writable(): - flags |= select.POLLOUT - if flags: - # Only check for exceptions if object was either readable - # or writable. - flags |= select.POLLERR | select.POLLHUP | select.POLLNVAL - pollster.register(fd, flags) - try: - r = pollster.poll(timeout) - except select.error, err: - if err[0] != EINTR: - raise - r = [] - for fd, flags in r: - obj = map.get(fd) - if obj is None: - continue - readwrite(obj, flags) - -poll3 = poll2 # Alias for backward compatibility - -def loop(timeout=30.0, use_poll=True, map=None, count=None): - if map is None: - map = socket_map - - if use_poll and hasattr(select, 'poll'): - poll_fun = poll2 - else: - poll_fun = poll - - if count is None: - while map: - poll_fun(timeout, map) - - else: - while map and count > 0: - poll_fun(timeout, map) - count = count - 1 - -class dispatcher: - - debug = False - connected = False - accepting = False - closing = False - addr = None - - def __init__(self, sock=None, map=None): - if map is None: - self._map = socket_map - else: - self._map = map - - if sock: - self.set_socket(sock, map) - # I think it should inherit this anyway - self.socket.setblocking(0) - self.connected = True - # XXX Does the constructor require that the socket passed - # be connected? - try: - self.addr = sock.getpeername() - except socket.error: - # The addr isn't crucial - pass - else: - self.socket = None - - def __repr__(self): - status = [self.__class__.__module__+"."+self.__class__.__name__] - if self.accepting and self.addr: - status.append('listening') - elif self.connected: - status.append('connected') - if self.addr is not None: - try: - status.append('%s:%d' % self.addr) - except TypeError: - status.append(repr(self.addr)) - return '<%s at %#x>' % (' '.join(status), id(self)) - - def add_channel(self, map=None): - #self.log_info('adding channel %s' % self) - if map is None: - map = self._map - map[self._fileno] = self - - def del_channel(self, map=None): - fd = self._fileno - if map is None: - map = self._map - if map.has_key(fd): - #self.log_info('closing channel %d:%s' % (fd, self)) - del map[fd] - self._fileno = None - - def create_socket(self, family, type): - self.family_and_type = family, type - self.socket = socket.socket(family, type) - self.socket.setblocking(0) - self._fileno = self.socket - self.add_channel() - - def set_socket(self, sock, map=None): - self.socket = sock -## self.__dict__['socket'] = sock - self._fileno = sock - self.add_channel(map) - - def set_reuse_addr(self): - # try to re-use a server port if possible - try: - self.socket.setsockopt( - socket.SOL_SOCKET, socket.SO_REUSEADDR, - self.socket.getsockopt(socket.SOL_SOCKET, - socket.SO_REUSEADDR) | 1 - ) - except socket.error: - pass - - # ================================================== - # predicates for select() - # these are used as filters for the lists of sockets - # to pass to select(). - # ================================================== - - def readable(self): - return True - - def writable(self): - return True - - # ================================================== - # socket object methods. - # ================================================== - - def listen(self, num): - self.accepting = True - if os.name == 'nt' and num > 5: - num = 1 - return self.socket.listen(num) - - def bind(self, addr): - self.addr = addr - return self.socket.bind(addr) - - def connect(self, address): - self.connected = False - err = self.socket.connect_ex(address) - # XXX Should interpret Winsock return values - if err in (EINPROGRESS, EALREADY, EWOULDBLOCK): - return - if err in (0, EISCONN): - self.addr = address - self.connected = True - self.handle_connect() - else: - raise socket.error, (err, errorcode[err]) - - def accept(self): - # XXX can return either an address pair or None - try: - conn, addr = self.socket.accept() - return conn, addr - except socket.error, why: - if why[0] == EWOULDBLOCK: - pass - else: - raise - - def send(self, data): - try: - result = self.socket.send(data) - return result - except socket.error, why: - if why[0] == EWOULDBLOCK: - return 0 - else: - raise - return 0 - - def recv(self, buffer_size): - try: - data = self.socket.recv(buffer_size) - if not data: - # a closed connection is indicated by signaling - # a read condition, and having recv() return 0. - self.handle_close() - return '' - else: - return data - except socket.error, why: - # winsock sometimes throws ENOTCONN - if why[0] in [ECONNRESET, ENOTCONN, ESHUTDOWN]: - self.handle_close() - return '' - else: - raise - - def close(self): - self.del_channel() - self.socket.close() - - # cheap inheritance, used to pass all other attribute - # references to the underlying socket object. - def __getattr__(self, attr): - return getattr(self.socket, attr) - - # log and log_info may be overridden to provide more sophisticated - # logging and warning methods. In general, log is for 'hit' logging - # and 'log_info' is for informational, warning and error logging. - - def log(self, message): - sys.stderr.write('log: %s\n' % str(message)) - - def log_info(self, message, type='info'): - if __debug__ or type != 'info': - print '%s: %s' % (type, message) - - def handle_read_event(self): - if self.accepting: - # for an accepting socket, getting a read implies - # that we are connected - if not self.connected: - self.connected = True - self.handle_accept() - elif not self.connected: - self.handle_connect() - self.connected = True - self.handle_read() - else: - self.handle_read() - - def handle_write_event(self): - # getting a write implies that we are connected - if not self.connected: - self.handle_connect() - self.connected = True - self.handle_write() - - def handle_expt_event(self): - self.handle_expt() - - def handle_error(self): - nil, t, v, tbinfo = compact_traceback() - - # sometimes a user repr method will crash. - try: - self_repr = repr(self) - except: - self_repr = '<__repr__(self) failed for object at %0x>' % id(self) - - self.log_info( - 'uncaptured python exception, closing channel %s (%s:%s %s)' % ( - self_repr, - t, - v, - tbinfo - ), - 'error' - ) - self.close() - - def handle_expt(self): - self.log_info('unhandled exception', 'warning') - - def handle_read(self): - self.log_info('unhandled read event', 'warning') - - def handle_write(self): - self.log_info('unhandled write event', 'warning') - - def handle_connect(self): - self.log_info('unhandled connect event', 'warning') - - def handle_accept(self): - self.log_info('unhandled accept event', 'warning') - - def handle_close(self): - self.log_info('unhandled close event', 'warning') - self.close() - -# --------------------------------------------------------------------------- -# adds simple buffered output capability, useful for simple clients. -# [for more sophisticated usage use asynchat.async_chat] -# --------------------------------------------------------------------------- - -class dispatcher_with_send(dispatcher): - - def __init__(self, sock=None, map=None): - dispatcher.__init__(self, sock, map) - self.out_buffer = '' - - def initiate_send(self): - num_sent = 0 - num_sent = dispatcher.send(self, self.out_buffer[:512]) - self.out_buffer = self.out_buffer[num_sent:] - - def handle_write(self): - self.initiate_send() - - def writable(self): - return (not self.connected) or len(self.out_buffer) - - def send(self, data): - if self.debug: - self.log_info('sending %s' % repr(data)) - self.out_buffer = self.out_buffer + data - self.initiate_send() - -# --------------------------------------------------------------------------- -# used for debugging. -# --------------------------------------------------------------------------- - -def compact_traceback(): - t, v, tb = sys.exc_info() - tbinfo = [] - assert tb # Must have a traceback - while tb: - tbinfo.append(( - tb.tb_frame.f_code.co_filename, - tb.tb_frame.f_code.co_name, - str(tb.tb_lineno) - )) - tb = tb.tb_next - - # just to be safe - del tb - - file, function, line = tbinfo[-1] - info = ' '.join(['[%s|%s|%s]' % x for x in tbinfo]) - return (file, function, line), t, v, info - -def close_all(map=None): - if map is None: - map = socket_map - for x in map.values(): - x.socket.close() - map.clear() - -# Asynchronous File I/O: -# -# After a little research (reading man pages on various unixen, and -# digging through the linux kernel), I've determined that select() -# isn't meant for doing asynchronous file i/o. -# Heartening, though - reading linux/mm/filemap.c shows that linux -# supports asynchronous read-ahead. So _MOST_ of the time, the data -# will be sitting in memory for us already when we go to read it. -# -# What other OS's (besides NT) support async file i/o? [VMS?] -# -# Regardless, this is useful for pipes, and stdin/stdout... - -if os.name == 'posix': - import fcntl - - class file_wrapper: - # here we override just enough to make a file - # look like a socket for the purposes of asyncore. - - def __init__(self, fd): - self.fd = fd - - def recv(self, *args): - return os.read(self.fd, *args) - - def send(self, *args): - return os.write(self.fd, *args) - - read = recv - write = send - - def close(self): - os.close(self.fd) - - def fileno(self): - return self.fd - - class file_dispatcher(dispatcher): - - def __init__(self, fd, map=None): - dispatcher.__init__(self, None, map) - self.connected = True - self.set_file(fd) - # set it to non-blocking mode - flags = fcntl.fcntl(fd, fcntl.F_GETFL, 0) - flags = flags | os.O_NONBLOCK - fcntl.fcntl(fd, fcntl.F_SETFL, flags) - - def set_file(self, fd): - self._fileno = fd - self.socket = file_wrapper(fd) - self.add_channel() diff --git a/Lib/test/test_asyncore.py b/Lib/test/test_asyncore.py deleted file mode 100644 --- a/Lib/test/test_asyncore.py +++ /dev/null @@ -1,733 +0,0 @@ -import asyncore -import unittest -import select -import os -import socket -import sys -import time -import warnings -import errno - -from test import test_support -from test.test_support import TESTFN, run_unittest, unlink, is_jython -from StringIO import StringIO - -try: - import threading -except ImportError: - threading = None - -HOST = test_support.HOST - -class dummysocket: - def __init__(self): - self.closed = False - - def close(self): - self.closed = True - - def fileno(self): - return 42 - -class dummychannel: - def __init__(self): - self.socket = dummysocket() - - def close(self): - self.socket.close() - -class exitingdummy: - def __init__(self): - pass - - def handle_read_event(self): - raise asyncore.ExitNow() - - handle_write_event = handle_read_event - handle_close = handle_read_event - handle_expt_event = handle_read_event - -class crashingdummy: - def __init__(self): - self.error_handled = False - - def handle_read_event(self): - raise Exception() - - handle_write_event = handle_read_event - handle_close = handle_read_event - handle_expt_event = handle_read_event - - def handle_error(self): - self.error_handled = True - -# used when testing senders; just collects what it gets until newline is sent -def capture_server(evt, buf, serv): - try: - serv.listen(5) - conn, addr = serv.accept() - except socket.timeout: - pass - else: - n = 200 - while n > 0: - r, w, e = select.select([conn], [], []) - if r: - data = conn.recv(10) - # keep everything except for the newline terminator - buf.write(data.replace('\n', '')) - if '\n' in data: - break - n -= 1 - time.sleep(0.01) - - conn.close() - finally: - serv.close() - evt.set() - - -class HelperFunctionTests(unittest.TestCase): - def test_readwriteexc(self): - # Check exception handling behavior of read, write and _exception - - # check that ExitNow exceptions in the object handler method - # bubbles all the way up through asyncore read/write/_exception calls - tr1 = exitingdummy() - self.assertRaises(asyncore.ExitNow, asyncore.read, tr1) - self.assertRaises(asyncore.ExitNow, asyncore.write, tr1) - self.assertRaises(asyncore.ExitNow, asyncore._exception, tr1) - - # check that an exception other than ExitNow in the object handler - # method causes the handle_error method to get called - tr2 = crashingdummy() - asyncore.read(tr2) - self.assertEqual(tr2.error_handled, True) - - tr2 = crashingdummy() - asyncore.write(tr2) - self.assertEqual(tr2.error_handled, True) - - tr2 = crashingdummy() - asyncore._exception(tr2) - self.assertEqual(tr2.error_handled, True) - - # asyncore.readwrite uses constants in the select module that - # are not present in Windows systems (see this thread: - # http://mail.python.org/pipermail/python-list/2001-October/109973.html) - # These constants should be present as long as poll is available - - @unittest.skipIf(is_jython,"FIXME: not working in Jython") - @unittest.skipUnless(hasattr(select, 'poll'), 'select.poll required') - def test_readwrite(self): - # Check that correct methods are called by readwrite() - - attributes = ('read', 'expt', 'write', 'closed', 'error_handled') - - expected = ( - (select.POLLIN, 'read'), - (select.POLLPRI, 'expt'), - (select.POLLOUT, 'write'), - (select.POLLERR, 'closed'), - (select.POLLHUP, 'closed'), - (select.POLLNVAL, 'closed'), - ) - - class testobj: - def __init__(self): - self.read = False - self.write = False - self.closed = False - self.expt = False - self.error_handled = False - - def handle_read_event(self): - self.read = True - - def handle_write_event(self): - self.write = True - - def handle_close(self): - self.closed = True - - def handle_expt_event(self): - self.expt = True - - def handle_error(self): - self.error_handled = True - - for flag, expectedattr in expected: - tobj = testobj() - self.assertEqual(getattr(tobj, expectedattr), False) - asyncore.readwrite(tobj, flag) - - # Only the attribute modified by the routine we expect to be - # called should be True. - for attr in attributes: - self.assertEqual(getattr(tobj, attr), attr==expectedattr) - - # check that ExitNow exceptions in the object handler method - # bubbles all the way up through asyncore readwrite call - tr1 = exitingdummy() - self.assertRaises(asyncore.ExitNow, asyncore.readwrite, tr1, flag) - - # check that an exception other than ExitNow in the object handler - # method causes the handle_error method to get called - tr2 = crashingdummy() - self.assertEqual(tr2.error_handled, False) - asyncore.readwrite(tr2, flag) - self.assertEqual(tr2.error_handled, True) - - def test_closeall(self): - self.closeall_check(False) - - def test_closeall_default(self): - self.closeall_check(True) - - def closeall_check(self, usedefault): - # Check that close_all() closes everything in a given map - - l = [] - testmap = {} - for i in range(10): - c = dummychannel() - l.append(c) - self.assertEqual(c.socket.closed, False) - testmap[i] = c - - if usedefault: - socketmap = asyncore.socket_map - try: - asyncore.socket_map = testmap - asyncore.close_all() - finally: - testmap, asyncore.socket_map = asyncore.socket_map, socketmap - else: - asyncore.close_all(testmap) - - self.assertEqual(len(testmap), 0) - - for c in l: - self.assertEqual(c.socket.closed, True) - - def test_compact_traceback(self): - try: - raise Exception("I don't like spam!") - except: - real_t, real_v, real_tb = sys.exc_info() - r = asyncore.compact_traceback() - else: - self.fail("Expected exception") - - (f, function, line), t, v, info = r - self.assertEqual(os.path.split(f)[-1], 'test_asyncore.py') - self.assertEqual(function, 'test_compact_traceback') - self.assertEqual(t, real_t) - self.assertEqual(v, real_v) - self.assertEqual(info, '[%s|%s|%s]' % (f, function, line)) - - -class DispatcherTests(unittest.TestCase): - def setUp(self): - pass - - def tearDown(self): - asyncore.close_all() - - def test_basic(self): - d = asyncore.dispatcher() - self.assertEqual(d.readable(), True) - self.assertEqual(d.writable(), True) - - def test_repr(self): - d = asyncore.dispatcher() - self.assertEqual(repr(d), '' % id(d)) - - def test_log(self): - d = asyncore.dispatcher() - - # capture output of dispatcher.log() (to stderr) - fp = StringIO() - stderr = sys.stderr - l1 = "Lovely spam! Wonderful spam!" - l2 = "I don't like spam!" - try: - sys.stderr = fp - d.log(l1) - d.log(l2) - finally: - sys.stderr = stderr - - lines = fp.getvalue().splitlines() - self.assertEqual(lines, ['log: %s' % l1, 'log: %s' % l2]) - - def test_log_info(self): - d = asyncore.dispatcher() - - # capture output of dispatcher.log_info() (to stdout via print) - fp = StringIO() - stdout = sys.stdout - l1 = "Have you got anything without spam?" - l2 = "Why can't she have egg bacon spam and sausage?" - l3 = "THAT'S got spam in it!" - try: - sys.stdout = fp - d.log_info(l1, 'EGGS') - d.log_info(l2) - d.log_info(l3, 'SPAM') - finally: - sys.stdout = stdout - - lines = fp.getvalue().splitlines() - expected = ['EGGS: %s' % l1, 'info: %s' % l2, 'SPAM: %s' % l3] - - self.assertEqual(lines, expected) - - @unittest.skipIf(is_jython,"FIXME: not working in Jython") - def test_unhandled(self): - d = asyncore.dispatcher() - d.ignore_log_types = () - - # capture output of dispatcher.log_info() (to stdout via print) - fp = StringIO() - stdout = sys.stdout - try: - sys.stdout = fp - d.handle_expt() - d.handle_read() - d.handle_write() - d.handle_connect() - d.handle_accept() - finally: - sys.stdout = stdout - - lines = fp.getvalue().splitlines() - expected = ['warning: unhandled incoming priority event', - 'warning: unhandled read event', - 'warning: unhandled write event', - 'warning: unhandled connect event', - 'warning: unhandled accept event'] - self.assertEqual(lines, expected) - - @unittest.skipIf(is_jython,"FIXME: not working in Jython") - def test_issue_8594(self): - # XXX - this test is supposed to be removed in next major Python - # version - d = asyncore.dispatcher(socket.socket()) - # make sure the error message no longer refers to the socket - # object but the dispatcher instance instead - self.assertRaisesRegexp(AttributeError, 'dispatcher instance', - getattr, d, 'foo') - # cheap inheritance with the underlying socket is supposed - # to still work but a DeprecationWarning is expected - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always") - family = d.family - self.assertEqual(family, socket.AF_INET) - self.assertEqual(len(w), 1) - self.assertTrue(issubclass(w[0].category, DeprecationWarning)) - - @unittest.skipIf(is_jython,"FIXME: _strerror not supported by Jython") - def test_strerror(self): - # refers to bug #8573 - err = asyncore._strerror(errno.EPERM) - if hasattr(os, 'strerror'): - self.assertEqual(err, os.strerror(errno.EPERM)) - err = asyncore._strerror(-1) - self.assertTrue(err != "") - - -class dispatcherwithsend_noread(asyncore.dispatcher_with_send): - def readable(self): - return False - - def handle_connect(self): - pass - -class DispatcherWithSendTests(unittest.TestCase): - usepoll = False - - def setUp(self): - pass - - def tearDown(self): - asyncore.close_all() - - @unittest.skipIf(is_jython,"FIXME: not working in Jython") - @unittest.skipUnless(threading, 'Threading required for this test.') - @test_support.reap_threads - def test_send(self): - evt = threading.Event() - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - sock.settimeout(3) - port = test_support.bind_port(sock) - - cap = StringIO() - args = (evt, cap, sock) - t = threading.Thread(target=capture_server, args=args) - t.start() - try: - # wait a little longer for the server to initialize (it sometimes - # refuses connections on slow machines without this wait) - time.sleep(0.2) - - data = "Suppose there isn't a 16-ton weight?" - d = dispatcherwithsend_noread() - d.create_socket(socket.AF_INET, socket.SOCK_STREAM) - d.connect((HOST, port)) - - # give time for socket to connect - time.sleep(0.1) - - d.send(data) - d.send(data) - d.send('\n') - - n = 1000 - while d.out_buffer and n > 0: - asyncore.poll() - n -= 1 - - evt.wait() - - self.assertEqual(cap.getvalue(), data*2) - finally: - t.join() - - -class DispatcherWithSendTests_UsePoll(DispatcherWithSendTests): - usepoll = True - - at unittest.skipUnless(hasattr(asyncore, 'file_wrapper'), - 'asyncore.file_wrapper required') -class FileWrapperTest(unittest.TestCase): - def setUp(self): - self.d = "It's not dead, it's sleeping!" - with file(TESTFN, 'w') as h: - h.write(self.d) - - def tearDown(self): - unlink(TESTFN) - - def test_recv(self): - fd = os.open(TESTFN, os.O_RDONLY) - w = asyncore.file_wrapper(fd) - os.close(fd) - - self.assertNotEqual(w.fd, fd) - self.assertNotEqual(w.fileno(), fd) - self.assertEqual(w.recv(13), "It's not dead") - self.assertEqual(w.read(6), ", it's") - w.close() - self.assertRaises(OSError, w.read, 1) - - - @unittest.skipIf(is_jython,"FIXME: not working in Jython") - def test_send(self): - d1 = "Come again?" - d2 = "I want to buy some cheese." - fd = os.open(TESTFN, os.O_WRONLY | os.O_APPEND) - w = asyncore.file_wrapper(fd) - os.close(fd) - - w.write(d1) - w.send(d2) - w.close() - self.assertEqual(file(TESTFN).read(), self.d + d1 + d2) - - @unittest.skipUnless(hasattr(asyncore, 'file_dispatcher'), - 'asyncore.file_dispatcher required') - def test_dispatcher(self): - fd = os.open(TESTFN, os.O_RDONLY) - data = [] - class FileDispatcher(asyncore.file_dispatcher): - def handle_read(self): - data.append(self.recv(29)) - s = FileDispatcher(fd) - os.close(fd) - asyncore.loop(timeout=0.01, use_poll=True, count=2) - self.assertEqual(b"".join(data), self.d) - - -class BaseTestHandler(asyncore.dispatcher): - - def __init__(self, sock=None): - asyncore.dispatcher.__init__(self, sock) - self.flag = False - - def handle_accept(self): - raise Exception("handle_accept not supposed to be called") - - def handle_connect(self): - raise Exception("handle_connect not supposed to be called") - - def handle_expt(self): - raise Exception("handle_expt not supposed to be called") - - def handle_close(self): - raise Exception("handle_close not supposed to be called") - - def handle_error(self): - raise - - -class TCPServer(asyncore.dispatcher): - """A server which listens on an address and dispatches the - connection to a handler. - """ - - def __init__(self, handler=BaseTestHandler, host=HOST, port=0): - asyncore.dispatcher.__init__(self) - self.create_socket(socket.AF_INET, socket.SOCK_STREAM) - self.set_reuse_addr() - self.bind((host, port)) - self.listen(5) - self.handler = handler - - @property - def address(self): - return self.socket.getsockname()[:2] - - def handle_accept(self): - sock, addr = self.accept() - self.handler(sock) - - def handle_error(self): - raise - - -class BaseClient(BaseTestHandler): - - def __init__(self, address): - BaseTestHandler.__init__(self) - self.create_socket(socket.AF_INET, socket.SOCK_STREAM) - self.connect(address) - - def handle_connect(self): - pass - - -class BaseTestAPI(unittest.TestCase): - - def tearDown(self): - asyncore.close_all() - - def loop_waiting_for_flag(self, instance, timeout=5): - timeout = float(timeout) / 100 - count = 100 - while asyncore.socket_map and count > 0: - asyncore.loop(timeout=0.01, count=1, use_poll=self.use_poll) - if instance.flag: - return - count -= 1 - time.sleep(timeout) - self.fail("flag not set") - - def test_handle_connect(self): - # make sure handle_connect is called on connect() - - class TestClient(BaseClient): - def handle_connect(self): - self.flag = True - - server = TCPServer() - client = TestClient(server.address) - self.loop_waiting_for_flag(client) - - def test_handle_accept(self): - # make sure handle_accept() is called when a client connects - - class TestListener(BaseTestHandler): - - def __init__(self): - BaseTestHandler.__init__(self) - self.create_socket(socket.AF_INET, socket.SOCK_STREAM) - self.bind((HOST, 0)) - self.listen(5) - self.address = self.socket.getsockname()[:2] - - def handle_accept(self): - self.flag = True - - server = TestListener() - client = BaseClient(server.address) - self.loop_waiting_for_flag(server) - - def test_handle_read(self): - # make sure handle_read is called on data received - - class TestClient(BaseClient): - def handle_read(self): - self.flag = True - - class TestHandler(BaseTestHandler): - def __init__(self, conn): - BaseTestHandler.__init__(self, conn) - self.send('x' * 1024) - - server = TCPServer(TestHandler) - client = TestClient(server.address) - self.loop_waiting_for_flag(client) - - def test_handle_write(self): - # make sure handle_write is called - - class TestClient(BaseClient): - def handle_write(self): - self.flag = True - - server = TCPServer() - client = TestClient(server.address) - self.loop_waiting_for_flag(client) - - def test_handle_close(self): - # make sure handle_close is called when the other end closes - # the connection - - class TestClient(BaseClient): - - def handle_read(self): - # in order to make handle_close be called we are supposed - # to make at least one recv() call - self.recv(1024) - - def handle_close(self): - self.flag = True - self.close() - - class TestHandler(BaseTestHandler): - def __init__(self, conn): - BaseTestHandler.__init__(self, conn) - self.close() - - server = TCPServer(TestHandler) - client = TestClient(server.address) - self.loop_waiting_for_flag(client) - - - @unittest.skipIf(is_jython,"Not supported on Jython") - @unittest.skipIf(sys.platform.startswith("sunos"), - "OOB support is broken on Solaris") - def test_handle_expt(self): - # Make sure handle_expt is called on OOB data received. - # Note: this might fail on some platforms as OOB data is - # tenuously supported and rarely used. - - class TestClient(BaseClient): - def handle_expt(self): - self.flag = True - - class TestHandler(BaseTestHandler): - def __init__(self, conn): - BaseTestHandler.__init__(self, conn) - self.socket.send(chr(244), socket.MSG_OOB) - - server = TCPServer(TestHandler) - client = TestClient(server.address) - self.loop_waiting_for_flag(client) - - def test_handle_error(self): - - class TestClient(BaseClient): - def handle_write(self): - 1.0 / 0 - def handle_error(self): - self.flag = True - try: - raise - except ZeroDivisionError: - pass - else: - raise Exception("exception not raised") - - server = TCPServer() - client = TestClient(server.address) - self.loop_waiting_for_flag(client) - - @unittest.skipIf(is_jython,"FIXME: not working in Jython") - def test_connection_attributes(self): - server = TCPServer() - client = BaseClient(server.address) - - # we start disconnected - self.assertFalse(server.connected) - self.assertTrue(server.accepting) - # this can't be taken for granted across all platforms - #self.assertFalse(client.connected) - self.assertFalse(client.accepting) - - # execute some loops so that client connects to server - asyncore.loop(timeout=0.01, use_poll=self.use_poll, count=100) - self.assertFalse(server.connected) - self.assertTrue(server.accepting) - self.assertTrue(client.connected) - self.assertFalse(client.accepting) - - # disconnect the client - client.close() - self.assertFalse(server.connected) - self.assertTrue(server.accepting) - self.assertFalse(client.connected) - self.assertFalse(client.accepting) - - # stop serving - server.close() - self.assertFalse(server.connected) - self.assertFalse(server.accepting) - - def test_create_socket(self): - s = asyncore.dispatcher() - s.create_socket(socket.AF_INET, socket.SOCK_STREAM) - self.assertEqual(s.socket.family, socket.AF_INET) - self.assertEqual(s.socket.type, socket.SOCK_STREAM) - - @unittest.skipIf(is_jython,"FIXME: not working in Jython") - def test_bind(self): - s1 = asyncore.dispatcher() - s1.create_socket(socket.AF_INET, socket.SOCK_STREAM) - s1.bind((HOST, 0)) - s1.listen(5) - port = s1.socket.getsockname()[1] - - s2 = asyncore.dispatcher() - s2.create_socket(socket.AF_INET, socket.SOCK_STREAM) - # EADDRINUSE indicates the socket was correctly bound - self.assertRaises(socket.error, s2.bind, (HOST, port)) - - @unittest.skipIf(is_jython,"FIXME: not working in Jython") - def test_set_reuse_addr(self): - sock = socket.socket() - try: - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - except socket.error: - unittest.skip("SO_REUSEADDR not supported on this platform") - else: - # if SO_REUSEADDR succeeded for sock we expect asyncore - # to do the same - s = asyncore.dispatcher(socket.socket()) - self.assertFalse(s.socket.getsockopt(socket.SOL_SOCKET, - socket.SO_REUSEADDR)) - s.create_socket(socket.AF_INET, socket.SOCK_STREAM) - s.set_reuse_addr() - self.assertTrue(s.socket.getsockopt(socket.SOL_SOCKET, - socket.SO_REUSEADDR)) - finally: - sock.close() - - -class TestAPI_UseSelect(BaseTestAPI): - use_poll = False - - at unittest.skipUnless(hasattr(select, 'poll'), 'select.poll required') -class TestAPI_UsePoll(BaseTestAPI): - use_poll = True - - -def test_main(): - tests = [HelperFunctionTests, DispatcherTests, DispatcherWithSendTests, - DispatcherWithSendTests_UsePoll, TestAPI_UseSelect, - TestAPI_UsePoll, FileWrapperTest] - run_unittest(*tests) - -if __name__ == "__main__": - test_main() -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Sat Aug 25 22:49:23 2012 From: jython-checkins at python.org (alan.kennedy) Date: Sat, 25 Aug 2012 22:49:23 +0200 (CEST) Subject: [Jython-checkins] =?utf-8?q?jython=3A_Re-adding_latest_cpython_2?= =?utf-8?q?=2E7_asyncore=2Epy_and_test=5Fasyncore=2Epy_from?= Message-ID: <3X4BLC48drzQXT@mail.python.org> http://hg.python.org/jython/rev/7ea49e16dd54 changeset: 6853:7ea49e16dd54 user: Alan Kennedy date: Sat Aug 25 18:46:29 2012 +0100 summary: Re-adding latest cpython 2.7 asyncore.py and test_asyncore.py from http://hg.python.org/cpython/file/c1c45755397b files: Lib/asyncore.py | 659 +++++++++++++++++++++++ Lib/test/test_asyncore.py | 743 ++++++++++++++++++++++++++ 2 files changed, 1402 insertions(+), 0 deletions(-) diff --git a/Lib/asyncore.py b/Lib/asyncore.py new file mode 100644 --- /dev/null +++ b/Lib/asyncore.py @@ -0,0 +1,659 @@ +# -*- Mode: Python -*- +# Id: asyncore.py,v 2.51 2000/09/07 22:29:26 rushing Exp +# Author: Sam Rushing + +# ====================================================================== +# Copyright 1996 by Sam Rushing +# +# All Rights Reserved +# +# Permission to use, copy, modify, and distribute this software and +# its documentation for any purpose and without fee is hereby +# granted, provided that the above copyright notice appear in all +# copies and that both that copyright notice and this permission +# notice appear in supporting documentation, and that the name of Sam +# Rushing not be used in advertising or publicity pertaining to +# distribution of the software without specific, written prior +# permission. +# +# SAM RUSHING DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN +# NO EVENT SHALL SAM RUSHING 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. +# ====================================================================== + +"""Basic infrastructure for asynchronous socket service clients and servers. + +There are only two ways to have a program on a single processor do "more +than one thing at a time". Multi-threaded programming is the simplest and +most popular way to do it, but there is another very different technique, +that lets you have nearly all the advantages of multi-threading, without +actually using multiple threads. it's really only practical if your program +is largely I/O bound. If your program is CPU bound, then pre-emptive +scheduled threads are probably what you really need. Network servers are +rarely CPU-bound, however. + +If your operating system supports the select() system call in its I/O +library (and nearly all do), then you can use it to juggle multiple +communication channels at once; doing other work while your I/O is taking +place in the "background." Although this strategy can seem strange and +complex, especially at first, it is in many ways easier to understand and +control than multi-threaded programming. The module documented here solves +many of the difficult problems for you, making the task of building +sophisticated high-performance network servers and clients a snap. +""" + +import select +import socket +import sys +import time +import warnings + +import os +from errno import EALREADY, EINPROGRESS, EWOULDBLOCK, ECONNRESET, EINVAL, \ + ENOTCONN, ESHUTDOWN, EINTR, EISCONN, EBADF, ECONNABORTED, EPIPE, EAGAIN, \ + errorcode + +_DISCONNECTED = frozenset((ECONNRESET, ENOTCONN, ESHUTDOWN, ECONNABORTED, EPIPE, + EBADF)) + +try: + socket_map +except NameError: + socket_map = {} + +def _strerror(err): + try: + return os.strerror(err) + except (ValueError, OverflowError, NameError): + if err in errorcode: + return errorcode[err] + return "Unknown error %s" %err + +class ExitNow(Exception): + pass + +_reraised_exceptions = (ExitNow, KeyboardInterrupt, SystemExit) + +def read(obj): + try: + obj.handle_read_event() + except _reraised_exceptions: + raise + except: + obj.handle_error() + +def write(obj): + try: + obj.handle_write_event() + except _reraised_exceptions: + raise + except: + obj.handle_error() + +def _exception(obj): + try: + obj.handle_expt_event() + except _reraised_exceptions: + raise + except: + obj.handle_error() + +def readwrite(obj, flags): + try: + if flags & select.POLLIN: + obj.handle_read_event() + if flags & select.POLLOUT: + obj.handle_write_event() + if flags & select.POLLPRI: + obj.handle_expt_event() + if flags & (select.POLLHUP | select.POLLERR | select.POLLNVAL): + obj.handle_close() + except socket.error, e: + if e.args[0] not in _DISCONNECTED: + obj.handle_error() + else: + obj.handle_close() + except _reraised_exceptions: + raise + except: + obj.handle_error() + +def poll(timeout=0.0, map=None): + if map is None: + map = socket_map + if map: + r = []; w = []; e = [] + for fd, obj in map.items(): + is_r = obj.readable() + is_w = obj.writable() + if is_r: + r.append(fd) + # accepting sockets should not be writable + if is_w and not obj.accepting: + w.append(fd) + if is_r or is_w: + e.append(fd) + if [] == r == w == e: + time.sleep(timeout) + return + + try: + r, w, e = select.select(r, w, e, timeout) + except select.error, err: + if err.args[0] != EINTR: + raise + else: + return + + for fd in r: + obj = map.get(fd) + if obj is None: + continue + read(obj) + + for fd in w: + obj = map.get(fd) + if obj is None: + continue + write(obj) + + for fd in e: + obj = map.get(fd) + if obj is None: + continue + _exception(obj) + +def poll2(timeout=0.0, map=None): + # Use the poll() support added to the select module in Python 2.0 + if map is None: + map = socket_map + if timeout is not None: + # timeout is in milliseconds + timeout = int(timeout*1000) + pollster = select.poll() + if map: + for fd, obj in map.items(): + flags = 0 + if obj.readable(): + flags |= select.POLLIN | select.POLLPRI + # accepting sockets should not be writable + if obj.writable() and not obj.accepting: + flags |= select.POLLOUT + if flags: + # Only check for exceptions if object was either readable + # or writable. + flags |= select.POLLERR | select.POLLHUP | select.POLLNVAL + pollster.register(fd, flags) + try: + r = pollster.poll(timeout) + except select.error, err: + if err.args[0] != EINTR: + raise + r = [] + for fd, flags in r: + obj = map.get(fd) + if obj is None: + continue + readwrite(obj, flags) + +poll3 = poll2 # Alias for backward compatibility + +def loop(timeout=30.0, use_poll=False, map=None, count=None): + if map is None: + map = socket_map + + if use_poll and hasattr(select, 'poll'): + poll_fun = poll2 + else: + poll_fun = poll + + if count is None: + while map: + poll_fun(timeout, map) + + else: + while map and count > 0: + poll_fun(timeout, map) + count = count - 1 + +class dispatcher: + + debug = False + connected = False + accepting = False + connecting = False + closing = False + addr = None + ignore_log_types = frozenset(['warning']) + + def __init__(self, sock=None, map=None): + if map is None: + self._map = socket_map + else: + self._map = map + + self._fileno = None + + if sock: + # Set to nonblocking just to make sure for cases where we + # get a socket from a blocking source. + sock.setblocking(0) + self.set_socket(sock, map) + self.connected = True + # The constructor no longer requires that the socket + # passed be connected. + try: + self.addr = sock.getpeername() + except socket.error, err: + if err.args[0] in (ENOTCONN, EINVAL): + # To handle the case where we got an unconnected + # socket. + self.connected = False + else: + # The socket is broken in some unknown way, alert + # the user and remove it from the map (to prevent + # polling of broken sockets). + self.del_channel(map) + raise + else: + self.socket = None + + def __repr__(self): + status = [self.__class__.__module__+"."+self.__class__.__name__] + if self.accepting and self.addr: + status.append('listening') + elif self.connected: + status.append('connected') + if self.addr is not None: + try: + status.append('%s:%d' % self.addr) + except TypeError: + status.append(repr(self.addr)) + return '<%s at %#x>' % (' '.join(status), id(self)) + + __str__ = __repr__ + + def add_channel(self, map=None): + #self.log_info('adding channel %s' % self) + if map is None: + map = self._map + map[self._fileno] = self + + def del_channel(self, map=None): + fd = self._fileno + if map is None: + map = self._map + if fd in map: + #self.log_info('closing channel %d:%s' % (fd, self)) + del map[fd] + self._fileno = None + + def create_socket(self, family, type): + self.family_and_type = family, type + sock = socket.socket(family, type) + sock.setblocking(0) + self.set_socket(sock) + + def set_socket(self, sock, map=None): + self.socket = sock +## self.__dict__['socket'] = sock + self._fileno = sock.fileno() + self.add_channel(map) + + def set_reuse_addr(self): + # try to re-use a server port if possible + try: + self.socket.setsockopt( + socket.SOL_SOCKET, socket.SO_REUSEADDR, + self.socket.getsockopt(socket.SOL_SOCKET, + socket.SO_REUSEADDR) | 1 + ) + except socket.error: + pass + + # ================================================== + # predicates for select() + # these are used as filters for the lists of sockets + # to pass to select(). + # ================================================== + + def readable(self): + return True + + def writable(self): + return True + + # ================================================== + # socket object methods. + # ================================================== + + def listen(self, num): + self.accepting = True + if os.name == 'nt' and num > 5: + num = 5 + return self.socket.listen(num) + + def bind(self, addr): + self.addr = addr + return self.socket.bind(addr) + + def connect(self, address): + self.connected = False + self.connecting = True + err = self.socket.connect_ex(address) + if err in (EINPROGRESS, EALREADY, EWOULDBLOCK) \ + or err == EINVAL and os.name in ('nt', 'ce'): + self.addr = address + return + if err in (0, EISCONN): + self.addr = address + self.handle_connect_event() + else: + raise socket.error(err, errorcode[err]) + + def accept(self): + # XXX can return either an address pair or None + try: + conn, addr = self.socket.accept() + except TypeError: + return None + except socket.error as why: + if why.args[0] in (EWOULDBLOCK, ECONNABORTED, EAGAIN): + return None + else: + raise + else: + return conn, addr + + def send(self, data): + try: + result = self.socket.send(data) + return result + except socket.error, why: + if why.args[0] == EWOULDBLOCK: + return 0 + elif why.args[0] in _DISCONNECTED: + self.handle_close() + return 0 + else: + raise + + def recv(self, buffer_size): + try: + data = self.socket.recv(buffer_size) + if not data: + # a closed connection is indicated by signaling + # a read condition, and having recv() return 0. + self.handle_close() + return '' + else: + return data + except socket.error, why: + # winsock sometimes throws ENOTCONN + if why.args[0] in _DISCONNECTED: + self.handle_close() + return '' + else: + raise + + def close(self): + self.connected = False + self.accepting = False + self.connecting = False + self.del_channel() + try: + self.socket.close() + except socket.error, why: + if why.args[0] not in (ENOTCONN, EBADF): + raise + + # cheap inheritance, used to pass all other attribute + # references to the underlying socket object. + def __getattr__(self, attr): + try: + retattr = getattr(self.socket, attr) + except AttributeError: + raise AttributeError("%s instance has no attribute '%s'" + %(self.__class__.__name__, attr)) + else: + msg = "%(me)s.%(attr)s is deprecated. Use %(me)s.socket.%(attr)s " \ + "instead." % {'me': self.__class__.__name__, 'attr':attr} + warnings.warn(msg, DeprecationWarning, stacklevel=2) + return retattr + + # log and log_info may be overridden to provide more sophisticated + # logging and warning methods. In general, log is for 'hit' logging + # and 'log_info' is for informational, warning and error logging. + + def log(self, message): + sys.stderr.write('log: %s\n' % str(message)) + + def log_info(self, message, type='info'): + if type not in self.ignore_log_types: + print '%s: %s' % (type, message) + + def handle_read_event(self): + if self.accepting: + # accepting sockets are never connected, they "spawn" new + # sockets that are connected + self.handle_accept() + elif not self.connected: + if self.connecting: + self.handle_connect_event() + self.handle_read() + else: + self.handle_read() + + def handle_connect_event(self): + err = self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR) + if err != 0: + raise socket.error(err, _strerror(err)) + self.handle_connect() + self.connected = True + self.connecting = False + + def handle_write_event(self): + if self.accepting: + # Accepting sockets shouldn't get a write event. + # We will pretend it didn't happen. + return + + if not self.connected: + if self.connecting: + self.handle_connect_event() + self.handle_write() + + def handle_expt_event(self): + # handle_expt_event() is called if there might be an error on the + # socket, or if there is OOB data + # check for the error condition first + err = self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR) + if err != 0: + # we can get here when select.select() says that there is an + # exceptional condition on the socket + # since there is an error, we'll go ahead and close the socket + # like we would in a subclassed handle_read() that received no + # data + self.handle_close() + else: + self.handle_expt() + + def handle_error(self): + nil, t, v, tbinfo = compact_traceback() + + # sometimes a user repr method will crash. + try: + self_repr = repr(self) + except: + self_repr = '<__repr__(self) failed for object at %0x>' % id(self) + + self.log_info( + 'uncaptured python exception, closing channel %s (%s:%s %s)' % ( + self_repr, + t, + v, + tbinfo + ), + 'error' + ) + self.handle_close() + + def handle_expt(self): + self.log_info('unhandled incoming priority event', 'warning') + + def handle_read(self): + self.log_info('unhandled read event', 'warning') + + def handle_write(self): + self.log_info('unhandled write event', 'warning') + + def handle_connect(self): + self.log_info('unhandled connect event', 'warning') + + def handle_accept(self): + self.log_info('unhandled accept event', 'warning') + + def handle_close(self): + self.log_info('unhandled close event', 'warning') + self.close() + +# --------------------------------------------------------------------------- +# adds simple buffered output capability, useful for simple clients. +# [for more sophisticated usage use asynchat.async_chat] +# --------------------------------------------------------------------------- + +class dispatcher_with_send(dispatcher): + + def __init__(self, sock=None, map=None): + dispatcher.__init__(self, sock, map) + self.out_buffer = '' + + def initiate_send(self): + num_sent = 0 + num_sent = dispatcher.send(self, self.out_buffer[:512]) + self.out_buffer = self.out_buffer[num_sent:] + + def handle_write(self): + self.initiate_send() + + def writable(self): + return (not self.connected) or len(self.out_buffer) + + def send(self, data): + if self.debug: + self.log_info('sending %s' % repr(data)) + self.out_buffer = self.out_buffer + data + self.initiate_send() + +# --------------------------------------------------------------------------- +# used for debugging. +# --------------------------------------------------------------------------- + +def compact_traceback(): + t, v, tb = sys.exc_info() + tbinfo = [] + if not tb: # Must have a traceback + raise AssertionError("traceback does not exist") + while tb: + tbinfo.append(( + tb.tb_frame.f_code.co_filename, + tb.tb_frame.f_code.co_name, + str(tb.tb_lineno) + )) + tb = tb.tb_next + + # just to be safe + del tb + + file, function, line = tbinfo[-1] + info = ' '.join(['[%s|%s|%s]' % x for x in tbinfo]) + return (file, function, line), t, v, info + +def close_all(map=None, ignore_all=False): + if map is None: + map = socket_map + for x in map.values(): + try: + x.close() + except OSError, x: + if x.args[0] == EBADF: + pass + elif not ignore_all: + raise + except _reraised_exceptions: + raise + except: + if not ignore_all: + raise + map.clear() + +# Asynchronous File I/O: +# +# After a little research (reading man pages on various unixen, and +# digging through the linux kernel), I've determined that select() +# isn't meant for doing asynchronous file i/o. +# Heartening, though - reading linux/mm/filemap.c shows that linux +# supports asynchronous read-ahead. So _MOST_ of the time, the data +# will be sitting in memory for us already when we go to read it. +# +# What other OS's (besides NT) support async file i/o? [VMS?] +# +# Regardless, this is useful for pipes, and stdin/stdout... + +if os.name == 'posix': + import fcntl + + class file_wrapper: + # Here we override just enough to make a file + # look like a socket for the purposes of asyncore. + # The passed fd is automatically os.dup()'d + + def __init__(self, fd): + self.fd = os.dup(fd) + + def recv(self, *args): + return os.read(self.fd, *args) + + def send(self, *args): + return os.write(self.fd, *args) + + def getsockopt(self, level, optname, buflen=None): + if (level == socket.SOL_SOCKET and + optname == socket.SO_ERROR and + not buflen): + return 0 + raise NotImplementedError("Only asyncore specific behaviour " + "implemented.") + + read = recv + write = send + + def close(self): + os.close(self.fd) + + def fileno(self): + return self.fd + + class file_dispatcher(dispatcher): + + def __init__(self, fd, map=None): + dispatcher.__init__(self, None, map) + self.connected = True + try: + fd = fd.fileno() + except AttributeError: + pass + self.set_file(fd) + # set it to non-blocking mode + flags = fcntl.fcntl(fd, fcntl.F_GETFL, 0) + flags = flags | os.O_NONBLOCK + fcntl.fcntl(fd, fcntl.F_SETFL, flags) + + def set_file(self, fd): + self.socket = file_wrapper(fd) + self._fileno = self.socket.fileno() + self.add_channel() diff --git a/Lib/test/test_asyncore.py b/Lib/test/test_asyncore.py new file mode 100644 --- /dev/null +++ b/Lib/test/test_asyncore.py @@ -0,0 +1,743 @@ +import asyncore +import unittest +import select +import os +import socket +import sys +import time +import warnings +import errno +import struct + +from test import test_support +from test.test_support import TESTFN, run_unittest, unlink +from StringIO import StringIO + +try: + import threading +except ImportError: + threading = None + +HOST = test_support.HOST + +class dummysocket: + def __init__(self): + self.closed = False + + def close(self): + self.closed = True + + def fileno(self): + return 42 + +class dummychannel: + def __init__(self): + self.socket = dummysocket() + + def close(self): + self.socket.close() + +class exitingdummy: + def __init__(self): + pass + + def handle_read_event(self): + raise asyncore.ExitNow() + + handle_write_event = handle_read_event + handle_close = handle_read_event + handle_expt_event = handle_read_event + +class crashingdummy: + def __init__(self): + self.error_handled = False + + def handle_read_event(self): + raise Exception() + + handle_write_event = handle_read_event + handle_close = handle_read_event + handle_expt_event = handle_read_event + + def handle_error(self): + self.error_handled = True + +# used when testing senders; just collects what it gets until newline is sent +def capture_server(evt, buf, serv): + try: + serv.listen(5) + conn, addr = serv.accept() + except socket.timeout: + pass + else: + n = 200 + while n > 0: + r, w, e = select.select([conn], [], []) + if r: + data = conn.recv(10) + # keep everything except for the newline terminator + buf.write(data.replace('\n', '')) + if '\n' in data: + break + n -= 1 + time.sleep(0.01) + + conn.close() + finally: + serv.close() + evt.set() + + +class HelperFunctionTests(unittest.TestCase): + def test_readwriteexc(self): + # Check exception handling behavior of read, write and _exception + + # check that ExitNow exceptions in the object handler method + # bubbles all the way up through asyncore read/write/_exception calls + tr1 = exitingdummy() + self.assertRaises(asyncore.ExitNow, asyncore.read, tr1) + self.assertRaises(asyncore.ExitNow, asyncore.write, tr1) + self.assertRaises(asyncore.ExitNow, asyncore._exception, tr1) + + # check that an exception other than ExitNow in the object handler + # method causes the handle_error method to get called + tr2 = crashingdummy() + asyncore.read(tr2) + self.assertEqual(tr2.error_handled, True) + + tr2 = crashingdummy() + asyncore.write(tr2) + self.assertEqual(tr2.error_handled, True) + + tr2 = crashingdummy() + asyncore._exception(tr2) + self.assertEqual(tr2.error_handled, True) + + # asyncore.readwrite uses constants in the select module that + # are not present in Windows systems (see this thread: + # http://mail.python.org/pipermail/python-list/2001-October/109973.html) + # These constants should be present as long as poll is available + + @unittest.skipUnless(hasattr(select, 'poll'), 'select.poll required') + def test_readwrite(self): + # Check that correct methods are called by readwrite() + + attributes = ('read', 'expt', 'write', 'closed', 'error_handled') + + expected = ( + (select.POLLIN, 'read'), + (select.POLLPRI, 'expt'), + (select.POLLOUT, 'write'), + (select.POLLERR, 'closed'), + (select.POLLHUP, 'closed'), + (select.POLLNVAL, 'closed'), + ) + + class testobj: + def __init__(self): + self.read = False + self.write = False + self.closed = False + self.expt = False + self.error_handled = False + + def handle_read_event(self): + self.read = True + + def handle_write_event(self): + self.write = True + + def handle_close(self): + self.closed = True + + def handle_expt_event(self): + self.expt = True + + def handle_error(self): + self.error_handled = True + + for flag, expectedattr in expected: + tobj = testobj() + self.assertEqual(getattr(tobj, expectedattr), False) + asyncore.readwrite(tobj, flag) + + # Only the attribute modified by the routine we expect to be + # called should be True. + for attr in attributes: + self.assertEqual(getattr(tobj, attr), attr==expectedattr) + + # check that ExitNow exceptions in the object handler method + # bubbles all the way up through asyncore readwrite call + tr1 = exitingdummy() + self.assertRaises(asyncore.ExitNow, asyncore.readwrite, tr1, flag) + + # check that an exception other than ExitNow in the object handler + # method causes the handle_error method to get called + tr2 = crashingdummy() + self.assertEqual(tr2.error_handled, False) + asyncore.readwrite(tr2, flag) + self.assertEqual(tr2.error_handled, True) + + def test_closeall(self): + self.closeall_check(False) + + def test_closeall_default(self): + self.closeall_check(True) + + def closeall_check(self, usedefault): + # Check that close_all() closes everything in a given map + + l = [] + testmap = {} + for i in range(10): + c = dummychannel() + l.append(c) + self.assertEqual(c.socket.closed, False) + testmap[i] = c + + if usedefault: + socketmap = asyncore.socket_map + try: + asyncore.socket_map = testmap + asyncore.close_all() + finally: + testmap, asyncore.socket_map = asyncore.socket_map, socketmap + else: + asyncore.close_all(testmap) + + self.assertEqual(len(testmap), 0) + + for c in l: + self.assertEqual(c.socket.closed, True) + + def test_compact_traceback(self): + try: + raise Exception("I don't like spam!") + except: + real_t, real_v, real_tb = sys.exc_info() + r = asyncore.compact_traceback() + else: + self.fail("Expected exception") + + (f, function, line), t, v, info = r + self.assertEqual(os.path.split(f)[-1], 'test_asyncore.py') + self.assertEqual(function, 'test_compact_traceback') + self.assertEqual(t, real_t) + self.assertEqual(v, real_v) + self.assertEqual(info, '[%s|%s|%s]' % (f, function, line)) + + +class DispatcherTests(unittest.TestCase): + def setUp(self): + pass + + def tearDown(self): + asyncore.close_all() + + def test_basic(self): + d = asyncore.dispatcher() + self.assertEqual(d.readable(), True) + self.assertEqual(d.writable(), True) + + def test_repr(self): + d = asyncore.dispatcher() + self.assertEqual(repr(d), '' % id(d)) + + def test_log(self): + d = asyncore.dispatcher() + + # capture output of dispatcher.log() (to stderr) + fp = StringIO() + stderr = sys.stderr + l1 = "Lovely spam! Wonderful spam!" + l2 = "I don't like spam!" + try: + sys.stderr = fp + d.log(l1) + d.log(l2) + finally: + sys.stderr = stderr + + lines = fp.getvalue().splitlines() + self.assertEqual(lines, ['log: %s' % l1, 'log: %s' % l2]) + + def test_log_info(self): + d = asyncore.dispatcher() + + # capture output of dispatcher.log_info() (to stdout via print) + fp = StringIO() + stdout = sys.stdout + l1 = "Have you got anything without spam?" + l2 = "Why can't she have egg bacon spam and sausage?" + l3 = "THAT'S got spam in it!" + try: + sys.stdout = fp + d.log_info(l1, 'EGGS') + d.log_info(l2) + d.log_info(l3, 'SPAM') + finally: + sys.stdout = stdout + + lines = fp.getvalue().splitlines() + expected = ['EGGS: %s' % l1, 'info: %s' % l2, 'SPAM: %s' % l3] + + self.assertEqual(lines, expected) + + def test_unhandled(self): + d = asyncore.dispatcher() + d.ignore_log_types = () + + # capture output of dispatcher.log_info() (to stdout via print) + fp = StringIO() + stdout = sys.stdout + try: + sys.stdout = fp + d.handle_expt() + d.handle_read() + d.handle_write() + d.handle_connect() + d.handle_accept() + finally: + sys.stdout = stdout + + lines = fp.getvalue().splitlines() + expected = ['warning: unhandled incoming priority event', + 'warning: unhandled read event', + 'warning: unhandled write event', + 'warning: unhandled connect event', + 'warning: unhandled accept event'] + self.assertEqual(lines, expected) + + def test_issue_8594(self): + # XXX - this test is supposed to be removed in next major Python + # version + d = asyncore.dispatcher(socket.socket()) + # make sure the error message no longer refers to the socket + # object but the dispatcher instance instead + self.assertRaisesRegexp(AttributeError, 'dispatcher instance', + getattr, d, 'foo') + # cheap inheritance with the underlying socket is supposed + # to still work but a DeprecationWarning is expected + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + family = d.family + self.assertEqual(family, socket.AF_INET) + self.assertEqual(len(w), 1) + self.assertTrue(issubclass(w[0].category, DeprecationWarning)) + + def test_strerror(self): + # refers to bug #8573 + err = asyncore._strerror(errno.EPERM) + if hasattr(os, 'strerror'): + self.assertEqual(err, os.strerror(errno.EPERM)) + err = asyncore._strerror(-1) + self.assertTrue(err != "") + + +class dispatcherwithsend_noread(asyncore.dispatcher_with_send): + def readable(self): + return False + + def handle_connect(self): + pass + +class DispatcherWithSendTests(unittest.TestCase): + usepoll = False + + def setUp(self): + pass + + def tearDown(self): + asyncore.close_all() + + @unittest.skipUnless(threading, 'Threading required for this test.') + @test_support.reap_threads + def test_send(self): + evt = threading.Event() + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.settimeout(3) + port = test_support.bind_port(sock) + + cap = StringIO() + args = (evt, cap, sock) + t = threading.Thread(target=capture_server, args=args) + t.start() + try: + # wait a little longer for the server to initialize (it sometimes + # refuses connections on slow machines without this wait) + time.sleep(0.2) + + data = "Suppose there isn't a 16-ton weight?" + d = dispatcherwithsend_noread() + d.create_socket(socket.AF_INET, socket.SOCK_STREAM) + d.connect((HOST, port)) + + # give time for socket to connect + time.sleep(0.1) + + d.send(data) + d.send(data) + d.send('\n') + + n = 1000 + while d.out_buffer and n > 0: + asyncore.poll() + n -= 1 + + evt.wait() + + self.assertEqual(cap.getvalue(), data*2) + finally: + t.join() + + +class DispatcherWithSendTests_UsePoll(DispatcherWithSendTests): + usepoll = True + + at unittest.skipUnless(hasattr(asyncore, 'file_wrapper'), + 'asyncore.file_wrapper required') +class FileWrapperTest(unittest.TestCase): + def setUp(self): + self.d = "It's not dead, it's sleeping!" + with file(TESTFN, 'w') as h: + h.write(self.d) + + def tearDown(self): + unlink(TESTFN) + + def test_recv(self): + fd = os.open(TESTFN, os.O_RDONLY) + w = asyncore.file_wrapper(fd) + os.close(fd) + + self.assertNotEqual(w.fd, fd) + self.assertNotEqual(w.fileno(), fd) + self.assertEqual(w.recv(13), "It's not dead") + self.assertEqual(w.read(6), ", it's") + w.close() + self.assertRaises(OSError, w.read, 1) + + + def test_send(self): + d1 = "Come again?" + d2 = "I want to buy some cheese." + fd = os.open(TESTFN, os.O_WRONLY | os.O_APPEND) + w = asyncore.file_wrapper(fd) + os.close(fd) + + w.write(d1) + w.send(d2) + w.close() + self.assertEqual(file(TESTFN).read(), self.d + d1 + d2) + + @unittest.skipUnless(hasattr(asyncore, 'file_dispatcher'), + 'asyncore.file_dispatcher required') + def test_dispatcher(self): + fd = os.open(TESTFN, os.O_RDONLY) + data = [] + class FileDispatcher(asyncore.file_dispatcher): + def handle_read(self): + data.append(self.recv(29)) + s = FileDispatcher(fd) + os.close(fd) + asyncore.loop(timeout=0.01, use_poll=True, count=2) + self.assertEqual(b"".join(data), self.d) + + +class BaseTestHandler(asyncore.dispatcher): + + def __init__(self, sock=None): + asyncore.dispatcher.__init__(self, sock) + self.flag = False + + def handle_accept(self): + raise Exception("handle_accept not supposed to be called") + + def handle_connect(self): + raise Exception("handle_connect not supposed to be called") + + def handle_expt(self): + raise Exception("handle_expt not supposed to be called") + + def handle_close(self): + raise Exception("handle_close not supposed to be called") + + def handle_error(self): + raise + + +class TCPServer(asyncore.dispatcher): + """A server which listens on an address and dispatches the + connection to a handler. + """ + + def __init__(self, handler=BaseTestHandler, host=HOST, port=0): + asyncore.dispatcher.__init__(self) + self.create_socket(socket.AF_INET, socket.SOCK_STREAM) + self.set_reuse_addr() + self.bind((host, port)) + self.listen(5) + self.handler = handler + + @property + def address(self): + return self.socket.getsockname()[:2] + + def handle_accept(self): + sock, addr = self.accept() + self.handler(sock) + + def handle_error(self): + raise + + +class BaseClient(BaseTestHandler): + + def __init__(self, address): + BaseTestHandler.__init__(self) + self.create_socket(socket.AF_INET, socket.SOCK_STREAM) + self.connect(address) + + def handle_connect(self): + pass + + +class BaseTestAPI(unittest.TestCase): + + def tearDown(self): + asyncore.close_all() + + def loop_waiting_for_flag(self, instance, timeout=5): + timeout = float(timeout) / 100 + count = 100 + while asyncore.socket_map and count > 0: + asyncore.loop(timeout=0.01, count=1, use_poll=self.use_poll) + if instance.flag: + return + count -= 1 + time.sleep(timeout) + self.fail("flag not set") + + def test_handle_connect(self): + # make sure handle_connect is called on connect() + + class TestClient(BaseClient): + def handle_connect(self): + self.flag = True + + server = TCPServer() + client = TestClient(server.address) + self.loop_waiting_for_flag(client) + + def test_handle_accept(self): + # make sure handle_accept() is called when a client connects + + class TestListener(BaseTestHandler): + + def __init__(self): + BaseTestHandler.__init__(self) + self.create_socket(socket.AF_INET, socket.SOCK_STREAM) + self.bind((HOST, 0)) + self.listen(5) + self.address = self.socket.getsockname()[:2] + + def handle_accept(self): + self.flag = True + + server = TestListener() + client = BaseClient(server.address) + self.loop_waiting_for_flag(server) + + def test_handle_read(self): + # make sure handle_read is called on data received + + class TestClient(BaseClient): + def handle_read(self): + self.flag = True + + class TestHandler(BaseTestHandler): + def __init__(self, conn): + BaseTestHandler.__init__(self, conn) + self.send('x' * 1024) + + server = TCPServer(TestHandler) + client = TestClient(server.address) + self.loop_waiting_for_flag(client) + + def test_handle_write(self): + # make sure handle_write is called + + class TestClient(BaseClient): + def handle_write(self): + self.flag = True + + server = TCPServer() + client = TestClient(server.address) + self.loop_waiting_for_flag(client) + + def test_handle_close(self): + # make sure handle_close is called when the other end closes + # the connection + + class TestClient(BaseClient): + + def handle_read(self): + # in order to make handle_close be called we are supposed + # to make at least one recv() call + self.recv(1024) + + def handle_close(self): + self.flag = True + self.close() + + class TestHandler(BaseTestHandler): + def __init__(self, conn): + BaseTestHandler.__init__(self, conn) + self.close() + + server = TCPServer(TestHandler) + client = TestClient(server.address) + self.loop_waiting_for_flag(client) + + @unittest.skipIf(sys.platform.startswith("sunos"), + "OOB support is broken on Solaris") + def test_handle_expt(self): + # Make sure handle_expt is called on OOB data received. + # Note: this might fail on some platforms as OOB data is + # tenuously supported and rarely used. + + class TestClient(BaseClient): + def handle_expt(self): + self.flag = True + + class TestHandler(BaseTestHandler): + def __init__(self, conn): + BaseTestHandler.__init__(self, conn) + self.socket.send(chr(244), socket.MSG_OOB) + + server = TCPServer(TestHandler) + client = TestClient(server.address) + self.loop_waiting_for_flag(client) + + def test_handle_error(self): + + class TestClient(BaseClient): + def handle_write(self): + 1.0 / 0 + def handle_error(self): + self.flag = True + try: + raise + except ZeroDivisionError: + pass + else: + raise Exception("exception not raised") + + server = TCPServer() + client = TestClient(server.address) + self.loop_waiting_for_flag(client) + + def test_connection_attributes(self): + server = TCPServer() + client = BaseClient(server.address) + + # we start disconnected + self.assertFalse(server.connected) + self.assertTrue(server.accepting) + # this can't be taken for granted across all platforms + #self.assertFalse(client.connected) + self.assertFalse(client.accepting) + + # execute some loops so that client connects to server + asyncore.loop(timeout=0.01, use_poll=self.use_poll, count=100) + self.assertFalse(server.connected) + self.assertTrue(server.accepting) + self.assertTrue(client.connected) + self.assertFalse(client.accepting) + + # disconnect the client + client.close() + self.assertFalse(server.connected) + self.assertTrue(server.accepting) + self.assertFalse(client.connected) + self.assertFalse(client.accepting) + + # stop serving + server.close() + self.assertFalse(server.connected) + self.assertFalse(server.accepting) + + def test_create_socket(self): + s = asyncore.dispatcher() + s.create_socket(socket.AF_INET, socket.SOCK_STREAM) + self.assertEqual(s.socket.family, socket.AF_INET) + self.assertEqual(s.socket.type, socket.SOCK_STREAM) + + def test_bind(self): + s1 = asyncore.dispatcher() + s1.create_socket(socket.AF_INET, socket.SOCK_STREAM) + s1.bind((HOST, 0)) + s1.listen(5) + port = s1.socket.getsockname()[1] + + s2 = asyncore.dispatcher() + s2.create_socket(socket.AF_INET, socket.SOCK_STREAM) + # EADDRINUSE indicates the socket was correctly bound + self.assertRaises(socket.error, s2.bind, (HOST, port)) + + def test_set_reuse_addr(self): + sock = socket.socket() + try: + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + except socket.error: + unittest.skip("SO_REUSEADDR not supported on this platform") + else: + # if SO_REUSEADDR succeeded for sock we expect asyncore + # to do the same + s = asyncore.dispatcher(socket.socket()) + self.assertFalse(s.socket.getsockopt(socket.SOL_SOCKET, + socket.SO_REUSEADDR)) + s.create_socket(socket.AF_INET, socket.SOCK_STREAM) + s.set_reuse_addr() + self.assertTrue(s.socket.getsockopt(socket.SOL_SOCKET, + socket.SO_REUSEADDR)) + finally: + sock.close() + + @unittest.skipUnless(threading, 'Threading required for this test.') + @test_support.reap_threads + def test_quick_connect(self): + # see: http://bugs.python.org/issue10340 + server = TCPServer() + t = threading.Thread(target=lambda: asyncore.loop(timeout=0.1, count=500)) + t.start() + + for x in xrange(20): + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.settimeout(.2) + s.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, + struct.pack('ii', 1, 0)) + try: + s.connect(server.address) + except socket.error: + pass + finally: + s.close() + + +class TestAPI_UseSelect(BaseTestAPI): + use_poll = False + + at unittest.skipUnless(hasattr(select, 'poll'), 'select.poll required') +class TestAPI_UsePoll(BaseTestAPI): + use_poll = True + + +def test_main(): + tests = [HelperFunctionTests, DispatcherTests, DispatcherWithSendTests, + DispatcherWithSendTests_UsePoll, TestAPI_UseSelect, + TestAPI_UsePoll, FileWrapperTest] + run_unittest(*tests) + +if __name__ == "__main__": + test_main() -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Sat Aug 25 22:49:25 2012 From: jython-checkins at python.org (alan.kennedy) Date: Sat, 25 Aug 2012 22:49:25 +0200 (CEST) Subject: [Jython-checkins] =?utf-8?q?jython=3A_Jython_customisations_for_a?= =?utf-8?q?syncore?= Message-ID: <3X4BLF0QRgzQJ9@mail.python.org> http://hg.python.org/jython/rev/3aa3d5d67709 changeset: 6854:3aa3d5d67709 user: Alan Kennedy date: Sat Aug 25 21:46:40 2012 +0100 summary: Jython customisations for asyncore files: Lib/asyncore.py | 53 ++++++++++++++------------ Lib/select.py | 4 +- Lib/test/test_asyncore.py | 14 ++++++- 3 files changed, 45 insertions(+), 26 deletions(-) diff --git a/Lib/asyncore.py b/Lib/asyncore.py --- a/Lib/asyncore.py +++ b/Lib/asyncore.py @@ -174,31 +174,34 @@ if timeout is not None: # timeout is in milliseconds timeout = int(timeout*1000) - pollster = select.poll() if map: - for fd, obj in map.items(): - flags = 0 - if obj.readable(): - flags |= select.POLLIN | select.POLLPRI - # accepting sockets should not be writable - if obj.writable() and not obj.accepting: - flags |= select.POLLOUT - if flags: - # Only check for exceptions if object was either readable - # or writable. - flags |= select.POLLERR | select.POLLHUP | select.POLLNVAL - pollster.register(fd, flags) try: - r = pollster.poll(timeout) - except select.error, err: - if err.args[0] != EINTR: - raise - r = [] - for fd, flags in r: - obj = map.get(fd) - if obj is None: - continue - readwrite(obj, flags) + pollster = select._poll_object_cache.get_poll_object() + for fd, obj in map.items(): + flags = 0 + if obj.readable(): + flags |= select.POLLIN | select.POLLPRI + # accepting sockets should not be writable + if obj.writable() and not obj.accepting: + flags |= select.POLLOUT + if flags: + # Only check for exceptions if object was either readable + # or writable. + flags |= select.POLLERR | select.POLLHUP | select.POLLNVAL + pollster.register(obj, flags) + try: + r = pollster.poll(timeout) + except select.error, err: + if err.args[0] != EINTR: + raise + r = [] + for obj, flags in r: + # obj = map.get(fd) + if obj is None: + continue + readwrite(obj, flags) + finally: + select._poll_object_cache.release_poll_object(pollster) poll3 = poll2 # Alias for backward compatibility @@ -210,6 +213,8 @@ poll_fun = poll2 else: poll_fun = poll + if sys.platform.startswith('java'): + poll_fun = poll2 if count is None: while map: @@ -301,7 +306,7 @@ def set_socket(self, sock, map=None): self.socket = sock ## self.__dict__['socket'] = sock - self._fileno = sock.fileno() + self._fileno = sock self.add_channel(map) def set_reuse_addr(self): diff --git a/Lib/select.py b/Lib/select.py --- a/Lib/select.py +++ b/Lib/select.py @@ -1,5 +1,5 @@ """ -This is an select module for use on JVMs > 1.5. +This is an select module for use on JVMs >= 1.5. It is documented, along with known issues and workarounds, on the jython wiki. http://wiki.python.org/jython/SelectModule """ @@ -22,6 +22,8 @@ # (, ) : lambda: +(java.nio.channels.ClosedChannelException, ALL) : error(errno.ENOTCONN, 'Socket is not connected'), +(java.nio.channels.CancelledKeyException, ALL) : error(errno.ENOTCONN, 'Socket is not connected'), (java.nio.channels.IllegalBlockingModeException, ALL) : error(errno.ESOCKISBLOCKING, 'socket must be in non-blocking mode'), } diff --git a/Lib/test/test_asyncore.py b/Lib/test/test_asyncore.py --- a/Lib/test/test_asyncore.py +++ b/Lib/test/test_asyncore.py @@ -72,6 +72,7 @@ else: n = 200 while n > 0: + conn.setblocking(False) r, w, e = select.select([conn], [], []) if r: data = conn.recv(10) @@ -548,6 +549,8 @@ client = BaseClient(server.address) self.loop_waiting_for_flag(server) + @unittest.skipIf(sys.platform.startswith("java"), + "FIXME: Currently not working on jython") def test_handle_read(self): # make sure handle_read is called on data received @@ -575,6 +578,8 @@ client = TestClient(server.address) self.loop_waiting_for_flag(client) + @unittest.skipIf(sys.platform.startswith("java"), + "FIXME: Currently not working on jython") def test_handle_close(self): # make sure handle_close is called when the other end closes # the connection @@ -599,6 +604,8 @@ client = TestClient(server.address) self.loop_waiting_for_flag(client) + @unittest.skipIf(sys.platform.startswith("java"), + "OOB not supported on java/jython") @unittest.skipIf(sys.platform.startswith("sunos"), "OOB support is broken on Solaris") def test_handle_expt(self): @@ -682,8 +689,13 @@ s2 = asyncore.dispatcher() s2.create_socket(socket.AF_INET, socket.SOCK_STREAM) + # On jython, binding is not enough to create a server socket. + # Must first bind + s2.bind((HOST, port)) + # And then listen, which will cause the actual server socket to be created + # and then to listen, and thus cause the error # EADDRINUSE indicates the socket was correctly bound - self.assertRaises(socket.error, s2.bind, (HOST, port)) + self.assertRaises(socket.error, s2.listen, (5)) def test_set_reuse_addr(self): sock = socket.socket() -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Mon Aug 27 20:52:10 2012 From: jython-checkins at python.org (alan.kennedy) Date: Mon, 27 Aug 2012 20:52:10 +0200 (CEST) Subject: [Jython-checkins] =?utf-8?q?jython=3A_Restoring_the_original_poll?= =?utf-8?q?2_function=2C_and_making_jython_support_both_more?= Message-ID: <3X5Mf22ZVwzPqF@mail.python.org> http://hg.python.org/jython/rev/5e12ad3013ff changeset: 6855:5e12ad3013ff user: Alan Kennedy date: Mon Aug 27 19:50:36 2012 +0100 summary: Restoring the original poll2 function, and making jython support both more explicit and easier to push upstream into cpython files: Lib/asyncore.py | 47 ++++++++++++++++++++++++++++++++++-- 1 files changed, 44 insertions(+), 3 deletions(-) diff --git a/Lib/asyncore.py b/Lib/asyncore.py --- a/Lib/asyncore.py +++ b/Lib/asyncore.py @@ -174,6 +174,47 @@ if timeout is not None: # timeout is in milliseconds timeout = int(timeout*1000) + pollster = select.poll() + if map: + for fd, obj in map.items(): + flags = 0 + if obj.readable(): + flags |= select.POLLIN | select.POLLPRI + # accepting sockets should not be writable + if obj.writable() and not obj.accepting: + flags |= select.POLLOUT + if flags: + # Only check for exceptions if object was either readable + # or writable. + flags |= select.POLLERR | select.POLLHUP | select.POLLNVAL + pollster.register(fd, flags) + try: + r = pollster.poll(timeout) + except select.error, err: + if err.args[0] != EINTR: + raise + r = [] + for fd, flags in r: + obj = map.get(fd) + if obj is None: + continue + readwrite(obj, flags) + +poll3 = poll2 # Alias for backward compatibility + +def jython_poll_fun(timeout=0.0, map=None): + # On jython, select.poll() is the mechanism to use, + # select.select is implemented on top of it. + # Also, we have to use a cache of such objects, because of problems with frequent + # creation and destruction of such objects on windows + # "select() crashes with IOException": http://bugs.jython.org/issue1291 + # So this function is basically the same function as poll2 above, except + # with the select.poll() functionality wrapped in a try..finally clause. + if map is None: + map = socket_map + if timeout is not None: + # timeout is in milliseconds + timeout = int(timeout*1000) if map: try: pollster = select._poll_object_cache.get_poll_object() @@ -203,8 +244,6 @@ finally: select._poll_object_cache.release_poll_object(pollster) -poll3 = poll2 # Alias for backward compatibility - def loop(timeout=30.0, use_poll=False, map=None, count=None): if map is None: map = socket_map @@ -214,7 +253,7 @@ else: poll_fun = poll if sys.platform.startswith('java'): - poll_fun = poll2 + poll_fun = jython_poll_fun if count is None: while map: @@ -306,6 +345,8 @@ def set_socket(self, sock, map=None): self.socket = sock ## self.__dict__['socket'] = sock + # On jython, the socket object itself is what is watchable. + # http://mail.python.org/pipermail/python-dev/2007-May/073443.html self._fileno = sock self.add_channel(map) -- Repository URL: http://hg.python.org/jython