[Jython-checkins] jython: More PEP 3101 work and related changes (#1718).
nicholas.riley
jython-checkins at python.org
Wed Mar 21 21:34:59 CET 2012
http://hg.python.org/jython/rev/78c49afd03c9
changeset: 6462:78c49afd03c9
user: Nicholas Riley <njriley at illinois.edu>
date: Wed Mar 21 16:34:52 2012 -0400
summary:
More PEP 3101 work and related changes (#1718).
- Allow auto-numbered replacement fields in format strings (http://bugs.python.org/issue5237).
- Use 'nan', 'inf' and '-inf', not Unicode equivalents produced by Java number formatters.
- Limit recursion level for recursive format specs.
- Check some more error conditions.
All of test_str should pass now except the tests which require the as-yet-unimplemented float/datetime __format__.
files:
src/org/python/core/PyFloat.java | 7 +-
src/org/python/core/PyString.java | 81 ++++++---
src/org/python/core/stringlib/MarkupIterator.java | 38 ++++
3 files changed, 93 insertions(+), 33 deletions(-)
diff --git a/src/org/python/core/PyFloat.java b/src/org/python/core/PyFloat.java
--- a/src/org/python/core/PyFloat.java
+++ b/src/org/python/core/PyFloat.java
@@ -122,9 +122,12 @@
}
private String formatDouble(int precision) {
- if (Double.isNaN(getValue())) {
+ if (Double.isNaN(value))
return "nan";
- }
+ else if (value == Double.NEGATIVE_INFINITY)
+ return "-inf";
+ else if (value == Double.POSITIVE_INFINITY)
+ return "inf";
String result = String.format("%%.%dg", precision);
result = Py.newString(result).__mod__(this).toString();
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
@@ -13,6 +13,8 @@
import org.python.expose.MethodType;
import java.math.BigInteger;
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
/**
* A builtin python string.
@@ -2549,15 +2551,15 @@
@ExposedMethod(doc = BuiltinDocs.str_format_doc)
final PyObject str_format(PyObject[] args, String[] keywords) {
try {
- return new PyString(buildFormattedString(getString(), args, keywords));
+ return new PyString(buildFormattedString(getString(), args, keywords, null));
} catch (IllegalArgumentException e) {
throw Py.ValueError(e.getMessage());
}
}
- private String buildFormattedString(String value, PyObject[] args, String[] keywords) {
+ private String buildFormattedString(String value, PyObject[] args, String[] keywords, MarkupIterator enclosingIterator) {
StringBuilder result = new StringBuilder();
- MarkupIterator it = new MarkupIterator(value);
+ MarkupIterator it = new MarkupIterator(value, enclosingIterator);
while (true) {
MarkupIterator.Chunk chunk = it.nextChunk();
if (chunk == null) {
@@ -2565,32 +2567,29 @@
}
result.append(chunk.literalText);
if (chunk.fieldName.length() > 0) {
- outputMarkup(result, chunk, args, keywords);
+ PyObject fieldObj = getFieldObject(chunk.fieldName, args, keywords);
+ if (fieldObj == null) {
+ continue;
+ }
+ if ("r".equals(chunk.conversion)) {
+ fieldObj = fieldObj.__repr__();
+ } else if ("s".equals(chunk.conversion)) {
+ fieldObj = fieldObj.__str__();
+ } else if (chunk.conversion != null) {
+ throw Py.ValueError("Unknown conversion specifier " + chunk.conversion);
+ }
+ String formatSpec = chunk.formatSpec;
+ if (chunk.formatSpecNeedsExpanding) {
+ if (enclosingIterator != null) // PEP 3101 says only 2 levels
+ throw Py.ValueError("Max string recursion exceeded");
+ formatSpec = buildFormattedString(formatSpec, args, keywords, it);
+ }
+ renderField(fieldObj, formatSpec, result);
}
}
return result.toString();
}
- private void outputMarkup(StringBuilder result, MarkupIterator.Chunk chunk,
- PyObject[] args, String[] keywords) {
- PyObject fieldObj = getFieldObject(chunk.fieldName, args, keywords);
- if (fieldObj == null) {
- return;
- }
- if ("r".equals(chunk.conversion)) {
- fieldObj = fieldObj.__repr__();
- } else if ("s".equals(chunk.conversion)) {
- fieldObj = fieldObj.__str__();
- } else if (chunk.conversion != null) {
- throw Py.ValueError("Unknown conversion specifier " + chunk.conversion);
- }
- String formatSpec = chunk.formatSpec;
- if (chunk.formatSpecNeedsExpanding) {
- formatSpec = buildFormattedString(formatSpec, args, keywords);
- }
- renderField(fieldObj, formatSpec, result);
- }
-
private PyObject getFieldObject(String fieldName, PyObject[] args, String[] keywords) {
FieldNameIterator iterator = new FieldNameIterator(fieldName);
Object head = iterator.head();
@@ -2672,6 +2671,12 @@
* @return the result of the formatting
*/
public static String formatString(String text, InternalFormatSpec spec) {
+ if (spec.sign != '\0')
+ throw new IllegalArgumentException("Sign not allowed in string format specifier");
+ if (spec.alternate)
+ throw new IllegalArgumentException("Alternate form (#) not allowed in string format specifier");
+ if (spec.align == '=')
+ throw new IllegalArgumentException("'=' alignment not allowed in string format specifier");
if (spec.precision >= 0 && text.length() > spec.precision) {
text = text.substring(0, spec.precision);
}
@@ -2957,10 +2962,23 @@
}
}
+ static class DecimalFormatTemplate {
+ static DecimalFormat template;
+ static {
+ template = new DecimalFormat("#,##0.#####");
+ DecimalFormatSymbols symbols = template.getDecimalFormatSymbols();
+ symbols.setNaN("nan");
+ symbols.setInfinity("inf");
+ template.setDecimalFormatSymbols(symbols);
+ template.setGroupingUsed(false);
+ }
+ }
+ private static final DecimalFormat getDecimalFormat() {
+ return (DecimalFormat)DecimalFormatTemplate.template.clone();
+ }
+
private String formatFloatDecimal(double v, boolean truncate) {
checkPrecision("decimal");
- java.text.NumberFormat numberFormat = java.text.NumberFormat.getInstance(
- java.util.Locale.US);
int prec = precision;
if (prec == -1)
prec = 6;
@@ -2968,11 +2986,12 @@
v = -v;
negative = true;
}
- numberFormat.setMaximumFractionDigits(prec);
- numberFormat.setMinimumFractionDigits(truncate ? 0 : prec);
- numberFormat.setGroupingUsed(false);
-
- String ret = numberFormat.format(v);
+
+ DecimalFormat decimalFormat = getDecimalFormat();
+ decimalFormat.setMaximumFractionDigits(prec);
+ decimalFormat.setMinimumFractionDigits(truncate ? 0 : prec);
+
+ String ret = decimalFormat.format(v);
return ret;
}
diff --git a/src/org/python/core/stringlib/MarkupIterator.java b/src/org/python/core/stringlib/MarkupIterator.java
--- a/src/org/python/core/stringlib/MarkupIterator.java
+++ b/src/org/python/core/stringlib/MarkupIterator.java
@@ -18,9 +18,18 @@
private final String markup;
private int index;
+ private final FieldNumbering numbering;
public MarkupIterator(String markup) {
+ this(markup, null);
+ }
+
+ public MarkupIterator(String markup, MarkupIterator enclosingIterator) {
this.markup = markup;
+ if (enclosingIterator != null)
+ numbering = enclosingIterator.numbering;
+ else
+ numbering = new FieldNumbering();
}
@Override
@@ -140,6 +149,17 @@
} else {
result.fieldName = fieldMarkup;
}
+ if (result.fieldName.isEmpty()) {
+ result.fieldName = numbering.nextAutomaticFieldNumber();
+ return;
+ }
+ char c = result.fieldName.charAt(0);
+ if (c == '.' || c == '[') {
+ result.fieldName = numbering.nextAutomaticFieldNumber() + result.fieldName;
+ return;
+ }
+ if (Character.isDigit(c))
+ numbering.useManualFieldNumbering();
}
private int indexOfFirst(String s, int start, char c1, char c2) {
@@ -154,6 +174,24 @@
return Math.min(i1, i2);
}
+ static final class FieldNumbering {
+ private boolean manualFieldNumberSpecified;
+ private int automaticFieldNumber = 0;
+
+ String nextAutomaticFieldNumber() {
+ if (manualFieldNumberSpecified)
+ throw new IllegalArgumentException("cannot switch from manual field specification to automatic field numbering");
+ return Integer.toString(automaticFieldNumber++);
+ }
+ void useManualFieldNumbering() {
+ if (manualFieldNumberSpecified)
+ return;
+ if (automaticFieldNumber != 0)
+ throw new IllegalArgumentException("cannot switch from automatic field numbering to manual field specification");
+ manualFieldNumberSpecified = true;
+ }
+ }
+
public static final class Chunk {
public String literalText;
public String fieldName;
--
Repository URL: http://hg.python.org/jython
More information about the Jython-checkins
mailing list