[Jython-checkins] jython: Prevent String.atof() from accepting hex notation.
jeff.allen
jython-checkins at python.org
Wed Dec 31 02:41:07 CET 2014
https://hg.python.org/jython/rev/f125a1eeacbc
changeset: 7478:f125a1eeacbc
user: Jeff Allen <ja.py at farowl.co.uk>
date: Wed Dec 17 08:27:24 2014 +0000
summary:
Prevent String.atof() from accepting hex notation.
Fixes a test failure in test_float: float("0x3.p-1") to raise ValueError.
files:
Lib/test/test_float.py | 5 +-
src/org/python/core/PyString.java | 106 ++++++++++-------
2 files changed, 66 insertions(+), 45 deletions(-)
diff --git a/Lib/test/test_float.py b/Lib/test/test_float.py
--- a/Lib/test/test_float.py
+++ b/Lib/test/test_float.py
@@ -35,9 +35,8 @@
self.assertEqual(float(" 3.14 "), 3.14)
self.assertRaises(ValueError, float, " 0x3.1 ")
- #FIXME: not raising ValueError on Jython:
- #self.assertRaises(ValueError, float, " -0x3.p-1 ")
- #self.assertRaises(ValueError, float, " +0x3.p-1 ")
+ self.assertRaises(ValueError, float, " -0x3.p-1 ")
+ self.assertRaises(ValueError, float, " +0x3.p-1 ")
self.assertRaises(ValueError, float, "++3.14")
self.assertRaises(ValueError, float, "+-3.14")
diff --git a/src/org/python/core/PyString.java b/src/org/python/core/PyString.java
--- a/src/org/python/core/PyString.java
+++ b/src/org/python/core/PyString.java
@@ -4,6 +4,8 @@
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.math.BigInteger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import org.python.core.buffer.BaseBuffer;
import org.python.core.buffer.SimpleStringBuffer;
@@ -2710,55 +2712,75 @@
}
}
+ /**
+ * Convert this PyString to a floating-point value according to Python rules.
+ *
+ * @return the value
+ */
public double atof() {
- StringBuilder s = null;
- int n = getString().length();
- for (int i = 0; i < n; i++) {
- char ch = getString().charAt(i);
- if (ch == '\u0000') {
- throw Py.ValueError("null byte in argument for float()");
+ String bogus = null;
+ double x = 0.0;
+ Matcher m = atofPattern.matcher(getString());
+
+ if (m.matches()) {
+ // Might be a valid float
+ try {
+ if (m.group(3) == null) {
+ // No numeric part was found: it's something like "-Inf" or "hOrsE"
+ x = atofSpecials(m.group(1));
+ } else {
+ // A numeric part was present, try to convert the whole
+ x = Double.parseDouble(m.group(1));
+ }
+ } catch (NumberFormatException e) {
+ bogus = m.group(1);
}
- if (Character.isDigit(ch)) {
- if (s == null) {
- s = new StringBuilder(getString());
- }
- int val = Character.digit(ch, 10);
- s.setCharAt(i, Character.forDigit(val, 10));
- }
+ } else {
+ // This doesn't match the pattern for a float value
+ bogus = getString().trim();
}
- String sval = getString();
- if (s != null) {
- sval = s.toString();
+
+ // At this point, bogus will have been set to the trimmed string if there was a problem.
+ if (bogus == null) {
+ return x;
+ } else {
+ String fmt = "could not convert string to float: %s";
+ throw Py.ValueError(String.format(fmt, bogus));
}
- try {
- // Double.valueOf allows format specifier ("d" or "f") at the end
- String lowSval = sval.toLowerCase();
- if (lowSval.equals("nan")) {
+
+ }
+
+ /**
+ * Regular expression that includes all valid a Python float() arguments, in which group 1
+ * captures the whole, stripped of white space, and group 3 will be present only if the form is
+ * numeric. Invalid non numerics are accepted ("+hOrsE" as "-inf").
+ */
+ private static Pattern atofPattern = Pattern
+ .compile("\\s*([+-]?(((\\d+(\\.\\d*)?|\\.\\d+)([eE][+-]?\\d+)?)|\\p{Alpha}+))\\s*");
+
+ /**
+ * Conversion for non-numeric floats, accepting signed or unsigned "inf" and "nan", in any case.
+ *
+ * @param s to convert
+ * @return non-numeric result (if valid)
+ * @throws NumberFormatException if not a valid non-numeric indicator
+ */
+ private static double atofSpecials(String s) throws NumberFormatException {
+ switch (s.toLowerCase()) {
+ case "nan":
+ case "+nan":
+ case "-nan":
return Double.NaN;
- } else if (lowSval.equals("+nan")) {
- return Double.NaN;
- } else if (lowSval.equals("-nan")) {
- return Double.NaN;
- } else if (lowSval.equals("inf")) {
+ case "inf":
+ case "+inf":
+ case "infinity":
+ case "+infinity":
return Double.POSITIVE_INFINITY;
- } else if (lowSval.equals("+inf")) {
- return Double.POSITIVE_INFINITY;
- } else if (lowSval.equals("-inf")) {
+ case "-inf":
+ case "-infinity":
return Double.NEGATIVE_INFINITY;
- } else if (lowSval.equals("infinity")) {
- return Double.POSITIVE_INFINITY;
- } else if (lowSval.equals("+infinity")) {
- return Double.POSITIVE_INFINITY;
- } else if (lowSval.equals("-infinity")) {
- return Double.NEGATIVE_INFINITY;
- }
-
- if (lowSval.endsWith("d") || lowSval.endsWith("f")) {
- throw new NumberFormatException("format specifiers not allowed");
- }
- return Double.valueOf(sval).doubleValue();
- } catch (NumberFormatException exc) {
- throw Py.ValueError("invalid literal for __float__: " + getString());
+ default:
+ throw new NumberFormatException();
}
}
--
Repository URL: https://hg.python.org/jython
More information about the Jython-checkins
mailing list