Repository: mpmath/mpmath (python)

Issue:
Unsupported format string passed to mpf.__format__
```
$ python
Python 3.12.3 (main, Feb  4 2025, 14:48:35) [GCC 13.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import mpmath
>>> mpmath.__version__
'1.3.0'
>>> val = mpmath.mpf('0.1')
>>> f"{val:.016f}"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported format string passed to mpf.__format__
>>> 
```
Can you fix this bug?

Pull request description:
Misc fixes
* relax requirements on precision format, allow 0-prefixes.  closes #915
* correct handling of zeropad and fill_char options


Patch:
diff --git a/mpmath/libmp/libmpf.py b/mpmath/libmp/libmpf.py
index 1d1d4a1..459e571 100644
--- a/mpmath/libmp/libmpf.py
+++ b/mpmath/libmp/libmpf.py
@@ -1397,10 +1397,10 @@ def from_str(x, prec=0, rnd=round_fast, base=0):
     (?P<sign>[-+ ]?)
     (?P<no_neg_0>z)?
     (?P<alternate>\#)?
-    (?P<zeropad>0)?
-    (?P<width>0|[1-9][0-9]*)?
+    (?P<zeropad>0(?=0*[1-9]))?
+    (?P<width>[0-9]+)?
     (?P<thousands_separators>[,_])?
-    (?:\.(?P<precision>0|[1-9][0-9]*))?
+    (?:\.(?P<precision>[0-9]+))?
     (?P<rounding>[UDYZN])?
     (?P<type>[aAbeEfFgG%])?
 """, re.DOTALL | re.VERBOSE).fullmatch
@@ -1470,13 +1470,11 @@ def read_format_spec(format_spec):
         if rounding_char is not None:
             format_dict['rounding'] = _GMPY_ROUND_CHAR_DICT[rounding_char]
 
-        if match['zeropad'] and match['fill_char']:
-            raise ValueError('Cannot specify both 0-padding and a fill '
-                             'character')
-
         if match['zeropad']:
-            format_dict['align'] = '='
-            format_dict['fill_char'] = '0'
+            if not match['align']:
+                format_dict['align'] = '='
+            if not match['fill_char']:
+                format_dict['fill_char'] = '0'
 
         if format_dict['precision'] < 0 and format_dict['type'].lower() not in ['', 'a', 'b']:
             format_dict['precision'] = 6


Test patch:
diff --git a/mpmath/tests/test_format.py b/mpmath/tests/test_format.py
index b1c480d..3aeb69d 100644
--- a/mpmath/tests/test_format.py
+++ b/mpmath/tests/test_format.py
@@ -54,7 +54,9 @@ def fmt_str(draw, types='fFeE', for_complex=False):
         skip_thousand_separators = True
 
     # Width
-    res += draw(st.sampled_from(['']*7 + list(map(str, range(1, 40)))))
+    res += draw(st.sampled_from(['']*7 + list(map(str, range(1, 40)))
+                                + ([] if for_complex else ['0' + str(_)
+                                                           for _ in range(40)])))
 
     # grouping character (thousand_separators)
     gchar = draw(st.sampled_from([''] + list(',_')))
@@ -62,7 +64,8 @@ def fmt_str(draw, types='fFeE', for_complex=False):
         res += gchar
 
     # Precision
-    prec = draw(st.sampled_from(['']*7 + list(map(str, range(40)))))
+    prec = draw(st.sampled_from(['']*7 + list(map(str, range(40)))
+                + ['0' + str(_) for _ in range(40)]))
     if prec:
         res += '.' + prec
 
@@ -468,6 +471,8 @@ def test_mpf_fmt_cpython():
                  allow_infinity=True,
                  allow_subnormal=True))
 @example(fmt='.0g', x=9.995074823339339e-05)  # issue 880
+@example(fmt='.016f', x=0.1)  # issue 915
+@example(fmt='0030f', x=0.3)
 def test_mpf_floats_bulk(fmt, x):
     '''
     These are additional random tests that check that mp.mpf and fp.mpf yield
@@ -477,6 +482,9 @@ def test_mpf_floats_bulk(fmt, x):
     if not x and math.copysign(1, x) == -1:
         return  # skip negative zero
     spec = read_format_spec(fmt)
+    if (spec['width'] > 0 and spec['fill_char'] == '0'
+            and spec['thousands_separators']):
+        return  # issue 917
     if not spec['type'] and spec['precision'] < 0 and math.isfinite(x):
         # The mpmath could choose a different decimal
         # representative (wrt CPython) for same binary
@@ -797,11 +805,6 @@ def test_errors():
     with pytest.raises(ValueError, match="Invalid format specifier '12.3 E '"):
         f"{mp.mpf('4'):12.3 E }"
 
-    with pytest.raises(ValueError, match="Cannot specify both 0-padding "
-                       "and a fill character"):
-        f"{mp.mpf('4'):q<03f}"
-
-
 
 @settings(max_examples=10000)
 @given(st.floats(allow_nan=True, allow_infinity=True,