* cformat.rs: refactor fill_string to take fill_with_precision
This allows it to be used with both self.min_field_width and
self.precision, which is necessary for padding out %ds with precision.
* cformat.rs: zero-pad %d entries using precision
This matches CPython's behaviour.
* cformat.rs: don't left-adjust when filling with precision
That will always be prepending 0s to %d arguments, the LEFT_ADJUST flag
will be used by a later call to `fill_string` with the 0-filled string
as the `string` param.
* floats: handle alternate form of general formatting
* cformat.rs: convert width/precision to i32
In CPython, width can be isize but precision can only be i32. Our
implementation currently assumes the same type for both: as CPython's
tests assert on overflows for precision, but not for width, we use that
size for both.
* test_format: run test_common_format
Except for the line which raises an OverflowError in CPython, because
overflows in Rust (and therefore RustPython) abort the process
immediately.
* test_types: don't expect test_float_to_string to fail
Its string formatting usage now works as expected.
In CPython's PyErr_NewException[0], the dotted "module.name" passed in is
split on that dot: the content before the dot is set as __module__ in
the created exception class, and the content after it is set as
__name__.
This commit mirrors that behaviour, by introducing a `module` argument
to `PyContext.new_class`: if Some("module_name") is passed, it will be
set as `__module__`. All exception-creating uses of `new_class` are
updated to pass in their module and class names separately.
[0] https://github.com/python/cpython/blob/main/Python/errors.c#L1082-L1087
Without this, the code would accept `b"eggs" % b"ham"` and `b"eggs" %
bytearray(b"ham")` because it would detect bytes and bytearray as
mappings (which _are_ permitted to have leftover, unprocessed
arguments).
Unlike strings, b'%a' and b'%r' are equivalent, and they match the '%a'
behaviour of strings, not '%r'.
Thanks to @youknowone for improving this implementation.
The previous code assumed that, after parsing was complete, both the
padding and the precision integers would always be positive: it
therefore cast them to usize unconditionally. This meant that when a
negative integer was encountered, it would underflow and attempt to
generate a padded string of around usize::MAX, or a float representation
of that precision: unsurprisingly, these fail to allocate.
For simple format strings (e.g. `%-3s`), this works fine: `-` is
detected as a separate token during parsing, so only the positive
integer (3) is passed through.
However, `%*s` expresses "take the first value as the padding integer,
and the second as the string to be padded" (`"%*s" % (-3, ...)` is
analogous to the previous example). In this codepath, the parsing does
not handle the padding integer, so it can't strip the leading `-`: the
padding integer is negative. A negative precision can also be given in
similar fashion, with `"%.*f" % (-3, ...)`.
N.B. This commit does not cause the code to produce the correct _output_
for negative padding integers or precisions provided this way, it
addresses only the crash.