Index: jython-2.3/src/org/python/core/PyString.java =================================================================== --- jython-2.3/src/org/python/core/PyString.java (revision 3045) +++ jython-2.3/src/org/python/core/PyString.java (working copy) @@ -3416,7 +3416,16 @@ } public PyObject __int__() { - return Py.newInteger(atoi(10)); + try + { + return Py.newInteger(atoi(10)); + } + catch (PyException e) + { + if (Py.matchException(e, Py.ValueError)) + return atol(10); + throw e; + } } public PyLong __long__() { Index: jython-2.3/src/org/python/core/PyFloat.java =================================================================== --- jython-2.3/src/org/python/core/PyFloat.java (revision 3045) +++ jython-2.3/src/org/python/core/PyFloat.java (working copy) @@ -1316,6 +1316,9 @@ return new PyFloat(0); } + if (value < 0 && iw != Math.floor(iw)) + throw Py.ValueError("negative number cannot be raised to a fractional power"); + double ret = Math.pow(value, iw); if (modulo == null) { return new PyFloat(ret); @@ -1359,11 +1362,11 @@ return float___int__(); } - final PyInteger float___int__() { + final PyObject float___int__() { if (value <= Integer.MAX_VALUE && value >= Integer.MIN_VALUE) { return new PyInteger((int)value); } - throw Py.OverflowError("float too large to convert"); + return __long__(); } public PyLong __long__() { Index: jython-2.3/src/org/python/core/__builtin__.java =================================================================== --- jython-2.3/src/org/python/core/__builtin__.java (revision 3045) +++ jython-2.3/src/org/python/core/__builtin__.java (working copy) @@ -32,6 +32,10 @@ return __builtin__.range(Py.py2int(arg1, "range(): 1st arg can't be coerced to int")); case 3: + if (!(arg1 instanceof PyString)) + throw Py.TypeError("ord() expected string of length 1, but " + arg1.getType().getFullName() + " found"); + if (arg1.__len__() > 1) + throw Py.TypeError("ord() expected a character, but string of length " + arg1.__len__() + " found"); return Py.newInteger(__builtin__.ord(Py.py2char(arg1, "ord(): 1st arg can't be coerced to char"))); case 5: @@ -200,12 +204,15 @@ } public static char unichr(int i) { - return chr(i); + if (i < 0 || i > 65535) { + throw Py.ValueError("unichr() arg not in range(65536)"); + } + return (char) i; } public static char chr(int i) { - if (i < 0 || i > 65535) { - throw Py.ValueError("chr() arg not in range(65535)"); + if (i < 0 || i > 255) { + throw Py.ValueError("chr() arg not in range(256)"); } return (char) i; } @@ -398,7 +405,13 @@ } public static PyString hex(PyObject o) { - return o.__hex__(); + try { + return o.__hex__(); + } catch (PyException e) { + if (Py.matchException(e, Py.AttributeError)) + throw Py.TypeError("hex() argument can't be converted to hex"); + throw e; + } } public static long id(PyObject o) { @@ -533,6 +546,9 @@ } public static PyObject min(PyObject[] l) { + if (l.length == 0) { + throw Py.TypeError("min expected 1 arguments, got 0"); + } if (l.length == 1) { return min(l[0]); } @@ -559,7 +575,7 @@ /** * Open a file read-only. - * + * * @param name the file to open. * @exception java.io.IOException */ @@ -569,7 +585,7 @@ /** * Open a file with the specified mode. - * + * * @param name name of the file to open. * @param mode open mode of the file. Use "r", "w", "r+", "w+" and "a". * @exception java.io.IOException @@ -580,7 +596,7 @@ /** * Open a file with the specified mode and buffer size. - * + * * @param name name of the file to open. * @param mode open mode of the file. Use "r", "w", "r+", "w+" and "a". * @param bufsize size of the internal buffer. Not currently used. @@ -824,10 +840,10 @@ /* * public static PyString unicode(PyObject v) { return unicode(v.__str__(), * null, null); } - * + * * public static PyString unicode(PyString v, String encoding) { return * unicode(v, encoding, null); } - * + * * public static PyString unicode(PyString v, String encoding, String * errors) { return new PyString(codecs.decode(v, encoding, errors)); } */ @@ -852,7 +868,13 @@ } public static PyObject vars(PyObject o) { - return o.__getattr__("__dict__"); + try { + return o.__getattr__("__dict__"); + } catch (PyException e) { + if (Py.matchException(e, Py.AttributeError)) + throw Py.TypeError("vars() argument must have __dict__ attribute"); + throw e; + } } public static PyObject vars() { Index: jython-2.3/src/org/python/modules/binascii.java =================================================================== --- jython-2.3/src/org/python/modules/binascii.java (revision 3045) +++ jython-2.3/src/org/python/modules/binascii.java (working copy) @@ -9,10 +9,14 @@ package org.python.modules; +import java.util.regex.Pattern; + +import org.python.core.ArgParser; import org.python.core.Py; import org.python.core.PyException; import org.python.core.PyObject; import org.python.core.PyString; +import org.python.core.PyStringMap; import org.python.core.PyTuple; /** @@ -124,11 +128,21 @@ public static String __doc__ = "Conversion between binary data and ASCII"; - public static final PyString Error = new PyString("binascii.Error"); + public static final PyObject Error = Py.makeClass("Error", + new PyObject[] { Py.RuntimeError }, + Py.newJavaCode(binascii.class, "empty__init__"), + Py.None); - public static final PyString Incomplete = - new PyString("binascii.Incomplete"); + public static PyObject empty__init__(PyObject[] arg, String[] kws) { + PyObject dict = new PyStringMap(); + dict.__setitem__("__module__", new PyString("binascii")); + return dict; + } + public static final PyObject Incomplete = Py.makeClass("Incomplete", + new PyObject[] { Py.RuntimeError }, + Py.newJavaCode(binascii.class, "empty__init__"), + Py.None); // hqx lookup table, ascii->binary. private static char RUNCHAR = 0x90; @@ -277,7 +291,7 @@ int bin_len = (ascii_data.charAt(0) - ' ') & 077; - for (i = 0; bin_len > 0; i++, ascii_len--) { + for (i = 0; bin_len > 0 && ascii_len > 0; i++, ascii_len--) { this_ch = ascii_data.charAt(i+1); if (this_ch == '\n' || this_ch == '\r' || ascii_len <= 0) { // Whitespace. Assume some spaces got eaten at @@ -304,6 +318,7 @@ bin_len--; } } + // Finally, check that if there's anything left on the line // that it's whitespace only. while (ascii_len-- > 0) { @@ -314,6 +329,11 @@ throw new PyException(Error, "Trailing garbage"); } } + + // finally, if we haven't decoded enough stuff, fill it up with zeros + for (; i s; Hexadecimal representation of binary data.\n" + "\n" + @@ -879,8 +898,127 @@ return a2b_hex(argbuf); } + final private static char[] upper_hexdigit = "0123456789ABCDEF".toCharArray(); + + private static StringBuffer qpEscape(StringBuffer sb, char c) + { + sb.append('='); + sb.append(upper_hexdigit[(c >>> 4) & 0xF]); + sb.append(upper_hexdigit[c & 0xF]); + return sb; + } + final private static Pattern WS = Pattern.compile("=\r?\n|[\t ]+$"); + final private static Pattern UNDERSCORE = Pattern.compile("_"); + final public static PyString __doc__a2b_qp = new PyString("Decode a string of qp-encoded data"); + + public static String a2b_qp(PyObject[] arg, String[] kws) + { + ArgParser ap = new ArgParser("a2b_qp", arg, kws, new String[] {"s", "header"}); + String s = ap.getString(0); + StringBuffer sb = new StringBuffer(); + boolean header = ((Boolean)ap.getPyObject(1, Py.False).__tojava__(Boolean.class)).booleanValue(); + if (header) + s = UNDERSCORE.matcher(s).replaceAll(" "); + + s = WS.matcher(s).replaceAll(""); + + for (int i=0, m=s.length(); i= '0' && c <= '9' || c >= 'A' && c <= 'F') && i < m) { + char nc = s.charAt(i++); + if ((nc >= '0' && nc <= '9' || nc >= 'A' && nc <= 'F')) { + sb.append((char)(Character.digit(c, 16) << 4 | Character.digit(nc, 16))); + } else { + sb.append('=').append(c).append(nc); + } + } else if (c != '\n') { + sb.append('=').append(c); + } + } + } else { + sb.append(c); + } + } + + return sb.toString(); + } + + final private static Pattern RN_TO_N = Pattern.compile("\r\n"); + final private static Pattern N_TO_RN = Pattern.compile("(? s;\n" + + "Encode a string using quoted-printable encoding.\n\n" + + "On encoding, when istext is set, newlines are not encoded, and white\n" + + "space at end of lines is. When istext is not set, \r and \n (CR/LF) are\n" + + "both encoded. When quotetabs is set, space and tabs are encoded."); + + public static String b2a_qp(PyObject[] arg, String[] kws) { + ArgParser ap = new ArgParser("b2a_qp", arg, kws, new String[] {"s", "quotetabs", "istext", "header"}); + String s = ap.getString(0); + boolean quotetabs = ((Boolean)ap.getPyObject(1, Py.False).__tojava__(Boolean.class)).booleanValue(); + boolean istext = ((Boolean)ap.getPyObject(2, Py.True).__tojava__(Boolean.class)).booleanValue(); + boolean header = ((Boolean)ap.getPyObject(3, Py.False).__tojava__(Boolean.class)).booleanValue(); + + String lineEnd; + int pos = s.indexOf('\n'); + if (pos > 0 && s.charAt(pos-1) == '\r') { + lineEnd = "\r\n"; + s = N_TO_RN.matcher(s).replaceAll("\r\n"); + } else { + lineEnd = "\n"; + s = RN_TO_N.matcher(s).replaceAll("\n"); + } + StringBuffer sb = new StringBuffer(); + int count = 0; + for (int i=0, m=s.length(); i' <= c && c <= '^') + || ('`' <= c && c <= '~') + || (c == '_' && !header) + || (c == '\n' || c == '\r' && istext)) { + if (count == 75) { + sb.append("=").append(lineEnd); + count = 0; + } + sb.append(c); + count++; + } + else if (!quotetabs && (c == '\t' || c == ' ')) { + if (count >= 72) { + sb.append("=").append(lineEnd); + count = 0; + } + + if (count >= 71) { + count += 3; + qpEscape(sb, c); + } else { + if (c == ' ' && header) + sb.append('_'); + else + sb.append(c); + count += 1; + } + } else { + if (count >= 72) { + sb.append("=").append(lineEnd); + count = 0; + } + count += 3; + qpEscape(sb, c); + } + } + return sb.toString(); + } + /* public static void main(String[] args) { String l = b2a_uu("Hello");