diff --git a/IPython/core/formatters.py b/IPython/core/formatters.py index f28336f..6d94c26 100644 --- a/IPython/core/formatters.py +++ b/IPython/core/formatters.py @@ -20,6 +20,7 @@ Authors: #----------------------------------------------------------------------------- # Stdlib imports +import sys import abc # We must use StringIO, as cStringIO doesn't handle unicode properly. from StringIO import StringIO @@ -27,7 +28,7 @@ from StringIO import StringIO # Our own imports from IPython.config.configurable import Configurable from IPython.external import pretty -from IPython.utils.traitlets import Bool, Dict, Int, Str +from IPython.utils.traitlets import Bool, Dict, Int, Str, CStr #----------------------------------------------------------------------------- @@ -352,13 +353,65 @@ class PlainTextFormatter(BaseFormatter): # The newline character. newline = Str('\n', config=True) + + # format-string for pprinting floats + float_format = Str('%r') + # setter for float precision, either int or direct format-string + float_precision = CStr('', config=True) + + def _float_precision_changed(self, name, old, new): + """float_precision changed, set float_format accordingly. + + float_precision can be set by int or str. + This will set float_format, after interpreting input. + If numpy has been imported, numpy print precision will also be set. + + integer `n` sets format to '%.nf', otherwise, format set directly. + + An empty string returns to defaults (repr for float, 8 for numpy). + + This parameter can be set via the '%precision' magic. + """ + + if '%' in new: + # got explicit format string + fmt = new + try: + fmt%3.14159 + except Exception: + raise ValueError("Precision must be int or format string, not %r"%new) + elif new: + # otherwise, should be an int + try: + i = int(new) + assert i >= 0 + except ValueError: + raise ValueError("Precision must be int or format string, not %r"%new) + except AssertionError: + raise ValueError("int precision must be non-negative, not %r"%i) + + fmt = '%%.%if'%i + if 'numpy' in sys.modules: + # set numpy precision if it has been imported + import numpy + numpy.set_printoptions(precision=i) + else: + # default back to repr + fmt = '%r' + if 'numpy' in sys.modules: + import numpy + # numpy default is 8 + numpy.set_printoptions(precision=8) + self.float_format = fmt # Use the default pretty printers from IPython.external.pretty. def _singleton_printers_default(self): return pretty._singleton_pprinters.copy() def _type_printers_default(self): - return pretty._type_pprinters.copy() + d = pretty._type_pprinters.copy() + d[float] = lambda obj,p,cycle: p.text(self.float_format%obj) + return d def _deferred_printers_default(self): return pretty._deferred_type_pprinters.copy() diff --git a/IPython/core/magic.py b/IPython/core/magic.py index 977c3f3..27bac57 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -3478,58 +3478,33 @@ Defaulting color scheme to 'NoColor'""" In [1]: from math import pi In [2]: %precision 3 + Out[2]: '%.3f' In [3]: pi Out[3]: 3.142 In [4]: %precision %i + Out[4]: '%i' In [5]: pi Out[5]: 3 In [6]: %precision %e + Out[6]: '%e' In [7]: pi**10 Out[7]: 9.364805e+04 In [8]: %precision + Out[8]: '%r' In [9]: pi**10 Out[9]: 93648.047476082982 """ - if '%' in s: - # got explicit format string - fmt = s - try: - fmt%3.14159 - except Exception: - raise ValueError("Precision must be int or format string, not %r"%s) - elif s: - # otherwise, should be an int - try: - i = int(s) - assert i >= 0 - except (ValueError, AssertionError): - raise ValueError("Precision must be non-negative int or format string, not %r"%s) - - fmt = '%%.%if'%i - if 'numpy' in sys.modules: - import numpy - numpy.set_printoptions(precision=i) - else: - # default back to repr - fmt = '%r' - if 'numpy' in sys.modules: - import numpy - # numpy default is 8 - numpy.set_printoptions(precision=8) - - def _pretty_float(obj,p,cycle): - p.text(fmt%obj) - ptformatter = self.shell.display_formatter.formatters['text/plain'] - ptformatter.for_type(float, _pretty_float) + ptformatter.float_precision = s + return ptformatter.float_format # end Magic diff --git a/IPython/core/tests/test_formatters.py b/IPython/core/tests/test_formatters.py index 2a48b5a..ea1b834 100644 --- a/IPython/core/tests/test_formatters.py +++ b/IPython/core/tests/test_formatters.py @@ -1,9 +1,15 @@ """Tests for the Formatters. """ +from math import pi + +try: + import numpy +except: + numpy = None import nose.tools as nt -from IPython.core.formatters import FormatterABC, DefaultFormatter +from IPython.core.formatters import FormatterABC, PlainTextFormatter class A(object): def __repr__(self): @@ -17,7 +23,7 @@ def foo_printer(obj, pp, cycle): pp.text('foo') def test_pretty(): - f = DefaultFormatter() + f = PlainTextFormatter() f.for_type(A, foo_printer) nt.assert_equals(f(A()), 'foo') nt.assert_equals(f(B()), 'foo') @@ -26,5 +32,43 @@ def test_pretty(): nt.assert_equals(f(B()), 'B()') def test_deferred(): - f = DefaultFormatter() + f = PlainTextFormatter() + +def test_precision(): + """test various values for float_precision.""" + f = PlainTextFormatter() + nt.assert_equals(f(pi), repr(pi)) + f.float_precision = 0 + if numpy: + po = numpy.get_printoptions() + nt.assert_equals(po['precision'], 0) + nt.assert_equals(f(pi), '3') + f.float_precision = 2 + if numpy: + po = numpy.get_printoptions() + nt.assert_equals(po['precision'], 2) + nt.assert_equals(f(pi), '3.14') + f.float_precision = '%g' + if numpy: + po = numpy.get_printoptions() + nt.assert_equals(po['precision'], 2) + nt.assert_equals(f(pi), '3.14159') + f.float_precision = '%e' + nt.assert_equals(f(pi), '3.141593e+00') + f.float_precision = '' + if numpy: + po = numpy.get_printoptions() + nt.assert_equals(po['precision'], 8) + nt.assert_equals(f(pi), repr(pi)) + +def test_bad_precision(): + """test various invalid values for float_precision.""" + f = PlainTextFormatter() + def set_fp(p): + f.float_precision=p + nt.assert_raises(ValueError, set_fp, '%') + nt.assert_raises(ValueError, set_fp, '%.3f%i') + nt.assert_raises(ValueError, set_fp, 'foo') + nt.assert_raises(ValueError, set_fp, -1) + diff --git a/IPython/core/tests/test_magic.py b/IPython/core/tests/test_magic.py index 486b5d7..271846f 100644 --- a/IPython/core/tests/test_magic.py +++ b/IPython/core/tests/test_magic.py @@ -386,4 +386,23 @@ def doctest_who(): In [7]: %who_ls Out[7]: ['alpha', 'beta'] - """ \ No newline at end of file + """ + +def doctest_precision(): + """doctest for %precision + + In [1]: f = get_ipython().shell.display_formatter.formatters['text/plain'] + + In [2]: %precision 5 + Out[2]: '%.5f' + + In [3]: f.float_format + Out[3]: '%.5f' + + In [4]: %precision %e + Out[4]: '%e' + + In [5]: f(3.1415927) + Out[5]: '3.141593e+00' + """ +