Repository: mpmath/mpmath
Pull request #954 (2025-05-09 08:45:58)

Support bin/hex float literals in the CLI
See https://github.com/skirpichev/peps/pull/4 and https://github.com/python/cpython/issues/114667.

skirpichev: I'm going to merge that.  This looks helpful for interactive work.  Also, it's the only example of non-AST-based source transformations.

Patch:
diff --git a/CHANGES b/CHANGES
index 7ed6806..8268a2c 100644
--- a/CHANGES
+++ b/CHANGES
@@ -25,8 +25,8 @@ Features:
 * Support pickling for matrices and mpi, see #761 (Sergey B Kirpichev)
 * Support matrix.__array__() dunder method, see #767 (Sergey B Kirpichev)
 * Support more number syntaxes, see #778 (Sergey B Kirpichev)
-* Run mpmath as a module for interactive work, see #773, #923, #931, #936 and
-  #939 (Sergey B Kirpichev)
+* Run mpmath as a module for interactive work, see #773, #923, #931, #936,
+  #939 and #954 (Sergey B Kirpichev)
 * Add signed option to to_man_exp(), see #783 (Sergey B Kirpichev)
 * Add fp.hypot, see #798 (Sergey B Kirpichev)
 * Support inf/nan's in ctx.almosteq(), #802 (Sergey B Kirpichev)
diff --git a/mpmath/__main__.py b/mpmath/__main__.py
index 0ecc64f..004f281 100644
--- a/mpmath/__main__.py
+++ b/mpmath/__main__.py
@@ -16,7 +16,8 @@
 import tokenize
 
 from mpmath import __version__
-from mpmath._interactive import IntegerDivisionWrapper, wrap_float_literals
+from mpmath._interactive import (IntegerDivisionWrapper, wrap_float_literals,
+                                 wrap_hexbinfloats)
 
 
 __all__ = ()
@@ -80,9 +81,10 @@ def main():
             shell.run_cell(l, silent=True)
         if not args.no_wrap_floats:
             source = """
-from mpmath._interactive import wrap_float_literals
+from mpmath._interactive import wrap_float_literals, wrap_hexbinfloats
 ip = get_ipython()
 ip.input_transformers_post.append(wrap_float_literals)
+ip.input_transformers_post.append(wrap_float_literals)
 del ip
 """
             shell.run_cell(source)
@@ -95,6 +97,7 @@ def main():
         if not args.no_wrap_division:
             ast_transformers.append(IntegerDivisionWrapper())
         if not args.no_wrap_floats:
+            source_transformers.append(wrap_hexbinfloats)
             source_transformers.append(wrap_float_literals)
 
         try:
diff --git a/mpmath/_interactive.py b/mpmath/_interactive.py
index 09d40e6..7a11fd2 100644
--- a/mpmath/_interactive.py
+++ b/mpmath/_interactive.py
@@ -1,5 +1,6 @@
 import ast
 import io
+import re
 import tokenize
 
 
@@ -58,3 +59,32 @@ def wrap_float_literals(lines):
     ast.fix_missing_locations(tree)
     source = ast.unparse(tree)
     return source.splitlines(keepends=True)
+
+
+_HEXFLT_MATCHER = re.compile(r"""
+    (?: [^"' ]|^)[ ]*(?P<hexflt>
+         0x
+         [0-9a-z]+
+         (?: \.[0-9a-z]*)?
+         p(?:[+-])?[0-9]+
+     )
+""", re.VERBOSE | re.IGNORECASE)
+_BINFLT_MATCHER = re.compile(r"""
+    (?: [^"' ]|^)[ ]*(?P<binflt>
+         0b
+         [01]+
+         (?: \.[01]*)?
+         p(?:[+-])?[0-9]+
+     )
+""", re.VERBOSE | re.IGNORECASE)
+
+
+def wrap_hexbinfloats(lines):
+    new_lines = []
+    for line in lines:
+        for r in _HEXFLT_MATCHER.findall(line):
+            line = line.replace(r, 'mpf("' + r + '", base=16)')
+        for r in _BINFLT_MATCHER.findall(line):
+            line = line.replace(r, 'mpf("' + r + '", base=2)')
+        new_lines.append(line)
+    return new_lines


Test patch:
diff --git a/mpmath/tests/test_cli.py b/mpmath/tests/test_cli.py
index 6ad217f..b0cc661 100644
--- a/mpmath/tests/test_cli.py
+++ b/mpmath/tests/test_cli.py
@@ -116,6 +116,10 @@ def test_bare_console_wrap_floats():
     assert c.expect_exact("mpc(real='1.0', imag='10.899999999999999999999999999995')\r\n>>> ") == 0
     assert c.send('mpf(10.9)\r\n') == 11
     assert c.expect_exact("mpf('10.899999999999999999999999999995')\r\n>>> ") == 0
+    assert c.send('0x1p-1\r\n') == 8
+    assert c.expect_exact("mpf('0.5')\r\n>>> ") == 0
+    assert c.send('0b1p+1\r\n') == 8
+    assert c.expect_exact("mpf('2.0')\r\n>>> ") == 0
 
 
 def test_bare_console_pretty():