Repository: mpmath/mpmath (python)

Issue:
Incompatible "new-style" formatting without type specifiers
Development version allow for [formatting similar to Python floats](https://mpmath.readthedocs.io/en/latest/general.html#mpmath-mpf-format), including same format specifiers (i.e. `'f'` or `'e'`).  Though, this introduced an incompatibility in case when the format specifier is missing.

On v1.3.0:
```pycon
>>> from mpmath import *
>>> x = mpf(pi)
>>> f"{x}"
'3.14159265358979'
>>> f"{x!s}"
'3.14159265358979'
>>> f"{x!r}"
"mpf('3.1415926535897931')"
>>> f"{x!a}"
"mpf('3.1415926535897931')"
>>> f"{x=}"
"x=mpf('3.1415926535897931')"
```
In the master:
```pycon
>>> from mpmath import *
>>> x = mpf(pi)
>>> f"{x}"  # (!)
'3.1415926535897931'
>>> f"{x!s}"
'3.14159265358979'
>>> f"{x!r}"
"mpf('3.1415926535897931')"
>>> f"{x!a}"
"mpf('3.1415926535897931')"
>>> f"{x=}"
"x=mpf('3.1415926535897931')"
```

A quick fix (not sure if it's a good idea):
```diff
diff --git a/mpmath/libmp/libmpf.py b/mpmath/libmp/libmpf.py
index 9eb24222..3ca77388 100644
--- a/mpmath/libmp/libmpf.py
+++ b/mpmath/libmp/libmpf.py
@@ -1633,7 +1633,7 @@ def format_digits(num, format_dict, prec):
                 strip_last_zero = True
 
         if precision < 0:
-            precision = repr_dps(prec)
+            precision = prec_to_dps(prec)
         if precision == 0:
             precision = 1
 
```
The problem is that it breaks `float(format(x)) == float(format(float(x)))` invariant.

Pull request:
Fix new-style formatting without type specifier (repr_dps vs dps)
Closes #943

Patch:
diff --git a/docs/basics.rst b/docs/basics.rst
index e41f5fe..db94131 100644
--- a/docs/basics.rst
+++ b/docs/basics.rst
@@ -221,7 +221,10 @@ Setting the ``mp.pretty`` option will use the ``str()``-style output for ``repr(
     >>> mpf(0.6)
     mpf('0.59999999999999998')
 
-To use enough digits to be able recreate value exactly, set ``mp.pretty_dps`` to ``"repr"``.
+To use enough digits to be able recreate value exactly, set ``mp.pretty_dps``
+to ``"repr"`` (default value is ``"str"``).  Same option is used to control
+default number of digits in the new-style string formatting *without format
+specifier*, i.e. `format(exp(mpf(1)))`.
 
 The number of digits with which numbers are printed by default is determined by
 the working precision.  To specify the number of digits to show without
diff --git a/mpmath/ctx_mp_python.py b/mpmath/ctx_mp_python.py
index feb6765..af702f0 100644
--- a/mpmath/ctx_mp_python.py
+++ b/mpmath/ctx_mp_python.py
@@ -427,7 +427,8 @@ def __format__(s, format_spec):
         """
 
         _, _, (prec, _) = s._ctxdata
-        return format_mpf(s._mpf_, format_spec, prec)
+        ctx = s.context
+        return format_mpf(s._mpf_, format_spec, prec, ctx._pretty_repr_dps)
 
     def sqrt(s):
         return s.context.sqrt(s)
@@ -695,7 +696,8 @@ def __format__(s, format_spec):
         """
 
         _, _, (prec, _) = s._ctxdata
-        return format_mpc(s._mpc_, format_spec, prec)
+        ctx = s.context
+        return format_mpc(s._mpc_, format_spec, prec, ctx._pretty_repr_dps)
 
 
 complex_types = (complex, _mpc)
diff --git a/mpmath/libmp/libmpf.py b/mpmath/libmp/libmpf.py
index 9eb2422..b1f5f0a 100644
--- a/mpmath/libmp/libmpf.py
+++ b/mpmath/libmp/libmpf.py
@@ -1601,7 +1601,7 @@ def fill_sep(digits, sep, prev, nmod, sep_range):
                            for pos in range(nmod, len(digits), sep_range))
 
 
-def format_digits(num, format_dict, prec):
+def format_digits(num, format_dict, prec, _pretty_repr_dps):
     capitalize = False
     if format_dict['type'] in list('AFGE'):
         capitalize = True
@@ -1633,7 +1633,7 @@ def format_digits(num, format_dict, prec):
                 strip_last_zero = True
 
         if precision < 0:
-            precision = repr_dps(prec)
+            precision = repr_dps(prec) if _pretty_repr_dps else prec_to_dps(prec)
         if precision == 0:
             precision = 1
 
@@ -1741,9 +1741,9 @@ def format_digits(num, format_dict, prec):
     return sign, int_part + digits
 
 
-def format_mpf(num, format_spec, prec):
+def format_mpf(num, format_spec, prec, _pretty_repr_dps):
     format_dict = read_format_spec(format_spec)
-    sign, digits = format_digits(num, format_dict, prec)
+    sign, digits = format_digits(num, format_dict, prec, _pretty_repr_dps)
     nchars = len(digits) + len(sign)
     lpad, rpad = calc_padding(
             nchars, format_dict['width'], format_dict['align'])
@@ -1756,7 +1756,7 @@ def format_mpf(num, format_spec, prec):
             + rpad*format_dict['fill_char']
 
 
-def format_mpc(num, format_spec, prec):
+def format_mpc(num, format_spec, prec, _pretty_repr_dps):
     format_dict = read_format_spec(format_spec)
 
     if format_dict['fill_char'] == '0':
@@ -1772,10 +1772,10 @@ def format_mpc(num, format_spec, prec):
     fmt_type = format_dict['type'].lower()
     if not fmt_type:
         format_dict['type'] = 'g'
-    sign_re, digits_re = format_digits(num[0], format_dict, prec)
+    sign_re, digits_re = format_digits(num[0], format_dict, prec, _pretty_repr_dps)
     fmt_sign = format_dict['sign']
     format_dict['sign'] = '+'
-    sign_im, digits_im = format_digits(num[1], format_dict, prec)
+    sign_im, digits_im = format_digits(num[1], format_dict, prec, _pretty_repr_dps)
     digits_im += 'j'
 
     if not fmt_type:


Test patch:
diff --git a/mpmath/tests/test_format.py b/mpmath/tests/test_format.py
index 1932d99..f07ae6a 100644
--- a/mpmath/tests/test_format.py
+++ b/mpmath/tests/test_format.py
@@ -469,6 +469,10 @@ def test_mpf_fmt_cpython():
     # No formatting code.
 
     assert f'{mp.mpf(0.0):.0}' == '0e+00'
+    mp.pretty_dps = 'repr'
+    assert f'{mp.pi}' == '3.1415926535897931'
+    mp.pretty_dps = 'str'
+    assert f'{mp.pi}' == '3.14159265358979'
 
 
 @settings(max_examples=20000)
@@ -489,10 +493,12 @@ def test_mpf_floats_bulk(fmt, x):
     the same results for default precision.
     '''
 
+    mp.pretty_dps = "repr"
     if not x and math.copysign(1, x) == -1:
         return  # skip negative zero
     spec = read_format_spec(fmt)
     if spec['frac_separators'] and vinfo < (3, 14):
+        mp.pretty_dps = "str"
         return  # see also python/cpython#130860
     if not spec['type'] and spec['precision'] < 0 and math.isfinite(x):
         # The mpmath could choose a different decimal
@@ -503,6 +509,7 @@ def test_mpf_floats_bulk(fmt, x):
         if spec['type'] == '%' and math.isinf(100*x):
             return  # mpf can't overflow
         assert format(x, fmt) == format(mp.mpf(x), fmt)
+    mp.pretty_dps = "str"
 
 
 @settings(max_examples=20000)
@@ -511,8 +518,10 @@ def test_mpf_floats_bulk(fmt, x):
                           allow_infinity=True,
                           allow_subnormal=True))
 def test_mpc_complexes(fmt, z):
+    mp.pretty_dps = "repr"
     if ((not z.real and math.copysign(1, z.real) == -1)
             or (not z.imag and math.copysign(1, z.imag) == -1)):
+        mp.pretty_dps = "str"
         return  # skip negative zero
     spec = read_format_spec(fmt)
     if spec['frac_separators'] and vinfo < (3, 14):
@@ -527,6 +536,7 @@ def test_mpc_complexes(fmt, z):
             assert complex(format(z)) == complex(format(mp.mpc(z)))
     else:
         assert format(z, fmt) == format(mp.mpc(z), fmt)
+    mp.pretty_dps = "str"
 
 
 def test_mpc_fmt():