Skip to content

Commit 4dd7d25

Browse files
committed
Add the implementation of int.__formatter__
1 parent a846473 commit 4dd7d25

File tree

6 files changed

+721
-158
lines changed

6 files changed

+721
-158
lines changed

rt4core/src/main/java/uk/co/farowl/vsj4/core/PyFloat.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import uk.co.farowl.vsj4.stringlib.InternalFormat.FormatError;
2121
import uk.co.farowl.vsj4.stringlib.InternalFormat.FormatOverflow;
2222
import uk.co.farowl.vsj4.stringlib.InternalFormat.FormatSpec;
23+
import uk.co.farowl.vsj4.stringlib.InternalFormat.FormatUnknown;
2324
import uk.co.farowl.vsj4.support.InterpreterError;
2425
import uk.co.farowl.vsj4.types.Exposed;
2526
import uk.co.farowl.vsj4.types.Exposed.PythonMethod;
@@ -248,6 +249,10 @@ static final Object __format__(Object self, Object formatSpec) {
248249

249250
} catch (FormatOverflow fe) {
250251
throw PyErr.format(PyExc.OverflowError, fe.getMessage());
252+
} catch (FormatUnknown fe) {
253+
throw PyErr.format(PyExc.ValueError,
254+
"%s for object of type '%s'", fe.getMessage(),
255+
PyType.of(self).getName());
251256
} catch (FormatError fe) {
252257
throw PyErr.format(PyExc.ValueError, fe.getMessage());
253258
} catch (NoConversion e) {
@@ -279,7 +284,6 @@ private static String formatDouble(double value,
279284

280285
// formatter ------------------------------------------------------
281286

282-
// TODO: implement __format__ and (revised) stringlib
283287
/**
284288
* A {@link Formatter}, constructed from a {@link FormatSpec}, with
285289
* specific validations for {@code int.__format__}.
@@ -338,7 +342,7 @@ private static FormatSpec validated(FormatSpec spec,
338342
switch (spec.type) {
339343

340344
case 'n':
341-
if (spec.grouper != 0) {
345+
if (spec.group > 0) {
342346
throw notAllowed("Grouping", spec.grouper,
343347
TYPE.getName(), spec.type);
344348
}
@@ -367,7 +371,7 @@ private static FormatSpec validated(FormatSpec spec,
367371

368372
default:
369373
// The type code was not recognised
370-
throw unknownFormat(spec.type, TYPE.getName());
374+
throw new FormatUnknown(spec.type);
371375
}
372376

373377
/*

rt4core/src/main/java/uk/co/farowl/vsj4/core/PyLong.java

Lines changed: 124 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,13 @@
1212

1313
import uk.co.farowl.vsj4.core.PyUtil.NoConversion;
1414
import uk.co.farowl.vsj4.kernel.Representation;
15-
import uk.co.farowl.vsj4.support.MissingFeature;
15+
import uk.co.farowl.vsj4.stringlib.IntegerFormatter;
16+
import uk.co.farowl.vsj4.stringlib.InternalFormat;
17+
import uk.co.farowl.vsj4.stringlib.InternalFormat.AbstractFormatter;
18+
import uk.co.farowl.vsj4.stringlib.InternalFormat.FormatError;
19+
import uk.co.farowl.vsj4.stringlib.InternalFormat.FormatOverflow;
20+
import uk.co.farowl.vsj4.stringlib.InternalFormat.FormatSpec;
21+
import uk.co.farowl.vsj4.stringlib.InternalFormat.FormatUnknown;
1622
import uk.co.farowl.vsj4.types.Exposed.Default;
1723
import uk.co.farowl.vsj4.types.Exposed.DocString;
1824
import uk.co.farowl.vsj4.types.Exposed.PositionalOnly;
@@ -196,131 +202,129 @@ private static Object intImpl(Object x, Object base)
196202

197203
// int methods ----------------------------------------------------
198204

199-
// TODO: implement __format__ and (revised) stringlib
200-
201205
@PythonMethod
202206
static final Object __format__(Object self, Object formatSpec) {
203-
throw new MissingFeature("int.__format__");
204-
}
205207

206-
// @PythonMethod
207-
// static final Object __format__(Object self, Object formatSpec) {
208-
//
209-
// String stringFormatSpec = PyUnicode.asString(formatSpec,
210-
// o -> Abstract.argumentTypeError("__format__",
211-
// "specification", "str", o));
212-
//
213-
// try {
214-
// // Parse the specification
215-
// Spec spec = InternalFormat.fromText(stringFormatSpec);
216-
//
217-
// // Get a formatter for the specification
218-
// AbstractFormatter f;
219-
// if ("efgEFG%".indexOf(spec.type) >= 0) {
220-
// // These are floating-point formats
221-
// f = new PyFloat.Formatter(spec);
222-
// } else {
223-
// f = new PyLong.Formatter(spec);
224-
// }
225-
//
226-
// /*
227-
// * Format, pad and return a result according to as the
228-
// * specification argument.
229-
// */
230-
// return f.format(self).pad().getResult();
231-
//
232-
// } catch (FormatOverflow fe) {
233-
// throw new OverflowError(fe.getMessage());
234-
// } catch (FormatError fe) {
235-
// throw new ValueError(fe.getMessage());
236-
// } catch (NoConversion e) {
237-
// throw Abstract.impossibleArgumentError(TYPE.name, self);
238-
// }
239-
// }
208+
String stringFormatSpec = PyUnicode.asString(formatSpec,
209+
o -> Abstract.argumentTypeError("__format__",
210+
"specification", "str", o));
211+
212+
try {
213+
// Parse the specification
214+
FormatSpec spec = InternalFormat.fromText(stringFormatSpec);
215+
216+
// Get a formatter for the specification
217+
AbstractFormatter f;
218+
if ("efgEFG%".indexOf(spec.type) >= 0) {
219+
// These are floating-point formats
220+
f = new PyFloat.Formatter(spec);
221+
} else {
222+
f = new PyLong.Formatter(spec);
223+
}
224+
225+
/*
226+
* Format, pad and return a result according to as the
227+
* specification argument.
228+
*/
229+
return f.format(self).pad().getResult();
230+
231+
} catch (FormatOverflow fe) {
232+
throw PyErr.format(PyExc.OverflowError, fe.getMessage());
233+
} catch (FormatUnknown fe) {
234+
throw PyErr.format(PyExc.ValueError,
235+
"%s for object of type '%s'", fe.getMessage(),
236+
PyType.of(self).getName());
237+
} catch (FormatError fe) {
238+
throw PyErr.format(PyExc.ValueError, fe.getMessage());
239+
} catch (NoConversion e) {
240+
throw Abstract.impossibleArgumentError(TYPE.getName(),
241+
self);
242+
}
243+
}
240244

241245
// formatter ------------------------------------------------------
242246

243-
// TODO: implement __format__ and (revised) stringlib
244-
/// **
245-
// * An {@link IntegerFormatter}, constructed from a {@link Spec},
246-
// * with validations customised for {@code int.__format__}.
247-
// */
248-
// private static class Formatter extends IntegerFormatter {
249-
//
250-
// /**
251-
// * Prepare an {@link IntegerFormatter} in support of
252-
// * {@link PyLong#__format__(Object, Object) int.__format__}.
253-
// *
254-
// * @param spec a parsed PEP-3101 format specification.
255-
// * @return a formatter ready to use.
256-
// * @throws FormatOverflow if a value is out of range (including
257-
// * the precision)
258-
// * @throws FormatError if an unsupported format character is
259-
// * encountered
260-
// */
261-
// Formatter(Spec spec) throws FormatError {
262-
// super(validated(spec));
263-
// }
264-
//
265-
/// **
266-
// * Validations and defaults specific to {@code int.__format__}.
267-
// * (Note that {@code int.__mod__} has slightly different rules.)
268-
// *
269-
// * @param spec to validate
270-
// * @return validated spec with defaults filled
271-
// * @throws FormatError on failure to validate
272-
// */
273-
// private static Spec validated(Spec spec) throws FormatError {
274-
// String type = TYPE.name;
275-
// switch (spec.type) {
276-
//
277-
// case 'c':
278-
//// Character data: specific prohibitions.
279-
// if (Spec.specified(spec.sign)) {
280-
// throw signNotAllowed("integer", spec.type);
281-
// } else if (spec.alternate) {
282-
// throw alternateFormNotAllowed("integer",
283-
// spec.type);
284-
// }
285-
//// $FALL-THROUGH$
286-
//
287-
// case 'x':
288-
// case 'X':
289-
// case 'o':
290-
// case 'b':
291-
// case 'n':
292-
// if (spec.grouping) {
293-
// throw notAllowed("Grouping", ',', "integer",
294-
// spec.type);
295-
// }
296-
//// $FALL-THROUGH$
297-
//
298-
// case Spec.NONE:
299-
// case 'd':
300-
//// Check for disallowed parts of the specification
301-
// if (Spec.specified(spec.precision)) {
302-
// throw precisionNotAllowed("integer");
303-
// }
304-
// break;
305-
//
306-
// default:
307-
// // The type code was not recognised
308-
// throw unknownFormat(spec.type, type);
309-
// }
310-
//
311-
/// *
312-
// * spec may be incomplete. The defaults are those commonly
313-
// * used for numeric formats.
314-
// */
315-
// return spec.withDefaults(Spec.NUMERIC);
316-
// }
317-
//
318-
// @Override
319-
// public IntegerFormatter format(Object o)
320-
// throws NoConversion, FormatError {
321-
// return format(convertToBigInteger(o));
322-
// }
323-
// }
247+
/**
248+
* An {@link IntegerFormatter}, constructed from a
249+
* {@link FormatSpec}, with validations customised for
250+
* {@code int.__format__}.
251+
*/
252+
private static class Formatter extends IntegerFormatter {
253+
254+
/**
255+
* Prepare an {@link IntegerFormatter} in support of
256+
* {@link PyLong#__format__(Object, Object) int.__format__}.
257+
*
258+
* @param spec a parsed PEP-3101 format specification.
259+
* @return a formatter ready to use.
260+
* @throws FormatOverflow if a value is out of range (including
261+
* the precision)
262+
* @throws FormatError if an unsupported format character is
263+
* encountered
264+
*/
265+
Formatter(FormatSpec spec) throws FormatError {
266+
super(validated(spec));
267+
}
268+
269+
/**
270+
* Validations and defaults specific to {@code int.__format__}.
271+
* (Note that {@code int.__mod__} has slightly different rules.)
272+
*
273+
* @param spec to validate
274+
* @return validated spec with defaults filled
275+
* @throws FormatError on failure to validate
276+
*/
277+
private static FormatSpec validated(FormatSpec spec)
278+
throws FormatError {
279+
switch (spec.type) {
280+
281+
case 'c':
282+
// Character data: specific prohibitions.
283+
if (FormatSpec.specified(spec.sign)) {
284+
throw signNotAllowed("integer", spec.type);
285+
} else if (spec.alternate) {
286+
throw alternateFormNotAllowed("integer",
287+
spec.type);
288+
}
289+
// $FALL-THROUGH$
290+
291+
case 'n':
292+
if (spec.group > 0) {
293+
throw notAllowed("Grouping", spec.grouper,
294+
"integer", spec.type);
295+
}
296+
// $FALL-THROUGH$
297+
298+
case 'b':
299+
case 'o':
300+
case 'x':
301+
case 'X':
302+
case FormatSpec.NONE:
303+
case 'd':
304+
// Check for disallowed parts of the specification
305+
if (FormatSpec.specified(spec.precision)) {
306+
throw precisionNotAllowed("integer");
307+
}
308+
break;
309+
310+
default:
311+
// The type code was not recognised
312+
throw new FormatUnknown(spec.type);
313+
}
314+
315+
/*
316+
* spec may be incomplete. The defaults are those commonly
317+
* used for numeric formats.
318+
*/
319+
return spec.withDefaults(FormatSpec.NUMERIC);
320+
}
321+
322+
@Override
323+
public IntegerFormatter format(Object o)
324+
throws NoConversion, FormatError {
325+
return format(convertToBigInteger(o));
326+
}
327+
}
324328

325329
// Representations of the value -----------------------------------
326330

rt4core/src/main/java/uk/co/farowl/vsj4/stringlib/FloatFormatter.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import uk.co.farowl.vsj4.stringlib.InternalFormat.FormatError;
1010
import uk.co.farowl.vsj4.stringlib.InternalFormat.FormatOverflow;
1111
import uk.co.farowl.vsj4.stringlib.InternalFormat.FormatSpec;
12+
import uk.co.farowl.vsj4.stringlib.InternalFormat.FormatUnknown;
1213

1314
/**
1415
* A class that provides the implementation of floating-point
@@ -291,7 +292,7 @@ public FloatFormatter format(double value, String positivePrefix)
291292
* Should never get here, since this was checked in
292293
* PyFloat.
293294
*/
294-
throw unknownFormat(spec.type, "float");
295+
throw new FormatUnknown(spec.type);
295296
}
296297

297298
/*

0 commit comments

Comments
 (0)